From a0286f213e289c023e6790ee2b4bb5a5ea00aeb0 Mon Sep 17 00:00:00 2001 From: Elias Stepanik <40958815+eliasstepanik@users.noreply.github.com> Date: Mon, 25 Sep 2023 19:43:56 +0200 Subject: [PATCH] vdo.ninja docker --- app/Dockerfile | 7 + app/docker-compose.yml | 10 + app/nginx.conf | 33 + .gitignore => app/static/.gitignore | 0 AGPLv3.md => app/static/AGPLv3.md | 0 CONTRIBUTING.md => app/static/CONTRIBUTING.md | 38 +- IFRAME.md => app/static/IFRAME.md | 0 LICENCE.md => app/static/LICENCE.md | 28 +- README.md => app/static/README.md | 0 check.html => app/static/check.html | 1698 +- cloudflare.html => app/static/cloudflare.html | 532 +- codecs.html => app/static/codecs.html | 166 +- comms.html => app/static/comms.html | 4658 +- control.html => app/static/control.html | 240 +- convert.html => app/static/convert.html | 568 +- devices.css => app/static/devices.css | 0 devices.html => app/static/devices.html | 454 +- .../static/devices.json.html | 46 +- dock.html => app/static/dock.html | 594 +- electron.html => app/static/electron.html | 4 +- esports.html => app/static/esports.html | 260 +- .../static/examples}/addtoscene.html | 288 +- .../static/examples}/bigmutebutton.html | 240 +- .../static/examples}/changepass.html | 52 +- {examples => app/static/examples}/chat.html | 318 +- .../static/examples}/chatoverlay.html | 324 +- .../static/examples}/control.html | 244 +- .../examples}/custom_video_switcher.html | 0 .../static/examples}/draggable.html | 660 +- {examples => app/static/examples}/dual.html | 150 +- .../static/examples}/esports.html | 260 +- {examples => app/static/examples}/github.svg | 0 .../examples}/iframe.outbound-stats.html | 0 {examples => app/static/examples}/index.html | 4 +- {examples => app/static/examples}/main.css | 0 {examples => app/static/examples}/midi.html | 1110 +- {examples => app/static/examples}/mini.css | 4 +- {examples => app/static/examples}/mixer.html | 900 +- .../static/examples}/mobiledirector.css | 294 +- {examples => app/static/examples}/multi.html | 222 +- .../static/examples}/muteguestiframe.html | 636 +- {examples => app/static/examples}/nes.min.css | 0 .../static/examples}/obs_remote/index.html | 800 +- .../examples}/obs_remote/interface.html | 946 +- .../thirdparty/obs-websocket.min.js | 0 .../static/examples}/overlay.html | 290 +- {examples => app/static/examples}/p2p.html | 136 +- .../static/examples}/powerpoint.html | 270 +- {examples => app/static/examples}/readme.md | 46 +- .../static/examples}/remoteapi.html | 952 +- .../static/examples}/rotated.html | 268 +- .../static/examples}/sensoroverlay.html | 530 +- .../static/examples}/sensors.html | 558 +- {examples => app/static/examples}/socal.html | 368 +- {examples => app/static/examples}/status.html | 344 +- .../static/examples}/teleprompt.html | 576 +- .../static/examples}/teleprompter.html | 1350 +- .../static/examples}/test_overlay.html | 42 +- {examples => app/static/examples}/twitch.html | 714 +- .../static/examples}/waitingroom.html | 240 +- {examples => app/static/examples}/webhid.html | 266 +- .../static/examples}/youtube.html | 256 +- {examples => app/static/examples}/youtube.svg | 0 {examples => app/static/examples}/zoom.html | 364 +- fileshare.html => app/static/fileshare.html | 332 +- {filters => app/static/filters}/anon.js | 0 .../filters}/anon/addVideoRecordingEffect.js | 0 .../static/filters}/anon/anonymous.json | 0 .../static/filters}/anon/anonymous.png | Bin .../static/filters}/anon/anonymous_mask.blend | Bin {filters => app/static/filters}/dog.js | 0 .../static/filters}/dog/ThreeFlexMaterial.js | 0 .../filters}/dog/images/texture_pink.jpg | Bin .../filters}/dog/images/texture_white.jpg | Bin .../static/filters}/dog/index.html | 2 +- .../static/filters}/dog/libs/glfx.js | 0 {filters => app/static/filters}/dog/main.js | 0 .../filters}/dog/models/dog/alpha_ears.jpg | Bin .../dog/models/dog/alpha_ears_1024.jpg | Bin .../dog/models/dog/alpha_ears_256.jpg | Bin .../filters}/dog/models/dog/displace_nez.jpg | Bin .../dog/models/dog/displace_oreilles.jpg | Bin .../filters}/dog/models/dog/dog_ears.json | 0 .../filters}/dog/models/dog/dog_nose.json | 0 .../filters}/dog/models/dog/dog_tongue.jpg | Bin .../filters}/dog/models/dog/dog_tongue.json | 0 .../filters}/dog/models/dog/flex_ears.jpg | Bin .../dog/models/dog/flex_ears_1024.jpg | Bin .../filters}/dog/models/dog/flex_ears_256.jpg | Bin .../filters}/dog/models/dog/flex_nose.png | Bin .../filters}/dog/models/dog/flex_tongue.png | Bin .../dog/models/dog/flex_tongue_1024.png | Bin .../dog/models/dog/flex_tongue_256.png | Bin .../filters}/dog/models/dog/normal_ears.jpg | Bin .../filters}/dog/models/dog/normal_nose.jpg | Bin .../filters}/dog/models/dog/texture_ears.jpg | Bin .../filters}/dog/models/dog/texture_nose.jpg | Bin .../filters}/dog/models/dog/tongue_alpha.jpg | Bin .../dog/models/dog/tongue_alpha_1024.jpg | Bin .../dog/models/dog/tongue_alpha_256.jpg | Bin {filters => app/static/filters}/readme.md | 4 +- {filters => app/static/filters}/sample.js | 122 +- .../static/iframe-examples.js | 0 iframe.css => app/static/iframe.css | 0 iframe.html => app/static/iframe.html | 2 +- index.html => app/static/index.html | 48 +- install.md => app/static/install.md | 0 lib.js => app/static/lib.js | 78100 ++++++++-------- .../static/lineawesome}/LICENSE.txt | 0 .../static/lineawesome}/Readme.md | 0 .../static/lineawesome}/css/line-awesome.css | 4 +- .../lineawesome}/css/line-awesome.min.css | 2 +- .../lineawesome}/fonts/la-solid-900.eot | Bin .../lineawesome}/fonts/la-solid-900.svg | 0 .../lineawesome}/fonts/la-solid-900.ttf | Bin .../lineawesome}/fonts/la-solid-900.woff | Bin .../lineawesome}/fonts/la-solid-900.woff2 | Bin main.css => app/static/main.css | 4 +- main.js => app/static/main.js | 12934 +-- {media => app/static/media}/accept.png | Bin {media => app/static/media}/avatar.webp | Bin {media => app/static/media}/avatar1.png | Bin {media => app/static/media}/avatar2.png | Bin {media => app/static/media}/avatar3.png | Bin {media => app/static/media}/bg_sample.webp | Bin {media => app/static/media}/bg_sample2.webp | Bin .../static/media}/camera_inkscape.svg | 0 {media => app/static/media}/cap.webm | Bin {media => app/static/media}/fakesteve.webm | Bin {media => app/static/media}/favicon-16x16.png | Bin {media => app/static/media}/favicon-32x32.png | Bin {media => app/static/media}/favicon.ico | Bin {media => app/static/media}/grid_inkscape.svg | 0 {media => app/static/media}/hd.svg | 0 {media => app/static/media}/icon.png | Bin {media => app/static/media}/icon.svg | 0 {media => app/static/media}/join.mp3 | Bin {media => app/static/media}/join.ogg | Bin {media => app/static/media}/join.wav | Bin {media => app/static/media}/leave.mp3 | Bin {media => app/static/media}/leave.ogg | Bin {media => app/static/media}/leave.wav | Bin {media => app/static/media}/logo_cropped.png | Bin .../static/media}/logo_cropped_512.png | Bin {media => app/static/media}/micro.mp4 | Bin .../static/media}/monitor_inkscape.svg | 0 {media => app/static/media}/old_icon.png | Bin {media => app/static/media}/old_logo.png | Bin .../static/media}/permissions_chrome.jpg | Bin .../static/media}/plane_inkscape.svg | 0 {media => app/static/media}/profile.png | Bin {media => app/static/media}/robot.mp3 | Bin {media => app/static/media}/screenshare.webm | Bin {media => app/static/media}/sd.svg | 0 {media => app/static/media}/share.jpg | Bin {media => app/static/media}/streamdeck.png | Bin {media => app/static/media}/svg.md | 2 +- {media => app/static/media}/thirds.svg | 14 +- {media => app/static/media}/thirdshead.svg | 0 {media => app/static/media}/tone.mp3 | Bin {media => app/static/media}/tone.ogg | Bin .../static/media}/vdoNinja_logo_full.png | Bin {media => app/static/media}/vdoninja.svg | 0 meet.html => app/static/meet.html | 3950 +- midi.html => app/static/midi.html | 1150 +- .../static/minidirector.css | 292 +- mixer.html => app/static/mixer.html | 8382 +- monitor.html => app/static/monitor.html | 1180 +- popout.html => app/static/popout.html | 732 +- publish.html => app/static/publish.html | 2 +- regions.html => app/static/regions.html | 186 +- remotemidi.html => app/static/remotemidi.html | 3446 +- results.html => app/static/results.html | 1064 +- .../static/serviceWorker.js | 0 speedtest.css => app/static/speedtest.css | 0 speedtest.html => app/static/speedtest.html | 1186 +- stats.css => app/static/stats.css | 1664 +- stats.html => app/static/stats.html | 12 +- supports.css => app/static/supports.css | 0 supports.html => app/static/supports.html | 488 +- .../static/teleprompter.html | 1538 +- .../static/thirdparty}/CodecsHandler.js | 0 .../static/thirdparty}/StreamSaver.js | 0 .../static/thirdparty}/adapter.js | 0 {thirdparty => app/static/thirdparty}/aes.js | 0 .../static/thirdparty}/canvasFilters.js | 0 .../static/thirdparty}/ffmpeg.min.js | 0 .../static/thirdparty}/focus_worker.js | 0 .../thirdparty}/jeeliz/JeelizResizer.js | 0 .../thirdparty}/jeeliz/JeelizThreeHelper.js | 0 .../static/thirdparty}/jeeliz/Tween.min.js | 0 .../jeeliz/helpers/HeadControls.js | 0 .../jeeliz/helpers/JeelizCanvas2DHelper.js | 0 .../jeeliz/helpers/JeelizFaceCut.js | 0 .../jeeliz/helpers/JeelizResizer.js | 0 .../jeeliz/helpers/JeelizThreeHelper.js | 0 .../thirdparty}/jeeliz/helpers/README.md | 0 .../jeeliz/helpers/addDragEventListener.js | 0 .../thirdparty}/jeeliz/jeelizFaceFilter.js | 0 .../jeeliz/modules/jeelizFaceFilter.module.js | 0 .../modules/jeelizFaceFilter.moduleNoDOM.js | 0 .../jeeliz/neuralNets/NN_4EXPR_0.json | 0 .../jeeliz/neuralNets/NN_DEFAULT.json | 0 .../jeeliz/neuralNets/NN_INTEL1536.json | 0 .../jeeliz/neuralNets/NN_LIGHT_0.json | 0 .../jeeliz/neuralNets/NN_VERYLIGHT_0.json | 0 .../jeeliz/neuralNets/NN_VIEWTOP_0.json | 0 .../jeeliz/neuralNets/NN_WIDEANGLES_0.json | 0 .../static/thirdparty}/jeeliz/readme.md | 10 +- .../three/ShaderParticleEngine/SPE.min.js | 0 .../FlexMaterial/ThreeFlexMaterial.js | 0 .../GlowMaterial/threex.atmospherematerial.js | 0 .../threex.atmospherematerialdatgui.js | 0 .../GlowMaterial/threex.dilategeometry.js | 0 .../GlowMaterial/threex.geometricglow.js | 0 .../jeeliz/three/matrix/THREEMatrix.js | 0 .../jeeliz/three/v112/GLTFLoader.js | 0 .../thirdparty}/jeeliz/three/v112/three.js | 0 .../jeeliz/three/v112/three.min.js | 0 .../static/thirdparty}/jquery/jquery-3.6.0.js | 0 .../static/thirdparty}/jquery/jquery-ui.css | 0 .../static/thirdparty}/jquery/jquery-ui.js | 0 .../thirdparty}/jquery/jquery-ui.min.js | 0 .../static/thirdparty}/lyra/README.md | 0 .../thirdparty}/lyra/model_coeffs/README.md | 10 +- .../lyra/model_coeffs/lyragan.tflite | Bin .../lyra/model_coeffs/quantizer.tflite | Bin .../model_coeffs/soundstream_encoder.tflite | Bin .../lyra/webassembly_codec_wrapper.js | 0 .../lyra/webassembly_codec_wrapper.wasm | Bin .../static/thirdparty}/measureBlur.js | 0 .../static/thirdparty}/mitm.html | 0 .../static/thirdparty}/polyfill.min.js | 0 .../static/thirdparty}/polyfill.min.js.map | 0 .../static/thirdparty}/qrcode.min.js | 0 .../static/thirdparty}/readme.md | 4 +- {thirdparty => app/static/thirdparty}/sw.js | 0 .../tfjs/face-landmarks-detection.js | 124 +- .../thirdparty}/tfjs/tf-backend-webgl.js | 130 +- .../thirdparty}/tfjs/tf-backend-webgl.js.map | 0 .../static/thirdparty}/tfjs/tf-converter.js | 170 +- .../thirdparty}/tfjs/tf-converter.js.map | 0 .../static/thirdparty}/tfjs/tf-core.js | 2774 +- .../static/thirdparty}/tfjs/tf-core.js.map | 0 .../static/thirdparty}/tflite/README.md | 0 .../thirdparty}/tflite/segm_full_v679.tflite | Bin .../static/thirdparty}/tflite/tflite-simd.js | 1774 +- .../thirdparty}/tflite/tflite-simd.wasm | Bin .../static/thirdparty}/tflite/tflite.wasm | Bin .../static/thirdparty}/webmidi.js | 0 .../static/thirdparty}/webmidi3.js | 0 .../static/translations}/blank.json | 0 .../static/translations}/cn.json | 0 .../static/translations}/cs.json | 0 .../static/translations}/de.json | 0 .../static/translations}/en.json | 0 .../static/translations}/es.json | 0 .../static/translations}/eu.json | 0 .../static/translations}/fr.json | 0 .../static/translations}/it.json | 0 .../static/translations}/ja.json | 0 .../static/translations}/makepig.js | 146 +- .../static/translations}/nl.json | 0 .../static/translations}/pig.json | 0 .../static/translations}/pt-br.json | 0 .../static/translations}/pt.json | 0 .../static/translations}/readme.md | 36 +- .../static/translations}/ru.json | 0 .../static/translations}/tr.json | 0 .../static/translations}/translate.js | 550 +- .../static/translations}/uk.json | 0 .../static/turn-credentials-php.sample | 0 turnserver.conf => app/static/turnserver.conf | 0 turnserver.md => app/static/turnserver.md | 0 .../static/turnserver2.conf | 0 .../static/turnserver3.conf | 0 webrtc.js => app/static/webrtc.js | 2 +- whip.html => app/static/whip.html | 2 +- zoom.html => app/static/zoom.html | 364 +- 279 files changed, 73673 insertions(+), 73623 deletions(-) create mode 100644 app/Dockerfile create mode 100644 app/docker-compose.yml create mode 100644 app/nginx.conf rename .gitignore => app/static/.gitignore (100%) rename AGPLv3.md => app/static/AGPLv3.md (100%) rename CONTRIBUTING.md => app/static/CONTRIBUTING.md (99%) rename IFRAME.md => app/static/IFRAME.md (100%) rename LICENCE.md => app/static/LICENCE.md (98%) rename README.md => app/static/README.md (100%) rename check.html => app/static/check.html (95%) rename cloudflare.html => app/static/cloudflare.html (97%) rename codecs.html => app/static/codecs.html (96%) rename comms.html => app/static/comms.html (96%) rename control.html => app/static/control.html (96%) rename convert.html => app/static/convert.html (95%) rename devices.css => app/static/devices.css (100%) rename devices.html => app/static/devices.html (93%) rename devices.json.html => app/static/devices.json.html (95%) rename dock.html => app/static/dock.html (96%) rename electron.html => app/static/electron.html (99%) rename esports.html => app/static/esports.html (96%) rename {examples => app/static/examples}/addtoscene.html (96%) rename {examples => app/static/examples}/bigmutebutton.html (95%) rename {examples => app/static/examples}/changepass.html (96%) rename {examples => app/static/examples}/chat.html (95%) rename {examples => app/static/examples}/chatoverlay.html (96%) rename {examples => app/static/examples}/control.html (96%) rename {examples => app/static/examples}/custom_video_switcher.html (100%) rename {examples => app/static/examples}/draggable.html (95%) rename {examples => app/static/examples}/dual.html (96%) rename {examples => app/static/examples}/esports.html (96%) rename {examples => app/static/examples}/github.svg (100%) rename {examples => app/static/examples}/iframe.outbound-stats.html (100%) rename {examples => app/static/examples}/index.html (97%) rename {examples => app/static/examples}/main.css (100%) rename {examples => app/static/examples}/midi.html (96%) rename {examples => app/static/examples}/mini.css (95%) rename {examples => app/static/examples}/mixer.html (96%) rename {examples => app/static/examples}/mobiledirector.css (94%) rename {examples => app/static/examples}/multi.html (96%) rename {examples => app/static/examples}/muteguestiframe.html (96%) rename {examples => app/static/examples}/nes.min.css (100%) rename {examples => app/static/examples}/obs_remote/index.html (96%) rename {examples => app/static/examples}/obs_remote/interface.html (96%) rename {examples => app/static/examples}/obs_remote/thirdparty/obs-websocket.min.js (100%) rename {examples => app/static/examples}/overlay.html (96%) rename {examples => app/static/examples}/p2p.html (97%) rename {examples => app/static/examples}/powerpoint.html (96%) rename {examples => app/static/examples}/readme.md (98%) rename {examples => app/static/examples}/remoteapi.html (96%) rename {examples => app/static/examples}/rotated.html (95%) rename {examples => app/static/examples}/sensoroverlay.html (95%) rename {examples => app/static/examples}/sensors.html (96%) rename {examples => app/static/examples}/socal.html (95%) rename {examples => app/static/examples}/status.html (95%) rename {examples => app/static/examples}/teleprompt.html (94%) rename {examples => app/static/examples}/teleprompter.html (96%) rename {examples => app/static/examples}/test_overlay.html (93%) rename {examples => app/static/examples}/twitch.html (96%) rename {examples => app/static/examples}/waitingroom.html (95%) rename {examples => app/static/examples}/webhid.html (96%) rename {examples => app/static/examples}/youtube.html (96%) rename {examples => app/static/examples}/youtube.svg (100%) rename {examples => app/static/examples}/zoom.html (96%) rename fileshare.html => app/static/fileshare.html (95%) rename {filters => app/static/filters}/anon.js (100%) rename {filters => app/static/filters}/anon/addVideoRecordingEffect.js (100%) rename {filters => app/static/filters}/anon/anonymous.json (100%) rename {filters => app/static/filters}/anon/anonymous.png (100%) rename {filters => app/static/filters}/anon/anonymous_mask.blend (100%) rename {filters => app/static/filters}/dog.js (100%) rename {filters => app/static/filters}/dog/ThreeFlexMaterial.js (100%) rename {filters => app/static/filters}/dog/images/texture_pink.jpg (100%) rename {filters => app/static/filters}/dog/images/texture_white.jpg (100%) rename {filters => app/static/filters}/dog/index.html (98%) rename {filters => app/static/filters}/dog/libs/glfx.js (100%) rename {filters => app/static/filters}/dog/main.js (100%) rename {filters => app/static/filters}/dog/models/dog/alpha_ears.jpg (100%) rename {filters => app/static/filters}/dog/models/dog/alpha_ears_1024.jpg (100%) rename {filters => app/static/filters}/dog/models/dog/alpha_ears_256.jpg (100%) rename {filters => app/static/filters}/dog/models/dog/displace_nez.jpg (100%) rename {filters => app/static/filters}/dog/models/dog/displace_oreilles.jpg (100%) rename {filters => app/static/filters}/dog/models/dog/dog_ears.json (100%) rename {filters => app/static/filters}/dog/models/dog/dog_nose.json (100%) rename {filters => app/static/filters}/dog/models/dog/dog_tongue.jpg (100%) rename {filters => app/static/filters}/dog/models/dog/dog_tongue.json (100%) rename {filters => app/static/filters}/dog/models/dog/flex_ears.jpg (100%) rename {filters => app/static/filters}/dog/models/dog/flex_ears_1024.jpg (100%) rename {filters => app/static/filters}/dog/models/dog/flex_ears_256.jpg (100%) rename {filters => app/static/filters}/dog/models/dog/flex_nose.png (100%) rename {filters => app/static/filters}/dog/models/dog/flex_tongue.png (100%) rename {filters => app/static/filters}/dog/models/dog/flex_tongue_1024.png (100%) rename {filters => app/static/filters}/dog/models/dog/flex_tongue_256.png (100%) rename {filters => app/static/filters}/dog/models/dog/normal_ears.jpg (100%) rename {filters => app/static/filters}/dog/models/dog/normal_nose.jpg (100%) rename {filters => app/static/filters}/dog/models/dog/texture_ears.jpg (100%) rename {filters => app/static/filters}/dog/models/dog/texture_nose.jpg (100%) rename {filters => app/static/filters}/dog/models/dog/tongue_alpha.jpg (100%) rename {filters => app/static/filters}/dog/models/dog/tongue_alpha_1024.jpg (100%) rename {filters => app/static/filters}/dog/models/dog/tongue_alpha_256.jpg (100%) rename {filters => app/static/filters}/readme.md (98%) rename {filters => app/static/filters}/sample.js (97%) rename iframe-examples.js => app/static/iframe-examples.js (100%) rename iframe.css => app/static/iframe.css (100%) rename iframe.html => app/static/iframe.html (99%) rename index.html => app/static/index.html (98%) rename install.md => app/static/install.md (100%) rename lib.js => app/static/lib.js (97%) rename {lineawesome => app/static/lineawesome}/LICENSE.txt (100%) rename {lineawesome => app/static/lineawesome}/Readme.md (100%) rename {lineawesome => app/static/lineawesome}/css/line-awesome.css (99%) rename {lineawesome => app/static/lineawesome}/css/line-awesome.min.css (62%) rename {lineawesome => app/static/lineawesome}/fonts/la-solid-900.eot (100%) rename {lineawesome => app/static/lineawesome}/fonts/la-solid-900.svg (100%) rename {lineawesome => app/static/lineawesome}/fonts/la-solid-900.ttf (100%) rename {lineawesome => app/static/lineawesome}/fonts/la-solid-900.woff (100%) rename {lineawesome => app/static/lineawesome}/fonts/la-solid-900.woff2 (100%) rename main.css => app/static/main.css (99%) rename main.js => app/static/main.js (97%) rename {media => app/static/media}/accept.png (100%) rename {media => app/static/media}/avatar.webp (100%) rename {media => app/static/media}/avatar1.png (100%) rename {media => app/static/media}/avatar2.png (100%) rename {media => app/static/media}/avatar3.png (100%) rename {media => app/static/media}/bg_sample.webp (100%) rename {media => app/static/media}/bg_sample2.webp (100%) rename {media => app/static/media}/camera_inkscape.svg (100%) rename {media => app/static/media}/cap.webm (100%) rename {media => app/static/media}/fakesteve.webm (100%) rename {media => app/static/media}/favicon-16x16.png (100%) rename {media => app/static/media}/favicon-32x32.png (100%) rename {media => app/static/media}/favicon.ico (100%) rename {media => app/static/media}/grid_inkscape.svg (100%) rename {media => app/static/media}/hd.svg (100%) rename {media => app/static/media}/icon.png (100%) rename {media => app/static/media}/icon.svg (100%) rename {media => app/static/media}/join.mp3 (100%) rename {media => app/static/media}/join.ogg (100%) rename {media => app/static/media}/join.wav (100%) rename {media => app/static/media}/leave.mp3 (100%) rename {media => app/static/media}/leave.ogg (100%) rename {media => app/static/media}/leave.wav (100%) rename {media => app/static/media}/logo_cropped.png (100%) rename {media => app/static/media}/logo_cropped_512.png (100%) rename {media => app/static/media}/micro.mp4 (100%) rename {media => app/static/media}/monitor_inkscape.svg (100%) rename {media => app/static/media}/old_icon.png (100%) rename {media => app/static/media}/old_logo.png (100%) rename {media => app/static/media}/permissions_chrome.jpg (100%) rename {media => app/static/media}/plane_inkscape.svg (100%) rename {media => app/static/media}/profile.png (100%) rename {media => app/static/media}/robot.mp3 (100%) rename {media => app/static/media}/screenshare.webm (100%) rename {media => app/static/media}/sd.svg (100%) rename {media => app/static/media}/share.jpg (100%) rename {media => app/static/media}/streamdeck.png (100%) rename {media => app/static/media}/svg.md (98%) rename {media => app/static/media}/thirds.svg (99%) rename {media => app/static/media}/thirdshead.svg (100%) rename {media => app/static/media}/tone.mp3 (100%) rename {media => app/static/media}/tone.ogg (100%) rename {media => app/static/media}/vdoNinja_logo_full.png (100%) rename {media => app/static/media}/vdoninja.svg (100%) rename meet.html => app/static/meet.html (96%) rename midi.html => app/static/midi.html (96%) rename minidirector.css => app/static/minidirector.css (95%) rename mixer.html => app/static/mixer.html (96%) rename monitor.html => app/static/monitor.html (95%) rename popout.html => app/static/popout.html (96%) rename publish.html => app/static/publish.html (99%) rename regions.html => app/static/regions.html (95%) rename remotemidi.html => app/static/remotemidi.html (96%) rename results.html => app/static/results.html (95%) rename serviceWorker.js => app/static/serviceWorker.js (100%) rename speedtest.css => app/static/speedtest.css (100%) rename speedtest.html => app/static/speedtest.html (95%) rename stats.css => app/static/stats.css (96%) rename stats.html => app/static/stats.html (93%) rename supports.css => app/static/supports.css (100%) rename supports.html => app/static/supports.html (94%) rename teleprompter.html => app/static/teleprompter.html (96%) rename {thirdparty => app/static/thirdparty}/CodecsHandler.js (100%) rename {thirdparty => app/static/thirdparty}/StreamSaver.js (100%) rename {thirdparty => app/static/thirdparty}/adapter.js (100%) rename {thirdparty => app/static/thirdparty}/aes.js (100%) rename {thirdparty => app/static/thirdparty}/canvasFilters.js (100%) rename {thirdparty => app/static/thirdparty}/ffmpeg.min.js (100%) rename {thirdparty => app/static/thirdparty}/focus_worker.js (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/JeelizResizer.js (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/JeelizThreeHelper.js (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/Tween.min.js (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/helpers/HeadControls.js (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/helpers/JeelizCanvas2DHelper.js (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/helpers/JeelizFaceCut.js (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/helpers/JeelizResizer.js (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/helpers/JeelizThreeHelper.js (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/helpers/README.md (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/helpers/addDragEventListener.js (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/jeelizFaceFilter.js (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/modules/jeelizFaceFilter.module.js (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/modules/jeelizFaceFilter.moduleNoDOM.js (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/neuralNets/NN_4EXPR_0.json (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/neuralNets/NN_DEFAULT.json (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/neuralNets/NN_INTEL1536.json (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/neuralNets/NN_LIGHT_0.json (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/neuralNets/NN_VERYLIGHT_0.json (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/neuralNets/NN_VIEWTOP_0.json (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/neuralNets/NN_WIDEANGLES_0.json (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/readme.md (97%) rename {thirdparty => app/static/thirdparty}/jeeliz/three/ShaderParticleEngine/SPE.min.js (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/three/customMaterials/FlexMaterial/ThreeFlexMaterial.js (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/three/customMaterials/GlowMaterial/threex.atmospherematerial.js (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/three/customMaterials/GlowMaterial/threex.atmospherematerialdatgui.js (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/three/customMaterials/GlowMaterial/threex.dilategeometry.js (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/three/customMaterials/GlowMaterial/threex.geometricglow.js (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/three/matrix/THREEMatrix.js (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/three/v112/GLTFLoader.js (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/three/v112/three.js (100%) rename {thirdparty => app/static/thirdparty}/jeeliz/three/v112/three.min.js (100%) rename {thirdparty => app/static/thirdparty}/jquery/jquery-3.6.0.js (100%) rename {thirdparty => app/static/thirdparty}/jquery/jquery-ui.css (100%) rename {thirdparty => app/static/thirdparty}/jquery/jquery-ui.js (100%) rename {thirdparty => app/static/thirdparty}/jquery/jquery-ui.min.js (100%) rename {thirdparty => app/static/thirdparty}/lyra/README.md (100%) rename {thirdparty => app/static/thirdparty}/lyra/model_coeffs/README.md (97%) rename {thirdparty => app/static/thirdparty}/lyra/model_coeffs/lyragan.tflite (100%) rename {thirdparty => app/static/thirdparty}/lyra/model_coeffs/quantizer.tflite (100%) rename {thirdparty => app/static/thirdparty}/lyra/model_coeffs/soundstream_encoder.tflite (100%) rename {thirdparty => app/static/thirdparty}/lyra/webassembly_codec_wrapper.js (100%) rename {thirdparty => app/static/thirdparty}/lyra/webassembly_codec_wrapper.wasm (100%) rename {thirdparty => app/static/thirdparty}/measureBlur.js (100%) rename {thirdparty => app/static/thirdparty}/mitm.html (100%) rename {thirdparty => app/static/thirdparty}/polyfill.min.js (100%) rename {thirdparty => app/static/thirdparty}/polyfill.min.js.map (100%) rename {thirdparty => app/static/thirdparty}/qrcode.min.js (100%) rename {thirdparty => app/static/thirdparty}/readme.md (98%) rename {thirdparty => app/static/thirdparty}/sw.js (100%) rename {thirdparty => app/static/thirdparty}/tfjs/face-landmarks-detection.js (99%) rename {thirdparty => app/static/thirdparty}/tfjs/tf-backend-webgl.js (99%) rename {thirdparty => app/static/thirdparty}/tfjs/tf-backend-webgl.js.map (100%) rename {thirdparty => app/static/thirdparty}/tfjs/tf-converter.js (99%) rename {thirdparty => app/static/thirdparty}/tfjs/tf-converter.js.map (100%) rename {thirdparty => app/static/thirdparty}/tfjs/tf-core.js (99%) rename {thirdparty => app/static/thirdparty}/tfjs/tf-core.js.map (100%) rename {thirdparty => app/static/thirdparty}/tflite/README.md (100%) rename {thirdparty => app/static/thirdparty}/tflite/segm_full_v679.tflite (100%) rename {thirdparty => app/static/thirdparty}/tflite/tflite-simd.js (97%) rename {thirdparty => app/static/thirdparty}/tflite/tflite-simd.wasm (100%) rename {thirdparty => app/static/thirdparty}/tflite/tflite.wasm (100%) rename {thirdparty => app/static/thirdparty}/webmidi.js (100%) rename {thirdparty => app/static/thirdparty}/webmidi3.js (100%) rename {translations => app/static/translations}/blank.json (100%) rename {translations => app/static/translations}/cn.json (100%) rename {translations => app/static/translations}/cs.json (100%) rename {translations => app/static/translations}/de.json (100%) rename {translations => app/static/translations}/en.json (100%) rename {translations => app/static/translations}/es.json (100%) rename {translations => app/static/translations}/eu.json (100%) rename {translations => app/static/translations}/fr.json (100%) rename {translations => app/static/translations}/it.json (100%) rename {translations => app/static/translations}/ja.json (100%) rename {translations => app/static/translations}/makepig.js (97%) rename {translations => app/static/translations}/nl.json (100%) rename {translations => app/static/translations}/pig.json (100%) rename {translations => app/static/translations}/pt-br.json (100%) rename {translations => app/static/translations}/pt.json (100%) rename {translations => app/static/translations}/readme.md (98%) rename {translations => app/static/translations}/ru.json (100%) rename {translations => app/static/translations}/tr.json (100%) rename {translations => app/static/translations}/translate.js (96%) rename {translations => app/static/translations}/uk.json (100%) rename turn-credentials-php.sample => app/static/turn-credentials-php.sample (100%) rename turnserver.conf => app/static/turnserver.conf (100%) rename turnserver.md => app/static/turnserver.md (100%) rename turnserver2.conf => app/static/turnserver2.conf (100%) rename turnserver3.conf => app/static/turnserver3.conf (100%) rename webrtc.js => app/static/webrtc.js (94%) rename whip.html => app/static/whip.html (99%) rename zoom.html => app/static/zoom.html (96%) diff --git a/app/Dockerfile b/app/Dockerfile new file mode 100644 index 0000000..704277c --- /dev/null +++ b/app/Dockerfile @@ -0,0 +1,7 @@ +FROM nginx:alpine + +WORKDIR /app + +COPY .. . + +COPY nginx.conf /etc/nginx/nginx.conf \ No newline at end of file diff --git a/app/docker-compose.yml b/app/docker-compose.yml new file mode 100644 index 0000000..7d21766 --- /dev/null +++ b/app/docker-compose.yml @@ -0,0 +1,10 @@ +version: "3.9" + +services: + app: + container_name: app + image: app + build: . + restart: always + ports: + - "8080:80" \ No newline at end of file diff --git a/app/nginx.conf b/app/nginx.conf new file mode 100644 index 0000000..9af4091 --- /dev/null +++ b/app/nginx.conf @@ -0,0 +1,33 @@ +events { + worker_connections 1024; +} + +http { + include mime.types; + sendfile on; + + server { + listen 80; + listen [::]:80; + + server_name vdo.ninja; + + root /app/static; + index index.html; + + + location ~ ^/([^/]+)/([^/?]+)$ { + root /app/static; + try_files /$1/$2 /$1/$2.html /$1/$2/ /$2 /$2/ /$1/index.html; + add_header Access-Control-Allow-Origin *; + } + + location / { + if ($request_uri ~ ^/(.*)\.html$) { + return 302 /$1; + } + try_files $uri $uri.html $uri/ /index.html; + add_header Access-Control-Allow-Origin *; + } + } +} \ No newline at end of file diff --git a/.gitignore b/app/static/.gitignore similarity index 100% rename from .gitignore rename to app/static/.gitignore diff --git a/AGPLv3.md b/app/static/AGPLv3.md similarity index 100% rename from AGPLv3.md rename to app/static/AGPLv3.md diff --git a/CONTRIBUTING.md b/app/static/CONTRIBUTING.md similarity index 99% rename from CONTRIBUTING.md rename to app/static/CONTRIBUTING.md index d0477b5..00460ea 100644 --- a/CONTRIBUTING.md +++ b/app/static/CONTRIBUTING.md @@ -1,19 +1,19 @@ -# Contributor License Agreement (CLA) - -To ensure the long-term viability of this project, and for the protection of its creator and its users, we request that contributors to the project first agree to some basic terms. The terms when accepted applies to all of your past, present and future contributions. - -# Contribution Policy - -You are invited to digitally sign the CLA with the provided CLA Assissant service, with a link to sign it provided automatically after contributing to this Github project. You may also print, sign, scan, and then email the CLA to steve@seguin.email. - -It is not required that you sign the CLA for every contribution. Once you execute a CLA, it is valid until the CLA agreement is meaningfully changed and requires updating. - -If you are contributing on behalf of your company, an officer of your company (usually a VP or higher title) must sign the CLA on behalf of the company, indicating his or her title. The company can choose to list the specific individuals authorized to make contributions on the "Full Name" line, or may cover all employees with a blanket CLA by not limiting contributors to an authorized list. If necessary, the company may provide a list of authorized contributors in an attachment. The executive signing the CLA must be the first name on such an attached list, and this executive must sign the attachment as well. It may well be the case that your company already has signed a company-wide CLA with Stephen Seguin. Please check this first. - -You can stop your participation in a project at any time, but you cannot rescind your assignments or grants with respect to prior contributions. - -You hereby grant Steve Seguin (Steve) a perpetual, worldwide, royalty-free, irrevocable, non-exclusive, and transferable license to use, reproduce, prepare derivative works of, publicly display, publicly perform, distribute the submissions, and to sublicense such rights to others. The rights granted may be exercised in any form or format, and Steve may distribute and sublicense to others on any licensing terms, including without limitation: (a) open source licenses like the GNU General Public License (GPL), or the Berkeley Software Distribution license (BSD); or (b) binary, proprietary, or commercial licenses. - -You hereby represent that you are the sole and original author of all Submissions and that, to the best of your knowledge, the submissions do not infringe upon the rights of any third party. - -You agree to these terms with your continued submission. +# Contributor License Agreement (CLA) + +To ensure the long-term viability of this project, and for the protection of its creator and its users, we request that contributors to the project first agree to some basic terms. The terms when accepted applies to all of your past, present and future contributions. + +# Contribution Policy + +You are invited to digitally sign the CLA with the provided CLA Assissant service, with a link to sign it provided automatically after contributing to this Github project. You may also print, sign, scan, and then email the CLA to steve@seguin.email. + +It is not required that you sign the CLA for every contribution. Once you execute a CLA, it is valid until the CLA agreement is meaningfully changed and requires updating. + +If you are contributing on behalf of your company, an officer of your company (usually a VP or higher title) must sign the CLA on behalf of the company, indicating his or her title. The company can choose to list the specific individuals authorized to make contributions on the "Full Name" line, or may cover all employees with a blanket CLA by not limiting contributors to an authorized list. If necessary, the company may provide a list of authorized contributors in an attachment. The executive signing the CLA must be the first name on such an attached list, and this executive must sign the attachment as well. It may well be the case that your company already has signed a company-wide CLA with Stephen Seguin. Please check this first. + +You can stop your participation in a project at any time, but you cannot rescind your assignments or grants with respect to prior contributions. + +You hereby grant Steve Seguin (Steve) a perpetual, worldwide, royalty-free, irrevocable, non-exclusive, and transferable license to use, reproduce, prepare derivative works of, publicly display, publicly perform, distribute the submissions, and to sublicense such rights to others. The rights granted may be exercised in any form or format, and Steve may distribute and sublicense to others on any licensing terms, including without limitation: (a) open source licenses like the GNU General Public License (GPL), or the Berkeley Software Distribution license (BSD); or (b) binary, proprietary, or commercial licenses. + +You hereby represent that you are the sole and original author of all Submissions and that, to the best of your knowledge, the submissions do not infringe upon the rights of any third party. + +You agree to these terms with your continued submission. diff --git a/IFRAME.md b/app/static/IFRAME.md similarity index 100% rename from IFRAME.md rename to app/static/IFRAME.md diff --git a/LICENCE.md b/app/static/LICENCE.md similarity index 98% rename from LICENCE.md rename to app/static/LICENCE.md index 7f8c8d6..070988d 100644 --- a/LICENCE.md +++ b/app/static/LICENCE.md @@ -1,14 +1,14 @@ -The VDO.Ninja source repository is governed by the GNU AFFERO GENERAL PUBLIC LICENSE. (AGPL-3.0) -That AGPL-3.0 licence can be found here: [AGPLv3.md](https://github.com/steveseguin/vdo.ninja/blob/master/AGPLv3.md) - -In essence, VDO.Ninja is open-source and free to use, both for commercial and non-commercial use. -Modifications of AGPL-3.0 licenced code must be made publicly accessible. Please refer to that licence. - -Some individual source files may contain different licencing term and perhaps different copyright holders. -Such licencing and copyright information will be contained in the file's header and be limited to those files. -If no such header is present in a file, the default AGPL-3.0 licence applies. - -Unless stated otherwise, all code is copyright 2021 Stephen Seguin. All rights reserved. -Contributors to the VDO.Ninja project must first agree to the Contributor License Agreement (CLA). - -Thank you for your understanding. +The VDO.Ninja source repository is governed by the GNU AFFERO GENERAL PUBLIC LICENSE. (AGPL-3.0) +That AGPL-3.0 licence can be found here: [AGPLv3.md](https://github.com/steveseguin/vdo.ninja/blob/master/AGPLv3.md) + +In essence, VDO.Ninja is open-source and free to use, both for commercial and non-commercial use. +Modifications of AGPL-3.0 licenced code must be made publicly accessible. Please refer to that licence. + +Some individual source files may contain different licencing term and perhaps different copyright holders. +Such licencing and copyright information will be contained in the file's header and be limited to those files. +If no such header is present in a file, the default AGPL-3.0 licence applies. + +Unless stated otherwise, all code is copyright 2021 Stephen Seguin. All rights reserved. +Contributors to the VDO.Ninja project must first agree to the Contributor License Agreement (CLA). + +Thank you for your understanding. diff --git a/README.md b/app/static/README.md similarity index 100% rename from README.md rename to app/static/README.md diff --git a/check.html b/app/static/check.html similarity index 95% rename from check.html rename to app/static/check.html index 123c7a6..77f76d8 100644 --- a/check.html +++ b/app/static/check.html @@ -1,849 +1,849 @@ - - - - - - - VDON Stream Test - - - -
-

- Welcome -

-
-

- This application will access your camera and complete a video test stream. -
- -
- The full test will take a few minutes to complete.
- ⭐⭐⭐⭐ - -

-
- - - - - - - - - - - - + + + + + + + VDON Stream Test + + + +
+

+ Welcome +

+
+

+ This application will access your camera and complete a video test stream. +
+ +
+ The full test will take a few minutes to complete.
+ ⭐⭐⭐⭐ + +

+
+ + + + + + + + + + + + diff --git a/cloudflare.html b/app/static/cloudflare.html similarity index 97% rename from cloudflare.html rename to app/static/cloudflare.html index 6b0f218..a724623 100644 --- a/cloudflare.html +++ b/app/static/cloudflare.html @@ -1,267 +1,267 @@ - - - - Generate Cloudflare Auth - - - -
-

Generate Cloudflare Auth for VDO.Ninja

-
- - -

- - -

- - -

- -
-

- - - -
-

- What you can do with Cloudflare + VDO.Ninja? -

-

Restream VDO.Ninja as an RTMP Output

-

Live Video Inputs (Cloudflare feature) can be set up to forward any input to another input. This can be a RTMP(S) service such as YouTube, Twitch or Facebook Live.

-

In theory you could publish from VDO.Ninja WHIP output to Cloudstream, and then to your RTMP destinations, like Youtube.

-

-

Meshcast-alternative

-

Instead of using Meshcast to broadcast video from director to guest, or guest to scene, you can use Cloudflare instead.

-

Meshcast, or any compatible WHIP/WHEP service, can help reduce CPU and network load of guests by offloading distribution to a server, compared to using the peer-to-peer default of VDO.Ninja -

Automatic isolated guest recording

-

Cloudflare will automatically record incoming videos, allowing you (in theory) to have a backup of each guest in a room.

-

This offers a redundant backup for your recordings, but also makes it easier to do higher quality VODs edits after the live ends.

-

SRT, HLS, DASH, MP4, WHIP/WHEP options

-

Lots input and output options, although if you're here, you're probably interested in the WHIP/WHEP mainly.

-

VDO.Ninja is compatible with WHEP and WHIP!

-

Very competitive pricing

-

There's a free tier, which is more than enough for testing.

-

Or pay $1 per $1000 minutes of streaming.

-
-
-
-

- How it works? -

-

When used with VDO.Ninja, video is published to Cloudflare via WHIP, and the WHEP playback URL is distributed to viewers. Unless otherwise specified, viewers will use the WHEP URL as the source of media from the publisher, instead of using the normal peer-to-peer mode. This has the effect of reducing the CPU and network load when sharing media with multiple videos, as instead of distributing media via peer-to-peer, the media is distributed via a server. This approach does have some downsides also, so its not normally advisable unless desired or needed.

-

- Why do I need a special URL parameter? -

-

The reason we need a special generated URL parameter is because Cloudflare requires user accounts, unlike Meshcast. While you can generate WHIP URLs within your Cloudflare dashboard, and use them on VDO.Ninja links using &whipout, you'd need to create one per guest. Instead here, we're using our Cloudflare credentials to automatically create unique WHIP ingest URLs on demand for each guest, so you can get away with one-invite link for all your guests.

-

Since it's not advisable to share your Cloudflare credentials, particularly with random guests, this page will encrypt your credentials into URL-friendly parameter. Only the VDO.Ninja servers knows the decryption key, which limits what guest can do with the encrypted key. You can delete or restrict the credentials provided to VDO.Ninja from your Cloudflare dashboard, allowing you to limit or revoke any trust provided to VDO.Ninja.

-

- Where to get my Cloudflare account ID and token? -

- -

The Cloudflare account ID can be found on the right-hand side of the Workers & Pages (Overview) page, or it can be found on the right-lower side of any of your Website (domain) overview pages.

-

- As for the API token, you'll need to create it, with limited permissions. -

- You should now have access to both access token and account ID. -

-

- Can I self-host or hard-code my Cloudflare credentials? -

-

Yes, the code is open-sourced and it can be self-hosted, however please be aware there is limited support for those self-hosting.

-

- Does VDO.Ninja track me or store my private information? -

-

Please refer to the privacy policy, although the short answer is no. I can't say the same for Cloudflare, so please refer to their terms of service. -

- - + + + + Generate Cloudflare Auth + + + +
+

Generate Cloudflare Auth for VDO.Ninja

+
+ + +

+ + +

+ + +

+ +
+

+ + + +
+

+ What you can do with Cloudflare + VDO.Ninja? +

+

Restream VDO.Ninja as an RTMP Output

+

Live Video Inputs (Cloudflare feature) can be set up to forward any input to another input. This can be a RTMP(S) service such as YouTube, Twitch or Facebook Live.

+

In theory you could publish from VDO.Ninja WHIP output to Cloudstream, and then to your RTMP destinations, like Youtube.

+

+

Meshcast-alternative

+

Instead of using Meshcast to broadcast video from director to guest, or guest to scene, you can use Cloudflare instead.

+

Meshcast, or any compatible WHIP/WHEP service, can help reduce CPU and network load of guests by offloading distribution to a server, compared to using the peer-to-peer default of VDO.Ninja +

Automatic isolated guest recording

+

Cloudflare will automatically record incoming videos, allowing you (in theory) to have a backup of each guest in a room.

+

This offers a redundant backup for your recordings, but also makes it easier to do higher quality VODs edits after the live ends.

+

SRT, HLS, DASH, MP4, WHIP/WHEP options

+

Lots input and output options, although if you're here, you're probably interested in the WHIP/WHEP mainly.

+

VDO.Ninja is compatible with WHEP and WHIP!

+

Very competitive pricing

+

There's a free tier, which is more than enough for testing.

+

Or pay $1 per $1000 minutes of streaming.

+
+
+
+

+ How it works? +

+

When used with VDO.Ninja, video is published to Cloudflare via WHIP, and the WHEP playback URL is distributed to viewers. Unless otherwise specified, viewers will use the WHEP URL as the source of media from the publisher, instead of using the normal peer-to-peer mode. This has the effect of reducing the CPU and network load when sharing media with multiple videos, as instead of distributing media via peer-to-peer, the media is distributed via a server. This approach does have some downsides also, so its not normally advisable unless desired or needed.

+

+ Why do I need a special URL parameter? +

+

The reason we need a special generated URL parameter is because Cloudflare requires user accounts, unlike Meshcast. While you can generate WHIP URLs within your Cloudflare dashboard, and use them on VDO.Ninja links using &whipout, you'd need to create one per guest. Instead here, we're using our Cloudflare credentials to automatically create unique WHIP ingest URLs on demand for each guest, so you can get away with one-invite link for all your guests.

+

Since it's not advisable to share your Cloudflare credentials, particularly with random guests, this page will encrypt your credentials into URL-friendly parameter. Only the VDO.Ninja servers knows the decryption key, which limits what guest can do with the encrypted key. You can delete or restrict the credentials provided to VDO.Ninja from your Cloudflare dashboard, allowing you to limit or revoke any trust provided to VDO.Ninja.

+

+ Where to get my Cloudflare account ID and token? +

+ +

The Cloudflare account ID can be found on the right-hand side of the Workers & Pages (Overview) page, or it can be found on the right-lower side of any of your Website (domain) overview pages.

+

+ As for the API token, you'll need to create it, with limited permissions. +

+ You should now have access to both access token and account ID. +

+

+ Can I self-host or hard-code my Cloudflare credentials? +

+

Yes, the code is open-sourced and it can be self-hosted, however please be aware there is limited support for those self-hosting.

+

+ Does VDO.Ninja track me or store my private information? +

+

Please refer to the privacy policy, although the short answer is no. I can't say the same for Cloudflare, so please refer to their terms of service. +

+ + \ No newline at end of file diff --git a/codecs.html b/app/static/codecs.html similarity index 96% rename from codecs.html rename to app/static/codecs.html index 62caa7c..0ca8ed2 100644 --- a/codecs.html +++ b/app/static/codecs.html @@ -1,84 +1,84 @@ - - -
- - - + + +
+ + + \ No newline at end of file diff --git a/comms.html b/app/static/comms.html similarity index 96% rename from comms.html rename to app/static/comms.html index d88edfc..3692ca4 100644 --- a/comms.html +++ b/app/static/comms.html @@ -1,2330 +1,2330 @@ - - - Comms app - - - - - - - - - - - - - - - - -
-
-
- - -
-
-

Comms

multi-group audio chat

- - - - Generate a random room name - -
-
-

- -

- -
-
-
-
-
- - -
- - - + + + Comms app + + + + + + + + + + + + + + + + +
+
+
+ + +
+
+

Comms

multi-group audio chat

+ + + + Generate a random room name + +
+
+

+ +

+ +
+
+
+
+
+ + +
+ + + \ No newline at end of file diff --git a/control.html b/app/static/control.html similarity index 96% rename from control.html rename to app/static/control.html index 976f36e..b1457ad 100644 --- a/control.html +++ b/app/static/control.html @@ -1,121 +1,121 @@ - - - - - - - - - - - + + + + + + + + + + + \ No newline at end of file diff --git a/convert.html b/app/static/convert.html similarity index 95% rename from convert.html rename to app/static/convert.html index 82e1d4d..99d6df2 100644 --- a/convert.html +++ b/app/static/convert.html @@ -1,285 +1,285 @@ - - - - - - - -
-
-

Web-based Media Conversion Tools

-
-

WebM (or MKV/FLV) to MP4 (fixed 1280x720 resolution) (very slow!)

-
- The same as: ffmpeg -i input.webm -vf scale="1280:720" output.mp4 - -
-
-
-

WebM to MP4 files (no transcoding, *attempts* to force high resolutions)

-
- The same as: ffmpeg.exe -i concat:"cap.webm|input.webm" -safe 0 -c copy -avoid_negative_ts 1 -strict experimental output.mp4 - -
-
-
-

WebM to Audio-only files (opus or wav)

-
- The same as: ffmpeg -i input.webm -vn -acodec copy output.wav - -
-
-
-

MKV (or FLV/WebM) to MP4 (no transcoding)

-
- The same as: ffmpeg -i INPUTFILE -vcodec copy -acodec copy output.mp4 - -
-
-
-

Having problems?

-
- For larger files, over 2-gigabytes in size, the browser may not be able to properly process the video in memory. -
-
- Please consider using FFmpeg [get it free here] to run these processes from the command-line instead. The corresponding commands are provided above, where you need to replace input.webm with your own file. -
-
- Other users who find FFmpeg too challenging have had luck using the graphical Handbrake application instead. -
-
-
- - - -
-
- - - - - + + + + + + + +
+
+

Web-based Media Conversion Tools

+
+

WebM (or MKV/FLV) to MP4 (fixed 1280x720 resolution) (very slow!)

+
+ The same as: ffmpeg -i input.webm -vf scale="1280:720" output.mp4 + +
+
+
+

WebM to MP4 files (no transcoding, *attempts* to force high resolutions)

+
+ The same as: ffmpeg.exe -i concat:"cap.webm|input.webm" -safe 0 -c copy -avoid_negative_ts 1 -strict experimental output.mp4 + +
+
+
+

WebM to Audio-only files (opus or wav)

+
+ The same as: ffmpeg -i input.webm -vn -acodec copy output.wav + +
+
+
+

MKV (or FLV/WebM) to MP4 (no transcoding)

+
+ The same as: ffmpeg -i INPUTFILE -vcodec copy -acodec copy output.mp4 + +
+
+
+

Having problems?

+
+ For larger files, over 2-gigabytes in size, the browser may not be able to properly process the video in memory. +
+
+ Please consider using FFmpeg [get it free here] to run these processes from the command-line instead. The corresponding commands are provided above, where you need to replace input.webm with your own file. +
+
+ Other users who find FFmpeg too challenging have had luck using the graphical Handbrake application instead. +
+
+
+ + + +
+
+ + + + + \ No newline at end of file diff --git a/devices.css b/app/static/devices.css similarity index 100% rename from devices.css rename to app/static/devices.css diff --git a/devices.html b/app/static/devices.html similarity index 93% rename from devices.html rename to app/static/devices.html index 3c24de2..47ba2b0 100644 --- a/devices.html +++ b/app/static/devices.html @@ -1,227 +1,227 @@ - - - - - - - - - - -
-
- Device IDs are bound to a combination of domain and browser.
If - you want to use electron-capture, open this URL on the electron-capture - app.
- Click device names to preset them. Select multiple audio inputs by clicking multiple devices. -
-
- Check for browser and camera capabilities here. -
-
-

🎤 Audio Inputs

-
-
-
-

📹 Video Inputs

-
-
-
-

🔉 Audio Outputs

-
-
-
- - - - - + + + + + + + + + + +
+
+ Device IDs are bound to a combination of domain and browser.
If + you want to use electron-capture, open this URL on the electron-capture + app.
+ Click device names to preset them. Select multiple audio inputs by clicking multiple devices. +
+
+ Check for browser and camera capabilities here. +
+
+

🎤 Audio Inputs

+
+
+
+

📹 Video Inputs

+
+
+
+

🔉 Audio Outputs

+
+
+
+ + + + + diff --git a/devices.json.html b/app/static/devices.json.html similarity index 95% rename from devices.json.html rename to app/static/devices.json.html index e5f80e6..bd95666 100644 --- a/devices.json.html +++ b/app/static/devices.json.html @@ -1,24 +1,24 @@ - - - - - + + + + + \ No newline at end of file diff --git a/dock.html b/app/static/dock.html similarity index 96% rename from dock.html rename to app/static/dock.html index df0ff58..ac786b5 100644 --- a/dock.html +++ b/app/static/dock.html @@ -1,298 +1,298 @@ - - - - - - - -
- -

- - - -
- -
- -
- -
-
- - -
-
- - -
-
- - -
-
- Add password: - -

- Add to a room: - -
-
- -
- -
- - -
- + + + + + + + +
+ +

+ + + +
+ +
+ +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ Add password: + +

+ Add to a room: + +
+
+ +
+ +
+ + +
+ \ No newline at end of file diff --git a/electron.html b/app/static/electron.html similarity index 99% rename from electron.html rename to app/static/electron.html index a749d01..3c9ccbf 100644 --- a/electron.html +++ b/app/static/electron.html @@ -2,7 +2,7 @@ - + - - - - - -
- -
- + + + IFRAME Example + + + + + + + + +
+ +
+ \ No newline at end of file diff --git a/examples/addtoscene.html b/app/static/examples/addtoscene.html similarity index 96% rename from examples/addtoscene.html rename to app/static/examples/addtoscene.html index 2138d00..964f356 100644 --- a/examples/addtoscene.html +++ b/app/static/examples/addtoscene.html @@ -1,145 +1,145 @@ - - - IFRAME Example - - - - - - - - -
- - -
- The password for guests is 1234
-
-
- Custom guest invites and toggles for add/removing from scene=1 are on the bottom. -
-
- Scene=1 link: https://vdo.ninja/?scene=1&room=teststeve123&password=1234 - - - - -
- + + + IFRAME Example + + + + + + + + +
+ + +
+ The password for guests is 1234
+
+
+ Custom guest invites and toggles for add/removing from scene=1 are on the bottom. +
+
+ Scene=1 link: https://vdo.ninja/?scene=1&room=teststeve123&password=1234 + + + + +
+ \ No newline at end of file diff --git a/examples/bigmutebutton.html b/app/static/examples/bigmutebutton.html similarity index 95% rename from examples/bigmutebutton.html rename to app/static/examples/bigmutebutton.html index 44b8e47..7280e58 100644 --- a/examples/bigmutebutton.html +++ b/app/static/examples/bigmutebutton.html @@ -1,121 +1,121 @@ - -Twitch + Video - - - - - -
-
- -
-
- - + +Twitch + Video + + + + + +
+
+ +
+
+ + \ No newline at end of file diff --git a/examples/changepass.html b/app/static/examples/changepass.html similarity index 96% rename from examples/changepass.html rename to app/static/examples/changepass.html index 6ea3b3c..9c195a2 100644 --- a/examples/changepass.html +++ b/app/static/examples/changepass.html @@ -1,27 +1,27 @@ - \ No newline at end of file diff --git a/examples/chat.html b/app/static/examples/chat.html similarity index 95% rename from examples/chat.html rename to app/static/examples/chat.html index 3a5080b..4e68957 100644 --- a/examples/chat.html +++ b/app/static/examples/chat.html @@ -1,159 +1,159 @@ - - - - - OBSN Chat Overlay - - - - - - + + + + + OBSN Chat Overlay + + + + + + diff --git a/examples/chatoverlay.html b/app/static/examples/chatoverlay.html similarity index 96% rename from examples/chatoverlay.html rename to app/static/examples/chatoverlay.html index 2cd5ae7..718db33 100644 --- a/examples/chatoverlay.html +++ b/app/static/examples/chatoverlay.html @@ -1,162 +1,162 @@ - - - - - VDON Chat Overlay - - - - - - + + + + + VDON Chat Overlay + + + + + + diff --git a/examples/control.html b/app/static/examples/control.html similarity index 96% rename from examples/control.html rename to app/static/examples/control.html index f9a97d4..0b67a83 100644 --- a/examples/control.html +++ b/app/static/examples/control.html @@ -1,123 +1,123 @@ - - - - - - - - - - - + + + + + + + + + + + \ No newline at end of file diff --git a/examples/custom_video_switcher.html b/app/static/examples/custom_video_switcher.html similarity index 100% rename from examples/custom_video_switcher.html rename to app/static/examples/custom_video_switcher.html diff --git a/examples/draggable.html b/app/static/examples/draggable.html similarity index 95% rename from examples/draggable.html rename to app/static/examples/draggable.html index 27575e7..dbb272f 100644 --- a/examples/draggable.html +++ b/app/static/examples/draggable.html @@ -1,331 +1,331 @@ - -Dual Input - - - - - - -You can drag and resize the generated windows; multiple can be created. - -
- - - + +Dual Input + + + + + + +You can drag and resize the generated windows; multiple can be created. + +
+ + + \ No newline at end of file diff --git a/examples/dual.html b/app/static/examples/dual.html similarity index 96% rename from examples/dual.html rename to app/static/examples/dual.html index b3aab36..1756ad3 100644 --- a/examples/dual.html +++ b/app/static/examples/dual.html @@ -1,76 +1,76 @@ - -Dual Input - - - - - - - - - - - - - + +Dual Input + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/esports.html b/app/static/examples/esports.html similarity index 96% rename from examples/esports.html rename to app/static/examples/esports.html index 5185b7b..87d76d7 100644 --- a/examples/esports.html +++ b/app/static/examples/esports.html @@ -1,131 +1,131 @@ - - - IFRAME Example - - - - - - - - -
- -
- + + + IFRAME Example + + + + + + + + +
+ +
+ \ No newline at end of file diff --git a/examples/github.svg b/app/static/examples/github.svg similarity index 100% rename from examples/github.svg rename to app/static/examples/github.svg diff --git a/examples/iframe.outbound-stats.html b/app/static/examples/iframe.outbound-stats.html similarity index 100% rename from examples/iframe.outbound-stats.html rename to app/static/examples/iframe.outbound-stats.html diff --git a/examples/index.html b/app/static/examples/index.html similarity index 97% rename from examples/index.html rename to app/static/examples/index.html index 02d9be0..bf77ee5 100644 --- a/examples/index.html +++ b/app/static/examples/index.html @@ -1,5 +1,5 @@ - + - VDO.Ninja MIDI Controller - - - - -
-
-

VDO.Ninja MIDI test app

- -
-

About

-
- You can check the console debug logs for added details. -

You can download a virtual MIDI I/O controller for windwos here:
- http://www.tobias-erichsen.de/software/loopmidi.html -

This code uses the WebMIDI.js library, referenced here:
- https://github.com/djipco/webmidi -

- Below you can test the MIDI hotkey commands for VDO.Ninja below:
-
-
-
-

Select the MIDI Output device:

-
- - -
-
-
-

&midi=1

-
- -
-
-
-

&midi=3

-
- -
-
-
-

&midi=4 ; director

-
- -
-
-
-

&midi=4 ; guest 1

-
- -
-
-
-

&midi=4 ; guest 2

-
- -
-
-
-

Sample Remote Director Control links

-
- -
-
-
-
- - - -
- -
- - - + + + + + + VDO.Ninja MIDI Controller + + + + +
+
+

VDO.Ninja MIDI test app

+ +
+

About

+
+ You can check the console debug logs for added details. +

You can download a virtual MIDI I/O controller for windwos here:
+ http://www.tobias-erichsen.de/software/loopmidi.html +

This code uses the WebMIDI.js library, referenced here:
+ https://github.com/djipco/webmidi +

+ Below you can test the MIDI hotkey commands for VDO.Ninja below:
+
+
+
+

Select the MIDI Output device:

+
+ + +
+
+
+

&midi=1

+
+ +
+
+
+

&midi=3

+
+ +
+
+
+

&midi=4 ; director

+
+ +
+
+
+

&midi=4 ; guest 1

+
+ +
+
+
+

&midi=4 ; guest 2

+
+ +
+
+
+

Sample Remote Director Control links

+
+ +
+
+
+
+ + + +
+ +
+ + + diff --git a/examples/mini.css b/app/static/examples/mini.css similarity index 95% rename from examples/mini.css rename to app/static/examples/mini.css index 6a4100f..65e9665 100644 --- a/examples/mini.css +++ b/app/static/examples/mini.css @@ -1,3 +1,3 @@ -.tile { - max-width:200px !important; +.tile { + max-width:200px !important; } \ No newline at end of file diff --git a/examples/mixer.html b/app/static/examples/mixer.html similarity index 96% rename from examples/mixer.html rename to app/static/examples/mixer.html index 538a80c..be72e98 100644 --- a/examples/mixer.html +++ b/app/static/examples/mixer.html @@ -1,451 +1,451 @@ - - - IFRAME Example - - - - - - -
-
-
REMOVE
-
-
- -
-
SLOT 1
-
SLOT 2
-
SLOT 3
-
SLOT 4
-
-
-
- + + + IFRAME Example + + + + + + +
+
+
REMOVE
+
+
+ +
+
SLOT 1
+
SLOT 2
+
SLOT 3
+
SLOT 4
+
+
+
+ \ No newline at end of file diff --git a/examples/mobiledirector.css b/app/static/examples/mobiledirector.css similarity index 94% rename from examples/mobiledirector.css rename to app/static/examples/mobiledirector.css index 5b72f61..cc2b74e 100644 --- a/examples/mobiledirector.css +++ b/app/static/examples/mobiledirector.css @@ -1,147 +1,147 @@ -body{ - zoom: 75%; -} -button[data-action-type='solo-chat'] { - display:none! important; -} - -button[data-action-type] { - padding: 20px 10px; -} - -#controlButtons{ - display:none! important; -} - -button[data-cluster='2'] { - display:none! important; -} - -button[data-action-type='solo-video'] { - display:unset!important; - visibility: visible; - width:unset; - height:unset; - opacity: 1; -} - -div > a.soloLink{ - display:none! important; -} - -div.shift{ - display:none! important; -} - -div.streamID{ - display:none! important; -} - -button[data-action-type='forward'] { - display:none! important; -} - -button[data-action-type='direct-chat'] { - display:none! important; -} - -button[data-action-type='hangup'] { - display:none! important; -} - -button[data-action-type='solo-chat'] { - display:none! important; -} - -button[data-action-type='solo-chat'] { - display:none! important; -} - -button[data-cluster='1'] { - display:none! important; -} - - -button[data-action-type='recorder-local'] { - display:none! important; -} -span[data-action-type='ordering'] { - display:none! important; -} -button[data-action-type='open-file-share'] { - display:none! important; -} - -button[data-action-type='add-channel']{ - display:none! important; -} -button[data-action-type='toggle-remote-speaker']{ - display:none! important; -} -button[data-action-type='toggle-remote-display']{ - display:none! important; -} -button[data-action-type='hide-guest']{ - display:none! important; -} -button[data-action-type='create-timer']{ - display:none! important; -} -button[data-action-type='change-url']{ - display:none! important; -} -button[data-action-type='change-params']{ - display:none! important; -} -span[data-action-type='change-quality']{ - display:none! important; -} - -span[data-action-type='sceneCluster2']{ - display:none! important; -} -span[data-action-type='sceneCluster1']{ - display:none! important; -} - -.orderspan{ - display:none! important; -} -#roomHeader{ - display:none! important; -} -.directorContainer { - display:none!important; -} - -.hideDropMenu{ - display:none!important; -} - -#header{ - display:none!important; -} -body { - background-color: #000; -} - -button[class="pull-right"]{ - display:none! important; -} - -div#guestFeeds { - padding: 1px!important; - margin: 1px!important; -} -div[class="vidcon directorMargins"] { - padding: 1px!important; - margin: 1px!important; - width: 260px; -} -button[data-action-type]{ - margin: 1px!important; -} - - - - +body{ + zoom: 75%; +} +button[data-action-type='solo-chat'] { + display:none! important; +} + +button[data-action-type] { + padding: 20px 10px; +} + +#controlButtons{ + display:none! important; +} + +button[data-cluster='2'] { + display:none! important; +} + +button[data-action-type='solo-video'] { + display:unset!important; + visibility: visible; + width:unset; + height:unset; + opacity: 1; +} + +div > a.soloLink{ + display:none! important; +} + +div.shift{ + display:none! important; +} + +div.streamID{ + display:none! important; +} + +button[data-action-type='forward'] { + display:none! important; +} + +button[data-action-type='direct-chat'] { + display:none! important; +} + +button[data-action-type='hangup'] { + display:none! important; +} + +button[data-action-type='solo-chat'] { + display:none! important; +} + +button[data-action-type='solo-chat'] { + display:none! important; +} + +button[data-cluster='1'] { + display:none! important; +} + + +button[data-action-type='recorder-local'] { + display:none! important; +} +span[data-action-type='ordering'] { + display:none! important; +} +button[data-action-type='open-file-share'] { + display:none! important; +} + +button[data-action-type='add-channel']{ + display:none! important; +} +button[data-action-type='toggle-remote-speaker']{ + display:none! important; +} +button[data-action-type='toggle-remote-display']{ + display:none! important; +} +button[data-action-type='hide-guest']{ + display:none! important; +} +button[data-action-type='create-timer']{ + display:none! important; +} +button[data-action-type='change-url']{ + display:none! important; +} +button[data-action-type='change-params']{ + display:none! important; +} +span[data-action-type='change-quality']{ + display:none! important; +} + +span[data-action-type='sceneCluster2']{ + display:none! important; +} +span[data-action-type='sceneCluster1']{ + display:none! important; +} + +.orderspan{ + display:none! important; +} +#roomHeader{ + display:none! important; +} +.directorContainer { + display:none!important; +} + +.hideDropMenu{ + display:none!important; +} + +#header{ + display:none!important; +} +body { + background-color: #000; +} + +button[class="pull-right"]{ + display:none! important; +} + +div#guestFeeds { + padding: 1px!important; + margin: 1px!important; +} +div[class="vidcon directorMargins"] { + padding: 1px!important; + margin: 1px!important; + width: 260px; +} +button[data-action-type]{ + margin: 1px!important; +} + + + + diff --git a/examples/multi.html b/app/static/examples/multi.html similarity index 96% rename from examples/multi.html rename to app/static/examples/multi.html index 1c9c932..f5d49f0 100644 --- a/examples/multi.html +++ b/app/static/examples/multi.html @@ -1,112 +1,112 @@ - -Dual Input - - - - - - - - + +Dual Input + + + + + + + + \ No newline at end of file diff --git a/examples/muteguestiframe.html b/app/static/examples/muteguestiframe.html similarity index 96% rename from examples/muteguestiframe.html rename to app/static/examples/muteguestiframe.html index 74f24bd..17724e2 100644 --- a/examples/muteguestiframe.html +++ b/app/static/examples/muteguestiframe.html @@ -1,319 +1,319 @@ - - - IFRAME Example - - - - - - - - -
- - -
- The password for guests is 1234
-
-
- Custom guest invites and toggles for add/removing from scene=1 are on the bottom. -
- + + + IFRAME Example + + + + + + + + +
+ + +
+ The password for guests is 1234
+
+
+ Custom guest invites and toggles for add/removing from scene=1 are on the bottom. +
+ \ No newline at end of file diff --git a/examples/nes.min.css b/app/static/examples/nes.min.css similarity index 100% rename from examples/nes.min.css rename to app/static/examples/nes.min.css diff --git a/examples/obs_remote/index.html b/app/static/examples/obs_remote/index.html similarity index 96% rename from examples/obs_remote/index.html rename to app/static/examples/obs_remote/index.html index 58c3d63..e51ecfb 100644 --- a/examples/obs_remote/index.html +++ b/app/static/examples/obs_remote/index.html @@ -1,401 +1,401 @@ - - - - - OBS Controller Demo using VDO.Ninja - - - -
-

OBS remote (server)

- - - - - -
- - - - -
- - -
- -
- Code available on GitHub: https://github.com/steveseguin/remote_ninja -
- - + + + + + OBS Controller Demo using VDO.Ninja + + + +
+

OBS remote (server)

+ + + + + +
+ + + + +
+ + +
+ +
+ Code available on GitHub: https://github.com/steveseguin/remote_ninja +
+ + \ No newline at end of file diff --git a/examples/obs_remote/interface.html b/app/static/examples/obs_remote/interface.html similarity index 96% rename from examples/obs_remote/interface.html rename to app/static/examples/obs_remote/interface.html index 7d37875..35c3d5e 100644 --- a/examples/obs_remote/interface.html +++ b/app/static/examples/obs_remote/interface.html @@ -1,474 +1,474 @@ - - - - - - - OBS Controller Demo using VDO.NInja - - -
-

OBS remote (client)

-
-
-

Scenes

-
-
-
- -
-

Output

-
- -
-
-
-

Active Sources

-
-
-
-
-

All Sources

-
-
-
- - -
-

Custom Commands

- -
- A list of possible commands available here:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
- - + + + + + + + OBS Controller Demo using VDO.NInja + + +
+

OBS remote (client)

+
+
+

Scenes

+
+
+
+ +
+

Output

+
+ +
+
+
+

Active Sources

+
+
+
+
+

All Sources

+
+
+
+ + +
+

Custom Commands

+ +
+ A list of possible commands available here:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + \ No newline at end of file diff --git a/examples/obs_remote/thirdparty/obs-websocket.min.js b/app/static/examples/obs_remote/thirdparty/obs-websocket.min.js similarity index 100% rename from examples/obs_remote/thirdparty/obs-websocket.min.js rename to app/static/examples/obs_remote/thirdparty/obs-websocket.min.js diff --git a/examples/overlay.html b/app/static/examples/overlay.html similarity index 96% rename from examples/overlay.html rename to app/static/examples/overlay.html index d402e82..f6cf6a0 100644 --- a/examples/overlay.html +++ b/app/static/examples/overlay.html @@ -1,146 +1,146 @@ - -overlay + Video - - - - - - - -
-

Apply an Overlay to VDO.Ninja

- - - (Leave blank and press start to see a default sample result) -
- - + +overlay + Video + + + + + + + +
+

Apply an Overlay to VDO.Ninja

+ + + (Leave blank and press start to see a default sample result) +
+ + \ No newline at end of file diff --git a/examples/p2p.html b/app/static/examples/p2p.html similarity index 97% rename from examples/p2p.html rename to app/static/examples/p2p.html index 36b6989..cf4e1bb 100644 --- a/examples/p2p.html +++ b/app/static/examples/p2p.html @@ -1,69 +1,69 @@ - - -
- starting... -
- - + + +
+ starting... +
+ + \ No newline at end of file diff --git a/examples/powerpoint.html b/app/static/examples/powerpoint.html similarity index 96% rename from examples/powerpoint.html rename to app/static/examples/powerpoint.html index d208493..f7745d9 100644 --- a/examples/powerpoint.html +++ b/app/static/examples/powerpoint.html @@ -1,136 +1,136 @@ - -PowerPoint Remote Controller - - - - - - - - - - -
-

PowerPoint Remote Control interface

-
-
- This app is a custom remote client for VDO.Ninja's PowerPoint remote control feature. -

- For this to work, the remote VDO.Ninja peer will need &midiin added to their URL, a virtual MIDI loopback device installed, PowerPoint running as an application, and the AutoHotKey script found here running, with the MIDI loopback device selected as a MIDI Input device. -
- - + +PowerPoint Remote Controller + + + + + + + + + + +
+

PowerPoint Remote Control interface

+
+
+ This app is a custom remote client for VDO.Ninja's PowerPoint remote control feature. +

+ For this to work, the remote VDO.Ninja peer will need &midiin added to their URL, a virtual MIDI loopback device installed, PowerPoint running as an application, and the AutoHotKey script found here running, with the MIDI loopback device selected as a MIDI Input device. +
+ + \ No newline at end of file diff --git a/examples/readme.md b/app/static/examples/readme.md similarity index 98% rename from examples/readme.md rename to app/static/examples/readme.md index e46702f..e890f20 100644 --- a/examples/readme.md +++ b/app/static/examples/readme.md @@ -1,23 +1,23 @@ -p2p is a sample of how to use vdo.ninja as a data transport tunneling service - -twitch is an example of how to have a twitch live chat side-by-side with VDO.NInja on the same screen - -dual is an example of how to have two VDO.Ninja windows (or any windows really) open on the same page; Picture-in-Picture style - -sensors is an example of how to transmit sensor and video data from a phone to a computer, drawing it to canvas: youtube video for this exists - -midi demonstrates the MIDI API for VDO.Ninja - -draggable demonstrates how to drag multiple windows around, if you wanted to create a custom layout of elements. (experimental) - -chat.html is an example of a chat-only interface for VDO.NInja; maybe dockable into OBS even - -iframe.outbound-stats.html demostrates how to get stats from VDO.Ninja using the IFRAME API - -changepass lets you create passwords and related HASH values for VDO.NInja rooms - -webhid demonstrates how to interface with a USB device, like a streamdeck (mouse/keyboard not supported) - -zoom.html is a tool for letting you publish into VDO.Ninja, but then full-screen the window once setup, allowing for window-capturing into zoom. - -obs_remote is also hosted on github elsewhere, but it's an example of how to remotely control OBS using VDO.Ninja's tunneling abilities +p2p is a sample of how to use vdo.ninja as a data transport tunneling service + +twitch is an example of how to have a twitch live chat side-by-side with VDO.NInja on the same screen + +dual is an example of how to have two VDO.Ninja windows (or any windows really) open on the same page; Picture-in-Picture style + +sensors is an example of how to transmit sensor and video data from a phone to a computer, drawing it to canvas: youtube video for this exists + +midi demonstrates the MIDI API for VDO.Ninja + +draggable demonstrates how to drag multiple windows around, if you wanted to create a custom layout of elements. (experimental) + +chat.html is an example of a chat-only interface for VDO.NInja; maybe dockable into OBS even + +iframe.outbound-stats.html demostrates how to get stats from VDO.Ninja using the IFRAME API + +changepass lets you create passwords and related HASH values for VDO.NInja rooms + +webhid demonstrates how to interface with a USB device, like a streamdeck (mouse/keyboard not supported) + +zoom.html is a tool for letting you publish into VDO.Ninja, but then full-screen the window once setup, allowing for window-capturing into zoom. + +obs_remote is also hosted on github elsewhere, but it's an example of how to remotely control OBS using VDO.Ninja's tunneling abilities diff --git a/examples/remoteapi.html b/app/static/examples/remoteapi.html similarity index 96% rename from examples/remoteapi.html rename to app/static/examples/remoteapi.html index 7af07a0..8a5df33 100644 --- a/examples/remoteapi.html +++ b/app/static/examples/remoteapi.html @@ -1,477 +1,477 @@ - - - - - - - - - -
-
- - + + + + + + + + + +
+
+ + \ No newline at end of file diff --git a/examples/rotated.html b/app/static/examples/rotated.html similarity index 95% rename from examples/rotated.html rename to app/static/examples/rotated.html index 990ab7e..dddb72e 100644 --- a/examples/rotated.html +++ b/app/static/examples/rotated.html @@ -1,135 +1,135 @@ - -Rotated Scene - - - - - - - - + +Rotated Scene + + + + + + + + \ No newline at end of file diff --git a/examples/sensoroverlay.html b/app/static/examples/sensoroverlay.html similarity index 95% rename from examples/sensoroverlay.html rename to app/static/examples/sensoroverlay.html index 9531806..7fbcab3 100644 --- a/examples/sensoroverlay.html +++ b/app/static/examples/sensoroverlay.html @@ -1,266 +1,266 @@ - -Video with sensor overlayed data - - - -
- -
- - + +Video with sensor overlayed data + + + +
+ +
+ + \ No newline at end of file diff --git a/examples/sensors.html b/app/static/examples/sensors.html similarity index 96% rename from examples/sensors.html rename to app/static/examples/sensors.html index 9265939..31fe8aa 100644 --- a/examples/sensors.html +++ b/app/static/examples/sensors.html @@ -1,280 +1,280 @@ - -Sensor and video stream access example - - - - - - - - - - - - -

-Add &sensor to the push link to send data; see: https://docs.vdo.ninja/source-settings/sensor -
- -
- - - + +Sensor and video stream access example + + + + + + + + + + + + +

+Add &sensor to the push link to send data; see: https://docs.vdo.ninja/source-settings/sensor +
+ +
+ + + \ No newline at end of file diff --git a/examples/socal.html b/app/static/examples/socal.html similarity index 95% rename from examples/socal.html rename to app/static/examples/socal.html index 559a252..3d1e00b 100644 --- a/examples/socal.html +++ b/app/static/examples/socal.html @@ -1,185 +1,185 @@ - -SocialStream + Video - - - - - -
-
-
-

Which social integration are you adding?

- - -
-
-

Use VDO.Ninja and SocialStream chat at the same time

- - - -
- - + +SocialStream + Video + + + + + +
+
+
+

Which social integration are you adding?

+ + +
+
+

Use VDO.Ninja and SocialStream chat at the same time

+ + + +
+ + \ No newline at end of file diff --git a/examples/status.html b/app/static/examples/status.html similarity index 95% rename from examples/status.html rename to app/static/examples/status.html index 34af89f..060341d 100644 --- a/examples/status.html +++ b/app/static/examples/status.html @@ -1,172 +1,172 @@ - - - - - VDON Chat Overlay - - - - - - - - - + + + + + VDON Chat Overlay + + + + + + + + + diff --git a/examples/teleprompt.html b/app/static/examples/teleprompt.html similarity index 94% rename from examples/teleprompt.html rename to app/static/examples/teleprompt.html index ab7ad45..8fc0cf5 100644 --- a/examples/teleprompt.html +++ b/app/static/examples/teleprompt.html @@ -1,289 +1,289 @@ - - - - - - - -
- - -
- - + + + + + + + +
+ + +
+ + \ No newline at end of file diff --git a/examples/teleprompter.html b/app/static/examples/teleprompter.html similarity index 96% rename from examples/teleprompter.html rename to app/static/examples/teleprompter.html index 600761a..bbe7bb6 100644 --- a/examples/teleprompter.html +++ b/app/static/examples/teleprompter.html @@ -1,676 +1,676 @@ - - - - - Teleprompter - VDO.Ninja - - - -
-
-
- - - - - - - - - -
-
- -
-
- -
-
- -
- - + + + + + Teleprompter - VDO.Ninja + + + +
+
+
+ + + + + + + + + +
+
+ +
+
+ +
+
+ +
+ + \ No newline at end of file diff --git a/examples/test_overlay.html b/app/static/examples/test_overlay.html similarity index 93% rename from examples/test_overlay.html rename to app/static/examples/test_overlay.html index 30a764e..7db7d82 100644 --- a/examples/test_overlay.html +++ b/app/static/examples/test_overlay.html @@ -1,22 +1,22 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/examples/twitch.html b/app/static/examples/twitch.html similarity index 96% rename from examples/twitch.html rename to app/static/examples/twitch.html index aba3f54..2574018 100644 --- a/examples/twitch.html +++ b/app/static/examples/twitch.html @@ -1,358 +1,358 @@ - -Twitch + Video - - - - - -
-
-
- - - - - -
-
-

Use VDO.Ninja and Twitch chat at the same time

- VDO.Ninja Stream ID or URL: - - Twitch Username or URL: - - - -


-

- This app lets you publish video/audio via VDO.Ninja at the same time as viewing your Twitch chat.

If you have feature requests or suggestions, please report them at https://discord.vdo.ninja in the #feature-request channel. -

-
- - + +Twitch + Video + + + + + +
+
+
+ + + + + +
+
+

Use VDO.Ninja and Twitch chat at the same time

+ VDO.Ninja Stream ID or URL: + + Twitch Username or URL: + + + +


+

+ This app lets you publish video/audio via VDO.Ninja at the same time as viewing your Twitch chat.

If you have feature requests or suggestions, please report them at https://discord.vdo.ninja in the #feature-request channel. +

+
+ + \ No newline at end of file diff --git a/examples/waitingroom.html b/app/static/examples/waitingroom.html similarity index 95% rename from examples/waitingroom.html rename to app/static/examples/waitingroom.html index e3fa620..322c7bb 100644 --- a/examples/waitingroom.html +++ b/app/static/examples/waitingroom.html @@ -1,121 +1,121 @@ - -Video with sensor overlayed data - - - - -
- - + +Video with sensor overlayed data + + + + +
+ + \ No newline at end of file diff --git a/examples/webhid.html b/app/static/examples/webhid.html similarity index 96% rename from examples/webhid.html rename to app/static/examples/webhid.html index 90a6184..99191c5 100644 --- a/examples/webhid.html +++ b/app/static/examples/webhid.html @@ -1,133 +1,133 @@ - - - - - WebHID Demo - - - -

STREAMDECK DEMO

-
- - -
-
- - - - - + + + + + WebHID Demo + + + +

STREAMDECK DEMO

+
+ + +
+
+ + + + + diff --git a/examples/youtube.html b/app/static/examples/youtube.html similarity index 96% rename from examples/youtube.html rename to app/static/examples/youtube.html index f9f4a1f..8bfa432 100644 --- a/examples/youtube.html +++ b/app/static/examples/youtube.html @@ -1,129 +1,129 @@ - -YouTube Chat + VDON - - - - - - - - -
-
-
- - - - -
- - + +YouTube Chat + VDON + + + + + + + + +
+
+
+ + + + +
+ + \ No newline at end of file diff --git a/examples/youtube.svg b/app/static/examples/youtube.svg similarity index 100% rename from examples/youtube.svg rename to app/static/examples/youtube.svg diff --git a/examples/zoom.html b/app/static/examples/zoom.html similarity index 96% rename from examples/zoom.html rename to app/static/examples/zoom.html index 8c8969a..778551b 100644 --- a/examples/zoom.html +++ b/app/static/examples/zoom.html @@ -1,182 +1,182 @@ - - - - - - -
-
- -
-
- -
-
- - - - - - - - - - - - - - + + + + + + +
+
+ +
+
+ +
+
+ + + + + + + + + + + + + + diff --git a/fileshare.html b/app/static/fileshare.html similarity index 95% rename from fileshare.html rename to app/static/fileshare.html index d8fdeaf..354da4c 100644 --- a/fileshare.html +++ b/app/static/fileshare.html @@ -1,167 +1,167 @@ - - - - WebRTC File Sharing - - - -

WebRTC File Sharing

- -
- - - -
- - - - - + + + + WebRTC File Sharing + + + +

WebRTC File Sharing

+ +
+ + + +
+ + + + + \ No newline at end of file diff --git a/filters/anon.js b/app/static/filters/anon.js similarity index 100% rename from filters/anon.js rename to app/static/filters/anon.js diff --git a/filters/anon/addVideoRecordingEffect.js b/app/static/filters/anon/addVideoRecordingEffect.js similarity index 100% rename from filters/anon/addVideoRecordingEffect.js rename to app/static/filters/anon/addVideoRecordingEffect.js diff --git a/filters/anon/anonymous.json b/app/static/filters/anon/anonymous.json similarity index 100% rename from filters/anon/anonymous.json rename to app/static/filters/anon/anonymous.json diff --git a/filters/anon/anonymous.png b/app/static/filters/anon/anonymous.png similarity index 100% rename from filters/anon/anonymous.png rename to app/static/filters/anon/anonymous.png diff --git a/filters/anon/anonymous_mask.blend b/app/static/filters/anon/anonymous_mask.blend similarity index 100% rename from filters/anon/anonymous_mask.blend rename to app/static/filters/anon/anonymous_mask.blend diff --git a/filters/dog.js b/app/static/filters/dog.js similarity index 100% rename from filters/dog.js rename to app/static/filters/dog.js diff --git a/filters/dog/ThreeFlexMaterial.js b/app/static/filters/dog/ThreeFlexMaterial.js similarity index 100% rename from filters/dog/ThreeFlexMaterial.js rename to app/static/filters/dog/ThreeFlexMaterial.js diff --git a/filters/dog/images/texture_pink.jpg b/app/static/filters/dog/images/texture_pink.jpg similarity index 100% rename from filters/dog/images/texture_pink.jpg rename to app/static/filters/dog/images/texture_pink.jpg diff --git a/filters/dog/images/texture_white.jpg b/app/static/filters/dog/images/texture_white.jpg similarity index 100% rename from filters/dog/images/texture_white.jpg rename to app/static/filters/dog/images/texture_white.jpg diff --git a/filters/dog/index.html b/app/static/filters/dog/index.html similarity index 98% rename from filters/dog/index.html rename to app/static/filters/dog/index.html index fb10fac..5edecf1 100644 --- a/filters/dog/index.html +++ b/app/static/filters/dog/index.html @@ -31,7 +31,7 @@ - + diff --git a/filters/dog/libs/glfx.js b/app/static/filters/dog/libs/glfx.js similarity index 100% rename from filters/dog/libs/glfx.js rename to app/static/filters/dog/libs/glfx.js diff --git a/filters/dog/main.js b/app/static/filters/dog/main.js similarity index 100% rename from filters/dog/main.js rename to app/static/filters/dog/main.js diff --git a/filters/dog/models/dog/alpha_ears.jpg b/app/static/filters/dog/models/dog/alpha_ears.jpg similarity index 100% rename from filters/dog/models/dog/alpha_ears.jpg rename to app/static/filters/dog/models/dog/alpha_ears.jpg diff --git a/filters/dog/models/dog/alpha_ears_1024.jpg b/app/static/filters/dog/models/dog/alpha_ears_1024.jpg similarity index 100% rename from filters/dog/models/dog/alpha_ears_1024.jpg rename to app/static/filters/dog/models/dog/alpha_ears_1024.jpg diff --git a/filters/dog/models/dog/alpha_ears_256.jpg b/app/static/filters/dog/models/dog/alpha_ears_256.jpg similarity index 100% rename from filters/dog/models/dog/alpha_ears_256.jpg rename to app/static/filters/dog/models/dog/alpha_ears_256.jpg diff --git a/filters/dog/models/dog/displace_nez.jpg b/app/static/filters/dog/models/dog/displace_nez.jpg similarity index 100% rename from filters/dog/models/dog/displace_nez.jpg rename to app/static/filters/dog/models/dog/displace_nez.jpg diff --git a/filters/dog/models/dog/displace_oreilles.jpg b/app/static/filters/dog/models/dog/displace_oreilles.jpg similarity index 100% rename from filters/dog/models/dog/displace_oreilles.jpg rename to app/static/filters/dog/models/dog/displace_oreilles.jpg diff --git a/filters/dog/models/dog/dog_ears.json b/app/static/filters/dog/models/dog/dog_ears.json similarity index 100% rename from filters/dog/models/dog/dog_ears.json rename to app/static/filters/dog/models/dog/dog_ears.json diff --git a/filters/dog/models/dog/dog_nose.json b/app/static/filters/dog/models/dog/dog_nose.json similarity index 100% rename from filters/dog/models/dog/dog_nose.json rename to app/static/filters/dog/models/dog/dog_nose.json diff --git a/filters/dog/models/dog/dog_tongue.jpg b/app/static/filters/dog/models/dog/dog_tongue.jpg similarity index 100% rename from filters/dog/models/dog/dog_tongue.jpg rename to app/static/filters/dog/models/dog/dog_tongue.jpg diff --git a/filters/dog/models/dog/dog_tongue.json b/app/static/filters/dog/models/dog/dog_tongue.json similarity index 100% rename from filters/dog/models/dog/dog_tongue.json rename to app/static/filters/dog/models/dog/dog_tongue.json diff --git a/filters/dog/models/dog/flex_ears.jpg b/app/static/filters/dog/models/dog/flex_ears.jpg similarity index 100% rename from filters/dog/models/dog/flex_ears.jpg rename to app/static/filters/dog/models/dog/flex_ears.jpg diff --git a/filters/dog/models/dog/flex_ears_1024.jpg b/app/static/filters/dog/models/dog/flex_ears_1024.jpg similarity index 100% rename from filters/dog/models/dog/flex_ears_1024.jpg rename to app/static/filters/dog/models/dog/flex_ears_1024.jpg diff --git a/filters/dog/models/dog/flex_ears_256.jpg b/app/static/filters/dog/models/dog/flex_ears_256.jpg similarity index 100% rename from filters/dog/models/dog/flex_ears_256.jpg rename to app/static/filters/dog/models/dog/flex_ears_256.jpg diff --git a/filters/dog/models/dog/flex_nose.png b/app/static/filters/dog/models/dog/flex_nose.png similarity index 100% rename from filters/dog/models/dog/flex_nose.png rename to app/static/filters/dog/models/dog/flex_nose.png diff --git a/filters/dog/models/dog/flex_tongue.png b/app/static/filters/dog/models/dog/flex_tongue.png similarity index 100% rename from filters/dog/models/dog/flex_tongue.png rename to app/static/filters/dog/models/dog/flex_tongue.png diff --git a/filters/dog/models/dog/flex_tongue_1024.png b/app/static/filters/dog/models/dog/flex_tongue_1024.png similarity index 100% rename from filters/dog/models/dog/flex_tongue_1024.png rename to app/static/filters/dog/models/dog/flex_tongue_1024.png diff --git a/filters/dog/models/dog/flex_tongue_256.png b/app/static/filters/dog/models/dog/flex_tongue_256.png similarity index 100% rename from filters/dog/models/dog/flex_tongue_256.png rename to app/static/filters/dog/models/dog/flex_tongue_256.png diff --git a/filters/dog/models/dog/normal_ears.jpg b/app/static/filters/dog/models/dog/normal_ears.jpg similarity index 100% rename from filters/dog/models/dog/normal_ears.jpg rename to app/static/filters/dog/models/dog/normal_ears.jpg diff --git a/filters/dog/models/dog/normal_nose.jpg b/app/static/filters/dog/models/dog/normal_nose.jpg similarity index 100% rename from filters/dog/models/dog/normal_nose.jpg rename to app/static/filters/dog/models/dog/normal_nose.jpg diff --git a/filters/dog/models/dog/texture_ears.jpg b/app/static/filters/dog/models/dog/texture_ears.jpg similarity index 100% rename from filters/dog/models/dog/texture_ears.jpg rename to app/static/filters/dog/models/dog/texture_ears.jpg diff --git a/filters/dog/models/dog/texture_nose.jpg b/app/static/filters/dog/models/dog/texture_nose.jpg similarity index 100% rename from filters/dog/models/dog/texture_nose.jpg rename to app/static/filters/dog/models/dog/texture_nose.jpg diff --git a/filters/dog/models/dog/tongue_alpha.jpg b/app/static/filters/dog/models/dog/tongue_alpha.jpg similarity index 100% rename from filters/dog/models/dog/tongue_alpha.jpg rename to app/static/filters/dog/models/dog/tongue_alpha.jpg diff --git a/filters/dog/models/dog/tongue_alpha_1024.jpg b/app/static/filters/dog/models/dog/tongue_alpha_1024.jpg similarity index 100% rename from filters/dog/models/dog/tongue_alpha_1024.jpg rename to app/static/filters/dog/models/dog/tongue_alpha_1024.jpg diff --git a/filters/dog/models/dog/tongue_alpha_256.jpg b/app/static/filters/dog/models/dog/tongue_alpha_256.jpg similarity index 100% rename from filters/dog/models/dog/tongue_alpha_256.jpg rename to app/static/filters/dog/models/dog/tongue_alpha_256.jpg diff --git a/filters/readme.md b/app/static/filters/readme.md similarity index 98% rename from filters/readme.md rename to app/static/filters/readme.md index 644d8c0..630155a 100644 --- a/filters/readme.md +++ b/app/static/filters/readme.md @@ -1,3 +1,3 @@ -Some of the code in this folder is based on the sample code made available from Jeeliz; -Apache-2.0 License +Some of the code in this folder is based on the sample code made available from Jeeliz; +Apache-2.0 License https://github.com/jeeliz/jeelizFaceFilter/blob/master/LICENSE \ No newline at end of file diff --git a/filters/sample.js b/app/static/filters/sample.js similarity index 97% rename from filters/sample.js rename to app/static/filters/sample.js index 1e19ebd..fb73f6a 100644 --- a/filters/sample.js +++ b/app/static/filters/sample.js @@ -1,61 +1,61 @@ -async function effectsEngine(){ - console.log('LOADED SAMPLE'); - var loadList = []; - loadList.push("./thirdparty/jeeliz/jeelizFaceFilter.js"); - loadList.push("./thirdparty/jeeliz/helpers/JeelizCanvas2DHelper.js"); - - if (loadList.length){ - loadList.reverse(); - while (loadList.length){ - await loadScript(loadList.pop()); - } - } - - function main(){ // entry point - console.log("LOADED MAIN OF SAMPLE"); - if (session.canvasSource && session.canvasSource.videoWidth && session.canvasSource.videoHeight && session.canvasWebGL){ - if (session.canvasWebGL && !(document.getElementById("effectsCanvasTarget"))){ - session.canvasWebGL.id = "effectsCanvasTarget"; - session.canvasWebGL.style.position="fixed"; - session.canvasWebGL.style.top= "-9999px"; - session.canvasWebGL.style.left= "-9999px"; - document.body.appendChild(session.canvasWebGL); - } - start(JEELIZFACEFILTER, "effectsCanvasTarget", session.canvasSource, 'yellow'); - } else { - setTimeout(main, 500); - } - } - - function start(jeeFaceFilterAPIInstance, canvasId, videoElement, borderColor){ - let cvd = null; // return of Canvas2DDisplay - - jeeFaceFilterAPIInstance.init({ - canvasId: canvasId, - videoSettings: { - videoElement: videoElement - }, - NNCPath: './thirdparty/jeeliz/neuralNets/', // root of NN_DEFAULT.json file - callbackReady: function(errCode, spec){ - if (errCode){ - console.log('AN ERROR HAPPENS. SORRY BRO :( . ERR =', errCode); - return; - } - console.log('INFO: JEELIZFACEFILTER IS READY'); - cvd = JeelizCanvas2DHelper(spec); - cvd.ctx.strokeStyle = borderColor; - }, - callbackTrack: function(detectState){ // drawing loop - if (detectState.detected>0.6){ - // draw a border around the face: - const faceCoo = cvd.getCoordinates(detectState); - cvd.ctx.clearRect(0,0,cvd.canvas.width, cvd.canvas.height); - cvd.ctx.strokeRect(faceCoo.x, faceCoo.y, faceCoo.w, faceCoo.h); - cvd.update_canvasTexture(); - } - cvd.draw(); - } - }); - } - main(); -}; +async function effectsEngine(){ + console.log('LOADED SAMPLE'); + var loadList = []; + loadList.push("./thirdparty/jeeliz/jeelizFaceFilter.js"); + loadList.push("./thirdparty/jeeliz/helpers/JeelizCanvas2DHelper.js"); + + if (loadList.length){ + loadList.reverse(); + while (loadList.length){ + await loadScript(loadList.pop()); + } + } + + function main(){ // entry point + console.log("LOADED MAIN OF SAMPLE"); + if (session.canvasSource && session.canvasSource.videoWidth && session.canvasSource.videoHeight && session.canvasWebGL){ + if (session.canvasWebGL && !(document.getElementById("effectsCanvasTarget"))){ + session.canvasWebGL.id = "effectsCanvasTarget"; + session.canvasWebGL.style.position="fixed"; + session.canvasWebGL.style.top= "-9999px"; + session.canvasWebGL.style.left= "-9999px"; + document.body.appendChild(session.canvasWebGL); + } + start(JEELIZFACEFILTER, "effectsCanvasTarget", session.canvasSource, 'yellow'); + } else { + setTimeout(main, 500); + } + } + + function start(jeeFaceFilterAPIInstance, canvasId, videoElement, borderColor){ + let cvd = null; // return of Canvas2DDisplay + + jeeFaceFilterAPIInstance.init({ + canvasId: canvasId, + videoSettings: { + videoElement: videoElement + }, + NNCPath: './thirdparty/jeeliz/neuralNets/', // root of NN_DEFAULT.json file + callbackReady: function(errCode, spec){ + if (errCode){ + console.log('AN ERROR HAPPENS. SORRY BRO :( . ERR =', errCode); + return; + } + console.log('INFO: JEELIZFACEFILTER IS READY'); + cvd = JeelizCanvas2DHelper(spec); + cvd.ctx.strokeStyle = borderColor; + }, + callbackTrack: function(detectState){ // drawing loop + if (detectState.detected>0.6){ + // draw a border around the face: + const faceCoo = cvd.getCoordinates(detectState); + cvd.ctx.clearRect(0,0,cvd.canvas.width, cvd.canvas.height); + cvd.ctx.strokeRect(faceCoo.x, faceCoo.y, faceCoo.w, faceCoo.h); + cvd.update_canvasTexture(); + } + cvd.draw(); + } + }); + } + main(); +}; diff --git a/iframe-examples.js b/app/static/iframe-examples.js similarity index 100% rename from iframe-examples.js rename to app/static/iframe-examples.js diff --git a/iframe.css b/app/static/iframe.css similarity index 100% rename from iframe.css rename to app/static/iframe.css diff --git a/iframe.html b/app/static/iframe.html similarity index 99% rename from iframe.html rename to app/static/iframe.html index afa7545..53c2dc0 100644 --- a/iframe.html +++ b/app/static/iframe.html @@ -3,7 +3,7 @@ IFRAME Example - + + + @@ -83,21 +83,21 @@ - + - + - + - - - + + +
- + "; - } - } - if (session.hostedTransfers){ - getById("activeShares").innerHTML += "
"+session.hostedTransfers.length + " file transfers in progress.
"; - } -} - -function toggleChat(event = null) { // TODO: I need to have this be MUTE, toggle, with volume not touched. - if (session.chat == false) { - setTimeout(function() { - document.addEventListener("click", toggleChat); - }, 10); - - getById("chatModule").addEventListener("click", function(e) { - e.stopPropagation(); - return false; - }); - session.chat = true; - getById("chattoggle").className = "las la-comment-dots toggleSize"; - getById("chatModule").classList.remove("hidden"); - getById("chatInput").focus(); // give it keyboard focus - } else { - - session.chat = false; - getById("chattoggle").className = "las la-comment-alt toggleSize"; - getById("chatModule").classList.add("hidden"); - - document.removeEventListener("click", toggleChat); - getById("chatModule").removeEventListener("click", function(e) { - e.stopPropagation(); - return false; - }); - } - if (getById("chatNotification").value) { - getById("chatNotification").value = 0; - } - getById("chatNotification").classList.remove("notification", "red"); -} - -function directorAdvanced(ele) { - var target = document.createElement("div"); - target.style = "position:absolute;float:left;width:270px;height:222px;background-color:#7E7E7E;"; - - var closeButton = document.createElement("button"); - closeButton.innerHTML = " close"; - closeButton.style.left = "5px"; - closeButton.style.position = "relative"; - closeButton.onclick = function() { - target.parentNode.removeChild(target); - }; - target.appendChild(closeButton); - - var someButton = document.createElement("button"); - someButton.innerHTML = " some action "; - someButton.style.left = "5px"; - someButton.style.position = "relative"; - someButton.onclick = function() { - var actionMsg = {}; - session.sendRequest(actionMsg, ele.dataset.UUID); - }; - target.appendChild(someButton); - - ele.parentNode.appendChild(target); -} - -function directorSendMessage(ele) { - - var UUID = ele.dataset.UUID; - var target = document.querySelector("[data--u-u-i-d='"+UUID+"'][data-action-type='messaging-box']"); - if (!target){return;} - - if (target.classList.contains("hidden")){ - target.classList.remove("hidden"); - ele.classList.add("pressed"); ele.ariaPressed = "true"; - } else { - target.classList.add("hidden"); - ele.classList.remove("pressed"); ele.ariaPressed = "false"; - return; - } - - - var inputField = target.querySelector("[data-action-type='messaging-box-text']"); - if (inputField){ - inputField.focus(); - inputField.select(); - } - if ("overlay" in target){ - return; - } - - target.overlay = true; - - if (inputField){ - inputField.addEventListener("keydown", function(e) { - if (e.keyCode == 13) { - e.preventDefault(); - sendButton.click(); - } else if (e.keyCode == 27) { - e.preventDefault(); - inputField.value = ""; - target.parentNode.removeChild(target); - } - }); - } - - var sendButton = target.querySelector("[data-action-type='messaging-box-send']"); - if (sendButton){ - sendButton.onclick = function() { - var chatMsg = {}; - chatMsg.chat = inputField.value; - if (sendButton.parentNode.overlay) { - chatMsg.overlay = sendButton.parentNode.overlay; - } - session.sendRequest(chatMsg, ele.dataset.UUID); - inputField.value = ""; - //target.parentNode.removeChild(target); - }; - } - - var closeButton = target.querySelector("[data-action-type='messaging-box-close']"); - if (closeButton){ - closeButton.onclick = function() { - inputField.value = ""; - target.classList.add("hidden"); - ele.classList.remove("pressed"); ele.ariaPressed = "false"; - }; - } - - var overlayMsg = target.querySelector("[data-action-type='messaging-box-toggle']"); - if (overlayMsg){ - overlayMsg.onclick = function(e) { - log(e.target.parentNode.parentNode); - if (e.target.parentNode.parentNode.overlay === true) { - e.target.parentNode.parentNode.overlay = false; - e.target.parentNode.innerHTML = ""; - } else { - e.target.parentNode.parentNode.overlay = true; - e.target.parentNode.innerHTML = ""; - } - } - } -} - -function toggleAutoVideoMute(){ // for iOS devices, that tab out. - // document.visibilityState - if (!session.videoMuted && (session.permaid!==false)){ - var msg = {}; - msg.videoMuted = (document.visibilityState === 'hidden') || false; - //try { - session.sendMessage(msg); - //} catch(e){errorlog(e);} - pokeIframeAPI('video-mute-state', document.visibilityState); - } -} - -function toggleVideoMute(apply = false) { // TODO: I need to have this be MUTE, toggle, with volume not touched. - if (apply) { - session.videoMuted = !session.videoMuted; - } - - if (!session.remoteVideoMuted){ - getById("head8").classList.add("hidden"); - } - - if (session.videoMuted == false) { - session.videoMuted = true; - getById("mutevideotoggle").className = "las la-video-slash toggleSize"; - if (!(session.cleanOutput)){ - getById("mutevideobutton").classList.add("red"); - getById("mutevideobutton").ariaPressed = "true"; - getById("header").classList.add("red"); - if (session.remoteVideoMuted){ - getById("head8").classList.remove("hidden"); - } - } - if (session.streamSrc) { - session.streamSrc.getVideoTracks().forEach((track) => { - track.enabled = false; - }); - } - } else if (session.remoteVideoMuted){ // the director has muted this guest's video feed - session.videoMuted = false; // just setting it back to the pre-toggled state - getById("mutevideotoggle").className = "las la-video toggleSize"; - if (!(session.cleanOutput)){ - getById("head8").classList.remove("hidden"); - getById("header").classList.add("red"); - getById("mutevideobutton").classList.remove("red"); - getById("mutevideobutton").ariaPressed = "false"; - } - if (session.streamSrc) { - session.streamSrc.getVideoTracks().forEach((track) => { - track.enabled = false; - }); - } - - } else { - session.videoMuted = false; - - getById("mutevideotoggle").className = "las la-video toggleSize"; - if (!(session.cleanOutput)){ - getById("mutevideobutton").classList.remove("red"); - getById("mutevideobutton").ariaPressed = "false"; - getById("header").classList.remove("red"); - - } - if (session.streamSrc) { - session.streamSrc.getVideoTracks().forEach((track) => { - track.enabled = true; - }); - } - } - - if (session.avatar && session.avatar.ready && !apply){ - updateRenderOutpipe(); - if (session.videoMuted){ - var msg = {}; - msg.videoMuted = false; // doesn't matter the actual mute state; this is the avatar - session.sendMessage(msg); - } - } else if (!apply) { - var msg = {}; - msg.videoMuted = session.videoMuted; - session.sendMessage(msg); - } - - pokeIframeAPI("video-mute-state", (session.videoMuted || session.remoteVideoMuted)); - - if (!apply){ - pokeAPI("videoMuted", (session.videoMuted || session.remoteVideoMuted)); - } - -} - -var toggleSettingsState = false; -async function toggleSettings(forceShow = false) { // TODO: I need to have this be MUTE, toggle, with volume not touched. - - if (session.nosettings){return;} - - getById("multiselect-trigger3").dataset.state = "0"; - getById("multiselect-trigger3").classList.add('closed'); - getById("multiselect-trigger3").classList.remove('open'); - getById("chevarrow2").classList.add('bottom'); - - if (toggleSettingsState == true) { - if (forceShow == true) { - await enumerateDevices().then(gotDevices2); - return; - } - } // don't close if already open - if (getById("popupSelector").style.display == "none") { - - updateConstraintSliders(); - - setTimeout(function() { - document.addEventListener("click", toggleSettings); - }, 10); - - getById("popupSelector").addEventListener("click", function(e) { - e.stopPropagation(); - return false; - }); - - if (navigator.userAgent.indexOf('Chrome') != -1) { - try { - await navigator.permissions.query({ - name: "camera" - }).then(async function(promise) { - if (promise && promise.state) { - if (promise.state == "prompt") { - await navigator.mediaDevices.getUserMedia({ - video: true - , audio: false - }).then(async function(stream) { - await enumerateDevices().then(gotDevices2).then(function() { - stream.getTracks().forEach(function(track) { - //stream.removeTrack(track); - track.stop(); // clean up? - }); - }); - - }).catch(async function(err) { - await enumerateDevices().then(gotDevices2).then(function() {}); - }); - } else { - await enumerateDevices().then(gotDevices2).then(function() {}); - } - } else { - await enumerateDevices().then(gotDevices2).then(function() {}); - } - }); - } catch (e) { - await enumerateDevices().then(gotDevices2).then(function() {}); - } - } else { - await enumerateDevices().then(gotDevices2).then(function() {}); - } - - getById("popupSelector").style.display = "inline-block" - getById("settingsbutton").classList.add("brown"); - getById("settingsbutton").ariaPressed = "true"; - - loadTFLITEImages() // only triggers if effects==5 is true - - setTimeout(function() { - getById("popupSelector").style.right = "0px"; - }, 1); - toggleSettingsState = true; - } else { - document.removeEventListener("click", toggleSettings); - getById("popupSelector").removeEventListener("click", function(e) { - e.stopPropagation(); - return false; - }); - - getById("popupSelector").style.right = "-500px"; - - - getById("settingsbutton").classList.remove("brown"); - getById("settingsbutton").ariaPressed = "false"; - setTimeout(function() { - getById("popupSelector").style.display = "none"; - }, 200); - toggleSettingsState = false; - document.getElementById('videoSettings3').style.display = "none"; - } - pokeIframeAPI("settings-menu-state",toggleSettingsState); -} - -function hangup() { // TODO: I need to have this be MUTE, toggle, with volume not touched. - if (session.hostedTransfers.length){ - confirmAlt("There are still file transfer in progress\nAre you sure you wish to exit?").then(res=>{ - if (res){ - try { - //if (document.fullscreenElement && session.mobile){ - // getById("main").innerHTML = document.getElementById("hangupTemplateMobileFullscreen").innerHTML; - //} else { - getById("main").innerHTML = document.getElementById("hangupTemplate").innerHTML; - //} - } catch(e){} - - setTimeout(function() { - session.hangup(); - }, 0); - } - }); - } else { - - try { - //if (document.fullscreenElement && session.mobile){ - // getById("main").innerHTML = document.getElementById("hangupTemplateMobileFullscreen").innerHTML; - //} else { - getById("main").innerHTML = document.getElementById("hangupTemplate").innerHTML; - //} - } catch(e){} - - setTimeout(function() { - session.hangup(); - }, 0); - } -} - -function hangup2() { - session.hangupDirector(); - getById("miniPerformer").innerHTML = ""; - getById("press2talk").dataset.enabled = false; - getById("screensharebutton").classList.add("hidden"); - getById("screenshare2button").classList.add("hidden"); - getById("screenshare3button").classList.add("hidden"); - getById("settingsbutton").classList.add("hidden"); - getById("mutebutton").classList.add("hidden"); - getById("hangupbutton2").classList.add("hidden"); - //getById("chatbutton").classList.remove("hidden"); - getById("controlButtons").classList.remove("hidden"); - //getById("mutespeakerbutton").classList.add("hidden"); - getById("mutevideobutton").classList.add("hidden"); - - getById("screensharebutton").classList.remove("green"); - getById("screensharebutton").ariaPressed = "false"; - - - if (!session.showDirector) { - getById("miniPerformer").innerHTML = ''; - miniTranslate(getById("miniPerformer")); - } else { - getById("miniPerformer").innerHTML = ''; - } - getById("miniPerformer").className = ""; -} - -function hangupComplete() { - try { - //if (document.fullscreenElement && session.mobile){ - // getById("main").innerHTML = document.getElementById("hangupTemplateMobileFullscreen").innerHTML; - //} else { - getById("main").innerHTML = document.getElementById("hangupTemplate").innerHTML; - //} - } catch(e){} - - updateMixerRun = function(){}; - - pokeIframeAPI("hungup",true); // don't use Hangup, as that's an action. - pokeAPI("hangup",true); -} - -function reloadRequested() { - pokeIframeAPI("reloading",true); - window.removeEventListener("beforeunload", confirmUnload); // clear the confirm on reload - location.reload(); // the main reload function call -} -function confirmUnload(event){ - if (!session.noExitPrompt && !session.cleanOutput && (session.scene === false) && (session.seeding || (session.roomid!==false) || (session.permaid!==false) || session.director)){ - (event || window.event).returnValue = "Are you sure you want to exit?"; //Gecko + IE - return "Are you sure you want to exit?"; - } else { - //setTimeout(function(){session.hangup();},0); - return undefined; // ADDED OCT 29th; get rid of popup. Just close the socket connection if the user is refreshing the page. It's one or the other. - } -} - -function gobackSlide(){ - var data = {}; - data.data = [176, 110, 10]; - sendRawMIDI(data); - try { - pokeIframeAPI("back-slide",true); - } catch(e){} -} - -function nextSlide(){ - var data = {}; - data.data = [176, 110, 11]; - sendRawMIDI(data); - - try { - pokeIframeAPI("next-slide",true); - } catch(e){} -} - -function raisehand() { - if (session.directorUUID == false) { // fine - log("no director in room yet"); - return false; - } - - var data = {}; - var handstate = false; - - log(data); - if (getById("raisehandbutton").dataset.raised == "0") { - getById("raisehandbutton").dataset.raised = "1"; - getById("raisehandbutton").classList.add("raisedHand"); - data.chat = "Raised hand"; - handstate = true; - log("hand raised"); - } else { - log("hand lowered"); - getById("raisehandbutton").dataset.raised = "0"; - getById("raisehandbutton").classList.remove("raisedHand"); - data.chat = "Lowered hand"; - handstate = false; - } - for (var i=0;i{ - if (ge.value==1){ - cc+=1; - } - }); - if (!cc){ - getById("container_director").style.backgroundColor = null; - getById("container_director").classList.remove("containerGreen"); - } - } else { - var cc = 0; - getById("container_" + ele.dataset.UUID).querySelectorAll('[data-action-type="addToScene"]').forEach(ge=>{ - if (ge.value==1){ - cc+=1; - log("ge.value: '"+ge.value+"'"); - } else { - log("ge.value:--'"+ge.value+"'"); - } - }); - log(cc + " " +"container_" + ele.dataset.UUID); - if (!cc){ - getById("container_" + ele.dataset.UUID).style.backgroundColor = null; - getById("container_" + ele.dataset.UUID).classList.remove("containerGreen"); - } - } - } else { - ele.value = 1; - ele.classList.add("pressed"); ele.ariaPressed = "true"; - if (ele.children[1]){ - ele.children[1].innerHTML = "Remove"; - } - if (director){ - getById("container_director").classList.add("containerGreen"); - } else { - getById("container_" + ele.dataset.UUID).classList.add("containerGreen"); - } - } - } - - var msg = {}; - - scene = scene+""; - - msg.scene = scene; - msg.action = "display"; - msg.value = ele.value; - msg.target = ele.dataset.sid; - - try { - if (msg.value==1){ - pokeIframeAPI("add-to-scene", scene, ele.dataset.UUID); - } else { - pokeIframeAPI("remove-from-scene", scene, ele.dataset.UUID); - } - } catch(e){} - - //for (var uuid in session.pcs){ // removing this since it's obsolete at this point. - // if (session.pcs[uuid].stats.info && ("version" in session.pcs[uuid].stats.info) && (session.pcs[uuid].stats.info.version < 17.2)){ - //// msg.request = "sendroom"; - // session.sendMsg(msg); - // return; - // } - //} - - for (var uuid in session.pcs){ - if (session.pcs[uuid].scene===scene){ - session.sendMessage(msg, uuid); - } - } - syncDirectorState(ele); - - if (msg.value){ - return true; - } else { - return false; - } -} - -function syncDirectorState(ele){ - //if (session.director){ // assumed director, since this is a directEnable sub-function - var msg = {}; - msg.directorState = getDetailedState(ele.dataset.sid); - - for (var uuid in session.pcs){ - if (session.pcs[uuid].coDirector){ - session.sendMessage(msg, uuid); - } - } - for (var i in session.directorList){ - var uuid = session.directorList[i]; - if (session.rpcs[uuid]){ - session.sendRequest(msg, uuid); - } - } -} - -function getDetailedState(sid=false){ - var streamList = {}; - var guestFeeds = document.getElementById("guestFeeds"); - - for (var UUID in session.rpcs){ - if (session.rpcs[UUID].streamID){ - if (sid && (sid!==session.rpcs[UUID].streamID)){continue;} - let item = {}; - item.streamID = session.rpcs[UUID].streamID; - item.label = session.rpcs[UUID].label; - item.group = session.rpcs[UUID].group; - - try { - item.layout = session.rpcs[UUID].layout; - if (session.director && session.slotmode){ - item.slot = query("[data--u-u-i-d='"+UUID+"'][data-slot]").dataset.slot || false; - } else if (session.currentSlots){ - item.slot = Object.keys(session.currentSlots).find(key => session.currentSlots[key] === session.rpcs[UUID].streamID) || false; - } - if (item.slot){ - item.slot = parseInt(item.slot); - } - if (session.director){ - let featured = query("[data--u-u-i-d='"+UUID+"'][data-action-type='solo-video']"); - if (featured && parseInt(featured.value)){ - item.featured = true; - } else { - item.featured = false; - } - } else if (session.infocus && session.infocus===UUID){ - item.featured = true; - } else { - item.featured = false; - } - - } catch(e){errorlog(e);} - - item.iframeSrc = session.rpcs[UUID].iframeSrc; - item.localStream = false; - item.muted = session.rpcs[UUID].remoteMuteState; - item.videoMuted = session.rpcs[UUID].videoMuted; - try { - item.activeSpeaker = session.rpcs[UUID].activelySpeaking; - item.defaultSpeaker = session.rpcs[UUID].defaultSpeaker; - } catch(e){ - errorlog(e); - } - - item.videoVisible = session.rpcs[UUID].videoElement && session.rpcs[UUID].videoElement.checkVisibility(); - if (session.rpcs[UUID].videoElement){ - item.videoVolume = session.rpcs[UUID].videoElement.volume; - } - item.iframeVisible = session.rpcs[UUID].iframeVisible && session.rpcs[UUID].iframeVisible.checkVisibility(); - - if (session.directorList.indexOf(UUID)>=0){ - item.director = true; - } else { - item.director = false; - } - try { - if (session.director){ - if (guestFeeds){ - var lock = parseInt(document.getElementById("position_"+UUID).dataset.locked); - if (lock){ - item.position = lock; // probably should make a universal function to do this, for all lock requesting - } else { - var child = document.getElementById('container_'+UUID); - if (child){ - var parent = child.parentNode; - if (parent.id == "guestFeeds"){ - item.position = Array.prototype.indexOf.call(parent.children, child) + 1; - } - } - } - } - var scenes = getById("container_" + UUID).querySelectorAll('[data-action-type="addToScene"][data-scene][data--u-u-i-d="'+UUID+'"]'); - var sceneState = {}; - for (var i=0;i session.currentSlots[key] === session.streamID) || false; - } - } catch(e){errorlog(e);} - - if (session.info && session.info.out){ - streamList[session.streamID].outbound = session.info.out; - } - - - if (session.showDirector && session.director){ - var child = document.getElementById('container_director'); - if (child){ - var parent = child.parentNode; - if (parent.id == "guestFeeds"){ - streamList[session.streamID].position = Array.prototype.indexOf.call(parent.children, child) + 1; - } - } - } - - - if (session.notifyScreenShare){ - streamList[session.streamID].screenSharing = session.screenShareState; - } else { - streamList[session.streamID].screenSharing = false; - } - - if (session.streamSrc){ - streamList[session.streamID].audioTrack = (session.streamSrc.getAudioTracks().length !== 0); - streamList[session.streamID].videoTrack = (session.streamSrc.getVideoTracks().length !== 0); - } else { - streamList[session.streamID].audioTrack = false; - streamList[session.streamID].videoTrack = false; - } - - return streamList; -} - -function getGuestList(){ - var guestFeeds = document.getElementById("guestFeeds"); - if (!guestFeeds){return {};} - var streamList = {}; - for (var i=0; i< guestFeeds.children.length; i++){ - try { - if (session.rpcs[guestFeeds.children[i].dataset.UUID]){ - streamList[(i+1)+""] = {streamID:session.rpcs[guestFeeds.children[i].dataset.UUID].streamID, label: session.rpcs[guestFeeds.children[i].dataset.UUID].label || ""}; - } else if (guestFeeds.children[i].id == "container_director"){ - streamList[(i+1)+""] = {streamID:session.streamID, label: session.label || ""}; - } else if (guestFeeds.children[i].id == "container_screen_director"){ - streamList[(i+1)+""] = {streamID:session.streamID+":s", label: session.screenShareLabel || ""}; - } - } catch(e){errorlog(e);} - } - - return streamList; -} - -function syncOtherState(sid){ - if (!session.syncState){return;} - if (!session.syncState[sid]){return;} - - - /* if (session.rpcs[ele.dataset.UUID].directorMutedState==1){ - pokeIframeAPI("director-mute-state", true, ele.dataset.UUID); - pokeAPI("directorMuted", true, session.rpcs[ele.dataset.UUID].streamID); - } else { - pokeIframeAPI("director-mute-state", false, ele.dataset.UUID); - pokeAPI("directorMuted", false, session.rpcs[ele.dataset.UUID].streamID); - } */ - - var others = session.syncState[sid].others; - - log(others); - - for (var other in others){ - if (other == "toggle-group"){continue;} - var ele = document.querySelector('[data-sid="'+sid+'"][data-action-type="'+other+'"]'); - if (ele){ - if (others[other]){ - if (!("value" in ele)){ - errorlog("NO DEFAULT VALUE IN SPECIFIED ELEMENT; guessing default: "+other); - ele.value = 0; - } - var changed = true; - if (ele.value==others[other]){ - changed=false - } - if (other == "mute-guest"){ - if (changed){ - remoteMute(ele, false, true); - } - } else if (other == "hide-guest"){ - if (changed){ - remoteHideVideo(ele, true, true); - } - } else if (other == "mute-video-guest"){ - if (changed){ - remoteMuteVideo(ele, true, true); - } - } else { - ele.value = others[other]; - - if (ele.nodeName.toLowerCase() == "input"){ - ele.value = parseInt(others[other]); - } else if (parseInt(others[other])){ - ele.classList.add("pressed"); ele.ariaPressed = "true"; - } else { - ele.classList.remove("pressed"); ele.ariaPressed = "false"; - } - } - } - } - } - - var UUID = document.querySelector('[data-sid="'+sid+'"][data--u-u-i-d'); - if (UUID && UUID.dataset.UUID){ - UUID = UUID.dataset.UUID; - if (session.syncState[sid].group && session.rpcs[UUID]){ - session.rpcs[UUID].group = session.syncState[sid].group; - syncGroup(session.rpcs[UUID].group, UUID); - } - } -} - -function htmlToElement(html) { - var template = document.createElement('template'); - html = html.trim(); // Never return a text node of whitespace as the result - template.innerHTML = html; - return template.content.firstChild; -} - -function syncGroup(groups, UUID){ - if (!groups || (typeof groups !== 'object')){ - errorlog("Group isn't an object"); - return; - } - groups.forEach(group=>{ - var ele = getById("container_" + UUID).querySelector('[data-action-type="toggle-group"][data--u-u-i-d="'+UUID+'"][data-group="'+group+'"]'); - if (!ele){ - var newGroup = htmlToElement(''); - - var added = false; - getById("container_" + UUID).querySelectorAll('.customGroup>[data-group]').forEach(ele=>{ - log(ele); - if (!added && ele.dataset.group>group+""){ - ele.parentNode.insertBefore(newGroup, ele); - added = true; - } - }); - if (!added){ - var newGroupCon = getById("container_" + UUID).querySelector(".customGroup"); - if (!newGroupCon){ - newGroupCon = document.createElement("div"); - newGroupCon.classList.add("customGroup"); - getById("container_" + UUID).appendChild(newGroupCon); - } - newGroupCon.appendChild(newGroup); - } - } - }); - - var elements = document.querySelectorAll('[data-action-type="toggle-group"][data--u-u-i-d="'+UUID+'"][data-group]'); - if (elements.length){ - for (var i=0;i0){ - session.roomTimer = false; - } else { - session.roomTimer = Date.now()/1000 - session.roomTimer; - } - ele.innerHTML = ' Remove Timer'; - ele.classList.add("red"); - } else { - ele.value = 2; - ele.classList.remove("pressed"); ele.ariaPressed = "false"; - session.roomTimer = false; - msg.stopClock = true; - stopClock(); - msg.hideClock = true; - hideClock(); - ele.innerHTML = ' Create Timer'; - } - //miniTranslate(ele); - } else if (event.ctrlKey || event.metaKey){ - if (ele.value == 1) { - ele.value = 3; - msg.pauseClock = true; - pauseClock(); - if (!session.roomTimer){ - session.roomTimer = false; - } else if (session.roomTimer0){ - session.roomTimer = false; - } else { - session.roomTimer = Date.now()/1000 - session.roomTimer; - } - ele.innerHTML = ' Remove Timer'; - ele.classList.add("red"); - } - } - if (ele.dataset.UUID){ - session.sendRequest(msg, ele.dataset.UUID); - } else { - session.sendRequest(msg); - } -} - -function updateRemoteTimerButton(UUID, currentTime) { - var elements = document.querySelectorAll('[data-action-type="create-timer"][data--u-u-i-d="' + UUID + '"]'); - if (elements[0]){ - if (elements[0].value != 2) { - var time = parseInt(currentTime) || 0; - elements[0].classList.add("pressed"); elements[0].ariaPressed = "true"; - elements[0].value = 1; - if (time<0) { - time = time * -1; - var minutes = Math.floor(time / 60); - var seconds = time - minutes * 60; - elements[0].classList.add("red"); - elements[0].innerHTML = ' -' + (minutes) + "m : " + zpadTime(seconds) + "s"; - } else { - var minutes = Math.floor(time / 60); - var seconds = time - minutes * 60; - elements[0].classList.remove("red"); - elements[0].innerHTML = ' ' + (minutes) + "m : " + zpadTime(seconds) + "s"; - } - } else { - elements[0].classList.remove("pressed"); elements[0].ariaPressed = "false"; - elements[0].classList.remove("red"); - elements[0].innerHTML = ' Create Timer'; - } - } -} - - -function directMute(ele, event=false) { // A directing room only is controlled by the Director, with the exception of MUTE. - log("mute 2"); - - if (!event || (!((event.ctrlKey) || (event.metaKey)))) { - if (ele.value == 1) { - ele.value = 0; - ele.classList.remove("pressed"); ele.ariaPressed = "false"; - miniTranslate(ele,"mute-scene"); - } else { - ele.value = 1; - ele.classList.add("pressed"); ele.ariaPressed = "true"; - miniTranslate(ele,"unmute"); - } - } - var msg = {}; - msg.scene = true; - msg.action = "mute"; - msg.value = ele.value; - msg.target = ele.dataset.sid; - - log(msg); - log("ele:"); - log(ele); - //for (var uuid in session.pcs){ // obsolete at this point; v22 - // if (session.pcs[uuid].stats.info && ("version" in session.pcs[uuid].stats.info) && (session.pcs[uuid].stats.info.version < 17.2)){ - // msg.request = "sendroom"; - // session.sendMsg(msg); - // return; - // } - //} - - for (var uuid in session.pcs){ - if (session.pcs[uuid].scene!==false){ // send to all scenes (but scene = 0) - session.sendMessage(msg, uuid); - } - } - - syncDirectorState(ele); - - if (msg.value){ - return true; - } else { - return false; - } -} - -function requestFileUpload(ele){ - ele.classList.add("pressed"); ele.ariaPressed = "true"; - ele.disabled = true; - ele.innerHTML = ' Requesting..'; - setTimeout(function(ele){ - try{ - ele.innerHTML = ' Request File'; - ele.classList.remove("pressed"); ele.ariaPressed = "false"; - ele.disabled = false - } catch(e){} - },15000, ele); - var msg = {}; - msg.requestUpload = true; // toggleFileshare - msg.UUID = ele.dataset.UUID; - session.sendRequest(msg, ele.dataset.UUID); -} - -function remoteSpeakerMute(ele, event=false){ - log("speaker mute"); - if (!event || (!((event.ctrlKey) || (event.metaKey)))) { - if (ele.value == 1) { - ele.value = 0; - ele.classList.remove("pressed"); ele.ariaPressed = "false"; - ele.innerHTML = ' Deafen'; - } else { - ele.value = 1; - ele.classList.add("pressed"); ele.ariaPressed = "true"; - ele.innerHTML = ' Undeafen'; - } - miniTranslate(ele); - } - - var msg = {}; - if (ele.value == 0) { - msg.speakerMute = false - } else { - msg.speakerMute = true; - } - msg.UUID = ele.dataset.UUID; - session.sendRequest(msg, ele.dataset.UUID); - syncDirectorState(ele); - - errorlog(msg); - - return msg.speakerMute; -} - -function updateRemoteSpeakerMute(UUID) { - var ele = document.querySelectorAll('[data-action-type="toggle-remote-speaker"][data--u-u-i-d="' + UUID + '"]'); - if (ele[0]) { - ele[0].classList.add("pressed"); ele[0].ariaPressed = "true"; - ele[0].value = 1; - ele[0].innerHTML = ' undeafen'; - miniTranslate(ele[0]); - } - return true; -} - -function updateRemoteDisplayMute(UUID, blind=true) { - var ele = document.querySelectorAll('[data-action-type="toggle-remote-display"][data--u-u-i-d="' + UUID + '"]'); - if (ele[0]) { - if (blind){ - ele[0].classList.add("pressed"); ele[0].ariaPressed = "true"; - ele[0].value = 1; - ele[0].innerHTML = ' unblind'; - miniTranslate(ele[0]); - return true; - } else { - ele[0].classList.remove("pressed"); ele[0].ariaPressed = "false"; - ele[0].value = 0; - ele[0].innerHTML = ' blind'; - miniTranslate(ele[0]); - return false; - } - } - return false; -} - -function blindAllGuests(ele, event=false){ - if (!session.director){ - if (!session.cleanOutput){warnUser("Only a director can mute other guests");} - return; - } // only a director can use this button. - - log("blind all display mute"); - if (!event || (!((event.ctrlKey) || (event.metaKey)))) { - if (ele.value == 1) { - ele.value = 0; - ele.classList.remove("pressed"); ele.ariaPressed = "false"; - ele.classList.remove("red"); - ele.innerHTML = ''; - } else { - ele.value = 1; - ele.classList.add("pressed"); ele.ariaPressed = "true"; - ele.classList.add("red"); - ele.innerHTML = ''; - } - } - - var msg = {}; - if (ele.value == 0) { - msg.displayMute = false; - session.directorBlindAllGuests = false; - } else { - msg.displayMute = true; - session.directorBlindAllGuests= true; - } - for (var UUID in session.rpcs){ // doesn't include scenes, as they don't publiish and this is rpcs - if (session.directorList.indexOf(UUID)>=0){continue;} // don't try to mute other directors - try { - session.sendRequest(msg, UUID); - updateRemoteDisplayMute(UUID, msg.displayMute); - } catch(e){errorlog(e);} - } - syncDirectorState(ele); - return msg.displayMute; -} - -function remoteDisplayMute(ele, event=false) { - log("display mute"); - if (!event || (!((event.ctrlKey) || (event.metaKey)))) { - if (ele.value == 1) { - ele.value = 0; - ele.classList.remove("pressed"); ele.ariaPressed = "false"; - ele.innerHTML = ' Blind'; - } else { - ele.value = 1; - ele.classList.add("pressed"); ele.ariaPressed = "true"; - ele.innerHTML = ' Unblind'; - } - miniTranslate(ele); - } - - var msg = {}; - if (ele.value == 0) { - msg.displayMute = false; - } else { - msg.displayMute = true; - } - msg.UUID = ele.dataset.UUID; - session.sendRequest(msg, ele.dataset.UUID); - syncDirectorState(ele); - return msg.displayMute; -} - -function remoteLowerhands(UUID) { - var msg = {}; - msg.lowerhand = true; - msg.UUID = UUID; - session.sendRequest(msg, UUID); - - try{ - getById("hands_"+UUID).classList.add("hidden"); - session.rpcs[UUID].remoteRaisedHandElement.classList.add("hidden"); - } catch(e){} - return true; -} - - -function remoteMute(ele, event=false, skipSend=false) { - log("mute"); - var val = parseInt(ele.value) || 0; - if (!event || (!((event.ctrlKey) || (event.metaKey)))) { - if (val == 1){ - ele.value = 0; - ele.classList.remove("pressed"); ele.ariaPressed = "false"; - //ele.innerHTML = ' mute'; - miniTranslate(ele,"mute"); - //ele.innerHTML += getTranslation("mute"); - } else { - ele.value = 1; - ele.classList.add("pressed"); ele.ariaPressed = "true"; - //ele.innerHTML = ' unmute'; - miniTranslate(ele,"unmute"); - } - } - - try { - session.rpcs[ele.dataset.UUID].directorMutedState = ele.value; - var volume = session.rpcs[ele.dataset.UUID].directorVolumeState; - } catch (e) { - errorlog(e); - var volume = 100; - } - - if (!skipSend){ - var msg = {}; - if (val == 1) { - msg.volume = volume; - } else { - msg.volume = 0; - } - msg.UUID = ele.dataset.UUID; - session.sendRequest(msg, ele.dataset.UUID); - syncDirectorState(ele); - log(msg); - } - - if (session.rpcs[ele.dataset.UUID].directorMutedState==1){ - pokeIframeAPI("director-mute-state", true, ele.dataset.UUID); - pokeAPI("directorMuted", true, session.rpcs[ele.dataset.UUID].streamID); - } else { - pokeIframeAPI("director-mute-state", false, ele.dataset.UUID); - pokeAPI("directorMuted", false, session.rpcs[ele.dataset.UUID].streamID); - } - - if (val){ - return true; - } else { - return false; - } -} - -function toggleQualityGear3(){ - toggle(document.getElementById('videoSettings3'), inline=false); - if (getById("gear_webcam3").style.display === "inline-block") { - - var videoSelect = document.querySelector("select#videoSource3").options; - var obscam = false; - log(videoSelect[videoSelect.selectedIndex].text); - if (videoSelect[videoSelect.selectedIndex].text.startsWith("OBS-Camera")) { // OBS Virtualcam - obscam = true; - } else if (videoSelect[videoSelect.selectedIndex].text.startsWith("OBS Virtual Camera")) { // OBS Virtualcam - obscam = true; - } - - updateStats(obscam); - } -} - -function remoteHideVideo(ele, event=false, skipSend=false) { - log("video hide"); - - if (!event || ((event.ctrlKey) || (event.metaKey))) { - //ele.children[1].innerHTML = getTranslation("armed"); - miniTranslate(ele.children[1],"armed"); - //ele.style.backgroundColor = "#BF3F3F"; - ele.classList.add("armed"); - Callbacks.push([remoteHideVideo, ele, false]); - log("video queued"); - return; - } else { - if (ele.value == 1) { - ele.value = 0; - ele.classList.remove("pressed"); ele.ariaPressed = "false"; - ele.innerHTML = ' Hide'; - } else { - ele.value = 1; - ele.classList.add("pressed"); ele.ariaPressed = "true"; - ele.innerHTML = ' Unhide'; - } - miniTranslate(ele); - ele.classList.remove("armed");//ele.style.backgroundColor = null; - } - - var msg = {}; - if (ele.value == 0) { - msg.directVideoMuted = false; - } else { - msg.directVideoMuted = true; - } - - if (!skipSend){ - - for (var i in session.pcs){ - msg.target = ele.dataset.UUID; - - if (i === msg.target){ - msg.target = true; - } - try{ - session.pcs[i].sendChannel.send(JSON.stringify(msg)); - } catch(e){} - - } - syncDirectorState(ele); - } - - pokeIframeAPI("director-video-hide-state", msg.directVideoMuted, ele.dataset.UUID); - pokeAPI("directorVideoHide", msg.directVideoMuted, session.rpcs[ele.dataset.UUID].streamID); - - return msg.directVideoMuted; -} - -function remoteMuteVideo(ele, event=false, skipSend=false) { - log("video mute"); - - if (!event || ((event.ctrlKey) || (event.metaKey))) { - //ele.children[1].innerHTML = getTranslation("armed"); - miniTranslate(ele.children[1],"armed"); - ele.classList.add("armed");//ele.style.backgroundColor = "#BF3F3F"; - Callbacks.push([remoteMuteVideo, ele, false]); - log("video queued"); - return; - } else { - if (ele.value == 1) { - ele.value = 0; - ele.classList.remove("pressed"); ele.ariaPressed = "false"; - ele.innerHTML = ' Video off'; - } else { - ele.value = 1; - ele.classList.add("pressed"); ele.ariaPressed = "true"; - ele.innerHTML = ' Video on'; - } - miniTranslate(ele); - ele.classList.remove("armed"); - } - - var msg = {}; - if (ele.value == 0) { - msg.remoteVideoMuted = false; - } else { - msg.remoteVideoMuted = true; - } - - if (!skipSend){ - session.sendRequest(msg, ele.dataset.UUID) - syncDirectorState(ele); - } - - pokeIframeAPI("remote-video-mute-state", msg.remoteVideoMuted, ele.dataset.UUID); - pokeAPI("remoteVideoMuted", msg.remoteVideoMuted, session.rpcs[ele.dataset.UUID].streamID); - - return msg.remoteVideoMuted; -} -function updateDirectorVideoHide(UUID) { - var ele = document.querySelectorAll('[data-action-type="hide-guest"][data--u-u-i-d="' + UUID + '"]'); - if (ele[0]) { - ele[0].value = 1; - ele[0].classList.add("pressed"); ele[0].ariaPressed = "true"; - ele[0].innerHTML = ' Unhide'; - miniTranslate(ele[0]); - } - return true; -} -function updateDirectorVideoMute(UUID) { - var ele = document.querySelectorAll('[data-action-type="mute-video-guest"][data--u-u-i-d="' + UUID + '"]'); - if (ele[0]) { - ele[0].value = 1; - ele[0].classList.add("pressed"); ele[0].ariaPressed = "true"; - ele[0].innerHTML = ' Video on'; - miniTranslate(ele[0]); - } - return true; -} - -function directVolume(ele) { // NOT USED ANYMORE - log("volume"); - var msg = {}; - msg.scene = true; - msg.action = "volume"; - msg.target = ele.dataset.sid; // i want to focus on the STREAM ID, not the UUID... - msg.value = ele.value; - - //for (var uuid in session.pcs){ - // if (session.pcs[uuid].stats.info && ("version" in session.pcs[uuid].stats.info) && (session.pcs[uuid].stats.info.version < 17.2)){ - // msg.request = "sendroom"; - // session.sendMsg(msg); - // return; - // } - //} - - for (var uuid in session.pcs){ - if (session.pcs[uuid].scene!==false){ // send to all scenes (but scene = 0) - session.sendMessage(msg, uuid); - } - } - - syncDirectorState(ele); - return msg.value; -} - -function applyMuteState(UUID){ // this is the mute state of PLAYBACK audio; not the microphone or outbound. - if (!(UUID in session.rpcs)){return "UUID not found";} - var muteOutcome = session.rpcs[UUID].mutedState || session.rpcs[UUID].mutedStateMixer || session.rpcs[UUID].mutedStateScene || session.speakerMuted || session.rpcs[UUID].bandwidthMuted; - - if (!muteOutcome && (session.noaudio !== false)){ - if (session.noaudio===true){ - muteOutcome = true; - } else if (session.noaudio.length){ - if (("streamID" in session.rpcs[UUID]) && session.rpcs[UUID].streamID && !session.noaudio.includes(session.rpcs[UUID].streamID)){ - muteOutcome = true; - } - } else { - muteOutcome = true; - } - } - - if (session.rpcs[UUID].videoElement){ - if (session.rpcs[UUID].videoElement && session.rpcs[UUID].videoElement.usermuted===true){return "usermuted true";} - session.rpcs[UUID].videoElement.muted = muteOutcome; - } - - // session.scene - return muteOutcome; -} - -function checkMuteState(UUID){ // this is the mute state of PLAYBACK audio; not the microphone or outbound. - if (!(UUID in session.rpcs)){return false;} - var muteOutcome = session.rpcs[UUID].mutedState || session.rpcs[UUID].mutedStateMixer || session.rpcs[UUID].mutedStateScene || session.speakerMuted || session.rpcs[UUID].bandwidthMuted; - return muteOutcome; -} - -function remoteVolumeUI(ele){ - ele.nextElementSibling.innerHTML = ele.value + "%"; - if (Date.now() - remoteSliderTimeout > 100){ - remoteSliderTimeout = Date.now(); - remoteVolume(ele); - } - //setVolumeColor(ele); - return ele.value; -} - -/* function setVolumeColor(ele){ - var vol1 = 200-parseInt(ele.value); - if (vol1<0){vol1=0}; - ele.style.backgroundColor = "hsl("+vol1+", 100%, 50%)"; -} */ - -function remoteVolume(ele) { // A directing room only is controlled by the Director, with the exception of MUTE. - log("volume: "+session.rpcs[ele.dataset.UUID].directorMutedState); - var msg = {}; - var muted = session.rpcs[ele.dataset.UUID].directorMutedState; - // - //log(ele); - if (muted == true) { // 1 is a string, not an int, so == and not ===. this happens in a few places :/ - session.rpcs[ele.dataset.UUID].directorVolumeState = ele.value; - } else { - session.rpcs[ele.dataset.UUID].directorVolumeState = ele.value; - msg.volume = ele.value; - msg.UUID = ele.dataset.UUID; - session.sendRequest(msg, ele.dataset.UUID); - } - - pokeIframeAPI("director-volume-state", ele.value, ele.dataset.UUID); - - syncDirectorState(ele); - return ele.value; -} - -function clearDirectorSettings(){ // make sure to wipe the director's room settings if creating a new room. - removeStorage("directorCustomize"); - removeStorage("directorWebsiteShare"); -} - -function saveDirectorSettings(){ - var settings = {}; - - if (getById("customizeLinks").classList.contains("hidden")){ - settings.customizeLinks = true; - } - - var customizeLinks1 = getById("customizeLinks1").querySelectorAll("input"); - settings.customizeLinks1 = {}; - for (var i=0;i { - try { - if (customizeLinks1.querySelector('[data-param="'+key+'"]').checked != settings.customizeLinks1[key]){ - customizeLinks1.querySelector('[data-param="'+key+'"]').checked = settings.customizeLinks1[key]; - customizeLinks1.querySelector('[data-param="'+key+'"]').onchange(); - } - } catch(e){errorlog(e);} - }); - } - - if (settings.customizeLinks3){ - var customizeLinks3 = getById("customizeLinks3"); - Object.keys(settings.customizeLinks3).forEach((key, index) => { - try { - if (customizeLinks3.querySelector('[data-param="'+key+'"]').checked == settings.customizeLinks3[key]){ - customizeLinks3.querySelector('[data-param="'+key+'"]').checked = settings.customizeLinks3[key]; - customizeLinks3.querySelector('[data-param="'+key+'"]').onchange(); - } - } catch(e){errorlog(e);} - }); - } - - if (settings.directorLinks1){ - var directorLinks1 = getById("directorLinks1"); - Object.keys(settings.directorLinks1).forEach((key, index) => { - try { - if (directorLinks1.querySelector('[data-param="'+key+'"]').checked == settings.directorLinks1[key]){ - directorLinks1.querySelector('[data-param="'+key+'"]').checked = settings.directorLinks1[key]; - directorLinks1.querySelector('[data-param="'+key+'"]').onchange(); - } - } catch(e){ - errorlog("key :"+key); - errorlog(e); - } - }); - } - - if (settings.directorLinks2){ - var directorLinks2 = getById("directorLinks2"); - Object.keys(settings.directorLinks2).forEach((key, index) => { - try { - if (directorLinks2.querySelector('[data-param="'+key+'"]').checked == settings.directorLinks2[key]){ - directorLinks2.querySelector('[data-param="'+key+'"]').checked = settings.directorLinks2[key]; - directorLinks2.querySelector('[data-param="'+key+'"]').onchange(); - } - } catch(e){ - errorlog("key :"+key); - errorlog(e); - } - }); - } -} - - - -function sendChat(chatmessage = "hi", UUID=false, overlay=false) { // A directing room only is controlled by the Director, with the exception of MUTE. - log("Chat message"); - var msg = {}; - msg.chat = chatmessage; - msg.overlay = overlay; - session.sendPeers(msg, UUID); - return true; -} - -var activatedStream = false; - -async function publishScreen() { - if (activatedStream == true) { - return; - } - activatedStream = true; - setTimeout(function() { - activatedStream = false; - }, 1000); - - formSubmitting = false; - - var quality = 0; - - if (document.getElementById("webcamquality2")){ - quality = parseInt(document.getElementById("webcamquality2").elements.namedItem("resolution2").value) || 0; - } - - session.quality_ss = quality; - - if (session.quality !== false) { - quality = session.quality; // override the user's setting - } - - if (session.screensharequality !== false){ - quality = session.screensharequality; - } - - var video = {} - - if (quality == -1) { - // unlocked capture resolution - } else if (quality == 0) { - - video.width = { - ideal: 1920 - }; - video.height = { - ideal: 1080 - }; - } else if (quality == 1) { - video.width = { - ideal: 1280 - }; - video.height = { - ideal: 720 - }; - } else if (quality == 2) { - video.width = { - ideal: 640 - }; - video.height = { - ideal: 360 - }; - } else if (quality >= 3) { // lowest - video.width = { - ideal: 320 - }; - video.height = { - ideal: 180 - }; - } else { - video.width = { - min: 640 - }; - video.height = { - min: 360 - }; - } - - if (session.width) { - video.width = { - ideal: session.width - }; - } - if (session.height) { - video.height = { - ideal: session.height - }; - } - - var constraints = { - audio: { - echoCancellation: false, - autoGainControl: false, - noiseSuppression: false - }, - video: video - }; - - if (session.noiseSuppression === true) { - constraints.audio.noiseSuppression = true;; // the defaults for screen publishing should be off. - } - if (session.autoGainControl === true) { - constraints.audio.autoGainControl = true; // the defaults for screen publishing should be off. - } - if (session.echoCancellation === true) { - constraints.audio.echoCancellation = true; // the defaults for screen publishing should be off. - } - - try { - let supportedConstraints = navigator.mediaDevices.getSupportedConstraints(); - if (supportedConstraints.cursor) { - if (session.screensharecursor){ - constraints.video.cursor = ["always", "motion"]; - } else { - constraints.video.cursor = "never"; - } - } - if (session.suppressLocalAudioPlayback && supportedConstraints.suppressLocalAudioPlayback){ - constraints.audio.suppressLocalAudioPlayback = true; - } - // - if (session.preferCurrentTab){ - constraints.preferCurrentTab = true; - } - if (session.selfBrowserSurface){ - constraints.selfBrowserSurface = session.selfBrowserSurface; // exclude or include - } - if (session.surfaceSwitching){ - constraints.surfaceSwitching = session.surfaceSwitching; // exclude or include - } - if (session.systemAudio){ - constraints.systemAudio = session.systemAudio; // exclude or include - } - if (session.displaySurface && supportedConstraints.displaySurface){ - constraints.video.displaySurface = session.displaySurface; // monitor, window, or browser - } - } catch(e){ - warnlog("navigator.mediaDevices.getSupportedConstraints() not supported"); - } - - var overrideFramerate = false; - if ((session.frameRate !== false) && (session.maxframeRate != false)){ - overrideFramerate = session.frameRate; - constraints.video.frameRate = { - ideal: session.maxframeRate, - max: session.maxframeRate - }; - } else if (session.frameRate !== false) { - constraints.video.frameRate = session.frameRate; - } else if (session.maxframeRate != false){ - constraints.video.frameRate = { - ideal: session.maxframeRate, - max: session.maxframeRate - }; - } else { - constraints.video.frameRate = { - ideal: 60 - }; - } - - var audioSelect = getById('audioSourceScreenshare'); - var outputSelect = getById('outputSourceScreenshare'); - - try { - session.sink = outputSelect.options[outputSelect.selectedIndex].value; // will probably fail on Safari. - log("Session Sink: " + session.sink); - saveSettings(); - } catch (e){warnlog(e);} - - return await publishScreen2(constraints, audioSelect, true, overrideFramerate).then((res) => { - if (res == false) { - return; - } // no screen selected - log("streamID is: " + session.streamID); - if (session.transcript) { - setTimeout(function() { - setupClosedCaptions(); - }, 1000); - } - - if (!session.cleanOutput){ - getById("mutebutton").classList.remove("hidden"); - getById("mutespeakerbutton").classList.remove("hidden"); - //getById("mutespeakerbutton").className="float"; - getById("chatbutton").className = "float"; - getById('sharefilebutton').classList.remove("hidden"); // we won't override "display:none", if set, though. - getById("mutevideobutton").className = "float"; - getById("hangupbutton").className = "float"; - if (session.showSettings) { - getById("settingsbutton").className = "float"; - } - if (session.raisehands) { - getById("raisehandbutton").className = "float"; - } - if (session.pptControls){ - getById("pptbackbutton").classList.remove("hidden"); - getById("pptnextbutton").classList.remove("hidden"); - } - if (session.recordLocal !== false) { - getById("recordLocalbutton").classList.remove("hidden"); - } - if (session.screensharebutton) { - getById("screensharebutton").className = "float"; - } - getById("controlButtons").classList.remove("hidden"); - getById("helpbutton").style.display = "inherit"; - getById("reportbutton").style.display = ""; - } else if (session.cleanish && session.recordLocal!==false){ - getById("recordLocalbutton").classList.remove("hidden"); - getById("mutebutton").classList.add("hidden"); - getById("mutespeakerbutton").classList.add("hidden"); - getById("chatbutton").classList.add("hidden"); - getById("mutevideobutton").classList.add("hidden"); - getById("hangupbutton").classList.add("hidden"); - getById("hangupbutton2").classList.add("hidden"); - getById("controlButtons").classList.remove("hidden"); - getById("settingsbutton").classList.add("hidden"); - getById("screenshare2button").classList.add("hidden"); - getById("screensharebutton").classList.add("hidden"); - getById("screenshare3button").classList.add("hidden"); - getById("queuebutton").classList.add("hidden"); - } else { - getById("controlButtons").classList.add("hidden"); - } - - if (session.chatbutton === true) { - getById("chatbutton").classList.remove("hidden"); - getById("controlButtons").classList.remove("hidden"); - } else if (session.chatbutton === false) { - getById("chatbutton").classList.add("hidden"); - } - - if (session.screensharebutton === true) { - getById("controlButtons").classList.remove("hidden"); - getById("screensharebutton").className = "float"; - } - - if (session.hangupbutton === true){ - getById("controlButtons").classList.remove("hidden"); - getById("hangupbutton").className = "float"; - } - - - getById("head1").className = 'hidden'; - getById("head2").className = 'hidden'; - - return res; - }).catch(() => {}); -} - -function getWidth() { - return Math.max( - document.body.scrollWidth, - document.documentElement.scrollWidth, - document.body.offsetWidth, - document.documentElement.offsetWidth, - document.documentElement.clientWidth - ); -} - -function getHeight() { - return Math.max( - document.documentElement.clientHeight - ); -} - -function updateForceRotate(skipLastBit=false){ - var capabilities = {facingMode:"unknown"}; - var FirefoxSucks = false; - - if (session.orientation){ - try { - var track = false; - if (session.streamSrc){ - var tracks = session.streamSrc.getVideoTracks(); - if (tracks.length){ - track = tracks[0]; - } - } - if (!track){ - return; - } - - - const settings = track.getSettings(); - session.currentCameraConstraints = settings; - - - if (screen && screen.orientation && screen.orientation.type){ - if (!screen.orientation.type.includes("portrait")){ - if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ - session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; - } - } - } else if (!window.matchMedia("(orientation: portrait)").matches){ - if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ - session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; - } - } - - session.forceRotate = 0; - - if (track.getCapabilities){ - capabilities = track.getCapabilities(); // firefox sucks? - - if ("width" in settings){ - if ("height" in settings){ - if (settings.width < settings.height){ - if (session.orientation=="landscape"){ - if (capabilities){ - if (capabilities.facingMode == "environment"){ - session.forceRotate=270; - } else { - session.forceRotate=90; - } - } - } else { - session.forceRotate = 0; - } - } else if (settings.width > settings.height){ - if (session.orientation=="portrait"){ - if (capabilities){ - if (capabilities.facingMode == "environment"){ - session.forceRotate=90; - } else { - session.forceRotate=270; - } - } - } else { - session.forceRotate = 0; - } - } else { - session.forceRotate = 0; - } - } else { - return; - } - } - - } else if (Firefox && session.mobile){ // firefox sucks... - try { - var vs1 = document.getElementById('videoSourceSelect') || document.getElementById('videoSource3'); - if (vs1){ - vs1 = vs1.options[vs1.selectedIndex].textContent; - if (vs1.includes(" back")){ - capabilities = {facingMode:"environment"}; - FirefoxSucks = 2; - } else if (vs1.includes(" front")){ - capabilities = {facingMode:"user"}; - FirefoxSucks = 1; - } - } - getById("videosource").style.transform = ""; - if (session.orientation=="landscape"){ - if (FirefoxSucks === 1){ - session.forceRotate = 90;; - // video needs to be flipped - getById("videosource").style.transform = "rotate(90deg)"; - } else if (FirefoxSucks === 2){ - getById("videosource").style.transform = "rotate(270deg)"; - session.forceRotate = 270;; - } - } - - } catch(e){} - } else { - return; - } - - var msg = {}; - if (session.forceRotate!==false){ - if (session.rotate){ - msg.rotate_video = session.forceRotate + parseInt(session.rotate); - } else { - msg.rotate_video = session.forceRotate; - } - } else { - msg.rotate_video = session.rotate; - } - - if (msg.rotate_video && (msg.rotate_video>=360)){ - msg.rotate_video-=360; - } - - var msgEncoded = JSON.stringify(msg); - for (var UUID in session.pcs){ - try{ - if (session.pcs[UUID].rotation != msg.rotate_video){ // 0 == false will skip I think - session.pcs[UUID].sendChannel.send(msgEncoded); - session.pcs[UUID].rotation = msg.rotate_video; - //log("sending updated rotation info"); - } - } catch(e){ - warnlog("RTC Connection seems to be dead or not yet open? 8"); - } - } - - } catch(e){errorlog(e);} - - if (!(Firefox && session.mobile)){ - updateForceRotatedCSS() - } - } else if (Firefox && session.mobile){ - try { - var vs1 = document.getElementById('videoSourceSelect') || document.getElementById('videoSource3'); - if (vs1){ - vs1 = vs1.options[vs1.selectedIndex].textContent; - if (vs1.includes(" back")){ - FirefoxSucks = 2; - } else if (vs1.includes(" front")){ - FirefoxSucks = 1; - } - } - - if (screen && screen.orientation && screen.orientation.type){ - if (screen.orientation.type.includes("portrait")){ - session.forceRotate = 0; - } else if (screen.orientation.type.includes("landscape")){ - if (FirefoxSucks === 1){ - session.forceRotate = 90;; - } else if (FirefoxSucks === 2){ - session.forceRotate = 270;; - } - } - } else if (window.matchMedia("(orientation: portrait)").matches){ // legacy support; it seems to update late, 100ms or so after screen.orientation, so lets not use it - session.forceRotate = 0; - } else if (window.matchMedia("(orientation: landscape)").matches){ - if (FirefoxSucks === 1){ - session.forceRotate = 90;; - } else if (FirefoxSucks === 2){ - session.forceRotate = 270;; - } - } - - var msg = {}; - if (session.forceRotate!==false){ - if (session.rotate){ - msg.rotate_video = session.forceRotate + parseInt(session.rotate); - } else { - msg.rotate_video = session.forceRotate; - } - if (msg.rotate_video && (msg.rotate_video>=360)){ - msg.rotate_video-=360; - } - warnlog("FIREFOX MOBILE ONLY ROTATE: "+msg.rotate_video); - //session.sendMessage(msg); - - var msgEncoded = JSON.stringify(msg); - for (var UUID in session.pcs){ - try{ - if (session.pcs[UUID].rotation != msg.rotate_video){ - session.pcs[UUID].sendChannel.send(msgEncoded); - session.pcs[UUID].rotation = msg.rotate_video; - //log("sending updated rotation info"); - } - } catch(e){ - warnlog("RTC Connection seems to be dead or not yet open? 8"); - } - } - } - } catch(e){} - } - if (!skipLastBit){ - applyMirror(session.mirrorExclude); - session.setResolution(); // probably only triggers with mobile devices? - } -} - -function updateForceRotatedCSS(rotateThis=session.forceRotate){ - if (rotateThis==270){ - document.body.setAttribute( "style", "transform: rotate(270deg);position: absolute;top: 100vh;left: 0;height: 100vw;width: 100vh;transform-origin: 0 0;"); - document.body.dataset.rotated = "1"; - } else if (rotateThis==90){ - document.body.setAttribute( "style", "transform: rotate(90deg);position: absolute;top: 0;left: 100vw;height: 100vw;width: 100vh;transform-origin: 0 0;"); - document.body.dataset.rotated = "1"; - } else if (rotateThis==180){ - document.body.setAttribute( "style", "transform: rotate(180deg);position: absolute;top: 100vh;left: 100vw;height: 100vh;width: 100vw;transform-origin: 0 0;"); - document.body.dataset.rotated = "" - } else { - document.body.setAttribute( "style", ""); - document.body.dataset.rotated = "" - } -} - -async function joinDataMode(){ // join the room, but without publishing anything. - await session.connect(); - if (session.roomid){ - getById("head3").classList.add('hidden'); - getById("head3a").classList.add('hidden'); - joinRoom(session.roomid); - } else if (session.view){ - window.onresize = updateMixer; - play(); - if (session.permaid!==false){ - session.postPublish(); - } - } else if (session.permaid!==false){ - session.postPublish(); - } -} - -function publishWebcam(btn = false, miconly=false) { - - if (btn) { - if (btn.dataset.ready == "false") { - warnlog("Clicked too quickly; button not enabled yet"); - return; - } - - if (getById("passwordBasicInput").value.length){ - session.password = getById("passwordBasicInput").value; - session.password = sanitizePassword(session.password); - if (session.password.length==0){ - session.password = false; - } else { - session.defaultPassword = false; - if (urlParams.has('pass')) { - updateURL("pass=" + session.password); - } else if (urlParams.has('pw')) { - updateURL("pw=" + session.password); - } else if (urlParams.has('p')) { - updateURL("p=" + session.password); - } else { - updateURL("password=" + session.password); - } - } - } - } - - if (activatedStream == true) { - return; - } - activatedStream = true; - log("PRESSED PUBLISH WEBCAM!!"); - - formSubmitting = false; - window.scrollTo(0, 0); // iOS has a nasty habit of overriding the CSS when changing camaera selections, so this addresses that. - - getById("head2").className = 'hidden'; - - if (session.roomid !== false) { // they are in a room or a faux room - - window.onresize = updateMixer; - window.onorientationchange = function(){ - if (Firefox){ - updateForceRotate(true); - } - setTimeout(async function(){ - if (session.forceAspectRatio){ - await updateCameraConstraints("aspectRatio", session.forceAspectRatio); - } - if (session.effect && (session.effect === "7")){digitalZoom(true);} - updateForceRotate(); - updateMixer(); - }, 200); - }; - - if ((session.roomid === "") && ((!(session.view)) || (session.view === ""))) { - // no room, no viewing, viewing disabled - if (session.manual===null){ - session.manual = true; - } - if (!(session.cleanOutput)) { - var showReshare = getStorage("showReshare"); - if (showReshare){ - generateHash(session.streamID + session.salt + "bca321", 4).then(function(hash) { // million to one error. - if (showReshare === hash){ - getById("head3").classList.remove('hidden'); - getById("head3a").classList.remove('hidden'); - } - }).catch(errorlog); - } - } - - } else { - log("ROOM ID ENABLED"); - log("Update Mixer Event on REsize SET"); - getById("main").style.overflow = "hidden"; - //session.cbr=0; // we're just going to override it - - if (session.stereo == 5) { - if (session.roomid === "") { - session.stereo = 1; - } else { - session.stereo = 3; - } - } - joinRoom(session.roomid); - if (session.roomid !== "") { - if (!(session.cleanOutput)) { - getById("head2").className = ''; - } - } - getById("head3").classList.add('hidden'); - getById("head3a").classList.add('hidden'); - } - - } else { // they are not in a room or faux room - if (session.manual===null){ - session.manual = true; - } - getById("head3").classList.remove('hidden'); - getById("head3a").classList.remove('hidden'); - getById("logoname").style.display = 'none'; - generateHash(session.streamID + session.salt + "bca321", 4).then(function(hash) { // million to one error. - setStorage("showReshare", hash, 24*30) - }).catch(errorlog); - } - - log("streamID is: " + session.streamID); - getById("head1").className = 'hidden'; - - - if (!session.cleanOutput){ - getById("mutebutton").classList.remove("hidden"); - getById("mutespeakerbutton").classList.remove("hidden"); - //getById("mutespeakerbutton").className="float"; - getById("chatbutton").className = "float"; - getById('sharefilebutton').classList.remove("hidden"); // we won't override "display:none", if set, though. - getById("mutevideobutton").className = "float"; - getById("hangupbutton").className = "float"; - if (session.showSettings) { - getById("settingsbutton").className = "float"; - } - if (session.raisehands) { - getById("raisehandbutton").className = "float"; - } - if (session.pptControls){ - getById("pptbackbutton").classList.remove("hidden"); - getById("pptnextbutton").classList.remove("hidden"); - } - if (session.recordLocal !== false) { - getById("recordLocalbutton").classList.remove("hidden"); - } - if (session.screensharebutton) { - if (session.roomid) { - if (session.screenshareType===3){ - getById("screenshare3button").className = "float"; - getById("screensharebutton").className = "float hidden"; - getById("screenshare2button").className = "float hidden"; - } else if (session.screenshareType===1){ - getById("screensharebutton").className = "float"; - getById("screenshare3button").className = "float hidden"; - getById("screenshare2button").className = "float hidden"; - } else if (session.screenshareType===2){ - getById("screenshare2button").className = "float"; - getById("screensharebutton").className = "float hidden"; - getById("screenshare3button").className = "float hidden"; - } else { - getById("screenshare3button").className = "float"; - getById("screensharebutton").className = "float hidden"; - getById("screenshare2button").className = "float hidden"; - } - } else { - getById("screensharebutton").className = "float"; - getById("screenshare2button").className = "float hidden"; - getById("screenshare3button").className = "float hidden"; - } - } - getById("controlButtons").classList.remove("hidden"); - getById("helpbutton").style.display = "inherit"; - getById("reportbutton").style.display = ""; - } else if (session.cleanish && session.recordLocal!==false){ - getById("recordLocalbutton").classList.remove("hidden"); - getById("mutebutton").classList.add("hidden"); - getById("mutespeakerbutton").classList.add("hidden"); - getById("chatbutton").classList.add("hidden"); - getById("mutevideobutton").classList.add("hidden"); - getById("hangupbutton").classList.add("hidden"); - getById("hangupbutton2").classList.add("hidden"); - getById("controlButtons").classList.remove("hidden"); - getById("settingsbutton").classList.add("hidden"); - getById("screenshare2button").classList.add("hidden"); - getById("screensharebutton").classList.add("hidden"); - getById("queuebutton").classList.add("hidden"); - } else { - getById("controlButtons").classList.add("hidden"); - } - - if (session.chatbutton === true) { - getById("chatbutton").classList.remove("hidden"); - getById("controlButtons").classList.remove("hidden"); - } else if (session.chatbutton === false) { - getById("chatbutton").classList.add("hidden"); - } - - updatePushId() - - if (session.dataMode){ // skip the media stuff. - errorlog("this shoulnd't happen.."); - session.postPublish(); - return; - } - - if (!session.streamSrc){ - checkBasicStreamsExist(); // create srcObject + videoElement - } - - if (Firefox && session.mobile && session.streamSrc){ // this just keeps the phone active; firefox is more annoying - - setInterval(function(){ - if (document.getElementById("keepAlivePlayer") && session.streamSrc){ - getById("keepAlivePlayer").remove(); - } else if (!document.getElementById("keepAlivePlayer")){ - let fakeElement = document.createElement("video"); - fakeElement.autoplay = true; - fakeElement.loop = true; - fakeElement.muted = true; - fakeElement.src = "./media/micro.mp4"; - fakeElement.style.width = "1px"; - fakeElement.style.height ="1px"; - fakeElement.controls = false; - fakeElement.id = "keepAlivePlayer"; - getById("main").appendChild(fakeElement); - } - }, 4000); - } else if (!session.avatar && session.mobile && session.streamSrc && !session.streamSrc.getVideoTracks().length){ // this just keeps the phone active. - - setInterval(function(){ - if (document.getElementById("keepAlivePlayer") && session.streamSrc.getVideoTracks().length){ - getById("keepAlivePlayer").remove(); - } else if (!document.getElementById("keepAlivePlayer")){ - let fakeElement = document.createElement("video"); - fakeElement.autoplay = true; - fakeElement.loop = true; - fakeElement.muted = true; - fakeElement.src = "./media/micro.mp4"; - fakeElement.style.width = "1px"; - fakeElement.style.height ="1px"; - fakeElement.controls = false; - fakeElement.id = "keepAlivePlayer"; - getById("main").appendChild(fakeElement); - } - }, 4000); - } - - - session.publishStream(getById("previewWebcam")); // calls session.postPublish at the end. -} - -function createYoutubeLink(vidid){ - return "https://www.youtube.com/embed/"+vidid+"?modestbranding=1&playsinline=1&enablejsapi=1"; -} -function parseURL4Iframe(iframeURL){ - if (iframeURL==""){ - iframeURL="./"; - } - if (iframeURL === session.iframeSrc){return iframeURL;} - - if (iframeURL.startsWith("http://")){ - try { - iframeURL = "https://"+ iframeURL.split("http://")[1]; - } catch(e){errorlog(e);} - } - - if (iframeURL.startsWith("https://") || iframeURL.startsWith("http://")){ - var domain = (new URL(iframeURL)); - domain = domain.hostname; - - if (domain == "youtu.be"){ - iframeURL = iframeURL.replace("youtu.be/","youtube.com/watch?v="); - } - - if ((domain == "youtu.be") || (domain=="www.youtube.com") || (domain=="youtube.com")){ - var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/; - var match = iframeURL.match(regExp); - var vidid = (match&&match[7].length==11)? match[7] : false; - - // https://www.youtube.com/live_chat?v=&embed_domain= - if (iframeURL.includes("/live_chat")){ - if (!iframeURL.includes("&embed_domain=")){ - iframeURL += "&embed_domain="+location.hostname; - } - } - - if (vidid){ - //specialResult = {}; - //specialResult.originalSrc = iframeURL; - //specialResult.parsedSrc = "https://www.youtube.com/embed/"+vidid+"?autoplay=1&modestbranding=1&playsinline=1&enablejsapi=1"; - //specialResult.handler = "youtube"; - //specialResult.vid = vidid; - //iframeURL = specialResult; - iframeURL = createYoutubeLink(vidid); - } else { // see if there is a playlist link here or not. - - // https://youtube.com/playlist?list=PLWodc2tCfAH1l_LDvEyxEqFf42hOBKqQM - iframeURL = iframeURL.replace("playlist?list=","embed/videoseries?list="); - - var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(videoseries\?))\??list?=?([^#&?]*).*/; - var match = iframeURL.match(regExp); - var plid = (match&&match[7].length==34)? match[7] : false; - if (plid){ - iframeURL = 'https://www.youtube.com/embed/videoseries?list='+plid+"&autoplay=1&modestbranding=1&playsinline=1&enablejsapi=1"; - } - - } - - } else if ((domain=="twitch.tv") || (domain=="www.twitch.tv")){ - if (iframeURL.includes("twitch.tv/popout/")){ - // this is a twitch live chat window - iframeURL = iframeURL.replace("/popout/","/embed/"); - iframeURL = iframeURL.replace("?popout=","?parent="+location.hostname); - iframeURL = iframeURL.replace("?popout","?parent="+location.hostname); - iframeURL = iframeURL.replace("&popout=","?parent="+location.hostname); - iframeURL = iframeURL.replace("&popout","?parent="+location.hostname); - if (iframeURL.includes("darkpopout=")){ - iframeURL = iframeURL.replace("?darkpopout=","?darkpopout=&parent="+location.hostname); - } else { - iframeURL = iframeURL.replace("?darkpopout","?darkpopout&parent="+location.hostname); - } - } else { - var vidid = iframeURL.split('/').pop().split('#')[0].split('?')[0]; - if (vidid){ - iframeURL = "https://player.twitch.tv/?channel="+vidid+"&parent="+location.hostname; - } - } - } else if ((domain=="www.vimeo.com") || (domain=="vimeo.com")){ - iframeURL = iframeURL.replace("//vimeo.com/","//player.vimeo.com/video/"); - iframeURL = iframeURL.replace("//www.vimeo.com/","//player.vimeo.com/video/"); - } else if (domain.includes("tiktok.com")){ - var split = iframeURL.split("/video/"); - if (split.length>1){ - split = split[1].split("/")[0].split("?")[0].split("#")[0]; - iframeURL = "https://www.tiktok.com/embed/v2/" + split; - } - } - } - return iframeURL; -} - -function soloLinkGenerator(streamID, scene=true){ - - var codecGroupFlag=""; - if (session.codecGroupFlag){ - codecGroupFlag = session.codecGroupFlag; - } - if (session.bitrateGroupFlag){ - codecGroupFlag += session.bitrateGroupFlag; - } - - var wss = ""; - if (session.wssSetViaUrl){ - if (session.customWSS && (session.customWSS!==true)){ - wss = "&pie="+session.customWSS; - } else { - wss = "&wss="+session.wss; - } - } - - var passAdd2=""; - if (session.password){ - if (session.defaultPassword===false){ - passAdd2="&password="+session.password; - } - } - - if (session.token){ - passAdd2+="&token="+session.token; - } - - if (scene){ - return "https://"+location.host+location.pathname+"?view="+streamID+"&solo"+codecGroupFlag+"&room="+session.roomid+passAdd2+wss+soloLinkAppended; - } else { - return "https://"+location.host+location.pathname+"?view="+streamID+codecGroupFlag+passAdd2+wss+soloLinkAppended; - } -} - -function YoutubeAPI(iframe, func, args) { // playVideo, pauseVideo, stopVideo - if (!(iframe && iframe.contentWindow)){return;} - try { - iframe.contentWindow.postMessage(JSON.stringify({ - "event": "command", - "func": func, - "args": args || [], - "id": iframe.id || "unknown" - }), "*"); - } catch(e){} -} - -function YoutubeListen(iframe_id){ - var iframe = document.getElementById(iframe_id); - if (!iframe){return;} - if (iframe.loadedYoutubeListen){return;} - try { - iframe.contentWindow.postMessage(JSON.stringify({"event":"listening","id":iframe_id}),'*'); // - } catch(e){ } - setTimeout(function(iframe_id){YoutubeListen(iframe_id);},1000,iframe_id); -} - -function processYoutubeEvent(e){ - if (!(e.type && (e.type === "message"))){return;} - try { - var data = JSON.parse(e.data); - if ("id" in data){ - var iframe = document.getElementById(data.id); - if (!iframe){return;} - if (!iframe.loadedYoutubeListen){ - iframe.loadedYoutubeListen = true; - } - } - if (!("mediaReferenceTime" in data.info)){ - return; - } - } catch(e){return;} - - log(e); - - if (iframe.id=="iframe_source"){ - - if (!session.iframeEle.sendOnNewConnect){ - session.iframeEle.sendOnNewConnect = {}; - session.iframeEle.sendOnNewConnect.ifs = {}; - session.iframeEle.sendOnNewConnect.ifs.t = null; - session.iframeEle.sendOnNewConnect.ifs.v = null; - session.iframeEle.sendOnNewConnect.ifs.s = null; - session.iframeEle.sendOnNewConnect.ifs.r = null; - } - - try { - var msg = {}; - msg.ifs = {} - - try{ - msg.ifs.t = parseFloat(data.info.mediaReferenceTime+0.01) || 0; - session.iframeEle.sendOnNewConnect.ifs.t = msg.ifs.t; - } catch(e){return;} - - if ("playerState" in data.info){ - msg.ifs.s = parseInt(data.info.playerState); - - if (msg.ifs.s == -1){ - msg.ifs.s = 0; - } - if (msg.ifs.s == 2){ - if (session.iframeEle.sendOnNewConnect.ifs.s==3){ - delete(msg.ifs.s); - } else { - msg.ifs.s = 3; - } - } - if (msg.ifs.s && (session.iframeEle.sendOnNewConnect.ifs.s != msg.ifs.s)){ - session.iframeEle.sendOnNewConnect.ifs.s = msg.ifs.s; - } else { - //delete(msg.ifs.s); - } - - if ("videoData" in data.info){ - if (session.iframeEle.sendOnNewConnect.ifs.v != data.info.videoData.video_id){ - session.iframeEle.sendOnNewConnect.ifs.v = data.info.videoData.video_id; - msg.ifs.v = data.info.videoData.video_id; - - var vidSrc = createYoutubeLink(msg.ifs.v); - if (vidSrc!==session.iframeSrc){ - session.iframeSrc = vidSrc; - var data = {} - data.iframeSrc = session.iframeSrc; - if (parseInt(msg.ifs.t)>1){ - data.iframeSrc += "&start="+parseInt(Math.ceil(msg.ifs.t))+""; - } - - for (var UUID in session.pcs){ - if (session.pcs[UUID].allowIframe===true){ - session.sendMessage(data, UUID); - } - } - return; - } - } - } - // we will still be sending the msg data if available. - } else if ("videoData" in data.info){ - if (session.iframeEle.sendOnNewConnect.ifs.v != data.info.videoData.video_id){ - msg.ifs.v = data.info.videoData.video_id; - session.iframeEle.sendOnNewConnect.ifs.v = msg.ifs.v; - var vidSrc = createYoutubeLink(msg.ifs.v); - if (vidSrc!==session.iframeSrc){ - session.iframeSrc = vidSrc; - var data = {} - data.iframeSrc = session.iframeSrc; - if (parseInt(msg.ifs.t)>1){ - data.iframeSrc += "&start="+parseInt(Math.ceil(msg.ifs.t))+""; - } - if (session.iframeEle.sendOnNewConnect.ifs.s == "1"){ - data.iframeSrc += "&autoplay=1"; - } else { - data.iframeSrc += "&autoplay=0"; - } - for (var UUID in session.pcs){ - if (session.pcs[UUID].allowIframe===true){ - session.sendMessage(data, UUID); - } - } - return; - } - } - } else { - if ("playbackRate" in data.info){ - msg.ifs.r = parseFloat(data.info.playbackRate); - if (session.iframeEle.sendOnNewConnect.ifs.r != msg.ifs.r){ - session.iframeEle.sendOnNewConnect.ifs.r = msg.ifs.r; - } else { - delete(msg.ifs.r); - } - } - if (session.iframeEle.sendOnNewConnect.ifs.s == 1){ - if ("t" in msg.ifs){ - delete(msg.ifs.t); - } - } - } - - if (Object.keys(msg.ifs).length == 0){return;} - - for (var UUID in session.pcs){ - if (session.pcs[UUID].allowIframe){ - session.sendMessage(msg); - } - } - } catch(e){return;} - } else { - try{ - var UUID = iframe.dataset.UUID; - var msg = {}; - msg.ifs = {} - if ("t" in msg.ifs){ - msg.ifs.t = parseFloat(data.info.mediaReferenceTime+0.01) || 0; - /* if (!iframe.sendOnNewConnect){ - iframe.sendOnNewConnect = msg; - } else { - iframe.sendOnNewConnect.ifs.t = msg.ifs.t; - } */ - } - - if ("playerState" in data.info){ - msg.ifs.s = parseInt(data.info.playerState); - } - if ("videoData" in data.info){ - msg.ifs.v = data.info.videoData.video_id; - } - if (("playbackRate" in data.info) && (data.info.playbackRate!==1)){ - msg.ifs.r = parseFloat(data.info.playbackRate); - } - // TODO: the viewers don't have a way to tell the director if they reload what the time is at. - session.sendRequest(msg, UUID); // send to the iframe's owner only. let them be the controller for others. - } catch(e){return;} - } -} - -function processIframeSyncFeedback(ifs, UUID){ // remote iframe feedback from the remote viewers - // YoutubeAPI("iframe_source", "seekTo", [700]); - // YoutubeAPI("iframe_source", "volume", [100]); - - warnlog(ifs); - return; - - - if (!session.iframeEle.sendOnNewConnect){ - session.iframeEle.sendOnNewConnect = {}; - session.iframeEle.sendOnNewConnect.ifs = {}; - session.iframeEle.sendOnNewConnect.ifs.t = null; - session.iframeEle.sendOnNewConnect.ifs.v = null; - session.iframeEle.sendOnNewConnect.ifs.s = null; - session.iframeEle.sendOnNewConnect.ifs.r = null; - } - - if ("t" in ifs){ - if (Math.abs(session.iframeEle.sendOnNewConnect.ifs.t-ifs.t)>=1){ - //session.iframeEle.sendOnNewConnect.ifs.t = ifs.t; - } else { - delete(ifs.t); - } - } - if ("v" in ifs){ - if (session.iframeEle.sendOnNewConnect.ifs.v != ifs.v){ - //session.iframeEle.sendOnNewConnect.ifs.v = ifs.v; - } else { - delete(ifs.v); - } - } - if ("s" in ifs){ - - if (ifs.s == -1){ - ifs.s = 0; - } - if (session.iframeEle.sendOnNewConnect.ifs.s == -1){ - session.iframeEle.sendOnNewConnect.ifs.s = 0; - } - - if (ifs.s == 2){ - ifs.s = 3; - } - if (session.iframeEle.sendOnNewConnect.ifs.s == 2){ - session.iframeEle.sendOnNewConnect.ifs.s = 3; - } - - if (session.iframeEle.sendOnNewConnect.ifs.s != ifs.s){ - //session.iframeEle.sendOnNewConnect.ifs.s = ifs.s; - } else { - delete(ifs.s); - } - } - if ("r" in ifs){ - if (session.iframeEle.sendOnNewConnect.ifs.r != ifs.r){ - //session.iframeEle.sendOnNewConnect.ifs.r = ifs.r; - } else { - delete(ifs.r); - } - } - - - if (session.iframeEle){ - if (ifs.v){ // I need to have this change videos . - var vidSrc = createYoutubeLink(ifs.v); - if (vidSrc!==session.iframeSrc){ - session.iframeSrc = vidSrc; - session.iframeEle.src = vidSrc; - } - } else if ("t" in ifs){ - YoutubeAPI(session.iframeEle, "seekTo", [parseFloat(ifs.t)]); - } else if (ifs.r){ /// setPlaybackRate - YoutubeAPI(session.iframeEle, "setPlaybackRate", [parseFloat(ifs.r)]); - } else if ("s" in ifs){ /// setPlaybackState - if (ifs.s == -1) { YoutubeAPI(session.iframeEle, "stopVideo"); } - else if (ifs.s == 0) { YoutubeAPI(session.iframeEle, "stopVideo"); } // player stops. - else if (ifs.s == 1) { YoutubeAPI(session.iframeEle, "playVideo"); } //Video is playing - else if (ifs.s == 2) { YoutubeAPI(session.iframeEle, "pauseVideo"); } //Video is paused - else if (ifs.s == 3) { YoutubeAPI(session.iframeEle, "pauseVideo"); } //video is buffering - else if (ifs.s == 5) { } //Video is cued. - } - } else if (session.iframeSrc){ - if (ifs.v){ - var vidSrc = createYoutubeLink(ifs.v); - if (vidSrc!==session.iframeSrc){ - session.iframeSrc = vidSrc; - var data = {} - data.iframeSrc = session.iframeSrc; - if (ifs.t && (parseInt(ifs.t)>1)){ - data.iframeSrc += "&start="+parseInt(Math.ceil(ifs.t)); - } - if (ifs.s == "1"){ - data.iframeSrc += "&autoplay=1"; - } else { - data.iframeSrc += "&autoplay=0"; - } - for (var uuid in session.pcs){ - if (uuid == UUID){continue;} - if (session.pcs[uuid].allowIframe===true){ - session.sendMessage(data, uuid); - } - } - return; - } - } - // we're going to forward the message directly to the other viewers instead - if ("s" in ifs){ /// setPlaybackState - var msg = {}; - msg.ifs = ifs; - for (var uuid in session.pcs){ - if (uuid == UUID){continue;} - if (session.pcs[uuid].allowIframe){ - session.sendMessage(msg, uuid); - } - } - } - } -} - -function processIframeSyncUpdates(ifs, UUID){ // playback updates from remote guest. - // YoutubeAPI("iframe_source", "seekTo", [700]); - // YoutubeAPI("iframe_source", "volume", [100]); - if (ifs.v && ("s" in ifs)){ - // - } else if ("s" in ifs){ - if ("t" in ifs){ - YoutubeAPI(session.rpcs[UUID].iframeEle, "seekTo", [parseFloat(ifs.t)]); - } - YoutubeAPI(session.rpcs[UUID].iframeEle, "playVideo"); - } else if ("t" in ifs){ - YoutubeAPI(session.rpcs[UUID].iframeEle, "seekTo", [parseFloat(ifs.t)]); - } - if (ifs.r){ /// setPlaybackRate - YoutubeAPI(session.rpcs[UUID].iframeEle, "setPlaybackRate", [parseFloat(ifs.r)]); - } - if ("s" in ifs){ /// setPlaybackState - if (ifs.s == -1) { YoutubeAPI(session.rpcs[UUID].iframeEle, "stopVideo"); } - else if (ifs.s == 0) { YoutubeAPI(session.rpcs[UUID].iframeEle, "stopVideo"); } // player stops. - else if (ifs.s == 1) { YoutubeAPI(session.rpcs[UUID].iframeEle, "playVideo"); } //Video is playing - else if (ifs.s == 2) { YoutubeAPI(session.rpcs[UUID].iframeEle, "pauseVideo"); } //Video is paused - else if (ifs.s == 3) { YoutubeAPI(session.rpcs[UUID].iframeEle, "pauseVideo"); } //video is buffering - else if (ifs.s == 5) { } //Video is cued. - } -} - -function updatePushId(){ - if (session.doNotSeed){return;} - - if (urlParams.has('push')){ - updateURL("push="+session.streamID); - } else if (urlParams.has('id')){ - updateURL("id="+session.streamID); - } else if (urlParams.has('permaid')){ - updateURL("permaid="+session.streamID); - } else { - updateURL("push="+session.streamID); - } -} - -session.publishIFrame = function(iframeURL){ - - if (!session.cleanOutput){ - getById("websitesharebutton2").classList.remove('hidden'); - } - - if (session.transcript){ - setTimeout(function(){setupClosedCaptions();},1000); - } - - session.iframeSrc = parseURL4Iframe(iframeURL); - - var iframe = document.createElement("iframe"); - iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;display-capture;midi;"; - iframe.src = session.iframeSrc; - iframe.id = "iframe_source"; - iframe.loadedYoutubeListen = false; - session.iframeEle = iframe; - - var container = document.createElement("div"); - iframe.container = container; - container.id = "container_iframe"; - container.appendChild(iframe); - getById("gridlayout").appendChild(container); - - if (session.iframeSrc.startsWith("https://www.youtube.com/")){ // special handler. - setTimeout(function(iframe_id){YoutubeListen(iframe_id);}, 1000, iframe.id); - } - - if (session.cover){ - container.style.setProperty('height', '100%', 'important'); - } - - if (session.roomid!==false){ - if ((session.roomid==="") && ((!(session.view)) || (session.view===""))){ - - } else { - log("ROOMID EANBLED"); - getById("head3").classList.add('hidden'); - getById("head3a").classList.add('hidden'); - joinRoom(session.roomid); - } - - } else { - getById("head3").classList.remove('hidden'); - getById("head3a").classList.remove('hidden'); - getById("logoname").style.display = 'none'; - } - getById("head1").className = 'hidden'; - - updatePushId() - - getById("head1").className = 'hidden'; - getById("head2").className = 'hidden'; - - if (!(session.cleanOutput)){ - getById("chatbutton").className="float"; - getById("hangupbutton").className="float"; - getById("controlButtons").classList.remove("hidden"); - getById('sharefilebutton').classList.remove("hidden"); // we won't override "display:none", if set, though. - getById("helpbutton").style.display = "inherit"; - getById("reportbutton").style.display = ""; - } else { - getById("controlButtons").classList.add("hidden"); - } - - if (session.chatbutton === false) { - getById("chatbutton").classList.add("hidden"); - } - - if (session.director){ - // - } else if (session.scene!==false){ - updateMixer(); - } else if (session.roomid!==false){ - if (session.roomid===""){ - if (!(session.view) || (session.view==="")){ - session.windowed = true; - container.classList.add("vidcon"); - - getById("mutespeakerbutton").classList.add("hidden"); - container.style.width="100%"; - container.style.height="100%"; - container.style.alignItems = "center"; - container.style.maxWidth= "100%"; - container.style.maxHeight= "100%"; - container.style.verticalAlign= "middle"; - container.style.margin= "auto"; - container.style.backgroundColor = "#666"; - container.style.border = "2px solid"; - - } else { - session.windowed = false; - window.onresize = updateMixer; - updateMixer(); - } - } else { - window.onresize = updateMixer; - session.windowed = false; - updateMixer(); - } - } else { - window.onresize = updateMixer; - container.style.maxHeight= "1280px"; - container.style.maxWidth= "720px"; - container.style.verticalAlign= "middle"; - container.style.height="100%"; - container.style.width= "100%"; - container.style.margin= "auto"; - container.style.alignItems = "center"; - container.style.backgroundColor = "#666"; - } - - session.seeding=true; - - updateReshareLink(); - pokeIframeAPI('started-iframe-share'); - session.seedStream(); - - return container; -} // publishIframe - - -/* session.publishWhepSrc = function(){ - - if (!session.whepSrc){errorlog("no WHEP Src");return;} - - if (!session.cleanOutput){ - getById("websitesharebutton2").classList.remove('hidden'); - } - - var UUID = whepIn(session.whepSrc); - - var container = document.createElement("div"); - iframe.container = container; - container.id = "container_iframe"; - container.appendChild(iframe); - getById("gridlayout").appendChild(container); - - if (session.iframeSrc.startsWith("https://www.youtube.com/")){ // special handler. - setTimeout(function(iframe_id){YoutubeListen(iframe_id);}, 1000, iframe.id); - } - - if (session.cover){ - container.style.setProperty('height', '100%', 'important'); - } - - if (session.roomid!==false){ - if ((session.roomid==="") && ((!(session.view)) || (session.view===""))){ - - } else { - log("ROOMID EANBLED"); - getById("head3").classList.add('hidden'); - getById("head3a").classList.add('hidden'); - joinRoom(session.roomid); - } - - } else { - getById("head3").classList.remove('hidden'); - getById("head3a").classList.remove('hidden'); - getById("logoname").style.display = 'none'; - } - getById("head1").className = 'hidden'; - - updatePushId() - - getById("head1").className = 'hidden'; - getById("head2").className = 'hidden'; - - if (!(session.cleanOutput)){ - getById("chatbutton").className="float"; - getById("hangupbutton").className="float"; - getById("controlButtons").classList.remove("hidden"); - getById('sharefilebutton').classList.remove("hidden"); // we won't override "display:none", if set, though. - getById("helpbutton").style.display = "inherit"; - getById("reportbutton").style.display = ""; - } else { - getById("controlButtons").classList.add("hidden"); - } - - if (session.chatbutton === false) { - getById("chatbutton").classList.add("hidden"); - } - - if (session.director){ - // - } else if (session.scene!==false){ - updateMixer(); - } else if (session.roomid!==false){ - if (session.roomid===""){ - if (!session.fullscreen && (!(session.view) || (session.view===""))){ - session.windowed = true; - container.classList.add("vidcon"); - - getById("mutespeakerbutton").classList.add("hidden"); - container.style.width="100%"; - container.style.height="100%"; - container.style.alignItems = "center"; - container.style.maxWidth= "100%"; - container.style.maxHeight= "100%"; - container.style.verticalAlign= "middle"; - container.style.margin= "auto"; - container.style.backgroundColor = "#666"; - container.style.border = "2px solid"; - - } else { - session.windowed = false; - window.onresize = updateMixer; - updateMixer(); - } - } else { - window.onresize = updateMixer; - session.windowed = false; - updateMixer(); - } - } else { - window.onresize = updateMixer; - container.style.maxHeight= "1280px"; - container.style.maxWidth= "720px"; - container.style.verticalAlign= "middle"; - container.style.height="100%"; - container.style.width= "100%"; - container.style.margin= "auto"; - container.style.alignItems = "center"; - container.style.backgroundColor = "#666"; - } - - session.seeding=true; - - updateReshareLink(); - pokeIframeAPI('started-iframe-share'); - session.seedStream(); - - return container; -} // publishWhepSrc */ - - -function outboundAudioPipeline(){ // this function isn't letting me change the audio source - - if (session.disableWebAudio) { - // if (iOS || iPad){return session.streamSrc;} // iOS devices can't remap video tracks, else KABOOM. Might as well do this for android also. - - if (session.streamSrcClone){ - log("123a"); - session.streamSrcClone.getTracks().forEach(function(track) { - session.streamSrcClone.removeTrack(track); - track.stop(); - }); - } - - if (session.streamSrc && session.streamSrc.clone){ - log("123b"); - var streamSrc = session.streamSrc.clone(); - session.streamSrcClone = streamSrc; - return streamSrc; - } else { - log("123c"); - var newStream = createMediaStream(); - session.streamSrcClone = newStream; - - if (session.streamSrc){ - session.streamSrc.getAudioTracks().forEach(function(track) { // this seems to fix a bug with macbooks. - newStream.addTrack(track, session.streamSrc); - }); - } - if (session.videoElement && session.videoElement.srcObject){ - session.videoElement.srcObject.getVideoTracks().forEach(function(track) { // this seems to fix a bug with macbooks. - newStream.addTrack(track, session.videoElement.srcObject); - }); - } else if (session.streamSrc){ - session.streamSrc.getVideoTracks().forEach(function(track) { // this seems to fix a bug with macbooks. - newStream.addTrack(track, session.streamSrc); - }); - } - - } - return newStream; - } - - if (!session.streamSrc){ - errorlog("STREAM DOES NOT EXIST. This is a problem"); - checkBasicStreamsExist(); - return session.streamSrc; - } - - var streamSrc = session.streamSrc; - - if (iOS || iPad){ - - if (session.streamSrcClone){ - var tracks = session.streamSrcClone.getAudioTracks(); - if (tracks.length) { - for (var waid in session.webAudios) { // TODO: EXCLUDE CURRENT TRACK IF ALREADY EXISTS ... if (track.id === wa.id){.. - session.webAudios[waid].stop(); - delete session.webAudios[waid]; - } - } - session.streamSrcClone.getTracks().forEach(function(track) { - session.streamSrcClone.removeTrack(track); - track.stop(); - }); - } - - if (session.streamSrc && session.streamSrc.clone){ // modern - streamSrc = session.streamSrc.clone(); - session.streamSrcClone = streamSrc; - } else { // backup. - streamSrc = createMediaStream(); - - if (session.streamSrc){ - session.streamSrc.getAudioTracks().forEach(function(track) { // this seems to fix a bug with macbooks. - streamSrc.addTrack(track, session.streamSrc); - }); - } - if (session.videoElement && session.videoElement.srcObject){ - session.videoElement.srcObject.getVideoTracks().forEach(function(track) { // this seems to fix a bug with macbooks. - streamSrc.addTrack(track, session.videoElement.srcObject); - }); - } else if (session.streamSrc){ - session.streamSrc.getVideoTracks().forEach(function(track) { // this seems to fix a bug with macbooks. - streamSrc.addTrack(track, session.streamSrc); - }); - } - session.streamSrcClone = streamSrc; - } - } - - for (var waid in session.webAudios) { // TODO: EXCLUDE CURRENT TRACK IF ALREADY EXISTS ... if (track.id === wa.id){.. - session.webAudios[waid].stop(); - delete session.webAudios[waid]; - } - - try { - log("Web Audio"); - var tracks = streamSrc.getAudioTracks(); - if (tracks.length) { - var webAudio = {}; - webAudio.micDelay = false; - webAudio.compressor = false; - webAudio.analyser = false; - webAudio.gainNode = false; - webAudio.splitter = false; - webAudio.subGainNodes = false; - - webAudio.lowEQ = false; - webAudio.midEQ = false; - webAudio.highEQ = false; - webAudio.lowcut1 = false; - webAudio.lowcut2 = false; - webAudio.lowcut3 = false; - - webAudio.id = tracks[0].id; // first track is used. - - if (session.audioCtxOutbound){ - // already Created - } else if (session.audioLatency !== false) { // session.audioLatency could be useful for fixing clicking issues? - session.audioCtxOutbound = new AudioContext({ - latencyHint: session.audioLatency / 1000.0 //, // needs to be in seconds, but VDON user input is via milliseconds - // sampleRate: 48000 // not sure this is a great idea, but might as well add this here, versus later on since it is needed anyways. - }); - } else { - session.audioCtxOutbound = new AudioContext(); - } - - var audioContext = session.audioCtxOutbound; - webAudio.audioContext = session.audioCtxOutbound; - - webAudio.destination = audioContext.createMediaStreamDestination(); - - if (tracks.length>1){ // tries to - try { - webAudio.mediaStreamSource = createMediaStream(); - var maxChannelCount = 2; - if (session.stereo===false){ - maxChannelCount = 1; - } - - webAudio.subGainNodes = {};// - - var merger = audioContext.createChannelMerger(maxChannelCount); - for (var i=0;i1){ - ng2 = parseInt(session.noisegateSettings[1]) || ng2; // not loud (threshold level) - } - if (session.noisegateSettings.length>2){ - ng3 = parseInt(session.noisegateSettings[2]) || 0; // stickiness; time (ms) - ng3 = ng3 / 100.0; // convert to the actual units (100ms) - } - } - if (session.noisegate){ - changeGatingGain(ng1,200); - } - - function draw() { - try { - analyser.getByteFrequencyData(dataArray); - var total = 0; - for (var i = 0; i < dataArray.length; i++) { - total += dataArray[i]; - } - total = total / 100; - if (session.quietOthers && (session.quietOthers==2)){ - if (total>10){ - if (session.muted_activeSpeaker==false){ - session.muted_activeSpeaker=true; - session.speakerMuted=true; - clearTimeout(timer); - toggleSpeakerMute(true); // okay, sicne this is quietOthers - } - } else if (session.muted_activeSpeaker==true){ - session.speakerMuted=false; - session.muted_activeSpeaker=false; - session.activelySpeaking=false; - clearTimeout(timer); - timer = setTimeout(function(){toggleSpeakerMute(true);},250); // okay, sicne this is quietOthers - } - } - - if (session.pushLoudness==true){ - var loudnessObj = {}; - loudnessObj[session.streamID] = parseInt(total); - if (isIFrame){ - parent.postMessage({"loudness": loudnessObj, "action":"loudness", "value":total}, session.iframetarget); - } - } - - if (session.noisegate){ - if (total<=ng2){ - if (currentlyActive==ng3){ - changeGatingGain(ng1,200); // set volume to 40% relative to what it is now. - log("GAIN LOWERED"); - currentlyActive=(ng3+1); - } else if (currentlyActive 150) { - if (total > 200) { - total = 200; - } - meter1.style.width = "150px"; - meter2.style.width = (total - 150) + "px"; - } - return; - } else if (toggleSettingsState && document.getElementById("meter3")){ - if (total == 0) { - meter3.style.width = "1px"; - meter4.style.width = "0px"; - } else if (total <= 1) { - meter3.style.width = "1px"; - meter4.style.width = "0px"; - } else if (total <= 150) { - meter3.style.width = total + "px"; - meter4.style.width = "0px"; - } else if (total > 150) { - if (total > 200) { - meter3.style.width = "150px"; - meter4.style.width = "50px"; - } else { - meter3.style.width = "150px"; - meter4.style.width = (total - 150) + "px"; - } - } - if (document.getElementById("mutetoggle")) { - total *= 3; - if (total > 255) { - total = 255; - } - total = parseInt(total); - document.getElementById("mutetoggle").style.color = "rgb(" + (255 - total) + ",255," + (255 - total) + ")"; - } - meter1 = false; - return; - } else if (session.cleanOutput){ - meter1 = false; - return; - } else if (document.getElementById("mutetoggle")) { - total *= 3; - if (total > 255) { - total = 255; - } - total = parseInt(total); - document.getElementById("mutetoggle").style.color = "rgb(" + (255 - total) + ",255," + (255 - total) + ")"; - } else { - clearInterval(analyser.interval); - warnlog("METERS NOT FOUND"); - } - meter1 = false; - } catch(e){ - errorlog(e); - } - }; - - analyser.interval = setInterval(function() { - draw(); - }, 100); - return analyser; -} - - - -function audioCompressor(mediaStreamSource, audioContext) { - var compressor = audioContext.createDynamicsCompressor(); - compressor.threshold.value = -40; - compressor.knee.value = 10; - compressor.ratio.value = 4; // 3 - compressor.attack.value = 0.002; // 0.001 - compressor.release.value = 0.1; // 0.06 - mediaStreamSource.connect(compressor); - return compressor; -} - -function audioLimiter(mediaStreamSource, audioContext) { - var compressor = audioContext.createDynamicsCompressor(); - compressor.threshold.value = -5; - compressor.knee.value = 0; - compressor.ratio.value = 20.0; // 1 to 20 - compressor.attack.value = 0.001; - compressor.release.value = 0.1; - mediaStreamSource.connect(compressor); - return compressor; -} - - -function activeSpeaker(border=false) { - var lastActiveSpeaker = null; - - var someoneElseIfSpeaking = false; - var anyoneIsSpeaking = false; - var defaultSpeaker = false; - - for (var UUID in session.rpcs) { - - if ((session.activeSpeaker>2) && !(session.rpcs[UUID].videoElement && session.rpcs[UUID].videoElement.srcObject && session.rpcs[UUID].videoElement.srcObject.getVideoTracks().length && !session.rpcs[UUID].videoMuted)){ - session.rpcs[UUID].activelySpeaking = false; // we're not showing audio-only sources in this mode. - session.rpcs[UUID].defaultSpeaker = false; - continue; - } - - if (session.rpcs[UUID].stats._Audio_Loudness_average) { - if (session.rpcs[UUID].stats.Audio_Loudness && (session.rpcs[UUID].stats.Audio_Loudness>10)){ - session.rpcs[UUID].stats._Audio_Loudness_average = parseFloat(session.rpcs[UUID].stats.Audio_Loudness*0.07 + session.rpcs[UUID].stats._Audio_Loudness_average*0.93); - } else { - session.rpcs[UUID].stats._Audio_Loudness_average = parseFloat(session.rpcs[UUID].stats._Audio_Loudness_average*0.975); - } - } else { - session.rpcs[UUID].stats._Audio_Loudness_average = 1; - } - if (session.rpcs[UUID].stats._Audio_Loudness_average > 13) { - - if (border) { - if (session.rpcs[UUID].videoElement) { - session.rpcs[UUID].videoElement.style.border = "green solid 1px"; - session.rpcs[UUID].videoElement.style.padding = "0"; - } - } else if (!session.rpcs[UUID].activelySpeaking){ - - session.rpcs[UUID].activelySpeaking = true; - lastActiveSpeaker = UUID; - session.rpcs[UUID].stats._Audio_Loudness_average+=50; - } - - } else if (session.rpcs[UUID].stats._Audio_Loudness_average > 6) { - // - } else { - if (border){ - if (session.rpcs[UUID].videoElement) { - session.rpcs[UUID].videoElement.style.border = ""; - session.rpcs[UUID].videoElement.style.padding = "1px"; - } - } else if (session.rpcs[UUID].activelySpeaking) { - session.rpcs[UUID].activelySpeaking=false; - lastActiveSpeaker = UUID; - } - } - if ((session.rpcs[UUID].stats.Audio_Loudness > 13) || ((session.rpcs[UUID].stats.Audio_Loudness > 5) && (session.rpcs[UUID].stats._Audio_Loudness_average>3)) || (session.rpcs[UUID].stats._Audio_Loudness_average>6)){ - someoneElseIfSpeaking = true; - } - if (session.rpcs[UUID].activelySpeaking){ - anyoneIsSpeaking=true; - } - if (session.rpcs[UUID].defaultSpeaker){ - defaultSpeaker=true; - } - } - - var loudest=null; - var loudestActive=null; - var changed = false; - if ((session.activeSpeaker===1) || (session.activeSpeaker===3)){ // will only show one speaker at a time; the loudest or last-loud speaker - if (!anyoneIsSpeaking){ - if (defaultSpeaker){ - // already good to go. - } else if (lastActiveSpeaker){ - session.rpcs[lastActiveSpeaker].defaultSpeaker=true; - changed=true; - } else if (session.scene===false || (session.nopreview===false && session.minipreview!==1)){ - // we don't need to care. - } else { - for (var UUID in session.rpcs) { - if (session.rpcs[UUID].videoElement && session.rpcs[UUID].videoElement.srcObject && session.rpcs[UUID].videoElement.srcObject.getVideoTracks().length && !session.rpcs[UUID].videoMuted){ - session.rpcs[UUID].defaultSpeaker=true; - changed=true; - break - } - } - if (!changed && (session.activeSpeaker<=2)){ // switch to streams that have no video track - for (var UUID in session.rpcs) { - if (session.rpcs[UUID].label){ - session.rpcs[UUID].defaultSpeaker=true; - changed=true; - break - } else if (!changed){ - session.rpcs[UUID].defaultSpeaker=true; - changed=true; - } - } - } - } - } else { - for (var UUID in session.rpcs) { - if (!("_Audio_Loudness_average" in session.rpcs[UUID].stats)){ // never could have been loudest, since no loudness value. - continue; - } - /* if (!loudest){ - loudest = UUID; - } else if (session.rpcs[UUID].stats._Audio_Loudness_average > session.rpcs[loudest].stats._Audio_Loudness_average){ - loudest = UUID; - } */ - - if (session.rpcs[UUID].activelySpeaking){ - if (!loudestActive){ - loudestActive = UUID; - } else if (session.rpcs[UUID].stats._Audio_Loudness_average > session.rpcs[loudestActive].stats._Audio_Loudness_average){ - if (session.rpcs[loudestActive].defaultSpeaker){ - session.rpcs[loudestActive].defaultSpeaker=false; - changed=true - } - loudestActive = UUID; - } else if (session.rpcs[UUID].defaultSpeaker){ - session.rpcs[UUID].defaultSpeaker=false; - changed=true; - } - } else if (session.rpcs[UUID].defaultSpeaker){ - session.rpcs[UUID].defaultSpeaker=false; - changed=true - } - } - if (loudestActive && !session.rpcs[loudestActive].defaultSpeaker){ - session.rpcs[loudestActive].defaultSpeaker = true; - changed = true; - } - } - } else if ((session.activeSpeaker===2) || (session.activeSpeaker===4)){ // will show whoever is talking; mixed together; if no one is talking, just shows yourself - - if (!anyoneIsSpeaking){ - if (defaultSpeaker){ - // already good to go. - } else if (lastActiveSpeaker){ - session.rpcs[lastActiveSpeaker].defaultSpeaker=true; - changed=true; - } else if (session.scene===false || (session.nopreview===false && session.minipreview!==1)){ - // we don't need to care. - } else { - for (var UUID in session.rpcs) { - if (session.rpcs[UUID].videoElement && session.rpcs[UUID].videoElement.srcObject && session.rpcs[UUID].videoElement.srcObject.getVideoTracks().length && !session.rpcs[UUID].videoMuted){ - session.rpcs[UUID].defaultSpeaker=true; - changed=true; - break - } - } - if (!changed && (session.activeSpeaker<=2)){ - for (var UUID in session.rpcs) { - if (session.rpcs[UUID].label){ - session.rpcs[UUID].defaultSpeaker=true; - changed=true; - break - } else if (!changed){ - session.rpcs[UUID].defaultSpeaker=true; - changed=true; - } - } - } - } - } else { - for (var UUID in session.rpcs) { - if (session.rpcs[UUID].activelySpeaking && !session.rpcs[UUID].defaultSpeaker){ - session.rpcs[UUID].defaultSpeaker = true; - changed = true; - } else if (!session.rpcs[UUID].activelySpeaking && session.rpcs[UUID].defaultSpeaker){ - session.rpcs[UUID].defaultSpeaker = false; - changed=true - } - } - } - } - if (session.quietOthers && (session.quietOthers===1)){ - if (someoneElseIfSpeaking){ - if (session.muted_activeSpeaker==false){ - session.muted_activeSpeaker=true; - session.muted=true; - toggleMute(true); - } - } else if (session.muted_activeSpeaker==true){ - session.muted=false; - session.muted_activeSpeaker=false; - toggleMute(true); - } - } else if (session.quietOthers && (session.quietOthers===3)){ // purely for fun. It's the opposite of a noise-gate I guess. - if (someoneElseIfSpeaking){ - if (session.muted_activeSpeaker==false){ - session.muted_activeSpeaker=true; - session.speakerMuted=true; - toggleSpeakerMute(true); // okay, sicne this is quietOthers - } - } else if (session.muted_activeSpeaker==true){ - session.speakerMuted=false; - session.muted_activeSpeaker=false; - toggleSpeakerMute(true); // okay, sicne this is quietOthers - } - } - - if (changed) { - setTimeout(function(){updateMixer();},0); - } -} - - - -function randomizeArray(unshuffled) { - - var arr = unshuffled.map((a) => ({ - sort: Math.random(), value: a - })).sort((a, b) => a.sort - b.sort).map((a) => a.value); // shuffle once - - for (var i = arr.length - 1; i > 0; i--) { // shuffle twice - var j = Math.floor(Math.random() * (i + 1)); - var tmp = arr[i]; - arr[i] = arr[j]; - arr[j] = tmp; - } - return arr -} - -function joinRoom(roomname) { - if (roomname.length) { - roomname = sanitizeRoomName(roomname); - log("Join room: " + roomname); - updateVolume(false); // chance of a race condition, but unlikely and not a big deal if so. - session.joinRoom(roomname).then(function(response) { // callback from server; we've joined the room. Just the listing is returned - - if (session.joiningRoom === "seedPlz") { // allow us to seed, now that we have joined the room. - session.joiningRoom = false; // joined - session.seedStream(); - } else { - session.joiningRoom = false; // no seeding callback - } - var token = ""; - if (session.token){ - token+="&token="+session.token; - } - - if (!session.cleanOutput){ - if (session.roomhost){ - if (session.defaultPassword===false){ - if (session.password === false){ - var invite = "https://"+location.host+location.pathname+"?room="+session.roomid+"&password=false"+token; - warnUser("You can invite others with:\n\n"+invite+"", false, false); - } else { - generateHash(session.password + session.salt, 4).then(function(hash) { - var invite = "https://"+location.host+location.pathname+"?room="+session.roomid+"&hash="+hash+token; - warnUser("You can invite others with:\n\n"+invite+"", false, false); - }); - } - } else { - var invite = "https://"+location.host+location.pathname+"?room="+session.roomid+token; - warnUser("You can invite others with:\n\n"+invite+"", false, false); - } - - } - } - - log("Members in Room"); - log(response); - - if (session.randomize === true) { - response = randomizeArray(response); - log("Randomized List of Viewers"); - log(response); - for (var i in response) { - if ("UUID" in response[i]) { - if ("streamID" in response[i]) { - if (response[i].UUID in session.rpcs) { - log("RTC already connected"); /// lets just say instead of Stream, we have - } else { - log(response[i].streamID); - var streamID = session.desaltStreamID(response[i].streamID); - if (session.queue){ - if (session.directorList.indexOf(response[i].UUID)>=0){ - warnlog("PLAYING DIRECTOR"); - play(streamID, response[i].UUID); - } else if (session.view_set && session.view_set.includes(streamID)){ - play(streamID, response[i].UUID); - } else if (session.queueList.length<5000){ - if ((!(streamID in session.watchTimeoutList)) && (!session.queueList.includes(streamID))){ - session.queueList.push(streamID); - } - } - } else { - log("STREAM ID DESALTED 3: " + streamID); - setTimeout(function(sid) { - play(sid); - }, (Math.floor(Math.random() * 100)), streamID); // add some furtherchance with up to 100ms added latency - } - } - } - } - } - } else { - for (var i in response) { - if ("UUID" in response[i]) { - if ("streamID" in response[i]) { - if (response[i].UUID in session.rpcs) { - log("RTC already connected"); /// lets just say instead of Stream, we have - } else { - log(response[i].streamID); - var streamID = session.desaltStreamID(response[i].streamID); - if (session.queue){ - if (session.directorList.indexOf(response[i].UUID)>=0){ - play(streamID, response[i].UUID); - } else if (session.view_set && session.view_set.includes(streamID)){ - play(streamID, response[i].UUID); - } else if (session.queueList.length<5000){ - if ((!(streamID in session.watchTimeoutList)) && (!session.queueList.includes(streamID))){ - session.queueList.push(streamID); - } - } - } else { - log("STREAM ID DESALTED 3: " + streamID); - play(streamID, response[i].UUID); // play handles the group room mechanics here - } - } - } - } - } - } - updateQueue(); - pokeIframeAPI("joined-room-complete"); - - if (session.include.length){ // we want to request what hasn't been requested already, since we are joining a room. - session.include.forEach(sid =>{ - if (sid in session.waitingWatchList){ - return; - } else { - session.watchStream(sid); - } - }); - } - - - }, function(error) { - return {}; - }); - } else { - log("Room name not long enough or contained all bad characaters"); - } -} - -async function createRoom(roomname = false) { - - if (roomname == false) { - roomname = getById("videoname1").value; - roomname = sanitizeRoomName(roomname); - - clearDirectorSettings(); - - if (roomname.length != 0) { - if (urlParams.has('dir')){ - updateURL("dir=" + roomname, true, false); // make the link reloadable. - } else { - updateURL("director=" + roomname, true, false); // make the link reloadable. - } - } - } - if (roomname.length == 0) { - //if (!(session.cleanOutput)) { - // warnUser("Please enter a room name before continuing"); - //} - - getById("videoname1").focus(); - getById("videoname1").classList.remove("shake"); - setTimeout(function(){getById("videoname1").classList.add("shake");},10); - - return; - } - log(roomname); - session.roomid = roomname; - - getById("dirroomid").innerHTML = decodeURIComponent(session.roomid); - getById("roomid").innerHTML = session.roomid; - - var passwordRoom = getById("passwordRoom").value; - passwordRoom = sanitizePassword(passwordRoom); - - var passAdd = ""; - var passAdd2 = ""; - - if (passwordRoom.length) { - - session.password = passwordRoom; - session.defaultPassword = false; - - if ((session.password === "false") || (session.password === "0") || (session.password === "off")){ - session.password = false; - if (urlParams.has('pass')) { - updateURL("pass=0"); - passAdd = "&pass=0"; - passAdd2 = "&pass=0"; - } else if (urlParams.has('pw')) { - updateURL("pw=0"); - passAdd = "&pw=0"; - passAdd2 = "&pw=0"; - } else if (urlParams.has('p')) { - updateURL("p=0"); - passAdd = "&p=0"; - passAdd2 = "&p=0"; - } else if (urlParams.has('password')) { - updateURL("password=false"); - passAdd = "&password=false"; - passAdd2 = "&password=false"; - } else { - updateURL("p=0"); - passAdd = "&p=0"; - passAdd2 = "&p=0"; - } - } else { - if (urlParams.has('pass')) { - updateURL("pass=" + session.password); - } else if (urlParams.has('pw')) { - updateURL("pw=" + session.password); - } else if (urlParams.has('p')) { - updateURL("p=" + session.password); - } else { - updateURL("password=" + session.password); - } - } - } - - await registerToken(); - - if ((session.defaultPassword === false) && (session.password)) { - passAdd2 = "&password=" + session.password; - return generateHash(session.password + session.salt, 4).then(async function(hash) { - passAdd = "&hash=" + hash; - await createRoomCallback(passAdd, passAdd2); - }).catch(errorlog); - } else if ((session.defaultPassword === false) && (session.password===false)){ - passAdd = "&p=0"; - passAdd2 = "&p=0"; - await createRoomCallback(passAdd, passAdd2); - } else { - await createRoomCallback(passAdd, passAdd2); - } - - - if (session.meshcast){ - if (!session.cleanOutput && !session.cleanDirector){ - document.getElementById("meshcastMenu").classList.remove("hidden"); - } - } - - pokeIframeAPI("create-room", roomname); -} - -function copyVideoFrameToClipboard(videoElement, e=false) { - try{ - var canvas = document.createElement("canvas"); - - canvas.width = videoElement.videoWidth; - canvas.height = videoElement.videoHeight; - - var ctx = canvas.getContext("2d"); - ctx.drawImage(videoElement, 0, 0); - - var img = new Image(); - img.src = canvas.toDataURL(); - - canvas.toBlob(function(blob) { - navigator.clipboard.write([new ClipboardItem({'image/png': blob})]); - }, 'image/png'); - - popupMessage(e, "Frame copied to clipboard as as PNG Image"); - } catch(e){ - errorlog(e); - } -} - -function saveVideoFrameToClipboard(videoElement, e=false) { - try{ - var canvas = document.createElement("canvas"); - - canvas.width = videoElement.videoWidth; - canvas.height = videoElement.videoHeight; - - var ctx = canvas.getContext("2d"); - ctx.drawImage(videoElement, 0, 0); - - var img = new Image(); - img.src = canvas.toDataURL(); - - canvas.toBlob(function(blob) { - var link = document.createElement("a"); - link.download = (videoElement.id||"video")+"_"+parseInt(performance.now())+".png"; - link.href = URL.createObjectURL(blob); - link.click(); - URL.revokeObjectURL(link.href); - }, 'image/png'); - - popupMessage(e, "Saving current frame to disk"); - } catch(e){ - errorlog(e); - } -} - - -async function checkDirectorStreamID(){ - if (session.directorStreamID){ - for (var UUID in session.rpcs){ - if (session.rpcs[UUID].streamID){ - var hashedSID = await generateHash(session.rpcs[UUID].streamID); - if (hashedSID===session.directorStreamID){ - session.directorUUID = UUID; // main director - session.directorList = []; - session.directorList.push(UUID); // approved co/directors - session.directorUUID = UUID; - session.newMainDirectorSetup(); - return; - } - } - } - for (var UUID in session.pcs){ - if (session.pcs[UUID].streamID){ - var hashedSID = await generateHash(session.pcs[UUID].streamID); - if (hashedSID===session.directorStreamID){ - session.directorList = []; - session.directorList.push(UUID); - session.directorUUID = UUID; - session.newMainDirectorSetup(); - return; - } - } - } - if (session.streamID == session.directorStreamID){ - session.directorState = true; - session.directorUUID = false; - pokeAPI("director", true); - pokeIframeAPI("director", true); - warnlog("You are joining with a token, but are the director?"); - } - session.directorList = []; - } -} - -async function checkToken(){ // this lets us use a server+password validation method for the director. - if (!session.token){return;} - if (!session.roomid){return;} - if (session.mainDirectorPassword){return;} - - try { - var request = new XMLHttpRequest(); - - var hashedRoom = session.roomid; - if (session.password){ - hashedRoom += session.password; - } - hashedRoom += "i^4&u#Fz5Eu#MsK^chF5*XAEYi1g"; - hashedRoom = await generateHash(hashedRoom); - hashedRoom = hashedRoom.slice(0, 50); - - request.open('GET', "https://tokens.vdo.ninja/?token="+session.token+"&room="+hashedRoom, false); - request.send(null); - - if (request.status === 200) { - try { - var result = JSON.parse(request.responseText); - if ("UUID" in result){ - session.directorUUID = result.UUID; - session.directorList = []; - session.directorList.push(session.directorUUID); - session.directorStreamID = false; - session.newMainDirectorSetup(); - } else if ("streamID" in result){ - session.directorStreamID = result.streamID; - checkDirectorStreamID(); - } - } catch(e){ - session.directorUUID = false; - session.directorStreamID = false; - session.directorList = []; - errorlog(e); - } - } else { - session.directorUUID = false; - session.directorStreamID = false; - session.directorList = []; - errorlog("Didn't get a token response"); - } - } catch(e){ - errorlog(e); - } -} - -async function registerToken(){ // this lets us use a server+password validation method for the director. - if (!session.roomid){return;} - if (!session.streamID){return;} - if (!session.mainDirectorPassword){return;} - - var longToken = session.mainDirectorPassword + "3wJVW^5qYU4DxGi6VhxN6RF04Q%$"; // this lets us use the same token across multiple rooms - var hashedToken = await generateHash(longToken); // keep it anonymous - hashedToken = hashedToken.slice(0, 50); - - var hashedRoom = session.roomid; - if (session.password){ - hashedRoom += session.password; - } - hashedRoom += "i^4&u#Fz5Eu#MsK^chF5*XAEYi1g"; - hashedRoom = await generateHash(hashedRoom); - hashedRoom = hashedRoom.slice(0, 50); - - var data2send = {}; - var hashedSID = await generateHash(session.streamID); - data2send.streamID = hashedSID; // not sure if there's a way around this. - data2send = JSON.stringify(data2send); - - var request = new XMLHttpRequest(); - request.open('POST', "https://tokens.vdo.ninja/?token="+hashedToken+"&room="+hashedRoom, false); - console.log("https://tokens.vdo.ninja/?token="+hashedToken+"&room="+hashedRoom); - request.send(data2send); - - if (request.status === 200) { - try { - if (request.responseText && (request.responseText.length===16)){ - session.token = request.responseText; - console.log("share token: "+session.token); - session.directorState = true; - pokeAPI("director", true); - pokeIframeAPI("director", true); - } - } catch(e){ - session.directorState = false; - pokeAPI("director", false); - pokeIframeAPI("director", false); - } - } else { - session.directorState = false; - pokeAPI("director", false); - pokeIframeAPI("director", false); - } -} - -function hideDirectorinvites(ele, skip=true) { - - if (getById("directorLinks2").style.display == "none") { - ele.innerHTML = ' LINKS (GUEST INVITES & SCENES)'; - getById("directorLinks2").style.display = "inline-block"; - getById("customizeLinks").classList.remove("hidden"); - } else { - ele.innerHTML = ' LINKS (GUEST INVITES & SCENES)' - getById("directorLinks2").style.display = "none"; - getById("help_directors_room").style.display = "none"; - getById("roomnotes2").style.display = "none"; - getById("customizeLinks").classList.add("hidden"); - } - if (getById("directorLinks1").style.display == "none") { - getById("directorLinks1").style.display = "inline-block"; - getById("customizeLinks").classList.remove("hidden"); - } else { - getById("directorLinks1").style.display = "none"; - getById("help_directors_room").style.display = "none"; - getById("roomnotes2").style.display = "none"; - getById("customizeLinks").classList.add("hidden"); - - } - if (skip){ - saveDirectorSettings(); - } -} - -function toggleCoDirector_changeurl(ele){ - session.codirector_changeURL = ele.checked; // doesn't do anything yet though. -} - -function toggleCoDirector_transfer(ele){ - session.codirector_transfer = ele.checked; -} - - - -async function toggleCoDirector(ele){ - //session.coDirectorAllowed = ele.checked; - if (!ele.checked){ - getById("codirectorSettings").style.display = "none"; - return; - } - if (!session.directorPassword){ - session.directorPassword = await promptAlt(getTranslation("enter-new-codirector-password"), false); - if (!session.directorPassword){ - session.directorPassword=false; - ele.checked=false; - return; - } - session.directorPassword = sanitizePassword(session.directorPassword) - } - updateURL("codirector="+session.directorPassword, true, false); - getById("coDirectorEnableSpan").style.display = "none"; - - await generateHash(session.directorPassword + session.salt + "abc123", 12).then(function(hash) { // million to one error. - log("dir room hash is " + hash); - session.directorHash = hash; - return; - }).catch(errorlog); - - if (session.codirector_transfer){ - getById("codirectorSettings_transfer").checked = true; - } else { - getById("codirectorSettings_transfer").checked = false; - } - if (session.codirector_changeURL){ - getById("codirectorSettings_changeurl").checked = true; - } else { - getById(codirectorSettings_changeurl).checked = false; - } - - var token = ""; - if (session.token){ - token+="&token="+session.token; - } - - getById("codirectorSettings_invite").value = "https://"+location.host+location.pathname+"?dir="+session.roomid+"&codirector="+session.directorPassword+token; - if (session.password!==session.sitePassword){ - if (session.password===false){ - getById("codirectorSettings_invite").value += "&password=false"; - } else{ - getById("codirectorSettings_invite").value += "&password="+session.password; - } - } - - getById("codirectorSettings").style.display = "block"; -} - - -async function toggleWidgetURL(ele){ - if (ele.id === "widgetURL"){ - ele = getById("widgetURCheck"); - } else if (!ele.checked){ - getById("widgetURL").classList.add("hidden"); - session.widget = false; - - var data = {}; - data.widgetSrc = false; - for (var UUID in session.pcs){ - if (session.pcs[UUID].allowWidget===true){ - session.sendMessage(data, UUID); - } - } - - if (session.director){ - let widget = document.getElementById("widget"); - if (widget){ - getById("widget").remove(); - getById("guestFeeds").style.width = "100%"; - } - } - pokeIframeAPI("widget-src", session.widget); - return; - } - var widget = await promptAlt(getTranslation("enter-url-for-widget"), false, false, session.widget); - if (widget!==null){ - session.widget = widget; - } - if (session.widget){ - getById("widgetURL").value = session.widget; - getById("widgetURL").classList.remove("hidden"); - updateMixer(); - } else { - session.widget = false; - getById("widgetURL").classList.add("hidden"); - ele.checked = false; - } - - var data = {}; - data.widgetSrc = session.widget; - for (var UUID in session.pcs){ - if (session.pcs[UUID].allowWidget===true){ - session.sendMessage(data, UUID); - } - } - - if (session.director){ - let widget = document.getElementById("widget"); - if (!widget){ - if (session.widget){ - widget = document.createElement("iframe"); - widget.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;display-capture;midi;"; - widget.id = "widget"; - widget.src = parseURL4Iframe(session.widget); - log(widget.src); - document.body.appendChild(widget); - getById("guestFeeds").style.width = "75%"; - } - } else if (session.widget){ - if (session.widget){ - widget.src = parseURL4Iframe(session.widget); - } else { - getById("widget").remove(); - getById("guestFeeds").style.width = "100%"; - } - } - } - - pokeIframeAPI("widget-src", session.widget); -} - -async function createRoomCallback(passAdd, passAdd2) { - - if (!session.switchMode){ - getById("directorlayout").classList.remove("hidden"); - getById("gridlayout").classList.add("hidden"); - } - - var broadcastFlag = getById("broadcastFlag"); - try { - if (broadcastFlag.checked) { - broadcastFlag = true; - } else { - broadcastFlag = false; - } - } catch (e) { - broadcastFlag = false; - } - - var broadcastString = ""; - if (broadcastFlag) { - broadcastString = "&broadcast"; - getById("broadcastSlider").checked = true; - } - - var wss = ""; - if (session.wssSetViaUrl){ - if (session.customWSS && (session.customWSS!==true)){ - wss = "&pie="+session.customWSS; - } else { - wss = "&wss="+session.wss; - } - } - - var queue = ""; - if (session.queue){ - queue = "&queue"; - getById("directorLinks2").style.opacity = "0.2"; - getById("directorLinks2").style.pointerEvents = "none"; - getById("directorLinks2").style.cursor = "not-allowed"; - } - - var showdirectorFlag = getById("showdirectorFlag"); - try { - if (showdirectorFlag.checked) { - showdirectorFlag = true; - } else { - showdirectorFlag = false; - } - } catch (e) { - showdirectorFlag = false; - } - - if (showdirectorFlag) { - updateURL("showdirector", true, false); - session.showDirector = session.showDirector || true; - //getById("broadcastSlider").checked=true; - } - - - var codecGroupFlag = getById("codecGroupFlag"); - - if (codecGroupFlag.value) { - if (codecGroupFlag.value === "vp9") { - codecGroupFlag = "&codec=vp9"; - getById("codech264toggle").disabled=true; - } else if (codecGroupFlag.value === "h264") { - codecGroupFlag = "&codec=h264"; - getById("codech264toggle").checked=true; - } else if (codecGroupFlag.value === "vp8") { - codecGroupFlag = "&codec=vp8"; - getById("codech264toggle").disabled=true; - } else if (codecGroupFlag.value === "av1") { - codecGroupFlag = "&codec=av1"; - getById("codech264toggle").disabled=true; - } else { - codecGroupFlag = ""; - } - } else { - codecGroupFlag = ""; - } - if (codecGroupFlag) { - session.codecGroupFlag = codecGroupFlag; - } - - if (session.bitrateGroupFlag){ - codecGroupFlag += session.bitrateGroupFlag; - } - - - formSubmitting = false; - try { - var m = getById("mainmenu"); - m.remove(); - document.querySelectorAll(".hidden2").forEach(ele2=>{ - ele2.classList.remove("hidden2"); - }); - } catch(e){} - - getById("head1").className = 'hidden'; - getById("head2").className = 'hidden'; - getById("head4").className = ''; - - try { - if (session.label === false) { - document.title = "Control Room"; - } - } catch (e) { - errorlog(e); - }; - - session.director = true; - screensharesupport = false; - - if (session.meterStyle ===false){ - session.meterStyle = 1; // director specific style - } - if (session.signalMeter===null){ - session.signalMeter = true; - } - if (session.batteryMeter===null){ - session.batteryMeter = true; - } - - if (session.directorPassword){ - getById("coDirectorEnable").checked = true; - getById("coDirectorEnableSpan").style.display = "none"; - - var token = ""; - if (session.token){ - token+="&token="+session.token; - } - - getById("codirectorSettings_invite").value = "https://"+location.host+location.pathname+"?dir="+session.roomid+"&codirector="+session.directorPassword+token; - if (session.password!==session.sitePassword){ - if (session.password==false){ - getById("codirectorSettings_invite").value += "&password=false"; - } else{ - getById("codirectorSettings_invite").value += "&password="+session.password; - } - } - - if (session.codirector_transfer){ - getById("codirectorSettings_transfer").checked = true; - } else { - getById("codirectorSettings_transfer").checked = false; - } - if (session.codirector_changeURL){ - getById("codirectorSettings_changeurl").checked = true; - } else { - getById("codirectorSettings_changeurl").checked = false; - } - getById("codirectorSettings").style.display = "block"; - } - - - window.onresize = updateMixer; - window.onorientationchange = function(){ - if (Firefox){ - updateForceRotate(true); - } - setTimeout(async function(){ - if (session.forceAspectRatio){ - await updateCameraConstraints("aspectRatio", session.forceAspectRatio); - } - if (session.effect && (session.effect === "7")){digitalZoom(true);} - updateForceRotate(); - updateMixer(); - }, 200); - }; - getById("reshare").parentNode.removeChild(getById("reshare")); - - //getById("mutespeakerbutton").style.display = null; - if (session.speakerMuted_default===false){ - //session.speakerMuted = false; // the director will start with audio playback muted. - toggleSpeakerMute(true); // let it be what it is. - } else { - session.speakerMuted = true; // the director will start with audio playback muted. - toggleSpeakerMute(true); // okay since only run on start - } - - var token = ""; - if (session.token){ - token+="&token="+session.token; - } - - if (session.cleanDirector == false && session.cleanOutput==false) { - - getById("roomHeader").style.display = ""; - //getById("directorLinks").style.display = ""; - getById("directorLinks1").style.display = "inline-block"; - getById("directorLinks2").style.display = "inline-block"; - - - getById("director_block_1").dataset.raw = "https://" + location.host + location.pathname + "?room=" + session.roomid + broadcastString + passAdd + wss + queue + token; - getById("director_block_1").href = "https://" + location.host + location.pathname + "?room=" + session.roomid + broadcastString + passAdd + wss + queue + token; - getById("director_block_1").innerText = "https://" + location.host + location.pathname + "?room=" + session.roomid + broadcastString + passAdd + wss + queue + token; - - - getById("director_block_3").dataset.raw = "https://" + location.host + location.pathname + "?scene&room=" + session.roomid + codecGroupFlag + passAdd2 + wss + token; - getById("director_block_3").href = "https://" + location.host + location.pathname + "?scene&room=" + session.roomid + codecGroupFlag + passAdd2 + wss + token; - getById("director_block_3").innerText = "https://" + location.host + location.pathname + "?scene&room=" + session.roomid + codecGroupFlag + passAdd2 + wss + token; - - getById("calendarButton").style.display = "inline-block"; - - } else { - getById("guestFeeds").innerHTML = ''; - } - getById("guestFeeds").style.display = ""; - - if (!(session.cleanOutput)) { - if (session.queue){ - getById("queuebutton").classList.remove("hidden"); - } - getById("chatbutton").classList.remove("hidden"); - getById('sharefilebutton').classList.remove("hidden"); // we won't override "display:none", if set, though. - getById("controlButtons").classList.remove("hidden"); - getById("mutespeakerbutton").classList.remove("hidden"); - getById("websitesharebutton").classList.remove("hidden"); - //getById("screensharebutton").classList.remove("hidden"); - - if (session.totalRoomBitrate){ - getById("roomsettingsbutton").classList.remove("hidden"); - } - - if (!session.showDirector) { // if null or false, we want to show the solo link, since the director won't have their control box. The director will be visible in their solo link - getById("miniPerformer").innerHTML = ''; - miniTranslate(getById("miniPerformer")); - getById("grabDirectorSoloLink").dataset.raw = "https://" + location.host + location.pathname + "?solo&r=" + session.roomid + "&v="+session.streamID + passAdd2 + wss + token; - getById("grabDirectorSoloLink").href = "https://" + location.host + location.pathname + "?solo&r=" + session.roomid + "&v="+session.streamID + passAdd2 + wss + token; - getById("grabDirectorSoloLink").innerText = "https://" + location.host + location.pathname + "?solo&r=" + session.roomid + "&v="+session.streamID + passAdd2 + wss + token; - getById("grabDirectorSoloLinkParent").classList.remove("hidden"); - } else { - getById("miniPerformer").innerHTML = ''; - } - getById("miniPerformer").className = ""; - - var tabindex = 26; - if (session.rooms && session.rooms.length > 0){ - var container = getById("rooms"); - container.innerHTML += 'Arm Transfer: '; - session.rooms.forEach(function (r) { - // if(session.roomid == r) return; //don't include self - container.innerHTML += ''; - tabindex++; - }); - } - - } else { - getById("miniPerformer").style.display = "none"; - getById("controlButtons").classList.add("hidden"); - } - - if (session.chatbutton === true) { - getById("chatbutton").classList.remove("hidden"); - getById("controlButtons").classList.remove("hidden"); - } else if (session.chatbutton === false) { - getById("chatbutton").classList.add("hidden"); - } - - if (session.effect===false){ - session.effect = null; // so the director can see the effects - } - - getById("avatarDiv3").classList.remove("hidden"); // lets the director see the avatar option - - clearInterval(session.updateLocalStatsInterval); - session.updateLocalStatsInterval = setInterval(function(){updateLocalStats();},session.statsInterval); - - var directorWebsiteShare = getStorage("directorWebsiteShare"); // {"website":session.iframeSrc, "roomid":session.roomid} - - if (typeof directorWebsiteShare === 'object' && directorWebsiteShare !== null && "website" in directorWebsiteShare){ - if (directorWebsiteShare.website == false){ - clearDirectorSettings(); - } else if (directorWebsiteShare.roomid && (directorWebsiteShare.roomid==session.roomid)){ - session.iframeSrc = directorWebsiteShare.website; - session.defaultIframeSrc = directorWebsiteShare.website; - - getById("websitesharebutton").classList.add("hidden"); - getById("websitesharebutton2").classList.remove("hidden"); - } - } - - session.group.forEach(group=>{ - // changeGroupDirectorAPI(group, state=null, update=true) - changeGroupDirectorAPI(group, true, false); // update the UI only - }); - - session.groupView.forEach(group=>{ - // changeGroupDirectorAPI(group, state=null, update=true) - changeGroupViewDirectorAPI(group, true); // update the UI only - }); - - if (session.showDirector){ - getById("highlightDirectorSpan").style.display = "none"; - getById("highlightDirectorSpan").remove(); - } else { - getById("highlightDirector").dataset.sid = session.streamID; - } - - setTimeout(function(){loadDirectorSettings();},100); - - joinRoom(session.roomid); - - try { - if (!gotDevices2AlreadyRan && (iOS || iPad)){ - await enumerateDevices().then(gotDevices2); // this is needed for iOS; was previous set to timeout at 100ms, but would be useful everywhere I think. (Breaks director's auto start, so just iOS for now) - } - }catch(e){ - errorlog(e); - } - - if (session.autostart){ - setTimeout(function(){press2talk(true);},400); - } else { - session.seeding=true; - session.seedStream(); - } -} // createRoomCallback - -function handleRoomSelect(room) { - var elems = document.querySelectorAll(".btnArmTransferRoom"); - [].forEach.call(elems, function(el) { - el.classList.remove("selected"); - }); - if (previousRoom == room) { - previousRoom = ""; - armedTransfer = false; - stillNeedRoom = true; - } else { - previousRoom = room; - stillNeedRoom = false; - armedTransfer = true; - getById("roomselect_" + room).classList.add('selected'); - } -} - -function getDirectorSettings(scene=false){ - var settings = {}; - - var eles = document.querySelectorAll('[data-action-type="solo-video"]'); - settings.soloVideo = false; - for (var i=0;ibiggestSlot){ - biggestSlot = parseInt(slots[i].dataset.slot); - } - if (slotDefault===parseInt(slots[i].dataset.slot)){ - slotDefault = null; - } - allSlots.push(parseInt(slots[i].dataset.slot)); - } - biggestSlot+=1; - } - if (slotDefault!==null){ - biggestSlot = slotDefault; - } else if (session.slotmode==1){ - var bestfree = 0 ; - for (var i =1; i<=biggestSlot; i++){ - if (allSlots.includes(i)){ - continue; - } else { - bestfree = i; - break; - } - } - biggestSlot = bestfree; - } - var slotName = "slot: "+biggestSlot; - if (!biggestSlot){ - slotName = "unset"; - } - - buttons += "
\ -
"; - - } - buttons += "
\ -
ID: " + session.streamID + "\ - \ - \ - "+getTranslation("add-a-label")+"\ -
\ -
"; - - container.innerHTML = buttons; - - var oldGroups = []; - document.querySelectorAll("#groups [data-action-type='toggle-group'][data-group]:not(.green)").forEach(ee=>{ - oldGroups.push(ee.dataset.group); - }); - getById("groups").remove(); - - if (session.hidesololinks==false){ // won't be updating the solo link to a view-only one ever, since director is always expected to be in a room - controls.innerHTML += "
\ - " + sanitizeChat(soloLink) + "\ - \ -
\ -
"; - if (session.directorUUID){ - controls.innerHTML += "

This is you, a co-director.
You are also a performer.

"; - } else { - controls.innerHTML += "

This is you, the director.
You are also a performer.

"; - } - } - - controls.querySelectorAll('[data-action-type]').forEach((ele) => { // give action buttons some self-reference - ele.dataset.sid = session.streamID; - }); - - container.appendChild(controls); - - getById("guestFeeds").appendChild(container); - - Object.keys(session.sceneList).forEach((scene, index) => { - if (document.getElementById("container_director")){ - if (!(getById("container_director").querySelectorAll('[data-scene="'+scene+'"]').length)){ - var newScene = document.createElement("div"); - newScene.innerHTML = ''; - newScene.classList.add("customScene"); - //getById("container_director").appendChild(newScene); - - var added = false; - getById("container_director").querySelectorAll('.customScene>[data-scene]').forEach(ele=>{ - if (!added && ele.dataset.scene>scene+""){ - ele.parentNode.parentNode.insertBefore(newScene, ele.parentNode); - added = true; - } - }); - if (!added){ - getById("container_director").appendChild(newScene); - } - - } - } - }); - - getById("groups").showDirector = true; - - session.group.forEach(group=>{ - // changeGroupDirectorAPI(group, state=null, update=true) - changeGroupDirectorAPI(group, true, false); // update the UI only / - }); - - oldGroups.forEach(group=>{ - // changeGroupDirectorAPI(group, state=null, update=true) - changeGroupDirectorAPI(group, false, false); // update the UI only / - }); - - - var labelID = document.getElementById("label_director"); - - labelID.onclick = async function(ee){ - var oldlabel = ee.target.innerText; - if (session.label===false){ - oldlabel = ""; - } - window.focus(); - var newlabel = await promptAlt(getTranslation("enter-new-display-name"), false, false, oldlabel); - if (newlabel!==null){ - newlabel = newlabel.trim(); - if (newlabel === ""){ - newlabel = false; - //ee.target.innerHTML = getTranslation("add-a-label"); - miniTranslate(ee.target,"add-a-label"); - ee.target.classList.add("addALabel"); - } else { - ee.target.innerText = newlabel; - ee.target.classList.remove("addALabel"); - } - session.label = newlabel; - var data = {}; - data.changeLabel = true; - data.value = session.label; - session.sendMessage(data); - } - } - labelID.style.float = "left"; - labelID.style.top = "2px"; - labelID.style.marginLeft = "5px"; - labelID.style.position = "relative"; - labelID.style.cursor="pointer"; - if (session.label){ - labelID.innerText = session.label; - } - pokeIframeAPI("control-box", true, true); - if (session.slotmode){ - pokeIframeAPI("slot-updated", biggestSlot, null, session.streamID); // need to support self-director - session.pastSlots[session.streamID] = biggestSlot; - - createSlotUpdate(); - } -} - -function createSlotUpdate(UUID=false){ - try { - var newSlots = {}; - document.querySelectorAll("[data--u-u-i-d][data-slot]").forEach(ele=>{ - newSlots[ele.dataset.slot] = ele.dataset.sid; - }); - if (!UUID){ - for (var uid in session.pcs){ - if (session.pcs[uid].layout){ - session.sendMessage({slotsUpdate:newSlots}, uid); - } - } - } else { - session.sendMessage({slotsUpdate:newSlots}, UUID); - } - } catch(e){ - errorlog(e); - } -} - -async function createDirectorScreenshareOnlyBox() { // sstype=3 - - var soloLink = soloLinkGenerator(session.streamID+":s"); - - if (document.getElementById("deleteme")) { - getById("deleteme").parentNode.removeChild(getById("deleteme")); - } - var controls = getById("controls_directors_blank").cloneNode(true); - controls.classList.remove("hidden"); - controls.id = "controls_screen_director"; - - var container = document.createElement("div"); - container.className = "vidcon directorMargins"; - container.id = "container_screen_director"; // needed to delete on user disconnect - - - var buttons = ""; - if (session.slotmode){ - var slots = document.querySelectorAll("div.slotsbar[data-slot]"); - var biggestSlot=0; - - var slotDefault = null; - if (session.streamID+":s" in session.pastSlots){ - slotDefault = session.pastSlots[session.streamID+":s"]; - } - var allSlots = []; - if (session.slotmode==1){ - for (var i=0;ibiggestSlot){ - biggestSlot = parseInt(slots[i].dataset.slot); - } - if (slotDefault===parseInt(slots[i].dataset.slot)){ - slotDefault = null; - } - allSlots.push(parseInt(slots[i].dataset.slot)); - } - biggestSlot+=1; - } - if (slotDefault!==null){ - biggestSlot = slotDefault; - } else if (session.slotmode==1){ - var bestfree = 0; - for (var i =1; i<=biggestSlot; i++){ - if (allSlots.includes(i)){ - continue; - } else { - bestfree = i; - break; - } - } - biggestSlot = bestfree; - } - - var slotName = "slot: "+biggestSlot; - if (!biggestSlot){ - slotName = "unset"; - } - - - buttons += "
\ -
"; - - } - buttons += "
\ -
ID: " + session.streamID+":s\ - \ - \ - "+getTranslation("add-a-label")+"\ -
\ -
"; - - container.innerHTML = buttons; - - var oldGroups = []; - document.querySelectorAll("#groups [data-action-type='toggle-group'][data-group]:not(.green)").forEach(ee=>{ - oldGroups.push(ee.dataset.group); - }); - getById("groups").remove(); - - if (session.hidesololinks==false){ // won't be updating the solo link to a view-only one ever, since director is always expected to be in a room - controls.innerHTML += "
\ - " + sanitizeChat(soloLink) + "\ - \ -
\ -
"; - if (session.directorUUID){ - controls.innerHTML += "

This is you, a co-director.
You are also a performer.

"; - } else if (session.showDirector===false){ - try { - controls.querySelectorAll('[data-action-type="addToScene"]').forEach((ele) => { ele.classList.add("hidden"); }); - } catch(e){errorlog(e);} - controls.innerHTML += "

This is your screen share
It's *not* a performer.

"; - } else { - controls.innerHTML += "

This your screen share.
It's also a performer.

"; - } - } - - controls.querySelectorAll('[data-action-type]').forEach((ele) => { // give action buttons some self-reference - ele.dataset.sid = session.streamID+":s" ; - }); - - container.appendChild(controls); - - getById("guestFeeds").appendChild(container); - - - - Object.keys(session.sceneList).forEach((scene, index) => { - if (document.getElementById("container_screen_director")){ - if (!(getById("container_screen_director").querySelectorAll('[data-scene="'+scene+'"]').length)){ - var newScene = document.createElement("div"); - newScene.innerHTML = ''; - newScene.classList.add("customScene"); - //getById("container_screen_director").appendChild(newScene); - - var added = false; - getById("container_screen_director").querySelectorAll('.customScene>[data-scene]').forEach(ele=>{ - if (!added && ele.dataset.scene>scene+""){ - ele.parentNode.parentNode.insertBefore(newScene, ele.parentNode); - added = true; - } - }); - if (!added){ - getById("container_screen_director").appendChild(newScene); - } - - } - } - }); - - getById("groups").showDirector = true; - - session.group.forEach(group=>{ - // changeGroupDirectorAPI(group, state=null, update=true) - changeGroupDirectorAPI(group, true, false); // update the UI only / - }); - - oldGroups.forEach(group=>{ - // changeGroupDirectorAPI(group, state=null, update=true) - changeGroupDirectorAPI(group, false, false); // update the UI only / - }); - - - var labelID = document.getElementById("label_director"); - - labelID.onclick = async function(ee){ - var oldlabel = ee.target.innerText; - if (session.label===false){ - oldlabel = ""; - } - window.focus(); - var newlabel = await promptAlt(getTranslation("enter-new-display-name"), false, false, oldlabel); - if (newlabel!==null){ - newlabel = newlabel.trim(); - if (newlabel === ""){ - newlabel = false; - //ee.target.innerHTML = getTranslation("add-a-label"); - miniTranslate(ee.target,"add-a-label"); - ee.target.classList.add("addALabel"); - } else { - ee.target.innerText = newlabel; - ee.target.classList.remove("addALabel"); - } - session.label = newlabel; - var data = {}; - data.changeLabel = true; - data.value = session.label; - session.sendMessage(data); - } - } - labelID.style.float = "left"; - labelID.style.top = "2px"; - labelID.style.marginLeft = "5px"; - labelID.style.position = "relative"; - labelID.style.cursor="pointer"; - if (session.label){ - labelID.innerText = session.label; - } - pokeIframeAPI("control-box", true, true); - if (session.slotmode){ - pokeIframeAPI("slot-updated", biggestSlot, null, session.streamID+":s"); // need to support self-director - session.pastSlots[session.streamID+":s"] = biggestSlot; - - createSlotUpdate(); - } -} - -function shiftPC(ele, shift, director=false){ - if (director){ - var target = document.getElementById("container_director"); - } else { - var target = document.getElementById("container_"+ele.dataset.UUID); - } - - if (!target){return;} - - target.shifted = true; - - var target2 = false; - - if (shift==1){ - if (target.nextSibling){ - target2 = target.nextSibling; - target.parentNode.insertBefore(target.nextSibling, target); - } - } else { - if (target.previousSibling){ - target2 = target.previousSibling; - target.parentNode.insertBefore(target, target.previousSibling); - } - } - updateLockedElements(); - - if (session.api){ - var slots = {} - var elements = getById("guestFeeds").children; - for (var i = 0;i"; - ele.parentNode.classList.add("locked"); - - while (currentPosition>parseInt(ele.dataset.locked)){ - var node = document.getElementById("container_"+UUID); - parent = node.parentNode, - prev = node.previousSibling, - oldChild = parent.removeChild(node); - parent.insertBefore( oldChild, prev ); - currentPosition = Array.prototype.indexOf.call(getById("guestFeeds").children, document.getElementById("container_"+UUID))+1; - } - - while ((currentPositioncurrentPosition)){ - var node = document.getElementById("container_"+UUID); - parent = node.parentNode, - next = node.nextSibling, - oldChild = parent.removeChild(node); - parent.insertBefore(node, next.nextSibling); - currentPosition = Array.prototype.indexOf.call(getById("guestFeeds").children, document.getElementById("container_"+UUID))+1; - } - } - } else { - ele.dataset.locked = 0; - ele.innerHTML = ""; - ele.parentNode.classList.remove("locked"); - } - } else { - if (ele.dataset.locked && parseInt(ele.dataset.locked)){ - ele.dataset.locked = 0; - ele.innerHTML = ""; - ele.parentNode.classList.remove("locked"); - } else { - if (getById("guestFeeds")){ - ele.dataset.locked = Array.prototype.indexOf.call(getById("guestFeeds").children, document.getElementById("container_"+UUID))+1; - ele.innerHTML = "#"+ele.dataset.locked+""; - ele.parentNode.classList.add("locked"); - } - } - } -} - -function allowDropSlot(event) { - event.preventDefault(); -} - -function dragSlot(event) { - log("drag"); - - var ele = event.target; - if (!ele.dataset.UUID && ele.parentNode.dataset.UUID){ - ele = ele.parentNode; - } - - event.dataTransfer.setDragImage( getById('dragImage'), 24, 24); - event.dataTransfer.setData("text", ele.dataset.UUID); - - var eles = document.querySelectorAll(".slotsbar"); - for (var i=0;i -
- × - ${message} -
-
-
`; - document.body.insertAdjacentHTML("beforeend", modalTemplate); // Insert modal at body end - - document.getElementById("modalBackdrop").addEventListener("click", closeModal); - - document.getElementById("alertModalMessage").querySelectorAll("div[data-slot]").forEach(choice=>{ - choice.onclick = function(){ - setSlot(ele, parseInt(this.dataset.slot)); - closeModal(); - }; - }); - - document.getElementById("alertModal").style.left = (event.pageX)+"px"; // 165px 356px - document.getElementById("alertModal").style.top = (event.pageY+180)+"px"; - - getById("alertModal").addEventListener("click", function(e) { - e.stopPropagation(); - return false; - }); - - } else { - var slot = await promptAlt("Which slot to change to?"); - setSlot(ele,slot); - } -} - -function setSlot(ele,slot){ - log("setSlot()"); - getById("slotPicker").classList.add("hidden"); - if (slot!==null){ - slot = (parseInt(slot) || 0); - var slots = document.querySelectorAll("div.slotsbar[data-slot]"); - for (var i=0;i streamID.toLowerCase()){ - getById("guestFeeds").insertBefore(container, getById("guestFeeds").children[i]); - added = true; - break; - } - } - } - - } - if (!added){ - getById("guestFeeds").appendChild(container); - } - } catch(e){ - getById("guestFeeds").appendChild(container); - } - } else { - getById("guestFeeds").appendChild(container); - } - - if (!session.rpcs[UUID].voiceMeter) { - if (session.meterStyle==1){ // director specific style - session.rpcs[UUID].voiceMeter = getById("voiceMeterTemplate2").cloneNode(true); - } else { - session.rpcs[UUID].voiceMeter = getById("voiceMeterTemplate").cloneNode(true); - session.rpcs[UUID].voiceMeter.style.opacity = 0; - if (session.meterStyle==2){ - session.rpcs[UUID].voiceMeter.classList.add("video-meter-2"); - session.rpcs[UUID].voiceMeter.classList.remove("video-meter"); - } else { - session.rpcs[UUID].voiceMeter.classList.add("video-meter-director"); - } - } - session.rpcs[UUID].voiceMeter.id = "voiceMeter_" + UUID; - session.rpcs[UUID].voiceMeter.dataset.level = 0; - session.rpcs[UUID].voiceMeter.classList.remove("hidden"); - } - - session.rpcs[UUID].remoteMuteElement = getById("muteStateTemplate").cloneNode(true); - session.rpcs[UUID].remoteMuteElement.id = ""; - session.rpcs[UUID].remoteMuteElement.style.top = "5px"; - session.rpcs[UUID].remoteMuteElement.style.right = "7px"; - - session.rpcs[UUID].remoteVideoMuteElement = getById("videoMuteStateTemplate").cloneNode(true); - session.rpcs[UUID].remoteVideoMuteElement.id = ""; - session.rpcs[UUID].remoteVideoMuteElement.style.top = "5px"; - session.rpcs[UUID].remoteVideoMuteElement.style.right = "28px"; - - session.rpcs[UUID].remoteRaisedHandElement = getById("raisedHandTemplate").cloneNode(true); - session.rpcs[UUID].remoteRaisedHandElement.id = ""; - session.rpcs[UUID].remoteRaisedHandElement.style.top = "5px"; - session.rpcs[UUID].remoteRaisedHandElement.style.right = "49px"; - - var videoContainer = document.createElement("div"); - videoContainer.id = "videoContainer_" + UUID; // needed to delete on user disconnect - videoContainer.style.margin = "0"; - videoContainer.style.position = "relative"; - videoContainer.style.minHeight = "30px"; - - var iframeDetails = document.createElement("div"); - iframeDetails.id = "iframeDetails_" + UUID; // needed to delete on user disconnect - iframeDetails.className = "iframeDetails hidden"; - - controls.innerHTML += ""; - controls.innerHTML += ""; - - var handsID = "hands_" + UUID; - - // controls.innerHTML += "
Links
"; //Seems to create an empty div. - - if (session.hidesololinks==false){ - controls.innerHTML += "
\ - " + sanitizeChat(soloLink) + "\ - \ -
"; - } - - controls.innerHTML += "\ -
"; - - - controls.innerHTML += "\ -
"; - - controls.querySelectorAll('[data-action-type]').forEach((ele) => { // give action buttons some self-reference - ele.dataset.UUID = UUID; - ele.dataset.sid = streamID; - }); - - var buttons = ""; - if (session.slotmode){ - var slots = document.querySelectorAll("div.slotsbar[data-slot]"); - var biggestSlot=0; - var slotDefault = null; - - if (slot_init && (session.slotmode==1)){ - slotDefault = slot_init || null; - } - - if (streamID in session.pastSlots){ - slotDefault = session.pastSlots[streamID]; - } - - var allSlots = []; - if (session.slotmode==1){ - for (var i=0;ibiggestSlot){ - biggestSlot = parseInt(slots[i].dataset.slot); - } - allSlots.push(parseInt(slots[i].dataset.slot)); - if (slotDefault===parseInt(slots[i].dataset.slot)){ // already taken - slotDefault = null; - } - } - biggestSlot+=1; - } - if (slotDefault!==null){ // the default slot is avialable - biggestSlot = slotDefault; - } else if (slot_init && (session.slotmode==1)){ // was manually set, so can't be something else but 0; 0 or false would still end up with 0 - biggestSlot = 0; - } else if (session.slotmode==1){ - var bestfree = 0; - for (var i =1; i<=biggestSlot; i++){ - if (allSlots.includes(i)){ - continue; - } else { - bestfree = i; - break; - } - } - biggestSlot = bestfree; - } - - var slotName = "slot: "+biggestSlot; - if (!biggestSlot){ - slotName = "unset"; - } - - buttons += "
\ -
"; - - } - buttons += "
ID: " + streamID + "\ - \ - \ - \ -
"; - - container.innerHTML = buttons; - updateLockedElements(); - - var videoContainerControlBox = document.createElement("div"); - videoContainerControlBox.className = "controlVideoBox"; - container.containerControlBox = videoContainerControlBox - - container.appendChild(videoContainerControlBox); - videoContainerControlBox.appendChild(videoContainer); - - if (session.signalMeter){ - if (!session.rpcs[UUID].signalMeter){ - session.rpcs[UUID].signalMeter = getById("signalMeterTemplate").cloneNode(true); - session.rpcs[UUID].signalMeter.id = "signalMeter_" + UUID; - session.rpcs[UUID].signalMeter.dataset.level = 0; - session.rpcs[UUID].signalMeter.classList.remove("hidden"); - session.rpcs[UUID].signalMeter.dataset.UUID = UUID; - session.rpcs[UUID].signalMeter.title = getTranslation("signal-meter"); - - if (session.rpcs[UUID].stats.info && session.rpcs[UUID].stats.info.cpuLimited){ // was quality_limitation_reason - session.rpcs[UUID].signalMeter.dataset.cpu = "1"; - } - - if (session.statsMenu !==false){ - session.rpcs[UUID].signalMeter.addEventListener('click', function(e) { // show stats of video if double clicked - log("clicked signal meter"); - try { - e.preventDefault(); - if (session.statsMenu !==false){ - var uid = e.currentTarget.dataset.UUID; - if ("stats" in session.rpcs[uid]){ - - var [menu, innerMenu] = statsMenuCreator(); - printViewStats(innerMenu, uid ); - menu.interval = setInterval(printViewStats, session.statsInterval, innerMenu, uid); - - } - } - e.stopPropagation(); - return false; - - } catch(e){errorlog(e);} - }); - } - } - videoContainer.appendChild(session.rpcs[UUID].signalMeter); - } - - if (session.batteryMeter){ - //////// - if (!session.rpcs[UUID].batteryMeter){ - session.rpcs[UUID].batteryMeter = getById("batteryMeterTemplate").cloneNode(true); - session.rpcs[UUID].batteryMeter.id = "batteryMeter_" + UUID; - /* - if (session.rpcs[UUID].stats.info && (session.rpcs[UUID].stats.info.power_level!==null)){ - var level = session.rpcs[UUID].batteryMeter.querySelector(".battery-level"); - if (level){ - var value = session.rpcs[UUID].stats.info.power_level; - if (value > 100){value = 100;} - else if (value < 0){ value = 0;} - level.style.height = parseInt(value)+"%"; - if (value<10){ - session.rpcs[UUID].batteryMeter.classList.add("alert"); - } else if (value<25){ - session.rpcs[UUID].batteryMeter.classList.add("warn"); - } - if (value<100){ - session.rpcs[UUID].batteryMeter.classList.remove("hidden"); - } - session.rpcs[UUID].batteryMeter.title = (Math.round(value*10)/10)+"% battery remaining"; - } - } - if (session.rpcs[UUID].stats.info && ("plugged_in" in session.rpcs[UUID].stats.info) && (session.rpcs[UUID].stats.info.plugged_in===false)){ - session.rpcs[UUID].batteryMeter.dataset.plugged = "0"; - session.rpcs[UUID].batteryMeter.classList.remove("hidden"); - } else { - session.rpcs[UUID].batteryMeter.dataset.plugged = "1"; - } - */ - batteryMeterInfoUpdate(UUID); - } - videoContainer.appendChild(session.rpcs[UUID].batteryMeter); - } - - if (session.showConnections){ - if (!session.rpcs[UUID].connectionDetails){ - createConnectionDetailsEle(UUID); - } - videoContainer.appendChild(session.rpcs[UUID].connectionDetails); - } - - videoContainer.appendChild(session.rpcs[UUID].voiceMeter); - videoContainer.appendChild(session.rpcs[UUID].remoteMuteElement); - videoContainer.appendChild(session.rpcs[UUID].remoteVideoMuteElement); - videoContainer.appendChild(session.rpcs[UUID].remoteRaisedHandElement); - videoContainer.appendChild(iframeDetails); - container.appendChild(controls); - - session.group.forEach(group=>{ - var ele = controls.querySelector('[data-action-type="toggle-group"][data--u-u-i-d="'+UUID+'"][data-group="'+group+'"]'); - if (!ele){ - var newGroup = htmlToElement(''); - - var added = false; - container.querySelectorAll('.customGroup>[data-group]').forEach(ele=>{ - log(ele); - if (!added && ele.dataset.group>group+""){ - ele.parentNode.insertBefore(newGroup, ele); - added = true; - } - }); - if (!added){ - var newGroupCon = container.querySelector(".customGroup"); - if (!newGroupCon){ - newGroupCon = document.createElement("div"); - newGroupCon.classList.add("customGroup"); - container.appendChild(newGroupCon); - } - newGroupCon.appendChild(newGroup); - } - } - }); - - initSceneList(UUID); - syncSceneState(streamID); - syncOtherState(streamID); - - pokeIframeAPI("control-box", true, UUID); - if (session.slotmode){ - pokeIframeAPI("slot-updated", biggestSlot, UUID); // need to support self-director - session.pastSlots[streamID] = biggestSlot; - - createSlotUpdate(); - } -} - - -function minimizeMe(button, director=false){ - if (!director){ - getById("container_"+button.dataset.UUID).classList.toggle("minimized"); - } else { - getById(director).classList.toggle("minimized"); - } -} -function cycleCameras(){ - if (session.screenShareState) { - warnUser("Stop the screen-share first."); - return; - } - var videoSelect = document.querySelector("select#videoSource3").options; - // don't show flip option if only one camera. - // don't show if not a mobile device - // don't show if AD=0 - - - var matched = false; - var maxIndex = parseInt(getById("flipcamerabutton").dataset.maxIndex) || parseInt(videoSelect.length); - if (maxIndex > parseInt(videoSelect.length)){ - maxIndex = parseInt(videoSelect.length); - } - - for(var i = 0; i < maxIndex; i++){ - var selOption = videoSelect[i]; - if (selOption.selected) { - matched=true; - } else if (matched){ - if (getById("flipcamerabutton").classList.contains("flip")){ - getById("flipcamerabutton").classList.remove("flip"); - getById("flipcamerabutton").classList.add("flip2"); - } else { - getById("flipcamerabutton").classList.remove("flip2"); - getById("flipcamerabutton").classList.add("flip"); - } - document.querySelector("select#videoSource3").value = selOption.value; - activatedPreview = false; - grabVideo(session.quality, "videosource", "select#videoSource3"); - return; - } - } - for(var i = 0; i < maxIndex; i++){ - var selOption = videoSelect[i]; - if (selOption.selected) { - return; // do nothing; the camera that is selected is the only camera available it seems. - } else { - if (getById("flipcamerabutton").classList.contains("flip")){ - getById("flipcamerabutton").classList.remove("flip"); - getById("flipcamerabutton").classList.add("flip2"); - } else { - getById("flipcamerabutton").classList.remove("flip2"); - getById("flipcamerabutton").classList.add("flip"); - } - document.querySelector("select#videoSource3").value = selOption.value; - activatedPreview = false; - grabVideo(session.quality, "videosource", "select#videoSource3"); - return; - } - } -} - - - -function addToGoogleCalendar(){ - var title = "Live Stream"; - //var dates = "20180512T230000Z/20180513T030000Z"; - var linkout = getById("director_block_1").innerText; - var details = "Join the live stream as a performer at the following link:

===> "+linkout+"

To test your connection and camera ahead of time, please visit https://vdo.ninja/speedtest

Do not share the details of this invite with others, unless explicitly told to."; - details = details.split(' ').join('+'); - details = details.split('&').join('%26'); - var linkToOpen = "https://calendar.google.com/calendar/r/eventedit?text="+title+"&details="+details; - //https://calendar.google.com/calendar/r/eventedit?text=My+Custom+Event&dates=20180512T230000Z/20180513T030000Z&details=For+details,+link+here:+https://example.com/tickets-43251101208&location=Garage+Boston+-+20+Linden+Street+-+Allston,+MA+02134 - - window.open(linkToOpen); - -} - -function addToOutlookCalendar(){ - var title = "Live Stream"; - var linkout = getById("director_block_1").innerText; - var details = "Join the live stream as a performer at the following link:

===> "+linkout+"

To test your connection and camera ahead of time, please visit https://vdo.ninja/speedtest

Do not share the details of this invite with others, unless explicitly told to."; - details = details.split(' ').join('%20'); - details = details.split('&').join('%26'); - - - var linkToOpen = "https://outlook.live.com/owa/?path=%2Fcalendar%2Faction%2Fcompose&rru=addevent&subject="+title+"&body="+details; - //https://calendar.google.com/calendar/r/eventedit?text=My+Custom+Event&dates=20180512T230000Z/20180513T030000Z&details=For+details,+link+here:+https://example.com/tickets-43251101208&location=Garage+Boston+-+20+Linden+Street+-+Allston,+MA+02134 - - window.open(linkToOpen); -} - -function addToYahooCalendar(){ - var title = "Live Stream"; - var linkout = getById("director_block_1").innerText; - var details = "Join the live stream as a performer at the following link:

===> "+linkout+"

To test your connection and camera ahead of time, please visit https://vdo.ninja/speedtest

Do not share the details of this invite with others, unless explicitly told to."; - details = details.split(' ').join('%20'); - details = details.split('&').join('%26'); - var linkToOpen = "https://calendar.yahoo.com?v60&title="+title+"&desc="+details; - //https://calendar.google.com/calendar/r/eventedit?text=My+Custom+Event&dates=20180512T230000Z/20180513T030000Z&details=For+details,+link+here:+https://example.com/tickets-43251101208&location=Garage+Boston+-+20+Linden+Street+-+Allston,+MA+02134 - - window.open(linkToOpen); -} - -function toggle(ele, tog = false, inline = true) { - var x = ele; - if (x.style.display === "none") { - if (inline) { - x.style.display = "inline-block"; - } else { - x.style.display = "block"; - } - } else { - x.style.display = "none"; - } - if (tog) { - if (tog.dataset.saved) { - tog.innerHTML = tog.dataset.saved; - delete(tog.dataset.saved); - } else { - tog.dataset.saved = tog.innerHTML; - tog.innerHTML = "Hide This"; - } - } -} - -function toggleByDataset(filter) { - var elements = document.querySelectorAll('[data-cluster="'+filter+'"]'); // ie: .cluster1 - for (var i = 0; i < elements.length; i++) { - elements[i].classList.toggle('hidden'); - } -} - - -var SelectedAudioOutputDevices = false; // session.sink -var SelectedAudioInputDevices = []; // .. -var SelectedVideoInputDevices = []; // .. - -async function enumerateDevices() { - - log("enumerated start"); - - if (typeof navigator.enumerateDevices === "function") { - log("enumerated failed 1"); - return await navigator.enumerateDevices(); - } else if (typeof navigator.mediaDevices === "object" && typeof navigator.mediaDevices.enumerateDevices === "function") { - return await navigator.mediaDevices.enumerateDevices(); - } else { - return await new Promise((resolve, reject) => { - try { - if (window.MediaStreamTrack == null || window.MediaStreamTrack.getSources == null) { - throw new Error(); - } - window.MediaStreamTrack.getSources((devices) => { - resolve(devices - .filter(device => { - return device.kind.toLowerCase() === "video" || device.kind.toLowerCase() === "videoinput"; - }) - .map(device => { - return { - deviceId: device.deviceId != null ? device.deviceId : "" - , groupId: device.groupId - , kind: "videoinput" - , label: device.label - , toJSON: /* istanbul ignore next */ function() { - return this; - } - }; - })); - }); - } catch (e) { - errorlog(e); - } - }); - } -} - -function requestOutputAudioStream() { - try { - //warnlog("GET USER MEDIA"); - return navigator.mediaDevices.getUserMedia({ - audio: true - , video: false - }).then(function(stream1) { // Apple needs thi to happen before I can access EnumerateDevices. - log("get media sources; request audio stream"); - return enumerateDevices().then(function(deviceInfos) { - stream1.getTracks().forEach(function(track) { // We don't want to keep it without audio; so we are going to try to add audio now. - track.stop(); // I need to do this after the enumeration step, else it breaks firefox's labels - }); - const audioOutputSelect = getById('outputSourceScreenshare'); - audioOutputSelect.remove(0); - audioOutputSelect.removeAttribute("onclick"); - - for (let i = 0; i !== deviceInfos.length; ++i) { - const deviceInfo = deviceInfos[i]; - if (deviceInfo == null) { - continue; - } - const option = document.createElement('option'); - option.value = deviceInfo.deviceId; - if (deviceInfo.kind === 'audiooutput') { - const option = document.createElement('option'); - if (audioOutputSelect.length === 0) { - option.dataset.default = true; - } else { - option.dataset.default = false; - } - option.value = deviceInfo.deviceId || "default"; - if (option.value == session.sink) { - option.selected = "true"; - } - option.text = deviceInfo.label || `Speaker ${audioOutputSelect.length + 1}`; - audioOutputSelect.appendChild(option); - } else { - log('Some other kind of source/device: ', deviceInfo); - } - } - }); - }); - } catch (e) { - if (!(session.cleanOutput)) { - if (window.isSecureContext) { - warnUser("An error has occured when trying to access the default audio device. The reason is not known."); - } else if (iOS || iPad) { - warnUser("iOS version 13.4 and up is generally recommended; older than iOS 11 is not supported."); - } else { - warnUser("Error acessing the default audio device.\n\nThe website may be loaded in an insecure context.\n\nPlease see: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia"); - } - } - } -} - - -async function requestAudioStream() { - try { - //warnlog("GET USER MEDIA"); - return await navigator.mediaDevices.getUserMedia({ - audio: true - , video: false - }).then(async function(stream1) { // Apple needs thi to happen before I can access EnumerateDevices. - log("get media sources; request audio stream"); - return await enumerateDevices().then(function async(deviceInfos) { - stream1.getTracks().forEach(function(track) { // We don't want to keep it without audio; so we are going to try to add audio now. - track.stop(); // I need to do this after the enumeration step, else it breaks firefox's labels - }); - log("updating audio"); - const audioInputSelect = getById('audioSourceScreenshare'); - audioInputSelect.remove(1); - audioInputSelect.removeAttribute("onchange"); - - - for (let i = 0; i !== deviceInfos.length; ++i) { - const deviceInfo = deviceInfos[i]; - if (deviceInfo == null) { - continue; - } - const option = document.createElement('option'); - option.value = deviceInfo.deviceId; - if (deviceInfo.kind === 'audioinput') { - option.text = deviceInfo.label || `Microphone ${audioInputSelect.length + 1}`; - audioInputSelect.appendChild(option); - } else { - log('Some other kind of source/device: ', deviceInfo); - } - } - audioInputSelect.style.minHeight = ((audioInputSelect.childElementCount + 1) * 1.15 * 16) + 'px'; - audioInputSelect.style.minWidth = "342px"; - - if (session.audioDevice && (typeof session.audioDevice === "object") && session.audioDevice.length){ - for (let i = 0; i !== audioInputSelect.length; ++i) { - let deviceInfo = audioInputSelect[i]; - if (session.audioDevice.includes(deviceInfo.value)){ - deviceInfo.selected = true; - } else if ((deviceInfo.innerText.replace(/[\W]+/g, "_").toLowerCase().startsWith(session.audioDevice))) { - deviceInfo.selected = true; - } else if ((deviceInfo.innerText.replace(/[\W]+/g, "_").toLowerCase().includes(session.audioDevice))) { - deviceInfo.selected = true; - } - } - } - - }); - }); - } catch (e) { - if (!(session.cleanOutput)) { - if (window.isSecureContext) { - warnUser("An error has occured when trying to access the default audio device. The reason is not known."); - } else if (iOS || iPad) { - warnUser("iOS version 13.4 and up is generally recommended; older than iOS 11 is not supported."); - } else { - warnUser("Error acessing the default audio device.\n\nThe website may be loaded in an insecure context.\n\nPlease see: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia"); - } - } - } -} - -function saveSettings(){ - if (session.store){ - try { - var tmp = {}; - if (SelectedAudioInputDevices){ - tmp.SelectedAudioInputDevices = SelectedAudioInputDevices.filter(n => n); - } - if (session.sink && (session.sink!="default")){ - tmp.SelectedAudioOutputDevices = session.sink; - } else if (!session.sink && SelectedAudioOutputDevices && (SelectedAudioOutputDevices!="default")){ - tmp.SelectedAudioOutputDevices = SelectedAudioOutputDevices; - } - tmp.SelectedVideoInputDevices = SelectedVideoInputDevices; - setStorage("session_store", JSON.stringify(tmp)); - log("Saving settings"); - } catch(e){errorlog(e);} - } -} - -function loadSettings(){ - if (session.store){ - try { - session.store = getStorage("session_store"); - if (session.store){ - session.store = JSON.parse(session.store); - } else { - session.store = {}; - } - - if (session.store && session.store.SelectedAudioOutputDevices){ - if (typeof session.store.SelectedAudioOutputDevices == "string"){ - SelectedAudioOutputDevices = session.store.SelectedAudioOutputDevices; - } else if (typeof session.store.SelectedAudioOutputDevices == "object"){ - if (session.store.SelectedAudioOutputDevices.length){ - SelectedAudioOutputDevices = session.store.SelectedAudioOutputDevices[0]; - } - } - } - if (session.store && session.store.SelectedAudioInputDevices){ - session.store.SelectedAudioInputDevices = session.store.SelectedAudioInputDevices.filter(n => n); - SelectedAudioInputDevices = session.store.SelectedAudioInputDevices; - } - if (session.store && session.store.SelectedVideoInputDevices){ - SelectedVideoInputDevices = session.store.SelectedVideoInputDevices; - } - } catch(e){} - } -} - -function gotDevices(deviceInfos, miconly=false) { - - log("got devices!1"); - log(deviceInfos); - try { - - if (Firefox && !FirefoxEnumerated){ - if (session.streamSrc && session.streamSrc.getTracks().length){ - FirefoxEnumerated=true; - } - } - - var option = document.createElement('input'); - option.type = "checkbox"; - option.value = "ZZZ"; - option.name = "multiselect1"; - option.id = "multiselect1"; - option.style.display = "none"; - option.checked = true; - - - var label = document.createElement('label'); - label.for = option.name; - label.innerHTML = ' No Audio'; - - var listele = document.createElement('li'); - listele.appendChild(option); - listele.appendChild(label); - - const audioInputSelect = document.getElementById('audioSource') || document.getElementById('audioSource3'); - audioInputSelect.innerHTML = ""; - audioInputSelect.appendChild(listele); - - const audioOutputSelect = document.getElementById('outputSource') || document.getElementById('outputSource3'); - audioOutputSelect.innerHTML = ""; - - option.onchange = function(event) { // make sure to clear 'no audio option' if anything else is selected - if (!(getById("multiselect1").checked)){ - getById("multiselect1").checked = true; - } else { - var list = audioInputSelect.querySelectorAll("li>input"); - for (var i = 0; i < list.length; i++) { - if (list[i].id !== "multiselect1") { - list[i].checked = false; - } - } - } - SelectedAudioInputDevices = [event.currentTarget.value]; - saveSettings(); - }; - - const multiselectTrigger = document.getElementById('multiselect-trigger') || document.getElementById('multiselect-trigger3'); - multiselectTrigger.dataset.state = '0'; - multiselectTrigger.classList.add('closed'); - multiselectTrigger.classList.remove('open'); - getById('chevarrow1').classList.add('bottom'); - - const videoSelect = document.getElementById('videoSourceSelect') || document.getElementById('videoSource3'); - const selectors = [videoSelect]; - - const values = selectors.map(select => select.value); - selectors.forEach(select => { - while (select.firstChild) { - select.removeChild(select.firstChild); - } - }); - - - function comp(a, b) { - if (a.kind === 'audioinput') { - return 0; - } else if (a.kind === 'audiooutput') { - return 0; - } - const labelA = a.label.toUpperCase(); - const labelB = b.label.toUpperCase(); - if (labelA > labelB) { - return 1; - } else if (labelA < labelB) { - return -1; - } - return 0; - } - //deviceInfos.sort(comp); // I like this idea, but it messes with the defaults. I just don't know what it will do. - var deviceInfo; - - // This is to hide NDI from default device. NDI Tools fucks up. - var tmp = []; - for (let i = 0; i !== deviceInfos.length; ++i) { - deviceInfo = deviceInfos[i]; - if (!((deviceInfo.kind === 'videoinput') && (deviceInfo.label.toLowerCase().startsWith("ndi") || deviceInfo.label.toLowerCase().startsWith("newtek")))) { - tmp.push(deviceInfo); - } - } - - for (let i = 0; i !== deviceInfos.length; ++i) { - deviceInfo = deviceInfos[i]; - if ((deviceInfo.kind === 'videoinput') && (deviceInfo.label.toLowerCase().startsWith("ndi") || deviceInfo.label.toLowerCase().startsWith("newtek"))) { - tmp.push(deviceInfo); - log("V DEVICE FOUND = " + deviceInfo.label.replace(/[\W]+/g, "_").toLowerCase()); - } - } - deviceInfos = tmp; - - if (typeof session.audioDevice == "object") { // this sorts according to users's manual selection - var matched = []; - var notmatched = []; - for (let i = 0; i !== deviceInfos.length; ++i) { - if (deviceInfos[i].kind === 'audioinput'){ - if (session.audioDevice.includes(deviceInfos[i].deviceId)) { - matched.push(deviceInfos[i]); - } else { - for (var j=0;j { - if (Array.prototype.slice.call(select.childNodes).some(n => n.value === values[selectorIndex])) { - select.value = values[selectorIndex]; - } - }); - - } catch (e) { - errorlog(e); - } -} - - -function getUserMediaVideoParams(resolutionFallbackLevel, isSafariBrowser) { - switch (resolutionFallbackLevel) { - case -1: - return {}; - case 0: - if (isSafariBrowser) { - return { - width: { - min: 360 - , ideal: 1920 - , max: 1920 - } - , height: { - min: 360 - , ideal: 1080 - , max: 1080 - } - }; - } else if (Firefox){ - return { - width: { - ideal: 1920 - } - , height: { - ideal: 1080 - } - }; - } else { - return { - width: { - min: 720 - , ideal: 1920 - , max: 1920 - } - , height: { - min: 720 - , ideal: 1080 - , max: 1920 - } - }; - } - case 1: - if (isSafariBrowser) { - return { - width: { - min: 360 - , ideal: 1280 - , max: 1280 - } - , height: { - min: 360 - , ideal: 720 - , max: 720 - } - }; - } else if (Firefox){ - return { - width: { - ideal: 1280 - } - , height: { - ideal: 720 - } - }; - } else { - return { - width: { - min: 720 - , ideal: 1280 - , max: 1280 - } - , height: { - min: 720 - , ideal: 720 - , max: 1280 - } - }; - } - case 2: - if (isSafariBrowser) { - return { - width: { - min: 640 - } - , height: { - min: 360 - } - }; - } else if (Firefox){ - return { - width: { - ideal: 640 - } - , height: { - ideal: 360 - } - }; - } else { - return { - width: { - min: 240 - , ideal: 640 - , max: 1280 - } - , height: { - min: 240 - , ideal: 360 - , max: 1280 - } - }; - } - case 3: - if (isSafariBrowser) { - return { - width: { - min: 360 - , ideal: 1280 - , max: 1440 - } - }; - } else { - return { - width: { - min: 360 - , ideal: 1280 - , max: 1440 - } - }; - } - case 4: - if (isSafariBrowser) { - return { - height: { - min: 360 - , ideal: 720 - , max: 960 - } - }; - } else { - return { - height: { - ideal: 720 - , max: 960 - } - }; - } - case 5: - if (isSafariBrowser) { - return { - width: { - min: 360 - , ideal: 640 - , max: 1440 - } - , height: { - min: 360 - , ideal: 360 - , max: 720 - } - }; - } else { - return { - width: { - ideal: 640 - , max: 1920 - } - , height: { - ideal: 360 - , max: 1920 - } - }; // same as default, but I didn't want to mess with frameRates until I gave it all a try first - } - case 6: - if (isSafariBrowser) { - return {}; // iphone users probably don't need to wait any longer, so let them just get to it - } else { - return { - width: { - min: 360 - , ideal: 640 - , max: 3840 - } - , height: { - min: 360 - , ideal: 360 - , max: 2160 - } - }; - - } - case 7: - return { // If the camera is recording in low-light, it may have a low frameRate. It coudl also be recording at a very high resolution. - width: { - min: 360 - , ideal: 640 - } - , height: { - min: 360 - , ideal: 360 - } - , }; - - case 8: - return { - width: { - min: 360 - } - , height: { - min: 360 - } - , frameRate: 10 - }; // same as default, but I didn't want to mess with frameRates until I gave it all a try first - case 9: - return { - frameRate: 0 - }; // Some Samsung Devices report they can only support a frameRate of 0. - case 10: - return {} - default: - return {}; - } -} - -function addScreenDevices(device) { - if (device.kind == "audio") { - const audioInputSelect = getById('audioSource3'); - const listele = document.createElement('li'); - listele.style.display = "block"; - - const option = document.createElement('input'); - option.type = "checkbox"; - option.checked = true; - - if (getById('multiselect-trigger3').dataset.state == 0) { - option.style.display = "none"; - } - - option.value = device.id; - option.name = device.label; - option.dataset.type = "screen"; - option.label = device.label; - - const label = document.createElement('label'); - label.for = option.name; - label.innerHTML = " " + device.label; - listele.appendChild(option); - listele.appendChild(label); - - option.onchange = function(event) { // make sure to clear 'no audio option' if anything else is selected - log("change 4644"); - if (!CtrlPressed) { - document.querySelectorAll("#audioSource3 input[type='checkbox']").forEach(function(item) { - if (!item.value){return;} - if (event.currentTarget.value !== item.value) { // this shoulnd't happen, but if it does. - - item.checked = false; - - if (item.dataset.type == "screen") { - item.parentElement.parentElement.removeChild(item.parentElement); - } - - while (SelectedAudioInputDevices.indexOf(item.value) > -1) { - SelectedAudioInputDevices.splice(SelectedAudioInputDevices.indexOf(item.value), 1); - } - - activatedPreview = false; - grabAudio("#audioSource3"); // exclude item.id - - } else { - if (SelectedAudioInputDevices.indexOf(item.value) == -1){ - if (SelectedAudioInputDevices.length && SelectedAudioInputDevices.includes("ZZZ")){ - SelectedAudioInputDevices = []; - } - SelectedAudioInputDevices.push(item.value); - } - - item.checked = true; - activatedPreview = false; - grabAudio("#audioSource3", item.value); // exclude item.id. we will reconnect, even if already connected, as a way to 'reset' a device if it isn't working. - } - }); - } - saveSettings(); - event.stopPropagation(); - return false; - }; - audioInputSelect.appendChild(listele); - getById("audioSourceNoAudio2").checked = false; - - } else if (device.kind == "video") { - const videoSelect = getById('videoSource3'); - //const selectors = [ videoSelect]; - //const values = selectors.map(select => select.value); - const option = document.createElement('option'); - option.value = device.id; - option.text = device.label; - option.selected = "true"; - option.label = device.label; - videoSelect.appendChild(option); - } -} - -var gotDevices2AlreadyRan = false; -function gotDevices2(deviceInfos) { - gotDevices2AlreadyRan=true; - log("got devices!2"); - log(deviceInfos); - getById("multiselect-trigger3").dataset.state = "0"; - getById("multiselect-trigger3").classList.add('closed'); - getById("multiselect-trigger3").classList.remove('open'); - getById("chevarrow2").classList.add('bottom'); - - if (!session.streamSrc){ - checkBasicStreamsExist(); - } - - var knownTrack = false; - - try { - const audioInputSelect = getById('audioSource3'); - const videoSelect = getById('videoSource3'); - const audioOutputSelect = getById('outputSource3'); - const selectors = [videoSelect]; - - [audioInputSelect].forEach(select => { - while (select.firstChild) { - select.removeChild(select.firstChild); - } - }); - - const values = selectors.map(select => select.value); - selectors.forEach(select => { - while (select.firstChild) { - select.removeChild(select.firstChild); - } - }); - - [audioOutputSelect].forEach(select => { - while (select.firstChild) { - select.removeChild(select.firstChild); - } - }); - - var counter = 0; - for (let i = 0; i !== deviceInfos.length; ++i) { - const deviceInfo = deviceInfos[i]; - if (deviceInfo == null) { - continue; - } - - if (deviceInfo.kind === 'audioinput') { - var option = document.createElement('input'); - option.type = "checkbox"; - counter++; - var listele = document.createElement('li'); - listele.style.display = "none"; - - session.streamSrc.getAudioTracks().forEach(function(track) { - if (deviceInfo.label == track.label) { - option.checked = true; - listele.style.display = "inherit"; - } - }); - - option.style.display = "none" - option.value = deviceInfo.deviceId || "default"; - option.name = "multiselecta" + counter; - option.id = "multiselecta" + counter; - option.dataset.label = deviceInfo.label || ("microphone " + ((audioInputSelect.length || 0) + 1)); - - var label = document.createElement('label'); - label.for = option.name; - - label.innerHTML = " " + (deviceInfo.label || ("microphone " + ((audioInputSelect.length || 0) + 1))); - - listele.appendChild(option); - listele.appendChild(label); - audioInputSelect.appendChild(listele); - - option.onchange = function(event) { // make sure to clear 'no audio option' if anything else is selected - log("change 4768"); - if (!(CtrlPressed)) { - document.querySelectorAll("#audioSource3 input[type='checkbox']").forEach(function(item) { - if (event.currentTarget.value !== item.value) { - item.checked = false; - if (item.dataset.type == "screen") { - item.parentElement.parentElement.removeChild(item.parentElement); - } - while (SelectedAudioInputDevices.indexOf(item.value) > -1) { - SelectedAudioInputDevices.splice(SelectedAudioInputDevices.indexOf(item.value), 1); - } - } else { - item.checked = true; - if (SelectedAudioInputDevices.indexOf(event.currentTarget.value) == -1){ - if (SelectedAudioInputDevices.length && SelectedAudioInputDevices.includes("ZZZ")){ - SelectedAudioInputDevices = []; - } - SelectedAudioInputDevices.push(event.currentTarget.value); - } - } - }); - } else { - - if (SelectedAudioInputDevices.indexOf(event.currentTarget.value) == -1){ - if (SelectedAudioInputDevices.length && SelectedAudioInputDevices.includes("ZZZ")){ - SelectedAudioInputDevices = []; - } - SelectedAudioInputDevices.push(event.currentTarget.value); - } - - getById("audioSourceNoAudio2").checked = false; - } - saveSettings(); - }; - - } else if (deviceInfo.kind === 'videoinput') { - var option = document.createElement('option'); - option.value = deviceInfo.deviceId || "default"; - option.text = deviceInfo.label || `camera ${videoSelect.length + 1}`; - try { - if (!knownTrack && session.canvasSource){ - session.canvasSource.srcObject.getVideoTracks().forEach(function(track) { - if (option.text == track.label) { - option.selected = "true"; - knownTrack = true; - } - }); - - } - if (!knownTrack && session.streamSrc){ - session.streamSrc.getVideoTracks().forEach(function(track) { - if (option.text == track.label) { - option.selected = "true"; - knownTrack = true; - } - }); - } - } catch (e) { - errorlog(e); - } - videoSelect.appendChild(option); - - } else if (deviceInfo.kind === 'audiooutput') { - var option = document.createElement('option'); - if (audioOutputSelect.length === 0) { - option.dataset.default = true; - } else { - option.dataset.default = false; - } - option.value = deviceInfo.deviceId || "default"; - if (option.value == session.sink) { - option.selected = "true"; - } else if (!session.sink && SelectedAudioOutputDevices && (SelectedAudioOutputDevices == option.value)){ - option.selected = "true"; - session.sink = option.value; // added 8-dec-22, as the director's saved mic wasn't applying otherwise. - } - option.text = deviceInfo.label || `Speaker ${audioOutputSelect.length + 1}`; - audioOutputSelect.appendChild(option); - - } else { - log('Some other kind of source/device: ', deviceInfo); - } - } - - if (Firefox && !session.mobile){ - var option = document.createElement('option'); - option.value = "others"; - option.text = "Show more options"; - audioOutputSelect.appendChild(option); - } - - - if (audioOutputSelect.childNodes.length == 0) { - var option = document.createElement('option'); - option.value = "default"; - option.text = "System Default"; - audioOutputSelect.appendChild(option); - } - - if (videoSelect.childNodes.length <= 1) { - getById("flipcamerabutton").style.display = "none"; // don't show the camera cycle button - getById("flipcamerabutton").dataset.maxndex = videoSelect.childNodes.length; - } else { - getById("flipcamerabutton").style.display = "unset"; - getById("flipcamerabutton").dataset.maxIndex = videoSelect.childNodes.length; - } - - //////////// - session.streamSrc.getAudioTracks().forEach(function(track) { // add active ScreenShare audio tracks to the list - log("Checking for screenshare audio"); - var matched = false; - for (var i = 0; i !== deviceInfos.length; ++i) { - var deviceInfo = deviceInfos[i]; - if (deviceInfo == null) { - continue; - } - log("---"); - if (track.label == deviceInfo.label) { - matched = true; - continue; - } - } - if (matched == false) { // Not a gUM device - var listele = document.createElement('li'); - listele.style.display = "block"; - var option = document.createElement('input'); - option.type = "checkbox"; - option.value = track.id; - option.checked = true; - option.style.display = "none"; - option.name = track.label; - option.label = track.label; - option.dataset.type = "screen"; - var label = document.createElement('label'); - label.for = option.name; - label.innerHTML = " " + track.label; - listele.appendChild(option); - listele.appendChild(label); - option.onchange = function(event) { // make sure to clear 'no audio option' if anything else is selected - log("change 4873"); - var trackid = null; - if (!(CtrlPressed)) { - - document.querySelectorAll("#audioSource3 input[type='checkbox']").forEach(function(item) { - if (event.currentTarget.value !== item.value) { // this shoulnd't happen, but if it does. - item.checked = false; - if (item.dataset.type == "screen") { - item.parentElement.parentElement.removeChild(item.parentElement); - } - } else { - event.currentTarget.checked = true; - trackid = item.value; - } - }); - } else { - //getById("audioSourceNoAudio2").checked=false; - if (event.currentTarget.dataset.type == "screen") { - event.currentTarget.parentElement.parentElement.removeChild(event.currentTarget.parentElement); - } - } - activatedPreview = false; - grabAudio("#audioSource3", trackid); // exclude item.id. - event.stopPropagation(); - return false; - }; - audioInputSelect.appendChild(listele); - } - }); - - /////////// no video option - var optionss = false; - if (screensharesupport) { - optionss = document.createElement('option'); - optionss.text = "Screen Share (replace camera)"; - optionss.value = "XXX"; - videoSelect.appendChild(optionss); // NO AUDIO OPTION - } - - var option = document.createElement('option'); // no video - option.text = "Disable Video"; - option.value = "ZZZ"; - videoSelect.appendChild(option); - - if (session.streamSrc.getVideoTracks().length == 0) { - option.selected = "true"; - } else if (knownTrack == false) { - var option = document.createElement('option'); // no video - option.text = session.streamSrc.getVideoTracks()[0].label; - option.value = "YYY"; - videoSelect.appendChild(option); - option.selected = "true"; - } - - if (optionss) { - optionss.lastSelected = videoSelect.selectedIndex; - } - - - videoSelect.onchange = function(event) { - try { - if (event.target.options[event.target.options.selectedIndex].value === "XXX") { - - videoSelect.selectedIndex = event.target.options[event.target.options.selectedIndex].lastSelected; - if (session.screenShareState == false) { - toggleScreenShare(); - } else { - toggleScreenShare(true); - } - return; - } - } catch (e) {} - activatedPreview = false; - grabVideo(session.quality, "videosource", "select#videoSource3"); - - if (!(getById('audioSource3').querySelectorAll("input[data-type='screen']").length)){ - if (session.screenShareState){ - session.screenShareState = false; - pokeIframeAPI('screen-share-state', session.screenShareState, null, session.streamID); - notifyOfScreenShare(); - //session.refreshScale(); - } - getById("screensharebutton").classList.remove("green"); - getById("screensharebutton").ariaPressed = "false"; - } - - }; - - ///////////// /// NO AUDIO appended option - - - var option = document.createElement('input'); - option.type = "checkbox"; - option.value = "ZZZ"; - option.style.display = "none" - option.id = "audioSourceNoAudio2"; - - var label = document.createElement('label'); - label.for = option.name; - label.innerHTML = " No Audio"; - var listele = document.createElement('li'); - - if (session.streamSrc.getAudioTracks().length == 0) { - option.checked = true; - } else { - listele.style.display = "none"; - option.checked = false; - } - option.onchange = function(event) { // make sure to clear 'no audio option' if anything else is selected - log("change 4938"); - if (!(CtrlPressed)) { - document.querySelectorAll("#audioSource3 input[type='checkbox']").forEach(function(item) { - if (event.currentTarget.value !== item.value) { - item.checked = false; - if (item.dataset.type == "screen") { - item.parentElement.parentElement.removeChild(item.parentElement); - } - - while (SelectedAudioInputDevices.indexOf(item.value) > -1) { - SelectedAudioInputDevices.splice(SelectedAudioInputDevices.indexOf(item.value), 1); - } - } else { - item.checked = true; - if (SelectedAudioInputDevices.indexOf(event.currentTarget.value) == -1){ - if (SelectedAudioInputDevices.length && SelectedAudioInputDevices.includes("ZZZ")){ - SelectedAudioInputDevices = []; - } - SelectedAudioInputDevices.push(event.currentTarget.value); - } - } - }); - } else { - document.querySelectorAll("#audioSource3 input[type='checkbox']").forEach(function(item) { - if (event.currentTarget.value === item.value) { - event.currentTarget.checked = true; - if (SelectedAudioInputDevices.indexOf(event.currentTarget.value) == -1){ - if (SelectedAudioInputDevices.length && SelectedAudioInputDevices.includes("ZZZ")){ - SelectedAudioInputDevices = []; - } - SelectedAudioInputDevices.push(event.currentTarget.value); - } - } else { - item.checked = false; - if (item.dataset.type == "screen") { - item.parentElement.parentElement.removeChild(item.parentElement); - } - while (SelectedAudioInputDevices.indexOf(item.value) > -1) { - SelectedAudioInputDevices.splice(SelectedAudioInputDevices.indexOf(item.value), 1); - } - } - - }); - } - saveSettings(); - }; - listele.appendChild(option); - listele.appendChild(label); - audioInputSelect.appendChild(listele); - - //////////// - - - //selectors.forEach((select, selectorIndex) => { - // if (Array.prototype.slice.call(select.childNodes).some(n => n.value === values[selectorIndex])) { - // select.value = values[selectorIndex]; - // } - //}); - - audioInputSelect.onchange = function() { - log("Audio OPTION HAS CHANGED? 2"); - activatedPreview = false; - setTimeout(function(){ - grabAudio("#audioSource3"); - },10) - }; - - getById("refreshVideoButton").onclick = function() { - refreshVideoDevice(); - }; - - if (Firefox && !session.mobile){ - audioOutputSelect.onclick = function() { - log("audioOutputSelect.onclick = function() {"); - if (audioOutputSelect.options[audioOutputSelect.selectedIndex].value === "others"){ - log("Trying to increase the output device list"); - navigator.mediaDevices.selectAudioOutput().then((device) => { - - if (device.kind == "audiooutput"){ - session.sink = device.deviceId; - - try { - var matched = false; - audioOutputSelect.childNodes.forEach(ele =>{ - if (ele.value === device.deviceId){ - matched = true; - ele.selected = true; - } - }) - if (!matched){ - var option = document.createElement('option'); - option.value = device.deviceId; - option.text = device.label; - audioOutputSelect.appendChild(option); - option.selected = true; - } - - saveSettings(); // we're saving because there was an explicit action to change devices - } catch(e){errorlog(e);} - - if (!session.sink){return;} // Not sure this would ever happen, but whatever. - - resetupAudioOut(); // we'll probalby use session.sink, since outputSelect3 doesn't exist. - } - - }); - } - } - } - - audioOutputSelect.onchange = function() { - log("audioOutputSelect.onchange = function() {"); - - if (iOS || iPad) { - return; - } - - if (Firefox && !session.mobile){ - if (audioOutputSelect.options[audioOutputSelect.selectedIndex].value === "others"){ // we handle this elsewhere - return; - } - } - - try { - session.sink = audioOutputSelect.options[audioOutputSelect.selectedIndex].value; - saveSettings(); - } catch (e) { - errorlog(e); - } - if (!session.sink){return;} - - resetupAudioOut(); - - log("done audioOutputSelect.onchange = function() {"); - } - - } catch (e) { - errorlog(e); - } -} - -function refreshVideoDevice(){ - if (session.screenShareState) { - log("can't refresh a screenshare"); - return; - } - log("video source changed"); - activatedPreview = false; - grabVideo(session.quality, "videosource", "select#videoSource3"); -} - -function gotDevicesRemote(deviceInfos, UUID) { - - try { - if (document.getElementById("remoteVideoSelect_"+UUID)){ - var videoSelect = document.getElementById("remoteVideoSelect_"+UUID); - var length = videoSelect.options.length; - for (i = length-1; i >= 0; i--) { - videoSelect.options[i] = null; - } - } else { - var videoSelect = document.createElement("select"); - videoSelect.id = "remoteVideoSelect_"+UUID; - - - - videoSelect.onchange = function(){ - if (session.rpcs[UUID].stats.info && session.rpcs[UUID].stats.info.consent){ - getById("requestVideoDevice_"+UUID).innerHTML = ' apply'; - getById("requestVideoDevice_"+UUID).title = "This will update the remote device to the selected one"; - } else { - getById("requestVideoDevice_"+UUID).innerHTML = ' request'; - getById("requestVideoDevice_"+UUID).title = "This will ask the remote guest for permission to change"; - } - } - - var buttonGO = document.createElement("button"); - buttonGO.innerHTML = ' refresh'; - buttonGO.title = "This will refresh the current device"; - buttonGO.id = "requestVideoDevice_"+UUID; - buttonGO.onclick = function(){ - var data = {} - data.changeCamera = videoSelect.value; - data.UUID = UUID; - session.sendRequest(data, UUID); // Viewer is requesting the PUBLISHER - }; - - var videoSelectDiv = document.createElement("div"); - query("#container_"+UUID+" .advancedVideoSettings").appendChild(videoSelectDiv); - videoSelectDiv.appendChild(videoSelect); - videoSelectDiv.appendChild(buttonGO); - } - - if (document.getElementById("remoteAudioSelect_"+UUID)){ - log("remoteAudioSelect_ "); - var audioSelect = document.getElementById("remoteAudioSelect_"+UUID); - var length = audioSelect.options.length; - for (i = length-1; i >= 0; i--) { - audioSelect.options[i] = null; - } - } else { - var audioSelect = document.createElement("select"); - audioSelect.id = "remoteAudioSelect_"+UUID; - - - audioSelect.onchange = function(){ - log("ON CHANGE"); - if (session.rpcs[UUID].stats.info && session.rpcs[UUID].stats.info.consent){ - getById("requestAudioDevice_"+UUID).innerHTML = ' apply'; - getById("requestAudioDevice_"+UUID).title = "This will update the remote device to the selected one"; - } else { - getById("requestAudioDevice_"+UUID).innerHTML = ' request'; - getById("requestAudioDevice_"+UUID).title = "This will ask the remote guest for permission to change"; - } - } - - var buttonGO = document.createElement("button"); - buttonGO.innerHTML = ' refresh'; - // buttonGO.style = "padding: 5px;"; - buttonGO.title = "This will refresh the current device"; - buttonGO.id = "requestAudioDevice_"+UUID; - - buttonGO.onclick = function(){ - var data = {} - data.changeMicrophone = audioSelect.value; - data.UUID = UUID; - session.sendRequest(data, UUID); // Viewer is requesting the PUBLISHER - } - var audioSelectDiv = document.createElement("div"); - query("#container_"+UUID+" .advancedAudioSettings").appendChild(audioSelectDiv); - audioSelectDiv.appendChild(audioSelect); - audioSelectDiv.appendChild(buttonGO); - - } - - if (document.getElementById("remoteAudioOutputSelect_"+UUID)){ - var audioOutputSelect = document.getElementById("remoteAudioOutputSelect_"+UUID); - var length = audioOutputSelect.options.length; - for (i = length-1; i >= 0; i--) { - audioOutputSelect.options[i] = null; - } - } else { - var audioOutputSelect = document.createElement("select"); - audioOutputSelect.id = "remoteAudioOutputSelect_"+UUID; - - audioOutputSelect.onchange = function(){ - if (session.rpcs[UUID].stats.info && session.rpcs[UUID].stats.info.consent){ - getById("requestAudioOutputDevice_"+UUID).innerHTML = ' apply'; - getById("requestAudioOutputDevice_"+UUID).title = "This will update the remote device to the selected one"; - } else { - getById("requestAudioOutputDevice_"+UUID).innerHTML = ' request'; - getById("requestAudioOutputDevice_"+UUID).title = "This will ask the remote guest for permission to change"; - } - } - - var buttonGO = document.createElement("button"); - buttonGO.innerHTML = ' refresh'; - buttonGO.title = "This will refresh the current device"; - buttonGO.id = "requestAudioOutputDevice_"+UUID; - buttonGO.onclick = function(){ - var data = {} - data.changeSpeaker = audioOutputSelect.value; - data.UUID = UUID; - session.sendRequest(data, UUID); // Viewer is requesting the PUBLISHER - } - - var audioOutputSelectContainer = document.createElement("div"); - query("#container_"+UUID+" .advancedAudioSettings").appendChild(audioOutputSelectContainer); - audioOutputSelectContainer.appendChild(audioOutputSelect); - audioOutputSelectContainer.appendChild(buttonGO); - } - - var matched = false; - for (let i = 0; i !== deviceInfos.length; ++i) { - const deviceInfo = deviceInfos[i]; - if (deviceInfo == null) { - continue; - } - if (deviceInfo.kind === 'videoinput'){ - const option = document.createElement('option'); - option.value = deviceInfo.deviceId || "default"; - option.text = deviceInfo.label || `camera ${videoSelect.length + 1}`; - if (getById("remoteVideoLabel_"+UUID).innerText == option.text){ - option.selected = "true"; - matched = true; - } - videoSelect.appendChild(option); - - } else if (deviceInfo.kind === 'audioinput'){ - const option = document.createElement('option'); - option.value = deviceInfo.deviceId || "default"; - option.text = deviceInfo.label || `microphone ${audioSelect.length + 1}`; - if (getById("remoteAudioLabel_"+UUID).innerText == option.text){ - option.selected = "true"; - } - audioSelect.appendChild(option); - - } else if (deviceInfo.kind === 'audiooutput'){ - const option = document.createElement('option'); - option.value = deviceInfo.deviceId || "default"; - option.text = deviceInfo.label || `microphone ${audioOutputSelect.length + 1}`; - if (getById("remoteAudioOutputSelect_"+UUID).innerText == option.text){ - option.selected = "true"; - } - audioOutputSelect.appendChild(option); - } - } - - - if (!matched){ - getById("requestVideoDevice_"+UUID).innerHTML = ' request'; - getById("requestVideoDevice_"+UUID).title = "This will ask the remote guest for permission to change"; - } else { - getById("requestVideoDevice_"+UUID).innerHTML = ' refresh'; - getById("requestVideoDevice_"+UUID).title = "This will reconnect the guest's active video source."; - } - - - } catch(e){errorlog(e);} - - pokeIframeAPI("remote-devices-info", deviceInfos, UUID); -} - -var timeoutTone = false; -function playtone(screen = false, tonename="testtone") { - if (timeoutTone){return;} - setTimeout(function(){ - timeoutTone = false; - },500); - timeoutTone = true; - - if (iOS || iPad) { - // try{ - // session.audioContext.resume(); - // } catch(e){errorlog(e);} - var toneEle = document.getElementById(tonename); - if (toneEle) { - toneEle.mute - toneEle.play(); - } - return; - } - - if (screen) { - try{ - var outputSelect = getById('outputSourceScreenshare'); - if (outputSelect){ - session.sink = outputSelect.options[outputSelect.selectedIndex].value; - saveSettings(); - } - } catch(e){errorlog(e);} - } - - var toneEle = document.getElementById(tonename); - if (toneEle) { - if (session.sink) { - try { - toneEle.setSinkId(session.sink).then(() => { // TODO: iOS doens't support sink. Needs to bypass if IOS - log("changing audio sink:" + session.sink); - toneEle.play(); - }).catch(error => { - warnlog(error); - }); - } catch (e) { - warnlog(e); // firefox? - toneEle.play(); - } - } else { - toneEle.play(); - } - } -} - -function showNotification(title, body="") { - if (!Notification){return;} - if (Notification.permission !== 'granted') { - Notification.requestPermission(); - } else { - let icon = '/media/old_icon.png'; //this is a large image may take more time to show notifiction, replace with small size icon - - let notification = new Notification(title, { body, icon }); - - notification.onclick = () => { - notification.close(); - window.parent.focus(); - } - } -} - -async function getAudioOnly(selector, trackid = null, override = false) { - var audioSelect = document.querySelector(selector).querySelectorAll("input"); - var audioList = []; - var streams = []; - log("getAudioOnly()"); - for (var i = 0; i < audioSelect.length; i++) { - if (audioSelect[i].value == "ZZZ") { - continue; - } else if (trackid == audioSelect[i].value) { // skip already excluded - continue; - } else if ("screen" == audioSelect[i].dataset.type) { // skip already excluded ---------- !!!!!! DOES THIS MAKE SENSE? TODO: CHECK - continue; - } else if (audioSelect[i].checked) { - log(audioSelect[i]); - audioList.push(audioSelect[i]); - } - } - - for (var i = 0; i < audioList.length; i++) { - - if ((session.echoCancellation !== false) && (session.autoGainControl !== false) && (session.noiseSuppression !== false)) { - var constraint = { - audio: { - deviceId: { - exact: audioList[i].value - } - } - }; - } else { // Just trying to avoid problems with some systems that don't support these features - var constraint = { - audio: { - deviceId: { - exact: audioList[i].value - } - } - }; - if (session.echoCancellation === false) { - constraint.audio.echoCancellation = false; - } else { - constraint.audio.echoCancellation = true; - } - if (session.autoGainControl === false) { - constraint.audio.autoGainControl = false; - } else { - constraint.audio.autoGainControl = true; - } - if (session.noiseSuppression === false) { - constraint.audio.noiseSuppression = false; - } else { - constraint.audio.noiseSuppression = true; - } - } - constraint.video = false; - if (override !== false) { - log("Override true"); - if (override.audio && override.audio.deviceId){ - if (audioList[i].value == override.audio.deviceId){ - constraint = override; - } else { - // not the device we want to hack. - } - } else { - constraint = override; - } - } - - if (audioList[i].value && SelectedAudioInputDevices){ - if (SelectedAudioInputDevices.indexOf(audioList[i].value) === -1) { - if (SelectedAudioInputDevices.length && SelectedAudioInputDevices.includes("ZZZ")){ - SelectedAudioInputDevices = []; - } - SelectedAudioInputDevices.push(audioList[i].value); - } - } - - if (session.audioInputChannels) { - if (constraint.audio === true) { - constraint.audio = {}; - constraint.audio.channelCount = session.audioInputChannels; - } else if (constraint.audio) { - constraint.audio.channelCount = session.audioInputChannels; - } - } - - - if (session.micSampleRate){ - if (constraint.audio === true) { - constraint.audio = {}; - constraint.audio.sampleRate = parseInt(session.micSampleRate); - } else if (constraint.audio) { - constraint.audio.sampleRate = parseInt(session.micSampleRate); - } - } - - if (session.micSampleSize){ - if (constraint.audio === true) { - constraint.audio = {}; - constraint.audio.sampleSize = parseInt(session.micSampleSize); - } else if (constraint.audio) { - constraint.audio.sampleSize = parseInt(session.micSampleSize); - } - } - - log("CONSTRAINT"); - log(constraint); - - var stream = await navigator.mediaDevices.getUserMedia(constraint).then(function(stream2) { - log("get audio sucecss"); - pokeIframeAPI("local-microphone-event"); - return stream2; - }).catch(function(err) { - warnlog(err); - if (!(session.cleanOutput)) { - if (override !== false) { - if (err.name) { - if (err.constraint) { - warnUser(err['name'] + ": " + err['constraint']); - } - } - } - } - return false; - }); // More error reporting maybe? - if (stream) { - streams.push(stream); - } - } - - return streams; -} - -function applyMirror(mirror) { // true unmirrors as its already mirrored - if (!session.videoElement){return;} - try { - var transFlip = ""; - var transNorm = ""; - if (document.getElementById('videosource') && (session.windowed)) { - transFlip = " translate(0, 50%)"; - transNorm = " translate(0, -50%)"; - } - - if (session.mirrored == 2) { - mirror = true; - } else if (session.mirrored === 0) { - mirror = true; - } - - if (!session.videoElement.style){ - session.videoElement.style = ""; - } - - if (session.permaMirrored){ - mirror = !mirror; - } - - if (mirror) { - if (session.mirrored && session.flipped) { - session.videoElement.style.transform = "scaleX(-1) scaleY(-1)" + transFlip; - session.videoElement.classList.add("mirrorControl"); - } else if (session.mirrored) { - session.videoElement.style.transform = "scaleX(-1)" + transNorm; - session.videoElement.classList.add("mirrorControl"); - } else if (session.flipped) { - session.videoElement.style.transform = "scaleY(-1) scaleX(1)" + transFlip; - session.videoElement.classList.remove("mirrorControl"); - } else { - session.videoElement.style.transform = "scaleX(1)" + transNorm; - session.videoElement.classList.remove("mirrorControl"); - } - } else { - if (session.mirrored && session.flipped) { - session.videoElement.style.transform = "scaleX(1) scaleY(-1)" + transFlip; - session.videoElement.classList.remove("mirrorControl"); - } else if (session.mirrored) { - session.videoElement.style.transform = "scaleX(1)" + transNorm; - session.videoElement.classList.remove("mirrorControl"); - } else if (session.flipped) { - session.videoElement.style.transform = "scaleY(-1) scaleX(-1)" + transFlip; - session.videoElement.classList.add("mirrorControl"); - } else { - session.videoElement.style.transform = "scaleX(-1)" + transNorm; - session.videoElement.classList.add("mirrorControl"); - } - } - - var rotate = 0; - if (session.forceRotate!==false){ - if (session.rotate){ - rotate = (session.forceRotate * -1) + parseInt(session.rotate); - } else { - rotate = session.forceRotate * -1; - } - if (session.forceRotate){ - rotate+=180; - } - } else { - rotate = session.rotate; - } - - if (rotate && (rotate>=360)){ - rotate-=360; - } - - session.videoElement.rotated = rotate; - - if (document.getElementById("previewWebcam") || document.getElementById('videosource')){ - var eleName = document.getElementById("previewWebcam") || document.getElementById('videosource'); - if (rotate){ - if (eleName.style.transform){ - eleName.style.transform += " rotate("+rotate+"deg)"; - } else { - eleName.style.transform = "rotate("+rotate+"deg)"; - } - eleName.classList.add("rotate"); - } else { - eleName.classList.remove("rotate"); - } - } else if (document.getElementById("container")){ - if (rotate==0){ - document.getElementById("container").classList.remove("rotate"); - document.getElementById("container").style.transform = "unset"; - document.getElementById("container").style.transformOrigin = "unset"; - } else { - document.getElementById("container").style.transform = "rotate("+rotate+"deg)"; - } - } else if (document.getElementById("minipreview")){ - var eleName = document.getElementById("minipreview"); - if (rotate==90 ){ - eleName.style.transform = "rotate(90deg)"; - eleName.style.transformOrigin = "50% 100%"; - eleName.style.height = eleName.style.width; - eleName.style.width = "unset"; - } else if (session.videoElement.rotated==270){ - eleName.style.transform = "rotate(270deg)"; - eleName.style.transformOrigin = "50% 100%"; - eleName.style.width = "unset"; - eleName.style.height = eleName.style.width; - } else if (session.videoElement.rotated==180){ - eleName.style.transform = "rotate(180deg)"; - eleName.style.transformOrigin = "unset"; - } else { - eleName.classList.remove("rotate"); - eleName.style.transform = "unset"; - eleName.style.transformOrigin = "unset"; - } - } - // if not one of these, then it's going to be handled by the automixer automatically for us. - - } catch(e){errorlog(e);} -} - - -function applyMirrorGuest(mirror, videoElement) { // true unmirrors as its already mirrored - try { - if (mirror) { - videoElement.style.transform = "scaleX(-1)"; - videoElement.classList.add("mirrorControl"); - } else { - videoElement.style.transform = "scaleX(1)"; - videoElement.classList.remove("mirrorControl"); - } - } catch(e){errorlog(e);} -} - -function cleanupMediaTracks() { - getUserMediaRequestID += 1; - try { - - if (session.streamSrcClone){ - session.streamSrcClone.getTracks().forEach(function(track) { - session.streamSrcClone.removeTrack(track); - track.stop(); - log("stopping old track"); - }); - } - if (session.streamSrc) { - session.streamSrc.getTracks().forEach(function(track) { - session.streamSrc.removeTrack(track); - track.stop(); - log("stopping old track"); - }); - } else { - checkBasicStreamsExist(); - } - if (session.videoElement && session.videoElement.srcObject) { - session.videoElement.srcObject.getTracks().forEach(function(track) { - session.videoElement.srcObject.removeTrack(track); - track.stop(); - log("stopping old track"); - }); - } else { - session.videoElement.srcObject = createMediaStream(); - } - activatedPreview = false; - } catch (e) { - errorlog(e); - } -} - -/// Detect system changes; handle change or use for debugging -var lastAudioDevice = null; -var lastVideoDevice = null; -var lastPlaybackDevice = null; - -var audioReconnectTimeout = null; -var videoReconnectTimeout = null; -var grabDevicesTimeout = null; -var playbackReconnectTimeout = null; - -function reconnectDevices(event) { /// TODO: Perhaps change this to only if there is a DISCONNECT; rather than ON NEW DEVICE? - - try { - if (session.firstPlayTriggered && (session.audioCtx.state == "suspended")){ - session.audioCtx.resume(); - } - } catch(e){warnlog("session.audioCtx.resume(); failed");} - - warnlog("A media device has changed"); - - if (iOS || iPad) { // consider adding this back, but if no problem, whatever. - return; - } - - if (document.getElementById("previewWebcam")) { // rest of the code isn't setup to support pre-connection setup. - clearTimeout(playbackReconnectTimeout); - playbackReconnectTimeout = setTimeout(function(){ - if (document.getElementById("previewWebcam")){ - enumerateDevices().then(gotDevices).then(function(){ - if (document.getElementById("previewWebcam")){ - resetupAudioOut(document.getElementById("previewWebcam")); - } - }); - } - }, 1000); - return; - } - - try { - if (!session.streamSrc){ - checkBasicStreamsExist(); - } else { - session.streamSrc.getTracks().forEach(function(track) { - if (track.readyState == "ended") { - if (track.kind == "audio") { - lastAudioDevice = track.label; - } else if (track.kind == "video") { - lastVideoDevice = track.label; - } - session.streamSrc.removeTrack(track); - log("remove ended old track"); - } - }); - - if (session.streamSrcClone){ - session.streamSrcClone.getTracks().forEach(function(track) { - if (track.readyState == "ended") { - session.streamSrcClone.removeTrack(track); - track.stop(); - } - }); - } - - if (session.videoElement && session.videoElement.srcObject){ - session.videoElement.srcObject.getTracks().forEach(function(track) { - if (track.readyState == "ended") { - session.videoElement.srcObject.removeTrack(track); - log("remove ended old track"); - } - }); - } - } - /* } else { - clearTimeout(playbackReconnectTimeout); - playbackReconnectTimeout = setTimeout(function() { - enumerateDevices().then(gotDevices2).then(function() { - resetupAudioOut(); - }); - }, 1000); - return; - } */ - } catch (e) { - errorlog(e); - } - - clearTimeout(audioReconnectTimeout); - audioReconnectTimeout = null; - if (lastAudioDevice) { - audioReconnectTimeout = setTimeout(function() { // only reconnect same audio device. If reconnected, clear the disconnected flag. - enumerateDevices().then(gotDevices2).then(function() { - // TODO: check to see if any audio is connected? - var streamConnected = false; - var audioSelect = getById("audioSource3").querySelectorAll("input"); - for (var i = 0; i < audioSelect.length; i++) { - if (audioSelect[i].value == "ZZZ") { - continue; - } else if (audioSelect[i].checked) { - log("checked"); - streamConnected = true; - break; - } - } - - if (!streamConnected) { - for (var i = 0; i < audioSelect.length; i++) { - if (audioSelect[i].value == "ZZZ") { - continue; - } - if (lastAudioDevice == audioSelect[i].dataset.label) { // if the last disconnected device matches. - audioSelect[i].checked = true; - streamConnected = true; - lastAudioDevice = null; - warnlog("DISCONNECTED AUDIO DEVICE RECONNECTED"); - break; - } - } - } - activatedPreview = false; - grabAudio("#audioSource3"); - setTimeout(function() { - enumerateDevices().then(gotDevices2).then(function() { - }); - }, 1000); - }); - }, 2000); - } - - clearTimeout(videoReconnectTimeout); // only reconnect same video device. - videoReconnectTimeout = null; - if (lastVideoDevice) { - videoReconnectTimeout = setTimeout(function() { - enumerateDevices().then(gotDevices2).then(function() { - var streamConnected = false; - var videoSelect = getById("videoSource3"); - errorlog(videoSelect.value); - - if (videoSelect.value == "ZZZ") { - for (var i = 0; i < videoSelect.options.length; i++) { - try { - if (videoSelect.options[i].innerHTML == lastVideoDevice) { - videoSelect.options[i].selected = "true"; - streamConnected = true; - lastVideoDevice = null; - break; - } - } catch (e) { - errorlog(e); - } - } - } - - if (streamConnected) { - activatedPreview = false; - grabVideo(session.quality, "videosource", "select#videoSource3"); - setTimeout(function() { - enumerateDevices().then(gotDevices2).then(function() {}); - }, 1000); - } - - }); - }, 2000); - } - clearTimeout(playbackReconnectTimeout); - playbackReconnectTimeout = setTimeout(function() { - enumerateDevices().then(gotDevices2).then(function() { - resetupAudioOut(); - }); - }, 1000); -} - -var vingesterFixed = false; -function resetupAudioOut(ele=false, forceReset=false) { // this re-sets ALL output devices / sources - log("resetupAudioOut"); - if (iOS || iPad || SafariVersion || (ChromiumVersion && session.mobile)) { // TODO : TEST TO SEE IF THIS WORKS WITH SAFARI? it might. - if (ele){return;} - for (var UUID in session.rpcs) { - if (session.rpcs[UUID].videoElement){ - try{ - session.rpcs[UUID].videoElement.pause().then(() => { - setTimeout(function(uuid) { - log("win"); - try{ - session.rpcs[uuid].videoElement.play().then(() => { - // updateIncomingAudioElement(uuid); // DO NOT DO THIS; it would cause a loop, as this updateIncomingAudioElement calls reseet - log("toggle pause/play"); - }); - } catch(e){errorlog(e);} - }, 0, UUID); - }).catch(errorlog); - } catch(e){warnlog(e);} - } - } - return; - } - - //if (isVingester){return;} - - var outputSelect = document.getElementById("outputSource") || document.getElementById("outputSource3") || false; - var sinkSet = false; - - try { // function tries to get vingester working, by listening for its first tweak. - if (!session.sink && !vingesterFixed && document.getElementById("testtone") && document.getElementById("testtone").sinkId && isVingester){ - vingesterFixed = true; // only set the session.sink one time, as vingester on should be needing to set it one time. - session.sink = document.getElementById("testtone").sinkId; - } - } catch(e){ - errorlog(e); - } - - if (outputSelect && outputSelect.options && outputSelect.options.length){ - for (var i = 0; i < outputSelect.options.length; i++) { - if (outputSelect.options[i].value == session.sink) { - outputSelect.options[i].selected = "true"; - sinkSet = true; - } - } - if (sinkSet == false) { - if (outputSelect.options[0]) { - outputSelect.options[0].selected = "true"; - sinkSet = outputSelect.value; - } - } else { - sinkSet = session.sink; - } - } else { - sinkSet = session.sink; - } - - if (ele){ - try { - var eleSink = sinkSet; - if (ele.manualSink){ - eleSink = ele.manualSink; - log("Manual Sink Identified"); - } - if (eleSink){ - if (forceReset){ - ele.setSinkId("default").then(() => { - ele.setSinkId(eleSink).then(() => { - log("New Output Device"); - }).catch(error => { - if (!Firefox){ - warnlog(error); - } - // TODO: If error, then see if I need to add mic support, and grab it if needed. - }); - }).catch(error => { - if (!Firefox){ - errorlog(error); - } - ele.setSinkId(eleSink).then(() => { - log("New Output Device"); - }).catch(error => { - if (!Firefox){ - warnlog(error); - } - // TODO: If error, then see if I need to add mic support, and grab it if needed. - }); - }); - } - - ele.setSinkId(eleSink).then(() => { - log("New Output Device for self-preview"); - }).catch(error => { - if (!Firefox){ - warnlog("Need to add mic support, and grab it if needed."); - warnlog(error); - } - // TODO: If error, then see if I need to add mic support, and grab it if needed. - }); - } - } catch(e){warnlog("can't use setsink");} - log("audio sink: "+eleSink); - return; - } - - if (session.videoElement){ // this would be a preview or videosource - try { - var eleSink = sinkSet; - if (session.videoElement.manualSink){ - eleSink = session.videoElement.manualSink; - } - if (eleSink){ - session.videoElement.setSinkId(eleSink).then(() => { - log("New Output Device for self-preview"); - }).catch(error => { - if (!Firefox){ - warnlog(error); - } - // TODO: If error, then see if I need to add mic support, and grab it if needed. - }); - } - } catch(e){warnlog("can't use setsink");} - } - - for (UUID in session.rpcs) { - try{ - if (session.rpcs[UUID].videoElement){ - var eleSink = sinkSet; - if (session.rpcs[UUID].videoElement.manualSink){ - eleSink = session.rpcs[UUID].videoElement.manualSink; - } - if (eleSink){ - session.rpcs[UUID].videoElement.setSinkId(eleSink).then(() => { - log("New Output Device for: " + UUID); - }).catch(error => { - if (!Firefox){ - warnlog(error); - } - // TODO: If error, then see if I need to add mic support, and grab it if needed. - }); - } - } - } catch(e){warnlog(e);} - } - log("audio sink 2: "+eleSink); -} - - -function lyraDecode(receiver){ // WIP. does not work - warnlog("lyraDecode"); - const receiverStreams = receiver.createEncodedStreams(); - var xx = receiverStreams.readable; - xx.pipeThrough(new TransformStream()); - xx.pipeTo(receiverStreams.writable); -} -function lyraEncode(UUID){ // WIP. does not work - warnlog("lyraEncode"); - var senders = getSenders2(UUID); - senders.forEach((sender) => { - if (!sender.track){return;} - if (sender.track.kind === "audio") { - try { - var senderStreams = sender.createEncodedStreams(); - senderStreams.readable.pipeThrough(new TransformStream()).pipeTo(senderStreams.writable); - } catch (e){ - errorlog(e); - } - } - }); -} - -///////// WIP. does not work //////////// -// function encodeFunctionLyra(encodedFrame, controller) { // Snippet is Apache 2.0 licenced. Source: https://github.com/Flash-Meeting/lyra-webrtc - // const inputDataArray = new Uint8Array(encodedFrame.data); - - // const inputBufferPtr = session.lyraCodecModule._malloc(encodedFrame.data.byteLength); - // const encodedBufferPtr = session.lyraCodecModule._malloc(1024); - - // session.lyraCodecModule.HEAPU8.set(inputDataArray, inputBufferPtr); - // const length = session.lyraCodecModule.encode(inputBufferPtr, inputDataArray.length, 16000, encodedBufferPtr); - - // const newData = new ArrayBuffer(length); - // if (length > 0){ - // const newDataArray = new Uint8Array(newData); - // newDataArray.set(session.lyraCodecModule.HEAPU8.subarray(encodedBufferPtr, encodedBufferPtr + length)); - // } - // session.lyraCodecModule._free(inputBufferPtr); - // session.lyraCodecModule._free(encodedBufferPtr); - // encodedFrame.data = newData; - // controller.enqueue(encodedFrame); -// } -// function decodeFunctionLyra(encodedFrame, controller) { // Apache 2.0 licenced. Source: https://github.com/Flash-Meeting/lyra-webrtc - // const newData = new ArrayBuffer(16000 * 0.02 * 2); - // if (encodedFrame.data.byteLength > 0) { - // const inputDataArray = new Uint8Array(encodedFrame.data); - // const inputBufferPtr = session.lyraCodecModule._malloc(encodedFrame.data.byteLength); - // const outputBufferPtr = session.lyraCodecModule._malloc(2048); - // session.lyraCodecModule.HEAPU8.set(inputDataArray, inputBufferPtr); - // const length = session.lyraCodecModule.decode(inputBufferPtr, - // inputDataArray.length, 16000, - // outputBufferPtr); - // const newDataArray = new Uint8Array(newData); - // newDataArray.set(session.lyraCodecModule.HEAPU8.subarray(outputBufferPtr, outputBufferPtr + length)); - // session.lyraCodecModule._free(inputBufferPtr); - // session.lyraCodecModule._free(outputBufferPtr); - // } - // encodedFrame.data = newData; - // controller.enqueue(encodedFrame); -// } -////////// - -function obfuscateURL(input) { - if (input.startsWith("https://obs.ninja/")) { - input = input.replace('https://obs.ninja/', 'obs.ninja/'); - } else if (input.startsWith("http://obs.ninja/")) { - input = input.replace('http://obs.ninja/', 'obs.ninja/'); - } else if (input.startsWith("obs.ninja/")) { - input = input.replace('obs.ninja/', 'obs.ninja/'); - } else if (input.startsWith("https://vdo.ninja/")) { - input = input.replace('https://vdo.ninja/', 'vdo.ninja/'); - } else if (input.startsWith("http://vdo.ninja/")) { - input = input.replace('http://vdo.ninja/', 'vdo.ninja/'); - } else if (input.startsWith("vdo.ninja/")) { - input = input.replace('vdo.ninja/', 'vdo.ninja/'); - } - - input = input.replace('&view=', '&v='); - input = input.replace('&view&', '&v&'); - input = input.replace('?view&', '?v&'); - input = input.replace('?view=', '?v='); - - input = input.replace('&videobitrate=', '&vb='); - input = input.replace('?videobitrate=', '?vb='); - input = input.replace('&bitrate=', '&vb='); - input = input.replace('?bitrate=', '?vb='); - - input = input.replace('?audiodevice=', '?ad='); - input = input.replace('&audiodevice=', '&ad='); - - input = input.replace('?label=', '?l='); - input = input.replace('&label=', '&l='); - - input = input.replace('?stereo=', '?s='); - input = input.replace('&stereo=', '&s='); - input = input.replace('&stereo&', '&s&'); - input = input.replace('?stereo&', '?s&'); - - input = input.replace('?webcam&', '?wc&'); - input = input.replace('&webcam&', '&wc&'); - - input = input.replace('?remote=', '?rm='); - input = input.replace('&remote=', '&rm='); - - input = input.replace('?password=', '?p='); - input = input.replace('&password=', '&p='); - - input = input.replace('&maxvideobitrate=', '&mvb='); - input = input.replace('?maxvideobitrate=', '?mvb='); - - input = input.replace('&maxbitrate=', '&mvb='); - input = input.replace('?maxbitrate=', '?mvb='); - - input = input.replace('&height=', '&h='); - input = input.replace('?height=', '?h='); - - input = input.replace('&width=', '&w='); - input = input.replace('?width=', '?w='); - - input = input.replace('&quality=', '&q='); - input = input.replace('?quality=', '?q='); - - input = input.replace('&cleanoutput=', '&clean='); - input = input.replace('?cleanoutput=', '?clean='); - - input = input.replace('&maxviewers=', '&clean='); - input = input.replace('?maxviewers=', '?clean='); - - input = input.replace('&frameRate=', '&fr='); - input = input.replace('?frameRate=', '?fr='); - - input = input.replace('&fps=', '&fr='); - input = input.replace('?fps=', '?fr='); - - input = input.replace('&roomid=', '&r='); - input = input.replace('?roomid=', '?r='); - - input = input.replace('&room=', '&r='); - input = input.replace('?room=', '?r='); - - log(input); - var key = "OBSNINJAFORLIFE"; - var encrypted = CryptoJS.AES.encrypt(input, key); - var output = "https://invite.cam/" + encrypted.toString(); - return output; -} - -function notifyOfScreenShare(){ - if (session.notifyScreenShare){ - var data = {}; - data.screenShareState = session.screenShareState; - session.sendMessage(data); - } -} - -var beforeScreenShare = null; // video -var screenShareAudioTrack = null; -async function toggleScreenShare(reload = false) { /// &sstype=1 - - var quality = session.quality_ss; - - if (quality === false){ - quality = session.quality_wb; - } - - if (session.quality !== false){ - quality = session.quality; - } - if (session.screensharequality!==false){ - quality = session.screensharequality; - } - - if (reload) { // quality = 0, audio = true, videoOnEnd = false) { - await grabScreen(quality, true, true).then(res => { - if (res != false) { - session.screenShareState = true; - pokeIframeAPI('screen-share-state', session.screenShareState, null, session.streamID); - notifyOfScreenShare(); - - getById("screensharebutton").classList.add("green"); - getById("screensharebutton").ariaPressed = "true"; - enumerateDevices().then(gotDevices2).then(function() {}); - } - - }); - return; - } - if (session.screenShareState == false) { // adding a screen - await grabScreen(quality, true, true).then(res => { - if (res != false) { - session.screenShareState = true; - pokeIframeAPI('screen-share-state', session.screenShareState, null, session.streamID); - notifyOfScreenShare(); - - //session.refreshScale(); - - getById("screensharebutton").classList.add("green"); - getById("screensharebutton").ariaPressed = "true"; - enumerateDevices().then(gotDevices2).then(function() {}); - - //if (session.videoElement.readyState!==4){ - session.videoElement.play().then(() => { - log("start play doublecheck"); - }); - //} - updateMixer(); - pokeIframeAPI("screen-share-state", true); - } - }); - - } else { // removing a screen . session.screenShareState already true true ///////////////////////////////// - - session.screenShareState = false; - pokeIframeAPI('screen-share-state', session.screenShareState, null, session.streamID); - notifyOfScreenShare(); - - if (!session.streamSrc){ - checkBasicStreamsExist(); - } - - if (screenShareAudioTrack){ - if (session.videoElement && session.videoElement.srcObject){ - session.videoElement.srcObject.getAudioTracks().forEach(function(track) { // previous video track; saving it. Must remove the track at some point. - if (screenShareAudioTrack.id == track.id) { // since there are more than one audio track, lets see if we can remove JUST the audio track for the screen share. - session.videoElement.srcObject.removeTrack(track); - track.stop(); - } - }); - } - - if (session.streamSrcClone){ // - session.streamSrcClone.getAudioTracks().forEach(function(track) { - if (screenShareAudioTrack.id == track.id) { // since there are more than one audio track, lets see if we can remove JUST the audio track for the screen share. - session.streamSrcClone.removeTrack(track); - track.stop(); - } - }); - } - if (session.streamSrc){ - session.streamSrc.getAudioTracks().forEach(function(track) { // previous video track; saving it. Must remove the track at some point. - if (screenShareAudioTrack.id == track.id) { // since there are more than one audio track, lets see if we can remove JUST the audio track for the screen share. - session.streamSrc.removeTrack(track); - track.stop(); - } - }); - } - - session.videoElement.srcObject = outboundAudioPipeline(); // updateREnderOoutput is just for video if videoElement is already activated. - screenShareAudioTrack=null; - senderAudioUpdate(); - } - - var addedAlready = false; - if (session.streamSrc){ - session.streamSrc.getVideoTracks().forEach(function(track) { - if (beforeScreenShare && (track.id == beforeScreenShare.id)){ - addedAlready=true; - } else { - session.streamSrc.removeTrack(track); - track.stop(); - } - }); - } - - if (session.streamSrcClone){ - session.streamSrcClone.getVideoTracks().forEach(function(track) { - if (beforeScreenShare && (track.id == beforeScreenShare.id)){ - // - } else { - session.streamSrcClone.removeTrack(track); - track.stop(); - } - }); - } - - if (session.videoElement && session.videoElement.srcObject){ - session.videoElement.srcObject.getVideoTracks().forEach(function(track) { - if (beforeScreenShare && (track.id == beforeScreenShare.id)){ - addedAlready=true; - } else { - session.videoElement.srcObject.removeTrack(track); - track.stop(); - } - }); - } - - getById("screensharebutton").classList.remove("green"); - getById("screensharebutton").ariaPressed = "false"; - - if (beforeScreenShare){ - if (addedAlready==false){ - session.streamSrc.addTrack(beforeScreenShare); // add back in the video track we had before we started screen sharing. It should be NULL if we changed the video track else where (such as via the settings). #TODO: - } - } - - beforeScreenShare = null; - updateRenderOutpipe(); // this syncs the video - - toggleSettings(true); // forceShow - updateMixer(); - } -} -var ipcRenderer = false; -var ElectronDesktopCapture = false; -if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1) { // this enables Screen Capture in Electron - try { - if (!ipcRenderer){ - ipcRenderer = require('electron').ipcRenderer; - } - window.navigator.mediaDevices.getDisplayMedia = (constraints=false) => { - return new Promise(async (resolve, reject) => { - try { - - if (session.autostart){ - session.autostart = false; - if (parseInt(session.screenshare)+"" === session.screenshare){ - var sscid = parseInt(session.screenshare)-1; - if (sscid<0){sscid=0;} - //ipcRenderer.sendSync('prompt', {title, val}); - const sources = await ipcRenderer.sendSync('getSources',{types: ['screen']}); - - var new_constraints = { - audio:{ - mandatory: { - chromeMediaSource: 'desktop', - chromeMediaSourceId: sources[sscid].id - } - }, - video: { - mandatory: { - chromeMediaSource: 'desktop', - chromeMediaSourceId: sources[sscid].id - } - } - }; - if (session.audioDevice===0){ - new_constraints.audio = false; - } - try { - if (constraints.video.width.ideal){ - new_constraints.video.mandatory.maxWidth = constraints.video.width.ideal; - } - } catch(e){} - try { - if (constraints.video.height.ideal){ - new_constraints.video.mandatory.maxHeight = constraints.video.height.ideal; - } - } catch(e){} - try { - if (constraints.video.frameRate.ideal){ - new_constraints.video.mandatory.maxFrameRate = constraints.video.frameRate.ideal; - } - } catch(e){} - /// - const stream = await window.navigator.mediaDevices.getUserMedia(new_constraints); - resolve(stream); - } else if (session.screenshare && (session.screenshare!==true)){ - var sscid=null; - const sources = await ipcRenderer.sendSync('getSources',{types: ['window']}); - for (var i=0; i -
    - ${sources.map(({id, name, thumbnail, display_id, appIcon}) => ` -
  • - -
  • - `).join('')} - -
- - `; - } else { - selectionElem.innerHTML = ` -
-
    - ${sources.map(({id, name, thumbnail, display_id, appIcon}) => ` -
  • - -
  • - `).join('')} -

    Include Desktop Audio
    - - - -
-
- `; - } - document.body.appendChild(selectionElem); - - if (macOS){ - getById("captureDesktopAudio").style.display = "none"; - getById("alsoCaptureAudio").checked = false; - getById("alsoCaptureAudioParent1").style.display = "none"; - getById("alsoCaptureAudioParent2").style.display = "inline-block"; - } - - document.getElementById('cancelscreenshare').addEventListener('click', async () => { - selectionElem.remove(); - reject(null); - }); - document.querySelectorAll('.desktop-capturer-click').forEach(button => { - button.addEventListener('click', async () => { - try { - if (button.id == "captureDesktopAudio"){ - var new_constraints = { - audio: { - mandatory: { - chromeMediaSource: 'desktop' - } - }, - video: { - mandatory: { - chromeMediaSource: 'desktop', - } - } - } - new_constraints.video.mandatory.maxFrameRate = 1; - warnlog(new_constraints); - const stream = await window.navigator.mediaDevices.getUserMedia(new_constraints); - if (stream.getVideoTracks().length){ - var track = stream.getVideoTracks()[0]; - stream.removeTrack(stream.getVideoTracks()[0]); - track.stop(); - } - resolve(stream); - selectionElem.remove(); - } else { - var audioStream = false; - if (getById("alsoCaptureAudio").checked){ - var new_constraints = { - audio: { - mandatory: { - chromeMediaSource: 'desktop' - } - }, - video: { - mandatory: { - chromeMediaSource: 'desktop', - } - } - } - new_constraints.video.mandatory.maxFrameRate = 1; - warnlog(new_constraints); - audioStream = await window.navigator.mediaDevices.getUserMedia(new_constraints); - if (audioStream.getVideoTracks().length){ - var track = audioStream.getVideoTracks()[0]; - audioStream.removeTrack(audioStream.getVideoTracks()[0]); - track.stop(); - } - } - - const id = button.getAttribute('data-id'); - const source = sources.find(source => source.id === id); - if (!source) { - throw new Error(`Source with id ${id} does not exist`); - } - var new_constraints = { - audio: false, - video: { - mandatory: { - chromeMediaSource: 'desktop', - chromeMediaSourceId: source.id - } - } - }; - try { - if (constraints.video.width.ideal){ - new_constraints.video.mandatory.maxWidth = constraints.video.width.ideal; - } - } catch(e){} - try { - if (constraints.video.height.ideal){ - new_constraints.video.mandatory.maxHeight = constraints.video.height.ideal; - } - } catch(e){} - try { - if (constraints.video.frameRate.ideal){ - new_constraints.video.mandatory.maxFrameRate = constraints.video.frameRate.ideal; - } - } catch(e){} - warnlog(new_constraints); - const stream = await window.navigator.mediaDevices.getUserMedia(new_constraints); - - if (audioStream && audioStream.getAudioTracks().length){ - stream.addTrack(audioStream.getAudioTracks()[0]); - } - - resolve(stream); - selectionElem.remove(); - } - } catch (err) { - errorlog('Error selecting desktop capture source:', err); - reject(err); - } - }) - }); - } - } catch (err) { - errorlog('Error displaying desktop capture sources:', err); - reject(err); - } - }) - } - ElectronDesktopCapture = true; - } catch(e){ - warnlog("Couldn't load electron's screen capture. Elevate the app's permission to allow it (right-click?)"); - } -} - -async function grabScreen(quality = 0, audio = true, videoOnEnd = false) { - if (!navigator.mediaDevices.getDisplayMedia) { - if (!(session.cleanOutput)) { - setTimeout(function() { - if (iOS || iPad){ - warnUser(getTranslation("ios-no-screen-share"), false, false); - } else if (session.mobile){ - warnUser(getTranslation("android-no-screen-share"), false, false); - } else { - warnUser(getTranslation("no-screen-share-supported"), false, false); - } - }, 1); - } - return false; - } - - if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1) { - if (!ElectronDesktopCapture){ - if (!(session.cleanOutput)) { - warnUser("Enable Elevated Privileges to allow screen-sharing. (right click this window to see that option)"); - } - return false; - } - } - - var video = {} - - if (quality == -1) { - // unlocked capture resolution - } else if (quality == 0) { - video.width = { - ideal: 1920 - }; - video.height = { - ideal: 1080 - }; - } else if (quality == 1) { - video.width = { - ideal: 1280 - }; - video.height = { - ideal: 720 - }; - } else if (quality == 2) { - video.width = { - ideal: 640 - }; - video.height = { - ideal: 360 - }; - } else if (quality >= 3) { // lowest - video.width = { - ideal: 320 - }; - video.height = { - ideal: 180 - }; - } - - if (session.width) { - video.width = { - ideal: session.width - }; - } - if (session.height) { - video.height = { - ideal: session.height - }; - } - - var constraints = { // this part is a bit annoying. Do I use the same settings? I can add custom setting controls here later - audio: { - echoCancellation: false, // For screen sharing, we want it off by default. - autoGainControl: false, - noiseSuppression: false - }, - video: video - //,cursor: {exact: "none"} - }; - - - try { - let supportedConstraints = navigator.mediaDevices.getSupportedConstraints(); - if (supportedConstraints.cursor) { - if (session.screensharecursor){ - constraints.video.cursor = ["always", "motion"]; - } else { - constraints.video.cursor = "never"; - } - } - if (session.suppressLocalAudioPlayback && supportedConstraints.suppressLocalAudioPlayback){ - constraints.audio.suppressLocalAudioPlayback = true; - } - if (session.preferCurrentTab){ - constraints.preferCurrentTab = true; - } - if (session.selfBrowserSurface){ - constraints.selfBrowserSurface = session.selfBrowserSurface; // exclude or include - } - if (session.surfaceSwitching){ - constraints.surfaceSwitching = session.surfaceSwitching; // exclude or include - } - if (session.systemAudio){ - constraints.systemAudio = session.systemAudio; // exclude or include - } - if (session.displaySurface && supportedConstraints.displaySurface){ - constraints.video.displaySurface = session.displaySurface; // monitor, window, or browser - } - } catch(e){ - warnlog("navigator.mediaDevices.getSupportedConstraints() not supported"); - } - - - if (session.echoCancellation === true) { - constraints.audio.echoCancellation = true; - } - if (session.autoGainControl === true) { - constraints.audio.autoGainControl = true; - } - if (session.noiseSuppression === true) { - constraints.audio.noiseSuppression = true; - } - if (audio == false) { - constraints.audio = false; - } - - - var overrideFramerate = false; - if ((session.frameRate !== false) && (session.maxframeRate != false)){ - overrideFramerate = session.frameRate; - constraints.video.frameRate = { - ideal: session.maxframeRate, - max: session.maxframeRate - }; - } else if (session.frameRate !== false) { - constraints.video.frameRate = session.frameRate; - } else if (session.maxframeRate != false){ - constraints.video.frameRate = { - ideal: session.maxframeRate, - max: session.maxframeRate - }; - } else { - constraints.video.frameRate = { - ideal: 60 - }; - } - - if (session.screenshareVideoOnly){ - constraints.audio = false; - } - - if (session.forceAspectRatio){ // await updateCameraConstraints("aspectRatio", session.forceAspectRatio); - if (constraints.video && constraints.video!==true){ - - constraints.video.aspectRatio = { ideal: parseFloat(session.forceAspectRatio)}; - - - if (constraints.video.width && !session.width){ - delete constraints.video.width; - } else if (constraints.video.height && !session.height){ - delete constraints.video.height; - } - } - } - - if ((constraints.video!==false) && (Object.keys(constraints.video).length==0)){ - constraints.video = true; - } - - var wasDisabled = true; - - return navigator.mediaDevices.getDisplayMedia(constraints).then(async function(stream) { - log("adding video tracks 2245"); - - try { - var constraint = {}; - if (session.forceAspectRatio && (session.forceScreenShareAspectRatio===null)){ - constraint.aspectRatio = parseFloat(session.forceAspectRatio); - } else if (session.forceScreenShareAspectRatio){ - constraint.aspectRatio = parseFloat(session.forceScreenShareAspectRatio); - } - if (overrideFramerate){ - constraint.frameRate = overrideFramerate; - } - if (Object.keys(constraint).length){ - await stream.getVideoTracks()[0].applyConstraints({ - advanced: [constraint] - }); - log({ - advanced: [constraint] - }); - } - } catch(e){errorlog(e);} - - try { - if (session.streamSrc) { - session.streamSrc.getVideoTracks().forEach(function(track) { - //track.stop(); - beforeScreenShare = track; - session.streamSrc.removeTrack(track); - wasDisabled = false; // - log("stopping video track"); - }); - if (session.streamSrcClone){ - session.streamSrcClone.getVideoTracks().forEach(function(track) { - session.streamSrcClone.removeTrack(track); - track.stop(); - }); - } - if (session.videoElement && session.videoElement.srcObject){ - session.videoElement.srcObject.getVideoTracks().forEach(function(track) { - //track.stop(); - wasDisabled = false; - session.videoElement.srcObject.removeTrack(track); - log("stopping video track 2"); - }); - } else { - checkBasicStreamsExist(); - } - } else { - checkBasicStreamsExist(); // create srcObject + videoElement - } - } catch (e) { - warnlog(e); - } - - try { - stream.getVideoTracks()[0].onended = function(e) { // if screen share stops, - warnlog(e); - if (session.streamSrc){ - session.streamSrc.getVideoTracks().forEach(function(track) { - session.streamSrc.removeTrack(track); - track.stop(); - log("stopping video track 3"); - - if (beforeScreenShare && (beforeScreenShare.id == track.id)){ - beforeScreenShare.stop(); - beforeScreenShare=null; - } - }); - } - - if (session.streamSrcClone){ - session.streamSrcClone.getVideoTracks().forEach(function(track) { - session.streamSrcClone.removeTrack(track); - track.stop(); - }); - } - - if (session.videoElement && session.videoElement.srcObject){ - session.videoElement.srcObject.getVideoTracks().forEach(function(track) { - session.videoElement.srcObject.removeTrack(track); - track.stop(); - log("stopping video track 4"); - }); - } else { - //session.videoElement.srcObject = createMediaStream(); - session.videoElement.srcObject = outboundAudioPipeline(); - } - - if (screenShareAudioTrack){ - if (session.streamSrc){ - session.streamSrc.getAudioTracks().forEach(function(track) { // previous video track; saving it. Must remove the track at some point. - if (screenShareAudioTrack.id == track.id) { // since there are more than one audio track, lets see if we can remove JUST the audio track for the screen share. - session.streamSrc.removeTrack(track); - track.stop(); - } - }); - } - if (session.streamSrcClone){ - session.streamSrcClone.getAudioTracks().forEach(function(track) { // previous video track; saving it. Must remove the track at some point. - if (screenShareAudioTrack.id == track.id) { // since there are more than one audio track, lets see if we can remove JUST the audio track for the screen share. - session.streamSrcClone.removeTrack(track); - track.stop(); - } - }); - } - screenShareAudioTrack=null; - senderAudioUpdate(); - } - - session.screenShareState = false; - pokeIframeAPI('screen-share-state', session.screenShareState, null, session.streamID); - notifyOfScreenShare(); - - - getById("screensharebutton").classList.remove("green"); - getById("screensharebutton").ariaPressed = "false"; - - if (videoOnEnd == true) { - if (beforeScreenShare) { - session.streamSrc.addTrack(beforeScreenShare); // updateRenderOutpipe - beforeScreenShare = null; - } - updateRenderOutpipe(); - toggleSettings(true); // forceshow - } else { - //session.refreshScale(); // since updateREnderOutput already has htis. - } - updateMixer(); - }; - } catch (e) { - log("No Video selected; screensharing?"); - } - - stream.getTracks().forEach(function(track) { - addScreenDevices(track); - session.streamSrc.addTrack(track, stream); // Lets not add the audio to this preview; echo can be annoying - }); - updateRenderOutpipe(); - - if (wasDisabled && stream.getVideoTracks().length && !session.videoMuted){ - var msg = {}; - msg.videoMuted = session.videoMuted; - session.sendMessage(msg); - } - - if (stream.getAudioTracks().length){ - screenShareAudioTrack = stream.getAudioTracks()[0]; - senderAudioUpdate(); - } - - session.applySoloChat(); // mute streams that should be muted if a director - session.applyIsolatedChat(); - - applyMirror(true); - - - return true; - }).catch(function(err) { - errorlog(err); - errorlog(err.name); - if ((err.name == "NotAllowedError") || (err.name == "PermissionDeniedError")) { - // User Stopped it. - if (macOS){ - warnUser(getTranslation("screen-permissions-denied"), false, false); - } - } else { - if (audio == true) { - if (err.name == "NotReadableError"){ - if (!(session.cleanOutput)){ - warnUser(getTranslation("change-audio-output-device"), false, false); - } - return false; - } else { - setTimeout(function() { - grabScreen(quality, false); - }, 1); - } - } - if (!(session.cleanOutput)) { - setTimeout(function(e) { - errorlog(e); - }, 1, err); // TypeError: Failed to execute 'getDisplayMedia' on 'MediaDevices': Audio capture is not supported - } - } - return false; - }); -} - -function toggleBufferSettings(UUID){ - //bufferSliderValue - /* try { - session.rpcs[taskItemInContext.dataset.UUID].buffer = parseInt(inputElement.value); - inputElement.title = session.rpcs[taskItemInContext.dataset.UUID].buffer + " ms"; - getById("bufferSliderValue").innerText = session.rpcs[taskItemInContext.dataset.UUID].buffer + " ms"; - playoutdelay(taskItemInContext.dataset.UUID); // trigger - } catch(e){ - errorlog(e); - */ - - - toggle(getById('bufferSettings')); - if (getById('bufferSettings').style.display=="none"){ - getById("modalBackdrop").innerHTML = ''; // Delete modal - getById("modalBackdrop").remove(); - } else { - getById("modalBackdrop").innerHTML = ''; // Delete modal - getById("modalBackdrop").remove(); - zindex = 25; - getById('bufferSettings').style.zIndex = 25; - var modalTemplate = `
`; - document.body.insertAdjacentHTML("beforeend", modalTemplate); // Insert modal at body end - document.getElementById("modalBackdrop").addEventListener("click", toggleBufferSettings); - - - var buffer = session.rpcs[UUID].buffer; - if (buffer === false){ - buffer = session.buffer || 0; - } - getById('bufferSettings').querySelectorAll("input").forEach(ele=>{ - ele.value = parseInt(buffer); - ele.title = ele.value + " ms"; - //getById("bufferSliderValue").innerText = ele.title - ele.onchange = function(e){ - session.rpcs[UUID].buffer = parseInt(e.target.value); - //getById("bufferSliderValue").innerText = session.rpcs[UUID].buffer + " ms"; - getById('bufferSettings').querySelectorAll("input").forEach(ele2=>{ - if (ele2!==e.target){ - ele2.value = parseInt(e.target.value); - } - ele2.title = parseInt(e.target.value) + " ms"; - }); - playoutdelay(UUID); // trigger - }; - ele.oninput = function(e){ - getById('bufferSettings').querySelectorAll("input").forEach(ele2=>{ - if (ele2!==e.target){ - ele2.value = parseInt(e.target.value); - } - ele2.title = parseInt(e.target.value) + " ms"; - }); - }; - }); - } -} - - -function toggleRoomSettings(){ - toggle(getById('roomSettings')); - if (getById('roomSettings').style.display=="none"){ - //getById("modalBackdrop").innerHTML = ''; // Delete modal - //getById("modalBackdrop").remove(); - } else { - //getById("modalBackdrop").innerHTML = ''; // Delete modal - //getById("modalBackdrop").remove(); - zindex = 25; - getById('roomSettings').style.zIndex = 25; - var modalTemplate = `
`; - // document.body.insertAdjacentHTML("beforeend", modalTemplate); // Insert modal at body end - // document.getElementById("modalBackdrop").addEventListener("click", toggleRoomSettings); - document.getElementById('trbSettingInput').value = session.totalRoomBitrate; - document.getElementById('trbSettingInputManual').value = session.totalRoomBitrate; - document.getElementById('trbSettingInputFeedback').innerHTML = session.totalRoomBitrate; - } -} - -function changeTRB(ele){ - session.totalRoomBitrate = parseInt(ele.value); - var msg = {}; - msg.directorSettings={}; - msg.directorSettings.totalRoomBitrate=session.totalRoomBitrate; - session.sendMessage(msg); - pokeIframeAPI('total-room-bitrate', session.totalRoomBitrate); -} - -function sendMediaDevices(UUID){ - enumerateDevices().then(function(deviceInfos){ - var data = {}; - data.UUID = UUID; - data.mediaDevices = deviceInfos; - session.sendMessage(data, data.UUID); - }); -} - -function changeVideoDevice(index, quality=0){ - enumerateDevices().then(gotDevices2).then(function() { - activatedPreview=false; - document.getElementById("videoSource3").selectedIndex = index+""; - grabVideo(quality, "videosource", "#videoSource3"); - }); -} - -function changeAudioDevice(index){ - enumerateDevices().then(gotDevices2).then(function() { - activatedPreview=false; - var audioSelect = document.getElementById("audioSource3").querySelectorAll("input"); - for (var i = 0; i < audioSelect.length; i++) { - audioSelect[i].checked = false; - } - audioSelect[index-1].checked = true; - grabAudio("#audioSource3"); - }); -} - -function changeVideoDeviceById(deviceId, UUID=false){ - enumerateDevices().then(gotDevices2).then(function() { - var opts = document.getElementById("videoSource3").options; - var index = false - for (var opt, j = 0; opt = opts[j]; j++) { - if (opt.value == deviceId) { - index = j; - break; - } - } - if (index!==false){ - if (document.getElementById("videoSource3").selectedIndex === index){ // this is just refreshing the device. - activatedPreview=false; - grabVideo(0, "videosource", "#videoSource3", UUID); - } else if (UUID && !session.consent){ - window.focus(); - confirmAlt("Allow the director to change your video device to:\n\n"+opts[index].text+" ?").then(res=>{ - if (res){ - document.getElementById("videoSource3").selectedIndex = index; - activatedPreview=false; - grabVideo(0, "videosource", "#videoSource3", UUID); - } else { - try { - var data = {}; - data.UUID = UUID; - data.rejected = "changeCamera"; - session.sendMessage(data, data.UUID); - } catch(e){} - } - }); - } else { - document.getElementById("videoSource3").selectedIndex = index; - activatedPreview=false; - grabVideo(0, "videosource", "#videoSource3", UUID); - } - } - }); -} - -function changeAudioDeviceById(deviceId, UUID=false){ - enumerateDevices().then(gotDevices2).then(function() { - var audioSelect = document.getElementById("audioSource3").querySelectorAll("input"); - var matched = false; - var exists = false; - for (var i = 0; i < audioSelect.length; i++) { - if (audioSelect[i].value == deviceId){ - exists = true; - if (audioSelect[i].checked){ - matched = true; - } - } - } - - if (exists){ - if (matched){ // this is just refreshing the device. - activatedPreview=false; - grabAudio("#audioSource3", UUID); - } else if (UUID && !session.consent){ - window.focus(); - confirmAlt("Allow the director to change your audio mic source?").then(res=>{ - if (res){ - // enumerateDevices().then(gotDevices2).then(function() { - var audioSelect = document.getElementById("audioSource3").querySelectorAll("input"); - for (var i = 0; i < audioSelect.length; i++) { - if (audioSelect[i].value == deviceId){ - audioSelect[i].checked=true; - } else { - audioSelect[i].checked = false; - } - } - activatedPreview=false; - grabAudio("#audioSource3", UUID); - // }); - } else { - try { - var data = {}; - data.UUID = UUID; - data.rejected = "changeMicrophone"; - session.sendMessage(data, data.UUID); - } catch(e){} - } - }); - } else { - //enumerateDevices().then(gotDevices2).then(function() { - var audioSelect = document.getElementById("audioSource3").querySelectorAll("input"); - for (var i = 0; i < audioSelect.length; i++) { - if (audioSelect[i].value == deviceId){ - audioSelect[i].checked=true; - } else { - audioSelect[i].checked = false; - } - } - activatedPreview=false; - grabAudio("#audioSource3", UUID); - // }); - } - } - }); -} - -function changeAudioOutputDeviceById(deviceId, UUID=false){ // remote control of the speaker output. - warnlog(deviceId); - if (document.getElementById("outputSource3")){ - enumerateDevices().then(gotDevices2).then(function() { - var index = false - if (document.getElementById("outputSource3")){ - var opts = document.getElementById("outputSource3").options; - for (var opt, j = 0; opt = opts[j]; j++) { - if (opt.value == deviceId) { - index = j; - break; - } - } - } - if (index!==false){ - if (document.getElementById("outputSource3").selectedIndex === index){ // this is just refreshing the device. - session.sink = deviceId; - saveSettings(); - resetupAudioOut(); - } else if (UUID && !session.consent){ // UUID just lets us inform the requester - window.focus(); - confirmAlt("Allow the director to change your audio's speaker to:\n\n"+opts[index].text+" ?").then(res=>{ - if (res){ - if (index!==false){ - document.getElementById("outputSource3").selectedIndex = index; - } - session.sink = deviceId; - saveSettings(); - resetupAudioOut(); - var data = {}; - data.UUID = UUID; - sendMediaDevices(data.UUID); - session.sendMessage(data, data.UUID); - } else { - try { - var data = {}; - data.UUID = UUID; - data.rejected = "changeSpeaker"; - session.sendMessage(data, data.UUID); - } catch(e){} - } - }); - } else { - if (index!==false){ - document.getElementById("outputSource3").selectedIndex = index; - } - session.sink = deviceId; - saveSettings(); - resetupAudioOut(); - } - } - }); - } else { - session.sink = deviceId; - saveSettings(); - resetupAudioOut(); - } -} - -function checkBasicStreamsExist(){ - log("checkBasicStreamsExist()"); - if (!session.streamSrc) { - session.streamSrc = createMediaStream(); - } - if (!session.videoElement) { - if (document.getElementById("videosource")) { - session.videoElement = document.getElementById("videosource"); - } else if (document.getElementById("previewWebcam")) { - session.videoElement = document.getElementById("previewWebcam"); - } else { - session.videoElement = createVideoElement(); - } - - session.videoElement.addEventListener("playing", (e)=>{ - resetupAudioOut(session.videoElement, true); - }, { once: true }); - - } - session.videoElement.srcObject = outboundAudioPipeline(); - toggleMute(true); - return session.videoElement; -} - -var getUserMediaRequestID = 0; -var grabVideoUserMediaTimeout = null; -var grabVideoTimer = null; - -async function grabVideo(quality = 0, eleName = 'previewWebcam', selector = "select#videoSourceSelect", callback = false) { - if (activatedPreview == true) { - log("activated preview return 2"); - return; - } - - if (session.miconly){return;} - - activatedPreview = true; - log("Grabbing video: " + quality); - if (grabVideoTimer) { - clearTimeout(grabVideoTimer); - } - log("element:" + eleName); - - var wasDisabled = true; - try { - if (session.streamSrc) { - - if (session.canvasWebGL){ - session.canvasWebGL.remove() - session.canvasWebGL=null; - } - - if (session.canvasSource){ - session.canvasSource.srcObject.getTracks().forEach(function(trk) { - session.canvasSource.srcObject.removeTrack(trk); - trk.stop(); - wasDisabled=false; - }); - } - if (session.streamSrc){ - session.streamSrc.getVideoTracks().forEach(function(track) { - session.streamSrc.removeTrack(track); - track.stop(); - wasDisabled=false; - }); - } - if (session.streamSrcClone){ - session.streamSrcClone.getVideoTracks().forEach(function(track) { - session.streamSrcClone.removeTrack(track); - track.stop(); - }); - } - } else { - checkBasicStreamsExist(); - log("CREATE NEW STREAM"); - } - - - if (session.videoElement && session.videoElement.srcObject) { - session.videoElement.srcObject.getVideoTracks().forEach(function(track) { - session.videoElement.srcObject.removeTrack(track); - track.stop(); - session.videoElement.load(); - wasDisabled=false; - }); - } else { - checkBasicStreamsExist(); - } - - } catch (e) { - errorlog(e); - } - - session.videoElement.controls = session.showControls || false; - - log("selector: " + selector); - var videoSelect = document.querySelector(selector); // document.querySelector("videoSource3").value == "ZZZ" - log(videoSelect); - var mirror = false; - getById("cameraTip1").classList.add("hidden"); - - if (!videoSelect || videoSelect.value == "ZZZ"){ // if there is no video, or if manually set to audio ready, then do this step. - - clearTimeout(grabVideoUserMediaTimeout); - getUserMediaRequestID += 1; - - warnlog("ZZZ SET - so no VIDEO"); - SelectedVideoInputDevices = []; - saveSettings(); - - if (session.avatar && session.avatar.ready){ - updateRenderOutpipe(); - } else if (session.mobile && !document.getElementById("keepAlivePlayer")){ // keep alive player doens't exist - - setInterval(function(){ - if (document.getElementById("keepAlivePlayer") && session.streamSrc.getVideoTracks().length){ - getById("keepAlivePlayer").remove(); - } else if (!document.getElementById("keepAlivePlayer")){ - let fakeElement = document.createElement("video"); - fakeElement.autoplay = true; - fakeElement.loop = true; - fakeElement.muted = true; - fakeElement.src = "./media/micro.mp4"; - fakeElement.style.width = "1px"; - fakeElement.style.height ="1px"; - fakeElement.controls = false; - fakeElement.id = "keepAlivePlayer"; - getById("main").appendChild(fakeElement); - } - }, 4000); - - } - - if ((eleName == "previewWebcam") && document.getElementById("previewWebcam")){ - if (session.autostart) { - publishWebcam(); // no need to mirror as there is no video... - return; - } else { - log("4462"); - updateStats(); - if (document.getElementById("gowebcam")) { - document.getElementById("gowebcam").dataset.ready = "true"; - if (document.getElementById("gowebcam").dataset.audioready == "true"){ - document.getElementById("gowebcam").disabled = false; - //document.getElementById("gowebcam").innerHTML = getTranslation("start"); - miniTranslate(document.getElementById("gowebcam"),"start"); - document.getElementById("gowebcam").focus(); - } - } - } - } else { // If they disabled the video but not in preview mode; but actualy live. We will want to remove the stream from the publishing - // we don't want to do this otherwise, as we are "replacing" the track in other cases. - // this does cause a problem, as previous bitrate settings & resolutions might not be applied if switched back.... must test - - if (session.avatar && session.avatar.ready){ - updateRenderOutpipe(); - return; - } - - if (session.chunked){ - for (UUID in session.pcs) { - session.chunkedStream(UUID); // make sure we check that this connection allows video / audio - } - return; - } - - if (session.whipOut && session.whipOut.getSenders){ - miscSenders.push(session.whipOut); - } - - miscSenders.forEach(dataRTC=>{ - if (dataRTC && dataRTC.getSenders){ - dataRTC.getSenders().forEach((sender) => { // I suppose there could be a race condition between negotiating and updating this. if joining at the same time as changnig streams? - if (sender.track && sender.track.kind == "video") { - var trk = getMeshcastCanvasTrack(dataRTC); - if (session.screenShareState && session.screenshareContentHint && (trk.kind === "video")){ - try { - trk.contentHint = session.screenshareContentHint; - } catch(e){ - errorlog(e); - } - } else if (session.contentHint && (trk.kind === "video")){ - try { - trk.contentHint = session.contentHint; - } catch(e){ - errorlog(e); - } - } - sender.replaceTrack(trk); // replace may not be supported by all browsers. eek. - } - }); - } - }); - - - for (UUID in session.pcs) { - if ("realUUID" in session.pcs[UUID]){continue;} // do not apply to screen shares. - // for any connected peer, update the video they have if connected with a video already. - var senders = getSenders2(UUID); - senders.forEach((sender) => { // I suppose there could be a race condition between negotiating and updating this. if joining at the same time as changnig streams? - if (sender.track && sender.track.kind == "video") { - sender.track.enabled = false; // I'm not entirely sure if I shoudl be doing this to a video stream... but I suppose new connections won't get a stream, and old connections will just replace it? - getById("mutevideobutton").classList.add("hidden"); // hide the mute button, so they can't unmute while no video. - //session.pcs[UUID].removeTrack(sender); // replace may not be supported by all browsers. eek. - //errorlog("DELETED SENDER"); - } - }); - } - - var msg = {}; - msg.videoMuted = true; // doesn;t matter if video is actually muted or not; no video is being sent - session.sendMessage(msg); - } - return; - } else { - - if (videoSelect && videoSelect.value){ - SelectedVideoInputDevices = [videoSelect.value]; - saveSettings(); - } - - if (session.avatar && session.avatar.timer){ - clearInterval(session.avatar.timer); - session.avatar.timer=null; - } - - var sq = 0; - if (session.quality === false) { - sq = session.quality_wb; - } else if (session.quality > 2) { // 1080, 720, and 360p - sq = 2; // hacking my own code. TODO: ugly, so I need to revisit this. - } else { - sq = session.quality; - } - - if (session.director && (quality !== false)){ // URL-based quality won't matter if DIRECTOR; - // quality = quality; - } else if ((quality === false) || (quality < sq)) { - quality = sq; // override the user's setting - } - - if ((iOS || iPad) && SafariVersion<15) { // iOS will not work correctly at 1080p; likely a h264 codec issue. - if (quality == 0) { - quality = 1; - } - } - - var constraints = { - audio: false, - video: getUserMediaVideoParams(quality, (iOS || iPad)) - }; - - //if (Firefox){ - // constraints.video.height = constraints.video.height.ideal; - // constraints.video.width = constraints.video.height.ideal; - //} - - log("Quality selected:" + quality); - - if (session.facingMode){ - constraints.video.facingMode = { exact: session.facingMode }; // user or environment - } else if (iOS || iPad) { - constraints.video.deviceId = { - exact: videoSelect.value - }; // iPhone 6s compatible ? Needs to be exact for iPhone 6s - - } else if (Firefox){ // is firefox. - constraints.video.deviceId = { - exact: videoSelect.value - }; // Firefox is a dick. Needs it to be exact. - - } else if (videoSelect.options[videoSelect.selectedIndex].text.includes("NDI Video")) { // NDI does not like "EXACT" - constraints.video.deviceId = videoSelect.value; // NDI is fucked up - } else { - constraints.video.deviceId = { - exact: videoSelect.value - }; // Default. Should work for Logitech, etc. - } - - if (session.width) { - constraints.video.width = { - exact: session.width - }; // manually specified - so must be exact - } - if (session.height) { - constraints.video.height = { - exact: session.height - }; - } - - if (session.frameRate) { - constraints.video.frameRate = { - exact: session.frameRate - }; - } else if (session.maxframeRate != false){ - constraints.video.frameRate = { - ideal: session.maxframeRate, - max: session.maxframeRate - }; - } else if ((iOS || iPad) && (SafariVersion>15)) { // iOS supports 720p60, but just 1080p30 : iphone 11 on march 2023 - if (quality === 1) { // iphone 11 and older - if (!constraints.video.frameRate){ - constraints.video.frameRate = { - ideal: 60, - max: 60 - }; - } - } else if (iPhone12Up && (quality<1)){ // iphone 12 and up? - if (!constraints.video.frameRate){ - try { - if (videoSelect.options[videoSelect.selectedIndex].innerText.startsWith("Back ")){ // front seems to be limited to 720p60 / 1080p30 - constraints.video.frameRate = { - ideal: 60, - max: 60 - }; - } - } catch(e){ - errorlog(e); - } - } - } - } - - - - if (session.ptz){ - if (constraints.video && constraints.video!==true){ - if (ChromiumVersion && ChromiumVersion>80){ - constraints.video.pan=true; - constraints.video.tilt=true; - constraints.video.zoom=true; - } - } - } - - if (session.forceAspectRatio){ // await updateCameraConstraints("aspectRatio", session.forceAspectRatio); - if (constraints.video && constraints.video!==true){ - - constraints.video.aspectRatio = { ideal: parseFloat(session.forceAspectRatio)}; - - - if (constraints.video.width && !session.width){ - delete constraints.video.width; - } else if (constraints.video.height && !session.height){ - delete constraints.video.height; - } - } - } - - var obscam = false; - var mirrorcheck = false; - log(videoSelect.options[videoSelect.selectedIndex].text); - - if (!videoSelect.options[videoSelect.selectedIndex]){ - if (session.mobile){ - mirrorcheck = true; - mirror = false; - } else { - mirror = false; - } - } else if (videoSelect.options[videoSelect.selectedIndex].text.startsWith("OBS-Camera")) { // OBS Virtualcam - mirror = true; - obscam = true; - } else if (videoSelect.options[videoSelect.selectedIndex].text.startsWith("OBS Virtual Camera")) { // OBS Virtualcam - mirror = true; - obscam = true; - } else if (videoSelect.options[videoSelect.selectedIndex].text.startsWith("Streamlabs ")) { // OBS Virtualcam - mirror = true; - obscam = true; - } else if (videoSelect.options[videoSelect.selectedIndex].text.startsWith("Dummy video device")) { // Linuxv - mirror = true; - } else if (videoSelect.options[videoSelect.selectedIndex].text.startsWith("vMix Video")) { // vMix - mirror = true; - } else if (videoSelect.options[videoSelect.selectedIndex].text.startsWith("Blackmagic")) { // Blackmagic devices - mirror = true; - } else if (videoSelect.options[videoSelect.selectedIndex].text.startsWith("screen-capture-recorder")) { // screen-capture-recorder - mirror = true; - } else if (videoSelect.options[videoSelect.selectedIndex].text.includes(" back")) { // Android - mirror = true; - } else if (videoSelect.options[videoSelect.selectedIndex].text.includes(" rear")) { // Android - mirror = true; - } else if (videoSelect.options[videoSelect.selectedIndex].text.includes("NDI Video")) { // NDI Virtualcam - mirror = true; - } else if (videoSelect.options[videoSelect.selectedIndex].text.startsWith("Back Camera")) { // iPhone and iOS - mirror = true; - } else if (videoSelect.options[videoSelect.selectedIndex].text.toLowerCase().includes("c922")) { - if ((session.quality!==2) && !session.cleanOutput){ - //getById("cameraTipContext1").innerHTML = getTranslation("camera-tip-c922"); - miniTranslate(getById("cameraTipContext1"),"camera-tip-c922"); - getById("cameraTip1").classList.remove("hidden"); - } - } else if (videoSelect.options[videoSelect.selectedIndex].text.toLowerCase().includes("cam link")) { - if (!session.cleanOutput){ - //getById("cameraTipContext1").innerHTML = getTranslation("camera-tip-camlink"); - miniTranslate(getById("cameraTipContext1"),"camera-tip-camlink"); - getById("cameraTip1").classList.remove("hidden"); - } - - } else if (session.mobile){ - mirrorcheck = true; - mirror = false; - } else { - mirror = false; - } - - if (SamsungASeries && ChromiumVersion){ - if (!session.cleanOutput){ - //getById("cameraTipContext1").innerHTML = getTranslation("samsung-a-series"); - miniTranslate(getById("cameraTipContext1"),"samsung-a-series"); - getById("cameraTip1").classList.remove("hidden"); - } - } - if (session.nomirror){ // do not have the camera be mirrored by default, unless using &mirror - session.mirrorExclude = true; - } else { - session.mirrorExclude = mirror; - } - - if (constraints.video && (constraints.video!==true) && (Object.keys(constraints.video).length==0)){ - constraints.video = true; - } else if (constraints.video && (constraints.video!==true) && (Object.keys(constraints.video).length==1) && ("deviceId" in constraints.video) && ("exact" in constraints.video.deviceId) && (constraints.video.deviceId.exact==="default")){ - constraints.video = true; // solves issues with IOS, where no permission yet given - can't request device ID it seems until permissions is given. - } - - log(constraints); - clearTimeout(grabVideoUserMediaTimeout); - getUserMediaRequestID += 1; - var gumMediaID = getUserMediaRequestID; - var delayStart = 100; - if (ChromiumVersion>110){ // aded july 16th; speed up camera switching. - delayStart = 20; - } else if (Firefox){ - delayStart = 500; // cause firefox is buggy as crap - } - grabVideoUserMediaTimeout = setTimeout(function(gumID, callback2) { - if (getUserMediaRequestID !== gumID) {return;} // cancel - navigator.mediaDevices.getUserMedia(constraints).then(function(stream) { - - if (getUserMediaRequestID !== gumID) { - warnlog("GET USER MEDIA CALL HAS EXPIRED"); - stream.getTracks().forEach(function(track) { - stream.removeTrack(track); - track.stop(); - log("stopping old track"); - }); - return; - } - log("adding video tracks 2412"); - - stream.getVideoTracks().forEach(function(track) { - - try{ - if (mirrorcheck){ - try { - var capabilities = track.getCapabilities(); - } catch(e){ - var capabilities = {}; - } - if ("facingMode" in capabilities){ - if (capabilities.facingMode == "environment"){ - session.mirrorExclude = true; - } - } - if ("backgroundBlur" in capabilities){ // Chrome original trial, until v117, and then??? - query('#effectSelector option[value="13"]').classList.remove("hidden"); - query('#effectSelector option[value="13"]').disabled = null; - query('#effectSelector3 option[value="13"]').classList.remove("hidden"); - query('#effectSelector3 option[value="13"]').disabled = null; - } else { - query('#effectSelector option[value="13"]').disabled = true; - query('#effectSelector3 option[value="13"]').disabled = true; - } - } - } catch(e){} - - session.streamSrc.addTrack(track); // tracks previously removed. - - try{ - track.onended = function(e) { // hurrah! - warnlog(e); - refreshVideoDevice(); - } - } catch(e){errorlog(e);} - - if (session.mobile){ - if (!(iPad || iOS || Firefox)){ - try{ - applySavedVideoSettings(track); - } catch(e){errorlog(e);} - } - } - }); - - if (Firefox && !FirefoxEnumerated){ - if (session.streamSrc && session.streamSrc.getTracks().length){ - FirefoxEnumerated=true; - enumerateDevices().then(gotDevices); - } - } - - - updateRenderOutpipe(); - // senderAudioUpdate - - if (wasDisabled && !session.videoMuted){ - var msg = {}; - msg.videoMuted = session.videoMuted; - session.sendMessage(msg); - } - - applyMirror(session.mirrorExclude); - - session.videoElement.play().then(() => { - log("start play doublecheck"); - }); - - if ((eleName == "previewWebcam") && document.getElementById("previewWebcam")){ - if (session.autostart) { - publishWebcam(); - } else { - log("4620"); - if (document.getElementById("gear_webcam")) { - updateStats(obscam); - } - if (document.getElementById("gowebcam")) { - document.getElementById("gowebcam").dataset.ready = "true"; - if (document.getElementById("gowebcam").dataset.audioready == "true"){ - document.getElementById("gowebcam").disabled = false; - //document.getElementById("gowebcam").innerHTML = getTranslation("start"); - miniTranslate(document.getElementById("gowebcam"),"start"); - document.getElementById("gowebcam").focus(); - } - } - } - } else if (getById("gear_webcam3").style.display === "inline-block") { - updateStats(obscam); - } - - // Once crbug.com/711524 is fixed, we won't need to wait anymore. This is - // currently needed because capabilities can only be retrieved after the - // device starts streaming. This happens after and asynchronously w.r.t. - // getUserMedia() returns. - if (grabVideoTimer) { - clearTimeout(grabVideoTimer); - if ((eleName == "previewWebcam") && document.getElementById("previewWebcam")){ - session.videoElement.controls = true; - } - } - if (getById("popupSelector_constraints_video")) { - getById("popupSelector_constraints_video").innerHTML = ""; - } - if (getById("popupSelector_constraints_audio")) { - getById("popupSelector_constraints_audio").innerHTML = ""; - } - if (getById("popupSelector_constraints_loading")) { - getById("popupSelector_constraints_loading").style.display = ""; - } - - if (iOS || iPad){ // TEMPORARY: iOS 15.3 beta fix - toggleSpeakerMute(true); - } - if (!((eleName == "previewWebcam") || document.getElementById("previewWebcam"))){ - updateMixer(); // not with the preview, but after. - } - - pokeIframeAPI('local-camera-event'); - - let grabVideoPostTimeoutValue = 1000; - - if (Firefox || session.mobile){ // wait longer for these; they are more likely to crash if too quick. - grabVideoPostTimeoutValue = 2000; - } - - grabVideoTimer = setTimeout(async function(callback3, gumid) { - - if (getUserMediaRequestID !== gumid) { // new camera selected in this time. - return; - } - makeImages(true); - - if (getById("popupSelector_constraints_loading")) { - getById("popupSelector_constraints_loading").style.display = "none"; - } - if ((eleName == "previewWebcam") && document.getElementById("previewWebcam")){ - session.videoElement.controls = true; - try { - var track0 = session.streamSrc.getVideoTracks(); - if (track0.length) { - track0 = track0[0]; - if (track0.getCapabilities) { - session.cameraConstraints = track0.getCapabilities(); - } else { - session.cameraConstraints = {}; - } - log(session.cameraConstraints); - if (track0.getSettings) { - session.currentCameraConstraints = track0.getSettings(); - - - if (screen && screen.orientation && screen.orientation.type){ - if (screen.orientation.type.includes("portrait")){ - if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ - session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; - } - } - } else if (window.matchMedia("(orientation: portrait)").matches){ - if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ - session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; - } - } - } else { - session.currentCameraConstraints = {}; - } - log(session.currentCameraConstraints); - } - } catch (e) { - errorlog(e); - } - } else if (toggleSettingsState) { - log("16047"); - updateConstraintSliders();//listCameraSettings(); - } - if (callback3){ - try { - var data = {}; - data.UUID = callback3; - data.videoOptions = listVideoSettingsPrep(); - sendMediaDevices(data.UUID); - session.sendMessage(data, data.UUID); - } catch(e){} - } - - if (iOS || iPad){ // TEMPORARY: iOS 15.3 beta fix - toggleSpeakerMute(true); - } - - if (session.forceAspectRatio){ - await updateCameraConstraints("aspectRatio", session.forceAspectRatio); - } - - updateForceRotate(); // this contains session.setResolution(); - - if (iOS || iPad){ - // if we don't do this, portrait videos may be detected as horizontal - if (!document.getElementById("previewWebcam")){ - updateMixer(); // not with the preview, but after. - } - } - - try { - if (session.pip3){ - if (!eleName.pip){ - eleName.pip=true; - toggleSystemPip(session.videoElement, true); - } - } - } catch(e){} - - // this will reset scaling for all viewers of this stream. I also call it when aspect ratio, width, or height is changed via applyConstraints - - dragElement(session.videoElement); - }, grabVideoPostTimeoutValue, callback2, gumID); // focus - - log("DONE - found stream"); - }).catch(function(e) { - - - if (getUserMediaRequestID !== gumID) { - warnlog("the previously selected camera attempted failed, but not a big deal, since its now void"); - return; - } - - warnlog(e); - if (e.name === "OverconstrainedError") { - warnlog(e.message || e); - log("Resolution or frameRate didn't work"); - } else if (e.name === "NotReadableError"){ - if (quality <= 10) { - activatedPreview = false; - grabVideo(quality + 1, eleName, selector); - } else if (session.facingMode){ - session.facingMode = false; - activatedPreview = false; - grabVideo(false, eleName, selector); // restart. - } else { - if (!(session.cleanOutput)) { - if (iOS) { - warnUser("An error occured. Closing existing tabs in Safari may solve this issue."); - } else { - warnUser("Error: Could not start video source.\n\nTypically this means the Camera is already be in use elsewhere. Most webcams can only be accessed by one program at a time.\n\nTry a different camera or perhaps try re-plugging in the device."); - } - } - activatedPreview = true; - if (getById('gowebcam')) { - getById('gowebcam').innerHTML = "Problem with Camera"; - } - - } - return; - } else if (e.name === "NavigatorUserMediaError") { - if (getById('gowebcam')) { - getById('gowebcam').innerHTML = "Problem with Camera"; - } - if (!(session.cleanOutput)) { - warnUser("Unknown error: 'NavigatorUserMediaError'"); - } - return; - } else if (e.name === "timedOut") { - activatedPreview = true; - if (getById('gowebcam')) { - getById('gowebcam').innerHTML = "Problem with Camera"; - } - if (!(session.cleanOutput)) { - warnUser(e.message); - } - return; - } else { - errorlog("An unknown camera error occured"); - } - - if (quality <= 10) { - activatedPreview = false; - grabVideo(quality + 1, eleName, selector); - } else if (session.facingMode){ - session.facingMode = false; - activatedPreview = false; - grabVideo(false, eleName, selector); // restart. - } else { - errorlog("********Camera failed to work"); - activatedPreview = true; - if (getById('gowebcam')) { - getById('gowebcam').innerHTML = "Problem with Camera"; - } - if (!(session.cleanOutput)) { - if (session.width || session.height || session.frameRate) { - warnUser(" Camera failed to load.\n\nPlease ensure your camera supports the resolution and frameRate that has been manually specified. Perhaps use &quality=0 instead.", false, false); - } else { - warnUser(" Camera failed to load.\n\nPlease make sure it is not already in use by another application.\n\nPlease make sure you have accepted the camera permissions.", false, false); - } - } - } - }); - }, delayStart, gumMediaID, callback); - } -} - -function updateRenderOutpipe(){ // video only. - log("updateRenderOutpipe()"); - - if (session.canvasWebGL){ - session.canvasWebGL.remove() - session.canvasWebGL=null; - } - - if (session.canvasSource){ - session.canvasSource.srcObject.getTracks().forEach(function(trk) { - session.canvasSource.srcObject.removeTrack(trk); - //trk.stop(); - }); - } - - if (session.videoElement && session.videoElement.srcObject) { - session.videoElement.srcObject.getVideoTracks().forEach(function(track) { - session.videoElement.srcObject.removeTrack(track); - //track.stop(); - //session.videoElement.load(); - }); - } else { - checkBasicStreamsExist(); - } - - if (session.streamSrc){ - var tracks = session.streamSrc.getVideoTracks(); - - if (!tracks.length || session.videoMuted){ - tracks = setAvatarImage(tracks); - if (tracks.length){ - - if (tracks.length && !session.cleanOutput && !session.cleanish){ - getById("mutevideobutton").classList.remove("hidden"); - } - - tracks.forEach(function(track) { - session.videoElement.srcObject.addTrack(track); - if (session.avatar && session.avatar.tracks){ - var msg = {}; - msg.videoMuted = false; // doesn't matter actual mute state, since its the avatar - session.sendMessage(msg); - } else { - toggleVideoMute(true); - } - pushOutVideoTrack(track); // video only - }); - } else { - var msg = {}; - msg.videoMuted = true; - session.sendMessage(msg); - session.videoElement.load(); - getById("mutevideobutton").classList.add("hidden"); - } - } else if (tracks.length){ - applyMirror(session.mirrorExclude); - tracks.forEach(function(track) { - track = applyEffects(track); // updates with the correct track session.streamSrc - session.videoElement.srcObject.addTrack(track); - toggleVideoMute(true); - pushOutVideoTrack(track); // video only - }); - - if (tracks.length && !session.cleanOutput && !session.cleanish){ - getById("mutevideobutton").classList.remove("hidden"); - } - } - } -} - -function pushOutVideoTrack(track){ - log("pushOutVideoTrack"); - - pokeIframeAPI('push-video-track', track.id, false, session.streamID); // (action, value = null, UUID = null, SID=null) - - if (session.chunked){ - for (UUID in session.pcs) { - session.chunkedStream(UUID); // I need to update chunkedStream with the current track? If sstype=3, then skip this - } - return; - } - - if (session.audioContentHint && (track.kind === "audio")){ // why am I pushing an audio track? - errorlog("this shouldn't occur, since only video tracks are expected"); - try { - track.contentHint = session.audioContentHint; - } catch(e){ - errorlog(e); - } - } - if (session.screenShareState && session.screenshareContentHint && (track.kind === "video")){ // I need to check if this is actually a screenshare before setting the hint (sstype=3) - try { - track.contentHint = session.screenshareContentHint; - } catch(e){ - errorlog(e); - } - } else if (session.contentHint && (track.kind === "video")){ - try { - track.contentHint = session.contentHint; - } catch(e){ - errorlog(e); - } - } - - - if (session.whipOut && session.whipOut.getSenders){ // should only be 0 or 1 video sender, ever. - //var added = false; - session.whipOut.getSenders().forEach((sender) => { // I suppose there could be a race condition between negotiating and updating this. if joining at the same time as changnig streams? - if (sender.track && sender.track.kind == "video") { - errorlog("Replacing track"); - sender.replaceTrack(track); // replace may not be supported by all browsers. eek. - //sender.track.enabled = true; - //added = true; - } - }); - } - - for (UUID in session.pcs){ - var videoAdded = false; - try { - if ("realUUID" in session.pcs[UUID]){continue;} - if ((session.pcs[UUID].guest == true) && (session.roombitrate === 0)) { - log("room rate restriction detected. No videos will be published to other guests"); - } else if (session.pcs[UUID].allowVideo == true) { // allow - - // for any connected peer, update the video they have if connected with a video already. - var added = false; - var senders = getSenders2(UUID); - senders.forEach((sender) => { // I suppose there could be a race condition between negotiating and updating this. if joining at the same time as changnig streams? - if (added) { - return; - } - if (sender.track && sender.track.kind == "video") { - sender.replaceTrack(track); // replace may not be supported by all browsers. eek. - log("Track replaced"); - log(track); - sender.track.enabled = true; - added = true; - } - }); - if (added == false) { - videoAdded = true; - session.pcs[UUID].addTrack(track, session.videoElement.srcObject); // can't replace, so adding - setTimeout(function(uuid){session.optimizeBitrate(uuid);},session.rampUpTime, UUID); // 3 seconds lets us ramp up the quality a bit and figure out the total bandwidth quicker - } - } - } catch (e) { - errorlog(e); - } - - if (iOS || iPad){ ///////// THIS IS A FIX FOR iOS 15.4. When a video is loaded (view/push), the bitrate from iOS devices is stuck low, and resolution needs toggle to fix. - // videoAdded value needs to be deleted from above also - if (SafariVersion && (SafariVersion<=13)){ - // - } else if (videoAdded){ - setTimeout(function(uuid){ - session.setScale(uuid, null); - }, 2000, UUID); - setTimeout(function(uuid){ - var scale = 100;session.setScale - if (session.pcs[uuid].scale){ - scale = session.pcs[uuid].scale; - } - session.setScale(uuid, scale); - },5000, UUID); - } - } - } - if (track.kind === "audio"){ - session.applyIsolatedChat(); - } - session.refreshScale(); -} - - -async function grabAudio(selector = "#audioSource", trackid = null, override = false, callbackUUID = false, callback = false) { // trackid is the excluded track , callback is UUID - - - if (activatedPreview == true) { - log("activated preview return 2"); - return; - } - activatedPreview = true; - log("TRACK EXCLUDED:" + trackid); - - try { - var audioSelect = document.querySelector(selector).querySelectorAll("input"); - var audioExcludeList = []; - for (var i = 0; i < audioSelect.length; i++) { - try { - if ("screen" == audioSelect[i].dataset.type) { // skip already excluded ---------- !!!!!! DOES THIS MAKE SENSE? TODO: CHECK - if (audioSelect[i].checked) { - audioExcludeList.push(audioSelect[i]); - } - } - } catch (e) { - errorlog(e); - } - } - } catch (e) { - errorlog(e); - } - - try { - if (session.videoElement && session.videoElement.srcObject) { - session.videoElement.srcObject.getAudioTracks().forEach(function(track) { // TODO: Confirm that I even need this? - for (var i = 0; i < audioExcludeList.length; i++) { - try { - if (audioExcludeList[i].label == track.label) { - warnlog("DONE"); - return; - } - } catch (e) {errorlog(e);} - } - if (trackid && (track.id == trackid)) { - warnlog("SKIPPED EXCLUDED TRACK?"); - return; - } - session.videoElement.srcObject.removeTrack(track); - track.stop(); // remove then stop. - }); - } else { // if no stream exists - checkBasicStreamsExist(); - } - } catch (e) { - errorlog(e); - } - - try { - if (session.streamSrc){ - session.streamSrc.getAudioTracks().forEach(function(track) { - for (var i = 0; i < audioExcludeList.length; i++) { - try { - if (audioExcludeList[i].label == track.label) { - warnlog("EXCLUDING TRACK; PROBABLY SCREEN SHARE"); - return; - } - } catch (e) {errorlog(e);} - } - if (trackid && (track.id == trackid)) { - warnlog("SKIPPED EXCLUDED TRACK?"); - return; - } - session.streamSrc.removeTrack(track); - track.stop(); - }); - } else { // if no stream exists - checkBasicStreamsExist(); - } - } catch (e) { - errorlog(e); - } - - try { - if (session.streamSrcClone){ - session.streamSrcClone.getAudioTracks().forEach(function(track) { - for (var i = 0; i < audioExcludeList.length; i++) { - try { - if (audioExcludeList[i].label == track.label) { - warnlog("EXCLUDING TRACK; PROBABLY SCREEN SHARE"); - return; - } - } catch (e) {errorlog(e);} - } - if (trackid && (track.id == trackid)) { - warnlog("SKIPPED EXCLUDED TRACK?"); - return; - } - session.streamSrcClone.removeTrack(track); - track.stop(); - }); - } - } catch (e) { - errorlog(e); - } - - var streams = await getAudioOnly(selector, trackid, override); // Get audio streams - - try { - log("STREAMS: "+streams.length); - - for (var i = 0; i < streams.length; i++) { - streams[i].getAudioTracks().forEach(function(track) { - try { - session.streamSrc.addTrack(track); // add video track to the preview video - - track.onended = function(){ - errorlog("Track ended unexpectedly"); - if (!session.cleanOutput){ - toggleSettings(true); // forceshow - } - }; - log("ok?"); - // applySavedAudioSettings(track); ## this doesn't work as echo-cancellation(+) needs to be applied via getuserMedia only. - } catch(e){ - errorlog(e); - } - }); - } - } catch(e){errorlog(e);} - - if (Firefox && !FirefoxEnumerated){ - if (session.streamSrc && session.streamSrc.getTracks().length){ - FirefoxEnumerated=true; - enumerateDevices().then(gotDevices); - } - } - - if (callback){ - callback(); - } - senderAudioUpdate(callbackUUID); -} - -session.toggleSoloChat = function(UUID, event=false){ // ==> applyIsolatedChat -- this should be trigger by the director only I think - - if (session.director){ - if (!session.directorEnabledPPT){ - warnUser("Enable the director's microphone first.",2000); - return false; - } - } - - if (Firefox){ - warnlog("Solo talk support for Firefox is currently experimental"); - } - - var msg = {}; - msg.micIsolate = false; - - if (session.soloChatUUID.includes(UUID)){ // already added, so lets toggle off - session.soloChatUUID.splice(session.soloChatUUID.indexOf(UUID), 1); // Toggles. Adds target to soloChatUUID list - msg.lowerVolume = false; - } else { - session.soloChatUUID.push(UUID); //not added, so lets toggle on - msg.lowerVolume = true; - } - - if (event){ - if ( event.ctrlKey || event.metaKey){ - if (session.soloChatUUID.includes(UUID)){ - msg.micIsolate = 1; - } - } - } - - session.sendRequest(msg, UUID); - - log(session.soloChatUUID); - - var ele = document.querySelector('[data-action-type="solo-chat"][data--u-u-i-d="'+UUID+'"]'); // [data--u-u-i-d="'+UUID+'"] // this all just updates the buttons - log(ele); - - var ret = 0; - - if (session.soloChatUUID.includes(UUID)){ - if (msg.micIsolate){ - ret = 2; - ele.classList.add("altpress"); // we will do this later. - ele.value = 2; - } else { - ret = 1; - ele.value = 1; - } - } else { - ele.classList.remove("pressed"); ele.ariaPressed = "false"; - ele.classList.remove("altpress"); - ele.value = 0; - } - - session.applySoloChat(false); - return ret - -}; -/////////////////////// - -session.togglePrivateChat = function(ele){ - var msg = {}; - warnlog(ele); - if (ele.value == 0) { - msg.micIsolate = true; - ele.value = 1; - ele.classList.add("pressed"); ele.ariaPressed = "true"; - } else { - msg.micIsolate = false; - ele.value = 0; - ele.classList.remove("pressed"); ele.ariaPressed = "false"; - } - session.sendRequest(msg, ele.dataset.UUID); - warnlog(msg); -}; - -// we call this via session.applyIsolatedChat, just in case -session.applyIsolatedVolume = function(){ // mutes outbound mic audio; for guests, and not the director - - var i = session.lowerVolume.length; - while (i--){ - if (!(session.lowerVolume[i] in session.rpcs)){ // clean up dead connections - session.lowerVolume.splice(i, 1); - } - } - - var soloMode = false; - - /* if (!(session.cleanOutput)){ - if (session.lowerVolume.length){ - getById("header").classList.add('orange'); - getById("head6").classList.remove('hidden'); - } else if (session.audioGain === 0){ - // do nothing. - } else { - getById("header").classList.remove('orange'); - getById("head6").classList.add('hidden'); - } - } */ - - if (session.lowerVolume.length){ - soloMode = true; - } - - if (soloMode){ - for (var UUID in session.rpcs){ - if (session.lowerVolume.includes(UUID)){ - if (session.rpcs[UUID].videoElement && (session.rpcs[UUID].savedVolume!==false)){ // isolated - session.rpcs[UUID].videoElement.volume = session.rpcs[UUID].savedVolume; - session.rpcs[UUID].savedVolume = false; - } - continue; - } - if (session.rpcs[UUID].videoElement && (session.rpcs[UUID].savedVolume==false)){ // not isolated - session.rpcs[UUID].savedVolume = session.rpcs[UUID].videoElement.volume; - session.rpcs[UUID].videoElement.volume = session.rpcs[UUID].savedVolume*0.25; - } - } - } else { - for (var UUID in session.rpcs){ - if (session.rpcs[UUID].videoElement && (session.rpcs[UUID].savedVolume!==false)){ // isolated - session.rpcs[UUID].videoElement.volume = session.rpcs[UUID].savedVolume; - session.rpcs[UUID].savedVolume = false; - } - } - } -} - -session.applyIsolatedChat = function(UUID=false){ // mutes outbound mic audio; for guests, and not the director - log("applyIsolatedChat"); - session.applyIsolatedVolume(); // this toggle the speaker output - - var i = session.micIsolated.length; - while (i--){ - if (!(session.micIsolated[i] in session.pcs) && !(session.micIsolated[i] in session.rpcs)){ - session.micIsolated.splice(i, 1); - } - } - - var muteList = [...session.micIsolated]; // one thing I hate about Javascript. Doesn't actually copy arrays. - var soloMode = false; - - if (session.micIsolatedAutoMute){ // session.micIsolatedAutoMute - soloMode = true; - session.micIsolatedAutoMute.forEach(uid =>{ - if (!muteList.includes(uid) && (uid in session.rpcs || uid in session.pcs)){ - muteList.push(uid); - } - }); - } - - if (muteList.length){ - soloMode = true; - } - - if (!(session.cleanOutput)){ - if (soloMode){ - getById("header").classList.add('orange'); - getById("head6").classList.remove('hidden'); - } else if (session.audioGain === 0){ - // do nothing. - } else { - getById("header").classList.remove('orange'); - getById("head6").classList.add('hidden'); - } - } - - ///// - if (session.directorSpeakerMuted!==null){ - for (var uuid in session.rpcs){ - try{ - var receivers = getReceivers2(uuid);//session.rpcs[uuid].getReceivers(); - for (var i=0; i { - if (!sender.track){return;} - if (sender.track.kind !== "audio"){return;} - - var settings = {}; - if (!soloMode){ - settings.active = true; - session.pcs[UUID].audioMutedOverride = false; - } else if (muteList.indexOf(UUID)>=0){ - settings.active = true; - session.pcs[UUID].audioMutedOverride = false; - } else { - log("MUTING via session.applyIsolatedChat"); - settings.active = false; - session.pcs[UUID].audioMutedOverride = true - } - setEncodings(sender, settings); - }); - } catch(e){errorlog(e);} - } else { - for (var UUID in session.pcs){ - try { - var senders = getSenders2(UUID); - senders.forEach((sender) => { - if (!sender.track){return;} - if (sender.track.kind !== "audio"){return;} - - var settings = {}; - if (!soloMode){ - settings.active = true; - session.pcs[UUID].audioMutedOverride = false; - } else if (muteList.indexOf(UUID)>=0){ - settings.active = true; - session.pcs[UUID].audioMutedOverride = false; - } else { - log("MUTING via session.applyIsolatedChat"); - settings.active = false; - session.pcs[UUID].audioMutedOverride = true; - } - setEncodings(sender, settings); - }); - } catch(e){errorlog(e);} - } - } -} - -var FirefoxSenders = {}; - -function setEncodings(sender, settings=null, callback=null, cbarg=null){ - if (!settings){ - if (!(sender.encodingsQueue)){ // not set - return; - } else if (!sender.encodingsQueue.length){ // none left - return; - } - } else if (!("encodingsQueue" in sender)){ - sender.encodingsQueue = [[settings, callback, cbarg]]; - } else { - sender.encodingsQueue.push([settings, callback, cbarg]); - } - - if (sender.encodingsQueueActive){return;} - - try { - sender.encodingsQueueActive = true; // we're now busy. - - var options = sender.encodingsQueue.shift(); - settings = options[0]; - callback = options[1]; - cbarg = options[2]; - - const params = sender.getParameters(); - if (!params.encodings || (params.encodings.length==0)){ - params.encodings = [{}]; - } - var changed = false; - for (var setting in settings){ - if (settings[setting]===null){ - if (setting in params.encodings[0]){ - delete params.encodings[0][setting]; - changed = true - } - } else { - if (setting in params.encodings[0]){ - if (params.encodings[0][setting] !== settings[setting]){ - changed = true - } - } else { - changed = true - } - params.encodings[0][setting] = settings[setting]; - } - } - - log(settings); - - // if old Firefox, see if I can do something other than Active? - - if (!changed && !Firefox && !SafariVersion){ - log("SET ENCODINGS MATCH INPUT; skipping"); - if (callback){ - if (cbarg){ - setTimeout(function(){callback(cbarg);},0); - } else { - setTimeout(function(){callback();},0); - } - } - sender.encodingsQueueActive = false; - setEncodings(sender); - return; - } - - if (Firefox && !(Firefox >=110)){ // https://developer.mozilla.org/en-US/docs/Web/API/RTCRtpEncodingParameters now supported in v110, but old versions will need this function still - if ("active" in settings){ - warnlog("Firefox does not support track active state. We will use enable/disable for that instead."); - if (FirefoxSenders.sender){ - if (FirefoxSenders.sender.lastState === false){ - FirefoxSenders.sender.activeState = settings.active; - // already set to false, so should stay disabled - } else { - FirefoxSenders.sender.activeState = settings.active; - sender.track.enabled = settings.active; // either true or false - } - } else { - FirefoxSenders.sender = {lastState: sender.track.enabled, activeState: settings.active}; - sender.track.enabled = settings.active; - } - - delete settings.active; - if (!Object.keys(settings).length){ - if (callback){ - if (cbarg){ - setTimeout(function(){callback(cbarg);},0); - } else { - setTimeout(function(){callback();},0); - } - } - log("COMPELTED FIREFOX SET ENCODINGS"); - sender.encodingsQueueActive = false; - setEncodings(sender); - return; - } - } - } else if (Firefox){ // Firefox , all versions, don't support active state with audio yet.?? GAhhhhhhhh! - if (("track" in sender) && ("kind" in sender.track) && (sender.track.kind == "audio")){ - if ("active" in settings){ - warnlog("Firefox does not support track active state with AUDIO yet... We will use enable/disable for that instead."); - if (FirefoxSenders.sender){ - if (FirefoxSenders.sender.lastState === false){ - FirefoxSenders.sender.activeState = settings.active; - // already set to false, so should stay disabled - } else { - FirefoxSenders.sender.activeState = settings.active; - sender.track.enabled = settings.active; // either true or false - } - } else { - FirefoxSenders.sender = {lastState: sender.track.enabled, activeState: settings.active}; - sender.track.enabled = settings.active; - } - - delete settings.active; - if (!Object.keys(settings).length){ - if (callback){ - if (cbarg){ - setTimeout(function(){callback(cbarg);},0); - } else { - setTimeout(function(){callback();},0); - } - } - log("COMPELTED FIREFOX SET ENCODINGS"); - sender.encodingsQueueActive = false; - setEncodings(sender); - return; - } - } - - } - } - - sender.setParameters(params).then(() => { - if (callback){ - if (cbarg){ - setTimeout(function(){callback(cbarg);},0); - } else { - setTimeout(function(){callback();},0); - } - } - sender.encodingsQueueActive = false; - setEncodings(sender); - }).catch((e)=>{ - errorlog(e); - sender.encodingsQueueActive = false; - setEncodings(sender); - }); - } catch(e){ - errorlog(e); - sender.encodingsQueueActive = false; - } -} - -session.applySoloChat = function(apply=true){ // mutes outbound mic audio; ;; does the actual solo chat muting for the director - if (session.director===false){ - session.applyIsolatedChat(); - return; - } else if (!session.directorEnabledPPT){ - return; - } - - log("applySoloChat()"); - - var i = session.soloChatUUID.length; - while (i--){ - if (!(session.soloChatUUID[i] in session.pcs)){ - session.soloChatUUID.splice(i, 1); - log("splicing out: "+i); - } - } - - for (var uuid in session.pcs){ // not sure what to do here wrt to screen tracks - try { - var senders = getSenders2(uuid); - senders.forEach((sender) => { - if (!sender.track){return;} - if (sender.track.kind !== "audio"){return;} - - var settings = {}; - - if (session.soloChatUUID.length && (session.soloChatUUID.includes(uuid))){ - settings.active = true; - setEncodings(sender, settings, function(uid){ - log("2: "+uid); - try { - document.querySelectorAll('[data-action-type="solo-chat"][data--u-u-i-d="'+uid+'"]')[0].classList.add("pressed"); - document.querySelectorAll('[data-action-type="solo-chat"][data--u-u-i-d="'+uid+'"]')[0].ariaPressed = "true"; - document.querySelectorAll('[data-action-type="solo-chat"][data--u-u-i-d="'+uid+'"]')[0].classList.remove("hint"); - } catch(e){ - warnlog(e); - } - }, uuid); - - - } else if (session.soloChatUUID.length==0){ - settings.active = true; - setEncodings(sender, settings, function(uid){ - log(uid); - try { - document.querySelectorAll('[data-action-type="solo-chat"][data--u-u-i-d="'+uid+'"]')[0].classList.remove("pressed"); - document.querySelectorAll('[data-action-type="solo-chat"][data--u-u-i-d="'+uid+'"]')[0].ariaPressed = "false"; - document.querySelectorAll('[data-action-type="solo-chat"][data--u-u-i-d="'+uid+'"]')[0].classList.remove("hint"); - } catch(e){ - warnlog(e); - } - }, uuid); - } else { - settings.active = false; - setEncodings(sender, settings, function(uid){ - warnlog("muted the output to:"+ uid); - try { - document.querySelectorAll('[data-action-type="solo-chat"][data--u-u-i-d="'+uid+'"]')[0].classList.remove("pressed"); - document.querySelectorAll('[data-action-type="solo-chat"][data--u-u-i-d="'+uid+'"]')[0].ariaPressed = "false"; - document.querySelectorAll('[data-action-type="solo-chat"][data--u-u-i-d="'+uid+'"]')[0].classList.add("hint"); - } catch(e){ - warnlog(e); - } - }, uuid); - - } - }); - - } catch(e){errorlog(e);} - } - if (apply==false){ - if (session.soloChatUUID.length){ - session.muted_savedState=session.muted; - session.muted=false; - data = {}; - data.muteState = session.muted; - for (var i=0;i{ - try { - trk.contentHint = session.audioContentHint; - } catch(e){ - errorlog(e); - } - }); - } - - if (session.whipOut && session.whipOut.getSenders && tracks.length){ // mixMinus won't work with meshcast, so don't bother. - session.whipOut.getSenders().forEach((sender) => { // I suppose there could be a race condition between negotiating and updating this. if joining at the same time as changnig streams? - if (sender.track && sender.track.kind == "audio") { - tracks.forEach(trk=>{ - sender.replaceTrack(trk); - }) - } - }); - } - - for (UUID in session.pcs) { - if ("realUUID" in session.pcs[UUID]){continue;} // do not process the screen share audio - if (session.pcs[UUID].allowAudio == true) { - var senders = getSenders2(UUID); - if (session.mixMinus){ - log("mixMinus START .."); - var STRM = mixMinusAudio(UUID); - if (!STRM){continue;} - STRM.getAudioTracks().forEach(trk=>{ - - if (session.audioContentHint){ - trk.contentHint = session.audioContentHint; - } - - var added = false; - senders.forEach((sender) => { - if (added) { - if (sender.track && (sender.track.kind == "audio")){ - sender.track.enabled = false; - } - return; - } - if (sender.track && (sender.track.kind == "audio")) { - sender.replaceTrack(trk); - sender.track.enabled = true; - added = true; - warnlog("ADDED 5"); - } - }); - if (added) { - return; - } - session.pcs[UUID].addTrack(trk, STRM); - }); - continue; - } - senders.forEach((sender) => { - var good = false; - if (sender.track && sender.track.id && (sender.track.kind == "audio")) { - tracks.forEach(function(track) { // audio also - if (track.id == sender.track.id) { - good = true; - } - }); - } else { // video or something else; ignore it. - return; - } - if (good) { - return; - } - sender.track.enabled = false; - //session.pcs[UUID].removeTrack(sender); // Apparently removeTrack causes renogiation; also kills send/recv. - }); - - if (tracks.length) { - tracks.forEach(function(track) { - var matched = false; - var senders = getSenders2(UUID); - senders.forEach((sender) => { - if (sender.track && sender.track.id && (sender.track.kind == "audio")) { - warnlog(sender.track.id + " " + track.id); - if (sender.track.id == track.id) { - warnlog("MATCHED 1"); - matched = true; - } - } - }); - if (matched) { - return; - } - var added = false; - var senders = getSenders2(UUID); - senders.forEach((sender) => { - if (added) { - return; - } - if (sender.track && (sender.track.kind == "audio") && (sender.track.enabled == false)) { - sender.replaceTrack(track); - sender.track.enabled = true; - added = true; - warnlog("ADDED 2"); - } - }); - if (added) { - return; - } - var sender = session.pcs[UUID].addTrack(track, session.videoElement.srcObject); - }); - } else { - var senders = getSenders2(UUID); - senders.forEach((sender) => { - if (sender.track && sender.track.kind == "audio") { - sender.track.enabled = false; // (trying this instead) - //session.pcs[UUID].removeTrack(sender); // Apparently removeTrack causes renogiation; also kills send/recv. - } - }); - } - } - } - if (session.director!==false){ - session.applySoloChat(); // mute streams that should be muted if a director - } - session.applyIsolatedChat(); - - try { - if (toggleSettingsState){ - updateConstraintSliders(); - } - } catch(e){} - - if (callback){ - try{ - var data = {}; - data.UUID = callback; - data.audioOptions = listAudioSettingsPrep(); - sendMediaDevices(data.UUID); - session.sendMessage(data, data.UUID); - } catch(e){} - } - } catch (e) { - errorlog(e); - } - if (document.getElementById("gowebcam")) { - document.getElementById("gowebcam").dataset.audioready = true; - if (document.getElementById("gowebcam").dataset.ready && (document.getElementById("gowebcam").dataset.ready=="true")){ - document.getElementById("gowebcam").disabled = false; - miniTranslate(document.getElementById("gowebcam"),"start"); - document.getElementById("gowebcam").focus(); - } - } -} - -async function press2talk(clean = false) { - var ele = getById("press2talk"); - ele.style.minWidth = "127px"; - ele.style.padding = "7px"; - getById("settingsbutton").classList.remove("hidden"); - - if (!document.getElementById("controls_director") && session.showDirector){ - createDirectorOnlyBox(); - } - - if (session.taintedSession){ - var msg = {}; - msg.virtualHangup = false; - session.sendMessage(msg); - } - - log("DIRECTOR STREAM SETUP"); - - if (getById("press2talk").dataset.enabled == true){log("already enabled");return;} - getById("press2talk").dataset.enabled = true; - getById("press2talk").outerHTML = ""; - getById("mutebutton").classList.remove("hidden"); - getById("hangupbutton2").classList.remove("hidden"); - - if (!session.showDirector && (session.recordLocal!==false)){ - getById("recordLocalbutton").classList.remove("hidden"); - } - - if (session.screenshareType===3){ - getById("screenshare3button").className = "float"; - getById("screensharebutton").className = "float hidden"; - getById("screenshare2button").className = "float hidden"; - } else if (session.screenshareType===1){ - getById("screensharebutton").className = "float"; - getById("screenshare3button").className = "float hidden"; - getById("screenshare2button").className = "float hidden"; - } else if (session.screenshareType===2){ - getById("screenshare2button").className = "float"; - getById("screensharebutton").className = "float hidden"; - getById("screenshare3button").className = "float hidden"; - } else if (session.broadcast===null){ - // sstype=1, since in self-broadcast mode - getById("screensharebutton").className = "float"; - getById("screenshare2button").className = "float hidden"; - getById("screenshare3button").className = "float hidden"; - } else { - // sstype=3, since not in broadcast mode - getById("screensharebutton").className = "float hidden"; - getById("screenshare2button").className = "float hidden"; - getById("screenshare3button").className = "float"; - } - - - checkBasicStreamsExist(); - session.videoElement.id = "videosource"; // could be set to UUID in the future - session.videoElement.dataset.menu = "context-menu-video"; - - if (session.streamID){ - session.videoElement.dataset.sid = session.streamID; - } - - - // videosource - session.videoElement.muted = true; - session.videoElement.autoplay = true; - session.videoElement.controls = session.showControls || false; - session.videoElement.setAttribute("playsinline",""); - - if (document.getElementById("videoContainer_director")){ - getById("videoContainer_director").appendChild(session.videoElement); - } else { - getById("miniPerformer").appendChild(session.videoElement); - } - - if (session.screenShareElement && document.getElementById("videoScreenContainer_director")){ - getById("videoScreenContainer_director").appendChild(session.screenShareElement); - } else if (session.screenShareElement){ - getById("miniPerformer").appendChild(session.screenShareElement); - } - - session.videoElement.title = "This is the preview of the Director's audio and video output."; - - session.videoElement.onpause = (event) => { // prevent things from pausing; human or other - - if (!((event.ctrlKey) || (event.metaKey) )){ - log("Video paused; auto playing"); - event.currentTarget.play().then(_ => { - log("playing 9"); - }).catch(warnlog); - } - }; - - session.videoElement.addEventListener('click', function(e) { // show stats of video if double clicked - log("click"); - try { - if ((e.ctrlKey)||(e.metaKey)){ - e.preventDefault(); - - //////////////////////// - - var [menu, innerMenu] = statsMenuCreator(); - - ////////////////////////////////// - - menu.interval = setInterval(printMyStats,session.statsInterval, innerMenu); - printMyStats(innerMenu); - e.stopPropagation(); - - return false; - } - } catch(e){errorlog(e);} - }); - - updatePushId(); - - - /* if (session.directorEnabledPPT){ - enumerateDevices().then(gotDevices).then(async function() { - console.log("done"); - toggleSettings(); - }); - - return; - } */ - //await toggleSettings(); - - var constraint = {video: false, audio: true}; - - if (session.videoDevice){ - constraint.video = true; - } - if (session.audioDevice===0){ - constraint.audio = false; - } - requestBasicPermissions(constraint, function(){ - log("requestBasicPermissions done"); - enumerateDevices().then(gotDevices).then(async function() { - log("enumerateDevices+gotDevices complete"); - - pokeIframeAPI('director-share', true, false, session.streamID); // director has started publishing; even if no audio/video. - - log("session.directorEnabledPPT: " +session.directorEnabledPPT); - - if (session.directorEnabledPPT){ - return; - } - - if (session.audioDevice!==0){ // change from Auto to Selected Audio Device - log("SETTING AUDIO DEVICE!!"); - activatedPreview = false; - await grabAudio("#audioSource3"); - - } - - - - if (session.videoDevice !== 0) { - activatedPreview = false; - if (session.quality !== false) { - await grabVideo(session.quality, 'videosource', "#videoSource3"); - } else { - //session.quality_wb = parseInt(getById("webcamquality").elements.namedItem("resolution").value); - await grabVideo(session.quality_wb || 0, 'videosource', "#videoSource3"); - } - } - - - if (session.videoMutedFlag){ - session.videoMuted = true; - toggleVideoMute(true); - } - - session.directorEnabledPPT = true; - toggleMute(true); - - //await toggleSettings(); - - log("session.seeding: " +session.seeding); - - if (session.seeding){ - setTimeout(function(){meshcast()},1000); - return; - } - - if (session.autorecord || session.autorecordlocal){ - log("AUTO RECORD START"); - setTimeout(function(v){ - - var videoKbps = 4000; - if (session.recordLocal !== false) { - videoKbps = session.recordLocal; - } - - if (session.director){ - recordVideo(document.querySelector("[data-action-type='recorder-local'][data-sid='"+session.streamID+"']"), null, videoKbps) - } else if (v.stopWriter || v.recording){ - - } else if (v.startWriter){ - v.startWriter(); - } else { - recordLocalVideo(null, videoKbps, v) - } - },2000, session.videoElement); - } - - session.seeding=true; - await session.seedStream(); - //meshcast(); - }); - }); -}; // publishdirector - -function statsMenuCreator(){ - if (getById("menuStatsBox")){ - clearInterval(getById("menuStatsBox").interval); - getById("menuStatsBox").remove(); - } - - var menu = document.createElement("div"); - menu.id = "menuStatsBox"; - menu.className = "debugStats remotestats"; - getById('main').appendChild(menu); - - menu.style.left = parseInt(Math.random()*10)+15+"px" - menu.style.top = parseInt(Math.random()*10)+"px" - - menu.innerHTML="

Statistics

"; - var menuCloseBtn = document.createElement("button"); - menuCloseBtn.className="close"; - menuCloseBtn.innerHTML="×"; - menu.appendChild(menuCloseBtn); - - var innerMenu = document.createElement("div"); - menu.appendChild(innerMenu); - - menuCloseBtn.addEventListener('click', function(eve) { - clearInterval(menu.interval); - eve.currentTarget.parentNode.remove(); - eve.preventDefault(); - eve.stopPropagation(); - }); - return [menu, innerMenu]; -} - - -// WEBCAM -session.publishStream = function(v){ // stream is used to generated an SDP - log("STREAM SETUP"); - - if (session.transcript){ - setTimeout(function(){setupClosedCaptions();},1000); - } - - if (!session.streamSrc){ - checkBasicStreamsExist(); - } - - session.streamSrc.oninactive = function streamoninactive() { - warnlog('Stream inactive'); - if (session.videoElement.recording){ - session.videoElement.recorder.stop(); - } - }; - - if (session.streamSrc.getVideoTracks().length==0){ - warnlog("NO VIDEO TRACK INCLUDED"); - } - - if (session.streamSrc.getAudioTracks().length==0){ - warnlog("NO AUDIO TRACK INCLUDED"); - } - - - var container = document.createElement("div"); - v.container = container; - container.id = "container"; - - - if (session.cleanOutput){ - container.style.height = "100%"; - v.style.maxWidth = "100%"; - v.style.boxShadow = "none"; - } - - if (session.cover){ - container.style.setProperty('height', '100%', 'important'); - } - - //container.className = "vidcon"; - getById("gridlayout").appendChild(container); - - v.className = "tile"; //"tile task"; TODO: get working (will add task later on instead) - - - v.muted = true; - v.autoplay = true; - if (session.showControls!==null){ - v.controls = session.showControls; - } else if (session.mobile){ - v.controls = true; - } else { - v.controls = session.showControls || false; - } - v.setAttribute("playsinline",""); - v.id = "videosource"; // could be set to UUID in the future - v.oncanplay = null; - - session.videoElement = v; - - container.appendChild(v); - - toggleMute(true); - - if (session.nopreview){ - v.style.display="none"; - container.style.display="none"; - } - - - if (((session.roomid===false || session.roomid==="") && (session.quality===false)) || session.forceMediaSettings){ - try { - if ((session.quality_wb!==false) && (session.quality===false)){ - getById("webcamquality3").elements.namedItem("resolution").value = session.quality_wb; - } else if (session.quality!==false){ - getById("webcamquality3").elements.namedItem("resolution").value = session.quality; - } - getById("gear_webcam3").style.display = "inline-block"; - getById("webcamquality3").onchange = function(event) { - if (parseInt(getById("webcamquality3").elements.namedItem("resolution").value) == 2) { - if (session.maxframeRate===false){ - session.maxframeRate = 30; - session.maxframeRate_q2 = true; - } - } else if (session.maxframeRate_q2){ - session.maxframeRate = false; - session.maxframeRate_q2 = false; - } - activatedPreview = false; - session.quality_wb = parseInt(getById("webcamquality3").elements.namedItem("resolution").value); - grabVideo(session.quality_wb, "videosource", "select#videoSource3"); - }; - } catch (e) {errorlog(e);} - } - - - var bigPlayButton = document.getElementById("bigPlayButton"); - if (bigPlayButton){ - bigPlayButton.parentNode.removeChild(bigPlayButton); - } - - if (session.streamID){ - session.videoElement.dataset.sid = session.streamID; - } - - if (session.statsMenu){ - var [menu, innerMenu] = statsMenuCreator(); - menu.interval = setInterval(printMyStats,session.statsInterval, innerMenu); - printMyStats(innerMenu); - } - - if (session.director){ // the director doesn't load a webcam by default anyways. - // audio is not mucked with - } else if (session.scene!==false){ // it's a scene, and there are no previews in a scene. - //setTimeout(function(){updateMixer();},10); - } else if (session.roomid!==false){ - if (session.roomid===""){ - if (!session.view || (session.view==="")){ - if (session.fullscreen){ - session.windowed = false; - } else { - v.className = "myVideo"; //"myVideo task"; TODO: get working - session.windowed = true; - container.classList.add("vidcon"); - } - getById("mutespeakerbutton").classList.add("hidden"); - - applyMirror(session.mirrorExclude); - - container.style.width="100%"; - //container.style.height="100%"; - - container.style.alignItems = "center"; - container.backgroundColor = "#666"; - - setTimeout(function (){dragElement(v);},1000); - play(); - } else { - session.windowed = false; - applyMirror(session.mirrorExclude); - play(); - //setTimeout(function(){updateMixer();},10); - } - } else { - //session.cbr=0; // we're just going to override it - if (session.stereo==5){ // not a scene or director, so we will assume its a guest. changing to stereo=3 - session.stereo=3; - } - session.windowed = false; - applyMirror(session.mirrorExclude); - - if (session.include.length){ - play(); - } - - //setTimeout(function(){updateMixer();},10); - } - } else { - - if (session.fullscreen){ - session.windowed = false; - } else { - v.className = "myVideo"; //"myVideo task"; TODO: get working - container.classList.add("vidcon"); - session.windowed = true; - } - getById("mutespeakerbutton").classList.add("hidden"); - - applyMirror(session.mirrorExclude); - - container.style.width="100%"; - //container.style.height="100%"; - //container.style.display = "flex"; - - container.style.alignItems = "center"; - container.backgroundColor = "#666"; - - setTimeout(function (){dragElement(v);},1000); - - } - - v.onpause = (event) => { // prevent things from pausing; human or other - if (!((event.ctrlKey) || (event.metaKey) )){ - log("Video paused; auto playing"); - event.currentTarget.play().then(_ => { - log("playing 10"); - }).catch(warnlog); - } - }; - - v.addEventListener('click', function(e) { - log("click"); - try { - if ((e.ctrlKey)||(e.metaKey)){ - e.preventDefault(); - - var [menu, innerMenu] = statsMenuCreator(); - - menu.interval = setInterval(printMyStats,session.statsInterval, innerMenu); - - printMyStats(innerMenu); - e.stopPropagation(); - return false; - } - } catch(e){errorlog(e);} - }); - - v.touchTimeOut = null; - v.touchLastTap = 0; - v.touchCount = 0; - - v.addEventListener('touchend', function(event) { - if (session.disableMouseEvents){return;} - log("touched"); - - //document.ontouchup = null; - //document.onmouseup = null; - document.onmousemove = null; - document.ontouchmove = null; - - var currentTime = new Date().getTime(); - var tapLength = currentTime - v.touchLastTap; - clearTimeout(v.touchTimeOut); - if (tapLength < 500 && tapLength > 0) { - /// - log("double touched"); - v.touchCount+=1; - event.preventDefault(); - if (v.touchCount<5){ - v.touchLastTap = currentTime; - return false; - } - v.touchLastTap = 0; - v.touchCount=0; - - var [menu, innerMenu] = statsMenuCreator(); - - menu.interval = setInterval(printMyStats,session.statsInterval, innerMenu); - - printMyStats(innerMenu); - event.stopPropagation(); - return false; - ////// - } else { - v.touchCount=1; - v.touchLastTap = currentTime; - - v.touchTimeOut = setTimeout(function(vv) { - clearTimeout(vv.touchTimeOut); - vv.touchLastTap = 0; - vv.touchCount=0; - }, 5000, v); - - } - - }); - - updateReshareLink(); - pokeIframeAPI('started-camera'); // depreciated - pokeIframeAPI('camera-share', true); - - if (session.videoMutedFlag){ - session.videoMuted = true; - toggleVideoMute(true); - } - - if (!gotDevices2AlreadyRan){ - enumerateDevices().then(gotDevices2); // this is needed for iOS; was previous set to timeout at 100ms, but would be useful everywhere I think - } - - v.dataset.menu = "context-menu-video"; - if (!session.cleanOutput){ - v.classList.add("task"); // this adds the right-click menu - } - - session.postPublish(); - - if (session.autorecord || session.autorecordlocal){ - log("AUTO RECORD START"); - setTimeout(function(v){ - var videoKbps = 4000; - if (session.recordLocal !== false) { - videoKbps = session.recordLocal; - } - - if (session.director){ - recordVideo(document.querySelector("[data-action-type='recorder-local'][data-sid='"+session.streamID+"']"), null, videoKbps) - } else if (v.stopWriter || v.recording){ - - } else if (v.startWriter){ - v.startWriter(); - } else { - recordLocalVideo(null, videoKbps, v) - } - },2000, v); - } - - setTimeout(function(){updateMixer();},10); - -}; // publishStream - -function stickyMessage(message){ - var textOverlay = getById("stickyMsgs"); - if (textOverlay) { - var spanOverlay = document.createElement("span"); - spanOverlay.innerHTML = message; - var closeBtn = document.createElement("button"); - closeBtn.className = "overlayCloseBtn"; - closeBtn.innerText = "X"; - closeBtn.onclick = function(){this.parentNode.remove();}; - textOverlay.appendChild(spanOverlay); - spanOverlay.appendChild(closeBtn); - textOverlay.classList.remove("hidden"); - } -} - -session.postPublish = async function(){ - log("Post publish"); - if (session.welcomeMessage){ - stickyMessage(session.welcomeMessage); - // getChatMessage(session.welcomeMessage, false, true, true); - } - - if (session.welcomeImage){ - var welcomeoverlay = document.createElement("img"); - welcomeoverlay.src = session.welcomeImage; - welcomeoverlay.className = "welcomeOverlay"; - document.body.appendChild(welcomeoverlay); - await sleep(2000); - setTimeout(function(welcomeoverlay){ - welcomeoverlay.style = "animation: fadeout 1s;" - setTimeout(function(welcomeoverlay){ - welcomeoverlay.remove(); - },990,welcomeoverlay); - }, 1000, welcomeoverlay); - } - - if (session.welcomeHTML){ - var welcomeHTML = document.createElement("div"); - welcomeHTML.innerHTML = session.welcomeHTML; - welcomeHTML.className = "welcomeOverlay"; - document.body.appendChild(welcomeHTML); - setTimeout(function(welcomeHTML){ - welcomeHTML.style = "animation: fadeout 1s;" - setTimeout(function(welcomeHTML){ - welcomeHTML.remove(); - },990,welcomeHTML); - }, 3000, welcomeHTML); - } - - clearInterval(session.updateLocalStatsInterval); - session.updateLocalStatsInterval = setInterval(function(){updateLocalStats();},session.statsInterval); - - pokeIframeAPI("screen-share-state", false); - - session.seeding=true; - session.seedStream(); - - if (session.whipOutput){ - whipOut(); - } - if (session.whepHost){ - whepOut(); - } - -} - - -async function publishScreen2(constraints, audioList=[], audio=true, overrideFramerate=false){ // webcam stream is used to generated an SDP - log("SCREEN SHARE SETUP"); - - if (!navigator.mediaDevices.getDisplayMedia){ - setTimeout(function(){ - if (iOS || iPad){ - warnUser("Sorry, but your iOS browser does not support screen-sharing.\n\nPlease see this guide for an alternative method to do so.", false, false); - } else if (session.mobile){ - warnUser("Sorry, your browser does not support screen-sharing.\n\nThe Android native app should support it though.", false, false); - } else { - warnUser("Sorry, your browser does not support screen-sharing.\n\nPlease use the desktop versions of Firefox or Chrome instead."); - } - },1); - return false; - } - if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1) { - if (!ElectronDesktopCapture){ - if (!(session.cleanOutput && session.cleanish==false)){ - warnUser("Enable Elevated Privileges to allow screen-sharing. (right click this window to see that option)"); - } - return false; - } - } - - var streams = []; - for (var i=1; i{ - if (getUserMediaRequestID !== gumID) { - warnlog("GET USER MEDIA CALL HAS EXPIRED 3"); - stream.getTracks().forEach(function(track) { - stream.removeTrack(track); - track.stop(); - log("stopping old track"); - }); - return; - } - streams.push(stream); - }).catch(errorlog); - } - } - - if (session.audioDevice === 0 ){ - constraints.audio = false; - } - - if (session.screenshareVideoOnly){ - constraints.audio = false; - } - - if ((constraints.video!==false) && (Object.keys(constraints.video).length==0)){ - constraints.video = true; - } - - - log(constraints); - getUserMediaRequestID+=1; - var gumID = getUserMediaRequestID; - return navigator.mediaDevices.getDisplayMedia(constraints).then(async function (stream){ - if (getUserMediaRequestID !== gumID) { - warnlog("GET USER MEDIA CALL HAS EXPIRED 3"); - stream.getTracks().forEach(function(track) { - stream.removeTrack(track); - track.stop(); - log("stopping old track"); - }); - return; - } - - try { - var constraint = {}; - - if (session.forceAspectRatio && (session.forceScreenShareAspectRatio===null)){ - constraint.aspectRatio = parseFloat(session.forceAspectRatio); - } else if (session.forceScreenShareAspectRatio){ - constraint.aspectRatio = parseFloat(session.forceScreenShareAspectRatio); - } - if (overrideFramerate){ - constraint.frameRate = overrideFramerate; - } - if (Object.keys(constraint).length){ - await stream.getVideoTracks()[0].applyConstraints({ - advanced: [constraint] - }); - log({ - advanced: [constraint] - }); - } - } catch(e){errorlog(e);} - - /// RETURN stream for preview? rather than jumping right in. - session.screenShareState=true; - pokeIframeAPI('screen-share-state', session.screenShareState, null, session.streamID); - notifyOfScreenShare(); - - try { - stream.getVideoTracks()[0].onended = function () { - toggleScreenShare(); - }; - } catch(e){log("No Video selected; screensharing?");} - - // OR, jump right in, and let user change from there - if (session.roomid!==false){ - if ((session.roomid==="") && ((!(session.view)) || (session.view===""))){ - - } else { - getById("head3").classList.add('hidden'); - getById("head3a").classList.add('hidden'); - log("ROOMID EANBLED"); - log("Update Mixer Event on REsize SET"); - window.onresize = updateMixer; - window.onorientationchange = function(){ - if (Firefox){ - updateForceRotate(true); - } - setTimeout(async function(){ - if (session.forceAspectRatio){ - await updateCameraConstraints("aspectRatio", session.forceAspectRatio); - } - if (session.effect && (session.effect === "7")){digitalZoom(true);} - updateForceRotate(); - updateMixer(); - }, 200); - }; - joinRoom(session.roomid); - } - } else { - getById("head3").classList.remove('hidden'); - getById("head3a").classList.remove('hidden'); - getById("logoname").style.display = 'none'; - } - - updatePushId(); - - if (stream.getAudioTracks().length){ - screenShareAudioTrack = stream.getAudioTracks()[0]; - } - - log("adding tracks"); - for (var i=0; i{ - stream.addTrack(track); - }); - } - streams = null; - if (!session.screenshareVideoOnly && session.audioDevice !== 0){ - if (stream.getAudioTracks().length==0){ - if (!(session.cleanOutput)){ - if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1){ - // Electron has no audio. - } else { - setTimeout(function(){warnUser(getTranslation("no-audio-source-detected"));},300); - } - } - } - } - - - try { - session.streamSrc = stream; - } catch (e){errorlog(e);} - toggleMute(true); - - var v = createVideoElement(); - session.videoElement = v; - - if (session.streamID){ - session.videoElement.dataset.sid = session.streamID; - } - - var container = document.createElement("div"); - v.container = container; - container.id = "container_screen"; - container.style.height = "100%"; - - if (session.cleanOutput){ - v.style.maxWidth = "100%"; - v.style.boxShadow = "none"; - } - - //container.className = "vidcon"; - getById("gridlayout").appendChild(container); - - if (session.nopreview){ - v.style.display="none"; - container.style.display="none"; - } - - //if (session.cover){ - // container.style.setProperty('height', '100%', 'important'); - //} - - container.appendChild(v); - - v.className = "tile"; - - if (session.director){ - } else if (session.scene!==false){ - setTimeout(function(){updateMixer();},1); - } else if (session.roomid!==false){ - if (session.roomid===""){ - if (!(session.view) || (session.view==="")){ - - getById("mutespeakerbutton").classList.add("hidden"); - - if (session.fullscreen){ - session.windowed = false; - if (session.mirrored && session.flipped){ - v.style.transform = " scaleX(-1) scaleY(-1)"; - v.classList.add("mirrorControl"); - } else if (session.mirrored){ - v.style.transform = "scaleX(-1)"; - v.classList.add("mirrorControl"); - } else if (session.flipped){ - v.style.transform = "scaleY(-1)"; - v.classList.remove("mirrorControl"); - } else { - v.style.transform = ""; - v.classList.remove("mirrorControl"); - } - } else { - v.className = "myVideo"; - session.windowed = true; - if (session.mirrored && session.flipped){ - v.style.transform = " scaleX(-1) scaleY(-1) translate(0, 50%)"; - v.classList.add("mirrorControl"); - } else if (session.mirrored){ - v.style.transform = "scaleX(-1) translate(0, -50%)"; - v.classList.add("mirrorControl"); - } else if (session.flipped){ - v.style.transform = "scaleY(-1) translate(0, 50%)"; - v.classList.remove("mirrorControl"); - } else { - v.style.transform = " translate(0, -50%)"; - v.classList.remove("mirrorControl"); - } - } - - container.style.width="100%"; - //container.style.height="100%"; - container.style.alignItems = "center"; - container.backgroundColor = "#666"; - - setTimeout(function (){dragElement(v);},1000); - play(); - } else { - play(); - setTimeout(function(){updateMixer();},1); - } - } else { - setTimeout(function(){updateMixer();},1); - } - } else { - - getById("mutespeakerbutton").classList.add("hidden"); - if (session.fullscreen){ - session.windowed = false; - if (session.mirrored && session.flipped){ - v.style.transform = " scaleX(-1) scaleY(-1)"; - v.classList.add("mirrorControl"); - } else if (session.mirrored){ - v.style.transform = "scaleX(-1)"; - v.classList.add("mirrorControl"); - } else if (session.flipped){ - v.style.transform = "scaleY(-1)"; - v.classList.remove("mirrorControl"); - } else { - v.style.transform = ""; - v.classList.remove("mirrorControl"); - } - } else { - v.className = "myVideo"; - session.windowed = true; - container.classList.add("vidcon"); - if (session.mirrored && session.flipped){ - v.style.transform = " scaleX(-1) scaleY(-1) translate(0, 50%)"; - v.classList.add("mirrorControl"); - } else if (session.mirrored){ - v.style.transform = "scaleX(-1) translate(0, -50%)"; - v.classList.add("mirrorControl"); - } else if (session.flipped){ - v.style.transform = "scaleY(-1) translate(0, 50%)"; - v.classList.remove("mirrorControl"); - } else { - v.style.transform = " translate(0, -50%)"; - v.classList.remove("mirrorControl"); - } - } - - container.style.width="100%"; - //container.style.height="100%"; - container.style.alignItems = "center"; - container.backgroundColor = "#666"; - } - - if (!session.windowed){ - window.onresize = updateMixer; - window.onorientationchange = function(){ - if (Firefox){ - updateForceRotate(true); - } - setTimeout(async function(){ - if (session.forceAspectRatio){ - await updateCameraConstraints("aspectRatio", session.forceAspectRatio); - } - if (session.effect && (session.effect === "7")){digitalZoom(true);} - updateForceRotate(); - updateMixer(); - }, 200); - }; - } - - v.autoplay = true; - v.controls = session.showControls || false; - v.setAttribute("playsinline",""); - v.muted = true; - v.id = "videosource"; - v.dataset.menu = "context-menu-video"; - - if (!session.cleanOutput){ - v.classList.add("task"); // this adds the right-click menu - } - - //if (!v.srcObject || v.srcObject.id !== stream.id) { - // v.srcObject = stream; - v.srcObject = outboundAudioPipeline(); - //} - - v.onpause = (event) => { // prevent things from pausing; human or other - if (!((event.ctrlKey) || (event.metaKey) )){ - log("Video paused; auto playing"); - event.currentTarget.play().then(_ => { - log("playing 11"); - }).catch(warnlog); - } - }; - - v.addEventListener('click', function(e) { // show stats of video if double clicked - log("click"); - try { - if ((e.ctrlKey)||(e.metaKey)){ - e.preventDefault(); - - var [menu, innerMenu] = statsMenuCreator(); - - menu.interval = setInterval(printMyStats,session.statsInterval, innerMenu); - - printMyStats(innerMenu); - e.stopPropagation(); - return false; - } - } catch(e){errorlog(e);} - }); - - updateReshareLink(); - - if (session.videoMutedFlag){ - session.videoMuted = true; - toggleVideoMute(true); - } - - clearInterval(session.updateLocalStatsInterval); - session.updateLocalStatsInterval = setInterval(function(){updateLocalStats();},session.statsInterval); - - session.seeding=true; - session.seedStream(); - - //pokeIframeAPI('started-screenshare'); // depreciated - pokeIframeAPI('screen-share-state', true, null, session.streamID); // (action, value = null, UUID = null, SID=null) - - if (session.autorecord || session.autorecordlocal){ - log("AUTO RECORD START"); - setTimeout(function(v){ - var videoKbps = 4000; - if (session.recordLocal !== false) { - videoKbps = session.recordLocal; - } - - if (session.director){ - recordVideo(document.querySelector("[data-action-type='recorder-local'][data-sid='"+session.streamID+"']"), null, videoKbps) - } else if (v.stopWriter || v.recording){ - - } else if (v.startWriter){ - v.startWriter(); - } else { - recordLocalVideo(null, videoKbps, v) - } - },2000, v); - } - - return true; - }).catch(function(err){ - errorlog(err); - errorlog(err.name); - if ((err.name == "NotAllowedError") || (err.name == "PermissionDeniedError")){ - // User Stopped it. (is this next part needed??) - session.screenShareState=false; - pokeIframeAPI('screen-share-state', session.screenShareState, null, session.streamID); - notifyOfScreenShare(); - - if (macOS){ - warnUser(getTranslation("screen-permissions-denied"), false, false); - } - return false; - } else { - if (audio==true){ - if (err.name == "NotReadableError"){ - if (!(session.cleanOutput)){ - warnUser(getTranslation("change-audio-output-device"), false, false); - } - return false; - } else { - constraints.audio=false; - if (!(session.cleanOutput)){ - setTimeout(function(){warnUser(err);},1); // TypeError: Failed to execute 'getDisplayMedia' on 'MediaDevices': Audio capture is not supported - } - return publishScreen2(constraints, audioList, false); - } - } else { - if (!(session.cleanOutput)){ - setTimeout(function(){warnUser(err);},1); // TypeError: Failed to execute 'getDisplayMedia' on 'MediaDevices': Audio capture is not supported - } - return false; - } - } - }); -}; // publishStream2 - -var transferList = []; -var msgTransferList = []; - -function cancelFile(ele){ - var idx = ele.dataset.tid; - try{ - transferList[idx].dc.close(); - } catch(e){} - transferList[idx].status = 5; - updateDownloadLink(idx); -} - -function requestFile(ele){ - var idx = ele.dataset.tid; - transferList[idx].status = 1; - - var fid = ele.dataset.fid; - var UUID = ele.dataset.uuid; - var msg = {}; - msg.requestFile = fid; - msg.UUID = UUID; - session.sendRequest(msg, msg.UUID); - - updateDownloadLink(idx); - pokeIframeAPI('request-file', fid, UUID); -} - -function clearDownloadFile(ele){ - var idx = ele.dataset.tid; - transferList[idx].status = 6; - updateDownloadLink(idx); -} - -function addDownloadLink(fileList, UUID, pc){ - if (session.nodownloads){return;} // downloads are blocked - log(fileList); - if (!fileList || !fileList.length){return;} - for (var i = 0; i< fileList.length; i++){ - fileList[i].UUID = UUID; - fileList[i].completed = 0; - fileList[i].status = 0; - fileList[i].time = Date.now(); - fileList[i].pc = pc[UUID]; - transferList.push(fileList[i]); - } - - if (session.chatbutton===false){return;} // messages can still appear as overlays - - updateMessages(); - - if (session.beepToNotify) { - playtone(); - } - - if (session.chat == false) { - getById("chattoggle").className = "las la-comments toggleSize pulsate"; - getById("chatbutton").className = "float"; - - if (getById("chatNotification").value) { - getById("chatNotification").value = getById("chatNotification").value + 1; - } else { - getById("chatNotification").value = 1; - } - getById("chatNotification").classList.add("notification", "red"); - } - - //if (session.broadcastChannel !== false) { - // session.broadcastChannel.postMessage(data); /* send */ - //} -} - -function updateDownloadLink(idx){ - idx = parseInt(idx); - var elements = document.querySelectorAll('[data-tid="'+idx+'"]'); - if (elements[0]) { - if (transferList[idx].status === 0){ - elements[0].innerHTML = "Download it here"; - } else if (transferList[idx].status === 1){ - elements[0].innerHTML = "Requested"; - //elements[0].onclick='cancelFile(this);' - } else if (transferList[idx].status === 2){ - elements[0].innerHTML = "Downloading: "+parseInt(transferList[idx].completed*100)+"%"; - elements[0].onclick = function(){cancelFile(this);} - } else if (transferList[idx].status === 3){ - elements[0].innerHTML = "Completed"; - elements[0].onclick = null; - elements[0].disabled = true; - } else if (transferList[idx].status === 4){ - elements[0].innerHTML = "No longer available"; - elements[0].onclick = null; - elements[0].disabled = true; - } else if (transferList[idx].status === 5){ - elements[0].innerHTML = "Cancelled"; - elements[0].onclick = null; - elements[0].disabled = true; - } else if (transferList[idx].status === 6){ - getById("transfer_"+idx).style.display = "none"; - //delete(transferList[idx]); - } - } -} - -function showDownloadLinks(){ - if (session.nodownloads){return;} // downloads are blocked - msgTransferList=[]; - if (!transferList || !transferList.length){return;} - for (var i = 0; i< transferList.length; i++){ - fileShareMessage(transferList[i], i); - } -} - -function fileShareMessage(fileinfo, idx){ - - fileinfo.name = sanitizeChat(fileinfo.name); // keep it clean. - - var label = false; - if (fileinfo.pc){ - if (fileinfo.pc.label) { - label = sanitizeLabel(fileinfo.pc.label); - } - } - var data = {}; - data.idx = idx; - if (fileinfo.status === 0){ - data.msg = " has a shared a file with you:
"+fileinfo.name+"
Do you trust them? "; - } else if (fileinfo.status === 1){ - data.msg = " has a shared a file with you:
"+fileinfo.name+"
"; - } else if (fileinfo.status === 2){ - data.msg = " has a shared a file with you:
"+fileinfo.name+"
"; - } else if (fileinfo.status === 3){ - data.msg = " has a shared a file with you:
"+fileinfo.name+"
"; - transferList[idx].status = 6; - } else if (fileinfo.status === 4){ - data.msg = " has a shared a file with you:
"+fileinfo.name+"
"; - } else if (fileinfo.status === 5){ - data.msg = " has a shared a file with you:
"+fileinfo.name+"
"; - transferList[idx].status = 6; - } else if (fileinfo.status === 6){ - return; - } - - var director=false; // add back in later. - if (session.directorList.indexOf(fileinfo.UUID)>=0){ - director=true; - } - if (label) { - data.label = label; - if (director) { - data.label = "" + data.label + ""; - } else { - data.label = "" + data.label + ""; - } - } else if (director) { - data.label = "Director"; - } else { - data.label = "Someone"; - } - data.type = "action"; - - msgTransferList.push(data); -} - -session.shareFile = function(ele, UUID=false, event=false){ // webcam stream is used to generated an SDP - if (session.hostedFiles===false){return;} // disabled - - for (var i = 0; i < ele.files.length; i++){ // changing from a FileList to an Array. Arrays are easier to modify later on - ele.files[i].id = session.generateStreamID(8); // can't be too short, else can be brute forced - ele.files[i].state = 1; - ele.files[i].restricted = UUID; - session.hostedFiles.push(ele.files[i]); - } - log(session.hostedFiles); - //for (var in rpcs and pcs .... goes here - if (UUID===false){ - for (UUID in session.pcs){ - session.provideFileList(UUID); - } - for (UUID in session.rpcs){ - if (UUID in session.pcs){continue;} - session.provideFileList(UUID); - } - } else { - session.provideFileList(UUID); - } - pokeIframeAPI('file-share', true); - closeModal(); -} - - -session.hostFile = function(ele, event){ // webcam stream is used to generated an SDP - log("FILE TRANSFER SETUP"); - session.hostedFiles = []; - for (var i = 0; i < ele.files.length; i++){ // changing from a FileList to an Array. Arrays are easier to modify later on - ele.files[i].id = session.generateStreamID(8); // can't be too short, else can be brute forced - ele.files[i].state = 1; - session.hostedFiles.push(ele.files[i]); - } - log(session.hostedFiles); - - var container = document.createElement("div"); - container.id = "container_host"; - getById("gridlayout").appendChild(container); - - if (session.cover){ - container.style.setProperty('height', '100%', 'important'); - } - - if (session.roomid!==false){ - if ((session.roomid==="") && ((!(session.view)) || (session.view===""))){ - - } else { - log("ROOMID EANBLED"); - //log("Update Mixer Event on REsize SET"); - //window.addEventListener("resize", updateMixer);// TODO FIX - //window.addEventListener("orientationchange", updateMixer);// TODO FIX - getById("head3").classList.add('hidden'); - getById("head3a").classList.add('hidden'); - joinRoom(session.roomid); - } - - } else { - getById("head3").classList.remove('hidden'); - getById("head3a").classList.remove('hidden'); - getById("logoname").style.display = 'none'; - } - getById("head1").className = 'hidden'; - - updatePushId() - - getById("head1").className = 'hidden'; - getById("head2").className = 'hidden'; - - if (!(session.cleanOutput)){ - getById("chatbutton").className="float"; - getById('sharefilebutton').classList.remove("hidden"); // we won't override "display:none", if set, though. - getById("hangupbutton").className="float"; - getById("controlButtons").classList.remove("hidden"); - getById("helpbutton").style.display = "inherit"; - getById("reportbutton").style.display = ""; - } else { - getById("controlButtons").classList.add("hidden"); - } - - - updateReshareLink(); - - pokeIframeAPI('file-share', true); - pokeIframeAPI('started-fileshare'); // deprecated - - clearInterval(session.updateLocalStatsInterval); - session.updateLocalStatsInterval = setInterval(function(){updateLocalStats();},session.statsInterval); - - session.seeding=true; - session.seedStream(); -} - -function updateReshareLink(){ - - try{ - var m = getById("mainmenu"); - m.remove(); - document.querySelectorAll(".hidden2").forEach(ele2=>{ - ele2.classList.remove("hidden2"); - }); - } catch (e){} - - var added = ""; - if (session.defaultPassword===false){ - if (session.password!==false){ - added="&pw="+session.password; - } else { - added="&pw=false"; - } - } - - var wss = ""; - if (session.wssSetViaUrl){ - if (session.customWSS && (session.customWSS!==true)){ - wss = "&pie="+session.customWSS; - } else { - wss = "&wss="+session.wss; - } - } - - var shareLink = "https://"+location.host+location.pathname+"?view="+session.streamID+added+wss; - if (document.getElementById("reshare")){ - document.getElementById("reshare").href = shareLink; - document.getElementById("reshare").text = shareLink; - document.getElementById("reshare").style.width = ((document.getElementById("reshare").text.length + 1)*1.15 * 8) + 'px'; - } - pokeIframeAPI('share-link', shareLink); -} - - - -session.changePublishFile = function(ele, event){ // webcam stream is used to generated an SDP - log("FILE VIDEO STREAM CHANGE"); - var files = []; - for (var i = 0; i < ele.files.length; i++){ // changing from a FileList to an Array. Arrays are easier to modify later on - files.push(ele.files[i]); - } - var vid = getById("videosource"); - vid.playlist = files; - nextFilePlaylist(vid); -} - -function nextFilePlaylist(vid) { - log("nextFilePlaylistD"); - var filenext = vid.playlist.shift(); - vid.pause(); - vid.removeAttribute('src'); // empty source - vid.src = URL.createObjectURL(filenext); - - vid.onloadeddata = function(){ - - if (Firefox){ - session.streamSrc = vid.mozCaptureStream(); - } else { - session.streamSrc = vid.captureStream(); // gaaaaaaaaaaaahhhhhhhh! - } - - var tracks = session.streamSrc.getVideoTracks(); - if (tracks.length){ - pushOutVideoTrack(tracks[0]); // video only - } - - var tracks = session.streamSrc.getAudioTracks(); - senderAudioUpdate(false, tracks); // don't apply audio effects - } - - session.applySoloChat(); // mute streams that should be muted if a director - session.applyIsolatedChat(); - - vid.load(); - vid.play().then(_ => { - log("playing 2"); - }).catch(warnlog); -} - -session.publishFile = function(ele, event){ // webcam stream is used to generated an SDP - log("FILE STREAM SETUP"); - - if (session.transcript){ - setTimeout(function(){setupClosedCaptions();},1000); - } - - var files = []; - for (var i = 0; i < ele.files.length; i++){ // changing from a FileList to an Array. Arrays are easier to modify later on - files.push(ele.files[i]); - } - log(files); - //var type = file.type; - - var fileURL = URL.createObjectURL(files[0]); - var container = document.createElement("div"); - container.id = "container"; - //container.className = "vidcon"; - - if (session.cover){ - container.style.setProperty('height', '100%', 'important'); - } - - var v = createVideoElement(); - v.container = container; - - if (session.cleanOutput){ - container.style.height = "100%"; - v.style.maxWidth = "100%"; - v.style.boxShadow = "none"; - } - - if (session.streamID){ - v.dataset.sid = session.streamID; - } - - getById("gridlayout").appendChild(container); - - - if (session.roomid!==false){ - if ((session.roomid==="") && ((!(session.view)) || (session.view===""))){ - - } else { - log("ROOMID EANBLED"); - log("Update Mixer Event on REsize SET"); - //window.addEventListener("resize", updateMixer);// TODO FIX - //window.addEventListener("orientationchange", updateMixer);// TODO FIX - getById("head3").classList.add('hidden'); - getById("head3a").classList.add('hidden'); - joinRoom(session.roomid); - } - - } else { - getById("head3").classList.remove('hidden'); - getById("head3a").classList.remove('hidden'); - getById("logoname").style.display = 'none'; - } - getById("head1").className = 'hidden'; - - updatePushId() - - getById("head1").className = 'hidden'; - getById("head2").className = 'hidden'; - - if (!(session.cleanOutput)){ - getById("chatbutton").className="float"; - getById('sharefilebutton').classList.remove("hidden"); // we won't override "display:none", if set, though. - getById("hangupbutton").className="float"; - getById("controlButtons").classList.remove("hidden"); - getById("helpbutton").style.display = "inherit"; - getById("reportbutton").style.display = ""; - } else { - getById("controlButtons").classList.add("hidden"); - } - - var bigPlayButton = document.getElementById("bigPlayButton"); - if (bigPlayButton){ - bigPlayButton.parentNode.removeChild(bigPlayButton); - } - - v.autoplay = false; - if (session.showControls!==null){ - v.controls = session.showControls; - } else { - v.controls = true; - } - v.muted = false; - - if (files.length ==1){ // we don't want to do the complex logic if there is just one video - v.loop = true; - } else { - v.loop = false; // triggers the complex track/rtc logic. - } - - v.setAttribute("playsinline",""); - v.src = fileURL; - - - try { - if (Firefox){ - session.streamSrc = v.mozCaptureStream(); - } else { - session.streamSrc = v.captureStream(); // gaaaaaaaaaaaahhhhhhhh! - } - toggleMute(true); - } catch (e){ - errorlog(e); - return; - } - - v.id = "videosource"; // could be set to UUID in the future - v.dataset.menu = "context-menu-video"; - v.playlist = files; - v.addEventListener('ended',myHandler,false); // only fires if the video doesn't loop. - - - function myHandler(e) { - log("MY HANDLER TRIGGERED"); - var vid = getById("videosource"); - nextFilePlaylist(vid); - } - - // no preview doesn't work, so just stop it from doing its thing. - - v.className = "tile clean fileshare"; - session.videoElement = v; - - container.appendChild(v); - - session.mirrorExclude=true; - - if (session.director){ - } else if (session.scene!==false){ - - } else if (session.roomid!==false){ - if (session.roomid===""){ - if (!(session.view) || (session.view==="")){ - if (session.fullscreen){ - session.windowed = false; - } else { - v.className = "myVideo clean fileshare"; - container.classList.add("vidcon"); - session.windowed = true; - } - getById("mutespeakerbutton").classList.add("hidden"); - container.style.width="100%"; - container.style.alignItems = "center"; - container.backgroundColor = "#666"; - play(); - } else { - session.windowed = false; - play(); - } - } else { - //session.cbr=0; // we're just going to override it - if (session.stereo==5){ - session.stereo=3; - } - session.windowed = false; - } - applyMirror(session.mirrorExclude); - } else { - if (session.fullscreen){ - session.windowed = false; - } else { - v.className = "myVideo clean fileshare"; - container.classList.add("vidcon"); - session.windowed = true; - } - getById("mutespeakerbutton").classList.add("hidden"); - container.style.width="100%"; - container.style.alignItems = "center"; - container.backgroundColor = "#666"; - applyMirror(session.mirrorExclude); - } - - - v.addEventListener('click', function(e){ - log("click"); - try { - if ((e.ctrlKey)||(e.metaKey)){ - e.preventDefault(); - - var [menu, innerMenu] = statsMenuCreator(); - - menu.interval = setInterval(printMyStats,session.statsInterval, innerMenu); - - printMyStats(innerMenu); - e.stopPropagation(); - return false; - } - } catch(e){errorlog(e);} - }); - - v.touchTimeOut = null; - v.touchLastTap = 0; - v.touchCount = 0; - v.addEventListener('touchend', function(event) { - if (session.disableMouseEvents){return;} - log("touched"); - - //document.ontouchup = null; - //document.onmouseup = null; - document.onmousemove = null; - document.ontouchmove = null; - - var currentTime = new Date().getTime(); - var tapLength = currentTime - v.touchLastTap; - clearTimeout(v.touchTimeOut); - if (tapLength < 500 && tapLength > 0) { - /// - log("double touched"); - v.touchCount+=1; - event.preventDefault(); - if (v.touchCount<5){ - v.touchLastTap = currentTime; - return false; - } - v.touchLastTap = 0; - v.touchCount=0; - - var [menu, innerMenu] = statsMenuCreator(); - - menu.interval = setInterval(printMyStats,session.statsInterval, innerMenu); - - printMyStats(innerMenu); - event.stopPropagation(); - return false; - ////// - } else { - v.touchCount=1; - v.touchTimeOut = setTimeout(function(vv) { - clearTimeout(vv.touchTimeOut); - vv.touchLastTap = 0; - vv.touchCount=0; - }, 5000, v); - v.touchLastTap = currentTime; - } - - }); - - - - updateReshareLink(); - pokeIframeAPI('started-fileshare'); // depreciated - pokeIframeAPI('file-share', true); - - clearInterval(session.updateLocalStatsInterval); - session.updateLocalStatsInterval = setInterval(function(){updateLocalStats();},session.statsInterval); - - session.seeding=true; - - if (session.videoMutedFlag){ - session.videoMuted = true; - toggleVideoMute(true); - } - - session.seedStream(); -}; // publishFile - - -function tryAgain(event) { // audio or video agnostic track reconnect ------------not actually in use,. maybe out of date - log("TRY AGAIN TRIGGERED"); - warnlog(event); -} - - -function enterPressedClick(event, ele) { - if (event.keyCode === 13) { - event.preventDefault(); - ele.click(); - } -} - -function enterPressed(event, callback) { - // Number 13 is the "Enter" key on the keyboard - if (event.keyCode === 13) { - event.preventDefault(); - callback(); - } -} - - -function dragElement(elmnt) { - if (session.disableMouseEvents){return;} - log("dragElement started"); - - function onvideoclick() { - log("onvideoclick"); - log(pos3 + " " + pos4); - //log(pos3o + " " + pos4o); - tapToFocus(parseInt(pos3*100/elmnt.clientWidth), parseInt(pos4/elmnt.clientHeight*100)); - return false; - } - - function elementDrag(e) { - - e = e || window.event; - e.preventDefault(); - // calculate the new cursor position: - log("dragging"); - log(e); - if (Date.now() - millis < 100) { - return; - } - - dragged = true; - millis = Date.now(); - - - - if (e.type == 'touchstart' || e.type == 'touchmove' || e.type == 'touchend' || e.type == 'touchcancel') { - var touch = e.touches[0] || e.originalEvent.touches[0] || e.originalEvent.changedTouches[0]; - pos1 = touch.clientX; - pos2 = touch.clientY; - } else if (e.type == 'mousedown' || e.type == 'mouseup' || e.type == 'mousemove' || e.type == 'mouseover' || e.type == 'mouseout' || e.type == 'mouseenter' || e.type == 'mouseleave') { - pos1 = e.clientX; - pos2 = e.clientY; - } - - if (!zoomable){ - return; - } - - var zoom = parseFloat((pos4 - pos2) * 2 / elmnt.offsetHeight); - - if (zoom > 1) { - zoom = 1.0; - } else if (zoom < -1) { - zoom = -1.0; - } - input.value = zoom * (input.max - input.min) + input.min; - updateCameraConstraints("zoom", input.value, false, false); - - } - function closeDragElement(e) { - - log("closeDragElement"); - log(e); - - // focusable - if (!dragged){ - log("dragged: "+dragged); - onvideoclick(); - } - dragged = false; - - elmnt.removeEventListener('touchend', closeDragElement); - elmnt.removeEventListener('mouseup', closeDragElement); - - /* stop moving when mouse button is released:*/ - //document.ontouchend = null; - //document.onmouseup = null; - document.onmousemove = null; - document.ontouchmove = null; - } - function dragMouseDown(e) { - log("dragMouseDown"); - log(e); - - dragged = false; - millis = Date.now(); - - e = e || window.event; - e.preventDefault(); - - pos0 = input.value; - if (e.type == 'touchstart' || e.type == 'touchmove' || e.type == 'touchend' || e.type == 'touchcancel') { - var touch = e.touches[0] || e.originalEvent.touches[0] || e.originalEvent.changedTouches[0]; - pos3 = touch.clientX; - pos4 = touch.clientY; - //pos3o = touch.offsetX; - //pos4o = touch.offsetX; - } else if (e.type == 'mousedown' || e.type == 'mouseup' || e.type == 'mousemove' || e.type == 'mouseover' || e.type == 'mouseout' || e.type == 'mouseenter' || e.type == 'mouseleave') { - pos3 = e.clientX; - pos4 = e.clientY; - //pos3o = e.offsetX; - //pos4o = e.offsetX; - } - elmnt.addEventListener('touchend', closeDragElement); - elmnt.addEventListener('mouseup', closeDragElement); - document.ontouchmove = elementDrag; - document.onmousemove = elementDrag; - } - - try { - var stream = elmnt.srcObject; - try { - var track0 = stream.getVideoTracks(); - } catch (e) { - return; - } - - if (!(track0.length)) { - return; - } - var focusable = false; - var zoomable = false; - var dragged = false; - var input = getById("zoomSlider"); - track0 = track0[0]; - if (track0.getCapabilities) { - var capabilities = track0.getCapabilities(); - var settings = track0.getSettings(); - - if ("focusDistance" in capabilities){ - log("focusable"); - focusable = true; - } - - if ('zoom' in capabilities) { - if (capabilities.zoom.min !== capabilities.zoom.max){ - log("zoomable;"); - zoomable = true; - input.min = capabilities.zoom.min; - input.max = capabilities.zoom.max; - input.step = capabilities.zoom.step; - input.value = settings.zoom; - } - - } - } - - var millis = Date.now(); - var pos0 = 1; - var pos3 = 0; - var pos4 = 0; - var pos1 = 0; - var pos2 = 0; - //var pos3o = 0; - //var pos4o = 0; - } catch (e) { - errorlog(e); - return; - } - - if (!focusable && !zoomable){return;} // can't be zoomed or focused. - - log("drag on"); - elmnt.onmousedown = dragMouseDown; - elmnt.ontouchstart = dragMouseDown; - - -} - -function previewIframe(iframeSrc) { // this is pretty important if you want to avoid camera permission popup problems. You can also call it automatically via: loadIframe();"> , but don't call it before the page loads. - - var iframe = document.createElement("iframe"); - iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;display-capture;midi;"; - iframe.style.width = "100%"; - iframe.style.height = "100%"; - iframe.style.border = "10px dashed rgb(64 65 62)"; - - iframeSrc = parseURL4Iframe(iframeSrc); - - /* if (typeof iframeSrc == "object"){ // special handler. - iframeSrc = iframeSrc.parsedSrc; - } */ - - iframe.src = iframeSrc; - getById("previewIframe").innerHTML = ""; - getById("previewIframe").style.width = "640px"; - getById("previewIframe").style.height = "360px"; - getById("previewIframe").style.margin = "auto"; - getById("previewIframe").appendChild(iframe); -} - -function loadIframe(iframesrc, UUID) { // this is pretty important if you want to avoid camera permission popup problems. You can also call it automatically via: loadIframe();"> , but don't call it before the page loads. - /* if (document.getElementById("mainmenu")) { - var m = getById("mainmenu"); - m.remove(); - } */ - var iframeID = "iframe_"+UUID; - - var iframe = document.createElement("iframe"); - iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;display-capture;midi;"; - iframe.style.width = "100%"; - iframe.style.height = "100%"; - iframe.style.border = "10px dashed rgb(64 65 62)"; - iframe.id = iframeID; - iframe.dataset.UUID = UUID; - iframe.loadedYoutubeListen = false; - - if (session.director){ - // - } else if (session.scene!==false){ - if (session.view){ // specific video to be played - iframe.style.display="block"; - } else if (session.scene==="0"){ - iframe.style.display="block"; - } else { // group scene I guess; needs to be added manually - iframe.style.display="none"; - } - } else if (session.roomid!==false){ - // - } else { - iframe.style.display="block"; - } - if (iframesrc == "") { - iframesrc = "./"; - iframe.style.border = "0"; - } - - // trusted domains - if (iframesrc.startsWith("https://vdo.ninja/")){ - iframe.style.border = "0"; - } else if (iframesrc.startsWith("https://obs.ninja/")){ - iframe.style.border = "0"; - } else if (iframesrc.startsWith("https://vmix.ninja/")){ - iframe.style.border = "0"; - } else if (iframesrc.startsWith("https://backup.vdo.ninja/")){ - iframe.style.border = "0"; - } else if (iframesrc.startsWith("https://backup.obs.ninja/")){ - iframe.style.border = "0"; - } else if (iframesrc.startsWith("https://www.youtube.com/")){ - iframe.style.border = "0"; - setTimeout(function(iframe_id){YoutubeListen(iframe_id);}, 1000, iframeID); // create stats feedback for the director; syncing. - } else if (iframesrc.startsWith("https://player.twitch.tv/")){ - iframe.style.border = "0"; - } else if (iframesrc.startsWith("https://twitch.tv/")){ - iframe.style.border = "0"; - } else if (iframesrc.startsWith("https://www.twitch.tv/")){ - iframe.style.border = "0"; - } else if (iframesrc.startsWith("https://vimeo.com/")){ - iframe.style.border = "0"; - } else if (iframesrc.startsWith("https://player.vimeo.com/")){ - iframe.style.border = "0"; - } else if (iframesrc.startsWith("https://meshcast.io/")){ - //iframesrc = iframesrc.replace("//meshcast.io/", "//meshcast.vdo.ninja/"); - iframe.style.border = "0"; - // iframe.dataset.meshcast = true; // TODO: this was a bit of a fail - if (document.domain==="backup.vdo.ninja"){ - document.domain = 'vdo.ninja'; - } else if (document.domain==="isolated.vdo.ninja"){ - document.domain = 'vdo.ninja'; - } - } else if (iframesrc.startsWith("https://s10.fun/")){ - iframe.style.border = "0"; - } else if (iframesrc.startsWith("https://play.rozy.tv/")){ - iframe.style.border = "0"; - } - - iframe.src = iframesrc; - - pokeIframeAPI('iframe-loaded', iframesrc); - return iframe -} - -function dropDownButtonAction(ele) { - var ele = getById("dropButton"); - if (ele) { - ele.parentNode.removeChild(ele); - //getById('container-5').classList.remove('hidden'); - //getById('container-8').classList.remove('hidden'); - //getById('container-6').classList.remove('hidden'); - document.querySelectorAll("div.column.card").forEach(child=>{ - child.classList.remove('hidden'); - }); - } -} - -function updateConstraintSliders() { - log("updateConstraintSliders"); - if (session.roomid !== false && session.roomid !== "" && session.director !== true && session.forceMediaSettings == false) { - if (session.controlRoomBitrate !== false) { - listCameraSettings(); - } - if (session.effect!==false){ - //if ((iOS) || (iPad)){ - //} else { - getById("effectsDiv3").style.display = "block"; - getById("effectSelector3").value = session.effect || "0"; - //} - } - } else { - listAudioSettings(); - listCameraSettings(); - - //if ((iOS) || (iPad)){ - // } else { - if (session.effect!==false){ - getById("effectsDiv3").style.display = "block"; - try{ - getById("effectSelector3").value = session.effect || "0"; - } catch(E){} - } - //} - } - //checkIfPIP(); // this doesn't actually work on iOS still, so whatever. -} - -function checkIfPIP() { - try { - if (session.videoElement && ((session.videoElement.webkitSupportsPresentationMode && typeof session.videoElement.webkitSetPresentationMode === "function") || (document.pictureInPictureEnabled || !videoElement.disablePictureInPicture))) { - // Toggle PiP when the user clicks the button. - - getById("pIpStartButton").addEventListener("click", function(event) { - // if ( (document.pictureInPictureEnabled || !videoElement.disablePictureInPicture)){ - //session.videoElement.requestPictureInPicture(); - // } else { - session.videoElement.webkitSetPresentationMode(session.videoElement.webkitPresentationMode === "picture-in-picture" ? "inline" : "picture-in-picture"); - // } - }); - getById("pIpStartButton").style.display = "inline-block"; - } - } catch (e) { - errorlog(e); - } -} - -function togglePictureInPicture(videoElement) { - if (document.pictureInPictureElement) { - if (document.pictureInPictureElement.id == videoElement.id){ - document.exitPictureInPicture(); - pokeIframeAPI('picture-in-picture', false); - return false; - } else { - document.exitPictureInPicture(); - pokeIframeAPI('picture-in-picture', false); - videoElement.requestPictureInPicture(); - pokeIframeAPI('picture-in-picture', true); - } - } else if (document.pictureInPictureEnabled) { - videoElement.requestPictureInPicture(); - pokeIframeAPI('picture-in-picture', true); - } - return true; -} - - -function mixMinusAudio(uid=false){ - var audioContext = new AudioContext(); - - if (session.stereo===false){ - var merger = audioContext.createChannelMerger(1); - } else { - var merger = audioContext.createChannelMerger(2); - } - - if (session.videoElement && session.videoElement.srcObject){ - var tracks = session.videoElement.srcObject.getAudioTracks(); - for (var i=0;i 0) { - log("FINAL:" + Final_transcript); - try { - var data = {}; - data.isFinal = true; - data.transcript = Final_transcript; - data.counter = TranscriptionCounter; - session.sendMessage(data); - TranscriptionCounter += 1; - Final_transcript = ""; - Interim_transcript = ""; - pokeIframeAPI('transcription-text', Final_transcript); - } catch (e) { - errorlog(e); - } - - } else { - try { - var data = {}; - data.isFinal = false; - data.transcript = Interim_transcript; - data.counter = TranscriptionCounter; - session.sendMessage(data); - } catch (e) { - errorlog(e); - Interim_transcript = ""; - } - } - }; - - Recognition.start(); - } else if (!session.cleanOutput){ - warnUser(getTranslation("speech-not-suppoted"), false, false); - } -} - - -async function requestVideoRecord(ele, state=null, bitrate=null) { - var UUID = ele.dataset.UUID; - if (!state && ele.classList.contains("pressed")) { - var msg = {}; - msg.requestVideoRecord = false; - msg.UUID = UUID; - session.sendRequest(msg, msg.UUID); - ele.classList.remove("pressed"); ele.ariaPressed = "false"; - } else if (state==null || state){ - var msg = {}; - msg.requestVideoRecord = true; - msg.UUID = UUID; - if (bitrate===null){ - window.focus(); - bitrate = await promptAlt(getTranslation("what-bitrate"), false, false, 6000); - } - if (bitrate) { - msg.value = bitrate; - session.sendRequest(msg, msg.UUID); - ele.classList.add("pressed"); ele.ariaPressed = "true"; - } - } - pokeIframeAPI('request-video-record', msg.requestVideoRecord, UUID); -} - -function changeOrderDirector(value) { - if (session.order==false){ - session.order=0; - } - session.order += parseInt(value) || 0; - - var elements = document.querySelectorAll('[data-action-type="order-value-director"]'); - //log(elements); - if (elements[0]){ - elements[0].innerText = parseInt(session.order) || 0; - } - - var data = {}; - data = {}; - data.order = session.order; - session.sendPeers(data); - pokeIframeAPI('director-order', data.order); -} - - - -function changeOrder(value, UUID) { - var msg = {}; - msg.changeOrder = value; - msg.UUID = UUID; - session.sendRequest(msg, msg.UUID); - pokeIframeAPI('change-order', value, UUID); -} - -function requestVideoHack(keyname, value, UUID, ctrl=false) { - var msg = {}; - msg.requestVideoHack = true; - msg.keyname = keyname; - msg.value = value; - msg.UUID = UUID; - msg.ctrl = ctrl; - session.sendRequest(msg, msg.UUID); - pokeIframeAPI('request-video-setting', {value:value, keyname:keyname, ctrl:ctrl}, UUID); -} - -function requestAudioHack(keyname, value, UUID, deviceId = "default") { // updateAudioConstraints - var msg = {}; - msg.requestAudioHack = true; - msg.keyname = keyname; - msg.value = value; - msg.UUID = UUID; - msg.deviceId = deviceId; - session.sendRequest(msg, msg.UUID); - pokeIframeAPI('request-audio-setting', {value:value, keyname:keyname, deviceId:deviceId}, UUID); -} - -function requestChangeEQ(keyname, value, UUID, track = 0) { // updateAudioConstraints - var msg = {}; - msg.requestChangeEQ = true; - msg.keyname = keyname; - msg.value = value; - msg.UUID = UUID; - msg.track = track; // pointless atm - session.sendRequest(msg, msg.UUID); - pokeIframeAPI('request-change-eq', {value:value, keyname:keyname, track:track}, UUID); -} - -function requestChangeGating(keyname, value, UUID, track = 0) { // updateAudioConstraints - var msg = {}; - msg.requestChangeGating = true; - msg.keyname = keyname; - msg.value = value; - msg.UUID = UUID; - msg.track = track; // pointless atm - session.sendRequest(msg, msg.UUID); - pokeIframeAPI('request-change-gating', {value:value, keyname:keyname, track:track}, UUID); -} -function requestChangeCompressor(keyname, value, UUID, track = 0) { // updateAudioConstraints - var msg = {}; - msg.requestChangeCompressor = true; - msg.keyname = keyname; - msg.value = value; - msg.UUID = UUID; - msg.track = track; // pointless atm - session.sendRequest(msg, msg.UUID); - pokeIframeAPI('request-change-compressor', {value:value, keyname:keyname, track:track}, UUID); -} -function requestChangeMicDelay(value, UUID, track = 0) { // updateAudioConstraints - var msg = {}; - msg.requestChangeMicDelay = true; - msg.value = value; - msg.UUID = UUID; - msg.track = track; // pointless atm - session.sendRequest(msg, msg.UUID); - pokeIframeAPI('request-change-mic-delay', {value:value, track:track}, UUID); -} - -function requestChangeSubGain(value, UUID, deviceId) { // updateAudioConstraints - var msg = {}; - msg.requestChangeSubGain = true; - msg.value = value; - msg.UUID = UUID; - msg.deviceId = deviceId; // pointless atm - log(msg); - session.sendRequest(msg, msg.UUID); - pokeIframeAPI('request-sub-gain', {value:value, deviceId:deviceId}, UUID); -} - -function requestChangeLowcut(value, UUID, track = 0) { // updateAudioConstraints - var msg = {}; - msg.requestChangeLowcut = true; - msg.value = value; - msg.UUID = UUID; - msg.track = track; // pointless atm - session.sendRequest(msg, msg.UUID); - pokeIframeAPI('request-low-cut', value, UUID); -} - -function toggleSystemPip(vid) { - try{ - if (vid.webkitSupportsPresentationMode && (typeof vid.webkitSetPresentationMode === "function")) { - vid.webkitSetPresentationMode( - vid.webkitPresentationMode === "picture-in-picture" - ? "inline" - : "picture-in-picture" - ); - } else { - if (document.pictureInPictureElemen) { - document.exitPictureInPicture(); - vid.requestPictureInPicture(); - } else { - vid.requestPictureInPicture(); - } - } - } catch(e){ - errorlog(e); - } -} - -function updateDirectorsAudio(dataN, UUID) { - var audioEle = document.createElement("div"); - query("#container_"+UUID+" .advancedAudioSettings").innerHTML = ""; - query("#container_"+UUID+" .advancedAudioSettings").classList.remove("hidden"); - - //log(dataN); - if (!dataN.length) { - return; - } - - for (var n = 0; n < dataN.length; n += 1) { - var data = dataN[n]; - - if (dataN.length==1) { - if (data.trackLabel) { - var label = document.createElement("label"); - label.innerText = data.trackLabel; - label.style.display = "block"; - label.id = "remoteAudioLabel_"+UUID; - label.classList.add("settingsLabel"); - label.dataset.UUID = UUID; - audioEle.appendChild(label); - } - } - //if (n !== 0) { - //var label = document.createElement("span"); - //label.innerText = "Coming Soon"; - //audioEle.appendChild(label); - // continue; // remove to more than one audio device (assuming other fixes are applied) - //} - - if (("micDelay" in data) && n==0) { - var label = document.createElement("label"); - var i = "micDelay"; - var div = document.createElement("div"); - label.id = "label_" + i + "_"+UUID; - label.htmlFor = "constraints_" + i + "_"+UUID; - - var input = document.createElement("input"); - input.min = 0; - input.max = 500; - input.value = data.micDelay || 0; - - input.title = "Previously was: "+input.value; - - input.type = "range"; - input.dataset.keyname = i; - //input.dataset.labelname = "mic delay (ms):"; - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + " (ms):"; - - - var manualInput = document.createElement("input"); - manualInput.type = "number"; - manualInput.dataset.keyname = i; - manualInput.value = parseFloat(input.value); - manualInput.className = "manualInput"; - manualInput.id = "constraints_manual_" + i + "_"+UUID; - manualInput.dataset.UUID = UUID; - manualInput.dataset.track = n; - - input.dataset.track = n; - input.dataset.UUID = UUID; - input.id = "constraints_" + i + "_"+UUID; - input.style = "display:block; width:100%;"; - input.name = "constraints_" + i; - input.style.margin = "2px 0px 5px"; - - manualInput.onchange = function(e) { - getById("constraints_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); - requestChangeMicDelay(parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); - }; - - input.onchange = function(e) { - //e.target.title = e.target.value; - getById("constraints_manual_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); - requestChangeMicDelay(parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); - }; - - input.oninput = function(e) { - getById("constraints_manual_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); - if (Date.now() - remoteSliderTimeout > 100){ - remoteSliderTimeout = Date.now(); - requestChangeMicDelay(parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); - } - }; - - audioEle.appendChild(div) - div.appendChild(label); - div.appendChild(manualInput); - audioEle.appendChild(input); - } - - - if (data.lowcut!==false && n==0) { - var label = document.createElement("label"); - var i = "lowCut"; - label.id = "label_" + i + "_"+UUID; - label.htmlFor = "constraints_" + i + "_"+UUID; - - var input = document.createElement("input"); - input.min = 50; - input.max = 150; - input.value = data.lowcut; - - input.title = "Previously was: "+input.value; - - input.type = "range"; - input.dataset.keyname = i; - //input.dataset.labelname = "low cut:"; - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; - - - var manualInput = document.createElement("input"); - manualInput.type = "number"; - manualInput.dataset.keyname = i; - manualInput.value = parseFloat(input.value); - manualInput.className = "manualInput"; - manualInput.id = "constraints_manual_" + i + "_"+UUID; - manualInput.dataset.UUID = UUID; - manualInput.dataset.track = n; - - input.dataset.track = n; - input.dataset.UUID = UUID; - input.id = "constraints_" + i + "_"+UUID; - input.style = "display:block; width:100%;"; - input.name = "constraints_" + i; - input.style.margin = "2px 0px 5px"; - - manualInput.onchange = function(e) { - getById("constraints_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); - requestChangeLowcut(parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); - }; - - input.onchange = function(e) { - //e.target.title = e.target.value; - getById("constraints_manual_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); - requestChangeLowcut(parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); - }; - - input.oninput = function(e) { - getById("constraints_manual_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); - if (Date.now() - remoteSliderTimeout > 100){ - remoteSliderTimeout = Date.now(); - requestChangeLowcut(parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); - } - }; - - audioEle.appendChild(label); - audioEle.appendChild(manualInput); - audioEle.appendChild(input); - } - - if (data.equalizer && n==0) { - var label = document.createElement("label"); - var i = "Low_EQ"; - //label.id = "label_" +i + "_"+UUID; - label.htmlFor = "constraints_" +i + "_"+UUID; - - - var input = document.createElement("input"); - input.min = -50; - input.max = 50; - input.value = data.lowEQ; - input.title = "Previously was: "+input.value; - input.type = "range"; - input.dataset.keyname = i; - input.dataset.labelname = "low EQ:" - - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; - - var manualInput = document.createElement("input"); - manualInput.type = "number"; - manualInput.dataset.keyname = i; - manualInput.value = parseFloat(input.value); - manualInput.className = "manualInput"; - manualInput.id = "label_" + i + "_"+UUID; - manualInput.dataset.UUID = UUID; - manualInput.dataset.track = n; - - input.dataset.track = n; - input.dataset.UUID = UUID; - input.id = "constraints_" + i + "_"+UUID; - input.style = "display:block; width:100%;"; - input.name = "constraints_" + i; - input.style.margin = "2px 0px 5px"; - - manualInput.onchange = function(e) { - getById("constraints_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); - requestChangeEQ("low", parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); - }; - - input.onchange = function(e) { - getById("label_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); - requestChangeEQ("low", parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); - }; - - input.oninput = function(e) { - getById("label_"+ e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); - if (Date.now() - remoteSliderTimeout > 100){ - remoteSliderTimeout = Date.now(); - requestChangeEQ("low", parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); - } - }; - - audioEle.appendChild(label); - audioEle.appendChild(manualInput); - audioEle.appendChild(input); - - var label = document.createElement("label"); - var i = "midEQ"; - //label.id = "label_" + i + "_"+UUID; - label.htmlFor = "constraints_" + i + "_"+UUID; - - - - var input = document.createElement("input"); - input.min = -50; - input.max = 50; - input.value = data.midEQ; - input.title = "Previously was: "+input.value; - input.type = "range"; - input.dataset.keyname = i; - input.dataset.labelname = "mid EQ:"; - - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; - - var manualInput = document.createElement("input"); - manualInput.type = "number"; - manualInput.dataset.keyname = i; - manualInput.value = parseFloat(input.value); - manualInput.className = "manualInput"; - manualInput.id = "label_" + i + "_"+UUID; - manualInput.dataset.UUID = UUID; - manualInput.dataset.track = n; - - input.dataset.track = n; - input.dataset.UUID = UUID; - input.id = "constraints_" + i + "_"+UUID; - input.style = "display:block; width:100%;"; - input.name = "constraints_" + i; - input.style.margin = "2px 0px 5px"; - - manualInput.onchange = function(e) { - getById("constraints_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); - requestChangeEQ("mid", parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); - }; - - input.onchange = function(e) { - getById("label_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); - requestChangeEQ("mid", parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); - }; - - input.oninput = function(e) { - getById("label_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); - if (Date.now() - remoteSliderTimeout > 100){ - remoteSliderTimeout = Date.now(); - requestChangeEQ("mid", parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); - } - }; - - - audioEle.appendChild(label); - audioEle.appendChild(manualInput); - audioEle.appendChild(input); - - - var label = document.createElement("label"); - var i = "highEQ"; - //label.id = "label_" + i + "_"+UUID; - label.htmlFor = "constraints_" + i + "_"+UUID; - - - var input = document.createElement("input"); - input.min = -50; - input.max = 50; - input.value = data.highEQ; - input.title = "Previously was: "+input.value; - input.type = "range"; - input.dataset.keyname = i; - input.dataset.labelname = "high EQ:"; - - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; - - var manualInput = document.createElement("input"); - manualInput.type = "number"; - manualInput.dataset.keyname = i; - manualInput.value = parseFloat(input.value); - manualInput.className = "manualInput"; - manualInput.id = "label_" + i + "_"+UUID; - manualInput.dataset.UUID = UUID; - manualInput.dataset.track = n; - - input.dataset.track = n; - input.dataset.UUID = UUID; - input.id = "constraints_" + i + "_"+UUID; - input.classList.add("inputConstraint"); - input.name = "constraints_" + i; - - manualInput.onchange = function(e) { - getById("constraints_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); - requestChangeEQ("high", parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); - }; - - input.onchange = function(e) { - getById("label_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); - requestChangeEQ("high", parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); - }; - - input.oninput = function(e) { - getById("label_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); - if (Date.now() - remoteSliderTimeout > 100){ - remoteSliderTimeout = Date.now(); - requestChangeEQ("high", parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); - } - }; - - audioEle.appendChild(label); - audioEle.appendChild(manualInput); - audioEle.appendChild(input); - } - - if (("gating" in data) && n==0) { // only show once. - var label = document.createElement("label"); - var i = "noiseGate"; - var div = document.createElement("div"); - var label = document.createElement("label"); - label.id = "label_" + i + "_"+n + "_"+UUID; - label.htmlFor = "constraints_" + i + "_"+n + "_"+UUID; - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; - label.style = "display:inline-block; padding:0;"; - label.dataset.keyname = i; - label.dataset.track = n; - var input = document.createElement("select"); - var c = document.createElement("option"); - - var opt = new Option("Off", false); - input.options.add(opt); - opt = new Option("On", true); - input.options.add(opt); - - if (data.gating){ - opt.selected = "true"; - } - - input.dataset.deviceId = data.deviceId; - input.id = "constraints_" + i + "_"+n + "_"+UUID; - input.className = "constraintCameraInput"; - input.name = "constraints_" + i + "_"+n; - input.style = "display:inline; padding:2px;"; - input.dataset.keyname = i; - input.dataset.track = n; - input.dataset.UUID = UUID; - input.dataset.chosen = input.value; - input.onchange = function(e) { - this.dataset.chosen = this.value; - //getById("label_"+e.target.dataset.keyname).innerText =e.target.dataset.keyname+": "+e.target.value; - //updateAudioConstraints(e.target.dataset.keyname, e.target.value); - requestChangeGating("gating", e.target.value, e.target.dataset.UUID, parseInt(e.target.dataset.track)); - log(e.target.dataset.keyname, e.target.value); - }; - audioEle.appendChild(div); - div.appendChild(label); - div.appendChild(input); - } - - if (("compressor" in data) && n==0) { - var label = document.createElement("label"); - var i = "compressor"; - var div = document.createElement("div"); - var label = document.createElement("label"); - label.id = "label_" + i + "_"+n+ "_"+UUID; - label.htmlFor = "constraints_" + i + "_"+n+ "_"+UUID; - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; - label.style = "display:inline-block; padding:0;"; - label.dataset.keyname = i; - label.dataset.track = n; - - var input = document.createElement("select"); - var c = document.createElement("option"); - - var opt = new Option("Off", false); - input.options.add(opt); - opt = new Option("On", 1); - input.options.add(opt); - - if (data.compressor==1){ - opt.selected = "true"; - } - opt = new Option("Limiter", 2); - input.options.add(opt); - - if (data.compressor==2){ - opt.selected = "true"; - } - - input.dataset.deviceId = data.deviceId; - input.id = "constraints_" + i + "_"+n+ "_"+UUID; - input.className = "constraintCameraInput"; - input.name = "constraints_" + i + "_"+n; - input.style = "display:inline; padding:2px;"; - input.dataset.keyname = i; - input.dataset.track = n; - input.dataset.UUID = UUID; - input.dataset.chosen = input.value; - input.onchange = function(e) { - this.dataset.chosen = this.value; - //getById("label_"+e.target.dataset.keyname).innerText =e.target.dataset.keyname+": "+e.target.value; - //updateAudioConstraints(e.target.dataset.keyname, e.target.value); - requestChangeCompressor("compressor", e.target.value, e.target.dataset.UUID, parseInt(e.target.dataset.track)); - log(e.target.dataset.keyname, e.target.value); - }; - audioEle.appendChild(div); - div.appendChild(label); - div.appendChild(input); - } - - if (dataN.length>1){ - if (data.trackLabel) { - var label = document.createElement("label"); - label.innerText = data.trackLabel; - label.style.display = "block"; - label.id = "remoteAudioLabel_"+UUID+"_"+n+ "_"+UUID; - label.classList.add("settingsLabel"); - audioEle.appendChild(label); - } - } - - warnlog(data); - - for (var i in data.audioConstraints) { - try { - log(i); - log(data.audioConstraints[i]); - if ((typeof data.audioConstraints[i] === 'object') && (data.audioConstraints[i] !== null) && ("max" in data.audioConstraints[i]) && ("min" in data.audioConstraints[i])) { - if (i === "aspectRatio") { - continue; - } else if (i === "width") { - continue; - } else if (i === "height") { - continue; - } else if (i === "frameRate") { - continue; - } else if (i === "latency") { - // continue; - } else if (i === "sampleRate") { - continue; - } else if (i === "channelCount") { - // continue; - } else if (i === "volume"){ - continue; - } - - if (!("deviceId" in data.audioConstraints)){continue;} // not going to support older versions. - - var label = document.createElement("label"); - //label.id = "label_" + i + "_"+n+ "_"+UUID; - label.htmlFor = "constraints_" + i + "_"+n+ "_"+UUID; - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; - - var input = document.createElement("input"); - input.min = data.audioConstraints[i].min; - input.max = data.audioConstraints[i].max; - - - if (parseFloat(input.min) == parseFloat(input.max)) { - continue; - } - - var manualInput = document.createElement("input"); - manualInput.type = "number"; - - if ("step" in data.audioConstraints[i]) { - input.step = data.audioConstraints[i].step; - manualInput.step = data.audioConstraints[i].step; - } else if ("volume" == i) { - input.step = 0.01; - manualInput.step = 0.01; - } - - manualInput.dataset.keyname = i; - manualInput.className = "manualInput"; - manualInput.id = "label_" + i + "_"+n+ "_"+UUID; - manualInput.max = data.audioConstraints[i].max; - manualInput.min = data.audioConstraints[i].min; - manualInput.dataset.UUID = UUID; - manualInput.dataset.track = n; - manualInput.dataset.keyname = i; - - if (i in data.currentAudioConstraints) { - input.value = data.currentAudioConstraints[i]; - manualInput.value = parseFloat(input.value); - //label.innerText = i + ": " + data.currentAudioConstraints[i]; - label.title = "Previously was: " + data.currentAudioConstraints[i]; - input.title = "Previously was: " + data.currentAudioConstraints[i]; - } else { - label.innerText = i; - } - - if ((i === "height") || (i === "width")){ - input.title = "Hold CTRL (or cmd) to lock width and height together when changing them"; - input.min = 16; - } - - - input.type = "range"; - input.dataset.keyname = i; - input.dataset.track = n; - input.dataset.deviceId = data.deviceId; - input.dataset.UUID = UUID; - input.id = "constraints_" + i + "_"+n+ "_"+UUID; - input.style = "display:block; width:100%;"; - input.name = "constraints_" + i + "_"+n + "_"+ UUID; - - if (i=="channelCount"){ - input.style.display = "none"; - manualInput.style.margin = "5px 0px 9px 10px"; - } - - manualInput.onchange = function(e) { - getById("constraints_" + e.target.dataset.keyname + "_"+e.target.dataset.track + "_"+ e.target.dataset.UUID).value = parseFloat(e.target.value); - requestAudioHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, e.target.dataset.deviceId); - }; - - input.onchange = function(e) { - //e.target.title = e.target.value; - getById("label_" + e.target.dataset.keyname + "_"+e.target.dataset.track + "_"+ e.target.dataset.UUID ).value = parseFloat(e.target.value); - //updateAudioConstraints(e.target.dataset.keyname, e.target.value); - requestAudioHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, e.target.dataset.deviceId); - }; - - audioEle.appendChild(label); - audioEle.appendChild(manualInput); - audioEle.appendChild(input); - } else if ((typeof data.audioConstraints[i] === 'object') && (data.audioConstraints[i] !== null)) { - if (i == "resizeMode") { - continue; - } - - var div = document.createElement("div"); - var label = document.createElement("label"); - label.id = "label_" + i + "_"+n+ "_"+UUID; - label.htmlFor = "constraints_" + i + "_"+n+ "_"+UUID; - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; - label.style = "display:inline-block; padding:0;"; - var input = document.createElement("select"); - var c = document.createElement("option"); - - - if (data.audioConstraints[i].length > 1) { - for (var opts in data.audioConstraints[i]) { - log(opts); - if (data.audioConstraints[i][opts] === false){ - var opt = new Option("Off", data.audioConstraints[i][opts]); - } else if (data.audioConstraints[i][opts] === true){ - var opt = new Option("On", data.audioConstraints[i][opts]); - } else { - var opt = new Option(data.audioConstraints[i][opts], data.audioConstraints[i][opts]); - } - input.options.add(opt); - if (i in data.currentAudioConstraints) { - if (data.audioConstraints[i][opts] == data.currentAudioConstraints[i]) { - opt.selected = "true"; - } - } - } - } else if (i.toLowerCase == "torch") { - var opt = new Option("Off", false); - input.options.add(opt); - opt = new Option("On", true); - input.options.add(opt); - try{ - if (i in data.currentAudioConstraints) { - if (data.audioConstraints[i]['torch'] == true) { - opt.selected = "true"; - } - } - } catch(e){} - } else { - continue; - } - - input.id = "constraints_" + i + "_"+n+ "_"+UUID; - input.className = "constraintCameraInput"; - input.name = input.id; - input.style = "display:inline; padding:2px;"; - input.dataset.keyname = i; - input.dataset.track = n; - input.dataset.deviceId = data.deviceId; - input.dataset.UUID = UUID; - input.dataset.chosen = input.value; - input.onchange = function(e) { - this.dataset.chosen = this.value; - //getById("label_"+e.target.dataset.keyname).innerText =e.target.dataset.keyname+": "+e.target.value; - //updateAudioConstraints(e.target.dataset.keyname, e.target.value); - requestAudioHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, e.target.dataset.deviceId); - log(e.target.dataset.keyname, e.target.value); - }; - audioEle.appendChild(div); - div.appendChild(label); - div.appendChild(input); - } else if (typeof data.audioConstraints[i] === 'boolean') { - - var div = document.createElement("div"); - var label = document.createElement("label"); - label.id = "label_" + i + "_"+n+ "_"+UUID; - label.htmlFor = "constraints_" + i + "_"+n+ "_"+UUID; - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; - label.style = "display:inline-block; padding:0;"; - label.dataset.keyname = i ; - label.dataset.track = n; - var input = document.createElement("select"); - var c = document.createElement("option"); - - var opt = new Option("Off", false); - input.options.add(opt); - opt = new Option("On", true); - input.options.add(opt); - - try{ - if (data.audioConstraints[i] === true){ - opt.selected = "true"; - } - } catch(e){} - - input.dataset.deviceId = data.deviceId; - input.id = "constraints_" + i + "_"+n+ "_"+UUID; - input.className = "constraintCameraInput"; - input.name = input.id; - input.style = "display:inline; padding:2px;"; - input.dataset.keyname = i; - input.dataset.track = n; - input.dataset.UUID = UUID; - input.dataset.chosen = input.value; - input.onchange = function(e) { - this.dataset.chosen = this.value; - //getById("label_"+e.target.dataset.keyname).innerText =e.target.dataset.keyname+": "+e.target.value; - //updateAudioConstraints(e.target.dataset.keyname, e.target.value); - requestAudioHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, e.target.dataset.deviceId); - log(e.target.dataset.keyname, e.target.value); - }; - audioEle.appendChild(div); - div.appendChild(label); - div.appendChild(input); - } - } catch (e) { - errorlog(e); - } - } - - - if (data.subGain!==false) { - var label = document.createElement("label"); - var i = "Gain"; - var div = document.createElement("div"); - label.id = "label_" + i + "_"+n+ "_"+UUID; - label.htmlFor = "constraints_" + i + "_" + n+ "_"+UUID; - - var input = document.createElement("input"); - input.min = 0; - input.max = 200; - input.value = data.subGain*100; - input.title = "Previously was: "+parseInt(input.value); - input.type = "range"; - input.dataset.keyname = i; - input.dataset.track = n; - input.dataset.labelname = "Gain:" - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; - - var manualInput = document.createElement("input"); - manualInput.type = "number"; - manualInput.dataset.keyname = i; - manualInput.value = parseFloat(input.value); - manualInput.className = "manualInput"; - manualInput.id = "label_" + i + "_" + n+ "_"+UUID; - manualInput.dataset.UUID = UUID; - manualInput.dataset.track = n; - - - input.dataset.track = data.deviceId; - input.dataset.UUID = UUID; - input.id = "constraints_" + i + "_" + n+ "_"+UUID; - input.style = "display:block; width:100%;"; - input.name = input.id; - input.style.margin = "2px 0px 5px"; - - manualInput.onchange = function(e) { - getById("constraints_" + e.target.dataset.keyname + "_"+e.target.dataset.track + "_"+ e.target.dataset.UUID).value = parseFloat(e.target.value); - requestChangeSubGain(parseInt(e.target.value), e.target.dataset.UUID, e.target.dataset.track); - }; - - input.onchange = function(e) { - getById("label_" + e.target.dataset.keyname + "_"+e.target.dataset.track + "_"+ e.target.dataset.UUID).value = parseFloat(e.target.value); - requestChangeSubGain(parseInt(e.target.value), e.target.dataset.UUID, e.target.dataset.track); - }; - - input.oninput = function(e) { - getById("label_" + e.target.dataset.keyname + "_"+e.target.dataset.track + "_"+ e.target.dataset.UUID).value = parseFloat(e.target.value); - if (Date.now() - remoteSliderTimeout > 100){ - remoteSliderTimeout = Date.now(); - requestChangeSubGain(parseInt(e.target.value), e.target.dataset.UUID, e.target.dataset.track); - } - }; - - audioEle.appendChild(div) - div.appendChild(label); - div.appendChild(manualInput); - audioEle.appendChild(input); - } - - query("#container_"+UUID+" .advancedAudioSettings").appendChild(audioEle); - } - - if (fixScrollReset){ - clearTimeout(fixScrollReset); - fixScrollReset = null; - getById("directorlayout").scrollTop = fixScrollResetValue; - } -} - -var remoteSliderTimeout = 0; - -function updateDirectorsVideo(data, UUID) { - var videoEle = document.createElement("div"); - if (data.trackLabel) { - var label = document.createElement("label"); - label.innerText = data.trackLabel; - label.style.display = "block"; - label.id = "remoteVideoLabel_"+UUID; - label.dataset.UUID = UUID; - label.classList.add("settingsLabel") - videoEle.appendChild(label); - } - - for (var i in data.cameraConstraints) { - try { - log(i); - log(data.cameraConstraints[i]); - - - if (i === "focusMode") { - continue // I'll handle this with FocusDistance instead - } else if (i === "whiteBalanceMode") { - continue // I'll handle this elsewhere - } else if (i === "exposureMode") { - continue // I'll handle this elsewhere - } - - - if ((typeof data.cameraConstraints[i] === 'object') && (data.cameraConstraints[i] !== null) && ("max" in data.cameraConstraints[i]) && ("min" in data.cameraConstraints[i])){ - if (i === "aspectRatio") { - // continue; - } else if (i === "width") { - // continue; - } else if (i === "height") { - // continue; - } else if (i === "frameRate") { - // continue; - } else if (i === "latency") { - // continue; - } else if (i === "sampleRate") { - continue; - } else if (i === "channelCount") { - // continue; - } - - var manualMode = false; - var manualLabel = false; - if (i ==="exposureTime"){ - if (data.currentCameraConstraints["exposureMode"]){ - manualMode = document.createElement("input"); - manualMode.type = "checkbox"; - manualMode.id = "manual_"+i+"_"+UUID; - manualMode.dataset.UUID = UUID; - manualMode.dataset.keyname = "exposureMode"; - manualMode.onchange = function(e) { - var value = "manual"; - if (e.target.checked){ - value = "continuous"; - } - requestVideoHack(e.target.dataset.keyname, value, e.target.dataset.UUID, true); - //getById("constraints_" + e.target.dataset.keyname + "_" + e.target.dataset.UUID).value = parseFloat(e.target.value); - //getById("label_" + e.target.dataset.keyname+ "_" + e.target.dataset.UUID).value = parseFloat(e.target.value); - }; - - manualLabel = document.createElement("label"); - manualLabel.htmlFor = manualMode.id; - manualLabel.innerHTML = "Auto: "; - manualLabel.style.marginLeft = "20px"; - if (data.currentCameraConstraints["exposureMode"] == "continuous"){ - manualMode.checked = true; - } - } - } else if (i ==="focusDistance"){ - if (data.currentCameraConstraints["focusMode"]){ - manualMode = document.createElement("input"); - manualMode.type = "checkbox"; - manualMode.id = "manual_"+i+"_"+UUID; - manualMode.dataset.UUID = UUID; - manualMode.dataset.keyname = "focusMode"; - manualMode.onchange = function(e) { - var value = "manual"; - if (e.target.checked){ - value = "continuous"; - } - requestVideoHack(e.target.dataset.keyname, value, e.target.dataset.UUID, true); - }; - manualLabel = document.createElement("label"); - manualLabel.htmlFor = manualMode.id; - manualLabel.innerHTML = "Auto: "; - manualLabel.style.marginLeft = "20px"; - if (data.currentCameraConstraints["focusMode"] == "continuous"){ - manualMode.checked = true; - } - } - } else if (i ==="colorTemperature"){ - if (data.currentCameraConstraints["whiteBalanceMode"]){ - manualMode = document.createElement("input"); - manualMode.type = "checkbox"; - manualMode.id = "manual_"+i+"_"+UUID; - manualMode.dataset.UUID = UUID; - manualMode.dataset.keyname = "whiteBalanceMode"; - manualMode.onchange = function(e) { - var value = "manual"; - if (e.target.checked){ - value = "continuous"; - } - requestVideoHack(e.target.dataset.keyname, value, e.target.dataset.UUID, true); - }; - manualLabel = document.createElement("label"); - manualLabel.htmlFor = manualMode.id; - manualLabel.innerHTML = "Auto: "; - manualLabel.style.marginLeft = "20px"; - if (data.currentCameraConstraints["whiteBalanceMode"] == "continuous"){ - manualMode.checked = true; - } - } - } - - - - var label = document.createElement("label"); - //label.id = "label_" + i; - label.htmlFor = "constraints_" + i + " _"+ UUID; - if (i === "colorTemperature"){ - label.innerText = "Color Temp:"; - } else if (i === "exposureCompensation"){ - label.innerText = "Exposure Comp:"; - } else if (i === "exposureTime"){ - label.innerText = "Exposure:"; - } else if (i === "focusDistance"){ - label.innerText = "Focus:"; - } else { - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; - } - - - if (i === "zoom" || i === "pan" || i === "til"){ - label.innerHTML = " "+label.innerText - } - - var input = document.createElement("input"); - - if (i === "aspectRatio") { - input.max = 5; - input.min = 0.2; - input.step = 0.00001 - } else { - input.min = data.cameraConstraints[i].min; - input.max = data.cameraConstraints[i].max; - } - - if (parseFloat(input.min) == parseFloat(input.max)) { - continue; - } - - if (i in data.currentCameraConstraints) { - input.value = data.currentCameraConstraints[i]; - label.title = "Previously was: " + data.currentCameraConstraints[i]; - input.title = "Previously was: " + data.currentCameraConstraints[i]; - } - - input.type = "range"; - input.dataset.keyname = i; - input.dataset.UUID = UUID; - input.id = "constraints_" + i + "_" + UUID; - input.name = input.id; - input.classList.add("inputConstraint"); - input.manualMode = manualMode; - - - if ((i === "height") || (i === "width")){ - input.title = "Hold CTRL (or cmd) to lock width and height together when changing them"; - input.min = 16; - } - - var manualInput = document.createElement("input"); - manualInput.type = "number"; - manualInput.value = parseFloat(input.value); - manualInput.className = "manualInput"; - manualInput.id = "label_" + i + "_" + UUID; - manualInput.name = manualInput.id; - manualInput.dataset.keyname = i; - manualInput.dataset.UUID = UUID; - manualInput.manualMode = manualMode; - - if ("step" in data.cameraConstraints[i]) { - manualInput.step = data.cameraConstraints[i].step; - input.step = data.cameraConstraints[i].step; - } else if (i === "aspectRatio") { - input.step = 0.000001 - manualInput.step = 0.005; - } - - manualInput.onchange = function(e) { - getById("constraints_" + e.target.dataset.keyname + "_" + e.target.dataset.UUID).value = parseFloat(e.target.value); - if (e.target.manualMode){ - requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, true); - } else { - requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, false); - } - }; - - input.onchange = function(e) { - getById("label_" + e.target.dataset.keyname+ "_" + e.target.dataset.UUID).value = parseFloat(e.target.value); - //updateVideoConstraints(e.target.dataset.keyname, e.target.value); - if (CtrlPressed || e.target.manualMode){ - requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, true); - } else { - requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, false); - } - }; - - input.oninput = function(e) { - getById("label_" + e.target.dataset.keyname+ "_" + e.target.dataset.UUID).value = parseFloat(e.target.value); - if (Date.now() - remoteSliderTimeout > 100){ - remoteSliderTimeout = Date.now(); - if (CtrlPressed){ - requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, true); - } else { - requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, false); - } - } - }; - - videoEle.appendChild(label); - videoEle.appendChild(manualInput); - - if (manualMode && manualLabel){ - videoEle.appendChild(manualLabel); - videoEle.appendChild(manualMode); - } - - if (i === "aspectRatio") { - var preSelectButton = document.createElement("button"); - preSelectButton.value = 16/9.0; - preSelectButton.innerText = "16:9"; - preSelectButton.dataset.keyname = i; - preSelectButton.dataset.UUID = UUID; - preSelectButton.className = "preSelectButton"; - preSelectButton.onclick = function(e) { - getById("constraints_" + e.target.dataset.keyname+ "_" + e.target.dataset.UUID).value = parseFloat(e.target.value); - getById("label_" + e.target.dataset.keyname+ "_" + e.target.dataset.UUID).value = parseFloat(e.target.value); - requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, false); - }; - videoEle.appendChild(preSelectButton); - var preSelectButton = document.createElement("button"); - preSelectButton.value = 9/16.0; - preSelectButton.innerText = "9:16"; - preSelectButton.dataset.UUID = UUID; - preSelectButton.className = "preSelectButton"; - preSelectButton.dataset.keyname = i; - preSelectButton.onclick = function(e) { - getById("constraints_" + e.target.dataset.keyname+ "_" + e.target.dataset.UUID).value = parseFloat(e.target.value); - getById("label_" + e.target.dataset.keyname+ "_" + e.target.dataset.UUID).value = parseFloat(e.target.value); - requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, false); - }; - videoEle.appendChild(preSelectButton); - } - - - videoEle.appendChild(input); - } else if ((typeof data.cameraConstraints[i] === 'object') && (data.cameraConstraints[i] !== null)) { - if (i == "resizeMode") { - continue; - } - - var div = document.createElement("div"); - var label = document.createElement("label"); - label.id = "label_" + i + "_"+UUID; - label.name = label.id; - label.htmlFor = "constraints_" + i + "_"+UUID; - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; - label.style = "display:inline-block; padding:0;"; - label.dataset.keyname = i; - label.dataset.UUID = UUID; - var input = document.createElement("select"); - var c = document.createElement("option"); - - if (data.cameraConstraints[i].length > 1) { - for (var opts in data.cameraConstraints[i]) { - log(opts); - - if (data.cameraConstraints[i][opts] === false){ - var opt = new Option("Off", data.cameraConstraints[i][opts]); - } else if (data.cameraConstraints[i][opts] === true){ - var opt = new Option("On", data.cameraConstraints[i][opts]); - } else { - var opt = new Option(data.cameraConstraints[i][opts], data.cameraConstraints[i][opts]); - } - - input.options.add(opt); - if (i in data.currentCameraConstraints) { - if (data.cameraConstraints[i][opts] == data.currentCameraConstraints[i]) { - opt.selected = "true"; - } - } - } - } else if (i.toLowerCase == "torch") { - var opt = new Option("Off", false); - input.options.add(opt); - opt = new Option("On", true); - input.options.add(opt); - try{ - if (i in data.currentCameraConstraints) { - if (data.cameraConstraints[i]['torch'] == true) { - opt.selected = "true"; - } - } - } catch(e){} - } else { - continue; - } - - input.id = "constraints_" + i + "_"+UUID; - input.className = "constraintCameraInput"; - input.name = input.id; - input.dataset.UUID = UUID; - input.style = "display:inline; padding:2px;"; - input.dataset.keyname = i; - input.dataset.chosen = input.value; - input.onchange = function(e) { - this.dataset.chosen = this.value; - //getById("label_"+e.target.dataset.keyname+ "_" + e.target.dataset.UUID).innerText =e.target.dataset.keyname+": "+e.target.value; - //updateVideoConstraints(e.target.dataset.keyname, e.target.value); - if (CtrlPressed){ - requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, true); - } else { - requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, false); - } - log(e.target.dataset.keyname, e.target.value); - }; - videoEle.appendChild(div); - div.appendChild(label); - div.appendChild(input); - } else if (typeof data.cameraConstraints[i] === 'boolean') { - - var div = document.createElement("div"); - var label = document.createElement("label"); - label.id = "label_" + + i + "_"+UUID; - label.name = label.id; - label.htmlFor = "constraints_" + + i + "_"+UUID; - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; - label.style = "display:inline-block; padding:0;"; - label.dataset.keyname = i; - label.dataset.UUID = UUID; - var input = document.createElement("select"); - var c = document.createElement("option"); - - var opt = new Option("Off", false); - input.options.add(opt); - opt = new Option("On", true); - input.options.add(opt); - - try{ - if (data.audioConstraints[i] === true){ - opt.selected = "true"; - } - } catch(e){} - - input.id = "constraints_" + + i + "_"+UUID; - input.className = "constraintCameraInput"; - input.name = input.id; - input.style = "display:inline; padding:2px;"; - input.dataset.UUID = UUID; - input.dataset.keyname = i; - input.dataset.chosen = input.value; - input.onchange = function(e) { - this.dataset.chosen = this.value; - //getById("label_"+e.target.dataset.keyname+ "_" + e.target.dataset.UUID).innerText =e.target.dataset.keyname+": "+e.target.value; - //updateVideoConstraints(e.target.dataset.keyname, e.target.value); - if (CtrlPressed){ - requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, true); - } else { - requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, false); - } - log(e.target.dataset.keyname, e.target.value); - }; - videoEle.appendChild(div); - div.appendChild(label); - div.appendChild(input); - } - } catch (e) { - errorlog(e); - } - } - - query("#container_"+UUID+" .advancedVideoSettings").innerHTML = ""; - query("#container_"+UUID+" .advancedVideoSettings").appendChild(videoEle); - query("#container_"+UUID+" .advancedVideoSettings").classList.remove("hidden"); - - if (fixScrollReset){ - clearTimeout(fixScrollReset); - fixScrollReset = null; - getById("directorlayout").scrollTop = fixScrollResetValue; - } -} - -/////// -function capitalizeFirstLetter(string) { - return string.charAt(0).toUpperCase() + string.slice(1); -} - -function listAudioSettings() { - getById("popupSelector_constraints_audio").innerHTML = ""; - - var tracks = session.streamSrc.getAudioTracks(); - if (!tracks.length){ - warnlog("session.streamSrc contains no audio tracks"); - return - } - - for (var ii = 0; ii< tracks.length; ii++){ - track0 = tracks[ii]; - if (track0.getCapabilities) { - session.audioConstraints = track0.getCapabilities(); - } else if (Firefox){ // let's pretend like Firefox doesn't actually suck - session.audioConstraints = { - "autoGainControl": [ - true, - false - ], - // "channelCount": { - // "max": 2, - // "min": 1 - // }, - // "deviceId": "default", - "echoCancellation": [ - true, - false - ], - // "groupId": "a3cbdec54a9b6ed473fd950415626f7e76f9d1b90f8c768faab572175a355a17", - // "latency": { - // "max": 0.01, - // "min": 0.01 - // }, - "noiseSuppression": [ - true, - false - ], - // "sampleRate": { - // "max": 48000, - // "min": 48000 - // }, - // "sampleSize": { - // "max": 16, - // "min": 16 - /// } - }; - } - - try { - if (track0.getSettings) { - session.currentAudioConstraints = track0.getSettings(); - - if (!session.stereo){ - try { - delete session.currentAudioConstraints.channelCount; - delete session.audioConstraints.channelCount; - } catch(e){}; - } else if (session.audioInputChannels && (session.audioInputChannels==1)){ // this is pretty hacky, but it gets around not being able to actually set 1-channel. Not sure why. - session.currentAudioConstraints.channelCount = 1; - } - - } - } catch (e) { - errorlog(e); - } - - ////// - if (ii==0){ - for (var webAudio in session.webAudios) { - if (session.webAudios[webAudio].gainNode ) { - - if (getById("popupSelector_constraints_audio").style.display == "none") { - getById("advancedOptionsAudio").style.display = "inline-flex"; - } - var div = document.createElement("div"); - var label = document.createElement("label"); - var i = "masterGain"; - //label.id = "label_" + i; - label.htmlFor = "constraints_" + i; - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; - label.style = "display:inline-block;"; - - var input = document.createElement("input"); - input.min = 0; - input.max = 200; - - input.dataset.deviceid = track0.id; // pointless - - input.type = "range"; - input.dataset.keyname = i; - input.dataset.labelname = label.innerHTML; - input.id = "constraints_" + i; - input.style = "display:block; width:100%;"; - input.name = "constraints_" + i; - - input.value = session.webAudios[webAudio].gainNode.gain.value * 100; - //label.innerHTML += " " + parseInt(session.webAudios[webAudio].gainNode.gain.value * 100); - input.title = parseInt(input.value); - - var manualInput = document.createElement("input"); - manualInput.type = "number"; - manualInput.dataset.keyname = i; - manualInput.dataset.deviceid = track0.id; - manualInput.dataset.labelname = label.innerHTML; - manualInput.value = parseFloat(input.value); - manualInput.className = "manualInput"; - manualInput.id = "label_" + i; - - manualInput.onchange = function(e) { - getById("constraints_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - changeMainGain(e.target.value); - e.target.title = e.target.value; - pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - - input.oninput = function(e) { - getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - changeMainGain(e.target.value); - e.target.title = e.target.value; - }; - - input.onchange = function(e) { - getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - changeMainGain(e.target.value); - e.target.title = e.target.value; - pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - - getById("popupSelector_constraints_audio").appendChild(div); - div.appendChild(label); - div.appendChild(manualInput); - div.appendChild(input); - break; - } - } - } - - if (session.micDelay!==false && ii==0) { // ii==0 implies only track0 is supported by the web audio pipeline currently (or everything after the mixer node) - if (getById("popupSelector_constraints_audio").style.display == "none") { - getById("advancedOptionsAudio").style.display = "inline-flex"; - } - - var label = document.createElement("label"); - var i = "micDelay"; - label.htmlFor = "constraints_" + i; - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + " (ms):"; - - var input = document.createElement("input"); - input.min = 0; - input.max = 500; - - input.dataset.deviceid = track0.id; // pointless, for now - - input.type = "range"; - input.dataset.keyname = i; - input.dataset.labelname = label.innerHTML; - input.id = "constraints_" + i; - input.style = "display:block; width:100%;"; - input.name = "constraints_" + i; - - for (var webAudio in session.webAudios) { - if (session.webAudios[webAudio].micDelay) { // session.webAudios[waid].micDelay.delayTime.setValueAtTime - input.value = session.webAudios[webAudio].micDelay.delayTime.value*1000; - label.innerHTML += " " + parseInt(session.webAudios[webAudio].micDelay.delayTime.value*1000); - input.title = input.value; - break; - } - } - - var manualInput = document.createElement("input"); - manualInput.type = "number"; - manualInput.dataset.keyname = i; - manualInput.dataset.labelname = label.innerHTML; - manualInput.value = parseFloat(input.value); - manualInput.className = "manualInput"; - manualInput.id = "label_" + i; - - manualInput.onchange = function(e) { - getById("constraints_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - changeMicDelay(e.target.value, e.target.dataset.deviceid); - e.target.title = e.target.value; - pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - - input.oninput = function(e) { - getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - changeMicDelay(e.target.value, e.target.dataset.deviceid); - e.target.title = e.target.value; - }; - - input.onchange = function(e) { - getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - changeMicDelay(e.target.value, e.target.dataset.deviceid); - e.target.title = e.target.value; - pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - - getById("popupSelector_constraints_audio").appendChild(label); - getById("popupSelector_constraints_audio").appendChild(manualInput); - getById("popupSelector_constraints_audio").appendChild(input); - } - - - if (session.lowcut && ii==0) { // ii==0 implies only track0 is supported by the web audio pipeline currently (or everything after the mixer node) - if (getById("popupSelector_constraints_audio").style.display == "none") { - getById("advancedOptionsAudio").style.display = "inline-flex"; - } - - var label = document.createElement("label"); - var i = "Low_Cut"; - label.htmlFor = "constraints_" + i; - label.innerText = "Low Cut:"; - - var input = document.createElement("input"); - input.min = 50; - input.max = 400; - - input.dataset.deviceid = track0.id; // pointless - - input.type = "range"; - input.dataset.keyname = i; - input.dataset.labelname = label.innerHTML; - input.id = "constraints_" + i; - input.style = "display:block; width:100%;"; - input.name = "constraints_" + i; - - for (var webAudio in session.webAudios) { - if (session.webAudios[webAudio].lowcut1) { - input.value = session.webAudios[webAudio].lowcut1.frequency.value; - label.innerHTML += " " + session.webAudios[webAudio].lowcut1.frequency.value; - input.title = input.value; - break; - } - } - - var manualInput = document.createElement("input"); - manualInput.type = "number"; - manualInput.dataset.keyname = i; - manualInput.dataset.labelname = label.innerHTML; - manualInput.value = parseFloat(input.value); - manualInput.className = "manualInput"; - manualInput.id = "label_" + i; - - manualInput.onchange = function(e) { - getById("constraints_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - changeLowCut(e.target.value, e.target.dataset.deviceid); - e.target.title = e.target.value; - pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - - input.oninput = function(e) { - getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - changeLowCut(e.target.value, e.target.dataset.deviceid); - e.target.title = e.target.value; - }; - - input.onchange = function(e) { - getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - changeLowCut(e.target.value, e.target.dataset.deviceid); - e.target.title = e.target.value; - pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - - getById("popupSelector_constraints_audio").appendChild(label); - getById("popupSelector_constraints_audio").appendChild(manualInput); - getById("popupSelector_constraints_audio").appendChild(input); - } - - if (session.equalizer && ii==0) { // ii==0 implies only track0 is supported by the web audio pipeline currently (or everything after the mixer node) - if (getById("popupSelector_constraints_audio").style.display == "none") { - getById("advancedOptionsAudio").style.display = "inline-flex"; - } - - var label = document.createElement("label"); - var i = "Low_EQ"; - //label.id = "label_" + i; - label.htmlFor = "constraints_" + i; - label.innerHTML = "Low EQ:"; - - var input = document.createElement("input"); - input.min = -50; - input.max = 50; - - input.dataset.deviceid = track0.id; // pointless - - input.type = "range"; - input.dataset.keyname = i; - input.dataset.labelname = label.innerHTML; - input.id = "constraints_" + i; - input.style = "display:block; width:100%;"; - input.name = "constraints_" + i; - - for (var webAudio in session.webAudios) { - if (session.webAudios[webAudio].lowEQ) { - input.value = session.webAudios[webAudio].lowEQ.gain.value; - label.innerHTML += " " + session.webAudios[webAudio].lowEQ.gain.value; - input.title = input.value; - } - } - - var manualInput = document.createElement("input"); - manualInput.type = "number"; - manualInput.dataset.keyname = i; - manualInput.dataset.labelname = label.innerHTML; - manualInput.value = parseFloat(input.value); - manualInput.className = "manualInput"; - manualInput.id = "label_" + i; - - manualInput.onchange = function(e) { - getById("constraints_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - changeLowEQ(e.target.value, e.target.dataset.deviceid); - e.target.title = e.target.value; - pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - - input.oninput = function(e) { - getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - changeLowEQ(e.target.value, e.target.dataset.deviceid); - e.target.title = e.target.value; - }; - - input.onchange = function(e) { - getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - changeLowEQ(e.target.value, e.target.dataset.deviceid); - e.target.title = e.target.value; - pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - - getById("popupSelector_constraints_audio").appendChild(label); - getById("popupSelector_constraints_audio").appendChild(manualInput); - getById("popupSelector_constraints_audio").appendChild(input); - // - if (getById("popupSelector_constraints_audio").style.display == "none") { - getById("advancedOptionsAudio").style.display = "inline-flex"; - } - - var label = document.createElement("label"); - var i = "Mid_EQ"; - //label.id = "label_" + i; - label.htmlFor = "constraints_" + i; - label.innerHTML = "Mid EQ:"; - - var input = document.createElement("input"); - input.min = -50; - input.max = 50; - - input.dataset.deviceid = track0.id; // pointless - - input.type = "range"; - input.dataset.keyname = i; - input.dataset.labelname = label.innerHTML; - input.id = "constraints_" + i; - input.style = "display:block; width:100%;"; - input.name = "constraints_" + i; - - - for (var webAudio in session.webAudios) { - if (session.webAudios[webAudio].midEQ) { - input.value = session.webAudios[webAudio].midEQ.gain.value; - label.innerHTML += " " + session.webAudios[webAudio].midEQ.gain.value; - input.title = input.value; - } - } - - var manualInput = document.createElement("input"); - manualInput.type = "number"; - manualInput.dataset.keyname = i; - manualInput.dataset.labelname = label.innerHTML; - manualInput.value = parseFloat(input.value); - manualInput.className = "manualInput"; - manualInput.id = "label_" + i; - - manualInput.onchange = function(e) { - getById("constraints_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - changeMidEQ(e.target.value, e.target.dataset.deviceid); - e.target.title = e.target.value; - pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - - input.oninput = function(e) { - getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - changeMidEQ(e.target.value, e.target.dataset.deviceid); - e.target.title = e.target.value; - }; - - input.onchange = function(e) { - getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - changeMidEQ(e.target.value, e.target.dataset.deviceid); - e.target.title = e.target.value; - pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - - getById("popupSelector_constraints_audio").appendChild(label); - getById("popupSelector_constraints_audio").appendChild(manualInput); - getById("popupSelector_constraints_audio").appendChild(input); - // - if (getById("popupSelector_constraints_audio").style.display == "none") { - getById("advancedOptionsAudio").style.display = "inline-flex"; - } - - var label = document.createElement("label"); - var i = "High_EQ"; - //label.id = "label_" + i; - label.htmlFor = "constraints_" + i; - label.innerHTML = "High EQ:"; - - var input = document.createElement("input"); - input.min = -50; - input.max = 50; - - input.dataset.deviceid = track0.id; // pointless - - input.type = "range"; - input.dataset.keyname = i; - input.dataset.labelname = label.innerHTML; - input.id = "constraints_" + i; - input.style = "display:block; width:100%;"; - input.name = "constraints_" + i; - - for (var webAudio in session.webAudios) { - if (session.webAudios[webAudio].highEQ) { - input.value = session.webAudios[webAudio].highEQ.gain.value; - label.innerHTML += " " + session.webAudios[webAudio].highEQ.gain.value; - input.title = input.value; - } - } - - var manualInput = document.createElement("input"); - manualInput.type = "number"; - manualInput.dataset.keyname = i; - manualInput.dataset.labelname = label.innerHTML; - manualInput.value = parseFloat(input.value); - manualInput.className = "manualInput"; - manualInput.id = "label_" + i; - - manualInput.onchange = function(e) { - getById("constraints_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - changeHighEQ(e.target.value, e.target.dataset.deviceid); - e.target.title = e.target.value; - pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - - input.oninput = function(e) { - getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - changeHighEQ(e.target.value, e.target.dataset.deviceid); - e.target.title = e.target.value; - }; - - input.onchange = function(e) { - getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - changeHighEQ(e.target.value, e.target.dataset.deviceid); - e.target.title = e.target.value; - pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - - getById("popupSelector_constraints_audio").appendChild(label); - getById("popupSelector_constraints_audio").appendChild(manualInput); - getById("popupSelector_constraints_audio").appendChild(input); - } - - if ((session.noisegate!==false) && (ii==0)) { - for (var webAudio in session.webAudios) { - if (session.webAudios[webAudio].gatingNode) { - - var div = document.createElement("div"); - var label = document.createElement("label"); - - var i = "noiseGating"; - - label.id = "label_" + i + "_"+ii; - label.htmlFor = "constraints_" + i + "_"+ii; - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; - label.style = "display:inline-block;"; - label.dataset.keyname = i; - label.title = "This will reduce the gain ~80% when there is no one talking loudly"; - var input = document.createElement("select"); - var c = document.createElement("option"); - - - input.dataset.deviceid = track0.id; - - var opt = new Option("Off", false); - input.options.add(opt); - opt = new Option("On", true); - if (session.noisegate){ - opt.selected = "true"; - } - input.options.add(opt); - - if (getById("popupSelector_constraints_audio").style.display == "none") { - getById("advancedOptionsAudio").style.display = "inline-flex"; - } - - input.id = "constraints_" + i + "_"+ii; - input.className = "constraintCameraInput"; - input.name = "constraints_" + i + "_"+ii; - input.style = "display:inline; padding:2px;"; - input.dataset.keyname = i; - input.dataset.chosen = input.value; - input.onchange = function(e) { - this.dataset.chosen = this.value; - if (e.target.value == "false"){ - session.noisegate = null; - } else if (e.target.value == "true"){ - session.noisegate = true; - } else { - session.noisegate = e.target.value; - } - if (!session.noisegate){ - changeGatingGain(100); - changeGatingGain(100,3100); - } - pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - - getById("popupSelector_constraints_audio").appendChild(div); - div.appendChild(label); - div.appendChild(input); - break; - } - } - } - - - //////// - if (tracks.length>1){ - var label = document.createElement("h4"); - label.innerHTML = track0.label; - label.style = "text-shadow: 0 0 10px #fff3;margin:0px 0 10px 0" - if (ii>0){ - label.style = "text-shadow: 0 0 10px #fff3;margin:40px 0 10px 0" - } - getById("popupSelector_constraints_audio").appendChild(label); - } - - for (var i in session.audioConstraints) { - try { - log(i); - log(session.audioConstraints[i]); - - if ((typeof session.audioConstraints[i] === 'object') && (session.audioConstraints[i] !== null) && ("max" in session.audioConstraints[i]) && ("min" in session.audioConstraints[i])) { - if (i === "aspectRatio") { - continue; - } else if (i === "width") { - continue; - } else if (i === "height") { - continue; - } else if (i === "frameRate") { - continue; - } else if (i === "latency") { - // continue; - } else if (i === "sampleRate") { - //continue; - } else if (i === "sampleSize") { - //continue; - } else if (i === "channelCount") { - if (!session.stereo){ - continue; - } - } else if (!session.disableWebAudio && (i === "volume")){ - continue; - } - - var label = document.createElement("label"); - //label.id = "label_" + i + "_"+ii; - label.htmlFor = "constraints_" + i + "_"+ii; - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; - - - var input = document.createElement("input"); - input.min = session.audioConstraints[i].min; - input.max = session.audioConstraints[i].max; - - input.dataset.deviceid = track0.id; - - - - if (getById("popupSelector_constraints_audio").style.display == "none") { - getById("advancedOptionsAudio").style.display = "inline-flex"; - } - - var manualInput = document.createElement("input"); - manualInput.type = "number"; - - if ("step" in session.audioConstraints[i]) { - input.step = session.audioConstraints[i].step; - manualInput.step = session.audioConstraints[i].step; - } else if ("volume" == i) { - input.step = 0.01; - manualInput.step = 0.01; - } - - if (i in session.currentAudioConstraints) { - input.value = parseFloat(session.currentAudioConstraints[i]); - label.title = "Previously was: " + session.currentAudioConstraints[i]; - input.title = "Previously was: " + session.currentAudioConstraints[i]; - } - - if ((i === "height") || (i === "width")){ - input.title = "Hold CTRL (or cmd) to lock width and height together when changing them"; - input.min = 16; - } - - - input.type = "range"; - input.dataset.keyname = i; - input.dataset.track = ii; - - input.id = "constraints_" + i + "_"+ii; - input.style = "display:block; width:100%;"; - input.name = "constraints_" + i + "_"+ii; - - manualInput.dataset.keyname = i; - manualInput.dataset.track = ii; - manualInput.dataset.deviceid = track0.id; - - manualInput.className = "manualInput"; - manualInput.id = "label_" + i + "_"+ii; - manualInput.max = session.audioConstraints[i].max; - manualInput.min = session.audioConstraints[i].min; - - manualInput.value = parseFloat(session.currentAudioConstraints[i]); - - if (i=="channelCount"){ - input.style.display = "none"; - } - - manualInput.onchange = function(e) { - try { - getById("constraints_" + e.target.dataset.keyname+"_"+ e.target.dataset.track).value = parseFloat(e.target.value); - applyAudioHack(e.target.dataset.keyname, e.target.value, e.target.dataset.deviceid); - e.target.title = e.target.value; - pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }catch(e){errorlog(e);} - }; - - input.onchange = function(e) { - try { - getById("label_" + e.target.dataset.keyname+"_"+ e.target.dataset.track).value = parseFloat(e.target.value); - applyAudioHack(e.target.dataset.keyname, e.target.value, e.target.dataset.deviceid); - e.target.title = e.target.value; - pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }catch(e){errorlog(e);} - }; - - // not sure if I should include "oninput" as well? Probably not needed. - - var div = document.createElement("div"); - if (parseFloat(input.min) == parseFloat(input.max)) { - manualInput.disabled = true - manualInput.title = "Only one option available, so can't be changed"; - label.title = "Only one option available, so can't be changed"; - div.appendChild(label); - div.appendChild(manualInput); - getById("popupSelector_constraints_audio").appendChild(div); - } else { - div.appendChild(label); - div.appendChild(manualInput); - div.appendChild(input); - getById("popupSelector_constraints_audio").appendChild(div); - } - } else if ((typeof session.audioConstraints[i] === 'object') && (session.audioConstraints[i] !== null)) { - if (i == "resizeMode") { - continue; - } - - var div = document.createElement("div"); - var label = document.createElement("label"); - label.id = "label_" + i + "_"+ii; - label.htmlFor = "constraints_" + i + "_"+ii; - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; - label.style = "display:inline-block;"; - label.dataset.keyname = i; - - var input = document.createElement("select"); - var c = document.createElement("option"); - - if (session.audioConstraints[i].length == 2) { - for (var opts in session.audioConstraints[i]) { - log(opts); - if (session.audioConstraints[i][opts]===true){ - var opt = new Option("On", session.audioConstraints[i][opts]); - } else if (session.audioConstraints[i][opts]===false){ - var opt = new Option("Off", session.audioConstraints[i][opts]); - } else { - var opt = new Option(session.audioConstraints[i][opts], session.audioConstraints[i][opts]); - } - input.options.add(opt); - - if (i in session.currentAudioConstraints) { - if (session.audioConstraints[i][opts] == session.currentAudioConstraints[i]) { - opt.selected = "true"; - } - } - } - } else if (session.audioConstraints[i].length > 1) { - for (var opts in session.audioConstraints[i]) { - log(opts); - var opt = new Option(session.audioConstraints[i][opts], session.audioConstraints[i][opts]); - input.options.add(opt); - - if (i in session.currentAudioConstraints) { - if (session.audioConstraints[i][opts] == session.currentAudioConstraints[i]) { - opt.selected = "true"; - } - } - - } - } else if (i.toLowerCase == "torch") { - var opt = new Option("Off", false); - input.options.add(opt); - opt = new Option("On", true); - input.options.add(opt); - } else { - continue; - } - - if (getById("popupSelector_constraints_audio").style.display == "none") { - getById("advancedOptionsAudio").style.display = "inline-flex"; - } - - input.id = "constraints_" + i + "_"+ii; - input.className = "constraintCameraInput"; - input.name = "constraints_" + i + "_"+ii; - input.dataset.deviceid = track0.id; - input.style = "display:inline; padding:2px;"; - input.dataset.keyname = i; - input.dataset.chosen = input.value; - input.onchange = function(e) { - this.dataset.chosen = this.value; - applyAudioHack(e.target.dataset.keyname, e.target.value, e.target.dataset.deviceid); - pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - getById("popupSelector_constraints_audio").appendChild(div); - div.appendChild(label); - div.appendChild(input); - } else if (typeof session.audioConstraints[i] === 'boolean') { - - var div = document.createElement("div"); - var label = document.createElement("label"); - label.id = "label_" + i + "_"+ii; - label.htmlFor = "constraints_" + i + "_"+ii; - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; - label.style = "display:inline-block;"; - label.dataset.keyname = i; - var input = document.createElement("select"); - var c = document.createElement("option"); - - - input.dataset.deviceid = track0.id; - - var opt = new Option("Off", false); - input.options.add(opt); - opt = new Option("On", true); - input.options.add(opt); - - - - if (getById("popupSelector_constraints_audio").style.display == "none") { - getById("advancedOptionsAudio").style.display = "inline-flex"; - } - - input.id = "constraints_" + i + "_"+ii; - input.className = "constraintCameraInput"; - input.name = "constraints_" + i + "_"+ii; - input.style = "display:inline; padding:2px;"; - input.dataset.keyname = i; - input.dataset.chosen = input.value; - input.onchange = function(e) { - this.dataset.chosen = this.value; - //getById("label_"+e.target.dataset.keyname).innerHTML =e.target.dataset.keyname+": "+e.target.value; - //updateAudioConstraints(e.target.dataset.keyname, e.target.value); - applyAudioHack(e.target.dataset.keyname, e.target.value, e.target.dataset.deviceid); - pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - getById("popupSelector_constraints_audio").appendChild(div); - div.appendChild(label); - div.appendChild(input); - } - } catch (e) { - errorlog(e); - } - } - - if (tracks.length>1){ - for (var webAudio in session.webAudios) { - if (session.webAudios[webAudio].subGainNodes && (track0.id in session.webAudios[webAudio].subGainNodes)) { - - if (getById("popupSelector_constraints_audio").style.display == "none") { - getById("advancedOptionsAudio").style.display = "inline-flex"; - } - var div = document.createElement("div"); - var label = document.createElement("label"); - var i = "Gain"; - //label.id = "label_" + i + "_" + track0.id; - label.htmlFor = "constraints_" + i + "_" + track0.id; - label.innerText = "Gain:"; - label.style = "display:inline-block; padding:0;margin-top: 15px"; - - var input = document.createElement("input"); - input.min = 0; - input.max = 200; - - input.dataset.deviceid = track0.id; // pointless - - input.type = "range"; - input.dataset.keyname = i; - input.dataset.labelname = label.innerHTML; - input.id = "constraints_" + i+ "_" + track0.id; - input.style = "display:block; width:100%;"; - input.name = "constraints_" + i + "_" + track0.id; - - input.value = session.webAudios[webAudio].subGainNodes[track0.id].gain.value * 100; - label.innerHTML += " " + parseInt(session.webAudios[webAudio].subGainNodes[track0.id].gain.value * 100); - input.title = parseInt(input.value); - - var manualInput = document.createElement("input"); - manualInput.type = "number"; - manualInput.dataset.keyname = i; - manualInput.dataset.deviceid = track0.id; - manualInput.dataset.labelname = label.innerHTML; - manualInput.value = parseFloat(input.value); - manualInput.className = "manualInput"; - manualInput.id = "label_" + i + "_" + track0.id; - - manualInput.onchange = function(e) { - getById("constraints_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - changeSubGain(e.target.value, e.target.dataset.deviceid); - e.target.title = e.target.value; - pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - - input.oninput = function(e) { - getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - changeSubGain(e.target.value, e.target.dataset.deviceid); - e.target.title = e.target.value; - }; - - input.onchange = function(e) { - getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - changeSubGain(e.target.value, e.target.dataset.deviceid); - e.target.title = e.target.value; - pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - - getById("popupSelector_constraints_audio").appendChild(div); - div.appendChild(label); - div.appendChild(manualInput); - div.appendChild(input); - break; - } - } - } - - } -} - - -function applyAudioHack(constraint, value = null, deviceid="default") { - if (value == parseFloat(value)) { - value = parseFloat(value); - if (constraint == "channelCount"){ - session.audioInputChannels = value; - } - value = { - exact: value - }; - } else if (value == "true") { - value = true; - } else if (value == "false") { - value = false; - } - //////////////// - try { - var tracks = session.streamSrc.getAudioTracks(); - if (tracks.length) { - var track0 = tracks[0]; - for (var ii = 0;ii session.totalRoomBitrate) { - return; - } else { - session.controlRoomBitrate = parseInt(e.target.value); - } - updateMixer(); - pokeIframeAPI("camera-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - - input.onchange = function(e) { - getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - if (e.target.value > session.totalRoomBitrate) { - return; - } else { - session.controlRoomBitrate = parseInt(e.target.value); - } - updateMixer(); - pokeIframeAPI("camera-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - getById("popupSelector_constraints_video").appendChild(label); - getById("popupSelector_constraints_video").appendChild(manualInput); - getById("popupSelector_constraints_video").appendChild(input); - } - try { - var track0 = session.streamSrc.getVideoTracks(); - if (track0.length) { - track0 = track0[0]; - if (track0.getCapabilities) { - session.cameraConstraints = track0.getCapabilities(); - } else { - session.cameraConstraints = {}; // probably firefox... - } - log(session.cameraConstraints); - } - } catch (e) { - errorlog(e); - return; - } - - try { - if (track0.getSettings) { - session.currentCameraConstraints = track0.getSettings(); - - if (screen && screen.orientation && screen.orientation.type){ - if (!screen.orientation.type.includes("portrait")){ - if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ - session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; - } - } - } else if (!window.matchMedia("(orientation: portrait)").matches){ - if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ - session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; - } - } - } else { - session.currentCameraConstraints = {}; - } - } catch (e) { - errorlog(e); - } - - - for (var i in session.cameraConstraints) { - try { - log(i); - log(session.cameraConstraints[i]); - - if (i === "focusMode") { - continue // I'll handle this with FocusDistance instead - } else if (i === "whiteBalanceMode") { - continue // I'll handle this elsewhere - } else if (i === "exposureMode") { - continue // I'll handle this elsewhere - } - - if ((typeof session.cameraConstraints[i] === 'object') && (session.cameraConstraints[i] !== null) && ("max" in session.cameraConstraints[i]) && ("min" in session.cameraConstraints[i])) { - - var manualMode = false; - var manualLabel = false; - if (i ==="exposureTime"){ - if (session.currentCameraConstraints["exposureMode"]){ - manualMode = document.createElement("input"); - manualMode.type = "checkbox"; - manualMode.id = "manual_"+i; - manualMode.dataset.keyname = "exposureMode"; - manualMode.onchange = function(e) { - var value = "manual"; - if (e.target.checked){ - value = "continuous"; - } - if (CtrlPressed){ - updateCameraConstraints(e.target.dataset.keyname, value, true, false); - } else { - updateCameraConstraints(e.target.dataset.keyname, value, false, false); - } - pokeIframeAPI("camera-constraint-changed", {name:e.target.dataset.keyname, value:value}); - }; - - manualLabel = document.createElement("label"); - manualLabel.htmlFor = manualMode.id; - manualLabel.innerHTML = "Auto: "; - manualLabel.style.marginLeft = "20px"; - if (session.currentCameraConstraints["exposureMode"] == "continuous"){ - manualMode.checked = true; - } - } - } else if (i ==="focusDistance"){ - if (session.currentCameraConstraints["focusMode"]){ - manualMode = document.createElement("input"); - manualMode.type = "checkbox"; - manualMode.id = "manual_"+i; - manualMode.dataset.keyname = "focusMode"; - manualMode.onchange = function(e) { - var value = "manual"; - if (e.target.checked){ - value = "continuous"; - } - if (CtrlPressed){ - updateCameraConstraints(e.target.dataset.keyname, value, true, false); - } else { - updateCameraConstraints(e.target.dataset.keyname, value, false, false); - } - pokeIframeAPI("camera-constraint-changed", {name:e.target.dataset.keyname, value:value}); - }; - manualLabel = document.createElement("label"); - manualLabel.htmlFor = manualMode.id; - manualLabel.innerHTML = "Auto: "; - manualLabel.style.marginLeft = "20px"; - if (session.currentCameraConstraints["focusMode"] == "continuous"){ - manualMode.checked = true; - } - } - } else if (i ==="colorTemperature"){ - if (session.currentCameraConstraints["whiteBalanceMode"]){ - manualMode = document.createElement("input"); - manualMode.type = "checkbox"; - manualMode.id = "manual_"+i; - manualMode.dataset.keyname = "whiteBalanceMode"; - manualMode.onchange = function(e) { - var value = "manual"; - if (e.target.checked){ - value = "continuous"; - } - if (CtrlPressed){ - updateCameraConstraints(e.target.dataset.keyname, value, true, false); - } else { - updateCameraConstraints(e.target.dataset.keyname, value, false, false); - } - pokeIframeAPI("camera-constraint-changed", {name:e.target.dataset.keyname, value:value}); - }; - manualLabel = document.createElement("label"); - manualLabel.htmlFor = manualMode.id; - manualLabel.innerHTML = "Auto: "; - manualLabel.style.marginLeft = "20px"; - if (session.currentCameraConstraints["whiteBalanceMode"] == "continuous"){ - manualMode.checked = true; - } - } - } - - - - var label = document.createElement("label"); - label.htmlFor = "constraints_" + i; - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; - - var input = document.createElement("input"); - input.min = parseFloat(session.cameraConstraints[i].min); - - if (i === "aspectRatio") { - input.max = 5; - input.min = 0.2 - } else { - input.min = parseFloat(session.cameraConstraints[i].min); - input.max = parseFloat(session.cameraConstraints[i].max); - } - - if (parseFloat(input.min) == parseFloat(input.max)) { - continue; - } - - if (getById("popupSelector_constraints_video").style.display == "none") { - getById("advancedOptionsCamera").style.display = "inline-flex"; - } - - var manualInput = document.createElement("input"); - manualInput.type = "number"; - manualInput.dataset.keyname = i; - - manualInput.className = "manualInput"; - manualInput.id = "label_" + i; - - if ("step" in session.cameraConstraints[i]) { - input.step = session.cameraConstraints[i].step; - manualInput.step = session.cameraConstraints[i].step; - } else if (i === "aspectRatio") { - input.step = 0.000001 - manualInput.step = 0.005; - } - - if (i in session.currentCameraConstraints) { - input.value = parseFloat(session.currentCameraConstraints[i]); - //label.innerHTML = i + ": " + session.currentCameraConstraints[i]; - manualInput.value = parseFloat(session.currentCameraConstraints[i]); - label.title = "Previously was: " + session.currentCameraConstraints[i]; - input.title = "Previously was: " + session.currentCameraConstraints[i]; - } else { - label.innerHTML = i; - } - if ((i === "height") || (i === "width")){ - input.title = "Hold CTRL (or cmd) to lock width and height together when changing them"; - input.min = 16; - } - - input.type = "range"; - input.dataset.keyname = i; - input.id = "constraints_" + i; - input.style = "display:block; width:100%;"; - input.name = "constraints_" + i; - - // on manualInput.change = .. update the input field! gotta riprocate - - manualInput.onchange = function(e) { - getById("constraints_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - updateCameraConstraints(e.target.dataset.keyname, e.target.value, false, false); - pokeIframeAPI("camera-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - - input.oninput = function(e) { - getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - if (CtrlPressed){ - updateCameraConstraints(e.target.dataset.keyname, e.target.value, true, false, false); - } else { - updateCameraConstraints(e.target.dataset.keyname, e.target.value, false, false, false); - } - }; - - input.onchange = function(e) { - getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - if (CtrlPressed){ - updateCameraConstraints(e.target.dataset.keyname, e.target.value, true, false); - } else { - updateCameraConstraints(e.target.dataset.keyname, e.target.value, false, false); - } - pokeIframeAPI("camera-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - - - getById("popupSelector_constraints_video").appendChild(label); - getById("popupSelector_constraints_video").appendChild(manualInput); - if (manualMode && manualLabel){ - getById("popupSelector_constraints_video").appendChild(manualLabel); - getById("popupSelector_constraints_video").appendChild(manualMode); - } - - if (i === "aspectRatio") { - var preSelectButton = document.createElement("button"); - preSelectButton.value = 16/9.0; - preSelectButton.innerText = "16:9"; - preSelectButton.dataset.keyname = i; - preSelectButton.className = "preSelectButton"; - preSelectButton.onclick = function(e) { - getById("constraints_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - updateCameraConstraints(e.target.dataset.keyname, e.target.value, false, false); - }; - getById("popupSelector_constraints_video").appendChild(preSelectButton); - var preSelectButton = document.createElement("button"); - preSelectButton.value = 9/16.0; - preSelectButton.innerText = "9:16"; - preSelectButton.className = "preSelectButton"; - preSelectButton.dataset.keyname = i; - preSelectButton.onclick = function(e) { - getById("constraints_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); - updateCameraConstraints(e.target.dataset.keyname, e.target.value, false, false); - }; - getById("popupSelector_constraints_video").appendChild(preSelectButton); - } - - getById("popupSelector_constraints_video").appendChild(input); - } else if ((typeof session.cameraConstraints[i] === 'object') && (session.cameraConstraints[i] !== null)) { - if (i == "resizeMode") { - continue; - } - - var div = document.createElement("div"); - var label = document.createElement("label"); - label.id = "label_" + i; - label.htmlFor = "constraints_" + i; - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; - label.style = "display:inline-block;"; - label.dataset.keyname = i; - var input = document.createElement("select"); - - if (session.cameraConstraints[i].length > 1) { - var included = false; - for (var opts in session.cameraConstraints[i]) { - log(opts); - var opt = new Option(session.cameraConstraints[i][opts], session.cameraConstraints[i][opts]); - input.options.add(opt); - if (i in session.currentCameraConstraints) { - if (session.cameraConstraints[i][opts] == session.currentCameraConstraints[i]) { - opt.selected = "true"; - included = true; - } - } - } - if (!included){ - if (i in session.currentCameraConstraints) { - var opt = new Option(session.currentCameraConstraints[i], session.currentCameraConstraints[i]); - input.options.add(opt); - opt.selected = "true"; - } - } - } else if (i.toLowerCase == "torch") { - warnlog("TORCH"); - var opt = new Option("Off", false); - input.options.add(opt); - opt = new Option("On", true); - input.options.add(opt); - try{ - if (session.currentCameraConstraints[i]){ - opt.selected = "selected"; - } - } catch(e){} - } else if (session.cameraConstraints[i].length && ("continuous" == session.cameraConstraints[i][0])){ - var opt = new Option("continuous", "continuous"); - input.options.add(opt); - if (i in session.currentCameraConstraints) { - if ("continuous" == session.currentCameraConstraints[i]) { - opt.selected = "true"; - var opt = new Option("manual", "manual"); - input.options.add(opt); - var opt = new Option("none", "none"); - input.options.add(opt); - } else { - var opt = new Option(session.currentCameraConstraints[i], session.currentCameraConstraints[i]); - input.options.add(opt); - opt.selected = "true"; - if (session.currentCameraConstraints[i]=="none"){ - var opt = new Option("manual", "manual"); - input.options.add(opt); - } else { - var opt = new Option("none", "none"); - input.options.add(opt); - } - } - } else { - opt.selected = "true"; - var opt = new Option("manual", "manual"); - input.options.add(opt); - var opt = new Option("none", "none"); - input.options.add(opt); - } - } else if (session.cameraConstraints[i].length && ("manual" == session.cameraConstraints[i][0])){ - var opt = new Option("manual", "manual"); - input.options.add(opt); - if (i in session.currentCameraConstraints) { - if ("manual" == session.currentCameraConstraints[i]) { - opt.selected = "true"; - var opt = new Option("continuous", "continuous"); - input.options.add(opt); - var opt = new Option("none", "none"); - input.options.add(opt); - } else { - var opt = new Option(session.currentCameraConstraints[i], session.currentCameraConstraints[i]); - input.options.add(opt); - opt.selected = "true"; - if (session.currentCameraConstraints[i]=="none"){ - var opt = new Option("continuous", "continuous"); - input.options.add(opt); - } else { - var opt = new Option("none", "none"); - input.options.add(opt); - } - } - } else { - opt.selected = "true"; - var opt = new Option("continuous", "continuous"); - input.options.add(opt); - var opt = new Option("none", "none"); - input.options.add(opt); - } - } else { - continue; - } - - if (getById("popupSelector_constraints_video").style.display == "none") { - getById("advancedOptionsCamera").style.display = "inline-flex"; - } - - input.id = "constraints_" + i; - input.className = "constraintCameraInput"; - input.name = "constraints_" + i; - input.style = "display:inline; padding:2px;"; - input.dataset.keyname = i; - input.dataset.chosen = input.value; - input.onchange = function(e) { - this.dataset.chosen = this.value; - //getById("label_"+e.target.dataset.keyname).innerHTML =e.target.dataset.keyname+": "+e.target.value; - if (CtrlPressed){ - updateCameraConstraints(e.target.dataset.keyname, e.target.value, true, false, false); - } else { - updateCameraConstraints(e.target.dataset.keyname, e.target.value, false, false, false); - } - pokeIframeAPI("camera-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - getById("popupSelector_constraints_video").appendChild(div); - div.appendChild(label); - div.appendChild(input); - } else if (typeof session.cameraConstraints[i] === 'boolean') { - - var div = document.createElement("div"); - var label = document.createElement("label"); - label.id = "label_" + i; - label.htmlFor = "constraints_" + i; - label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; - label.style = "display:inline-block;"; - label.dataset.keyname = i; - var input = document.createElement("select"); - - var opt = new Option("Off", "false"); - input.options.add(opt); - - opt = new Option("On", "true"); - input.options.add(opt); - if (session.currentCameraConstraints[i]){ - opt.selected = "true"; - } - - if (getById("popupSelector_constraints_video").style.display == "none") { - getById("advancedOptionsCamera").style.display = "inline-flex"; - } - - input.id = "constraints_" + i; - input.className = "constraintCameraInput"; - input.name = "constraints_" + i; - input.style = "display:inline; padding:2px;"; - input.dataset.keyname = i; - input.dataset.chosen = input.value; - input.onchange = function(e) { - this.dataset.chosen = this.value; - //getById("label_"+e.target.dataset.keyname).innerHTML =e.target.dataset.keyname+": "+e.target.value; - if (CtrlPressed){ - updateCameraConstraints(e.target.dataset.keyname, e.target.value, true, false, false); - } else { - updateCameraConstraints(e.target.dataset.keyname, e.target.value, false, false, false); - } - pokeIframeAPI("camera-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); - }; - getById("popupSelector_constraints_video").appendChild(div); - div.appendChild(label); - div.appendChild(input); - } - } catch (e) { - errorlog(e); - } - } - - if (session.currentCameraConstraints.deviceId){ - if (getStorage("camera_"+session.currentCameraConstraints.deviceId)){ - var button = document.createElement("button"); - button.innerHTML = "Reset video settings to default"; - button.style.display = "block"; - button.style.padding = "10px 5px"; - button.dataset.deviceId = session.currentCameraConstraints.deviceId; - button.onclick = function(){ - var deviceId = this.dataset.deviceId; - var cameraSettings = getStorage("camera_"+deviceId); - var constraints = {}; - var resetResolution = false; - var failed = false; - if (cameraSettings['default']){ - if (cameraSettings['current']){ - for (var i in cameraSettings['default']){ - if (i == "groupId"){ - continue; - } else if (i === "aspectRatio") { // do not load from storage; causes issues - continue; - } else if (i === "width") { - // continue; - } else if (i === "height") { - // continue; - } else { // if I include any of these, it will complain about mixing types and fail - if (i in cameraSettings['current']){ - if (cameraSettings['current'][i] != cameraSettings['default'][i]){ - track0.applyConstraints({ - advanced: [{[i]:cameraSettings['default'][i]}] - }).then(() => {}).catch(e => { - errorlog("Failed to reset to defaults"); - failed = true; - }); - } - } - continue; - } - - if (i in cameraSettings['current']){ - if (cameraSettings['current'][i] != cameraSettings['default'][i]){ - if (i in session.cameraConstraints){ - if ("min" in session.cameraConstraints[i]){ - if (session.cameraConstraints[i].min>cameraSettings['default'][i]){ - continue; - } - } - if ("max" in session.cameraConstraints[i]){ - if (session.cameraConstraints[i].max { - if (!failed){ - removeStorage("camera_"+deviceId); - } - listCameraSettings(); - - if (resetResolution){ - session.setResolution(); // this will reset scaling for all viewers of this stream - } - }).catch(e => { - errorlog("Failed to reset to defaults"); - errorlog(e); - }); - } else if (!failed){ - removeStorage("camera_"+deviceId); - listCameraSettings(); - } - }; - - getById("popupSelector_constraints_video").appendChild(button); - } - } -} - -// applySavedAudioSettings is currently disabled since aec/denoise/etc do not work except with constraints. -function applySavedAudioSettings(track0){ // just applies any saved settings. This then assumes there are already default settings saved, as saved won't be there without the default also. - if (track0.getSettings) { - log("applySavedAudioSettings"); - session.currentAudioConstraints = track0.getSettings(); - if ("deviceId" in session.currentAudioConstraints){ - var deviceId = session.currentAudioConstraints.deviceId; - if (getStorage("audio_"+deviceId)){ - var audioSettings = getStorage("audio_"+deviceId); - var constraints = {}; - if (audioSettings['deviceId']){ - for (var i in session.currentAudioConstraints){ - if (i in audioSettings){ - if (audioSettings[i] != session.currentAudioConstraints[i]){ - if (i == "autoGainControl"){ - } else if (i == "echoCancellation"){ - } else if (i == "noiseSuppression"){ - } else { - continue; - } - constraints[i]=audioSettings[i]; - warnlog("DIFF: "+i); - } - } - } - } - warnlog(constraints); - if (Object.keys(constraints).length){ - track0.applyConstraints({ - advanced: [constraints] // ignore - }).then(() => { - warnlog("audio settings updated for deviceId:"+deviceId); - //removeStorage("audio_"+deviceId); - //listCameraSettings(); - }).catch(e => { - errorlog("Failed to reset to audio defaults"); - }); - } - } - } - } -} - -function applySavedVideoSettings(track0){ // just applies any saved settings. This then assumes there are already default settings saved, as saved won't be there without the default also. - if (track0.getSettings) { - session.currentCameraConstraints = track0.getSettings(); - - if (screen && screen.orientation && screen.orientation.type){ - if (!screen.orientation.type.includes("portrait")){ - if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ - session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; - } - } - } else if (!window.matchMedia("(orientation: portrait)").matches){ - if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ - session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; - } - } - - if ("deviceId" in session.currentCameraConstraints){ - var deviceId = session.currentCameraConstraints.deviceId; - if (getStorage("camera_"+deviceId)){ - var cameraSettings = getStorage("camera_"+deviceId); - var constraints = {}; - if (cameraSettings['current']){ - for (var i in session.currentCameraConstraints){ - if (i in cameraSettings['current']){ - if (cameraSettings['current'][i] != session.currentCameraConstraints[i]){ - if (i == "groupId"){ - continue; - } else if (session.forceAspectRatio && (i === "aspectRatio")){ - log("Skipping saved AspectRatio setting"); - continue; - } - constraints[i]=cameraSettings['current'][i]; - warnlog("DIFF: "+i); - } - } - } - } - - warnlog(constraints); - if (Object.keys(constraints).length){ - track0.applyConstraints({ - advanced: [constraints] // ignore - }).then(() => { - warnlog("video settings updated for deviceId:"+deviceId); - //removeStorage("camera_"+deviceId); - //listCameraSettings(); - }).catch(e => { - errorlog("Failed to reset to defaults"); - }); - } - } - } - } - -} - - -var updateCameraConstraintsBusy = false; -var updateCameraConstraintsNext = false; - -async function updateCameraConstraints(constraint, value = null, ctrl=false, UUID=false, save=true) { - - if ((constraint === "zoom") && (value === 0)){ - log("can't zoom to zero"); - return; - } - - log("updateCameraConstraintsBusy..?"); - - if (updateCameraConstraintsBusy){ - updateCameraConstraintsNext = [constraint, value, ctrl, UUID, save]; - return; - } else { - updateCameraConstraintsBusy = true; - updateCameraConstraintsNext = false; - } - - try { - var track0 = session.streamSrc.getVideoTracks(); - track0 = track0[0]; // shoud only be one video track anyways. - - if (!track0 || (track0.readyState && track0.readyState != 'live') || (!track0.enabled)) { - if (!save){ - errorlog("TRACK IS NOT ENABLED"); - updateCameraConstraintsBusy = false; - updateCameraConstraintsNext = false; - } - } - - - if (value == parseFloat(value)) { - value = parseFloat(value); - } else if (value == "true") { - value = true; - } else if (value == "false") { - value = false; - } - log({ - advanced: [{ - [constraint]: value - }] - }); - - } catch(e){ - errorlog(e); - updateCameraConstraintsBusy = false; - updateCameraConstraintsNext = false; - return e; - } - - log("updateCameraConstraintsNext:"); - log(updateCameraConstraintsNext); - - try { - if (track0.getSettings){ - var cameraSettings = {}; - session.currentCameraConstraints = track0.getSettings(); - - if (screen && screen.orientation && screen.orientation.type){ - if (!screen.orientation.type.includes("portrait")){ - if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ - session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; - } - } - } else if (!window.matchMedia("(orientation: portrait)").matches){ - if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ - session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; - } - } - if (session.currentCameraConstraints.deviceId){ - if (!getStorage("camera_"+session.currentCameraConstraints.deviceId)){ - cameraSettings['default'] = JSON.parse(JSON.stringify(session.currentCameraConstraints)); - log(cameraSettings['default']); - } else { - cameraSettings = getStorage("camera_"+session.currentCameraConstraints.deviceId); - } - } - } - } catch(e){errorlog(e);} - - - if (constraint=="width"){ - var constraits = {"width": value}; - - if (session.currentCameraConstraints && session.currentCameraConstraints.frameRate){ - constraits.frameRate = session.currentCameraConstraints.frameRate; - } - - if (screen && screen.orientation && screen.orientation.type){ - if (!screen.orientation.type.includes("portrait") && session.currentCameraConstraints){ - if (!ctrl && session.currentCameraConstraints.height){ - constraits.height = session.currentCameraConstraints.height; - } - } - } else if (!window.matchMedia("(orientation: portrait)").matches && session.currentCameraConstraints){ - if (!ctrl && session.currentCameraConstraints.height){ - constraits.height = session.currentCameraConstraints.height; - } - } - - } else if (constraint=="height"){ - var constraits = {"height": value}; - - if (session.currentCameraConstraints && session.currentCameraConstraints.frameRate){ - constraits.frameRate = session.currentCameraConstraints.frameRate; - } - - if (screen && screen.orientation && screen.orientation.type){ - if (!screen.orientation.type.includes("portrait") && session.currentCameraConstraints){ - if (!ctrl && session.currentCameraConstraints.width){ - constraits.width = session.currentCameraConstraints.width; - } - } - } else if (!window.matchMedia("(orientation: portrait)").matches && session.currentCameraConstraints){ - - if (!ctrl && session.currentCameraConstraints.width){ - constraits.width = session.currentCameraConstraints.width; - } - } - } else if (!ctrl && (constraint=="frameRate")){ - var constraits = {"frameRate": value}; - - if (screen && screen.orientation && screen.orientation.type){ - if (!screen.orientation.type.includes("portrait") && session.currentCameraConstraints){ - if (session.currentCameraConstraints.height && session.currentCameraConstraints.width){ - constraits.height = session.currentCameraConstraints.height; - constraits.width = session.currentCameraConstraints.width; - } - } - } else if (!window.matchMedia("(orientation: portrait)").matches && session.currentCameraConstraints){ - if (session.currentCameraConstraints.height && session.currentCameraConstraints.width){ - constraits.height = session.currentCameraConstraints.height; - constraits.width = session.currentCameraConstraints.width; - } - } - } else if ((constraint=="exposureMode") && (value=="manual")){ - - var constraits = {}; // try to force the current focus, to get the actual current value. - if (session.currentCameraConstraints && session.currentCameraConstraints.exposureTime){ // just requested a second a go - constraits.exposureTime = session.currentCameraConstraints.exposureTime; // needs the focus set for the manual to activate. - } - await track0.applyConstraints({ // apply what we have on record, to try to force it. - advanced: [constraits] - }) - session.currentCameraConstraints = track0.getSettings(); // now get the actual focus distance; solves a bug - - if (screen && screen.orientation && screen.orientation.type){ - if (!screen.orientation.type.includes("portrait")){ - if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ - session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; - } - } - } else if (!window.matchMedia("(orientation: portrait)").matches){ - if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ - session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; - } - } - - var constraits = {[constraint]: value}; // now we can set things to manual; if we don't set the focusDistance, it won't work otherwise. - if (session.currentCameraConstraints && session.currentCameraConstraints.exposureTime){ - constraits.exposureTime = session.currentCameraConstraints.exposureTime; // needs the focus set for the manual to activate. - } - } else if (constraint=="exposureTime"){ - var constraits = {[constraint]: value}; - constraits.exposureMode = "manual"; - - } else if ((constraint=="focusMode") && (value=="manual")){ - - var constraits = {}; // try to force the current focus, to get the actual current value. - if (session.currentCameraConstraints && session.currentCameraConstraints.focusDistance){ // just requested a second a go - constraits.focusDistance = session.currentCameraConstraints.focusDistance; // needs the focus set for the manual to activate. - } - await track0.applyConstraints({ // apply what we have on record, to try to force it. - advanced: [constraits] - }) - session.currentCameraConstraints = track0.getSettings(); // now get the actual focus distance; solves a bug - - if (screen && screen.orientation && screen.orientation.type){ - if (!screen.orientation.type.includes("portrait")){ - if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ - session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; - } - } - } else if (!window.matchMedia("(orientation: portrait)").matches){ // legacy - if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ - session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; - } - } - - var constraits = {[constraint]: value}; // now we can set things to manual; if we don't set the focusDistance, it won't work otherwise. - if (session.currentCameraConstraints && session.currentCameraConstraints.focusDistance){ - constraits.focusDistance = session.currentCameraConstraints.focusDistance; // needs the focus set for the manual to activate. - } - } else if (constraint=="focusDistance"){ - var constraits = {[constraint]: value}; - constraits.focusMode = "manual"; - - } else if ((constraint=="whiteBalanceMode") && (value=="manual")){ - - var constraits = {}; // try to force the current colorTemperature, to get the actual current value. - if (session.currentCameraConstraints && session.currentCameraConstraints.colorTemperature){ // just requested a second a go - constraits.colorTemperature = session.currentCameraConstraints.colorTemperature; // needs the colorTemperature set for the manual to activate. - } - await track0.applyConstraints({ // apply what we have on record, to try to force it. - advanced: [constraits] - }) - session.currentCameraConstraints = track0.getSettings(); // now get the actual colorTemperature; solves a bug - - if (screen && screen.orientation && screen.orientation.type){ - if (!screen.orientation.type.includes("portrait")){ - if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ - session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; - } - } - } else if (!window.matchMedia("(orientation: portrait)").matches){ // legacy - if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ - session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; - } - } - - var constraits = {[constraint]: value}; - if (session.cameraConstraints.colorTemperature && ("max" in session.cameraConstraints.colorTemperature) && ("min" in session.cameraConstraints.colorTemperature)){ - if (session.currentCameraConstraints && session.currentCameraConstraints.colorTemperature){ - constraits.colorTemperature = session.currentCameraConstraints.colorTemperature; - } else if ((5000>=session.cameraConstraints.colorTemperature.min) && (5000<=session.cameraConstraints.colorTemperature.max)){ - constraits.colorTemperature = 5000; // whiteBalanceMode won't work unless a colorTemperature is set. 5000 is a good default. - } else { - constraits.colorTemperature = session.cameraConstraints.colorTemperature.max; - } - } - } else if ((constraint=="whiteBalanceMode") && (value=="continuous")){ - var constraits = {[constraint]: value}; - - if (session.mobile && ChromiumVersion){ // trying to fix the issue that chrome mobile has. - constraits.colorTemperature = 5000; - } - - } else if (constraint=="colorTemperature"){ - var constraits = {[constraint]: value}; - constraits.whiteBalanceMode = "manual"; - - } else if (constraint=="aspectRatio"){ - var constraits = {[constraint]: value}; - if (session.currentCameraConstraints && session.currentCameraConstraints.frameRate){ - constraits.frameRate = session.currentCameraConstraints.frameRate; - } - } else { - var constraits = {[constraint]: value}; - } - - if (screen && screen.orientation && screen.orientation.type){ - if (screen.orientation.type.includes("portrait")){ - if (constraits.aspectRatio){ - constraits.aspectRatio = 1/constraits.aspectRatio; - } - } - } else if (window.matchMedia("(orientation: portrait)").matches){ // legacy - if (constraits.aspectRatio){ - constraits.aspectRatio = 1/constraits.aspectRatio; - } - } - - log("20788"); - log(constraits); - - await track0.applyConstraints({ - advanced: [constraits] - }).then(() => { - log("applied constraint"); - if (save){ - if (track0.getSettings){ // -- updateCameraConstraints - if (session.currentCameraConstraints.deviceId){ - session.currentCameraConstraints = track0.getSettings(); - - - if (screen && screen.orientation && screen.orientation.type){ - if (!screen.orientation.type.includes("portrait")){ - if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ - session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; - } - } - } else if (!window.matchMedia("(orientation: portrait)").matches){ // legacy - if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ - session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; - } - } - - cameraSettings['current'] = session.currentCameraConstraints; // this won't let failed settings be stored. - //cameraSettings['current'][constraint] = value; // setting value is a problem, as it will allow for failed settings to be stored. - setStorage("camera_"+session.currentCameraConstraints.deviceId, cameraSettings); - if (toggleSettingsState == true) { - listCameraSettings(); - } - } - } - - if (UUID){ - var data = {}; - data.UUID = UUID; - data.videoOptions = listVideoSettingsPrep(); - sendMediaDevices(data.UUID); - session.sendMessage(data, data.UUID); - } - if ((constraint == "width") || (constraint == "height") || (constraint == "aspectRatio")){ - session.setResolution(); // this will reset scaling for all viewers of this stream - } - } - - if (updateCameraConstraintsNext){ - setTimeout(function(){ - updateCameraConstraintsBusy = false; - updateCameraConstraints(updateCameraConstraintsNext[0], updateCameraConstraintsNext[1], updateCameraConstraintsNext[2], updateCameraConstraintsNext[3], updateCameraConstraintsNext[4]); - },30) - } else { - updateCameraConstraintsBusy = false; - } - }).catch(e => { - errorlog(e.message); - errorlog("coulnd't save defaults"); // this doesn't get triggered when a setting fails for some reason. - - window.focus(); - updateCameraConstraintsBusy = false; - updateCameraConstraintsNext = false; - return; - }); - return; -} - -function toggleAudioUser(ele){ - ele.classList.toggle('highlight'); - toggle(getById('popupSelector_constraints_audio'),false,false); - getById('popupSelector_constraints_loading').style.visibility='visible'; - getById('popupSelector_constraints_video').style.display = "none"; - getById('popupSelector_user_settings').style.display = "none"; -} -function toggleVideoUser(ele){ - ele.classList.toggle('highlight'); - toggle(getById('popupSelector_constraints_video'),false,false); - getById('popupSelector_constraints_loading').style.visibility='visible'; - getById('popupSelector_user_settings').style.display = "none"; - getById('popupSelector_constraints_audio').style.display = "none"; -} -function toggleUserUser(ele){ - ele.classList.toggle('highlight'); - toggle(getById('popupSelector_user_settings'),false,false); - getById('popupSelector_user_settings').style.visibility='visible'; - getById('popupSelector_constraints_video').style.display = "none"; - getById('popupSelector_constraints_audio').style.display = "none"; -} - -function setupWebcamSelection(miconly=false) { - log("setupWebcamSelection();"); - - checkBasicStreamsExist(); - - try { - return enumerateDevices().then(function(dInfo){return gotDevices(dInfo, miconly)}).then(function() { - - if (getById("webcamquality").elements && parseInt(getById("webcamquality").elements.namedItem("resolution").value) == 3) { // this is junk?? - if (session.maxframeRate===false){ - session.maxframeRate = 30; - session.maxframeRate_q2 = true; - } - } else if (session.maxframeRate_q2){ - session.maxframeRate = false; - session.maxframeRate_q2 = false; - } - - var audioSelect = getById('audioSource'); - var videoSelect = getById('videoSourceSelect'); - var outputSelect = getById('outputSource'); - - audioSelect.onchange = function() { - - if (document.getElementById("gowebcam")) { - document.getElementById("gowebcam").disabled = true; - document.getElementById("gowebcam").dataset.audioready = "false"; - //document.getElementById("gowebcam").style.backgroundColor = "#DDDDDD"; - document.getElementById("gowebcam").style.fontWeight = "normal"; - document.getElementById("gowebcam").innerHTML = "Waiting for mic to load"; - miniTranslate(document.getElementById("gowebcam"), "waiting-for-mic-to-load"); - } - activatedPreview = false; - grabAudio(); - }; - videoSelect.onchange = function() { - - if (document.getElementById("gowebcam")) { - document.getElementById("gowebcam").disabled = true; - document.getElementById("gowebcam").dataset.ready = "false"; - //document.getElementById("gowebcam").style.backgroundColor = "#DDDDDD"; - document.getElementById("gowebcam").style.fontWeight = "normal"; - document.getElementById("gowebcam").innerHTML = "Waiting for Camera to load"; - miniTranslate(document.getElementById("gowebcam"), "waiting-for-camera-to-load"); - } - warnlog("video source changed"); - - activatedPreview = false; - if (session.quality !== false) { - grabVideo(session.quality); - } else { - session.quality_wb = parseInt(getById("webcamquality").elements.namedItem("resolution").value); - grabVideo(session.quality_wb); - } - }; - - if (Firefox && !session.mobile){ - outputSelect.onclick = function() { - log("on click"); - if (outputSelect.options[outputSelect.selectedIndex].value === "others"){ - - navigator.mediaDevices.selectAudioOutput().then((device) => { - - if (device.kind == "audiooutput"){ - - session.sink = device.deviceId; - - try { - var matched = false; - outputSelect.childNodes.forEach(ele =>{ - if (ele.value === device.deviceId){ - matched = true; - ele.selected = true; - } - }) - if (!matched){ - var option = document.createElement('option'); - option.value = device.deviceId; - option.text = device.label; - outputSelect.appendChild(option); - option.selected = true; - } - - saveSettings(); // we're saving because there was an explicit action to change devices - } catch(e){errorlog(e);} - - if (!session.sink){return;} // Not sure this would ever happen, but whatever. - - resetupAudioOut(); // we'll probalby use session.sink, since outputSelect3 doesn't exist. - } - - }); - } - } - } - - - outputSelect.onchange = function() { - if (iOS || iPad) { - return; - } - if (Firefox && !session.mobile){ - if (outputSelect.options[outputSelect.selectedIndex].value === "others"){ - return; - } - } - - try{ - session.sink = outputSelect.options[outputSelect.selectedIndex].value; - saveSettings(); // we're saving because there was an explicit action to change devices - } catch(e){errorlog(e);} - - if (!session.sink){return;} // Not sure this would ever happen, but whatever. - - resetupAudioOut(); // we'll probalby use session.sink, since outputSelect3 doesn't exist. - } - - getById("webcamquality").onchange = function() { - - if (document.getElementById("gowebcam")) { - document.getElementById("gowebcam").disabled = true; - document.getElementById("gowebcam").dataset.ready = "false"; - // document.getElementById("gowebcam").style.backgroundColor = "#DDDDDD"; - document.getElementById("gowebcam").style.fontWeight = "normal"; - document.getElementById("gowebcam").innerHTML = "Waiting for Camera to load"; - miniTranslate(document.getElementById("gowebcam"), "waiting-for-camera-to-load"); - } - - if (parseInt(getById("webcamquality").elements.namedItem("resolution").value) == 2) { - if (session.maxframeRate===false){ - session.maxframeRate = 30; - session.maxframeRate_q2 = true; - } - } else if (session.maxframeRate_q2){ - session.maxframeRate = false; - session.maxframeRate_q2 = false; - } - - activatedPreview = false; - session.quality_wb = parseInt(getById("webcamquality").elements.namedItem("resolution").value); - grabVideo(session.quality_wb); - }; - - if (session.safemode){ - if (document.getElementById("gowebcam")){ - document.getElementById("gowebcam").disabled = false; - //document.getElementById("gowebcam").innerHTML = getTranslation("start"); - miniTranslate(document.getElementById("gowebcam"),"start"); - document.getElementById("gowebcam").dataset.audioready = "true"; - document.getElementById("gowebcam").dataset.ready = "true"; - document.getElementById("gowebcam").focus(); - setTimeout(function(){updateForceRotate();},1000); - - if (session.autostart) { - publishWebcam(); // no need to mirror as there is no video... - } - return; - } - } - - if (session.audioDevice!==0){ // change from Auto to Selected Audio Device - log("SETTING AUDIO DEVICE!!"); - activatedPreview = false; - grabAudio(); - } else if (document.getElementById("gowebcam")){ - document.getElementById("gowebcam").dataset.audioready = "true"; - } - - if ((session.videoDevice === 0) || miconly) { - if (session.autostart) { - publishWebcam(); // no need to mirror as there is no video... - return; - } else { - if (document.getElementById("gowebcam")) { - document.getElementById("gowebcam").dataset.ready = "true"; - if (document.getElementById("gowebcam").dataset.audioready == "true"){ - document.getElementById("gowebcam").disabled = false; - miniTranslate(document.getElementById("gowebcam"),"start"); - document.getElementById("gowebcam").focus(); - //document.getElementById("gowebcam").innerHTML = getTranslation("start"); - } - } - } - } else { - log("GRabbing video: " + session.quality); - activatedPreview = false; - if (session.quality !== false) { - grabVideo(session.quality); - } else { - session.quality_wb = parseInt(getById("webcamquality").elements.namedItem("resolution").value); - grabVideo(session.quality_wb); - } - } - - if (!(iOS || iPad || session.mobile)) { - try { - if (outputSelect.selectedIndex >= 0) { - session.sink = outputSelect.options[outputSelect.selectedIndex].value; - saveSettings(); - if (session.videoElement && !session.videoElement.paused){ - resetupAudioOut(); - } - } - } catch(e){errorlog(e);} - } - - }).catch(e => { - errorlog(e); - }); - } catch (e) { - errorlog(e); - } -} - -Promise.wait = function(ms) { - return new Promise(function(resolve) { - setTimeout(resolve, ms); - }); -}; - -Promise.prototype.timeout = function(ms) { - return Promise.race([ - this, Promise.wait(ms).then(function() { - if (iOS || iPad){ - var errormsg = new Error("Time Out\nDid you accept camera permissions in time? Please do so first.\n\nIf using an iPhone or iPad, try fully closing your browser and open it again; Safari sometimes jams up the camera."); - errormsg.name = "timedOut"; - errormsg.message = "Time Out\nDid you accept camera permissions in time? Please do so first.\n\nIf using an iPhone or iPad, try fully closing your browser and open it again; Safari sometimes jams up the camera." - throw errormsg; - } else if (session.mobile){ - var errormsg = new Error("Time Out\nDid you accept camera permissions in time? Please do so first.\n\nMake sure no other application is using the camera already and that you are using a compatible browser. If issues persist, maybe try the official native mobile app."); - errormsg.name = "timedOut"; - errormsg.message = "Time Out\nDid you accept camera permissions in time? Please do so first.\n\nMake sure no other application is using the camera already and that you are using a compatible browser. If issues persist, maybe try the official native mobile app." - throw errormsg; - } else { - var errormsg = new Error("Time Out\nDid you accept camera permissions in time? Please do so first.\n\nOtherwise, do you have NDI Tools installed? Maybe try uninstalling it.\n\nPlease also ensure your camera and audio device are correctly connected and not already in use. You may also need to refresh the page."); - errormsg.name = "timedOut"; - errormsg.message = "Time Out\nDid you accept camera permissions in time? Please do so first.\n\nOtherwise, do you have NDI Tools installed? Maybe try uninstalling it.\n\nPlease also ensure your camera and audio device are correctly connected and not already in use. You may also need to refresh the page." - throw errormsg; - } - }) - ]) -}; - - -async function shareWebsite(autostart=false, evt=false){ - - if (session.iframeSrc){ - - if (!session.cleanOutput){ - getById("websitesharebutton2").classList.remove('hidden'); - } - - if (evt && (evt.ctrlKey || evt.metaKey)){ - if (getById("websitesharebutton2").classList.contains("green")){ - var actionMsg = {}; - actionMsg.infocus = false; - - for (var UUID in session.pcs){ - if (session.pcs[UUID].allowIframe===true){ - session.sendMessage(actionMsg, UUID); - } - } - - getById("websitesharebutton2").classList.remove("green"); - getById("websitesharebutton2").ariaPressed = "false"; - getById("websitesharebutton2").title = "Hold CTRL (or CMD) and click to spotlight this shared website" - } else { - if (session.streamID){ - var actionMsg = {}; - actionMsg.infocus = session.streamID; - - for (var UUID in session.pcs){ - if (session.pcs[UUID].allowIframe===true){ - session.sendMessage(actionMsg, UUID); - } - } - - getById("websitesharebutton2").classList.add("green"); - getById("websitesharebutton2").ariaPressed = "true"; - getById("websitesharebutton2").title = "Video is currently spotlighted"; - } - } - return; - } - getById("websitesharebutton2").classList.remove("green"); - getById("websitesharebutton2").ariaPressed = "false"; - session.iframeSrc = false; - - - if (session.director){ - clearDirectorSettings(); - //setStorage("directorWebsiteShare", {"website":session.iframeSrc, "roomid":session.roomid}); - } else if (document.getElementById("container_iframe") || session.iframeEle){ - if (session.iframeEle){ - session.iframeEle.remove(); - session.iframeEle = false; - } - if (document.getElementById("container_iframe")){ - document.getElementById("container_iframe").remove(); - } - - updateMixer(); - } - getById("websitesharebutton2").classList.add("hidden"); - getById("websitesharebutton").classList.remove("hidden"); - - - - var data = {}; - data.iframeSrc = false; - for (var UUID in session.pcs){ - if (session.pcs[UUID].allowIframe===true){ - session.sendMessage(data, UUID); - } - } - getById("websitesharebutton2").title = "Hold CTRL (or CMD) and click to spotlight this shared website" - return - } - getById("websitesharebutton2").classList.remove("green"); - getById("websitesharebutton2").ariaPressed = "false"; - - if (autostart===false){ - window.focus(); - var iframeURL = await promptAlt(getTranslation("enter-website"), false, false, session.defaultIframeSrc); - } else { - var iframeURL = autostart; - } - if (!iframeURL){ - return; - } - if (iframeURL == session.iframeSrc){return;} - session.defaultIframeSrc = iframeURL; - - warnlog(iframeURL); - - session.iframeSrc = parseURL4Iframe(iframeURL); - - if (session.director && !autostart){ - setStorage("directorWebsiteShare", {"website":session.iframeSrc, "roomid":session.roomid}); - } else if (session.iframeEle){ - session.iframeEle.src = session.iframeSrc; - if (session.iframeSrc.startsWith("https://www.youtube.com/")){ // special handler. - setTimeout(function(iframe_id){ YoutubeListen(iframe_id);}, 1000, iframe.id); - } - } else { - var iframe = document.createElement("iframe"); - iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;display-capture;midi;"; - iframe.src = session.iframeSrc; - iframe.id = "iframe_source"; - iframe.loadedYoutubeListen = false; - session.iframeEle = iframe; - - var container = document.createElement("div"); - iframe.container = container; - container.id = "container_iframe"; - - if (session.iframeSrc.startsWith("https://www.youtube.com/")){ // special handler. - setTimeout(function(iframe_id){ YoutubeListen(iframe_id);}, 1000, iframe.id); - } - updateMixer(); - } - - getById("websitesharebutton2").classList.remove("hidden"); - getById("websitesharebutton").classList.add("hidden"); - - var data = {}; - data.iframeSrc = session.iframeSrc; - for (var UUID in session.pcs){ - if (session.pcs[UUID].allowIframe===true){ - session.sendMessage(data, UUID); - } - } -} - - -function screenshareTypeDecider(sstype=1){ - if (session.screenshareType){ - sstype = session.screenshareType; - } - if (sstype==1){ - toggleScreenShare(); - } else if (sstype==2){ - createIframePopup(); - } else if (sstype==3){ - createSecondStream(); - } -} - -function createScreenShareURL(transparent=true){ // iframe.src = - if (session.screenShareElement) { - var iFrameID = session.streamID.substring(0, 12) + "_" + session.generateStreamID(5); - } else if (session.screenshareid) { - var iFrameID = session.screenshareid; - } else { - var iFrameID = session.streamID.substring(0, 12) + "_" + session.generateStreamID(5); - } - - if (session.exclude) { - session.exclude.push(iFrameID); - } else { - session.exclude = []; - session.exclude.push(iFrameID); - } - - var extras = ""; - if (session.password){ - extras += "&password=" + session.password; // encodeURIComponent( - } - - if (session.privacy){ - extras += "&privacy"; - } - - if (session.meshcast){ - extras += "&meshcast"; - } - - if (session.token){ - extras+="&token="+session.token; - } - - if (session.remote){ - if (session.remote===true){ - extras += "&remote"; - } else { - extras += "&remote="+session.remote; - } - } - if (session.salt !== location.hostname){ // this is default. - extras += "&salt="+session.salt; - } - if (session.whipOutVideoBitrate){ - extras += "&wovb="+session.whipOutVideoBitrate; - } - if (session.whipOutScreenShareBitrate){ - extras += "&wossbitrate="+session.whipOutScreenShareBitrate; - } - - if (!session.notifyScreenShare){ - extras += "&smallshare"; - } - - if (session.screenshareContentHint){ - extras += "&sshint="+session.screenshareContentHint; - } else if (session.contentHint){ - extras += "&sshint="+session.contentHint; - } - - if (session.audioContentHint){ - extras += "&audiohint="+session.audioContentHint; - } - - if (session.whipOutScreenShareCodec){ - extras += "&whipoutcodec="+session.whipOutScreenShareCodec; - } else if (session.whipOutCodec){ - extras += "&whipoutcodec="+session.whipOutCodec; - } - - if (session.screensharequality!==false){ - extras += "&q="+session.screensharequality; // &quality works here, since only thing we are doing - } else if (session.quality!==false){ - extras += "&q="+session.quality; - } else if (session.quality_ss!==false){ - extras += "&q="+session.quality_ss; - } else { - extras += "&q=0"; - } - - if (session.screenShareLabel!==false){ - if (session.screenShareLabel){ - extras += "&label="+encodeURIComponent(session.screenShareLabel); - } else if (session.label){ - extras += "&label="+encodeURIComponent(session.label); - } - } - - if (session.screensharefps!==false){ - extras += "&maxframeRate="+parseInt(session.screensharefps*100)/100.0; - } - if (session.screenshareAEC!==false){ - extras += "&aec=1"; - } - if (session.screenshareDenoise!==false){ - extras += "&denoise=1"; - } - if (session.screenshareAutogain!==false){ - extras += "&autogain=1"; - } - if (session.screenshareStereo!==false){ - extras += "&stereo="+session.screenshareStereo; - } - if (session.forceAspectRatio && (session.forceScreenShareAspectRatio===null)){ - extras += "&ar="+session.forceAspectRatio; - } else if (session.forceScreenShareAspectRatio){ - extras += "&ar="+session.forceScreenShareAspectRatio; - } - if (session.muted){ - extras += "&muted"; - } - - if (session.recordLocal){ - extras += "&record="+session.recordLocal; - } - - if (session.autorecordlocal){ - extras += "&autorecordlocal"; - } - - if (transparent){ - extras += "&transparent&cleanish"; - } - // manual, since I don't want to use the auto-mixer. - return "?manual&audiodevice=1&screenshare&cb=0&nvb&nsb&autostart&view&room=" + session.roomid + "&push=" + iFrameID + extras; -} - -function createIframePopup() { - - if (session.screenShareElement) { - postMessageIframe(session.screenShareElement, {"close": true}); - session.screenShareElement.parentNode.removeChild(session.screenShareElement); - session.screenShareElement = false; - updateMixer(); - getById("screenshare2button").classList.remove("green"); - getById("screenshare2button").ariaPressed = "false"; - return; - } - - if ((session.queue && !session.transferred) || (session.screenShareState && !session.queue && session.transferred)){ // if (session.queue || session.transferred){ - //getById("screenshare2button").classList.add("hidden"); - //getById("screensharebutton").classList.remove("hidden"); - toggleScreenShare(); - return; - } // can't secondary-screen share if in a queue. - - var iframe = document.createElement("iframe"); - iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;display-capture;midi;"; - iframe.src = "./"+createScreenShareURL(); - - iframe.style.width = "100%"; - iframe.style.height = "100%"; - iframe.style.overflow = "hidden"; - iframe.id = "screensharesource"; - iframe.dataset.sid = "#screensharesource"; - iframe.style.zIndex = "0"; - - session.screenShareElement = iframe; - session.screenShareElement.dataset.doNotMove = true; - - - document.getElementById("main").appendChild(iframe); - - if (session.screenShareElementHidden){ - session.screenShareElement.style.display = "none"; - } - - updateMixer(); - - getById("screenshare2button").classList.add("green"); - getById("screenshare2button").ariaPressed = "true"; - - return; // ignore the rest. -} - -function previewWebcam(miconly=false) { - - if (session.taintedSession === null) { - log("STILL WAITING ON HASH TO VALIDATE"); - setTimeout(function(miconly) { - previewWebcam(miconly); - }, 1000, miconly); - return; - } else if (session.taintedSession === true) { - warnlog("HASH FAILED; PASSWORD NOT VALID"); - return; - } else { - log("NOT TAINTED"); - } - - if (activatedPreview == true) { - log("activeated preview return 1"); - return; - } - activatedPreview = true; - - - if (miconly){ // this just shares the preview section with the mic-only and video+mic modes - if (!getById("add_camera_inner").cloned){ - getById("add_camera_inner").cloned = true; - insertAfter(getById("add_camera_inner"),getById("add_microphone")); - document.getElementById('videoSourceSelect').innerHTML = ""; - } - } else if (getById("add_camera_inner").cloned){ - getById("add_camera_inner").cloned = false; - insertAfter(getById("add_camera_inner"),getById("add_camera")); - document.getElementById('videoSourceSelect').innerHTML = ""; - } - - - if (session.audioDevice === 0) { // OFF - var constraint = { - audio: false - }; - } else if ((session.echoCancellation !== false) && (session.autoGainControl !== false) && (session.noiseSuppression !== false)) { // AUTO - var constraint = { - audio: true - }; - } else { // Disable Echo Cancellation and stuff for the PREVIEW (DEFAULT CAM/MIC) - var constraint = { - audio: {} - }; - if (session.echoCancellation !== false) { // if not disabled, we assume it's on - constraint.audio.echoCancellation = true; - } else { - constraint.audio.echoCancellation = false; - if (!session.cleanoutput){ - getById("headphoneTip1").classList.remove("hidden"); - miniTranslate(getById("headphoneTipContext1"),"headphones-tip"); - //getById("headphoneTipContext1").innerHTML = getTranslation("headphones-tip"); - } - } - if (session.autoGainControl !== false) { - constraint.audio.autoGainControl = true; - } else { - constraint.audio.autoGainControl = false; - } - if (session.noiseSuppression !== false) { - constraint.audio.noiseSuppression = true; - } else { - constraint.audio.noiseSuppression = false; - } - } - - if ((session.videoDevice === 0) || miconly) { - constraint.video = false; - } else { - constraint.video = true; - } - - window.onorientationchange = function(){ - if (Firefox){ - updateForceRotate(true); - } - setTimeout(async function(){ - if (session.forceAspectRatio){ - await updateCameraConstraints("aspectRatio", session.forceAspectRatio); - } - if (session.effect && (session.effect === "7")){digitalZoom(true);} - updateForceRotate(); - }, 200); - }; - - if ((constraint.video === false) && (constraint.audio === false)){ - if (session.autostart) { - publishWebcam(false, miconly); // no need to mirror as there is no video... - return; - } else { - getById("getPermissions").style.display = "none"; - if (document.getElementById("gowebcam")) { - document.getElementById("gowebcam").dataset.ready = "true"; - document.getElementById("gowebcam").dataset.audioready = "true"; - document.getElementById("gowebcam").disabled = false; - //document.getElementById("gowebcam").innerHTML = getTranslation("start"); - miniTranslate(document.getElementById("gowebcam"),"start"); - document.getElementById("gowebcam").focus(); - } - } - return; - } - - enumerateDevices().then(function(devices) { - log("enumeratated"); - log(devices); - var vtrue = false; - var atrue = false; - devices.forEach(function(device) { - if (device.kind === 'audioinput') { - atrue = true; - } else if (device.kind === 'videoinput') { - vtrue = true; - } - }); - if (atrue === false) { - constraint.audio = false; - } - if (vtrue === false) { - constraint.video = false; - } - setTimeout(function(constraint, miconly) { - requestBasicPermissions(constraint, setupWebcamSelection, miconly); - }, 0, constraint, miconly); - }).catch((error) => { - log("enumeratated failed. Seeking permissions."); - setTimeout(function(constraint, miconly) { - requestBasicPermissions(constraint, setupWebcamSelection, miconly); - }, 0, constraint, miconly); - }); - -} - -async function requestBasicPermissions(constraint = {video: true, audio: true}, callback=setupWebcamSelection, miconly=false) { - if (session.taintedSession === null) { - log("STILL WAITING ON HASH TO VALIDATE"); - setTimeout(function(constraint, callback, miconly) { - requestBasicPermissions(constraint, callback, miconly); - }, 1000, constraint, callback, miconly); - return null; - } else if (session.taintedSession === true) { - warnlog("HASH FAILED; PASSWORD NOT VALID"); - return false; - } else { - log("NOT TAINTED 1"); - } - setTimeout(function() { - getById("getPermissions").style.display = "none"; - getById("gowebcam").style.display = ""; - }, 0); - log("REQUESTING BASIC PERMISSIONS"); - - try { - var timerBasicCheck = null; - if (!(session.cleanOutput)) { - log("Setting Timer for getUserMedia"); - timerBasicCheck = setTimeout(function() { - if (!(session.cleanOutput)) { - if (session.mobile){ - warnUser("Notice: Camera timed out\n\nDid you accept the camera permissions?\n\nThis error may also appear if you are in a phone call or another app is already using the camera or microphone."); - } else { - warnUser("Camera Access Request Timed Out\nDid you accept camera permissions? Please do so first.\n\nOtherwise, do you have NDI Tools installed? Maybe try uninstalling NDI tools.\n\nPlease also ensure that your camera and audio devices are correctly connected and not already in use. You may also need to refresh the page."); - } - } - }, 10000); - } - - if (session.audioInputChannels) { - if (constraint.audio === true) { - constraint.audio = {}; - constraint.audio.channelCount = session.audioInputChannels; - } else if (constraint.audio) { - constraint.audio.channelCount = session.audioInputChannels; - } - } - - - if (session.micSampleRate){ - if (constraint.audio === true) { - constraint.audio = {}; - constraint.audio.sampleRate = parseInt(session.micSampleRate); - } else if (constraint.audio) { - constraint.audio.sampleRate = parseInt(session.micSampleRate); - } - } - if (session.micSampleSize){ - if (constraint.audio === true) { - constraint.audio = {}; - constraint.audio.sampleSize = parseInt(session.micSampleSize); - } else if (constraint.audio) { - constraint.audio.sampleSize = parseInt(session.micSampleSize); - } - } - - if (session.safemode){ - if (constraint.video){ - constraint.video = true; - } - if (constraint.audio){ - constraint.audio = true; - } - } - getUserMediaRequestID +=1 ; - var gumID = getUserMediaRequestID; - log("CONSTRAINT"); - log(constraint); - var timeoutStart = 0; - if (Firefox){ - timeoutStart = 500; - } - setTimeout(function(gumID,constraint, timerBasicCheck, callback, miconly){ - log(gumID); - navigator.mediaDevices.getUserMedia(constraint).then(function(stream) { // Apple needs thi to happen before I can access EnumerateDevices. - - log("got first stream"); - clearTimeout(timerBasicCheck); - if (getUserMediaRequestID !== gumID) { - warnlog("GET USER MEDIA CALL HAS EXPIRED 3"); - stream.getTracks().forEach(function(track) { - stream.removeTrack(track); - track.stop(); - log("stopping old track"); - }); - return; - } - closeModal(); - - log(stream.getTracks()); - - session.streamSrc=stream; - checkBasicStreamsExist(); - updateRenderOutpipe(); - - if (callback){ - callback(miconly); - } - }).catch(function(err) { - clearTimeout(timerBasicCheck); - warnlog("some error with GetUSERMEDIA"); - console.warn(err); /* handle the error */ - if (err.name == "NotFoundError" || err.name == "DevicesNotFoundError") { - //required track is missing - } else if (err.name == "NotReadableError" || err.name == "TrackStartError") { - //webcam or mic are already in use - } else if (err.name == "OverconstrainedError" || err.name == "ConstraintNotSatisfiedError") { - //constraints can not be satisfied by avb. devices - } else if (err.name == "NotAllowedError" || err.name == "PermissionDeniedError") { - //permission denied in browser - if (!(session.cleanOutput)) { - setTimeout(function() { - if (window.obsstudio){ - warnUser("Permissions denied.\n\nTo access the camera or microphone from within OBS, please refer to:\ndocs.vdo.ninja/guides/share-webcam-from-inside-obs.", false, false); - } else if (ChromiumVersion && !session.mobile){ - warnUser("

Camera/mic permissions denied

\nPlease ensure you have allowed the mic/camera permissions in your browser, such as like:\n\n\n\nFor further help on how to resolve this issue, please refer to:\n\nhttps://docs.vdo.ninja/common-errors-and-known-issues/enable-camera-microphone-permissions.", false, false); - } else { - warnUser("Permission access to the camera or microphone was denied.\n\nPlease ensure you have allowed the mic/camera permissions in your browser.\n\nFor guides on how to resolve this issue, please refer to:\n\nhttps://docs.vdo.ninja/common-errors-and-known-issues/enable-camera-microphone-permissions.", false, false); - } - }, 1); - } - return; - } else if (err.name == "TypeError" || err.name == "TypeError") { - //empty constraints object - } else { - //permission denied in browser - if (!(session.cleanOutput)) { - setTimeout(function(err) { - warnUser(err); - }, 1,err); - } - } - warnlog("trying to list webcam again"); - - if (callback){ - callback(miconly); - } - - }); - }, timeoutStart, gumID, constraint, timerBasicCheck, callback, miconly); - } catch (e) { - console.warn(e); - if (!(session.cleanOutput)) { - if (window.isSecureContext) { - warnUser("An error has occured when trying to access the webcam or microphone. The reason is not known."); - } else if (iOS || iPad) { - warnUser("iOS version 13.4 and up is generally recommended; older than iOS 11 is not supported."); - } else { - warnUser("Error acessing camera or microphone.\n\nThe website may be loaded in an insecure context.\n\nPlease see: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia"); - } - } - } - return null -} - - -function copyFunction(copyText, evt = false) { - if (evt){ - if ("buttons" in evt) { - if (evt.buttons !== 0){return;} - } else if ("which" in evt){ - if (evt.which !== 0){return;} - } - popupMessage(evt); - evt.preventDefault(); - evt.stopPropagation(); - } - - try { - copyText.select(); - copyText.setSelectionRange(0, 99999); - document.execCommand("copy"); - } catch (e) { - var dummy = document.createElement('input'); - document.body.appendChild(dummy); - dummy.value = copyText; - dummy.select(); - document.execCommand('copy'); - document.body.removeChild(dummy); - } - return false; -} - -function generateQRPage() { - var pass = sanitizePassword(getById("invite_password").value); - if (pass.length) { - return generateHash(pass + session.salt, 4).then(function(hash) { - generateQRPageCallback(hash); - }).catch(errorlog); - } else { - generateQRPageCallback(""); - } -} - -async function updateLinkWelcome(arg, input) { - if (input.checked){ - var response = await promptAlt("Enter the message you'd like the guests to see"); - response = encodeURIComponent(response); - var param = input.dataset.param.split("=")[0]; - input.dataset.param = param + "=" + response; - } - updateLink(arg, input); -} - - -function updateLinkWebP(arg, input) { - if (input.checked){ - if (!((getById("director_block_" + arg).dataset.raw.includes("&broadcast")) || (getById("director_block_" + arg).dataset.raw.includes("?broadcast")))){ - getById("broadcastSlider").checked=true; - updateLink(arg, getById("broadcastSlider")); - } - } - updateLink(arg, input); -} - -var soloLinkAppended = ""; -function updateLink(arg, input, solo=false) { - log("updateLink"); - log(input.dataset.param); - if (input.checked) { - - getById("director_block_" + arg).dataset.raw += input.dataset.param; - - if (solo){ - soloLinkAppended += input.dataset.param; - } - - var string = getById("director_block_" + arg).dataset.raw; - - if ((arg==1) && (getById("obfuscate_director_" + arg).checked)) { - string = obfuscateURL(string); - } - - getById("director_block_" + arg).href = string; - getById("director_block_" + arg).innerText = string; - } else { - var string = getById("director_block_" + arg).dataset.raw + "&"; - string = string.replace(input.dataset.param + "&", "&"); - string = string.substring(0, string.length - 1); - getById("director_block_" + arg).dataset.raw = string; - - if (solo){ - soloLinkAppended += "&"; - soloLinkAppended = soloLinkAppended.replace(input.dataset.param + "&", "&"); - soloLinkAppended = soloLinkAppended.substring(0, soloLinkAppended.length - 1); - } - - if ((arg==1) && (getById("obfuscate_director_" + arg).checked)) { - string = obfuscateURL(string); - } - - // document.querySelector("soloLink") - // soloLink - - getById("director_block_" + arg).href = string; - getById("director_block_" + arg).innerText = string; - } - if (solo){ - document.querySelectorAll("a.soloLink").forEach(ele=>{ - try { - var href = ele.getAttribute("value") + soloLinkAppended; - ele.href = href; - ele.innerHTML = href; - } catch(e){ - errorlog(e); - } - }); - } - - saveDirectorSettings(); -} - -function changeURL(changeURL){ - window.focus(); - if (session.consent){ - hangup(); - window.location.href = changeURL; - } else { - confirmAlt(getTranslation("director-redirect-1")+changeURL+getTranslation("director-redirect-2")).then(res=>{ - if (res){ - hangup(); - window.location.href = changeURL; - }; - }); - } -} - -function updateLinkInverse(arg, input) { - log("updateLinkInverse"); - log(input.dataset.param); - if (!(input.checked)) { - - getById("director_block_" + arg).dataset.raw += input.dataset.param; - - var string = getById("director_block_" + arg).dataset.raw; - - if ((arg==1) && (getById("obfuscate_director_" + arg).checked)) { - string = obfuscateURL(string); - } - - - getById("director_block_" + arg).href = string; - getById("director_block_" + arg).innerText = string; - } else { - var string = getById("director_block_" + arg).dataset.raw + "&"; - string = string.replace(input.dataset.param + "&", "&"); - string = string.substring(0, string.length - 1); - getById("director_block_" + arg).dataset.raw = string; - - if ((arg==1) && (getById("obfuscate_director_" + arg).checked)) { - string = obfuscateURL(string); - } - - getById("director_block_" + arg).href = string; - getById("director_block_" + arg).innerText = string; - } -} - -function updateLinkScene(arg, input) { - log("updateLinkScene"); - var string = getById("director_block_" + arg).dataset.raw; - - if (input.checked) { - string = changeParam(string, "scene", "0"); - } else { - string = changeParam(string, "scene", "1"); - } - getById("director_block_" + arg).dataset.raw = string; - - if ((arg==1) && (getById("obfuscate_director_" + arg).checked)) { - string = obfuscateURL(string); - } - - getById("director_block_" + arg).href = string; - getById("director_block_" + arg).innerText = string; -} - -function fullscreenPageToggle(state=null){ - try { - if (!document.fullscreenElement) { // not currently full screen - if (state!==false){ // if state is false, we are already not full screen - if (document.documentElement.requestFullscreen){ - document.documentElement.requestFullscreen(); - } else if (document.documentElement.webkitRequestFullscreen){ - document.documentElement.webkitRequestFullscreen(); - } - } - } else if (document.exitFullscreen) { - if (!state){ // if toggle mode or state=false - document.exitFullscreen(); - } - } else if (document.webkitExitFullscreen) { - if (!state){ // if toggle mode or state=false - document.webkitExitFullscreen(); - } - } - //updateMixer(); // we will do this on the event for this instead - } catch(e){errorlog(e);} -} - -session.pipWindow = false; -async function PictureInPicturePageToggle(state=null){ - try { - if (typeof documentPictureInPicture === "undefined"){return;} - if (session.pipWindow){ - getById("testtone").parentNode.insertBefore(session.pipWindow.document.getElementById("gridlayout"), getById("testtone")); - session.pipWindow.close(); - session.pipWindow = null; - updateMixer(); - getById("PictureInPicturePage").classList.remove("green"); - } else{ - session.pipWindow = await documentPictureInPicture.requestWindow({width:564,height:346}); // 360 + 30px for the window header - - session.pipWindow.addEventListener("pagehide", (event) => { - if (session.pipWindow){ - getById("testtone").parentNode.insertBefore(session.pipWindow.document.getElementById("gridlayout"), getById("testtone")); - session.pipWindow.close(); - session.pipWindow = null; - updateMixer(); - getById("PictureInPicturePage").classList.remove("green"); - } - }); - - session.pipWindow.document.body.className = "main"; - session.pipWindow.document.head.innerHTML = 'Pop-out Window' - session.pipWindow.document.body.style = document.body.style; - session.pipWindow.document.title = "Pop-out Window"; - session.pipWindow.document.body.append(getById("gridlayout")); - session.pipWindow.onresize = updateMixer; - updateMixer(); // just in case onresize doesn't trigger - getById("PictureInPicturePage").classList.add("green"); - } - } catch(e){errorlog(e);} -} - -function resetGen() { - getById("gencontent").style.display = "block"; - getById("gencontent2").style.display = "none"; - getById("gencontent2").className = ""; //container-inner - getById("gencontent").className = "container-inner"; // - getById("gencontent2").innerHTML = ""; - getById("videoname4").focus(); -} - -function generateQRPageCallback(hash) { - try { - var title = getById("videoname4").value; - if (title.length) { - title = title.replace(/[\W]+/g, "_").replace(/_+/g, '_'); // but not what others might get. TODO: allow for non-alphanumeric characters; santitize, then URL encode instead, - title = "&label=" + title; - } - var sid = session.generateStreamID(); - - var viewstr = ""; - var sendstr = ""; - - if (getById("invite_bitrate").checked) { - viewstr += "&bitrate=20000"; - } - if (getById("invite_vp9").checked) { - viewstr += "&codec=vp9"; - } - if (getById("invite_stereo").checked) { - viewstr += "&stereo"; - sendstr += "&stereo"; - } - if (getById("invite_automic").checked) { - sendstr += "&audiodevice=1"; - } - if (getById("invite_automic").checked) { - sendstr += "&audiodevice=1"; - } - if (getById("invite_effects").checked) { - sendstr += "&effects"; - } - - if (getById("invite_remotecontrol").checked) { // - var remote_gen_id = session.generateStreamID(); - sendstr += "&remote=" + remote_gen_id; // security - viewstr += "&remote=" + remote_gen_id; - } - - if (getById("invite_joinroom").value.trim().length) { - sendstr += "&ssid&room=" + getById("invite_joinroom").value.trim(); - viewstr += "&solo&room=" + getById("invite_joinroom").value.trim(); - } - - if (getById("invite_password").value.trim().length) { - sendstr += "&hash=" + hash; - viewstr += "&password=" + sanitizePassword(getById("invite_password").value.trim()); - } - - if (session.token){ - sendstr += "&token=" + session.token; - viewstr += "&token=" + session.token; - } - - if (getById("invite_group_chat_type").value) { // 0 is default - if (getById("invite_group_chat_type").value == 1) { // no video - sendstr += "&novideo"; - } else if (getById("invite_group_chat_type").value == 2) { // no view or audio - sendstr += "&view"; - } - } - - if (getById("invite_quality").value) { - if (getById("invite_quality").value == 0) { - sendstr += "&quality=0"; - } else if (getById("invite_quality").value == 1) { - sendstr += "&quality=1"; - } else if (getById("invite_quality").value == 2) { - sendstr += "&quality=2"; - } - } - - var wss = ""; - - if (session.wssSetViaUrl){ - if (session.customWSS && (session.customWSS!==true)){ - wss = "&pie="+session.customWSS; - } else { - wss = "&wss="+session.wss; - } - } - - var hoststr = ""; - if (getById("invite_hostlink").checked) { - hoststr = 'https://' + location.host + location.pathname + '?push=' + sid + "_hostlink" + "&view="+ sid + sendstr + "&bitrate=500" + title + wss; - sendstr = 'https://' + location.host + location.pathname + '?push=' + sid + "&view="+sid + "_hostlink" + sendstr + "&bitrate=1200" + title + wss; - } else { - sendstr = 'https://' + location.host + location.pathname + '?push=' + sid + sendstr + title + wss; - } - - if (getById("invite_obfuscate").checked) { - sendstr = obfuscateURL(sendstr); - } - - viewstr = 'https://' + location.host + location.pathname + '?view=' + sid + viewstr + title + wss; - getById("gencontent").style.display = "none"; - getById("gencontent").className = ""; // - getById("gencontent2").style.display = "block"; - getById("gencontent2").className = "container-inner"; - - getById("gencontent2").innerHTML = '
\ -

Guest Invite Link:

\ - ' + sendstr + '

\ -

and don\'t forget the

OBS Browser Source Link:

' + viewstr + ' \ - '; - - if (hoststr){ - getById("gencontent2").innerHTML += '

Host Chat Link:

' + hoststr + ' '; - } - - getById("gencontent2").innerHTML += '

\ - \ -
  • This invite link and OBS ingestion link are reusable.
  • \ -
  • Only one person may use a specific invite at a time.
  • \ -
  • The stream ID can be changed manually to something else; keep it unique and alphanumeric.
  • \ -
  • Nothing is stored server-side; links do not expire, nor is there anything to delete.
  • \ -


    \ - '; - - var qrcode = new QRCode(getById("qrcode"), { - width: 300 - , height: 300 - , colorDark: "#000000" - , colorLight: "#FFFFFF" - , useSVG: false - }); - qrcode.makeCode(sendstr); - getById("qrcode").title = ""; - setTimeout(function() { - getById("qrcode").title = ""; - if (getById("qrcode").getElementsByTagName('img').length) { - getById("qrcode").getElementsByTagName('img')[0].style.cursor = "none"; - getById("qrcode").getElementsByTagName('img')[0].style.margin = "0 auto"; - } - }, 100); // i really hate the title overlay that the qrcode function makes - - } catch (e) { - errorlog(e); - } -} - - -function initSceneList(UUID){ - Object.keys(session.sceneList).forEach((scene, index) => { - if (getById("container_" + UUID).querySelectorAll('[data-scene="'+scene+'"]').length){return;} // already exists. - var newScene = document.createElement("div"); - newScene.innerHTML = ''; - newScene.classList.add("customScene"); - - var added = false; - getById("container_" + UUID).querySelectorAll('.customScene>[data-scene]').forEach(ele=>{ - log(ele); - if (!added && ele.dataset.scene>scene+""){ - ele.parentNode.parentNode.insertBefore(newScene, ele.parentNode); - added = true; - } - }); - if (!added){ - getById("container_" + UUID).appendChild(newScene); - } - }); -} - -function updateSceneList(scene){ // custom scenes only. - if (!session.director){return;} - if (scene in session.sceneList){return;} - - if ((parseInt(scene)+"")===scene){ - if ((parseInt(scene)>=0) && (parseInt(scene)<=8)){ - return; - } - } - - session.sceneList[scene] = true; - for (var UUID in session.rpcs){ - var newScene = document.createElement("div"); - newScene.innerHTML = ''; - newScene.classList.add("customScene"); - var added = false; - getById("container_" + UUID).querySelectorAll('.customScene>[data-scene]').forEach(ele=>{ - log(ele); - if (!added && ele.dataset.scene>scene+""){ - ele.parentNode.parentNode.insertBefore(newScene, ele.parentNode); - added = true; - } - }); - if (!added){ - getById("container_" + UUID).appendChild(newScene); - } - } - - - if (session.showDirector){ - if (document.getElementById("container_director")){ - var newScene = document.createElement("div"); - newScene.innerHTML = ''; - newScene.classList.add("customScene"); - //getById("container_director").appendChild(newScene); - - var added = false; - getById("container_director").querySelectorAll('.customScene>[data-scene]').forEach(ele=>{ - if (!added && ele.dataset.scene>scene+""){ - ele.parentNode.parentNode.insertBefore(newScene, ele.parentNode); - added = true; - } - }); - if (!added){ - getById("container_director").appendChild(newScene); - } - } - } -} - -var vis = (function() { - var stateKey, eventKey, keys = { - hidden: "visibilitychange" - , webkitHidden: "webkitvisibilitychange" - , mozHidden: "mozvisibilitychange" - , msHidden: "msvisibilitychange" - }; - for (stateKey in keys) { - if (stateKey in document) { - eventKey = keys[stateKey]; - break; - } - } - return function(c) { - if (c) { - document.addEventListener(eventKey, c); - //document.addEventListener("blur", c); - //document.addEventListener("focus", c); - } - return !document[stateKey]; - }; -})(); - -function unPauseVideo(videoEle, update=true){ - try { - if (!videoEle){return;} - else if (!(videoEle.dataset.UUID in session.rpcs)){return;} - else if (!("prePausedBandwidth" in session.rpcs[videoEle.dataset.UUID])){return;} // not paused; useless to have, but might as well - session.rpcs[videoEle.dataset.UUID].manualBandwidth = false; - //session.rpcs[videoEle.dataset.UUID].manualAudioBandwidth = false; - - if (session.rpcs[videoEle.dataset.UUID].videoElement){ - session.rpcs[videoEle.dataset.UUID].videoElement.play(); - } - - delete(session.rpcs[videoEle.dataset.UUID].prePausedBandwidth); - session.requestRateLimit(false, videoEle.dataset.UUID, false); // passing a bitrate of false forces the saved existing bitrate to be requested. - videoEle.classList.remove("paused"); - videoEle.classList.remove("partialFadeout"); - if (update){ - updateMixer(); - } - }catch(e){errorlog(e);} -} - -function pauseVideo(videoEle, update=true){ - if (!videoEle){return;} - else if (!(videoEle.dataset.UUID in session.rpcs)){return;} - session.rpcs[videoEle.dataset.UUID].prePausedBandwidth = session.rpcs[videoEle.dataset.UUID].manualBandwidth; // useless, but whatever - session.rpcs[videoEle.dataset.UUID].manualBandwidth = 0; - - if (session.rpcs[videoEle.dataset.UUID].videoElement){ - session.rpcs[videoEle.dataset.UUID].videoElement.pause(); - } - //session.rpcs[videoEle.dataset.UUID].manualAudioBandwidth = 0; - session.requestRateLimit(false, videoEle.dataset.UUID, true); // passing a bitrate of false forces the saved existing bitrate to be requested. - videoEle.classList.add("paused"); - videoEle.classList.add("partialFadeout"); - if (update){ - updateMixer(); - } -} - -(function rightclickmenuthing() { // right click menu - "use strict"; - - function clickInsideElement(e, value="menu") { - - var el = e.srcElement || e.target; - if (el.dataset && (value in el.dataset)) { - return el; - } else { - while (el = el.parentNode) { - if (el.dataset && (value in el.dataset)) { - return el; - } - } - } - return false; - } - - function getPosition(event2) { - var posx = 0; - var posy = 0; - - if (!event2){var event = window.event;} - - if (event2.pageX || event2.pageY){ - posx = event2.pageX; - posy = event2.pageY; - } else if (event2.clientX || event2.clientY) { - posx = event2.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; - posy = event2.clientY + document.body.scrollTop + document.documentElement.scrollTop; - } - - return {x: posx, y: posy}; - } - - var taskItemInContext; - var clickCoordsX; - var clickCoordsY; - var menu; - var menuState = 0; - var lastMenu= false; - var menuWidth; - var menuHeight; - var windowWidth; - var windowHeight; - - - - function contextListener() { - document.addEventListener("contextmenu", function(e) { - - if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1){ - if (e && (!e.ctrlKey && !e.metaKey)){return;} - } else if (e && (e.ctrlKey || e.metaKey)){return;} // allow for development ease - - taskItemInContext = clickInsideElement(e, "menu"); - - if (taskItemInContext) { - e.preventDefault(); - e.stopPropagation(); - if (taskItemInContext.dataset && taskItemInContext.dataset.menu){ - toggleMenuOn(taskItemInContext.dataset.menu, taskItemInContext); - } else { - toggleMenuOn(); - } - positionMenu(e); - return false; - } else { - taskItemInContext = null; - toggleMenuOff(); - } - }); - } - - function menuClickListener(e) { - var clickeElIsLink = clickInsideElement(e, "action"); - if (clickeElIsLink) { - e.preventDefault(); - e.stopPropagation(); - menuItemListener(clickeElIsLink, false, e); - return false; - } else { - var button = e.which || e.button; - if (button === 1) { - toggleMenuOff(); - } - } - } - - function handleInputElement(e){ // for the input range slider version - var clickeElIsLink = clickInsideElement(e, "action"); - if (clickeElIsLink) { - e.preventDefault(); - e.stopPropagation(); - menuItemListener(clickeElIsLink, e.srcElement, e); - return false; - } else { - var button = e.which || e.button; - if (button === 1) { - toggleMenuOff(); - } - } - } - - function toggleMenuOn(menutype=false, ele=false) { - if (lastMenu && (lastMenu !== menutype)){ - try { - menuState = 0; - getById(lastMenu).classList.remove("context-menu--active"); - - document.removeEventListener("click", menuClickListener); - menu.querySelectorAll("input").forEach(ele=>{ - ele.removeEventListener("input", handleInputElement); - }); - } catch(e){} - } - menu = getById(menutype || "context-menu"); - menuItemSyncState(menu); - if (menuState !== 1) { - menuState = 1; - menu.classList.add("context-menu--active"); - document.addEventListener("click", menuClickListener); - menu.querySelectorAll("input").forEach(ele=>{ - ele.addEventListener("input", handleInputElement); - }); - } - - if (ele && ele.classOptions){ - menu.classList.add(ele.classOptions); - } - lastMenu = menutype || "context-menu"; - - } - - function toggleMenuOff() { - if (menuState !== 0) { - menuState = 0; - menu.classList.remove("context-menu--active"); - - document.removeEventListener("click", menuClickListener); - menu.querySelectorAll("input").forEach(ele=>{ - ele.removeEventListener("input", handleInputElement); - }); - } - lastMenu = false; - } - - function positionMenu(e) { - var clickCoords = getPosition(e); - clickCoordsX = clickCoords.x; - clickCoordsY = clickCoords.y; - - menuWidth = menu.offsetWidth + 4; - menuHeight = menu.offsetHeight + 4; - - windowWidth = window.innerWidth; - windowHeight = window.innerHeight; - - if ((windowWidth - clickCoordsX) < menuWidth) { - menu.style.left = windowWidth - menuWidth + "px"; - } else { - menu.style.left = clickCoordsX + "px"; - } - - if ((windowHeight - clickCoordsY) < menuHeight) { - menu.style.top = windowHeight - menuHeight + "px"; - } else { - menu.style.top = clickCoordsY + "px"; - } - } - - async function menuItemListener(link, inputElement=false, e=false) { - if (link.getAttribute("data-action") === "Open") { - window.open(taskItemInContext.href); - } else if (link.getAttribute("data-action") === "Copy") { - copyFunction(taskItemInContext.href); - } else if (link.getAttribute("data-action") === "Mirror") { - if ((taskItemInContext.id == "videosource") || (taskItemInContext.id == "previewWebcam")){ - session.mirrored = !session.mirrored; - applyMirror(false); - log("session.mirrored"); - } else { - if ("mirror" in taskItemInContext){ - taskItemInContext.mirror = !taskItemInContext.mirror; - applyMirrorGuest(taskItemInContext.mirror, taskItemInContext); - } else { - taskItemInContext.mirror = true; - applyMirrorGuest(taskItemInContext.mirror, taskItemInContext); - } - } - } else if (link.getAttribute("data-action") === "FullWindow") { - if ((taskItemInContext.id == "videosource") || (taskItemInContext.id == "previewWebcam")){ - session.infocus=true; - } else { - session.infocus = taskItemInContext.dataset.UUID; - } - updateMixer(); - } else if (link.getAttribute("data-action") === "ShrinkWindow") { - session.infocus=false; - updateMixer(); - } else if (link.getAttribute("data-action") === "Pause") { - pauseVideo(taskItemInContext); - } else if (link.getAttribute("data-action") === "UnPause") { - unPauseVideo(taskItemInContext); - } else if (link.getAttribute("data-action") === "PiP") { - togglePictureInPicture(taskItemInContext); - } else if (link.getAttribute("data-action") === "PiP2") { - PictureInPicturePageToggle(); - } else if (link.getAttribute("data-action") === "Record") { - if (taskItemInContext.stopWriter || taskItemInContext.recording){ - } else if (taskItemInContext.startWriter){ - taskItemInContext.startWriter(); - } else { - var videoKbps = 4000; - if (session.recordLocal !== false) { - videoKbps = session.recordLocal; - } - recordLocalVideo(null, videoKbps, taskItemInContext) - } - } else if (link.getAttribute("data-action") === "StopRecording") { - if (taskItemInContext.stopWriter){ - taskItemInContext.stopWriter(); - } else if (taskItemInContext.recording){ - recordLocalVideo("stop", null, taskItemInContext); - } - } else if (link.getAttribute("data-action") === "CopyFrameAsImage") { - copyVideoFrameToClipboard(taskItemInContext, e); - } else if (link.getAttribute("data-action") === "SaveFrameToDisk") { - saveVideoFrameToClipboard(taskItemInContext, e); - } else if (link.getAttribute("data-action") === "ChangeBuffer") { - toggleBufferSettings(taskItemInContext.dataset.UUID); - } else if (link.getAttribute("data-action") === "Cast") { - //copyFunction(taskItemInContext.href); - } else if (link.getAttribute("data-action") === "Controls") { - taskItemInContext.controls = true; - } else if (link.getAttribute("data-action") === "HideControls") { - taskItemInContext.controls = false; - } else if (link.getAttribute("data-action") === "Edit") { - //copyFunction(taskItemInContext.href); - var response = await promptAlt("Please note, manual edits to the URL may conflict with the toggles", false, false, taskItemInContext.href); - if (response){ - taskItemInContext.href = response; - taskItemInContext.dataset.raw = response; - taskItemInContext.innerHTML = response; - - } - } else if (link.getAttribute("data-action") === "QRCode") { - warnUser("Loading QR Code"); - loadQR(function tt(url){ - getById("alertModalMessage").innerHTML = ""; - var qrcode = new QRCode(getById("alertModalMessage"), { - width: 300 - , height: 300 - , colorDark: "#000000" - , colorLight: "#FFFFFF" - , useSVG: false - }); - qrcode.makeCode(url); - getById("alertModalMessage").title = ""; - setTimeout(function() { - getById("alertModalMessage").title = ""; - if (getById("alertModalMessage").getElementsByTagName('img').length) { - getById("alertModalMessage").getElementsByTagName('img')[0].style.cursor = "none"; - } - }, 100); - }, taskItemInContext.href); - } else if (link.getAttribute("data-action") === "ShowStats"){ - if ((taskItemInContext.id == "videosource") || (taskItemInContext.id == "previewWebcam")){ - var [menu, innerMenu] = statsMenuCreator(); - menu.interval = setInterval(printMyStats,session.statsInterval, innerMenu); - printMyStats(innerMenu); - } else if (taskItemInContext.dataset.UUID && (taskItemInContext.dataset.UUID in session.rpcs)){ - var [menu, innerMenu] = statsMenuCreator(); - printViewStats(innerMenu, taskItemInContext.dataset.UUID ); - menu.interval = setInterval(printViewStats, session.statsInterval, innerMenu, taskItemInContext.dataset.UUID); - } - } else if (link.getAttribute("data-action") === "OutputAudio"){ - enumerateDevices().then(function(deviceInfo){ - var ele = getById(taskItemInContext.id); - - var deviceListElement = gotDevices3(deviceInfo, ele); - if (deviceListElement){ - warnUser("Select the audio playback destination for this media:\n\n"); - getById("alertModalMessage").appendChild(deviceListElement); - } else { - warnUser("No output devices available"); - } - }); - - // - } else if (link.getAttribute("data-action") === "RemoteHangup") { - if (session.rpcs[taskItemInContext.dataset.UUID] && session.rpcs[taskItemInContext.dataset.UUID].stats.info && ("remote" in session.rpcs[taskItemInContext.dataset.UUID].stats.info) && session.rpcs[taskItemInContext.dataset.UUID].stats.info.remote){ - var confirmHangup = confirm(getTranslation("confirm-disconnect-user")); - if (confirmHangup) { - var msg = {}; - msg.hangup = true; - msg.remote = session.remote; - msg = await session.encodeRemote(msg); - session.sendRequest(msg, taskItemInContext.dataset.UUID); - pokeIframeAPI("hungup", "remote", taskItemInContext.dataset.UUID); - } - } - } else if (link.getAttribute("data-action") === "RemoteReload") { - if (session.rpcs[taskItemInContext.dataset.UUID] && session.rpcs[taskItemInContext.dataset.UUID].stats.info && ("remote" in session.rpcs[taskItemInContext.dataset.UUID].stats.info) && session.rpcs[taskItemInContext.dataset.UUID].stats.info.remote){ - var confirmReload = confirm(getTranslation("confirm-reload-user")); - if (confirmReload) { - var msg = {}; - msg.reload = true; - msg.remote = session.remote; - msg = await session.encodeRemote(msg); - session.sendRequest(msg, taskItemInContext.dataset.UUID); - pokeIframeAPI("reload", "remote", taskItemInContext.dataset.UUID); - } - } - } else if (link.getAttribute("data-action") === "SSNewTab") { - var URL = "https://"+window.location.hostname+location.pathname+createScreenShareURL(false); - log(URL); - window.open(URL, '_blank').focus(); - } else if (link.getAttribute("data-action") === "pip-clock") { - popOutClock(taskItemInContext.children[0]); - } else if (link.getAttribute("data-action") === "Publish") { - - var URL = taskItemInContext.href; - URL+="&clean&chroma=000&ssar=landscape&nosettings&prefercurrenttab&selfbrowsersurface=include&displaysurface=browser&np&nopush&publish&whippush&whippushtoken"; - var win = window.open( URL ,'targetWindow', 'toolbar=no,location=no,status=no,scaling=no,menubar=no,scrollbars=no,resizable=no,width=1280,height=720'); - win.focus(); - win.resizeTo(1280,720); - } - - if (inputElement===false){ - log("Task ID - " + taskItemInContext + ", Task action - " + link.getAttribute("data-action")); - toggleMenuOff(); - } - } - - function menuItemSyncState(menu) { - var items = menu.querySelectorAll("[data-action]"); - for (var i=0;i -1){ - items[i].parentNode.classList.add("hidden"); - } else { - items[i].parentNode.classList.remove("hidden"); - } - } else if (items[i].getAttribute("data-action") === "Publish") { - if (taskItemInContext.classList.contains("publish")){ - items[i].parentNode.classList.remove("hidden"); - } else { - items[i].parentNode.classList.add("hidden"); - } - } - } - } - contextListener(); - -})(); - - -function gotDevices3(deviceInfos, vid){ - var audioEle = document.createElement("select"); - log(deviceInfos); - if (!deviceInfos.length) { - return false; - } - for (let i = 0; i !== deviceInfos.length; ++i) { - if (deviceInfos[i].kind === 'audiooutput') { - var opt = document.createElement("option"); - opt.innerText = deviceInfos[i].label; - opt.value = deviceInfos[i].deviceId; - audioEle.appendChild(opt); - audioEle.videoTarget = vid; - if (vid.sinkId){ - if (vid.sinkId == deviceInfos[i].deviceId){ - opt.selected = true; - } - } else if (vid.manualSink){ - if (vid.manualSink == deviceInfos[i].deviceId){ - opt.selected = true; - } - } else if (session.sink){ - if (session.sink == deviceInfos[i].deviceId){ - opt.selected = true; - } - } - } - } - audioEle.onchange = function(){ - vid.manualSink = this.options[this.selectedIndex].value; - if (this.videoTarget && this.videoTarget.dataset.UUID){ - session.audioEffects = true; - updateIncomingAudioElement(this.videoTarget.dataset.UUID); - } - resetupAudioOut(this.videoTarget); - } - return audioEle; -} - -function popupMessage(e, message = "Copied to Clipboard") { // right click menu - - var posx = 0; - var posy = 0; - - if (!e) var e = window.event; - - if (e.pageX || e.pageY) { - posx = e.pageX; - posy = e.pageY; - } else if (e.clientX || e.clientY) { - posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; - posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; - } - - posx += 10; - - - var menu = getById("messagePopup"); - menu.innerHTML = "
    " + message + "
    "; - var menuState = 0; - var menuWidth; - var menuHeight; - var menuPosition; - var menuPositionX; - var menuPositionY; - - var windowWidth; - var windowHeight; - - if (menuState !== 1) { - menuState = 1; - menu.classList.add("context-menu--active"); - } - - menuWidth = menu.offsetWidth + 4; - menuHeight = menu.offsetHeight + 4; - - windowWidth = window.innerWidth; - windowHeight = window.innerHeight; - - if ((windowWidth - posx) < menuWidth) { - menu.style.left = windowWidth - menuWidth + "px"; - } else { - menu.style.left = posx + "px"; - } - - if ((windowHeight - posy) < menuHeight) { - menu.style.top = windowHeight - menuHeight + "px"; - } else { - menu.style.top = posy + "px"; - } - - - function toggleMenuOff() { - if (menuState !== 0) { - menuState = 0; - menu.classList.remove("context-menu--active"); - } - } - menu.classList.remove("fadeout"); - - var showlength = message.length*50 || 500; - - setTimeout(function() { - menu.classList.add("fadeout"); - }, showlength); - - setTimeout(function() { - toggleMenuOff(); - }, showlength+1000); -} - -function timeSince(date) { - - var seconds = Math.floor((new Date() - date) / 1000); - - var interval = seconds / 31536000; - - if (interval > 1) { - return Math.floor(interval) + " years"; - } - interval = seconds / 2592000; - if (interval > 1) { - return Math.floor(interval) + " months"; - } - interval = seconds / 86400; - if (interval > 1) { - return Math.floor(interval) + " days"; - } - interval = seconds / 3600; - if (interval > 1) { - return Math.floor(interval) + " hours"; - } - interval = seconds / 60; - if (interval > 1) { - return Math.floor(interval) + " minutes"; - } - return "Seconds ago"; -} - -var messageList = [] -function sendChatMessage(chatMsg = false, bc = false) { // filtered + visual - var data = {}; - if (chatMsg === false) { - var msg = document.getElementById('chatInput').value; - } else { - var msg = chatMsg; - } - //msg = sanitizeChat(msg); - if (msg == "") { - return false; - } - - msg = convertShortcodes(msg); - - var label = ""; - if (session.label){ - if (session.director){ - label = "" + session.label + ": "; - } else { - label = "" + session.label + ": "; - } - } else if (session.director){ - label = "Director: "; - } - - if (msg.trim()==="/list"){ - var listMsg = null; - for (var UUID in session.rpcs){ - if (session.rpcs[UUID].label){ - listMsg = UUID+": "+session.rpcs[UUID].label - } else if (session.directorList.indexOf(UUID)>=0){ - listMsg = UUID+": Director"; - } else { - listMsg = UUID+": Unknown User"; - } - var data = {}; - data.msg = listMsg; - data.label = false; - data.type = "alert"; - data.time = Date.now(); - messageList.push(data); - } - for (var UUID in session.pcs){ - if (UUID in session.rpcs){continue;} - if (session.pcs[UUID].label){ - listMsg = UUID+"; "+session.pcs[UUID].label - } else if (session.directorList.indexOf(UUID)>=0){ - listMsg = UUID+"; Director"; - } else { - listMsg = UUID+"; Unknown User"; - } - var data = {}; - data.msg = listMsg; - data.label = false; - data.type = "alert"; - data.time = Date.now(); - messageList.push(data); - } - if (listMsg===null){ - data.msg = "No other users are connected to you"; - data.label = false; - data.type = "alert"; - data.time = Date.now(); - messageList.push(data); - } - } else if (msg.startsWith("\/msg ")){ - var msg = msg.split("\/msg ")[1]; - msg = msg.split(" "); - uid = msg.shift().toLowerCase(); - msg = msg.join(" "); - if (msg == ""){return false;} - var sent = false; - for (var UUID in session.rpcs){ - if (UUID.startsWith(uid)){ - sendChat(msg, UUID); // send message to peers - var data = {}; - data.time = Date.now(); - data.msg = sanitizeChat(msg); // this is what the other person should see - data.label = label; - data.type = "sent"; - messageList.push(data); - sent=true; - } else if (session.rpcs[UUID].label && session.rpcs[UUID].label.toLowerCase().startsWith(uid)){ - sendChat(msg, UUID); // send message to peers - var data = {}; - data.time = Date.now(); - data.msg = sanitizeChat(msg); // this is what the other person should see - data.label = label; - data.type = "sent"; - messageList.push(data); - sent=true; - } else if ((session.directorList.indexOf(UUID)>=0) && "director".startsWith(uid)){ - sendChat(msg, UUID); // send message to peers - var data = {}; - data.time = Date.now(); - data.msg = sanitizeChat(msg); // this is what the other person should see - data.label = label; - data.type = "sent"; - messageList.push(data); - sent=true; - } - } - for (var UUID in session.pcs){ - if (UUID in session.rpcs){continue;} - if (UUID.startsWith(uid)){ - sendChat(msg, UUID); // send message to peers - var data = {}; - data.time = Date.now(); - data.msg = sanitizeChat(msg); // this is what the other person should see - data.label = label; - data.type = "sent"; - messageList.push(data); - sent=true; - } else if (session.pcs[UUID].label && session.pcs[UUID].label.toLowerCase().startsWith(uid)){ - sendChat(msg, UUID); // send message to peers - var data = {}; - data.time = Date.now(); - data.msg = sanitizeChat(msg); // this is what the other person should see - data.label = label; - data.type = "sent"; - messageList.push(data); - sent=true; - } else if ((session.directorList.indexOf(UUID)>=0) && "director".startsWith(uid)){ - sendChat(msg, UUID); // send message to peers - var data = {}; - data.time = Date.now(); - data.msg = sanitizeChat(msg); // this is what the other person should see - data.label = label; - data.type = "sent"; - messageList.push(data); - sent=true; - } - } - if (sent == false){ - var data = {}; - data.msg = "No user found. Message not sent."; - data.label = false; - data.type = "alert"; - data.time = Date.now(); - messageList.push(data); - updateMessages(); - return false; - } - } else if (msg.startsWith("\/")){ - data.msg = "Unknown command. Try '/list' or '/msg username message'."; - data.label = false; - data.type = "alert"; - data.time = Date.now(); - messageList.push(data); - updateMessages(); - return false; - } else if (session.directorChat===true){ - if (session.directorList.length){ - for (var i = 0;i{ - ele.click(); - }); -} - -function toggleQualityDirector(bitrate, UUID, ele) { // ele is specific to the button in the director's room - var eles = ele.parentNode.childNodes; - for (var i=0;i/g, ">").replace(/["']/g, ""); // try to sanitize things, just in case. - - var punc = ""; - while (url[url.length-1] === "."){ - url = url.slice(0,-1); - punc += "."; - } - while (url[url.length-1] === ";"){ - url = url.slice(0,-1); - punc += ";"; - } - while (url[url.length-1] === ","){ - url = url.slice(0,-1); - punc += ","; - } - while (url[url.length-1] === "!"){ - url = url.slice(0,-1); - punc += "!"; - } - while (url[url.length-1] === ":"){ - url = url.slice(0,-1); - punc += ":"; - } - while (url[url.length-1] === "*"){ - url = url.slice(0,-1); - punc += "*"; - } - while (url[url.length-1] === ")"){ - url = url.slice(0,-1); - punc += ")"; - } - while (url[url.length-1] === "?"){ - url = url.slice(0,-1); - punc += "?"; - } - - var hyperlink = url; - if (!hyperlink.match('^https?:\/\/')) { - hyperlink = 'http://' + hyperlink; - } - if (url.length>35){ - url = url.substring(0, 35)+"..."; - } - return '' + url + ''+punc; - }); -} - -function getChatMessage(msg, label = false, director = false, overlay = false) { - - msg = sanitizeChat(msg); // keep it clean. - if (msg == "") { - return; - } - - if (label) { - label = sanitizeLabel(label); - } - - data = {}; - data.time = Date.now(); - data.msg = msg; - if (label) { - data.label = label; - if (director) { - data.label = "" + data.label + ": "; - } else { - data.label = "" + data.label + ": "; - } - label = ""+label+":"; // label+":"; - } else if (director) { - data.label = "Director: "; - label = "Director:"; - } else { - if (session.director){ - data.label = "Someone: "; - } else { - data.label = ""; - } - label = ""; - } - data.type = "recv"; - - if (overlay) { - if (!(session.cleanOutput && session.cleanish==false)){ - var textOverlay = getById("overlayMsgs"); - if (textOverlay) { - var spanOverlay = document.createElement("span"); - spanOverlay.innerHTML = "" + label + " " + msg + "
    "; - textOverlay.appendChild(spanOverlay); - textOverlay.style.display = "block"; - var showtime = msg.length * 200 + 3000; - if (showtime > 8000) { - showtime = 8000; - } - setTimeout(function(ele) { - ele.parentNode.removeChild(ele); - }, showtime, spanOverlay); - } - } - } - - if (isIFrame) { - parent.postMessage({ - "gotChat": data - }, session.iframetarget); - } - - if (session.chatbutton===false){return;} // messages can still appear as overlays ^ - - messageList.push(data); - messageList = messageList.slice(-100); - - if (session.beepToNotify) { - playtone(); - showNotification("new message", msg); - } - updateMessages(); - - if (session.chat == false) { - getById("chattoggle").className = "las la-comments toggleSize pulsate"; - getById("chatbutton").className = "float"; - - if (getById("chatNotification").value) { - getById("chatNotification").value = getById("chatNotification").value + 1; - } else { - getById("chatNotification").value = 1; - } - getById("chatNotification").classList.add("notification", "red"); - } - - - if (session.broadcastChannel !== false) { - session.broadcastChannel.postMessage(data); /* send */ - } - -} - -function updateClosedCaptions(msg, label, UUID) { - msg.counter = parseInt(msg.counter); - var temp = document.createElement('div'); - temp.innerText = msg.transcript; - temp.innerText = temp.innerHTML; - var transcript = temp.textContent || temp.innerText || ""; - - if (transcript == "") { - return; - } - - transcript = transcript.charAt(0).toUpperCase() + transcript.slice(1); - //transcript = transcript.substr(-1, 5000); // keep it from being too long - - - if (label && (!(session.view && !session.view_set))) { - label = sanitizeLabel(label); - label = "" + label + ": "; - } else { - label = ""; - } - - var textOverlay = getById("overlayMsgs"); - if (textOverlay) { - if (document.getElementById(UUID + "_" + msg.counter)) { - var spanOverlay = document.getElementById(UUID + "_" + msg.counter); - } else { - var spanOverlay = document.createElement("span"); - spanOverlay.id = UUID + "_" + msg.counter; - textOverlay.appendChild(spanOverlay); - textOverlay.style.height = "unset"; - textOverlay.style.textAlign = "left"; - textOverlay.style.display = "block"; - textOverlay.style.position = "fixed"; - textOverlay.style.bottom = "0"; - - } - spanOverlay.innerHTML = label + transcript + "
    "; - spanOverlay.style.fontSize = (parseInt(session.labelsize || 100) / 100.0 * 4.5) + "vh"; - spanOverlay.style.lineHeight = (parseInt(session.labelsize || 100) / 100 * 6) + "vh"; - spanOverlay.style.margin = (parseInt(session.labelsize || 100) / 100.0 * 0.75) + "vh"; - - if (msg.isFinal) { - var showtime = 3000; - clearTimeout(spanOverlay.timeout); - spanOverlay.timeout = setTimeout(function(ele) { - ele.parentNode.removeChild(ele); - }, showtime, spanOverlay); - } else { - clearTimeout(spanOverlay.timeout); - spanOverlay.timeout = setTimeout(function(ele) { - ele.parentNode.removeChild(ele); - }, 30000, spanOverlay); - } - - } -} - -var chatUpdateTimeout = null; -function updateMessages(){ - if (session.chatbutton===false){return;} - document.getElementById("chatBody").innerHTML = ""; - for (var i in messageList) { - - var time = timeSince(messageList[i].time) || ""; - time = " - "+time+""; - var msg = document.createElement("div"); - var message = replaceURLs(messageList[i].msg); - - if (messageList[i].type == "sent") { - msg.innerHTML = message + "" + time + ""; - msg.classList.add("outMessage"); - } else if ((messageList[i].type == "recv") || (messageList[i].type == "action")) { - var label = ""; - if (messageList[i].label) { - label = messageList[i].label; - } - msg.innerHTML = label + message + "" + time + ""; - msg.classList.add("inMessage"); - } else if (messageList[i].type == "alert") { - msg.innerHTML = message + "" + time + ""; - msg.classList.add("inMessage"); - } else { - msg.innerHTML = message; - msg.classList.add("outMessage"); - } - - document.getElementById("chatBody").appendChild(msg); - } - showDownloadLinks(); - for (var i in msgTransferList) { - var time = timeSince(msgTransferList[i].time) || ""; - time = " - "+time+""; - - var msg = document.createElement("div"); - if ("idx" in msgTransferList[i]){ - msg.id = "transfer_"+msgTransferList[i].idx; - msg.classList.add("transfer"); - } - if (msgTransferList[i].type == "sent") { - msg.innerHTML = msgTransferList[i].msg + "" + time + ""; - msg.classList.add("outMessage"); - } else if ((msgTransferList[i].type == "recv") || (msgTransferList[i].type == "action")) { - var label = ""; - if (msgTransferList[i].label) { - label = msgTransferList[i].label; - } - msg.innerHTML = label + msgTransferList[i].msg + "" + time + ""; - msg.classList.add("inMessage"); - } else if (msgTransferList[i].type == "alert") { - msg.innerHTML = msgTransferList[i].msg + "" + time + ""; - msg.classList.add("inMessage"); - } else { - msg.innerHTML = msgTransferList[i].msg; - msg.classList.add("outMessage"); - } - - if (msg.id && document.getElementById(msg.id)){ - document.getElementById(msg.id).innerHTML = msg.innerHTML; - } else { - getById("chatBody").appendChild(msg); - } - } - if (chatUpdateTimeout) { - clearInterval(chatUpdateTimeout); - } - document.getElementById("chatBody").scrollTop = document.getElementById("chatBody").scrollHeight; - chatUpdateTimeout = setTimeout(function() { - updateMessages(); - }, 60000); -} - -function EnterButtonChat(event) { - // Number 13 is the "Enter" key on the keyboard - var key = event.which || event.keyCode; - if (key === 13) { - // Cancel the default action, if needed - event.preventDefault(); - // Trigger the button element with a click - sendChatMessage(); - } -} - -function showCustomizer(arg, ele) { - //getById("directorLinksButton").innerHTML=' LINKS (GUEST INVITES & SCENES)' - getById("showCustomizerButton1").style.backgroundColor = ""; - getById("showCustomizerButton2").style.backgroundColor = ""; - getById("showCustomizerButton3").style.backgroundColor = ""; - getById("showCustomizerButton4").style.backgroundColor = ""; - getById("showCustomizerButton1").style.boxShadow = ""; - getById("showCustomizerButton2").style.boxShadow = ""; - getById("showCustomizerButton3").style.boxShadow = ""; - getById("showCustomizerButton4").style.boxShadow = ""; - - - if (getById("customizeLinks" + arg).style.display != "none") { - getById("customizeLinks").style.display = "none"; - getById("customizeLinks" + arg).style.display = "none"; - } else { - //directorLinks").style.display="none"; - getById("showCustomizerButton" + arg).style.backgroundColor = "#1e0000"; - getById("showCustomizerButton" + arg).style.boxShadow = "inset 0px 0px 1px #b90000"; - getById("customizeLinks1").style.display = "none"; - getById("customizeLinks3").style.display = "none"; - getById("customizeLinks").style.display = "block"; - getById("customizeLinks" + arg).style.display = "block"; - } -} - -var PPTHotkey = getStorage("PPTHotkey") || false; -if (PPTHotkey){ - var key = ""; - if (PPTHotkey.ctrl){ - key += "Control"; - } - if (PPTHotkey.meta){ - if (key){ - key += " + "; - } - key += "Meta"; - } - if (PPTHotkey.alt){ - if (key){ - key += " + "; - } - key += "Alt"; - } - - if (PPTHotkey.key=="Control"){ - // - } else if (PPTHotkey.key=="Alt"){ - // - } else if (PPTHotkey.key=="Meta"){ - // - } else if (PPTHotkey.key !== false){ - if (key){ - key += " + "; - } - if (PPTHotkey.key === " "){ - key += "Space" - } else { - key += PPTHotkey.key; - } - } else if (key && (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1)){ - getById("pptHotKey").title = "Note: Global hot-keys can't simply be Control, Alt, or Meta keys."; - } - getById("pptHotKey").value = key; - - try { - if (window.electronApi && window.electronApi.updatePPT){ - window.electronApi.updatePPT(PPTHotkey); - } else if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1) { - if (!ipcRenderer){ - ipcRenderer = require('electron').ipcRenderer; - } - if (ipcRenderer){ - ipcRenderer.send('PPTHotkey', PPTHotkey); - } - } - } catch(e){errorlog(e);} -} - -function setHotKey(keyinput=true){ - if (!keyinput){ // clears if false - getById("pptHotKey").value = ""; - PPTHotkey = false; - removeStorage("PPTHotkey"); - - try { - if (window.electronApi && window.electronApi.updatePPT){ - window.electronApi.updatePPT(PPTHotkey); - } else if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1) { - if (!ipcRenderer){ - ipcRenderer = require('electron').ipcRenderer; - } - if (ipcRenderer){ - ipcRenderer.send('PPTHotkey', PPTHotkey); - } - } - } catch(e){errorlog(e);} - - return; - } - - PPTHotkey = { - ctrl:false, - alt: false, - meta: false, - key: false - }; - - log(event); - var key = ""; - if (event.ctrlKey){ - key += "Control"; - PPTHotkey.ctrl = true; - } - if (event.metaKey){ - if (key){ - key += " + "; - } - key += "Meta"; - PPTHotkey.meta = true; - } - if (event.altKey){ - if (key){ - key += " + "; - } - key += "Alt"; - PPTHotkey.alt = true; - } - - if (event.key=="Control"){ - // - } else if (event.key=="Alt"){ - // - } else if (event.key=="Meta"){ - // - } else if (event.key || (event.key === " " || (event.key===0))){ - if (key){ - key += " + "; - } - if (event.key === " "){ - key += "Space" - } else { - key += event.key; - } - PPTHotkey.key = event.key; - } - setStorage("PPTHotkey", PPTHotkey, 99999); - event.target.value = key; - - try { - if (window.electronApi && window.electronApi.updatePPT){ - window.electronApi.updatePPT(PPTHotkey); - } else if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1) { - if (!ipcRenderer){ - ipcRenderer = require('electron').ipcRenderer; - } - if (ipcRenderer){ - ipcRenderer.send('PPTHotkey', PPTHotkey); - } - } - } catch(e){errorlog(e);} - - event.preventDefault(); - event.stopPropagation(); - return false; -} - - -async function streamVideoToDropbox(filename) { - - if (!session.dbx){return;} - - return await session.dbx.filesUploadSessionStart({ close: false }).then(function (response) { - var sessionId = response.result.session_id; - var offset = 0; - var queue = []; - - console.log(response); - - function uploadChunk(chunk, oldCursor = false) { - - if (queue.length){ // still uploading - queue.push(chunk); - return; - } - - queue.push(chunk); - - if (chunk===false){ - - console.log("DONE UPLOADING.. closing dropbox file: "+offset); - console.log(oldCursor); - - session.dbx.filesUploadSessionFinish({ cursor: { session_id: sessionId, offset: offset }, commit: { path: '/' + filename } }).then(function (response) { - console.log(response); - console.log('File uploaded to Dropbox:', response.path_display); - }) - .catch(function (error) { - console.error('Error uploading file:', error); - }); - - } else { - - var currentOffset = offset; - offset += chunk.size; - - var cursor = { session_id: sessionId, offset: currentOffset }; - - console.log(cursor); - - session.dbx.filesUploadSessionAppendV2({ cursor: cursor, close: false, contents: chunk }).then(function () { - console.log("uploaded"); - console.log(queue); - var x = queue.shift(); - if (queue.length){ - uploadChunk(queue.shift(), cursor) - } - }).catch(function (error) { - console.error('Error appending chunk:', error); - }); - } - } - - return uploadChunk; - - }).catch(function (error) { - console.error('Error starting upload session:', error); - }); -} - -var recordingBitratePromise = false; -var defaultRecordingBitrate = false; -async function recordVideo(target, event = null, videoKbps = false) { // event.currentTarget,this.parentNode.parentNode.dataset.UUID - - if (session.record === false){warnlog("recordings are disabled by decree of thy host magistrate");} - - var UUID = target.dataset.UUID; - - if (!UUID){return;} - - var video = session.rpcs[UUID].videoElement; - - if (!video){return;} - - if (video.stopWriter){ - video.stopWriter(); - updateLocalRecordButton(UUID, -1); - return; - } else if (video.startWriter){ - await video.startWriter(); - updateLocalRecordButton(UUID, 0); - return; - } - - - var audioKbps = false; - - if (event === null) { - if (defaultRecordingBitrate === null) { - updateLocalRecordButton(UUID, -1); - return; - } - } else if ((event.ctrlKey) || (event.metaKey)) { - updateLocalRecordButton(UUID, -3); - Callbacks.push([recordVideo, target, null, false]); - log("Record Video queued"); - defaultRecordingBitrate = false; - recordingBitratePromise = false; - return; - } else { - defaultRecordingBitrate = false; - recordingBitratePromise = false; - } - - log("Record Video Clicked"); - if ("recording" in video) { - log("ALREADY RECORDING!"); - updateLocalRecordButton(UUID, -2); - video.recorder.stop(); - session.requestRateLimit(35, UUID); // 100kbps - if (session.audiobitrate===false){ - session.requestAudioRateLimit(-1,UUID); - } - - var elements = document.querySelectorAll('[data-action-type="change-quality2"][data--u-u-i-d="' + UUID + '"]'); - if (elements[0]) { - elements[0].classList.add("pressed"); elements[0].ariaPressed = "true"; - } - var elements = document.querySelectorAll('[data-action-type="change-quality1"][data--u-u-i-d="' + UUID + '"]'); - if (elements[0]) { - elements[0].classList.remove("pressed"); elements[0].ariaPressed = "false"; - } - var elements = document.querySelectorAll('[data-action-type="change-quality3"][data--u-u-i-d="' + UUID + '"]'); - if (elements[0]) { - elements[0].classList.remove("pressed"); elements[0].ariaPressed = "false"; - } - return; - } else { - updateLocalRecordButton(UUID, 0); - //target.style.backgroundColor = "#FCC"; - //target.innerHTML = " Download"; - video.recording = true; - } - - video.recorder = {}; - - if (videoKbps === false) { - if (defaultRecordingBitrate == false) { - - videoKbps = 4000; - if (session.recordLocal !== false) { - videoKbps = session.recordLocal; - } - - if (!recordingBitratePromise){ - window.focus(); - recordingBitratePromise = promptAlt(getTranslation("press-ok-to-record"), false, false, videoKbps); - } - videoKbps = await recordingBitratePromise; - log("videoKbps: "+videoKbps+", UUID:"+UUID); - if (videoKbps === null) { - //target.style.backgroundColor = null; - //target.innerHTML = ' record local'; - updateLocalRecordButton(UUID, -1); - target.style.backgroundColor = ""; - delete(video.recorder); - delete(video.recording); - defaultRecordingBitrate = null; - return; - } - videoKbps = parseInt(videoKbps); - defaultRecordingBitrate = videoKbps; - } else { - videoKbps = defaultRecordingBitrate; - } - } - - if (videoKbps <= 0) { - audioKbps = videoKbps * (-1); - videoKbps = false; - if (session.audiobitrate===false){ - if ((audioKbps>0) && (audioKbps>=128)){ - session.requestAudioRateLimit(128,UUID); // no point going higher - } else if (audioKbps==0){ - session.requestAudioRateLimit(256,UUID); // PCM - } else { - session.requestAudioRateLimit(parseInt(audioKbps),UUID); // exact? sure. why not. - } - } - } else if (videoKbps < 50) { // this just makes sure you can't set 0 on the record bitrate. - videoKbps = 50; - session.requestRateLimit(parseInt(videoKbps * 0.8), UUID); // 3200kbps transfer bitrate. Less than the recording bitrate, to avoid waste. - } else { - session.requestRateLimit(parseInt(videoKbps * 0.8), UUID); // 3200kbps transfer bitrate. Less than the recording bitrate, to avoid waste. - - if (videoKbps>4000){ - if (session.audiobitrate===false){ - if (session.pcm){ - session.requestAudioRateLimit(256,UUID); - } else { - session.requestAudioRateLimit(128,UUID); - } - } - } else if (videoKbps>2500){ - if (session.audiobitrate===false){ - if (session.pcm){ - session.requestAudioRateLimit(256,UUID); - } else { - session.requestAudioRateLimit(80,UUID); - } - } - } - - } - - var timestamp = Date.now(); - var filename = ""; - if (session.rpcs[UUID].label || session.rpcs[UUID].streamID) { - filename = session.rpcs[UUID].label || session.rpcs[UUID].streamID; - filename = filename.replace(/[\W]+/g, "_"); - filename = filename.substring(0, 200); - } - - filename += "_" + timestamp.toString(); - - var cancell = false; - if (typeof video.srcObject === "undefined" || !video.srcObject) { - return; - } - - video.recorder.stop = function(restart = false, notify = false) { - - if (session.dbx && video.recorder && video.recorder.dropbox){ - video.recorder.dropbox(false); - } - - if (!video.recording) { - errorlog("ALREADY STOPPED"); - updateLocalRecordButton(UUID, -1); - return; - } - - if (notify){ - if (!session.cleanOutput){ - warnUser("A local recording has stopped unexpectedly."); - } - if (session.beepToNotify){ - playtone(); - - } - target.classList.remove("shake"); - setTimeout(function(target){target.classList.add("shake");},10, target); - } - - video.recording = false; - updateLocalRecordButton(UUID, -2); - try { - if (video.recorder.mediaRecorder.state !== "inactive") { - video.recorder.mediaRecorder.stop(); - } - } catch (e) { - errorlog(e); - } - - session.requestRateLimit(35, UUID); // 100kbps - if (session.audiobitrate===false){ - session.requestAudioRateLimit(-1,UUID); - } - var elements = document.querySelectorAll('[data-action-type="change-quality2"][data--u-u-i-d="' + UUID + '"]'); - if (elements[0]) { - elements[0].classList.add("pressed"); elements[0].ariaPressed = "true"; - } - var elements = document.querySelectorAll('[data-action-type="change-quality1"][data--u-u-i-d="' + UUID + '"]'); - if (elements[0]) { - elements[0].classList.remove("pressed"); elements[0].ariaPressed = "false"; - } - var elements = document.querySelectorAll('[data-action-type="change-quality3"][data--u-u-i-d="' + UUID + '"]'); - if (elements[0]) { - elements[0].classList.remove("pressed"); elements[0].ariaPressed = "false"; - } - - cancell = true; - // log('Recorded Blobs: ', recordedBlobs); - // download(); - setTimeout((writer1,UUID1,video1) => { - try{ - writer1.close(); - } catch(e){} - updateLocalRecordButton(UUID1, -1); - delete(video1.recorder); - delete(video1.recording); - }, 1200, writer, UUID, video); - }; - - const {readable, writable} = new TransformStream({ - transform: (chunk, ctrl) => chunk.arrayBuffer().then(b => ctrl.enqueue(new Uint8Array(b))) - }); - var writer = writable.getWriter(); - readable.pipeTo(streamSaver.createWriteStream(filename.toString() + '.webm', video.recorder.stop)); - video.recorder.writer = writer; - pokeIframeAPI("recording-started"); - - let options = {}; - - if (videoKbps) { - - var tryCodec = false; - if (session.recordingVideoCodec){ - tryCodec = session.recordingVideoCodec; - } - if (tryCodec && MediaRecorder.isTypeSupported('video/webm;codecs='+tryCodec)) { - if (!session.cleanOutput){ - warnUser("The browser 'says' it supports "+tryCodec); - } - options.mimeType = 'video/webm;codecs='+tryCodec; - if (session.pcm){ - if (MediaRecorder.isTypeSupported('video/webm;codecs="'+tryCodec+', pcm"')){ - options.mimeType = 'video/webm;codecs="'+tryCodec+', pcm"'; - } else { - options.mimeType = "video/webm;codecs=pcm"; - } - } - } else { - if (session.pcm){ - if (MediaRecorder.isTypeSupported("video/webm;codecs=pcm")) { - options.mimeType = "video/webm;codecs=pcm"; - } else { - options.mimeType = "video/webm"; - } - } else { - options.mimeType = "video/webm"; - } - } - if (videoKbps < 1000) { - options.videoBitsPerSecond = parseInt(videoKbps * 1024); // 100 kbps audio - } else { - options.bitsPerSecond = parseInt(videoKbps * 1024); // 100 to 132 kbps audio - } - video.recorder.mediaRecorder = new MediaRecorder(video.srcObject, options); - - //if (session.dbx){ - // video.recorder.dropbox = await streamVideoToDropbox(); // i don't want to upload to dropbox remote streams; just local - //} - - } else { - options.mimeType = 'audio/webm'; - if (audioKbps == 0) { - if (MediaRecorder.isTypeSupported("audio/webm;codecs=pcm")) { - options.mimeType = "audio/webm;codecs=pcm"; - } - } else { - options.bitsPerSecond = parseInt(audioKbps * 1024); - } - var stream = createMediaStream(); - video.srcObject.getAudioTracks().forEach((track) => { - stream.addTrack(track, video.srcObject); - }); - video.recorder.mediaRecorder = new MediaRecorder(stream, options); - - //if (session.dbx){ - // video.recorder.dropbox = await streamVideoToDropbox(); - //} - } - - log(options); - - function download() { - const blob = new Blob(recordedBlobs, { - type: "video/webm" - }); - const url = window.URL.createObjectURL(blob); - const a = document.createElement('a'); - a.style.display = 'none'; - a.href = url; - a.download = filename + ".webm"; - document.body.appendChild(a); - a.click(); - setTimeout(function(uu,aa){ - document.body.removeChild(aa); - window.URL.revokeObjectURL(uu); - }, 100, url,a); - } - - function handleDataAvailable(event) { - if (event.data && event.data.size > 0) { - //recordedBlobs.push(event.data); - try{ - writer.write(event.data); //////////// - if (video.recording) { - updateLocalRecordButton(UUID, (parseInt((Date.now() - timestamp) / 1000) || 0)); - } - } catch(e){warnlog("Stream recording error or ended");} - - try { - if (session.dbx && video.recorder && video.recorder.dropbox){ - video.recorder.dropbox(event.data); - } - - } catch(e){ - errorlog(e); - } - } - } - - video.recorder.mediaRecorder.ondataavailable = handleDataAvailable; - - video.recorder.mediaRecorder.onerror = function(event) { - errorlog(event); - video.recorder.stop(); - session.requestRateLimit(35, UUID); - if (!(session.cleanOutput)) { - setTimeout(function() { - warnUser("an error occured with the media recorder; stopping recording"); - }, 1); - } - }; - - video.srcObject.ended = function(event) { - video.recorder.stop(); - session.requestRateLimit(35, UUID); - if (!(session.cleanOutput)) { - setTimeout(function() { - warnUser("stream ended! stopping recording"); - }, 1); - } - }; - - - setTimeout(function(v) { - v.recorder.mediaRecorder.start(1000); - }, 500, video); // 100ms chunks - - return; -} - -function updateRemoteRecordButton(UUID, recorder) { - var elements = document.querySelectorAll('[data-action-type="recorder-remote"][data--u-u-i-d="' + UUID + '"]'); - if (elements[0]) { - var time = parseInt(recorder) || 0; - if (time == -4) { - if (!session.cleanOutput){ - warnUser("A remote recording has stopped unexpectedly.\n\nDid a user cancel the file downlaod?"); - } - if (session.beepToNotify){ - playtone(); - } - elements[0].classList.add("pressed"); elements[0].ariaPressed = "true"; - elements[0].classList.remove("shake"); - elements[0].innerHTML = ' stopping...'; - setTimeout(function(ele){ele.classList.add("shake");},10,elements[0]); - } else if (time == -3) { - elements[0].classList.remove("pressed"); elements[0].ariaPressed = "false"; - elements[0].disabled = true; - elements[0].innerHTML = ' Not Supported'; - if (!(session.cleanOutput)) { - setTimeout(function() { - warnUser('The remote browser does not support recording.\n\nPerhaps try local recording instead.'); - }, 0); - } - } else if (time == -5) { - if (!(session.cleanOutput)) { - setTimeout(function() { - warnUser('The remote browser has only experimental support for media recording.\n\nAlso, when this download stops, the remote user may be asked to download the file for it to save.'); - }, 0); - } - } else if (time == -2) { - elements[0].classList.add("pressed"); elements[0].ariaPressed = "true"; - elements[0].innerHTML = ' stopping...'; - } else if (time == -1) { - elements[0].classList.remove("pressed"); elements[0].ariaPressed = "false"; - elements[0].innerHTML = ' Record Remote'; - } else { - var minutes = Math.floor(time / 60); - var seconds = time - minutes * 60; - elements[0].classList.add("pressed"); elements[0].ariaPressed = "true"; - elements[0].innerHTML = ' ' + (minutes) + "m : " + zpadTime(seconds) + "s"; - } - } -} - -function updateLocalRecordButton(UUID, recorder) { - var elements = document.querySelectorAll('[data-action-type="recorder-local"][data--u-u-i-d="' + UUID + '"]'); - if (elements[0]) { - var time = parseInt(recorder) || 0; - - //target.innerHTML = ' ARMED'; - // - if (time == -3) { - elements[0].classList.add("pressed"); elements[0].ariaPressed = "true"; - elements[0].innerHTML = ' ARMED'; - elements[0].style.backgroundColor = "#BF3F3F"; - } else if (time == -2) { - elements[0].classList.add("pressed"); elements[0].ariaPressed = "true"; - elements[0].innerHTML = ' stopping...'; - elements[0].style.backgroundColor = ""; - } else if (time == -1) { - elements[0].classList.remove("pressed"); elements[0].ariaPressed = "false"; - elements[0].innerHTML = ' Record Local'; - elements[0].style.backgroundColor = ""; - } else { - var minutes = Math.floor(time / 60); - var seconds = time - minutes * 60; - elements[0].classList.add("pressed"); elements[0].ariaPressed = "true"; - elements[0].innerHTML = ' ' + (minutes) + "m : " + zpadTime(seconds) + "s"; - elements[0].style.backgroundColor = ""; - } - } -} - -function recordLocalVideoToggle() { - if (!session.videoElement){return;} - log("recordLocalVideoToggle()"); - - var ele = getById("recordLocalbutton"); - if (ele.dataset.state == "0") { - ele.dataset.state = "1"; - ele.style.backgroundColor = "red"; - ele.innerHTML = ''; - if ("recording" in session.videoElement) { - - } else { - recordLocalVideo("start"); - } - - if (session.director){ - var elements = document.querySelectorAll('[data-action-type="recorder-local"][data-sid="' + session.streamID + '"]'); - if (elements[0]) { - elements[0].classList.add("pressed"); elements[0].ariaPressed = "true"; - elements[0].innerHTML = ' Record'; - } - } - return true; - } else { - if ("recording" in session.videoElement) { - recordLocalVideo("stop"); - } - ele.dataset.state = "0"; - ele.style.backgroundColor = ""; - ele.innerHTML = ''; - - if (session.director){ - var elements = document.querySelectorAll('[data-action-type="recorder-local"][data-sid="' + session.streamID + '"]'); - if (elements[0]) { - elements[0].classList.remove("pressed"); elements[0].ariaPressed = "false"; - elements[0].innerHTML = ' Record'; - } - } - return false; - } -} - -function setupSensorData(pollrate = 30) { - session.sensors = {}; - session.sensors.data = {}; - - if (window.Accelerometer && session.sensorDataFilter.includes("acc")) { - session.sensors.data.acc = {}; - session.sensors.Accelerometer = new Accelerometer({ - frequency: pollrate - }); - session.sensors.Accelerometer.addEventListener('reading', e => { - session.sensors.data.acc.x = session.sensors.Accelerometer.x.toFixed(5); - session.sensors.data.acc.y = session.sensors.Accelerometer.y.toFixed(5); - session.sensors.data.acc.z = session.sensors.Accelerometer.z.toFixed(5); - session.sensors.data.acc.t = parseInt(Math.round(session.sensors.Accelerometer.timestamp)); - }); - session.sensors.Accelerometer.start(); - } - if (window.Gyroscope && session.sensorDataFilter.includes("gyro")) { - session.sensors.data.gyro = {}; - session.sensors.Gyroscope = new Gyroscope({ - frequency: pollrate - }); - session.sensors.Gyroscope.addEventListener('reading', e => { - session.sensors.data.gyro.x = session.sensors.Gyroscope.x.toFixed(5); - session.sensors.data.gyro.y = session.sensors.Gyroscope.y.toFixed(5); - session.sensors.data.gyro.z = session.sensors.Gyroscope.z.toFixed(5); - session.sensors.data.gyro.t = parseInt(Math.round(session.sensors.Gyroscope.timestamp)); - }); - session.sensors.Gyroscope.start(); - } - if (window.Magnetometer && session.sensorDataFilter.includes("mag")) { - session.sensors.data.mag = {}; - session.sensors.Magnetometer = new Magnetometer({ - frequency: pollrate - }); - session.sensors.Magnetometer.addEventListener('reading', e => { - session.sensors.data.mag.x = session.sensors.Magnetometer.x.toFixed(5); - session.sensors.data.mag.y = session.sensors.Magnetometer.y.toFixed(5); - session.sensors.data.mag.z = session.sensors.Magnetometer.z.toFixed(5); - session.sensors.data.mag.t = parseInt(Math.round(session.sensors.Magnetometer.timestamp)); - - }); - session.sensors.Magnetometer.start(); - session.sensors.deviceorientation = false; - } else if (session.sensorDataFilter.includes("ori")){ - try{ - window.addEventListener('deviceorientation', e => { - session.sensors.data.ori = {}; - try{ - session.sensors.data.ori.d = e.absolute; - } catch(event){} - session.sensors.data.ori.a = e.alpha.toFixed(5); - session.sensors.data.ori.b = e.beta.toFixed(5); - session.sensors.data.ori.g = e.gamma.toFixed(5); - session.sensors.data.ori.t = parseInt(Math.round(e.timestamp)) || Date.now(); - }); - session.sensors.deviceorientation = true; - } catch(e){ - session.sensors.deviceorientation = false; - } - } - if (window.LinearAccelerationSensor && session.sensorDataFilter.includes("lin")) { - session.sensors.data.lin = {}; - session.sensors.LinearAccelerationSensor = new LinearAccelerationSensor({ - frequency: pollrate - }); - session.sensors.LinearAccelerationSensor.addEventListener('reading', e => { - session.sensors.data.lin.x = session.sensors.LinearAccelerationSensor.x.toFixed(5); - session.sensors.data.lin.y = session.sensors.LinearAccelerationSensor.y.toFixed(5); - session.sensors.data.lin.z = session.sensors.LinearAccelerationSensor.z.toFixed(5); - session.sensors.data.lin.t = parseInt(Math.round(session.sensors.LinearAccelerationSensor.timestamp)); - }); - session.sensors.LinearAccelerationSensor.start(); - } - - if (navigator.geolocation && session.sensorDataFilter.includes("pos")){ - navigator.geolocation.watchPosition(function(pos){ - try { - session.sensors.data.pos = {}; - session.sensors.data.pos.speed = pos.coords.speed.toFixed(3); - session.sensors.data.pos.alt = pos.coords.altitude.toFixed(3); - session.sensors.data.pos.t = pos.timestamp; - }catch(e){} - }, errorlog, { - enableHighAccuracy: true, - timeout: 5000, - maximumAge: 0 - }); - } - - setInterval(function() { - session.sendMessage({sensors: session.sensors.data}); - }, parseInt(1000 / pollrate)); -} - - -async function recordLocalVideo(action = null, videoKbps = false, remote=false) { // event.currentTarget,this.parentNode.parentNode.dataset.UUID - - - if (session.record === false){warnlog("recordings are disabled by decree of thy host magistrate");} - - var audioKbps = false; - if (remote){ - var video = remote; - if (remote.id === "videosource"){ - remote = false; - } - } else { - var video = session.videoElement; - } - log(video.id); - - if (!video){return;} - - if ("recording" in video) { - if (action == "estop") { - video.recorder.eStop(); - log("EMERGENCY Stopping RECORDING!"); - video.recorder.stop(); - return; - } else if (action == "stop") { - log("Stopping RECORDING!"); - video.recorder.stop(); - return; - } else if (action == "start") { - log("ALREADY RECORDING!"); - if (remote){ - getById("recordLocalbutton").dataset.state = "1"; - getById("recordLocalbutton").style.backgroundColor = "red"; - getById("recordLocalbutton").innerHTML = ''; - } - return; - } else { - log("STOPPING RECORDING by default toggle!"); - video.recorder.stop(); - return; - } - return; - } else if (action == "start") { - if (!MediaRecorder) { - var msg = {}; - msg.recorder = -3; - for (var i = 0;i { - try { - video.recorder.writer.close(); - } catch(e){} - pokeIframeAPI("recording-stopped"); - if (!remote){ - try { - if (session.directorUUID) { - var msg = {}; - msg.recorder = -1; - for (var i = 0;i { - audioTrack = true; - stream.addTrack(track, video.srcObject); - }); - - if (!audioTrack){ - errorlog("Failing the recording; no audio track"); - try { - video.recorder.writer.close(); - } catch(e){} - try { - delete(video.recorder); - delete(video.recording); - } catch(e){} - var msg = {}; - msg.recorder = -3; - for (var i = 0;i 0) { - writer.write(event.data); - if (session.directorList.length) { - if (video.recording) { - var msg = {}; - msg.recorder = parseInt((Date.now() - timestamp) / 1000) || 0; - for (var i =0;i chunk.arrayBuffer().then(b => ctrl.enqueue(new Uint8Array(b))) - }); - var writer = writable.getWriter(); - readable.pipeTo(streamSaver.createWriteStream(filename.toString() + '.webm', video.recorder.stop)); - video.recorder.writer = writer; - - video.recorder.mediaRecorder.start(1000); // 100ms chunks - - pokeIframeAPI("recording-started"); - - getById("recordLocalbutton").dataset.state = "1"; - getById("recordLocalbutton").style.backgroundColor = "red"; - getById("recordLocalbutton").innerHTML = ''; - - if (session.directorList.length) { - var msg = {}; - - msg.recorder = 0; - for (var i =0;i{ - var UUID = target.dataset.UUID; - if (!UUID){return;} - var video = session.rpcs[UUID].videoElement; - if (!video){return;} - if (!video.stopWriter){ - recordVideo(target); // if not started, start - } - }); - recordLocalVideo("start"); // self -} -function localGlobalRecordStop(){ - document.querySelectorAll('[data-action-type=\'recorder-local\']').forEach(target=>{ - var UUID = target.dataset.UUID; - if (!UUID){return;} - var video = session.rpcs[UUID].videoElement; - if (!video){return;} - if (video.stopWriter){ - recordVideo(target); // if started, stop - } - }); - recordLocalVideo("stop"); // self -} -async function remoteGlobalRecordStart(){ - window.focus(); - var bitrate = await promptAlt(miscTranslations['what-bitrate'], false, false, 6000); - document.querySelectorAll('[data-action-type=\'recorder-remote\']').forEach(target=>{ - requestVideoRecord(target, true, bitrate); - }); -} -function remoteGlobalRecordStop(){ - document.querySelectorAll('[data-action-type=\'recorder-remote\']').forEach(target=>{ - if (target.classList.contains('pressed')){ - requestVideoRecord(target, false); - } - }); -} -session.onTrack = function(event, UUID){ - - if (session.badStreamList.includes(session.rpcs[UUID].streamID)){ - errorlog("new connection is contained in badStreamList 2! This shouldn't happen"); - // we will have none of this. - return; - } - - var newTracks = []; - var newStream = false; - if (event.streams && event.streams[0]){ - newStream = event.streams[0]; - newTracks = newStream.getTracks(); - } else if (event.track){ - newTracks.push(event.track); - } else { - errorlog("Something went wrong with incoming track.."); - return; - } - - if (session.rpcs[UUID].streamSrc){ - var tracks = session.rpcs[UUID].streamSrc.getTracks(); - newTracks.forEach(function(trk){ - tracks.forEach(function(trk2){ - if ((trk.id == trk2.id) && (trk.kind == trk2.kind)){ - var index = newTracks.indexOf(trk); - if (index > -1) { - newTracks.splice(index, 1); - } - } - }); - }); - } - - var screenshare = false; - if (session.rpcs[UUID].screenIndexes && session.rpcs[UUID].screenIndexes.length){ - log("session.rpcs[UUID].screenIndexes: " + session.rpcs[UUID].screenIndexes); - var receievers = session.rpcs[UUID].getReceivers(); // excluded - for (var i=0;i{ - if ((trk.id == e1.track.id) && (trk.kind == e1.track.kind)){ - session.rpcs[UUID].streamSrc.removeTrack(trk); - } - }); - if ( e1.track.kind=="video"){ - updateIncomingVideoElement(UUID, true, false); - } else { - updateIncomingVideoElement(UUID, false, true); - } - // updateIncomingVideoElement(UUID); // session.rpcs[UUID].videoElement.srcObject = session.rpcs[UUID].streamSrc; - setTimeout(function(){updateMixer();},1); - } catch(e){} - }; - - newStream.onerror = function(e1){ - errorlog(e1); - try{ - warnlog("Track threw an error; going to reconnect it"); - session.rpcs[UUID].streamSrc.getTracks().forEach((trk)=>{ - try{ - if ((trk.id == e1.track.id) && (trk.kind == e1.track.kind)){ - session.rpcs[UUID].streamSrc.removeTrack(trk); - } - } catch(e){} - }); - if ( e1.track.kind=="video"){ - updateIncomingVideoElement(UUID, true, false); - } else { - updateIncomingVideoElement(UUID, false, true); - } - setTimeout(function(){updateMixer();},1); - } catch(e){errorlog(e);} - }; - } - - createRichVideoElement(UUID); - - if (!session.rpcs[UUID].streamSrc) { - session.rpcs[UUID].streamSrc = createMediaStream(); - mediaSourceUpdated(UUID, session.rpcs[UUID].streamID); - } - - - var videoAdded=false; - var audioAdded=false; - - newTracks.forEach((trk)=>{ - if (trk.kind=="video"){ - videoAdded=true; - } else if (trk.kind=="audio"){ - audioAdded=true; - } - log("adding track"); - session.rpcs[UUID].streamSrc.addTrack(trk); - }); - - if (newTracks.length > session.rpcs[UUID].streamSrc.getTracks().length){ - errorlog("Not all the tracks were added to the local stream; are the tracks' IDs not unique?"); - console.log("streamSrc total tracks: "+session.rpcs[UUID].streamSrc.getTracks().length); - } - - if (isIFrame && session.sendframes){ - newTracks.forEach((trk)=>{ - if (trk.kind==="video"){ - log("STARTING NEW VIDEO TRACK"); - - trk.frameReader = new MediaStreamTrackProcessor(trk).readable.getReader(); - trk.frameReader.read().then(function processFrame2({done, value}) { - if (done) { - if (value){ - value.close(); - } - return; - } - try { - parent.postMessage({"frame":value, UUID:UUID, streamID:session.rpcs[UUID].streamID, trackID: trk.id, kind: "video"}, session.sendframes, [value]); - } catch(e){ - value.close(); - return; - } - value.close(); - trk.frameReader.read().then(processFrame2); - - }); - } else if (trk.kind==="audio"){ - log("STARTING NEW AUDIO TRACK"); - - trk.frameReader = new MediaStreamTrackProcessor(trk).readable.getReader(); - trk.frameReader.read().then(function processFrameAudio2({done, value}) { - if (done) { - if (value){ - value.close(); - } - return; - } - try { - parent.postMessage({"frame":value, UUID:UUID, streamID:session.rpcs[UUID].streamID, trackID: trk.id, kind: "audio"}, session.sendframes, [new ArrayBuffer(value)]); - } catch(e){ - value.close(); - return; - } - value.close(); - trk.frameReader.read().then(processFrameAudio2); - - }); - } - }); - } - - - - if (audioAdded && videoAdded){ - updateIncomingVideoElement(UUID); - } else if (videoAdded){ - updateIncomingVideoElement(UUID, true, false); - } else if (audioAdded){ - try { - if (session.audioCodec == "lyra"){ // not supported currently - lyraDecode(event.receiver); - } - } catch(e){errorlog(e);} - updateIncomingVideoElement(UUID, false, true); - if (!session.roomid && session.view && !session.permaid){ - setTimeout(function(){updateMixer();},10); // video already has an auto-start, with aspect ratio size change. audio doesn't. - } - } - - return session; - -}; - - -function updateIncomingVideoElement(UUID, video=true, audio=true){ - - if (!session.rpcs[UUID].videoElement){ - return;} - if (!session.rpcs[UUID].streamSrc){ - return;} - - if (!session.rpcs[UUID].videoElement.srcObject) { - session.rpcs[UUID].videoElement.srcObject = createMediaStream(); - } - - if (video){ - var tracks = session.rpcs[UUID].videoElement.srcObject.getVideoTracks(); // add video track - - session.rpcs[UUID].streamSrc.getVideoTracks().forEach((trk)=>{ - var added = false; - tracks.forEach(trk2 =>{ - if ((trk.id == trk2.id) && (trk.kind == trk2.kind)){ - added=true; - } - }); - if (!added){ - session.rpcs[UUID].videoElement.srcObject.getVideoTracks().forEach((trk2)=>{ // make sure only one video track is added at a time. - session.rpcs[UUID].videoElement.srcObject.removeTrack(trk2); - }); - - if (trk.muted && (trk.kind=="video") && session.director){ - trk.onunmute = function(e){ - if (!session.rpcs[UUID]){return;} - this.onunmute = null; - warnlog("ON UN-MUTE"); - updateIncomingVideoElement(UUID, true, false); - }; - } else { - if (session.rpcs[UUID].videoElement.controls){ - session.rpcs[UUID].videoElement.controls = session.showControls || false; - if (session.showControls===null){ - setTimeout(function(ele){ - if (ele){ - ele.controls = true; - } - },500, session.rpcs[UUID].videoElement); - } - } - session.rpcs[UUID].videoElement.srcObject.addTrack(trk); - mediaVideoTrackUpdated(UUID, session.rpcs[UUID].streamID); - } - } - }); - - if (session.motionSwitch && !session.rpcs[UUID].motionDetectionInterval){ - session.rpcs[UUID].motionDetectionInterval = setTimeout(function(){ - setInterval(function(){ - motionDetection(session.rpcs[UUID].videoElement, session.motionSwitch); - },400); - },2000); - } - - } - if (audio){ - updateIncomingAudioElement(UUID) // do the same for audio now. - } -} - -function updateIncomingAudioElement(UUID){ // this can be called when turning on/off inbound audio processing. - if (!session.rpcs[UUID] || !session.rpcs[UUID].videoElement || !session.rpcs[UUID].streamSrc){return;} - - if (!session.rpcs[UUID].videoElement.srcObject) { - session.rpcs[UUID].videoElement.srcObject = createMediaStream(); - } - - log("updateIncomingAudioElement: "+UUID); - if ((session.audioEffects===true) || session.pushLoudness){ - var tracks = session.rpcs[UUID].streamSrc.getAudioTracks(); - if (tracks.length){ - var track = tracks[0]; - track = addAudioPipeline(UUID, track); - log(track); - var added = false; - var tracks2 = session.rpcs[UUID].videoElement.srcObject.getAudioTracks(); - log(tracks2); - tracks2.forEach(trk2 =>{ - if (trk2.label && (trk2.label == "MediaStreamAudioDestinationNode")){ // an old morphed node; delete it. - session.rpcs[UUID].videoElement.srcObject.removeTrack(trk2); - } else if ((track.id == trk2.id) && (track.kind == trk2.kind)){ // maybe it didn't morph; already added either way - added = true; - } else if ((tracks[0].id == trk2.id) && (tracks[0].kind == trk2.kind) && (track.id != tracks[0].id)){ // remove original audio track that is now morphed - session.rpcs[UUID].videoElement.srcObject.removeTrack(trk2); - } - }); - if (!added){ - session.rpcs[UUID].videoElement.srcObject.addTrack(track); - mediaAudioTrackUpdated(UUID, session.rpcs[UUID].streamID); - } - - } else { - session.rpcs[UUID].videoElement.srcObject.getAudioTracks().forEach(trk=>{ // make sure to remove all tracks. - session.rpcs[UUID].videoElement.srcObject.remove(trk); - }); - } - } else { - var expected = []; - tracks = session.rpcs[UUID].videoElement.srcObject.getAudioTracks(); // add audio tracks - session.rpcs[UUID].streamSrc.getAudioTracks().forEach((trk)=>{ - var added = false; - tracks.forEach(trk2 =>{ - if ((trk.id == trk2.id) && (trk.kind == trk2.kind)){ - added=true; - expected.push(trk2); // - } - }); - if (!added){ - session.rpcs[UUID].videoElement.srcObject.addTrack(trk); - mediaAudioTrackUpdated(UUID, session.rpcs[UUID].streamID); - } - }); - tracks.forEach((trk)=>{ - var added = false; - expected.forEach((trk2)=>{ - if ((trk.id == trk2.id) && (trk.kind == trk2.kind)){ - added=true; - } - }); - if (!added){ // not expected. so lets delete. - warnlog("this shouldn't happen that often, audio track orphaned. removing it"); - session.rpcs[UUID].videoElement.srcObject.removeTrack(trk); - } - }); - } - - - if (session.mixMinus){ - stream = mixMinusAudio(UUID); // only works with p2p; no chunked mode. - } - -} - - -function cycleStyleOptions(){ - session.style +=1; - if (session.style >6 ){ - session.style = 1; - } else if (session.style == 4 ){ - session.style = 5; - } - - for (var UUID in session.rpcs){ - if (session.rpcs[UUID].canvas){ - try{ - if (session.rpcs[UUID].canvas){ - session.rpcs[UUID].canvas.remove(); - } - } catch(e){} - session.rpcs[UUID].canvas = null; - } - updateIncomingAudioElement(UUID); - } - updateMixer(); -} - -function addAudioPipeline(UUID, track){ // INBOUND AUDIO EFFECTS ; audio tracks only - try{ - - if (session.disableViewerWebAudioPipeline){ - log("ignoring addAudioPipeline - disableViewerWebAudioPipeline is enabled (noap)"); - return track; - } - - log("Triggered webaudio effects path"); - - for (var tid in session.rpcs[UUID].inboundAudioPipeline){ - delete session.rpcs[UUID].inboundAudioPipeline[tid]; // get rid of old nodes. - } - var trackid = track.id; // this is an audio track, or should be. - - session.rpcs[UUID].inboundAudioPipeline[trackid] = {}; - - session.rpcs[UUID].inboundAudioPipeline[trackid].mediaStream = createMediaStream(); - session.rpcs[UUID].inboundAudioPipeline[trackid].mediaStream.addTrack(track); - - if (ChromiumVersion && session.audioEffects){ // I'm going to deprecate this. - session.rpcs[UUID].inboundAudioPipeline[trackid].mutedAudio = createAudioElement(); // TODO: I don't know if this mutedAudio thing matters any more, in recent versions of Chrome, since it won't play even if muted. - session.rpcs[UUID].inboundAudioPipeline[trackid].mutedAudio.muted = true; - session.rpcs[UUID].inboundAudioPipeline[trackid].mutedAudio.playsinline = true; // ## Added Oct 9th 2022. Not sure it's does anything, but might help with iPhones? - session.rpcs[UUID].inboundAudioPipeline[trackid].mutedAudio.srcObject = session.rpcs[UUID].inboundAudioPipeline[trackid].mediaStream; // needs to be added as an streamed element to be usable, even if its hidden - session.rpcs[UUID].inboundAudioPipeline[trackid].mutedAudio.muted = true; - //session.rpcs[UUID].inboundAudioPipeline[trackid].mutedAudio.volume = 0.01; - session.rpcs[UUID].inboundAudioPipeline[trackid].mutedAudio.play().then(_ => { - //session.rpcs[UUID].inboundAudioPipeline[trackid].mutedAudio.muted = false; - log("playing 1"); - }).catch(warnlog); - } - - // https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/createMediaStreamTrackSource - var source = session.audioCtx.createMediaStreamSource(session.rpcs[UUID].inboundAudioPipeline[trackid].mediaStream); - - ////////////////// - - var screwedUp = false; - session.rpcs[UUID].inboundAudioPipeline[trackid].destination = false; - - if (session.sync!==false){ - log("adding a delay node to audio"); - source = addDelayNode( source, UUID, trackid); - screwedUp = true; - } - if (session.style===2){ - log("adding a fftwave node to audio"); - try { - if (session.rpcs[UUID].inboundAudioPipeline[trackid]){ // clear audioMeterGuest, if active. - clearTimeout(session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.interval); - } - } catch(e){ } - source = fftWaveform( source, UUID, trackid); - } else if (session.style===3 || session.meterStyle){ - log("adding a loudness meter node to audio"); - source = audioMeterGuest(source, UUID, trackid); - } else if (session.audioMeterGuest){ - log("adding a loudness meter node to audio"); - source = audioMeterGuest(source, UUID, trackid); - } else if (session.activeSpeaker){ - log("adding a loudness meter node to audio"); - source = audioMeterGuest(source, UUID, trackid); - } else if (session.quietOthers){ - log("adding a loudness meter node to audio"); - source = audioMeterGuest(source, UUID, trackid); - } else if (session.pushLoudness){ - source = audioMeterGuest(source, UUID, trackid); - } else { - try { - if (session.rpcs[UUID].inboundAudioPipeline[trackid]){ // nothign active, so clear - clearTimeout(session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.interval); - } - } catch(e){ } - } - - if (session.playChannel){ - session.rpcs[UUID].inboundAudioPipeline[trackid].destination = session.audioCtx.createMediaStreamDestination(); - source = selectChannel( session.rpcs[UUID].inboundAudioPipeline[trackid].destination, source, session.playChannel); - screwedUp = true; - } else if (session.rpcs[UUID].channelOffset !== false){ - log("custom offset set"); - session.rpcs[UUID].inboundAudioPipeline[trackid].destination = session.audioCtx.createMediaStreamDestination(); - source = offsetChannel( session.rpcs[UUID].inboundAudioPipeline[trackid].destination, source, session.rpcs[UUID].channelOffset, session.rpcs[UUID].channelWidth); - screwedUp = true; - } else if (session.offsetChannel !== false){ // proably better to do this last. - log("adding offset channels"); - session.rpcs[UUID].inboundAudioPipeline[trackid].destination = session.audioCtx.createMediaStreamDestination(); - source = offsetChannel( session.rpcs[UUID].inboundAudioPipeline[trackid].destination, source, session.offsetChannel, session.channelWidth); - screwedUp = true; - } else if (session.panning !== false){ // proably better to do this last. - log("adding offset channels"); - session.rpcs[UUID].inboundAudioPipeline[trackid].destination = session.audioCtx.createMediaStreamDestination(); - source = stereoPanning( source, UUID, trackid, session.panning); - screwedUp = true; - } else if (session.rpcs[UUID].videoElement && session.rpcs[UUID].videoElement.manualSink){ - screwedUp = true; // added June-3-22 to allow for custom outputs to different audio output destinations. - } - - if (screwedUp){ - warnlog("screwedUp mode activated. dun dun"); - if (session.rpcs[UUID].inboundAudioPipeline[trackid].destination===false){ - session.rpcs[UUID].inboundAudioPipeline[trackid].destination = session.audioCtx.createMediaStreamDestination(); - } - source.connect(session.rpcs[UUID].inboundAudioPipeline[trackid].destination); - - try { - if (session.firstPlayTriggered && (session.audioCtx.state == "suspended")){ - log("trying to resume.."); - session.audioCtx.resume(); - } - } catch(e){warnlog("session.audioCtx.resume(); failed");} - - return session.rpcs[UUID].inboundAudioPipeline[trackid].destination.stream.getAudioTracks()[0]; - } - - try { - if (session.firstPlayTriggered && (session.audioCtx.state == "suspended")){ - session.audioCtx.resume(); - } - } catch(e){warnlog("session.audioCtx.resume(); failed 2");} - - return track; - } catch(e) {errorlog(e);} - return track; -} - -function processMiniInfoUpdate(miniInfo, UUID){ - if ("qlr" in miniInfo){ - session.rpcs[UUID].stats.info.quality_limitation_reason = miniInfo.qlr; - } - - if ("con" in miniInfo){ - session.rpcs[UUID].stats.info.conn_type = miniInfo.con; - } - - if ("cpu" in miniInfo){ - session.rpcs[UUID].stats.info.cpuLimited = miniInfo.cpu; - if (session.rpcs[UUID].signalMeter){ - if (miniInfo.cpu){ - session.rpcs[UUID].signalMeter.dataset.cpu = "1"; - } else if ("cpu" in miniInfo){ - session.rpcs[UUID].signalMeter.dataset.cpu = "0"; - } - } - } - - if ("hw_enc" in miniInfo){ - session.rpcs[UUID].stats.info.hardware_video_encoder = miniInfo.hw_enc; - } - - if ("bat" in miniInfo){ - if (typeof miniInfo.bat == "number"){ - session.rpcs[UUID].stats.info.power_level = miniInfo.bat*100; - } else { - session.rpcs[UUID].stats.info.power_level = null; - } - } - if ("chrg" in miniInfo){ - session.rpcs[UUID].stats.info.plugged_in = miniInfo.chrg; - } - - if (("out" in miniInfo) && ("c" in miniInfo.out)){ - session.rpcs[UUID].stats.info.total_outbound_p2p_connections = miniInfo.out.c; - if (session.showConnections && session.rpcs[UUID].connectionDetails){ - session.rpcs[UUID].connectionDetails.innerText = "🔗"+session.rpcs[UUID].stats.info.total_outbound_p2p_connections; - session.rpcs[UUID].connectionDetails.dataset.value = session.rpcs[UUID].stats.info.total_outbound_p2p_connections; - } - } - - if (session.rpcs[UUID].batteryMeter){ - batteryMeterInfoUpdate(UUID); - } -} - - -function batteryMeterInfoUpdate(UUID){ - if (session.rpcs[UUID].stats.info && (session.rpcs[UUID].stats.info.power_level!==null)){ - var level = session.rpcs[UUID].batteryMeter.querySelector(".battery-level"); - if (level){ - var value = session.rpcs[UUID].stats.info.power_level; - if (value > 100){value = 100;} - if (value < 0){ value = 0;} - level.style.height = parseInt(value)+"%"; - if (value<15){ - session.rpcs[UUID].batteryMeter.classList.remove("warn"); - session.rpcs[UUID].batteryMeter.classList.add("alert"); - } else if (value<25){ - session.rpcs[UUID].batteryMeter.classList.remove("alert"); - session.rpcs[UUID].batteryMeter.classList.add("warn"); - } else { - session.rpcs[UUID].batteryMeter.classList.remove("alert"); - session.rpcs[UUID].batteryMeter.classList.remove("warn"); - } - if (value<100){ - session.rpcs[UUID].batteryMeter.classList.remove("hidden"); - } - //session.rpcs[UUID].batteryMeter.title = value+"% battery remaining"; - session.rpcs[UUID].batteryMeter.title = parseInt(value)+"% battery remaining"; - } - } - - if (session.rpcs[UUID].stats.info && ("plugged_in" in session.rpcs[UUID].stats.info) && (session.rpcs[UUID].stats.info.plugged_in===false)){ - session.rpcs[UUID].batteryMeter.dataset.plugged = "0"; - session.rpcs[UUID].batteryMeter.classList.remove("hidden"); - } else { - session.rpcs[UUID].batteryMeter.dataset.plugged = "1"; - // add on - session.rpcs[UUID].batteryMeter.title = parseInt(value)+"% charging"; - session.rpcs[UUID].batteryMeter.classList.add("hidden"); - } -} - -function setupGuestLabelControl(UUID){ - var labelID = getById("label_"+UUID); - if (labelID){ - labelID.classList.add("contolboxLabel"); - labelID.dataset.UUID = UUID; - if (session.rpcs[UUID].label){ - labelID.innerText = session.rpcs[UUID].label; // Replace underscores with a Space when publishing to HTML. No Double spaces. - labelID.classList.remove("addALabel"); - } else if (session.directorUUID === UUID){ - miniTranslate(labelID,"main-director"); - //labelID.innerHTML = getTranslation("main-director"); - labelID.classList.remove("addALabel"); - } else { - miniTranslate(labelID,"add-a-label"); - //labelID.innerHTML = getTranslation("add-a-label"); // Replace underscores with a Space when publishing to HTML. No Double spaces. - labelID.classList.add("addALabel"); - } - labelID.onclick = async function(ee){ - var oldlabel = ee.target.innerText; - if (session.rpcs[ee.target.dataset.UUID].label===false){ - oldlabel = ""; - } - window.focus(); - var newlabel = await promptAlt(getTranslation("new-display-name"), false, false, oldlabel); - if (newlabel!==null){ - newlabel = newlabel.trim(); - if (newlabel == ""){ - newlabel = false; - if (session.directorUUID === UUID){ - miniTranslate(ee.target,"main-director"); - //ee.target.innerHTML = getTranslation("main-director"); - ee.target.classList.remove("addALabel"); - } else { - miniTranslate(ee.target,"add-a-label"); - //ee.target.innerHTML = getTranslation("add-a-label"); - ee.target.classList.add("addALabel"); - } - } else { - ee.target.innerText = newlabel; - ee.target.classList.remove("addALabel"); - } - var data = {}; - data.UUID = ee.target.dataset.UUID; - data.changeLabel = true; - data.value = newlabel; - session.sendRequest(data, data.UUID); - } - } - } -} - -function updateLabelDirectors(UUID){ - var elements = getById("label_"+UUID); - if (session.rpcs[UUID].label){ - elements.innerText = session.rpcs[UUID].label; - elements.classList.remove("addALabel"); - } else if (session.directorUUID === UUID){ - //elements.innerHTML = getTranslation("main-director"); - miniTranslate(elements,"main-director"); - elements.classList.remove("addALabel"); - } else { - //elements.innerHTML = getTranslation("add-a-label"); - miniTranslate(elements,"add-a-label"); - elements.classList.add("addALabel"); - } -} -function updateLabelDirectors2(UUID){ - var elements = getById("label_"+UUID); - if (session.directorUUID === UUID){ - //elements.innerHTML = getTranslation("main-director"); - miniTranslate(elements,"main-director"); - elements.classList.remove("addALabel"); - } else { - //elements.innerHTML = getTranslation("add-a-label"); - miniTranslate(elements,"add-a-label"); - elements.classList.add("addALabel"); - } -} - -function directorCoDirectorColoring(UUID){ - if (UUID === session.directorUUID){ - try{ - session.rpcs[UUID].stats.info.director = true; - getById("container_"+UUID).classList.add("directorBox"); - } catch(e){} - } else if (session.directorList.indexOf(UUID)>=0){ - try{ - session.rpcs[UUID].stats.info.coDirector = true; - addDirectorBlue(UUID); - - } catch(e){} - } -} - -function addDirectorBlue(UUID){ - getById("container_"+UUID).classList.add("directorBlue"); -} - -function soloLinkGeneratorInit(UUID){ - document.querySelectorAll("container_"+UUID).forEach(ele=>{ - ele.querySelectorAll("[data-sololink]").forEach(ele2=>{ // value='" + soloLink + "' href='" + soloLink + "'/>" + soloLink + " - var soloLink = soloLinkGenerator(session.rpcs[UUID].streamID, false); - ele2.value = soloLink; - ele2.href = soloLink; - ele2.innerText = soloLink; - }); - }); -} - -function initRecordingImpossible(UUID){ - var ele = document.querySelectorAll('[data-action-type="mute-guest"][data--u-u-i-d="'+UUID+'"]'); - if (ele){ - ele.disabled = true; - ele.title = getTranslation("Audio processing is disabled with this guest. Can't mute or change volume"); - } - var ele = document.querySelectorAll('[data-action-type="volume"][data--u-u-i-d="'+UUID+'"]'); - if (ele){ - ele.disabled = true; - ele.title = title = getTranslation("Audio processing is disabled with this guest. Can't mute or change volume"); - ele.style.opacity = 0.2; - } -} - -function initAudioButtons(audioGain, UUID){ - if (audioGain===0){ - var ele = document.querySelector('[data-action-type="mute-guest"][data--u-u-i-d="'+UUID+'"]'); - if (ele){ - ele.value = 1; - ele.classList.add("pressed"); ele.ariaPressed = "true"; - miniTranslate(ele.children[1],"unmute"); - session.rpcs[UUID].directorMutedState = 1; - } - pokeIframeAPI("director-mute-state", true, UUID); - } else { - var ele = document.querySelector('[data-action-type="volume"][data--u-u-i-d="'+UUID+'"]'); - if (ele){ - ele.value = audioGain; - session.rpcs[UUID].directorVolumeState = audioGain; - remoteVolumeUI(ele); - } - } -} - -function initGroupButtons(UUID){ - var elements = document.querySelectorAll('[data-action-type="toggle-group"][data--u-u-i-d="'+UUID+'"]'); - for (var i=0;i -1){ - session.group.splice(index, 1); - change=true; - } - } else if (ele.classList.contains("pressed")){ - ele.classList.remove("pressed"); ele.ariaPressed = "false"; - if (index > -1){ - session.group.splice(index, 1); - change=true; - } - } else { - ele.classList.add("pressed"); ele.ariaPressed = "true"; - if (index === -1){ - session.group.push(group); - change=true; - } - } - - if (session.group.length || session.allowNoGroup){ - session.sendMessage({"group":session.group.join(",")}); - } else { - session.sendMessage({"group":false}); - } - if (change){ - pokeIframeAPI("group-set-updated", session.group); - } - - if (session.group.indexOf(group)===-1){ - return false; - } else { - return true; - } -} - -function changeGroupDirectorAPI(group, state=null, update=true){ - log("changeGroupDirectorAPI()"); - group = sanitizeLabel(group); - - if (document.getElementById("container_director")){ - var ele = getById("container_director").querySelector('[data-action-type="toggle-group"][data-group="'+group+'"]'); - if (ele){ - if (update){ - ele.click(); - } else if (state===true){ - ele.classList.add("pressed"); ele.ariaPressed = "true"; - } - if (session.group.indexOf(group)===-1){ - return false; - } else { - return true; - } - } - } - - var index = session.group.indexOf(group); - - var eleGroup = getById("groups"); - eleGroup.classList.remove("hidden"); - var ele = eleGroup.querySelector('[data-action-type="toggle-group"][data-group="'+group+'"'); - - if (eleGroup.showDirector){ - if (!ele){ - ele = htmlToElement(''); - - var added = false; - eleGroup.querySelectorAll('[data-group]').forEach(ele2=>{ - log(ele2); - if (!added && ele2.dataset.group>group+""){ - ele2.parentNode.insertBefore(ele, ele2); - added = true; - } - }); - if (!added){ - eleGroup.appendChild(ele); - } - } - } else if (!ele){ - ele = document.createElement("div"); - ele.dataset.actionType = "toggle-group"; - ele.dataset.group = group; - ele.classList.add('float'); - ele.style.display = "inline-block"; - ele.role = "button"; - ele.innerHTML = '
    '+group; - eleGroup.appendChild(ele); - ele.onclick = function(){ - changeGroupDirectorAPI(this.dataset.group); - } - } - var changed = false; - - if (state===true){ - if (eleGroup.showDirector){ - ele.classList.add("pressed"); ele.ariaPressed = "true"; - } else { - ele.classList.add("green"); ele.ariaPressed = "true"; - } - if (index === -1){ - session.group.push(group); - changed=true; - } - } else if (state === false){ - if (eleGroup.showDirector){ - ele.classList.remove("pressed"); ele.ariaPressed = "false"; - } else { - ele.classList.remove("green"); ele.ariaPressed = "false"; - } - if (index > -1){ - session.group.splice(index, 1); - changed=true; - } - } else if (ele.classList.contains("green")){ - if (eleGroup.showDirector){ - ele.classList.remove("pressed"); ele.ariaPressed = "false"; - } else { - ele.classList.remove("green"); ele.ariaPressed = "false"; - } - if (index > -1){ - session.group.splice(index, 1); - changed=true; - } - } else { - if (eleGroup.showDirector){ - ele.classList.add("pressed"); ele.ariaPressed = "true"; - } else { - ele.classList.add("green"); ele.ariaPressed = "true"; - } - if (index === -1){ - session.group.push(group); - changed=true; - } - } - if (update){ - if (session.group.length || session.allowNoGroup){ - session.sendMessage({"group":session.group.join(",")}); - } else { - session.sendMessage({"group":false}); - } - } - if (changed){ - pokeIframeAPI("group-set-updated", session.group); - } - - if (state!==null){ - return true; - } else if (session.group.indexOf(group)===-1){ - return false; - } else { - return true; - } -} - -function changeGroupViewDirectorAPI(group, state=null){ - log("changeGroupViewDirectorAPI()"); - group = sanitizeLabel(group); - - var index = session.groupView.indexOf(group); - - var changed = false; - - if (state===true){ - if (index === -1){ - session.groupView.push(group); - changed=true; - } - } else if (state === false){ - if (index > -1){ - session.groupView.splice(index, 1); - changed=true; - } - } else { - if (index > -1){ - session.groupView.splice(index, 1); - } else { - session.groupView.push(group); - } - changed=true; - } - - - if (changed){ - pokeIframeAPI("group-view-set-updated", session.groupView); - } - - if (state!==null){ - return true; - } else if (session.groupView.indexOf(group)===-1){ - return false; - } else { - return true; - } -} - - -function changeGroup(ele, state=null){ - - var group = ele.dataset.group; - - var index = session.rpcs[ele.dataset.UUID].group.indexOf(group); - - if (state===true){ - ele.classList.add("pressed"); ele.ariaPressed = "true"; - if (index === -1){ - session.rpcs[ele.dataset.UUID].group.push(group); - } - } else if (state === false){ - ele.classList.remove("pressed"); ele.ariaPressed = "false"; - if (index > -1){ - session.rpcs[ele.dataset.UUID].group.splice(index, 1); - } - } else if (ele.classList.contains("pressed")){ - ele.classList.remove("pressed"); ele.ariaPressed = "false"; - if (index > -1){ - session.rpcs[ele.dataset.UUID].group.splice(index, 1); - } - } else { - ele.classList.add("pressed"); ele.ariaPressed = "true"; - if (index === -1){ - session.rpcs[ele.dataset.UUID].group.push(group); - } - } - if (session.rpcs[ele.dataset.UUID].group.length){ - session.sendRequest({"group":session.rpcs[ele.dataset.UUID].group.join(",")}, ele.dataset.UUID); - } else { - session.sendRequest({"group":false}, ele.dataset.UUID); - } - syncDirectorState(ele); - - if (session.rpcs[ele.dataset.UUID].group.indexOf(group)===-1){ - return false; - } else { - return true; - } -} - -function changeChannelOffset(UUID, channel){ - var ele = document.querySelectorAll('[data-action-type="add-channel"][data--u-u-i-d="' + UUID + '"]'); - for (var i=0;i1){value=1;} - } - //// some reverb logic goes here... - ///var reverbNode = session.audioCtx.createStereoPanner(); - ///session.rpcs[UUID].inboundAudioPipeline[trackid].reverbNode = reverbNode; - //// - - source.connect(reverbNode); - return reverbNode; -} - -function stereoPanning(source, UUID, trackid, value){ - if (parseInt(value) === -1){ - value = Math.random() * (Math.random()*2-1); - warnlog(value); - } else if (value === false){ - return source; - } else if (value === true){ - value = 90; - } else { - value = parseFloat(value/90) -1 || 0; - if (value<-1){value=-1;} - if (value>1){value=1;} - } - - var gainNode = session.audioCtx.createGain(); - session.rpcs[UUID].inboundAudioPipeline[trackid].gainPanNode = gainNode; - gainNode.value = (1-Math.abs(value)/2); // the stereo panner seems to make things extra loud, so they clip. REDUCE IT. - source.connect(gainNode); - - var panNode = session.audioCtx.createStereoPanner(); - session.rpcs[UUID].inboundAudioPipeline[trackid].panNode = panNode; - panNode.pan.value = value; - gainNode.connect(panNode); - return panNode; -} - -function adjustPan(UUID, value){ - - if (value === true){ - value = Math.random() * (Math.random()*2-1); - } else if (value === false){ - value=0; - } else { - value = parseFloat(value/90) -1 || 0; - if (value<-1){value=-1;} - if (value>1){value=1;} - } - - for (var trackid in session.rpcs[UUID].inboundAudioPipeline){ - if ("panNode" in session.rpcs[UUID].inboundAudioPipeline[trackid]){ - session.rpcs[UUID].inboundAudioPipeline[trackid].panNode.pan.setValueAtTime(value, session.audioCtx.currentTime); - } - if ("gainPanNode" in session.rpcs[UUID].inboundAudioPipeline[trackid]){ - session.rpcs[UUID].inboundAudioPipeline[trackid].gainPanNode.setValueAtTime((1-Math.abs(value)/2), session.audioCtx.currentTime); - } - } -} - -function addDelayNode(source, UUID, trackid){ // append the delay Node to the track??? WOULD THIS WORK? - - var delay = parseFloat(session.sync) || 0; - if (delay<0){delay=0;} - - if (session.buffer && (session.buffer>0)){ - delay += parseFloat(session.buffer); - } - - delay = delay/1000; - - session.rpcs[UUID].inboundAudioPipeline[trackid].delayNode = session.audioCtx.createDelay(delay+5);// 5 seconds additionally added for the purpose of flexibility - - session.rpcs[UUID].inboundAudioPipeline[trackid].delayNode.delayTime.value = delay; // delayTime takes it in seconds. - source.connect(session.rpcs[UUID].inboundAudioPipeline[trackid].delayNode); - log("added new delay node"); - return session.rpcs[UUID].inboundAudioPipeline[trackid].delayNode; -} - - -function createStyleCanvas(UUID){ // append the delay Node to the track??? WOULD THIS WORK? - if (!session.rpcs[UUID].canvas){ // just make sure that if using &effects or something, to null the canvas after use, else this won't trigger. - session.rpcs[UUID].canvas = document.createElement("canvas"); - session.rpcs[UUID].canvas.dataset.UUID = UUID - if (session.rpcs[UUID].streamID){ - session.rpcs[UUID].canvas.dataset.sid = session.rpcs[UUID].streamID; - } - session.rpcs[UUID].canvas.style.pointerEvents = "auto"; - session.rpcs[UUID].canvasCtx = session.rpcs[UUID].canvas.getContext('2d', { alpha: session.alpha }); - // - session.rpcs[UUID].canvas.addEventListener('click', function(e) { // show stats of video if double clicked - log("clicked"); - try { - var uid = e.currentTarget.dataset.UUID; - if ((e.ctrlKey)||(e.metaKey)){ - e.preventDefault(); - if (session.statsMenu !==false){ - if ("stats" in session.rpcs[uid]){ - var [menu, innerMenu] = statsMenuCreator(); - printViewStats(innerMenu, uid ); - menu.interval = setInterval(printViewStats, session.statsInterval, innerMenu, uid); - } - } - e.stopPropagation(); - return false; - } else if ("prePausedBandwidth" in session.rpcs[uid]){ - unPauseVideo(e.currentTarget); - } - - } catch(e){errorlog(e);} - }); - - if (session.statsMenu){ - if ("stats" in session.rpcs[UUID]){ - var [menu, innerMenu] = statsMenuCreator(); - printViewStats(innerMenu, UUID ); - menu.interval = setInterval(printViewStats, session.statsInterval, innerMenu, UUID); - } - } - - if (session.aspectRatio){ - if (session.aspectRatio==1){ - session.rpcs[UUID].canvas.width="720"; - session.rpcs[UUID].canvas.height="1280"; - } else if (session.aspectRatio==2){ - session.rpcs[UUID].canvas.width="960"; - session.rpcs[UUID].canvas.height="960"; - } else if (session.aspectRatio==3){ - session.rpcs[UUID].canvas.width="1280"; - session.rpcs[UUID].canvas.height="960"; - } - } else { - session.rpcs[UUID].canvas.width="1280"; - session.rpcs[UUID].canvas.height="720"; - } - - updateMixer(); - return true; - } else { - return false; - } -} - - -function applyStyleEffect(UUID){ - if (!session.rpcs[UUID].canvas || !session.rpcs[UUID].canvasCtx){return;} - - /* session.rpcs[UUID].canvasContainer = document.createElement("div"); - session.rpcs[UUID].canvasContainer.appendChild(session.rpcs[UUID].canvas); - session.rpcs[UUID].canvas.style = "width:100%;height:100%;display:block;"; - session.rpcs[UUID].canvasContainer.appendChild(session.rpcs[UUID].videoElement); */ - - if (session.style==3){ // black - session.rpcs[UUID].canvasCtx.fillStyle = "rgb(0, 0, 0)"; - session.rpcs[UUID].canvasCtx.fillRect(0, 0, session.rpcs[UUID].canvas.width, session.rpcs[UUID].canvas.height); - } else if (session.style==4){ - session.rpcs[UUID].canvasCtx.fillStyle = "rgb(0, 0, 0)"; - session.rpcs[UUID].canvasCtx.fillRect(0, 0, session.rpcs[UUID].canvas.width, session.rpcs[UUID].canvas.height); - } else if (session.style==5){ - var r = Math.random()*255; - var g = Math.random()*255; - var b = Math.random()*255; - session.rpcs[UUID].canvasCtx.fillStyle = "rgb("+r+", "+g+", "+b+")"; - session.rpcs[UUID].canvasCtx.fillRect(0, 0, session.rpcs[UUID].canvas.width, session.rpcs[UUID].canvas.height); - } else if (session.style==6){ - - session.rpcs[UUID].canvasCtx.fillStyle = "rgb(0,0,0)"; - session.rpcs[UUID].canvasCtx.fillRect(0, 0, session.rpcs[UUID].canvas.width, session.rpcs[UUID].canvas.height); - - var r = Math.random()*150+50; - var g = Math.random()*150+50; - var b = Math.random()*150+50; - session.rpcs[UUID].canvasCtx.fillStyle = "rgb("+r+", "+g+", "+b+")"; - session.rpcs[UUID].canvasCtx.beginPath(); - session.rpcs[UUID].canvasCtx.arc(parseInt(session.rpcs[UUID].canvas.width/2), parseInt(session.rpcs[UUID].canvas.height/2), parseInt(session.rpcs[UUID].canvas.height/4), 0, 2 * Math.PI, false); - session.rpcs[UUID].canvasCtx.fill(); - - if (session.rpcs[UUID].label){ - session.rpcs[UUID].canvasCtx.fillStyle = "rgb(0,0,0)"; - session.rpcs[UUID].canvasCtx.textAlign = "center"; - session.rpcs[UUID].canvasCtx.font = parseInt(session.rpcs[UUID].canvas.height/2.11)+"px Arial"; - session.rpcs[UUID].canvasCtx.fillText(session.rpcs[UUID].label[0].toUpperCase(), parseInt(session.rpcs[UUID].canvas.width/2), parseInt(session.rpcs[UUID].canvas.height*2/3)); - } else { - var tmp = getComputedStyle(document.querySelector(':root')).getPropertyValue('--video-background-image').split('"'); - if (tmp.length===3){ - var img = new Image(); - img.onload = function() { - session.rpcs[UUID].canvasCtx.fillStyle = "rgb(25,0,0)"; - session.rpcs[UUID].canvasCtx.drawImage(img, parseInt(session.rpcs[UUID].canvas.width/2-110), parseInt(session.rpcs[UUID].canvas.height/2-110),220,220); - } - img.src = tmp[1]; - } - } - } -} - -function capitalizeFirstLetter(string) { - return string.charAt(0).toUpperCase() + string.slice(1); -} - -function fftWaveform( source, UUID, trackid){ // append the delay Node to the track??? WOULD THIS WORK? - // https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode - session.rpcs[UUID].inboundAudioPipeline[trackid].analyser = session.audioCtx.createAnalyser(); - session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.fftSize = 512; - var bufferLength = session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.frequencyBinCount; - var dataArray = new Uint8Array(bufferLength); - session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.getByteTimeDomainData(dataArray); - // analyser.getByteTimeDomainData(dataArray); - source.connect(session.rpcs[UUID].inboundAudioPipeline[trackid].analyser); - - createStyleCanvas(UUID); - clearInterval(session.rpcs[UUID].canvasIntervalAction); - var canvasIntervalAction = setInterval(function(uuid){ - - if (session.style!==2){ - clearInterval(canvasIntervalAction); // this is FFT only, so okay to kill. - return; - } - - try{ - session.rpcs[uuid].inboundAudioPipeline[trackid].analyser.getByteTimeDomainData(dataArray); - session.rpcs[uuid].canvasCtx.fillStyle = "rgba(0, 0, 0, 0.1)"; - session.rpcs[uuid].canvasCtx.fillRect(0, 0, session.rpcs[uuid].canvas.width, session.rpcs[uuid].canvas.height); - session.rpcs[uuid].canvasCtx.lineWidth = 10; - session.rpcs[uuid].canvasCtx.strokeStyle = "rgb(111, 255, 111)"; - - var sliceWidth = session.rpcs[uuid].canvas.width * 1.0 / bufferLength; - - var loudness = dataArray; - var Squares = loudness.map((val) => ((val-128.0)*(val-128.0))); - var Sum = Squares.reduce((acum, val) => (acum + val)); - var Mean = Sum/loudness.length; - loudness = Math.sqrt(Mean)*10; - session.rpcs[uuid].stats.Audio_Loudness = parseInt(loudness); - - if (session.pushLoudness==true){ - var loudnessObj = {}; - loudnessObj[session.rpcs[uuid].streamID] = session.rpcs[uuid].stats.Audio_Loudness; - - if (isIFrame){ - parent.postMessage({"loudness": loudnessObj, "action":"loudness", "value":loudness, "UUID":uuid}, session.iframetarget); - } - } - - if (loudness<2){return;} - - //log(bufferLength); - session.rpcs[uuid].canvasCtx.beginPath(); - var m = session.rpcs[uuid].canvas.height / 256.0; - session.rpcs[uuid].canvasCtx.moveTo(0, dataArray[0]*m); - var x = 0; - for (var i = 1; i < bufferLength; i++){ - var y = dataArray[i] * m; - session.rpcs[uuid].canvasCtx.lineTo(x, y); - x += sliceWidth; - } - session.rpcs[uuid].canvasCtx.lineTo(session.rpcs[uuid].canvas.width, session.rpcs[uuid].canvas.height / 2); - session.rpcs[uuid].canvasCtx.stroke(); - } catch(e){ - warnlog(e); - warnlog("Did the remote source disconnect?"); - clearInterval(canvasIntervalAction); - warnlog(session.rpcs[uuid]); - } - },50, UUID); - session.rpcs[UUID].canvasIntervalAction = canvasIntervalAction; - return session.rpcs[UUID].inboundAudioPipeline[trackid].analyser; -} - -function audioMeterGuest(mediaStreamSource, UUID, trackid){ - log("audioMeterGuest started"); - session.rpcs[UUID].inboundAudioPipeline[trackid].analyser = session.audioCtx.createAnalyser(); - mediaStreamSource.connect(session.rpcs[UUID].inboundAudioPipeline[trackid].analyser); - session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.fftSize = 256; - session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.smoothingTimeConstant = 0.05; - - var bufferLength = session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.frequencyBinCount; - var dataArray = new Uint8Array(bufferLength); - - function updateLevels() { - - try { - session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.getByteFrequencyData(dataArray); - var total = 0; - for (var i = 0; i < dataArray.length; i++){ - total += dataArray[i]; - } - total = parseInt(total/150); - session.rpcs[UUID].stats.Audio_Loudness = total; - - if (session.pushLoudness==true){ - var loudnessObj = {}; - loudnessObj[session.rpcs[UUID].streamID] = session.rpcs[UUID].stats.Audio_Loudness; - - if (isIFrame){ - parent.postMessage({"loudness": loudnessObj, "action":"loudness", "value":session.rpcs[UUID].stats.Audio_Loudness, "UUID":UUID}, session.iframetarget); - } - } - - try{ - clearTimeout(session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.interval); - session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.interval = setTimeout(function(){updateLevels();},100); - } catch(e){ - log("closing old inaudio pipeline"); - } - - if (session.style==3 || session.meterStyle){ // overrides style - if (session.rpcs[UUID].videoElement){ - if (total>40){ - session.rpcs[UUID].videoElement.dataset.speaking = "2"; - } else if (total>10){ - session.rpcs[UUID].videoElement.dataset.speaking = "1"; - } else { - session.rpcs[UUID].videoElement.dataset.speaking = "0"; - } - - if (session.meterStyle==4){ - session.rpcs[UUID].videoElement.dataset.loudness = total; - return; // this is cause we are using the data-loudness - } - } else if (session.meterStyle==4){ - return; - } - } else if (session.scene!==false){ // if a scene, cancel - return; - } else if (session.audioMeterGuest===false){ // don't show if we just want the volume levels - return; - } - - if (session.rpcs[UUID].voiceMeter){ - session.rpcs[UUID].voiceMeter.dataset.level = total; - if (session.meterStyle==1){ - var perct = Math.min(total,100); - - session.rpcs[UUID].voiceMeter.style.height = perct + "%"; - if (total>80){ - var R = parseInt(255 * perct/100).toString(16).padStart(2, '0'); - var G = parseInt(255 - 255 * perct /100).toString(16).padStart(2, '0'); - session.rpcs[UUID].voiceMeter.style.backgroundColor = "#" + R + G + "00"; - } else { - session.rpcs[UUID].voiceMeter.style.backgroundColor = "#00FF00"; - } - - } else { - if (total>15){ - session.rpcs[UUID].voiceMeter.style.opacity = 100; // temporary - } else { - session.rpcs[UUID].voiceMeter.style.opacity = 0; // temporary - } - } - - } else { - session.rpcs[UUID].voiceMeter = document.createElement("div"); - session.rpcs[UUID].voiceMeter.id = "voiceMeter_"+UUID; - session.rpcs[UUID].voiceMeter.dataset.level = total; - if (session.meterStyle==1){ - session.rpcs[UUID].voiceMeter.classList.add("video-meter2"); - } else { - if (total>15){ - session.rpcs[UUID].voiceMeter.style.opacity = 100; // temporary - } else { - session.rpcs[UUID].voiceMeter.style.opacity = 0; // temporary - } - if (session.meterStyle==2){ - session.rpcs[UUID].voiceMeter.classList.add("video-meter-2"); - } else { - session.rpcs[UUID].voiceMeter.classList.add("video-meter"); - } - } - updateMixer(); - } - - } catch(e){ - warnlog(e); - // fail as an exception; this is a control close. - return; - } - }; - clearTimeout(session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.interval); - session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.interval = setTimeout(function(){updateLevels();},100); - return session.rpcs[UUID].inboundAudioPipeline[trackid].analyser; -} - -function effectsDynamicallyUpdate(event, ele){ - log("effectsDynamicallyUpdate"); - session.effect = ele.options[ele.selectedIndex].value; - - getById("selectImageTFLITE").style.display = "none"; - getById("selectImageTFLITE3").style.display = "none"; - getById("selectEffectAmount").style.display = "none"; - getById("selectEffectAmount3").style.display = "none"; - - if (session.effect === "1"){ - updateRenderOutpipe(); - return; - } - - if (session.effect === "7"){ // digitalZoom - getById("selectEffectAmount").style.display = "block"; - getById("selectEffectAmount3").style.display = "block"; - session.effectValue = 1.0; - getById("selectEffectAmountInput").min = 1; - getById("selectEffectAmountInput").max = 1.99; - getById("selectEffectAmountInput").step = 0.01 - getById("selectEffectAmountInput3").min = 1; - getById("selectEffectAmountInput3").max = 1.99; - getById("selectEffectAmountInput3").step = 0.01 - - getById("selectEffectAmountInput").value = session.effectValue; - getById("selectEffectAmountInput3").value = session.effectValue; - updateRenderOutpipe(); - return; - } - - if (session.effect === "8"){ // like zoom but none - updateRenderOutpipe(); - return; - } - - if (session.effect == "3a"){ - session.effect = "3"; - session.effectValue = 5; - } - if ((session.effectValue_default===false) && (session.effect=="3")){ - session.effectValue = 2; - } else { - session.effectValue = session.effectValue_default; - } - - if (session.effect == "0" || !session.effect){ - updateRenderOutpipe(); - return; - } else if (session.effect === "3" || session.effect === "4"){ - attemptTFLiteJsFileLoad(); - if (!session.tfliteModule.looping){ - updateRenderOutpipe(); - } - if ((session.effect === "3") && (session.effectValue_default==false)){ - getById("selectEffectAmount").style.display = "block"; - getById("selectEffectAmount3").style.display = "block"; - - getById("selectEffectAmountInput").min = 0; - getById("selectEffectAmountInput").max = 20; - getById("selectEffectAmountInput").step = 1; - getById("selectEffectAmountInput3").min = 0; - getById("selectEffectAmountInput3").max = 20; - getById("selectEffectAmountInput3").step = 1; - - getById("selectEffectAmountInput").value = session.effectValue; - getById("selectEffectAmountInput3").value = session.effectValue; - } - } else if (session.effect === "5"){ - attemptTFLiteJsFileLoad(); - if (!session.tfliteModule.looping){ - updateRenderOutpipe(); - } - loadTFLITEImages(); - } else if (session.effect === "6"){ - if (!gpgpuSupport){ - if (!session.cleanOutput){ - warnUser("Hardware acceleration isn't detected.

    This effect will not work",4000,false); - return; - } - } else if (gpgpuSupport == "Google SwiftShader"){ - if (!session.cleanOutput){ - warnUser("Hardware acceleration isn't detected.

    Please enable it for this effect to work correctly.

    Settings -> Advanced -> System -> Use hardware-accleration", false, false); - } - return; - } - loadTensorflowJS(); - updateRenderOutpipe(); - //mainMeshMask(); - } else { - //loadEffect(session.effect); - updateRenderOutpipe(); - } - - if ((session.permaid===false) && (session.roomid===false) && (session.view===false) && (session.director===false)){ - updateURL("effects"); - } -} - -function loadTFLITEImages(){ - if (session.effect!=="5"){return;} // only load if effects 5 is set. - - if (session.defaultBackgroundImages){ - try { - session.defaultBackgroundImages.reverse(); - }catch(e){ - errorlog("Could not process image list"); - session.defaultBackgroundImages = false; - session.selectImageTFLITE_contents = getById("selectImageTFLITE_contents"); - return; - } - session.defaultBackgroundImages.forEach(imgSrc=>{ - try { - var img = document.createElement("img"); - img.onerror = function(){this.style.display="none";}; // hide images that fail to load - img.crossOrigin = "Anonymous"; - img.src = imgSrc; - img.style="max-width:130px;max-height:73.5px;display:inline-block;margin:10px;cursor:pointer;"; - img.onclick=function(event){changeTFLiteImage(event, this);}; - getById("selectImageTFLITE_contents").prepend(img); - } catch(e){}; - }); - session.defaultBackgroundImages = false; - session.selectImageTFLITE_contents = getById("selectImageTFLITE_contents"); - } else if (!session.selectImageTFLITE_contents){ - session.selectImageTFLITE_contents = getById("selectImageTFLITE_contents"); - } - if (document.getElementById("selectImageTFLITE")){ - document.getElementById("selectImageTFLITE").style.display = "block"; - document.getElementById("selectImageTFLITE").appendChild(session.selectImageTFLITE_contents); - session.selectImageTFLITE_contents.classList.remove("hidden"); - } else if (document.getElementById("selectImageTFLITE3")){ - document.getElementById("selectImageTFLITE3").style.display = "block"; - document.getElementById("selectImageTFLITE3").appendChild(session.selectImageTFLITE_contents); - session.selectImageTFLITE_contents.classList.remove("hidden"); - } -} - -var effectsLoaded = {}; -var JEELIZFACEFILTER = null; -async function loadEffect(effect){ - warnlog("effect:"+effect); - var filename = effect.replace(/\W/g, ''); - if (effectsLoaded[filename]){ - effectsLoaded[filename](); - return; - } else { - effectsLoaded[filename] = function(){}; - } - warnlog("Loading Effect: "+effect); - var script = document.createElement('script'); - script.onload = async function() { - log("LOADED EFFECT"); - effectsLoaded[filename] = await effectsEngine(filename); - log("effectsEngine();"); - if (gpgpuSupport == "Google SwiftShader"){ - if (!session.cleanOutput){ - warnUser("Hardware acceleration isn't detected.

    Please enable it for better performance.

    Settings -> Advanced -> System -> Use hardware-accleration", false, false); - } - } - effectsLoaded[filename](); - } - script.src = "./filters/"+filename+".js?"+parseInt(1000*Math.random()); - document.head.appendChild(script); - warnUser("Loading custom effects model...",1000); -} - -async function loadScript(url, callback=false){ - var res = null; - var rej = null; - var promise = new Promise((resolve, reject) => { - res = resolve; - rej = reject; - }); - - var check = document.querySelector("script[src='"+url+"']"); - if (check){ - if(callback){callback();} - } else { - var script = document.createElement('script'); - script.type = 'text/javascript'; - script.src = url; - script.onload = function(){ - res(); - if(callback){ - callback(); - } - }; - document.head.appendChild(script); - } - return await promise; -} - -var tokenClient=false; -function YoutubeChatInterface(remote=false){ // this lets us query Youtube for chat messages, but its quota limited :( - if (!tokenClient){ - tokenClient=true; - } else { - return; - } - - var gisInited = false; - var gapiInited = false; - var busy = 0; - - function handleAuthClick() { - tokenClient.callback = async (resp) => { - if (resp.error){ - errorlog(resp.error); - } - closeModal(); - var auths = gapi.client.getToken(); - if (auths){ - setStorage("YoutubeAuth", JSON.stringify(auths), auths.expires_in || 3600); - } - listBroadcasts(); - }; - var saved = getStorage("YoutubeAuth"); - - if (saved){ - gapi.client.setToken(JSON.parse(saved)); - listBroadcasts(); - } else if (gapi.client.getToken() === null) { - if (remote){ - tokenClient.requestAccessToken({prompt:"consent"}); - } else { - warnUser("", false, false); - } - } else { - if (remote){ - tokenClient.requestAccessToken({prompt: ""}); - } else { - warnUser("", false, false); - } - } - } - - function maybeEnableButtons() { - if (gapiInited && gisInited){ - handleAuthClick(); - } - } - - async function initializeGapiClient() { - await gapi.client.init({ - apiKey: session.youtubeKey.split(",")[1], - discoveryDocs: ['https://www.googleapis.com/discovery/v1/apis/youtube/v3/rest'], - }); - gapiInited = true; - maybeEnableButtons(); - } - - function handleSignoutClick() { - let token = gapi.client.getToken(); - if (token !== null) { - google.accounts.oauth2.revoke(token.access_token); - gapi.client.setToken(''); - } - } - - async function listBroadcasts() { - try { - var response = await gapi.client.youtube.liveBroadcasts.list({ - "broadcastStatus": "active" - }); - } catch (err) { - errorlog(err); - return; - } - - let broadcasts = response.result.items; - if (!broadcasts || broadcasts.length == 0) { - return; - } - broadcasts.forEach(broadcast=>{ - setTimeout(function(liveChatId){ - listMessages(liveChatId); - busy+=1; - },1000, broadcast.snippet.liveChatId); - }); - } - - async function listMessages(liveChatId, pageToken = false) { - try { - if (pageToken){ - var response = await gapi.client.youtube.liveChatMessages.list({ - "liveChatId": liveChatId, - "part": ["id", "snippet", "authorDetails"], - "pageToken": pageToken - }); - } else { - var response = await gapi.client.youtube.liveChatMessages.list({ - "liveChatId": liveChatId, - "part": ["id", "snippet", "authorDetails"] - }); - } - - var messages = response.result.items; - messages.forEach(msg =>{ - pokeIframeAPI("YoutubeChat",msg); - }); - - var polling = response.result.pollingIntervalMillis; - var pageToken = response.result.nextPageToken; - - if (busy>1){ - // popular eh? Lets quickly check for more. - } else if (busy>0){ // a message ! hurrah - if (polling<2000){polling=2000;} // Was it just luck? - } else if (polling<5000){ - polling=5000; // let's not spam the api, cause we know there isn't anything waiting.. - } - busy=0; // reset - setTimeout(function(liveChatId,pageToken){ - listMessages(liveChatId, pageToken); - }, polling, liveChatId, pageToken) - - } catch (err) { - return; - } - } - - function gisLoaded() { - tokenClient = google.accounts.oauth2.initTokenClient({ - client_id: session.youtubeKey.split(",")[0], - scope: 'https://www.googleapis.com/auth/youtube', - callback: '', - }); - gisInited = true; - maybeEnableButtons(); - } - function gapiLoaded() { - gapi.load('client', initializeGapiClient); - } - - loadScript("https://apis.google.com/js/api.js",gapiLoaded); - loadScript("https://accounts.google.com/gsi/client",gisLoaded); -} - -function loadTensorflowJS(){ - if (session.TFJSModel!=null){ - return; - } - log("loadTensorflowJS()"); - session.TFJSModel=true; - var script = document.createElement('script'); - var script2 = document.createElement('script'); - var script3 = document.createElement('script'); - var script4 = document.createElement('script'); - script.onload = function() { - document.head.appendChild(script2); - } - script2.onload = function() { - document.head.appendChild(script3); - } - script3.onload = function() { - document.head.appendChild(script4); - } - script4.onload = function() { - async function loadModel(){ - session.TFJSModel = await faceLandmarksDetection.load(faceLandmarksDetection.SupportedPackages.mediapipeFacemesh); - closeModal(); - warnUser("Almost done loading model...",3000); - } - loadModel(); - - } - script.src = "./thirdparty/tfjs/tf-core.js"; - script2.src = "./thirdparty/tfjs/tf-converter.js"; - script3.src = "./thirdparty/tfjs/tf-backend-webgl.js"; - script4.src = "./thirdparty/tfjs/face-landmarks-detection.js"; - warnUser("Downloading a big effects model... may take a minute",15000); - - script.type = 'text/javascript';script2.type = 'text/javascript';script3.type = 'text/javascript';script4.type = 'text/javascript'; - document.head.appendChild(script); -} - - - -var TFLITELOADING = false; -function attemptTFLiteJsFileLoad(){ - if (session.tfliteModule!==false){ - return true; - } - warnUser("Loading effects model..."); - TFLITELOADING=true; - session.tfliteModule={}; - - if (!document.getElementById("tflitesimdjs")){ - var tmpScript = document.createElement('script'); - tmpScript.onload = loadTFLiteModel; - tmpScript.type = 'text/javascript'; - tmpScript.src = "./thirdparty/tflite/tflite-simd.js?ver=2"; - tmpScript.id = "tflitesimdjs"; - document.head.appendChild(tmpScript); - } - - return false; -} -async function changeTFLiteImage(ev, ele){ - if (ele.files && ele.files[0]) { - if (session.tfliteModule.img){ - session.tfliteModule.img.classList.remove("selectedTFImage"); - } - session.tfliteModule.img = document.createElement("img"); - session.tfliteModule.img.style="max-width:130px;max-height:73.5px;display:inline-block;margin:10px;cursor:pointer;"; - session.tfliteModule.img.onclick=function(event){changeTFLiteImage(event, this);}; - ele.parentNode.parentNode.insertBefore(session.tfliteModule.img, ele.parentNode); - session.tfliteModule.img.onload = () => { - URL.revokeObjectURL(session.tfliteModule.img.src); // no longer needed, free memory - } - session.tfliteModule.img.src = URL.createObjectURL(ele.files[0]); // set src to blob url - session.tfliteModule.img.classList.add("selectedTFImage"); - - } else if (ele.tagName.toLowerCase() == "img"){ - session.tfliteModule.img.classList.remove("selectedTFImage"); - session.tfliteModule.img = ele - session.tfliteModule.img.classList.add("selectedTFImage"); - } -} -async function changeEffectAmount(ev, ele){ - session.effectValue = ele.value; - if (ele.id === "selectEffectAmountInput"){ - getById("selectEffectAmountInput3").value = ele.value - } - log("session.effectValue: "+session.effectValue); -} -async function loadTFLiteModel(){ - try { - - if (session.tfliteModule && (session.tfliteModule.img)){ - var img = session.tfliteModule.img; - session.tfliteModule = await createTFLiteSIMDModule(); - session.tfliteModule.img = img; - } else { - session.tfliteModule = {}; - session.tfliteModule = await createTFLiteSIMDModule(); - } - if (!session.tfliteModule.simd){ - var elements = document.querySelectorAll('[data-warnSimdNotice]') - for (let i = 0; i < elements.length; i++) { - elements[i].style.display = "inline-block"; - } - } - } catch(e){ - warnlog("TF-LITE FAILED TO LOAD"); - closeModal(); - return; - } - const modelResponse = await fetch("./thirdparty/tflite/segm_full_v679.tflite"); - session.tfliteModule.model = await modelResponse.arrayBuffer(); - - session.tfliteModule.HEAPU8.set(new Uint8Array(session.tfliteModule.model), session.tfliteModule._getModelBufferMemoryOffset()); - session.tfliteModule._loadModel(session.tfliteModule.model.byteLength); - session.tfliteModule.activelyProcessing = false; - TFLITELOADING = false; - closeModal(); - if (LaunchTFWorkerCallback){TFLiteWorker();} -} -function smdInfo(){ - warnUser("For improved performance, use Chrome v87 or newer with SIMD support enabled.
    Enable SIMD here: chrome://flags/#enable-webassembly-simd", false, false); -} - -function getGuestTarget(type, id){ - var element = document.querySelectorAll('[data-action-type="'+type+'"][data-sid="'+id+'"]'); // data-sid="P5MQpia" - if (!element.length){ - return element = getRightOrderedElement('[data-action-type="'+type+'"][data--u-u-i-d]', id); - } else { - element = element[0]; - } - return element; -} - -function getGuestTargetScene(scene, id){ - var element = document.querySelectorAll('[data-action-type="addToScene"][data-scene="'+scene+'"][data-sid="'+id+'"]'); // data-sid="P5MQpia" - if (!element.length){ - return element = getRightOrderedElement('[data-action-type="addToScene"][data-scene="'+scene+'"][data--u-u-i-d]', id); - } else { - element = element[0]; - } - return element; -} -function getGuestTargetGroup(group, id){ - var element = document.querySelectorAll('[data-action-type="toggle-group"][data-group="'+group+'"][data-sid="'+id+'"]'); // data-sid="P5MQpia" - if (!element.length){ - return getRightOrderedElement('[data-action-type="toggle-group"][data-group="'+group+'"][data--u-u-i-d]', id); - } else { - element = element[0]; - } - return element; -} - -function targetGuest(target, action, value=null){ - if (target){ - if ((target == (parseInt(target)+"")) && target<100){ - target -=1; - } - } else { - target=1; - } - warnlog("target "+target); - warnlog("action "+action); - warnlog("value "+value); - if ((action == 0) || (action == "forward")) { - var element = getGuestTarget("forward", target); - if (element) { - directMigrate(element, true, value); // if value is set, it will auto transfer the guest to that room. - } - } else if ((action == 1) || (action == "addScene")) { - var scene = 1; - if (value == "null" || value == null || value == "toggle"){ - scene = 1; - } else if ((value !== true) && (value !== false)){ - scene = value; - } - var element = getGuestTargetScene(scene, target); // oscid/action/target/value 1/1/scene - if (element) { - if (value===true){ - element.value = 1; - } else if (value===false){ - element.value = 0; - } - return directEnable(element, true); // false or true return - } - } else if ((action == 2) || (action == "muteScene")) { - var element = getGuestTarget("mute-scene", target); - if (element) { - if (value===true){ - element.value = 1; - } else if (value===false){ - element.value = 0; - } - return directMute(element, true); // false/true - } - } else if ((action == 3) || (action == "mic")) { - var element = getGuestTarget("mute-guest", target); - if (element) { - if (value===true){ - element.value = 1; - } else if (value===false){ - element.value = 0; - } - return remoteMute(element, true); // false/true - } - } else if ((action == 4) || (action == "hangup")) { - var element = getGuestTarget("hangup", target); - if (element) { - return directHangup(element, true); // false or true; false if confirmed no - } - } else if ((action == 5) || (action == "soloChat")) { // see soloChatBidirectional action=9 for two-way - var element = getGuestTarget("solo-chat", target); - if (element) { - if (value===true){ - element.value = 1; - } else if (value===false){ - element.value = 0; - } - return session.toggleSoloChat(element.dataset.UUID); - } - } else if ((action == 6) || (action == "speaker")) { - var element = getGuestTarget("toggle-remote-speaker", target); - if (element) { - if (value===true){ - element.value = 1; - } else if (value===false){ - element.value = 0; - } - return remoteSpeakerMute(element); - - } - } else if ((action == 7) || (action == "display")) { - var element = getGuestTarget("toggle-remote-display", target); - if (element) { - if (value===true){ - element.value = 1; - } else if (value===false){ - element.value = 0; - } - return remoteDisplayMute(element); - } - } else if ((action == 8) || (action == "group")) { - if (value == "null" || value == null){ - value = 1; - } - var element = getGuestTargetGroup(value, target); - if (element) { - return changeGroup(element, null, value); - } - } else if ((action == 9) || (action == "soloChatBidirectional")) { - var element = getGuestTarget("solo-chat", target); - if (element) { - var ctrl = {}; - ctrl.ctrlKey = true; - if (value===true){ - element.value = 1; - } else if (value===false){ - element.value = 0; - } - return session.toggleSoloChat(element.dataset.UUID, ctrl); - - } - } else if ((action == 12) || (action == "addScene2")) { - var element = getGuestTargetScene(2, target); - if (element) { - if (value===true){ - element.value = 1; - } else if (value===false){ - element.value = 0; - } - return directEnable(element, true) - } - } else if ((action == 13) || (action == "addScene3")) { - var element = getGuestTargetScene(3, target); - if (element) { - if (value===true){ - element.value = 1; - } else if (value===false){ - element.value = 0; - } - return directEnable(element, true) - } - } else if ((action == 14) || (action == "addScene4")) { - var element = getGuestTargetScene(4, target); - if (element) { - if (value===true){ - element.value = 1; - } else if (value===false){ - element.value = 0; - } - return directEnable(element, true) - } - } else if ((action == 15) || (action == "addScene5")) { - var element = getGuestTargetScene(5, target); - if (element) { - if (value===true){ - element.value = 1; - } else if (value===false){ - element.value = 0; - } - return directEnable(element, true) - } - } else if ((action == 16) || (action == "addScene6")) { - var element = getGuestTargetScene(6, target); - if (element) { - if (value===true){ - element.value = 1; - } else if (value===false){ - element.value = 0; - } - return directEnable(element, true) - } - } else if ((action == 17) || (action == "addScene7")) { - var element = getGuestTargetScene(7, target); - if (element) { - if (value===true){ - element.value = 1; - } else if (value===false){ - element.value = 0; - } - return directEnable(element, true) - } - } else if ((action == 18) || (action == "addScene8")) { - var element = getGuestTargetScene(8, target); - if (element) { - if (value===true){ - element.value = 1; - } else if (value===false){ - element.value = 0; - } - return directEnable(element, true) - } - } else if ((action == 19) || (action == "forceKeyframe")) { - var element = getGuestTarget("force-keyframe", target); - if (element) { - return requestKeyframeScene(element); - } - } else if ((action == 20) || (action == "soloVideo")) { - var element = getGuestTarget("solo-video", target); - if (element) { - if (value===true){ - element.value = 1; - } else if (value===false){ - element.value = 0; - } - return requestInfocus(element); - } - } else if ((action == 21) || (action == "sendChat")) { - var element = getGuestTarget("solo-video", target); // just something that probably exists. - if (element) { - return sendChat(value, element.dataset.UUID); - } - } else if ((action == 22) || (action == "sendDirectorChat")) { - var element = getGuestTarget("solo-video", target); // just something that probably exists. - if (element) { - return sendChat(value, element.dataset.UUID, true); - } - } else if ((action == 27) || (action == "volume")){ - var element = getGuestTarget("volume", target); - if (element) { - element.value = parseInt(value) || 0; - return remoteVolume(element); - } - } else if (action == "startRoomTimer"){ - var element = getGuestTarget("create-timer", target); - if (element) { - element.value = 0; - return directTimer(element, false, value); - } - } else if (action == "pauseRoomTimer"){ - var element = getGuestTarget("create-timer", target); - if (element) { - if (element.value == 3){ - return directTimer(element, {ctrlKey:true}); - } else { - return directTimer(element, {ctrlKey:true}); - } - } - } else if (action == "stopRoomTimer"){ - var element = getGuestTarget("create-timer", target); - if (element) { - element.value = 1; - return directTimer(element); - } - } - return false; -} -async function startPublishing(){ - - if (query("#publishOutURL input[type='text']").dataset.twitch=="true"){ - session.whipOutput = "https://g.webrtc.live-video.net:4443/v2/offer"; - } else { - session.whipOutput = query("#publishOutURL input[type='text']").value || session.whipOutput || null; - } - - if (!session.whipOutput){ - warnUser("Please first provided an output destination",2500); - return; - } - - if (!session.whipOutputToken){ - session.whipOutputToken = query("#publishOutToken input[type='password']").value || false; - } - - if (!session.whipOutputToken && query("#publishOutURL input[type='text']").dataset.twitch=="true"){ - warnUser("Please enter a Twitch stream token first",2000); - return; - } - getById("publishSettings").classList.add("hidden"); - - - if (!getById("whipoutvbrcbr").classList.contains("hidden")){ - if (getById("whipoutvbrcbr").value ==="cbr"){ - session.cbr = 1; - } else { - session.cbr = 0; - } - } - if (!getById("whipoutdenoise").classList.contains("hidden")){ - if (getById("whipoutdenoise").value ==="1"){ - session.noiseSuppression = true; - } else { - session.noiseSuppression = false; - } - } - if (!getById("whipoutautogain").classList.contains("hidden")){ - if (getById("whipoutautogain").value ==="1"){ - session.autoGainControl = true; - } else { - session.autoGainControl = false; - } - } - if (!getById("whipoutstereo").classList.contains("hidden")){ - if (getById("whipoutstereo").value ==="1"){ - session.stereo = 1; - } else { - session.stereo = 0; - } - } - if (!getById("whipoutbitrateGroupFlag").classList.contains("hidden")){ - session.whipOutVideoBitrate = parseInt(getById("whipoutbitrateGroupFlag").value); - } - if (!getById("whipoutaudiobitrate").classList.contains("hidden")){ - session.whipOutAudioBitrate = parseInt(getById("whipoutaudiobitrate").value); - } - - var ret = await publishScreen(); - if (ret){ - getById("publishSettings").classList.add("hidden"); - resizeWindow(1280,720); - document.title="PUBLISHING🔴"+document.title; - } else { - getById("publishSettings").classList.remove("hidden"); - } -} - -function twitchSelect(ele){ - if (ele.checked){ - //query("#publishOutURL input[type='text']").value = - query("#publishOutURL input[type='text']").disabled = true; - query("#publishOutURL input[type='text']").classList.add("disable"); - query("#publishOutURL input[type='text']").dataset.twitch = "true"; - - query("#publishOutToken input[type='password']").placeholder = "Twitch stream token here"; - } else { - query("#publishOutURL input[type='text']").disabled = null; - query("#publishOutURL input[type='text']").classList.remove("disable"); - delete getById("publishOutURL").disabled; - query("#publishOutURL input[type='text']").dataset.twitch = "false"; - query("#publishOutToken input[type='password']").placeholder = "WHIP auth token here"; - } -} - -function resizeWindow(width, height){ - if (window.outerWidth) { - window.resizeTo( - width + (window.outerWidth - window.innerWidth), - height + (window.outerHeight - window.innerHeight) - ); - } else { - window.resizeTo(500, 500); - window.resizeTo( - width + (500 - document.body.offsetWidth), - height + (500 - document.body.offsetHeight) - ); - } - - setInterval(function(){ - if ((window.innerWidth/window.innerHeight > 17/9) && (window.innerWidth/window.innerHeight < 15/9)){ - return; - } - if (window.outerWidth) { - window.resizeTo( - width + (window.outerWidth - window.innerWidth), - height + (window.outerHeight - window.innerHeight) - ); - } else { - window.resizeTo(500, 500); - window.resizeTo( - width + (500 - document.body.offsetWidth), - height + (500 - document.body.offsetHeight) - ); - } - },5000); -} - -function configureWhipOutSDP(description){ // THIS IS FOR WHIP-OUTPUT; it has - - var configs = false; - - if (SafariVersion && (SafariVersion<=13) && (iOS || iPad)){ - // skip. Not going to try to tinker with older iOS SDPs - } else if ((session.stereo==3) || (session.stereo==5) || (session.stereo==6) || (session.stereo==1)){ // stereo out - configs = { - 'stereo': 1, - 'useinbandfec': session.noFEC ? 0 : 1, - 'maxptime': session.maxptime, - 'minptime': session.minptime, - 'ptime': session.ptime, - 'dtx': session.dtx // "usedtx", if no loud audio, stops sending audio for 400ms. default. - }; - log("stereo enabled"); - } else if (iOS || iPad){ // iOS doesn't have multichannel, so why even bother - configs = { - 'useinbandfec': session.noFEC ? 0 : 1, - 'maxptime': session.maxptime, - 'minptime': session.minptime, - 'ptime': session.ptime, - 'dtx': session.dtx // "usedtx", if no loud audio, stops sending audio for 400ms. default. - }; - - } else if (session.stereo==4){ - configs = { - 'stereo': 2, - 'useinbandfec': session.noFEC ? 0 : 1, - 'maxptime': session.maxptime, - 'minptime': session.minptime, - 'ptime': session.ptime, - 'dtx': session.dtx // "usedtx", if no loud audio, stops sending audio for 400ms. default. - }; - log("stereo enabled"); - } else { - configs = { - 'stereo': 0, - 'useinbandfec': session.noFEC ? 0 : 1, - 'maxptime': session.maxptime, - 'minptime': session.minptime, - 'ptime': session.ptime, - 'dtx': session.dtx // "usedtx", if no loud audio, stops sending audio for 400ms. default. - }; - } - - if (session.whipOutAudioBitrate){ - if (!configs){ - configs = { - 'maxaveragebitrate': session.whipOutAudioBitrate * 1024, - 'cbr': session.cbr - }; - } else{ - configs.maxaveragebitrate = session.whipOutAudioBitrate * 1024; - configs.cbr = session.cbr; - } - } - - if (configs){ - log("Processing sdp of type: "+description.type+ " ..."); - description.sdp = CodecsHandler.setOpusAttributes(description.sdp, configs, true); - } - - if (iOS || iPad){ // solves issues with iOS rotation not being correct - if (session.removeOrientationFlag && description.sdp.includes("a=extmap:3 urn:3gpp:video-orientation\r\n")){ - description.sdp = description.sdp.replace('a=extmap:3 urn:3gpp:video-orientation\r\n', ''); - } - } - - if (session.screenShareState && (typeof session.whipOutScreenShareCodec === "object")){ - session.whipOutScreenShareCodec.reverse().forEach(codec=>{ - description.sdp = CodecsHandler.preferCodec(description.sdp, codec); - - if (session.whipOutScreenShareBitrate || session.whipOutVideoBitrate){ - description.sdp = CodecsHandler.setVideoBitrates(description.sdp , { - min: parseInt((session.whipOutScreenShareBitrate || session.whipOutVideoBitrate)/10) || 1, - max: session.whipOutScreenShareBitrate || session.whipOutVideoBitrate || 1 - }, codec); - } - }); - } else if (session.screenShareState && session.whipOutScreenShareCodec){ - description.sdp = CodecsHandler.preferCodec(description.sdp, session.whipOutScreenShareCodec); - - if (session.whipOutScreenShareBitrate || session.whipOutVideoBitrate){ - description.sdp = CodecsHandler.setVideoBitrates(description.sdp , { - min: parseInt((session.whipOutScreenShareBitrate || session.whipOutVideoBitrate)/10) || 1, - max: session.whipOutScreenShareBitrate || session.whipOutVideoBitrate || 1 - }, session.whipOutScreenShareCodec); - } - } else if (typeof session.whipOutCodec === "object"){ - session.whipOutCodec.reverse().forEach(codec=>{ - description.sdp = CodecsHandler.preferCodec(description.sdp, codec); - - if (session.whipOutVideoBitrate){ - description.sdp = CodecsHandler.setVideoBitrates(description.sdp , { - min: parseInt(session.whipOutVideoBitrate/10) || 1, - max: session.whipOutVideoBitrate || 1 - }, codec); - } - }); - } else if (session.whipOutCodec){ - description.sdp = CodecsHandler.preferCodec(description.sdp, session.whipOutCodec); - if (session.whipOutVideoBitrate){ - description.sdp = CodecsHandler.setVideoBitrates(description.sdp , { - min: parseInt(session.whipOutVideoBitrate/10) || 1, - max: session.whipOutVideoBitrate || 1 - }, session.whipOutCodec); - } - } else { - if (iOS || iPad){ - jsep.sdp = jsep.sdp.replace(/42e01f/gi,"42e01f"); // openH264 - jsep.sdp = jsep.sdp.replace(/42001f/gi,"42e01f"); // external encoder - jsep.sdp = jsep.sdp.replace(/420029/gi,"42e01f"); // external encoder - jsep.sdp = jsep.sdp.replace(/42a01e/gi,"42e01f"); // external encoder - jsep.sdp = jsep.sdp.replace(/42a014/gi,"42e01f"); // external encoder - jsep.sdp = jsep.sdp.replace(/42a00b/gi,"42e01f"); // external encoder - jsep.sdp = jsep.sdp.replace(/640c1f/gi,"42e01f"); // will not work - } else { - description.sdp = CodecsHandler.preferCodec(description.sdp,"h264"); // default - description.sdp = description.sdp.replace(/42001f/gi,"42e01f"); // openh264 set as default. - description.sdp = description.sdp.replace(/420029/gi,"42e01f"); - } - if (session.whipOutVideoBitrate){ - description.sdp = CodecsHandler.setVideoBitrates(description.sdp , { - min: parseInt(session.whipOutVideoBitrate/10) || 1, - max: session.whipOutVideoBitrate || 1 - }, "h264"); - } - } - - var bitrate = 2500; - if (session.whipOutVideoBitrate!==false){ - bitrate = session.whipOutVideoBitrate; - } - if (session.screenShareState && (session.whipOutScreenShareBitrate!==false)){ - bitrate = session.whipOutScreenShareBitrate; - } - - session.whipOut.savedBitrate = bitrate; // actual target - session.whipOut.setBitrate = bitrate; // max - - return description; -} - - -function whipOut(){ - log("whipOut"); - var candidates = []; - var codec = false; - var keyframe = false; - async function whipConnect(){ - - try { - - if (!session.configuration){ - await chooseBestTURN(); - } - - session.whipOut = new RTCPeerConnection(session.configuration); - session.whipOut.stats = {}; - session.whipOut.maxBandwidth = null; // based on max available bitrate - session.whipOut.scale = false; - - session.whipOut.offerToReceiveAudio = false; - session.whipOut.offerToReceiveVideo = false; - - - } catch(err){ - errorlog(err); - if (!session.cleanOutput){ - warnUser("An RTC error occured"); - } - } - - try { - var tracks = false; - if (session.videoElement && session.videoElement.srcObject){ - tracks = session.videoElement.srcObject.getAudioTracks(); - } - var streamsource = false; - if (!tracks || !tracks.length){ - var audioCtx = new AudioContext(); - warnlog("No audio track; using a webaudio node instead"); - var destination = audioCtx.createMediaStreamDestination(); - streamsource = destination.stream; - destination.stream.getAudioTracks().forEach(trk=>{ - tracks = trk; - }); - } else { - tracks = tracks[0]; - streamsource = session.videoElement.srcObject; - } - - if (session.audioContentHint && (tracks.kind === "audio")){ - try { - tracks.contentHint = session.audioContentHint; - } catch(e){ - errorlog(e); - } - } - - if (tracks){ - try { - session.whipOut.addTransceiver(tracks, { - streams: [ streamsource ], - direction: 'sendonly' - }); - } catch(e){ - errorlog(e); - session.whipOut.addTrack(tracks); - } - } - //} - /////// - - //// video tracks - var tracks = false; - if (session.videoElement && session.videoElement.srcObject){ - tracks = session.videoElement.srcObject.getVideoTracks(); - } - //// - - //if (!tracks || !tracks.length){ - // tracks = getMeshcastCanvasTrack(); - //} else { - tracks = tracks[0]; - //} - - if (session.screenShareState && session.screenshareContentHint && (tracks.kind === "video")){ - try { - tracks.contentHint = session.screenshareContentHint; - } catch(e){ - errorlog(e); - } - } else if (session.contentHint && (tracks.kind === "video")){ - try { - tracks.contentHint = session.contentHint; - } catch(e){ - errorlog(e); - } - } - if (tracks){ - try { - session.whipOut.addTransceiver(tracks, { - streams: [ session.videoElement.srcObject ], - direction: 'sendonly' - }); - } catch(e){ - errorlog(e); - session.whipOut.addTrack(tracks); - } - } - //} - - session.whipOut.onnegotiationneeded = publish; // bug: https://groups.google.com/forum/#!topic/discuss-webrtc/3-TmyjQ2SeE - - session.whipOut.onicecandidate = function(event){ //event - if (event.candidate==null){ - log("END OF ICE CANDIDATES"); - return; - } - //log(event.candidate); - candidates.push(event.candidate); - }; - - } catch(e){errorlog(e);} - } - var publishing = false; - - - function publish(event){ - if (publishing){ - log(event); - errorlog("onnegotiationneeded again?"); - return; - } - publishing = true; - warnlog("ON NEGO NEEDED"); - warnlog(event); - try { - session.whipOut.createOffer().then(function(description){ - - - try { - description = configureWhipOutSDP(description); - } catch(e){ - errorlog(e); - } - - return session.whipOut.setLocalDescription(description); - }).then(function() { - warnlog(session.whipOut.localDescription.sdp); - var sdp = session.whipOut.localDescription.sdp; - - if (sdp.includes("sendrecv")){ - errorlog("Should not include sendrecv"); - sdp = sdp.replace("a=sendrecv","a=sendonly"); - sdp = sdp.replace("v=sendrecv","v=sendonly"); - } - ajax(sdp, "sdp"); - }).catch(function(err){}); - } catch(e){errorlog(e);} - } - - function ajax(data, type, callback=false){ - log("AJAX: "+type); - //log(data); - try { - var xhttp = new XMLHttpRequest(); - xhttp.onreadystatechange = function() { - if (this.readyState == 4 && (this.status == 200 || this.status == 201)) { - - var contentType = this.getResponseHeader('content-type'); - - if (contentType.startsWith("text/plain")){ - warnlog("The WHIP output destination responded with an incorrect content type; will attempt to continue still."); - } - - if (!session.whipOut.stats){ - session.whipOut.stats = {}; - } - session.whipOut.stats.whipHost = "generic"; - session.whipOut.stats.whep_URL = false - session.whipOut.stats.watch_URL = false; // cloudflare and meshcast have this, but meh. aec is an issue, so won't bother for now. - var WHELPlaybackURL = false; // we will try to determine the WHEP address, if we need to share it with other viewers. - try { - log(this.getAllResponseHeaders()); - if (this.getAllResponseHeaders().indexOf("whep") >= 0) { - WHELPlaybackURL = this.getResponseHeader('whep') || false; - } - if (!WHELPlaybackURL && session.whipOutput){ - var targetDomain = session.whipOutput.split("/"); - if (targetDomain[2].endsWith(".cloudflarestream.com") && (targetDomain[3].length == 65)){ - WHELPlaybackURL = "https://"+targetDomain[2]+"/"+targetDomain[3].slice(33,65)+"/webRTC/play"; - session.whipOut.stats.whipHost = "Cloudflare"; - } - } - log("WHELPlaybackURL: "+WHELPlaybackURL); - session.whipOut.stats.whep_URL = WHELPlaybackURL - - } catch(e){errorlog(e);} - - if (WHELPlaybackURL){ - session.whipoutSettings = {type:"whep", "url": WHELPlaybackURL}; - } - - if (contentType.startsWith("application/sdp") || contentType.startsWith("text/plain")){ - var jsep = {}; - jsep.sdp = this.responseText; - jsep.type = "answer"; - - try { - jsep = configureWhipOutSDP(jsep); - } catch(e){ - errorlog(e); - } - - warnlog("Processing answer:"); - warnlog(jsep); - - session.whipOut.setRemoteDescription(jsep).then(async function(){ - warnlog("SHOULD BE CONNECTED?"); - var content = ""; - while (candidates.length){ - var candidate = candidates.pop(); - content += candidate.candidate; - - } - warnlog("Content: "+content.length) - //if (content){ - // warnlog("SENDING TRICKLE"); //.. I should, but I'm not, since most sites don't support it still. - if (!keyframe){ - keyframe = setInterval(function(){GOP();},6000); // ensure GOP no longer than 6s - } - session.whipOutSetScale(); - - await sleep(1000); // give whip server a moment to setup I guess. - if (session.whipoutSettings){ - for (var UUID in session.pcs){ - if (session.pcs[UUID].whipout===null){ - var data = {} - data.whepSettings = session.whipoutSettings; - if (session.sendMessage(data, UUID)){ - session.pcs[UUID].whipout = true; - } - } - } - } - //} - - }).catch(function(e){log(e);}); - - } else if (contentType == "application/error"){ - if (this.responseText==432){ - warnUser("Whip out error: 432"); - } else { - warnUser("Unknown Whipe Out error"); - } - } else if (callback){ - callback(); - } - } - }; - if (type==="trickle-ice-sdpfrag"){ - xhttp.open("PATCH", session.whipOutput, true); // Not supported by most sites yet - } else { - xhttp.open("POST", session.whipOutput, true); - } - - if (session.whipOutputToken){ - xhttp.setRequestHeader('Authorization', 'Bearer ' + session.whipOutputToken); - } - - xhttp.setRequestHeader('Content-Type', 'application/'+type); - - xhttp.onerror = function(e) { - errorlog(e); - warnUser("Whip out failed."); - }; - xhttp.send(data); - - } catch(e){errorlog(e);} - } - function GOP(){ - log("Sending keyframe"); - try { - if (!session.whipOut){return;} - - var senders = session.whipOut.getSenders(); - var sender = false; - senders.forEach((senderVideo)=>{ - if (senderVideo.track && senderVideo.track.id && (senderVideo.track.kind == "video")){ - sender = senderVideo; - } - }); - - if (!sender){ - warnlog("can't change bitrate; no video sender found"); - return false; - } - - var settings = {}; - settings.scaleResolutionDownBy = 10; // 50% of default max - - - setEncodings(sender, settings, function(sendr){ - var settings = {}; - - var chromeVersion = getChromiumVersion(); - if (chromeVersion>80){ // just because - settings.scaleResolutionDownBy = null; - } else { - settings.scaleResolutionDownBy = 1.0; - } - - setEncodings(sendr, settings, function(){ - //log("scaleResolutionDownBy set 3b!"); - }); - }, sender); - - return true; - } catch(e){ - errorlog(e); - } - } - whipConnect(); -} - -function whipClient(){ // publish to whip.vdo.ninja with obs, to use. experimental - if (!session.whipView){return;} - warnlog("WHIP Client started"); - - var socket = null; - var connecting = false; - var failedCount = 0; - - function connect(){ - clearTimeout(connecting); - if (socket){ - if (socket.readyState === socket.OPEN){return;} - try{ - socket.close(); - } catch(e){} - } - log("Trying to load whip websocket..."); - - socket = new WebSocket("wss://whip.vdo.ninja"); - - socket.onclose = function (){ - failedCount+=1; - clearTimeout(connecting); - connecting = setTimeout(function(){connect();},100*(failedCount-1)); - - }; - - socket.onerror = function (e){ - console.error(e); - failedCount+=1; - clearTimeout(connecting); - connecting = setTimeout(function(){connect();},100*failedCount); - }; - - socket.onopen = function (){ - failedCount = 0; - try{ - var settings = {}; - socket.send(JSON.stringify({"join":session.whipView})); - } catch(e){ - connecting = setTimeout(function(){connect();},1); - } - }; - - socket.addEventListener('message', async function (event) { - if (event.data){ - - var data = JSON.parse(event.data); - - if ("sdp" in data){ - var resp = await processWHIP(data); - if (resp){ - var ret = {}; - var get = data.get; - data = {}; - if (get){ - data.get = get; - data.result = resp; - ret.callback = data; - log(ret); - socket.send(JSON.stringify(ret)); - } - } - } else if ("delete" in data){ - warnlog("WHIP Client is actively disconnecting"); - // session.closeRPC(i, true); - } - } - }); - } - connect(); -} - -async function processWHIP(data){ // LISTEN FOR REMOTE WHIP - var msg = {}; - msg.description = {}; - msg.description.type = "offer"; - msg.description.sdp = data.sdp; - // msg.session = session.generateRandomString(5); - msg.UUID = session.generateRandomString(25); // fake - - if (data.streamID){ - msg.streamID = data.streamID; - } else { - msg.streamID = session.generateRandomString(15); // fake - } - log("setupIncoming"); - await session.setupIncoming(msg); // could end up setting up the peer the wrong way. - - try { - // session.rpcs[msg.UUID].addTransceiver('video', {direction: 'recvonly'}); - // session.rpcs[msg.UUID].addTransceiver('audio', {direction: 'recvonly'}); - } catch(e){errorlog(e);} - - session.rpcs[msg.UUID].whip = true; - var callback = null; - var promise = new Promise((resolve, reject) => { - callback = resolve; - }); - session.rpcs[msg.UUID].whipCallback = callback; - - var callback2 = null; - var promise2 = new Promise((resolve, reject) => { - callback2 = resolve; - }); - session.rpcs[msg.UUID].whipCallback2 = callback2; - - log("CONNECT PEEER"); - session.connectPeer(msg); - log("CONNECT PEEER DONE"); - - if (!session.manual || !session.director){ - window.onresize = updateMixer; - window.onorientationchange = function(){ - setTimeout(updateMixer, 200); - }; - } - - if ((session.roomid === false) && !session.permaid){ - getById("header").classList.add("hidden"); - } - - log("ICE BUNDLE PROMISE"); - setTimeout(function(UUID){ - log("ICE BUNDLE PROMISE TIMEOUT"); - if (session.rpcs[UUID].whipCallback2){ - session.rpcs[UUID].whipCallback2([...session.rpcs[UUID].iceBundle]); - clearTimeout(session.rpcs[UUID].iceTimer); - session.rpcs[UUID].iceTimer = null; - session.rpcs[UUID].iceBundle = [] - session.rpcs[UUID].whipCallback2 = null; - - } - },3000,msg.UUID); - var iceBundle = await promise2; // waiting for ICE GATHER COMPLETE - session.rpcs[msg.UUID].whipCallback2 = null; - - log("ICE BUNDLE DONE"); - log(iceBundle); - - await promise; - session.rpcs[msg.UUID].whipCallback = null; - sdpAnswer = session.rpcs[msg.UUID].localDescription.sdp; - - var insertIce = ""; - iceBundle.forEach(ice=>{ - if (ice.candidate){ - insertIce += "a="+ice.candidate+"\r\n"; - } - }); - sdpAnswer = sdpAnswer.replace("a=ice-ufrag", insertIce+"a=ice-ufrag"); - - //if (session.stereo){ - // sdpAnswer = CodecsHandler.setOpusAttributes(sdpAnswer, {stereo:1}, true); - //} - - if (sdpAnswer.includes("sendrecv")){ - errorlog("Should not include sendrecv"); - sdpAnswer = sdpAnswer.replace("a=sendrecv","a=recvonly"); - sdpAnswer = sdpAnswer.replace("v=sendrecv","v=recvonly"); - } - - log("completed"); - warnlog(sdpAnswer); - - return sdpAnswer; // return SDP answer for the remote WHIP request -} - -async function whepIn(whepInput=false,whepInputToken=false, UUID=false){ // PLAY WHEP - var candidates = []; - var responseLocation = false; - if (!UUID){ - UUID = "whep_"+session.generateRandomString(25); // fake - } - - whepInput = whepInput || session.whepInput; - if (!whepInput){ - errorlog("no whepInput"); - return; - } - whepInputToken = whepInputToken || session.whepInputToken; - - async function whepConnect(){ - try { - - if (!session.configuration){ - await chooseBestTURN(); - } - - if (!(UUID in session.rpcs)){ - session.rpcs[UUID] = {}; - session.rpcs[UUID].stats = {}; - session.rpcs[UUID].allowGraphs = false; - session.rpcs[UUID].inboundAudioPipeline = {}; - session.rpcs[UUID].channelOffset = false; - session.rpcs[UUID].channelWidth = false; - session.rpcs[UUID].settings = false; - session.rpcs[UUID].lockedVideoBitrate = false; // doesn't do anything - session.rpcs[UUID].lockedAudioBitrate = false; - session.rpcs[UUID].manualBandwidth = false; // doesn't do anything, except maybe help keep track of pause/play states - session.rpcs[UUID].motionDetectionInterval = false; - } - var config = {...session.configuration}; - - if (whepInput.includes("cloudflare")){ - config.iceTransportPolicy = "relay"; // oof. Doesn't work with Cloudflare without this? - } - - try { - session.rpcs[UUID].whep = new RTCPeerConnection(config); - } catch(err){ - errorlog(err); - if (!session.cleanOutput){ - warnUser("An RTC error occured"); - } - } - - var video = true; - var audio = true; - - if ((session.novideo !== false) && (!session.novideo.includes(session.rpcs[UUID].streamID))){ - video = false; - } else if (session.rpcs[UUID].settings && !session.rpcs[UUID].settings.video){ - video = false; - } - if ((session.noaudio !== false) && (!session.noaudio.includes(session.rpcs[UUID].streamID))){ - audio = false; - } else if (session.rpcs[UUID].settings && !session.rpcs[UUID].settings.audio){ - audio = false; - } - - if (!audio && !video){ - errorlog("We will not request the whep source as no audio or video is requested"); - return; - } - - if (!session.manual || !session.director){ - window.onresize = updateMixer; - window.onorientationchange = function(){ - setTimeout(updateMixer, 200); - }; - } - - try { - if (video){ - session.rpcs[UUID].whep.addTransceiver('video', {direction: 'recvonly'}); - } - if (audio){ - session.rpcs[UUID].whep.addTransceiver('audio', {direction: 'recvonly'}); - } - } catch(e){errorlog(e);} - - session.rpcs[UUID].whep.ontrack = function(event) { - warnlog("TRACK INBOUND!"); - warnlog(event); - session.onTrack(event, UUID); - }; - - } catch(err){ - errorlog(err); - if (!session.cleanOutput){ - warnUser("An RTC error occured"); - } - } - - session.rpcs[UUID].whep.onnegotiationneeded = requestStream; // bug: https://groups.google.com/forum/#!topic/discuss-webrtc/3-TmyjQ2SeE - - session.rpcs[UUID].whep.onicecandidate = function(event){ //event - if (event.candidate==null){ - log("END OF ICE CANDIDATES"); - return; - } - //log(event.candidate); - candidates.push(event.candidate); - }; - - log("onnegotiationneeded event setup"); - } - - var requestingStream = false; - function requestStream(event){ - if (requestingStream){ - log(event); - errorlog("onnegotiationneeded again?"); - return; - } - requestingStream = true; - warnlog("ON NEGO NEEDED"); - warnlog(event); - - try { - session.rpcs[UUID].whep.createOffer().then(async function(offer){ - return session.rpcs[UUID].whep.setLocalDescription(offer); - }).then(async function() { - //log(session.rpcs[UUID].whep.localDescription); - await sleep(6000); - var sdp = session.rpcs[UUID].whep.localDescription.sdp; - if (sdp.includes("sendrecv")){ - errorlog("Should not include sendrecv"); - sdp = sdp.replace("a=sendrecv","a=recvonly"); - sdp = sdp.replace("v=sendrecv","v=recvonly"); - } - ajax(sdp, "sdp"); - }).catch(function(err){}); - } catch(e){errorlog(e);} - } - - function ajax(dataPayload, type, callback=false){ - //log(dataPayload); - try { - var xhttp = new XMLHttpRequest(); - xhttp.onreadystatechange = function() { - if (this.readyState == 4 && (this.status == 200 || this.status == 201)) { - - var contentType = this.getResponseHeader('content-type'); - responseLocation = this.getResponseHeader('location'); - - - if (contentType.startsWith("application/sdp")){ - var jsep = {}; - jsep.sdp = this.responseText; - jsep.type = "answer"; - - warnlog("Processing answer:"); - - session.rpcs[UUID].whep.setRemoteDescription(jsep).then(function(){ - warnlog("SHOULD BE CONNECTED?"); - /* var content = ""; - while (candidates.length){ - var candidate = candidates.pop(); - content += candidate.candidate; - } - if (content){ - ajax(content, "trickle-ice-sdpfrag", function(){ - }); - } */ - }).catch(function(e){log(e);}); - - } else if (contentType == "application/error"){ - if (this.responseText==432){ - warnUser("Whep in error: 432"); - } else { - warnUser("Unknown Whep In error"); - } - } else if (callback){ - callback(); - } - } - }; - if (type==="trickle-ice-sdpfrag"){ - if (responseLocation){ - xhttp.open("PATCH", whepInput, true); - } else { - xhttp.open("PATCH", whepInput, true); - } - } else { - xhttp.open("POST", whepInput, true); - } - xhttp.setRequestHeader('Content-Type', 'application/'+type); - - if (whepInputToken){ - xhttp.setRequestHeader('Authorization', 'Bearer ' + whepInputToken); - } - xhttp.onerror = function(e) { - errorlog(e); - warnUser("Whep in failed."); - }; - - xhttp.send(dataPayload); - - } catch(e){errorlog(e);} - } - - whepConnect(); - return UUID; -} -//////// -function whepOut(){ // publish to whep.vdo.ninja with obs, to use. experimental - if (!session.whepHost){return;} - warnlog("WHEP Client started"); - - var socket = null; - var connecting = false; - var failedCount = 0; - - function connect(){ - clearTimeout(connecting); - if (socket){ - if (socket.readyState === socket.OPEN){return;} - try{ - socket.close(); - } catch(e){} - } - log("Trying to load whep websocket..."); - - socket = new WebSocket("wss://whep.vdo.ninja:81"); - - socket.onclose = function (){ - failedCount+=1; - clearTimeout(connecting); - connecting = setTimeout(function(){connect();},100*(failedCount-1)); - - }; - - socket.onerror = function (e){ - console.error(e); - failedCount+=1; - clearTimeout(connecting); - connecting = setTimeout(function(){connect();},100*failedCount); - }; - - socket.onopen = function (){ - failedCount = 0; - try{ - var settings = {}; - socket.send(JSON.stringify({"join":session.whepHost})); - } catch(e){ - connecting = setTimeout(function(){connect();},1); - } - }; - - socket.addEventListener('message', async function (event) { - if (event.data){ - - var data = JSON.parse(event.data); - - if ("sdp" in data){ - var resp = await processWHEPout(data); - if (resp){ - var ret = {}; - var get = data.get; - data = {}; - if (get){ - data.get = get; - data.result = resp; - ret.callback = data; - log(ret); - socket.send(JSON.stringify(ret)); - } - } - } else if ("delete" in data){ - warnlog("WHIP Client is actively disconnecting"); - // session.closeRPC(i, true); - } - } - }); - } - connect(); -} - -async function processWHEPout(data){ // LISTEN FOR REMOTE WHIP - var msg = {}; - msg.description = {}; - msg.description.type = "offer"; - msg.description.sdp = data.sdp; - // msg.session = session.generateRandomString(5); - msg.UUID = session.generateRandomString(25); // fake - - log("setupoutgoing"); - - try { - //if (session.meshcast!=="video"){ - var tracks = false; - if (session.videoElement && session.videoElement.srcObject){ - tracks = session.videoElement.srcObject.getAudioTracks(); - } - var streamsource = false; - if (!tracks || !tracks.length){ - var audioCtx = new AudioContext(); - streamsource = destination.stream; - var destination = audioCtx.createMediaStreamDestination(); - destination.stream.getAudioTracks().forEach(trk=>{ - tracks = trk; - }); - } else { - tracks = tracks[0]; - streamsource = session.videoElement.srcObject; - } - - if (session.audioContentHint && (tracks.kind === "audio")){ - try { - tracks.contentHint = session.audioContentHint; - } catch(e){ - errorlog(e); - } - } - - if (tracks){ - try { - session.whipOut.addTransceiver(tracks, { - streams: [ streamsource ], - direction: 'sendonly' - }); - } catch(e){ - errorlog(e); - session.whipOut.addTrack(tracks); - } - } - //} - /////// - - //// video tracks - //if (session.meshcast!=="audio"){ - var tracks = false; - if (session.videoElement && session.videoElement.srcObject){ - tracks = session.videoElement.srcObject.getVideoTracks(); - } - //// - - //if (!tracks || !tracks.length){ - // tracks = getMeshcastCanvasTrack(); - //} else { - tracks = tracks[0]; - //} - - if (session.screenShareState && session.screenshareContentHint && (tracks.kind === "video")){ - try { - tracks.contentHint = session.screenshareContentHint; - } catch(e){ - errorlog(e); - } - } else if (session.contentHint && (tracks.kind === "video")){ - try { - tracks.contentHint = session.contentHint; - } catch(e){ - errorlog(e); - } - } - if (tracks){ - try { - session.whipOut.addTransceiver(tracks, { - streams: [ session.videoElement.srcObject ], - direction: 'sendonly' - }); - } catch(e){ - errorlog(e); - session.whipOut.addTrack(tracks); - } - } - //} - - session.whipOut.onnegotiationneeded = publish; // bug: https://groups.google.com/forum/#!topic/discuss-webrtc/3-TmyjQ2SeE - - session.whipOut.onicecandidate = function(event){ //event - if (event.candidate==null){ - log("END OF ICE CANDIDATES"); - return; - } - //log(event.candidate); - candidates.push(event.candidate); - }; - - } catch(e){errorlog(e);} - - session.rpcs[msg.UUID].whip = true; - var callback = null; - var promise = new Promise((resolve, reject) => { - callback = resolve; - }); - session.rpcs[msg.UUID].whipCallback = callback; - - var callback2 = null; - var promise2 = new Promise((resolve, reject) => { - callback2 = resolve; - }); - session.rpcs[msg.UUID].whipCallback2 = callback2; - - log("CONNECT PEEER"); - session.connectPeer(msg); - log("CONNECT PEEER DONE"); - - if (!session.manual || !session.director){ - window.onresize = updateMixer; - window.onorientationchange = function(){ - setTimeout(updateMixer, 200); - }; - } - - log("ICE BUNDLE PROMISE"); - setTimeout(function(UUID){ - log("ICE BUNDLE PROMISE TIMEOUT"); - if (session.rpcs[UUID].whipCallback2){ - session.rpcs[UUID].whipCallback2([...session.rpcs[UUID].iceBundle]); - clearTimeout(session.rpcs[UUID].iceTimer); - session.rpcs[UUID].iceTimer = null; - session.rpcs[UUID].iceBundle = [] - session.rpcs[UUID].whipCallback2 = null; - - } - },3000,msg.UUID); - var iceBundle = await promise2; // waiting for ICE GATHER COMPLETE - session.rpcs[msg.UUID].whipCallback2 = null; - - log("ICE BUNDLE DONE"); - log(iceBundle); - - await promise; - session.rpcs[msg.UUID].whipCallback = null; - sdpAnswer = session.rpcs[msg.UUID].localDescription.sdp; - - var insertIce = ""; - iceBundle.forEach(ice=>{ - if (ice.candidate){ - insertIce += "a="+ice.candidate+"\r\n"; - } - }); - sdpAnswer = sdpAnswer.replace("a=ice-ufrag", insertIce+"a=ice-ufrag"); - - if (sdpAnswer.includes("sendrecv")){ - errorlog("Should not include sendrecv"); - sdpAnswer = sdpAnswer.replace("a=sendrecv","a=recvonly"); - sdpAnswer = sdpAnswer.replace("v=sendrecv","v=recvonly"); - } - - log("completed"); - warnlog(sdpAnswer); - - return sdpAnswer; // return SDP answer for the remote WHIP request -} -///// -function pokePostAPI(action, data, streamID){ - var msg = {}; - msg.update = {}; - msg.update.streamID = streamID || session.streamID || null; - msg.update.action = action; - msg.update.value = data; - - try { - var xhttp = new XMLHttpRequest(); - xhttp.onreadystatechange = function() { - if (this.readyState == 4 && (this.status == 200 || this.status == 201)) { - log("good"); - } else { - warnlog("post api didn't work?"); - } - }; - xhttp.open("POST", session.postApi, true); - xhttp.setRequestHeader('Content-type', 'application/json'); - - xhttp.onerror = function(e) { - errorlog(e); - }; - xhttp.send(JSON.stringify(msg)); - - } catch(e){errorlog(e);} -} - - -var queuedSendingAPIMsgs = []; -function pokeAPI(action, data, streamID = null){ - - if (session.postApi){ - pokePostAPI(action, data, streamID); - } - - if (!session.api){return;} - - if (session.apiSocket){ - try { - var msg = {}; - msg.update = {}; - msg.update.streamID = streamID || session.streamID || null; - msg.update.action = action; - msg.update.value = data; - session.apiSocket.send(JSON.stringify(msg)); - } catch(e){ - errorlog(e); - } - } else if (session.apiSocket!==null){ - queuedSendingAPIMsgs.push([action, data, streamID]); - if (queuedSendingAPIMsgs.length>20){ - queuedSendingAPIMsgs.shift(); - } - } -} - -function oscClient(){ // api.vdo.ninja api OSC (websocket / https API hotkey support). The iFrame API method provides greater customization. - if (!session.api){return;} - warnlog("oscClient started"); - - var socket = null; - var connecting = false; - var failedCount = 0; - - function connect(){ - clearTimeout(connecting); - if (socket){ - if (socket.readyState === socket.OPEN){return;} - try{ - socket.close(); - } catch(e){} - } - socket = new WebSocket(session.apiserver); - - socket.onclose = function (){ - session.apiSocket = false; - failedCount+=1; - clearTimeout(connecting); - connecting = setTimeout(function(){connect();},100*(failedCount-1)); - - }; - - socket.onerror = function (){ - failedCount+=1; - clearTimeout(connecting); - connecting = setTimeout(function(){connect();},100*failedCount); - }; - - socket.onopen = function (){ - failedCount = 0; - try{ - socket.send(JSON.stringify({"join":session.api})); - session.apiSocket = socket; - if (queuedSendingAPIMsgs.length){ - queuedSendingAPIMsgs.forEach(msg=>{ - pokeAPI(msg[0],msg[1], msg[2]); - }); - queuedSendingAPIMsgs = []; - } - pokeAPI("details", getDetailedState(session.streamID)); - } catch(e){ - connecting = setTimeout(function(){connect();},1); - } - - }; - - socket.addEventListener('message', async function (event) { - if (event.data){ - - var data = JSON.parse(event.data); - - if ("msg" in data){ - data = data.msg - } - - if ("value" in data){ - if (("action" in data) && (data.action == "layout")){ - try { - data.value = JSON.parse(data.value) || data.value; - } catch(e){} - } - } - - var resp = processMessage(data); - if (resp!==null){ - var ret = {}; - data.result = resp; - ret.callback = data; - log(ret); - socket.send(JSON.stringify(ret)); - } - } - }); - } - connect(); -} - -function setupCommands(){ - var commands = {} - - commands.raisehand = function(value=null,value2=null){ - return raisehand(); - }; - commands.togglehand = function(value=null,value2=null){ - return raisehand(); - }; - commands.togglescreenshare = function(value=null,value2=null){ - toggleScreenShare(); - return session.screenShareState; - }; - commands.chat = function(value=null,value2=null){ - toggleChat(value); - return session.chat; - }; - commands.speaker = function(value=null,value2=null){ - if (value === true) { // unmute - session.speakerMuted = false; // set - toggleSpeakerMute(true); // apply - } else if (value === false) { // mute - session.speakerMuted = true; // set - toggleSpeakerMute(true); // apply - } else if (value === "toggle") { // toggle - toggleSpeakerMute(); - } - return session.speakerMuted; - }; // mute speaker - commands.mic = function(value=null,value2=null){ - if (value === true) { // unmute - session.muted = false; // set - log(session.muted); - toggleMute(true); // apply - } else if (value === false) { // mute - session.muted = true; // set - log(session.muted); - toggleMute(true); // apply - } else if (value === "toggle") { // toggle - toggleMute(); - } - return session.muted; - }; - commands.camera = function(value=null,value2=null){ - if (value === true) { // unmute - session.videoMuted = false; // set - log(session.videoMuted); - toggleVideoMute(true); // apply - } else if (value === false) { // mute - session.videoMuted = true; // set - log(session.videoMuted); - toggleVideoMute(true); // apply - } else if (value === "toggle") { // toggle - toggleVideoMute(); - } - return session.videoMuted; - } - commands.hangup = function(value=null,value2=null){ - hangup(); - return true; - }; - commands.bitrate = function(value=null,value2=null){ - if (value===false){ - value = 0; - } else if (value===true){ - value = -1; - } else { - value = parseInt(value) || 0; - } - for (var i in session.rpcs) { - try { - session.requestRateLimit(value, i); - } catch (e) { - errorlog(e); - } - } - return value; - }; - - commands.getDetails = function(value=null,value2=null){ - return getDetailedState(); - } - - commands.getGuestList = function(value=null,value2=null){ - return getGuestList(); - } - - commands.reload = function(value=null,value2=null){ - reloadRequested(); - return true; - }; - commands.volume = function(value=null,value2=null){ - if (value===false){ - value = 0; - } else if (value===true){ - value = 100 - } else { - value = parseInt(value) || 0; - } - value = parseFloat(value/100); - for (var i in session.rpcs) { - try { - session.rpcs[i].videoElement.volume = parseFloat(value); - } catch (e) { - errorlog(e); - } - } - return value; - }; - - commands.forceKeyframe = function(value=null,value2=null){ - return session.forcePLI(); - }; - - commands.panning = function(value=null,value2=null){ - if (value===false){ - value = 90; - } else if (value===true){ - value = 90 - } else { - value = parseInt(value); - } - for (var uuid in session.rpcs) { - try { - adjustPan(uuid, value); // &panning needs to be added to enable. playback only; not mic out. - } catch (e) { - errorlog(e); - } - } - return value; - }; - - commands.record = function(value=null,value2=null){ - - if (!session.videoElement){return;} - - if (value === false) { // mute - if ("recording" in session.videoElement) { - recordLocalVideo("stop"); - } - } else if (value === true){ - if ("recording" in session.videoElement) { - // already recording - } else { - recordLocalVideo("start"); - } - } - return value; - }; - - commands.group = function(value=null,value2=null){ - if (value && (value !== "null")){ - return changeGroupDirectorAPI(value); - } - return false; - }; - - commands.joinGroup = function(value=null,value2=null){ - if (value && (value !== "null")){ - return changeGroupDirectorAPI(value, true); - } - return false; - }; - - commands.leaveGroup = function(value=null,value2=null){ - if (value && (value !== "null")){ - return changeGroupDirectorAPI(value, false); - } - return false; - }; - - commands.viewGroup = function(value=null,value2=null){ - if (value && (value !== "null")){ - return changeGroupViewDirectorAPI(value); - } - return false; - }; - - commands.joinViewGroup = function(value=null,value2=null){ - if (value && (value !== "null")){ - return changeGroupViewDirectorAPI(value, true); - } - return false; - }; - - commands.leaveViewGroup = function(value=null,value2=null){ - if (value && (value !== "null")){ - return changeGroupViewDirectorAPI(value, false); - } - return false; - }; - - commands.sendChat = function(value=null,value2=null){ - sendChat(value); - // sendChatMessage // this would add it to the chat message - return true; - }; - - commands.startRoomTimer = function(value=null,value2=null){ - getById("globalTimerDirectorToggle").value = 0; // reset - directRoomTimer(getById("globalTimerDirectorToggle"), false, value); - return true; - }; - - commands.pauseRoomTimer = function(value=null,value2=null){ - if (getById("globalTimerDirectorToggle").value == 3){ - directRoomTimer(getById("globalTimerDirectorToggle"), {ctrlKey:true}, value); - } else { - directRoomTimer(getById("globalTimerDirectorToggle"), {ctrlKey:true}, value); - } - return true; - }; - - commands.stopRoomTimer = function(value=null,value2=null){ - getById("globalTimerDirectorToggle").value = 1; // pause - directRoomTimer(getById("globalTimerDirectorToggle"), false, value); - return true; - }; - - commands.prevSlide = function(value=null,value2=null){ - var data = {}; - data.d = [176, 110, 10]; - playbackMIDI(data); - return true; - }; - - commands.nextSlide = function(value=null,value2=null){ - var data = {}; - data.d = [176, 110, 11]; - playbackMIDI(data); - return true; - }; - - commands.nextSlide = function(value=null,value2=null){ - var data = {}; - data.d = [176, 110, 11]; - playbackMIDI(data); - return true; - }; - - commands.soloVideo = function(value=null,value2=null){ - var element = getById("highlightDirector"); - if (value && (value == "toggle")){ - return requestInfocus(element); - } else if (value && (value !== "null")){ - return requestInfocus(element, null, true); - } else if (value && (value === "null")){ - return requestInfocus(element); - } else { - return requestInfocus(element, null, false); - } - return false; - }; - commands.highlight = function(value=null,value2=null){ - return commands.soloVideo(value, value2); - }; - - commands.layout = function(value=null,value2=null){ - try { - if (parseInt(value)==value){ - value = parseInt(value); - if (value ==0){ - value = false; - } else { - value -= 1; - } - } else if (typeof value === "object"){ - session.layout = value; - pokeIframeAPI("layout-updated", session.layout); - if (session.director){ - var combined = {}; - for (var i=0;i 110){ - var guestslot = command-111; - if (value == 0) { - var ele = getRightOrderedElement('[data-action-type="forward"][data--u-u-i-d]', guestslot); - if (ele) { - directMigrate(ele, true); - } - } else if (value == 1) { - var ele = getRightOrderedElement('[data-action-type="addToScene"][data-scene="1"][data--u-u-i-d]', guestslot); - if (ele) { - directEnable(ele, true); - } - } else if (value == 2) { - var ele = getRightOrderedElement('[data-action-type="mute-scene"][data--u-u-i-d]', guestslot); - if (ele) { - directMute(ele, true); - } - } else if (value == 3) { - var ele = getRightOrderedElement('[data-action-type="mute-guest"][data--u-u-i-d]', guestslot); - if (ele) { - remoteMute(ele, true); - } - } else if (value == 4) { - var ele = getRightOrderedElement('[data-action-type="hangup"][data--u-u-i-d]', guestslot); - if (ele) { - directHangup(ele, true); - } - } else if (value == 5) { - var ele = getRightOrderedElement('[data-action-type="solo-chat"][data--u-u-i-d]', guestslot); - if (ele) { - session.toggleSoloChat(ele.dataset.UUID); - } - } else if (value == 6) { - var ele = getRightOrderedElement('[data-action-type="toggle-remote-speaker"][data--u-u-i-d]', guestslot); - if (ele) { - remoteSpeakerMute(ele); - } - } else if (value == 7) { - var ele = getRightOrderedElement('[data-action-type="toggle-remote-display"][data--u-u-i-d]', guestslot); - if (ele) { - remoteDisplayMute(ele); - } - } else if (value == 8) { - var ele = getRightOrderedElement('[data-action-type="force-keyframe"][data--u-u-i-d]', guestslot); - if (ele) { - requestKeyframeScene(ele); - } - } else if (value == 12) { - var ele = getRightOrderedElement('[data-action-type="addToScene"][data-scene="2"][data--u-u-i-d]', guestslot); - if (ele) { - directEnable(ele, true); - } - } else if (value == 13) { - var ele = getRightOrderedElement('[data-action-type="addToScene"][data-scene="3"][data--u-u-i-d]', guestslot); - if (ele) { - directEnable(ele, true); - } - } else if (value == 14) { - var ele = getRightOrderedElement('[data-action-type="addToScene"][data-scene="4"][data--u-u-i-d]', guestslot); - if (ele) { - directEnable(ele, true); - } - } else if (value == 15) { - var ele = getRightOrderedElement('[data-action-type="addToScene"][data-scene="5"][data--u-u-i-d]', guestslot); - if (ele) { - directEnable(ele, true); - } - } else if (value == 16) { - var ele = getRightOrderedElement('[data-action-type="addToScene"][data-scene="6"][data--u-u-i-d]', guestslot); - if (ele) { - directEnable(ele, true); - } - } else if (value == 17) { - var ele = getRightOrderedElement('[data-action-type="addToScene"][data-scene="7"][data--u-u-i-d]', guestslot); - if (ele) { - directEnable(ele, true); - } - } else if (value == 18) { - var ele = getRightOrderedElement('[data-action-type="addToScene"][data-scene="8"][data--u-u-i-d]', guestslot); - if (ele) { - directEnable(ele, true); - } - } else if ((value => 27)) { - var ele = getRightOrderedElement('[data-action-type="volume"][data--u-u-i-d]', guestslot); - if (ele) { - ele.value = parseInt(value-27); - remoteVolume(ele); - } - } - } -} - -function sendRawMIDI(input, UUID=false, streamID=false){ - // session.sendRawMIDI(e.data.sendRawMIDI); - var msg = {}; - msg.midi = {}; - msg.midi.d = input.data; - - if ("timestamp" in input){ - msg.midi.s = input.timestamp; - } else { - msg.midi.s = Date.now(); // unix timestamp - } - - if (input.message && input.message.channel){ - msg.midi.c = input.message.channel; - } else if (input && input.channel){ - msg.midi.c = input.channel; - } - - if (UUID && session.pcs[UUID] && session.pcs[UUID].allowMIDI){ - session.sendMessage(msg, UUID); - } else if (UUID && session.rpcs[UUID] && session.rpcs[UUID].allowMIDI){ - session.sendRequest(msg, UUID); - } else if (streamID){ - for (var UID in session.rpcs){ - if (session.rpcs[UID].allowMIDI && (session.rpcs[UID].streamID === streamID)){ // specific to gstreamer code aplication - session.sendRequest(msg, UID) - return; // only one stream ID should match - } - } - } else { - var list = []; - for (var UID in session.pcs){ - if (session.pcs[UID].allowMIDI){ - if (session.sendMessage(msg, UID)){ - list.push(UID); - } - } - } - for (var UID in session.rpcs){ - if (session.rpcs[UID].allowMIDI){ // specific to gstreamer code aplication - if (!list.includes(UID)){ - session.sendRequest(msg, UID) - } - } - } - } -} -function playOutMidi(msg){ - console.log("Playing out remotely sourced MIDI"); - if (session.midiIn===true){ - if ("d" in msg){ - for (var i in WebMidi.outputs){ - try { - if ("c" in msg){ - WebMidi.outputs[i].channels[msg.c].send(msg.d); - } else { - WebMidi.outputs[i].send(msg.d); - } - } catch(e){errorlog(e);} - } - } - } else if (session.midiIn==parseInt(session.midiIn)){ - try { - var i = parseInt(session.midiIn)-1; - if ("d" in msg){ - if ("c" in msg){ - WebMidi.outputs[i].channels[msg.c].send(msg.d); - } else { - WebMidi.outputs[i].send(msg.d); - } - } - } catch(e){errorlog(e);}; - } -} -function playbackMIDI(msg, unsafe=false){ - if (session.midiIn===false && session.midiRemote===false){return;} // just in case; security - else if ((session.midiOut===session.midiIn) && (session.midiRemote===false)){return;} // avoid feedback loops - - //msg.midi.d = e.data; - //msg.midi.s = e.timestamp; - //msg.midi.t = e.type; - if (session.midiDelay && ("t" in msg)){ - var timeDelay = session.midiDelay - (Date.now() - msg.t); - if (timeDelay<=0){ - playOutMidi(msg); - } else { - setTimeout(function(msg){playOutMidi(msg)},timeDelay,msg); - } - } else { - playOutMidi(msg); - } - - if (unsafe){return;} // I don't know how midi remote works in reverse, so lets ignore it - - if (session.midiRemote==4){ - if (msg.d[0] == 176){ - midiHotkeysCommand(msg.d[1], msg.d[2]); - } - } else if (session.midiRemote==1 || session.midiRemote==2 || session.midiRemote==3){ - if (msg.d[0] == 156){ - if (msg.d[1] == 33){ - midiHotkeysNote("A1", msg.d[2]); - } else if (msg.d[1] == 55){ - midiHotkeysNote("G3", msg.d[2]); - } else if (msg.d[1] == 57){ - midiHotkeysNote("A3", msg.d[2]); - } else if (msg.d[1] == 59){ - midiHotkeysNote("B3", msg.d[2]); - } else if (msg.d[1] == 60){ - midiHotkeysNote("C4", msg.d[2]); - } else if (msg.d[1] == 62){ - midiHotkeysNote("D4", msg.d[2]); - } else if (msg.d[1] == 64){ - midiHotkeysNote("E4", msg.d[2]); - } else if (msg.d[1] == 65){ - midiHotkeysNote("F4", msg.d[2]); - } else if (msg.d[1] == 67){ - midiHotkeysNote("G4", msg.d[2]); - } else if (msg.d[1] == 69){ - midiHotkeysNote("A4", msg.d[2]); - } else if (msg.d[1] == 43){ - midiHotkeysNote("G2", msg.d[2]); - } else if (msg.d[1] == 35){ - midiHotkeysNote("B1", msg.d[2]); - } else if (msg.d[1] == 36){ - midiHotkeysNote("C2", msg.d[2]); - } else if (msg.d[1] == 38){ - midiHotkeysNote("D2", msg.d[2]); - } else if (msg.d[1] == 40){ - midiHotkeysNote("E2", msg.d[2]); - } else if (msg.d[1] == 41){ - midiHotkeysNote("F2", msg.d[2]); - } else if (msg.d[1] == 24){ - midiHotkeysNote("C1", msg.d[2]); - } - } - } - //var output = WebMidi.getOutputById("123456789"); - //output = WebMidi.getOutputByName("Axiom Pro 25 Ext Out"); - //output = WebMidi.outputs[0]; -} - -function addEventToAll(targets, trigger, callback) { // js helper - const target = document.querySelectorAll(targets); - var triggers = trigger.split(" "); - for (let i = 0; i < target.length; i++) { - for (let j = 0; j < triggers.length; j++) { - setTimeout(function(t1,t2){ - t1.addEventListener(t2, function(e) { - callback(e, t1); - }); - },0,target[i],triggers[j]); - } - } -} - -function insertAfter(newNode, existingNode) { - existingNode.parentNode.insertBefore(newNode, existingNode.nextSibling); -} -addEventToAll(".column", 'click', function(e, ele) { - if (ele.classList.contains("skip-animation")) { - return; - } - try { - var bounding_box = ele.getBoundingClientRect(); - } catch(e){ - return; - } - ele.style.top = bounding_box.top + "px"; - ele.style.left = (bounding_box.left - 20) + "px"; - ele.classList.add('in-animation'); - ele.classList.remove('pointer'); - ele.classList.remove('rounded'); - - if (document.getElementById("empty-container")) { - getById("empty-container").parentNode.removeChild(getById("empty-container")); - } - var empty = document.createElement("DIV"); - empty.id = "empty-container"; - empty.className = "column"; - ele.parentNode.insertBefore(empty, ele.nextSibling); - const styles = "\ - @keyframes outlightbox {\ - 0% {\ - height: 100%;\ - width: 100%;\ - top: 0px;\ - left: 0px;\ - }\ - 50% {\ - height: 200px;\ - top: " + bounding_box.y + "px;\ - }\ - 100% {\ - height: 200px;\ - width: " + bounding_box.width + "px;\ - top: " + bounding_box.y + "px;\ - left: " + bounding_box.x + "px;\ - }\ - }\ - "; - if (document.getElementById('lightbox-animations')) { - getById("lightbox-animations").innerHTML = styles; - } - document.body.style.overflow = "hidden"; -}); -addEventToAll(".close", 'click', function(e, ele) { - cleanupMediaTracks(); - - document.querySelectorAll(".hidden2").forEach(ele2=>{ - ele2.classList.remove("hidden2"); - }); - - ele.style.display = "none"; - mapToAll(".container-inner", function(target) { - target.style.display = "none"; - }); - document.body.style.overflow = "auto"; - var bounding_box = getById("empty-container").parentNode.getBoundingClientRect(); - setTimeout(function() { // just smoothes things out; breathing room to clean up things first. - ele.parentNode.classList.add('out-animation'); - }, 1); - ele.parentNode.style.top = bounding_box.top + 'px'; - ele.parentNode.style.left = bounding_box.left + 'px'; - e.stopPropagation(); -}); -addEventToAll(".column", 'animationend', function(e, ele) { - if (e.animationName == 'inlightbox') { - ele.classList.add("skip-animation"); - mapToAll(".close", function(target) { - target.style.display = "block"; - }, ele); - document.querySelectorAll("#header, #miniTaskBarm, #credits, .columnfade").forEach(ele2=>{ - if (ele2!==ele){ - ele2.classList.add("hidden2"); - } - }); - mapToAll(".container-inner", function(target) { - target.style.display = "block"; - }, ele); - } else if (e.animationName == 'outlightbox') { - ele.classList.remove('in-animation'); - ele.classList.remove('out-animation'); - ele.classList.remove("skip-animation"); - ele.classList.remove('columnfade'); - ele.classList.add('pointer'); - ele.classList.add('rounded'); - getById("empty-container").parentNode.removeChild(getById("empty-container")); - getById("lightbox-animations").sheet.deleteRule(0); - } -}); -addEventToAll("#audioSource", 'mousedown touchend focusin focusout', function(e, ele) { - var state = getById('multiselect-trigger').dataset.state || 0; // Does this return TRU instead??. GAH. #TODO: - if (state == 0) { - getById('multiselect-trigger').dataset.state = 1; - getById('multiselect-trigger').classList.add('open'); - getById('multiselect-trigger').classList.remove('closed'); - mapToAll('.chevron', function(ele) { - ele.classList.remove('bottom'); - }, parentElement = getById('multiselect-trigger')); - mapToAll('.multiselect-contents', function(ele) { - ele.style.display = "block"; - mapToAll('input[type="checkbox"]', function(ele2) { - ele2.parentNode.style.display = "block"; - ele2.style.display = "inline-block"; - }, ele); - }, parentElement = getById('multiselect-trigger').parentNode); - } - e.stopPropagation(); - //e.preventDefault(); -}); -addEventToAll("#audioSource3", 'mousedown touchend focusin focusout', function(e, ele) { - var state = getById('multiselect-trigger3').dataset.state || 0; // Does this return TRU instead??. GAH. #TODO: - if (state == 0) { - getById('multiselect-trigger3').dataset.state = 1; - getById('multiselect-trigger3').classList.add('open'); - getById('multiselect-trigger3').classList.remove('closed'); - mapToAll(".chevron", function(target) { - target.classList.remove('bottom'); - }, getById('multiselect-trigger3')); - mapToAll(".multiselect-contents", function(target) { - target.style.display = "block"; - }, getById('multiselect-trigger3').parentNode); - mapToAll(".multiselect-contents", function(target) { - mapToAll('input[type="checkbox"]', function(target2) { - target2.style.display = "inline-block"; - target2.parentNode.style.display = "block"; - }, target); - }, getById('multiselect-trigger3').parentNode); - } - e.stopPropagation(); - //e.preventDefault(); -}); -addEventToAll("#multiselect-trigger", 'mousedown touchend focusin focusout', function(e, ele) { - var state = ele.dataset.state || 0; // Does this return TRU instead??. GAH. #TODO: - if (state == 0) { // open the dropdown - ele.dataset.state = 1; - ele.classList.add('open'); - ele.classList.remove('closed'); - mapToAll(".chevron", function(target) { - target.classList.remove('bottom'); - }, getById('multiselect-trigger')); - mapToAll(".multiselect-contents", function(target) { - target.style.display = "block"; - }, ele.parentNode); - mapToAll(".multiselect-contents", function(target) { - mapToAll('input[type="checkbox"]', function(target2) { - target2.style.display = "inline-block"; - target2.parentNode.style.display = "block"; - }, target); - }, ele.parentNode); - } else { // close the dropdown - ele.dataset.state = 0; - ele.classList.add('closed'); - ele.classList.remove('open'); - mapToAll(".chevron", function(target) { - target.classList.add('bottom'); - }, ele); - mapToAll(".multiselect-contents", function(target) { - mapToAll('input[type="checkbox"]', function(target2) { - target2.style.display = "none"; - if (!target2.checked) { - target2.parentNode.style.display = "none"; - } - }, target); - }, ele.parentNode); - } - e.preventDefault(); - e.stopPropagation(); -}); -addEventToAll("#multiselect-trigger3", 'mousedown touchend focusin focusout', function(e, ele) { - var state = ele.dataset.state || 0; // Does this return TRU instead??. GAH. #TODO: - if (state == 0) { // open the dropdown - ele.dataset.state = 1; - ele.classList.add('open'); - ele.classList.remove('closed'); - mapToAll(".chevron", function(target) { - target.classList.remove('bottom'); - }, ele); - mapToAll(".multiselect-contents", function(target) { - target.style.display = "block"; - }, ele.parentNode); - mapToAll(".multiselect-contents", function(target) { - mapToAll('input[type="checkbox"]', function(target2) { - target2.style.display = "inline-block"; - target2.parentNode.style.display = "block"; - }, target); - }, ele.parentNode); - } else { // close the dropdown - ele.dataset.state = 0; - ele.classList.add('closed'); - ele.classList.remove('open'); - mapToAll(".chevron", function(target) { - target.classList.add('bottom'); - }, ele); - mapToAll(".multiselect-contents", function(target) { - mapToAll('input[type="checkbox"]', function(target2) { - target2.style.display = "none"; - if (!target2.checked) { - target2.parentNode.style.display = "none"; - } - }, target); - }, ele.parentNode); - } - e.preventDefault(); - e.stopPropagation(); -}); - - -function getSenders2(UUID){ - var fixedSenders = []; - var isAlt = false; - if (!(UUID in session.pcs)){return fixedSenders;} - if ("realUUID" in session.pcs[UUID]){ - isAlt=true; - UUID = session.pcs[UUID].realUUID; - if (!(UUID in session.pcs)){return fixedSenders;} - } - var senders = session.pcs[UUID].getSenders(); - - if (isAlt){ - senders.forEach((sender)=>{ - if (sender.track && sender.track.id){ - if (sender.track.id in screenshareTracks) { // I'm not going to change track.kind, since OBS isn't part of this list - fixedSenders.push(sender); - } - } - }); - } else { - senders.forEach((sender)=>{ - if (sender.track && sender.track.id){ - if (!(sender.track.id in screenshareTracks)){ - fixedSenders.push(sender); - } - } - }); - } - - return fixedSenders; -} - -function getReceivers2(UUID){ - var fixedReceivers = []; - var isAlt = false; - var ssTracks = []; - if ("realUUID" in session.rpcs[UUID]){ - isAlt=true; - UUID = session.rpcs[UUID].realUUID; - if (!("screenIndexes" in session.rpcs[UUID])){ - errorlog("this is supposed to be a screen share, but no screen share index was found"); - return; - } - ssTracks = session.rpcs[UUID].screenIndexes; - } else if (("screenIndexes" in session.rpcs[UUID]) && session.rpcs[UUID].screenIndexes){ - ssTracks = session.rpcs[UUID].screenIndexes; - } - - var receivers = session.rpcs[UUID].getReceivers(); - - if (isAlt){ - for (var i=0;i { - resolve([]); - }); - } - } - - /* if (session.audioContentHint && tracks.length){ - tracks.forEach(trk=>{ - try { - - trk.contentHint = session.audioContentHint; - } catch(e){ - errorlog(e); - } - }); - } */ - - var senders = getSenders2(UUID+"_screen"); - var tracks = session.screenStream.getTracks(); - - for (var i=0;i= 3) { // lowest - video.width = { - ideal: 320 - }; - video.height = { - ideal: 180 - }; - } - - if (session.width) { - video.width = { - ideal: session.width - }; - } - if (session.height) { - video.height = { - ideal: session.height - }; - } - - var constraints = { // this part is a bit annoying. Do I use the same settings? I can add custom setting controls here later - audio: { - echoCancellation: true, // we want to cancel echo, since this is a secondary stream - autoGainControl: false, - noiseSuppression: false - }, - video: video - //,cursor: {exact: "none"} - }; - - try { - let supportedConstraints = navigator.mediaDevices.getSupportedConstraints(); - if (supportedConstraints.cursor) { - if (session.screensharecursor){ - constraints.video.cursor = ["always", "motion"]; - } else { - constraints.video.cursor = "never"; - } - } - if (session.suppressLocalAudioPlayback && supportedConstraints.suppressLocalAudioPlayback){ - constraints.audio.suppressLocalAudioPlayback = true; - } - // - if (session.preferCurrentTab){ - constraints.preferCurrentTab = true; - } - if (session.selfBrowserSurface){ - constraints.selfBrowserSurface = session.selfBrowserSurface; // exclude or include - } - if (session.surfaceSwitching){ - constraints.surfaceSwitching = session.surfaceSwitching; // exclude or include - } - if (session.systemAudio){ - constraints.systemAudio = session.systemAudio; // exclude or include - } - if (session.displaySurface && supportedConstraints.displaySurface){ - constraints.video.displaySurface = session.displaySurface; // monitor, window, or browser - } - } catch(e){ - warnlog("navigator.mediaDevices.getSupportedConstraints() not supported"); - } - - if (session.echoCancellation === false) { - constraints.audio.echoCancellation = false; - } - if (session.autoGainControl === true) { - constraints.audio.autoGainControl = true; - } - if (session.noiseSuppression === true) { - constraints.audio.noiseSuppression = true; - } - //if (audio == false) { - // constraints.audio = false; - //} - - - var overrideFramerate = false; - if ((session.frameRate !== false) && (session.maxframeRate != false)){ - overrideFramerate = session.frameRate; - constraints.video.frameRate = { - ideal: session.maxframeRate, - max: session.maxframeRate - }; - } else if (session.frameRate !== false) { - constraints.video.frameRate = session.frameRate; - } else if (session.maxframeRate != false){ - constraints.video.frameRate = { - ideal: session.maxframeRate, - max: session.maxframeRate - }; - } else { - constraints.video.frameRate = { - ideal: 60 - }; - } - - if (session.screenshareVideoOnly){ - constraints.audio = false; - } - - if (session.forceAspectRatio){ // await updateCameraConstraints("aspectRatio", session.forceAspectRatio); - if (constraints.video && constraints.video!==true){ - - - constraints.video.aspectRatio = { ideal: parseFloat(session.forceAspectRatio)}; - - - if (constraints.video.width && !session.width){ - delete constraints.video.width; - } else if (constraints.video.height && !session.height){ - delete constraints.video.height; - } - } - } - - if ((constraints.video!==false) && (Object.keys(constraints.video).length==0)){ - constraints.video = true; - } - - if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1) { - if (!ElectronDesktopCapture){ - if (!(session.cleanOutput)) { - warnUser("Enable Elevated Privileges to allow screen-sharing. (right click this window to see that option)"); - } - return false; - } - } - - log("sstype3 screen share"); - log(constraints); - - navigator.mediaDevices.getDisplayMedia(constraints).then(async function(stream) { - - - try { - var constraint = {}; - if (session.forceAspectRatio && (session.forceScreenShareAspectRatio===null)){ - constraint.aspectRatio = parseFloat(session.forceAspectRatio); - } else if (session.forceScreenShareAspectRatio){ - constraint.aspectRatio = parseFloat(session.forceScreenShareAspectRatio); - } - if (overrideFramerate){ - constraint.frameRate = overrideFramerate; - } - if (Object.keys(constraint).length){ - await stream.getVideoTracks()[0].applyConstraints({ - advanced: [constraint] - }); - log({ - advanced: [constraint] - }); - } - } catch(e){errorlog(e);} - - - session.screenShareState = true; - session.screenStream = stream; - pokeIframeAPI('screen-share-state', session.screenShareState, null, session.streamID); - - //if (!session.screenVideoElement){ - // session.screenVideoElement = createVideoElement() - //} - try { - stream.getVideoTracks()[0].onended = function () { - stopSecondScreenshare(); - }; - } catch(e){log("No Video selected; screensharing?");} - - session.screenStream.getTracks().forEach(function(track){ - screenshareTracks[track.id] = true; // obs isn't included, so no point to check track.kind - }); - for (UUID in session.pcs){ - createSecondStream2(UUID); - } - - if (!firsttime){ - var msg = {}; - msg.screenStopped = false; - session.sendMessage(msg); - } else if (!session.screenShareElement){ - session.screenShareElement = createVideoElement(); - session.screenShareElement.muted = true; - session.screenShareElement.autoplay = true; - session.screenShareElement.controls = session.showControls || false; - - session.screenShareElement.id = "screensharesource"; - session.screenShareElement.dataset.sid = session.streamID + ":s"; - - if (typeof session.volume == "number"){ - session.screenShareElement.volume = session.volume; - } else { - session.screenShareElement.volume = 1.0; // play audio automatically - } - session.screenShareElement.classList.add("tile"); - session.screenShareElement.setAttribute("playsinline",""); - session.screenShareElement.controlTimer = null; - - session.screenShareElement.dataset.menu = "context-menu-video"; - if (!session.cleanOutput){ - session.screenShareElement.classList.add("task"); // this adds the right-click menu - } - createDirectorScreenshareOnlyBox(); - - if (document.getElementById("videoScreenContainer_director")){ - getById("videoScreenContainer_director").appendChild(session.screenShareElement); - } - - session.screenShareElement.onpause = (event) => { // prevent things from pausing; human or other - if (!((event.ctrlKey) || (event.metaKey) )){ - log("Video paused; auto playing"); - event.currentTarget.play().then(_ => { - log("playing 10"); - }).catch(warnlog); - } - } - - session.screenShareElement.addEventListener('click', function(e) { - log("click"); - try { - if ((e.ctrlKey)||(e.metaKey)){ - e.preventDefault(); - - var [menu, innerMenu] = statsMenuCreator(); - - menu.interval = setInterval(printMyStats,session.statsInterval, innerMenu, true); - - printMyStats(innerMenu, true); - e.stopPropagation(); - return false; - } - } catch(e){errorlog(e);} - }); - - session.screenShareElement.touchTimeOut = null; - session.screenShareElement.touchLastTap = 0; - session.screenShareElement.touchCount = 0; - - session.screenShareElement.addEventListener('touchend', function(event) { - if (session.disableMouseEvents){return;} - log("touched"); - - //document.ontouchup = null; - //document.onmouseup = null; - document.onmousemove = null; - document.ontouchmove = null; - - var currentTime = new Date().getTime(); - var tapLength = currentTime - session.screenShareElement.touchLastTap; - clearTimeout(session.screenShareElement.touchTimeOut); - if (tapLength < 500 && tapLength > 0) { - /// - log("double touched"); - session.screenShareElement.touchCount+=1; - event.preventDefault(); - if (session.screenShareElement.touchCount<5){ - session.screenShareElement.touchLastTap = currentTime; - return false; - } - session.screenShareElement.touchLastTap = 0; - session.screenShareElement.touchCount=0; - - var [menu, innerMenu] = statsMenuCreator(); - - menu.interval = setInterval(printMyStats,session.statsInterval, innerMenu, true); - - printMyStats(innerMenu, true); - event.stopPropagation(); - return false; - ////// - } else { - session.screenShareElement.touchCount=1; - session.screenShareElement.touchLastTap = currentTime; - - session.screenShareElement.touchTimeOut = setTimeout(function(vv) { - clearTimeout(vv.touchTimeOut); - vv.touchLastTap = 0; - vv.touchCount=0; - }, 5000, session.screenShareElement); - - } - }); - } - - firsttime=false - - session.screenShareElement.srcObject = session.screenStream; - - getById("screensharebutton").classList.add("green"); - getById("screensharebutton").ariaPressed = "true"; - getById("screensharebutton").title = getTranslation("stop-screen-sharing"); - - getById("screenshare2button").classList.add("green"); - getById("screenshare2button").ariaPressed = "true"; - getById("screenshare2button").title = getTranslation("stop-screen-sharing"); - - getById("screenshare3button").classList.add("green"); - getById("screenshare3button").ariaPressed = "true"; - getById("screenshare3button").title = getTranslation("stop-screen-sharing"); - - - if (session.autorecord || session.autorecordlocal){ - log("AUTO RECORD START SSTYPE3"); - setTimeout(function(s){ - if (!session.screenStream){return;} - try { - var ele = document.getElementById("recordLocalScreenbutton"); - if (ele){ - ele.classList.add("red"); - ele.classList.remove("hidden"); - if (!ele.vid){ - var v = createVideoElement(); - v.muted = true; - v.srcObject = s; - ele.vid = v; - } - if (ele.vid.recorder || ele.vid.recording){ - ele.vid.recorder.stop(); - ele.classList.remove("red"); - ele.classList.add("hidden"); - ele.vid = null; - } else { - var videoKbps = 4000; - if (session.recordLocal !== false) { - videoKbps = session.recordLocal; - } - recordLocalVideo(null, videoKbps, ele.vid) - } - } - } catch(e){errorlog(e);} - },2000, session.screenStream); - } - - setTimeout(function(){ - updateMixer(); - },100); - - setTimeout(function(){ - updateMixer(); - },1000); - - }).catch(function(err) { - errorlog(err); - }); - } else { // removing a screen - stopSecondScreenshare(); - } -} -function recordLocalScreenStopRecord(){ - var ele = document.getElementById("recordLocalScreenbutton"); - if (ele){ - try { - ele.classList.remove("red"); - ele.classList.add("hidden"); - if (ele.vid){ - if (ele.vid.recorder || ele.vid.recording){ - ele.vid.recorder.stop(); - } - ele.vid = null; - } - }catch(e){errorlog(e);} - } -} -function stopSecondScreenshare(){ - var msg = {}; - msg.screenStopped = true; - session.sendMessage(msg); - - var ele = document.getElementById("recordLocalScreenbutton"); - if (ele){ - try { - ele.classList.remove("red"); - ele.classList.add("hidden"); - if (ele.vid){ - if (ele.vid.recorder || ele.vid.recording){ - ele.vid.recorder.stop(); - } - ele.vid = null; - } - }catch(e){errorlog(e);} - } - - session.screenStream.getTracks().forEach(function(track) { // previous video track; saving it. Must remove the track at some point. - for (UUID in session.pcs){ - if (!("realUUID" in session.pcs[UUID])){continue;} // not a screen share, so skip - var senders = getSenders2(UUID); - senders.forEach((sender) => { // I suppose there could be a race condition between negotiating and updating this. if joining at the same time as changnig streams? - if (sender.track && sender.track.kind == "video") { - sender.track.enabled = false; - } - }); - } - if (track.id in screenshareTracks) { // obs isn't included, so no point to check track.kind - session.screenStream.removeTrack(track); - track.stop(); - screenshareTracks[track.id] = false; - } - }); - session.screenStream = false; - session.screenShareState = false; - pokeIframeAPI('screen-share-state', session.screenShareState, null, session.streamID); - - getById("screensharebutton").classList.remove("green"); - getById("screensharebutton").ariaPressed = "false"; - getById("screensharebutton").title = getTranslation("share-a-screen"); - - getById("screenshare2button").classList.remove("green"); - getById("screenshare2button").ariaPressed = "false"; - getById("screenshare2button").title = getTranslation("share-a-screen"); - - getById("screenshare3button").classList.remove("green"); - getById("screenshare3button").ariaPressed = "false"; - getById("screenshare3button").title = getTranslation("share-a-screen"); - - setTimeout(function(){ - updateMixer(); - },100); - - setTimeout(function(){ - updateMixer(); - },1000); -} - -function createControlBoxScreenshare(UUID, soloLink, streamID) { - if (document.getElementById("deleteme")) { - getById("deleteme").parentNode.removeChild(getById("deleteme")); - } - var controls = getById("controls_blank").cloneNode(true); - controls.classList.remove("hidden"); - controls.id = "controls_" + UUID; - - var container = document.createElement("div"); - container.className = "vidcon directorMargins"; - container.id = "container_" + UUID; // needed to delete on user disconnect - container.UUID = UUID; - container.dataset.UUID = UUID; - - if (session.orderby){ - try { - var added = false; - for (var i=0;i streamID.toLowerCase()){ - getById("guestFeeds").insertBefore(container, getById("guestFeeds").children[i]); - added = true; - break; - } - } - } - - } - if (!added){ - getById("guestFeeds").appendChild(container); - } - } catch(e){ - getById("guestFeeds").appendChild(container); - } - } else { - getById("guestFeeds").appendChild(container); - } - - controls.querySelector(".controlsGrid").classList.add("notmain"); - - if (!session.rpcs[UUID].voiceMeter) { - if (session.meterStyle==1){ - session.rpcs[UUID].voiceMeter = getById("voiceMeterTemplate2").cloneNode(true); - } else { - session.rpcs[UUID].voiceMeter = getById("voiceMeterTemplate").cloneNode(true); - session.rpcs[UUID].voiceMeter.style.opacity = 0; - if (session.meterStyle==2){ - session.rpcs[UUID].voiceMeter.classList.add("video-meter-2"); - session.rpcs[UUID].voiceMeter.classList.remove("video-meter"); - } else { - session.rpcs[UUID].voiceMeter.classList.add("video-meter-director"); - } - } - session.rpcs[UUID].voiceMeter.id = "voiceMeter_" + UUID; - session.rpcs[UUID].voiceMeter.dataset.level = 0; - session.rpcs[UUID].voiceMeter.classList.remove("hidden"); - } - - session.rpcs[UUID].remoteMuteElement = getById("muteStateTemplate").cloneNode(true); - session.rpcs[UUID].remoteMuteElement.id = ""; - session.rpcs[UUID].remoteMuteElement.style.top = "5px"; - session.rpcs[UUID].remoteMuteElement.style.right = "7px"; - - session.rpcs[UUID].remoteVideoMuteElement = getById("videoMuteStateTemplate").cloneNode(true); - session.rpcs[UUID].remoteVideoMuteElement.id = ""; - session.rpcs[UUID].remoteVideoMuteElement.style.top = "5px"; - session.rpcs[UUID].remoteVideoMuteElement.style.right = "28px"; - - session.rpcs[UUID].remoteRaisedHandElement = getById("raisedHandTemplate").cloneNode(true); - session.rpcs[UUID].remoteRaisedHandElement.id = ""; - session.rpcs[UUID].remoteRaisedHandElement.style.top = "5px"; - session.rpcs[UUID].remoteRaisedHandElement.style.right = "49px"; - - var videoContainer = document.createElement("div"); - videoContainer.id = "videoContainer_" + UUID; // needed to delete on user disconnect - videoContainer.style.margin = "0"; - videoContainer.style.position = "relative"; - videoContainer.style.minHeight = "30px"; - - var iframeDetails = document.createElement("div"); - iframeDetails.id = "iframeDetails_" + UUID; // needed to delete on user disconnect - iframeDetails.className = "iframeDetails hidden"; - - //controls.innerHTML += ""; - //controls.innerHTML += ""; - - var handsID = "hands_" + UUID; - - controls.innerHTML += "
    "; - - if (session.hidesololinks==false){ - controls.innerHTML += "
    \ - " + sanitizeChat(soloLink) + "\ - \ -
    "; - } - - controls.innerHTML += "\ -
    "; - - controls.querySelectorAll('[data-action-type]').forEach((ele) => { // give action buttons some self-reference - ele.dataset.UUID = UUID; - ele.dataset.sid = streamID; - }); - - - var buttons = ""; - if (session.slotmode){ - var slots = document.querySelectorAll("div.slotsbar[data-slot]"); - var biggestSlot=0; - var slotDefault = null; - if (streamID in session.pastSlots){ - slotDefault = session.pastSlots[streamID]; - } - var allSlots = []; - if (session.slotmode==1){ - for (var i=0;ibiggestSlot){ - biggestSlot = parseInt(slots[i].dataset.slot); - } - if (slotDefault===parseInt(slots[i].dataset.slot)){ - slotDefault = null; - } - allSlots.push(parseInt(slots[i].dataset.slot)); - } - biggestSlot+=1; - } - if (slotDefault!==null){ - biggestSlot = slotDefault; - } else if (session.slotmode==1){ - var bestfree = 0; - for (var i =1; i<=biggestSlot; i++){ - if (allSlots.includes(i)){ - continue; - } else { - bestfree = i; - break; - } - } - biggestSlot = bestfree; - } - var slotName = "slot: "+biggestSlot; - if (!biggestSlot){ - slotName = "unset"; - } - session.pastSlots[streamID] = biggestSlot; - - buttons += "
    \ -
    "; - } - - buttons += "
    ID: " + streamID + "\ - \ - \ - \ -
    "; - - container.innerHTML = buttons; - updateLockedElements(); - - - var videoContainerControlBox = document.createElement("div"); - videoContainerControlBox.className = "controlVideoBox"; - container.containerControlBox = videoContainerControlBox - - container.appendChild(videoContainerControlBox); - videoContainerControlBox.appendChild(videoContainer); - - if (session.signalMeter){ - if (!session.rpcs[UUID].signalMeter){ - session.rpcs[UUID].signalMeter = getById("signalMeterTemplate").cloneNode(true); - session.rpcs[UUID].signalMeter.id = "signalMeter_" + UUID; - session.rpcs[UUID].signalMeter.dataset.level = 0; - session.rpcs[UUID].signalMeter.classList.remove("hidden"); - session.rpcs[UUID].signalMeter.dataset.UUID = UUID; - session.rpcs[UUID].signalMeter.title = getTranslation("signal-meter"); - - //if (session.rpcs[UUID].stats.info && session.rpcs[UUID].stats.info.cpu_maxed){ - // session.rpcs[UUID].signalMeter.dataset.cpu = "1"; - //} - - session.rpcs[UUID].signalMeter.addEventListener('click', function(e) { // show stats of video if double clicked - log("clicked signal meter"); - try { - e.preventDefault(); - if (session.statsMenu !==false){ - var uid = e.currentTarget.dataset.UUID; - if ("stats" in session.rpcs[uid]){ - - var [menu, innerMenu] = statsMenuCreator(); - printViewStats(innerMenu, uid ); - menu.interval = setInterval(printViewStats, session.statsInterval, innerMenu, uid); - - } - } - e.stopPropagation(); - return false; - - } catch(e){errorlog(e);} - }); - } - videoContainer.appendChild(session.rpcs[UUID].signalMeter); - } - - if (session.batteryMeter){ - //////// - if (!session.rpcs[UUID].batteryMeter){ - session.rpcs[UUID].batteryMeter = getById("batteryMeterTemplate").cloneNode(true); - session.rpcs[UUID].batteryMeter.id = "batteryMeter_" + UUID; - /* - if (session.rpcs[UUID].stats.info && (session.rpcs[UUID].stats.info.power_level!==null)){ - var level = session.rpcs[UUID].batteryMeter.querySelector(".battery-level"); - if (level){ - var value = session.rpcs[UUID].stats.info.power_level; - if (value > 100){value = 100;} - else if (value < 0){ value = 0;} - level.style.height = parseInt(value)+"%"; - if (value<10){ - session.rpcs[UUID].batteryMeter.classList.add("alert"); - } else if (value<25){ - session.rpcs[UUID].batteryMeter.classList.add("warn"); - } - if (value<100){ - session.rpcs[UUID].batteryMeter.classList.remove("hidden"); - } - session.rpcs[UUID].batteryMeter.title = (Math.round(value*10)/10)+"% battery remaining"; - } - } - if (session.rpcs[UUID].stats.info && ("plugged_in" in session.rpcs[UUID].stats.info) && (session.rpcs[UUID].stats.info.plugged_in===false)){ - session.rpcs[UUID].batteryMeter.dataset.plugged = "0"; - session.rpcs[UUID].batteryMeter.classList.remove("hidden"); - } else { - session.rpcs[UUID].batteryMeter.dataset.plugged = "1"; - } - */ - batteryMeterInfoUpdate(UUID); - - } - videoContainer.appendChild(session.rpcs[UUID].batteryMeter); - } - - if (session.showConnections){ - if (!session.rpcs[UUID].connectionDetails){ - createConnectionDetailsEle(UUID); - } - videoContainer.appendChild(session.rpcs[UUID].connectionDetails); - } - - videoContainer.appendChild(session.rpcs[UUID].voiceMeter); - videoContainer.appendChild(session.rpcs[UUID].remoteMuteElement); - videoContainer.appendChild(session.rpcs[UUID].remoteVideoMuteElement); - videoContainer.appendChild(session.rpcs[UUID].remoteRaisedHandElement); - videoContainer.appendChild(iframeDetails); - videoContainer.appendChild(session.rpcs[UUID].videoElement); - container.appendChild(controls); - - session.group.forEach(group=>{ - var ele = controls.querySelector('[data-action-type="toggle-group"][data--u-u-i-d="'+UUID+'"][data-group="'+group+'"]'); - if (!ele){ - var newGroup = htmlToElement(''); - - var added = false; - container.querySelectorAll('.customGroup>[data-group]').forEach(ele=>{ - log(ele); - if (!added && ele.dataset.group>group+""){ - ele.parentNode.insertBefore(newGroup, ele); - added = true; - } - }); - if (!added){ - var newGroupCon = container.querySelector(".customGroup"); - if (!newGroupCon){ - newGroupCon = document.createElement("div"); - newGroupCon.classList.add("customGroup"); - container.appendChild(newGroupCon); - } - newGroupCon.appendChild(newGroup); - } - } - }); - - initSceneList(UUID); - pokeIframeAPI("control-box", true, UUID); -} +/* +* Copyright (c) 2022 Steve Seguin. All Rights Reserved. +* +* Use of this source code is governed by the APGLv3 open-source license +* that can be found in the LICENSE file in the root of the source +* tree. Alternative licencing options can be made available on request. +* +*/ +/*jshint esversion: 6 */ + + +var formSubmitting = true; +var activatedPreview = false; +var screensharesupport = true; +var FirefoxEnumerated = false; + +var Callbacks = []; +var CtrlPressed = false; // global +var AltPressed = false; +var KeyPressedTimeout = 0; +var PPTKeyPressed = false; + +var translation = false; + +var miscTranslations = { // i can replace this list from time to time from the generated one in blank.json using translate.js + "start": "START", + "new-display-name": "Enter a new Display Name for this stream", + "submit-error-report": "Press OK to submit any error logs to VDO.Ninja. Error logs may contain private information.", + "director-redirect-1": "The director wishes to redirect you to the URL: ", + "director-redirect-2": "\n\nPress OK to be redirected.", + "add-a-label": "Add a label", + "audio-processing-disabled": "Audio processing is disabled with this guest. Can't mute or change volume", + "not-the-director": "You are not the director of this room. You will have limited to no control. See &codirector on how to become a co-director.", + "room-is-claimed": "The room is already claimed by someone else.\n\nOnly the first person to join a room is the assigned director.\n\nRefresh after the first director leaves to claim.", + "token-room-is-claimed": "The room is claimed by someone else.\n\nJoin as a guest or co-director instead.", + "room-is-claimed-codirector": "The room is already claimed by someone else.\n\nTrying to join as a co-director...", + "streamid-already-published": "The stream ID you are publishing to is already in use.\n\nPlease try with a different invite link or refresh to retry again.\n\nYou will now be disconnected.", + "director": "Director", + "unknown-user": "Unknown User", + "room-test-not-good": "The room name 'test' is very commonly used and may not be secure.\n\nAre you sure you wish to proceed?", + "load-previous-session": "Would you like to load your previous session's settings?", + "enter-password": "Please enter the password below: \n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)", + "enter-password-2": "Please enter the password below: \n\n(Note: Passwords are case-sensitive.)", + "enter-director-password": "Please enter the director's password:\n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)", + "password-incorrect": "The password was incorrect.\n\nRefresh and try again.", + "enter-display-name": "Please enter your display name:", + "enter-new-display-name": "Enter a new Display Name for this stream", + "what-bitrate": "What bitrate would you like to record at? (kbps)\n(note: This feature is experimental, so have backup recordings going)", + "enter-website": "Enter a website URL to share", + "press-ok-to-record": "Press OK to start recording. Press again to stop and download.\n\nWarning: Keep this browser tab active to continue recording.\n\nYou can change the default video bitrate if desired below (kbps)", + "no-streamID-provided": "No streamID was provided; one will be generated randomily.\n\nStream ID: ", + "alphanumeric-only": "Info: Only AlphaNumeric characters should be used for the stream ID.\n\nThe offending characters have been replaced by an underscore", + "stream-id-too-long": "The Stream ID should be less than 45 alPhaNuMeric characters long.\n\nWe will trim it to length.", + "share-with-trusted": "Share only with those you trust", + "pass-recommended": "A password is recommended", + "insecure-room-name": "Insecure room name.", + "allowed-chars": "Allowed chars", + "transfer": "transfer", + "armed": "armed", + "transfer-guest-to-room": "Transfer guests to room:\n\n(Please note: rooms must share the same password)", + "transfer-guest-to-url": "Transfer guests to new website URL.\n\nGuests will be prompted to accept unless they are using &consent", + "change-url": "change URL", + "mute-in-scene": "mute in scene", + "unmute-guest": "unmute guest", + "unmute": "unmute", + "undeafen": "undeafen", + "deafen": "deafen guest", + "unblind": "unblind", + "blind": "blind guest", + "mute-guest": "mute guest", + "mute": "mute", + "unhide": "unhide guest", + "hide-guest": "Hide", + "confirm-disconnect-users": "Are you sure you wish to disconnect these users?", + "confirm-disconnect-user": "Are you sure you wish to disconnect this user?", + "enter-new-codirector-password": "Enter a co-director password to use", + "control-room-co-director": "Control Room: Co-Director", + "volume-control": "Volume control for local playback only", + "signal-meter": "Video packet loss indicator of video preview; green is good, red is bad. Flame implies CPU is overloaded. May not reflect the packet loss seen by scenes or other guests.", + "waiting-for-the-stream": "Waiting for the stream. Tip: Adding &cleanoutput to the URL will hide this spinner, or click to retry, which will also hide it.", + "main-director": "Main Director", + "share-a-screen": "Share a screen", + "stop-screen-sharing": "Stop screen sharing", + "you-have-been-transferred": "You've been transferred to a different room", + "you-have-been-activated": "The director has allowed you to see others in the room now", + "you-are-no-longer-a-co-director": "You are no longer a co-director as you were transferred.", + "transferred": "Transferred", + "room-changed": "Your room has changed", + "headphones-tip": "Tip: Use headphones to avoid audio echo issues.", + "camera-tip-c922": "Tip: To achieve 60-fps with a C922 webcam, low-light compensation needs to be turned off, exposure set to auto, and 720p used.", + "camera-tip-camlink": "Tip: A Cam Link may glitch green/purple if accessed elsewhere while already in use.", + "samsung-a-series": "Samsung A-series phones may have issues with Chrome; if so, try Firefox Mobile instead or switch video codecs.", + "screen-permissions-denied": "Permission to capture denied. Ensure your browser has screen record system permissions\n\n1.On your Mac, choose Apple menu > System Preferences, click Security & Privacy , then click Privacy.\n2.Select Screen Recording.\n3.Select the checkbox next to your browser to allow it to record your screen.", + "change-audio-output-device": "Audio could not be captured. Please make sure you have an audio output device available.\n\nSome gaming headsets (ie: Corsair) may need to be set to 2-channel output to work, as surround sound drivers may cause problems.", + "prompt-access-request": " is trying to view your stream. Allow them?", + "confirm-reload-user": "Are you sure you wish to reload this user's browser?", + "webrtc-is-blocked": "⚠ This browser has either blocked WebRTC or does not support it.\n\nThis site will not work without it.\n\nDisable any browser extensions or privacy settings that may be blocking WebRTC, or try a different browser.", + "not-clean-session": "Video effects or canvas rendering failed.\n\nCheck to ensure any remotely hosted images are cross-origin allowed.", + "ios-no-screen-share": "Sorry, but your iOS browser does not support screen-sharing.\n\nPlease see this guide for an alternative method to do so.", + "android-no-screen-share": "Sorry, your mobile browser does not support screen-sharing.\n\nThe Android native app does offer basic support for it though.", + "no-screen-share-supported": "Sorry, your browser does not support screen-sharing.\n\nPlease use the desktop versions of Firefox or Chrome instead.", + "speech-not-suppoted": "⚠ Speech Recognition is not supported by this browser", + "blue-yeti-tip": "Tip: Blue Yeti microphones may experience issues being overly loud. Please see here for a solution or disable auto-gain in VDO.Ninja.", + "site-not-responsive": "

    Notice: The system cannot be accessed or is currently slow to respond.

    \nIf a routing issue, try adding &proxy to the URL; you can also try https://proxy.vdo.ninja or a VPN if the service is blocked in your country.\n\nIf the main service is down, a backup version is also available here: https://backup.vdo.ninja\n\nContact steve@seguin.email for added help.\n\nThis service requires the use of Websockets over port 443.", + "no-audio-source-detected": "No Audio Source was detected.\n\nIf you were wanting to capture an Application's Audio, please see:\nhttps://docs.vdo.ninja/help/guides-and-how-tos#audio for some guides.", + "viewer-count": "Total outbound p2p connections of this remote stream", + "enter-url-for-widget": "Enter a URL for a page to embed as a sidebar", + "director-password": "Enter the main director's password", + "vision-disabled": "The Director has disabled your vision temporarily

    ", + "invalid-remote-code": "Invalid remote control code.\n\nUse the field below to try again with a different passcode.", + "invalid-remote-code-obs": "Invalid remote control code.\n\nThe remote OBS system needs a matching passcode set using &remote.\n\nSee the documentation for help..", + "request-rejected-obs": "The request was rejected.\n\nThe remote OBS system needs a matching passcode set using &remote.\n\nSee the documentation for help.", + "remote-token-rejected": "The remote request failed; the &remote token did not match or the remote user does not allow remote control.", + "remote-control-failed": "The remote control request failed.", + "remote-peer-connected": "Remote peer connected to video stream.\n\nConnection to handshake server being killed on request. This increases security, but the peer will not be able to reconnect automatically on connection failure.\n\nPress OK to start the stream!", + "director-denied": "The main director denied you as a co-director", + "only-main-director": "Only the main director can transfer this guest", + "request-failed": "The request failed; you can't apply this action", + "tokens-did-not-match": "The remote request failed; the remote token did not match or the remote user does not allow remote control.", + "token-not-director": "The request failed; the remote user did not recognize you as the director", + "approved-as-director": "The director approved you as a co-director", + "you-are-a-codirector": "You are a co-director of this room; you have partial director control assigned to you.", + "this-is-you": "This is you, a co-director.
    You are also a performer.", + "preview-meshcast-disabled": "You can't adjust the preview bitrate for Meshcast or WHIP-based streams", + "no-network": "Network connection lost 🤷‍♀️❌📶", + "no-network-details": "Network connection lost. 🤷‍♀️❌📶\n\nHave you lost your Internet connection?" +} + +function getTranslation(key){ // when using this, instead of miniTranslate, if the user changes the language, it might not update. Used mainly when you don't want any HTML () being including in the translation + if (translation.innerHTML && (key in translation.innerHTML)){ // these are the proper translations + return translation.innerHTML[key]; + } else if (key in miscTranslations){ // i guess these can be transitioned to innerHTML + return miscTranslations[key]; + } else { + warnlog("misc translation not found"); + return key.replaceAll("-", " "); // + } +} + + +// function log(msg){ // uncomment to enable logging. + // console.log(msg); +// } +// function warnlog(msg, url=false, lineNumber=false){ + // onsole.warn(msg); + // if (lineNumber){ + // console.warn(lineNumber); + // } +// } +// function errorlog(msg, url=false, lineNumber=false){ + // console.error(msg); + // if (lineNumber){ + // console.error(lineNumber); + // } +// } + +if (typeof session === 'undefined') { // make sure to init the WebRTC if not exists. + var session = WebRTC.Media; + session.streamID = session.generateStreamID(); + errorlog("Serious error: WebRTC session didn't load in time"); +} + +try { // this is just in case orientationchange gets removed.. + if (!window.onorientationchange && screen.orientation){ // onorientationchange is deprecated. + window.onorientationchange = function(){ + log("screen.orientation triggered.. but nothing linked"); + }; + screen.orientation.addEventListener('change', window.onorientationchange); + } +} catch(e){ + errorlog(e); +} + +(function(w) { + w.URLSearchParams = w.URLSearchParams || function(searchString) { + var self = this; + searchString = searchString.replace("??", "?"); + self.searchString = searchString; + self.get = function(name) { + var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(self.searchString); + if (results == null) { + return null; + } else { + return decodeURI(results[1]) || 0; + } + }; + }; + +})(window); + +var urlEdited = window.location.search.replace(/\?\?/g, "?"); +urlEdited = urlEdited.replace(/\?/g, "&"); +urlEdited = urlEdited.replace(/\&/, "?"); +var urlParams = new URLSearchParams(urlEdited); + +if (urlParams.has("invite") || urlParams.has("i") || urlParams.has("code")){ + session.decodeInvite(urlParams.get("invite") || urlParams.get("i") || urlParams.get("code")); +} + +if (session.decrypted){ + session.decrypted = session.decrypted + urlEdited.replace("?","&"); + session.decrypted = session.decrypted.replace(/\?/g, "&"); + session.decrypted = session.decrypted.replace(/\&/, "?"); + urlParams = new URLSearchParams(session.decrypted); + //session.decrypted = true; +} else { + if (urlEdited !== window.location.search){ + warnlog(window.location.search + " changed to " + urlEdited); + window.history.pushState({path: urlEdited.toString()}, '', urlEdited.toString()); + } +} +delete urlEdited; + +var isIFrame = false; +if ( parent && (window.location !== window.parent.location )) { + isIFrame = true; +} + +function mapToAll(targets, callback, parentElement = document) { // js helper + if (!targets) { + return; + } + if (!parentElement) { + return; + } + const target = parentElement.querySelectorAll(targets); + for (let i = 0; i < target.length; i++) { + callback(target[i]); + } +} + +function changeParam(url, paramName, paramValue) { + paramName = paramName.replace("?", ""); + var qind = url.indexOf('?'); + url = url.replace("?", "&"); + var params = url.substring(qind + 1).split('&'); + var query = ''; + var match = false; + for (var i = 0; i < params.length; i++) { + var tokens = params[i].split('='); + var name = tokens[0]; + var value = ""; + if (tokens.length > 1 && tokens[1] !== '') { + value = tokens[1]; + } + + if (name == paramName) { + if (match) { + continue; + } // already matched the first time. + match = true; + value = paramValue; + } + if (value !== "") { + value = '=' + value; + } + + if (query == '') { + query = "?" + name + value; + } else { + query = query + '&' + name + value; + } + } + return url.substring(0, qind) + query; +} + +function saveRoom(ele){ + //this.title = "Quick load settings stored locally"; + session.sticky = true; + ele.parentNode.removeChild(ele); + setStorage("permission", "yes"); + setStorage("settings", encodeURI(window.location.href), 999); +} + +function updateURL(param, force = false, cleanUrl = false) { + if (session.decrypted){return;} + + param = param.replace("?", ""); + var para = param.split('='); + if (cleanUrl) { + if (history.pushState) { + var href = new URL(cleanUrl); + if (para.length == 1) { + href = changeParam(cleanUrl, para[0], ""); + } else { + href = changeParam(cleanUrl, para[0], para[1]); + } + log("--" + href.toString()); + window.history.pushState({path: href.toString()}, '', href.toString()); + } + } else if (!(urlParams.has(para[0]))) { // don't need to replace as it doesn't exist. + if (history.pushState) { + var href = window.location.href; + href = href.replace("??", "?"); + var arr = href.split('?'); + var newurl; + if (arr.length > 1 && arr[1] !== '') { + newurl = href + '&' + param; + } else { + newurl = href + '?' + param; + } + + window.history.pushState({path: newurl.toString()}, '', newurl.toString()); + } + } else if (force) { + if (history.pushState) { + var href = new URL(window.location.href); + if (para.length == 1) { + href = changeParam(window.location.href, para[0], ""); + } else { + href = changeParam(window.location.href, para[0], para[1]); + } + log("---" + href.toString()); + window.history.pushState({path: href.toString()}, '', href.toString()); + } + } + if (session.sticky) { + setStorage("settings", encodeURI(window.location.href), 999); + } + urlParams = new URLSearchParams(window.location.search); +} + +/* function changeGuestSettings(ele){ + var eles = ele.querySelectorAll('[data-param]'); + var UUID = ele.dataset.UUID; + var settings = {}; + for (var i = 0;i< eles.length; i++){ + if (eles[i].tagName.toLowerCase() == "input"){ + if (eles[i].checked===true){ + settings[eles[i].dataset.param] = true; + } else if (eles[i].checked===false){ + settings[eles[i].dataset.param] = false; + } else { + settings[eles[i].dataset.param] = eles[i].value; + } + } + } + warnlog(settings); + + if (!settings.changepassword){ + delete settings.password; + } + + delete settings.changepassword; + + if (!settings.changeroom){ + // send Migration message + delete settings.roomid; + } + delete settings.roomid; + delete settings.changeroom; + + warnlog(UUID); + var msg = {}; + msg.changeParams = settings; + session.sendRequest(msg, UUID); + closeModal(); +} */ + +// proper room migration needs to happen; in sync. +// updateMixer after settings changed +// password needs to be special cased +// room shouldn't be sent + +function applyNewParams(changeParams){ + for (var key in changeParams){ + session[key] = changeParams[key]; + log(key); + } + log(changeParams); + updateMixer(); +} + +function submitDebugLog(msg=false){ + try { + if (navigator.userAgent){ + var _, userAgent = navigator.userAgent; + appendDebugLog({"userAgent": userAgent}); + } + if (navigator.platform){ + appendDebugLog({"userAgent": navigator.platform}); + } + } catch(e){} + window.focus(); + var res = confirm(getTranslation("submit-error-report")); + if (res){ + var request = new XMLHttpRequest(); + + var recordResults = session.streamID + "_"+parseInt(Date.now()); + request.open('POST', "https://reports.vdo.ninja/?name="+recordResults); // php, well, whatever. + if (!session.cleanOutput){ + warnUser("Report any details of your bug report to steve@seguin.email, along with the following link: https://reports.vdo.ninja/?name="+recordResults+"", false, false); + } + console.log("Report any details of your bug report to steve@seguin.email, along with the following ID: "+recordResults); + + request.send(JSON.stringify(errorReport)); + errorReport = []; + if (document.getElementById("reportbutton")){ + getById("reportbutton").classList.add("hidden"); + } + } +} + +function URLFromFiles(files) { + const promises = files.map((file) => + fetch(file).then((response) => response.text()) + ); + + return Promise.all(promises).then((texts) => { + const text = texts.join(""); + const blob = new Blob([text], { type: "application/javascript" }); + + return URL.createObjectURL(blob); + }); +} + +function detectCPUSupport(){ + let cpuThreads = navigator.hardwareConcurrency; + if (cpuThreads){ + return cpuThreads+" threads"; + } + return false; +} + +function detectGPUSupport() { + try { + const gl = document.createElement('canvas').getContext('webgl'); + + if (!gl) { + return false; + } + + if (!Firefox){ + try { + const debugInfo = gl.getExtension('WEBGL_debug_renderer_info'); // chrome + if (debugInfo){ + return gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL); + } + } catch(e){} + } + + try { + return gl.getParameter(gl.RENDERER) || false; // firefox + } catch(e){} + + } catch(e){} + return false; +} + +function isOperaGX(){ + return (!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/75') >= 0; +} + +function isSamsungASeries(){ + return navigator.userAgent.includes("; SM-A") || false; +} + +function getChromiumVersion() { + var raw = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./); + return raw ? parseInt(raw[2], 10) : false; +} + +function getiOSVersion(){ + try { + var agent = navigator.userAgent; + var start = agent.indexOf("OS "); + if( ( agent.indexOf("iPhone") > -1 || agent.indexOf("iPad") > -1 ) && start > -1 ){ + return window.Number( agent.substr( start + 3, 3 ).replace("_","." ) ); + } + return 0; + } catch (e) { + return 0; + } + return 0; +} + +function safariVersion() { + var ver = 0; + try { + ver = navigator.appVersion.split("Version/"); + if (ver.length > 1) { + ver = ver[1].split(" Safari"); + } + if (ver.length > 1) { + ver = ver[0].split("."); + } + if (ver.length > 1) { + ver = parseInt(ver[0]); + } else { + ver = 0; + } + } catch (e) { + return 0; + } + return ver; +} + +try{ + var iOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform); // used by main.js also + var iPad = (navigator.maxTouchPoints && navigator.maxTouchPoints > 2 && /MacIntel/.test(navigator.platform)); + + var macOS = navigator.userAgent.indexOf('Mac OS X') != -1; + macOS = macOS && !(iOS || iPad); + var Firefox = navigator.userAgent.indexOf("Firefox")>=0; + if (Firefox){ + Firefox = parseInt(navigator.userAgent.split("irefox/").pop()) || true; + } + var Android = navigator.userAgent.toLowerCase().indexOf("android") > -1; //&& ua.indexOf("mobile"); + var ChromiumVersion = getChromiumVersion(); + var OperaGx = isOperaGX(); + var SafariVersion = safariVersion() || getiOSVersion(); // I should rename this to webkit + + if (iOS || iPad){ // iOS doesn't yet allow actual browsers, cause it's abusing its duopoly. + if (SafariVersion){ + if (Firefox){ + Firefox = false; // I should rename this to gecko + } + if (ChromiumVersion){ + ChromiumVersion = false; // I should rename this to chromium + } + } + } + var SamsungASeries = isSamsungASeries(); + var isVingester = navigator.userAgent.indexOf("Vingester")>=0; + + var gpgpuSupport = detectGPUSupport(); + log(gpgpuSupport); + + var cpuSupport = detectCPUSupport(); + log(cpuSupport); + + var iPhone12Up = false; + + if (iOS && !iPad){ + if ((window.devicePixelRatio.toFixed(2)>=3) && (window.screen.height>800) && (window.screen.width!=414)){ // for reference, https://www.ios-resolution.com/ + iPhone12Up = true; // iPhone SE is left out. + } + } + +} catch(e){errorlog(e);} + + + +if (isVingester){ + console.warn("If Vingester isn't able to capture audio, get a fixed version of Vingester from here: https://github.com/steveseguin/vingester/releases/"); +} + +function isAlphaNumeric(str) { + var code, i, len; + for (i = 0, len = str.length; i < len; i++) { + code = str.charCodeAt(i); + if (!(code > 47 && code < 58) && // numeric (0-9) + !(code > 64 && code < 91) && // upper alpha (A-Z) + !(code > 96 && code < 123)) { // lower alpha (a-z) + return false; + } + } + return true; +} + +function convertStringToArrayBufferView(str){ + var bytes = new Uint8Array(str.length); + for (var iii = 0; iii < str.length; iii++){ + bytes[iii] = str.charCodeAt(iii); + } + return bytes; +} + +function toHexString(byteArray){ + return Array.prototype.map.call(byteArray, function(byte){ + return ('0' + (byte & 0xFF).toString(16)).slice(-2); + }).join(''); +} +function toByteArray(hexString){ + var result = []; + for (var i = 0; i < hexString.length; i += 2){ + result.push(parseInt(hexString.substr(i, 2), 16)); + } + return new Uint8Array(result); +} + +function playAllVideos(){ + + if (session.firstPlayTriggered && (session.audioCtx.state == "suspended")){ // added oct 9th 2022 + try { + session.audioCtx.resume(); + } catch(e){warnlog(e);} + } + + for (var i in session.rpcs){ + if (session.rpcs[i].whip){continue;} + try{ + if (session.rpcs[i].videoElement){ + log("I: "+i); + if (session.rpcs[i].videoElement.paused){ + setTimeout(function(UUID){ + session.rpcs[UUID].videoElement.play().then(_ => { + log("playing 3 "); + if ((session.audioEffects===true) || session.pushLoudness){ + log("updateIncomingAudioElement('"+UUID+"')"); + updateIncomingAudioElement(UUID); + } + }).catch(errorlog); + },0,i); + } else if ((session.audioEffects===true) || session.pushLoudness){ + updateIncomingAudioElement(i); + log("updateIncomingAudioElement('"+i+"')"); + } + + } + } catch(e){ + errorlog(e); + } + } + +} + +var videoElements = Array.from(document.querySelectorAll("video")); +var audioElements = Array.from(document.querySelectorAll("audio")); +var mediaStreamCounter = 0; + + +function createMediaStream(){ + mediaStreamCounter+=1; + return new MediaStream(); +} + +function deleteOldMedia(){ + log("CHECKING FOR OLD MEDIA"); + var i = videoElements.length; + while (i--) { + //if ((videoElements[i].id == "videosource") || (videoElements[i].id == "previewWebcam")){continue;} // exclude this one, for safety reasons. (Also, iOS safari blanks the video if streams are detached and moved between video elements) + if (videoElements[i].isConnected === false){ + if ((videoElements[i].srcObject==null) || (videoElements[i].srcObject && videoElements[i].srcObject.active === false)){ + if (videoElements[i].dataset && videoElements[i].dataset.UUID){ + if (videoElements[i].dataset.UUID in session.rpcs){continue;} // still active, so lets not delete it. + } + videoElements[i].pause(); + videoElements[i].removeAttribute("id"); + videoElements[i].removeAttribute('src'); // empty source + videoElements[i].load(); + videoElements[i].remove(); + videoElements[i] = null; + videoElements.splice(i, 1); + } + } + } + i = audioElements.length; + while (i--) { + if (audioElements[i].isConnected === false){ + if ((audioElements[i].srcObject==null) || (audioElements[i].srcObject && audioElements[i].srcObject.active === false)){ + if (audioElements[i].dataset && audioElements[i].dataset.UUID){ + if (audioElements[i].dataset.UUID in session.rpcs){continue;} // still active, so lets not delete it. + } + audioElements[i].pause(); + audioElements[i].id = null; + audioElements[i].removeAttribute('src'); // empty source + audioElements[i].load(); + audioElements[i].remove(); + audioElements[i] = null; + audioElements.splice(i, 1); + } + } + } +} + +function createAudioElement(){ + try{ + deleteOldMedia(); + } catch(e){errorlog(e);} + var a = document.createElement("audio"); + audioElements.push(a); + return a; +} + +function compare_deltas( a, b ) { + var aa = a.delta || 0; + var bb = b.delta || 0; + if ( aa > bb ){ + return 1; + } + if ( aa < bb ){ + return -1; + } + return 0; +} + +async function fetchWithTimeout(URL, timeout=8000){ // ref: https://dmitripavlutin.com/timeout-fetch-request/ + try { + const controller = new AbortController(); + const timeout_id = setTimeout(() => controller.abort(), timeout); + const response = await fetch(URL, {...{timeout:timeout}, signal: controller.signal}); + clearTimeout(timeout_id); + return response; + } catch(e){ + errorlog(e); + return await fetch(URL); // iOS 11.x/12.0 + } +} + +function createVideoElement(){ + try{ + deleteOldMedia(); + } catch(e){errorlog(e);} + var v = document.createElement("video"); + videoElements.push(v); + if (typeof session.volume == "number"){ + v.volume = session.volume; // setting default volume + log("setting volume to manual"); + } + + return v; +} + +function getTimezone(){ + if (session.tz!==false){ + return session.tz; + } + const stdTimezoneOffset = () => { + var jan = new Date(0, 1); + var jul = new Date(6, 1); + return Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); + } + var today = new Date(); + const isDstObserved = (today) => { + return today.getTimezoneOffset() < stdTimezoneOffset(); + } + if (isDstObserved(today)) { + return today.getTimezoneOffset()+60; + } else { + return today.getTimezoneOffset(); + } +} + +function promptUser(eleId, UUID=null){ + if (session.beepToNotify){ + playtone(); + } + if (document.getElementById("modalBackdrop")){ + getById("promptModal").innerHTML = ''; // Delete modal + getById("promptModal").remove(); + getById("modalBackdrop").innerHTML = ''; // Delete modal + getById("modalBackdrop").remove(); + } + + zindex = 30 + document.querySelectorAll('#promptModal').length; + modalTemplate = + `
    +
    + × + +
    +
    +
    `; + document.body.insertAdjacentHTML("beforeend", modalTemplate); // Insert modal at body end + + getById("promptModalMessage").innerHTML = getById(eleId).innerHTML; + if (UUID){ + getById("promptModalMessage").dataset.UUID = UUID; + } + + document.getElementById("modalBackdrop").addEventListener("click", closeModal); + + getById("promptModal").addEventListener("click", function(e) { + e.stopPropagation(); + return false; + }); +} + +async function delay(ms) { + return await new Promise((resolve, reject) => { + setTimeout(resolve, ms); + }); +} + +var Prompts = {}; +async function promptAlt(inputText, block=false, asterix=false, value=false, time=false){ + var result = null; + if (session.beepToNotify){ + playtone(); + } + await new Promise((resolve, reject) => { + var promptID = "pid_"+Math.random().toString(36).substr(2, 9); + Prompts[promptID] = {}; + Prompts[promptID].resolve = resolve; + Prompts[promptID].reject = reject; + + var zindex = 1030 + document.querySelectorAll('.promptModal').length; + + if (block){ + var backdropClass = "opaqueBackdrop"; + } else { + var backdropClass = "modalBackdrop"; + } + + inputText = ""+inputText.replace("\n","
    ")+""; + inputText = inputText.replace(/\n/g,"
    "); + var type = "text"; + if (asterix){ + type = "password"; + } + + if (time){ + modalTemplate = + ` +
    `; + } else { + modalTemplate = + ` +
    `; + } + + document.body.insertAdjacentHTML("beforeend", modalTemplate); // Insert modal at body end + + document.getElementById("input_"+promptID).focus(); + + if (value!==false){ + if (time){ + document.getElementById("input_"+promptID).value = parseInt(value/60); + document.getElementById("input_"+promptID+"_sec").value = parseInt(value)%60; + } else { + document.getElementById("input_"+promptID).value = value; + } + } + + + if (!time){ + document.getElementById("input_"+promptID).addEventListener("keyup", function(event) { + if (event.key === "Enter") { + var pid = event.target.dataset.pid; + result = document.getElementById("input_"+pid).value; + document.getElementById("modal_"+pid).remove(); + document.getElementById("modalBackdrop_"+pid).remove(); + Prompts[pid].resolve(); + } + }); + } else { + document.getElementById("input_"+promptID).addEventListener("keyup", function(event) { + if (event.key === "Enter") { + document.getElementById("input_"+promptID+"_sec").focus(); + } + }); + document.getElementById("input_"+promptID+"_sec").addEventListener("keyup", function(event) { + if (event.key === "Enter") { + document.getElementById("submit_"+promptID).focus(); + } + }); + document.getElementById("countup_"+promptID).addEventListener("click", function(event) { + if (document.getElementById("countup_"+promptID).checked){ + document.getElementById("input_"+promptID).disabled = true + document.getElementById("input_"+promptID+"_sec").disabled = true + } else { + document.getElementById("input_"+promptID).disabled = false + document.getElementById("input_"+promptID+"_sec").disabled = false + delete document.getElementById("input_"+promptID).disabled; + delete document.getElementById("input_"+promptID+"_sec").disabled; + } + }); + } + + + document.getElementById("submit_"+promptID).addEventListener("click", function(event){ + var pid = event.target.dataset.pid; + if (time){ + result = parseInt(document.getElementById("input_"+pid+"_sec").value) + parseInt(document.getElementById("input_"+pid).value)*60; + + if (document.getElementById("countup_"+promptID).checked){ + result = 0; + } + + } else { + result = document.getElementById("input_"+pid).value; + } + + document.getElementById("modal_"+pid).remove(); + document.getElementById("modalBackdrop_"+pid).remove(); + + Prompts[pid].resolve(); + }); + + document.getElementById("cancel_"+promptID).addEventListener("click", function(event){ + var pid = event.target.dataset.pid; + document.getElementById("modal_"+pid).remove(); + document.getElementById("modalBackdrop_"+pid).remove(); + Prompts[pid].resolve(); + }); + + document.getElementById("close_"+promptID).addEventListener("click", function(event){ + var pid = event.target.dataset.pid; + document.getElementById("modal_"+pid).remove(); + document.getElementById("modalBackdrop_"+pid).remove(); + Prompts[pid].resolve(); + }); + + getById("modal_"+promptID).addEventListener("click", function(e) { + e.stopPropagation(); + return false; + }); + return; + }); + return result; +} + +async function promptTransfer(value=null, bcmode = null, updateurl = null, queueMode = null){ + var result = {roomid:null}; + if (session.beepToNotify){ + playtone(); + } + await new Promise((resolve, reject) => { + var promptID = "pid_"+Math.random().toString(36).substr(2, 9); + Prompts[promptID] = {}; + Prompts[promptID].resolve = resolve; + Prompts[promptID].reject = reject; + + var zindex = 30 + document.querySelectorAll('.promptModal').length; + var backdropClass = "modalBackdrop"; + + var inputText = ""+(getTranslation("transfer-guest-to-room").replace("\n","
    "))+""; + inputText = inputText.replace(/\n/g,"
    "); + + modalTemplate = + ` +
    `; + + + document.body.insertAdjacentHTML("beforeend", modalTemplate); // Insert modal at body end + + document.getElementById("input_"+promptID).focus(); + + if (value!==null){ + document.getElementById("input_"+promptID).value = value; + } + + if (bcmode!==null){ + document.getElementById("broadcast_"+promptID).checked = bcmode; + } + if (queueMode!==null){ + document.getElementById("queued_"+promptID).checked = queueMode; + } + + if (updateurl!==null){ + document.getElementById("private_"+promptID).checked = updateurl; + } + + document.getElementById("input_"+promptID).addEventListener("keyup", function(event) { + if (event.key === "Enter") { + var pid = event.target.dataset.pid; + var room = document.getElementById("input_"+pid).value; + var updateurl = document.getElementById("private_"+pid).checked; + var broadcast = document.getElementById("broadcast_"+pid).checked; + var queue = document.getElementById("queued_"+pid).checked; + document.getElementById("modal_"+pid).remove(); + document.getElementById("modalBackdrop_"+pid).remove(); + Prompts[pid].resolve(); + result = {roomid:room, updateurl:updateurl, broadcast:broadcast, queue:queue}; + } + }); + + document.getElementById("submit_"+promptID).addEventListener("click", function(event){ + var pid = event.target.dataset.pid; + var room = document.getElementById("input_"+pid).value; + var updateurl = document.getElementById("private_"+pid).checked; + var broadcast = document.getElementById("broadcast_"+pid).checked; + var queue = document.getElementById("queued_"+pid).checked; + document.getElementById("modal_"+pid).remove(); + document.getElementById("modalBackdrop_"+pid).remove(); + Prompts[pid].resolve(); + result = {roomid:room, updateurl:updateurl, broadcast:broadcast, queue:queue}; + }); + + document.getElementById("cancel_"+promptID).addEventListener("click", function(event){ + var pid = event.target.dataset.pid; + document.getElementById("modal_"+pid).remove(); + document.getElementById("modalBackdrop_"+pid).remove(); + Prompts[pid].resolve(); + }); + + document.getElementById("close_"+promptID).addEventListener("click", function(event){ + var pid = event.target.dataset.pid; + document.getElementById("modal_"+pid).remove(); + document.getElementById("modalBackdrop_"+pid).remove(); + Prompts[pid].resolve(); + }); + + getById("modal_"+promptID).addEventListener("click", function(e) { + e.stopPropagation(); + return false; + }); + return; + }); + return result; +} + +function youveBeenTransferred(){ + getChatMessage( getTranslation("you-have-been-transferred"), label = false, director = false, overlay = true); // "you-have-been-transferred" + getById("head2").innerHTML = getTranslation("room-changed"); // not sure this is right?? + + miniTranslate(getById("head2"), "room-changed"); + + if (session.director){ + getById("head4").innerHTML = getTranslation("you-are-no-longer-a-co-director"); //"You are no longer a co-director as you were transferred."; // + } + + if (session.label){ + document.title = session.label + " - " + getTranslation("transferred"); + } else { + document.title = getTranslation("transferred"); + } + + hideHomeCheck(); +} + +function youveBeenActivated(){ + getChatMessage( getTranslation("you-have-been-activated"), label = false, director = false, overlay = true); // "you-have-been-transferred" + hideHomeCheck(); +} + +async function confirmAlt(inputText, block=false){ + var result = null; + if (session.beepToNotify){ + playtone(); + } + await new Promise((resolve, reject) => { + var promptID = "pid_"+Math.random().toString(36).substr(2, 9); + Prompts[promptID] = {}; + Prompts[promptID].resolve = resolve; + Prompts[promptID].reject = reject; + + var zindex = 30 + document.querySelectorAll('.promptModal').length; + + if (block){ + var backdropClass = "opaqueBackdrop"; + } else { + var backdropClass = "modalBackdrop"; + } + + inputText = ""+inputText.replace("\n","
    ")+""; + inputText = inputText.replace(/\n/g,"
    "); + + modalTemplate = + ` +
    `; + + + document.body.insertAdjacentHTML("beforeend", modalTemplate); // Insert modal at body end + + document.getElementById("submit_"+promptID).focus(); + + document.getElementById("submit_"+promptID).addEventListener("click", function(event){ + var pid = event.target.dataset.pid; + result = true; + document.getElementById("modalBackdrop_"+pid).remove(); + document.getElementById("modal_"+pid).remove(); + Prompts[pid].resolve(); + }); + + document.getElementById("cancel_"+promptID).addEventListener("click", function(event){ + var pid = event.target.dataset.pid; + document.getElementById("modalBackdrop_"+pid).remove(); + document.getElementById("modal_"+pid).remove(); + Prompts[pid].resolve(); + }); + + document.getElementById("close_"+promptID).addEventListener("click", function(event){ + var pid = event.target.dataset.pid; + document.getElementById("modalBackdrop_"+pid).remove(); + document.getElementById("modal_"+pid).remove(); + Prompts[pid].resolve(); + }); + + getById("modal_"+promptID).addEventListener("click", function(e) { + e.stopPropagation(); + return false; + }); + return; + }); + return result; +} + +var modalTimeout=null; +function warnUser(message, timeout=false, sanitize=true){ + // Allows for multiple alerts to stack better. + // Every modal and backdrop has an increasing z-index + // to block the previous modal + if (!message){return;} + + if (document.getElementById("modalBackdrop")){ + getById("alertModal").innerHTML = ''; // Delete modal + getById("alertModal").remove(); + getById("modalBackdrop").innerHTML = ''; // Delete modal + getById("modalBackdrop").remove(); + } + + zindex = 31 + document.querySelectorAll('.alertModal').length; + try{ + if (sanitize){ + message = sanitizeChat(message,2000); + } + message = message.replace(/\n/g,"
    "); + } catch(e){ + errorlog(message); + } + modalTemplate = + `
    +
    + × + ${message} +
    +
    +
    `; + document.body.insertAdjacentHTML("beforeend", modalTemplate); // Insert modal at body end + + document.getElementById("modalBackdrop").addEventListener("click", closeModal); + + clearTimeout(modalTimeout); + if (timeout){ + modalTimeout = setTimeout(closeModal, timeout); + } + getById("alertModal").addEventListener("click", function(e) { + e.stopPropagation(); + return false; + }); + +} +function closeModal(ele=false){ + clearTimeout(modalTimeout); + try { + getById("modalBackdrop").innerHTML = ''; // Delete modal + getById("modalBackdrop").remove(); + getById("alertModal").innerHTML = ''; // Delete modal + getById("alertModal").remove(); + getById("promptModal").innerHTML = ''; // Delete modal + getById("promptModal").remove(); + + query(".modalBackdrop").innerHTML = ''; // Delete modal + query(".modalBackdrop").remove(); + + if (ele && ele.innerHTML && ele.remove){ + ele.innerHTML = ''; // Delete specific modal + ele.remove(); + } + } catch(e){ + warnlog(e); + } +} + +var sanitizeStreamID = function(streamID) { + streamID = streamID.trim(); + + if (streamID.length < 1) { + streamID = session.generateStreamID(8); + if (!(session.cleanOutput)) { + warnUser(getTranslation("no-streamID-provided") + streamID, false, false); + } + } + var streamID_sanitized = streamID.replace(/[\W]+/g, "_"); + if (streamID !== streamID_sanitized) { + if (!(session.cleanOutput)) { + warnUser(getTranslation("alphanumeric-only"), false, false); + } + } + if (streamID_sanitized.length > 44) { + streamID_sanitized = streamID_sanitized.substring(0, 50); + if (!(session.cleanOutput)) { + warnUser(getTranslation("stream-id-too-long"), false, false); + } + } + return streamID_sanitized; +}; + +var checkStrength = function(string) { + var matcher = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{7,30}$/; + if (string.match(matcher)) { + return true; + } else if (string.length > 20) { + return true; + } else { + return false; + } +}; + +var checkStrengthRoom = function() { + var result1 = checkStrength(getById('videoname1').value); + var result2 = getById('passwordRoom').value.length; + var target = getById('securityLevelRoom'); + target.style.display = "block"; + if (result1) { + if (result2) { + target.innerHTML = ""+getTranslation("share-with-trusted")+""; + } else { + target.innerHTML = ""+getTranslation("pass-recommended")+""; + } + } else { + target.innerHTML = ""+getTranslation("insecure-room-name")+" "+getTranslation("allowed-chars")+": A-Z, a-z, 0-9, _"; + } +}; + +var emojiShortCodes ={":joy:":"😂",":heart:":"❤️",":heart_eyes:":"😍",":sob:":"😭",":blush:":"😊",":unamused:":"😒",":two_hearts:":"💕",":weary:":"😩",":ok_hand:":"👌",":pensive:":"😔",":smirk:":"😏",":grin:":"😁",":wink:":"😉",":thumbsup:":"👍",":pray:":"🙏",":relieved:":"😌",":notes:":"🎶",":flushed:":"😳",":raised_hands:":"🙌",":see_no_evil:":"🙈",":cry:":"😢",":sunglasses:":"😎",":v:":"✌️",":eyes:":"👀",":sweat_smile:":"😅",":sparkles:":"✨",":sleeping:":"😴",":smile:":"😄",":purple_heart:":"💜",":broken_heart:":"💔",":blue_heart:":"💙",":confused:":"😕",":disappointed:":"😞",":yum:":"😋",":neutral_face:":"😐",":sleepy:":"😪",":clap:":"👏",":cupid:":"💘",":heartpulse:":"💗",":kiss:":"💋",":point_right:":"👉",":scream:":"😱",":fire:":"🔥",":rage:":"😡",":smiley:":"😃",":tada:":"🎉",":tired_face:":"😫",":camera:":"📷",":rose:":"🌹",":muscle:":"💪",":skull:":"💀",":sunny:":"☀️",":yellow_heart:":"💛",":triumph:":"😤",":laughing:":"😆",":sweat:":"😓",":point_left:":"👈",":grinning:":"😀",":mask:":"😷",":green_heart:":"💚",":wave:":"👋",":persevere:":"😣",":heartbeat:":"💓",":crown:":"👑",":innocent:":"😇",":headphones:":"🎧",":confounded:":"😖",":angry:":"😠",":grimacing:":"😬",":star2:":"🌟",":gun:":"🔫",":raising_hand:":"🙋",":thumbsdown:":"👎",":dancer:":"💃",":musical_note:":"🎵",":no_mouth:":"😶",":dizzy:":"💫",":fist:":"✊",":point_down:":"👇",":no_good:":"🙅",":boom:":"💥",":tongue:":"👅",":poop:":"💩",":cold_sweat:":"😰",":gem:":"💎",":ok_woman:":"🙆",":pizza:":"🍕",":joy_cat:":"😹",":leaves:":"🍃",":sweat_drops:":"💦",":penguin:":"🐧",":zzz:":"💤",":walking:":"🚶",":airplane:":"✈️",":balloon:":"🎈",":star:":"⭐",":ribbon:":"🎀",":worried:":"😟",":underage:":"🔞",":fearful:":"😨",":hibiscus:":"🌺",":microphone:":"🎤",":open_hands:":"👐",":ghost:":"👻",":palm_tree:":"🌴",":nail_care:":"💅",":alien:":"👽",":bow:":"🙇",":cloud:":"☁",":soccer:":"⚽",":angel:":"👼",":dancers:":"👯",":snowflake:":"❄️",":point_up:":"☝️",":rainbow:":"🌈",":gift_heart:":"💝",":gift:":"🎁",":beers:":"🍻",":anguished:":"😧",":earth_africa:":"🌍",":movie_camera:":"🎥",":anchor:":"⚓",":zap:":"⚡",":runner:":"🏃",":sunflower:":"🌻",":bouquet:":"💐",":dog:":"🐶",":moneybag:":"💰",":herb:":"🌿",":couple:":"👫",":fallen_leaf:":"🍂",":tulip:":"🌷",":birthday:":"🎂",":cat:":"🐱",":coffee:":"☕",":dizzy_face:":"😵",":point_up_2:":"👆",":open_mouth:":"😮",":hushed:":"😯",":basketball:":"🏀",":ring:":"💍",":astonished:":"😲",":hear_no_evil:":"🙉",":dash:":"💨",":cactus:":"🌵",":hotsprings:":"♨️",":telephone:":"☎️",":maple_leaf:":"🍁",":princess:":"👸",":massage:":"💆",":love_letter:":"💌",":trophy:":"🏆",":blossom:":"🌼",":lips:":"👄",":fries:":"🍟",":doughnut:":"🍩",":frowning:":"😦",":ocean:":"🌊",":bomb:":"💣",":cyclone:":"🌀",":rocket:":"🚀",":umbrella:":"☔",":couplekiss:":"💏",":lollipop:":"🍭",":clapper:":"🎬",":pig:":"🐷",":smiling_imp:":"😈",":imp:":"👿",":bee:":"🐝",":kissing_cat:":"😽",":anger:":"💢",":santa:":"🎅",":earth_asia:":"🌏",":football:":"🏈",":guitar:":"🎸",":panda_face:":"🐼",":strawberry:":"🍓",":smirk_cat:":"😼",":banana:":"🍌",":watermelon:":"🍉",":snowman:":"⛄",":smile_cat:":"😸",":eggplant:":"🍆",":crystal_ball:":"🔮",":calling:":"📲",":iphone:":"📱",":partly_sunny:":"⛅",":warning:":"⚠️",":scream_cat:":"🙀",":baby:":"👶",":feet:":"🐾",":footprints:":"👣",":beer:":"🍺",":wine_glass:":"🍷",":video_camera:":"📹",":rabbit:":"🐰",":smoking:":"🚬",":peach:":"🍑",":snake:":"🐍",":turtle:":"🐢",":cherries:":"🍒",":kissing:":"😗",":frog:":"🐸",":milky_way:":"🌌",":closed_book:":"📕",":candy:":"🍬",":hamburger:":"🍔",":bear:":"🐻",":tiger:":"🐯",":icecream:":"🍦",":pineapple:":"🍍",":ear_of_rice:":"🌾",":syringe:":"💉",":tv:":"📺",":pill:":"💊",":octopus:":"🐙",":grapes:":"🍇",":smiley_cat:":"😺",":cd:":"💿",":cocktail:":"🍸",":cake:":"🍰",":video_game:":"🎮",":lipstick:":"💄",":whale:":"🐳",":cookie:":"🍪",":dolphin:":"🐬",":loud_sound:":"🔊",":man:":"👨",":monkey:":"🐒",":books:":"📚",":guardsman:":"💂",":loudspeaker:":"📢",":scissors:":"✂️",":girl:":"👧",":mortar_board:":"🎓",":baseball:":"⚾️",":woman:":"👩",":fireworks:":"🎆",":stars:":"🌠",":mushroom:":"🍄",":pouting_cat:":"😾",":left_luggage:":"🛅",":high_heel:":"👠",":dart:":"🎯",":swimmer:":"🏊",":key:":"🔑",":bikini:":"👙",":family:":"👪",":pencil2:":"✏",":elephant:":"🐘",":droplet:":"💧",":seedling:":"🌱",":apple:":"🍎",":dollar:":"💵",":book:":"📖",":haircut:":"💇",":computer:":"💻",":bulb:":"💡",":boy:":"👦",":tangerine:":"🍊",":sunrise:":"🌅",":poultry_leg:":"🍗",":shaved_ice:":"🍧",":bird:":"🐦",":eyeglasses:":"👓",":goat:":"🐐",":older_woman:":"👵",":new_moon:":"🌑",":customs:":"🛃",":house:":"🏠",":full_moon:":"🌕",":lemon:":"🍋",":baby_bottle:":"🍼",":spaghetti:":"🍝",":wind_chime:":"🎐",":fish_cake:":"🍥",":nose:":"👃",":pig_nose:":"🐽",":fish:":"🐟",":koala:":"🐨",":ear:":"👂",":shower:":"🚿",":bug:":"🐛",":ramen:":"🍜",":tophat:":"🎩",":fuelpump:":"⛽",":horse:":"🐴",":watch:":"⌚",":monkey_face:":"🐵",":baby_symbol:":"🚼",":sparkler:":"🎇",":corn:":"🌽",":tennis:":"🎾",":battery:":"🔋",":wolf:":"🐺",":moyai:":"🗿",":cow:":"🐮",":mega:":"📣",":older_man:":"👴",":dress:":"👗",":link:":"🔗",":chicken:":"🐔",":whale2:":"🐋",":bento:":"🍱",":pushpin:":"📌",":dragon:":"🐉",":hamster:":"🐹",":golf:":"⛳",":surfer:":"🏄",":mouse:":"🐭",":blue_car:":"🚙",":bread:":"🍞",":cop:":"👮",":tea:":"🍵",":bike:":"🚲",":rice:":"🍚",":radio:":"📻",":baby_chick:":"🐤",":sheep:":"🐑",":lock:":"🔒",":green_apple:":"🍏",":racehorse:":"🐎",":fried_shrimp:":"🍤",":volcano:":"🌋",":rooster:":"🐓",":inbox_tray:":"📥",":wedding:":"💒",":sushi:":"🍣",":ice_cream:":"🍨",":tomato:":"🍅",":rabbit2:":"🐇",":beetle:":"🐞",":bath:":"🛀",":no_entry:":"⛔",":crocodile:":"🐊",":dog2:":"🐕",":cat2:":"🐈",":hammer:":"🔨",":meat_on_bone:":"🍖",":shell:":"🐚",":poodle:":"🐩",":stew:":"🍲",":jeans:":"👖",":honey_pot:":"🍯",":unlock:":"🔓",":black_nib:":"✒",":snowboarder:":"🏂",":white_flower:":"💮",":necktie:":"👔",":womens:":"🚺",":ant:":"🐜",":city_sunset:":"🌇",":dragon_face:":"🐲",":snail:":"🐌",":dvd:":"📀",":shirt:":"👕",":game_die:":"🎲",":dolls:":"🎎",":8ball:":"🎱",":bus:":"🚌",":custard:":"🍮",":camel:":"🐫",":curry:":"🍛",":hospital:":"🏥",":bell:":"🔔",":pear:":"🍐",":door:":"🚪",":saxophone:":"🎷",":church:":"⛪",":bicyclist:":"🚴",":dango:":"🍡",":office:":"🏢",":rowboat:":"🚣",":womans_hat:":"👒",":mans_shoe:":"👞",":love_hotel:":"🏩",":mount_fuji:":"🗻",":handbag:":"👜",":hourglass:":"⌛",":trumpet:":"🎺",":school:":"🏫",":cow2:":"🐄",":toilet:":"🚽",":pig2:":"🐖",":violin:":"🎻",":credit_card:":"💳",":ferris_wheel:":"🎡",":bowling:":"🎳",":barber:":"💈",":purse:":"👛",":rat:":"🐀",":date:":"📅",":ram:":"🐏",":tokyo_tower:":"🗼",":kimono:":"👘",":ship:":"🚢",":mag_right:":"🔎",":mag:":"🔍",":fire_engine:":"🚒",":police_car:":"🚓",":black_joker:":"🃏",":package:":"📦",":calendar:":"📆",":horse_racing:":"🏇",":tiger2:":"🐅",":boot:":"👢",":ambulance:":"🚑",":boar:":"🐗",":pound:":"💷",":ox:":"🐂",":rice_ball:":"🍙",":sandal:":"👡",":tent:":"⛺",":seat:":"💺",":taxi:":"🚕",":briefcase:":"💼",":newspaper:":"📰",":circus_tent:":"🎪",":mens:":"🚹",":flashlight:":"🔦",":foggy:":"🌁",":bamboo:":"🎍",":ticket:":"🎫",":helicopter:":"🚁",":minidisc:":"💽",":oncoming_bus:":"🚍",":melon:":"🍈",":notebook:":"📓",":no_bell:":"🔕",":oden:":"🍢",":flags:":"🎏",":blowfish:":"🐡",":sweet_potato:":"🍠",":ski:":"🎿",":construction:":"🚧",":satellite:":"📡",":euro:":"💶",":ledger:":"📒",":leopard:":"🐆",":truck:":"🚚",":sake:":"🍶",":railway_car:":"🚃",":speedboat:":"🚤",":vhs:":"📼",":yen:":"💴",":mute:":"🔇",":wheelchair:":"♿",":paperclip:":"📎",":atm:":"🏧",":telescope:":"🔭",":rice_scene:":"🎑",":blue_book:":"📘",":postbox:":"📮",":e-mail:":"📧",":mouse2:":"🐁",":nut_and_bolt:":"🔩",":hotel:":"🏨",":wc:":"🚾",":green_book:":"📗",":tractor:":"🚜",":fountain:":"⛲",":metro:":"🚇",":clipboard:":"📋",":no_smoking:":"🚭",":slot_machine:":"🎰",":bathtub:":"🛁",":scroll:":"📜",":station:":"🚉",":rice_cracker:":"🍘",":bank:":"🏦",":wrench:":"🔧",":bar_chart:":"📊",":minibus:":"🚐",":tram:":"🚊",":microscope:":"🔬",":bookmark:":"🔖",":pouch:":"👝",":fax:":"📠",":sound:":"🔉",":chart:":"💹",":floppy_disk:":"💾",":post_office:":"🏣",":speaker:":"🔈",":japan:":"🗾",":mahjong:":"🀄",":orange_book:":"📙",":restroom:":"🚻",":train:":"🚋",":trolleybus:":"🚎",":postal_horn:":"📯",":factory:":"🏭",":train2:":"🚆",":pager:":"📟",":outbox_tray:":"📤",":mailbox:":"📫",":light_rail:":"🚈",":busstop:":"🚏",":file_folder:":"📁",":card_index:":"📇",":monorail:":"🚝",":no_bicycles:":"🚳",":hugging:":"🤗",":thinking:":"🤔",":nerd:":"🤓",":zipper_mouth:":"🤐",":rolling_eyes:":"🙄",":upside_down:":"🙃",":slight_smile:":"🙂",":writing_hand:":"✍",":eye:":"👁",":man_in_suit:":"🕴",":golfer:":"🏌",":golfer_woman:":"🏌‍♀",":anger_right:":"🗯",":coffin:":"⚰",":gear:":"⚙",":alembic:":"⚗",":scales:":"⚖",":keyboard:":"⌨",":shield:":"🛡",":bed:":"🛏",":ballot_box:":"🗳",":compression:":"🗜",":wastebasket:":"🗑",":file_cabinet:":"🗄",":trackball:":"🖲",":printer:":"🖨",":joystick:":"🕹",":hole:":"🕳",":candle:":"🕯",":prayer_beads:":"📿",":amphora:":"🏺",":label:":"🏷",":film_frames:":"🎞",":level_slider:":"🎚",":thermometer:":"🌡",":motorway:":"🛣",":synagogue:":"🕍",":mosque:":"🕌",":kaaba:":"🕋",":stadium:":"🏟",":desert:":"🏜",":cityscape:":"🏙",":camping:":"🏕",":rosette:":"🏵",":volleyball:":"🏐",":medal:":"🏅",":popcorn:":"🍿",":champagne:":"🍾",":hot_pepper:":"🌶",":burrito:":"🌯",":taco:":"🌮",":hotdog:":"🌭",":shamrock:":"☘",":comet:":"☄",":turkey:":"🦃",":scorpion:":"🦂",":lion_face:":"🦁",":crab:":"🦀",":spider_web:":"🕸",":spider:":"🕷",":chipmunk:":"🐿",":fog:":"🌫",":chains:":"⛓",":pick:":"⛏",":stopwatch:":"⏱",":ferry:":"⛴",":mountain:":"⛰",":ice_skate:":"⛸",":skier:":"⛷",":sad:":"😥",":egg:":"🥚",":drum:":"🥁"}; + +function convertShortcodes(string){ + if (string.split(":").length>2){ + for (var i in emojiShortCodes) { + if (string.includes(i)) { + string = string.replaceAll(i, emojiShortCodes[i]); + } + } + } + return string; +} + +var sanitizeChat = function(string, maxlength=500) { + var temp = document.createElement('div'); + temp.innerText = string; + temp.innerText = temp.innerHTML; + temp = temp.textContent || temp.innerText || ""; + temp = temp.substring(0, Math.min(temp.length, maxlength)); + return temp.trim(); +}; + +var sanitizeString = function(str) { + str = str.replace(/[^a-z0-9áéíóúñü \.,_-]/gim, ""); + return str.trim(); +}; + +var sanitizeLabel = function(string) { + let temp = document.createElement("div"); + temp.innerText = string; + temp.innerText = temp.innerHTML; + temp = temp.textContent || temp.innerText || ""; + temp = temp.substring(0, Math.min(temp.length, 100)); + return temp.trim(); +}; + +var sanitizeRoomName = function(roomid) { + roomid = roomid.trim(); + if (roomid === "") { + return roomid; + } else if (roomid === false) { + return roomid; + } + + var sanitized = roomid.replace(/[\W]+/g, "_"); + if (roomid.replace(/ /g, "_") !== sanitized) { + if (!(session.cleanOutput)) { + warnUser("Info: Only AlphaNumeric characters should be used for the room name.\n\nThe offending characters have been replaced by an underscore"); + } + } + if (sanitized.length > 30) { + sanitized = sanitized.substring(0, 30); + if (!(session.cleanOutput)) { + warnUser("The Room name should be less than 31 alPhaNuMeric characters long.\n\nWe will trim it to length."); + } + } + return sanitized; +}; + +var sanitizePassword = function(passwrd) { + if (passwrd === "") { + return passwrd; + } else if (passwrd === false) { + return passwrd; + } else if (passwrd === null) { + return passwrd; + } + passwrd = passwrd.trim(); + if (passwrd.length < 1) { + if (!(session.cleanOutput)) { + warnUser("The password provided was blank."); + } + } + var sanitized = encodeURIComponent(passwrd);//.replace(/[\W]+/g, "_"); + //if (sanitized !== passwrd) { + // if (!(session.cleanOutput)) { + // warnUser("Info: Only AlphaNumeric characters should be used in the password.\n\nThe offending characters have been replaced by an underscore"); + // } + //} + return sanitized; +}; + +function checkConnection() { + if (session.ws === null) { + return; + } + if (!session.cleanOutput){ + if (document.getElementById("qos")) { // true or false; null might cause problems? + getById("logoname").style.display = "unset"; + if ((session.ws) && (session.ws.readyState === WebSocket.OPEN)) { + getById("qos").style.color = "white"; + } else { + getById("qos").style.color = "red"; + } + } + } +} + +function combinedLayout(layout){ + var combined = {}; + for (var i=0;i{ + if (!layout[i]){return;} + + if (i===""){ + layout[i].forEach(j=>{ + if (!j){return;} + var streamID = null; + if ("slot" in j){ + try { + streamID = session.currentSlots[parseInt( j.slot)+1]; // slot 1 is index of 0, but slot 0 is considered NULL; I need to stream line this a bit + } catch(e){ + errorlog(e); + streamID = null; + } + } + if (!streamID){ + if (combined[""]){ + combined[""].push(j); + } else { + combined[""] = j; + } + } else { + combined[streamID] = j; + } + }); + + } else { + + var streamID = null; + if ("slot" in layout[i]){ + try { + streamID = session.currentSlots[parseInt(layout[i].slot)+1]; // slot 1 is index of 0, but slot 0 is considered NULL; I need to stream line this a bit + } catch(e){ + errorlog(e); + streamID = null; + } + } + if (!streamID){ + if (combined[""]){ + combined[""].push(layout[i]); + } else { + combined[""] = [layout[i]]; + } + } else { + combined[streamID] = layout[i]; + } + } + }); + return combined; +} + +session.obsSceneSync = function(){ + if (session.layouts && session.obsSceneTriggers && session.obsState && session.obsState.details && session.obsState.details.currentScene.name && session.obsSceneTriggers.includes(session.obsState.details.currentScene.name)){ + var idx = session.obsSceneTriggers.indexOf(session.obsState.details.currentScene.name); + if (idx>=0){ + if (session.layouts[idx]){ + var layout = combinedLayout(session.layouts[idx]); + if (layout){ + session.layout = layout; + updateMixer(); + } + } + } + return true + } + return false; +} + + +session.sceneSync = function(UUID){ + if (!session.rpcs[UUID]){return;} + else if (!session.rpcs[UUID].videoElement){return;} // i'll want to consider other things, such as canvas at some point. + + var msg = {}; + msg.sceneDisplay = (session.rpcs[UUID].videoElement.style.display!="none"); + msg.sceneMute = session.rpcs[UUID].mutedState; + + if (session.optimize!==false){ // if not visible in the scene anymore, lets lets optimize. This is outside the scope of OBS + var bandwidth = parseInt(session.rpcs[UUID].targetBandwidth); // wtf is goign on here? + if (msg.sceneDisplay===false){ + if ((bandwidth > session.optimize) || (bandwidth<0)){ // limit to optimized bitrate + bandwidth = session.optimize; + } + } + if (session.rpcs[UUID].bandwidth !== bandwidth){ // bandwidth already set correctly. don't resend. + msg.bitrate = bandwidth; + if (session.sendRequest(msg, UUID)){ + session.rpcs[UUID].bandwidth=bandwidth; // this is letting the system know what the actual bandwidth is, even if it isn't the real target. + } else { + errorlog("Unable to set update OBS Visibility"); + } + } else { + session.sendRequest(msg, UUID); + } + } else { + session.sendRequest(msg, UUID); + } +} + +var TriggerOnNewDetails = false; +session.obsStateSync = function(data2send=false, uid=false){ + if (session.disableOBS){return;} + if (!window.obsstudio){return;} // this isn't OBS + // they can disable remote control via OBS brower source drop-down itself. + + log(data2send); + + if (data2send && (data2send == "sourceActive") && session.obsState.sourceActive){ + TriggerOnNewDetails = true; + } else if (data2send && (data2send == "details") && session.obsState.sourceActive && TriggerOnNewDetails){ + if (session.obsState.details && session.obsState.details.currentScene && session.obsState.details.currentScene.name){ + session.obsState.details.thisScene = session.obsState.details.currentScene.name; + TriggerOnNewDetails = false; + } + } + + var needOptimize = false; + if (session.obsState.visibility!==null){ + if (session.obsState.visibility===false){ /////////////////// I need to change tis to .state or whatever, anc catch/handle these events to update the buttons in the pop up menu + needOptimize=true; + } + } + + session.obsSceneSync(); + + for (var UUID in session.rpcs){ + if (uid && (uid!==UUID)){continue;} // target just a single connection. + + var msg = {}; + if (!data2send){ + msg.obsState = session.obsState; + if (session.rpcs[UUID].obsControl===false){ + msg.obsState.details = null; // we don't want to send needless data + } + } else if (data2send in session.obsState){ + if (data2send == "details"){ + if (session.rpcs[UUID].obsControl===false){ + continue; // we don't want to send needless data; this isn't a visibility update, so skip. + } + msg.obsState = {}; + msg.obsState[data2send] = session.obsState[data2send]; + } else { + msg.obsState = {}; + msg.obsState[data2send] = session.obsState[data2send]; + } + } + + if (session.filterOBSscenes && msg.obsState && msg.obsState.details && msg.obsState.details.scenes && msg.obsState.details.scenes.length){ + var scenes = []; + msg.obsState.details.scenes.forEach(scene=>{ + if (session.filterOBSscenes && session.filterOBSscenes.length){ + if (session.filterOBSscenes.includes(scene)){ + scenes.push(scene); + } + } + }); + msg.obsState.details.scenes = scenes; + } + + if (session.optimize!==false){ + var bandwidth = parseInt(session.rpcs[UUID].targetBandwidth); + if (needOptimize){ + if ((bandwidth > session.optimize) || (bandwidth<0)){ // limit to optimized bitrate + bandwidth = session.optimize; + } + } + if (session.rpcs[UUID].bandwidth !== bandwidth){ // bandwidth already set correctly. don't resend. + msg.bitrate = bandwidth; + warnlog("Message to be sent: "); + warnlog(msg); + if (session.sendRequest(msg, UUID)){ + session.rpcs[UUID].bandwidth=bandwidth; // this is letting the system know what the actual bandwidth is, even if it isn't the real target. + } else { + errorlog("Unable to set update OBS Visibility"); + } + } else { + warnlog("Message to be sent: "); + warnlog(msg); + session.sendRequest(msg, UUID); + } + } else { + warnlog("Message to be sent: "); + warnlog(msg); + session.sendRequest(msg, UUID); + } + } +} + +session.getOBSOptimization = function(msg, UUID){ + if (session.obsState){ + msg.obsState = {}; + var needOptimize = false; + if (session.obsState.visibility!==null){ + msg.obsState.visibility = session.obsState.visibility; + if (session.obsState.visibility===false){ + needOptimize=true; + } + } + if (session.obsState.sourceActive!==null){ + msg.obsState.sourceActive = session.obsState.sourceActive; + //if (session.obsState.sourceActive===false){ + // needOptimize=true; + //} + } + if (session.obsState.recording!==null){ + msg.obsState.recording = session.obsState.recording; + } + if (session.obsState.streaming!==null){ + msg.obsState.streaming = session.obsState.streaming; + } + if (session.obsState.virtualcam!==null){ + msg.obsState.virtualcam = session.obsState.virtualcam; + } + } + if (session.optimize!==false){ + msg.optimizedBitrate = parseInt(session.optimize); // not setting a bitrate; just letting them know what the optimized bitrate is. + if (needOptimize){ + session.rpcs[UUID].bandwidth = msg.optimizedBitrate; + } + } + return msg; +} + +function getOBSDetails(callbackname = "details"){ + if (session.disableOBS){return false;} + if (!window.obsstudio){return;} + + if (!("details" in session.obsState)){ + session.obsState.details = {}; + } + + var readOnlyFuncs = [ + "getControlLevel", + //"getStatus", + "getCurrentScene", + "getScenes", + //"getTransitions", + //"getCurrentTransition", + //"pluginVersion" + ]; + + var promises = {}; + promises.main = true; + + Object.keys(window.obsstudio).forEach(async (key)=>{ + try { + if (typeof window.obsstudio[key] === 'function'){ + + if (readOnlyFuncs.includes(key)){ + try { + promises[key] = true; + window.obsstudio[key](function(out){ + var shortkey = key.replace("get",""); + shortkey = shortkey[0].toLowerCase() + shortkey.slice(1); + session.obsState.details[shortkey] = out; + delete promises[key] + if (!Object.keys(promises).length){ + session.obsStateSync(callbackname); + } + }); + } catch(e){ + delete promises[key] + } + } + /* } else if (typeof window.obsstudio[key] === 'object'){ // none of these values I really need right now. + var shortkey = key.replace("get",""); + shortkey = shortkey[0].toLowerCase() + shortkey.slice(1); + session.obsState.details[shortkey] = window.obsstudio[key]; + } else { + var shortkey = key.replace("get",""); + shortkey = shortkey[0].toLowerCase() + shortkey.slice(1); + session.obsState.details[shortkey] = window.obsstudio[key]; */ + } + } catch(e){ + errorlog(e); + } + }); + delete promises.main; + if (!Object.keys(promises).length){ + session.obsStateSync(callbackname); + } +} + +function toggleOBSControls(){ + toggle(getById('remoteOBSControl')); + if (getById('remoteOBSControl').style.display=="none"){ + getById("modalBackdrop").innerHTML = ''; // Delete modal + getById("modalBackdrop").remove(); + } else { + getById("modalBackdrop").innerHTML = ''; // Delete modal + getById("modalBackdrop").remove(); + var modalTemplate = `
    `; + document.body.insertAdjacentHTML("beforeend", modalTemplate); // Insert modal at body end + document.getElementById("modalBackdrop").addEventListener("click", toggleOBSControls); + } +} + +function requestOBSAction(ele){ + if (session.disableOBS){return false;} +} +function obsSceneChanged(event){ + log(event.detail.name); + getOBSDetails(); // contains obsStateSync +} +function obsVirtualcamStarted(event){ + session.obsState.virtualcam = true; + session.obsStateSync("virtualcam"); +} +function obsVirtualcamStopped(event){ + session.obsState.virtualcam = false; + session.obsStateSync("virtualcam"); +} +function obsStreamingStarted(event){ + session.obsState.streaming = true; + session.obsStateSync("streaming"); +} +function obsStreamingStopped(event){ + session.obsState.streaming = false; + session.obsStateSync("streaming"); +} +function obsRecordingStarted(event){ + session.obsState.recording = true; + session.obsStateSync("recording"); +} +function obsRecordingStopped(event){ + session.obsState.recording = false; + session.obsStateSync("recording"); +} +function obsSourceActiveChanged(event){ + warnlog("obsSourceActiveChanged"); + warnlog( event.detail); + + try { + if (typeof event==="boolean"){var sourceActive = event;} + else if (typeof event.detail === "boolean"){var sourceActive = event.detail;} + else if (typeof event.detail.active === "boolean"){var sourceActive = event.detail.active;} + else {var sourceActive = event.detail.active;} + + if (typeof sourceActive === "undefined"){return;} // Just fail. + + if (session.obsState.sourceActive!==sourceActive){ // only move forward if there is a change; the event likes to double fire you see. + session.obsState.sourceActive = sourceActive; + session.obsStateSync("sourceActive"); + } + + } catch (e){errorlog(e);} +} + +function obsSourceVisibleChanged(event){ // accounts for visible in VDO.Ninja scene AND visible in OBS scene + warnlog("obsSourceVisibleChanged"); + warnlog(event.detail); + try { + if (typeof event==="boolean"){var visibility = event;} + else if (typeof event.detail === "boolean"){var visibility = event.detail;} + else if (typeof event.detail.visible === "boolean"){var visibility = event.detail.visible;} + else {var visibility = event.detail.visible;} + + if (typeof visibility === "undefined"){ // fall back + if (typeof document.visibilityState !== "undefined"){ + visibility = document.visibilityState==="visible"; // modern + } else if (typeof document.hidden !== "undefined"){ + visibility = !document.hidden; // legacy + } else { + return; // ... unknown input? fail. + } + } + + if (session.obsState.visibility!==visibility){ // only move forward if there is a change; the event likes to double fire you see. + session.obsState.visibility = visibility; + session.obsStateSync("visibility"); + } + + } catch (e){errorlog(e);} +} + + + +function manageSceneState(data, UUID){ // incoming obs details + if (session.disableOBS){return;} + var processNeeded = false + try{ + if ("sceneDisplay" in data){ + processNeeded=true; + session.pcs[UUID].sceneDisplay = data.sceneDisplay; + } + if ("sceneMute" in data){ + processNeeded=true; + session.pcs[UUID].sceneMute = data.sceneMute; + } + + if (data.obsState){ + if ("sourceActive" in data.obsState){ + processNeeded=true; + session.pcs[UUID].obsState.sourceActive = data.obsState.sourceActive; + } + if ("visibility" in data.obsState){ + processNeeded=true; + session.pcs[UUID].obsState.visibility = data.obsState.visibility; + session.optimizeBitrate(UUID); // &optimize flag; sets video bitrate to target value if this flag == HIDDEN (if optimize=0, disables both audio and video) + } + if ("details" in data.obsState){ + processNeeded=true; + session.pcs[UUID].obsState.details = data.obsState.details; + } + if ("streaming" in data.obsState){ + processNeeded=true; + session.pcs[UUID].obsState.streaming = data.obsState.streaming; + } + if ("recording" in data.obsState){ + processNeeded=true; + session.pcs[UUID].obsState.recording = data.obsState.recording; + } + if ("virtualcam" in data.obsState){ + processNeeded=true; + session.pcs[UUID].obsState.virtualcam = data.obsState.virtualcam; + } + } + } catch(e){ + errorlog(e); + } + + + if (processNeeded){ + log(data); + applySceneState(); + } else { + return; + } + + if (isIFrame){ + pokeIframeAPI("obs-state", data.obsState, UUID); + } + + if (session.obsControls===false){ + return; + } + + try { + var control = 0; + if (session.pcs[UUID].obsState && session.pcs[UUID].obsState.details){ + control = parseInt(session.pcs[UUID].obsState.details.controlLevel) || 0; //0 for NONE, 1 for READ_OBS (OBS data), 2 for READ_USER (User data), 3 for BASIC, 4 for ADVANCED and 5 for ALL + } + + if (control >= 4){ + if (session.director || !session.roomid){ + if (session.pcs[UUID].remote){ + if (session.obsControls!==false){ + getById("obscontrolbutton").classList.remove("hidden"); // so they get a tip. + } + } + } + } + + var multi = false; + getById("obsControlButtons").querySelectorAll("[data-system]").forEach(ele=>{ + if (ele.dataset.system in session.pcs){ + if (ele.dataset.system !==UUID){ + multi = true; + } + } else { // delete, since no longer active. + ele.remove(); + } + }); + + getById("obsSceneNames").querySelectorAll("[data-system]").forEach(ele=>{ + if (ele.dataset.system in session.pcs){ + if (ele.dataset.system !==UUID){ + multi = true; + } + } else { // delete, since no longer active. + ele.remove(); + } + }); + + if (control==0){ + var obsControlButtonsBox = getById("obsControlButtons").querySelector("[data-system='"+UUID+"']"); + if (obsControlButtonsBox){ + obsControlButtonsBox.remove(); + } + var obsSceneNamesBox = getById("obsSceneNames").querySelector("[data-system='"+UUID+"']"); // this hides if less than 2, so hide it now. + if (obsSceneNamesBox){ + obsSceneNamesBox.remove(); + } + if (!multi){ + getById("obsControlHelp").classList.remove("hidden"); + } + return; + } + + getById("obsControlHelp").classList.add("hidden"); + + var obsControlButtonsBox = getById("obsControlButtons").querySelector("[data-system='"+UUID+"']"); + if (!obsControlButtonsBox){ + obsControlButtonsBox = document.createElement("div"); + obsControlButtonsBox.dataset.system = UUID; + getById("obsControlButtons").appendChild(obsControlButtonsBox); + } else { + obsControlButtonsBox.innerHTML = ""; + } + + if (multi){ + var h3 = document.createElement("h3"); + h3.innerText = "OBS instance: " + (session.pcs[UUID].label || session.pcs[UUID].scene || UUID); + obsControlButtonsBox.appendChild(h3); + } + + if (session.pcs[UUID].obsState && ("streaming" in session.pcs[UUID].obsState)){ + + var controlButton = document.createElement("button"); + controlButton.dataset.UUID = UUID; + + if (session.pcs[UUID].obsState.streaming){ + controlButton.classList.add("pressed"); + controlButton.ariaPressed = "true"; + controlButton.dataset.obsAction = "stopStreaming"; + controlButton.innerText = "📡 stop streaming"; + } else { + controlButton.dataset.obsAction = "startStreaming"; + controlButton.innerText = "📡 start streaming"; + } + + if (control<5){ + controlButton.disabled = true; + controlButton.style.cursor = "not-allowed"; + controlButton.title = "Source is lacking required permissions."; + } else { + controlButton.onclick = async function(){ + var msg = {}; + msg.obsCommand = {} + msg.obsCommand.action = this.dataset.obsAction; + msg.UUID = this.dataset.UUID; + if (document.querySelector("#obsRemotePassword>input") && document.querySelector("#obsRemotePassword>input").value){ + msg.remote = document.querySelector("#obsRemotePassword>input").value; + } else { + msg.remote = session.remote; + } + msg = await session.encodeRemote(msg); + session.anysend(msg); + log("action request: "+this.dataset.obsAction); + } + } + obsControlButtonsBox.appendChild(controlButton); + } + if (session.pcs[UUID].obsState && ("recording" in session.pcs[UUID].obsState)){ + + var controlButton = document.createElement("button"); + controlButton.dataset.UUID = UUID; + + if (session.pcs[UUID].obsState.recording){ + controlButton.classList.add("pressed"); + controlButton.ariaPressed = "true"; + controlButton.dataset.obsAction = "stopRecording"; + controlButton.innerText = "📽 stop recording"; + } else { + controlButton.dataset.obsAction = "startRecording"; + controlButton.innerText = "📽 start recording"; + } + + if (control<5){ + controlButton.disabled = true; + controlButton.style.cursor = "not-allowed"; + controlButton.title = "Source is lacking required permissions."; + } else { + controlButton.onclick = async function(){ + var msg = {}; + msg.obsCommand = {}; + msg.obsCommand.action = this.dataset.obsAction; + msg.UUID = this.dataset.UUID; + if (document.querySelector("#obsRemotePassword>input").value){ + msg.remote = document.querySelector("#obsRemotePassword>input").value; + } else { + msg.remote = session.remote; + } + msg = await session.encodeRemote(msg); + session.anysend(msg); + log("action request: "+this.dataset.obsAction); + } + } + obsControlButtonsBox.appendChild(controlButton); + } + if (session.pcs[UUID].obsState && ("virtualcam" in session.pcs[UUID].obsState)){ + + var controlButton = document.createElement("button"); + + controlButton.dataset.UUID = UUID; + + if (session.pcs[UUID].obsState.virtualcam){ + controlButton.classList.add("pressed"); + controlButton.ariaPressed = "true"; + controlButton.dataset.obsAction = "stopVirtualcam"; + controlButton.innerText = "💻 stop virtualcam"; + } else { + controlButton.dataset.obsAction = "startVirtualcam"; + controlButton.innerText = "💻 start virtualcam"; + } + + if (control<5){ + controlButton.disabled = true; + controlButton.style.cursor = "not-allowed"; + controlButton.title = "Source is lacking required permissions."; + } else { + controlButton.onclick = async function(){ + var msg = {}; + msg.obsCommand = {} + msg.obsCommand.action = this.dataset.obsAction; + msg.UUID = this.dataset.UUID; + if (document.querySelector("#obsRemotePassword>input").value){ + msg.remote = document.querySelector("#obsRemotePassword>input").value; + } else { + msg.remote = session.remote; + } + msg = await session.encodeRemote(msg); + session.anysend(msg); + log("action request: "+this.dataset.obsAction); + } + } + obsControlButtonsBox.appendChild(controlButton); + } + } catch(e){errorlog(e);} // just in case the client has disconnected. + + + if (control<2){ + var obsSceneNamesBox = getById("obsSceneNames").querySelector("[data-system='"+UUID+"']"); + if (obsSceneNamesBox){ + obsSceneNamesBox.remove(); + } + return; + } + + var obsSceneNamesBox = getById("obsSceneNames").querySelectorAll("div[data-system='"+UUID+"']"); + if (!obsSceneNamesBox.length){ + obsSceneNamesBox = document.createElement("div"); + obsSceneNamesBox.dataset.system = UUID; + getById("obsSceneNames").appendChild(obsSceneNamesBox); + } else { + obsSceneNamesBox = obsSceneNamesBox[0]; + obsSceneNamesBox.innerHTML = ""; + } + + if (multi){ + var h3 = document.createElement("h3"); + h3.innerText = "OBS instance: " + (session.pcs[UUID].label || session.pcs[UUID].scene || UUID); + obsSceneNamesBox.appendChild(h3); + } + + if (session.pcs[UUID].obsState.details){ + var details = session.pcs[UUID].obsState.details; + if (details.scenes){ + details.scenes.forEach(scene=>{ + + var sceneButton = document.createElement("button"); + sceneButton.dataset.obsScene = scene; + sceneButton.dataset.UUID = UUID; + sceneButton.innerText = scene; + if (details.currentScene && details.currentScene.name && (details.currentScene.name === scene)){ + sceneButton.classList.add("pressed"); + sceneButton.ariaPressed = "true"; + } + obsSceneNamesBox.appendChild(sceneButton); + if (control<4){ + sceneButton.disabled = true; + sceneButton.style.cursor = "not-allowed"; + sceneButton.title = "Source is lacking required permissions."; + } else { + sceneButton.onclick = async function(){ + var msg = {}; + msg.obsCommand = {action: "setCurrentScene", value: this.dataset.obsScene}; + msg.UUID = this.dataset.UUID; + if (document.querySelector("#obsRemotePassword>input").value){ + msg.remote = document.querySelector("#obsRemotePassword>input").value; + } else { + msg.remote = session.remote; + } + msg = await session.encodeRemote(msg); + session.anysend(msg); + log("scene change request: "+this.dataset.obsScene); + }; + } + }); + } + } + getById("debugRemoteOBSControl").innerText = JSON.stringify(session.pcs[UUID].obsState); +} + + +function processOBSCommand(msg){ + if (session.disableOBS){return false;} + else if (!window.obsstudio){return false;} + else if (typeof msg.obsCommand !== "object"){return false;} + else if ("remote" in msg){ + if (((msg.remote === session.remote) && session.remote) || (session.remote===true)){ + // approved + } else { + if (msg.UUID && msg.obsCommand.action){ + var data = {} + data.rejected = "obsCommand"; + //data.debug = msg.remote; + session.sendRequest(data, msg.UUID); // this skips the server + } + warnlog("Denied access; remote does not match"); + return false; + } + } else { + if (msg.UUID && msg.obsCommand.action){ + var data = {} + data.rejected = "obsCommand"; + //data.debug = "no remote code provided"; + session.sendRequest(data, msg.UUID); // this skips the server + } + return false; + } + + try { // {changeScene: this.dataset.obsScene} + if (msg.obsCommand.action && (typeof msg.obsCommand.action=="string")){ + if (msg.obsCommand.value && (typeof msg.obsCommand.value=="string")){ + if ((msg.obsCommand.action == "setCurrentScene") && session.filterOBSscenes && session.filterOBSscenes.length){ + try { + if (!session.filterOBSscenes.includes(msg.obsCommand.value)){ + return false; + } + } catch(e){errorlog(e);return false;} + } + window.obsstudio[msg.obsCommand.action](msg.obsCommand.value); + } else { + window.obsstudio[msg.obsCommand.action](); + } + } + } catch(e){ + errorlog(e); + return false; + } + return true; +} + +function applySceneState(){ // guest side; tally light, etc. + if (session.disableOBS){return;} // the guest can decide to hide the tally light + + if (document.getElementById("videosource")){ + var visibility = false; + var ondeck = false; + var recording = false; + for (var uid in session.pcs){ + + if (session.pcs[uid].obsState.sourceActive!==false && session.pcs[uid].obsState.visibility && (session.pcs[uid].sceneDisplay!==false)){ + visibility=true; + } else if (session.pcs[uid].obsState.visibility && (session.pcs[uid].sceneDisplay!==false)){ + ondeck=true; + } + if ((session.pcs[uid].obsState.recording || session.pcs[uid].obsState.streaming) && (session.pcs[uid].obsState.sourceActive!==false && session.pcs[uid].obsState.visibility && (session.pcs[uid].sceneDisplay!==false))){ // the scene that is recording must be visible also. + recording=true; + } + } + + if (!session.cleanOutput){ + getById("obsState").classList.remove("hidden"); + } + + if (recording){ + getById("obsState").classList.remove("ondeck"); + getById("obsState").classList.add("recording"); // TODO: this needs to check all peers to make sure it's valid + getById("obsState").innerHTML = "ON AIR"; + + if (session.tallyStyle){ + getById("main").classList.remove("ondeck"); + getById("main").classList.add("recording"); + } + + } else if (ondeck && !visibility){ + getById("obsState").classList.remove("recording"); + getById("obsState").classList.add("ondeck"); // TODO: this needs to check all peers to make sure it's valid + getById("obsState").innerHTML = "STAND BY"; + + if (session.tallyStyle){ + getById("main").classList.remove("recording"); + getById("main").classList.add("ondeck"); + } + + } else if (visibility){ + getById("obsState").classList.remove("recording"); + getById("obsState").classList.remove("ondeck"); + getById("obsState").innerHTML = "ACTIVE"; + + if (session.tallyStyle){ + getById("main").classList.remove("recording"); + getById("main").classList.remove("ondeck"); + } + } else { + getById("obsState").classList.remove("recording"); + getById("obsState").classList.remove("ondeck"); + getById("obsState").innerHTML = "INACTIVE"; + getById("obsState").classList.add("hidden"); // I don't think most people care to see inactive. + + if (session.tallyStyle){ + getById("main").classList.remove("recording"); + getById("main").classList.remove("ondeck"); + } + } + + if (visibility){ // BASIC TALLY LIGHT (on deck disabled) + getById("obsState").classList.add("onair"); // LIVE + if (session.tallyStyle){ + getById("main").classList.add("onair"); + } + } else { + getById("obsState").classList.remove("onair"); + if (session.tallyStyle){ + getById("main").classList.remove("onair"); + } + } + + + + if (session.automute){ + if (!visibility){ + session.micIsolatedAutoMute = []; + if (session.automute !== "2"){ + for (var uid in session.pcs){ + if (session.directorList.indexOf(uid)>=0){ // allow validated directors to hear the guest + session.micIsolatedAutoMute.push(uid); + } + } + } + } else { + session.micIsolatedAutoMute = false; + } + session.applyIsolatedChat(); + } + } +} + +function compare_vids( a, b ) { + var aa = a.order || 0; + var bb = b.order || 0; + if ( aa < bb ){ + return 1; + } + if ( aa > bb ){ + return -1; + } + return 0; +} + +function compare_vids_sid( a, b ) { + var aa = a.dataset.sid || 0; + var bb = b.dataset.sid || 0; + if ( aa > bb ){ + return 1; + } + if ( aa < bb ){ + return -1; + } + return 0; +} +function compare_vids_label( a, b ) { + if (a.dataset.UUID && session.rpcs[a.dataset.UUID] && session.rpcs[a.dataset.UUID].label){ + var aa = session.rpcs[a.dataset.UUID].label.toLowerCase(); + } else { + var aa = 0; + } + + if (b.dataset.UUID && session.rpcs[b.dataset.UUID] && session.rpcs[b.dataset.UUID].label){ + var bb = session.rpcs[b.dataset.UUID].label.toLowerCase(); + } else { + var bb = 0; + } + + if ( aa > bb ){ + return 1; + } + if ( aa < bb ){ + return -1; + } + return 0; +} + + +function sortByZ(mediaPool, layout) { + function sortABZ( a, b ) { + if (layout[a.dataset.sid]){ + var aa = layout[a.dataset.sid].zIndex || layout[a.dataset.sid].z || 0; + } else { + var aa = 0; + } + if (layout[b.dataset.sid]){ + var bb = layout[b.dataset.sid].zIndex || layout[b.dataset.sid].z || 0; + } else { + var bb = 0; + } + if ( aa < bb ){ + return -1; + } + if ( aa > bb ){ + return 1; + } + return 0; + } + mediaPool.sort(sortABZ); + return mediaPool; +} + +window.onpopstate = function() { + if (session.firstPlayTriggered) { + window.location.reload(true); // deprecated, but it seems to work, so w/e + } +}; + +var miniPerformerX = null; +var miniPerformerY = null; +function makeMiniDraggableElement(elmnt) { + + if (session.disableMouseEvents){return;} + + try { + elmnt.dragElement = false; + // elmnt.style.bottom = "auto"; + elmnt.style.cursor = "grab"; + + elmnt.stashonmouseup = null; + elmnt.stashonmousemove = null; + + } catch (e) { + errorlog(e); + return; + } + + var pos1 = 0; + var pos2 = 0; + var pos3 = 0; + var pos4 = 0; + + var timestamp = false; + + function elementDrag(e) { // ON DRAG + timestamp = false; + if (session.infocus){return;} + try { + e = e || window.event; + + if (e.type !== "touchmove"){ + if (("buttons" in e) && (e.buttons!==1)){ + closeDragElement(e); + return; + } + e.preventDefault(); + } + e.stopPropagation(); + + elmnt.dragElement = true; + + if (e.type === "touchmove"){ + pos1 = pos3 - e.touches[0].clientX; + pos2 = pos4 - e.touches[0].clientY; + pos3 = e.touches[0].clientX; + pos4 = e.touches[0].clientY; + } else { + pos1 = pos3 - e.clientX; + pos2 = pos4 - e.clientY; + pos3 = e.clientX; + pos4 = e.clientY; + } + + var topDrag = (elmnt.offsetTop - pos2 ); + if (topDrag > (-3 + (window.innerHeight - elmnt.clientHeight))){ + topDrag = (-3 + (window.innerHeight - elmnt.clientHeight)); + } + + miniPerformerY = topDrag; + miniPerformerX = elmnt.offsetLeft - pos1; + + if (miniPerformerY > window.innerHeight-elmnt.clientHeight){ + miniPerformerY = window.innerHeight-elmnt.clientHeight; + } + if (miniPerformerX > window.innerWidth-elmnt.clientWidth){ + miniPerformerX = window.innerWidth-elmnt.clientWidth; + } + + miniPerformerX = 100 * miniPerformerX/window.innerWidth ; + miniPerformerY = 100 * miniPerformerY/window.innerHeight; + + if (session.widget && !session.leftMiniPreview){ + if (miniPerformerX>74){ + miniPerformerX = 74; + } + } + + if (miniPerformerY<0){ + miniPerformerY=0; + } else if (miniPerformerY>100){ + miniPerformerY=100; + } + if (miniPerformerX<0){ + miniPerformerX=0; + } else if (miniPerformerX>100){ + miniPerformerX=100; + } + + elmnt.style.right = "unset"; + elmnt.style.top = miniPerformerY + "%"; + elmnt.style.left = miniPerformerX + "%"; + + + } catch(e){errorlog(e);} + } + + + function closeDragElement(e) { // TOUCH END + e = e || window.event; + + if (e.type !== "touchend"){ + if (e.button !== 0){return;} + document.onmouseup = elmnt.stashonmouseup; + document.onmousemove = elmnt.stashonmousemove; + elmnt.onmouseleave=null; + + } + + + if (session.infocus){return;} + e.preventDefault(); + + if (timestamp && (Date.now()- timestamp>500)){ // long hold, so this is a drag + e.stopPropagation(); + if (e.type === "touchend"){ + if (session.infocus === true){ + session.infocus = false; + } else { + session.infocus = true; + log("session: myself"); + } + setTimeout(()=>updateMixer(),10); + } + } else if (timestamp && (e.type !== "touchend")){ + if (session.infocus === true){ + session.infocus = false; + } else { + session.infocus = true; + log("session: myself"); + } + setTimeout(()=>updateMixer(),10); + } + } + + function dragMouseDown(e) { ////// TOUCH START + + if (event.ctrlKey || event.metaKey) {return;} + + timestamp = Date.now(); + + e = e || window.event; + if (session.infocus){return;} + + e.preventDefault(); + if (e.type === "touchstart"){ + pos3 = e.touches[0].clientX; + pos4 = e.touches[0].clientY; + + elmnt.ontouchend = closeDragElement; + elmnt.ontouchmove = elementDrag; + } else { + if (e.button !== 0){return;} + pos3 = e.clientX; + pos4 = e.clientY; + elmnt.stashonmouseup = document.onmouseup; // I don't want to interfere with other drag events. + elmnt.stashonmousemove = document.onmousemove; + + document.onmouseup = closeDragElement; + document.onmousemove = elementDrag; + elmnt.onmouseleave = function(event){ + closeDragElement(event); + }; + } + + } + + elmnt.onmousedown = dragMouseDown; + elmnt.ontouchstart = dragMouseDown; +} + +function makeDraggableElement(element) { + + if (session.disableMouseEvents){return;} // this is here for a reason. :P + + element.initialX; + element.initialY; + element.currentX; + element.xOffset = 0; + element.currentY; + element.yOffset = 0; + element.isDragging = false; + element.dragElement = true; + + element.addEventListener('mousedown', dragStart); + + function dragStart(e) { + element.initialX = e.clientX - element.xOffset; + element.initialY = e.clientY - element.yOffset; + + document.addEventListener('mousemove', drag); + document.addEventListener('mouseup', dragEnd); + document.addEventListener('onmouseleave', dragEnd); + document.addEventListener('onmouseenter', dragEnd); + + element.isDragging = true; + } + + function dragEnd(e) { + element.initialX = element.currentX; + element.initialY = element.currentY; + + document.removeEventListener('mousemove', drag); + document.removeEventListener('mouseup', dragEnd); + document.removeEventListener('onmouseleave', dragEnd); + document.removeEventListener('onmouseenter', dragEnd); + + element.isDragging = false; + } + + function drag(e) { + if (element.isDragging) { + element.currentX = (e.clientX - element.initialX); + element.currentY = (e.clientY - element.initialY); + + // Get the dimensions of the viewport + let vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0); + let vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0); + + // Get the dimensions of the object + let elementWidth = element.offsetWidth; + let elementHeight = element.offsetHeight; + // console.log('elementWidth:\n',elementWidth) + // console.log('elementHeight:\n',elementHeight) + + // Calculate the boundaries + let maxX = vw - elementWidth; + let maxY = vh - elementHeight; + let minX = 0; + let minY = 0; + + // Calculate real boundaries (parent position: fixed issues) + let topOffset = 0; + let leftOffset = 0; + let elementOffset = element; + while (elementOffset) { + topOffset += elementOffset.offsetTop; + leftOffset += elementOffset.offsetLeft; + elementOffset = elementOffset.offsetParent; + } + + // Adjust the position if it's going beyond the boundaries + let realX = element.currentX + leftOffset; + let realY = element.currentY + topOffset; + + if (realX > maxX) { + element.currentX = maxX - leftOffset; + } else if (realX < minX) { + element.currentX = minX - leftOffset; + } + + if (realY > maxY) { + element.currentY = maxY - topOffset; + } else if (realY < minY) { + element.currentY = minY - topOffset; + } + // Update the position and offset + element.xOffset = element.currentX; + element.yOffset = element.currentY; + + element.style.transform = `translate(${element.currentX}px, ${element.currentY}px)`; + } + } + + // if (session.disableMouseEvents){return;} + + // try { + // elmnt.dragElement = false; + // elmnt.style.bottom = "auto"; + // elmnt.style.cursor = "grab"; + // elmnt.stashonmouseup = null; + // elmnt.stashonmousemove = null; + // } catch (e) { + // errorlog(e); + // return; + // } + // var pos1 = 0; + // var pos2 = 0; + // var pos3 = 0; + // var pos4 = 0; + // var timestamp = false; + + + // var enterEventCount = 0; + // var leaveEventCount = 0; + + + // function dragMouseDown(e) { + // timestamp = Date.now(); + + // e = e || window.event; + // e.preventDefault(); + + // pos3 = e.clientX; + // pos4 = e.clientY; + // //elmnt.stashonmouseup = document.onmouseup; // I don't want to interfere with other drag events. + // //elmnt.stashonmousemove = document.onmousemove; + // //elmnt.stashonclick = document.onclick; + + // document.onmouseup = function(event){closeDragElement(event, elmnt);}; + + // document.onmousemove = function(event){elementDrag(elmnt,event);}; + + // if ("stopDragTimeout" in elmnt){clearTimeout(elmnt.stopDragTimeout);} + + // elmnt.onmouseleave = function(event){ + // leaveEventCount+=1; + // elmnt.stopDragTimeout = setTimeout(function(ele,evt1){ + // closeDragElement(evt1, ele);} + // ,100, elmnt, event); + // }; + // elmnt.onmouseenter = function(event){ + // enterEventCount+=1; + // if (enterEventCount>=leaveEventCount){ + // if ("stopDragTimeout" in elmnt){clearTimeout(elmnt.stopDragTimeout);} + // } else { + // if (("stopDragTimeout" in elmnt) && (elmnt.stopDragTimeout)){ + // clearTimeout(elmnt.stopDragTimeout); + // elmnt.stopDragTimeout = setTimeout(function(ele,evt1){ + + // closeDragElement(evt1, ele);} + // ,100, elmnt, event); + // } + // } + // }; + + // } + // function elementDrag(ele,e) { + + // e = e || window.event; + // if (("buttons" in e) && (e.buttons!==1)){return;} + + // e.preventDefault(); + + // ele.dragElement = true; + // pos1 = pos3 - e.clientX; + // pos2 = pos4 - e.clientY; + // pos3 = e.clientX; + // pos4 = e.clientY; + + // var topDrag = (ele.offsetTop - pos2 ); + // if (absolute){ + // if (topDrag > (-3 + (window.innerHeight - ele.clientHeight))){ + // topDrag = (-3 + (window.innerHeight - ele.clientHeight)); + // } + // } else { + // if (topDrag > -3){ + // topDrag = -3; + // } + // } + // ele.style.top = topDrag + "px"; + // ele.style.left = (ele.offsetLeft - pos1) + "px"; + + // } + // function closeDragElement(event=false, ele=false) { + // document.onmouseup = null; + // document.onmousemove = null + // ele.onmouseleave = null; + // ele.onmouseenter = null; + // enterEventCount = 0; + // leaveEventCount = 0; + // updateMixer(); + // //document.onclick = elmnt.stashonclick; + // } + + // elmnt.onmousedown = dragMouseDown; +} + +function removeStorage(cname){ + localStorage.removeItem(cname); +} + +function clearStorage(){ + localStorage.clear(); + if (!session.cleanOutput){ + warnUser("The local storage and saved settings have been cleared", 1000); + } +} + +function setStorage(cname, cvalue, hours=9999){ // not actually a cookie + var now = new Date(); + var item = { + value: cvalue, + expiry: now.getTime() + (hours * 60 * 60 * 1000), + }; + try{ + localStorage.setItem(cname, JSON.stringify(item)); + }catch(e){errorlog(e);} +} + +function getStorage(cname) { + try { + var itemStr = localStorage.getItem(cname); + } catch(e){ + errorlog(e); + return; + } + if (!itemStr) { + return ""; + } + var item = JSON.parse(itemStr); + var now = new Date(); + if (now.getTime() > item.expiry) { + localStorage.removeItem(cname); + return ""; + } + return item.value; +} + +function play(streamid=null, UUID=false){ // play whatever is in the URL params; or filter by a streamID option + log("play stream: "+session.view+ " " +streamid); + + if (session.viewDirectorOnly){ + if (!(UUID || streamid)){ + warnlog("No UUID and StreamID"); + return; + } else if (session.directorList.indexOf(UUID)==-1){ + warnlog("Not a director"); + return; + } + } + + if (session.view_set){ + var played = false; + for (var j in session.view_set){ + if (streamid===null){ // play what is in the view list ; not a group room probably + session.watchStream(session.view_set[j]); + played=true; + } else if (streamid === session.view_set[j]){ // plays if the group room list matches the explicit list + session.watchStream(session.view_set[j]); + played=true; + } + } + if (session.include){ + session.include.forEach(sid=>{ + if (session.view_set.includes(sid)){ + // already played + } else if (streamid===null){ // play what is in the view list ; not a group room probably + session.watchStream(sid); + } else if (streamid === sid){ // plays if the group room list matches the explicit list + session.watchStream(sid); + played=true; + } + }); + } + + if (!played && streamid){ + if (session.scene!==false){ + if (!session.permaid){ + if (!session.queue){ // I don't want to deal with queues. + if (session.exclude===false || (!session.exclude.includes(streamid))){ + if (UUID){ + if (session.directorList.indexOf(UUID)>=0){ + warnlog("stream ID added to badStreamList: "+streamid); + session.badStreamList.push(streamid); + session.watchStream(streamid); + } + } + } + } + } + } + } + + } else if (streamid && (session.exclude !== false)){ + if (session.exclude.includes(streamid)){ + // we don't play it at all. (if explicity listed as VIDEO, then OKay.) + } else { + session.watchStream(streamid); // I suppose we do play it. + } + } else if (streamid){ + session.watchStream(streamid); + } else if (session.include.length){ + session.include.forEach(sid=>{ + session.watchStream(sid); + }); + } +} + +function nextQueue(){ + if (!session.queue){return;} + if (!session.director){return;} + if (session.queueList.length==0){ + getById("queuebutton").classList.add("red"); + setTimeout(function(){ + getById("queuebutton").classList.remove("red"); + },50); + return; + } + var nextStream = session.queueList.shift(); + + getById("queuebutton").classList.add("red"); + setTimeout(function(){ + getById("queuebutton").classList.remove("red"); + },200); + + updateQueue(); + + session.watchStream(nextStream); + log("next stream loading: "+nextStream); +} + +function updateQueue(adding=false){ + if (!session.queue){return;} + if (!session.director){return;} + if (session.queueList.length) { + if (session.queueList.length>10){ + getById("queueNotification").innerHTML = "‼"; + } else { + getById("queueNotification").innerHTML = session.queueList.length; + } + getById("queueNotification").classList.add("queueNotification"); + } else { + getById("queueNotification").innerHTML = ""; + getById("queueNotification").classList.remove("queueNotification"); + } + + if (adding){ + if (session.beepToNotify){ + playtone(); + showNotification("someone joined the queue", "queue length: "+session.queueList.length); + } + getById("queuebutton").classList.remove("shake"); + setTimeout(function(){getById("queuebutton").classList.add("shake");},10); + } +} + +function hideStreamLowBandwidth(bandwidth, UUID){ + if (!session.lowBitrateCutoff){return;} + + if (session.directorList.includes(UUID) || session.rpcs[UUID].director){ + if (session.showDirector || session.rpcs[UUID].showDirector){ + // all good + } else { + return; // we don't include the director since not treated as a guest + } + } + + if (bandwidth<=session.lowBitrateCutoff){// <= used so 0 can be used as a trigger + if (session.lowBitrateSceneChange){ + changeSceneLowBandwidth(true); + } else if (!session.rpcs[UUID].bandwidthMuted){ + session.rpcs[UUID].bandwidthMuted = true; + updateMixer(); + } + } else if (session.lowBitrateSceneChange){ + changeSceneLowBandwidth(false); + } else if (session.rpcs[UUID].bandwidthMuted){ + session.rpcs[UUID].bandwidthMuted = false; + if (session.rpcs[UUID].videoElement){ + session.rpcs[UUID].videoElement.muted = checkMuteState(UUID); + } + updateMixer(); + } +} + +var changeSceneEnabled = false; +var changeSceneLowBandwidthRevert = false; +function changeSceneLowBandwidth(state){ + if (!session.lowBitrateSceneChange){return;} + if (!session.obsState){return;} + try { + if (session.obsState.sourceActive && session.obsState.details && session.obsState.details.currentScene){ + changeSceneLowBandwidthRevert = session.obsState.details.currentScene.name || false; + } else if (("sourceActive" in session.obsState) && !session.obsState.sourceActive && session.obsState.details && session.obsState.details.currentScene){ + if (session.obsState.details.currentScene.name !== session.lowBitrateSceneChange){ + return; // not the FML scene, nor are we visible, so we're not going to switch back. Assume the user has overtaken the setup. + } + } + if (!window.obsstudio || !window.obsstudio["setCurrentScene"]){return;} + if (state && changeSceneLowBandwidthRevert){ + if (changeSceneEnabled){ // bitrate was higher , so we can now cut off. + window.obsstudio["setCurrentScene"](session.lowBitrateSceneChange); + } + } else if (changeSceneLowBandwidthRevert){ + changeSceneEnabled = true; + window.obsstudio["setCurrentScene"](changeSceneLowBandwidthRevert); + } + } catch(e){ + errorlog(e); + } +} + + + +function setupIncomingScreenTracking(v, UUID){ // SCREEN element. + + if (session.directorList.indexOf(UUID)>=0){ + v.muted=false; + } + + v.addEventListener("playing", (e)=>{ + + try { + var bigPlayButton = document.getElementById("bigPlayButton"); + if (bigPlayButton){ + bigPlayButton.parentNode.removeChild(bigPlayButton); + } + } catch(e){} + + resetupAudioOut(e.target, true); + + try { + if (session.pip){ + if (v.readyState >= 3){ + if (!(v.pip)){ + v.pip=true; + toggleSystemPip(v, true); + } + } + } + } catch(e){} + + }, { once: true }); + + v.onpause = (event) => { // prevent things from pausing; human or other + + if (v.dataset.UUID && session.rpcs[v.dataset.UUID] && (session.rpcs[v.dataset.UUID].manualBandwidth === 0)){ + return true; + } + if (!((event.ctrlKey) || (event.metaKey) )){ + warnlog("Video paused; force it to play again"); + //return; + //session.audioCtx.resume(); + //log("ctx resume"); + + event.currentTarget.play().then(_ => { + log("playing 4"); + }).catch(error => { + warnlog("didnt play 1"); + }); + if (Firefox){ + unPauseVideo(v); + } + } + return true; + } + + if (session.pip){ + v.onloadedmetadata = function(){ + if (!v.paused){ + if (!(v.pip)){ + v.pip=true; + toggleSystemPip(v, true); + } + } + } + } + + v.addEventListener('resize', (e) => { // if the aspect ratio changes, then we might want to update the mixer. If audio only, then this doesn't matter. + var v = e.target; + var aspectRatio = parseFloat(v.videoWidth/v.videoHeight) || 0; + log("resize event: "+aspectRatio); + + if (!aspectRatio){ + v.resetAR = true; + return; + } // if Audio only, then we don't want to set or update any aspect ratio. + + if (v.resetAR){ + log("ASPECT RATIO UNMUTED"); + delete(v.resetAR); + v.dataset.aspectRatio = aspectRatio; + pokeIframeAPI("aspect-ratio", v.dataset.aspectRatio, v.dataset.UUID, v.dataset.sid); + setTimeout(function(){updateMixer();},1); + } else if (v.dataset.aspectRatio){ + if (aspectRatio != parseFloat(v.dataset.aspectRatio)){ + log("ASPECT RATIO CHANGED"); + v.dataset.aspectRatio = aspectRatio; + pokeIframeAPI("aspect-ratio", v.dataset.aspectRatio, v.dataset.UUID, v.dataset.sid); + setTimeout(function(){updateMixer();},1); // We don't want to run this on the first resize? just subsequent ones. + } + } else { + log("NEW VIDEO ? ASPECT RATIO new"); + v.dataset.aspectRatio = aspectRatio; + pokeIframeAPI("aspect-ratio", v.dataset.aspectRatio, v.dataset.UUID, v.dataset.sid); + setTimeout(function(){updateMixer();},1); + } + }); + if (typeof session.volume == "number"){ + v.volume = session.volume; + } else { + v.volume = 1.0; // play audio automatically + } + v.autoplay = true; + v.controls = session.showControls || false; + v.classList.add("tile"); + v.setAttribute("playsinline",""); + v.controlTimer = null; + + v.dataset.menu = "context-menu-video"; + if (!session.cleanOutput){ + v.classList.add("task"); // this adds the right-click menu + } + + if (document.getElementById("mainmenu")){ + var m = getById("mainmenu"); + m.remove(); + document.querySelectorAll(".hidden2").forEach(ele2=>{ + ele2.classList.remove("hidden2"); + }); + } + + if (session.director){ + if (session.showControls!==null){ + v.controls = session.showControls + } else { + v.controls = true; + } + var container = getById("screenContainer_"+UUID); + v.container = container; + v.disablePictureInPicture = false + v.setAttribute("controls","controls") + container.appendChild(v); + pokeIframeAPI("control-box-video-updated", v.id, UUID); + session.requestRateLimit(session.directorViewBitrate,UUID); /// limit resolution for director + v.title = "Hold CTRL or CMD (⌘) while clicking the video to open detailed stats"; + if (session.beepToNotify) { + playtone(); + } + + } else if (session.scene!==false){ + v.controls = session.showControls || false; + + if (session.view){ // specific video to be played + v.style.display="block"; + } else if (session.scene==="0"){ // auto plays, right? + v.style.display="block"; + } else { // group scene I guess; needs to be added manually + v.style.display="none"; + v.mutedStateScene = true; + } + + setTimeout(function(){updateMixer();},1); + } else if (session.roomid!==false){ + if (session.cleanOutput){ + v.controls = session.showControls || false; + } else if (session.studioSoftware) { + v.controls = session.showControls || false; + } else if (session.showControls!==null){ + v.controls = session.showControls; + } else { + v.controls = true; + } + //if ((session.roomid==="") && (session.bitrate)){ + // let's keep the default bitrates, since this isn't a real room and bitrates are specified. + //} //else if (session.novideo !== false){ + // if (session.novideo.includes(session.rpcs[UUID].streamID)){ // no video will have muted the video already anyways. + // session.requestRateLimit(0,UUID, false);// optimizing audio here doesn't later get turned back on. let the automixer disable audio instead + // } + //} //else { + // session.requestRateLimit(0,UUID, false);//// optimizing audio here doesn't later get turned back on. let the automixer disable audio instead + //} + setTimeout(function(){updateMixer();},1); + } else { + v.style.display="block"; + setTimeout(function(){updateMixer();},1); + } + + + v.addEventListener('click', function(e) { // show stats of video if double clicked + log("clicked"); + try { + var uid = e.currentTarget.dataset.UUID; + if (e.ctrlKey || e.metaKey){ + e.preventDefault(); + if (session.statsMenu !==false){ + if ("stats" in session.rpcs[uid]){ + var [menu, innerMenu] = statsMenuCreator(); + printViewStats(innerMenu, uid ); + menu.interval = setInterval(printViewStats, session.statsInterval, innerMenu, uid); + } + } + e.stopPropagation(); + return false; + } else if ("prePausedBandwidth" in session.rpcs[uid]){ + unPauseVideo(e.currentTarget); + } + } catch(e){errorlog(e);} + }); + + if (session.statsMenu){ + if ("stats" in session.rpcs[UUID]){ + + if (getById("menuStatsBox")){ + clearInterval(getById("menuStatsBox").interval); + getById("menuStatsBox").remove(); + } + + var [menu, innerMenu] = statsMenuCreator(); + printViewStats(innerMenu, UUID ); + menu.interval = setInterval(printViewStats, session.statsInterval, innerMenu, UUID); + } + } + + + v.touchTimeOut = null; + v.touchLastTap = 0; + v.touchCount = 0; + v.addEventListener('touchend', function(event) { + + if (session.disableMouseEvents){return;} + + log("touched"); + + //document.ontouchup = null; + //document.onmouseup = null; + document.onmousemove = null; + document.ontouchmove = null; + + var currentTime = new Date().getTime(); + var tapLength = currentTime - v.touchLastTap; + clearTimeout(v.touchTimeOut); + if (tapLength < 500 && tapLength > 0) { + /// + log("double touched"); + v.touchCount+=1; + event.preventDefault(); + if (v.touchCount<5){ + v.touchLastTap = currentTime; + return false; + } + v.touchLastTap = 0; + v.touchCount=0; + + log("double touched"); + if (session.statsMenu !==false){ + var uid = event.currentTarget.dataset.UUID; + if ("stats" in session.rpcs[uid]){ + + var [menu, innerMenu] = statsMenuCreator(); + + printViewStats(innerMenu, uid ); + + menu.interval = setInterval(printViewStats, session.statsInterval, innerMenu, uid); + } + } + event.stopPropagation(); + return false; + ////// + } else { + v.touchCount=1; + v.touchTimeOut = setTimeout(function(vv) { + clearTimeout(vv.touchTimeOut); + vv.touchLastTap = 0; + vv.touchCount=0; + }, 5000, v); + v.touchLastTap = currentTime; + } + + }); + + if (v.controls == false){ + v.addEventListener("click", function () { + if (v.paused){ + log("PLAYING MANUALLY?"); + v.play().then(_ => { + log("playing 5"); + }).catch(warnlog); + } + }); + if (session.nocursor==false){ // we do not want to show the controls. This is because MacOS + OBS does not work; so electron app needs this. + if (!(session.cleanOutput)){ + if (session.studioSoftware) { + } else if (session.showControls === false) { // explicitly disabled; default null. + } else if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1) { + } else { + if (v.controlTimer){ + clearInterval(v.controlTimer); + } + v.controlTimer = setTimeout(showControlBar.bind(null,v),1000); + //v.controlTimer = setTimeout(function (){v.controls=true;},3000); // 3 seconds before I enable the controls automatically. This way it doesn't auto appear during loading. 3s enough, right? + } + } + } + } + + //if (session.fadein){ + v.addEventListener('animationend', function(e) { + v.classList.remove("fadein"); // allows the video to fade in. + if (v.holder){ + v.holder.classList.remove("fadein"); + } + }); + // v.classList.add("fadein"); // allows the video to fade in. + // if (v.holder){ + // v.holder.classList.add("fadein"); + // } + //} + + applyMuteState(UUID);; // TODO; needs to be specific to screen video + v.usermuted = false; + + v.addEventListener('volumechange',function(e){ + var muteState = checkMuteState(UUID); + if (this.muted && (this.muted !== muteState)){ + this.usermuted = true; + } else if (!this.muted){ + this.usermuted = false; + } + }); + + if (session.screenShareStartPaused){ // we know this is a screen share already + pauseVideo(v, false); + } + + if (session.director){ + /* var wss = ""; + if (session.customWSS || session.wssSetViaUrl){ + if (session.customWSS && (session.customWSS!==true)){ + wss = "&pie="+session.customWSS; + } else { + wss = "&wss="+session.wss; + } + } */ + /* + var codecGroupFlag=""; + if (session.codecGroupFlag){ + codecGroupFlag = session.codecGroupFlag; + } */ + + /* var passAdd2=""; + if (session.password){ + if (session.defaultPassword===false){ + passAdd2="&password="+session.password; + } + } */ + + if (session.customWSS && ("isScene" in msg) && (msg.isScene!==false)){ + // this is a scene, so lets not show it. + } else { + var soloLink = soloLinkGenerator(session.rpcs[UUID].streamID); + createControlBoxScreenshare(UUID, soloLink, session.rpcs[UUID].streamID); + } + + } + + if (session.autorecord || session.autorecordremote){ + log("AUTO RECORD START"); + setTimeout(function(UUID, v){ + + var videoKbps = 4000; + if (session.recordLocal !== false) { + videoKbps = session.recordLocal; + } + + if (session.director){ + recordVideo(document.querySelector("[data-action-type='recorder-local'][data--u-u-i-d='"+UUID+"']"), null, videoKbps) + } else if (v.stopWriter || v.recording){ + + } else if (v.startWriter){ + v.startWriter(); + } else { + recordLocalVideo(null, videoKbps, v) + } + },2000, UUID, v); + } + + setTimeout(processStats, 100, UUID); +} + +function setupIncomingVideoTracking(v, UUID){ // video element. + + if (session.directorList.indexOf(UUID)>=0){ + v.muted=false; + } + + v.onpause = (event) => { // prevent things from pausing; human or other + + if (v.dataset.UUID && session.rpcs[v.dataset.UUID] && (session.rpcs[v.dataset.UUID].manualBandwidth === 0)){ + return true; + } + + if (!CtrlPressed){ + warnlog("Video paused; force it to play again"); + //return; + //session.audioCtx.resume(); + //log("ctx resume"); + + event.currentTarget.play().then(_ => { + log("playing 6"); + }).catch(error => { + warnlog("didnt play 1"); + }); + unPauseVideo(v); + + } else if (Firefox && CtrlPressed){ + log("CLICK 351"); + if (session.statsMenu !==false){ + var uid = event.currentTarget.dataset.UUID; + event.preventDefault(); + if ("stats" in session.rpcs[uid]){ + + var [menu, innerMenu] = statsMenuCreator(); + + printViewStats(innerMenu, uid ); + + menu.interval = setInterval(printViewStats, session.statsInterval, innerMenu, uid); + } + } + event.stopPropagation(); + return false; + + } + return true; + } + + /* v.onerror = function(event){ + errorlog(event); + try{ + warnlog("Vidieo element threw an error; going to reconnect it"); + session.rpcs[UUID].videoElement.stop(); + session.rpcs[UUID].videoElement.srcObject = null; + session.rpcs[UUID].videoElement.srcObject = session.rpcs[UUID].streamSrc; // replaecd with updateIncomingVideoElement these days + session.rpcs[UUID].videoElement.play(); + setTimeout(function(){updateMixer();},1); + } catch(e){errorlog(e);} + } */ + + if (session.pip){ + v.onloadedmetadata = function(){ + if (!v.paused){ + if (!(v.pip)){ + v.pip=true; + toggleSystemPip(v, true); + } + } + } + } + + v.addEventListener('resize', (e) => { + var v = e.target; + var aspectRatio = parseFloat(v.videoWidth/v.videoHeight) || 0; + log("resize event: "+aspectRatio); + + if (!aspectRatio){ + v.resetAR = true; + return; + } // if Audio only, then we don't want to set or update any aspect ratio. + + if (session.keepIncomingVideosInLandscape){ + if (aspectRatio < 1){ // session.keepIncomingVideosInLandscape + v.rotated = session.keepIncomingVideosInLandscape; + } else { + v.rotated = 0; + } + } + + if (v.resetAR){ + log("ASPECT RATIO UNMUTED"); + delete(v.resetAR); + v.dataset.aspectRatio = aspectRatio; + pokeIframeAPI("aspect-ratio", v.dataset.aspectRatio, v.dataset.UUID, v.dataset.sid); + setTimeout(function(){updateMixer();},1); + } else if (v.dataset.aspectRatio){ + if (aspectRatio != parseFloat(v.dataset.aspectRatio)){ + log("ASPECT RATIO CHANGED"); + v.dataset.aspectRatio = aspectRatio; + pokeIframeAPI("aspect-ratio", v.dataset.aspectRatio, v.dataset.UUID, v.dataset.sid); + setTimeout(function(){updateMixer();},1); // We don't want to run this on the first resize? just subsequent ones. + } + } else { + log("NEW VIDEO ? ASPECT RATIO new"); + v.dataset.aspectRatio = aspectRatio; + pokeIframeAPI("aspect-ratio", v.dataset.aspectRatio, v.dataset.UUID, v.dataset.sid); + setTimeout(function(){updateMixer();},1); + } + + + }); + + if (typeof session.volume == "number"){ + v.volume = session.volume; + } else { + v.volume = 1.0; // play audio automatically + } + v.autoplay = true; + v.controls = session.showControls || false; + v.classList.add("tile"); + v.setAttribute("playsinline",""); + v.controlTimer = null; + + v.dataset.menu = "context-menu-video"; + if (!session.cleanOutput){ + v.classList.add("task"); // this adds the right-click menu + } + + if (document.getElementById("mainmenu")){ + var m = getById("mainmenu"); + m.remove(); + document.querySelectorAll(".hidden2").forEach(ele2=>{ + ele2.classList.remove("hidden2"); + }); + } + + if (session.director){ + if (session.showControls===false){ + v.controls = false; + } else { + v.controls = true; + } + var container = getById("videoContainer_"+UUID); + v.container = container; + v.disablePictureInPicture = false + v.setAttribute("controls","controls") + container.appendChild(v); + pokeIframeAPI("control-box-video-updated", v.id, UUID); + container.classList.add("hasMedia"); + session.requestRateLimit(session.directorViewBitrate,UUID); /// limit resolution for director + v.title = "Hold CTRL or CMD (⌘) while clicking the video to open detailed stats"; + if (session.beepToNotify) { + playtone(); + } + + } else if (session.scene!==false){ + v.controls = session.showControls || false; + + if (session.view){ // specific video to be played + v.style.display="block"; + } else if (session.scene==="0"){ // auto plays, right? + v.style.display="block"; + } else if ((session.scene!==false) && session.autoadd && session.rpcs[UUID].streamID && session.autoadd.includes(session.rpcs[UUID].streamID)){ /// session.autoadd + v.style.display="block"; // auto added because manually added. + } else { // group scene I guess; needs to be added manually + v.style.display="none"; + session.rpcs[UUID].mutedStateScene = true; + } + + } else if (session.roomid!==false){ + if (session.cleanOutput){ + v.controls = session.showControls || false; + } else if (session.studioSoftware) { + v.controls = session.showControls || false; + } else if (session.showControls!==null){ + v.controls = session.showControls; + } else { + v.controls = true; + } + } else { + v.style.display="block"; + } + + v.addEventListener('click', function(e) { // show stats of video if double clicked + log("clicked"); + try { + var uid = e.currentTarget.dataset.UUID; + if (e.ctrlKey || e.metaKey){ + e.preventDefault(); + if (session.statsMenu !==false){ + if ("stats" in session.rpcs[uid]){ + + var [menu, innerMenu] = statsMenuCreator(); + printViewStats(innerMenu, uid ); + + menu.interval = setInterval(printViewStats, session.statsInterval, innerMenu, uid); + + + } + } + e.stopPropagation(); + return false; + } else if ("prePausedBandwidth" in session.rpcs[uid]){ + unPauseVideo(e.currentTarget); + } + } catch(e){errorlog(e);} + }); + + if (session.statsMenu){ + if ("stats" in session.rpcs[UUID]){ + + if (getById("menuStatsBox")){ + clearInterval(getById("menuStatsBox").interval); + getById("menuStatsBox").remove(); + } + + var [menu, innerMenu] = statsMenuCreator(); + printViewStats(innerMenu, UUID ); + menu.interval = setInterval(printViewStats, session.statsInterval, innerMenu, UUID); + } + } + + + v.touchTimeOut = null; + v.touchLastTap = 0; + v.touchCount = 0; + v.addEventListener('touchend', function(event) { + + if (session.disableMouseEvents){return;} + + log("touched"); + + //document.ontouchup = null; + //document.onmouseup = null; + document.onmousemove = null; + document.ontouchmove = null; + + var currentTime = new Date().getTime(); + var tapLength = currentTime - v.touchLastTap; + clearTimeout(v.touchTimeOut); + if (tapLength < 500 && tapLength > 0) { + /// + log("double touched"); + v.touchCount+=1; + event.preventDefault(); + if (v.touchCount<5){ + v.touchLastTap = currentTime; + return false; + } + v.touchLastTap = 0; + v.touchCount=0; + + log("double touched"); + if (session.statsMenu !==false){ + var uid = event.currentTarget.dataset.UUID; + if ("stats" in session.rpcs[uid]){ + + var [menu, innerMenu] = statsMenuCreator(); + printViewStats(innerMenu, uid ); + menu.interval = setInterval(printViewStats, session.statsInterval, innerMenu, uid); + } + } + event.stopPropagation(); + return false; + ////// + } else { + v.touchCount=1; + v.touchTimeOut = setTimeout(function(vv) { + clearTimeout(vv.touchTimeOut); + vv.touchLastTap = 0; + vv.touchCount=0; + }, 5000, v); + v.touchLastTap = currentTime; + } + + }); + + + if (session.rpcs[UUID].stats.info && ("remote" in session.rpcs[UUID].stats.info) && session.rpcs[UUID].stats.info.remote){ + v.addEventListener("wheel", remoteFocusZoomRequest); // just remote focus + } + + if (v.controls == false){ + v.addEventListener("click", function () { + log("click 33"); + if (v.paused){ + log("PLAYING MANUALLY?"); + v.play().then(_ => { + log("playing 7"); + }).catch(warnlog); + } + }); + if (session.nocursor==false){ // we do not want to show the controls. This is because MacOS + OBS does not work; so electron app needs this. + if (!(session.cleanOutput)){ + if (session.studioSoftware) { + } else if (session.showControls === false) { // explicitly disabled; default null. + } else if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1) { + } else { + if (v.controlTimer){ + clearInterval(v.controlTimer); + } + v.controlTimer = setTimeout(showControlBar.bind(null,v),1000); + //v.controlTimer = setTimeout(function (){v.controls=true;},3000); // 3 seconds before I enable the controls automatically. This way it doesn't auto appear during loading. 3s enough, right? + } + } + } + } + + //if (session.fadein){ + v.addEventListener('animationend', function(e) { + v.classList.remove("fadein"); // allows the video to fade in. + if (v.holder){ + v.holder.classList.remove("fadein"); + } + }); + //v.classList.add("fadein"); // allows the video to fade in. + // if (v.holder){ + //// v.holder.classList.add("fadein"); + // } + //} + + applyMuteState(UUID);; + v.usermuted = false; + + if (session.screenShareStartPaused && session.rpcs[UUID].screenShareState){ + pauseVideo(v, false); + } + + v.addEventListener('volumechange',function(e){ + var muteState = checkMuteState(UUID); + if (this.muted && (this.muted !== muteState)){ + this.usermuted = true; + } else if (!this.muted){ + this.usermuted = false; + } + }); + + if (session.autorecord || session.autorecordremote){ + log("AUTO RECORD START"); + setTimeout(function(UUID, v){ + + var videoKbps = 4000; + if (session.recordLocal !== false) { + videoKbps = session.recordLocal; + } + + if (session.director){ + recordVideo(document.querySelector("[data-action-type='recorder-local'][data--u-u-i-d='"+UUID+"']"), null, videoKbps) + } else if (v.stopWriter || v.recording){ + + } else if (v.startWriter){ + v.startWriter(); + } else { + recordLocalVideo(null, videoKbps, v) + } + },2000, UUID, v); + } + + setTimeout(processStats, 100, UUID); +} + +function remoteFocusZoomRequest(event){ + event.preventDefault(); + var scale = parseFloat(event.deltaY * -0.001); + log(event.currentTarget); + + if ((event.ctrlKey)||(event.metaKey)){ // focus + session.requestFocusChange(scale, event.currentTarget.dataset.UUID); + } else { // zoom + session.requestZoomChange(scale, event.currentTarget.dataset.UUID); + } +}; + +function mediaAudioTrackUpdated(UUID, streamID){ + pokeIframeAPI("new-audio-track-added", true, UUID, streamID); // videoTrack is whether video. audio will be false I guess. +} +function mediaVideoTrackUpdated(UUID, streamID){ + pokeIframeAPI("new-video-track-added", true, UUID, streamID); // videoTrack is whether video. audio will be false I guess. +} +function mediaSourceUpdated(UUID, streamID){ + pokeIframeAPI("new-stream-added", true, UUID, streamID); // videoTrack is whether video. audio will be false I guess. + pokeAPI("streamAdded", streamID); +} + +function showControlBar(vel){ + try { + vel.controls=true; + } catch(e){errorlog(e);} +} + +function createRichVideoElement(UUID){ // this function is used to check and generate a rich video element if needed + if (!session.rpcs[UUID].videoElement){ + log("video element is being created and any media tracks added"); + session.rpcs[UUID].videoElement = createVideoElement(); + session.rpcs[UUID].videoElement.dataset.UUID = UUID; + session.rpcs[UUID].videoElement.id = "videosource_"+UUID; // could be set to UUID in the future + + if (session.rpcs[UUID].streamID){ + session.rpcs[UUID].videoElement.dataset.sid = session.rpcs[UUID].streamID; + } + + if (session.rpcs[UUID].rotate !== false){ + session.rpcs[UUID].videoElement.rotated = session.rpcs[UUID].rotate; + } + + session.rpcs[UUID].videoElement.addEventListener("playing", (e)=>{ + + try { + var bigPlayButton = document.getElementById("bigPlayButton"); + if (bigPlayButton){ + bigPlayButton.parentNode.removeChild(bigPlayButton); + } + } catch(e){} + + resetupAudioOut(e.target, true); + + try { + if (session.pip){ + if (v.readyState >= 3){ + if (!(v.pip)){ + v.pip=true; + toggleSystemPip(v, true); + } + } + } + } catch(e){} + + }, { once: true }); + + if (session.rpcs[UUID].mirrorState){ + applyMirrorGuest(session.rpcs[UUID].mirrorState, session.rpcs[UUID].videoElement); + } else if (session.rpcs[UUID].mirrorState===false){ + applyMirrorGuest(session.rpcs[UUID].mirrorState, session.rpcs[UUID].videoElement); + } + + + if (session.posterImage){ + session.rpcs[UUID].videoElement.poster = session.posterImage; + } + + setupIncomingVideoTracking(session.rpcs[UUID].videoElement, UUID); + pokeIframeAPI("video-element-created", "videosource_"+UUID, UUID); + } + return session.rpcs[UUID].videoElement; +} + +function updateVolume(update=false){ + if (session.audioGain!==false){ + if (update){ + if (session.roomid){ + var pswd = session.password || ""; + generateHash(session.streamID + session.roomid + pswd + session.salt, 6).then(function(hash) { + setStorage("micVolume_"+hash, session.audioGain, hours=6); + }); + } + } + if (session.audioGain === 0){ + getById("header").classList.add('orange'); + getById("head7").classList.remove('hidden'); + } else { + getById("header").classList.remove('orange'); + getById("head7").classList.add('hidden'); + } + + } else { + var pswd = session.password || ""; + generateHash(session.streamID + session.roomid + pswd + session.salt, 6).then(function(hash) { + var volume = getStorage("micVolume_"+hash); + if (volume !== ""){ + if (parseInt(volume) === 0){ + getById("header").classList.add('orange'); + getById("head7").classList.remove('hidden'); + } else if (parseInt(volume)){ + getById("header").classList.remove('orange'); + getById("head7").classList.add('hidden'); + } else { + return; + } + session.audioGain = parseInt(volume); + var vol = parseFloat(session.audioGain/100) || 0; + for (var waid in session.webAudios){ // TODO: EXCLUDE CURRENT TRACK IF ALREADY EXISTS ... if (trackid === wa.id){.. + log("Adjusting Gain; only track 0 in all likely hood, unless more than track 0 support is added."); + session.webAudios[waid].gainNode.gain.setValueAtTime(vol, session.webAudios[waid].audioContext.currentTime); + } + } + }); + } +} + +function hideHomeCheck(){ + if (session.hidehome){ + getById("logoname").classList.add("permahide"); + getById("container-1").classList.add("permahide"); + getById("container-4").classList.add("permahide"); + getById("dropButton").classList.add("permahide"); + getById("head1").classList.add("permahide"); + if ((session.permaid === false) && (session.roomid == false)){ + getById("mainmenu").classList.add("permahide"); + } else { + getById("mainmenu").classList.remove("permahide"); + } + + getById("audioScreenCaptureDocs").classList.add("permahide"); + getById("audioScreenCaptureDocs2").classList.add("permahide"); + getById("translateButton").classList.add("permahide"); + getById("calendarButton").classList.add("permahide"); + getById("info").classList.add("permahide"); + getById("helpbutton").classList.add("permahide"); + } + + if (urlParams.has('headertitle')){ + let pageTitle = urlParams.get('headertitle') || ""; + pageTitle = decodeURIComponent(pageTitle) || ""; + document.title = pageTitle + getById("metaTitle").content = pageTitle; + } + + if (urlParams.has('favicon')){ + let favicon = ""; + if (urlParams.get('favicon')){ + favicon = decodeURIComponent(urlParams.get('favicon')) || ""; + } + getById("favicon1").href = favicon; + getById("favicon2").href = favicon; + getById("favicon3").href = favicon; + + } +} + +// toggleQualityDirector(1200, this.dataset.UUID, this) + +function switchModes(state=null){ + if (state===null){ + session.switchMode = !session.switchMode; + } else { + session.switchMode = state; + } + if (session.switchMode){ + getById("directorlayout").classList.add("hidden"); + getById("gridlayout").classList.remove("hidden"); + updateMixer(); + } else { + + getById("directorlayout").classList.remove("hidden"); + getById("gridlayout").classList.add("hidden"); + + for (var UUID in session.rpcs){ + if (session.rpcs[UUID].videoElement){ + session.rpcs[UUID].videoElement.style = ""; + session.rpcs[UUID].videoElement.alreadyAdded = false; + var target = document.querySelector("#container_"+UUID+" .controlVideoBox"); + if (target){ + target.prepend(session.rpcs[UUID].videoElement); + if (session.signalMeter){ + if (session.rpcs[UUID].signalMeter){ + target.appendChild(session.rpcs[UUID].signalMeter); + } + } + if (session.batteryMeter){ + if (session.rpcs[UUID].batteryMeter){ + target.appendChild(session.rpcs[UUID].batteryMeter); + } + } + if (session.rpcs[UUID].voiceMeter){ + target.appendChild(session.rpcs[UUID].voiceMeter); + } + if (session.rpcs[UUID].remoteMuteElement){ + target.appendChild(session.rpcs[UUID].remoteMuteElement); + } + } + } + } + + if (session.videoElement){ + session.videoElement.style = ""; + session.videoElement.alreadyAdded = false; + + if (session.showDirector == true) { + var target = document.querySelector("#videoContainer_director"); + if (target && session.videoElement){ + target.prepend(session.videoElement); + } + + } else if ((session.videoElement.srcObject && session.videoElement.srcObject.getTracks().length) || (getById("press2talk").dataset.enabled == true)){ + getById("miniPerformer").prepend(session.videoElement); + } + + } + + if (session.screenShareElement){ + session.screenShareElement.style = ""; + session.screenShareElement.alreadyAdded = false; + + if (session.showDirector == true) { + var target = document.querySelector("#videoScreenContainer_director"); + if (target && session.screenShareElement && session.screenShareElement.srcObject && session.screenShareElement.srcObject.getTracks().length){ + target.prepend(session.screenShareElement); + } + } else if ((session.screenShareElement.srcObject && session.screenShareElement.srcObject.getTracks().length) || (getById("press2talk").dataset.enabled == true)){ + getById("miniPerformer").prepend(session.videoElement); + } + + } + + applyQualityDirector(); + } +} + +var updateMixerTimer = null; +var updateMixerActive = false; +//var cleanupTimeout = null; +function updateMixer(e=false){ + + var controlBar = document.getElementById("subControlButtons"); + if (controlBar && controlBar.dragElement && !controlBar.isDragging){ + let vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0); + let vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0); + + // Calculate real boundaries (parent position: fixed issues) + let topOffset = 0; + let leftOffset = 0; + let elementOffset = controlBar; + while (elementOffset) { + topOffset += elementOffset.offsetTop; + leftOffset += elementOffset.offsetLeft; + elementOffset = elementOffset.offsetParent; + } + + let realX = controlBar.xOffset + leftOffset; + let realY = controlBar.yOffset + topOffset; + let maxX = vw - controlBar.offsetWidth; + let maxY = vh - controlBar.offsetHeight; + + if (realX > maxX) { + controlBar.xOffset = maxX - leftOffset; + } else if (realX < 0) { + controlBar.xOffset = 0 - leftOffset; + } + + if (realY > maxY) { + controlBar.yOffset = maxY - topOffset; + } else if (realY < 0) { + controlBar.yOffset = 0 - topOffset; + } + + controlBar.style.transform = `translate(${controlBar.xOffset}px, ${controlBar.yOffset}px)`; + } + + if (session.manual === true){return;} + else if (!session.switchMode && session.director){return;} + else if (session.windowed){return;} + + + clearInterval(updateMixerTimer); + if (updateMixerActive){ + if (session.mobile){ + updateMixerTimer = setTimeout(function(){updateMixer();},200); + } else { + updateMixerTimer = setTimeout(function(){updateMixer();},50); + } + return; + } + updateMixerActive=true; + log("updating mixer"); + + //console.log((new Error()).stack); // useful for breakpoints; finding what called updateMixer. + + try{ + updateMixerRun(e); + // clearInterval(cleanupTimeout); + // cleanupTimeout = setTimeout(function(){deleteOldMedia();},60000); + + } catch(e){} + + if (session.mobile){ + setTimeout(function(){updateMixerActive=false;},500); + } else { + setTimeout(function(){updateMixerActive=false;},100); + } +} + +function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a giant function that runs when there are changes to screensize, video track statuses, etc. + try { + + if (session.switchMode){} + else if (session.director){return;} + else if (session.manual === true){return;} + + var header = getById("header"); + var playarea = getById("gridlayout"); + + if (session.pipWindow){ + var hi = 0; + var w = session.pipWindow.clientHeight; + var h = session.pipWindow.clientWidth; + } else if (document.body.dataset.rotated){ + var hi = header.offsetHeight; + var w = document.body.clientHeight; + + if (session.widget){ + w *= 0.75; + try { + let widget = document.getElementById("widget"); + if (!widget){ + widget = document.createElement("iframe"); + widget.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;display-capture;midi;"; + widget.id = "widget"; + widget.src = parseURL4Iframe(session.widget); + log(widget.src); + document.body.appendChild(widget); + playarea.style.left = "0"; + playarea.style.width = "75%"; + } + widget.style.height = "calc(100% - " +hi + "px)"; + widget.style.top = hi; + } catch(e){ + errorlog(e); + } + } + + var h = document.body.clientWidth - hi; + if (session.dedicatedControlBarSpace || document.body.clientWidth<=700 ){ // # This needs to be reviewed. + if (session.dedicatedControlBarSpace!==false){ + if (document.getElementById("subControlButtons") && !session.overlayControls){ + if (!document.getElementById("subControlButtons").yOffset || (document.getElementById("subControlButtons").yOffset>-10)){ + h = document.body.clientWidth - hi - document.getElementById("subControlButtons").offsetHeight; + } + } + } + } + } else { + var hi = header.offsetHeight; + var w = window.innerWidth; + + if (session.widget){ + w *= 0.75; + try { + let widget = document.getElementById("widget"); + if (!widget){ + widget = document.createElement("iframe"); + widget.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;display-capture;midi;"; + widget.id = "widget"; + widget.src = parseURL4Iframe(session.widget); + log(widget.src); + document.body.appendChild(widget); + playarea.style.left = "0"; + playarea.style.width = "75%"; + } + widget.style.height = "calc(100% - " +hi + "px)"; + widget.style.top = hi; + } catch(e){ + errorlog(e); + } + } + + var h = window.innerHeight - hi; + if (session.dedicatedControlBarSpace || window.innerHeight<=700 ){ // # This needs to be reviewed. + if (session.dedicatedControlBarSpace!==false){ + if (document.getElementById("subControlButtons") && !session.overlayControls){ + if (!document.getElementById("subControlButtons").yOffset || (document.getElementById("subControlButtons").yOffset>-10)){ + h = window.innerHeight - hi - document.getElementById("subControlButtons").offsetHeight; + } + } + } + } + } + + + if (session.locked){ + var w123 = w; + var h123 = h; + + if (w>h*session.locked){ + w = h*session.locked; + } else if (h>w/session.locked){ + h = w/session.locked; + } + + playarea.style.left = ((w123-w)/2)+"px"; + playarea.style.top = ((h123-h)/2)+"px" + playarea.style.width = w+"px"; + playarea.style.height = h+"px"; + playarea.style.position = "absolute"; + playarea.style.display = "block"; + } + + var arW = 16.0; + var arH = 9.0; + + if (session.aspectRatio){ + if (session.aspectRatio==1){ + arW = 9.0; + arH = 16.0; + } else if (session.aspectRatio==2){ + arW = 12.0; // square root; cause why not. + arH = 12.0; + } else if (session.aspectRatio==3){ + arW = 12.0; // square root; cause why not. + arH = 9.0; + } + } + + var groups = [...session.group]; + if (session.groupView.length){ + groups.push(...session.groupView) + } + var sssid = false; + var soloVideo = false; + + if (session.infocus===true){ + soloVideo = true; + } else if (session.infocus && (session.infocus in session.rpcs)){ // if the infocus stream is connected + if (groups.length || session.allowNoGroup){ + try { + if (groups.some(item => session.rpcs[session.infocus].group.includes(item))){ + soloVideo = session.infocus; + } + } catch(e){errorlog(e);} + } else { + soloVideo = session.infocus; + } + } else if (session.infocus2===true){ + sssid = session.streamID; + } else if (session.infocus2 && (session.infocus2 in session.rpcs)){ // if the infocus2 stream is connected + if (groups.length || session.allowNoGroup){ + try { + if (groups.some(item => session.rpcs[session.infocus2].group.includes(item))){ + sssid = session.rpcs[session.infocus2].streamID; + } + } catch(e){errorlog(e);} + } else { + sssid = session.rpcs[session.infocus2].streamID; + } + } + + var ww = w/arW; + var hh = h/arH; + + var mediaPool = []; + var mediaPool_invisible = []; + + var miniPreview = session.minipreview; + + if (miniPreview && session.layout && session.streamID && (session.streamID in session.layout)){ + miniPreview = false; + if (session.videoElement.container && (session.videoElement.container.id == "minipreview")){ + delete session.videoElement.container; + } + if (document.getElementById("minipreview")){ + document.getElementById("minipreview").remove(); + } + } + + if (session.iframeEle && (session.iframeEle.style.display!=="none")){ // local feed + if (session.order!==false){ + session.iframeEle.order=session.order; + } else { + session.iframeEle.order=0; + } + if (session.activeSpeaker && (!session.activelySpeaking)){ + mediaPool_invisible.push(session.iframeEle); + } else { + mediaPool.push(session.iframeEle); + } + } + + if (session.videoElement && (session.videoElement.src || session.videoElement.srcObject)){ // I, myself, exist + if (session.videoElement.style.display!=="none"){ // local feed + if (miniPreview && (soloVideo!==true)){ + + /* session.videoElement.onclick = function(){ + if (soloVideo === true){ + soloVideo = false; + } else { + soloVideo = true; + log("session: myself"); + } + setTimeout(()=>updateMixer(),10); + }; */ + + } else { + if (session.order!==false){ + session.videoElement.order=session.order; + } else { + session.videoElement.order=0; + } + if (session.activeSpeaker && (!session.activelySpeaking)){ + //mediaPool_invisible.push(session.videoElement); + //} else if (session.videoElement && session.videoElement.srcObject && (session.videoElement.srcObject.getTracks().length === 0)){ + // do not show a video element if its completely empty. + } else if (session.videoElement && session.videoElement.srcObject && (session.videoElement.srcObject.getVideoTracks().length === 0)){ + // do not show a video element if its completely empty. + + } else if (soloVideo && soloVideo!==true){ + // + } else { + mediaPool.push(session.videoElement); + } + } + } + } + + if (session.screenShareState && session.screenShareElement){ // I, myself, exist + if (!session.screenShareElementHidden){ + if (session.order!==false){ + session.screenShareElement.order=session.order; + } else { + session.screenShareElement.order=0; + } + + if (soloVideo!==false){ + //session.screenShareElement.style.display="none"; + } else if (session.activeSpeaker && (!session.activelySpeaking)){ + //session.screenShareElement.style.display="none"; + } else { + mediaPool.push(session.screenShareElement); + } + } + } + + + if ((soloVideo) && (soloVideo in session.rpcs)){ // remote guest being full screened; infocus == UUID + mediaPool = []; // remove myself from fullscreen + + if (iOS || iPad){ + if (!miniPreview){ + miniPreview = 1; + } + } + + for (var j in session.rpcs){ + if (groups.length || session.allowNoGroup){ + try { + if (!(groups.some(item => session.rpcs[j].group.includes(item)))){ + continue; + } + } catch(e){errorlog(e);} + } + + if (j != soloVideo){ // this remote guest is NOT in focus + try { + if (session.rpcs[j].videoElement && session.rpcs[j].videoElement.style.display!=="none" ){ // Add it if not hidden + + if (document.pictureInPictureElement && document.pictureInPictureElement.id && (document.pictureInPictureElement.id == session.rpcs[j].videoElement.id)){ + var bitratePIP = parseInt(session.zoomedBitrate/4); + //warnUser("GOOD"); + session.requestRateLimit(bitratePIP, j); + } else { + session.requestRateLimit(0, j); // disable the video of non-fullscreen videos + } + if (session.rpcs[j].videoElement.srcObject && session.rpcs[j].videoElement.srcObject.getAudioTracks().length){ + // mediaPool_invisible.push(session.rpcs[j].videoElement); + } + } else if (session.rpcs[j].videoElement){ + session.requestRateLimit(0, j, true); // disable the video of non-fullscreen videos + } + } catch(e){errorlog(e);} + } else { // remote guest is in-focus video + //////// + try { + if (session.rpcs[j].iframeEle){ + if (session.rpcs[j].videoElement && (session.rpcs[j].videoElement.srcObject.getAudioTracks().length)){ + //mediaPool_invisible.push(session.rpcs[j].videoElement); + } + session.requestRateLimit(0, j); + mediaPool.push(session.rpcs[j].iframeEle); + continue; + } else if (session.rpcs[j].videoElement){ + + if (session.rpcs[j].order!==false){ + session.rpcs[j].videoElement.order=session.rpcs[j].order; + } else { + session.rpcs[j].videoElement.order=0; + } + /////////// + //if (session.activeSpeaker && (!session.rpcs[j].defaultSpeaker)){ // not the active speaker + //mediaPool_invisible.push(session.rpcs[j].videoElement); + // session.requestRateLimit(0, j); // keep audio good, but disable video + //} else { + + var totalRoomBitrate = session.totalRoomBitrate; + if ((session.controlRoomBitrate!==false) && (session.controlRoomBitrate!==true)){ + totalRoomBitrate = Math.min(session.controlRoomBitrate, totalRoomBitrate); + } + var targetBitrate = session.zoomedBitrate; + if (totalRoomBitrate>session.zoomedBitrate){ + targetBitrate = totalRoomBitrate; + } + + mediaPool.push(session.rpcs[j].videoElement); // active speaker + session.rpcs[j].videoElement.style.visibility = "visible"; + //if ((session.rpcs[j].targetBandwidth!==-1) && (session.rpcs[j].targetBandwidth session.rpcs[j].group.includes(item)))){ + continue; + } + } catch(e){errorlog(e);} + } + try { + if (session.rpcs[j].videoElement && (session.rpcs[j].videoElement.style.display!=="none")){ // Add it if not hidden + if (document.pictureInPictureElement && document.pictureInPictureElement.id && (document.pictureInPictureElement.id == session.rpcs[j].videoElement.id)){ + var bitratePIP = parseInt(session.zoomedBitrate/4); + session.requestRateLimit(bitratePIP, j); + //warnUser("GOOD"); + } else { + session.requestRateLimit(0, j); // disable the video of non-fullscreen videos + } + // mediaPool_invisible.push(session.rpcs[j].videoElement); + } else if (session.rpcs[j].videoElement){ + session.requestRateLimit(0, j, true); // other videos are disabled when previewing yourself, but audio retained + } + } catch(e){errorlog(e);} + } + } else { + var roomQuality = 0; + var screenShareTotal = 0; + + for (var i in session.rpcs){ + if (session.rpcs[i]===null){continue;} + if (groups.length || session.allowNoGroup){ + try { + if (!(groups.some(item => session.rpcs[i].group.includes(item)))){ + continue; + } + } catch(e){errorlog(e);} + } + if (session.rpcs[i].videoElement){ // remote feeds + if (session.rpcs[i].videoElement.style.display!=="none"){ + if (session.rpcs[i].videoElement.srcObject && session.rpcs[i].videoElement.srcObject.getVideoTracks().length){ // only count videos with actual video tracks; audio-only excluded + if (session.rpcs[i].videoMuted){ + // it's video muted + // mediaPool_invisible.push(session.rpcs[i].videoElement); // skipped later on + } else if (session.rpcs[i].directorVideoMuted){ + // it's muted by the director, so likely disabled. + // mediaPool_invisible.push(session.rpcs[i].videoElement); // skipped later on + } else if (session.rpcs[i].virtualHangup){ + + } else if (session.rpcs[i].bandwidthMuted){ + + } else if (session.rpcs[i].videoElement.style.opacity==="0"){ + // mediaPool_invisible.push(session.rpcs[i].videoElement); // skipped later on + } else { + roomQuality+=1; + if (session.rpcs[i].screenShareState){ + screenShareTotal+=1; + } + } + } + } + } + } + + if (session.broadcast !==false){ + if (session.layout && (session.streamID in session.layout)){ + // skip + } else { + if (roomQuality>0){ + if (session.nopreview!==false){ + for (var i=0;i0){ + roomBitrate = parseInt(totalRoomBitrate/(roomQuality-screenShareTotal)); + if (session.totalSceneBitrate){ + sceneBitrate = parseInt(session.totalSceneBitrate/(roomQuality-screenShareTotal)); + if (session.bitrate!==false){ + sceneBitrate = Math.min(session.bitrate, sceneBitrate); + } + } + } + } else if (screenShareTotal){ + try { + if ((session.roomid!==false) && (session.scene===false)){ + if ((roomQuality-screenShareTotal)<=0){ + roomBitrate = totalRoomBitrate; + screenShareBitrate = totalRoomBitrate; + } else { + screenShareBitrate = totalRoomBitrate/(1.5*screenShareTotal); + roomBitrate = parseInt((totalRoomBitrate - screenShareBitrate) /(roomQuality-screenShareTotal)); + } + } else if (session.totalSceneBitrate!==false){ + if ((roomQuality-screenShareTotal)<=0){ + sceneBitrate = session.totalSceneBitrate; + if (session.bitrate!==false){ + sceneBitrate = Math.min(session.bitrate, sceneBitrate); + } + screenShareBitrate = sceneBitrate; + } else { + screenShareBitrate = parseInt(totalRoomBitrate/(1.5*screenShareTotal)); + sceneBitrate = parseInt((totalRoomBitrate - screenShareBitrate)/(roomQuality-screenShareTotal)); + if (session.bitrate!==false){ + sceneBitrate = Math.min(session.bitrate, sceneBitrate); + screenShareBitrate = Math.min(session.bitrate, screenShareBitrate); + } + } + } else { + screenShareBitrate = false; + } + } catch(e){errorlog(e);} + } else { + roomBitrate = parseInt(totalRoomBitrate/roomQuality); + if (session.totalSceneBitrate){ + sceneBitrate = parseInt(session.totalSceneBitrate/roomQuality); + if (session.bitrate!==false){ + sceneBitrate = Math.min(session.bitrate, sceneBitrate); + } + } + } + + if (session.minimumRoomBitrate){ + if (session.totalRoomBitrate && (roomBitratesession.totalRoomBitrate){ + roomBitrate = session.totalRoomBitrate; + } + } + if (session.totalSceneBitrate && (sceneBitratesession.totalSceneBitrate){ + sceneBitrate = session.totalSceneBitrate; + } + } + } + + var i = null; + var countOrder = 0; + try{ + var RPCSkeys = Object.keys(session.rpcs); // default sorting type: time added; //RPCSkeys.sort(); + } catch(e){return;} + + for (var keyIndex = 0; keyIndex session.rpcs[i].group.includes(item)))){ + if (session.scene!==false){ + if (session.groupAudio){ + session.requestRateLimit(session.hiddenSceneViewBitrate, i, false); + } else { + session.requestRateLimit(session.hiddenSceneViewBitrate, i, true); // hidden. I dont want it to be super low, for video quality reasons. + session.rpcs[i].mutedStateMixer = true; + } + if (!session.hiddenSceneViewBitrate){ + session.rpcs[i].videoElement.nogb = 2; + } + } else { + if (session.groupAudio){ + session.requestRateLimit(0, i, false); + } else { + session.requestRateLimit(0, i, true); // w/e This is not in OBS, so we just set it as low as possible. Shoudln't exist really unless loading? + session.rpcs[i].mutedStateMixer = true; + } + } + applyMuteState(i); + continue; + } + } catch(e){} + } + applyMuteState(i); + var doNotPush = false; + + if (session.rpcs[i].iframeEle){ + if (session.rpcs[i].iframeEle.style.display=="none"){ + // pass + } else if (session.rpcs[i].iframeEle.style.opacity==="0"){ + // pass + } else { + session.rpcs[i].iframeEle.style.visibility = "visible"; + + if (session.rpcs[i].order!==false){ + session.rpcs[i].iframeEle.order=session.rpcs[i].order; + } else { + session.rpcs[i].iframeEle.order=0; + } + try{ + if (session.activeSpeaker && (!session.rpcs[i].defaultSpeaker)){ + mediaPool_invisible.push(session.rpcs[i].iframeEle); // TODO: this needs validation; will the iframe be maintained if activer speaker is going? do we even want this? + + /* } else if (session.rpcs[i].iframeEle.dataset.meshcast){ //////// MESH CAST ONLY LOGIC + if (session.rpcs[i].iframeEle.contentDocument && session.rpcs[i].iframeEle.contentDocument.querySelectorAll("video").length){ + if (session.rpcs[i].iframeVideo){ + mediaPool.push(session.rpcs[i].iframeVideo); + } else if (session.rpcs[i].iframeEle.contentDocument.querySelectorAll("video").length){ + session.rpcs[i].iframeVideo = session.rpcs[i].iframeEle.contentDocument.querySelectorAll("video")[0]; + session.rpcs[i].iframeVideo.id="meshcast_"+i; + //errorlog("THIS IS GOOD"); + mediaPool.push(session.rpcs[i].iframeVideo); + } else { + //errorlog("No video yet"); + } + } else { // this is a problem is not on the same domain. + if (!document.getElementById("iframe_"+i)){ + if (document.getElementById("hiddenElements")){ + document.getElementById("hiddenElements").append(session.rpcs[i].iframeEle); + } else { + document.body.append(session.rpcs[i].iframeEle); + } + if (session.rpcs[i].iframeVideo){ + mediaPool.push(session.rpcs[i].iframeVideo); + } else if (session.rpcs[i].iframeEle.contentDocument.querySelectorAll("video").length){ + session.rpcs[i].iframeVideo = session.rpcs[i].iframeEle.contentDocument.querySelectorAll("video")[0]; + session.rpcs[i].iframeVideo.id="meshcast_"+i; + mediaPool.push(session.rpcs[i].iframeVideo); + } else { + //errorlog("No video yet"); + } + + } else { + if (session.rpcs[i].iframeVideo){ + mediaPool.push(session.rpcs[i].iframeVideo); + } else { + //errorlog("Does not support contentDocument or something"); + } + } + } */ + } else { ///////// MESH CAST LOGIC ENDS HERE + //errorlog("not meshcast"); + mediaPool.push(session.rpcs[i].iframeEle); + } + } catch(e){errorlog(e);} + } + } + + if (session.rpcs[i].imageElement){ + if (session.rpcs[i].videoElement && (session.rpcs[i].videoElement.srcObject.getAudioTracks().length)){ // is there audio? + // mediaPool_invisible.push(session.rpcs[i].videoElement); // include audio as hidden track; + } + + if (session.rpcs[i].videoMuted || session.rpcs[i].directorVideoMuted || session.rpcs[i].virtualHangup || session.rpcs[i].bandwidthMuted){ + continue; + } + + if (session.rpcs[i].videoElement.style.display=="none"){ // currently this is considered the state of scenes. pertty dumb on my part. + continue; + } + + if (session.rpcs[i].order!==false){ + session.rpcs[i].imageElement.order=session.rpcs[i].order; + } else { + session.rpcs[i].imageElement.order=0; + } + if (session.activeSpeaker && (!session.rpcs[i].defaultSpeaker)){ + // mediaPool_invisible.push(session.rpcs[i].imageElement); + } else { + mediaPool.push(session.rpcs[i].imageElement); + } + + doNotPush = true; + } + + if (session.rpcs[i].videoElement){ // remote feeds + //session.rpcs[i].targetBandwidth = -1; + if (session.rpcs[i].videoElement.style.opacity==="0"){ + continue; + } + try{ + session.rpcs[i].videoElement.style.visibility = "visible"; + } catch(e){errorlog(e);} + + if (session.rpcs[i].virtualHangup || session.rpcs[i].bandwidthMuted || session.rpcs[i].directorVideoMuted){ + continue; + } + + if (session.style && (session.style >= 2)){ + if (session.rpcs[i].videoElement.srcObject && ((session.rpcs[i].videoElement.srcObject.getVideoTracks().length==0) || (session.rpcs[i].videoMuted))){ + + if (session.rpcs[i].videoElement.style.display=="none"){ // currently this is considered the state of scenes. pertty dumb on my part. + continue; + } + + if (createStyleCanvas(i)){ + applyStyleEffect(i); + } + + if (session.rpcs[i].order!==false){ + session.rpcs[i].canvas.order=session.rpcs[i].order; + } else { + session.rpcs[i].canvas.order=0; + } + if (session.activeSpeaker && (!session.rpcs[i].defaultSpeaker)){ + // mediaPool_invisible.push(session.rpcs[i].canvas); + } else { + mediaPool.push(session.rpcs[i].canvas); + } + + doNotPush = true; + //continue; + } + } else if (session.style==1){ + if (session.rpcs[i].videoElement.srcObject && ((session.rpcs[i].videoElement.srcObject.getVideoTracks().length==0) || session.rpcs[i].videoMuted)){ + //if (session.style==1){ // avatars and waveforms might be better done elsewhere? as a canvas effect even? + doNotPush = true; + //} + } + } else if (session.rpcs[i].videoElement.srcObject && ((session.rpcs[i].videoElement.srcObject.getVideoTracks().length==0) || session.rpcs[i].videoMuted)){ + if (session.rpcs[i].screenShareState){ + doNotPush = true; + } + } + //} else if (!session.directorList.indexOf(i)>=0){ // director is never audio-only. Video if need, yes, but not visualized-audio. + // if (session.rpcs[i].videoElement.srcObject && ((session.rpcs[i].videoElement.srcObject.getVideoTracks().length==0) || (session.rpcs[i].videoMuted)) && !session.rpcs[i].directorVideoMuted){ + // continue; + // } + //} + + + session.rpcs[i].opacityMuted = "1"; + if (session.rpcs[i].opacityDisconnect=="1"){ + if (session.rpcs[i].videoElement){ + session.rpcs[i].videoElement.style.opacity = "1"; + } + } + if (session.rpcs[i].videoMuted){ + if (session.rpcs[i].videoElement.srcObject.getAudioTracks().length==0){ // if no audio track, no point in removing the video track, since it will just stall out then. + continue; // easiest is to just not show anything if no video and no audio track. + } + if (session.rpcs[i].videoElement.srcObject){ + session.rpcs[i].videoElement.srcObject.getVideoTracks().forEach(track=>{ + session.rpcs[i].videoElement.srcObject.removeTrack(track); + session.rpcs[i].videoElement.load(); + }); + } + //continue; // currently disabling this, since we want to show it. + } else if (session.rpcs[i].virtualHangup || session.rpcs[i].bandwidthMuted || session.rpcs[i].directorVideoMuted){ + continue + } + + + if (session.scene!==false){ + if (session.sceneType === 3){ // order + countOrder+=1; + if (session.order === false){ + if (countOrder==1){ + session.rpcs[i].videoElement.style.display="block"; + } else { + session.rpcs[i].videoElement.style.display="none"; + } + } else if (session.order === countOrder){ + session.rpcs[i].videoElement.style.display="block"; + } else { + session.rpcs[i].videoElement.style.display="none"; + } + } + } + + if (session.rpcs[i].videoElement.style.display=="none"){ // Video is disabled; run at lowest + if (session.scene!==false){ + session.requestRateLimit(session.hiddenSceneViewBitrate, i, true); // hidden. I dont want it to be super low, for video quality reasons. + if (!session.hiddenSceneViewBitrate){ + session.rpcs[i].videoElement.nogb = 2; + } + } else { + session.requestRateLimit(0, i, true); // w/e This is not in OBS, so we just set it as low as possible. Shoudln't exist really unless loading? + } + } else if (session.scene!==false){ // max + if (sceneBitrate!==false){ + if ((screenShareBitrate!==false) && session.rpcs[i].screenShareState){ + session.requestRateLimit(screenShareBitrate, i); // well, screw that. Setting it to room quality. + } else { + session.requestRateLimit(sceneBitrate, i); // well, screw that. Setting it to room quality. + } + } else { + + session.requestRateLimit(-1, i); // unlock. + } + if (session.rpcs[i].order!==false){ + session.rpcs[i].videoElement.order=session.rpcs[i].order; + } else { + session.rpcs[i].videoElement.order=0; + } + if (session.activeSpeaker && (!session.rpcs[i].defaultSpeaker)){ + if (!(session.rpcs[i].videoElement in mediaPool_invisible)){ + // mediaPool_invisible.push(session.rpcs[i].videoElement); + } else { + errorlog("THIS SHOULD NOT HAPPEN; 650"); + } + } else if (!doNotPush){ + mediaPool.push(session.rpcs[i].videoElement); + } + } else if (session.roomid!==false){ // guests should see video at low bitrate, ie: 100kbps (not 35kbps like if disabled) + if (session.rpcs[i].order!==false){ + session.rpcs[i].videoElement.order=session.rpcs[i].order; + } else { + session.rpcs[i].videoElement.order=0; + } + if (session.activeSpeaker && (!session.rpcs[i].defaultSpeaker)){ + if (!(session.rpcs[i].videoElement in mediaPool_invisible)){ + // mediaPool_invisible.push(session.rpcs[i].videoElement); + } else { + errorlog("THIS SHOULD NOT HAPPEN; 665"); + } + } else if (!doNotPush){ + mediaPool.push(session.rpcs[i].videoElement); + } + if ((session.roomid==="") && (session.bitrate)){ + // we will let the URL specified bitrate hold, since this isn't a real room. + session.requestRateLimit(-1, i); + } else { + if ((screenShareBitrate!==false) && session.rpcs[i].screenShareState){ + session.requestRateLimit(screenShareBitrate, i); // well, screw that. Setting it to room quality. + } else { + session.requestRateLimit(roomBitrate, i); // well, screw that. Setting it to room quality. + } + } + } else { // view=xx,yy or whatever. This should be highest quality. + if (session.rpcs[i].order!==false){ + session.rpcs[i].videoElement.order=session.rpcs[i].order; + } else { + session.rpcs[i].videoElement.order=0; + } + if (session.activeSpeaker && (!session.rpcs[i].defaultSpeaker)){ + if (!(session.rpcs[i].videoElement in mediaPool_invisible)){ + // mediaPool_invisible.push(session.rpcs[i].videoElement); + } else { + errorlog("THIS SHOULD NOT HAPPEN; 684"); + } + } else if (!doNotPush){ + mediaPool.push(session.rpcs[i].videoElement); + } + if (sceneBitrate){ + session.requestRateLimit(sceneBitrate, i); + } else if ((session.screenShareBitrate!==false) && session.rpcs[i].screenShareState){ // session.screenShareBitrate is non-room + session.requestRateLimit(session.screenShareBitrate, i); // well, screw that. Setting it to room quality. + } else { + session.requestRateLimit(-1, i); + } + } + if (session.rpcs[i].videoElement.nogb==2){ + session.rpcs[i].videoElement.nogb = 1; + session.rpcs[i].videoElement.classList.add("nogb"); + } else if (session.rpcs[i].videoElement.nogb==1){ + session.rpcs[i].videoElement.nogb = 0; + session.rpcs[i].videoElement.classList.remove("nogb"); + } + } + } + } + + + if (session.broadcastIFrame && session.broadcastIFrame.src){ // keep alive iframes whennot visible. i think + if (!mediaPool.length){ + mediaPool.push(session.broadcastIFrame); + } + } + + if (document.fullscreenElement) { + try { + if (document.fullscreenElement.tagName === "VIDEO"){ // if its HTML, than we assume its the full canvas + for (var i=0;i1){ + var BB = 0; + var rw = 1; + var rh = 1; + var NW; + var NH; + var current; + for (NW=1; NW <= mpl; NW++){ + NH = Math.ceil(mpl/NW); + var www = ww/NW; + var hhh = hh/NH; + if (www>hhh){ + current = Math.round(hhh * hhh * (mpl/(NW*NH))); + } else { + current = Math.round(www * www * (mpl/(NW*NH))); + } + + if (current>=BB){ + BB = current; + rw = NW; + rh = NH; + } + + if (mediaPool[NW-1]){ + //if (mediaPool[NW-1].tagName == "VIDEO"){ + if (mediaPool[NW-1].dataset.UUID){ + if (mediaPool[NW-1].dataset.UUID in session.rpcs){ + if (session.rpcs[mediaPool[NW-1].dataset.UUID].screenShareState){ + sscount+=1; + sssid = mediaPool[NW-1].dataset.sid; + } + } + } else if (("id" in mediaPool[NW-1]) && (mediaPool[NW-1].id == "screensharesource") && session.notifyScreenShare){ + sscount+=1; + sssid = mediaPool[NW-1].dataset.sid; + } + } + } + } else { var rw=1; var rh=1;} + + if (sscount>1){ + sssid = false; // lets not maximize if more than one screen share. + } + } + + } catch(e){ + errorlog(e); + sssid = false + } + + var customLayout=false; + + if (!session.notifyScreenShare && (session.scene!==false)){ + // this is a scene, so lets assume &smallshare will disable larger screen shares since there is no one to screen share. + } else if (sssid && !session.layout){ + customLayout = {}; + + if (mediaPool.length>=12){ + customLayout[sssid] = {"x":0,"y":10,"w":90,"h":90, "c": false}; + } else if (mediaPool.length>=10){ + customLayout[sssid] = {"x":0,"y":10,"w":100,"h":90, "c": false}; + } else if (mediaPool.length>=8){ + customLayout[sssid] = {"x":0,"y":20,"w":80,"h":80, "c": false}; + } else if (mediaPool.length==7){ + customLayout[sssid] = {"x":0,"y":0,"w":83.33333,"h":100, "c": false}; + } else if (mediaPool.length==5){ + customLayout[sssid] = {"x":0,"y":0,"w":80,"h":100, "c": false}; + } else if (mediaPool.length==6){ + customLayout[sssid] = {"x":0,"y":0,"w":80,"h":100, "c": false}; + } else { + customLayout[sssid] = {"x":0,"y":0,"w":80,"h":100, "c": false}; + } + var posCount = 0; + for (var i = 0; i=10){ + if (posCount<10){ + customLayout[mediaPool[i].dataset.sid] = {"x":10*posCount,"y":0,"w":10,"h":10, "c":true}; + } else { + customLayout[mediaPool[i].dataset.sid] = {"x":90,"y":(posCount-9)*10,"w":10,"h":10, "c":true}; + } + } else { + customLayout[mediaPool[i].dataset.sid] = {"x":80,"y":posCount*20,"w":20,"h":20, "c":true}; // + } + posCount+=1; + } + } + + try { + if (!skip){ + var childNodes = playarea.childNodes; + + for (var n=0;n74){ + miniPerformerX = 74; + } + } + container.style.left = miniPerformerX + "%"; + } else if (session.leftMiniPreview!==false){ + container.style.left = session.leftMiniPreview + "%"; + togglePreview.style.left = session.leftMiniPreview + "%"; + } else if (session.widget){ + container.style.right = "25%"; + togglePreview.style.right = "25%"; + } else { + container.style.right = "2vw"; + togglePreview.style.right = "2vw"; + } + + container.appendChild(session.videoElement); + session.videoElement.container = container; + playarea.appendChild(container); + togglePreview.innerHTML = ''; + + if (!session.previewToggleState){ + container.classList.toggle("hidden"); + togglePreview.classList.toggle("blinded"); + } + if (!(iOS || iPad)){ + playarea.appendChild(togglePreview); + } + togglePreview.onclick = function(event){ + event.preventDefault(); + event.stopPropagation(); + getById("minipreview").classList.toggle("hidden"); + this.classList.toggle("blinded"); + session.previewToggleState = !session.previewToggleState; + return false; + }; + + makeMiniDraggableElement(container); + container.id = "minipreview"; + } + + container.style.width = "18%"; + //container.style.display = "flex"; + container.style.zIndex = "3"; + container.style.margin = "0"; + container.style.position ="absolute"; + container.style.cursor = "pointer"; + container.style.border = "2px #BBB solid"; + container.style.height = "block"; + + applyMirror(session.mirrorExclude); + + + } else if (soloVideo===true){ + if (document.getElementById("minipreview")){ + container = document.getElementById("minipreview"); + container.style.height = "100%"; + //container.style.transform = "block"; + //container.style.transformOrigin = "unset"; + } + } + if (session.ruleOfThirds){ + if (container && (container.id == "minipreview") && !container.svg){ + var svg = document.createElement("img"); + svg.src = session.ruleOfThirds; + svg.style.width = "100%"; + svg.style.height = "100%"; + svg.style.position= "absolute"; + svg.style.left = "0"; + svg.style.top = "0"; + container.svg = svg; + container.appendChild(svg); + } + + } + container = null; // clear reference + } + } else if (session.streamSrc && !session.videoElement.srcObject){ + warnlog("THIS SHOULD NOT HAPPEN; 2067"); + } + } + } + + try{ + + if (session.slots){ + var slotArray = []; + mediaPool.forEach(vid=>{ + if (vid.slotBlank){ + vid.slotBlank=false; + vid.slot=0; + } + + if (("slot" in vid) && vid.slot){ + if (!slotArray.includes(parseInt(vid.slot))){ + slotArray.push(parseInt(vid.slot)); + } else { + vid.slot=0; + //mediaPool_invisible.push(vid); + //var index = mediaPool.indexOf(vid); + //if (index > -1) { + // mediaPool.splice(index, 1); + //} + } + } + }) + var slotCounter = 1; + mediaPool.reverse() + var j = mediaPool.length; + while (j--){ + if (!("slot" in mediaPool[j]) || ( mediaPool[j].slot=="0") || !mediaPool[j].slot){ + while (slotArray.includes(slotCounter)){ + slotCounter+=1; + } + slotArray.push(slotCounter); + mediaPool[j].slot = slotCounter; + mediaPool[j].slotBlank = true; + } + if (!("slot" in mediaPool[j]) || !parseInt(mediaPool[j].slot) || (mediaPool[j].slot=="0") || !mediaPool[j].slot || (session.slots{ + if (vid){ + try { + vid.style.width = "0px"; + vid.style.height = "0px"; + vid.style.top = "0px"; + vid.style.left = "0px"; + vid.isInvisible = true; + if (vid.alreadyAdded && vid.alreadyAdded==true){ + vid.alreadyAdded=false; + return; + } else if (vid.dataset.doNotMove){ + return; + } + playarea.appendChild(vid); + } catch(e){errorlog(e);} + } + }); + } catch(e){errorlog(e);} + + + + var layout = false; + if (customLayout || session.layout){ + layout = session.layout || customLayout; + layout = {...layout}; + if (layout[""]){ + for(var i=0;i{ + try { + if (!vid || !("id" in vid)){ + errorlog(vid); + return; + } + + if (vid.needsLoading){ + try { + vid.load(); + } catch(e){ + errorlog(e); + } + } + + if (session.slots){ + if (("slot" in vid) && parseInt(vid.slot)){ + i = parseInt(vid.slot) - 1; + if(i<0){return;} + } else { + return; + } + } + + var offsetx=0; + if (i!==0){ + if (Math.ceil((i+0.01)/rw)==rh){ + if (mpl%rw){ + offsetx = Math.max(((rw - mpl%rw)*(w/rw))/2,0); + } + } + } + + var cover = session.cover + var borderOffset = session.border || 0; + var videoMargin = session.videoMargin || 0; + var borderRadius = session.borderRadius || 0; + var borderColor = session.borderColor || "#000"; + var fadein = session.fadein || false; + var backgroundMedia = session.defaultMedia || false; + var animated = session.animatedMoves || 0; + if (!borderOffset){ + borderColor = "#0000"; + } + + if (layout){ + if (!(vid.dataset.sid && (vid.dataset.sid in layout))){ + vid.isInvisible = true; + if (vid.container){ + vid.container.style.display = "none"; + } + if (vid.dataset.UUID){ + session.requestRateLimit(session.hiddenSceneViewBitrate, vid.dataset.UUID, false); // it's added already, so we know it needs sound. But lets d + } + return; + } + if ("borderThickness" in layout[vid.dataset.sid]){ + borderOffset = layout[vid.dataset.sid].borderThickness || 0; + } + if ("animated" in layout[vid.dataset.sid]){ + animated = layout[vid.dataset.sid].animated || 0; + if (animated===true){ + animated = session.animatedMoves || 50; + } + } + if ("margin" in layout[vid.dataset.sid]){ + videoMargin = layout[vid.dataset.sid].margin || 0; + } + if ("rounded" in layout[vid.dataset.sid]){ + borderRadius = layout[vid.dataset.sid].rounded || 0; + } + if (layout[vid.dataset.sid].borderColor){ + borderColor = layout[vid.dataset.sid].borderColor; + } + if (layout[vid.dataset.sid].fadeIn){ + fadein = layout[vid.dataset.sid].fadeIn; + } + if ("backgroundMedia" in layout[vid.dataset.sid]){ + backgroundMedia = layout[vid.dataset.sid].backgroundMedia || false; + } + if (vid.container){ + if (!((vid.nodeName == "IFRAME") && vid.isConnected)){ // moving an iframe will break it. + if (!vid.alreadyAdded || (vid.nodeName == "IFRAME")){ + playarea.appendChild(vid.container); + } + } + } + } + + var skipAnimation = false; + if (vid.isInvisible){ + vid.isInvisible = false; + skipAnimation = true; + if (fadein){ + vid.classList.add("fadein"); + if (vid.holder){ + vid.holder.classList.add("fadein"); + } + } + } + + offsety = Math.max((h - Math.ceil(mpl/rw)*Math.ceil(h/rh))/2,0); + + if (vid.container){ + var container = vid.container; + if (container.move){ + clearInterval(container.move); + container.move = null; + } + } else { + var container = document.createElement("div"); + vid.container = container; + } + container.style.position = "absolute"; + container.style.display = "block"; + container.classList.add("container_holder_video"); + + // ANIMATED - CONTAINER ; width/height/z-index/cover/////////////// + if (layout){ + var left = (w/100*layout[vid.dataset.sid].x) || layout[vid.dataset.sid].xp || 0; + var top = (h/100*layout[vid.dataset.sid].y) || layout[vid.dataset.sid].yp || 0; + top+=hi; + var width = (w/100*layout[vid.dataset.sid].w) || layout[vid.dataset.sid].wp || 0; + var height = (h/100*layout[vid.dataset.sid].h) || layout[vid.dataset.sid].hp || 0; + if (layout[vid.dataset.sid].cover || layout[vid.dataset.sid].c){ // this should be true/false + //vid.style.objectFit = "cover"; + cover = true; + } else { + //vid.style.objectFit = "contain"; // this should fall back to sessio.cover if no layout supplied + cover = false; + } + //container.style.zindex = 0; + container.style.zIndex = layout[vid.dataset.sid].zIndex || layout[vid.dataset.sid].z || 0; + + } else { + var left = Math.max(offsetx+Math.floor(((i%rw)+0)*w/rw),0); + var top = Math.max(offsety+Math.floor((Math.floor(i/rw)+0)*h/rh + hi),0); + var width = Math.ceil(w/rw); + var height = Math.ceil(h/rh); + //container.style.zIndex = 0; + } + + var computed = getComputedStyle(vid); + + if (animated && !skipAnimation){ + container.style.transition = "width "+animated+"ms ease-in-out 0s, height "+animated+"ms ease-in-out 0s, background-color "+animated+"ms ease-in-out 0s, transform "+animated+"ms ease-in-out 0s, top "+animated+"ms ease-in-out 0s, left "+animated+"ms ease-in-out 0s"; + } else { + container.style.transition = ""; + } + + if (layout){ ////////////////// NOT ANIMATED - CONTAINER ; width/height/z-index/cover/////////////// + container.style.left = left+"px"; + container.style.top = top+"px"; + container.style.width = width+"px"; + container.style.height = height+"px"; + container.twidth = width; + container.theight = height; + } else { + container.style.left = offsetx+Math.floor(((i%rw)+0)*w/rw)+"px"; + container.style.top = offsety+Math.floor((Math.floor(i/rw)+0)*h/rh + hi)+"px"; + container.twidth = Math.ceil(w/rw); + container.theight = Math.ceil(h/rh); + container.style.width = container.twidth+"px"; + container.style.height = container.theight+"px"; + } + + var maxWidth = 0; + if (parseInt(computed.width) > parseInt(container.style.width)){ + maxWidth = computed.width; + } else { + maxWidth = container.style.width; + } + + var maxHeight = 0; + if (parseInt(computed.height) > parseInt(container.style.height)){ + maxHeight = computed.height; + } else { + maxHeight = container.style.height; + } + + if (cover){ + vid.style.maxWidth = maxWidth; + vid.style.maxHeight = maxHeight; + vid.style.objectFit = "cover"; + } else { + vid.style.objectFit = "contain"; + vid.style.maxWidth = maxWidth; + vid.style.maxHeight = maxHeight; + } + + //try { + if (vid.alreadyAdded && vid.alreadyAdded==true){ + if (!container.holder){ + var holder = document.createElement("div"); + container.holder = holder; + holder.className = "holder"; + holder.dataset.holder = true; + container.appendChild(holder); + holder.appendChild(vid); + } else { + var holder = container.holder; + } + } else if (vid.dataset.doNotMove){ + vid.style.position = "absolute"; + vid.style.left = left+"px"; + vid.style.top = top+"px"; + vid.style.width = width+"px"; + vid.style.height = height+"px"; + vid.style.display = "flex"; + i+=1; + return; + } else { + if (!container.holder){ + var holder = document.createElement("div"); + container.holder = holder; + holder.className = "holder"; + holder.dataset.holder = true; + holder.appendChild(vid); + container.appendChild(holder); + } else { + var holder = container.holder; + holder.prepend(vid); + } + playarea.appendChild(container); + vid.style.maxWidth = "100%"; + vid.style.maxHeight = "100%"; + } + + if (layout){ + var wrw = (w/100*layout[vid.dataset.sid].w) || 0; + var hrh = (h/100*layout[vid.dataset.sid].h) || 0; + } else { + var wrw = (w/rw); + var hrh = (h/rh); + } + + if (backgroundMedia){ + container.style.backgroundImage = "url("+backgroundMedia+")"; + if (cover){ + container.style.backgroundSize = "cover"; + } else { + container.style.backgroundSize = "contain"; + } + container.style.backgroundPosition = "center"; + container.style.backgroundRepeat = "no-repeat"; + } else if (container.style.backgroundImage){ + container.style.backgroundImage = "unset"; + } + + + if (("rotated" in vid) && (vid.rotated!==false)){ + if (vid.rotated==90){ + vid.style.transform = "rotate(90deg)"; + } else if (vid.rotated==270){ + vid.style.transform = "rotate(270deg)"; + } else if (vid.rotated==180){ + vid.style.transform = "rotate(180deg)"; + } else if (!vid.rotated){ + vid.style.transform = "rotate(0deg)"; + } + } + + vid.style.width = "100%"; + vid.style.height = "100%"; + holder.style.position = "absolute"; + + + if (vid.classList.contains("paused")){ + if (holder.paused){ + holder.paused.className = "playButton"; + } else { + var paused = document.createElement("span"); + paused.id = "paused_"+vid.dataset.UUID; + paused.className = "playButton"; + paused.dataset.UUID = vid.dataset.UUID; + paused.onclick = function(){unPauseVideo(vid);}; + holder.paused = paused; + holder.appendChild(paused); + } + } else if (holder.paused){ + holder.paused.className = "hidden"; + } + + var vw = vid.naturalWidth || vid.videoWidth; // naturalWidth is for images I guess + var vh = vid.naturalHeight || vid.videoHeight; + + // log(vw + " : "+vh); + + if (cover){ + if ((("rotated" in vid) && ((vid.rotated==90) || (vid.rotated==270)))){ + holder.style.left = borderOffset + "px"; + holder.style.top = borderOffset + "px"; + holder.style.height = "calc(100% - "+(videoMargin*2)+"px)"; + holder.style.width = "calc(100% - "+(videoMargin*2)+"px)"; + + vid.style.width = (height - (borderOffset + videoMargin)*2) + "px"; + vid.style.height = (width - (borderOffset + videoMargin)*2) + "px"; + vid.style.left = 0; + vid.style.top = 0; + } else { + holder.style.left = videoMargin + "px"; + holder.style.top = videoMargin +"px"; + holder.style.height = "calc(100% - "+(videoMargin*2)+"px)"; + holder.style.width = "calc(100% - "+(videoMargin*2)+"px)"; + + vid.style.width = "100%"; + vid.style.height = "100%"; + vid.style.left = 0; + vid.style.top = 0; + } + + + ////////// COVER VERSION + if (session.sharperScreen && sssid && vid.dataset.sid && (vid.dataset.sid === sssid) ){ + // do not dynamically scale the screen share feed. + } else if (session.dynamicScale){ + if (vid.dataset.UUID){ + + let targetWidth = wrw; + let targetHeight = hrh; + + targetWidth -= (borderOffset + videoMargin)*2; + targetHeight -= (borderOffset + videoMargin)*2 + + if (targetWidth<0){targetWidth=0;} + if (targetHeight<0){targetHeight=0;} + + if (session.devicePixelRatio){ + session.requestResolution(vid.dataset.UUID, targetWidth * session.devicePixelRatio, targetHeight * session.devicePixelRatio, true, false, cover); // snap=true; if resolution close to 100%, send 100%. screenshare only + } else if (window.devicePixelRatio && parseInt(window.devicePixelRatio) > 1 ){ + session.requestResolution(vid.dataset.UUID, targetWidth*window.devicePixelRatio, targetHeight*window.devicePixelRatio, true, false, cover); + } else { + session.requestResolution(vid.dataset.UUID, targetWidth, targetHeight, true, false, cover); + } + + } + } + + vid.style.borderColor = borderColor; + vid.style.borderWidth = borderOffset+"px"; + vid.style.borderRadius = borderRadius+"px"; + holder.style.borderColor = borderColor; + holder.style.borderWidth = "0px"; + holder.style.borderRadius = borderRadius+"px"; + + } else if ((vw && vh) || (vid.width && vid.height) || vid.dataset.aspectRatio){ + if (("rotated" in vid) && ((vid.rotated==90) || (vid.rotated==270))){ + if (vw && vh){ + var vvw = parseInt(vh); + var vvh = parseInt(vw); + } else if (vid.width && vid.height){ + var vvw = parseInt(vid.height); + var vvh = parseInt(vid.width); + } else { // video disabled; fall back to aspect Ratio + var vvw = 1; + var vvh = vid.dataset.aspectRatio; + } + + vid.style.objectFit = "cover"; //contain; + vid.style.overflow = "unset"; //contain; + //vid.style.maxWidth = "unset"; + //vid.style.maxHeight = "unset"; + } else { + if (vw && vh){ + var vvw = parseInt(vw); + var vvh = parseInt(vh); + } else if (vid.width && vid.height){ + var vvw = parseInt(vid.width); + var vvh = parseInt(vid.height); + } else { + var vvw = vid.dataset.aspectRatio; + var vvh =1; + } + } + + var asw = (wrw - videoMargin*2 - borderOffset*2)/vvw; // (window.innerWidth/ N) / vid.videoHeight; + var ash = (hrh - videoMargin*2 - borderOffset*2)/vvh; + + if (session.structure){ + // wrw x hrh + var arx = (wrw - videoMargin*2 - borderOffset*2)/(hrh - videoMargin*2 - borderOffset*2); + var tarx = arW/arH; + //var arW = 16.0; + //var arH = 9.0; + if (arx>tarx){ // width is too long + var hsw = hrh*tarx - videoMargin*2*tarx - borderOffset*2; + var hsl = (wrw - hsw) / 2; + var hst = videoMargin; + var hsh = (hrh - videoMargin*2 ); + } else { + var hsh = (wrw - videoMargin*2 + borderOffset*2)/tarx; + var hst = (hrh - hsh) / 2; + var hsl = videoMargin; + var hsw = (wrw - videoMargin*2); + } + } else if (asw > ash){ + var hsh = hrh - videoMargin*2; + var hst = videoMargin; + var hsw = (hsh - borderOffset)*(vvw/vvh); + var hsl = (wrw - hsw)/2; + } else { + var hsw = wrw - videoMargin*2; + var hsl = videoMargin; + var hsh = hsw/(vvw/vvh) + borderOffset; + var hst = (hrh - hsh)/2; + } + + + holder.style.left = Math.floor(hsl )+ "px"; // this needs to be replaced with padding. This means testing with rotation = 90 + holder.style.top = Math.floor(hst)+ "px"; + holder.style.width = Math.ceil(hsw) + 'px'; + holder.style.height = Math.ceil(hsh) + 'px'; + //holder.style.padding = videoMargin + "px"; + + holder.style.borderColor = borderColor; + holder.style.borderWidth = borderOffset+"px"; + holder.style.borderRadius = borderRadius+"px"; + vid.style.borderWidth = "0px"; + + + if (("rotated" in vid) && ((vid.rotated==90) || (vid.rotated==270))){ + vid.style.width = Math.ceil(wrw - borderOffset*2) + "px"; + vid.style.height = Math.ceil(hsw - borderOffset*2) + "px"; + vid.style.left = 0; + + vid.style.maxWidth = "100vh"; + vid.style.maxHeight = "100vw"; + + if (ChromiumVersion && ChromiumVersion<77){ + if (!animated && (parseInt(container.style.width)>parseInt(holder.style.height))){ + vid.style.position = "relative"; + vid.style.objectFit = "contain"; //contain; + } else if (animated && (container.twidth && (parseInt(container.twidth)>parseInt(holder.style.height)))){ + vid.style.position = "relative"; + vid.style.objectFit = "contain"; //contain; + } + } else { + vid.style.position = "relative"; + } + + } else if ((session.blurBackground!==false) && (vid.nodeName == "VIDEO") && !container.blurred && vid.srcObject && (( asw>1 && ash>1) || asw>=1 || ash>=1)){ + + vid.srcObject.getVideoTracks().forEach(trk=>{ + if (!container.blurred){ + container.blurred = document.createElement("video"); + container.blurred.controls = false; + container.blurred.style = "z-index:-10000;position:absolute;left:0;width:100%;height:100%;top:0;object-fit:fill;-webkit-filter: blur("+session.blurBackground+"px)"; + container.blurred.srcObject = createMediaStream(); + container.blurred.srcObject.addTrack(trk); + container.blurred.play(); + holder.appendChild(container.blurred); + } else { + if (container.blurred.paused){ + container.blurred.play(); + } + } + }); + } else if ((session.blurBackground!==false) && (vid.nodeName == "VIDEO") && vid.srcObject && (( asw>1 && ash>1) || asw>=1 || ash>=1)){ + if (container.blurred.paused){ + container.blurred.play(); + } + //container.blurred.style = "z-index:-10000;position:absolute;left:0;width:100%;height:100%;top:0;object-fit:fill;-webkit-filter: blur("+session.blurBackground+"px)"; + } else if (container.blurred){ + try { + container.blurred.remove(); + } catch(e){} + try{ + delete container.blurred; + } catch(e){} + } + + ////////// NON-COVER VERSION (based on holder) + if (session.sharperScreen && sssid && vid.dataset.sid && (vid.dataset.sid === sssid) ){ + // do not dynamically scale the screen share feed. + } else if (session.dynamicScale){ + if (vid.dataset.UUID){ + + let targetWidth = wrw; + let targetHeight = hrh; + + targetWidth -= (borderOffset + videoMargin)*2; + targetHeight -= (borderOffset + videoMargin)*2 + + if (targetWidth<0){targetWidth=0;} + if (targetHeight<0){targetHeight=0;} + + if (session.devicePixelRatio){ + session.requestResolution(vid.dataset.UUID, targetWidth * session.devicePixelRatio, targetHeight * session.devicePixelRatio, true, false, cover); // snap=true; if resolution close to 100%, send 100%. screenshare only + } else if (window.devicePixelRatio && parseInt(window.devicePixelRatio) > 1 ){ + session.requestResolution(vid.dataset.UUID, targetWidth*window.devicePixelRatio, targetHeight*window.devicePixelRatio, true, false, cover); + } else { + session.requestResolution(vid.dataset.UUID, targetWidth, targetHeight, true, false, cover); + } + + } + } + + + } else { + holder.style.left = (borderOffset + videoMargin) + "px"; + holder.style.top = (borderOffset + videoMargin) + "px"; + holder.style.height = "calc(100% - "+((borderOffset + videoMargin*2))+"px)"; + holder.style.width = "calc(100% - "+((borderOffset + videoMargin*2))+"px)"; + + ////////// UNKNOWN VERSION + if (session.sharperScreen && sssid && vid.dataset.sid && (vid.dataset.sid === sssid) ){ + // do not dynamically scale the screen share feed. + } else if (session.dynamicScale){ + if (vid.dataset.UUID){ + + let targetWidth = wrw; + let targetHeight = hrh; + + targetWidth -= (borderOffset + videoMargin)*2; + targetHeight -= (borderOffset + videoMargin)*2 + + if (targetWidth<0){targetWidth=0;} + if (targetHeight<0){targetHeight=0;} + + if (session.devicePixelRatio){ + session.requestResolution(vid.dataset.UUID, targetWidth * session.devicePixelRatio, targetHeight * session.devicePixelRatio, true, false, cover); // snap=true; if resolution close to 100%, send 100%. screenshare only + } else if (window.devicePixelRatio && parseInt(window.devicePixelRatio) > 1 ){ + session.requestResolution(vid.dataset.UUID, targetWidth*window.devicePixelRatio, targetHeight*window.devicePixelRatio, true, false, cover); + } else { + session.requestResolution(vid.dataset.UUID, targetWidth, targetHeight, true, false, cover); + } + + } + } + /////////////// + holder.style.borderColor = borderColor; + holder.style.borderWidth = borderOffset+"px"; + holder.style.borderRadius = borderRadius+"px"; + vid.style.borderWidth = "0px"; + + } + + + + if (session.colorVideosBackground){ + vid.style.backgroundColor = session.colorVideosBackground; + } else { + vid.style.backgroundColor = "unset"; + } + + + + if (vid.dataset.UUID && session.rpcs[vid.dataset.UUID] && ("label" in session.rpcs[vid.dataset.UUID]) && (session.rpcs[vid.dataset.UUID].label !== false) && (session.showlabels===true)){ // remote source + + if (animated && container.twidth && container.theight){ + var vidwidth = container.twidth; + var vidheight = container.theight; + } else { + var vidwidth = vid.offsetWidth; + var vidheight = vid.offsetHeight; + } + + var fontsize = (vidwidth + vidheight)*0.03; + if ((vidwidth/16)>=(vidheight/9)){ + var voar = (vidwidth/16)/(vidheight/9); + } else { + var voar = (vidheight/9)/(vidwidth/16); + } + voar = Math.pow(voar,0.5); + fontsize = fontsize/voar; + // creates a video label holder inside the recently created label holder + if (holder.label){ + var label = holder.label; + } else { + var label = document.createElement("span"); + holder.label = label; + if (session.labelstyle){ + label.className = 'video-label '+session.labelstyle; + } else { + label.className = 'video-label'; + } + holder.appendChild(label); + } + + if (fontsize){ + if (session.labelsize){ + fontsize = fontsize*session.labelsize/100; + } + label.style.fontSize = parseInt(fontsize)+"px"; + } + label.innerText = session.rpcs[vid.dataset.UUID].label; + if (label.innerText){ + //label.innerHTML = label.innerHTML.replace(/\\n/g,"
    "); + label.innerHTML = label.innerHTML.split("\\n").map(word => `${word}`).join(" "); + } + } else if ((session.showlabels===true) && (vid.id === "videosource") && (session.label)){ // local source + // creates a label holder that's the same size of the vid element. + + if (animated && container.twidth && container.theight){ + var vidwidth = container.twidth; + var vidheight = container.theight; + } else { + var vidwidth = vid.offsetWidth; + var vidheight = vid.offsetHeight; + } + + var fontsize = (vidwidth + vidheight)*0.03; + if ((vidwidth/16)>=(vidheight/9)){ + var voar = (vidwidth/16)/(vidheight/9); + } else { + var voar = (vidheight/9)/(vidwidth/16); + } + voar = Math.pow(voar,0.5); + fontsize = fontsize/voar; + + if (holder.label){ + var label = holder.label; + } else { + var label = document.createElement("span"); + holder.label = label; + if (session.labelstyle){ + label.className = 'video-label '+session.labelstyle; + } else { + label.className = 'video-label'; + } + holder.appendChild(label); + } + if (fontsize){ + if (session.labelsize){ + fontsize = fontsize*session.labelsize/100; + } + label.style.fontSize = parseInt(fontsize)+"px"; + } + + label.innerText = sanitizeLabel(session.label);//.replace(/[\W]+/g,"_").replace(/_+/g, ' '); + + if (label.innerText){ + label.innerHTML = label.innerHTML.split("\\n").map(word => `${word}`).join(" "); + } + + holder.appendChild(label); + } else if (holder.label){ + holder.label.remove(); + delete holder.label; + } + + if (vid.dataset.UUID && session.rpcs[vid.dataset.UUID]){ + if (session.rpcs[vid.dataset.UUID].voiceMeter){ + holder.appendChild(session.rpcs[vid.dataset.UUID].voiceMeter); + } + if (session.rpcs[vid.dataset.UUID].remoteMuteElement){ + holder.appendChild(session.rpcs[vid.dataset.UUID].remoteMuteElement); + } + + if (session.signalMeter){ + if (vid.dataset.UUID && !session.rpcs[vid.dataset.UUID].signalMeter){ + session.rpcs[vid.dataset.UUID].signalMeter = getById("signalMeterTemplate").cloneNode(true); + session.rpcs[vid.dataset.UUID].signalMeter.classList.remove("hidden"); + session.rpcs[vid.dataset.UUID].signalMeter.id = "signalMeter_" + vid.dataset.UUID; + session.rpcs[vid.dataset.UUID].signalMeter.dataset.level = 0; + session.rpcs[vid.dataset.UUID].signalMeter.title = getTranslation("signal-meter"); + holder.appendChild(session.rpcs[vid.dataset.UUID].signalMeter); + holder.signalMeter = session.rpcs[vid.dataset.UUID].signalMeter; + } else if (vid.dataset.UUID && session.rpcs[vid.dataset.UUID].signalMeter){ + if (!holder.signalMeter){ + holder.appendChild(session.rpcs[vid.dataset.UUID].signalMeter); + holder.signalMeter = session.rpcs[vid.dataset.UUID].signalMeter; + } + } + } + + if (session.batteryMeter){ + if (vid.dataset.UUID && !session.rpcs[vid.dataset.UUID].batteryMeter){ + session.rpcs[vid.dataset.UUID].batteryMeter = getById("batteryMeterTemplate").cloneNode(true); + session.rpcs[vid.dataset.UUID].batteryMeter.classList.remove("hidden"); + session.rpcs[vid.dataset.UUID].batteryMeter.id = "batteryMeter_" + vid.dataset.UUID; + session.rpcs[vid.dataset.UUID].batteryMeter.dataset.level = 0; + session.rpcs[vid.dataset.UUID].batteryMeter.title = getTranslation("battery-meter"); + holder.appendChild(session.rpcs[vid.dataset.UUID].batteryMeter); + holder.batteryMeter = session.rpcs[vid.dataset.UUID].batteryMeter; + } else if (vid.dataset.UUID && session.rpcs[vid.dataset.UUID].batteryMeter){ + if (!holder.batteryMeter){ + holder.appendChild(session.rpcs[vid.dataset.UUID].batteryMeter); + holder.batteryMeter = session.rpcs[vid.dataset.UUID].batteryMeter; + } + } + } + + if (session.volumeControl && session.rpcs[vid.dataset.UUID].videoElement && (vid.tagName != "VIDEO")){ + if (vid.dataset.UUID && !session.rpcs[vid.dataset.UUID].volumeControl){ + session.rpcs[vid.dataset.UUID].volumeControl = getById("volumeControlTemplate").cloneNode(true); + session.rpcs[vid.dataset.UUID].volumeControl.classList.remove("hidden"); + session.rpcs[vid.dataset.UUID].volumeControl.id = "volumeControl_" + vid.dataset.UUID; + session.rpcs[vid.dataset.UUID].volumeControl.value = parseInt(session.rpcs[vid.dataset.UUID].videoElement.volume*100); + session.rpcs[vid.dataset.UUID].volumeControl.dataset.UUID = vid.dataset.UUID; + session.rpcs[vid.dataset.UUID].volumeControl.title = getTranslation("volume-control"); + session.rpcs[vid.dataset.UUID].volumeControl.oninput = function(){ + if (this.dataset.UUID && session.rpcs[this.dataset.UUID] && session.rpcs[this.dataset.UUID].videoElement){ + session.rpcs[this.dataset.UUID].videoElement.volume = parseFloat(this.value/100); + } + } + holder.appendChild(session.rpcs[vid.dataset.UUID].volumeControl); + holder.volumeControl = session.rpcs[vid.dataset.UUID].volumeControl; + } else if (vid.dataset.UUID && session.rpcs[vid.dataset.UUID].volumeControl){ + if (!holder.volumeControl && session.rpcs[vid.dataset.UUID].videoElement){ + holder.appendChild(session.rpcs[vid.dataset.UUID].volumeControl); + holder.volumeControl = session.rpcs[vid.dataset.UUID].volumeControl; + session.rpcs[vid.dataset.UUID].volumeControl.value = parseInt(session.rpcs[vid.dataset.UUID].videoElement.volume*100); + } + } + } + + if (session.showConnections){ + if (!session.rpcs[vid.dataset.UUID].connectionDetails){ + createConnectionDetailsEle(vid.dataset.UUID); + } + holder.appendChild(session.rpcs[vid.dataset.UUID].connectionDetails); + } + + } + + + if (session.ruleOfThirds){ + if (vid.id == "videosource"){ + if (!holder.svg){ + var svg = document.createElement("img"); + svg.src = session.ruleOfThirds; + svg.style.width = "100%"; + svg.style.height = "100%"; + svg.style.position= "absolute"; + svg.style.left = "0"; + svg.style.top = "0"; + svg.style.pointerEvents= "none"; + holder.svg = svg; + holder.appendChild(svg); + } + } + } + + try { + if (!(session.cleanOutput && session.cleanish==false)){ + if (session.firstPlayTriggered===false){ // don't play unless needed; might cause clicking or who knows what else. + if (vid.tagName.toLowerCase()=="video"){ // we don't want to try playing an Iframe or Canvas. + if (vid.paused){ + warnlog("VIDEO IS NOT PLAYING"); + var playPromise = vid.play(); + if (playPromise !== undefined){ + playPromise.then(_ => { + // playing + //session.firstPlayTriggered=true; // global tracking. "user gesture obtained", so no longer needed if playing. + }).catch((err)=>{ + var bigPlayButton = document.getElementById("bigPlayButton"); + if (bigPlayButton){ + bigPlayButton.innerHTML = ''; + bigPlayButton.style.display="block"; + } + }); + } else { + session.firstPlayTriggered=true; // well, I don't know if it's playing, and so whatever. fail gracefully. + } + } + } + } + } + } catch(e) { + errorlog(e); + var bigPlayButton = document.getElementById("bigPlayButton"); + if (bigPlayButton){ + bigPlayButton.parentNode.removeChild(bigPlayButton); + } + + } + + if (vid.nodeName == "IFRAME"){ // I need to add this back in at some point. + i+=1; + return; + } + + if (!session.cleanOutput && !session.nocursor){ + if ((session.roomid!==false) && (session.scene===false)){ + if (!((vid.id === "videosource") && miniPreview) && !session.infocusForceMode){ + + if (!holder.button){ + var button = document.createElement("div"); + holder.button = button; + holder.appendChild(button); + } else { + var button = holder.button; + } + + button.id = "button_"+vid.id; + button.dataset.button = true; + if (soloVideo){ + button.innerHTML = ""; + button.title = "Show all active videos togethers"; + button.style.visibility = "visible"; + } else if ((mpl>1) || session.fullscreenButton){ // with session.fullscreenButton we hide the actuall full screen button, so this replaces it + button.innerHTML = ""; + button.title = "Enlarge video and increase its clarity"; + button.style.visibility = "visible"; + } else { + button.style.visibility = "hidden"; + } + button.classList.add("fullwindowButton"); + + if (vid.id == "videosource"){ + button.onclick = function(event){ + if (session.infocus === true){ + session.infocus = false; + } else { + session.infocus = true; + } + setTimeout(()=>updateMixer(),10); + + if (session.fullscreenButton){ + if (session.infocus){ + fullscreenPageToggle(true); + } else { + fullscreenPageToggle(false); + } + } + }; + } else { + button.dataset.UUID = vid.dataset.UUID; + button.onclick = function(event){ + var target = event.currentTarget; + if (session.infocus === target.dataset.UUID){ + //target.childNodes[0].className = 'las la-arrows-alt'; + session.infocus = false; + } else { + //target.childNodes[0].className = 'las la-compress'; + session.infocus = target.dataset.UUID; + //log("session:"+target.dataset.UUID); + } + if (session.fullscreenButton){ + if (session.infocus){ + fullscreenPageToggle(true); + } else { + fullscreenPageToggle(false); + } + } + setTimeout(()=>updateMixer(),10); + }; + + } + vid.onclick = function(event){ + if (session.disableMouseEvents){return;} + button.style.display="block"; + container.style.backgroundColor= "#4444"; + button.style.opacity="100%"; + }; + button.onmouseenter = function(event){ + if (session.disableMouseEvents){return;} + button.style.display="block"; + container.style.backgroundColor= "#4444"; + setTimeout(function(button){button.style.opacity="100%";},1,button); + + }; + button.onmousemove = function(event){ + if (session.disableMouseEvents){return;} + button.style.display="block"; + container.style.backgroundColor= "#4444"; + button.style.opacity="100%"; + + }; + container.onmousemove = function(event){ + if (session.disableMouseEvents){return;} + button.style.display="block"; + container.style.backgroundColor= "#4444"; + button.style.opacity="100%"; + }; + container.onmouseenter = function(event){ + if (session.disableMouseEvents){return;} + button.style.display="block"; + container.style.backgroundColor= "#4444"; + setTimeout(function(button){button.style.opacity="100%";},1,button); + }; + container.onmouseleave = function(event){ + if (session.disableMouseEvents){return;} + button.style.display="none"; + container.style.backgroundColor= null; + button.style.opacity="10%"; + }; + } else if ((vid.id === "videosource") && miniPreview && soloVideo==true && !session.infocusForceMode){ + + if (!holder.button){ + var button = document.createElement("div"); + holder.button = button; + holder.appendChild(button); + } else { + var button = holder.button; + } + + button.id = "button_videosource"; + button.dataset.button = true; + if (soloVideo){ + button.innerHTML = ""; + button.title = "Show all active videos togethers"; + button.style.display="unset"; + } else { + button.style.visibility = "hidden"; + button.style.display="none"; + } + button.classList.add("fullwindowButton"); + + button.onclick = function(event){ + event.stopPropagation(); + event.preventDefault(); + + if (session.infocus === true){ + session.infocus = false; + setTimeout(()=>updateMixer(),10); + } + + if (session.fullscreenButton){ + if (session.infocus){ + fullscreenPageToggle(true); + } else { + fullscreenPageToggle(false); + } + } + }; + + } else if (session.infocusForceMode && holder.button){ + try{ + holder.button.remove(); + } catch(e){ + errorlog(e); + } + } + } + } + i+=1; + } catch(err){errorlog(err);} + }); + updateUserList() +} + + +var translationBacklog = []; + +function miniTranslate(ele, ident = false, direct=false) { + + if (!translation){ + translation = {}; + } + + if (ident){ + if (translation.innerHTML && (ident in translation.innerHTML)){ + if (ele.querySelector('[data-translate]')){ + ele.querySelector('[data-translate]').innerHTML = translation.innerHTML[ident]; + ele.querySelector('[data-translate]').dataset.translate = ident; + } else { + ele.innerHTML = translation.innerHTML[ident]; + ele.dataset.translate = ident; + } + return; + } else if (direct){ + if (ele.querySelector('[data-translate]')){ + ele.querySelector('[data-translate]').innerHTML = direct; + ele.querySelector('[data-translate]').dataset.translate = ident; + } else { + ele.dataset.translate = ident; + ele.innerHTML = direct; + } + return; + } else { + warnlog(ident + ": not found in translation file"); + + if (!(ident in miscTranslations)){ + var value = ident.replaceAll("-", " "); // lets use the key as the translation + } else { + var value = miscTranslations[ident]; // lets use a miscellaneous translation as backup? + } + + if (ele.querySelector('[data-translate]')){ + ele.querySelector('[data-translate]').innerHTML = value; + ele.querySelector('[data-translate]').dataset.translate = ident; + } else { + ele.innerHTML = value; + ele.dataset.translate = ident; + } + return; + } + } + + var allItems = ele.querySelectorAll('[data-translate]'); + allItems.forEach(function(ele2) { + if (translation.innerHTML && (ele2.dataset.translate in translation.innerHTML)){ + ele2.innerHTML = translation.innerHTML[ele2.dataset.translate]; + } else if (translation.miscellaneous && (ele2.dataset.translate in translation.miscellaneous)){ + ele2.innerHTML = translation.miscellaneous[ele2.dataset.translate]; + } + }); + if (ele.dataset){ + if (translation.innerHTML && (ele.dataset.translate in translation.innerHTML)){ + ele.innerHTML = translation.innerHTML[ele.dataset.translate]; + } else if (translation.miscellaneous && (ele.dataset.translate in translation.miscellaneous)){ + ele.innerHTML = translation.miscellaneous[ele.dataset.translate]; + } + } + if (translation.titles){ + var allTitles = ele.querySelectorAll('[title]'); + allTitles.forEach(function(ele2) { + var key = ele2.title.replace(/[\W]+/g, "-").toLowerCase(); + if (key in translation.titles) { + ele2.title = translation.titles[key]; + } + }); + if (ele.title){ + var key = ele.title.replace(/[\W]+/g, "-").toLowerCase(); + if (key in translation.titles) { + ele.title = translation.titles[key]; + } + } + } + if (translation.placeholders){ + var allPlaceholders = ele.querySelectorAll('[placeholder]'); + allPlaceholders.forEach(function(ele2) { + var key = ele2.placeholder.replace(/[\W]+/g, "-").toLowerCase(); + if (key in translation.placeholders) { + ele2.placeholder = translation.placeholders[key]; + } + }); + + if (ele.placeholder){ + var key = ele.placeholder.replace(/[\W]+/g, "-").toLowerCase(); + if (key in translation.placeholders) { + ele.placeholder = translation.placeholders[key]; + } + } + } +} + +var controlBarTimeout = false; +function showControl(e){ + if (controlBarTimeout){ + clearTimeout(controlBarTimeout); + } + if (session.mobile){ + getById("controlButtons").classList.remove("partialFadeout"); + } else { + getById("controlButtons").classList.remove("fadeout"); + } + controlBarTimeout = setTimeout(function(){ + if (session.mobile){ + getById("controlButtons").classList.add("partialFadeout"); + } else { + getById("controlButtons").classList.add("fadeout"); + } + }, 5000); +} + +async function changeLg(lang) { + log("changeLg: "+lang); + await fetchWithTimeout("./translations/" + lang + '.json',2000).then(async function(response) { + try{ + if (response.status !== 200) { + getById("mainmenu").style.opacity = 1; + return; + } + await response.json().then(async function(data) { + translation = data; // translation.innerHTML[ele.dataset.translate] + + if (data.miscellaneous){ + Object.keys(data.miscellaneous).forEach(key => { + miscTranslations[key] = data.miscellaneous[key]; + }); + } + data.miscellaneous = miscTranslations; + + var trans = data.innerHTML; + var allItems = document.querySelectorAll('[data-translate]'); + allItems.forEach(function(ele) { + if (ele.dataset.translate in trans) { + ele.innerHTML = trans[ele.dataset.translate]; + } else if (data.miscellaneous && (ele.dataset.translate in data.miscellaneous)){ + ele.innerHTML = data.miscellaneous[ele.dataset.translate]; // use the misc translation if no main one is found + } + }); + + trans = data.titles; + var allTitles = document.querySelectorAll('[title]'); + allTitles.forEach(function(ele) { + if (ele.dataset.key){ + if (ele.dataset.key in trans) { + ele.title = trans[ele.dataset.key]; + } + } else { + var key = ele.title.replace(/[\W]+/g, "-").toLowerCase(); + ele.dataset.key = key; + if (key in trans) { + ele.title = trans[key]; + } + } + }); + + trans = data.placeholders; + var allPlaceholders = document.querySelectorAll('[placeholder]'); + allPlaceholders.forEach(function(ele) { + var key = ele.placeholder.replace(/[\W]+/g, "-").toLowerCase(); + if (key in trans) { + ele.placeholder = trans[key]; + } + }); + + + if (translationBacklog.length){ + for (var i=0;i 1 && arr[1] !== '') { + window.location += "&room=" + roomname + passStr + "&host"; + } else { + window.location += "?room=" + roomname + passStr + "&host"; + } + } else { + getById("videoname1").focus(); + getById("videoname1").classList.remove("shake"); + setTimeout(function(){getById("videoname1").classList.add("shake");},10); + } +} + + +async function jumptoroom(event = null) { + + if (event) { + if (event.which !== 13) { + return; + } + } + + var arr = window.location.href.split('?'); + var roomname = getById("joinroomID").value; + roomname = sanitizeRoomName(roomname); + if (roomname.length) { + + var passStr = ""; + window.focus(); + var pass = await promptAlt("Enter a password if provided, otherwise just click Cancel", false, true); //sanitizePassword(session.password); + if (pass && pass.length) { + session.password = sanitizePassword(pass); + passStr = "&password=" + session.password; + } else { + session.password = false; + } + + if (arr.length > 1 && arr[1] !== '') { + window.location += "&room=" + roomname + passStr; + } else { + window.location += "?room=" + roomname + passStr; + } + } else { + getById("joinroomID").focus(); + getById("joinroomID").classList.remove("shake"); + setTimeout(function(){getById("joinroomID").classList.add("shake");},10); + } +} + + +async function jumptoURL(event = null) { // this is for the native app + + var url = getById('joinbyURL').value; + if (url.length) { + + if (url.startsWith('?')){url='./'+url}; + + if (url.startsWith('&')){url='./?'+url}; + + if (!url.startsWith('http') && !url.startsWith('.')){url='./?'+url}; + + setStorage("jumptoURL", url, 1008); // should be really only used by the native app; 6 months + + window.location = url; + } else { + getById("joinbyURL").focus(); + getById("joinbyURL").classList.remove("shake"); + setTimeout(function(){getById("joinbyURL").classList.add("shake");},10); + } +} + +function sleep(ms = 0) { + return new Promise(r => setTimeout(r, ms)); // LOLz! +} + + +async function changeAvatarImage(ev, ele, set=false){ + log("changeAvatarImage() triggered"); + + if (session.avatar && session.avatar.timer){ + clearInterval(session.avatar.timer); + session.avatar.timer=null; + } + + if (!session.streamSrc){ + checkBasicStreamsExist(); + } + + if (ele.files && ele.files.length) { + session.avatar = document.querySelector('img'); + session.avatar.ready=false; + session.avatar.onload = () => { + URL.revokeObjectURL(session.avatar.src); // no longer needed, free memory + session.avatar.ready=true; + getById("noAvatarSelected3").classList.remove("selected"); + getById("noAvatarSelected").classList.remove("selected"); + getById("defaultAvatar1").classList.add("selected"); + getById("defaultAvatar2").classList.add("selected"); + + var tracks = session.streamSrc.getVideoTracks(); + if (!tracks.length || session.videoMuted){ + updateRenderOutpipe(); + } + + } + + session.avatar.src = URL.createObjectURL(ele.files[0]); // set src to blob url + return; + } else if (ele.tagName.toLowerCase() == "img"){ + session.avatar = ele + session.avatar.ready=true; + getById("noAvatarSelected3").classList.remove("selected"); + getById("noAvatarSelected").classList.remove("selected"); + getById("defaultAvatar1").classList.add("selected"); + getById("defaultAvatar2").classList.add("selected"); + var tracks = session.streamSrc.getVideoTracks(); + if (!tracks.length || session.videoMuted){ + updateRenderOutpipe(); + } + } else { + session.avatar = false; + var tracks = session.streamSrc.getVideoTracks(); + if (!tracks.length || session.videoMuted){ + var msg = {}; + msg.videoMuted = true; + session.sendMessage(msg); + if (document.getElementById("videosource")){ + document.getElementById("videosource").load(); + } else if (document.getElementById('previewWebcam')) { + document.getElementById("previewWebcam").load(); + } + updateRenderOutpipe(); + } + + getById("noAvatarSelected3").classList.add("selected"); + getById("noAvatarSelected").classList.add("selected"); + getById("defaultAvatar1").classList.remove("selected"); + getById("defaultAvatar2").classList.remove("selected"); + return; + } + + +} + +session.autoSyncCallback = function(UUID=null){ // session.autoSyncObject has been updated. You can overwrite this with your own function + log(session.autoSyncObject); + pokeIframeAPI('auto-sync-updated', session.autoSyncObject, UUID); +} + +session.autoSync = function(alternative=null){ // Update session.autoSyncObject and then run session.autoSync() + var msg = {}; + if (alternative===null){ + msg.autoSync = session.autoSyncObject; + } else { + session.autoSyncObject = alternative; + msg.autoSync = session.autoSyncObject; + } + session.sendPeers(msg); +} + +function setupSharpnessTool(){ + var promise; + const worker = new Worker('./thirdparty/focus_worker.js', { type: 'module' }); + worker.onerror = (event) => { + errorlog(event); + promise.reject(event); + }; + worker.onmessage = (messageEvent) => { + log("Sharpness score: "+messageEvent.data.score.avg_edge_width_perc); + promise.resolve(messageEvent.data.score.avg_edge_width_perc); + }; + + measureBlur = (imageData) => { + worker.postMessage({ imageData }); + }; + + const canvas = document.createElement("canvas"); + // document.getElementById("header").appendChild(canvas); + + async function getSharpness(x=50,y=50){ + if (session.videoElement){ + log("XY"); + log(x + " : " +y); + canvas.width = session.videoElement.videoWidth/5; + canvas.height = session.videoElement.videoHeight/5; + + if (x<10){ + x=10; + } + if (y<10){ + y=10; + } + if (x>90){ + x=90; + } + if (y>90){ + y=90; + } + + var sx = session.videoElement.videoWidth/100*(x-10); + var sy = session.videoElement.videoHeight/100*(y-10); + var sw = session.videoElement.videoWidth*0.20; + var sh = session.videoElement.videoHeight*0.20 + + canvas.getContext('2d').filter = 'blur(3px)'; // denoise + canvas.getContext('2d').drawImage(session.videoElement, sx, sy, sw, sh, 0, 0, canvas.width, canvas.height); // for drawing the video element on the canvas + + const canvasData = canvas.getContext('2d').getImageData( + 0, + 0, + canvas.width, + canvas.height + ); + + var res, rej; + promise = new Promise((resolve, reject) => { + res = resolve; + rej = reject; + }); + promise.resolve = res; + promise.reject = rej; + + measureBlur(canvasData); + + return promise; + } + return null; + } + + return getSharpness; +} +var sharpnessToolActive = false; +var sharpnessTool = false; +async function tapToFocus(x,y, force=false){ + + if (isNaN(x) || isNaN(y)){return;} + + if (sharpnessToolActive){return;} + + if (!session.streamSrc){ + checkBasicStreamsExist(); + return; + } + + //var bestFocus = -1; + var track0 = session.streamSrc.getVideoTracks(); + if (!track0.length){ + log("No video tracks"); + return; + } + track0 = track0[0]; + if (!track0.getCapabilities){ + log("Track lacks advanced features. Firefox?"); + return; + } + + var capabilities = track0.getCapabilities(); + if (!("focusDistance" in capabilities)){ + log("Track doesn't support focusing"); + return; + } + + + var settings = track0.getSettings(); + if ("focusMode" in settings){ + if (!force && (settings.focusMode !== "manual")){ + log("Need to be in manual focus mode"); + return; + } + } + + if (!sharpnessTool){ + sharpnessTool = setupSharpnessTool(); + } + + var bestFocus = -1; + var bestSharpness = 999; + sharpnessToolActive = true; + + try { + log("Current focus distance: "+capabilities.focusDistance); + await track0.applyConstraints({advanced: [ {focusMode: "manual", focusDistance: capabilities.focusDistance.min} ]}); + await sleep(250); + + var stepping = capabilities.focusDistance.step || 0.1; + + if ((capabilities.focusDistance.max - capabilities.focusDistance.min)/stepping>100){ + stepping = parseInt((capabilities.focusDistance.max - capabilities.focusDistance.min)/100); + } + if (!stepping){ + stepping = 0.1; + } + for (var i = capabilities.focusDistance.min; i <= capabilities.focusDistance.max; i+= stepping){ + + await track0.applyConstraints({advanced: [ {focusMode: "manual", focusDistance: i} ]}); + await sleep(120); // wait long enough for a new frame and focus to adjust. + log("focus: " + i + ", " + x +"x"+y); + var response = await sharpnessTool(x,y); + if (response && (responsecapabilities.zoom.max){ + session.zoom = capabilities.zoom.max; + } else if (session.zoomcapabilities.focusDistance.max){ + session.focusDistance = capabilities.focusDistance.max; + } else if (session.focusDistancemaxH){ + width = parseInt(maxH/session.avatar.naturalHeight * session.avatar.naturalWidth); + height = maxH; + + if (width>maxW){ + width = maxW; + height = parseInt(maxW/width * height); + } + + } else if (session.avatar.naturalWidth && session.avatar.naturalWidth>maxW){ + width = maxW; + height = parseInt(maxW/session.avatar.naturalWidth*session.avatar.naturalHeight); + } else { + width = session.avatar.naturalWidth; + height = session.avatar.naturalHeight; + } + + session.canvasSource.width = width; + session.canvasSource.height = height; + + session.canvas.height = 2 * parseInt(height / 2); + session.canvas.width = 2 * parseInt(width / 2); + + session.canvasCtx.drawImage(session.avatar, 0, 0, session.canvas.width, session.canvas.height); + + session.avatar.timer = setInterval(function(){ + log("drawing"); + session.canvasCtx.drawImage(session.avatar, 0, 0, session.canvas.width, session.canvas.height); + },200); // too slow and it takes way too long for the video to udpate when a new guest joins + + applyMirror(true); + + session.avatar.tracks = session.canvas.captureStream().getVideoTracks(); + return session.avatar.tracks; + } + applyMirror(session.mirrorExclude); + return tracks; +} + +var drawOnScreenObject = null; +function drawOnScreen(){ + var canvas = document.getElementById("drawOnSCreen"); + if (!canvas){ + canvas = document.createElement("canvas"); + getById("gridlayout").appendChild(canvas); + getById("gridlayout").style.position = "relative"; + } else { + return; + } + + var ctx = canvas.getContext('2d'); + canvas.width = parseInt(getById("gridlayout").clientWidth/2); + canvas.height = parseInt(getById("gridlayout").clientHeight/2); + canvas.style.width = "100%"; + canvas.style.height = "100%"; + canvas.style.display = "block"; + canvas.style.position = "absolute"; + canvas.style.bottom = "0"; + canvas.style.left = "0"; + + + var flag = false, + prevX = 0, + currX = 0, + prevY = 0, + currY = 0, + dot_flag = false; + + var x = "black", + y = 2; + + var object = {}; + + object.stop = function stop() { + canvas.removeEventListener("mousemove", function (e) { + findxy('move', e) + }, false); + canvas.removeEventListener("mousedown", function (e) { + findxy('down', e) + }, false); + canvas.removeEventListener("mouseup", function (e) { + findxy('up', e) + }, false); + canvas.removeEventListener("mouseout", function (e) { + findxy('out', e) + }, false); + canvas.remove(); + + getById("startDrawScreen").classList.remove("hidden"); + + document.querySelectorAll(".drawActive").forEach(ele=>{ + ele.classList.add("hidden"); + }); + + delete canvas; + drawOnScreenObject = null; + object = null; + + } + + object.init = function init() { + //console.log("init"); + canvas.addEventListener("mousemove", function (e) { + findxy('move', e) + }, false); + canvas.addEventListener("mousedown", function (e) { + findxy('down', e) + }, false); + canvas.addEventListener("mouseup", function (e) { + findxy('up', e) + }, false); + canvas.addEventListener("mouseout", function (e) { + findxy('out', e) + }, false); + + getById("startDrawScreen").classList.add("hidden"); + + document.querySelectorAll(".drawActive").forEach(ele=>{ + ele.classList.remove("hidden"); + }); + } + + object.color = function color(obj) { + switch (obj.dataset.color) { + case "green": + x = "green"; + break; + case "blue": + x = "blue"; + break; + case "red": + x = "red"; + break; + case "yellow": + x = "yellow"; + break; + case "orange": + x = "orange"; + break; + case "black": + x = "black"; + break; + case "white": + x = "white"; + break; + } + if (x == "white") y = 14; + else y = 2; + + } + + function draw() { + //console.log('draw',prevX,currX); + ctx.beginPath(); + + var mx = canvas.width/ parseInt(getById("gridlayout").clientWidth); + var my = canvas.height/parseInt(getById("gridlayout").clientHeight); + var mo = parseInt(getById("header").clientHeight); + + ctx.moveTo(prevX*mx, prevY*my - mo*my); + ctx.lineTo(currX*mx, currY*my - mo*my); + ctx.strokeStyle = x; + ctx.lineWidth = y; + ctx.stroke(); + ctx.closePath(); + } + + object.erase = function erase() { + //var m = confirm("Want to clear"); + // if (m) { + ctx.clearRect(0, 0, canvas.width, canvas.height); + //document.getElementById("canvasimg").style.display = "none"; + // } + } + + object.save = function save() { + //document.getElementById("canvasimg").style.border = "2px solid"; + var dataURL = canvas.toDataURL(); + // document.getElementById("canvasimg").src = dataURL; + // document.getElementById("canvasimg").style.display = "inline"; + } + + function findxy(res, e) { + //console.log(res,e); + if (res == 'down') { + prevX = currX; + prevY = currY; + currX = e.clientX - canvas.offsetLeft; + currY = e.clientY - canvas.offsetTop; + + flag = true; + dot_flag = true; + if (dot_flag) { + ctx.beginPath(); + ctx.fillStyle = x; + ctx.fillRect(currX, currY, 2, 2); + ctx.closePath(); + dot_flag = false; + } + } + if (res == 'up' || res == "out") { + flag = false; + } + if (res == 'move') { + if (flag) { + prevX = currX; + prevY = currY; + currX = e.clientX - canvas.offsetLeft; + currY = e.clientY - canvas.offsetTop; + draw(); + } + } + } + object.init(); + drawOnScreenObject = object; + return object; +} +////////// Canvas Effects /////////////// + +function drawFrameMirrored(mirror=true, flip=false) { + session.canvasCtx.save(); + if (flip){ + if (mirror){ + session.canvasCtx.scale(-1, -1); + session.canvasCtx.drawImage(session.canvasSource, 0, 0, session.canvas.width * -1, session.canvas.height* -1); + } else { + session.canvasCtx.scale(1, -1); + session.canvasCtx.drawImage(session.canvasSource, 0, 0, session.canvas.width, session.canvas.height* -1); + } + } else if (mirror){ + session.canvasCtx.scale(-1, 1); + session.canvasCtx.drawImage(session.canvasSource, 0, 0, session.canvas.width * -1, session.canvas.height); + } else { + session.canvasCtx.drawImage(session.canvasSource, 0, 0, session.canvas.width, session.canvas.height); + } + session.canvasCtx.restore(); +} + +function motionDetection(video, threshold = 15, sensitivity=75){ + var targetSize = 16; + + if (!video.motionDetector){ + video.motionDetector = {}; + video.motionDetector.canvas = document.createElement("canvas"); + video.motionDetector.canvas.width = targetSize; + video.motionDetector.canvas.height = targetSize; + try { + video.motionDetector.ctx = video.motionDetector.canvas.getContext("2d", { willReadFrequently: true }); + } catch(e){ + video.motionDetector.ctx = video.motionDetector.canvas.getContext("2d"); + } + video.motionDetector.previous = []; + for (var y = 0; y < targetSize; y++) { + for (var x = 0; x < targetSize; x++) { + video.motionDetector.previous.push(0); + } + } + } + var motionDetector = video.motionDetector; + + motionDetector.ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight, 0, 0, targetSize, targetSize); + var data = motionDetector.ctx.getImageData(0, 0, targetSize, targetSize).data; + var matches = 0; + for (var y = 0; y < targetSize; y++) { + for (var x = 0; x < targetSize; x++) { + var pos = y*targetSize+x; + var pos2 = pos*3; + var value = data[pos2] + data[pos2+1] + data[pos2+2]; // convert to to greyscale + if (motionDetector.previous[pos] && Math.abs(motionDetector.previous[pos] - value) > sensitivity) { + matches+=1; + } + motionDetector.previous[pos] = value; + } + } + if (matches >= threshold){ + log("MOTION DETECTED: "+matches); + if (window.obsstudio && window.obsstudio["setCurrentScene"]){ + if (!changeSceneEnabled){ // the bit cut scene change is already active. + if (session.obsState && session.obsState.details && session.obsState.details.thisScene && session.obsState.details.currentScene){ + if (session.obsState.details.thisScene !== session.obsState.details.currentScene.name){ // don't trigger it multiple times; makes it hard to prep next scene + window.obsstudio["setCurrentScene"](session.obsState.details.thisScene); + } + } + } + } + pokeIframeAPI('motion-detected', true, video.dataset.UUID || true); + + if (session.infocus!==(video.dataset.UUID || true)){ + if (!session.layout){ + session.infocus = video.dataset.UUID || true; + updateMixer(); + } + } + } +} + +function setupCanvas() { + log("SETUP CANVAS"); + if (session.canvas === null) { + session.canvas = document.createElement("canvas"); + session.canvas.width = 512; + session.canvas.height = 288; + try { + session.canvasCtx = session.canvas.getContext('2d', {alpha: true, willReadFrequently: true}); + } catch(e){ + errorlog(e); + session.canvasCtx = session.canvas.getContext('2d'); + } + + session.canvasCtx.fillStyle = "blue"; + session.canvasCtx.fillRect(0, 0, 512, 288); + session.canvasSource = createVideoElement(); + session.canvasSource.autoplay = true; + session.canvasSource.srcObject = createMediaStream(); + session.canvasSource.id = "effectsVideoSource"; + + if (session.canvasSource.srcObject.getVideoTracks().length){ + session.canvasSource.width = session.canvasSource.srcObject.getVideoTracks()[0].getSettings().width || 1280; + session.canvasSource.height = session.canvasSource.srcObject.getVideoTracks()[0].getSettings().height || 720; + } + + if (iOS || iPad){ + session.canvasSource.style.position = "absolute"; + session.canvasSource.style.left = "0"; + session.canvasSource.style.top ="0"; + session.canvasSource.controls = session.showControls || false; + session.canvasSource.style.maxWidth = "1px"; + session.canvasSource.style.maxHeight = "1px"; + session.canvasSource.setAttribute("playsinline",""); + document.body.appendChild(session.canvasSource); + //session.canvasSource.play(); + } + } else { + session.canvasSource.srcObject.getVideoTracks().forEach(function(trk) { + session.canvasSource.srcObject.removeTrack(trk); + }); + } +} + +function applyEffects(track) { // video only please. do not touch audio. Run update Render Outpipe () instead of this directly. + log("applyEffects()"); + + if (session.effect == "0" || !session.effect) { // auto align face + return track; + } else if (session.effect == "1") { // auto align face + setupCanvas(); + session.canvasSource.srcObject.addTrack(track); + + session.canvasSource.width = track.getSettings().width || 1280; + session.canvasSource.height = track.getSettings().height || 720; + + session.canvas.height = 2 * parseInt(session.canvasSource.height / 2); + session.canvas.width = 2 * parseInt(session.canvasSource.width / 2); + + drawFace(); + } else if (session.effect == "7") { // manual zoom + setupCanvas(); + session.canvasSource.srcObject.addTrack(track); + + session.canvasSource.width = track.getSettings().width || 1280; + session.canvasSource.height = track.getSettings().height || 720; + session.canvas.height = 2 * parseInt(session.canvasSource.height / 2); + session.canvas.width = 2 * parseInt(session.canvasSource.width / 2); + + digitalZoom(); + } else if (session.effect == "8") { // manual zoom + setupCanvas(); + session.canvasSource.srcObject.addTrack(track); + + session.canvasSource.width = track.getSettings().width || 1280; + session.canvasSource.height = track.getSettings().height || 720; + session.canvas.height = 2 * parseInt(session.canvasSource.height / 2); + session.canvas.width = 2 * parseInt(session.canvasSource.width / 2); + + simpleDraw(); + } else if (session.effect == "2") { // mirror video at a canvas level + setupCanvas(); + session.canvasSource.srcObject.addTrack(track); + + session.canvasSource.width = track.getSettings().width || 1280; + session.canvasSource.height = track.getSettings().height || 720; + session.canvas.height = 2 * parseInt(session.canvasSource.height / 2); + session.canvas.width = 2 * parseInt(session.canvasSource.width / 2); + + var drawRate = parseInt(1000 / track.getSettings().frameRate) + 1; + if (session.canvasInterval !== null) { + clearInterval(session.canvasInterval); + } + session.canvasInterval = setInterval(function() { + drawFrameMirrored(true, false); + }, drawRate); + } else if (session.effect == "-1") { // mirror and flip video at a canvas level + setupCanvas(); + session.canvasSource.srcObject.addTrack(track); + + session.canvasSource.width = track.getSettings().width || 1280; + session.canvasSource.height = track.getSettings().height || 720; + session.canvas.height = 2 * parseInt(session.canvasSource.height / 2); + session.canvas.width = 2 * parseInt(session.canvasSource.width / 2); + + var drawRate = parseInt(1000 / track.getSettings().frameRate) + 1; + if (session.canvasInterval !== null) { + clearInterval(session.canvasInterval); + } + session.canvasInterval = setInterval(function() { + drawFrameMirrored(false, true); + }, drawRate); + } else if (session.effect == "-2") { // mirror and flip video at a canvas level + setupCanvas(); + session.canvasSource.srcObject.addTrack(track); + + session.canvasSource.width = track.getSettings().width || 1280; + session.canvasSource.height = track.getSettings().height || 720; + session.canvas.height = 2 * parseInt(session.canvasSource.height / 2); + session.canvas.width = 2 * parseInt(session.canvasSource.width / 2); + + var drawRate = parseInt(1000 / track.getSettings().frameRate) + 1; + if (session.canvasInterval !== null) { + clearInterval(session.canvasInterval); + } + session.canvasInterval = setInterval(function() { + drawFrameMirrored(true, true); + }, drawRate); + } else if ((session.effect == "3") || (session.effect == "4") || (session.effect == "5")){ // blur & greenscreen (low and high) + setupCanvas(); + session.canvasSource.srcObject.addTrack(track); + + session.canvasSource.width = track.getSettings().width || 1280; + session.canvasSource.height = track.getSettings().height || 720; + session.canvas.height = 2 * parseInt(session.canvasSource.height / 2); + session.canvas.width = 2 * parseInt(session.canvasSource.width / 2); + + TFLiteWorker(); + } else if (session.effect == "6"){ + setupCanvas(); + session.canvasSource.srcObject.addTrack(track); + + session.canvasSource.width = track.getSettings().width || 1280; + session.canvasSource.height = track.getSettings().height || 720; + session.canvas.height = 2 * parseInt(session.canvasSource.height / 2); + session.canvas.width = 2 * parseInt(session.canvasSource.width / 2); + + if (session.canvasSource.readyState >= 3){ + mainMeshMask(); + } else { + session.canvasSource.onloadeddata = mainMeshMask; + } + } else if (session.effect == "13"){ // ha, no way to turn it off once it's started, except to change cameras? not sure. + try { + track.applyConstraints({backgroundBlur: true}).then(() => { + const settings = track.getSettings(); + log(`Background blur is ${settings.backgroundBlur ? "ON" : "OFF"}`); + }).catch(errorlog); + } catch(e){ + errorlog(e); + } + return track; + } else { + if (session.canvasource){ + session.canvasSource.srcObject.getVideoTracks().forEach(function(trk) { + session.canvasSource.srcObject.removeTrack(trk); + }); + } else { + session.canvasSource = createVideoElement(); + session.canvasSource.srcObject = createMediaStream(); + } + + session.canvasSource.autoplay = true; + session.canvasSource.id = "effectsVideoSource"; + session.canvasSource.srcObject.addTrack(track); + session.canvasSource.width = track.getSettings().width || 1280; + session.canvasSource.height = track.getSettings().height || 720; + session.canvas.width = 512; + session.canvas.height = 288; + + if (iOS || iPad){ + session.canvasSource.style.position = "absolute"; + session.canvasSource.style.left = "0"; + session.canvasSource.style.top = "0"; + session.canvasSource.style.maxWidth = "1px"; + session.canvasSource.style.maxHeight = "1px"; + session.canvasSource.controls = session.showControls || false; + + session.canvasSource.setAttribute("playsinline",""); + document.body.appendChild(session.canvasSource); + //session.canvasSource.play(); + } + + try { + JEELIZFACEFILTER.destroy(); + } catch(e){} + if (session.canvasWebGL){ + session.canvasWebGL.remove() + session.canvasWebGL=null; + } + session.canvasWebGL = document.createElement("canvas"); + session.canvasWebGL.width = track.getSettings().width || 1280; + session.canvasWebGL.height = track.getSettings().height || 720; + session.canvasWebGL.id = "effectsCanvasTarget"; + session.canvasWebGL.style.position="fixed"; + session.canvasWebGL.style.top= "-9999px"; + session.canvasWebGL.style.left= "-9999px"; + + document.body.appendChild(session.canvasWebGL); + loadEffect(session.effect); + return session.canvasWebGL.captureStream().getVideoTracks()[0]; + } + try { + return session.canvas.captureStream().getVideoTracks()[0]; + } catch(e){ + if (!session.cleanOutput){ + warnUser(getTranslation("not-clean-session"), false, false); + } + return track; + } +} + +function dataURItoArraybuffer(dataURI) { + var byteString = atob(dataURI.split(',')[1]); + var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0] + var ab = new ArrayBuffer(byteString.length); + var ia = new Uint8Array(ab); + for (var i = 0; i < byteString.length; i++) { + ia[i] = byteString.charCodeAt(i); + } + return ab; +} + + +var makeImagesActive = null; +async function makeImages(startup=false){ + if (!session.webp){return;} + else if (makeImagesActive===true){return;} + else if (!session.videoElement){return;} + else if (session.videoMuted){return;} + else if (session.videoElement && session.videoElement.srcObject){ + // + } else if (session.videoElement && session.videoElement.src){ + // + } else { + errorlog("No video element; can't make images for webp mode"); + return; + } + + if (makeImagesActive===null){ + makeImagesActive=true; + session.webPcanvas = document.createElement("canvas"); + session.webPcanvas.makeImagesTimeout = null; + session.webPcanvas.nowTime = new Date().getTime(); + + var width = 480; + var height = 270; + var timeout = 100; // the answer to everything. + var quality = 0.66; + + if (session.webPquality===0){ + width = 1920; + height = 1080; + timeout = 33; + } else if (session.webPquality===1){ + width = 1280; + height = 720; + timeout = 33; + } else if (session.webPquality===2){ + width = 960; + height = 540; + timeout = 33; + } else if (session.webPquality===3){ + width = 853; + height = 480; + timeout = 33; + } else if (session.webPquality===4){ + width = 640; + height = 360; + timeout = 33; + } else if (session.webPquality===5){ + width = 480; + height = 270; + timeout = 33; + } else if (session.webPquality===6){ + width = 480; + height = 270; + timeout = 67; + } else if (session.webPquality===7){ + width = 480; + height = 270; + timeout = 200; + } else if (session.webPquality===8){ + width = 480; + height = 270; + timeout = 400; + } else if (session.webPquality===9){ + width = 640; + height = 360; + timeout = 1000; + } + + session.webPcanvas.timeout = timeout; + session.webPcanvas.quality = quality; + + const video = session.videoElement; + + if (video.videoWidth < width){ + session.webPcanvas.width = video.videoWidth; + } else { + session.webPcanvas.width = width; + } + + if (video.videoHeight < height){ + session.webPcanvas.height = video.videoHeight; + } else { + session.webPcanvas.height = height; + } + + var ar = session.webPcanvas.width/session.webPcanvas.height; + + if (session.forceAspectRatio && session.forceAspectRatio=ar){ + height = width*session.forceAspectRatio; + } + + session.webPcanvasCtx = session.webPcanvas.getContext('2d', {alpha: false}); + session.webPcanvasCtx.fillStyle = "black"; + session.webPcanvasCtx.fillRect(0, 0, width, height); + } else { + clearTimeout(session.webPcanvas.makeImagesTimeout); + makeImagesActive=true; + } + if (session.videoElement && session.videoElement.srcObject.getVideoTracks().length===0){ + makeImagesActive=false; + + var exit = true; + for (var i in session.pcs){ + if (session.pcs[i].allowWebp){ // just for safety, to avoid a race condition, double check that it's still not active. + exit = false; + } + } + if (exit){ + makeImagesActive=false; + return; + } + + session.webPcanvas.makeImagesTimeout = setTimeout(function(){makeImages();},timeout*3); + return; + } + + if (startup){ + var exit = true; + for (var i in session.pcs){ + if (session.pcs[i].allowWebp){ // just for safety, to avoid a race condition, double check that it's still not active. + exit = false; + } + } + if (exit){ + makeImagesActive=false; + return; + } + log("MAKE IMAGES STARTING?"); + } + + try{ + var broadcasting = false; + var arrayBuffer = false; + + + var width = 480; + var height = 270; + + if (session.webPquality===0){ + width = 1920; + height = 1080; + timeout = 33; + } else if (session.webPquality===1){ + width = 1280; + height = 720; + timeout = 33; + } else if (session.webPquality===2){ + width = 960; + height = 540; + timeout = 33; + } else if (session.webPquality===3){ + width = 853; + height = 480; + timeout = 33; + } else if (session.webPquality===4){ + width = 640; + height = 360; + timeout = 33; + } else if (session.webPquality===5){ + width = 480; + height = 270; + timeout = 33; + } else if (session.webPquality===6){ + width = 480; + height = 270; + timeout = 67; + } else if (session.webPquality===7){ + width = 480; + height = 270; + timeout = 200; + } else if (session.webPquality===8){ + width = 480; + height = 270; + timeout = 400; + } else if (session.webPquality===9){ + width = 640; + height = 360; + timeout = 1000; + } + + const video = session.videoElement; + + if (video.videoWidth < width){ + session.webPcanvas.width = video.videoWidth; + session.webPcanvasCtx.width = video.videoWidth; + } else { + session.webPcanvas.width = width; + session.webPcanvasCtx.width = width; + } + + if (video.videoHeight < height){ + session.webPcanvas.height = video.videoHeight; + session.webPcanvasCtx.height = video.videoHeight; + } else { + session.webPcanvas.height = height; + session.webPcanvasCtx.height = height; + } + + var ar = session.webPcanvas.width/session.webPcanvas.height; + + if (session.forceAspectRatio && session.forceAspectRatio>ar){ + session.webPcanvas.width = session.webPcanvas.height*session.forceAspectRatio; + } else if (session.forceAspectRatio && session.forceAspectRatio<=ar){ + session.webPcanvas.height = session.webPcanvas.width*session.forceAspectRatio; + } + + for (var i in session.pcs){ + try{ + if (session.pcs[i].allowWebp){ // only publish to those seeking this stream + broadcasting = true; + if (!session.pcs[i].sendChannel.bufferedAmount){ + if (!arrayBuffer){ + session.webPcanvasCtx.drawImage(session.videoElement, 0, 0, session.webPcanvas.width, session.webPcanvas.height); + arrayBuffer = dataURItoArraybuffer(session.webPcanvas.toDataURL("image/"+session.webp, session.webPcanvas.quality)); + } + session.pcs[i].sendChannel.send(arrayBuffer); + } + } + } catch(e){} + } + } catch(e){ + errorlog(e); + makeImagesActive=false; + return; + } + makeImagesActive=false; + if (broadcasting){ // wait a bit of time, now that we sent a frame out. + session.webPcanvas.lastTime = session.webPcanvas.nowTime; + session.webPcanvas.nowTime = new Date().getTime(); + var time = session.webPcanvas.timeout - (session.webPcanvas.nowTime - session.webPcanvas.lastTime); + if (time <= 0 ){ + session.webPcanvas.makeImagesTimeout = setTimeout(function(){makeImages();},0); + } else { + session.webPcanvas.makeImagesTimeout = setTimeout(function(){makeImages();},time); + } + + } else { // just double check that we shoulnd't be broadcasting. + for (var i in session.pcs){ + if (session.pcs[i].allowWebp){ + session.webPcanvas.makeImagesTimeout = setTimeout(function(){makeImages();},0); + return; + } + } + log("Stopping webP broadcast."); + } +} + +var updateUserListTimeout=null +var updateUserListActive = false; +function updateUserList(){ + if (session.showList===true){ + // continue + } + else if ((session.showList!==true) && (session.cleanOutput || (session.scene!==false) || !session.roomid || session.director || (session.showList===false))){return;} + clearInterval(updateUserListTimeout); + updateUserListTimeout = setTimeout(function(){ + if (updateUserListActive){return;} + updateUserListActive=true; + try { + var added = false; + getById("userList").innerHTML = ""; + + for (var UUID in session.rpcs){ + if ((session.rpcs[UUID].videoElement && session.rpcs[UUID].streamSrc && session.rpcs[UUID].streamSrc.getTracks().length) || (session.rpcs[UUID].canvas) || (session.rpcs[UUID].imageElement)){ + if (session.rpcs[UUID].videoElement && document.body.contains(session.rpcs[UUID].videoElement)){ + continue; + } else if (session.rpcs[UUID].canvas && document.body.contains(session.rpcs[UUID].canvas)){ + continue; + } else if (session.rpcs[UUID].imageElement && document.body.contains(session.rpcs[UUID].imageElement)){ + continue; + } + } + if (session.rpcs[UUID].virtualHangup){ // end of screen share / director ? + continue; + } + if ((session.rpcs[UUID].videoMuted || (!session.rpcs[UUID].imageElement && !session.rpcs[UUID].canvas)) || ( session.infocus && session.infocus!==UUID ) || (!session.rpcs[UUID].defaultSpeaker && session.activeSpeaker)){ + + if (session.directorList.indexOf(UUID)>=0){ + if (!session.rpcs[UUID].streamSrc){ // director not active yet, so we won't bother showing it. + continue; + } + } + + var insert = document.createElement("div"); + if (session.rpcs[UUID].label){ + insert.innerText = session.rpcs[UUID].label.split("\\n")[0] + ""; + } else if (session.directorList.indexOf(UUID)>=0){ + miniTranslate(insert,"director"); + //insert.innerHTML = getTranslation("director"); + } else { + miniTranslate(insert,"unknown-user"); + //insert.innerHTML = getTranslation("unknown-user"); + } + try { + insert.dataset.UUID = UUID; + insert.dataset.sid = session.rpcs[UUID].streamID; + insert.title = "Stream ID: "+session.rpcs[UUID].streamID; + + insert.addEventListener('click', function(e) { // show stats of video if double clicked + log("clicked"); + try { + if (session.statsMenu !==false){ + var uid = e.currentTarget.dataset.UUID; + if (e.ctrlKey||e.metaKey){ + e.preventDefault(); + + if ("stats" in session.rpcs[uid]){ + + var [menu, innerMenu] = statsMenuCreator(); + printViewStats(innerMenu, uid ); + menu.interval = setInterval(printViewStats, session.statsInterval, innerMenu, uid); + + } + e.stopPropagation(); + return false; + } + } + } catch(e){errorlog(e);} + }); + + if (session.statsMenu){ + if ("stats" in session.rpcs[UUID]){ + + if (getById("menuStatsBox")){ + clearInterval(getById("menuStatsBox").interval); + getById("menuStatsBox").remove(); + } + + var [menu, innerMenu] = statsMenuCreator(); + printViewStats(innerMenu, UUID ); + menu.interval = setInterval(printViewStats, session.statsInterval, innerMenu, UUID); + + } + } + } catch(e){} + + getById("userList").appendChild(insert); + + if (session.rpcs[UUID].videoElement){ + var volumeBarInsert = document.createElement("input"); + volumeBarInsert.type = "range"; + volumeBarInsert.className = "hidden"; + volumeBarInsert.setAttribute("orient","vertical"); + volumeBarInsert.max = 100; + volumeBarInsert.min = 0; + volumeBarInsert.step = 1; + volumeBarInsert.dataset.UUID = UUID; + volumeBarInsert.setAttribute("value",parseFloat(session.rpcs[UUID].videoElement.volume)*100); + volumeBarInsert.title = volumeBarInsert.value+"%"; + + volumeBarInsert.oninput = function(e){ + session.rpcs[this.dataset.UUID].videoElement.volume = parseInt(this.value)/100 || 0; + if (!session.rpcs[this.dataset.UUID].videoElement.volume){ + this.parentNode.classList.add("red"); + } else { + this.parentNode.classList.remove("red"); + } + }; + + volumeBarInsert.onchange = function(e){ + this.parentNode.querySelector("input").classList.toggle("hidden"); + }; + + var volumeButton = document.createElement("i"); + volumeButton.className = "las la-volume-up"; + volumeButton.onclick = function(){ + this.parentNode.querySelector("input").classList.toggle("hidden"); + this.parentNode.volumeBarInsert.focus(); + } + + var volumeInsert = document.createElement("div"); + volumeInsert.className = "volume-control-userlist"; + volumeInsert.volumeBarInsert = volumeBarInsert; + insert.appendChild(volumeInsert); + volumeInsert.appendChild(volumeBarInsert); + volumeInsert.appendChild(volumeButton); + } + + if (session.rpcs[UUID].remoteMuteState || !(session.rpcs[UUID].streamSrc)){ + var muteInsert = document.createElement("div"); + muteInsert.className = "video-mute-state-userlist"; + muteInsert.innerHTML = ''; + insert.appendChild(muteInsert); + } else if (session.rpcs[UUID].voiceMeter){ + insert.appendChild(session.rpcs[UUID].voiceMeter); + } + //getById("userList").innerHTML += "
    "; + added=true; + } + } + + if (!added){ + getById("connectUsers").style.display = "none"; + } else { + getById("connectUsers").style.display = "block"; + } + } catch(e){} + updateUserListActive=false; + },200); +} + + +function resetCanvas(){ + log("resetCanvas();"); + if (!session.streamSrc){ + checkBasicStreamsExist(); + return; + } + session.streamSrc.getVideoTracks().forEach((track) => { + session.canvasSource.width = track.getSettings().width || 1280; + session.canvasSource.height = track.getSettings().height || 720; + }); +} + +var LaunchTFWorkerCallback = false; +function TFLiteWorker(){ + if (session.tfliteModule==false){ + LaunchTFWorkerCallback=true + return; + } + if (TFLITELOADING){LaunchTFWorkerCallback=true;return;} + LaunchTFWorkerCallback=false; + log("TFLiteWorker() called"); + + if (!session.tfliteModule.img){ + if (!session.selectImageTFLITE_contents){ + session.selectImageTFLITE_contents = getById("selectImageTFLITE_contents"); + } + if (session.selectImageTFLITE_contents.querySelector("img")){ + session.tfliteModule.img = session.selectImageTFLITE_contents.querySelector("img"); + session.tfliteModule.img.classList.add("selectedTFImage"); + } else if (session.defaultBackgroundImages && session.defaultBackgroundImages.length){ + session.tfliteModule.img = document.createElement("img"); + session.tfliteModule.img.onload = function(){ + URL.revokeObjectURL(session.tfliteModule.img.src); // no longer needed, free memory + } + session.tfliteModule.img.src = session.defaultBackgroundImages[0]; + session.tfliteModule.img.classList.add("selectedTFImage"); + } else { + session.tfliteModule.img = document.createElement("img"); + session.tfliteModule.img.onload = function(){ + URL.revokeObjectURL(session.tfliteModule.img.src); // no longer needed, free memory + } + session.tfliteModule.img.src = "./media/bg_sample.webp"; + } + } + + if (session.tfliteModule.looping){return;} + + const segmentationWidth = 256; + const segmentationHeight = 144; + const segmentationPixelCount = segmentationWidth * segmentationHeight; + const inputMemoryOffset = session.tfliteModule._getInputMemoryOffset() / 4; + const outputMemoryOffset = session.tfliteModule._getOutputMemoryOffset() / 4; + const segmentationMask = new ImageData(segmentationWidth, segmentationHeight); + const segmentationMaskCanvas = document.createElement('canvas'); + segmentationMaskCanvas.width = segmentationWidth; + segmentationMaskCanvas.height = segmentationHeight; + const segmentationMaskCtx = segmentationMaskCanvas.getContext('2d', {alpha:true, willReadFrequently: true}); + session.tfliteModule.nowTime = new Date().getTime(); + session.tfliteModule.offsetTime = 0; + + + function process(){ + + clearTimeout(session.tfliteModule.timeout); + + if (!(session.effect=="3" || session.effect=="4" || session.effect=="5")){ + session.tfliteModule.looping=false; + return; + } + if (session.tfliteModule.activelyProcessing){return;} + + session.tfliteModule.activelyProcessing=true; + + if (session.mobile){ + if (screenWidth !== window.innerWidth){ + screenWidth = window.innerWidth; + setTimeout(function(){ + updateRenderOutpipe(); + },200); + session.tfliteModule.looping=false; + session.tfliteModule.activelyProcessing=false; + return; + } + } + + try { + segmentationMaskCtx.filter = 'none'; + segmentationMaskCtx.drawImage( + session.canvasSource, + 0, + 0, + session.canvasSource.width, + session.canvasSource.height, + 0, + 0, + segmentationWidth, + segmentationHeight + ) + + const imageData = segmentationMaskCtx.getImageData( + 0, + 0, + segmentationWidth, + segmentationHeight + ); + + for (let i = 0; i < segmentationPixelCount; i++) { + session.tfliteModule.HEAPF32[inputMemoryOffset + i * 3] = imageData.data[i * 4] / 255; + session.tfliteModule.HEAPF32[inputMemoryOffset + i * 3 + 1] = imageData.data[i * 4 + 1] / 255; + session.tfliteModule.HEAPF32[inputMemoryOffset + i * 3 + 2] = imageData.data[i * 4 + 2] / 255; + } + + session.tfliteModule._runInference(); + + if (!session.experimental){ // standard mode + + for (let i = 0; i < segmentationPixelCount; i++) { + const background = session.tfliteModule.HEAPF32[outputMemoryOffset + i * 2]; + const person = session.tfliteModule.HEAPF32[outputMemoryOffset + i * 2 + 1]; + const shift = Math.max(background, person); + const backgroundExp = Math.exp(background - shift); + const personExp = Math.exp(person - shift); + segmentationMask.data[i * 4 + 3] = Math.min(Math.pow((255 * personExp) / (backgroundExp + personExp),1.5) - 10,255); // softmax + } + + segmentationMaskCtx.putImageData(segmentationMask, 0, 0); + + session.canvasCtx.globalCompositeOperation = 'copy'; + session.canvasCtx.filter = 'blur(8px)'; + session.canvasCtx.drawImage( + segmentationMaskCanvas, + 0, + 0, + segmentationWidth, + segmentationHeight, + 0, + 0, + session.canvasSource.width, + session.canvasSource.height + ) + + session.canvasCtx.globalCompositeOperation = 'source-in'; + session.canvasCtx.filter = 'none'; + session.canvasCtx.drawImage(session.canvasSource, 0, 0); + + } else { // experimental mode, so contouring + + /* session.canvasCtx.clearRect(0, 0, session.canvasSource.width, session.canvasSource.height); + + for (let i = 0; i < segmentationPixelCount; i++) { + const background = session.tfliteModule.HEAPF32[outputMemoryOffset + i * 2]; + const person = session.tfliteModule.HEAPF32[outputMemoryOffset + i * 2 + 1]; + const shift = Math.max(background, person); + const backgroundExp = Math.exp(background - shift); + const personExp = Math.exp(person - shift); + const value = (255 * personExp) / (backgroundExp + personExp); // softmax + + if (value>70){ + maskData[i] = 1; + } else if (value>20 && maskData[i]===1){ // was positive, lets keep it positive + maskData[i] = 1; + } else { + maskData[i] = 0; + } + + } + + try { + contours = FindContours(maskData, segmentationWidth, segmentationHeight); + } catch(e){ + contours = []; + } + + var awidth = session.canvasSource.width/segmentationWidth; + var aheight = session.canvasSource.height/segmentationHeight; + + + + var biggestContour = []; + for (let contour of contours){ + if (!contour.isHole && (contour.points.length > biggestContour.length)){ + biggestContour = contour.points; + } + } + + if (biggestContour.length) { // don't show small things + session.canvasCtx.filter = "blur(4px)"; + session.canvasCtx.beginPath(); + biggestContour = approxPolyDP(biggestContour); + let pt = biggestContour.pop(); + + let lastx= Math.round(pt[0]*awidth); + let lasty= Math.round(pt[1]*aheight); + session.canvasCtx.moveTo(lastx, lasty); + while (biggestContour.length){ + pt = biggestContour.pop(); + if (pt[0]<=1){pt[0]=-100;} + else if (pt[0]>=segmentationWidth-2){pt[0]=segmentationWidth+100;} + if (pt[1]<=1){pt[1]=-100;} + else if (pt[1]>=segmentationHeight-2){pt[1]=segmentationHeight+100;} + + let nox= Math.round(pt[0]*awidth); + let noy= Math.round(pt[1]*aheight); + + session.canvasCtx.quadraticCurveTo(lastx, lasty,nox,noy); + lastx = nox; + lasty = noy; + } + session.canvasCtx.closePath(); + session.canvasCtx.fill(); + } + + session.canvasCtx.globalCompositeOperation = 'source-in'; + session.canvasCtx.filter = 'none'; + session.canvasCtx.drawImage(session.canvasSource, 0, 0); */ + } + + session.canvasCtx.globalCompositeOperation = 'destination-over'; + if (session.effect=="4"){ // greenscreen + session.canvasCtx.filter = 'none'; + session.canvasCtx.fillStyle = "#0F0"; + session.canvasCtx.fillRect(0, 0, session.canvas.width, session.canvas.height); + } else if (session.effect=="5"){ + session.canvasCtx.filter = 'none'; + if (session.tfliteModule.img.complete){ + try { + session.canvasCtx.drawImage(session.tfliteModule.img, 0, 0, session.canvas.width, session.canvas.height); + } catch(e){} + } + } else if (session.effect=="3"){ // BLUR + if (session.effectValue){ + session.canvasCtx.filter = 'blur('+(parseInt(session.effectValue)*2)+'px)'; + } else { + session.canvasCtx.filter = 'blur(4px)'; // Does not work on Safari + } + session.canvasCtx.drawImage(session.canvasSource, 0, 0); + session.canvasCtx.filter = 'none'; + } else { + session.tfliteModule.activelyProcessing=false; + session.tfliteModule.looping=false; + return; + } + + ////// + + } catch (e){ + errorlog(e); + session.tfliteModule.activelyProcessing=false; + session.tfliteModule.looping=false; + return; + } + + session.tfliteModule.lastTime = session.tfliteModule.nowTime; + session.tfliteModule.nowTime = new Date().getTime(); + + var time = 33 - (session.tfliteModule.nowTime - session.tfliteModule.lastTime); + time = time + session.tfliteModule.offsetTime; + session.tfliteModule.activelyProcessing=false; + if (time <= 0 ){ + session.tfliteModule.timeout = setTimeout(function(){process();},0); + session.tfliteModule.offsetTime = 0; + } else { + session.tfliteModule.timeout = setTimeout(function(){process();},time); + session.tfliteModule.offsetTime = time; + } + } + + function processiOS(){ + clearTimeout(session.tfliteModule.timeout); + if (!(session.effect=="3" || session.effect=="4" || session.effect=="5")){ + session.tfliteModule.looping=false; + return; + } + if (session.tfliteModule.activelyProcessing){return;} + session.tfliteModule.activelyProcessing=true; + + if (screenWidth !== window.innerWidth){ + screenWidth = window.innerWidth; + setTimeout(function(){ + updateRenderOutpipe(); + },200); + session.tfliteModule.looping=false; + session.tfliteModule.activelyProcessing=false; + return; + } + + try{ + segmentationMaskCtx.drawImage( + session.canvasSource, + 0, + 0, + session.canvasSource.width, + session.canvasSource.height, + 0, + 0, + segmentationWidth, + segmentationHeight + ) + + var imageData = segmentationMaskCtx.getImageData( + 0, + 0, + segmentationWidth, + segmentationHeight + ); + + for (let i = 0; i < segmentationPixelCount; i++) { + session.tfliteModule.HEAPF32[inputMemoryOffset + i * 3] = imageData.data[i * 4] / 255; + session.tfliteModule.HEAPF32[inputMemoryOffset + i * 3 + 1] = imageData.data[i * 4 + 1] / 255; + session.tfliteModule.HEAPF32[inputMemoryOffset + i * 3 + 2] = imageData.data[i * 4 + 2] / 255; + } + + session.tfliteModule._runInference(); + + for (let i = 0; i < segmentationPixelCount; i++) { + const background = session.tfliteModule.HEAPF32[outputMemoryOffset + i * 2]; + const person = session.tfliteModule.HEAPF32[outputMemoryOffset + i * 2 + 1]; + const shift = Math.max(background, person); + const backgroundExp = Math.exp(background - shift); + const personExp = Math.exp(person - shift); + segmentationMask.data[i * 4 + 3] = 255 - (255 * personExp) / (backgroundExp + personExp); // softmax + } + + segmentationMaskCtx.putImageData(segmentationMask, 0, 0); + + session.canvasCtx.globalCompositeOperation = 'copy'; + session.canvasCtx.drawImage(session.canvasSource, 0, 0); + + session.canvasCtx.globalCompositeOperation = 'destination-out'; + session.canvasCtx.drawImage( + segmentationMaskCanvas, + 0, + 0, + segmentationWidth, + segmentationHeight, + 0, + 0, + session.canvasSource.width, + session.canvasSource.height + ); + + session.canvasCtx.globalCompositeOperation = 'destination-over'; + + if (session.effect=="4"){ // greenscreen + session.canvasCtx.fillStyle = "#0F0"; + session.canvasCtx.fillRect(0, 0, session.canvas.width, session.canvas.height); + } else if (session.effect=="5"){ + if (session.tfliteModule.img.complete){ + try { + session.canvasCtx.drawImage(session.tfliteModule.img, 0, 0, session.canvas.width, session.canvas.height); + } catch(e){} + } + } else if (session.effect=="3"){ // BLUR + + const width = canvasBG.width; + const height = canvasBG.height; + ctxBG.drawImage(session.canvasSource, 0, 0, width, height); + imageData = ctxBG.getImageData(0, 0, width, height); + + const { data } = imageData; + + // THE BELOW BLUR CODE polyfil is by David Enke + // MIT License: Copyright (c) 2019 + // https://github.com/steveseguin/context-filter-polyfill/blob/master/src/filters/blur.filter.ts + const wm = width - 1; + const hm = height - 1; + const rad1 = amount + 1; + const r = []; + const g = []; + const b = []; + //const a = []; + + const vmin = []; + const vmax = []; + + let iterations = 3; // 1 - 3 + let p, p1, p2; + while (iterations-- > 0) { + let yw = 0; + let yi = 0; + + for (let y = 0; y < height; y++) { + let rsum = data[yw] * rad1; + let gsum = data[yw + 1] * rad1; + let bsum = data[yw + 2] * rad1; + + for (let i = 1; i <= amount; i++) { + p = yw + (((i > wm ? wm : i)) << 2); + rsum += data[p++]; + gsum += data[p++]; + bsum += data[p++]; + } + + for (let x = 0; x < width; x++) { + r[yi] = rsum; + g[yi] = gsum; + b[yi] = bsum; + + if (y === 0) { + vmin[x] = ((p = x + rad1) < wm ? p : wm) << 2; + vmax[x] = ((p = x - amount) > 0 ? p << 2 : 0); + } + + p1 = yw + vmin[x]; + p2 = yw + vmax[x]; + + rsum += data[p1++] - data[p2++]; + gsum += data[p1++] - data[p2++]; + bsum += data[p1++] - data[p2++]; + + yi++; + } + yw += (width << 2); + } + + for (let x = 0; x < width; x++) { + let yp = x; + let rsum = r[yp] * rad1; + let gsum = g[yp] * rad1; + let bsum = b[yp] * rad1; + + for (let i = 1; i <= amount; i++) { + yp += (i > hm ? 0 : width); + rsum += r[yp]; + gsum += g[yp]; + bsum += b[yp]; + } + + yi = x << 2; + + for (let y = 0; y < height; y++) { + data[yi] = ((rsum * mulSum) >>> shgSum); + data[yi + 1] = ((gsum * mulSum) >>> shgSum); + data[yi + 2] = ((bsum * mulSum) >>> shgSum); + + if (x === 0) { + vmin[y] = ((p = y + rad1) < hm ? p : hm) * width; + vmax[y] = ((p = y - amount) > 0 ? p * width : 0); + } + + p1 = x + vmin[y]; + p2 = x + vmax[y]; + + rsum += r[p1] - r[p2]; + gsum += g[p1] - g[p2]; + bsum += b[p1] - b[p2]; + yi += width << 2; + } + } + } + ////////////// END OF BLUR CODE - MIT LICENCED. + ctxBG.putImageData(imageData, 0, 0); + session.canvasCtx.drawImage(canvasBG, 0, 0, width, height, 0, 0, session.canvas.width, session.canvas.height); + } else { + session.tfliteModule.activelyProcessing=false; + session.tfliteModule.looping=false; + return; + } + } catch (e){ + session.tfliteModule.activelyProcessing=false; + session.tfliteModule.looping=false; + errorlog(e); + return; + } + + session.tfliteModule.lastTime = session.tfliteModule.nowTime; + session.tfliteModule.nowTime = new Date().getTime(); + var time = 33 - (session.tfliteModule.nowTime - session.tfliteModule.lastTime); + time = time + session.tfliteModule.offsetTime; + session.tfliteModule.activelyProcessing=false; + if (time <= 0 ){ + session.tfliteModule.timeout = setTimeout(function(){processiOS();},0); + session.tfliteModule.offsetTime = 0; + } else { + session.tfliteModule.timeout = setTimeout(function(){processiOS();},time); + session.tfliteModule.offsetTime = time; + } + } + session.tfliteModule.looping=true; + + var screenWidth = window.innerWidth; + + if (iOS || iPad || SafariVersion){ + var canvasBG = document.createElement("canvas"); + var ctxBG = canvasBG.getContext("2d", {alpha: false}); + var amount = 1.0; + var mulTable = [1, 57, 41, 21, 203, 34, 97, 73, 227, 91, 149, 62, 105, 45, 39, 137, 241, 107, 3, 173, 39, 71, 65, 238, 219, 101, 187, 87, 81, 151, 141, 133, 249, 117, 221, 209, 197, 187, 177, 169, 5, 153, 73, 139, 133, 127, 243, 233, 223, 107, 103, 99, 191, 23, 177, 171, 165, 159, 77, 149, 9, 139, 135, 131, 253, 245, 119, 231, 224, 109, 211, 103, 25, 195, 189, 23, 45, 175, 171, 83, 81, 79, 155, 151, 147, 9, 141, 137, 67, 131, 129, 251, 123, 30, 235, 115, 113, 221, 217, 53, 13, 51, 50, 49, 193, 189, 185, 91, 179, 175, 43, 169, 83, 163, 5, 79, 155, 19, 75, 147, 145, 143, 35, 69, 17, 67, 33, 65, 255, 251, 247, 243, 239, 59, 29, 229, 113, 111, 219, 27, 213, 105, 207, 51, 201, 199, 49, 193, 191, 47, 93, 183, 181, 179, 11, 87, 43, 85, 167, 165, 163, 161, 159, 157, 155, 77, 19, 75, 37, 73, 145, 143, 141, 35, 138, 137, 135, 67, 33, 131, 129, 255, 63, 250, 247, 61, 121, 239, 237, 117, 29, 229, 227, 225, 111, 55, 109, 216, 213, 211, 209, 207, 205, 203, 201, 199, 197, 195, 193, 48, 190, 47, 93, 185, 183, 181, 179, 178, 176, 175, 173, 171, 85, 21, 167, 165, 41, 163, 161, 5, 79, 157, 78, 154, 153, 19, 75, 149, 74, 147, 73, 144, 143, 71, 141, 140, 139, 137, 17, 135, 134, 133, 66, 131, 65, 129, 1]; + var mulSum = mulTable[amount]; + var shgTable = [0, 9, 10, 10, 14, 12, 14, 14, 16, 15, 16, 15, 16, 15, 15, 17, 18, 17, 12, 18, 16, 17, 17, 19, 19, 18, 19, 18, 18, 19, 19, 19, 20, 19, 20, 20, 20, 20, 20, 20, 15, 20, 19, 20, 20, 20, 21, 21, 21, 20, 20, 20, 21, 18, 21, 21, 21, 21, 20, 21, 17, 21, 21, 21, 22, 22, 21, 22, 22, 21, 22, 21, 19, 22, 22, 19, 20, 22, 22, 21, 21, 21, 22, 22, 22, 18, 22, 22, 21, 22, 22, 23, 22, 20, 23, 22, 22, 23, 23, 21, 19, 21, 21, 21, 23, 23, 23, 22, 23, 23, 21, 23, 22, 23, 18, 22, 23, 20, 22, 23, 23, 23, 21, 22, 20, 22, 21, 22, 24, 24, 24, 24, 24, 22, 21, 24, 23, 23, 24, 21, 24, 23, 24, 22, 24, 24, 22, 24, 24, 22, 23, 24, 24, 24, 20, 23, 22, 23, 24, 24, 24, 24, 24, 24, 24, 23, 21, 23, 22, 23, 24, 24, 24, 22, 24, 24, 24, 23, 22, 24, 24, 25, 23, 25, 25, 23, 24, 25, 25, 24, 22, 25, 25, 25, 24, 23, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 23, 25, 23, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 24, 22, 25, 25, 23, 25, 25, 20, 24, 25, 24, 25, 25, 22, 24, 25, 24, 25, 24, 25, 25, 24, 25, 25, 25, 25, 22, 25, 25, 25, 24, 25, 24, 25, 18]; + var shgSum = shgTable[amount]; + + log("session.canvas: "+session.canvas.width+"x"+session.canvas.height); + canvasBG.width = parseInt(session.canvas.width/12);; + canvasBG.height = parseInt(session.canvas.height/12);; + ctxBG.width = canvasBG.width; + ctxBG.height = canvasBG.height; + processiOS(); + + } else { + process(); + } +} + + +function mainMeshMask() { + if ((session.TFJSModel === null) || (session.TFJSModel === true)){ + setTimeout(function(){mainMeshMask();},1000); + return; + } + function heatMapColorforValue(value){ + var h = parseInt((1.0 - value) * 240); + if (h<0){h=0;} + if (h>240){h=240;} + return "hsl(" + h + ", 100%, 50%)"; + } + async function process(){ + if (session.TFJSModel.activelyProcessing){return;} + session.TFJSModel.activelyProcessing = true; + + clearTimeout(session.TFJSModel.timeout); + + if (session.effect!="6"){ + //session.TFJSModel.looping=false; + session.TFJSModel.activelyProcessing = false; + return; + } + + const predictions = await session.TFJSModel.estimateFaces({ + input: session.canvasSource + }); + + var output = []; + if (predictions.length > 0) { + for (let j = 0; j < predictions.length; j++) { + const fp = predictions[j].annotations; + session.canvasCtx.fillStyle = "#000000"; + session.canvasCtx.fillRect(0, 0, session.canvas.width, session.canvas.height); + const keypoints = predictions[j].scaledMesh + for (let i = 0; i < keypoints.length; i++) { + var [x,y,z] = keypoints[i]; + x=parseInt(x); + y=parseInt(y); + z=parseInt(z); + if (session.pushEffectsData){ + output.push(x); + output.push(y); + } + session.canvasCtx.fillStyle = heatMapColorforValue((z+40)/60); + session.canvasCtx.fillRect(x, y, 5, 5); + } + } + } + + if (session.pushEffectsData){ + //output = FastIntegerCompression.compress(output); + //log(output); + if (isIFrame){ + parent.postMessage({ + "effectsData": output, + "eID": session.pushEffectsData + }, session.iframetarget); + } else { + for (var i in session.pcs){ + if (!session.pcs[i].sendChannel.bufferedAmount){ // don't overload things. + session.sendMessage({"effectsData": output, "eID":session.effect},i); + } + } + } + } + + if (document.hidden) { + session.TFJSModel.lastTime = session.TFJSModel.nowTime || new Date().getTime(); + session.TFJSModel.nowTime = new Date().getTime(); + var time = 33 - (session.TFJSModel.nowTime - session.TFJSModel.lastTime); + if (time <= 0 ){ + session.TFJSModel.timeout = setTimeout(function(){process();},0); + } else { + session.TFJSModel.timeout = setTimeout(function(){process();},time); + } + session.TFJSModel.activelyProcessing = false; + } else { + session.TFJSModel.timeout = setTimeout(function(){process();},33); + session.TFJSModel.activelyProcessing = false; + window.requestAnimationFrame(process); + } + + } + process(); +} + +var faceDetector = false; +var faceAlignment=false; +var activeDetection = false; +function drawFace() { + if (session.effect !== "1"){return;} + if (faceAlignment){ + faceAlignment(); + return; + } else if (faceAlignment===null){ + return; + } + faceAlignment = null; + + var timers = {}; + timers.activelyProcessing=false; + timers.activelyProcessingDraw = false; + + var ctx = session.canvasCtx; + + function fde1(){ + + warnlog("LOADED drawFace()"); + + + var lastFace = {}; + + //session.canvasSource.width = session.canvasSource.srcObject.getVideoTracks()[0].getSettings().width || 1280; + //session.canvasSource.height = session.canvasSource.srcObject.getVideoTracks()[0].getSettings().height || 720; + + lastFace.x = session.canvasSource.width / 2; + lastFace.y = session.canvasSource.height / 2; + lastFace.w = session.canvasSource.width; + lastFace.h = session.canvasSource.height; + + session.canvas.height = 2 * parseInt(session.canvasSource.height / 2); + session.canvas.width = 2 * parseInt(session.canvasSource.width / 2); + + function detectFace(){ + if (activeDetection){return;} + activeDetection=true; + if (session.effect !== "1"){return;} + try { + faceDetector.detect(session.canvasSource).then(faces => { + if (faces.length){ + for (let face of faces) { + lastFace.x = face.boundingBox.x; + lastFace.y = face.boundingBox.y; + lastFace.w = face.boundingBox.width; + lastFace.h = face.boundingBox.height; + break; + } + } + //setTimeout(function(){draw();},0); + }).catch((e) => { + //errorlog("Boo, Face Detection failed: " + e); + }); + } catch(e){} + setTimeout(function(){detectFace();},200); + activeDetection = false; + } + + var wh = null; + var xa = null; + var ya = null; + + + function draw() { + if (timers.activelyProcessingDraw){return;} + timers.activelyProcessingDraw = true; + clearTimeout(timers.timeoutDraw); + if (session.effect !== "1"){ + timers.activelyProcessingDraw = false; + return; + } + + try { + if (!session.canvasSource.width){ + timers.timeoutDraw = setTimeout(function(){draw();},1000); + timers.activelyProcessingDraw = false; + return + } + if (wh === null && session.canvasSource.width){ + wh = Math.pow(session.canvasSource.width * session.canvasSource.width/36,0.5); + + xa = 0 + ya = 0; + } + + session.canvas.height = 2 * parseInt(session.canvasSource.height / 2); + session.canvas.width = 2 * parseInt(session.canvasSource.width / 2); + + if (lastFace.w){ + wh = wh * 0.999 + Math.pow(lastFace.w*lastFace.h,0.5)*0.001; + + var w = wh*6; + + if (w>session.canvasSource.width){ + w = session.canvasSource.width; + } + if (session.canvasSource.height>session.canvasSource.width){ + if (w>session.canvasSource.height && session.canvasSource.height>session.canvasSource.width){ + w = session.canvasSource.height; + } + } else if (w>session.canvasSource.width){ + w = session.canvasSource.width; + } + + var h = (w/session.canvasSource.width) * session.canvasSource.height; + + + xa = xa*0.998 + 0.002*(lastFace.x + lastFace.w/2); + ya = ya*0.998 + 0.002*(lastFace.y + lastFace.h/2); + + var x = xa - w/2; + var y = ya - h/2; + + if (x<0){x=0;} + if (y<0){y=0;} + + + if (x>session.canvasSource.width-w){x=session.canvasSource.width-w;} + if (y>session.canvasSource.height-h){y=session.canvasSource.height-h;} + + if (x<0){x=0;} + if (y<0){y=0;} + + } + //console.log(x, y, w, h, session.canvasSource.width, session.canvasSource.height); + + ctx.drawImage(session.canvasSource, x, y, w, h, 0, 0, session.canvasSource.width, session.canvasSource.height); + //ctx.beginPath(); + //ctx.rect(lastFace.x, lastFace.y, lastFace.w, lastFace.h); + // ctx.stroke(); + } catch(e){} + + if (document.hidden){ + timers.lastTimeDraw = timers.nowTimeDraw || new Date().getTime(); + timers.nowTimeDraw = new Date().getTime(); + var time = 33 - (timers.nowTimeDraw - timers.lastTimeDraw); + if (time <= 0 ){ + timers.timeoutDraw = setTimeout(function(){draw();},0); + } else { + timers.timeoutDraw = setTimeout(function(){draw();},time); + } + timers.activelyProcessingDraw = false; + } else { + timers.timeoutDraw = setTimeout(function(){draw();},33); + timers.activelyProcessingDraw = false; + window.requestAnimationFrame(draw); + } + } + + if (window.FaceDetector == undefined) { + if (!session.cleanOutput){ + warnUser('Face Detection API not detected.\n\nYou may be able to enable it here: chrome://flags/#enable-experimental-web-platform-features'); + } + faceDetector = false; + } else { + faceDetector = new FaceDetector(); + } + + function fde2(){ + if (!timers.activelyProcessingDraw){ + draw(); + } + if (!activeDetection){ + detectFace(); + } + }; + fde2(); + return fde2; + }; + faceAlignment = fde1(); +} +//////// END CANVAS EFFECTS /////////////////// + +var getFacesActive = false; +async function getFaces(){ + + if (getFacesActive){return;} + getFacesActive = true; + + if (session.grabFaceData){ + if (!faceDetector){ + if (window.FaceDetector == undefined) { + if (!session.cleanOutput){ + warnUser('Face Detection API not detected.\n\nYou may be able to enable it here: chrome://flags/#enable-experimental-web-platform-features'); + } + session.grabFaceData = false; + faceDetector = false; + + getFacesActive = false; + return; + } else { + session.grabFaceData = 1; + faceDetector = new FaceDetector(); + } + } + try { + var videos = {}; + for (var UUID in session.rpcs){ + if (session.rpcs[UUID].videoElement){ + await faceDetector.detect(session.rpcs[UUID].videoElement).then(faces => { + videos[session.rpcs[UUID].streamID] = {}; + videos[session.rpcs[UUID].streamID].videoWidth = session.rpcs[UUID].videoElement.videoWidth + videos[session.rpcs[UUID].streamID].videoHeight = session.rpcs[UUID].videoElement.videoHeight + videos[session.rpcs[UUID].streamID].faces = faces; + }).catch((e) => { + //errorlog("Boo, Face Detection failed: " + e); + }); + } + } + log(videos); + } catch(e){} + pokeIframeAPI('face-tracking-data',videos); + setTimeout(function(){getFaces();},200); + } + + getFacesActive=false; +} + +////// + +var simpleDrawMain=false; +function simpleDraw(reinit=false) { + if (session.effect !== "8"){return;} + if (simpleDrawMain){ + simpleDrawMain(reinit); + return; + } else if (simpleDrawMain===null){ + return; + } + simpleDrawMain = null; + + var timers = {}; + timers.activelyProcessing=false; + timers.activelyProcessingDraw = false; + + var ctx = session.canvasCtx; + + function fde1(){ + try{ + warnlog("LOADED simpleDraw()"); + + session.canvas.height = 2 * parseInt(session.canvasSource.height / 2); + session.canvas.width = 2 * parseInt(session.canvasSource.width / 2); + + function draw() { + if (timers.activelyProcessingDraw){return;} + timers.activelyProcessingDraw = true; + clearTimeout(timers.timeoutDraw); + if (session.effect !== "8"){ + timers.activelyProcessingDraw = false; + return; + } + try { + if (!session.canvasSource.width){ + timers.timeoutDraw = setTimeout(function(){draw();},1000); + timers.activelyProcessingDraw = false; + return + } + + session.canvas.height = 2 * parseInt(session.canvasSource.height / 2); + session.canvas.width = 2 * parseInt(session.canvasSource.width / 2); + + ctx.drawImage(session.canvasSource, 0, 0, session.canvasSource.width, session.canvasSource.height, 0,0,session.canvasSource.width, session.canvasSource.height); + + } catch(e){errorlog(e);} + + if (document.hidden){ + timers.lastTimeDraw = timers.nowTimeDraw || new Date().getTime(); + timers.nowTimeDraw = new Date().getTime(); + var time = 33 - (timers.nowTimeDraw - timers.lastTimeDraw); + if (time <= 0 ){ + timers.timeoutDraw = setTimeout(function(){draw();},0); + } else { + timers.timeoutDraw = setTimeout(function(){draw();},time); + } + timers.activelyProcessingDraw = false; + } else { + timers.timeoutDraw = setTimeout(function(){draw();},33); + timers.activelyProcessingDraw = false; + window.requestAnimationFrame(draw); + } + + } + } catch(e){ + errorlog(e); + timers.activelyProcessingDraw = false; + } + + function fde2(reinit=false){ + if (reinit){ + if (session.canvasSource && session.canvasSource.srcObject && session.canvasSource.srcObject.getVideoTracks().length){ + session.canvasSource.width = session.canvasSource.srcObject.getVideoTracks()[0].getSettings().width || 1280; + session.canvasSource.height = session.canvasSource.srcObject.getVideoTracks()[0].getSettings().height || 720; + } + } + if (!timers.activelyProcessingDraw){ + draw(); + } + }; + fde2(); + + return fde2; + }; + simpleDrawMain = fde1(); +} +//////// END CANVAS EFFECTS /////////////////// +////// + +var digitalZoomMain=false; +function digitalZoom(reinit=false) { + if (session.effect !== "7"){return;} + if (digitalZoomMain){ + digitalZoomMain(reinit); + return; + } else if (digitalZoomMain===null){ + return; + } + digitalZoomMain = null; + + var timers = {}; + timers.activelyProcessing=false; + timers.activelyProcessingDraw = false; + + var ctx = session.canvasCtx; + + function fde1(){ + try{ + warnlog("LOADED digitalZoom()"); + + session.canvas.height = 2 * parseInt(session.canvasSource.height / 2); + session.canvas.width = 2 * parseInt(session.canvasSource.width / 2); + + var xa = null; + var ya = null; + var zz = 1; + + + function draw() { + if (timers.activelyProcessingDraw){return;} + timers.activelyProcessingDraw = true; + clearTimeout(timers.timeoutDraw); + if (session.effect !== "7"){ + zz = 1.0; + xa = 0; + ya = 0; + timers.activelyProcessingDraw = false; + return; + } + try { + if (!session.canvasSource.width){ + timers.timeoutDraw = setTimeout(function(){draw();},1000); + timers.activelyProcessingDraw = false; + return + } + if (xa === null && session.canvasSource.width){ + xa = 0; + ya = 0; + } + + session.canvas.height = 2 * parseInt(session.canvasSource.height / 2); + session.canvas.width = 2 * parseInt(session.canvasSource.width / 2); + + + + if (session.effectValue){ + zz = 0.9*zz + session.effectValue *0.1; + + xa = xa*0.9+ 0.1*(session.canvasSource.width - session.canvasSource.width/zz); + ya = ya*0.9 + 0.1*(session.canvasSource.height - session.canvasSource.height/zz); + + } + //console.log(parseInt(zz), parseInt(xa), parseInt(ya), parseInt(session.canvasSource.width-xa*2), parseInt(session.canvasSource.height-ya*2)); + + ctx.drawImage(session.canvasSource, xa, ya, session.canvasSource.width-xa*2, session.canvasSource.height-ya*2, 0,0,session.canvasSource.width, session.canvasSource.height); + //ctx.beginPath(); + //ctx.rect(lastFace.x, lastFace.y, lastFace.w, lastFace.h); + // ctx.stroke(); + } catch(e){errorlog(e);} + + if (document.hidden){ + timers.lastTimeDraw = timers.nowTimeDraw || new Date().getTime(); + timers.nowTimeDraw = new Date().getTime(); + var time = 33 - (timers.nowTimeDraw - timers.lastTimeDraw); + if (time <= 0 ){ + timers.timeoutDraw = setTimeout(function(){draw();},0); + } else { + timers.timeoutDraw = setTimeout(function(){draw();},time); + } + timers.activelyProcessingDraw = false; + } else { + timers.timeoutDraw = setTimeout(function(){draw();},33); + timers.activelyProcessingDraw = false; + window.requestAnimationFrame(draw); + } + + } + } catch(e){ + errorlog(e); + timers.activelyProcessingDraw = false; + } + + function fde2(reinit=false){ + if (reinit){ + if (session.canvasSource && session.canvasSource.srcObject && session.canvasSource.srcObject.getVideoTracks().length){ + session.canvasSource.width = session.canvasSource.srcObject.getVideoTracks()[0].getSettings().width || 1280; + session.canvasSource.height = session.canvasSource.srcObject.getVideoTracks()[0].getSettings().height || 720; + } + xa = null; + } + if (!timers.activelyProcessingDraw){ + draw(); + } + }; + fde2(); + + return fde2; + }; + digitalZoomMain = fde1(); +} +//////// END CANVAS EFFECTS /////////////////// + + +function getNativeOutputResolution(){ + var tracks = session.videoElement.srcObject.getVideoTracks(); + if (tracks.length && tracks[0].getSettings){ + return tracks[0].getSettings(); + } else { + return false; + } +} + +function toggleSceneStats(button){ + + var UUID = button.dataset.UUID; + + if (button.value==1){ + button.value = 0; + button.classList.remove("pressed"); + button.ariaPressed = "false"; + session.rpcs[UUID].allowGraphs = false; + } else { + button.value = 1; + button.classList.add("pressed"); + button.ariaPressed = "true"; + session.rpcs[UUID].allowGraphs = true; + } + + if (button.value==1){ + getById("container_" + UUID).querySelectorAll('[data-no-scenes]').forEach(ele=>{ + ele.classList.remove("hidden"); + if (ele.dataset.message){ + ele.innerHTML = "Requesting data .."; + } + }); + + if (getById("container_" + UUID).querySelector('[data-action-type="stats-graphs-bitrate"]')){ + getById("container_" + UUID).querySelector('[data-action-type="stats-graphs-bitrate"]').classList.remove("hidden"); + } + if (getById("container_" + UUID).querySelector('[data-action-type="stats-graphs-details"]')){ + getById("container_" + UUID).querySelector('[data-action-type="stats-graphs-details"]').classList.remove("hidden"); + } + session.sendRequest({'requestStatsContinuous':true, }, UUID); + } else { + session.sendRequest({'requestStatsContinuous':false, }, UUID); + if (getById("container_" + UUID).querySelector('[data-action-type="stats-graphs-bitrate"]')){ + getById("container_" + UUID).querySelector('[data-action-type="stats-graphs-bitrate"]').classList.add("hidden"); + } + if (getById("container_" + UUID).querySelector('[data-action-type="stats-graphs-details"]')){ + getById("container_" + UUID).querySelector('[data-action-type="stats-graphs-details"]').classList.add("hidden"); + } + } +} +function getColor(value) { + var hue = ((value) * 120).toString(10); + return ["hsl(", hue, ",100%,50%)"].join(""); +} + +function plotData(info, UUID, uuid) { // type = "bitrate" or "nacks" + log("plot data"); + + var container = getById("container_" + UUID).querySelector('[data-action-type="stats-graphs-bitrate"]'); + + if (!container){ + log("container not found"); + return; + } + var canvas = getById("container_" + UUID).querySelector('canvas[data-uid="'+uuid+'"]'); + var canvasNew = false + if (!canvas){ + canvasNew = true; + canvas = document.createElement("canvas"); + canvas.height = 50; + canvas.width = 124; + canvas.className = "canvasStats"; + canvas.history_nacks = []; + canvas.history_bitrate = []; + canvas.target = 4000; + if (info.scene){ + canvas.title = "Scene: "+info.scene+". Red/orange implies packet loss. Y-axis is marked with 2500-kbps increments."; + } else if (info.label){ + canvas.title = "Label: "+info.label+". Red/orange implies packet loss. Y-axis is marked with 2500-kbps increments"; + } else { + canvas.title = "Red/orange implies packet loss. Y-axis is marked with 2500-kbps increments"; + } + + canvas.dataset.uid = uuid; + container.appendChild(canvas); + } + + selfDestructElement(UUID, uuid); + + var context = canvas.getContext("2d"); + + var bitrate = 0; + if ("video_bitrate_kbps" in info){ + bitrate = info.video_bitrate_kbps; + } + if (isNaN(bitrate)) { + bitrate = 0; + } + + if (bitrate<0){bitrate = 0;} + + var nacks = 0; + if ("nacks_per_second" in info){ + nacks = info.nacks_per_second; + } + if (isNaN(nacks)) { + nacks = 0; + } + if (nacks<0){nacks = 0;} + + var height = context.canvas.height; + var width = context.canvas.width; + + canvas.history_nacks.push(nacks); + canvas.history_bitrate.push(bitrate); + + canvas.history_nacks = canvas.history_nacks.slice(-125); + canvas.history_bitrate = canvas.history_bitrate.slice(-125); + + var maxBitrate = Math.max(...canvas.history_bitrate); + + var target = canvas.target || 4000; + if (target && (maxBitrate > target)){ + + canvas.target = maxBitrate*1.5; // set it higher than it needs to be, so it doens't jump around a lot + var yScale = height / canvas.target; + context.clearRect(0, 0, width, height); + var x = width - 1; + var w = 1; + + for (var i = 0; i1){val=1;} + else if (val<0){val=0;} + var color = getColor(val); + var y = height - bitrate * yScale; + context.fillStyle = color; + context.fillRect(x, y, w, height); + context.fillStyle = "#DDD5"; + context.fillRect(x, y-2, w, 4); + + if (y-5>0){ + context.fillStyle = "#FFF3"; + context.fillRect(x, y+2, w, 1); + } + + var imageData = context.getImageData(1, 0, width - 1, height); + context.putImageData(imageData, 0, 0); + context.clearRect(width - 1, 0, 1, height); + } + + for (var tt = 2500; tt1){val=1;} + else if (val<0){val=0;} + var color = getColor(val); + + var yScale = height / target; + + var x = width - 1; + var y = height - bitrate * yScale; + var w = 1; + + context.fillStyle = color; + context.fillRect(x, y, w, height); + context.fillStyle = "#DDD5"; + context.fillRect(x, y-2, w, 4); + + if (y-5>0){ + context.fillStyle = "#FFF3"; + context.fillRect(x, y+2, w, 1); + } + + context.fillStyle = "#0555"; + if (canvasNew){ + for (var tt = 2500; tt{ + ele.classList.remove("greyout"); + clearTimeout(ele.selfFadeout); + ele.selfFadeout = setTimeout(function(ele){ + ele.classList.add("greyout"); + }, 4000, ele); + + clearTimeout(ele.selfDestruct); + ele.selfDestruct = setTimeout(function(ele){ + ele.remove(); + }, 10000, ele); + }); +} + +function remoteStats(msg, UUID){ + if (isIFrame){ + parent.postMessage({"remoteStats": msg.remoteStats , "streamID": session.rpcs[UUID].streamID, "UUID": UUID}, session.iframetarget); + } + + if (!(session.rpcs[UUID].allowGraphs || session.allowGraphs)){return;} + + if (session.director){ + //var output = ""; + var size = 0; + for (var key in msg.remoteStats) { + if (msg.remoteStats.hasOwnProperty(key)){ + size++; + } + } + + if (!size){ + getById("container_" + UUID).querySelectorAll('[data-no-scenes]').forEach(ele=>{ + ele.classList.remove("hidden"); + if (ele.dataset.message){ + ele.innerHTML = "No scenes active"; + } + }); + + log("zero size"); + return; + } + getById("container_" + UUID).querySelectorAll('[data-no-scenes]').forEach(ele=>{ + ele.classList.add("hidden"); + }); + + for (var uuid in msg.remoteStats){ + var container = getById("container_" + UUID).querySelector('[data-action-type="stats-graphs-details-container"][data-uid="'+uuid+'"]'); + if (!container){ + container = getById("container_" + UUID).querySelector('[data-action-type="stats-graphs-details-container"]').cloneNode(true); + container.dataset.uid = uuid; + container.classList.remove("hidden"); + getById("container_" + UUID).querySelector('[data-action-type="stats-graphs-details"]').appendChild(container); + } + plotData(msg.remoteStats[uuid], UUID, uuid); + if (("video_bitrate_kbps" in msg.remoteStats[uuid]) && (msg.remoteStats[uuid].video_bitrate_kbps!=="video_bitrate_kbps")){ + var span = container.querySelector('[data-bitrate]'); + if (span){ + span.classList.remove("hidden"); + span.innerHTML = "video bitrate: "+parseInt(msg.remoteStats[uuid].video_bitrate_kbps) + " (kbps)"; + } + } + var span = container.querySelector('[data-scene-name]'); + if (span && ("label" in msg.remoteStats[uuid]) && msg.remoteStats[uuid].label){ + span.classList.remove("hidden"); + span.innerHTML = "stats for viewer: " + msg.remoteStats[uuid].label; + } else if (span && ("scene" in msg.remoteStats[uuid]) && (msg.remoteStats[uuid].scene !==false)){ + span.classList.remove("hidden"); + span.innerHTML = "stats for scene: " + msg.remoteStats[uuid].scene; + } else if (uuid==="meshcast"){ + span.classList.remove("hidden"); + span.innerHTML = "stats for meshcast ingest"; + span.title = "You can use &label=xxxx to give your view links a unique label"; + } else { + span.classList.remove("hidden"); + span.innerHTML = "stats for some viewer"; + span.title = "You can use &label=xxxx to give your view links a unique label"; + } + if ("resolution" in msg.remoteStats[uuid]){ + var span = container.querySelector('[data-resolution]'); + if (span){ + span.classList.remove("hidden"); + span.innerHTML = msg.remoteStats[uuid].resolution; + } + } + + if ("video_encoder" in msg.remoteStats[uuid]){ + var span = container.querySelector('[data-video-codec]'); + if (span){ + span.classList.remove("hidden"); + span.innerHTML = "video codec: "+msg.remoteStats[uuid].video_encoder; + } + } + } + }; +} + +function processStats(UUID){ + + // for (pc in session.pcs){session.pcs[pc].getStats().then(function(stats) {stats.forEach(stat=>{if (stat.id.includes("RTCIce")){console.log(stat)}})})}; + + if (!session.rpcs || !(UUID in session.rpcs)){ + return; + } + + try { + if (session.rpcs[UUID].videoElement.paused){ + if (session.firstPlayTriggered){ + if (session.audioCtx.state == "suspended"){ // added oct 9th 2022 + try { + session.audioCtx.resume(); + } catch(e){warnlog(e);} + } + if (session.audioCtx.state == "running"){ // NOTE: I Don't know why this was + log("trying to play"); + session.rpcs[UUID].videoElement.play().then(_ => { + log("playing 8"); + + //if ((session.audioEffects===true) || session.pushLoudness){ + // updateIncomingAudioElement(UUID); + //} + }).catch(warnlog); + } + } + } + } catch (e){}; + + if (session.rpcs[UUID].whep){ + processMeshcastStats(UUID); + } + + try { + + if (session.rpcs[UUID].realUUID && session.rpcs[session.rpcs[UUID].realUUID]){ + var node = session.rpcs[session.rpcs[UUID].realUUID]; + } else { + var node = session.rpcs[UUID]; + } + + var validTrackIds = []; + if (session.rpcs[UUID].streamSrc){ + session.rpcs[UUID].streamSrc.getTracks().forEach(trk=>{ + validTrackIds.push(trk.id); + }); + } + + if (node.getStats){ + node.getStats().then(function(stats){ + if (!(UUID in session.rpcs)){return;} + + setTimeout(processStats, session.statsInterval, UUID); + + if (!session.rpcs[UUID].stats['Peer-to-Peer_Connection']){ + session.rpcs[UUID].stats['Peer-to-Peer_Connection'] = {}; + } + + var nominatedCandidate = false; + var candidates = {}; + + stats.forEach(stat=>{ + + try { + if (stat.id && stat.id.startsWith("DEPRECATED_")){return;} + + var trackID = stat.trackIdentifier || stat.id || false; + + if ((stat.type=="track") && stat.remoteSource){ + + if (stat.trackIdentifier && !validTrackIds.includes(stat.trackIdentifier)){ + return; + } + + if (stat.id in session.rpcs[UUID].stats){ + session.rpcs[UUID].stats[stat.id]._trackID = stat.trackIdentifier; + session.rpcs[UUID].stats[stat.id].Jitter_Buffer_ms = parseInt(1000*(parseFloat(stat.jitterBufferDelay) - session.rpcs[UUID].stats[stat.id]._jitter_delay)/(parseInt(stat.jitterBufferEmittedCount) - session.rpcs[UUID].stats[stat.id]._jitter_count)) || 0; + session.rpcs[UUID].stats[stat.id]._jitter_delay = parseFloat(stat.jitterBufferDelay) || 0; + session.rpcs[UUID].stats[stat.id]._jitter_count = parseInt(stat.jitterBufferEmittedCount) || 0; + + if ("frameWidth" in stat){ + if ("frameHeight" in stat){ + session.rpcs[UUID].stats[stat.id].Resolution = stat.frameWidth+" x "+stat.frameHeight; + session.rpcs[UUID].stats[stat.id]._frameWidth = stat.frameWidth; + session.rpcs[UUID].stats[stat.id]._frameHeight = stat.frameHeight; + } + } + } else { + var media = {}; + media._jitter_delay = parseFloat(stat.jitterBufferDelay) || 0; + media._jitter_count = parseInt(stat.jitterBufferEmittedCount) || 0; + media.Jitter_Buffer_ms = 0; + media._trackID = stat.trackIdentifier; + + session.rpcs[UUID].stats[stat.id] = media; + + if (stat.kind && stat.kind=="audio"){ + session.rpcs[UUID].stats[stat.id].type = "Audio Track"; + session.rpcs[UUID].stats[stat.id]._type = "audio"; + } else if (stat.kind && stat.kind=="video"){ + session.rpcs[UUID].stats[stat.id].type = "Video Track"; + session.rpcs[UUID].stats[stat.id]._type = "video"; + } + } + } else if (stat.type == "remote-candidate") { + candidates[stat.id] = stat; + } else if (stat.type == "local-candidate") { + candidates[stat.id] = stat; + } else if ((stat.type == "candidate-pair" ) && (stat.nominated)) { + if (!nominatedCandidate){ + nominatedCandidate = stat; + } else if (nominatedCandidate.priority < stat.priority){ + nominatedCandidate = stat; + } + } else if (stat.type == "transport"){ + if ("bytesReceived" in stat) { + if ("_bytesReceived" in session.rpcs[UUID].stats['Peer-to-Peer_Connection']){ + if (session.rpcs[UUID].stats['Peer-to-Peer_Connection']._timestamp){ + if (stat.timestamp){ + session.rpcs[UUID].stats['Peer-to-Peer_Connection'].total_recv_bitrate_kbps = parseInt(8*(stat.bytesReceived - session.rpcs[UUID].stats['Peer-to-Peer_Connection']._bytesReceived)/(stat.timestamp - session.rpcs[UUID].stats['Peer-to-Peer_Connection']._timestamp)); + hideStreamLowBandwidth(session.rpcs[UUID].stats['Peer-to-Peer_Connection'].total_recv_bitrate_kbps, UUID); + //changeSceneLowBandwidth(session.rpcs[UUID].stats['Peer-to-Peer_Connection'].total_recv_bitrate_kbps, UUID); + } + } + } + session.rpcs[UUID].stats['Peer-to-Peer_Connection']._bytesReceived = stat.bytesReceived; + } + if ("timestamp" in stat) { + session.rpcs[UUID].stats['Peer-to-Peer_Connection']._timestamp = stat.timestamp; + if (!session.rpcs[UUID].stats['Peer-to-Peer_Connection']._timestampStart){ + session.rpcs[UUID].stats['Peer-to-Peer_Connection']._timestampStart = stat.timestamp; + } else { + session.rpcs[UUID].stats['Peer-to-Peer_Connection'].time_active_minutes = parseInt((stat.timestamp - session.rpcs[UUID].stats['Peer-to-Peer_Connection']._timestampStart)/600)/100; + } + } + + } else if ((stat.type=="inbound-rtp") && trackID){ + + if (stat.trackIdentifier && !validTrackIds.includes(stat.trackIdentifier)){ + return; + } + + session.rpcs[UUID].stats[trackID] = session.rpcs[UUID].stats[trackID] || {}; + + if (stat.trackIdentifier){ + session.rpcs[UUID].stats[trackID]._trackID = stat.trackIdentifier; + } + if ("jitterBufferDelay" in stat){ + session.rpcs[UUID].stats[trackID].Jitter_Buffer_ms = parseInt(1000*(parseFloat(stat.jitterBufferDelay) - session.rpcs[UUID].stats[trackID]._jitter_delay_2)/(parseInt(stat.jitterBufferEmittedCount) - session.rpcs[UUID].stats[trackID]._jitter_count_2)) || 0; + session.rpcs[UUID].stats[trackID]._jitter_delay_2 = parseFloat(stat.jitterBufferDelay) || 0; + session.rpcs[UUID].stats[trackID]._jitter_count_2 = parseInt(stat.jitterBufferEmittedCount) || 0; + } + + if ("frameWidth" in stat){ + if ("frameHeight" in stat){ + session.rpcs[UUID].stats[trackID].Resolution = stat.frameWidth+" x "+stat.frameHeight; + session.rpcs[UUID].stats[trackID]._frameWidth = stat.frameWidth; + session.rpcs[UUID].stats[trackID]._frameHeight = stat.frameHeight; + } + } + + session.rpcs[UUID].stats[trackID].Bitrate_in_kbps = parseInt(8*(stat.bytesReceived - (session.rpcs[UUID].stats[trackID]._last_bytes || 0))/( stat.timestamp - session.rpcs[UUID].stats[trackID]._last_time)); + + session.rpcs[UUID].stats[trackID]._last_bytes = stat.bytesReceived || session.rpcs[UUID].stats[trackID]._last_bytes; + session.rpcs[UUID].stats[trackID]._last_time = stat.timestamp || session.rpcs[UUID].stats[trackID]._last_time; + + if (stat.mediaType=="video"){ + + session.rpcs[UUID].stats._codecId = stat.codecId; + session.rpcs[UUID].stats._codecIdTrackId = trackID; + + session.rpcs[UUID].stats[trackID].type = "Video Stream" + session.rpcs[UUID].stats[trackID]._type = "video"; + if ((session.obsfix) && ("codec" in session.rpcs[UUID].stats) && (session.rpcs[UUID].stats.codec=="video/VP8")){ + session.rpcs[UUID].stats[trackID].pliDelta = (stat.pliCount - session.rpcs[UUID].stats[trackID].keyFramesRequested_pli) || 0; + session.rpcs[UUID].stats[trackID].nackTrigger = (stat.nackCount - session.rpcs[UUID].stats[trackID].streamErrors_nackCount + session.rpcs[UUID].stats[trackID].nackTrigger) || 0; + + log("OBS PLI FIX MODE ON"); + if ((session.rpcs[UUID].stats[trackID].pliDelta===0) && (session.rpcs[UUID].stats[trackID].nackTrigger >= session.obsfix)){ // heavy packet loss with no pliCount? + session.requestKeyframe(UUID); + session.rpcs[UUID].stats[trackID].nackTrigger = 0; + log("TRYING KEYFRAME"); + } else if (session.rpcs[UUID].stats[trackID].pliDelta>0){ + session.rpcs[UUID].stats[trackID].nackTrigger = 0; + } + } else if ((session.obsfix) && ("codec" in session.rpcs[UUID].stats) && (session.rpcs[UUID].stats.codec=="video/VP9")){ + session.rpcs[UUID].stats[trackID].pliDelta = (stat.pliCount - session.rpcs[UUID].stats[trackID].keyFramesRequested_pli) || 0; + session.rpcs[UUID].stats[trackID].nackTrigger = (stat.nackCount - session.rpcs[UUID].stats[trackID].streamErrors_nackCount + session.rpcs[UUID].stats[trackID].nackTrigger) || 0; + + log("OBS PLI FIX MODE ON"); + if ((session.rpcs[UUID].stats[trackID].pliDelta===0) && (session.rpcs[UUID].stats[trackID].nackTrigger >= (session.obsfix*4) )){ // heavy packet loss with no pliCount? well, VP9 will trigger hopefully not as often. + session.requestKeyframe(UUID); + session.rpcs[UUID].stats[trackID].nackTrigger = 0; + log("TRYING KEYFRAME"); + } else if (session.rpcs[UUID].stats[trackID].pliDelta>0){ + session.rpcs[UUID].stats[trackID].nackTrigger = 0; + } + } + + session.rpcs[UUID].stats[trackID].keyFramesRequested_pli = stat.pliCount || 0; + session.rpcs[UUID].stats[trackID].streamErrors_nackCount = stat.nackCount || 0; + + //warnlog(stat); + + if ("framesPerSecond" in stat){ + session.rpcs[UUID].stats[trackID].FPS = parseInt(stat.framesPerSecond); + } else if (("framesDecoded" in stat) && (stat.timestamp)){ + + var lastFramesDecoded = 0; + var lastTimestamp = 0; + try{ + lastFramesDecoded = session.rpcs[UUID].stats[trackID]._framesDecoded; + lastTimestamp = session.rpcs[UUID].stats[trackID]._timestamp; + } catch(e){} + session.rpcs[UUID].stats[trackID].FPS = parseInt(10*(stat.framesDecoded - lastFramesDecoded)/(stat.timestamp/1000 - lastTimestamp))/10; + + //session.rpcs[UUID].stats[trackID].FPS = parseInt((stat.framesDecoded - lastFramesDecoded)/(stat.timestamp/1000 - lastTimestamp)); + session.rpcs[UUID].stats[trackID]._framesDecoded = stat.framesDecoded; + session.rpcs[UUID].stats[trackID]._timestamp = stat.timestamp/1000; + + } + } else if (stat.mediaType=="audio"){ + //log("AUDIO LEVEL: "+stat.audioLevel); + session.rpcs[UUID].stats._audioCodecId = stat.codecId; + session.rpcs[UUID].stats._audioCodecIdTrackId = trackID; + session.rpcs[UUID].stats[trackID].type = "Audio Stream"; + session.rpcs[UUID].stats[trackID]._type = "audio"; + if ("audioLevel" in stat){ + session.rpcs[UUID].stats[trackID].audio_level = parseInt(parseFloat(stat.audioLevel)*10000)/10000.0; + } + } + + if ("packetsLost" in stat && "packetsReceived" in stat){ + + if (!("_packetsLost" in session.rpcs[UUID].stats[trackID])){ + session.rpcs[UUID].stats[trackID]._packetsLost = stat.packetsLost; + } + if (!("_packetsReceived" in session.rpcs[UUID].stats[trackID])){ + session.rpcs[UUID].stats[trackID]._packetsReceived = stat.packetsReceived; + } + + if (!("packetLoss_in_percentage" in session.rpcs[UUID].stats[trackID])){ + session.rpcs[UUID].stats[trackID].packetLoss_in_percentage = 0; + } + + let packetLoss = ((stat.packetsLost-session.rpcs[UUID].stats[trackID]._packetsLost)*100.0)/((stat.packetsReceived-session.rpcs[UUID].stats[trackID]._packetsReceived)+(stat.packetsLost-session.rpcs[UUID].stats[trackID]._packetsLost)) || 0; + + /* if (session.rpcs[UUID].stats[trackID]._type && (session.rpcs[UUID].stats[trackID]._type =="video")){ + if (packetLoss>1){ + var data = {}; + data.bitrate = parseInt(session.rpcs[UUID].stats[trackID].Bitrate_in_kbps*0.8); + session.sendRequest(data,UUID); + } else { + var data = {}; + data.bitrate = parseInt(session.rpcs[UUID].stats[trackID].Bitrate_in_kbps*1.1); + session.sendRequest(data,UUID); + } + } */ + + session.rpcs[UUID].stats[trackID].packetLoss_in_percentage = session.rpcs[UUID].stats[trackID].packetLoss_in_percentage*0.35 + 0.65*packetLoss; + + if (session.rpcs[UUID].signalMeter && (session.rpcs[UUID].stats[trackID]._type==="video")){ + if (session.rpcs[UUID].stats[trackID].packetLoss_in_percentage<0.01){ + if (session.rpcs[UUID].stats[trackID].Bitrate_in_kbps==0){ + session.rpcs[UUID].signalMeter.dataset.level = 0; + } else { + session.rpcs[UUID].signalMeter.dataset.level = 5; + } + } else if (session.rpcs[UUID].stats[trackID].packetLoss_in_percentage<0.3){ + session.rpcs[UUID].signalMeter.dataset.level = 4; + } else if (session.rpcs[UUID].stats[trackID].packetLoss_in_percentage<1.0){ + session.rpcs[UUID].signalMeter.dataset.level = 3; + } else if (session.rpcs[UUID].stats[trackID].packetLoss_in_percentage<3.5){ + session.rpcs[UUID].signalMeter.dataset.level = 2; + } else { + session.rpcs[UUID].signalMeter.dataset.level = 1; + } + } + + session.rpcs[UUID].stats[trackID]._packetsReceived = stat.packetsReceived; + session.rpcs[UUID].stats[trackID]._packetsLost = stat.packetsLost; + } + + } else if (("_codecId" in session.rpcs[UUID].stats) && (stat.id == session.rpcs[UUID].stats._codecId)){ + + if ("mimeType" in stat){ + if (session.rpcs[UUID].stats[session.rpcs[UUID].stats._codecIdTrackId]){ + session.rpcs[UUID].stats[session.rpcs[UUID].stats._codecIdTrackId].codec = stat.mimeType; + } else { + session.rpcs[UUID].stats[session.rpcs[UUID].stats._codecIdTrackId] = {}; + session.rpcs[UUID].stats[session.rpcs[UUID].stats._codecIdTrackId].codec = stat.mimeType; + } + + } + if ("frameHeight" in stat){ + if ("frameWidth" in stat){ + session.rpcs[UUID].stats.Resolution = parseInt(stat.frameWidth)+" x "+parseInt(stat.frameHeight); + } + } + } else if (("_audioCodecId" in session.rpcs[UUID].stats) && (stat.id == session.rpcs[UUID].stats._audioCodecId)){ + + if ("mimeType" in stat){ + var addOnDescription = stat.mimeType; + addOnDescription = addOnDescription.replace("audio/", ""); + + if ("sdpFmtpLine" in stat){ + if (stat.sdpFmtpLine.includes("useinbandfec=1")){ + addOnDescription += ", /w fec"; + } + } + + if (session.rpcs[UUID].stats[session.rpcs[UUID].stats._audioCodecIdTrackId]){ + } else { + session.rpcs[UUID].stats[session.rpcs[UUID].stats._audioCodecIdTrackId] = {}; + } + session.rpcs[UUID].stats[session.rpcs[UUID].stats._audioCodecIdTrackId].codec = addOnDescription; + session.rpcs[UUID].stats[session.rpcs[UUID].stats._audioCodecIdTrackId] + + } + } else if (Firefox){ + if ("frameWidth" in stat){ + session.rpcs[UUID].stats.resolution = stat.frameWidth +" x " + stat.frameHeight; + if ("framesPerSecond" in stat){ + session.rpcs[UUID].stats.resolution += " @ "+stat.framesPerSecond; + } + } + if (("mimeType" in stat) && ("type" in stat) && ("id" in stat) && (stat.type=="codec")) { + if (stat.mimeType.includes("video")){ + session.rpcs[UUID].stats.video_codec = stat.mimeType.split("video/")[1]; + } else if (stat.mimeType.includes("audio")){ + session.rpcs[UUID].stats.audio_codec = stat.mimeType.split("audio/")[1]; + } + } + /* if ("jitter" in stat){ + if (("kind" in stat) && (stat.kind=="video")){ + session.rpcs[UUID].stats.video_jitter_ms = parseInt(stat.jitter*1000); + } else if (("kind" in stat) && (stat.kind=="audio")){ + session.rpcs[UUID].stats.audio_jitter_ms = parseInt(stat.jitter*1000); + } + } */ + if ("bytesReceived" in stat){ + if (("kind" in stat) && (stat.kind=="video")){ + if ("_bytesReceived_video" in session.rpcs[UUID].stats){ + session.rpcs[UUID].stats.videoBitrate_kbps = parseInt((stat.bytesReceived - session.rpcs[UUID].stats._bytesReceived_video)/(1024*session.statsInterval/8000)); + } + session.rpcs[UUID].stats._bytesReceived_video = stat.bytesReceived + } else if (("kind" in stat) && (stat.kind=="audio")){ + if ("_bytesReceived_audio" in session.rpcs[UUID].stats){ + session.rpcs[UUID].stats.audioBitrate_kbps = parseInt((stat.bytesReceived - session.rpcs[UUID].stats._bytesReceived_audio)/(1024*session.statsInterval/8000)); + } + session.rpcs[UUID].stats._bytesReceived_audio = stat.bytesReceived + } + } + } + } catch(e){ + errorlog(e); + } + }); + + ////////// + + if (nominatedCandidate){ + if (nominatedCandidate.localCandidateId && session.rpcs[UUID].stats['Peer-to-Peer_Connection']._local_ice_id && (session.rpcs[UUID].stats['Peer-to-Peer_Connection']._local_ice_id !== nominatedCandidate.localCandidateId)){ + if ("candidateType" in nominatedCandidate){ + try { + session.rpcs[UUID].stats['Peer-to-Peer_Connection'].local_candidateType = null; + session.rpcs[UUID].stats['Peer-to-Peer_Connection'].local_relay_IP = null; + session.rpcs[UUID].stats['Peer-to-Peer_Connection'].local_relay_protocol = null; + } catch(e){} + } + } + if (nominatedCandidate.remoteCandidateId && session.rpcs[UUID].stats['Peer-to-Peer_Connection']._remote_ice_id && (session.rpcs[UUID].stats['Peer-to-Peer_Connection']._remote_ice_id !== nominatedCandidate.remoteCandidateId)){ + if ("candidateType" in nominatedCandidate){ + try { + session.rpcs[UUID].stats['Peer-to-Peer_Connection'].remote_candidateType = null; + session.rpcs[UUID].stats['Peer-to-Peer_Connection'].remote_relay_IP = null; + session.rpcs[UUID].stats['Peer-to-Peer_Connection'].remote_relay_protocol = null; + } catch(e){} + } + } + if (nominatedCandidate.localCandidateId){ + session.rpcs[UUID].stats['Peer-to-Peer_Connection']._local_ice_id = nominatedCandidate.localCandidateId; + } + if (nominatedCandidate.remoteCandidateId){ + session.rpcs[UUID].stats['Peer-to-Peer_Connection']._remote_ice_id = nominatedCandidate.remoteCandidateId; + } + if ("currentRoundTripTime" in nominatedCandidate){ + session.rpcs[UUID].stats['Peer-to-Peer_Connection'].Round_Trip_Time_ms = nominatedCandidate.currentRoundTripTime*1000; + } + } + + if (nominatedCandidate && nominatedCandidate.remoteCandidateId){ + if (candidates[nominatedCandidate.remoteCandidateId]){ + var candidate = candidates[nominatedCandidate.remoteCandidateId]; + if ("candidateType" in candidate) { + session.rpcs[UUID].stats.remote_candidateType = candidate.candidateType; + if (candidate.candidateType === "relay"){ + if ("ip" in candidate) { + session.rpcs[UUID].stats.remote_relay_IP = candidate.ip; + } + if ("relayProtocol" in candidate) { + session.rpcs[UUID].stats.remote_relay_protocol = candidate.relayProtocol; + } + } else { + try { + delete session.rpcs[UUID].stats.remote_relay_IP; + delete session.rpcs[UUID].stats.remote_relay_protocol; + } catch(e){} + } + } + } + } + if (nominatedCandidate && nominatedCandidate.localCandidateId){ + if (candidates[nominatedCandidate.localCandidateId]){ + var candidate = candidates[nominatedCandidate.localCandidateId]; + if ("candidateType" in candidate) { + session.rpcs[UUID].stats.local_candidateType = candidate.candidateType; + + if (candidate.candidateType === "relay"){ + if ("ip" in candidate) { + session.rpcs[UUID].stats.local_relay_IP = candidate.ip; + } + if ("relayProtocol" in candidate) { + session.rpcs[UUID].stats.local_relay_protocol = candidate.relayProtocol; + } + } else { + try { + delete session.rpcs[UUID].stats.local_relay_IP; + delete session.rpcs[UUID].stats.local_relay_protocol; + } catch(e){} + } + + } + } + } + + /////////// + if (nominatedCandidate && nominatedCandidate.remoteCandidateId){ + if (candidates[nominatedCandidate.remoteCandidateId]){ + var candidate = candidates[nominatedCandidate.remoteCandidateId]; + if ("candidateType" in candidate){ + session.rpcs[UUID].stats['Peer-to-Peer_Connection'].remote_candidateType = candidate.candidateType; + if (candidate.candidateType === "relay"){ + if ("relayProtocol" in candidate){ + session.rpcs[UUID].stats['Peer-to-Peer_Connection'].remote_relay_protocol = candidate.relayProtocol; + } else { + session.rpcs[UUID].stats['Peer-to-Peer_Connection'].remote_relay_protocol = null; + } + if ("ip" in candidate){session.rpcs[UUID].stats['Peer-to-Peer_Connection'].remote_relay_IP = candidate.ip;} + else {session.rpcs[UUID].stats['Peer-to-Peer_Connection'].remote_relay_IP = null;} + } else { + try { + session.rpcs[UUID].stats['Peer-to-Peer_Connection'].local_relay_IP = null; + session.rpcs[UUID].stats['Peer-to-Peer_Connection'].local_relay_protocol = null; + } catch(e){} + } + } + + if ("networkType" in candidate){ + session.rpcs[UUID].stats['Peer-to-Peer_Connection'].remote_networkType = candidate.networkType; + } + } + } + + + if (nominatedCandidate && nominatedCandidate.localCandidateId){ + if (candidates[nominatedCandidate.localCandidateId]){ + var candidate = candidates[nominatedCandidate.localCandidateId]; + + if ("candidateType" in candidate){ + session.rpcs[UUID].stats['Peer-to-Peer_Connection'].local_candidateType = candidate.candidateType; + if (candidate.candidateType === "relay"){ + if ("relayProtocol" in candidate){ + session.rpcs[UUID].stats['Peer-to-Peer_Connection'].local_relay_protocol = candidate.relayProtocol; + } else { + session.rpcs[UUID].stats['Peer-to-Peer_Connection'].local_relay_protocol = null; + } + if ("ip" in candidate){session.rpcs[UUID].stats['Peer-to-Peer_Connection'].local_relay_IP = candidate.ip;} + else {session.rpcs[UUID].stats['Peer-to-Peer_Connection'].local_relay_IP = null;} + } else { + try { + session.rpcs[UUID].stats['Peer-to-Peer_Connection'].local_relay_IP = null; + session.rpcs[UUID].stats['Peer-to-Peer_Connection'].local_relay_protocol = null; + } catch(e){} + } + } + + if ("networkType" in candidate){ + session.rpcs[UUID].stats['Peer-to-Peer_Connection'].local_networkType = candidate.networkType; + } + } + } + + + ////////////// + + playoutdelay(UUID); + + setTimeout(function(){ + session.directorSpeakerMute(); + session.directorDisplayMute(); + },0); + }); + } + } catch (e){errorlog(e);} + + pokeIframeAPI('view-stats-updated', true, UUID); +}; + +function createConnectionDetailsEle(UUID){ + if (!session.rpcs[UUID]){return false;} + + session.rpcs[UUID].connectionDetails = document.createElement("div"); + session.rpcs[UUID].connectionDetails.id = "remoteConnections_" + UUID; + if (session.rpcs[UUID].stats.info && ("total_outbound_p2p_connections" in session.rpcs[UUID].stats.info)){ + session.rpcs[UUID].connectionDetails.innerText = "🔗"+session.rpcs[UUID].stats.info.total_outbound_p2p_connections; + session.rpcs[UUID].connectionDetails.dataset.value = session.rpcs[UUID].stats.info.total_outbound_p2p_connections; + } + session.rpcs[UUID].connectionDetails.dataset.UUID = UUID; + session.rpcs[UUID].connectionDetails.title = getTranslation("viewer-count"); + session.rpcs[UUID].connectionDetails.className = "rem-con-count"; + + session.rpcs[UUID].connectionDetails.addEventListener('click', function(e) { // show stats of video if double clicked + log("clicked connectionDetails icon "); + try { + e.preventDefault(); + if (session.statsMenu !==false){ + var uid = e.currentTarget.dataset.UUID; + if ("stats" in session.rpcs[uid]){ + + var [menu, innerMenu] = statsMenuCreator(); + printViewStats(innerMenu, uid ); + menu.interval = setInterval(printViewStats, session.statsInterval, innerMenu, uid); + + } + } + e.stopPropagation(); + return false; + + } catch(e){errorlog(e);} + }); + return true +} + +function playoutdelay(UUID){ // applies a delay to all videos + try { + var target_buffer = session.buffer; + try{ + if (session.rpcs[UUID].buffer!==false){ + target_buffer = session.rpcs[UUID].buffer; + } + } catch(e){warnlog(e);} + + if (target_buffer!==false){ + + target_buffer = parseFloat(target_buffer); + + // if buffer is set, then session.sync will be set; at least to 0. + var receivers = getReceivers2(UUID).reverse() || []; //session.rpcs[UUID].getReceivers().reverse(); + + if (session.rpcs[UUID].whep){ + receivers = receivers.concat(getReceiversMC(UUID).reverse()); // if I try to reuse getReceivers2, I get some confused stats (not able to tell tracks apart) TODO: see if this issue is a problem else where, esp with screen shares. sstype==3 + } + receivers.forEach(function(receiver){ + try { + for (var tid in session.rpcs[UUID].stats){ + + if ((typeof( session.rpcs[UUID].stats[tid])=="object") && ("_trackID" in session.rpcs[UUID].stats[tid]) && (session.rpcs[UUID].stats[tid]._trackID===receiver.track.id) && (session.rpcs[UUID].stats[tid]._type == receiver.track.kind) && ("Jitter_Buffer_ms" in session.rpcs[UUID].stats[tid])){ + + + //if (ChromiumVersion<=103){ // I don't know the exact version, except I know OBS Studio is 103 and it uses the old way still.netwqor + var sync_offset = 0.0; + + if (session.rpcs[UUID].stats[tid]._sync_offset){ + sync_offset = session.rpcs[UUID].stats[tid]._sync_offset; + } else { + session.rpcs[UUID].stats[tid]._sync_offset = 0; + } + + sync_offset += target_buffer; + sync_offset -= session.rpcs[UUID].stats[tid].Jitter_Buffer_ms; + + if (session.includeRTT){ + sync_offset -= parseInt(session.rpcs[UUID].stats['Peer-to-Peer_Connection'].Round_Trip_Time_ms/2); // I can't be sure what the actual one-way delay is + } + + if (sync_offset>target_buffer){ + sync_offset=target_buffer; + } + + if (sync_offset<0){sync_offset=0;} + + session.rpcs[UUID].stats[tid].Added_Buffer_Delay_ms = sync_offset; + + session.rpcs[UUID].stats[tid].Total_Playout_Delay_ms = sync_offset + parseInt(session.rpcs[UUID].stats['Peer-to-Peer_Connection'].Round_Trip_Time_ms/2) + session.rpcs[UUID].stats[tid].Jitter_Buffer_ms; + + if (session.rpcs[UUID].stats[tid]._type=="audio"){ + + session.rpcs[UUID].stats[tid]._sync_offset = sync_offset; + + receiver.playoutDelayHint = parseFloat(sync_offset/1000); + // receiver.jitterBufferDelayhint = parseFloat(sync_offset/1000); // This is deprecated I believe + + if (session.sync!==false){ + var audio_delay = session.sync || 0; // video is typically showing greater delay than audio. + audio_delay += target_buffer - session.rpcs[UUID].stats[tid].Jitter_Buffer_ms; + + if ((receiver.track.kind=="audio") && (receiver.track.id in session.rpcs[UUID].inboundAudioPipeline)){ + if (session.rpcs[UUID].inboundAudioPipeline[receiver.track.id] && session.rpcs[UUID].inboundAudioPipeline[receiver.track.id].delayNode){ + if (audio_delay<0){audio_delay=0;} + try { + session.rpcs[UUID].inboundAudioPipeline[receiver.track.id].delayNode.delayTime.linearRampToValueAtTime(parseFloat(audio_delay/1000.0), session.audioCtx.currentTime + parseFloat(session.statsInterval/9000)); + } catch(e){ + session.rpcs[UUID].inboundAudioPipeline[receiver.track.id].delayNode.delayTime.setValueAtTime(parseFloat(audio_delay/1000.0), session.audioCtx.currentTime+1); + } + session.rpcs[UUID].stats[tid].Audio_Sync_Delay_ms = audio_delay; + } + } + } + } else if (session.rpcs[UUID].stats[tid]._type=="video"){ + + session.rpcs[UUID].stats[tid]._sync_offset = sync_offset; + receiver.playoutDelayHint = parseFloat(sync_offset/1000); // Chrome seems to somewhat sync audio and video when using the delay + // receiver.jitterBufferDelayhint = parseFloat(sync_offset/1000); // This is deprecated I believe + } + + + } + } + } catch (e){errorlog(e);} + }); + } + } catch (e){ + errorlog(e); + warnlog("device does not support playout delay"); + } +}; + + +function printViewStats(menu, UUID) { // Stats for viewing a remote video + if (session.statsMenu === false){ + return false; + } + + if (!session.rpcs[UUID]){ + menu.innerHTML = "


    Remote Publisher Disconnected"; + return false; + } + + var statsObj = session.rpcs[UUID].stats; + var streamID = session.rpcs[UUID].streamID; + var scrollLeft = menu.scrollLeft; + var scrollTop = menu.scrollTop; + menu.innerHTML = "StreamID: " + streamID + "
    "; + + //// doesn't work on viewer side. + //if (session.rpcs && session.rpcs[UUID] && session.rpcs[UUID] && session.rpcs[UUID].restartIce){ // only show if available + // menu.innerHTML += ""; + //} + + menu.innerHTML += printValues(statsObj); + menu.scrollTop = scrollTop; + menu.scrollLeft = scrollLeft; + return true; +} + + +function plotDataSimple(canvas, bitrate, nacks=0) { + canvas.height = 50; + canvas.width = 124; + canvas.className = "canvasStats"; + var context = canvas.getContext("2d"); + if (isNaN(bitrate)) { + bitrate = 0; + } + if (isNaN(nacks)) { + nacks = 0; + } + var height = context.canvas.height; + var width = context.canvas.width; + + var val = (10-nacks)/10; + if (val>1){val=1;} + else if (val<0){val=0;} + + var yScale = height / 4000; + var x = width - 1; + var y = height - bitrate * yScale; + var w = 1; + + context.fillStyle = getColor(val);; + context.fillRect(x, y, w, height); + context.fillStyle = "#FFFFFF55"; + context.fillRect(x, y-2, w, 4); + + if (y-5>0){ + context.fillStyle = "#FFFFFF44"; + context.fillRect(x, y+2, w, 1); + } + + context.putImageData(context.getImageData(1, 0, width - 1, height), 0, 0); + context.clearRect(width - 1, 0, 1, height); +} + +function printValues(obj) { // see: printViewStats + var out = ""; + for (var key in obj) { + if (typeof obj[key] === "object") { + if (obj[key] != null) { + var tmp = key; + tmp = sanitizeChat((tmp)); + out += "
  • " + tmp + "

  • " + out += printValues(obj[key]); + } + } else { + if (key.startsWith("_")) { + // if it starts with _, we don't want to show it. + } else { + try { + var unit = ''; + + var value = obj[key]; + + var stat = sanitizeChat(key); + + var hint = ""; + + if (typeof obj[key] == "string") { + value = sanitizeChat((value)); + } + + + if (key == 'useragent') { + value = ""+value+"" + } + + if (key == 'Bitrate_in_kbps') { + var unit = " kbps"; + stat = "Bitrate"; + hint = "You can refer to the documentation for ways to increase the target bitrate"; + } + else if (key == 'type') { + var unit = ""; + stat = 'Type'; + + if (value == "Audio Track") { + value = "🔊 " + value; + //out += ""; + } + + if (value == "Video Track") { + value = "📺 " + value; + } + + } + else if (key == 'packetLoss_in_percentage') { + var unit = " %"; + stat = 'Packet Loss 📶'; + value = parseInt(parseFloat(value) * 10000) / 10000.0; + hint = "A high packet loss will lower quality of the media"; + + } + else if (key == 'local_relay_IP') { + value = "" + value + ""; + } + else if (key == 'remote_relay_IP') { + value = "" + value + ""; + } + else if ((key == 'local_candidateType') && (value == "relay")){ + value = "💸 relay server"; + hint = 'no direct p2p connection made; using the TURN relay servers.'; + } + else if ((key == 'remote_candidateType') && (value == "relay")) { + value = "💸 relay server" + hint = 'no direct p2p connection made; using the TURN relay servers.'; + } + else if ((key == 'local_candidateType') && (value == "host")){ + hint = 'No NAT firewall, typical of LAN to LAN'; + } + else if ((key == 'remote_candidateType') && (value == "host")) { + hint = 'No NAT firewall, typical of LAN to LAN'; + } + else if ((key == 'local_candidateType') && (value == "srflx")){ + hint = 'direct p2p, but NAT firewall likely'; + } + else if ((key == 'remote_candidateType') && (value == "srflx")) { + hint = 'direct p2p, but NAT firewall likely'; + } + else if (key == 'height_url') { + if (value == false) { + continue; + } + } + else if (key == 'width_url') { + if (value == false) { + continue; + } + } + else if (key == 'height_url') { + if (value == false) { + continue; + } + } + else if (key == 'version') { + stat = "VDO.Ninja Version"; + } + else if (key == 'platform') { + stat = "Platform (OS)"; + } + else if (key == 'iPhone12Up') { + stat = "iPhone 12 and up"; + } + else if (key == 'aec_url') { + stat = "Echo-Cancellation"; + } + else if (key == 'agc_url') { + stat = "Auto-Gain (agc)"; + } + else if (key == 'denoise_url') { + stat = "De-noising "; + } + else if (key == 'audio_level') { + stat = "Audio Level"; + } + else if (key == 'Jitter_Buffer_ms') { + var unit = " ms"; + stat = 'Jitter Buffer Delay'; + } + else if (key == 'Added_Buffer_Delay_ms') { + var unit = " ms"; + stat = 'Added Buffer Delay'; + hint = "Value of playout buffer delay added if using &buffer"; + } + else if (key == 'Total_Playout_Delay_ms') { // doesn't include bluetooth / monitor / capture delay, etc. + var unit = " ms"; + stat = 'Total Playout Delay'; + hint = "Network latency + Jitter buffer + any manually added playout delay" + } + else if (value === null) { + value = "null"; + } + else if (key == "stereo_url") { + stat = "Pro-Audio
    (Stereo-mode)"; + if (value == 3) { + value = "3 (outbound hi-fi)
    Use Headphones"; + } else if (value == 1) { + value = "1 (in & out hi-fi)
    Use Headphones"; + } else if (value == 2) { + value = "3 (inbound hi-fi)"; + } else if (value == 4) { + value = "3 (multichannel)
    Use Headphones"; + } else if (value == 5) { + value = "5 (auto-mode)
    Use Headphones"; + } + } + else if (value === false) { + continue + } + else if (value === "false") { + continue + } + + stat = stat.replaceAll("_", " "); + stat = stat.trim(); + + if (hint){ + out += "
  • " + stat + "" + value + unit + "
  • "; + } else { + out += "
  • " + stat + "" + value + unit + "
  • "; + } + } catch (e) { + warnlog(e); + } + } + } + } + return out; +} + +function processMeshcastStats(UUID){ + try { + session.rpcs[UUID].whep.getStats().then(function(stats){ + if (!(UUID in session.rpcs)){return;} + + if (!session.rpcs[UUID].stats['Meshcast_Connection']){ + session.rpcs[UUID].stats['Meshcast_Connection'] = {}; + } + + // var qos = false;] + + var nominatedCandidate = false; + var candidates = {}; + + stats.forEach(stat=>{ + + if (stat.id && stat.id.startsWith("DEPRECATED_")){return;} + + var trackID = stat.trackIdentifier || stat.id || false; + + + if (stat.type == "remote-candidate") { + candidates[stat.id] = stat; + } else if (stat.type == "local-candidate") { + candidates[stat.id] = stat; + } else if ((stat.type == "candidate-pair" ) && (stat.nominated)) { + if (!nominatedCandidate){ + nominatedCandidate = stat; + } else if (nominatedCandidate.priority < stat.priority){ + nominatedCandidate = stat; + } + } else if ((stat.type=="track") && stat.remoteSource){ + + if (stat.id in session.rpcs[UUID].stats){ + session.rpcs[UUID].stats[stat.id]._trackID = stat.trackIdentifier; + session.rpcs[UUID].stats[stat.id].Jitter_Buffer_ms = parseInt(1000*(parseFloat(stat.jitterBufferDelay) - session.rpcs[UUID].stats[stat.id]._jitter_delay)/(parseInt(stat.jitterBufferEmittedCount) - session.rpcs[UUID].stats[stat.id]._jitter_count)) || 0; + session.rpcs[UUID].stats[stat.id]._jitter_delay = parseFloat(stat.jitterBufferDelay) || 0; + session.rpcs[UUID].stats[stat.id]._jitter_count = parseInt(stat.jitterBufferEmittedCount) || 0; + + if ("frameWidth" in stat){ + if ("frameHeight" in stat){ + session.rpcs[UUID].stats[stat.id].Resolution = stat.frameWidth+" x "+stat.frameHeight; + session.rpcs[UUID].stats[stat.id]._frameWidth = stat.frameWidth; + session.rpcs[UUID].stats[stat.id]._frameHeight = stat.frameHeight; + } + } + } else { + session.rpcs[UUID].stats[stat.id] = {}; + session.rpcs[UUID].stats[stat.id]._jitter_delay = parseFloat(stat.jitterBufferDelay) || 0; + session.rpcs[UUID].stats[stat.id]._jitter_count = parseInt(stat.jitterBufferEmittedCount) || 0; + session.rpcs[UUID].stats[stat.id].Jitter_Buffer_ms = 0; + session.rpcs[UUID].stats[stat.id]._trackID = stat.trackIdentifier; + + if (stat.kind && stat.kind=="audio"){ + session.rpcs[UUID].stats[stat.id].type = "Audio Track"; + session.rpcs[UUID].stats[stat.id]._type = "audio"; + } else if (stat.kind && (stat.kind=="video")){ + session.rpcs[UUID].stats[stat.id].type = "Video Track"; + session.rpcs[UUID].stats[stat.id]._type = "video"; + } + } + } else if (stat.type == "transport"){ + if ("bytesReceived" in stat) { + if ("_bytesReceived" in session.rpcs[UUID].stats['Meshcast_Connection']){ + if (session.rpcs[UUID].stats['Meshcast_Connection']._timestamp){ + if (stat.timestamp){ + session.rpcs[UUID].stats['Meshcast_Connection'].total_recv_bitrate_kbps = parseInt(8*(stat.bytesReceived - session.rpcs[UUID].stats['Meshcast_Connection']._bytesReceived)/(stat.timestamp - session.rpcs[UUID].stats['Meshcast_Connection']._timestamp)); + } + } + } + session.rpcs[UUID].stats['Meshcast_Connection']._bytesReceived = stat.bytesReceived; + } + if ("timestamp" in stat) { + session.rpcs[UUID].stats['Meshcast_Connection']._timestamp = stat.timestamp; + if (!session.rpcs[UUID].stats['Meshcast_Connection']._timestampStart){ + session.rpcs[UUID].stats['Meshcast_Connection']._timestampStart = stat.timestamp; + } else { + session.rpcs[UUID].stats['Meshcast_Connection'].time_active_minutes = parseInt((stat.timestamp - session.rpcs[UUID].stats['Meshcast_Connection']._timestampStart)/600)/100; + } + } + + } else if ((stat.type=="inbound-rtp") && trackID){ + + session.rpcs[UUID].stats[trackID] = session.rpcs[UUID].stats[trackID] || {}; + + if (stat.trackIdentifier){ + session.rpcs[UUID].stats[trackID]._trackID = stat.trackIdentifier; + } + if ("jitterBufferDelay" in stat){ + session.rpcs[UUID].stats[trackID].Jitter_Buffer_ms = parseInt(1000*(parseFloat(stat.jitterBufferDelay) - session.rpcs[UUID].stats[trackID]._jitter_delay_2)/(parseInt(stat.jitterBufferEmittedCount) - session.rpcs[UUID].stats[trackID]._jitter_count_2)) || 0; + session.rpcs[UUID].stats[trackID]._jitter_delay_2 = parseFloat(stat.jitterBufferDelay) || 0; + session.rpcs[UUID].stats[trackID]._jitter_count_2 = parseInt(stat.jitterBufferEmittedCount) || 0; + } + + if ("frameWidth" in stat){ + if ("frameHeight" in stat){ + session.rpcs[UUID].stats[trackID].Resolution = stat.frameWidth+" x "+stat.frameHeight; + session.rpcs[UUID].stats[trackID]._frameWidth = stat.frameWidth; + session.rpcs[UUID].stats[trackID]._frameHeight = stat.frameHeight; + } + } + + session.rpcs[UUID].stats[trackID].Bitrate_in_kbps = parseInt(8*(stat.bytesReceived - (session.rpcs[UUID].stats[trackID]._last_bytes || 0))/( stat.timestamp - session.rpcs[UUID].stats[trackID]._last_time)); + + session.rpcs[UUID].stats[trackID]._last_bytes = stat.bytesReceived || session.rpcs[UUID].stats[trackID]._last_bytes; + session.rpcs[UUID].stats[trackID]._last_time = stat.timestamp || session.rpcs[UUID].stats[trackID]._last_time; + + session.rpcs[UUID].stats._codecId = stat.codecId; + session.rpcs[UUID].stats._codecIdTrackId = trackID; + + if (stat.mediaType=="video"){ + session.rpcs[UUID].stats[trackID].type = "Video Stream" + session.rpcs[UUID].stats[trackID]._type = "video"; + if ((session.obsfix) && ("codec" in session.rpcs[UUID].stats) && (session.rpcs[UUID].stats.codec=="video/VP8")){ + session.rpcs[UUID].stats[trackID].pliDelta = (stat.pliCount - session.rpcs[UUID].stats[trackID].keyFramesRequested_pli) || 0; + session.rpcs[UUID].stats[trackID].nackTrigger = (stat.nackCount - session.rpcs[UUID].stats[trackID].streamErrors_nackCount + session.rpcs[UUID].stats[trackID].nackTrigger) || 0; + + log("OBS PLI FIX MODE ON"); + if ((session.rpcs[UUID].stats[trackID].pliDelta===0) && (session.rpcs[UUID].stats[trackID].nackTrigger >= session.obsfix)){ // heavy packet loss with no pliCount? + session.requestKeyframe(UUID); + session.rpcs[UUID].stats[trackID].nackTrigger = 0; + log("TRYING KEYFRAME"); + } else if (session.rpcs[UUID].stats[trackID].pliDelta>0){ + session.rpcs[UUID].stats[trackID].nackTrigger = 0; + } + } else if ((session.obsfix) && ("codec" in session.rpcs[UUID].stats) && (session.rpcs[UUID].stats.codec=="video/VP9")){ + session.rpcs[UUID].stats[trackID].pliDelta = (stat.pliCount - session.rpcs[UUID].stats[trackID].keyFramesRequested_pli) || 0; + session.rpcs[UUID].stats[trackID].nackTrigger = (stat.nackCount - session.rpcs[UUID].stats[trackID].streamErrors_nackCount + session.rpcs[UUID].stats[trackID].nackTrigger) || 0; + + log("OBS PLI FIX MODE ON"); + if ((session.rpcs[UUID].stats[trackID].pliDelta===0) && (session.rpcs[UUID].stats[trackID].nackTrigger >= (session.obsfix*4) )){ // heavy packet loss with no pliCount? well, VP9 will trigger hopefully not as often. + session.requestKeyframe(UUID); + session.rpcs[UUID].stats[trackID].nackTrigger = 0; + log("TRYING KEYFRAME"); + } else if (session.rpcs[UUID].stats[trackID].pliDelta>0){ + session.rpcs[UUID].stats[trackID].nackTrigger = 0; + } + } + + session.rpcs[UUID].stats[trackID].keyFramesRequested_pli = stat.pliCount || 0; + session.rpcs[UUID].stats[trackID].streamErrors_nackCount = stat.nackCount || 0; + + //warnlog(stat); + + if ("framesPerSecond" in stat){ + session.rpcs[UUID].stats[trackID].FPS = parseInt(stat.framesPerSecond); + } else if (("framesDecoded" in stat) && (stat.timestamp)){ + + var lastFramesDecoded = 0; + var lastTimestamp = 0; + try{ + lastFramesDecoded = session.rpcs[UUID].stats[trackID]._framesDecoded; + lastTimestamp = session.rpcs[UUID].stats[trackID]._timestamp; + } catch(e){} + session.rpcs[UUID].stats[trackID].FPS = parseInt(10*(stat.framesDecoded - lastFramesDecoded)/(stat.timestamp/1000 - lastTimestamp))/10; + + //session.rpcs[UUID].stats[trackID].FPS = parseInt((stat.framesDecoded - lastFramesDecoded)/(stat.timestamp/1000 - lastTimestamp)); + session.rpcs[UUID].stats[trackID]._framesDecoded = stat.framesDecoded; + session.rpcs[UUID].stats[trackID]._timestamp = stat.timestamp/1000; + + } + + + } else if (stat.mediaType=="audio"){ + //log("AUDIO LEVEL: "+stat.audioLevel); + session.rpcs[UUID].stats[trackID].type = "Audio Stream"; + session.rpcs[UUID].stats[trackID]._type = "audio"; + if ("audioLevel" in stat){ + session.rpcs[UUID].stats[trackID].audio_level = parseInt(parseFloat(stat.audioLevel)*10000)/10000.0; + } + } + + if ("packetsLost" in stat && "packetsReceived" in stat){ + + if (!("_packetsLost" in session.rpcs[UUID].stats[trackID])){ + session.rpcs[UUID].stats[trackID]._packetsLost = stat.packetsLost; + } + if (!("_packetsReceived" in session.rpcs[UUID].stats[trackID])){ + session.rpcs[UUID].stats[trackID]._packetsReceived = stat.packetsReceived; + } + + if (!("packetLoss_in_percentage" in session.rpcs[UUID].stats[trackID])){ + session.rpcs[UUID].stats[trackID].packetLoss_in_percentage = 0; + } + + session.rpcs[UUID].stats[trackID].packetLoss_in_percentage = session.rpcs[UUID].stats[trackID].packetLoss_in_percentage*0.35 + 0.65*((stat.packetsLost-session.rpcs[UUID].stats[trackID]._packetsLost)*100.0)/((stat.packetsReceived-session.rpcs[UUID].stats[trackID]._packetsReceived)+(stat.packetsLost-session.rpcs[UUID].stats[trackID]._packetsLost)) || 0; + + if (session.rpcs[UUID].stats[trackID]._type==="video"){ + qos = session.rpcs[UUID].stats[trackID].packetLoss_in_percentage; // packet loss of video track + } + + if (session.rpcs[UUID].signalMeter && (session.rpcs[UUID].stats[trackID]._type==="video")){ + if (session.rpcs[UUID].stats[trackID].packetLoss_in_percentage<0.01){ + if (session.rpcs[UUID].stats[trackID].Bitrate_in_kbps==0){ + session.rpcs[UUID].signalMeter.dataset.level = 0; + } else { + session.rpcs[UUID].signalMeter.dataset.level = 5; + } + } else if (session.rpcs[UUID].stats[trackID].packetLoss_in_percentage<0.3){ + session.rpcs[UUID].signalMeter.dataset.level = 4; + } else if (session.rpcs[UUID].stats[trackID].packetLoss_in_percentage<1.0){ + session.rpcs[UUID].signalMeter.dataset.level = 3; + } else if (session.rpcs[UUID].stats[trackID].packetLoss_in_percentage<3.5){ + session.rpcs[UUID].signalMeter.dataset.level = 2; + } else { + session.rpcs[UUID].signalMeter.dataset.level = 1; + } + } + + session.rpcs[UUID].stats[trackID]._packetsReceived = stat.packetsReceived; + session.rpcs[UUID].stats[trackID]._packetsLost = stat.packetsLost; + } + + } else if (("_codecId" in session.rpcs[UUID].stats) && (stat.id == session.rpcs[UUID].stats._codecId)){ + + if ("mimeType" in stat){ + if (session.rpcs[UUID].stats[session.rpcs[UUID].stats._codecIdTrackId]){ + session.rpcs[UUID].stats[session.rpcs[UUID].stats._codecIdTrackId].codec = stat.mimeType; + } else { + session.rpcs[UUID].stats[session.rpcs[UUID].stats._codecIdTrackId] = {}; + session.rpcs[UUID].stats[session.rpcs[UUID].stats._codecIdTrackId].codec = stat.mimeType; + } + } + if ("frameHeight" in stat){ + if ("frameWidth" in stat){ + session.rpcs[UUID].stats.Resolution = parseInt(stat.frameWidth)+" x "+parseInt(stat.frameHeight); + } + } + } else if (Firefox){ + if (("mimeType" in stat) && ("type" in stat) && ("id" in stat) && (stat.type=="codec")) { + + if (stat.mimeType.includes("video")){ + session.rpcs[UUID].stats.video_codec = stat.mimeType.split("video/")[1]; + } else if (stat.mimeType.includes("audio")){ + session.rpcs[UUID].stats.audio_codec = stat.mimeType.split("audio/")[1]; + } + } + + if ("frameWidth" in stat){ + session.rpcs[UUID].stats.resolution = stat.frameWidth +" x " + stat.frameHeight; + if ("framesPerSecond" in stat){ + session.rpcs[UUID].stats.resolution += " @ "+stat.framesPerSecond; + } + } + + if ("bytesReceived" in stat){ + + if (("kind" in stat) && (stat.kind=="video")){ + if ("_bytesReceived_video" in session.rpcs[UUID].stats){ + session.rpcs[UUID].stats.videoBitrate_kbps = parseInt((stat.bytesReceived - session.rpcs[UUID].stats._bytesReceived_video)/(1024*session.statsInterval/8000)); + } + session.rpcs[UUID].stats._bytesReceived_video = stat.bytesReceived + } else if (("kind" in stat) && (stat.kind=="audio")){ + if ("_bytesReceived_audio" in session.rpcs[UUID].stats){ + session.rpcs[UUID].stats.audioBitrate_kbps = parseInt((stat.bytesReceived - session.rpcs[UUID].stats._bytesReceived_audio)/(1024*session.statsInterval/8000)); + } + session.rpcs[UUID].stats._bytesReceived_audio = stat.bytesReceived + } + } + } + }); + + //////////// + + if (nominatedCandidate){ + if ("currentRoundTripTime" in nominatedCandidate){ + session.rpcs[UUID].stats['Meshcast_Connection'].Round_Trip_Time_ms = nominatedCandidate.currentRoundTripTime*1000; + } + } + + if (nominatedCandidate && nominatedCandidate.remoteCandidateId){ + if (candidates[nominatedCandidate.remoteCandidateId]){ + var candidate = candidates[nominatedCandidate.remoteCandidateId]; + if ("candidateType" in candidate){ + session.rpcs[UUID].stats['Meshcast_Connection'].remote_candidateType = candidate.candidateType; + if (candidate.candidateType === "relay"){ + if ("relayProtocol" in candidate){ + session.rpcs[UUID].stats['Meshcast_Connection'].remote_relay_protocol = candidate.relayProtocol; + } + if ("ip" in candidate){session.rpcs[UUID].stats['Meshcast_Connection'].remote_relay_IP = candidate.ip;} + } else { + try { + delete session.rpcs[UUID].stats['Meshcast_Connection'].local_relay_IP; + delete session.rpcs[UUID].stats['Meshcast_Connection'].local_relay_protocol; + } catch(e){} + } + if ("networkType" in candidate){ + session.rpcs[UUID].stats['Meshcast_Connection'].remote_networkType = candidate.networkType; + } + } + } + } + if (nominatedCandidate && nominatedCandidate.localCandidateId){ + if (candidates[nominatedCandidate.localCandidateId]){ + var candidate = candidates[nominatedCandidate.localCandidateId]; + if ("candidateType" in candidate){ + session.rpcs[UUID].stats['Meshcast_Connection'].local_candidateType = candidate.candidateType; + if (candidate.candidateType === "relay"){ + if ("relayProtocol" in candidate){ + session.rpcs[UUID].stats['Meshcast_Connection'].local_relay_protocol = candidate.relayProtocol; + } + if ("ip" in candidate){session.rpcs[UUID].stats['Meshcast_Connection'].local_relay_IP = candidate.ip;} + } else { + try { + delete session.rpcs[UUID].stats['Meshcast_Connection'].local_relay_IP; + delete session.rpcs[UUID].stats['Meshcast_Connection'].local_relay_protocol; + } catch(e){} + } + } + + if ("networkType" in candidate){ + session.rpcs[UUID].stats['Meshcast_Connection'].local_networkType = candidate.networkType; + } + } + } + + /* try{ // we want to let meshcast know if our node is getting overloaded, to avoid making it worse + if ((qos!==false) && session.rpcs[UUID].settings && session.rpcs[UUID].settings.url){ + var request = new XMLHttpRequest(); + var node = session.rpcs[UUID].settings.url.split("https://")[1].split(".meshcast.io")[0]; + if (node){ + request.open('POST', " https://qos.meshcast.io/?name="+node); + request.send(qos); + } + } + } catch(e){ + errorlog(e); + } */ + + //if (session.buffer!==false){ + playoutdelay(UUID); // it will handle itself for now on I guess + //} + }); + } catch (e){errorlog(e);} +} + + +function printMyStats(menu, screenshare=false) { // see: setupStatsMenu + + if (!session){return;} + + var scrollLeft = getById("menuStatsBox").scrollLeft; + var scrollTop = getById("menuStatsBox").scrollTop; + menu.innerHTML = ""; + + try { + session.stats.outbound_connections = Object.keys(session.pcs).length; + session.stats.inbound_connections = Object.keys(session.rpcs).length; + } catch(e){} + + try { + var obscam = false; + if (document.querySelector("select#videoSource3")){ + var videoSelect = document.querySelector("select#videoSource3").options; + if (videoSelect.length){ + if (videoSelect[videoSelect.selectedIndex].text.startsWith("OBS-Camera")) { // OBS Virtualcam + obscam = true; + } else if (videoSelect[videoSelect.selectedIndex].text.startsWith("OBS Virtual Camera")) { // OBS Virtualcam + obscam = true; + } + } + } + + if (session.streamSrc){ + session.streamSrc.getVideoTracks().forEach(function(track) { + session.currentCameraConstraints = track.getSettings(); + + if (screen && screen.orientation && screen.orientation.type){ + if (!screen.orientation.type.includes("portrait")){ + if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ + session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; + } + } + } else if (!window.matchMedia("(orientation: portrait)").matches){ // legacy + if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ + session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; + } + } + + if (obscam && (parseInt(session.currentCameraConstraints.frameRate) == 30)) { + session.stats.video_settings =(session.currentCameraConstraints.width || 0) + "x" + (session.currentCameraConstraints.height || 0); + } else { + var frameRateFPS = session.currentCameraConstraints.frameRate; + if (frameRateFPS){ + session.stats.video_settings = (session.currentCameraConstraints.width || 0) + "x" + (session.currentCameraConstraints.height || 0) + " @ " + (parseInt(frameRateFPS * 100) / 100.0) + "fps"; + } else { + session.stats.video_settings = (session.currentCameraConstraints.width || 0) + "x" + (session.currentCameraConstraints.height || 0); + } + } + }); + } + } catch(e){errorlog(e);} + + function printViewValues(obj, UUID=false) { + + if (!(document.getElementById("menuStatsBox"))){ + return; + } + + var keys = Object.keys(obj); + + keys.forEach(key=>{ + if (typeof obj[key] === "object") { + try{ + var tmp = key; + tmp = sanitizeChat((tmp)); + if (tmp === "info"){ + tmp = "Remote Peer Info"; + } + menu.innerHTML += "
  • " + tmp + "

  • " + } catch(e){} + printViewValues(obj[key]); + menu.innerHTML += "
    "; + } + }); + + if (UUID && session.pcs[UUID] && session.pcs[UUID].restartIce){ // only show if available + menu.innerHTML += ""; + } + + + keys.forEach(key=>{ + if (typeof obj[key] !== "object") { + if (key.startsWith("_")){return;} + + var stat = sanitizeChat(key); + var value = obj[key]; + if (typeof value == "string") { + value = sanitizeChat((value)); + } + + if (value === false){return;} + + if (key == 'useragent') { + value = ""+value+"" + } + + if (key == 'local_relay_IP') { + value = "" + value + ""; + } + if (key == 'remote_relay_IP') { + value = "" + value + ""; + } + if (key == 'watch_URL') { + value = "" + value + ""; + } + if ((key == 'local_candidateType') && (value == "relay")){ + value = "💸

    relay server

    "; + } + else if ((key == 'remote_candidateType') && (value == "relay")) { + value = "💸

    relay server

    "; + } + else if ((key == 'local_candidateType') && (value == "host")){ + value = "

    host

    "; + } + else if ((key == 'remote_candidateType') && (value == "host")) { + value = "

    host

    "; + } + else if ((key == 'local_candidateType') && (value == "srflx")){ + value = "

    srflx

    "; + } + else if ((key == 'remote_candidateType') && (value == "srflx")) { + value = "

    srflx

    "; + } + menu.innerHTML += "
  • " + stat + "" + value + "
  • "; + } + }); + + if (UUID && session.pcs[UUID]){ + if (session.pcs[UUID].maxBandwidth){ + menu.innerHTML += "
  • max bandwidth target" + session.pcs[UUID].maxBandwidth + "
  • "; + } + if (session.pcs[UUID].setBitrate){ + menu.innerHTML += "
  • init bitrate target" + session.pcs[UUID].setBitrate + "
  • "; + } + if (session.pcs[UUID].savedBitrate){ + menu.innerHTML += "
  • current bitrate target" + session.pcs[UUID].savedBitrate + "
  • "; + } + if (session.room!==false && !session.pcs[UUID].whipout && (session.meshcast!=="audio")){ + menu.innerHTML += "
  • adjust video bitrate
  • "; + } + } + } + + printViewValues(session.stats); + menu.innerHTML += ""; + + if (!screenshare && session.meshcast && session.whipOut && session.whipOut.stats){ + printViewValues({"Meshcast_connection":session.whipOut.stats}); + menu.innerHTML += "
    "; + } else if (!screenshare && session.whipOut && session.whipOut.stats){ + printViewValues({"Whip_Out_connection":session.whipOut.stats}); + menu.innerHTML += "
    "; + } + if (!screenshare && session.whepIn && session.whepIn.stats){ + printViewValues({"Whep_In_connection":session.whepIn.stats}); + menu.innerHTML += "
    "; + } + + for (var uuid in session.pcs) { + if (screenshare){ + if (session.pcs[uuid].realUUID){ + printViewValues(session.pcs[uuid].stats, uuid); + menu.innerHTML += "
    "; + } + } else if (!session.pcs[uuid].realUUID){ + printViewValues(session.pcs[uuid].stats, uuid); + menu.innerHTML += "
    "; + } + + } + if (iOS || iPad){ + menu.innerHTML += "
    "; + } + try { + getById("menuStatsBox").scrollLeft = scrollLeft; + getById("menuStatsBox").scrollTop = scrollTop; + } catch (e) {} +} + +function updateLocalStats(){ + + if (!session){return;} + + var totalBitrate = 0; + var totalBitrate2 = 0; + var cpuLimited = false; + var relayUsed = false; + var totalVideo = 0; + var totalAudio = 0; + var totalScenes = 0; + var meshcastActive = false; + var nackRate = 0; + var totalStreams = 0; + + var miscSenders = []; + + if (session.whipOut && session.whipOut.getSenders && session.whipOut.stats){ + miscSenders.push(session.whipOut); + } + + miscSenders.forEach(data=>{ + try { + var atot = 0; + var senders = data.getSenders(); // for any connected peer, update the video they have if connected with a video already. + senders.forEach((sender) => { // I suppose there could be a race condition between negotiating and updating this. if joining at the same time as changnig streams? + if (sender.track && sender.track.kind == "video" && sender.track.enabled) { + meshcastActive = true; + } else if (sender.track && sender.track.kind == "audio" && sender.track.enabled && !session.muted) { + meshcastActive = true; + } + }); + //totalAudio += atot; + + if ("video_bitrate_kbps" in data.stats){ + totalBitrate+=data.stats.video_bitrate_kbps || 0; + } + if ("audio_bitrate_kbps" in data.stats){ + totalBitrate+=data.stats.audio_bitrate_kbps || 0; + } + if ("total_sending_bitrate_kbps" in data.stats){ + totalBitrate2+=data.stats.total_sending_bitrate_kbps || 0; + } + + if ("quality_limitation_reason" in data.stats){ + if (data.stats.quality_limitation_reason == "cpu"){ + cpuLimited=true; + } + } + + + if ("nacks_per_second" in data.stats){ + nackRate += data.stats.nacks_per_second; + totalStreams += 1; + } + + //if ("local_candidateType" in session.pcs[uuid].stats){ + // if (session.pcs[uuid].stats.local_candidateType == "relay"){ + // if (session.pcs[uuid].startTime && (Date.now() - session.pcs[uuid].startTime > 30000)){ + // relayUsed=true; + // } + // } + //} + + + setTimeout(function(data){ + if (!data){return;} + data.getStats().then(function(stats) { + if ("audio_bitrate_kbps" in data.stats){ + data.stats.audio_bitrate_kbps=0; + } + + var nominatedCandidate = false; + var candidates = {}; + + stats.forEach(stat => { + + if (stat.id && stat.id.startsWith("DEPRECATED_")){return;} + + if (stat.type == "transport"){ + if ("bytesSent" in stat) { + if ("_bytesSent" in data.stats){ + if (data.stats._timestamp){ + if (stat.timestamp){ + data.stats.total_sending_bitrate_kbps = parseInt(8*(stat.bytesSent - data.stats._bytesSent)/(stat.timestamp - data.stats._timestamp)); + } + } + } + data.stats._bytesSent = stat.bytesSent; + } + if ("timestamp" in stat) { + data.stats._timestamp = stat.timestamp; + } + } else if (stat.type == "outbound-rtp") { + if (stat.kind == "video") { + if ("framesPerSecond" in stat) { + data.stats.resolution = stat.frameWidth + " x " + stat.frameHeight + " @ " + stat.framesPerSecond; + } else if ("frameHeight" in stat){ + + if (("framesEncoded" in stat) && stat.timestamp){ + var lastFramesEncoded = 0; + var lastTimestamp = 0; + try{ + lastFramesEncoded = data.stats._framesEncoded; + lastTimestamp = data.stats._timestamp; + } catch(e){} + data.stats._FPS = parseInt(10*(stat.framesEncoded - lastFramesEncoded)/(stat.timestamp/1000 - lastTimestamp))/10 || "?"; + data.stats._framesEncoded = stat.framesEncoded; + data.stats._timestamp = stat.timestamp/1000; + data.stats.resolution = stat.frameWidth + " x " + stat.frameHeight + " @ " + data.stats._FPS; + } else { + data.stats.resolution = stat.frameWidth + " x " + stat.frameHeight; + } + } + if ("encoderImplementation" in stat) { + data.stats.video_encoder = stat.encoderImplementation; + if (stat.encoderImplementation=="ExternalEncoder"){ + data.stats._hardwareEncoder = true; // I won't set this to false again, just because once I know it has one, I just need to assume it could always be used unexpectednly + data.encoder = true; + } else if (stat.encoderImplementation=="MediaFoundationVideoEncodeAccelerator"){ + data.stats._hardwareEncoder = true; // I won't set this to false again, just because once I know it has one, I just need to assume it could always be used unexpectednly + data.encoder = true; + } else { + data.encoder = false; // this may not be actually accurate, but lets assume so. + } + } + if ("qualityLimitationReason" in stat) { + if (data.stats.quality_limitation_reason){ + if (data.stats.quality_limitation_reason !== stat.qualityLimitationReason){ + try{ + var miniInfo = {}; + miniInfo.qlr = stat.qualityLimitationReason; + if ("_hardwareEncoder" in data.stats){ + miniInfo.hw_enc = data.stats._hardwareEncoder; + } else { + miniInfo.hw_enc = null; + } + session.sendMessage({"miniInfo":miniInfo}); + } catch(e){warnlog(e);} + } + } + data.stats.quality_limitation_reason = stat.qualityLimitationReason; + } + + if ("bytesSent" in stat) { + if ("_bytesSentVideo" in data.stats){ + if (data.stats._timestamp1){ + data.stats.video_bitrate_kbps = parseInt(8*(stat.bytesSent - data.stats._bytesSentVideo)/(stat.timestamp - data.stats._timestamp1)); + if (stat.timestamp){ + } + } + } + data.stats._bytesSentVideo = stat.bytesSent; + } + + if ("nackCount" in stat) { + if ("_nackCount" in data.stats){ + if (data.stats._timestamp1){ + if (stat.timestamp){ + data.stats.nacks_per_second = parseInt(10000*(stat.nackCount - data.stats._nackCount)/(stat.timestamp - data.stats._timestamp1))/10; + + } + } + } + } + if ("retransmittedBytesSent" in stat) { + if ("_retransmittedBytesSent" in data.stats){ + if (data.stats._timestamp1){ + if (stat.timestamp){ + data.stats.retransmitted_kbps = parseInt(8*(stat.retransmittedBytesSent - data.stats._retransmittedBytesSent)/(stat.timestamp - data.stats._timestamp1)); + } + } + } + } + + if ("nackCount" in stat) { + data.stats._nackCount = stat.nackCount; + } + + if ("retransmittedBytesSent" in stat) { + data.stats._retransmittedBytesSent = stat.retransmittedBytesSent; + + } + + if ("timestamp" in stat) { + data.stats._timestamp1 = stat.timestamp; + } + + if ("pliCount" in stat) { + data.stats.total_pli_count = stat.pliCount; + } + if ("keyFramesEncoded" in stat) { + data.stats.total_key_frames_encoded = stat.keyFramesEncoded; + } + + + } else if (stat.kind == "audio") { + if ("bytesSent" in stat) { + if (data.stats._bytesSentAudio){ + if (data.stats._timestamp2){ + if (stat.timestamp){ + if ("audio_bitrate_kbps" in data.stats){ + data.stats.audio_bitrate_kbps += parseInt(8*(stat.bytesSent - data.stats._bytesSentAudio)/(stat.timestamp - data.stats._timestamp2)); + } else { + data.stats.audio_bitrate_kbps=0; + } + } + } + } + } + if ("timestamp" in stat) { + data.stats._timestamp2 = stat.timestamp; + } + + if ("bytesSent" in stat) { + data.stats._bytesSentAudio = stat.bytesSent; + + } + } + } else if (stat.type == "remote-candidate") { + candidates[stat.id] = stat; + } else if (stat.type == "local-candidate") { + candidates[stat.id] = stat; + } else if ((stat.type == "candidate-pair" ) && (stat.nominated)) { + if (!nominatedCandidate){ + nominatedCandidate = stat; + } else if (nominatedCandidate.priority < stat.priority){ + nominatedCandidate = stat; + } + } else if (Firefox && ("mimeType" in stat) && ("type" in stat) && (stat.type=="codec")) { + if (stat.mimeType.includes("video")){ + data.stats.video_codec = stat.mimeType.split("video/")[1]; + } else if (stat.mimeType.includes("audio")){ + data.stats.audio_codec = stat.mimeType.split("audio/")[1]; + } + } + return; + }); + + if (nominatedCandidate){ + if ("availableOutgoingBitrate" in nominatedCandidate){ + data.stats.available_outgoing_bitrate_kbps = parseInt(nominatedCandidate.availableOutgoingBitrate/1024); + if (session.maxBandwidth!==false){ + session.limitMaxBandwidth(data.stats.available_outgoing_bitrate_kbps, session.pcs[UUID], false); + } + } + if ("totalRoundTripTime" in nominatedCandidate){ + if ("responsesReceived" in nominatedCandidate){ + data.stats.average_roundTripTime_ms = parseInt((nominatedCandidate.totalRoundTripTime/nominatedCandidate.responsesReceived)*1000); + } + } + } + if (nominatedCandidate && nominatedCandidate.remoteCandidateId){ + if (candidates[nominatedCandidate.remoteCandidateId]){ + var candidate = candidates[nominatedCandidate.remoteCandidateId]; + if ("candidateType" in candidate) { + data.stats.remote_candidateType = candidate.candidateType; + if (candidate.candidateType === "relay"){ + if ("ip" in candidate) { + data.stats.remote_relay_IP = candidate.ip; + } + if ("relayProtocol" in candidate) { + data.stats.remote_relay_protocol = candidate.relayProtocol; + } + } else { + try { + delete data.stats.remote_relay_IP; + delete data.stats.remote_relay_protocol; + } catch(e){} + } + } + } + } + if (nominatedCandidate && nominatedCandidate.localCandidateId){ + if (candidates[nominatedCandidate.localCandidateId]){ + var candidate = candidates[nominatedCandidate.localCandidateId]; + if ("candidateType" in candidate) { + data.stats.local_candidateType = candidate.candidateType; + + if (candidate.candidateType === "relay"){ + if ("ip" in candidate) { + data.stats.local_relay_IP = candidate.ip; + } + if ("relayProtocol" in candidate) { + data.stats.local_relay_protocol = candidate.relayProtocol; + } + } else { + try { + delete data.stats.local_relay_IP; + delete data.stats.local_relay_protocol; + } catch(e){} + } + + } + } + } + + + return; + }); + }, 0, data); + } catch(e){errorlog(e);} + }); + + for (var uuid in session.pcs) { + if (!session.pcs[uuid].stats){continue;} + + var atot = 0; + var senders = getSenders2(uuid); // for any connected peer, update the video they have if connected with a video already. + senders.forEach((sender) => { // I suppose there could be a race condition between negotiating and updating this. if joining at the same time as changnig streams? + if (sender.track && sender.track.kind == "video" && sender.track.enabled) { + totalVideo+=1 + } else if (sender.track && sender.track.kind == "audio" && sender.track.enabled && !session.muted) { + atot=1; + } + }); + totalAudio += atot; + + if ("scene" in session.pcs[uuid]){ + if (session.pcs[uuid].scene!==false){ + totalScenes+=1; + } + } + + if ("video_bitrate_kbps" in session.pcs[uuid].stats){ + totalBitrate+=session.pcs[uuid].stats.video_bitrate_kbps || 0; + } + if ("audio_bitrate_kbps" in session.pcs[uuid].stats){ + totalBitrate+=session.pcs[uuid].stats.audio_bitrate_kbps || 0; + } + if ("total_sending_bitrate_kbps" in session.pcs[uuid].stats){ + totalBitrate2+=session.pcs[uuid].stats.total_sending_bitrate_kbps || 0; + } + + if ("quality_limitation_reason" in session.pcs[uuid].stats){ + if (session.pcs[uuid].stats.quality_limitation_reason == "cpu"){ + cpuLimited=true; + } + } + + if ("nacks_per_second" in session.pcs[uuid].stats){ + nackRate += session.pcs[uuid].stats.nacks_per_second; + totalStreams += 1; + } + + //if ("local_candidateType" in session.pcs[uuid].stats){ + // if (session.pcs[uuid].stats.local_candidateType == "relay"){ + // if (session.pcs[uuid].startTime && (Date.now() - session.pcs[uuid].startTime > 30000)){ + // relayUsed=true; + // } + // } + //} + + if (uuid in session.rpcs){ + if (session.pcs[uuid].stats.label){ + session.pcs[uuid].stats.label = session.rpcs[uuid].label; + } + if (session.pcs[uuid].stats.streamID){ + session.pcs[uuid].stats.streamID = session.rpcs[uuid].streamID; + } + } + + var screenTracksIds = []; + if (session.screenStream !== false){ // null if already used. false if never used. + session.screenStream.getTracks().forEach(trk=>{ + screenTracksIds.push(trk.id); + }); + } + + setTimeout(function(UUID) { + if (!( session.pcs[UUID])){return;} + + + if (session.pcs[UUID].realUUID && session.pcs[session.pcs[UUID].realUUID]){ + var thisIsAlt = true;; + var node = session.pcs[session.pcs[UUID].realUUID]; + } else { + var thisIsAlt = false; + var node = session.pcs[UUID]; + } + + node.getStats().then(function(stats) { + + if (!(UUID in session.pcs)){return;} + + if ("audio_bitrate_kbps" in session.pcs[UUID].stats){ + session.pcs[UUID].stats.audio_bitrate_kbps=0; + } + + var nominatedCandidate = false; + var candidates = {}; + + var statObject = []; + var altStreamList = {}; + stats.forEach(stat => { + statObject.push(stat); + if (screenTracksIds.includes(stat.trackIdentifier)){ + altStreamList[stat.id] = stat.trackIdentifier; + } + }) + + statObject.forEach(stat => { + if (stat.id && stat.id.startsWith("DEPRECATED_")){return;} + + if (stat.type == "transport"){ + if ("bytesSent" in stat) { + if ("_bytesSent" in session.pcs[UUID].stats){ + if (session.pcs[UUID].stats._timestamp3){ + if (stat.timestamp){ + session.pcs[UUID].stats.total_sending_bitrate_kbps = parseInt(8*(stat.bytesSent - session.pcs[UUID].stats._bytesSent)/(stat.timestamp - session.pcs[UUID].stats._timestamp3)); + } + } + } + session.pcs[UUID].stats._bytesSent = stat.bytesSent; + } + if ("timestamp" in stat) { + session.pcs[UUID].stats._timestamp3 = stat.timestamp; + } + } else if (stat.type == "outbound-rtp") { + + if (thisIsAlt && stat.mediaSourceId && !altStreamList[stat.mediaSourceId]){ + // this isn't an alt stream, but we are in alt mode + return; + } else if (!thisIsAlt && stat.mediaSourceId && altStreamList[stat.mediaSourceId]){ + // this is an alt stream, but we are not in alt mode + return; + } + + if (stat.kind == "video") { + + if ("framesPerSecond" in stat) { + session.pcs[UUID].stats.resolution = stat.frameWidth + " x " + stat.frameHeight + " @ " + stat.framesPerSecond; + } else if ("frameHeight" in stat){ + + if (("framesEncoded" in stat) && stat.timestamp){ + var lastFramesEncoded = 0; + var lastTimestamp = 0; + try{ + lastFramesEncoded = session.pcs[UUID].stats._framesEncoded; + lastTimestamp = session.pcs[UUID].stats._timestamp; + } catch(e){} + session.pcs[UUID].stats._FPS = parseInt(10*(stat.framesEncoded - lastFramesEncoded)/(stat.timestamp - lastTimestamp))/10; + session.pcs[UUID].stats._framesEncoded = stat.framesEncoded; + session.pcs[UUID].stats._timestamp = stat.timestamp; + session.pcs[UUID].stats.resolution = stat.frameWidth + " x " + stat.frameHeight + " @ " + session.pcs[UUID].stats._FPS; + } else { + session.pcs[UUID].stats.resolution = stat.frameWidth + " x " + stat.frameHeight; + } + } + + var miniInfo = {}; + var sendMini = false; + + if ("encoderImplementation" in stat) { + session.pcs[UUID].stats.video_encoder = stat.encoderImplementation; + + if (stat.encoderImplementation=="ExternalEncoder"){ + session.pcs[UUID].stats._hardwareEncoder = true; // I won't set this to false again, just because once I know it has one, I just need to assume it could always be used unexpectednly + if (session.pcs[UUID].encoder !== true){ + session.pcs[UUID].encoder = true; + miniInfo.hw_enc = true; + sendMini = true; + } + } else if (stat.encoderImplementation=="MediaFoundationVideoEncodeAccelerator"){ + session.pcs[UUID].stats._hardwareEncoder = true; // I won't set this to false again, just because once I know it has one, I just need to assume it could always be used unexpectednly + if (session.pcs[UUID].encoder !== true){ + session.pcs[UUID].encoder = true; + miniInfo.hw_enc = true; + sendMini = true; + } + } else { + if (session.pcs[UUID].encoder === true){ + session.pcs[UUID].encoder = false; + miniInfo.hw_enc = false; + sendMini = true; + } + } + } + + if ("qualityLimitationReason" in stat) { + if (session.pcs[UUID].stats.quality_limitation_reason){ + if (session.pcs[UUID].stats.quality_limitation_reason !== stat.qualityLimitationReason){ + try{ + sendMini = true; + miniInfo.qlr = stat.qualityLimitationReason; + } catch(e){warnlog(e);} + } + } + session.pcs[UUID].stats.quality_limitation_reason = stat.qualityLimitationReason; + } + + if (sendMini){ + session.sendMessage({"miniInfo":miniInfo}, UUID); + } + + if ("bytesSent" in stat) { + if ("_bytesSentVideo" in session.pcs[UUID].stats){ + if (session.pcs[UUID].stats._timestamp1){ + if (stat.timestamp){ + session.pcs[UUID].stats.video_bitrate_kbps = parseInt(8*(stat.bytesSent - session.pcs[UUID].stats._bytesSentVideo)/(stat.timestamp - session.pcs[UUID].stats._timestamp1)); + } + } + } + session.pcs[UUID].stats._bytesSentVideo = stat.bytesSent; + } + + if ("nackCount" in stat) { + if ("_nackCount" in session.pcs[UUID].stats){ + if (session.pcs[UUID].stats._timestamp1){ + if (stat.timestamp){ + session.pcs[UUID].stats.nacks_per_second = parseInt(10000*(stat.nackCount - session.pcs[UUID].stats._nackCount)/(stat.timestamp - session.pcs[UUID].stats._timestamp1))/10; + + } + } + } + } + if ("retransmittedBytesSent" in stat) { + if ("_retransmittedBytesSent" in session.pcs[UUID].stats){ + if (session.pcs[UUID].stats._timestamp1){ + if (stat.timestamp){ + session.pcs[UUID].stats.retransmitted_kbps = parseInt(8*(stat.retransmittedBytesSent - session.pcs[UUID].stats._retransmittedBytesSent)/(stat.timestamp - session.pcs[UUID].stats._timestamp1)); + } + } + } + } + + if ("nackCount" in stat) { + session.pcs[UUID].stats._nackCount = stat.nackCount; + } + + if ("retransmittedBytesSent" in stat) { + session.pcs[UUID].stats._retransmittedBytesSent = stat.retransmittedBytesSent; + + } + + if ("timestamp" in stat) { + session.pcs[UUID].stats._timestamp1 = stat.timestamp; + } + + if ("pliCount" in stat) { + session.pcs[UUID].stats.total_pli_count = stat.pliCount; + } + if ("keyFramesEncoded" in stat) { + session.pcs[UUID].stats.total_key_frames_encoded = stat.keyFramesEncoded; + } + + + } else if (stat.kind == "audio") { + if ("bytesSent" in stat) { + if (session.pcs[UUID].stats._bytesSentAudio){ + if (session.pcs[UUID].stats._timestamp2){ + if (stat.timestamp){ + if ("audio_bitrate_kbps" in session.pcs[UUID].stats){ + session.pcs[UUID].stats.audio_bitrate_kbps += parseInt(8*(stat.bytesSent - session.pcs[UUID].stats._bytesSentAudio)/(stat.timestamp - session.pcs[UUID].stats._timestamp2)); + } else { + session.pcs[UUID].stats.audio_bitrate_kbps=0; + } + } + } + } + } + if ("timestamp" in stat) { + session.pcs[UUID].stats._timestamp2 = stat.timestamp; + } + + if ("bytesSent" in stat) { + session.pcs[UUID].stats._bytesSentAudio = stat.bytesSent; + + } + } + } else if (stat.type == "remote-candidate") { + candidates[stat.id] = stat; + } else if (stat.type == "local-candidate") { + candidates[stat.id] = stat; + } else if ((stat.type == "candidate-pair" ) && (stat.nominated)) { + if (!nominatedCandidate){ + nominatedCandidate = stat; + } else if (nominatedCandidate.priority < stat.priority){ + nominatedCandidate = stat; + } + } else if (Firefox && ("mimeType" in stat) && ("type" in stat) && (stat.type=="codec")) { + if (stat.mimeType.includes("video")){ + session.pcs[UUID].stats.video_codec = stat.mimeType.split("video/")[1]; + } else if (stat.mimeType.includes("audio")){ + session.pcs[UUID].stats.audio_codec = stat.mimeType.split("audio/")[1]; + } + } + return; + }); + + if (nominatedCandidate){ + if ("availableOutgoingBitrate" in nominatedCandidate){ + session.pcs[UUID].stats.available_outgoing_bitrate_kbps = parseInt(nominatedCandidate.availableOutgoingBitrate/1024); + if (session.maxBandwidth!==false){ + session.limitMaxBandwidth(session.pcs[UUID].stats.available_outgoing_bitrate_kbps, session.pcs[UUID], false); + } + } + if ("totalRoundTripTime" in nominatedCandidate){ + if ("responsesReceived" in nominatedCandidate){ + session.pcs[UUID].stats.average_roundTripTime_ms = parseInt((nominatedCandidate.totalRoundTripTime/nominatedCandidate.responsesReceived)*1000); + } + } + } + if (nominatedCandidate && nominatedCandidate.remoteCandidateId){ + if (candidates[nominatedCandidate.remoteCandidateId]){ + var candidate = candidates[nominatedCandidate.remoteCandidateId]; + if ("candidateType" in candidate) { + session.pcs[UUID].stats.remote_candidateType = candidate.candidateType; + if (candidate.candidateType === "relay"){ + if ("ip" in candidate) { + session.pcs[UUID].stats.remote_relay_IP = candidate.ip; + } + if ("relayProtocol" in candidate) { + session.pcs[UUID].stats.remote_relay_protocol = candidate.relayProtocol; + } + } else { + try { + delete session.pcs[UUID].stats.remote_relay_IP; + delete session.pcs[UUID].stats.remote_relay_protocol; + } catch(e){} + } + } + } + } + if (nominatedCandidate && nominatedCandidate.localCandidateId){ + if (candidates[nominatedCandidate.localCandidateId]){ + var candidate = candidates[nominatedCandidate.localCandidateId]; + if ("candidateType" in candidate) { + session.pcs[UUID].stats.local_candidateType = candidate.candidateType; + + if (candidate.candidateType === "relay"){ + if ("ip" in candidate) { + session.pcs[UUID].stats.local_relay_IP = candidate.ip; + } + if ("relayProtocol" in candidate) { + session.pcs[UUID].stats.local_relay_protocol = candidate.relayProtocol; + } + } else { + try { + delete session.pcs[UUID].stats.local_relay_IP; + delete session.pcs[UUID].stats.local_relay_protocol; + } catch(e){} + } + + } + } + } + + return; + }); + }, 0, uuid); + } + + + try{ + var totalCon = Object.keys(session.pcs).length || 0; + var headerStats = "🔗 "; + headerStats += totalCon + if (meshcastActive){ + if (totalAudio){ + headerStats += ", 👂 "+totalAudio; + } + if (totalVideo){ + headerStats += ", 👀 "+totalVideo; + } + headerStats += ", 📡Broadcast"; + } else { + headerStats += ", 👂 "+totalAudio; + headerStats += ", 👀 "+totalVideo; + } + if (session.roomid){ + headerStats += ", 🎬 "+totalScenes+""; + } + + var changed = false; + if (!session.info.out){ + session.info.out = {}; + session.info.out.v = totalVideo; + session.info.out.a = totalAudio; + session.info.out.c = totalCon; + session.info.out.s = totalScenes; + changed = true; + } else { + if (session.info.out.a !== totalAudio){ + session.info.out.a = totalAudio; + // changed = true; // I'm not sending this data, so why bother + } + if (session.info.out.v !== totalVideo){ + session.info.out.v = totalAudio; + //changed = true; // I'm not sending this data, so why bother + } + if (session.info.out.c !== totalCon){ + if (session.info.out.c){ + changed = true; // update if I'm not the first one + } + session.info.out.c = totalCon; + } + if (session.info.out.s !== totalScenes){ + if (session.info.out.s){ + changed = true; // update if I'm not the first one + } + session.info.out.s = totalScenes; + } + } + } catch(e){} + //session.info.out = {}; + + + var uploadQuality = nackRate / totalStreams || 0; + if (totalStreams === 0){ + uploadQuality = "title='Connection seems good' style='color: transparent; text-shadow: 0 0 0 #4d9bff;'"; + } else if (uploadQuality === 0){ + uploadQuality = "title='Connection seems good' style='color: transparent; text-shadow: 0 0 0 #0F0;'"; + } else if (uploadQuality <= 1){ + uploadQuality = "title='Mild connection issues' style='color: transparent; text-shadow: 0 0 0 yellow;'"; + } else if (uploadQuality <= 5){ + uploadQuality = "title='Moderate connection issues' style='color: transparent; text-shadow: 0 0 0 orange;'"; + } else { + uploadQuality = "title='Severe connection issues' style='color: transparent; text-shadow: 0 0 0 #F00;'"; + } + + if (Firefox && (totalBitrate===0 && totalBitrate2===0)){ + // does not support the current stats system + } else if (totalBitrate > totalBitrate2){ + headerStats += ", 🔺 "+(Math.round(totalBitrate/10.24)/100) + "-mbps"; + } else if (totalBitrate2>1000){ + headerStats += ", 🔺 "+(Math.round(totalBitrate2/10.24)/100) + "-mbps
    "; + } else{ + headerStats += ", 🔺 "+totalBitrate2 + "-kbps"; + } + + + if (session.director || !session.roomid){ // show stats if the director or if not in a group room + if (cpuLimited){ + headerStats += ", 🔥 CPU Overloaded"; + } + //if (relayUsed){ + // headerStats += " 💸"; + //} + } + + var miniInfo = {} + if (changed){ + miniInfo.out = {}; + miniInfo.out.c = session.info.out.c + } + + if (session.cpuLimited!==cpuLimited){ + session.cpuLimited = cpuLimited; + miniInfo.cpu = cpuLimited; + changed = true; + } + + if (changed){ + for (var uuid in session.pcs) { + session.sendMessage({"miniInfo":miniInfo}, uuid); // lets send it to everyone. + } + } + + try{ + if (Object.keys(session.pcs).length){ + getById("head5").classList.remove("hidden"); + } + } catch(e){} + getById("head5").innerHTML = headerStats; + getById("head5").onclick = function(){ + var [menu, innerMenu] = statsMenuCreator(); + menu.interval = setInterval(printMyStats,session.statsInterval, innerMenu); + printMyStats(innerMenu); + } +} + + +function updateStats(obsvc = false) { + if (document.getElementById('previewWebcam')) { + var ele = document.getElementById('previewWebcam'); + var wcs = "webcamstats"; + } else if (document.getElementById('videosource')) { + var ele = document.getElementById('videosource'); + var wcs = "webcamstats3"; + } else { + return; + } + + try { + getById(wcs).innerHTML = ""; + ele.srcObject.getVideoTracks().forEach( + function(track) { + if ((obsvc) && (parseInt(track.getSettings().frameRate) == 30)) { + getById(wcs).innerHTML = "Video Settings: " + (track.getSettings().width || 0) + "x" + (track.getSettings().height || 0) + " @ up to 60fps"; + } else { + var frameRateFPS = track.getSettings().frameRate; + if (frameRateFPS){ + getById(wcs).innerHTML = "Current Video Settings: " + (track.getSettings().width || 0) + "x" + (track.getSettings().height || 0) + "@" + (parseInt(frameRateFPS * 100) / 100.0) + "fps"; + } else { + getById(wcs).innerHTML = "Current Video Settings: " + (track.getSettings().width || 0) + "x" + (track.getSettings().height || 0); + } + } + } + ); + + } catch (e) { + errorlog(e); + } +} + +function toggleControlBar() { + if (!getById("controlButtons").classList.contains("hidden")) { + getById("controlButtons").dataset.enabled = true; + getById("controlButtons").classList.add("hidden"); + } else if (getById("controlButtons").dataset.enabled){ + getById("controlButtons").classList.remove("hidden"); + delete getById("controlButtons").dataset.enabled; + } +} + + +function toggleMute(apply = false, event=false) { // TODO: I need to have this be MUTE, toggle, with volume not touched. + + var mouseUp = null; + var touchEnd = null; + var timeStart = Date.now(); + if (event){ + mouseUp = document.onmouseup; + touchEnd = document.ontouchend; + document.onmouseup = function(){ + document.onmouseup = mouseUp; + document.ontouchend = touchEnd; + if (Date.now() - timeStart < 500){ + return; + } else { + toggleMute(); + } + } + document.ontouchend = function(){ + document.onmouseup = mouseUp; + document.ontouchend = touchEnd; + if (Date.now() - timeStart < 300){ + return; + } else { + toggleMute(); + } + } + } + + if (session.director) { + if (!session.directorEnabledPPT) { + log("Director doesn't have PPT enabled yet"); + // director has not enabled PTT yet. + return; + } + } + + if (apply) { + session.muted = !session.muted; // we flip here as we are going to flip again in a second. + } + //try{var ptt = getById("press2talk");} catch(e){var ptt=false;} + + + + if (session.muted == false) { + session.muted = true; + getById("mutetoggle").className = "las la-microphone-slash toggleSize"; + if (!(session.cleanOutput)){ + getById("mutebutton").classList.add("red", "pulsate"); + getById("mutebutton").ariaPressed = "true"; + getById("header").classList.add('red'); + + if (session.localMuteElement){ + session.localMuteElement.style.display = "block"; + } + + } + if (session.streamSrc) { + session.streamSrc.getAudioTracks().forEach((track) => { + track.enabled = false; + }); + } + if ((iOS || iPad) && session.videoElement && session.videoElement.srcObject) { + session.videoElement.srcObject.getAudioTracks().forEach((track) => { + track.enabled = false; + }); + } + + } else { + session.muted = false; + getById("mutetoggle").className = "las la-microphone toggleSize"; + if (!(session.cleanOutput)){ + + getById("mutebutton").classList.remove("red", "pulsate"); + getById("mutebutton").ariaPressed = "false"; + getById("header").classList.remove('red'); + + if (session.localMuteElement){ + session.localMuteElement.style.display = "none"; + } + + } + if (session.streamSrc) { + session.streamSrc.getAudioTracks().forEach((track) => { + track.enabled = true; + }); + } + if ((iOS || iPad) && session.videoElement && session.videoElement.srcObject) { + session.videoElement.srcObject.getAudioTracks().forEach((track) => { + track.enabled = true; + }); + } + //if (ptt){ + // ptt.innerHTML = "🔴 Push to Mute"; + //} + } + + try { + postMessageIframe(document.getElementById("screensharesource"), {"mic":!session.muted}); + } catch(e){} + + if (!apply) { // only if they are changing states do we bother to spam. + data = {}; + data.muteState = session.muted; + session.sendMessage(data); + log("SEND MUTE STATE TO PEERS"); + pokeIframeAPI('mic-mute-state', session.muted); + pokeAPI("muted", session.muted); + } +} + +function postMessageIframe(iFrameEle, message){ // iframes seem to only have the contentWindow work on the last placed iframe object, so this checks the dom first. + if (iFrameEle && (iFrameEle.nodeName == "IFRAME")){ + try{ + if (iFrameEle.id && document.getElementById(iFrameEle.id)){ + document.getElementById(iFrameEle.id).contentWindow.postMessage(message, '*'); + } else { + iFrameEle.contentWindow.postMessage(message, '*'); + } + } catch(e){errorlog(e);} + } +} + +function toggleSpeakerMute(apply = false) { // TODO: I need to have this be MUTE, toggle, with volume not touched. + + if (CtrlPressed) { + resetupAudioOut(); + } + + if (apply) { + session.speakerMuted = !session.speakerMuted; + } + if (session.speakerMuted == false) { // mute output + session.speakerMuted = true; + getById("mutespeakertoggle").className = "las la-volume-mute toggleSize"; + if (!(session.cleanOutput)){ + getById("mutespeakerbutton").className = "float red"; + } + var sounds = document.getElementsByTagName("video"); + + if (iOS || iPad){ + for (var i = 0; i < sounds.length; ++i) { + if (sounds[i].id === "keepAlivePlayer"){ // we need to keep this unmuted + continue; + } + sounds[i].muted = !sounds[i].muted; + sounds[i].muted = session.speakerMuted; + } + } else { + for (var i = 0; i < sounds.length; ++i) { + if (sounds[i].id === "keepAlivePlayer"){ // we need to keep this unmuted + continue; + } + sounds[i].muted = session.speakerMuted; + } + } + + } else { + session.speakerMuted = false; // unmute output + + getById("mutespeakertoggle").className = "las la-volume-up toggleSize"; + if (!(session.cleanOutput)){ + getById("mutespeakerbutton").className = "float"; + } + var sounds = document.getElementsByTagName("video"); + + if (iOS || iPad){ // attempting to fix an iOS bug + for (var i = 0; i < sounds.length; ++i) { + sounds[i].muted = !sounds[i].muted; + if (sounds[i].id === "videosource") { // don't unmute ourselves. feedback galore if so. + sounds[i].muted = true; + continue; + } else if (sounds[i].id === "previewWebcam") { + sounds[i].muted = true; + continue; + } else if (sounds[i].id === "screensharesource") { + sounds[i].muted = true; + continue; + } else if (sounds[i].id === "screenshare") { // this is a webcam + sounds[i].muted = true; + continue; + } else { + sounds[i].muted = session.speakerMuted; + } + } + } else { + for (var i = 0; i < sounds.length; ++i) { + + if (sounds[i].id === "videosource") { // don't unmute ourselves. feedback galore if so. + continue; + } else if (sounds[i].id === "screensharesource") { // don't unmute ourselves. feedback galore if so. + continue; + } else if (sounds[i].id === "previewWebcam") { + continue; + } else if (sounds[i].id === "screenshare") { // this is a webm + continue; + } else { + sounds[i].muted = session.speakerMuted; + } + } + } + } + + for (var UUID in session.rpcs) { + applyMuteState(UUID); + postMessageIframe(session.rpcs[UUID].iframeEle, {"mute":session.speakerMuted}); + } + + pokeIframeAPI("audio-mute-state", session.speakerMuted); + + if (!apply) { + pokeAPI("speakerMuted", session.speakerMuted); + } + + if (iOS || iPad) { + resetupAudioOut(); + } +} + +function toggleFileshare(UUID=false, event = null){ + if (UUID===false){ + var string = 'Share a file with the group
    '; + } else if (session.directorList.indexOf(UUID)>=0){ + var string = 'The director requested you share a file with them.
    '; + } else { + var string = 'Someone has requested you share a file with them.
    '; + } + warnUser(string, false, false); + if (session.hostedFiles){ + if (session.hostedFiles.length){ + getById("activeShares").innerHTML += "
    Files being shared:
    "; + } + for (var i=0;i (" + Math.ceil(session.hostedFiles[i].size/(1024*1024/10))/10 + "-MB)"; + } + } + if (session.hostedTransfers){ + getById("activeShares").innerHTML += "
    "+session.hostedTransfers.length + " file transfers in progress.
    "; + } +} + +function toggleChat(event = null) { // TODO: I need to have this be MUTE, toggle, with volume not touched. + if (session.chat == false) { + setTimeout(function() { + document.addEventListener("click", toggleChat); + }, 10); + + getById("chatModule").addEventListener("click", function(e) { + e.stopPropagation(); + return false; + }); + session.chat = true; + getById("chattoggle").className = "las la-comment-dots toggleSize"; + getById("chatModule").classList.remove("hidden"); + getById("chatInput").focus(); // give it keyboard focus + } else { + + session.chat = false; + getById("chattoggle").className = "las la-comment-alt toggleSize"; + getById("chatModule").classList.add("hidden"); + + document.removeEventListener("click", toggleChat); + getById("chatModule").removeEventListener("click", function(e) { + e.stopPropagation(); + return false; + }); + } + if (getById("chatNotification").value) { + getById("chatNotification").value = 0; + } + getById("chatNotification").classList.remove("notification", "red"); +} + +function directorAdvanced(ele) { + var target = document.createElement("div"); + target.style = "position:absolute;float:left;width:270px;height:222px;background-color:#7E7E7E;"; + + var closeButton = document.createElement("button"); + closeButton.innerHTML = " close"; + closeButton.style.left = "5px"; + closeButton.style.position = "relative"; + closeButton.onclick = function() { + target.parentNode.removeChild(target); + }; + target.appendChild(closeButton); + + var someButton = document.createElement("button"); + someButton.innerHTML = " some action "; + someButton.style.left = "5px"; + someButton.style.position = "relative"; + someButton.onclick = function() { + var actionMsg = {}; + session.sendRequest(actionMsg, ele.dataset.UUID); + }; + target.appendChild(someButton); + + ele.parentNode.appendChild(target); +} + +function directorSendMessage(ele) { + + var UUID = ele.dataset.UUID; + var target = document.querySelector("[data--u-u-i-d='"+UUID+"'][data-action-type='messaging-box']"); + if (!target){return;} + + if (target.classList.contains("hidden")){ + target.classList.remove("hidden"); + ele.classList.add("pressed"); ele.ariaPressed = "true"; + } else { + target.classList.add("hidden"); + ele.classList.remove("pressed"); ele.ariaPressed = "false"; + return; + } + + + var inputField = target.querySelector("[data-action-type='messaging-box-text']"); + if (inputField){ + inputField.focus(); + inputField.select(); + } + if ("overlay" in target){ + return; + } + + target.overlay = true; + + if (inputField){ + inputField.addEventListener("keydown", function(e) { + if (e.keyCode == 13) { + e.preventDefault(); + sendButton.click(); + } else if (e.keyCode == 27) { + e.preventDefault(); + inputField.value = ""; + target.parentNode.removeChild(target); + } + }); + } + + var sendButton = target.querySelector("[data-action-type='messaging-box-send']"); + if (sendButton){ + sendButton.onclick = function() { + var chatMsg = {}; + chatMsg.chat = inputField.value; + if (sendButton.parentNode.overlay) { + chatMsg.overlay = sendButton.parentNode.overlay; + } + session.sendRequest(chatMsg, ele.dataset.UUID); + inputField.value = ""; + //target.parentNode.removeChild(target); + }; + } + + var closeButton = target.querySelector("[data-action-type='messaging-box-close']"); + if (closeButton){ + closeButton.onclick = function() { + inputField.value = ""; + target.classList.add("hidden"); + ele.classList.remove("pressed"); ele.ariaPressed = "false"; + }; + } + + var overlayMsg = target.querySelector("[data-action-type='messaging-box-toggle']"); + if (overlayMsg){ + overlayMsg.onclick = function(e) { + log(e.target.parentNode.parentNode); + if (e.target.parentNode.parentNode.overlay === true) { + e.target.parentNode.parentNode.overlay = false; + e.target.parentNode.innerHTML = ""; + } else { + e.target.parentNode.parentNode.overlay = true; + e.target.parentNode.innerHTML = ""; + } + } + } +} + +function toggleAutoVideoMute(){ // for iOS devices, that tab out. + // document.visibilityState + if (!session.videoMuted && (session.permaid!==false)){ + var msg = {}; + msg.videoMuted = (document.visibilityState === 'hidden') || false; + //try { + session.sendMessage(msg); + //} catch(e){errorlog(e);} + pokeIframeAPI('video-mute-state', document.visibilityState); + } +} + +function toggleVideoMute(apply = false) { // TODO: I need to have this be MUTE, toggle, with volume not touched. + if (apply) { + session.videoMuted = !session.videoMuted; + } + + if (!session.remoteVideoMuted){ + getById("head8").classList.add("hidden"); + } + + if (session.videoMuted == false) { + session.videoMuted = true; + getById("mutevideotoggle").className = "las la-video-slash toggleSize"; + if (!(session.cleanOutput)){ + getById("mutevideobutton").classList.add("red"); + getById("mutevideobutton").ariaPressed = "true"; + getById("header").classList.add("red"); + if (session.remoteVideoMuted){ + getById("head8").classList.remove("hidden"); + } + } + if (session.streamSrc) { + session.streamSrc.getVideoTracks().forEach((track) => { + track.enabled = false; + }); + } + } else if (session.remoteVideoMuted){ // the director has muted this guest's video feed + session.videoMuted = false; // just setting it back to the pre-toggled state + getById("mutevideotoggle").className = "las la-video toggleSize"; + if (!(session.cleanOutput)){ + getById("head8").classList.remove("hidden"); + getById("header").classList.add("red"); + getById("mutevideobutton").classList.remove("red"); + getById("mutevideobutton").ariaPressed = "false"; + } + if (session.streamSrc) { + session.streamSrc.getVideoTracks().forEach((track) => { + track.enabled = false; + }); + } + + } else { + session.videoMuted = false; + + getById("mutevideotoggle").className = "las la-video toggleSize"; + if (!(session.cleanOutput)){ + getById("mutevideobutton").classList.remove("red"); + getById("mutevideobutton").ariaPressed = "false"; + getById("header").classList.remove("red"); + + } + if (session.streamSrc) { + session.streamSrc.getVideoTracks().forEach((track) => { + track.enabled = true; + }); + } + } + + if (session.avatar && session.avatar.ready && !apply){ + updateRenderOutpipe(); + if (session.videoMuted){ + var msg = {}; + msg.videoMuted = false; // doesn't matter the actual mute state; this is the avatar + session.sendMessage(msg); + } + } else if (!apply) { + var msg = {}; + msg.videoMuted = session.videoMuted; + session.sendMessage(msg); + } + + pokeIframeAPI("video-mute-state", (session.videoMuted || session.remoteVideoMuted)); + + if (!apply){ + pokeAPI("videoMuted", (session.videoMuted || session.remoteVideoMuted)); + } + +} + +var toggleSettingsState = false; +async function toggleSettings(forceShow = false) { // TODO: I need to have this be MUTE, toggle, with volume not touched. + + if (session.nosettings){return;} + + getById("multiselect-trigger3").dataset.state = "0"; + getById("multiselect-trigger3").classList.add('closed'); + getById("multiselect-trigger3").classList.remove('open'); + getById("chevarrow2").classList.add('bottom'); + + if (toggleSettingsState == true) { + if (forceShow == true) { + await enumerateDevices().then(gotDevices2); + return; + } + } // don't close if already open + if (getById("popupSelector").style.display == "none") { + + updateConstraintSliders(); + + setTimeout(function() { + document.addEventListener("click", toggleSettings); + }, 10); + + getById("popupSelector").addEventListener("click", function(e) { + e.stopPropagation(); + return false; + }); + + if (navigator.userAgent.indexOf('Chrome') != -1) { + try { + await navigator.permissions.query({ + name: "camera" + }).then(async function(promise) { + if (promise && promise.state) { + if (promise.state == "prompt") { + await navigator.mediaDevices.getUserMedia({ + video: true + , audio: false + }).then(async function(stream) { + await enumerateDevices().then(gotDevices2).then(function() { + stream.getTracks().forEach(function(track) { + //stream.removeTrack(track); + track.stop(); // clean up? + }); + }); + + }).catch(async function(err) { + await enumerateDevices().then(gotDevices2).then(function() {}); + }); + } else { + await enumerateDevices().then(gotDevices2).then(function() {}); + } + } else { + await enumerateDevices().then(gotDevices2).then(function() {}); + } + }); + } catch (e) { + await enumerateDevices().then(gotDevices2).then(function() {}); + } + } else { + await enumerateDevices().then(gotDevices2).then(function() {}); + } + + getById("popupSelector").style.display = "inline-block" + getById("settingsbutton").classList.add("brown"); + getById("settingsbutton").ariaPressed = "true"; + + loadTFLITEImages() // only triggers if effects==5 is true + + setTimeout(function() { + getById("popupSelector").style.right = "0px"; + }, 1); + toggleSettingsState = true; + } else { + document.removeEventListener("click", toggleSettings); + getById("popupSelector").removeEventListener("click", function(e) { + e.stopPropagation(); + return false; + }); + + getById("popupSelector").style.right = "-500px"; + + + getById("settingsbutton").classList.remove("brown"); + getById("settingsbutton").ariaPressed = "false"; + setTimeout(function() { + getById("popupSelector").style.display = "none"; + }, 200); + toggleSettingsState = false; + document.getElementById('videoSettings3').style.display = "none"; + } + pokeIframeAPI("settings-menu-state",toggleSettingsState); +} + +function hangup() { // TODO: I need to have this be MUTE, toggle, with volume not touched. + if (session.hostedTransfers.length){ + confirmAlt("There are still file transfer in progress\nAre you sure you wish to exit?").then(res=>{ + if (res){ + try { + //if (document.fullscreenElement && session.mobile){ + // getById("main").innerHTML = document.getElementById("hangupTemplateMobileFullscreen").innerHTML; + //} else { + getById("main").innerHTML = document.getElementById("hangupTemplate").innerHTML; + //} + } catch(e){} + + setTimeout(function() { + session.hangup(); + }, 0); + } + }); + } else { + + try { + //if (document.fullscreenElement && session.mobile){ + // getById("main").innerHTML = document.getElementById("hangupTemplateMobileFullscreen").innerHTML; + //} else { + getById("main").innerHTML = document.getElementById("hangupTemplate").innerHTML; + //} + } catch(e){} + + setTimeout(function() { + session.hangup(); + }, 0); + } +} + +function hangup2() { + session.hangupDirector(); + getById("miniPerformer").innerHTML = ""; + getById("press2talk").dataset.enabled = false; + getById("screensharebutton").classList.add("hidden"); + getById("screenshare2button").classList.add("hidden"); + getById("screenshare3button").classList.add("hidden"); + getById("settingsbutton").classList.add("hidden"); + getById("mutebutton").classList.add("hidden"); + getById("hangupbutton2").classList.add("hidden"); + //getById("chatbutton").classList.remove("hidden"); + getById("controlButtons").classList.remove("hidden"); + //getById("mutespeakerbutton").classList.add("hidden"); + getById("mutevideobutton").classList.add("hidden"); + + getById("screensharebutton").classList.remove("green"); + getById("screensharebutton").ariaPressed = "false"; + + + if (!session.showDirector) { + getById("miniPerformer").innerHTML = ''; + miniTranslate(getById("miniPerformer")); + } else { + getById("miniPerformer").innerHTML = ''; + } + getById("miniPerformer").className = ""; +} + +function hangupComplete() { + try { + //if (document.fullscreenElement && session.mobile){ + // getById("main").innerHTML = document.getElementById("hangupTemplateMobileFullscreen").innerHTML; + //} else { + getById("main").innerHTML = document.getElementById("hangupTemplate").innerHTML; + //} + } catch(e){} + + updateMixerRun = function(){}; + + pokeIframeAPI("hungup",true); // don't use Hangup, as that's an action. + pokeAPI("hangup",true); +} + +function reloadRequested() { + pokeIframeAPI("reloading",true); + window.removeEventListener("beforeunload", confirmUnload); // clear the confirm on reload + location.reload(); // the main reload function call +} +function confirmUnload(event){ + if (!session.noExitPrompt && !session.cleanOutput && (session.scene === false) && (session.seeding || (session.roomid!==false) || (session.permaid!==false) || session.director)){ + (event || window.event).returnValue = "Are you sure you want to exit?"; //Gecko + IE + return "Are you sure you want to exit?"; + } else { + //setTimeout(function(){session.hangup();},0); + return undefined; // ADDED OCT 29th; get rid of popup. Just close the socket connection if the user is refreshing the page. It's one or the other. + } +} + +function gobackSlide(){ + var data = {}; + data.data = [176, 110, 10]; + sendRawMIDI(data); + try { + pokeIframeAPI("back-slide",true); + } catch(e){} +} + +function nextSlide(){ + var data = {}; + data.data = [176, 110, 11]; + sendRawMIDI(data); + + try { + pokeIframeAPI("next-slide",true); + } catch(e){} +} + +function raisehand() { + if (session.directorUUID == false) { // fine + log("no director in room yet"); + return false; + } + + var data = {}; + var handstate = false; + + log(data); + if (getById("raisehandbutton").dataset.raised == "0") { + getById("raisehandbutton").dataset.raised = "1"; + getById("raisehandbutton").classList.add("raisedHand"); + data.chat = "Raised hand"; + handstate = true; + log("hand raised"); + } else { + log("hand lowered"); + getById("raisehandbutton").dataset.raised = "0"; + getById("raisehandbutton").classList.remove("raisedHand"); + data.chat = "Lowered hand"; + handstate = false; + } + for (var i=0;i{ + if (ge.value==1){ + cc+=1; + } + }); + if (!cc){ + getById("container_director").style.backgroundColor = null; + getById("container_director").classList.remove("containerGreen"); + } + } else { + var cc = 0; + getById("container_" + ele.dataset.UUID).querySelectorAll('[data-action-type="addToScene"]').forEach(ge=>{ + if (ge.value==1){ + cc+=1; + log("ge.value: '"+ge.value+"'"); + } else { + log("ge.value:--'"+ge.value+"'"); + } + }); + log(cc + " " +"container_" + ele.dataset.UUID); + if (!cc){ + getById("container_" + ele.dataset.UUID).style.backgroundColor = null; + getById("container_" + ele.dataset.UUID).classList.remove("containerGreen"); + } + } + } else { + ele.value = 1; + ele.classList.add("pressed"); ele.ariaPressed = "true"; + if (ele.children[1]){ + ele.children[1].innerHTML = "Remove"; + } + if (director){ + getById("container_director").classList.add("containerGreen"); + } else { + getById("container_" + ele.dataset.UUID).classList.add("containerGreen"); + } + } + } + + var msg = {}; + + scene = scene+""; + + msg.scene = scene; + msg.action = "display"; + msg.value = ele.value; + msg.target = ele.dataset.sid; + + try { + if (msg.value==1){ + pokeIframeAPI("add-to-scene", scene, ele.dataset.UUID); + } else { + pokeIframeAPI("remove-from-scene", scene, ele.dataset.UUID); + } + } catch(e){} + + //for (var uuid in session.pcs){ // removing this since it's obsolete at this point. + // if (session.pcs[uuid].stats.info && ("version" in session.pcs[uuid].stats.info) && (session.pcs[uuid].stats.info.version < 17.2)){ + //// msg.request = "sendroom"; + // session.sendMsg(msg); + // return; + // } + //} + + for (var uuid in session.pcs){ + if (session.pcs[uuid].scene===scene){ + session.sendMessage(msg, uuid); + } + } + syncDirectorState(ele); + + if (msg.value){ + return true; + } else { + return false; + } +} + +function syncDirectorState(ele){ + //if (session.director){ // assumed director, since this is a directEnable sub-function + var msg = {}; + msg.directorState = getDetailedState(ele.dataset.sid); + + for (var uuid in session.pcs){ + if (session.pcs[uuid].coDirector){ + session.sendMessage(msg, uuid); + } + } + for (var i in session.directorList){ + var uuid = session.directorList[i]; + if (session.rpcs[uuid]){ + session.sendRequest(msg, uuid); + } + } +} + +function getDetailedState(sid=false){ + var streamList = {}; + var guestFeeds = document.getElementById("guestFeeds"); + + for (var UUID in session.rpcs){ + if (session.rpcs[UUID].streamID){ + if (sid && (sid!==session.rpcs[UUID].streamID)){continue;} + let item = {}; + item.streamID = session.rpcs[UUID].streamID; + item.label = session.rpcs[UUID].label; + item.group = session.rpcs[UUID].group; + + try { + item.layout = session.rpcs[UUID].layout; + if (session.director && session.slotmode){ + item.slot = query("[data--u-u-i-d='"+UUID+"'][data-slot]").dataset.slot || false; + } else if (session.currentSlots){ + item.slot = Object.keys(session.currentSlots).find(key => session.currentSlots[key] === session.rpcs[UUID].streamID) || false; + } + if (item.slot){ + item.slot = parseInt(item.slot); + } + if (session.director){ + let featured = query("[data--u-u-i-d='"+UUID+"'][data-action-type='solo-video']"); + if (featured && parseInt(featured.value)){ + item.featured = true; + } else { + item.featured = false; + } + } else if (session.infocus && session.infocus===UUID){ + item.featured = true; + } else { + item.featured = false; + } + + } catch(e){errorlog(e);} + + item.iframeSrc = session.rpcs[UUID].iframeSrc; + item.localStream = false; + item.muted = session.rpcs[UUID].remoteMuteState; + item.videoMuted = session.rpcs[UUID].videoMuted; + try { + item.activeSpeaker = session.rpcs[UUID].activelySpeaking; + item.defaultSpeaker = session.rpcs[UUID].defaultSpeaker; + } catch(e){ + errorlog(e); + } + + item.videoVisible = session.rpcs[UUID].videoElement && session.rpcs[UUID].videoElement.checkVisibility(); + if (session.rpcs[UUID].videoElement){ + item.videoVolume = session.rpcs[UUID].videoElement.volume; + } + item.iframeVisible = session.rpcs[UUID].iframeVisible && session.rpcs[UUID].iframeVisible.checkVisibility(); + + if (session.directorList.indexOf(UUID)>=0){ + item.director = true; + } else { + item.director = false; + } + try { + if (session.director){ + if (guestFeeds){ + var lock = parseInt(document.getElementById("position_"+UUID).dataset.locked); + if (lock){ + item.position = lock; // probably should make a universal function to do this, for all lock requesting + } else { + var child = document.getElementById('container_'+UUID); + if (child){ + var parent = child.parentNode; + if (parent.id == "guestFeeds"){ + item.position = Array.prototype.indexOf.call(parent.children, child) + 1; + } + } + } + } + var scenes = getById("container_" + UUID).querySelectorAll('[data-action-type="addToScene"][data-scene][data--u-u-i-d="'+UUID+'"]'); + var sceneState = {}; + for (var i=0;i session.currentSlots[key] === session.streamID) || false; + } + } catch(e){errorlog(e);} + + if (session.info && session.info.out){ + streamList[session.streamID].outbound = session.info.out; + } + + + if (session.showDirector && session.director){ + var child = document.getElementById('container_director'); + if (child){ + var parent = child.parentNode; + if (parent.id == "guestFeeds"){ + streamList[session.streamID].position = Array.prototype.indexOf.call(parent.children, child) + 1; + } + } + } + + + if (session.notifyScreenShare){ + streamList[session.streamID].screenSharing = session.screenShareState; + } else { + streamList[session.streamID].screenSharing = false; + } + + if (session.streamSrc){ + streamList[session.streamID].audioTrack = (session.streamSrc.getAudioTracks().length !== 0); + streamList[session.streamID].videoTrack = (session.streamSrc.getVideoTracks().length !== 0); + } else { + streamList[session.streamID].audioTrack = false; + streamList[session.streamID].videoTrack = false; + } + + return streamList; +} + +function getGuestList(){ + var guestFeeds = document.getElementById("guestFeeds"); + if (!guestFeeds){return {};} + var streamList = {}; + for (var i=0; i< guestFeeds.children.length; i++){ + try { + if (session.rpcs[guestFeeds.children[i].dataset.UUID]){ + streamList[(i+1)+""] = {streamID:session.rpcs[guestFeeds.children[i].dataset.UUID].streamID, label: session.rpcs[guestFeeds.children[i].dataset.UUID].label || ""}; + } else if (guestFeeds.children[i].id == "container_director"){ + streamList[(i+1)+""] = {streamID:session.streamID, label: session.label || ""}; + } else if (guestFeeds.children[i].id == "container_screen_director"){ + streamList[(i+1)+""] = {streamID:session.streamID+":s", label: session.screenShareLabel || ""}; + } + } catch(e){errorlog(e);} + } + + return streamList; +} + +function syncOtherState(sid){ + if (!session.syncState){return;} + if (!session.syncState[sid]){return;} + + + /* if (session.rpcs[ele.dataset.UUID].directorMutedState==1){ + pokeIframeAPI("director-mute-state", true, ele.dataset.UUID); + pokeAPI("directorMuted", true, session.rpcs[ele.dataset.UUID].streamID); + } else { + pokeIframeAPI("director-mute-state", false, ele.dataset.UUID); + pokeAPI("directorMuted", false, session.rpcs[ele.dataset.UUID].streamID); + } */ + + var others = session.syncState[sid].others; + + log(others); + + for (var other in others){ + if (other == "toggle-group"){continue;} + var ele = document.querySelector('[data-sid="'+sid+'"][data-action-type="'+other+'"]'); + if (ele){ + if (others[other]){ + if (!("value" in ele)){ + errorlog("NO DEFAULT VALUE IN SPECIFIED ELEMENT; guessing default: "+other); + ele.value = 0; + } + var changed = true; + if (ele.value==others[other]){ + changed=false + } + if (other == "mute-guest"){ + if (changed){ + remoteMute(ele, false, true); + } + } else if (other == "hide-guest"){ + if (changed){ + remoteHideVideo(ele, true, true); + } + } else if (other == "mute-video-guest"){ + if (changed){ + remoteMuteVideo(ele, true, true); + } + } else { + ele.value = others[other]; + + if (ele.nodeName.toLowerCase() == "input"){ + ele.value = parseInt(others[other]); + } else if (parseInt(others[other])){ + ele.classList.add("pressed"); ele.ariaPressed = "true"; + } else { + ele.classList.remove("pressed"); ele.ariaPressed = "false"; + } + } + } + } + } + + var UUID = document.querySelector('[data-sid="'+sid+'"][data--u-u-i-d'); + if (UUID && UUID.dataset.UUID){ + UUID = UUID.dataset.UUID; + if (session.syncState[sid].group && session.rpcs[UUID]){ + session.rpcs[UUID].group = session.syncState[sid].group; + syncGroup(session.rpcs[UUID].group, UUID); + } + } +} + +function htmlToElement(html) { + var template = document.createElement('template'); + html = html.trim(); // Never return a text node of whitespace as the result + template.innerHTML = html; + return template.content.firstChild; +} + +function syncGroup(groups, UUID){ + if (!groups || (typeof groups !== 'object')){ + errorlog("Group isn't an object"); + return; + } + groups.forEach(group=>{ + var ele = getById("container_" + UUID).querySelector('[data-action-type="toggle-group"][data--u-u-i-d="'+UUID+'"][data-group="'+group+'"]'); + if (!ele){ + var newGroup = htmlToElement(''); + + var added = false; + getById("container_" + UUID).querySelectorAll('.customGroup>[data-group]').forEach(ele=>{ + log(ele); + if (!added && ele.dataset.group>group+""){ + ele.parentNode.insertBefore(newGroup, ele); + added = true; + } + }); + if (!added){ + var newGroupCon = getById("container_" + UUID).querySelector(".customGroup"); + if (!newGroupCon){ + newGroupCon = document.createElement("div"); + newGroupCon.classList.add("customGroup"); + getById("container_" + UUID).appendChild(newGroupCon); + } + newGroupCon.appendChild(newGroup); + } + } + }); + + var elements = document.querySelectorAll('[data-action-type="toggle-group"][data--u-u-i-d="'+UUID+'"][data-group]'); + if (elements.length){ + for (var i=0;i0){ + session.roomTimer = false; + } else { + session.roomTimer = Date.now()/1000 - session.roomTimer; + } + ele.innerHTML = ' Remove Timer'; + ele.classList.add("red"); + } else { + ele.value = 2; + ele.classList.remove("pressed"); ele.ariaPressed = "false"; + session.roomTimer = false; + msg.stopClock = true; + stopClock(); + msg.hideClock = true; + hideClock(); + ele.innerHTML = ' Create Timer'; + } + //miniTranslate(ele); + } else if (event.ctrlKey || event.metaKey){ + if (ele.value == 1) { + ele.value = 3; + msg.pauseClock = true; + pauseClock(); + if (!session.roomTimer){ + session.roomTimer = false; + } else if (session.roomTimer0){ + session.roomTimer = false; + } else { + session.roomTimer = Date.now()/1000 - session.roomTimer; + } + ele.innerHTML = ' Remove Timer'; + ele.classList.add("red"); + } + } + if (ele.dataset.UUID){ + session.sendRequest(msg, ele.dataset.UUID); + } else { + session.sendRequest(msg); + } +} + +function updateRemoteTimerButton(UUID, currentTime) { + var elements = document.querySelectorAll('[data-action-type="create-timer"][data--u-u-i-d="' + UUID + '"]'); + if (elements[0]){ + if (elements[0].value != 2) { + var time = parseInt(currentTime) || 0; + elements[0].classList.add("pressed"); elements[0].ariaPressed = "true"; + elements[0].value = 1; + if (time<0) { + time = time * -1; + var minutes = Math.floor(time / 60); + var seconds = time - minutes * 60; + elements[0].classList.add("red"); + elements[0].innerHTML = ' -' + (minutes) + "m : " + zpadTime(seconds) + "s"; + } else { + var minutes = Math.floor(time / 60); + var seconds = time - minutes * 60; + elements[0].classList.remove("red"); + elements[0].innerHTML = ' ' + (minutes) + "m : " + zpadTime(seconds) + "s"; + } + } else { + elements[0].classList.remove("pressed"); elements[0].ariaPressed = "false"; + elements[0].classList.remove("red"); + elements[0].innerHTML = ' Create Timer'; + } + } +} + + +function directMute(ele, event=false) { // A directing room only is controlled by the Director, with the exception of MUTE. + log("mute 2"); + + if (!event || (!((event.ctrlKey) || (event.metaKey)))) { + if (ele.value == 1) { + ele.value = 0; + ele.classList.remove("pressed"); ele.ariaPressed = "false"; + miniTranslate(ele,"mute-scene"); + } else { + ele.value = 1; + ele.classList.add("pressed"); ele.ariaPressed = "true"; + miniTranslate(ele,"unmute"); + } + } + var msg = {}; + msg.scene = true; + msg.action = "mute"; + msg.value = ele.value; + msg.target = ele.dataset.sid; + + log(msg); + log("ele:"); + log(ele); + //for (var uuid in session.pcs){ // obsolete at this point; v22 + // if (session.pcs[uuid].stats.info && ("version" in session.pcs[uuid].stats.info) && (session.pcs[uuid].stats.info.version < 17.2)){ + // msg.request = "sendroom"; + // session.sendMsg(msg); + // return; + // } + //} + + for (var uuid in session.pcs){ + if (session.pcs[uuid].scene!==false){ // send to all scenes (but scene = 0) + session.sendMessage(msg, uuid); + } + } + + syncDirectorState(ele); + + if (msg.value){ + return true; + } else { + return false; + } +} + +function requestFileUpload(ele){ + ele.classList.add("pressed"); ele.ariaPressed = "true"; + ele.disabled = true; + ele.innerHTML = ' Requesting..'; + setTimeout(function(ele){ + try{ + ele.innerHTML = ' Request File'; + ele.classList.remove("pressed"); ele.ariaPressed = "false"; + ele.disabled = false + } catch(e){} + },15000, ele); + var msg = {}; + msg.requestUpload = true; // toggleFileshare + msg.UUID = ele.dataset.UUID; + session.sendRequest(msg, ele.dataset.UUID); +} + +function remoteSpeakerMute(ele, event=false){ + log("speaker mute"); + if (!event || (!((event.ctrlKey) || (event.metaKey)))) { + if (ele.value == 1) { + ele.value = 0; + ele.classList.remove("pressed"); ele.ariaPressed = "false"; + ele.innerHTML = ' Deafen'; + } else { + ele.value = 1; + ele.classList.add("pressed"); ele.ariaPressed = "true"; + ele.innerHTML = ' Undeafen'; + } + miniTranslate(ele); + } + + var msg = {}; + if (ele.value == 0) { + msg.speakerMute = false + } else { + msg.speakerMute = true; + } + msg.UUID = ele.dataset.UUID; + session.sendRequest(msg, ele.dataset.UUID); + syncDirectorState(ele); + + errorlog(msg); + + return msg.speakerMute; +} + +function updateRemoteSpeakerMute(UUID) { + var ele = document.querySelectorAll('[data-action-type="toggle-remote-speaker"][data--u-u-i-d="' + UUID + '"]'); + if (ele[0]) { + ele[0].classList.add("pressed"); ele[0].ariaPressed = "true"; + ele[0].value = 1; + ele[0].innerHTML = ' undeafen'; + miniTranslate(ele[0]); + } + return true; +} + +function updateRemoteDisplayMute(UUID, blind=true) { + var ele = document.querySelectorAll('[data-action-type="toggle-remote-display"][data--u-u-i-d="' + UUID + '"]'); + if (ele[0]) { + if (blind){ + ele[0].classList.add("pressed"); ele[0].ariaPressed = "true"; + ele[0].value = 1; + ele[0].innerHTML = ' unblind'; + miniTranslate(ele[0]); + return true; + } else { + ele[0].classList.remove("pressed"); ele[0].ariaPressed = "false"; + ele[0].value = 0; + ele[0].innerHTML = ' blind'; + miniTranslate(ele[0]); + return false; + } + } + return false; +} + +function blindAllGuests(ele, event=false){ + if (!session.director){ + if (!session.cleanOutput){warnUser("Only a director can mute other guests");} + return; + } // only a director can use this button. + + log("blind all display mute"); + if (!event || (!((event.ctrlKey) || (event.metaKey)))) { + if (ele.value == 1) { + ele.value = 0; + ele.classList.remove("pressed"); ele.ariaPressed = "false"; + ele.classList.remove("red"); + ele.innerHTML = ''; + } else { + ele.value = 1; + ele.classList.add("pressed"); ele.ariaPressed = "true"; + ele.classList.add("red"); + ele.innerHTML = ''; + } + } + + var msg = {}; + if (ele.value == 0) { + msg.displayMute = false; + session.directorBlindAllGuests = false; + } else { + msg.displayMute = true; + session.directorBlindAllGuests= true; + } + for (var UUID in session.rpcs){ // doesn't include scenes, as they don't publiish and this is rpcs + if (session.directorList.indexOf(UUID)>=0){continue;} // don't try to mute other directors + try { + session.sendRequest(msg, UUID); + updateRemoteDisplayMute(UUID, msg.displayMute); + } catch(e){errorlog(e);} + } + syncDirectorState(ele); + return msg.displayMute; +} + +function remoteDisplayMute(ele, event=false) { + log("display mute"); + if (!event || (!((event.ctrlKey) || (event.metaKey)))) { + if (ele.value == 1) { + ele.value = 0; + ele.classList.remove("pressed"); ele.ariaPressed = "false"; + ele.innerHTML = ' Blind'; + } else { + ele.value = 1; + ele.classList.add("pressed"); ele.ariaPressed = "true"; + ele.innerHTML = ' Unblind'; + } + miniTranslate(ele); + } + + var msg = {}; + if (ele.value == 0) { + msg.displayMute = false; + } else { + msg.displayMute = true; + } + msg.UUID = ele.dataset.UUID; + session.sendRequest(msg, ele.dataset.UUID); + syncDirectorState(ele); + return msg.displayMute; +} + +function remoteLowerhands(UUID) { + var msg = {}; + msg.lowerhand = true; + msg.UUID = UUID; + session.sendRequest(msg, UUID); + + try{ + getById("hands_"+UUID).classList.add("hidden"); + session.rpcs[UUID].remoteRaisedHandElement.classList.add("hidden"); + } catch(e){} + return true; +} + + +function remoteMute(ele, event=false, skipSend=false) { + log("mute"); + var val = parseInt(ele.value) || 0; + if (!event || (!((event.ctrlKey) || (event.metaKey)))) { + if (val == 1){ + ele.value = 0; + ele.classList.remove("pressed"); ele.ariaPressed = "false"; + //ele.innerHTML = ' mute'; + miniTranslate(ele,"mute"); + //ele.innerHTML += getTranslation("mute"); + } else { + ele.value = 1; + ele.classList.add("pressed"); ele.ariaPressed = "true"; + //ele.innerHTML = ' unmute'; + miniTranslate(ele,"unmute"); + } + } + + try { + session.rpcs[ele.dataset.UUID].directorMutedState = ele.value; + var volume = session.rpcs[ele.dataset.UUID].directorVolumeState; + } catch (e) { + errorlog(e); + var volume = 100; + } + + if (!skipSend){ + var msg = {}; + if (val == 1) { + msg.volume = volume; + } else { + msg.volume = 0; + } + msg.UUID = ele.dataset.UUID; + session.sendRequest(msg, ele.dataset.UUID); + syncDirectorState(ele); + log(msg); + } + + if (session.rpcs[ele.dataset.UUID].directorMutedState==1){ + pokeIframeAPI("director-mute-state", true, ele.dataset.UUID); + pokeAPI("directorMuted", true, session.rpcs[ele.dataset.UUID].streamID); + } else { + pokeIframeAPI("director-mute-state", false, ele.dataset.UUID); + pokeAPI("directorMuted", false, session.rpcs[ele.dataset.UUID].streamID); + } + + if (val){ + return true; + } else { + return false; + } +} + +function toggleQualityGear3(){ + toggle(document.getElementById('videoSettings3'), inline=false); + if (getById("gear_webcam3").style.display === "inline-block") { + + var videoSelect = document.querySelector("select#videoSource3").options; + var obscam = false; + log(videoSelect[videoSelect.selectedIndex].text); + if (videoSelect[videoSelect.selectedIndex].text.startsWith("OBS-Camera")) { // OBS Virtualcam + obscam = true; + } else if (videoSelect[videoSelect.selectedIndex].text.startsWith("OBS Virtual Camera")) { // OBS Virtualcam + obscam = true; + } + + updateStats(obscam); + } +} + +function remoteHideVideo(ele, event=false, skipSend=false) { + log("video hide"); + + if (!event || ((event.ctrlKey) || (event.metaKey))) { + //ele.children[1].innerHTML = getTranslation("armed"); + miniTranslate(ele.children[1],"armed"); + //ele.style.backgroundColor = "#BF3F3F"; + ele.classList.add("armed"); + Callbacks.push([remoteHideVideo, ele, false]); + log("video queued"); + return; + } else { + if (ele.value == 1) { + ele.value = 0; + ele.classList.remove("pressed"); ele.ariaPressed = "false"; + ele.innerHTML = ' Hide'; + } else { + ele.value = 1; + ele.classList.add("pressed"); ele.ariaPressed = "true"; + ele.innerHTML = ' Unhide'; + } + miniTranslate(ele); + ele.classList.remove("armed");//ele.style.backgroundColor = null; + } + + var msg = {}; + if (ele.value == 0) { + msg.directVideoMuted = false; + } else { + msg.directVideoMuted = true; + } + + if (!skipSend){ + + for (var i in session.pcs){ + msg.target = ele.dataset.UUID; + + if (i === msg.target){ + msg.target = true; + } + try{ + session.pcs[i].sendChannel.send(JSON.stringify(msg)); + } catch(e){} + + } + syncDirectorState(ele); + } + + pokeIframeAPI("director-video-hide-state", msg.directVideoMuted, ele.dataset.UUID); + pokeAPI("directorVideoHide", msg.directVideoMuted, session.rpcs[ele.dataset.UUID].streamID); + + return msg.directVideoMuted; +} + +function remoteMuteVideo(ele, event=false, skipSend=false) { + log("video mute"); + + if (!event || ((event.ctrlKey) || (event.metaKey))) { + //ele.children[1].innerHTML = getTranslation("armed"); + miniTranslate(ele.children[1],"armed"); + ele.classList.add("armed");//ele.style.backgroundColor = "#BF3F3F"; + Callbacks.push([remoteMuteVideo, ele, false]); + log("video queued"); + return; + } else { + if (ele.value == 1) { + ele.value = 0; + ele.classList.remove("pressed"); ele.ariaPressed = "false"; + ele.innerHTML = ' Video off'; + } else { + ele.value = 1; + ele.classList.add("pressed"); ele.ariaPressed = "true"; + ele.innerHTML = ' Video on'; + } + miniTranslate(ele); + ele.classList.remove("armed"); + } + + var msg = {}; + if (ele.value == 0) { + msg.remoteVideoMuted = false; + } else { + msg.remoteVideoMuted = true; + } + + if (!skipSend){ + session.sendRequest(msg, ele.dataset.UUID) + syncDirectorState(ele); + } + + pokeIframeAPI("remote-video-mute-state", msg.remoteVideoMuted, ele.dataset.UUID); + pokeAPI("remoteVideoMuted", msg.remoteVideoMuted, session.rpcs[ele.dataset.UUID].streamID); + + return msg.remoteVideoMuted; +} +function updateDirectorVideoHide(UUID) { + var ele = document.querySelectorAll('[data-action-type="hide-guest"][data--u-u-i-d="' + UUID + '"]'); + if (ele[0]) { + ele[0].value = 1; + ele[0].classList.add("pressed"); ele[0].ariaPressed = "true"; + ele[0].innerHTML = ' Unhide'; + miniTranslate(ele[0]); + } + return true; +} +function updateDirectorVideoMute(UUID) { + var ele = document.querySelectorAll('[data-action-type="mute-video-guest"][data--u-u-i-d="' + UUID + '"]'); + if (ele[0]) { + ele[0].value = 1; + ele[0].classList.add("pressed"); ele[0].ariaPressed = "true"; + ele[0].innerHTML = ' Video on'; + miniTranslate(ele[0]); + } + return true; +} + +function directVolume(ele) { // NOT USED ANYMORE + log("volume"); + var msg = {}; + msg.scene = true; + msg.action = "volume"; + msg.target = ele.dataset.sid; // i want to focus on the STREAM ID, not the UUID... + msg.value = ele.value; + + //for (var uuid in session.pcs){ + // if (session.pcs[uuid].stats.info && ("version" in session.pcs[uuid].stats.info) && (session.pcs[uuid].stats.info.version < 17.2)){ + // msg.request = "sendroom"; + // session.sendMsg(msg); + // return; + // } + //} + + for (var uuid in session.pcs){ + if (session.pcs[uuid].scene!==false){ // send to all scenes (but scene = 0) + session.sendMessage(msg, uuid); + } + } + + syncDirectorState(ele); + return msg.value; +} + +function applyMuteState(UUID){ // this is the mute state of PLAYBACK audio; not the microphone or outbound. + if (!(UUID in session.rpcs)){return "UUID not found";} + var muteOutcome = session.rpcs[UUID].mutedState || session.rpcs[UUID].mutedStateMixer || session.rpcs[UUID].mutedStateScene || session.speakerMuted || session.rpcs[UUID].bandwidthMuted; + + if (!muteOutcome && (session.noaudio !== false)){ + if (session.noaudio===true){ + muteOutcome = true; + } else if (session.noaudio.length){ + if (("streamID" in session.rpcs[UUID]) && session.rpcs[UUID].streamID && !session.noaudio.includes(session.rpcs[UUID].streamID)){ + muteOutcome = true; + } + } else { + muteOutcome = true; + } + } + + if (session.rpcs[UUID].videoElement){ + if (session.rpcs[UUID].videoElement && session.rpcs[UUID].videoElement.usermuted===true){return "usermuted true";} + session.rpcs[UUID].videoElement.muted = muteOutcome; + } + + // session.scene + return muteOutcome; +} + +function checkMuteState(UUID){ // this is the mute state of PLAYBACK audio; not the microphone or outbound. + if (!(UUID in session.rpcs)){return false;} + var muteOutcome = session.rpcs[UUID].mutedState || session.rpcs[UUID].mutedStateMixer || session.rpcs[UUID].mutedStateScene || session.speakerMuted || session.rpcs[UUID].bandwidthMuted; + return muteOutcome; +} + +function remoteVolumeUI(ele){ + ele.nextElementSibling.innerHTML = ele.value + "%"; + if (Date.now() - remoteSliderTimeout > 100){ + remoteSliderTimeout = Date.now(); + remoteVolume(ele); + } + //setVolumeColor(ele); + return ele.value; +} + +/* function setVolumeColor(ele){ + var vol1 = 200-parseInt(ele.value); + if (vol1<0){vol1=0}; + ele.style.backgroundColor = "hsl("+vol1+", 100%, 50%)"; +} */ + +function remoteVolume(ele) { // A directing room only is controlled by the Director, with the exception of MUTE. + log("volume: "+session.rpcs[ele.dataset.UUID].directorMutedState); + var msg = {}; + var muted = session.rpcs[ele.dataset.UUID].directorMutedState; + // + //log(ele); + if (muted == true) { // 1 is a string, not an int, so == and not ===. this happens in a few places :/ + session.rpcs[ele.dataset.UUID].directorVolumeState = ele.value; + } else { + session.rpcs[ele.dataset.UUID].directorVolumeState = ele.value; + msg.volume = ele.value; + msg.UUID = ele.dataset.UUID; + session.sendRequest(msg, ele.dataset.UUID); + } + + pokeIframeAPI("director-volume-state", ele.value, ele.dataset.UUID); + + syncDirectorState(ele); + return ele.value; +} + +function clearDirectorSettings(){ // make sure to wipe the director's room settings if creating a new room. + removeStorage("directorCustomize"); + removeStorage("directorWebsiteShare"); +} + +function saveDirectorSettings(){ + var settings = {}; + + if (getById("customizeLinks").classList.contains("hidden")){ + settings.customizeLinks = true; + } + + var customizeLinks1 = getById("customizeLinks1").querySelectorAll("input"); + settings.customizeLinks1 = {}; + for (var i=0;i { + try { + if (customizeLinks1.querySelector('[data-param="'+key+'"]').checked != settings.customizeLinks1[key]){ + customizeLinks1.querySelector('[data-param="'+key+'"]').checked = settings.customizeLinks1[key]; + customizeLinks1.querySelector('[data-param="'+key+'"]').onchange(); + } + } catch(e){errorlog(e);} + }); + } + + if (settings.customizeLinks3){ + var customizeLinks3 = getById("customizeLinks3"); + Object.keys(settings.customizeLinks3).forEach((key, index) => { + try { + if (customizeLinks3.querySelector('[data-param="'+key+'"]').checked == settings.customizeLinks3[key]){ + customizeLinks3.querySelector('[data-param="'+key+'"]').checked = settings.customizeLinks3[key]; + customizeLinks3.querySelector('[data-param="'+key+'"]').onchange(); + } + } catch(e){errorlog(e);} + }); + } + + if (settings.directorLinks1){ + var directorLinks1 = getById("directorLinks1"); + Object.keys(settings.directorLinks1).forEach((key, index) => { + try { + if (directorLinks1.querySelector('[data-param="'+key+'"]').checked == settings.directorLinks1[key]){ + directorLinks1.querySelector('[data-param="'+key+'"]').checked = settings.directorLinks1[key]; + directorLinks1.querySelector('[data-param="'+key+'"]').onchange(); + } + } catch(e){ + errorlog("key :"+key); + errorlog(e); + } + }); + } + + if (settings.directorLinks2){ + var directorLinks2 = getById("directorLinks2"); + Object.keys(settings.directorLinks2).forEach((key, index) => { + try { + if (directorLinks2.querySelector('[data-param="'+key+'"]').checked == settings.directorLinks2[key]){ + directorLinks2.querySelector('[data-param="'+key+'"]').checked = settings.directorLinks2[key]; + directorLinks2.querySelector('[data-param="'+key+'"]').onchange(); + } + } catch(e){ + errorlog("key :"+key); + errorlog(e); + } + }); + } +} + + + +function sendChat(chatmessage = "hi", UUID=false, overlay=false) { // A directing room only is controlled by the Director, with the exception of MUTE. + log("Chat message"); + var msg = {}; + msg.chat = chatmessage; + msg.overlay = overlay; + session.sendPeers(msg, UUID); + return true; +} + +var activatedStream = false; + +async function publishScreen() { + if (activatedStream == true) { + return; + } + activatedStream = true; + setTimeout(function() { + activatedStream = false; + }, 1000); + + formSubmitting = false; + + var quality = 0; + + if (document.getElementById("webcamquality2")){ + quality = parseInt(document.getElementById("webcamquality2").elements.namedItem("resolution2").value) || 0; + } + + session.quality_ss = quality; + + if (session.quality !== false) { + quality = session.quality; // override the user's setting + } + + if (session.screensharequality !== false){ + quality = session.screensharequality; + } + + var video = {} + + if (quality == -1) { + // unlocked capture resolution + } else if (quality == 0) { + + video.width = { + ideal: 1920 + }; + video.height = { + ideal: 1080 + }; + } else if (quality == 1) { + video.width = { + ideal: 1280 + }; + video.height = { + ideal: 720 + }; + } else if (quality == 2) { + video.width = { + ideal: 640 + }; + video.height = { + ideal: 360 + }; + } else if (quality >= 3) { // lowest + video.width = { + ideal: 320 + }; + video.height = { + ideal: 180 + }; + } else { + video.width = { + min: 640 + }; + video.height = { + min: 360 + }; + } + + if (session.width) { + video.width = { + ideal: session.width + }; + } + if (session.height) { + video.height = { + ideal: session.height + }; + } + + var constraints = { + audio: { + echoCancellation: false, + autoGainControl: false, + noiseSuppression: false + }, + video: video + }; + + if (session.noiseSuppression === true) { + constraints.audio.noiseSuppression = true;; // the defaults for screen publishing should be off. + } + if (session.autoGainControl === true) { + constraints.audio.autoGainControl = true; // the defaults for screen publishing should be off. + } + if (session.echoCancellation === true) { + constraints.audio.echoCancellation = true; // the defaults for screen publishing should be off. + } + + try { + let supportedConstraints = navigator.mediaDevices.getSupportedConstraints(); + if (supportedConstraints.cursor) { + if (session.screensharecursor){ + constraints.video.cursor = ["always", "motion"]; + } else { + constraints.video.cursor = "never"; + } + } + if (session.suppressLocalAudioPlayback && supportedConstraints.suppressLocalAudioPlayback){ + constraints.audio.suppressLocalAudioPlayback = true; + } + // + if (session.preferCurrentTab){ + constraints.preferCurrentTab = true; + } + if (session.selfBrowserSurface){ + constraints.selfBrowserSurface = session.selfBrowserSurface; // exclude or include + } + if (session.surfaceSwitching){ + constraints.surfaceSwitching = session.surfaceSwitching; // exclude or include + } + if (session.systemAudio){ + constraints.systemAudio = session.systemAudio; // exclude or include + } + if (session.displaySurface && supportedConstraints.displaySurface){ + constraints.video.displaySurface = session.displaySurface; // monitor, window, or browser + } + } catch(e){ + warnlog("navigator.mediaDevices.getSupportedConstraints() not supported"); + } + + var overrideFramerate = false; + if ((session.frameRate !== false) && (session.maxframeRate != false)){ + overrideFramerate = session.frameRate; + constraints.video.frameRate = { + ideal: session.maxframeRate, + max: session.maxframeRate + }; + } else if (session.frameRate !== false) { + constraints.video.frameRate = session.frameRate; + } else if (session.maxframeRate != false){ + constraints.video.frameRate = { + ideal: session.maxframeRate, + max: session.maxframeRate + }; + } else { + constraints.video.frameRate = { + ideal: 60 + }; + } + + var audioSelect = getById('audioSourceScreenshare'); + var outputSelect = getById('outputSourceScreenshare'); + + try { + session.sink = outputSelect.options[outputSelect.selectedIndex].value; // will probably fail on Safari. + log("Session Sink: " + session.sink); + saveSettings(); + } catch (e){warnlog(e);} + + return await publishScreen2(constraints, audioSelect, true, overrideFramerate).then((res) => { + if (res == false) { + return; + } // no screen selected + log("streamID is: " + session.streamID); + if (session.transcript) { + setTimeout(function() { + setupClosedCaptions(); + }, 1000); + } + + if (!session.cleanOutput){ + getById("mutebutton").classList.remove("hidden"); + getById("mutespeakerbutton").classList.remove("hidden"); + //getById("mutespeakerbutton").className="float"; + getById("chatbutton").className = "float"; + getById('sharefilebutton').classList.remove("hidden"); // we won't override "display:none", if set, though. + getById("mutevideobutton").className = "float"; + getById("hangupbutton").className = "float"; + if (session.showSettings) { + getById("settingsbutton").className = "float"; + } + if (session.raisehands) { + getById("raisehandbutton").className = "float"; + } + if (session.pptControls){ + getById("pptbackbutton").classList.remove("hidden"); + getById("pptnextbutton").classList.remove("hidden"); + } + if (session.recordLocal !== false) { + getById("recordLocalbutton").classList.remove("hidden"); + } + if (session.screensharebutton) { + getById("screensharebutton").className = "float"; + } + getById("controlButtons").classList.remove("hidden"); + getById("helpbutton").style.display = "inherit"; + getById("reportbutton").style.display = ""; + } else if (session.cleanish && session.recordLocal!==false){ + getById("recordLocalbutton").classList.remove("hidden"); + getById("mutebutton").classList.add("hidden"); + getById("mutespeakerbutton").classList.add("hidden"); + getById("chatbutton").classList.add("hidden"); + getById("mutevideobutton").classList.add("hidden"); + getById("hangupbutton").classList.add("hidden"); + getById("hangupbutton2").classList.add("hidden"); + getById("controlButtons").classList.remove("hidden"); + getById("settingsbutton").classList.add("hidden"); + getById("screenshare2button").classList.add("hidden"); + getById("screensharebutton").classList.add("hidden"); + getById("screenshare3button").classList.add("hidden"); + getById("queuebutton").classList.add("hidden"); + } else { + getById("controlButtons").classList.add("hidden"); + } + + if (session.chatbutton === true) { + getById("chatbutton").classList.remove("hidden"); + getById("controlButtons").classList.remove("hidden"); + } else if (session.chatbutton === false) { + getById("chatbutton").classList.add("hidden"); + } + + if (session.screensharebutton === true) { + getById("controlButtons").classList.remove("hidden"); + getById("screensharebutton").className = "float"; + } + + if (session.hangupbutton === true){ + getById("controlButtons").classList.remove("hidden"); + getById("hangupbutton").className = "float"; + } + + + getById("head1").className = 'hidden'; + getById("head2").className = 'hidden'; + + return res; + }).catch(() => {}); +} + +function getWidth() { + return Math.max( + document.body.scrollWidth, + document.documentElement.scrollWidth, + document.body.offsetWidth, + document.documentElement.offsetWidth, + document.documentElement.clientWidth + ); +} + +function getHeight() { + return Math.max( + document.documentElement.clientHeight + ); +} + +function updateForceRotate(skipLastBit=false){ + var capabilities = {facingMode:"unknown"}; + var FirefoxSucks = false; + + if (session.orientation){ + try { + var track = false; + if (session.streamSrc){ + var tracks = session.streamSrc.getVideoTracks(); + if (tracks.length){ + track = tracks[0]; + } + } + if (!track){ + return; + } + + + const settings = track.getSettings(); + session.currentCameraConstraints = settings; + + + if (screen && screen.orientation && screen.orientation.type){ + if (!screen.orientation.type.includes("portrait")){ + if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ + session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; + } + } + } else if (!window.matchMedia("(orientation: portrait)").matches){ + if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ + session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; + } + } + + session.forceRotate = 0; + + if (track.getCapabilities){ + capabilities = track.getCapabilities(); // firefox sucks? + + if ("width" in settings){ + if ("height" in settings){ + if (settings.width < settings.height){ + if (session.orientation=="landscape"){ + if (capabilities){ + if (capabilities.facingMode == "environment"){ + session.forceRotate=270; + } else { + session.forceRotate=90; + } + } + } else { + session.forceRotate = 0; + } + } else if (settings.width > settings.height){ + if (session.orientation=="portrait"){ + if (capabilities){ + if (capabilities.facingMode == "environment"){ + session.forceRotate=90; + } else { + session.forceRotate=270; + } + } + } else { + session.forceRotate = 0; + } + } else { + session.forceRotate = 0; + } + } else { + return; + } + } + + } else if (Firefox && session.mobile){ // firefox sucks... + try { + var vs1 = document.getElementById('videoSourceSelect') || document.getElementById('videoSource3'); + if (vs1){ + vs1 = vs1.options[vs1.selectedIndex].textContent; + if (vs1.includes(" back")){ + capabilities = {facingMode:"environment"}; + FirefoxSucks = 2; + } else if (vs1.includes(" front")){ + capabilities = {facingMode:"user"}; + FirefoxSucks = 1; + } + } + getById("videosource").style.transform = ""; + if (session.orientation=="landscape"){ + if (FirefoxSucks === 1){ + session.forceRotate = 90;; + // video needs to be flipped + getById("videosource").style.transform = "rotate(90deg)"; + } else if (FirefoxSucks === 2){ + getById("videosource").style.transform = "rotate(270deg)"; + session.forceRotate = 270;; + } + } + + } catch(e){} + } else { + return; + } + + var msg = {}; + if (session.forceRotate!==false){ + if (session.rotate){ + msg.rotate_video = session.forceRotate + parseInt(session.rotate); + } else { + msg.rotate_video = session.forceRotate; + } + } else { + msg.rotate_video = session.rotate; + } + + if (msg.rotate_video && (msg.rotate_video>=360)){ + msg.rotate_video-=360; + } + + var msgEncoded = JSON.stringify(msg); + for (var UUID in session.pcs){ + try{ + if (session.pcs[UUID].rotation != msg.rotate_video){ // 0 == false will skip I think + session.pcs[UUID].sendChannel.send(msgEncoded); + session.pcs[UUID].rotation = msg.rotate_video; + //log("sending updated rotation info"); + } + } catch(e){ + warnlog("RTC Connection seems to be dead or not yet open? 8"); + } + } + + } catch(e){errorlog(e);} + + if (!(Firefox && session.mobile)){ + updateForceRotatedCSS() + } + } else if (Firefox && session.mobile){ + try { + var vs1 = document.getElementById('videoSourceSelect') || document.getElementById('videoSource3'); + if (vs1){ + vs1 = vs1.options[vs1.selectedIndex].textContent; + if (vs1.includes(" back")){ + FirefoxSucks = 2; + } else if (vs1.includes(" front")){ + FirefoxSucks = 1; + } + } + + if (screen && screen.orientation && screen.orientation.type){ + if (screen.orientation.type.includes("portrait")){ + session.forceRotate = 0; + } else if (screen.orientation.type.includes("landscape")){ + if (FirefoxSucks === 1){ + session.forceRotate = 90;; + } else if (FirefoxSucks === 2){ + session.forceRotate = 270;; + } + } + } else if (window.matchMedia("(orientation: portrait)").matches){ // legacy support; it seems to update late, 100ms or so after screen.orientation, so lets not use it + session.forceRotate = 0; + } else if (window.matchMedia("(orientation: landscape)").matches){ + if (FirefoxSucks === 1){ + session.forceRotate = 90;; + } else if (FirefoxSucks === 2){ + session.forceRotate = 270;; + } + } + + var msg = {}; + if (session.forceRotate!==false){ + if (session.rotate){ + msg.rotate_video = session.forceRotate + parseInt(session.rotate); + } else { + msg.rotate_video = session.forceRotate; + } + if (msg.rotate_video && (msg.rotate_video>=360)){ + msg.rotate_video-=360; + } + warnlog("FIREFOX MOBILE ONLY ROTATE: "+msg.rotate_video); + //session.sendMessage(msg); + + var msgEncoded = JSON.stringify(msg); + for (var UUID in session.pcs){ + try{ + if (session.pcs[UUID].rotation != msg.rotate_video){ + session.pcs[UUID].sendChannel.send(msgEncoded); + session.pcs[UUID].rotation = msg.rotate_video; + //log("sending updated rotation info"); + } + } catch(e){ + warnlog("RTC Connection seems to be dead or not yet open? 8"); + } + } + } + } catch(e){} + } + if (!skipLastBit){ + applyMirror(session.mirrorExclude); + session.setResolution(); // probably only triggers with mobile devices? + } +} + +function updateForceRotatedCSS(rotateThis=session.forceRotate){ + if (rotateThis==270){ + document.body.setAttribute( "style", "transform: rotate(270deg);position: absolute;top: 100vh;left: 0;height: 100vw;width: 100vh;transform-origin: 0 0;"); + document.body.dataset.rotated = "1"; + } else if (rotateThis==90){ + document.body.setAttribute( "style", "transform: rotate(90deg);position: absolute;top: 0;left: 100vw;height: 100vw;width: 100vh;transform-origin: 0 0;"); + document.body.dataset.rotated = "1"; + } else if (rotateThis==180){ + document.body.setAttribute( "style", "transform: rotate(180deg);position: absolute;top: 100vh;left: 100vw;height: 100vh;width: 100vw;transform-origin: 0 0;"); + document.body.dataset.rotated = "" + } else { + document.body.setAttribute( "style", ""); + document.body.dataset.rotated = "" + } +} + +async function joinDataMode(){ // join the room, but without publishing anything. + await session.connect(); + if (session.roomid){ + getById("head3").classList.add('hidden'); + getById("head3a").classList.add('hidden'); + joinRoom(session.roomid); + } else if (session.view){ + window.onresize = updateMixer; + play(); + if (session.permaid!==false){ + session.postPublish(); + } + } else if (session.permaid!==false){ + session.postPublish(); + } +} + +function publishWebcam(btn = false, miconly=false) { + + if (btn) { + if (btn.dataset.ready == "false") { + warnlog("Clicked too quickly; button not enabled yet"); + return; + } + + if (getById("passwordBasicInput").value.length){ + session.password = getById("passwordBasicInput").value; + session.password = sanitizePassword(session.password); + if (session.password.length==0){ + session.password = false; + } else { + session.defaultPassword = false; + if (urlParams.has('pass')) { + updateURL("pass=" + session.password); + } else if (urlParams.has('pw')) { + updateURL("pw=" + session.password); + } else if (urlParams.has('p')) { + updateURL("p=" + session.password); + } else { + updateURL("password=" + session.password); + } + } + } + } + + if (activatedStream == true) { + return; + } + activatedStream = true; + log("PRESSED PUBLISH WEBCAM!!"); + + formSubmitting = false; + window.scrollTo(0, 0); // iOS has a nasty habit of overriding the CSS when changing camaera selections, so this addresses that. + + getById("head2").className = 'hidden'; + + if (session.roomid !== false) { // they are in a room or a faux room + + window.onresize = updateMixer; + window.onorientationchange = function(){ + if (Firefox){ + updateForceRotate(true); + } + setTimeout(async function(){ + if (session.forceAspectRatio){ + await updateCameraConstraints("aspectRatio", session.forceAspectRatio); + } + if (session.effect && (session.effect === "7")){digitalZoom(true);} + updateForceRotate(); + updateMixer(); + }, 200); + }; + + if ((session.roomid === "") && ((!(session.view)) || (session.view === ""))) { + // no room, no viewing, viewing disabled + if (session.manual===null){ + session.manual = true; + } + if (!(session.cleanOutput)) { + var showReshare = getStorage("showReshare"); + if (showReshare){ + generateHash(session.streamID + session.salt + "bca321", 4).then(function(hash) { // million to one error. + if (showReshare === hash){ + getById("head3").classList.remove('hidden'); + getById("head3a").classList.remove('hidden'); + } + }).catch(errorlog); + } + } + + } else { + log("ROOM ID ENABLED"); + log("Update Mixer Event on REsize SET"); + getById("main").style.overflow = "hidden"; + //session.cbr=0; // we're just going to override it + + if (session.stereo == 5) { + if (session.roomid === "") { + session.stereo = 1; + } else { + session.stereo = 3; + } + } + joinRoom(session.roomid); + if (session.roomid !== "") { + if (!(session.cleanOutput)) { + getById("head2").className = ''; + } + } + getById("head3").classList.add('hidden'); + getById("head3a").classList.add('hidden'); + } + + } else { // they are not in a room or faux room + if (session.manual===null){ + session.manual = true; + } + getById("head3").classList.remove('hidden'); + getById("head3a").classList.remove('hidden'); + getById("logoname").style.display = 'none'; + generateHash(session.streamID + session.salt + "bca321", 4).then(function(hash) { // million to one error. + setStorage("showReshare", hash, 24*30) + }).catch(errorlog); + } + + log("streamID is: " + session.streamID); + getById("head1").className = 'hidden'; + + + if (!session.cleanOutput){ + getById("mutebutton").classList.remove("hidden"); + getById("mutespeakerbutton").classList.remove("hidden"); + //getById("mutespeakerbutton").className="float"; + getById("chatbutton").className = "float"; + getById('sharefilebutton').classList.remove("hidden"); // we won't override "display:none", if set, though. + getById("mutevideobutton").className = "float"; + getById("hangupbutton").className = "float"; + if (session.showSettings) { + getById("settingsbutton").className = "float"; + } + if (session.raisehands) { + getById("raisehandbutton").className = "float"; + } + if (session.pptControls){ + getById("pptbackbutton").classList.remove("hidden"); + getById("pptnextbutton").classList.remove("hidden"); + } + if (session.recordLocal !== false) { + getById("recordLocalbutton").classList.remove("hidden"); + } + if (session.screensharebutton) { + if (session.roomid) { + if (session.screenshareType===3){ + getById("screenshare3button").className = "float"; + getById("screensharebutton").className = "float hidden"; + getById("screenshare2button").className = "float hidden"; + } else if (session.screenshareType===1){ + getById("screensharebutton").className = "float"; + getById("screenshare3button").className = "float hidden"; + getById("screenshare2button").className = "float hidden"; + } else if (session.screenshareType===2){ + getById("screenshare2button").className = "float"; + getById("screensharebutton").className = "float hidden"; + getById("screenshare3button").className = "float hidden"; + } else { + getById("screenshare3button").className = "float"; + getById("screensharebutton").className = "float hidden"; + getById("screenshare2button").className = "float hidden"; + } + } else { + getById("screensharebutton").className = "float"; + getById("screenshare2button").className = "float hidden"; + getById("screenshare3button").className = "float hidden"; + } + } + getById("controlButtons").classList.remove("hidden"); + getById("helpbutton").style.display = "inherit"; + getById("reportbutton").style.display = ""; + } else if (session.cleanish && session.recordLocal!==false){ + getById("recordLocalbutton").classList.remove("hidden"); + getById("mutebutton").classList.add("hidden"); + getById("mutespeakerbutton").classList.add("hidden"); + getById("chatbutton").classList.add("hidden"); + getById("mutevideobutton").classList.add("hidden"); + getById("hangupbutton").classList.add("hidden"); + getById("hangupbutton2").classList.add("hidden"); + getById("controlButtons").classList.remove("hidden"); + getById("settingsbutton").classList.add("hidden"); + getById("screenshare2button").classList.add("hidden"); + getById("screensharebutton").classList.add("hidden"); + getById("queuebutton").classList.add("hidden"); + } else { + getById("controlButtons").classList.add("hidden"); + } + + if (session.chatbutton === true) { + getById("chatbutton").classList.remove("hidden"); + getById("controlButtons").classList.remove("hidden"); + } else if (session.chatbutton === false) { + getById("chatbutton").classList.add("hidden"); + } + + updatePushId() + + if (session.dataMode){ // skip the media stuff. + errorlog("this shoulnd't happen.."); + session.postPublish(); + return; + } + + if (!session.streamSrc){ + checkBasicStreamsExist(); // create srcObject + videoElement + } + + if (Firefox && session.mobile && session.streamSrc){ // this just keeps the phone active; firefox is more annoying + + setInterval(function(){ + if (document.getElementById("keepAlivePlayer") && session.streamSrc){ + getById("keepAlivePlayer").remove(); + } else if (!document.getElementById("keepAlivePlayer")){ + let fakeElement = document.createElement("video"); + fakeElement.autoplay = true; + fakeElement.loop = true; + fakeElement.muted = true; + fakeElement.src = "./media/micro.mp4"; + fakeElement.style.width = "1px"; + fakeElement.style.height ="1px"; + fakeElement.controls = false; + fakeElement.id = "keepAlivePlayer"; + getById("main").appendChild(fakeElement); + } + }, 4000); + } else if (!session.avatar && session.mobile && session.streamSrc && !session.streamSrc.getVideoTracks().length){ // this just keeps the phone active. + + setInterval(function(){ + if (document.getElementById("keepAlivePlayer") && session.streamSrc.getVideoTracks().length){ + getById("keepAlivePlayer").remove(); + } else if (!document.getElementById("keepAlivePlayer")){ + let fakeElement = document.createElement("video"); + fakeElement.autoplay = true; + fakeElement.loop = true; + fakeElement.muted = true; + fakeElement.src = "./media/micro.mp4"; + fakeElement.style.width = "1px"; + fakeElement.style.height ="1px"; + fakeElement.controls = false; + fakeElement.id = "keepAlivePlayer"; + getById("main").appendChild(fakeElement); + } + }, 4000); + } + + + session.publishStream(getById("previewWebcam")); // calls session.postPublish at the end. +} + +function createYoutubeLink(vidid){ + return "https://www.youtube.com/embed/"+vidid+"?modestbranding=1&playsinline=1&enablejsapi=1"; +} +function parseURL4Iframe(iframeURL){ + if (iframeURL==""){ + iframeURL="./"; + } + if (iframeURL === session.iframeSrc){return iframeURL;} + + if (iframeURL.startsWith("http://")){ + try { + iframeURL = "https://"+ iframeURL.split("http://")[1]; + } catch(e){errorlog(e);} + } + + if (iframeURL.startsWith("https://") || iframeURL.startsWith("http://")){ + var domain = (new URL(iframeURL)); + domain = domain.hostname; + + if (domain == "youtu.be"){ + iframeURL = iframeURL.replace("youtu.be/","youtube.com/watch?v="); + } + + if ((domain == "youtu.be") || (domain=="www.youtube.com") || (domain=="youtube.com")){ + var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/; + var match = iframeURL.match(regExp); + var vidid = (match&&match[7].length==11)? match[7] : false; + + // https://www.youtube.com/live_chat?v=&embed_domain= + if (iframeURL.includes("/live_chat")){ + if (!iframeURL.includes("&embed_domain=")){ + iframeURL += "&embed_domain="+location.hostname; + } + } + + if (vidid){ + //specialResult = {}; + //specialResult.originalSrc = iframeURL; + //specialResult.parsedSrc = "https://www.youtube.com/embed/"+vidid+"?autoplay=1&modestbranding=1&playsinline=1&enablejsapi=1"; + //specialResult.handler = "youtube"; + //specialResult.vid = vidid; + //iframeURL = specialResult; + iframeURL = createYoutubeLink(vidid); + } else { // see if there is a playlist link here or not. + + // https://youtube.com/playlist?list=PLWodc2tCfAH1l_LDvEyxEqFf42hOBKqQM + iframeURL = iframeURL.replace("playlist?list=","embed/videoseries?list="); + + var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(videoseries\?))\??list?=?([^#&?]*).*/; + var match = iframeURL.match(regExp); + var plid = (match&&match[7].length==34)? match[7] : false; + if (plid){ + iframeURL = 'https://www.youtube.com/embed/videoseries?list='+plid+"&autoplay=1&modestbranding=1&playsinline=1&enablejsapi=1"; + } + + } + + } else if ((domain=="twitch.tv") || (domain=="www.twitch.tv")){ + if (iframeURL.includes("twitch.tv/popout/")){ + // this is a twitch live chat window + iframeURL = iframeURL.replace("/popout/","/embed/"); + iframeURL = iframeURL.replace("?popout=","?parent="+location.hostname); + iframeURL = iframeURL.replace("?popout","?parent="+location.hostname); + iframeURL = iframeURL.replace("&popout=","?parent="+location.hostname); + iframeURL = iframeURL.replace("&popout","?parent="+location.hostname); + if (iframeURL.includes("darkpopout=")){ + iframeURL = iframeURL.replace("?darkpopout=","?darkpopout=&parent="+location.hostname); + } else { + iframeURL = iframeURL.replace("?darkpopout","?darkpopout&parent="+location.hostname); + } + } else { + var vidid = iframeURL.split('/').pop().split('#')[0].split('?')[0]; + if (vidid){ + iframeURL = "https://player.twitch.tv/?channel="+vidid+"&parent="+location.hostname; + } + } + } else if ((domain=="www.vimeo.com") || (domain=="vimeo.com")){ + iframeURL = iframeURL.replace("//vimeo.com/","//player.vimeo.com/video/"); + iframeURL = iframeURL.replace("//www.vimeo.com/","//player.vimeo.com/video/"); + } else if (domain.includes("tiktok.com")){ + var split = iframeURL.split("/video/"); + if (split.length>1){ + split = split[1].split("/")[0].split("?")[0].split("#")[0]; + iframeURL = "https://www.tiktok.com/embed/v2/" + split; + } + } + } + return iframeURL; +} + +function soloLinkGenerator(streamID, scene=true){ + + var codecGroupFlag=""; + if (session.codecGroupFlag){ + codecGroupFlag = session.codecGroupFlag; + } + if (session.bitrateGroupFlag){ + codecGroupFlag += session.bitrateGroupFlag; + } + + var wss = ""; + if (session.wssSetViaUrl){ + if (session.customWSS && (session.customWSS!==true)){ + wss = "&pie="+session.customWSS; + } else { + wss = "&wss="+session.wss; + } + } + + var passAdd2=""; + if (session.password){ + if (session.defaultPassword===false){ + passAdd2="&password="+session.password; + } + } + + if (session.token){ + passAdd2+="&token="+session.token; + } + + if (scene){ + return "https://"+location.host+location.pathname+"?view="+streamID+"&solo"+codecGroupFlag+"&room="+session.roomid+passAdd2+wss+soloLinkAppended; + } else { + return "https://"+location.host+location.pathname+"?view="+streamID+codecGroupFlag+passAdd2+wss+soloLinkAppended; + } +} + +function YoutubeAPI(iframe, func, args) { // playVideo, pauseVideo, stopVideo + if (!(iframe && iframe.contentWindow)){return;} + try { + iframe.contentWindow.postMessage(JSON.stringify({ + "event": "command", + "func": func, + "args": args || [], + "id": iframe.id || "unknown" + }), "*"); + } catch(e){} +} + +function YoutubeListen(iframe_id){ + var iframe = document.getElementById(iframe_id); + if (!iframe){return;} + if (iframe.loadedYoutubeListen){return;} + try { + iframe.contentWindow.postMessage(JSON.stringify({"event":"listening","id":iframe_id}),'*'); // + } catch(e){ } + setTimeout(function(iframe_id){YoutubeListen(iframe_id);},1000,iframe_id); +} + +function processYoutubeEvent(e){ + if (!(e.type && (e.type === "message"))){return;} + try { + var data = JSON.parse(e.data); + if ("id" in data){ + var iframe = document.getElementById(data.id); + if (!iframe){return;} + if (!iframe.loadedYoutubeListen){ + iframe.loadedYoutubeListen = true; + } + } + if (!("mediaReferenceTime" in data.info)){ + return; + } + } catch(e){return;} + + log(e); + + if (iframe.id=="iframe_source"){ + + if (!session.iframeEle.sendOnNewConnect){ + session.iframeEle.sendOnNewConnect = {}; + session.iframeEle.sendOnNewConnect.ifs = {}; + session.iframeEle.sendOnNewConnect.ifs.t = null; + session.iframeEle.sendOnNewConnect.ifs.v = null; + session.iframeEle.sendOnNewConnect.ifs.s = null; + session.iframeEle.sendOnNewConnect.ifs.r = null; + } + + try { + var msg = {}; + msg.ifs = {} + + try{ + msg.ifs.t = parseFloat(data.info.mediaReferenceTime+0.01) || 0; + session.iframeEle.sendOnNewConnect.ifs.t = msg.ifs.t; + } catch(e){return;} + + if ("playerState" in data.info){ + msg.ifs.s = parseInt(data.info.playerState); + + if (msg.ifs.s == -1){ + msg.ifs.s = 0; + } + if (msg.ifs.s == 2){ + if (session.iframeEle.sendOnNewConnect.ifs.s==3){ + delete(msg.ifs.s); + } else { + msg.ifs.s = 3; + } + } + if (msg.ifs.s && (session.iframeEle.sendOnNewConnect.ifs.s != msg.ifs.s)){ + session.iframeEle.sendOnNewConnect.ifs.s = msg.ifs.s; + } else { + //delete(msg.ifs.s); + } + + if ("videoData" in data.info){ + if (session.iframeEle.sendOnNewConnect.ifs.v != data.info.videoData.video_id){ + session.iframeEle.sendOnNewConnect.ifs.v = data.info.videoData.video_id; + msg.ifs.v = data.info.videoData.video_id; + + var vidSrc = createYoutubeLink(msg.ifs.v); + if (vidSrc!==session.iframeSrc){ + session.iframeSrc = vidSrc; + var data = {} + data.iframeSrc = session.iframeSrc; + if (parseInt(msg.ifs.t)>1){ + data.iframeSrc += "&start="+parseInt(Math.ceil(msg.ifs.t))+""; + } + + for (var UUID in session.pcs){ + if (session.pcs[UUID].allowIframe===true){ + session.sendMessage(data, UUID); + } + } + return; + } + } + } + // we will still be sending the msg data if available. + } else if ("videoData" in data.info){ + if (session.iframeEle.sendOnNewConnect.ifs.v != data.info.videoData.video_id){ + msg.ifs.v = data.info.videoData.video_id; + session.iframeEle.sendOnNewConnect.ifs.v = msg.ifs.v; + var vidSrc = createYoutubeLink(msg.ifs.v); + if (vidSrc!==session.iframeSrc){ + session.iframeSrc = vidSrc; + var data = {} + data.iframeSrc = session.iframeSrc; + if (parseInt(msg.ifs.t)>1){ + data.iframeSrc += "&start="+parseInt(Math.ceil(msg.ifs.t))+""; + } + if (session.iframeEle.sendOnNewConnect.ifs.s == "1"){ + data.iframeSrc += "&autoplay=1"; + } else { + data.iframeSrc += "&autoplay=0"; + } + for (var UUID in session.pcs){ + if (session.pcs[UUID].allowIframe===true){ + session.sendMessage(data, UUID); + } + } + return; + } + } + } else { + if ("playbackRate" in data.info){ + msg.ifs.r = parseFloat(data.info.playbackRate); + if (session.iframeEle.sendOnNewConnect.ifs.r != msg.ifs.r){ + session.iframeEle.sendOnNewConnect.ifs.r = msg.ifs.r; + } else { + delete(msg.ifs.r); + } + } + if (session.iframeEle.sendOnNewConnect.ifs.s == 1){ + if ("t" in msg.ifs){ + delete(msg.ifs.t); + } + } + } + + if (Object.keys(msg.ifs).length == 0){return;} + + for (var UUID in session.pcs){ + if (session.pcs[UUID].allowIframe){ + session.sendMessage(msg); + } + } + } catch(e){return;} + } else { + try{ + var UUID = iframe.dataset.UUID; + var msg = {}; + msg.ifs = {} + if ("t" in msg.ifs){ + msg.ifs.t = parseFloat(data.info.mediaReferenceTime+0.01) || 0; + /* if (!iframe.sendOnNewConnect){ + iframe.sendOnNewConnect = msg; + } else { + iframe.sendOnNewConnect.ifs.t = msg.ifs.t; + } */ + } + + if ("playerState" in data.info){ + msg.ifs.s = parseInt(data.info.playerState); + } + if ("videoData" in data.info){ + msg.ifs.v = data.info.videoData.video_id; + } + if (("playbackRate" in data.info) && (data.info.playbackRate!==1)){ + msg.ifs.r = parseFloat(data.info.playbackRate); + } + // TODO: the viewers don't have a way to tell the director if they reload what the time is at. + session.sendRequest(msg, UUID); // send to the iframe's owner only. let them be the controller for others. + } catch(e){return;} + } +} + +function processIframeSyncFeedback(ifs, UUID){ // remote iframe feedback from the remote viewers + // YoutubeAPI("iframe_source", "seekTo", [700]); + // YoutubeAPI("iframe_source", "volume", [100]); + + warnlog(ifs); + return; + + + if (!session.iframeEle.sendOnNewConnect){ + session.iframeEle.sendOnNewConnect = {}; + session.iframeEle.sendOnNewConnect.ifs = {}; + session.iframeEle.sendOnNewConnect.ifs.t = null; + session.iframeEle.sendOnNewConnect.ifs.v = null; + session.iframeEle.sendOnNewConnect.ifs.s = null; + session.iframeEle.sendOnNewConnect.ifs.r = null; + } + + if ("t" in ifs){ + if (Math.abs(session.iframeEle.sendOnNewConnect.ifs.t-ifs.t)>=1){ + //session.iframeEle.sendOnNewConnect.ifs.t = ifs.t; + } else { + delete(ifs.t); + } + } + if ("v" in ifs){ + if (session.iframeEle.sendOnNewConnect.ifs.v != ifs.v){ + //session.iframeEle.sendOnNewConnect.ifs.v = ifs.v; + } else { + delete(ifs.v); + } + } + if ("s" in ifs){ + + if (ifs.s == -1){ + ifs.s = 0; + } + if (session.iframeEle.sendOnNewConnect.ifs.s == -1){ + session.iframeEle.sendOnNewConnect.ifs.s = 0; + } + + if (ifs.s == 2){ + ifs.s = 3; + } + if (session.iframeEle.sendOnNewConnect.ifs.s == 2){ + session.iframeEle.sendOnNewConnect.ifs.s = 3; + } + + if (session.iframeEle.sendOnNewConnect.ifs.s != ifs.s){ + //session.iframeEle.sendOnNewConnect.ifs.s = ifs.s; + } else { + delete(ifs.s); + } + } + if ("r" in ifs){ + if (session.iframeEle.sendOnNewConnect.ifs.r != ifs.r){ + //session.iframeEle.sendOnNewConnect.ifs.r = ifs.r; + } else { + delete(ifs.r); + } + } + + + if (session.iframeEle){ + if (ifs.v){ // I need to have this change videos . + var vidSrc = createYoutubeLink(ifs.v); + if (vidSrc!==session.iframeSrc){ + session.iframeSrc = vidSrc; + session.iframeEle.src = vidSrc; + } + } else if ("t" in ifs){ + YoutubeAPI(session.iframeEle, "seekTo", [parseFloat(ifs.t)]); + } else if (ifs.r){ /// setPlaybackRate + YoutubeAPI(session.iframeEle, "setPlaybackRate", [parseFloat(ifs.r)]); + } else if ("s" in ifs){ /// setPlaybackState + if (ifs.s == -1) { YoutubeAPI(session.iframeEle, "stopVideo"); } + else if (ifs.s == 0) { YoutubeAPI(session.iframeEle, "stopVideo"); } // player stops. + else if (ifs.s == 1) { YoutubeAPI(session.iframeEle, "playVideo"); } //Video is playing + else if (ifs.s == 2) { YoutubeAPI(session.iframeEle, "pauseVideo"); } //Video is paused + else if (ifs.s == 3) { YoutubeAPI(session.iframeEle, "pauseVideo"); } //video is buffering + else if (ifs.s == 5) { } //Video is cued. + } + } else if (session.iframeSrc){ + if (ifs.v){ + var vidSrc = createYoutubeLink(ifs.v); + if (vidSrc!==session.iframeSrc){ + session.iframeSrc = vidSrc; + var data = {} + data.iframeSrc = session.iframeSrc; + if (ifs.t && (parseInt(ifs.t)>1)){ + data.iframeSrc += "&start="+parseInt(Math.ceil(ifs.t)); + } + if (ifs.s == "1"){ + data.iframeSrc += "&autoplay=1"; + } else { + data.iframeSrc += "&autoplay=0"; + } + for (var uuid in session.pcs){ + if (uuid == UUID){continue;} + if (session.pcs[uuid].allowIframe===true){ + session.sendMessage(data, uuid); + } + } + return; + } + } + // we're going to forward the message directly to the other viewers instead + if ("s" in ifs){ /// setPlaybackState + var msg = {}; + msg.ifs = ifs; + for (var uuid in session.pcs){ + if (uuid == UUID){continue;} + if (session.pcs[uuid].allowIframe){ + session.sendMessage(msg, uuid); + } + } + } + } +} + +function processIframeSyncUpdates(ifs, UUID){ // playback updates from remote guest. + // YoutubeAPI("iframe_source", "seekTo", [700]); + // YoutubeAPI("iframe_source", "volume", [100]); + if (ifs.v && ("s" in ifs)){ + // + } else if ("s" in ifs){ + if ("t" in ifs){ + YoutubeAPI(session.rpcs[UUID].iframeEle, "seekTo", [parseFloat(ifs.t)]); + } + YoutubeAPI(session.rpcs[UUID].iframeEle, "playVideo"); + } else if ("t" in ifs){ + YoutubeAPI(session.rpcs[UUID].iframeEle, "seekTo", [parseFloat(ifs.t)]); + } + if (ifs.r){ /// setPlaybackRate + YoutubeAPI(session.rpcs[UUID].iframeEle, "setPlaybackRate", [parseFloat(ifs.r)]); + } + if ("s" in ifs){ /// setPlaybackState + if (ifs.s == -1) { YoutubeAPI(session.rpcs[UUID].iframeEle, "stopVideo"); } + else if (ifs.s == 0) { YoutubeAPI(session.rpcs[UUID].iframeEle, "stopVideo"); } // player stops. + else if (ifs.s == 1) { YoutubeAPI(session.rpcs[UUID].iframeEle, "playVideo"); } //Video is playing + else if (ifs.s == 2) { YoutubeAPI(session.rpcs[UUID].iframeEle, "pauseVideo"); } //Video is paused + else if (ifs.s == 3) { YoutubeAPI(session.rpcs[UUID].iframeEle, "pauseVideo"); } //video is buffering + else if (ifs.s == 5) { } //Video is cued. + } +} + +function updatePushId(){ + if (session.doNotSeed){return;} + + if (urlParams.has('push')){ + updateURL("push="+session.streamID); + } else if (urlParams.has('id')){ + updateURL("id="+session.streamID); + } else if (urlParams.has('permaid')){ + updateURL("permaid="+session.streamID); + } else { + updateURL("push="+session.streamID); + } +} + +session.publishIFrame = function(iframeURL){ + + if (!session.cleanOutput){ + getById("websitesharebutton2").classList.remove('hidden'); + } + + if (session.transcript){ + setTimeout(function(){setupClosedCaptions();},1000); + } + + session.iframeSrc = parseURL4Iframe(iframeURL); + + var iframe = document.createElement("iframe"); + iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;display-capture;midi;"; + iframe.src = session.iframeSrc; + iframe.id = "iframe_source"; + iframe.loadedYoutubeListen = false; + session.iframeEle = iframe; + + var container = document.createElement("div"); + iframe.container = container; + container.id = "container_iframe"; + container.appendChild(iframe); + getById("gridlayout").appendChild(container); + + if (session.iframeSrc.startsWith("https://www.youtube.com/")){ // special handler. + setTimeout(function(iframe_id){YoutubeListen(iframe_id);}, 1000, iframe.id); + } + + if (session.cover){ + container.style.setProperty('height', '100%', 'important'); + } + + if (session.roomid!==false){ + if ((session.roomid==="") && ((!(session.view)) || (session.view===""))){ + + } else { + log("ROOMID EANBLED"); + getById("head3").classList.add('hidden'); + getById("head3a").classList.add('hidden'); + joinRoom(session.roomid); + } + + } else { + getById("head3").classList.remove('hidden'); + getById("head3a").classList.remove('hidden'); + getById("logoname").style.display = 'none'; + } + getById("head1").className = 'hidden'; + + updatePushId() + + getById("head1").className = 'hidden'; + getById("head2").className = 'hidden'; + + if (!(session.cleanOutput)){ + getById("chatbutton").className="float"; + getById("hangupbutton").className="float"; + getById("controlButtons").classList.remove("hidden"); + getById('sharefilebutton').classList.remove("hidden"); // we won't override "display:none", if set, though. + getById("helpbutton").style.display = "inherit"; + getById("reportbutton").style.display = ""; + } else { + getById("controlButtons").classList.add("hidden"); + } + + if (session.chatbutton === false) { + getById("chatbutton").classList.add("hidden"); + } + + if (session.director){ + // + } else if (session.scene!==false){ + updateMixer(); + } else if (session.roomid!==false){ + if (session.roomid===""){ + if (!(session.view) || (session.view==="")){ + session.windowed = true; + container.classList.add("vidcon"); + + getById("mutespeakerbutton").classList.add("hidden"); + container.style.width="100%"; + container.style.height="100%"; + container.style.alignItems = "center"; + container.style.maxWidth= "100%"; + container.style.maxHeight= "100%"; + container.style.verticalAlign= "middle"; + container.style.margin= "auto"; + container.style.backgroundColor = "#666"; + container.style.border = "2px solid"; + + } else { + session.windowed = false; + window.onresize = updateMixer; + updateMixer(); + } + } else { + window.onresize = updateMixer; + session.windowed = false; + updateMixer(); + } + } else { + window.onresize = updateMixer; + container.style.maxHeight= "1280px"; + container.style.maxWidth= "720px"; + container.style.verticalAlign= "middle"; + container.style.height="100%"; + container.style.width= "100%"; + container.style.margin= "auto"; + container.style.alignItems = "center"; + container.style.backgroundColor = "#666"; + } + + session.seeding=true; + + updateReshareLink(); + pokeIframeAPI('started-iframe-share'); + session.seedStream(); + + return container; +} // publishIframe + + +/* session.publishWhepSrc = function(){ + + if (!session.whepSrc){errorlog("no WHEP Src");return;} + + if (!session.cleanOutput){ + getById("websitesharebutton2").classList.remove('hidden'); + } + + var UUID = whepIn(session.whepSrc); + + var container = document.createElement("div"); + iframe.container = container; + container.id = "container_iframe"; + container.appendChild(iframe); + getById("gridlayout").appendChild(container); + + if (session.iframeSrc.startsWith("https://www.youtube.com/")){ // special handler. + setTimeout(function(iframe_id){YoutubeListen(iframe_id);}, 1000, iframe.id); + } + + if (session.cover){ + container.style.setProperty('height', '100%', 'important'); + } + + if (session.roomid!==false){ + if ((session.roomid==="") && ((!(session.view)) || (session.view===""))){ + + } else { + log("ROOMID EANBLED"); + getById("head3").classList.add('hidden'); + getById("head3a").classList.add('hidden'); + joinRoom(session.roomid); + } + + } else { + getById("head3").classList.remove('hidden'); + getById("head3a").classList.remove('hidden'); + getById("logoname").style.display = 'none'; + } + getById("head1").className = 'hidden'; + + updatePushId() + + getById("head1").className = 'hidden'; + getById("head2").className = 'hidden'; + + if (!(session.cleanOutput)){ + getById("chatbutton").className="float"; + getById("hangupbutton").className="float"; + getById("controlButtons").classList.remove("hidden"); + getById('sharefilebutton').classList.remove("hidden"); // we won't override "display:none", if set, though. + getById("helpbutton").style.display = "inherit"; + getById("reportbutton").style.display = ""; + } else { + getById("controlButtons").classList.add("hidden"); + } + + if (session.chatbutton === false) { + getById("chatbutton").classList.add("hidden"); + } + + if (session.director){ + // + } else if (session.scene!==false){ + updateMixer(); + } else if (session.roomid!==false){ + if (session.roomid===""){ + if (!session.fullscreen && (!(session.view) || (session.view===""))){ + session.windowed = true; + container.classList.add("vidcon"); + + getById("mutespeakerbutton").classList.add("hidden"); + container.style.width="100%"; + container.style.height="100%"; + container.style.alignItems = "center"; + container.style.maxWidth= "100%"; + container.style.maxHeight= "100%"; + container.style.verticalAlign= "middle"; + container.style.margin= "auto"; + container.style.backgroundColor = "#666"; + container.style.border = "2px solid"; + + } else { + session.windowed = false; + window.onresize = updateMixer; + updateMixer(); + } + } else { + window.onresize = updateMixer; + session.windowed = false; + updateMixer(); + } + } else { + window.onresize = updateMixer; + container.style.maxHeight= "1280px"; + container.style.maxWidth= "720px"; + container.style.verticalAlign= "middle"; + container.style.height="100%"; + container.style.width= "100%"; + container.style.margin= "auto"; + container.style.alignItems = "center"; + container.style.backgroundColor = "#666"; + } + + session.seeding=true; + + updateReshareLink(); + pokeIframeAPI('started-iframe-share'); + session.seedStream(); + + return container; +} // publishWhepSrc */ + + +function outboundAudioPipeline(){ // this function isn't letting me change the audio source + + if (session.disableWebAudio) { + // if (iOS || iPad){return session.streamSrc;} // iOS devices can't remap video tracks, else KABOOM. Might as well do this for android also. + + if (session.streamSrcClone){ + log("123a"); + session.streamSrcClone.getTracks().forEach(function(track) { + session.streamSrcClone.removeTrack(track); + track.stop(); + }); + } + + if (session.streamSrc && session.streamSrc.clone){ + log("123b"); + var streamSrc = session.streamSrc.clone(); + session.streamSrcClone = streamSrc; + return streamSrc; + } else { + log("123c"); + var newStream = createMediaStream(); + session.streamSrcClone = newStream; + + if (session.streamSrc){ + session.streamSrc.getAudioTracks().forEach(function(track) { // this seems to fix a bug with macbooks. + newStream.addTrack(track, session.streamSrc); + }); + } + if (session.videoElement && session.videoElement.srcObject){ + session.videoElement.srcObject.getVideoTracks().forEach(function(track) { // this seems to fix a bug with macbooks. + newStream.addTrack(track, session.videoElement.srcObject); + }); + } else if (session.streamSrc){ + session.streamSrc.getVideoTracks().forEach(function(track) { // this seems to fix a bug with macbooks. + newStream.addTrack(track, session.streamSrc); + }); + } + + } + return newStream; + } + + if (!session.streamSrc){ + errorlog("STREAM DOES NOT EXIST. This is a problem"); + checkBasicStreamsExist(); + return session.streamSrc; + } + + var streamSrc = session.streamSrc; + + if (iOS || iPad){ + + if (session.streamSrcClone){ + var tracks = session.streamSrcClone.getAudioTracks(); + if (tracks.length) { + for (var waid in session.webAudios) { // TODO: EXCLUDE CURRENT TRACK IF ALREADY EXISTS ... if (track.id === wa.id){.. + session.webAudios[waid].stop(); + delete session.webAudios[waid]; + } + } + session.streamSrcClone.getTracks().forEach(function(track) { + session.streamSrcClone.removeTrack(track); + track.stop(); + }); + } + + if (session.streamSrc && session.streamSrc.clone){ // modern + streamSrc = session.streamSrc.clone(); + session.streamSrcClone = streamSrc; + } else { // backup. + streamSrc = createMediaStream(); + + if (session.streamSrc){ + session.streamSrc.getAudioTracks().forEach(function(track) { // this seems to fix a bug with macbooks. + streamSrc.addTrack(track, session.streamSrc); + }); + } + if (session.videoElement && session.videoElement.srcObject){ + session.videoElement.srcObject.getVideoTracks().forEach(function(track) { // this seems to fix a bug with macbooks. + streamSrc.addTrack(track, session.videoElement.srcObject); + }); + } else if (session.streamSrc){ + session.streamSrc.getVideoTracks().forEach(function(track) { // this seems to fix a bug with macbooks. + streamSrc.addTrack(track, session.streamSrc); + }); + } + session.streamSrcClone = streamSrc; + } + } + + for (var waid in session.webAudios) { // TODO: EXCLUDE CURRENT TRACK IF ALREADY EXISTS ... if (track.id === wa.id){.. + session.webAudios[waid].stop(); + delete session.webAudios[waid]; + } + + try { + log("Web Audio"); + var tracks = streamSrc.getAudioTracks(); + if (tracks.length) { + var webAudio = {}; + webAudio.micDelay = false; + webAudio.compressor = false; + webAudio.analyser = false; + webAudio.gainNode = false; + webAudio.splitter = false; + webAudio.subGainNodes = false; + + webAudio.lowEQ = false; + webAudio.midEQ = false; + webAudio.highEQ = false; + webAudio.lowcut1 = false; + webAudio.lowcut2 = false; + webAudio.lowcut3 = false; + + webAudio.id = tracks[0].id; // first track is used. + + if (session.audioCtxOutbound){ + // already Created + } else if (session.audioLatency !== false) { // session.audioLatency could be useful for fixing clicking issues? + session.audioCtxOutbound = new AudioContext({ + latencyHint: session.audioLatency / 1000.0 //, // needs to be in seconds, but VDON user input is via milliseconds + // sampleRate: 48000 // not sure this is a great idea, but might as well add this here, versus later on since it is needed anyways. + }); + } else { + session.audioCtxOutbound = new AudioContext(); + } + + var audioContext = session.audioCtxOutbound; + webAudio.audioContext = session.audioCtxOutbound; + + webAudio.destination = audioContext.createMediaStreamDestination(); + + if (tracks.length>1){ // tries to + try { + webAudio.mediaStreamSource = createMediaStream(); + var maxChannelCount = 2; + if (session.stereo===false){ + maxChannelCount = 1; + } + + webAudio.subGainNodes = {};// + + var merger = audioContext.createChannelMerger(maxChannelCount); + for (var i=0;i1){ + ng2 = parseInt(session.noisegateSettings[1]) || ng2; // not loud (threshold level) + } + if (session.noisegateSettings.length>2){ + ng3 = parseInt(session.noisegateSettings[2]) || 0; // stickiness; time (ms) + ng3 = ng3 / 100.0; // convert to the actual units (100ms) + } + } + if (session.noisegate){ + changeGatingGain(ng1,200); + } + + function draw() { + try { + analyser.getByteFrequencyData(dataArray); + var total = 0; + for (var i = 0; i < dataArray.length; i++) { + total += dataArray[i]; + } + total = total / 100; + if (session.quietOthers && (session.quietOthers==2)){ + if (total>10){ + if (session.muted_activeSpeaker==false){ + session.muted_activeSpeaker=true; + session.speakerMuted=true; + clearTimeout(timer); + toggleSpeakerMute(true); // okay, sicne this is quietOthers + } + } else if (session.muted_activeSpeaker==true){ + session.speakerMuted=false; + session.muted_activeSpeaker=false; + session.activelySpeaking=false; + clearTimeout(timer); + timer = setTimeout(function(){toggleSpeakerMute(true);},250); // okay, sicne this is quietOthers + } + } + + if (session.pushLoudness==true){ + var loudnessObj = {}; + loudnessObj[session.streamID] = parseInt(total); + if (isIFrame){ + parent.postMessage({"loudness": loudnessObj, "action":"loudness", "value":total}, session.iframetarget); + } + } + + if (session.noisegate){ + if (total<=ng2){ + if (currentlyActive==ng3){ + changeGatingGain(ng1,200); // set volume to 40% relative to what it is now. + log("GAIN LOWERED"); + currentlyActive=(ng3+1); + } else if (currentlyActive 150) { + if (total > 200) { + total = 200; + } + meter1.style.width = "150px"; + meter2.style.width = (total - 150) + "px"; + } + return; + } else if (toggleSettingsState && document.getElementById("meter3")){ + if (total == 0) { + meter3.style.width = "1px"; + meter4.style.width = "0px"; + } else if (total <= 1) { + meter3.style.width = "1px"; + meter4.style.width = "0px"; + } else if (total <= 150) { + meter3.style.width = total + "px"; + meter4.style.width = "0px"; + } else if (total > 150) { + if (total > 200) { + meter3.style.width = "150px"; + meter4.style.width = "50px"; + } else { + meter3.style.width = "150px"; + meter4.style.width = (total - 150) + "px"; + } + } + if (document.getElementById("mutetoggle")) { + total *= 3; + if (total > 255) { + total = 255; + } + total = parseInt(total); + document.getElementById("mutetoggle").style.color = "rgb(" + (255 - total) + ",255," + (255 - total) + ")"; + } + meter1 = false; + return; + } else if (session.cleanOutput){ + meter1 = false; + return; + } else if (document.getElementById("mutetoggle")) { + total *= 3; + if (total > 255) { + total = 255; + } + total = parseInt(total); + document.getElementById("mutetoggle").style.color = "rgb(" + (255 - total) + ",255," + (255 - total) + ")"; + } else { + clearInterval(analyser.interval); + warnlog("METERS NOT FOUND"); + } + meter1 = false; + } catch(e){ + errorlog(e); + } + }; + + analyser.interval = setInterval(function() { + draw(); + }, 100); + return analyser; +} + + + +function audioCompressor(mediaStreamSource, audioContext) { + var compressor = audioContext.createDynamicsCompressor(); + compressor.threshold.value = -40; + compressor.knee.value = 10; + compressor.ratio.value = 4; // 3 + compressor.attack.value = 0.002; // 0.001 + compressor.release.value = 0.1; // 0.06 + mediaStreamSource.connect(compressor); + return compressor; +} + +function audioLimiter(mediaStreamSource, audioContext) { + var compressor = audioContext.createDynamicsCompressor(); + compressor.threshold.value = -5; + compressor.knee.value = 0; + compressor.ratio.value = 20.0; // 1 to 20 + compressor.attack.value = 0.001; + compressor.release.value = 0.1; + mediaStreamSource.connect(compressor); + return compressor; +} + + +function activeSpeaker(border=false) { + var lastActiveSpeaker = null; + + var someoneElseIfSpeaking = false; + var anyoneIsSpeaking = false; + var defaultSpeaker = false; + + for (var UUID in session.rpcs) { + + if ((session.activeSpeaker>2) && !(session.rpcs[UUID].videoElement && session.rpcs[UUID].videoElement.srcObject && session.rpcs[UUID].videoElement.srcObject.getVideoTracks().length && !session.rpcs[UUID].videoMuted)){ + session.rpcs[UUID].activelySpeaking = false; // we're not showing audio-only sources in this mode. + session.rpcs[UUID].defaultSpeaker = false; + continue; + } + + if (session.rpcs[UUID].stats._Audio_Loudness_average) { + if (session.rpcs[UUID].stats.Audio_Loudness && (session.rpcs[UUID].stats.Audio_Loudness>10)){ + session.rpcs[UUID].stats._Audio_Loudness_average = parseFloat(session.rpcs[UUID].stats.Audio_Loudness*0.07 + session.rpcs[UUID].stats._Audio_Loudness_average*0.93); + } else { + session.rpcs[UUID].stats._Audio_Loudness_average = parseFloat(session.rpcs[UUID].stats._Audio_Loudness_average*0.975); + } + } else { + session.rpcs[UUID].stats._Audio_Loudness_average = 1; + } + if (session.rpcs[UUID].stats._Audio_Loudness_average > 13) { + + if (border) { + if (session.rpcs[UUID].videoElement) { + session.rpcs[UUID].videoElement.style.border = "green solid 1px"; + session.rpcs[UUID].videoElement.style.padding = "0"; + } + } else if (!session.rpcs[UUID].activelySpeaking){ + + session.rpcs[UUID].activelySpeaking = true; + lastActiveSpeaker = UUID; + session.rpcs[UUID].stats._Audio_Loudness_average+=50; + } + + } else if (session.rpcs[UUID].stats._Audio_Loudness_average > 6) { + // + } else { + if (border){ + if (session.rpcs[UUID].videoElement) { + session.rpcs[UUID].videoElement.style.border = ""; + session.rpcs[UUID].videoElement.style.padding = "1px"; + } + } else if (session.rpcs[UUID].activelySpeaking) { + session.rpcs[UUID].activelySpeaking=false; + lastActiveSpeaker = UUID; + } + } + if ((session.rpcs[UUID].stats.Audio_Loudness > 13) || ((session.rpcs[UUID].stats.Audio_Loudness > 5) && (session.rpcs[UUID].stats._Audio_Loudness_average>3)) || (session.rpcs[UUID].stats._Audio_Loudness_average>6)){ + someoneElseIfSpeaking = true; + } + if (session.rpcs[UUID].activelySpeaking){ + anyoneIsSpeaking=true; + } + if (session.rpcs[UUID].defaultSpeaker){ + defaultSpeaker=true; + } + } + + var loudest=null; + var loudestActive=null; + var changed = false; + if ((session.activeSpeaker===1) || (session.activeSpeaker===3)){ // will only show one speaker at a time; the loudest or last-loud speaker + if (!anyoneIsSpeaking){ + if (defaultSpeaker){ + // already good to go. + } else if (lastActiveSpeaker){ + session.rpcs[lastActiveSpeaker].defaultSpeaker=true; + changed=true; + } else if (session.scene===false || (session.nopreview===false && session.minipreview!==1)){ + // we don't need to care. + } else { + for (var UUID in session.rpcs) { + if (session.rpcs[UUID].videoElement && session.rpcs[UUID].videoElement.srcObject && session.rpcs[UUID].videoElement.srcObject.getVideoTracks().length && !session.rpcs[UUID].videoMuted){ + session.rpcs[UUID].defaultSpeaker=true; + changed=true; + break + } + } + if (!changed && (session.activeSpeaker<=2)){ // switch to streams that have no video track + for (var UUID in session.rpcs) { + if (session.rpcs[UUID].label){ + session.rpcs[UUID].defaultSpeaker=true; + changed=true; + break + } else if (!changed){ + session.rpcs[UUID].defaultSpeaker=true; + changed=true; + } + } + } + } + } else { + for (var UUID in session.rpcs) { + if (!("_Audio_Loudness_average" in session.rpcs[UUID].stats)){ // never could have been loudest, since no loudness value. + continue; + } + /* if (!loudest){ + loudest = UUID; + } else if (session.rpcs[UUID].stats._Audio_Loudness_average > session.rpcs[loudest].stats._Audio_Loudness_average){ + loudest = UUID; + } */ + + if (session.rpcs[UUID].activelySpeaking){ + if (!loudestActive){ + loudestActive = UUID; + } else if (session.rpcs[UUID].stats._Audio_Loudness_average > session.rpcs[loudestActive].stats._Audio_Loudness_average){ + if (session.rpcs[loudestActive].defaultSpeaker){ + session.rpcs[loudestActive].defaultSpeaker=false; + changed=true + } + loudestActive = UUID; + } else if (session.rpcs[UUID].defaultSpeaker){ + session.rpcs[UUID].defaultSpeaker=false; + changed=true; + } + } else if (session.rpcs[UUID].defaultSpeaker){ + session.rpcs[UUID].defaultSpeaker=false; + changed=true + } + } + if (loudestActive && !session.rpcs[loudestActive].defaultSpeaker){ + session.rpcs[loudestActive].defaultSpeaker = true; + changed = true; + } + } + } else if ((session.activeSpeaker===2) || (session.activeSpeaker===4)){ // will show whoever is talking; mixed together; if no one is talking, just shows yourself + + if (!anyoneIsSpeaking){ + if (defaultSpeaker){ + // already good to go. + } else if (lastActiveSpeaker){ + session.rpcs[lastActiveSpeaker].defaultSpeaker=true; + changed=true; + } else if (session.scene===false || (session.nopreview===false && session.minipreview!==1)){ + // we don't need to care. + } else { + for (var UUID in session.rpcs) { + if (session.rpcs[UUID].videoElement && session.rpcs[UUID].videoElement.srcObject && session.rpcs[UUID].videoElement.srcObject.getVideoTracks().length && !session.rpcs[UUID].videoMuted){ + session.rpcs[UUID].defaultSpeaker=true; + changed=true; + break + } + } + if (!changed && (session.activeSpeaker<=2)){ + for (var UUID in session.rpcs) { + if (session.rpcs[UUID].label){ + session.rpcs[UUID].defaultSpeaker=true; + changed=true; + break + } else if (!changed){ + session.rpcs[UUID].defaultSpeaker=true; + changed=true; + } + } + } + } + } else { + for (var UUID in session.rpcs) { + if (session.rpcs[UUID].activelySpeaking && !session.rpcs[UUID].defaultSpeaker){ + session.rpcs[UUID].defaultSpeaker = true; + changed = true; + } else if (!session.rpcs[UUID].activelySpeaking && session.rpcs[UUID].defaultSpeaker){ + session.rpcs[UUID].defaultSpeaker = false; + changed=true + } + } + } + } + if (session.quietOthers && (session.quietOthers===1)){ + if (someoneElseIfSpeaking){ + if (session.muted_activeSpeaker==false){ + session.muted_activeSpeaker=true; + session.muted=true; + toggleMute(true); + } + } else if (session.muted_activeSpeaker==true){ + session.muted=false; + session.muted_activeSpeaker=false; + toggleMute(true); + } + } else if (session.quietOthers && (session.quietOthers===3)){ // purely for fun. It's the opposite of a noise-gate I guess. + if (someoneElseIfSpeaking){ + if (session.muted_activeSpeaker==false){ + session.muted_activeSpeaker=true; + session.speakerMuted=true; + toggleSpeakerMute(true); // okay, sicne this is quietOthers + } + } else if (session.muted_activeSpeaker==true){ + session.speakerMuted=false; + session.muted_activeSpeaker=false; + toggleSpeakerMute(true); // okay, sicne this is quietOthers + } + } + + if (changed) { + setTimeout(function(){updateMixer();},0); + } +} + + + +function randomizeArray(unshuffled) { + + var arr = unshuffled.map((a) => ({ + sort: Math.random(), value: a + })).sort((a, b) => a.sort - b.sort).map((a) => a.value); // shuffle once + + for (var i = arr.length - 1; i > 0; i--) { // shuffle twice + var j = Math.floor(Math.random() * (i + 1)); + var tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + return arr +} + +function joinRoom(roomname) { + if (roomname.length) { + roomname = sanitizeRoomName(roomname); + log("Join room: " + roomname); + updateVolume(false); // chance of a race condition, but unlikely and not a big deal if so. + session.joinRoom(roomname).then(function(response) { // callback from server; we've joined the room. Just the listing is returned + + if (session.joiningRoom === "seedPlz") { // allow us to seed, now that we have joined the room. + session.joiningRoom = false; // joined + session.seedStream(); + } else { + session.joiningRoom = false; // no seeding callback + } + var token = ""; + if (session.token){ + token+="&token="+session.token; + } + + if (!session.cleanOutput){ + if (session.roomhost){ + if (session.defaultPassword===false){ + if (session.password === false){ + var invite = "https://"+location.host+location.pathname+"?room="+session.roomid+"&password=false"+token; + warnUser("You can invite others with:\n\n"+invite+"", false, false); + } else { + generateHash(session.password + session.salt, 4).then(function(hash) { + var invite = "https://"+location.host+location.pathname+"?room="+session.roomid+"&hash="+hash+token; + warnUser("You can invite others with:\n\n"+invite+"", false, false); + }); + } + } else { + var invite = "https://"+location.host+location.pathname+"?room="+session.roomid+token; + warnUser("You can invite others with:\n\n"+invite+"", false, false); + } + + } + } + + log("Members in Room"); + log(response); + + if (session.randomize === true) { + response = randomizeArray(response); + log("Randomized List of Viewers"); + log(response); + for (var i in response) { + if ("UUID" in response[i]) { + if ("streamID" in response[i]) { + if (response[i].UUID in session.rpcs) { + log("RTC already connected"); /// lets just say instead of Stream, we have + } else { + log(response[i].streamID); + var streamID = session.desaltStreamID(response[i].streamID); + if (session.queue){ + if (session.directorList.indexOf(response[i].UUID)>=0){ + warnlog("PLAYING DIRECTOR"); + play(streamID, response[i].UUID); + } else if (session.view_set && session.view_set.includes(streamID)){ + play(streamID, response[i].UUID); + } else if (session.queueList.length<5000){ + if ((!(streamID in session.watchTimeoutList)) && (!session.queueList.includes(streamID))){ + session.queueList.push(streamID); + } + } + } else { + log("STREAM ID DESALTED 3: " + streamID); + setTimeout(function(sid) { + play(sid); + }, (Math.floor(Math.random() * 100)), streamID); // add some furtherchance with up to 100ms added latency + } + } + } + } + } + } else { + for (var i in response) { + if ("UUID" in response[i]) { + if ("streamID" in response[i]) { + if (response[i].UUID in session.rpcs) { + log("RTC already connected"); /// lets just say instead of Stream, we have + } else { + log(response[i].streamID); + var streamID = session.desaltStreamID(response[i].streamID); + if (session.queue){ + if (session.directorList.indexOf(response[i].UUID)>=0){ + play(streamID, response[i].UUID); + } else if (session.view_set && session.view_set.includes(streamID)){ + play(streamID, response[i].UUID); + } else if (session.queueList.length<5000){ + if ((!(streamID in session.watchTimeoutList)) && (!session.queueList.includes(streamID))){ + session.queueList.push(streamID); + } + } + } else { + log("STREAM ID DESALTED 3: " + streamID); + play(streamID, response[i].UUID); // play handles the group room mechanics here + } + } + } + } + } + } + updateQueue(); + pokeIframeAPI("joined-room-complete"); + + if (session.include.length){ // we want to request what hasn't been requested already, since we are joining a room. + session.include.forEach(sid =>{ + if (sid in session.waitingWatchList){ + return; + } else { + session.watchStream(sid); + } + }); + } + + + }, function(error) { + return {}; + }); + } else { + log("Room name not long enough or contained all bad characaters"); + } +} + +async function createRoom(roomname = false) { + + if (roomname == false) { + roomname = getById("videoname1").value; + roomname = sanitizeRoomName(roomname); + + clearDirectorSettings(); + + if (roomname.length != 0) { + if (urlParams.has('dir')){ + updateURL("dir=" + roomname, true, false); // make the link reloadable. + } else { + updateURL("director=" + roomname, true, false); // make the link reloadable. + } + } + } + if (roomname.length == 0) { + //if (!(session.cleanOutput)) { + // warnUser("Please enter a room name before continuing"); + //} + + getById("videoname1").focus(); + getById("videoname1").classList.remove("shake"); + setTimeout(function(){getById("videoname1").classList.add("shake");},10); + + return; + } + log(roomname); + session.roomid = roomname; + + getById("dirroomid").innerHTML = decodeURIComponent(session.roomid); + getById("roomid").innerHTML = session.roomid; + + var passwordRoom = getById("passwordRoom").value; + passwordRoom = sanitizePassword(passwordRoom); + + var passAdd = ""; + var passAdd2 = ""; + + if (passwordRoom.length) { + + session.password = passwordRoom; + session.defaultPassword = false; + + if ((session.password === "false") || (session.password === "0") || (session.password === "off")){ + session.password = false; + if (urlParams.has('pass')) { + updateURL("pass=0"); + passAdd = "&pass=0"; + passAdd2 = "&pass=0"; + } else if (urlParams.has('pw')) { + updateURL("pw=0"); + passAdd = "&pw=0"; + passAdd2 = "&pw=0"; + } else if (urlParams.has('p')) { + updateURL("p=0"); + passAdd = "&p=0"; + passAdd2 = "&p=0"; + } else if (urlParams.has('password')) { + updateURL("password=false"); + passAdd = "&password=false"; + passAdd2 = "&password=false"; + } else { + updateURL("p=0"); + passAdd = "&p=0"; + passAdd2 = "&p=0"; + } + } else { + if (urlParams.has('pass')) { + updateURL("pass=" + session.password); + } else if (urlParams.has('pw')) { + updateURL("pw=" + session.password); + } else if (urlParams.has('p')) { + updateURL("p=" + session.password); + } else { + updateURL("password=" + session.password); + } + } + } + + await registerToken(); + + if ((session.defaultPassword === false) && (session.password)) { + passAdd2 = "&password=" + session.password; + return generateHash(session.password + session.salt, 4).then(async function(hash) { + passAdd = "&hash=" + hash; + await createRoomCallback(passAdd, passAdd2); + }).catch(errorlog); + } else if ((session.defaultPassword === false) && (session.password===false)){ + passAdd = "&p=0"; + passAdd2 = "&p=0"; + await createRoomCallback(passAdd, passAdd2); + } else { + await createRoomCallback(passAdd, passAdd2); + } + + + if (session.meshcast){ + if (!session.cleanOutput && !session.cleanDirector){ + document.getElementById("meshcastMenu").classList.remove("hidden"); + } + } + + pokeIframeAPI("create-room", roomname); +} + +function copyVideoFrameToClipboard(videoElement, e=false) { + try{ + var canvas = document.createElement("canvas"); + + canvas.width = videoElement.videoWidth; + canvas.height = videoElement.videoHeight; + + var ctx = canvas.getContext("2d"); + ctx.drawImage(videoElement, 0, 0); + + var img = new Image(); + img.src = canvas.toDataURL(); + + canvas.toBlob(function(blob) { + navigator.clipboard.write([new ClipboardItem({'image/png': blob})]); + }, 'image/png'); + + popupMessage(e, "Frame copied to clipboard as as PNG Image"); + } catch(e){ + errorlog(e); + } +} + +function saveVideoFrameToClipboard(videoElement, e=false) { + try{ + var canvas = document.createElement("canvas"); + + canvas.width = videoElement.videoWidth; + canvas.height = videoElement.videoHeight; + + var ctx = canvas.getContext("2d"); + ctx.drawImage(videoElement, 0, 0); + + var img = new Image(); + img.src = canvas.toDataURL(); + + canvas.toBlob(function(blob) { + var link = document.createElement("a"); + link.download = (videoElement.id||"video")+"_"+parseInt(performance.now())+".png"; + link.href = URL.createObjectURL(blob); + link.click(); + URL.revokeObjectURL(link.href); + }, 'image/png'); + + popupMessage(e, "Saving current frame to disk"); + } catch(e){ + errorlog(e); + } +} + + +async function checkDirectorStreamID(){ + if (session.directorStreamID){ + for (var UUID in session.rpcs){ + if (session.rpcs[UUID].streamID){ + var hashedSID = await generateHash(session.rpcs[UUID].streamID); + if (hashedSID===session.directorStreamID){ + session.directorUUID = UUID; // main director + session.directorList = []; + session.directorList.push(UUID); // approved co/directors + session.directorUUID = UUID; + session.newMainDirectorSetup(); + return; + } + } + } + for (var UUID in session.pcs){ + if (session.pcs[UUID].streamID){ + var hashedSID = await generateHash(session.pcs[UUID].streamID); + if (hashedSID===session.directorStreamID){ + session.directorList = []; + session.directorList.push(UUID); + session.directorUUID = UUID; + session.newMainDirectorSetup(); + return; + } + } + } + if (session.streamID == session.directorStreamID){ + session.directorState = true; + session.directorUUID = false; + pokeAPI("director", true); + pokeIframeAPI("director", true); + warnlog("You are joining with a token, but are the director?"); + } + session.directorList = []; + } +} + +async function checkToken(){ // this lets us use a server+password validation method for the director. + if (!session.token){return;} + if (!session.roomid){return;} + if (session.mainDirectorPassword){return;} + + try { + var request = new XMLHttpRequest(); + + var hashedRoom = session.roomid; + if (session.password){ + hashedRoom += session.password; + } + hashedRoom += "i^4&u#Fz5Eu#MsK^chF5*XAEYi1g"; + hashedRoom = await generateHash(hashedRoom); + hashedRoom = hashedRoom.slice(0, 50); + + request.open('GET', "https://tokens.vdo.ninja/?token="+session.token+"&room="+hashedRoom, false); + request.send(null); + + if (request.status === 200) { + try { + var result = JSON.parse(request.responseText); + if ("UUID" in result){ + session.directorUUID = result.UUID; + session.directorList = []; + session.directorList.push(session.directorUUID); + session.directorStreamID = false; + session.newMainDirectorSetup(); + } else if ("streamID" in result){ + session.directorStreamID = result.streamID; + checkDirectorStreamID(); + } + } catch(e){ + session.directorUUID = false; + session.directorStreamID = false; + session.directorList = []; + errorlog(e); + } + } else { + session.directorUUID = false; + session.directorStreamID = false; + session.directorList = []; + errorlog("Didn't get a token response"); + } + } catch(e){ + errorlog(e); + } +} + +async function registerToken(){ // this lets us use a server+password validation method for the director. + if (!session.roomid){return;} + if (!session.streamID){return;} + if (!session.mainDirectorPassword){return;} + + var longToken = session.mainDirectorPassword + "3wJVW^5qYU4DxGi6VhxN6RF04Q%$"; // this lets us use the same token across multiple rooms + var hashedToken = await generateHash(longToken); // keep it anonymous + hashedToken = hashedToken.slice(0, 50); + + var hashedRoom = session.roomid; + if (session.password){ + hashedRoom += session.password; + } + hashedRoom += "i^4&u#Fz5Eu#MsK^chF5*XAEYi1g"; + hashedRoom = await generateHash(hashedRoom); + hashedRoom = hashedRoom.slice(0, 50); + + var data2send = {}; + var hashedSID = await generateHash(session.streamID); + data2send.streamID = hashedSID; // not sure if there's a way around this. + data2send = JSON.stringify(data2send); + + var request = new XMLHttpRequest(); + request.open('POST', "https://tokens.vdo.ninja/?token="+hashedToken+"&room="+hashedRoom, false); + console.log("https://tokens.vdo.ninja/?token="+hashedToken+"&room="+hashedRoom); + request.send(data2send); + + if (request.status === 200) { + try { + if (request.responseText && (request.responseText.length===16)){ + session.token = request.responseText; + console.log("share token: "+session.token); + session.directorState = true; + pokeAPI("director", true); + pokeIframeAPI("director", true); + } + } catch(e){ + session.directorState = false; + pokeAPI("director", false); + pokeIframeAPI("director", false); + } + } else { + session.directorState = false; + pokeAPI("director", false); + pokeIframeAPI("director", false); + } +} + +function hideDirectorinvites(ele, skip=true) { + + if (getById("directorLinks2").style.display == "none") { + ele.innerHTML = ' LINKS (GUEST INVITES & SCENES)'; + getById("directorLinks2").style.display = "inline-block"; + getById("customizeLinks").classList.remove("hidden"); + } else { + ele.innerHTML = ' LINKS (GUEST INVITES & SCENES)' + getById("directorLinks2").style.display = "none"; + getById("help_directors_room").style.display = "none"; + getById("roomnotes2").style.display = "none"; + getById("customizeLinks").classList.add("hidden"); + } + if (getById("directorLinks1").style.display == "none") { + getById("directorLinks1").style.display = "inline-block"; + getById("customizeLinks").classList.remove("hidden"); + } else { + getById("directorLinks1").style.display = "none"; + getById("help_directors_room").style.display = "none"; + getById("roomnotes2").style.display = "none"; + getById("customizeLinks").classList.add("hidden"); + + } + if (skip){ + saveDirectorSettings(); + } +} + +function toggleCoDirector_changeurl(ele){ + session.codirector_changeURL = ele.checked; // doesn't do anything yet though. +} + +function toggleCoDirector_transfer(ele){ + session.codirector_transfer = ele.checked; +} + + + +async function toggleCoDirector(ele){ + //session.coDirectorAllowed = ele.checked; + if (!ele.checked){ + getById("codirectorSettings").style.display = "none"; + return; + } + if (!session.directorPassword){ + session.directorPassword = await promptAlt(getTranslation("enter-new-codirector-password"), false); + if (!session.directorPassword){ + session.directorPassword=false; + ele.checked=false; + return; + } + session.directorPassword = sanitizePassword(session.directorPassword) + } + updateURL("codirector="+session.directorPassword, true, false); + getById("coDirectorEnableSpan").style.display = "none"; + + await generateHash(session.directorPassword + session.salt + "abc123", 12).then(function(hash) { // million to one error. + log("dir room hash is " + hash); + session.directorHash = hash; + return; + }).catch(errorlog); + + if (session.codirector_transfer){ + getById("codirectorSettings_transfer").checked = true; + } else { + getById("codirectorSettings_transfer").checked = false; + } + if (session.codirector_changeURL){ + getById("codirectorSettings_changeurl").checked = true; + } else { + getById(codirectorSettings_changeurl).checked = false; + } + + var token = ""; + if (session.token){ + token+="&token="+session.token; + } + + getById("codirectorSettings_invite").value = "https://"+location.host+location.pathname+"?dir="+session.roomid+"&codirector="+session.directorPassword+token; + if (session.password!==session.sitePassword){ + if (session.password===false){ + getById("codirectorSettings_invite").value += "&password=false"; + } else{ + getById("codirectorSettings_invite").value += "&password="+session.password; + } + } + + getById("codirectorSettings").style.display = "block"; +} + + +async function toggleWidgetURL(ele){ + if (ele.id === "widgetURL"){ + ele = getById("widgetURCheck"); + } else if (!ele.checked){ + getById("widgetURL").classList.add("hidden"); + session.widget = false; + + var data = {}; + data.widgetSrc = false; + for (var UUID in session.pcs){ + if (session.pcs[UUID].allowWidget===true){ + session.sendMessage(data, UUID); + } + } + + if (session.director){ + let widget = document.getElementById("widget"); + if (widget){ + getById("widget").remove(); + getById("guestFeeds").style.width = "100%"; + } + } + pokeIframeAPI("widget-src", session.widget); + return; + } + var widget = await promptAlt(getTranslation("enter-url-for-widget"), false, false, session.widget); + if (widget!==null){ + session.widget = widget; + } + if (session.widget){ + getById("widgetURL").value = session.widget; + getById("widgetURL").classList.remove("hidden"); + updateMixer(); + } else { + session.widget = false; + getById("widgetURL").classList.add("hidden"); + ele.checked = false; + } + + var data = {}; + data.widgetSrc = session.widget; + for (var UUID in session.pcs){ + if (session.pcs[UUID].allowWidget===true){ + session.sendMessage(data, UUID); + } + } + + if (session.director){ + let widget = document.getElementById("widget"); + if (!widget){ + if (session.widget){ + widget = document.createElement("iframe"); + widget.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;display-capture;midi;"; + widget.id = "widget"; + widget.src = parseURL4Iframe(session.widget); + log(widget.src); + document.body.appendChild(widget); + getById("guestFeeds").style.width = "75%"; + } + } else if (session.widget){ + if (session.widget){ + widget.src = parseURL4Iframe(session.widget); + } else { + getById("widget").remove(); + getById("guestFeeds").style.width = "100%"; + } + } + } + + pokeIframeAPI("widget-src", session.widget); +} + +async function createRoomCallback(passAdd, passAdd2) { + + if (!session.switchMode){ + getById("directorlayout").classList.remove("hidden"); + getById("gridlayout").classList.add("hidden"); + } + + var broadcastFlag = getById("broadcastFlag"); + try { + if (broadcastFlag.checked) { + broadcastFlag = true; + } else { + broadcastFlag = false; + } + } catch (e) { + broadcastFlag = false; + } + + var broadcastString = ""; + if (broadcastFlag) { + broadcastString = "&broadcast"; + getById("broadcastSlider").checked = true; + } + + var wss = ""; + if (session.wssSetViaUrl){ + if (session.customWSS && (session.customWSS!==true)){ + wss = "&pie="+session.customWSS; + } else { + wss = "&wss="+session.wss; + } + } + + var queue = ""; + if (session.queue){ + queue = "&queue"; + getById("directorLinks2").style.opacity = "0.2"; + getById("directorLinks2").style.pointerEvents = "none"; + getById("directorLinks2").style.cursor = "not-allowed"; + } + + var showdirectorFlag = getById("showdirectorFlag"); + try { + if (showdirectorFlag.checked) { + showdirectorFlag = true; + } else { + showdirectorFlag = false; + } + } catch (e) { + showdirectorFlag = false; + } + + if (showdirectorFlag) { + updateURL("showdirector", true, false); + session.showDirector = session.showDirector || true; + //getById("broadcastSlider").checked=true; + } + + + var codecGroupFlag = getById("codecGroupFlag"); + + if (codecGroupFlag.value) { + if (codecGroupFlag.value === "vp9") { + codecGroupFlag = "&codec=vp9"; + getById("codech264toggle").disabled=true; + } else if (codecGroupFlag.value === "h264") { + codecGroupFlag = "&codec=h264"; + getById("codech264toggle").checked=true; + } else if (codecGroupFlag.value === "vp8") { + codecGroupFlag = "&codec=vp8"; + getById("codech264toggle").disabled=true; + } else if (codecGroupFlag.value === "av1") { + codecGroupFlag = "&codec=av1"; + getById("codech264toggle").disabled=true; + } else { + codecGroupFlag = ""; + } + } else { + codecGroupFlag = ""; + } + if (codecGroupFlag) { + session.codecGroupFlag = codecGroupFlag; + } + + if (session.bitrateGroupFlag){ + codecGroupFlag += session.bitrateGroupFlag; + } + + + formSubmitting = false; + try { + var m = getById("mainmenu"); + m.remove(); + document.querySelectorAll(".hidden2").forEach(ele2=>{ + ele2.classList.remove("hidden2"); + }); + } catch(e){} + + getById("head1").className = 'hidden'; + getById("head2").className = 'hidden'; + getById("head4").className = ''; + + try { + if (session.label === false) { + document.title = "Control Room"; + } + } catch (e) { + errorlog(e); + }; + + session.director = true; + screensharesupport = false; + + if (session.meterStyle ===false){ + session.meterStyle = 1; // director specific style + } + if (session.signalMeter===null){ + session.signalMeter = true; + } + if (session.batteryMeter===null){ + session.batteryMeter = true; + } + + if (session.directorPassword){ + getById("coDirectorEnable").checked = true; + getById("coDirectorEnableSpan").style.display = "none"; + + var token = ""; + if (session.token){ + token+="&token="+session.token; + } + + getById("codirectorSettings_invite").value = "https://"+location.host+location.pathname+"?dir="+session.roomid+"&codirector="+session.directorPassword+token; + if (session.password!==session.sitePassword){ + if (session.password==false){ + getById("codirectorSettings_invite").value += "&password=false"; + } else{ + getById("codirectorSettings_invite").value += "&password="+session.password; + } + } + + if (session.codirector_transfer){ + getById("codirectorSettings_transfer").checked = true; + } else { + getById("codirectorSettings_transfer").checked = false; + } + if (session.codirector_changeURL){ + getById("codirectorSettings_changeurl").checked = true; + } else { + getById("codirectorSettings_changeurl").checked = false; + } + getById("codirectorSettings").style.display = "block"; + } + + + window.onresize = updateMixer; + window.onorientationchange = function(){ + if (Firefox){ + updateForceRotate(true); + } + setTimeout(async function(){ + if (session.forceAspectRatio){ + await updateCameraConstraints("aspectRatio", session.forceAspectRatio); + } + if (session.effect && (session.effect === "7")){digitalZoom(true);} + updateForceRotate(); + updateMixer(); + }, 200); + }; + getById("reshare").parentNode.removeChild(getById("reshare")); + + //getById("mutespeakerbutton").style.display = null; + if (session.speakerMuted_default===false){ + //session.speakerMuted = false; // the director will start with audio playback muted. + toggleSpeakerMute(true); // let it be what it is. + } else { + session.speakerMuted = true; // the director will start with audio playback muted. + toggleSpeakerMute(true); // okay since only run on start + } + + var token = ""; + if (session.token){ + token+="&token="+session.token; + } + + if (session.cleanDirector == false && session.cleanOutput==false) { + + getById("roomHeader").style.display = ""; + //getById("directorLinks").style.display = ""; + getById("directorLinks1").style.display = "inline-block"; + getById("directorLinks2").style.display = "inline-block"; + + + getById("director_block_1").dataset.raw = "https://" + location.host + location.pathname + "?room=" + session.roomid + broadcastString + passAdd + wss + queue + token; + getById("director_block_1").href = "https://" + location.host + location.pathname + "?room=" + session.roomid + broadcastString + passAdd + wss + queue + token; + getById("director_block_1").innerText = "https://" + location.host + location.pathname + "?room=" + session.roomid + broadcastString + passAdd + wss + queue + token; + + + getById("director_block_3").dataset.raw = "https://" + location.host + location.pathname + "?scene&room=" + session.roomid + codecGroupFlag + passAdd2 + wss + token; + getById("director_block_3").href = "https://" + location.host + location.pathname + "?scene&room=" + session.roomid + codecGroupFlag + passAdd2 + wss + token; + getById("director_block_3").innerText = "https://" + location.host + location.pathname + "?scene&room=" + session.roomid + codecGroupFlag + passAdd2 + wss + token; + + getById("calendarButton").style.display = "inline-block"; + + } else { + getById("guestFeeds").innerHTML = ''; + } + getById("guestFeeds").style.display = ""; + + if (!(session.cleanOutput)) { + if (session.queue){ + getById("queuebutton").classList.remove("hidden"); + } + getById("chatbutton").classList.remove("hidden"); + getById('sharefilebutton').classList.remove("hidden"); // we won't override "display:none", if set, though. + getById("controlButtons").classList.remove("hidden"); + getById("mutespeakerbutton").classList.remove("hidden"); + getById("websitesharebutton").classList.remove("hidden"); + //getById("screensharebutton").classList.remove("hidden"); + + if (session.totalRoomBitrate){ + getById("roomsettingsbutton").classList.remove("hidden"); + } + + if (!session.showDirector) { // if null or false, we want to show the solo link, since the director won't have their control box. The director will be visible in their solo link + getById("miniPerformer").innerHTML = ''; + miniTranslate(getById("miniPerformer")); + getById("grabDirectorSoloLink").dataset.raw = "https://" + location.host + location.pathname + "?solo&r=" + session.roomid + "&v="+session.streamID + passAdd2 + wss + token; + getById("grabDirectorSoloLink").href = "https://" + location.host + location.pathname + "?solo&r=" + session.roomid + "&v="+session.streamID + passAdd2 + wss + token; + getById("grabDirectorSoloLink").innerText = "https://" + location.host + location.pathname + "?solo&r=" + session.roomid + "&v="+session.streamID + passAdd2 + wss + token; + getById("grabDirectorSoloLinkParent").classList.remove("hidden"); + } else { + getById("miniPerformer").innerHTML = ''; + } + getById("miniPerformer").className = ""; + + var tabindex = 26; + if (session.rooms && session.rooms.length > 0){ + var container = getById("rooms"); + container.innerHTML += 'Arm Transfer: '; + session.rooms.forEach(function (r) { + // if(session.roomid == r) return; //don't include self + container.innerHTML += ''; + tabindex++; + }); + } + + } else { + getById("miniPerformer").style.display = "none"; + getById("controlButtons").classList.add("hidden"); + } + + if (session.chatbutton === true) { + getById("chatbutton").classList.remove("hidden"); + getById("controlButtons").classList.remove("hidden"); + } else if (session.chatbutton === false) { + getById("chatbutton").classList.add("hidden"); + } + + if (session.effect===false){ + session.effect = null; // so the director can see the effects + } + + getById("avatarDiv3").classList.remove("hidden"); // lets the director see the avatar option + + clearInterval(session.updateLocalStatsInterval); + session.updateLocalStatsInterval = setInterval(function(){updateLocalStats();},session.statsInterval); + + var directorWebsiteShare = getStorage("directorWebsiteShare"); // {"website":session.iframeSrc, "roomid":session.roomid} + + if (typeof directorWebsiteShare === 'object' && directorWebsiteShare !== null && "website" in directorWebsiteShare){ + if (directorWebsiteShare.website == false){ + clearDirectorSettings(); + } else if (directorWebsiteShare.roomid && (directorWebsiteShare.roomid==session.roomid)){ + session.iframeSrc = directorWebsiteShare.website; + session.defaultIframeSrc = directorWebsiteShare.website; + + getById("websitesharebutton").classList.add("hidden"); + getById("websitesharebutton2").classList.remove("hidden"); + } + } + + session.group.forEach(group=>{ + // changeGroupDirectorAPI(group, state=null, update=true) + changeGroupDirectorAPI(group, true, false); // update the UI only + }); + + session.groupView.forEach(group=>{ + // changeGroupDirectorAPI(group, state=null, update=true) + changeGroupViewDirectorAPI(group, true); // update the UI only + }); + + if (session.showDirector){ + getById("highlightDirectorSpan").style.display = "none"; + getById("highlightDirectorSpan").remove(); + } else { + getById("highlightDirector").dataset.sid = session.streamID; + } + + setTimeout(function(){loadDirectorSettings();},100); + + joinRoom(session.roomid); + + try { + if (!gotDevices2AlreadyRan && (iOS || iPad)){ + await enumerateDevices().then(gotDevices2); // this is needed for iOS; was previous set to timeout at 100ms, but would be useful everywhere I think. (Breaks director's auto start, so just iOS for now) + } + }catch(e){ + errorlog(e); + } + + if (session.autostart){ + setTimeout(function(){press2talk(true);},400); + } else { + session.seeding=true; + session.seedStream(); + } +} // createRoomCallback + +function handleRoomSelect(room) { + var elems = document.querySelectorAll(".btnArmTransferRoom"); + [].forEach.call(elems, function(el) { + el.classList.remove("selected"); + }); + if (previousRoom == room) { + previousRoom = ""; + armedTransfer = false; + stillNeedRoom = true; + } else { + previousRoom = room; + stillNeedRoom = false; + armedTransfer = true; + getById("roomselect_" + room).classList.add('selected'); + } +} + +function getDirectorSettings(scene=false){ + var settings = {}; + + var eles = document.querySelectorAll('[data-action-type="solo-video"]'); + settings.soloVideo = false; + for (var i=0;ibiggestSlot){ + biggestSlot = parseInt(slots[i].dataset.slot); + } + if (slotDefault===parseInt(slots[i].dataset.slot)){ + slotDefault = null; + } + allSlots.push(parseInt(slots[i].dataset.slot)); + } + biggestSlot+=1; + } + if (slotDefault!==null){ + biggestSlot = slotDefault; + } else if (session.slotmode==1){ + var bestfree = 0 ; + for (var i =1; i<=biggestSlot; i++){ + if (allSlots.includes(i)){ + continue; + } else { + bestfree = i; + break; + } + } + biggestSlot = bestfree; + } + var slotName = "slot: "+biggestSlot; + if (!biggestSlot){ + slotName = "unset"; + } + + buttons += "
    \ +
    "; + + } + buttons += "
    \ +
    ID: " + session.streamID + "\ + \ + \ + "+getTranslation("add-a-label")+"\ +
    \ +
    "; + + container.innerHTML = buttons; + + var oldGroups = []; + document.querySelectorAll("#groups [data-action-type='toggle-group'][data-group]:not(.green)").forEach(ee=>{ + oldGroups.push(ee.dataset.group); + }); + getById("groups").remove(); + + if (session.hidesololinks==false){ // won't be updating the solo link to a view-only one ever, since director is always expected to be in a room + controls.innerHTML += "
    \ + " + sanitizeChat(soloLink) + "\ + \ +
    \ +
    "; + if (session.directorUUID){ + controls.innerHTML += "

    This is you, a co-director.
    You are also a performer.

    "; + } else { + controls.innerHTML += "

    This is you, the director.
    You are also a performer.

    "; + } + } + + controls.querySelectorAll('[data-action-type]').forEach((ele) => { // give action buttons some self-reference + ele.dataset.sid = session.streamID; + }); + + container.appendChild(controls); + + getById("guestFeeds").appendChild(container); + + Object.keys(session.sceneList).forEach((scene, index) => { + if (document.getElementById("container_director")){ + if (!(getById("container_director").querySelectorAll('[data-scene="'+scene+'"]').length)){ + var newScene = document.createElement("div"); + newScene.innerHTML = ''; + newScene.classList.add("customScene"); + //getById("container_director").appendChild(newScene); + + var added = false; + getById("container_director").querySelectorAll('.customScene>[data-scene]').forEach(ele=>{ + if (!added && ele.dataset.scene>scene+""){ + ele.parentNode.parentNode.insertBefore(newScene, ele.parentNode); + added = true; + } + }); + if (!added){ + getById("container_director").appendChild(newScene); + } + + } + } + }); + + getById("groups").showDirector = true; + + session.group.forEach(group=>{ + // changeGroupDirectorAPI(group, state=null, update=true) + changeGroupDirectorAPI(group, true, false); // update the UI only / + }); + + oldGroups.forEach(group=>{ + // changeGroupDirectorAPI(group, state=null, update=true) + changeGroupDirectorAPI(group, false, false); // update the UI only / + }); + + + var labelID = document.getElementById("label_director"); + + labelID.onclick = async function(ee){ + var oldlabel = ee.target.innerText; + if (session.label===false){ + oldlabel = ""; + } + window.focus(); + var newlabel = await promptAlt(getTranslation("enter-new-display-name"), false, false, oldlabel); + if (newlabel!==null){ + newlabel = newlabel.trim(); + if (newlabel === ""){ + newlabel = false; + //ee.target.innerHTML = getTranslation("add-a-label"); + miniTranslate(ee.target,"add-a-label"); + ee.target.classList.add("addALabel"); + } else { + ee.target.innerText = newlabel; + ee.target.classList.remove("addALabel"); + } + session.label = newlabel; + var data = {}; + data.changeLabel = true; + data.value = session.label; + session.sendMessage(data); + } + } + labelID.style.float = "left"; + labelID.style.top = "2px"; + labelID.style.marginLeft = "5px"; + labelID.style.position = "relative"; + labelID.style.cursor="pointer"; + if (session.label){ + labelID.innerText = session.label; + } + pokeIframeAPI("control-box", true, true); + if (session.slotmode){ + pokeIframeAPI("slot-updated", biggestSlot, null, session.streamID); // need to support self-director + session.pastSlots[session.streamID] = biggestSlot; + + createSlotUpdate(); + } +} + +function createSlotUpdate(UUID=false){ + try { + var newSlots = {}; + document.querySelectorAll("[data--u-u-i-d][data-slot]").forEach(ele=>{ + newSlots[ele.dataset.slot] = ele.dataset.sid; + }); + if (!UUID){ + for (var uid in session.pcs){ + if (session.pcs[uid].layout){ + session.sendMessage({slotsUpdate:newSlots}, uid); + } + } + } else { + session.sendMessage({slotsUpdate:newSlots}, UUID); + } + } catch(e){ + errorlog(e); + } +} + +async function createDirectorScreenshareOnlyBox() { // sstype=3 + + var soloLink = soloLinkGenerator(session.streamID+":s"); + + if (document.getElementById("deleteme")) { + getById("deleteme").parentNode.removeChild(getById("deleteme")); + } + var controls = getById("controls_directors_blank").cloneNode(true); + controls.classList.remove("hidden"); + controls.id = "controls_screen_director"; + + var container = document.createElement("div"); + container.className = "vidcon directorMargins"; + container.id = "container_screen_director"; // needed to delete on user disconnect + + + var buttons = ""; + if (session.slotmode){ + var slots = document.querySelectorAll("div.slotsbar[data-slot]"); + var biggestSlot=0; + + var slotDefault = null; + if (session.streamID+":s" in session.pastSlots){ + slotDefault = session.pastSlots[session.streamID+":s"]; + } + var allSlots = []; + if (session.slotmode==1){ + for (var i=0;ibiggestSlot){ + biggestSlot = parseInt(slots[i].dataset.slot); + } + if (slotDefault===parseInt(slots[i].dataset.slot)){ + slotDefault = null; + } + allSlots.push(parseInt(slots[i].dataset.slot)); + } + biggestSlot+=1; + } + if (slotDefault!==null){ + biggestSlot = slotDefault; + } else if (session.slotmode==1){ + var bestfree = 0; + for (var i =1; i<=biggestSlot; i++){ + if (allSlots.includes(i)){ + continue; + } else { + bestfree = i; + break; + } + } + biggestSlot = bestfree; + } + + var slotName = "slot: "+biggestSlot; + if (!biggestSlot){ + slotName = "unset"; + } + + + buttons += "
    \ +
    "; + + } + buttons += "
    \ +
    ID: " + session.streamID+":s\ + \ + \ + "+getTranslation("add-a-label")+"\ +
    \ +
    "; + + container.innerHTML = buttons; + + var oldGroups = []; + document.querySelectorAll("#groups [data-action-type='toggle-group'][data-group]:not(.green)").forEach(ee=>{ + oldGroups.push(ee.dataset.group); + }); + getById("groups").remove(); + + if (session.hidesololinks==false){ // won't be updating the solo link to a view-only one ever, since director is always expected to be in a room + controls.innerHTML += "
    \ + " + sanitizeChat(soloLink) + "\ + \ +
    \ +
    "; + if (session.directorUUID){ + controls.innerHTML += "

    This is you, a co-director.
    You are also a performer.

    "; + } else if (session.showDirector===false){ + try { + controls.querySelectorAll('[data-action-type="addToScene"]').forEach((ele) => { ele.classList.add("hidden"); }); + } catch(e){errorlog(e);} + controls.innerHTML += "

    This is your screen share
    It's *not* a performer.

    "; + } else { + controls.innerHTML += "

    This your screen share.
    It's also a performer.

    "; + } + } + + controls.querySelectorAll('[data-action-type]').forEach((ele) => { // give action buttons some self-reference + ele.dataset.sid = session.streamID+":s" ; + }); + + container.appendChild(controls); + + getById("guestFeeds").appendChild(container); + + + + Object.keys(session.sceneList).forEach((scene, index) => { + if (document.getElementById("container_screen_director")){ + if (!(getById("container_screen_director").querySelectorAll('[data-scene="'+scene+'"]').length)){ + var newScene = document.createElement("div"); + newScene.innerHTML = ''; + newScene.classList.add("customScene"); + //getById("container_screen_director").appendChild(newScene); + + var added = false; + getById("container_screen_director").querySelectorAll('.customScene>[data-scene]').forEach(ele=>{ + if (!added && ele.dataset.scene>scene+""){ + ele.parentNode.parentNode.insertBefore(newScene, ele.parentNode); + added = true; + } + }); + if (!added){ + getById("container_screen_director").appendChild(newScene); + } + + } + } + }); + + getById("groups").showDirector = true; + + session.group.forEach(group=>{ + // changeGroupDirectorAPI(group, state=null, update=true) + changeGroupDirectorAPI(group, true, false); // update the UI only / + }); + + oldGroups.forEach(group=>{ + // changeGroupDirectorAPI(group, state=null, update=true) + changeGroupDirectorAPI(group, false, false); // update the UI only / + }); + + + var labelID = document.getElementById("label_director"); + + labelID.onclick = async function(ee){ + var oldlabel = ee.target.innerText; + if (session.label===false){ + oldlabel = ""; + } + window.focus(); + var newlabel = await promptAlt(getTranslation("enter-new-display-name"), false, false, oldlabel); + if (newlabel!==null){ + newlabel = newlabel.trim(); + if (newlabel === ""){ + newlabel = false; + //ee.target.innerHTML = getTranslation("add-a-label"); + miniTranslate(ee.target,"add-a-label"); + ee.target.classList.add("addALabel"); + } else { + ee.target.innerText = newlabel; + ee.target.classList.remove("addALabel"); + } + session.label = newlabel; + var data = {}; + data.changeLabel = true; + data.value = session.label; + session.sendMessage(data); + } + } + labelID.style.float = "left"; + labelID.style.top = "2px"; + labelID.style.marginLeft = "5px"; + labelID.style.position = "relative"; + labelID.style.cursor="pointer"; + if (session.label){ + labelID.innerText = session.label; + } + pokeIframeAPI("control-box", true, true); + if (session.slotmode){ + pokeIframeAPI("slot-updated", biggestSlot, null, session.streamID+":s"); // need to support self-director + session.pastSlots[session.streamID+":s"] = biggestSlot; + + createSlotUpdate(); + } +} + +function shiftPC(ele, shift, director=false){ + if (director){ + var target = document.getElementById("container_director"); + } else { + var target = document.getElementById("container_"+ele.dataset.UUID); + } + + if (!target){return;} + + target.shifted = true; + + var target2 = false; + + if (shift==1){ + if (target.nextSibling){ + target2 = target.nextSibling; + target.parentNode.insertBefore(target.nextSibling, target); + } + } else { + if (target.previousSibling){ + target2 = target.previousSibling; + target.parentNode.insertBefore(target, target.previousSibling); + } + } + updateLockedElements(); + + if (session.api){ + var slots = {} + var elements = getById("guestFeeds").children; + for (var i = 0;i"; + ele.parentNode.classList.add("locked"); + + while (currentPosition>parseInt(ele.dataset.locked)){ + var node = document.getElementById("container_"+UUID); + parent = node.parentNode, + prev = node.previousSibling, + oldChild = parent.removeChild(node); + parent.insertBefore( oldChild, prev ); + currentPosition = Array.prototype.indexOf.call(getById("guestFeeds").children, document.getElementById("container_"+UUID))+1; + } + + while ((currentPositioncurrentPosition)){ + var node = document.getElementById("container_"+UUID); + parent = node.parentNode, + next = node.nextSibling, + oldChild = parent.removeChild(node); + parent.insertBefore(node, next.nextSibling); + currentPosition = Array.prototype.indexOf.call(getById("guestFeeds").children, document.getElementById("container_"+UUID))+1; + } + } + } else { + ele.dataset.locked = 0; + ele.innerHTML = ""; + ele.parentNode.classList.remove("locked"); + } + } else { + if (ele.dataset.locked && parseInt(ele.dataset.locked)){ + ele.dataset.locked = 0; + ele.innerHTML = ""; + ele.parentNode.classList.remove("locked"); + } else { + if (getById("guestFeeds")){ + ele.dataset.locked = Array.prototype.indexOf.call(getById("guestFeeds").children, document.getElementById("container_"+UUID))+1; + ele.innerHTML = "#"+ele.dataset.locked+""; + ele.parentNode.classList.add("locked"); + } + } + } +} + +function allowDropSlot(event) { + event.preventDefault(); +} + +function dragSlot(event) { + log("drag"); + + var ele = event.target; + if (!ele.dataset.UUID && ele.parentNode.dataset.UUID){ + ele = ele.parentNode; + } + + event.dataTransfer.setDragImage( getById('dragImage'), 24, 24); + event.dataTransfer.setData("text", ele.dataset.UUID); + + var eles = document.querySelectorAll(".slotsbar"); + for (var i=0;i +
    + × + ${message} +
    + +
    `; + document.body.insertAdjacentHTML("beforeend", modalTemplate); // Insert modal at body end + + document.getElementById("modalBackdrop").addEventListener("click", closeModal); + + document.getElementById("alertModalMessage").querySelectorAll("div[data-slot]").forEach(choice=>{ + choice.onclick = function(){ + setSlot(ele, parseInt(this.dataset.slot)); + closeModal(); + }; + }); + + document.getElementById("alertModal").style.left = (event.pageX)+"px"; // 165px 356px + document.getElementById("alertModal").style.top = (event.pageY+180)+"px"; + + getById("alertModal").addEventListener("click", function(e) { + e.stopPropagation(); + return false; + }); + + } else { + var slot = await promptAlt("Which slot to change to?"); + setSlot(ele,slot); + } +} + +function setSlot(ele,slot){ + log("setSlot()"); + getById("slotPicker").classList.add("hidden"); + if (slot!==null){ + slot = (parseInt(slot) || 0); + var slots = document.querySelectorAll("div.slotsbar[data-slot]"); + for (var i=0;i streamID.toLowerCase()){ + getById("guestFeeds").insertBefore(container, getById("guestFeeds").children[i]); + added = true; + break; + } + } + } + + } + if (!added){ + getById("guestFeeds").appendChild(container); + } + } catch(e){ + getById("guestFeeds").appendChild(container); + } + } else { + getById("guestFeeds").appendChild(container); + } + + if (!session.rpcs[UUID].voiceMeter) { + if (session.meterStyle==1){ // director specific style + session.rpcs[UUID].voiceMeter = getById("voiceMeterTemplate2").cloneNode(true); + } else { + session.rpcs[UUID].voiceMeter = getById("voiceMeterTemplate").cloneNode(true); + session.rpcs[UUID].voiceMeter.style.opacity = 0; + if (session.meterStyle==2){ + session.rpcs[UUID].voiceMeter.classList.add("video-meter-2"); + session.rpcs[UUID].voiceMeter.classList.remove("video-meter"); + } else { + session.rpcs[UUID].voiceMeter.classList.add("video-meter-director"); + } + } + session.rpcs[UUID].voiceMeter.id = "voiceMeter_" + UUID; + session.rpcs[UUID].voiceMeter.dataset.level = 0; + session.rpcs[UUID].voiceMeter.classList.remove("hidden"); + } + + session.rpcs[UUID].remoteMuteElement = getById("muteStateTemplate").cloneNode(true); + session.rpcs[UUID].remoteMuteElement.id = ""; + session.rpcs[UUID].remoteMuteElement.style.top = "5px"; + session.rpcs[UUID].remoteMuteElement.style.right = "7px"; + + session.rpcs[UUID].remoteVideoMuteElement = getById("videoMuteStateTemplate").cloneNode(true); + session.rpcs[UUID].remoteVideoMuteElement.id = ""; + session.rpcs[UUID].remoteVideoMuteElement.style.top = "5px"; + session.rpcs[UUID].remoteVideoMuteElement.style.right = "28px"; + + session.rpcs[UUID].remoteRaisedHandElement = getById("raisedHandTemplate").cloneNode(true); + session.rpcs[UUID].remoteRaisedHandElement.id = ""; + session.rpcs[UUID].remoteRaisedHandElement.style.top = "5px"; + session.rpcs[UUID].remoteRaisedHandElement.style.right = "49px"; + + var videoContainer = document.createElement("div"); + videoContainer.id = "videoContainer_" + UUID; // needed to delete on user disconnect + videoContainer.style.margin = "0"; + videoContainer.style.position = "relative"; + videoContainer.style.minHeight = "30px"; + + var iframeDetails = document.createElement("div"); + iframeDetails.id = "iframeDetails_" + UUID; // needed to delete on user disconnect + iframeDetails.className = "iframeDetails hidden"; + + controls.innerHTML += ""; + controls.innerHTML += ""; + + var handsID = "hands_" + UUID; + + // controls.innerHTML += "
    Links
    "; //Seems to create an empty div. + + if (session.hidesololinks==false){ + controls.innerHTML += "
    \ + " + sanitizeChat(soloLink) + "\ + \ +
    "; + } + + controls.innerHTML += "\ + "; + + + controls.innerHTML += "\ + "; + + controls.querySelectorAll('[data-action-type]').forEach((ele) => { // give action buttons some self-reference + ele.dataset.UUID = UUID; + ele.dataset.sid = streamID; + }); + + var buttons = ""; + if (session.slotmode){ + var slots = document.querySelectorAll("div.slotsbar[data-slot]"); + var biggestSlot=0; + var slotDefault = null; + + if (slot_init && (session.slotmode==1)){ + slotDefault = slot_init || null; + } + + if (streamID in session.pastSlots){ + slotDefault = session.pastSlots[streamID]; + } + + var allSlots = []; + if (session.slotmode==1){ + for (var i=0;ibiggestSlot){ + biggestSlot = parseInt(slots[i].dataset.slot); + } + allSlots.push(parseInt(slots[i].dataset.slot)); + if (slotDefault===parseInt(slots[i].dataset.slot)){ // already taken + slotDefault = null; + } + } + biggestSlot+=1; + } + if (slotDefault!==null){ // the default slot is avialable + biggestSlot = slotDefault; + } else if (slot_init && (session.slotmode==1)){ // was manually set, so can't be something else but 0; 0 or false would still end up with 0 + biggestSlot = 0; + } else if (session.slotmode==1){ + var bestfree = 0; + for (var i =1; i<=biggestSlot; i++){ + if (allSlots.includes(i)){ + continue; + } else { + bestfree = i; + break; + } + } + biggestSlot = bestfree; + } + + var slotName = "slot: "+biggestSlot; + if (!biggestSlot){ + slotName = "unset"; + } + + buttons += "
    \ +
    "; + + } + buttons += "
    ID: " + streamID + "\ + \ + \ + \ +
    "; + + container.innerHTML = buttons; + updateLockedElements(); + + var videoContainerControlBox = document.createElement("div"); + videoContainerControlBox.className = "controlVideoBox"; + container.containerControlBox = videoContainerControlBox + + container.appendChild(videoContainerControlBox); + videoContainerControlBox.appendChild(videoContainer); + + if (session.signalMeter){ + if (!session.rpcs[UUID].signalMeter){ + session.rpcs[UUID].signalMeter = getById("signalMeterTemplate").cloneNode(true); + session.rpcs[UUID].signalMeter.id = "signalMeter_" + UUID; + session.rpcs[UUID].signalMeter.dataset.level = 0; + session.rpcs[UUID].signalMeter.classList.remove("hidden"); + session.rpcs[UUID].signalMeter.dataset.UUID = UUID; + session.rpcs[UUID].signalMeter.title = getTranslation("signal-meter"); + + if (session.rpcs[UUID].stats.info && session.rpcs[UUID].stats.info.cpuLimited){ // was quality_limitation_reason + session.rpcs[UUID].signalMeter.dataset.cpu = "1"; + } + + if (session.statsMenu !==false){ + session.rpcs[UUID].signalMeter.addEventListener('click', function(e) { // show stats of video if double clicked + log("clicked signal meter"); + try { + e.preventDefault(); + if (session.statsMenu !==false){ + var uid = e.currentTarget.dataset.UUID; + if ("stats" in session.rpcs[uid]){ + + var [menu, innerMenu] = statsMenuCreator(); + printViewStats(innerMenu, uid ); + menu.interval = setInterval(printViewStats, session.statsInterval, innerMenu, uid); + + } + } + e.stopPropagation(); + return false; + + } catch(e){errorlog(e);} + }); + } + } + videoContainer.appendChild(session.rpcs[UUID].signalMeter); + } + + if (session.batteryMeter){ + //////// + if (!session.rpcs[UUID].batteryMeter){ + session.rpcs[UUID].batteryMeter = getById("batteryMeterTemplate").cloneNode(true); + session.rpcs[UUID].batteryMeter.id = "batteryMeter_" + UUID; + /* + if (session.rpcs[UUID].stats.info && (session.rpcs[UUID].stats.info.power_level!==null)){ + var level = session.rpcs[UUID].batteryMeter.querySelector(".battery-level"); + if (level){ + var value = session.rpcs[UUID].stats.info.power_level; + if (value > 100){value = 100;} + else if (value < 0){ value = 0;} + level.style.height = parseInt(value)+"%"; + if (value<10){ + session.rpcs[UUID].batteryMeter.classList.add("alert"); + } else if (value<25){ + session.rpcs[UUID].batteryMeter.classList.add("warn"); + } + if (value<100){ + session.rpcs[UUID].batteryMeter.classList.remove("hidden"); + } + session.rpcs[UUID].batteryMeter.title = (Math.round(value*10)/10)+"% battery remaining"; + } + } + if (session.rpcs[UUID].stats.info && ("plugged_in" in session.rpcs[UUID].stats.info) && (session.rpcs[UUID].stats.info.plugged_in===false)){ + session.rpcs[UUID].batteryMeter.dataset.plugged = "0"; + session.rpcs[UUID].batteryMeter.classList.remove("hidden"); + } else { + session.rpcs[UUID].batteryMeter.dataset.plugged = "1"; + } + */ + batteryMeterInfoUpdate(UUID); + } + videoContainer.appendChild(session.rpcs[UUID].batteryMeter); + } + + if (session.showConnections){ + if (!session.rpcs[UUID].connectionDetails){ + createConnectionDetailsEle(UUID); + } + videoContainer.appendChild(session.rpcs[UUID].connectionDetails); + } + + videoContainer.appendChild(session.rpcs[UUID].voiceMeter); + videoContainer.appendChild(session.rpcs[UUID].remoteMuteElement); + videoContainer.appendChild(session.rpcs[UUID].remoteVideoMuteElement); + videoContainer.appendChild(session.rpcs[UUID].remoteRaisedHandElement); + videoContainer.appendChild(iframeDetails); + container.appendChild(controls); + + session.group.forEach(group=>{ + var ele = controls.querySelector('[data-action-type="toggle-group"][data--u-u-i-d="'+UUID+'"][data-group="'+group+'"]'); + if (!ele){ + var newGroup = htmlToElement(''); + + var added = false; + container.querySelectorAll('.customGroup>[data-group]').forEach(ele=>{ + log(ele); + if (!added && ele.dataset.group>group+""){ + ele.parentNode.insertBefore(newGroup, ele); + added = true; + } + }); + if (!added){ + var newGroupCon = container.querySelector(".customGroup"); + if (!newGroupCon){ + newGroupCon = document.createElement("div"); + newGroupCon.classList.add("customGroup"); + container.appendChild(newGroupCon); + } + newGroupCon.appendChild(newGroup); + } + } + }); + + initSceneList(UUID); + syncSceneState(streamID); + syncOtherState(streamID); + + pokeIframeAPI("control-box", true, UUID); + if (session.slotmode){ + pokeIframeAPI("slot-updated", biggestSlot, UUID); // need to support self-director + session.pastSlots[streamID] = biggestSlot; + + createSlotUpdate(); + } +} + + +function minimizeMe(button, director=false){ + if (!director){ + getById("container_"+button.dataset.UUID).classList.toggle("minimized"); + } else { + getById(director).classList.toggle("minimized"); + } +} +function cycleCameras(){ + if (session.screenShareState) { + warnUser("Stop the screen-share first."); + return; + } + var videoSelect = document.querySelector("select#videoSource3").options; + // don't show flip option if only one camera. + // don't show if not a mobile device + // don't show if AD=0 + + + var matched = false; + var maxIndex = parseInt(getById("flipcamerabutton").dataset.maxIndex) || parseInt(videoSelect.length); + if (maxIndex > parseInt(videoSelect.length)){ + maxIndex = parseInt(videoSelect.length); + } + + for(var i = 0; i < maxIndex; i++){ + var selOption = videoSelect[i]; + if (selOption.selected) { + matched=true; + } else if (matched){ + if (getById("flipcamerabutton").classList.contains("flip")){ + getById("flipcamerabutton").classList.remove("flip"); + getById("flipcamerabutton").classList.add("flip2"); + } else { + getById("flipcamerabutton").classList.remove("flip2"); + getById("flipcamerabutton").classList.add("flip"); + } + document.querySelector("select#videoSource3").value = selOption.value; + activatedPreview = false; + grabVideo(session.quality, "videosource", "select#videoSource3"); + return; + } + } + for(var i = 0; i < maxIndex; i++){ + var selOption = videoSelect[i]; + if (selOption.selected) { + return; // do nothing; the camera that is selected is the only camera available it seems. + } else { + if (getById("flipcamerabutton").classList.contains("flip")){ + getById("flipcamerabutton").classList.remove("flip"); + getById("flipcamerabutton").classList.add("flip2"); + } else { + getById("flipcamerabutton").classList.remove("flip2"); + getById("flipcamerabutton").classList.add("flip"); + } + document.querySelector("select#videoSource3").value = selOption.value; + activatedPreview = false; + grabVideo(session.quality, "videosource", "select#videoSource3"); + return; + } + } +} + + + +function addToGoogleCalendar(){ + var title = "Live Stream"; + //var dates = "20180512T230000Z/20180513T030000Z"; + var linkout = getById("director_block_1").innerText; + var details = "Join the live stream as a performer at the following link:

    ===> "+linkout+"

    To test your connection and camera ahead of time, please visit https://vdo.ninja/speedtest

    Do not share the details of this invite with others, unless explicitly told to."; + details = details.split(' ').join('+'); + details = details.split('&').join('%26'); + var linkToOpen = "https://calendar.google.com/calendar/r/eventedit?text="+title+"&details="+details; + //https://calendar.google.com/calendar/r/eventedit?text=My+Custom+Event&dates=20180512T230000Z/20180513T030000Z&details=For+details,+link+here:+https://example.com/tickets-43251101208&location=Garage+Boston+-+20+Linden+Street+-+Allston,+MA+02134 + + window.open(linkToOpen); + +} + +function addToOutlookCalendar(){ + var title = "Live Stream"; + var linkout = getById("director_block_1").innerText; + var details = "Join the live stream as a performer at the following link:

    ===> "+linkout+"

    To test your connection and camera ahead of time, please visit https://vdo.ninja/speedtest

    Do not share the details of this invite with others, unless explicitly told to."; + details = details.split(' ').join('%20'); + details = details.split('&').join('%26'); + + + var linkToOpen = "https://outlook.live.com/owa/?path=%2Fcalendar%2Faction%2Fcompose&rru=addevent&subject="+title+"&body="+details; + //https://calendar.google.com/calendar/r/eventedit?text=My+Custom+Event&dates=20180512T230000Z/20180513T030000Z&details=For+details,+link+here:+https://example.com/tickets-43251101208&location=Garage+Boston+-+20+Linden+Street+-+Allston,+MA+02134 + + window.open(linkToOpen); +} + +function addToYahooCalendar(){ + var title = "Live Stream"; + var linkout = getById("director_block_1").innerText; + var details = "Join the live stream as a performer at the following link:

    ===> "+linkout+"

    To test your connection and camera ahead of time, please visit https://vdo.ninja/speedtest

    Do not share the details of this invite with others, unless explicitly told to."; + details = details.split(' ').join('%20'); + details = details.split('&').join('%26'); + var linkToOpen = "https://calendar.yahoo.com?v60&title="+title+"&desc="+details; + //https://calendar.google.com/calendar/r/eventedit?text=My+Custom+Event&dates=20180512T230000Z/20180513T030000Z&details=For+details,+link+here:+https://example.com/tickets-43251101208&location=Garage+Boston+-+20+Linden+Street+-+Allston,+MA+02134 + + window.open(linkToOpen); +} + +function toggle(ele, tog = false, inline = true) { + var x = ele; + if (x.style.display === "none") { + if (inline) { + x.style.display = "inline-block"; + } else { + x.style.display = "block"; + } + } else { + x.style.display = "none"; + } + if (tog) { + if (tog.dataset.saved) { + tog.innerHTML = tog.dataset.saved; + delete(tog.dataset.saved); + } else { + tog.dataset.saved = tog.innerHTML; + tog.innerHTML = "Hide This"; + } + } +} + +function toggleByDataset(filter) { + var elements = document.querySelectorAll('[data-cluster="'+filter+'"]'); // ie: .cluster1 + for (var i = 0; i < elements.length; i++) { + elements[i].classList.toggle('hidden'); + } +} + + +var SelectedAudioOutputDevices = false; // session.sink +var SelectedAudioInputDevices = []; // .. +var SelectedVideoInputDevices = []; // .. + +async function enumerateDevices() { + + log("enumerated start"); + + if (typeof navigator.enumerateDevices === "function") { + log("enumerated failed 1"); + return await navigator.enumerateDevices(); + } else if (typeof navigator.mediaDevices === "object" && typeof navigator.mediaDevices.enumerateDevices === "function") { + return await navigator.mediaDevices.enumerateDevices(); + } else { + return await new Promise((resolve, reject) => { + try { + if (window.MediaStreamTrack == null || window.MediaStreamTrack.getSources == null) { + throw new Error(); + } + window.MediaStreamTrack.getSources((devices) => { + resolve(devices + .filter(device => { + return device.kind.toLowerCase() === "video" || device.kind.toLowerCase() === "videoinput"; + }) + .map(device => { + return { + deviceId: device.deviceId != null ? device.deviceId : "" + , groupId: device.groupId + , kind: "videoinput" + , label: device.label + , toJSON: /* istanbul ignore next */ function() { + return this; + } + }; + })); + }); + } catch (e) { + errorlog(e); + } + }); + } +} + +function requestOutputAudioStream() { + try { + //warnlog("GET USER MEDIA"); + return navigator.mediaDevices.getUserMedia({ + audio: true + , video: false + }).then(function(stream1) { // Apple needs thi to happen before I can access EnumerateDevices. + log("get media sources; request audio stream"); + return enumerateDevices().then(function(deviceInfos) { + stream1.getTracks().forEach(function(track) { // We don't want to keep it without audio; so we are going to try to add audio now. + track.stop(); // I need to do this after the enumeration step, else it breaks firefox's labels + }); + const audioOutputSelect = getById('outputSourceScreenshare'); + audioOutputSelect.remove(0); + audioOutputSelect.removeAttribute("onclick"); + + for (let i = 0; i !== deviceInfos.length; ++i) { + const deviceInfo = deviceInfos[i]; + if (deviceInfo == null) { + continue; + } + const option = document.createElement('option'); + option.value = deviceInfo.deviceId; + if (deviceInfo.kind === 'audiooutput') { + const option = document.createElement('option'); + if (audioOutputSelect.length === 0) { + option.dataset.default = true; + } else { + option.dataset.default = false; + } + option.value = deviceInfo.deviceId || "default"; + if (option.value == session.sink) { + option.selected = "true"; + } + option.text = deviceInfo.label || `Speaker ${audioOutputSelect.length + 1}`; + audioOutputSelect.appendChild(option); + } else { + log('Some other kind of source/device: ', deviceInfo); + } + } + }); + }); + } catch (e) { + if (!(session.cleanOutput)) { + if (window.isSecureContext) { + warnUser("An error has occured when trying to access the default audio device. The reason is not known."); + } else if (iOS || iPad) { + warnUser("iOS version 13.4 and up is generally recommended; older than iOS 11 is not supported."); + } else { + warnUser("Error acessing the default audio device.\n\nThe website may be loaded in an insecure context.\n\nPlease see: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia"); + } + } + } +} + + +async function requestAudioStream() { + try { + //warnlog("GET USER MEDIA"); + return await navigator.mediaDevices.getUserMedia({ + audio: true + , video: false + }).then(async function(stream1) { // Apple needs thi to happen before I can access EnumerateDevices. + log("get media sources; request audio stream"); + return await enumerateDevices().then(function async(deviceInfos) { + stream1.getTracks().forEach(function(track) { // We don't want to keep it without audio; so we are going to try to add audio now. + track.stop(); // I need to do this after the enumeration step, else it breaks firefox's labels + }); + log("updating audio"); + const audioInputSelect = getById('audioSourceScreenshare'); + audioInputSelect.remove(1); + audioInputSelect.removeAttribute("onchange"); + + + for (let i = 0; i !== deviceInfos.length; ++i) { + const deviceInfo = deviceInfos[i]; + if (deviceInfo == null) { + continue; + } + const option = document.createElement('option'); + option.value = deviceInfo.deviceId; + if (deviceInfo.kind === 'audioinput') { + option.text = deviceInfo.label || `Microphone ${audioInputSelect.length + 1}`; + audioInputSelect.appendChild(option); + } else { + log('Some other kind of source/device: ', deviceInfo); + } + } + audioInputSelect.style.minHeight = ((audioInputSelect.childElementCount + 1) * 1.15 * 16) + 'px'; + audioInputSelect.style.minWidth = "342px"; + + if (session.audioDevice && (typeof session.audioDevice === "object") && session.audioDevice.length){ + for (let i = 0; i !== audioInputSelect.length; ++i) { + let deviceInfo = audioInputSelect[i]; + if (session.audioDevice.includes(deviceInfo.value)){ + deviceInfo.selected = true; + } else if ((deviceInfo.innerText.replace(/[\W]+/g, "_").toLowerCase().startsWith(session.audioDevice))) { + deviceInfo.selected = true; + } else if ((deviceInfo.innerText.replace(/[\W]+/g, "_").toLowerCase().includes(session.audioDevice))) { + deviceInfo.selected = true; + } + } + } + + }); + }); + } catch (e) { + if (!(session.cleanOutput)) { + if (window.isSecureContext) { + warnUser("An error has occured when trying to access the default audio device. The reason is not known."); + } else if (iOS || iPad) { + warnUser("iOS version 13.4 and up is generally recommended; older than iOS 11 is not supported."); + } else { + warnUser("Error acessing the default audio device.\n\nThe website may be loaded in an insecure context.\n\nPlease see: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia"); + } + } + } +} + +function saveSettings(){ + if (session.store){ + try { + var tmp = {}; + if (SelectedAudioInputDevices){ + tmp.SelectedAudioInputDevices = SelectedAudioInputDevices.filter(n => n); + } + if (session.sink && (session.sink!="default")){ + tmp.SelectedAudioOutputDevices = session.sink; + } else if (!session.sink && SelectedAudioOutputDevices && (SelectedAudioOutputDevices!="default")){ + tmp.SelectedAudioOutputDevices = SelectedAudioOutputDevices; + } + tmp.SelectedVideoInputDevices = SelectedVideoInputDevices; + setStorage("session_store", JSON.stringify(tmp)); + log("Saving settings"); + } catch(e){errorlog(e);} + } +} + +function loadSettings(){ + if (session.store){ + try { + session.store = getStorage("session_store"); + if (session.store){ + session.store = JSON.parse(session.store); + } else { + session.store = {}; + } + + if (session.store && session.store.SelectedAudioOutputDevices){ + if (typeof session.store.SelectedAudioOutputDevices == "string"){ + SelectedAudioOutputDevices = session.store.SelectedAudioOutputDevices; + } else if (typeof session.store.SelectedAudioOutputDevices == "object"){ + if (session.store.SelectedAudioOutputDevices.length){ + SelectedAudioOutputDevices = session.store.SelectedAudioOutputDevices[0]; + } + } + } + if (session.store && session.store.SelectedAudioInputDevices){ + session.store.SelectedAudioInputDevices = session.store.SelectedAudioInputDevices.filter(n => n); + SelectedAudioInputDevices = session.store.SelectedAudioInputDevices; + } + if (session.store && session.store.SelectedVideoInputDevices){ + SelectedVideoInputDevices = session.store.SelectedVideoInputDevices; + } + } catch(e){} + } +} + +function gotDevices(deviceInfos, miconly=false) { + + log("got devices!1"); + log(deviceInfos); + try { + + if (Firefox && !FirefoxEnumerated){ + if (session.streamSrc && session.streamSrc.getTracks().length){ + FirefoxEnumerated=true; + } + } + + var option = document.createElement('input'); + option.type = "checkbox"; + option.value = "ZZZ"; + option.name = "multiselect1"; + option.id = "multiselect1"; + option.style.display = "none"; + option.checked = true; + + + var label = document.createElement('label'); + label.for = option.name; + label.innerHTML = ' No Audio'; + + var listele = document.createElement('li'); + listele.appendChild(option); + listele.appendChild(label); + + const audioInputSelect = document.getElementById('audioSource') || document.getElementById('audioSource3'); + audioInputSelect.innerHTML = ""; + audioInputSelect.appendChild(listele); + + const audioOutputSelect = document.getElementById('outputSource') || document.getElementById('outputSource3'); + audioOutputSelect.innerHTML = ""; + + option.onchange = function(event) { // make sure to clear 'no audio option' if anything else is selected + if (!(getById("multiselect1").checked)){ + getById("multiselect1").checked = true; + } else { + var list = audioInputSelect.querySelectorAll("li>input"); + for (var i = 0; i < list.length; i++) { + if (list[i].id !== "multiselect1") { + list[i].checked = false; + } + } + } + SelectedAudioInputDevices = [event.currentTarget.value]; + saveSettings(); + }; + + const multiselectTrigger = document.getElementById('multiselect-trigger') || document.getElementById('multiselect-trigger3'); + multiselectTrigger.dataset.state = '0'; + multiselectTrigger.classList.add('closed'); + multiselectTrigger.classList.remove('open'); + getById('chevarrow1').classList.add('bottom'); + + const videoSelect = document.getElementById('videoSourceSelect') || document.getElementById('videoSource3'); + const selectors = [videoSelect]; + + const values = selectors.map(select => select.value); + selectors.forEach(select => { + while (select.firstChild) { + select.removeChild(select.firstChild); + } + }); + + + function comp(a, b) { + if (a.kind === 'audioinput') { + return 0; + } else if (a.kind === 'audiooutput') { + return 0; + } + const labelA = a.label.toUpperCase(); + const labelB = b.label.toUpperCase(); + if (labelA > labelB) { + return 1; + } else if (labelA < labelB) { + return -1; + } + return 0; + } + //deviceInfos.sort(comp); // I like this idea, but it messes with the defaults. I just don't know what it will do. + var deviceInfo; + + // This is to hide NDI from default device. NDI Tools fucks up. + var tmp = []; + for (let i = 0; i !== deviceInfos.length; ++i) { + deviceInfo = deviceInfos[i]; + if (!((deviceInfo.kind === 'videoinput') && (deviceInfo.label.toLowerCase().startsWith("ndi") || deviceInfo.label.toLowerCase().startsWith("newtek")))) { + tmp.push(deviceInfo); + } + } + + for (let i = 0; i !== deviceInfos.length; ++i) { + deviceInfo = deviceInfos[i]; + if ((deviceInfo.kind === 'videoinput') && (deviceInfo.label.toLowerCase().startsWith("ndi") || deviceInfo.label.toLowerCase().startsWith("newtek"))) { + tmp.push(deviceInfo); + log("V DEVICE FOUND = " + deviceInfo.label.replace(/[\W]+/g, "_").toLowerCase()); + } + } + deviceInfos = tmp; + + if (typeof session.audioDevice == "object") { // this sorts according to users's manual selection + var matched = []; + var notmatched = []; + for (let i = 0; i !== deviceInfos.length; ++i) { + if (deviceInfos[i].kind === 'audioinput'){ + if (session.audioDevice.includes(deviceInfos[i].deviceId)) { + matched.push(deviceInfos[i]); + } else { + for (var j=0;j { + if (Array.prototype.slice.call(select.childNodes).some(n => n.value === values[selectorIndex])) { + select.value = values[selectorIndex]; + } + }); + + } catch (e) { + errorlog(e); + } +} + + +function getUserMediaVideoParams(resolutionFallbackLevel, isSafariBrowser) { + switch (resolutionFallbackLevel) { + case -1: + return {}; + case 0: + if (isSafariBrowser) { + return { + width: { + min: 360 + , ideal: 1920 + , max: 1920 + } + , height: { + min: 360 + , ideal: 1080 + , max: 1080 + } + }; + } else if (Firefox){ + return { + width: { + ideal: 1920 + } + , height: { + ideal: 1080 + } + }; + } else { + return { + width: { + min: 720 + , ideal: 1920 + , max: 1920 + } + , height: { + min: 720 + , ideal: 1080 + , max: 1920 + } + }; + } + case 1: + if (isSafariBrowser) { + return { + width: { + min: 360 + , ideal: 1280 + , max: 1280 + } + , height: { + min: 360 + , ideal: 720 + , max: 720 + } + }; + } else if (Firefox){ + return { + width: { + ideal: 1280 + } + , height: { + ideal: 720 + } + }; + } else { + return { + width: { + min: 720 + , ideal: 1280 + , max: 1280 + } + , height: { + min: 720 + , ideal: 720 + , max: 1280 + } + }; + } + case 2: + if (isSafariBrowser) { + return { + width: { + min: 640 + } + , height: { + min: 360 + } + }; + } else if (Firefox){ + return { + width: { + ideal: 640 + } + , height: { + ideal: 360 + } + }; + } else { + return { + width: { + min: 240 + , ideal: 640 + , max: 1280 + } + , height: { + min: 240 + , ideal: 360 + , max: 1280 + } + }; + } + case 3: + if (isSafariBrowser) { + return { + width: { + min: 360 + , ideal: 1280 + , max: 1440 + } + }; + } else { + return { + width: { + min: 360 + , ideal: 1280 + , max: 1440 + } + }; + } + case 4: + if (isSafariBrowser) { + return { + height: { + min: 360 + , ideal: 720 + , max: 960 + } + }; + } else { + return { + height: { + ideal: 720 + , max: 960 + } + }; + } + case 5: + if (isSafariBrowser) { + return { + width: { + min: 360 + , ideal: 640 + , max: 1440 + } + , height: { + min: 360 + , ideal: 360 + , max: 720 + } + }; + } else { + return { + width: { + ideal: 640 + , max: 1920 + } + , height: { + ideal: 360 + , max: 1920 + } + }; // same as default, but I didn't want to mess with frameRates until I gave it all a try first + } + case 6: + if (isSafariBrowser) { + return {}; // iphone users probably don't need to wait any longer, so let them just get to it + } else { + return { + width: { + min: 360 + , ideal: 640 + , max: 3840 + } + , height: { + min: 360 + , ideal: 360 + , max: 2160 + } + }; + + } + case 7: + return { // If the camera is recording in low-light, it may have a low frameRate. It coudl also be recording at a very high resolution. + width: { + min: 360 + , ideal: 640 + } + , height: { + min: 360 + , ideal: 360 + } + , }; + + case 8: + return { + width: { + min: 360 + } + , height: { + min: 360 + } + , frameRate: 10 + }; // same as default, but I didn't want to mess with frameRates until I gave it all a try first + case 9: + return { + frameRate: 0 + }; // Some Samsung Devices report they can only support a frameRate of 0. + case 10: + return {} + default: + return {}; + } +} + +function addScreenDevices(device) { + if (device.kind == "audio") { + const audioInputSelect = getById('audioSource3'); + const listele = document.createElement('li'); + listele.style.display = "block"; + + const option = document.createElement('input'); + option.type = "checkbox"; + option.checked = true; + + if (getById('multiselect-trigger3').dataset.state == 0) { + option.style.display = "none"; + } + + option.value = device.id; + option.name = device.label; + option.dataset.type = "screen"; + option.label = device.label; + + const label = document.createElement('label'); + label.for = option.name; + label.innerHTML = " " + device.label; + listele.appendChild(option); + listele.appendChild(label); + + option.onchange = function(event) { // make sure to clear 'no audio option' if anything else is selected + log("change 4644"); + if (!CtrlPressed) { + document.querySelectorAll("#audioSource3 input[type='checkbox']").forEach(function(item) { + if (!item.value){return;} + if (event.currentTarget.value !== item.value) { // this shoulnd't happen, but if it does. + + item.checked = false; + + if (item.dataset.type == "screen") { + item.parentElement.parentElement.removeChild(item.parentElement); + } + + while (SelectedAudioInputDevices.indexOf(item.value) > -1) { + SelectedAudioInputDevices.splice(SelectedAudioInputDevices.indexOf(item.value), 1); + } + + activatedPreview = false; + grabAudio("#audioSource3"); // exclude item.id + + } else { + if (SelectedAudioInputDevices.indexOf(item.value) == -1){ + if (SelectedAudioInputDevices.length && SelectedAudioInputDevices.includes("ZZZ")){ + SelectedAudioInputDevices = []; + } + SelectedAudioInputDevices.push(item.value); + } + + item.checked = true; + activatedPreview = false; + grabAudio("#audioSource3", item.value); // exclude item.id. we will reconnect, even if already connected, as a way to 'reset' a device if it isn't working. + } + }); + } + saveSettings(); + event.stopPropagation(); + return false; + }; + audioInputSelect.appendChild(listele); + getById("audioSourceNoAudio2").checked = false; + + } else if (device.kind == "video") { + const videoSelect = getById('videoSource3'); + //const selectors = [ videoSelect]; + //const values = selectors.map(select => select.value); + const option = document.createElement('option'); + option.value = device.id; + option.text = device.label; + option.selected = "true"; + option.label = device.label; + videoSelect.appendChild(option); + } +} + +var gotDevices2AlreadyRan = false; +function gotDevices2(deviceInfos) { + gotDevices2AlreadyRan=true; + log("got devices!2"); + log(deviceInfos); + getById("multiselect-trigger3").dataset.state = "0"; + getById("multiselect-trigger3").classList.add('closed'); + getById("multiselect-trigger3").classList.remove('open'); + getById("chevarrow2").classList.add('bottom'); + + if (!session.streamSrc){ + checkBasicStreamsExist(); + } + + var knownTrack = false; + + try { + const audioInputSelect = getById('audioSource3'); + const videoSelect = getById('videoSource3'); + const audioOutputSelect = getById('outputSource3'); + const selectors = [videoSelect]; + + [audioInputSelect].forEach(select => { + while (select.firstChild) { + select.removeChild(select.firstChild); + } + }); + + const values = selectors.map(select => select.value); + selectors.forEach(select => { + while (select.firstChild) { + select.removeChild(select.firstChild); + } + }); + + [audioOutputSelect].forEach(select => { + while (select.firstChild) { + select.removeChild(select.firstChild); + } + }); + + var counter = 0; + for (let i = 0; i !== deviceInfos.length; ++i) { + const deviceInfo = deviceInfos[i]; + if (deviceInfo == null) { + continue; + } + + if (deviceInfo.kind === 'audioinput') { + var option = document.createElement('input'); + option.type = "checkbox"; + counter++; + var listele = document.createElement('li'); + listele.style.display = "none"; + + session.streamSrc.getAudioTracks().forEach(function(track) { + if (deviceInfo.label == track.label) { + option.checked = true; + listele.style.display = "inherit"; + } + }); + + option.style.display = "none" + option.value = deviceInfo.deviceId || "default"; + option.name = "multiselecta" + counter; + option.id = "multiselecta" + counter; + option.dataset.label = deviceInfo.label || ("microphone " + ((audioInputSelect.length || 0) + 1)); + + var label = document.createElement('label'); + label.for = option.name; + + label.innerHTML = " " + (deviceInfo.label || ("microphone " + ((audioInputSelect.length || 0) + 1))); + + listele.appendChild(option); + listele.appendChild(label); + audioInputSelect.appendChild(listele); + + option.onchange = function(event) { // make sure to clear 'no audio option' if anything else is selected + log("change 4768"); + if (!(CtrlPressed)) { + document.querySelectorAll("#audioSource3 input[type='checkbox']").forEach(function(item) { + if (event.currentTarget.value !== item.value) { + item.checked = false; + if (item.dataset.type == "screen") { + item.parentElement.parentElement.removeChild(item.parentElement); + } + while (SelectedAudioInputDevices.indexOf(item.value) > -1) { + SelectedAudioInputDevices.splice(SelectedAudioInputDevices.indexOf(item.value), 1); + } + } else { + item.checked = true; + if (SelectedAudioInputDevices.indexOf(event.currentTarget.value) == -1){ + if (SelectedAudioInputDevices.length && SelectedAudioInputDevices.includes("ZZZ")){ + SelectedAudioInputDevices = []; + } + SelectedAudioInputDevices.push(event.currentTarget.value); + } + } + }); + } else { + + if (SelectedAudioInputDevices.indexOf(event.currentTarget.value) == -1){ + if (SelectedAudioInputDevices.length && SelectedAudioInputDevices.includes("ZZZ")){ + SelectedAudioInputDevices = []; + } + SelectedAudioInputDevices.push(event.currentTarget.value); + } + + getById("audioSourceNoAudio2").checked = false; + } + saveSettings(); + }; + + } else if (deviceInfo.kind === 'videoinput') { + var option = document.createElement('option'); + option.value = deviceInfo.deviceId || "default"; + option.text = deviceInfo.label || `camera ${videoSelect.length + 1}`; + try { + if (!knownTrack && session.canvasSource){ + session.canvasSource.srcObject.getVideoTracks().forEach(function(track) { + if (option.text == track.label) { + option.selected = "true"; + knownTrack = true; + } + }); + + } + if (!knownTrack && session.streamSrc){ + session.streamSrc.getVideoTracks().forEach(function(track) { + if (option.text == track.label) { + option.selected = "true"; + knownTrack = true; + } + }); + } + } catch (e) { + errorlog(e); + } + videoSelect.appendChild(option); + + } else if (deviceInfo.kind === 'audiooutput') { + var option = document.createElement('option'); + if (audioOutputSelect.length === 0) { + option.dataset.default = true; + } else { + option.dataset.default = false; + } + option.value = deviceInfo.deviceId || "default"; + if (option.value == session.sink) { + option.selected = "true"; + } else if (!session.sink && SelectedAudioOutputDevices && (SelectedAudioOutputDevices == option.value)){ + option.selected = "true"; + session.sink = option.value; // added 8-dec-22, as the director's saved mic wasn't applying otherwise. + } + option.text = deviceInfo.label || `Speaker ${audioOutputSelect.length + 1}`; + audioOutputSelect.appendChild(option); + + } else { + log('Some other kind of source/device: ', deviceInfo); + } + } + + if (Firefox && !session.mobile){ + var option = document.createElement('option'); + option.value = "others"; + option.text = "Show more options"; + audioOutputSelect.appendChild(option); + } + + + if (audioOutputSelect.childNodes.length == 0) { + var option = document.createElement('option'); + option.value = "default"; + option.text = "System Default"; + audioOutputSelect.appendChild(option); + } + + if (videoSelect.childNodes.length <= 1) { + getById("flipcamerabutton").style.display = "none"; // don't show the camera cycle button + getById("flipcamerabutton").dataset.maxndex = videoSelect.childNodes.length; + } else { + getById("flipcamerabutton").style.display = "unset"; + getById("flipcamerabutton").dataset.maxIndex = videoSelect.childNodes.length; + } + + //////////// + session.streamSrc.getAudioTracks().forEach(function(track) { // add active ScreenShare audio tracks to the list + log("Checking for screenshare audio"); + var matched = false; + for (var i = 0; i !== deviceInfos.length; ++i) { + var deviceInfo = deviceInfos[i]; + if (deviceInfo == null) { + continue; + } + log("---"); + if (track.label == deviceInfo.label) { + matched = true; + continue; + } + } + if (matched == false) { // Not a gUM device + var listele = document.createElement('li'); + listele.style.display = "block"; + var option = document.createElement('input'); + option.type = "checkbox"; + option.value = track.id; + option.checked = true; + option.style.display = "none"; + option.name = track.label; + option.label = track.label; + option.dataset.type = "screen"; + var label = document.createElement('label'); + label.for = option.name; + label.innerHTML = " " + track.label; + listele.appendChild(option); + listele.appendChild(label); + option.onchange = function(event) { // make sure to clear 'no audio option' if anything else is selected + log("change 4873"); + var trackid = null; + if (!(CtrlPressed)) { + + document.querySelectorAll("#audioSource3 input[type='checkbox']").forEach(function(item) { + if (event.currentTarget.value !== item.value) { // this shoulnd't happen, but if it does. + item.checked = false; + if (item.dataset.type == "screen") { + item.parentElement.parentElement.removeChild(item.parentElement); + } + } else { + event.currentTarget.checked = true; + trackid = item.value; + } + }); + } else { + //getById("audioSourceNoAudio2").checked=false; + if (event.currentTarget.dataset.type == "screen") { + event.currentTarget.parentElement.parentElement.removeChild(event.currentTarget.parentElement); + } + } + activatedPreview = false; + grabAudio("#audioSource3", trackid); // exclude item.id. + event.stopPropagation(); + return false; + }; + audioInputSelect.appendChild(listele); + } + }); + + /////////// no video option + var optionss = false; + if (screensharesupport) { + optionss = document.createElement('option'); + optionss.text = "Screen Share (replace camera)"; + optionss.value = "XXX"; + videoSelect.appendChild(optionss); // NO AUDIO OPTION + } + + var option = document.createElement('option'); // no video + option.text = "Disable Video"; + option.value = "ZZZ"; + videoSelect.appendChild(option); + + if (session.streamSrc.getVideoTracks().length == 0) { + option.selected = "true"; + } else if (knownTrack == false) { + var option = document.createElement('option'); // no video + option.text = session.streamSrc.getVideoTracks()[0].label; + option.value = "YYY"; + videoSelect.appendChild(option); + option.selected = "true"; + } + + if (optionss) { + optionss.lastSelected = videoSelect.selectedIndex; + } + + + videoSelect.onchange = function(event) { + try { + if (event.target.options[event.target.options.selectedIndex].value === "XXX") { + + videoSelect.selectedIndex = event.target.options[event.target.options.selectedIndex].lastSelected; + if (session.screenShareState == false) { + toggleScreenShare(); + } else { + toggleScreenShare(true); + } + return; + } + } catch (e) {} + activatedPreview = false; + grabVideo(session.quality, "videosource", "select#videoSource3"); + + if (!(getById('audioSource3').querySelectorAll("input[data-type='screen']").length)){ + if (session.screenShareState){ + session.screenShareState = false; + pokeIframeAPI('screen-share-state', session.screenShareState, null, session.streamID); + notifyOfScreenShare(); + //session.refreshScale(); + } + getById("screensharebutton").classList.remove("green"); + getById("screensharebutton").ariaPressed = "false"; + } + + }; + + ///////////// /// NO AUDIO appended option + + + var option = document.createElement('input'); + option.type = "checkbox"; + option.value = "ZZZ"; + option.style.display = "none" + option.id = "audioSourceNoAudio2"; + + var label = document.createElement('label'); + label.for = option.name; + label.innerHTML = " No Audio"; + var listele = document.createElement('li'); + + if (session.streamSrc.getAudioTracks().length == 0) { + option.checked = true; + } else { + listele.style.display = "none"; + option.checked = false; + } + option.onchange = function(event) { // make sure to clear 'no audio option' if anything else is selected + log("change 4938"); + if (!(CtrlPressed)) { + document.querySelectorAll("#audioSource3 input[type='checkbox']").forEach(function(item) { + if (event.currentTarget.value !== item.value) { + item.checked = false; + if (item.dataset.type == "screen") { + item.parentElement.parentElement.removeChild(item.parentElement); + } + + while (SelectedAudioInputDevices.indexOf(item.value) > -1) { + SelectedAudioInputDevices.splice(SelectedAudioInputDevices.indexOf(item.value), 1); + } + } else { + item.checked = true; + if (SelectedAudioInputDevices.indexOf(event.currentTarget.value) == -1){ + if (SelectedAudioInputDevices.length && SelectedAudioInputDevices.includes("ZZZ")){ + SelectedAudioInputDevices = []; + } + SelectedAudioInputDevices.push(event.currentTarget.value); + } + } + }); + } else { + document.querySelectorAll("#audioSource3 input[type='checkbox']").forEach(function(item) { + if (event.currentTarget.value === item.value) { + event.currentTarget.checked = true; + if (SelectedAudioInputDevices.indexOf(event.currentTarget.value) == -1){ + if (SelectedAudioInputDevices.length && SelectedAudioInputDevices.includes("ZZZ")){ + SelectedAudioInputDevices = []; + } + SelectedAudioInputDevices.push(event.currentTarget.value); + } + } else { + item.checked = false; + if (item.dataset.type == "screen") { + item.parentElement.parentElement.removeChild(item.parentElement); + } + while (SelectedAudioInputDevices.indexOf(item.value) > -1) { + SelectedAudioInputDevices.splice(SelectedAudioInputDevices.indexOf(item.value), 1); + } + } + + }); + } + saveSettings(); + }; + listele.appendChild(option); + listele.appendChild(label); + audioInputSelect.appendChild(listele); + + //////////// + + + //selectors.forEach((select, selectorIndex) => { + // if (Array.prototype.slice.call(select.childNodes).some(n => n.value === values[selectorIndex])) { + // select.value = values[selectorIndex]; + // } + //}); + + audioInputSelect.onchange = function() { + log("Audio OPTION HAS CHANGED? 2"); + activatedPreview = false; + setTimeout(function(){ + grabAudio("#audioSource3"); + },10) + }; + + getById("refreshVideoButton").onclick = function() { + refreshVideoDevice(); + }; + + if (Firefox && !session.mobile){ + audioOutputSelect.onclick = function() { + log("audioOutputSelect.onclick = function() {"); + if (audioOutputSelect.options[audioOutputSelect.selectedIndex].value === "others"){ + log("Trying to increase the output device list"); + navigator.mediaDevices.selectAudioOutput().then((device) => { + + if (device.kind == "audiooutput"){ + session.sink = device.deviceId; + + try { + var matched = false; + audioOutputSelect.childNodes.forEach(ele =>{ + if (ele.value === device.deviceId){ + matched = true; + ele.selected = true; + } + }) + if (!matched){ + var option = document.createElement('option'); + option.value = device.deviceId; + option.text = device.label; + audioOutputSelect.appendChild(option); + option.selected = true; + } + + saveSettings(); // we're saving because there was an explicit action to change devices + } catch(e){errorlog(e);} + + if (!session.sink){return;} // Not sure this would ever happen, but whatever. + + resetupAudioOut(); // we'll probalby use session.sink, since outputSelect3 doesn't exist. + } + + }); + } + } + } + + audioOutputSelect.onchange = function() { + log("audioOutputSelect.onchange = function() {"); + + if (iOS || iPad) { + return; + } + + if (Firefox && !session.mobile){ + if (audioOutputSelect.options[audioOutputSelect.selectedIndex].value === "others"){ // we handle this elsewhere + return; + } + } + + try { + session.sink = audioOutputSelect.options[audioOutputSelect.selectedIndex].value; + saveSettings(); + } catch (e) { + errorlog(e); + } + if (!session.sink){return;} + + resetupAudioOut(); + + log("done audioOutputSelect.onchange = function() {"); + } + + } catch (e) { + errorlog(e); + } +} + +function refreshVideoDevice(){ + if (session.screenShareState) { + log("can't refresh a screenshare"); + return; + } + log("video source changed"); + activatedPreview = false; + grabVideo(session.quality, "videosource", "select#videoSource3"); +} + +function gotDevicesRemote(deviceInfos, UUID) { + + try { + if (document.getElementById("remoteVideoSelect_"+UUID)){ + var videoSelect = document.getElementById("remoteVideoSelect_"+UUID); + var length = videoSelect.options.length; + for (i = length-1; i >= 0; i--) { + videoSelect.options[i] = null; + } + } else { + var videoSelect = document.createElement("select"); + videoSelect.id = "remoteVideoSelect_"+UUID; + + + + videoSelect.onchange = function(){ + if (session.rpcs[UUID].stats.info && session.rpcs[UUID].stats.info.consent){ + getById("requestVideoDevice_"+UUID).innerHTML = ' apply'; + getById("requestVideoDevice_"+UUID).title = "This will update the remote device to the selected one"; + } else { + getById("requestVideoDevice_"+UUID).innerHTML = ' request'; + getById("requestVideoDevice_"+UUID).title = "This will ask the remote guest for permission to change"; + } + } + + var buttonGO = document.createElement("button"); + buttonGO.innerHTML = ' refresh'; + buttonGO.title = "This will refresh the current device"; + buttonGO.id = "requestVideoDevice_"+UUID; + buttonGO.onclick = function(){ + var data = {} + data.changeCamera = videoSelect.value; + data.UUID = UUID; + session.sendRequest(data, UUID); // Viewer is requesting the PUBLISHER + }; + + var videoSelectDiv = document.createElement("div"); + query("#container_"+UUID+" .advancedVideoSettings").appendChild(videoSelectDiv); + videoSelectDiv.appendChild(videoSelect); + videoSelectDiv.appendChild(buttonGO); + } + + if (document.getElementById("remoteAudioSelect_"+UUID)){ + log("remoteAudioSelect_ "); + var audioSelect = document.getElementById("remoteAudioSelect_"+UUID); + var length = audioSelect.options.length; + for (i = length-1; i >= 0; i--) { + audioSelect.options[i] = null; + } + } else { + var audioSelect = document.createElement("select"); + audioSelect.id = "remoteAudioSelect_"+UUID; + + + audioSelect.onchange = function(){ + log("ON CHANGE"); + if (session.rpcs[UUID].stats.info && session.rpcs[UUID].stats.info.consent){ + getById("requestAudioDevice_"+UUID).innerHTML = ' apply'; + getById("requestAudioDevice_"+UUID).title = "This will update the remote device to the selected one"; + } else { + getById("requestAudioDevice_"+UUID).innerHTML = ' request'; + getById("requestAudioDevice_"+UUID).title = "This will ask the remote guest for permission to change"; + } + } + + var buttonGO = document.createElement("button"); + buttonGO.innerHTML = ' refresh'; + // buttonGO.style = "padding: 5px;"; + buttonGO.title = "This will refresh the current device"; + buttonGO.id = "requestAudioDevice_"+UUID; + + buttonGO.onclick = function(){ + var data = {} + data.changeMicrophone = audioSelect.value; + data.UUID = UUID; + session.sendRequest(data, UUID); // Viewer is requesting the PUBLISHER + } + var audioSelectDiv = document.createElement("div"); + query("#container_"+UUID+" .advancedAudioSettings").appendChild(audioSelectDiv); + audioSelectDiv.appendChild(audioSelect); + audioSelectDiv.appendChild(buttonGO); + + } + + if (document.getElementById("remoteAudioOutputSelect_"+UUID)){ + var audioOutputSelect = document.getElementById("remoteAudioOutputSelect_"+UUID); + var length = audioOutputSelect.options.length; + for (i = length-1; i >= 0; i--) { + audioOutputSelect.options[i] = null; + } + } else { + var audioOutputSelect = document.createElement("select"); + audioOutputSelect.id = "remoteAudioOutputSelect_"+UUID; + + audioOutputSelect.onchange = function(){ + if (session.rpcs[UUID].stats.info && session.rpcs[UUID].stats.info.consent){ + getById("requestAudioOutputDevice_"+UUID).innerHTML = ' apply'; + getById("requestAudioOutputDevice_"+UUID).title = "This will update the remote device to the selected one"; + } else { + getById("requestAudioOutputDevice_"+UUID).innerHTML = ' request'; + getById("requestAudioOutputDevice_"+UUID).title = "This will ask the remote guest for permission to change"; + } + } + + var buttonGO = document.createElement("button"); + buttonGO.innerHTML = ' refresh'; + buttonGO.title = "This will refresh the current device"; + buttonGO.id = "requestAudioOutputDevice_"+UUID; + buttonGO.onclick = function(){ + var data = {} + data.changeSpeaker = audioOutputSelect.value; + data.UUID = UUID; + session.sendRequest(data, UUID); // Viewer is requesting the PUBLISHER + } + + var audioOutputSelectContainer = document.createElement("div"); + query("#container_"+UUID+" .advancedAudioSettings").appendChild(audioOutputSelectContainer); + audioOutputSelectContainer.appendChild(audioOutputSelect); + audioOutputSelectContainer.appendChild(buttonGO); + } + + var matched = false; + for (let i = 0; i !== deviceInfos.length; ++i) { + const deviceInfo = deviceInfos[i]; + if (deviceInfo == null) { + continue; + } + if (deviceInfo.kind === 'videoinput'){ + const option = document.createElement('option'); + option.value = deviceInfo.deviceId || "default"; + option.text = deviceInfo.label || `camera ${videoSelect.length + 1}`; + if (getById("remoteVideoLabel_"+UUID).innerText == option.text){ + option.selected = "true"; + matched = true; + } + videoSelect.appendChild(option); + + } else if (deviceInfo.kind === 'audioinput'){ + const option = document.createElement('option'); + option.value = deviceInfo.deviceId || "default"; + option.text = deviceInfo.label || `microphone ${audioSelect.length + 1}`; + if (getById("remoteAudioLabel_"+UUID).innerText == option.text){ + option.selected = "true"; + } + audioSelect.appendChild(option); + + } else if (deviceInfo.kind === 'audiooutput'){ + const option = document.createElement('option'); + option.value = deviceInfo.deviceId || "default"; + option.text = deviceInfo.label || `microphone ${audioOutputSelect.length + 1}`; + if (getById("remoteAudioOutputSelect_"+UUID).innerText == option.text){ + option.selected = "true"; + } + audioOutputSelect.appendChild(option); + } + } + + + if (!matched){ + getById("requestVideoDevice_"+UUID).innerHTML = ' request'; + getById("requestVideoDevice_"+UUID).title = "This will ask the remote guest for permission to change"; + } else { + getById("requestVideoDevice_"+UUID).innerHTML = ' refresh'; + getById("requestVideoDevice_"+UUID).title = "This will reconnect the guest's active video source."; + } + + + } catch(e){errorlog(e);} + + pokeIframeAPI("remote-devices-info", deviceInfos, UUID); +} + +var timeoutTone = false; +function playtone(screen = false, tonename="testtone") { + if (timeoutTone){return;} + setTimeout(function(){ + timeoutTone = false; + },500); + timeoutTone = true; + + if (iOS || iPad) { + // try{ + // session.audioContext.resume(); + // } catch(e){errorlog(e);} + var toneEle = document.getElementById(tonename); + if (toneEle) { + toneEle.mute + toneEle.play(); + } + return; + } + + if (screen) { + try{ + var outputSelect = getById('outputSourceScreenshare'); + if (outputSelect){ + session.sink = outputSelect.options[outputSelect.selectedIndex].value; + saveSettings(); + } + } catch(e){errorlog(e);} + } + + var toneEle = document.getElementById(tonename); + if (toneEle) { + if (session.sink) { + try { + toneEle.setSinkId(session.sink).then(() => { // TODO: iOS doens't support sink. Needs to bypass if IOS + log("changing audio sink:" + session.sink); + toneEle.play(); + }).catch(error => { + warnlog(error); + }); + } catch (e) { + warnlog(e); // firefox? + toneEle.play(); + } + } else { + toneEle.play(); + } + } +} + +function showNotification(title, body="") { + if (!Notification){return;} + if (Notification.permission !== 'granted') { + Notification.requestPermission(); + } else { + let icon = '/media/old_icon.png'; //this is a large image may take more time to show notifiction, replace with small size icon + + let notification = new Notification(title, { body, icon }); + + notification.onclick = () => { + notification.close(); + window.parent.focus(); + } + } +} + +async function getAudioOnly(selector, trackid = null, override = false) { + var audioSelect = document.querySelector(selector).querySelectorAll("input"); + var audioList = []; + var streams = []; + log("getAudioOnly()"); + for (var i = 0; i < audioSelect.length; i++) { + if (audioSelect[i].value == "ZZZ") { + continue; + } else if (trackid == audioSelect[i].value) { // skip already excluded + continue; + } else if ("screen" == audioSelect[i].dataset.type) { // skip already excluded ---------- !!!!!! DOES THIS MAKE SENSE? TODO: CHECK + continue; + } else if (audioSelect[i].checked) { + log(audioSelect[i]); + audioList.push(audioSelect[i]); + } + } + + for (var i = 0; i < audioList.length; i++) { + + if ((session.echoCancellation !== false) && (session.autoGainControl !== false) && (session.noiseSuppression !== false)) { + var constraint = { + audio: { + deviceId: { + exact: audioList[i].value + } + } + }; + } else { // Just trying to avoid problems with some systems that don't support these features + var constraint = { + audio: { + deviceId: { + exact: audioList[i].value + } + } + }; + if (session.echoCancellation === false) { + constraint.audio.echoCancellation = false; + } else { + constraint.audio.echoCancellation = true; + } + if (session.autoGainControl === false) { + constraint.audio.autoGainControl = false; + } else { + constraint.audio.autoGainControl = true; + } + if (session.noiseSuppression === false) { + constraint.audio.noiseSuppression = false; + } else { + constraint.audio.noiseSuppression = true; + } + } + constraint.video = false; + if (override !== false) { + log("Override true"); + if (override.audio && override.audio.deviceId){ + if (audioList[i].value == override.audio.deviceId){ + constraint = override; + } else { + // not the device we want to hack. + } + } else { + constraint = override; + } + } + + if (audioList[i].value && SelectedAudioInputDevices){ + if (SelectedAudioInputDevices.indexOf(audioList[i].value) === -1) { + if (SelectedAudioInputDevices.length && SelectedAudioInputDevices.includes("ZZZ")){ + SelectedAudioInputDevices = []; + } + SelectedAudioInputDevices.push(audioList[i].value); + } + } + + if (session.audioInputChannels) { + if (constraint.audio === true) { + constraint.audio = {}; + constraint.audio.channelCount = session.audioInputChannels; + } else if (constraint.audio) { + constraint.audio.channelCount = session.audioInputChannels; + } + } + + + if (session.micSampleRate){ + if (constraint.audio === true) { + constraint.audio = {}; + constraint.audio.sampleRate = parseInt(session.micSampleRate); + } else if (constraint.audio) { + constraint.audio.sampleRate = parseInt(session.micSampleRate); + } + } + + if (session.micSampleSize){ + if (constraint.audio === true) { + constraint.audio = {}; + constraint.audio.sampleSize = parseInt(session.micSampleSize); + } else if (constraint.audio) { + constraint.audio.sampleSize = parseInt(session.micSampleSize); + } + } + + log("CONSTRAINT"); + log(constraint); + + var stream = await navigator.mediaDevices.getUserMedia(constraint).then(function(stream2) { + log("get audio sucecss"); + pokeIframeAPI("local-microphone-event"); + return stream2; + }).catch(function(err) { + warnlog(err); + if (!(session.cleanOutput)) { + if (override !== false) { + if (err.name) { + if (err.constraint) { + warnUser(err['name'] + ": " + err['constraint']); + } + } + } + } + return false; + }); // More error reporting maybe? + if (stream) { + streams.push(stream); + } + } + + return streams; +} + +function applyMirror(mirror) { // true unmirrors as its already mirrored + if (!session.videoElement){return;} + try { + var transFlip = ""; + var transNorm = ""; + if (document.getElementById('videosource') && (session.windowed)) { + transFlip = " translate(0, 50%)"; + transNorm = " translate(0, -50%)"; + } + + if (session.mirrored == 2) { + mirror = true; + } else if (session.mirrored === 0) { + mirror = true; + } + + if (!session.videoElement.style){ + session.videoElement.style = ""; + } + + if (session.permaMirrored){ + mirror = !mirror; + } + + if (mirror) { + if (session.mirrored && session.flipped) { + session.videoElement.style.transform = "scaleX(-1) scaleY(-1)" + transFlip; + session.videoElement.classList.add("mirrorControl"); + } else if (session.mirrored) { + session.videoElement.style.transform = "scaleX(-1)" + transNorm; + session.videoElement.classList.add("mirrorControl"); + } else if (session.flipped) { + session.videoElement.style.transform = "scaleY(-1) scaleX(1)" + transFlip; + session.videoElement.classList.remove("mirrorControl"); + } else { + session.videoElement.style.transform = "scaleX(1)" + transNorm; + session.videoElement.classList.remove("mirrorControl"); + } + } else { + if (session.mirrored && session.flipped) { + session.videoElement.style.transform = "scaleX(1) scaleY(-1)" + transFlip; + session.videoElement.classList.remove("mirrorControl"); + } else if (session.mirrored) { + session.videoElement.style.transform = "scaleX(1)" + transNorm; + session.videoElement.classList.remove("mirrorControl"); + } else if (session.flipped) { + session.videoElement.style.transform = "scaleY(-1) scaleX(-1)" + transFlip; + session.videoElement.classList.add("mirrorControl"); + } else { + session.videoElement.style.transform = "scaleX(-1)" + transNorm; + session.videoElement.classList.add("mirrorControl"); + } + } + + var rotate = 0; + if (session.forceRotate!==false){ + if (session.rotate){ + rotate = (session.forceRotate * -1) + parseInt(session.rotate); + } else { + rotate = session.forceRotate * -1; + } + if (session.forceRotate){ + rotate+=180; + } + } else { + rotate = session.rotate; + } + + if (rotate && (rotate>=360)){ + rotate-=360; + } + + session.videoElement.rotated = rotate; + + if (document.getElementById("previewWebcam") || document.getElementById('videosource')){ + var eleName = document.getElementById("previewWebcam") || document.getElementById('videosource'); + if (rotate){ + if (eleName.style.transform){ + eleName.style.transform += " rotate("+rotate+"deg)"; + } else { + eleName.style.transform = "rotate("+rotate+"deg)"; + } + eleName.classList.add("rotate"); + } else { + eleName.classList.remove("rotate"); + } + } else if (document.getElementById("container")){ + if (rotate==0){ + document.getElementById("container").classList.remove("rotate"); + document.getElementById("container").style.transform = "unset"; + document.getElementById("container").style.transformOrigin = "unset"; + } else { + document.getElementById("container").style.transform = "rotate("+rotate+"deg)"; + } + } else if (document.getElementById("minipreview")){ + var eleName = document.getElementById("minipreview"); + if (rotate==90 ){ + eleName.style.transform = "rotate(90deg)"; + eleName.style.transformOrigin = "50% 100%"; + eleName.style.height = eleName.style.width; + eleName.style.width = "unset"; + } else if (session.videoElement.rotated==270){ + eleName.style.transform = "rotate(270deg)"; + eleName.style.transformOrigin = "50% 100%"; + eleName.style.width = "unset"; + eleName.style.height = eleName.style.width; + } else if (session.videoElement.rotated==180){ + eleName.style.transform = "rotate(180deg)"; + eleName.style.transformOrigin = "unset"; + } else { + eleName.classList.remove("rotate"); + eleName.style.transform = "unset"; + eleName.style.transformOrigin = "unset"; + } + } + // if not one of these, then it's going to be handled by the automixer automatically for us. + + } catch(e){errorlog(e);} +} + + +function applyMirrorGuest(mirror, videoElement) { // true unmirrors as its already mirrored + try { + if (mirror) { + videoElement.style.transform = "scaleX(-1)"; + videoElement.classList.add("mirrorControl"); + } else { + videoElement.style.transform = "scaleX(1)"; + videoElement.classList.remove("mirrorControl"); + } + } catch(e){errorlog(e);} +} + +function cleanupMediaTracks() { + getUserMediaRequestID += 1; + try { + + if (session.streamSrcClone){ + session.streamSrcClone.getTracks().forEach(function(track) { + session.streamSrcClone.removeTrack(track); + track.stop(); + log("stopping old track"); + }); + } + if (session.streamSrc) { + session.streamSrc.getTracks().forEach(function(track) { + session.streamSrc.removeTrack(track); + track.stop(); + log("stopping old track"); + }); + } else { + checkBasicStreamsExist(); + } + if (session.videoElement && session.videoElement.srcObject) { + session.videoElement.srcObject.getTracks().forEach(function(track) { + session.videoElement.srcObject.removeTrack(track); + track.stop(); + log("stopping old track"); + }); + } else { + session.videoElement.srcObject = createMediaStream(); + } + activatedPreview = false; + } catch (e) { + errorlog(e); + } +} + +/// Detect system changes; handle change or use for debugging +var lastAudioDevice = null; +var lastVideoDevice = null; +var lastPlaybackDevice = null; + +var audioReconnectTimeout = null; +var videoReconnectTimeout = null; +var grabDevicesTimeout = null; +var playbackReconnectTimeout = null; + +function reconnectDevices(event) { /// TODO: Perhaps change this to only if there is a DISCONNECT; rather than ON NEW DEVICE? + + try { + if (session.firstPlayTriggered && (session.audioCtx.state == "suspended")){ + session.audioCtx.resume(); + } + } catch(e){warnlog("session.audioCtx.resume(); failed");} + + warnlog("A media device has changed"); + + if (iOS || iPad) { // consider adding this back, but if no problem, whatever. + return; + } + + if (document.getElementById("previewWebcam")) { // rest of the code isn't setup to support pre-connection setup. + clearTimeout(playbackReconnectTimeout); + playbackReconnectTimeout = setTimeout(function(){ + if (document.getElementById("previewWebcam")){ + enumerateDevices().then(gotDevices).then(function(){ + if (document.getElementById("previewWebcam")){ + resetupAudioOut(document.getElementById("previewWebcam")); + } + }); + } + }, 1000); + return; + } + + try { + if (!session.streamSrc){ + checkBasicStreamsExist(); + } else { + session.streamSrc.getTracks().forEach(function(track) { + if (track.readyState == "ended") { + if (track.kind == "audio") { + lastAudioDevice = track.label; + } else if (track.kind == "video") { + lastVideoDevice = track.label; + } + session.streamSrc.removeTrack(track); + log("remove ended old track"); + } + }); + + if (session.streamSrcClone){ + session.streamSrcClone.getTracks().forEach(function(track) { + if (track.readyState == "ended") { + session.streamSrcClone.removeTrack(track); + track.stop(); + } + }); + } + + if (session.videoElement && session.videoElement.srcObject){ + session.videoElement.srcObject.getTracks().forEach(function(track) { + if (track.readyState == "ended") { + session.videoElement.srcObject.removeTrack(track); + log("remove ended old track"); + } + }); + } + } + /* } else { + clearTimeout(playbackReconnectTimeout); + playbackReconnectTimeout = setTimeout(function() { + enumerateDevices().then(gotDevices2).then(function() { + resetupAudioOut(); + }); + }, 1000); + return; + } */ + } catch (e) { + errorlog(e); + } + + clearTimeout(audioReconnectTimeout); + audioReconnectTimeout = null; + if (lastAudioDevice) { + audioReconnectTimeout = setTimeout(function() { // only reconnect same audio device. If reconnected, clear the disconnected flag. + enumerateDevices().then(gotDevices2).then(function() { + // TODO: check to see if any audio is connected? + var streamConnected = false; + var audioSelect = getById("audioSource3").querySelectorAll("input"); + for (var i = 0; i < audioSelect.length; i++) { + if (audioSelect[i].value == "ZZZ") { + continue; + } else if (audioSelect[i].checked) { + log("checked"); + streamConnected = true; + break; + } + } + + if (!streamConnected) { + for (var i = 0; i < audioSelect.length; i++) { + if (audioSelect[i].value == "ZZZ") { + continue; + } + if (lastAudioDevice == audioSelect[i].dataset.label) { // if the last disconnected device matches. + audioSelect[i].checked = true; + streamConnected = true; + lastAudioDevice = null; + warnlog("DISCONNECTED AUDIO DEVICE RECONNECTED"); + break; + } + } + } + activatedPreview = false; + grabAudio("#audioSource3"); + setTimeout(function() { + enumerateDevices().then(gotDevices2).then(function() { + }); + }, 1000); + }); + }, 2000); + } + + clearTimeout(videoReconnectTimeout); // only reconnect same video device. + videoReconnectTimeout = null; + if (lastVideoDevice) { + videoReconnectTimeout = setTimeout(function() { + enumerateDevices().then(gotDevices2).then(function() { + var streamConnected = false; + var videoSelect = getById("videoSource3"); + errorlog(videoSelect.value); + + if (videoSelect.value == "ZZZ") { + for (var i = 0; i < videoSelect.options.length; i++) { + try { + if (videoSelect.options[i].innerHTML == lastVideoDevice) { + videoSelect.options[i].selected = "true"; + streamConnected = true; + lastVideoDevice = null; + break; + } + } catch (e) { + errorlog(e); + } + } + } + + if (streamConnected) { + activatedPreview = false; + grabVideo(session.quality, "videosource", "select#videoSource3"); + setTimeout(function() { + enumerateDevices().then(gotDevices2).then(function() {}); + }, 1000); + } + + }); + }, 2000); + } + clearTimeout(playbackReconnectTimeout); + playbackReconnectTimeout = setTimeout(function() { + enumerateDevices().then(gotDevices2).then(function() { + resetupAudioOut(); + }); + }, 1000); +} + +var vingesterFixed = false; +function resetupAudioOut(ele=false, forceReset=false) { // this re-sets ALL output devices / sources + log("resetupAudioOut"); + if (iOS || iPad || SafariVersion || (ChromiumVersion && session.mobile)) { // TODO : TEST TO SEE IF THIS WORKS WITH SAFARI? it might. + if (ele){return;} + for (var UUID in session.rpcs) { + if (session.rpcs[UUID].videoElement){ + try{ + session.rpcs[UUID].videoElement.pause().then(() => { + setTimeout(function(uuid) { + log("win"); + try{ + session.rpcs[uuid].videoElement.play().then(() => { + // updateIncomingAudioElement(uuid); // DO NOT DO THIS; it would cause a loop, as this updateIncomingAudioElement calls reseet + log("toggle pause/play"); + }); + } catch(e){errorlog(e);} + }, 0, UUID); + }).catch(errorlog); + } catch(e){warnlog(e);} + } + } + return; + } + + //if (isVingester){return;} + + var outputSelect = document.getElementById("outputSource") || document.getElementById("outputSource3") || false; + var sinkSet = false; + + try { // function tries to get vingester working, by listening for its first tweak. + if (!session.sink && !vingesterFixed && document.getElementById("testtone") && document.getElementById("testtone").sinkId && isVingester){ + vingesterFixed = true; // only set the session.sink one time, as vingester on should be needing to set it one time. + session.sink = document.getElementById("testtone").sinkId; + } + } catch(e){ + errorlog(e); + } + + if (outputSelect && outputSelect.options && outputSelect.options.length){ + for (var i = 0; i < outputSelect.options.length; i++) { + if (outputSelect.options[i].value == session.sink) { + outputSelect.options[i].selected = "true"; + sinkSet = true; + } + } + if (sinkSet == false) { + if (outputSelect.options[0]) { + outputSelect.options[0].selected = "true"; + sinkSet = outputSelect.value; + } + } else { + sinkSet = session.sink; + } + } else { + sinkSet = session.sink; + } + + if (ele){ + try { + var eleSink = sinkSet; + if (ele.manualSink){ + eleSink = ele.manualSink; + log("Manual Sink Identified"); + } + if (eleSink){ + if (forceReset){ + ele.setSinkId("default").then(() => { + ele.setSinkId(eleSink).then(() => { + log("New Output Device"); + }).catch(error => { + if (!Firefox){ + warnlog(error); + } + // TODO: If error, then see if I need to add mic support, and grab it if needed. + }); + }).catch(error => { + if (!Firefox){ + errorlog(error); + } + ele.setSinkId(eleSink).then(() => { + log("New Output Device"); + }).catch(error => { + if (!Firefox){ + warnlog(error); + } + // TODO: If error, then see if I need to add mic support, and grab it if needed. + }); + }); + } + + ele.setSinkId(eleSink).then(() => { + log("New Output Device for self-preview"); + }).catch(error => { + if (!Firefox){ + warnlog("Need to add mic support, and grab it if needed."); + warnlog(error); + } + // TODO: If error, then see if I need to add mic support, and grab it if needed. + }); + } + } catch(e){warnlog("can't use setsink");} + log("audio sink: "+eleSink); + return; + } + + if (session.videoElement){ // this would be a preview or videosource + try { + var eleSink = sinkSet; + if (session.videoElement.manualSink){ + eleSink = session.videoElement.manualSink; + } + if (eleSink){ + session.videoElement.setSinkId(eleSink).then(() => { + log("New Output Device for self-preview"); + }).catch(error => { + if (!Firefox){ + warnlog(error); + } + // TODO: If error, then see if I need to add mic support, and grab it if needed. + }); + } + } catch(e){warnlog("can't use setsink");} + } + + for (UUID in session.rpcs) { + try{ + if (session.rpcs[UUID].videoElement){ + var eleSink = sinkSet; + if (session.rpcs[UUID].videoElement.manualSink){ + eleSink = session.rpcs[UUID].videoElement.manualSink; + } + if (eleSink){ + session.rpcs[UUID].videoElement.setSinkId(eleSink).then(() => { + log("New Output Device for: " + UUID); + }).catch(error => { + if (!Firefox){ + warnlog(error); + } + // TODO: If error, then see if I need to add mic support, and grab it if needed. + }); + } + } + } catch(e){warnlog(e);} + } + log("audio sink 2: "+eleSink); +} + + +function lyraDecode(receiver){ // WIP. does not work + warnlog("lyraDecode"); + const receiverStreams = receiver.createEncodedStreams(); + var xx = receiverStreams.readable; + xx.pipeThrough(new TransformStream()); + xx.pipeTo(receiverStreams.writable); +} +function lyraEncode(UUID){ // WIP. does not work + warnlog("lyraEncode"); + var senders = getSenders2(UUID); + senders.forEach((sender) => { + if (!sender.track){return;} + if (sender.track.kind === "audio") { + try { + var senderStreams = sender.createEncodedStreams(); + senderStreams.readable.pipeThrough(new TransformStream()).pipeTo(senderStreams.writable); + } catch (e){ + errorlog(e); + } + } + }); +} + +///////// WIP. does not work //////////// +// function encodeFunctionLyra(encodedFrame, controller) { // Snippet is Apache 2.0 licenced. Source: https://github.com/Flash-Meeting/lyra-webrtc + // const inputDataArray = new Uint8Array(encodedFrame.data); + + // const inputBufferPtr = session.lyraCodecModule._malloc(encodedFrame.data.byteLength); + // const encodedBufferPtr = session.lyraCodecModule._malloc(1024); + + // session.lyraCodecModule.HEAPU8.set(inputDataArray, inputBufferPtr); + // const length = session.lyraCodecModule.encode(inputBufferPtr, inputDataArray.length, 16000, encodedBufferPtr); + + // const newData = new ArrayBuffer(length); + // if (length > 0){ + // const newDataArray = new Uint8Array(newData); + // newDataArray.set(session.lyraCodecModule.HEAPU8.subarray(encodedBufferPtr, encodedBufferPtr + length)); + // } + // session.lyraCodecModule._free(inputBufferPtr); + // session.lyraCodecModule._free(encodedBufferPtr); + // encodedFrame.data = newData; + // controller.enqueue(encodedFrame); +// } +// function decodeFunctionLyra(encodedFrame, controller) { // Apache 2.0 licenced. Source: https://github.com/Flash-Meeting/lyra-webrtc + // const newData = new ArrayBuffer(16000 * 0.02 * 2); + // if (encodedFrame.data.byteLength > 0) { + // const inputDataArray = new Uint8Array(encodedFrame.data); + // const inputBufferPtr = session.lyraCodecModule._malloc(encodedFrame.data.byteLength); + // const outputBufferPtr = session.lyraCodecModule._malloc(2048); + // session.lyraCodecModule.HEAPU8.set(inputDataArray, inputBufferPtr); + // const length = session.lyraCodecModule.decode(inputBufferPtr, + // inputDataArray.length, 16000, + // outputBufferPtr); + // const newDataArray = new Uint8Array(newData); + // newDataArray.set(session.lyraCodecModule.HEAPU8.subarray(outputBufferPtr, outputBufferPtr + length)); + // session.lyraCodecModule._free(inputBufferPtr); + // session.lyraCodecModule._free(outputBufferPtr); + // } + // encodedFrame.data = newData; + // controller.enqueue(encodedFrame); +// } +////////// + +function obfuscateURL(input) { + if (input.startsWith("https://obs.ninja/")) { + input = input.replace('https://obs.ninja/', 'obs.ninja/'); + } else if (input.startsWith("http://obs.ninja/")) { + input = input.replace('http://obs.ninja/', 'obs.ninja/'); + } else if (input.startsWith("obs.ninja/")) { + input = input.replace('obs.ninja/', 'obs.ninja/'); + } else if (input.startsWith("https://vdo.ninja/")) { + input = input.replace('https://vdo.ninja/', 'vdo.ninja/'); + } else if (input.startsWith("http://vdo.ninja/")) { + input = input.replace('http://vdo.ninja/', 'vdo.ninja/'); + } else if (input.startsWith("vdo.ninja/")) { + input = input.replace('vdo.ninja/', 'vdo.ninja/'); + } + + input = input.replace('&view=', '&v='); + input = input.replace('&view&', '&v&'); + input = input.replace('?view&', '?v&'); + input = input.replace('?view=', '?v='); + + input = input.replace('&videobitrate=', '&vb='); + input = input.replace('?videobitrate=', '?vb='); + input = input.replace('&bitrate=', '&vb='); + input = input.replace('?bitrate=', '?vb='); + + input = input.replace('?audiodevice=', '?ad='); + input = input.replace('&audiodevice=', '&ad='); + + input = input.replace('?label=', '?l='); + input = input.replace('&label=', '&l='); + + input = input.replace('?stereo=', '?s='); + input = input.replace('&stereo=', '&s='); + input = input.replace('&stereo&', '&s&'); + input = input.replace('?stereo&', '?s&'); + + input = input.replace('?webcam&', '?wc&'); + input = input.replace('&webcam&', '&wc&'); + + input = input.replace('?remote=', '?rm='); + input = input.replace('&remote=', '&rm='); + + input = input.replace('?password=', '?p='); + input = input.replace('&password=', '&p='); + + input = input.replace('&maxvideobitrate=', '&mvb='); + input = input.replace('?maxvideobitrate=', '?mvb='); + + input = input.replace('&maxbitrate=', '&mvb='); + input = input.replace('?maxbitrate=', '?mvb='); + + input = input.replace('&height=', '&h='); + input = input.replace('?height=', '?h='); + + input = input.replace('&width=', '&w='); + input = input.replace('?width=', '?w='); + + input = input.replace('&quality=', '&q='); + input = input.replace('?quality=', '?q='); + + input = input.replace('&cleanoutput=', '&clean='); + input = input.replace('?cleanoutput=', '?clean='); + + input = input.replace('&maxviewers=', '&clean='); + input = input.replace('?maxviewers=', '?clean='); + + input = input.replace('&frameRate=', '&fr='); + input = input.replace('?frameRate=', '?fr='); + + input = input.replace('&fps=', '&fr='); + input = input.replace('?fps=', '?fr='); + + input = input.replace('&roomid=', '&r='); + input = input.replace('?roomid=', '?r='); + + input = input.replace('&room=', '&r='); + input = input.replace('?room=', '?r='); + + log(input); + var key = "OBSNINJAFORLIFE"; + var encrypted = CryptoJS.AES.encrypt(input, key); + var output = "https://invite.cam/" + encrypted.toString(); + return output; +} + +function notifyOfScreenShare(){ + if (session.notifyScreenShare){ + var data = {}; + data.screenShareState = session.screenShareState; + session.sendMessage(data); + } +} + +var beforeScreenShare = null; // video +var screenShareAudioTrack = null; +async function toggleScreenShare(reload = false) { /// &sstype=1 + + var quality = session.quality_ss; + + if (quality === false){ + quality = session.quality_wb; + } + + if (session.quality !== false){ + quality = session.quality; + } + if (session.screensharequality!==false){ + quality = session.screensharequality; + } + + if (reload) { // quality = 0, audio = true, videoOnEnd = false) { + await grabScreen(quality, true, true).then(res => { + if (res != false) { + session.screenShareState = true; + pokeIframeAPI('screen-share-state', session.screenShareState, null, session.streamID); + notifyOfScreenShare(); + + getById("screensharebutton").classList.add("green"); + getById("screensharebutton").ariaPressed = "true"; + enumerateDevices().then(gotDevices2).then(function() {}); + } + + }); + return; + } + if (session.screenShareState == false) { // adding a screen + await grabScreen(quality, true, true).then(res => { + if (res != false) { + session.screenShareState = true; + pokeIframeAPI('screen-share-state', session.screenShareState, null, session.streamID); + notifyOfScreenShare(); + + //session.refreshScale(); + + getById("screensharebutton").classList.add("green"); + getById("screensharebutton").ariaPressed = "true"; + enumerateDevices().then(gotDevices2).then(function() {}); + + //if (session.videoElement.readyState!==4){ + session.videoElement.play().then(() => { + log("start play doublecheck"); + }); + //} + updateMixer(); + pokeIframeAPI("screen-share-state", true); + } + }); + + } else { // removing a screen . session.screenShareState already true true ///////////////////////////////// + + session.screenShareState = false; + pokeIframeAPI('screen-share-state', session.screenShareState, null, session.streamID); + notifyOfScreenShare(); + + if (!session.streamSrc){ + checkBasicStreamsExist(); + } + + if (screenShareAudioTrack){ + if (session.videoElement && session.videoElement.srcObject){ + session.videoElement.srcObject.getAudioTracks().forEach(function(track) { // previous video track; saving it. Must remove the track at some point. + if (screenShareAudioTrack.id == track.id) { // since there are more than one audio track, lets see if we can remove JUST the audio track for the screen share. + session.videoElement.srcObject.removeTrack(track); + track.stop(); + } + }); + } + + if (session.streamSrcClone){ // + session.streamSrcClone.getAudioTracks().forEach(function(track) { + if (screenShareAudioTrack.id == track.id) { // since there are more than one audio track, lets see if we can remove JUST the audio track for the screen share. + session.streamSrcClone.removeTrack(track); + track.stop(); + } + }); + } + if (session.streamSrc){ + session.streamSrc.getAudioTracks().forEach(function(track) { // previous video track; saving it. Must remove the track at some point. + if (screenShareAudioTrack.id == track.id) { // since there are more than one audio track, lets see if we can remove JUST the audio track for the screen share. + session.streamSrc.removeTrack(track); + track.stop(); + } + }); + } + + session.videoElement.srcObject = outboundAudioPipeline(); // updateREnderOoutput is just for video if videoElement is already activated. + screenShareAudioTrack=null; + senderAudioUpdate(); + } + + var addedAlready = false; + if (session.streamSrc){ + session.streamSrc.getVideoTracks().forEach(function(track) { + if (beforeScreenShare && (track.id == beforeScreenShare.id)){ + addedAlready=true; + } else { + session.streamSrc.removeTrack(track); + track.stop(); + } + }); + } + + if (session.streamSrcClone){ + session.streamSrcClone.getVideoTracks().forEach(function(track) { + if (beforeScreenShare && (track.id == beforeScreenShare.id)){ + // + } else { + session.streamSrcClone.removeTrack(track); + track.stop(); + } + }); + } + + if (session.videoElement && session.videoElement.srcObject){ + session.videoElement.srcObject.getVideoTracks().forEach(function(track) { + if (beforeScreenShare && (track.id == beforeScreenShare.id)){ + addedAlready=true; + } else { + session.videoElement.srcObject.removeTrack(track); + track.stop(); + } + }); + } + + getById("screensharebutton").classList.remove("green"); + getById("screensharebutton").ariaPressed = "false"; + + if (beforeScreenShare){ + if (addedAlready==false){ + session.streamSrc.addTrack(beforeScreenShare); // add back in the video track we had before we started screen sharing. It should be NULL if we changed the video track else where (such as via the settings). #TODO: + } + } + + beforeScreenShare = null; + updateRenderOutpipe(); // this syncs the video + + toggleSettings(true); // forceShow + updateMixer(); + } +} +var ipcRenderer = false; +var ElectronDesktopCapture = false; +if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1) { // this enables Screen Capture in Electron + try { + if (!ipcRenderer){ + ipcRenderer = require('electron').ipcRenderer; + } + window.navigator.mediaDevices.getDisplayMedia = (constraints=false) => { + return new Promise(async (resolve, reject) => { + try { + + if (session.autostart){ + session.autostart = false; + if (parseInt(session.screenshare)+"" === session.screenshare){ + var sscid = parseInt(session.screenshare)-1; + if (sscid<0){sscid=0;} + //ipcRenderer.sendSync('prompt', {title, val}); + const sources = await ipcRenderer.sendSync('getSources',{types: ['screen']}); + + var new_constraints = { + audio:{ + mandatory: { + chromeMediaSource: 'desktop', + chromeMediaSourceId: sources[sscid].id + } + }, + video: { + mandatory: { + chromeMediaSource: 'desktop', + chromeMediaSourceId: sources[sscid].id + } + } + }; + if (session.audioDevice===0){ + new_constraints.audio = false; + } + try { + if (constraints.video.width.ideal){ + new_constraints.video.mandatory.maxWidth = constraints.video.width.ideal; + } + } catch(e){} + try { + if (constraints.video.height.ideal){ + new_constraints.video.mandatory.maxHeight = constraints.video.height.ideal; + } + } catch(e){} + try { + if (constraints.video.frameRate.ideal){ + new_constraints.video.mandatory.maxFrameRate = constraints.video.frameRate.ideal; + } + } catch(e){} + /// + const stream = await window.navigator.mediaDevices.getUserMedia(new_constraints); + resolve(stream); + } else if (session.screenshare && (session.screenshare!==true)){ + var sscid=null; + const sources = await ipcRenderer.sendSync('getSources',{types: ['window']}); + for (var i=0; i +
      + ${sources.map(({id, name, thumbnail, display_id, appIcon}) => ` +
    • + +
    • + `).join('')} + +
    + + `; + } else { + selectionElem.innerHTML = ` +
    +
      + ${sources.map(({id, name, thumbnail, display_id, appIcon}) => ` +
    • + +
    • + `).join('')} +

      Include Desktop Audio
      + + + +
    +
    + `; + } + document.body.appendChild(selectionElem); + + if (macOS){ + getById("captureDesktopAudio").style.display = "none"; + getById("alsoCaptureAudio").checked = false; + getById("alsoCaptureAudioParent1").style.display = "none"; + getById("alsoCaptureAudioParent2").style.display = "inline-block"; + } + + document.getElementById('cancelscreenshare').addEventListener('click', async () => { + selectionElem.remove(); + reject(null); + }); + document.querySelectorAll('.desktop-capturer-click').forEach(button => { + button.addEventListener('click', async () => { + try { + if (button.id == "captureDesktopAudio"){ + var new_constraints = { + audio: { + mandatory: { + chromeMediaSource: 'desktop' + } + }, + video: { + mandatory: { + chromeMediaSource: 'desktop', + } + } + } + new_constraints.video.mandatory.maxFrameRate = 1; + warnlog(new_constraints); + const stream = await window.navigator.mediaDevices.getUserMedia(new_constraints); + if (stream.getVideoTracks().length){ + var track = stream.getVideoTracks()[0]; + stream.removeTrack(stream.getVideoTracks()[0]); + track.stop(); + } + resolve(stream); + selectionElem.remove(); + } else { + var audioStream = false; + if (getById("alsoCaptureAudio").checked){ + var new_constraints = { + audio: { + mandatory: { + chromeMediaSource: 'desktop' + } + }, + video: { + mandatory: { + chromeMediaSource: 'desktop', + } + } + } + new_constraints.video.mandatory.maxFrameRate = 1; + warnlog(new_constraints); + audioStream = await window.navigator.mediaDevices.getUserMedia(new_constraints); + if (audioStream.getVideoTracks().length){ + var track = audioStream.getVideoTracks()[0]; + audioStream.removeTrack(audioStream.getVideoTracks()[0]); + track.stop(); + } + } + + const id = button.getAttribute('data-id'); + const source = sources.find(source => source.id === id); + if (!source) { + throw new Error(`Source with id ${id} does not exist`); + } + var new_constraints = { + audio: false, + video: { + mandatory: { + chromeMediaSource: 'desktop', + chromeMediaSourceId: source.id + } + } + }; + try { + if (constraints.video.width.ideal){ + new_constraints.video.mandatory.maxWidth = constraints.video.width.ideal; + } + } catch(e){} + try { + if (constraints.video.height.ideal){ + new_constraints.video.mandatory.maxHeight = constraints.video.height.ideal; + } + } catch(e){} + try { + if (constraints.video.frameRate.ideal){ + new_constraints.video.mandatory.maxFrameRate = constraints.video.frameRate.ideal; + } + } catch(e){} + warnlog(new_constraints); + const stream = await window.navigator.mediaDevices.getUserMedia(new_constraints); + + if (audioStream && audioStream.getAudioTracks().length){ + stream.addTrack(audioStream.getAudioTracks()[0]); + } + + resolve(stream); + selectionElem.remove(); + } + } catch (err) { + errorlog('Error selecting desktop capture source:', err); + reject(err); + } + }) + }); + } + } catch (err) { + errorlog('Error displaying desktop capture sources:', err); + reject(err); + } + }) + } + ElectronDesktopCapture = true; + } catch(e){ + warnlog("Couldn't load electron's screen capture. Elevate the app's permission to allow it (right-click?)"); + } +} + +async function grabScreen(quality = 0, audio = true, videoOnEnd = false) { + if (!navigator.mediaDevices.getDisplayMedia) { + if (!(session.cleanOutput)) { + setTimeout(function() { + if (iOS || iPad){ + warnUser(getTranslation("ios-no-screen-share"), false, false); + } else if (session.mobile){ + warnUser(getTranslation("android-no-screen-share"), false, false); + } else { + warnUser(getTranslation("no-screen-share-supported"), false, false); + } + }, 1); + } + return false; + } + + if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1) { + if (!ElectronDesktopCapture){ + if (!(session.cleanOutput)) { + warnUser("Enable Elevated Privileges to allow screen-sharing. (right click this window to see that option)"); + } + return false; + } + } + + var video = {} + + if (quality == -1) { + // unlocked capture resolution + } else if (quality == 0) { + video.width = { + ideal: 1920 + }; + video.height = { + ideal: 1080 + }; + } else if (quality == 1) { + video.width = { + ideal: 1280 + }; + video.height = { + ideal: 720 + }; + } else if (quality == 2) { + video.width = { + ideal: 640 + }; + video.height = { + ideal: 360 + }; + } else if (quality >= 3) { // lowest + video.width = { + ideal: 320 + }; + video.height = { + ideal: 180 + }; + } + + if (session.width) { + video.width = { + ideal: session.width + }; + } + if (session.height) { + video.height = { + ideal: session.height + }; + } + + var constraints = { // this part is a bit annoying. Do I use the same settings? I can add custom setting controls here later + audio: { + echoCancellation: false, // For screen sharing, we want it off by default. + autoGainControl: false, + noiseSuppression: false + }, + video: video + //,cursor: {exact: "none"} + }; + + + try { + let supportedConstraints = navigator.mediaDevices.getSupportedConstraints(); + if (supportedConstraints.cursor) { + if (session.screensharecursor){ + constraints.video.cursor = ["always", "motion"]; + } else { + constraints.video.cursor = "never"; + } + } + if (session.suppressLocalAudioPlayback && supportedConstraints.suppressLocalAudioPlayback){ + constraints.audio.suppressLocalAudioPlayback = true; + } + if (session.preferCurrentTab){ + constraints.preferCurrentTab = true; + } + if (session.selfBrowserSurface){ + constraints.selfBrowserSurface = session.selfBrowserSurface; // exclude or include + } + if (session.surfaceSwitching){ + constraints.surfaceSwitching = session.surfaceSwitching; // exclude or include + } + if (session.systemAudio){ + constraints.systemAudio = session.systemAudio; // exclude or include + } + if (session.displaySurface && supportedConstraints.displaySurface){ + constraints.video.displaySurface = session.displaySurface; // monitor, window, or browser + } + } catch(e){ + warnlog("navigator.mediaDevices.getSupportedConstraints() not supported"); + } + + + if (session.echoCancellation === true) { + constraints.audio.echoCancellation = true; + } + if (session.autoGainControl === true) { + constraints.audio.autoGainControl = true; + } + if (session.noiseSuppression === true) { + constraints.audio.noiseSuppression = true; + } + if (audio == false) { + constraints.audio = false; + } + + + var overrideFramerate = false; + if ((session.frameRate !== false) && (session.maxframeRate != false)){ + overrideFramerate = session.frameRate; + constraints.video.frameRate = { + ideal: session.maxframeRate, + max: session.maxframeRate + }; + } else if (session.frameRate !== false) { + constraints.video.frameRate = session.frameRate; + } else if (session.maxframeRate != false){ + constraints.video.frameRate = { + ideal: session.maxframeRate, + max: session.maxframeRate + }; + } else { + constraints.video.frameRate = { + ideal: 60 + }; + } + + if (session.screenshareVideoOnly){ + constraints.audio = false; + } + + if (session.forceAspectRatio){ // await updateCameraConstraints("aspectRatio", session.forceAspectRatio); + if (constraints.video && constraints.video!==true){ + + constraints.video.aspectRatio = { ideal: parseFloat(session.forceAspectRatio)}; + + + if (constraints.video.width && !session.width){ + delete constraints.video.width; + } else if (constraints.video.height && !session.height){ + delete constraints.video.height; + } + } + } + + if ((constraints.video!==false) && (Object.keys(constraints.video).length==0)){ + constraints.video = true; + } + + var wasDisabled = true; + + return navigator.mediaDevices.getDisplayMedia(constraints).then(async function(stream) { + log("adding video tracks 2245"); + + try { + var constraint = {}; + if (session.forceAspectRatio && (session.forceScreenShareAspectRatio===null)){ + constraint.aspectRatio = parseFloat(session.forceAspectRatio); + } else if (session.forceScreenShareAspectRatio){ + constraint.aspectRatio = parseFloat(session.forceScreenShareAspectRatio); + } + if (overrideFramerate){ + constraint.frameRate = overrideFramerate; + } + if (Object.keys(constraint).length){ + await stream.getVideoTracks()[0].applyConstraints({ + advanced: [constraint] + }); + log({ + advanced: [constraint] + }); + } + } catch(e){errorlog(e);} + + try { + if (session.streamSrc) { + session.streamSrc.getVideoTracks().forEach(function(track) { + //track.stop(); + beforeScreenShare = track; + session.streamSrc.removeTrack(track); + wasDisabled = false; // + log("stopping video track"); + }); + if (session.streamSrcClone){ + session.streamSrcClone.getVideoTracks().forEach(function(track) { + session.streamSrcClone.removeTrack(track); + track.stop(); + }); + } + if (session.videoElement && session.videoElement.srcObject){ + session.videoElement.srcObject.getVideoTracks().forEach(function(track) { + //track.stop(); + wasDisabled = false; + session.videoElement.srcObject.removeTrack(track); + log("stopping video track 2"); + }); + } else { + checkBasicStreamsExist(); + } + } else { + checkBasicStreamsExist(); // create srcObject + videoElement + } + } catch (e) { + warnlog(e); + } + + try { + stream.getVideoTracks()[0].onended = function(e) { // if screen share stops, + warnlog(e); + if (session.streamSrc){ + session.streamSrc.getVideoTracks().forEach(function(track) { + session.streamSrc.removeTrack(track); + track.stop(); + log("stopping video track 3"); + + if (beforeScreenShare && (beforeScreenShare.id == track.id)){ + beforeScreenShare.stop(); + beforeScreenShare=null; + } + }); + } + + if (session.streamSrcClone){ + session.streamSrcClone.getVideoTracks().forEach(function(track) { + session.streamSrcClone.removeTrack(track); + track.stop(); + }); + } + + if (session.videoElement && session.videoElement.srcObject){ + session.videoElement.srcObject.getVideoTracks().forEach(function(track) { + session.videoElement.srcObject.removeTrack(track); + track.stop(); + log("stopping video track 4"); + }); + } else { + //session.videoElement.srcObject = createMediaStream(); + session.videoElement.srcObject = outboundAudioPipeline(); + } + + if (screenShareAudioTrack){ + if (session.streamSrc){ + session.streamSrc.getAudioTracks().forEach(function(track) { // previous video track; saving it. Must remove the track at some point. + if (screenShareAudioTrack.id == track.id) { // since there are more than one audio track, lets see if we can remove JUST the audio track for the screen share. + session.streamSrc.removeTrack(track); + track.stop(); + } + }); + } + if (session.streamSrcClone){ + session.streamSrcClone.getAudioTracks().forEach(function(track) { // previous video track; saving it. Must remove the track at some point. + if (screenShareAudioTrack.id == track.id) { // since there are more than one audio track, lets see if we can remove JUST the audio track for the screen share. + session.streamSrcClone.removeTrack(track); + track.stop(); + } + }); + } + screenShareAudioTrack=null; + senderAudioUpdate(); + } + + session.screenShareState = false; + pokeIframeAPI('screen-share-state', session.screenShareState, null, session.streamID); + notifyOfScreenShare(); + + + getById("screensharebutton").classList.remove("green"); + getById("screensharebutton").ariaPressed = "false"; + + if (videoOnEnd == true) { + if (beforeScreenShare) { + session.streamSrc.addTrack(beforeScreenShare); // updateRenderOutpipe + beforeScreenShare = null; + } + updateRenderOutpipe(); + toggleSettings(true); // forceshow + } else { + //session.refreshScale(); // since updateREnderOutput already has htis. + } + updateMixer(); + }; + } catch (e) { + log("No Video selected; screensharing?"); + } + + stream.getTracks().forEach(function(track) { + addScreenDevices(track); + session.streamSrc.addTrack(track, stream); // Lets not add the audio to this preview; echo can be annoying + }); + updateRenderOutpipe(); + + if (wasDisabled && stream.getVideoTracks().length && !session.videoMuted){ + var msg = {}; + msg.videoMuted = session.videoMuted; + session.sendMessage(msg); + } + + if (stream.getAudioTracks().length){ + screenShareAudioTrack = stream.getAudioTracks()[0]; + senderAudioUpdate(); + } + + session.applySoloChat(); // mute streams that should be muted if a director + session.applyIsolatedChat(); + + applyMirror(true); + + + return true; + }).catch(function(err) { + errorlog(err); + errorlog(err.name); + if ((err.name == "NotAllowedError") || (err.name == "PermissionDeniedError")) { + // User Stopped it. + if (macOS){ + warnUser(getTranslation("screen-permissions-denied"), false, false); + } + } else { + if (audio == true) { + if (err.name == "NotReadableError"){ + if (!(session.cleanOutput)){ + warnUser(getTranslation("change-audio-output-device"), false, false); + } + return false; + } else { + setTimeout(function() { + grabScreen(quality, false); + }, 1); + } + } + if (!(session.cleanOutput)) { + setTimeout(function(e) { + errorlog(e); + }, 1, err); // TypeError: Failed to execute 'getDisplayMedia' on 'MediaDevices': Audio capture is not supported + } + } + return false; + }); +} + +function toggleBufferSettings(UUID){ + //bufferSliderValue + /* try { + session.rpcs[taskItemInContext.dataset.UUID].buffer = parseInt(inputElement.value); + inputElement.title = session.rpcs[taskItemInContext.dataset.UUID].buffer + " ms"; + getById("bufferSliderValue").innerText = session.rpcs[taskItemInContext.dataset.UUID].buffer + " ms"; + playoutdelay(taskItemInContext.dataset.UUID); // trigger + } catch(e){ + errorlog(e); + */ + + + toggle(getById('bufferSettings')); + if (getById('bufferSettings').style.display=="none"){ + getById("modalBackdrop").innerHTML = ''; // Delete modal + getById("modalBackdrop").remove(); + } else { + getById("modalBackdrop").innerHTML = ''; // Delete modal + getById("modalBackdrop").remove(); + zindex = 25; + getById('bufferSettings').style.zIndex = 25; + var modalTemplate = `
    `; + document.body.insertAdjacentHTML("beforeend", modalTemplate); // Insert modal at body end + document.getElementById("modalBackdrop").addEventListener("click", toggleBufferSettings); + + + var buffer = session.rpcs[UUID].buffer; + if (buffer === false){ + buffer = session.buffer || 0; + } + getById('bufferSettings').querySelectorAll("input").forEach(ele=>{ + ele.value = parseInt(buffer); + ele.title = ele.value + " ms"; + //getById("bufferSliderValue").innerText = ele.title + ele.onchange = function(e){ + session.rpcs[UUID].buffer = parseInt(e.target.value); + //getById("bufferSliderValue").innerText = session.rpcs[UUID].buffer + " ms"; + getById('bufferSettings').querySelectorAll("input").forEach(ele2=>{ + if (ele2!==e.target){ + ele2.value = parseInt(e.target.value); + } + ele2.title = parseInt(e.target.value) + " ms"; + }); + playoutdelay(UUID); // trigger + }; + ele.oninput = function(e){ + getById('bufferSettings').querySelectorAll("input").forEach(ele2=>{ + if (ele2!==e.target){ + ele2.value = parseInt(e.target.value); + } + ele2.title = parseInt(e.target.value) + " ms"; + }); + }; + }); + } +} + + +function toggleRoomSettings(){ + toggle(getById('roomSettings')); + if (getById('roomSettings').style.display=="none"){ + //getById("modalBackdrop").innerHTML = ''; // Delete modal + //getById("modalBackdrop").remove(); + } else { + //getById("modalBackdrop").innerHTML = ''; // Delete modal + //getById("modalBackdrop").remove(); + zindex = 25; + getById('roomSettings').style.zIndex = 25; + var modalTemplate = `
    `; + // document.body.insertAdjacentHTML("beforeend", modalTemplate); // Insert modal at body end + // document.getElementById("modalBackdrop").addEventListener("click", toggleRoomSettings); + document.getElementById('trbSettingInput').value = session.totalRoomBitrate; + document.getElementById('trbSettingInputManual').value = session.totalRoomBitrate; + document.getElementById('trbSettingInputFeedback').innerHTML = session.totalRoomBitrate; + } +} + +function changeTRB(ele){ + session.totalRoomBitrate = parseInt(ele.value); + var msg = {}; + msg.directorSettings={}; + msg.directorSettings.totalRoomBitrate=session.totalRoomBitrate; + session.sendMessage(msg); + pokeIframeAPI('total-room-bitrate', session.totalRoomBitrate); +} + +function sendMediaDevices(UUID){ + enumerateDevices().then(function(deviceInfos){ + var data = {}; + data.UUID = UUID; + data.mediaDevices = deviceInfos; + session.sendMessage(data, data.UUID); + }); +} + +function changeVideoDevice(index, quality=0){ + enumerateDevices().then(gotDevices2).then(function() { + activatedPreview=false; + document.getElementById("videoSource3").selectedIndex = index+""; + grabVideo(quality, "videosource", "#videoSource3"); + }); +} + +function changeAudioDevice(index){ + enumerateDevices().then(gotDevices2).then(function() { + activatedPreview=false; + var audioSelect = document.getElementById("audioSource3").querySelectorAll("input"); + for (var i = 0; i < audioSelect.length; i++) { + audioSelect[i].checked = false; + } + audioSelect[index-1].checked = true; + grabAudio("#audioSource3"); + }); +} + +function changeVideoDeviceById(deviceId, UUID=false){ + enumerateDevices().then(gotDevices2).then(function() { + var opts = document.getElementById("videoSource3").options; + var index = false + for (var opt, j = 0; opt = opts[j]; j++) { + if (opt.value == deviceId) { + index = j; + break; + } + } + if (index!==false){ + if (document.getElementById("videoSource3").selectedIndex === index){ // this is just refreshing the device. + activatedPreview=false; + grabVideo(0, "videosource", "#videoSource3", UUID); + } else if (UUID && !session.consent){ + window.focus(); + confirmAlt("Allow the director to change your video device to:\n\n"+opts[index].text+" ?").then(res=>{ + if (res){ + document.getElementById("videoSource3").selectedIndex = index; + activatedPreview=false; + grabVideo(0, "videosource", "#videoSource3", UUID); + } else { + try { + var data = {}; + data.UUID = UUID; + data.rejected = "changeCamera"; + session.sendMessage(data, data.UUID); + } catch(e){} + } + }); + } else { + document.getElementById("videoSource3").selectedIndex = index; + activatedPreview=false; + grabVideo(0, "videosource", "#videoSource3", UUID); + } + } + }); +} + +function changeAudioDeviceById(deviceId, UUID=false){ + enumerateDevices().then(gotDevices2).then(function() { + var audioSelect = document.getElementById("audioSource3").querySelectorAll("input"); + var matched = false; + var exists = false; + for (var i = 0; i < audioSelect.length; i++) { + if (audioSelect[i].value == deviceId){ + exists = true; + if (audioSelect[i].checked){ + matched = true; + } + } + } + + if (exists){ + if (matched){ // this is just refreshing the device. + activatedPreview=false; + grabAudio("#audioSource3", UUID); + } else if (UUID && !session.consent){ + window.focus(); + confirmAlt("Allow the director to change your audio mic source?").then(res=>{ + if (res){ + // enumerateDevices().then(gotDevices2).then(function() { + var audioSelect = document.getElementById("audioSource3").querySelectorAll("input"); + for (var i = 0; i < audioSelect.length; i++) { + if (audioSelect[i].value == deviceId){ + audioSelect[i].checked=true; + } else { + audioSelect[i].checked = false; + } + } + activatedPreview=false; + grabAudio("#audioSource3", UUID); + // }); + } else { + try { + var data = {}; + data.UUID = UUID; + data.rejected = "changeMicrophone"; + session.sendMessage(data, data.UUID); + } catch(e){} + } + }); + } else { + //enumerateDevices().then(gotDevices2).then(function() { + var audioSelect = document.getElementById("audioSource3").querySelectorAll("input"); + for (var i = 0; i < audioSelect.length; i++) { + if (audioSelect[i].value == deviceId){ + audioSelect[i].checked=true; + } else { + audioSelect[i].checked = false; + } + } + activatedPreview=false; + grabAudio("#audioSource3", UUID); + // }); + } + } + }); +} + +function changeAudioOutputDeviceById(deviceId, UUID=false){ // remote control of the speaker output. + warnlog(deviceId); + if (document.getElementById("outputSource3")){ + enumerateDevices().then(gotDevices2).then(function() { + var index = false + if (document.getElementById("outputSource3")){ + var opts = document.getElementById("outputSource3").options; + for (var opt, j = 0; opt = opts[j]; j++) { + if (opt.value == deviceId) { + index = j; + break; + } + } + } + if (index!==false){ + if (document.getElementById("outputSource3").selectedIndex === index){ // this is just refreshing the device. + session.sink = deviceId; + saveSettings(); + resetupAudioOut(); + } else if (UUID && !session.consent){ // UUID just lets us inform the requester + window.focus(); + confirmAlt("Allow the director to change your audio's speaker to:\n\n"+opts[index].text+" ?").then(res=>{ + if (res){ + if (index!==false){ + document.getElementById("outputSource3").selectedIndex = index; + } + session.sink = deviceId; + saveSettings(); + resetupAudioOut(); + var data = {}; + data.UUID = UUID; + sendMediaDevices(data.UUID); + session.sendMessage(data, data.UUID); + } else { + try { + var data = {}; + data.UUID = UUID; + data.rejected = "changeSpeaker"; + session.sendMessage(data, data.UUID); + } catch(e){} + } + }); + } else { + if (index!==false){ + document.getElementById("outputSource3").selectedIndex = index; + } + session.sink = deviceId; + saveSettings(); + resetupAudioOut(); + } + } + }); + } else { + session.sink = deviceId; + saveSettings(); + resetupAudioOut(); + } +} + +function checkBasicStreamsExist(){ + log("checkBasicStreamsExist()"); + if (!session.streamSrc) { + session.streamSrc = createMediaStream(); + } + if (!session.videoElement) { + if (document.getElementById("videosource")) { + session.videoElement = document.getElementById("videosource"); + } else if (document.getElementById("previewWebcam")) { + session.videoElement = document.getElementById("previewWebcam"); + } else { + session.videoElement = createVideoElement(); + } + + session.videoElement.addEventListener("playing", (e)=>{ + resetupAudioOut(session.videoElement, true); + }, { once: true }); + + } + session.videoElement.srcObject = outboundAudioPipeline(); + toggleMute(true); + return session.videoElement; +} + +var getUserMediaRequestID = 0; +var grabVideoUserMediaTimeout = null; +var grabVideoTimer = null; + +async function grabVideo(quality = 0, eleName = 'previewWebcam', selector = "select#videoSourceSelect", callback = false) { + if (activatedPreview == true) { + log("activated preview return 2"); + return; + } + + if (session.miconly){return;} + + activatedPreview = true; + log("Grabbing video: " + quality); + if (grabVideoTimer) { + clearTimeout(grabVideoTimer); + } + log("element:" + eleName); + + var wasDisabled = true; + try { + if (session.streamSrc) { + + if (session.canvasWebGL){ + session.canvasWebGL.remove() + session.canvasWebGL=null; + } + + if (session.canvasSource){ + session.canvasSource.srcObject.getTracks().forEach(function(trk) { + session.canvasSource.srcObject.removeTrack(trk); + trk.stop(); + wasDisabled=false; + }); + } + if (session.streamSrc){ + session.streamSrc.getVideoTracks().forEach(function(track) { + session.streamSrc.removeTrack(track); + track.stop(); + wasDisabled=false; + }); + } + if (session.streamSrcClone){ + session.streamSrcClone.getVideoTracks().forEach(function(track) { + session.streamSrcClone.removeTrack(track); + track.stop(); + }); + } + } else { + checkBasicStreamsExist(); + log("CREATE NEW STREAM"); + } + + + if (session.videoElement && session.videoElement.srcObject) { + session.videoElement.srcObject.getVideoTracks().forEach(function(track) { + session.videoElement.srcObject.removeTrack(track); + track.stop(); + session.videoElement.load(); + wasDisabled=false; + }); + } else { + checkBasicStreamsExist(); + } + + } catch (e) { + errorlog(e); + } + + session.videoElement.controls = session.showControls || false; + + log("selector: " + selector); + var videoSelect = document.querySelector(selector); // document.querySelector("videoSource3").value == "ZZZ" + log(videoSelect); + var mirror = false; + getById("cameraTip1").classList.add("hidden"); + + if (!videoSelect || videoSelect.value == "ZZZ"){ // if there is no video, or if manually set to audio ready, then do this step. + + clearTimeout(grabVideoUserMediaTimeout); + getUserMediaRequestID += 1; + + warnlog("ZZZ SET - so no VIDEO"); + SelectedVideoInputDevices = []; + saveSettings(); + + if (session.avatar && session.avatar.ready){ + updateRenderOutpipe(); + } else if (session.mobile && !document.getElementById("keepAlivePlayer")){ // keep alive player doens't exist + + setInterval(function(){ + if (document.getElementById("keepAlivePlayer") && session.streamSrc.getVideoTracks().length){ + getById("keepAlivePlayer").remove(); + } else if (!document.getElementById("keepAlivePlayer")){ + let fakeElement = document.createElement("video"); + fakeElement.autoplay = true; + fakeElement.loop = true; + fakeElement.muted = true; + fakeElement.src = "./media/micro.mp4"; + fakeElement.style.width = "1px"; + fakeElement.style.height ="1px"; + fakeElement.controls = false; + fakeElement.id = "keepAlivePlayer"; + getById("main").appendChild(fakeElement); + } + }, 4000); + + } + + if ((eleName == "previewWebcam") && document.getElementById("previewWebcam")){ + if (session.autostart) { + publishWebcam(); // no need to mirror as there is no video... + return; + } else { + log("4462"); + updateStats(); + if (document.getElementById("gowebcam")) { + document.getElementById("gowebcam").dataset.ready = "true"; + if (document.getElementById("gowebcam").dataset.audioready == "true"){ + document.getElementById("gowebcam").disabled = false; + //document.getElementById("gowebcam").innerHTML = getTranslation("start"); + miniTranslate(document.getElementById("gowebcam"),"start"); + document.getElementById("gowebcam").focus(); + } + } + } + } else { // If they disabled the video but not in preview mode; but actualy live. We will want to remove the stream from the publishing + // we don't want to do this otherwise, as we are "replacing" the track in other cases. + // this does cause a problem, as previous bitrate settings & resolutions might not be applied if switched back.... must test + + if (session.avatar && session.avatar.ready){ + updateRenderOutpipe(); + return; + } + + if (session.chunked){ + for (UUID in session.pcs) { + session.chunkedStream(UUID); // make sure we check that this connection allows video / audio + } + return; + } + + if (session.whipOut && session.whipOut.getSenders){ + miscSenders.push(session.whipOut); + } + + miscSenders.forEach(dataRTC=>{ + if (dataRTC && dataRTC.getSenders){ + dataRTC.getSenders().forEach((sender) => { // I suppose there could be a race condition between negotiating and updating this. if joining at the same time as changnig streams? + if (sender.track && sender.track.kind == "video") { + var trk = getMeshcastCanvasTrack(dataRTC); + if (session.screenShareState && session.screenshareContentHint && (trk.kind === "video")){ + try { + trk.contentHint = session.screenshareContentHint; + } catch(e){ + errorlog(e); + } + } else if (session.contentHint && (trk.kind === "video")){ + try { + trk.contentHint = session.contentHint; + } catch(e){ + errorlog(e); + } + } + sender.replaceTrack(trk); // replace may not be supported by all browsers. eek. + } + }); + } + }); + + + for (UUID in session.pcs) { + if ("realUUID" in session.pcs[UUID]){continue;} // do not apply to screen shares. + // for any connected peer, update the video they have if connected with a video already. + var senders = getSenders2(UUID); + senders.forEach((sender) => { // I suppose there could be a race condition between negotiating and updating this. if joining at the same time as changnig streams? + if (sender.track && sender.track.kind == "video") { + sender.track.enabled = false; // I'm not entirely sure if I shoudl be doing this to a video stream... but I suppose new connections won't get a stream, and old connections will just replace it? + getById("mutevideobutton").classList.add("hidden"); // hide the mute button, so they can't unmute while no video. + //session.pcs[UUID].removeTrack(sender); // replace may not be supported by all browsers. eek. + //errorlog("DELETED SENDER"); + } + }); + } + + var msg = {}; + msg.videoMuted = true; // doesn;t matter if video is actually muted or not; no video is being sent + session.sendMessage(msg); + } + return; + } else { + + if (videoSelect && videoSelect.value){ + SelectedVideoInputDevices = [videoSelect.value]; + saveSettings(); + } + + if (session.avatar && session.avatar.timer){ + clearInterval(session.avatar.timer); + session.avatar.timer=null; + } + + var sq = 0; + if (session.quality === false) { + sq = session.quality_wb; + } else if (session.quality > 2) { // 1080, 720, and 360p + sq = 2; // hacking my own code. TODO: ugly, so I need to revisit this. + } else { + sq = session.quality; + } + + if (session.director && (quality !== false)){ // URL-based quality won't matter if DIRECTOR; + // quality = quality; + } else if ((quality === false) || (quality < sq)) { + quality = sq; // override the user's setting + } + + if ((iOS || iPad) && SafariVersion<15) { // iOS will not work correctly at 1080p; likely a h264 codec issue. + if (quality == 0) { + quality = 1; + } + } + + var constraints = { + audio: false, + video: getUserMediaVideoParams(quality, (iOS || iPad)) + }; + + //if (Firefox){ + // constraints.video.height = constraints.video.height.ideal; + // constraints.video.width = constraints.video.height.ideal; + //} + + log("Quality selected:" + quality); + + if (session.facingMode){ + constraints.video.facingMode = { exact: session.facingMode }; // user or environment + } else if (iOS || iPad) { + constraints.video.deviceId = { + exact: videoSelect.value + }; // iPhone 6s compatible ? Needs to be exact for iPhone 6s + + } else if (Firefox){ // is firefox. + constraints.video.deviceId = { + exact: videoSelect.value + }; // Firefox is a dick. Needs it to be exact. + + } else if (videoSelect.options[videoSelect.selectedIndex].text.includes("NDI Video")) { // NDI does not like "EXACT" + constraints.video.deviceId = videoSelect.value; // NDI is fucked up + } else { + constraints.video.deviceId = { + exact: videoSelect.value + }; // Default. Should work for Logitech, etc. + } + + if (session.width) { + constraints.video.width = { + exact: session.width + }; // manually specified - so must be exact + } + if (session.height) { + constraints.video.height = { + exact: session.height + }; + } + + if (session.frameRate) { + constraints.video.frameRate = { + exact: session.frameRate + }; + } else if (session.maxframeRate != false){ + constraints.video.frameRate = { + ideal: session.maxframeRate, + max: session.maxframeRate + }; + } else if ((iOS || iPad) && (SafariVersion>15)) { // iOS supports 720p60, but just 1080p30 : iphone 11 on march 2023 + if (quality === 1) { // iphone 11 and older + if (!constraints.video.frameRate){ + constraints.video.frameRate = { + ideal: 60, + max: 60 + }; + } + } else if (iPhone12Up && (quality<1)){ // iphone 12 and up? + if (!constraints.video.frameRate){ + try { + if (videoSelect.options[videoSelect.selectedIndex].innerText.startsWith("Back ")){ // front seems to be limited to 720p60 / 1080p30 + constraints.video.frameRate = { + ideal: 60, + max: 60 + }; + } + } catch(e){ + errorlog(e); + } + } + } + } + + + + if (session.ptz){ + if (constraints.video && constraints.video!==true){ + if (ChromiumVersion && ChromiumVersion>80){ + constraints.video.pan=true; + constraints.video.tilt=true; + constraints.video.zoom=true; + } + } + } + + if (session.forceAspectRatio){ // await updateCameraConstraints("aspectRatio", session.forceAspectRatio); + if (constraints.video && constraints.video!==true){ + + constraints.video.aspectRatio = { ideal: parseFloat(session.forceAspectRatio)}; + + + if (constraints.video.width && !session.width){ + delete constraints.video.width; + } else if (constraints.video.height && !session.height){ + delete constraints.video.height; + } + } + } + + var obscam = false; + var mirrorcheck = false; + log(videoSelect.options[videoSelect.selectedIndex].text); + + if (!videoSelect.options[videoSelect.selectedIndex]){ + if (session.mobile){ + mirrorcheck = true; + mirror = false; + } else { + mirror = false; + } + } else if (videoSelect.options[videoSelect.selectedIndex].text.startsWith("OBS-Camera")) { // OBS Virtualcam + mirror = true; + obscam = true; + } else if (videoSelect.options[videoSelect.selectedIndex].text.startsWith("OBS Virtual Camera")) { // OBS Virtualcam + mirror = true; + obscam = true; + } else if (videoSelect.options[videoSelect.selectedIndex].text.startsWith("Streamlabs ")) { // OBS Virtualcam + mirror = true; + obscam = true; + } else if (videoSelect.options[videoSelect.selectedIndex].text.startsWith("Dummy video device")) { // Linuxv + mirror = true; + } else if (videoSelect.options[videoSelect.selectedIndex].text.startsWith("vMix Video")) { // vMix + mirror = true; + } else if (videoSelect.options[videoSelect.selectedIndex].text.startsWith("Blackmagic")) { // Blackmagic devices + mirror = true; + } else if (videoSelect.options[videoSelect.selectedIndex].text.startsWith("screen-capture-recorder")) { // screen-capture-recorder + mirror = true; + } else if (videoSelect.options[videoSelect.selectedIndex].text.includes(" back")) { // Android + mirror = true; + } else if (videoSelect.options[videoSelect.selectedIndex].text.includes(" rear")) { // Android + mirror = true; + } else if (videoSelect.options[videoSelect.selectedIndex].text.includes("NDI Video")) { // NDI Virtualcam + mirror = true; + } else if (videoSelect.options[videoSelect.selectedIndex].text.startsWith("Back Camera")) { // iPhone and iOS + mirror = true; + } else if (videoSelect.options[videoSelect.selectedIndex].text.toLowerCase().includes("c922")) { + if ((session.quality!==2) && !session.cleanOutput){ + //getById("cameraTipContext1").innerHTML = getTranslation("camera-tip-c922"); + miniTranslate(getById("cameraTipContext1"),"camera-tip-c922"); + getById("cameraTip1").classList.remove("hidden"); + } + } else if (videoSelect.options[videoSelect.selectedIndex].text.toLowerCase().includes("cam link")) { + if (!session.cleanOutput){ + //getById("cameraTipContext1").innerHTML = getTranslation("camera-tip-camlink"); + miniTranslate(getById("cameraTipContext1"),"camera-tip-camlink"); + getById("cameraTip1").classList.remove("hidden"); + } + + } else if (session.mobile){ + mirrorcheck = true; + mirror = false; + } else { + mirror = false; + } + + if (SamsungASeries && ChromiumVersion){ + if (!session.cleanOutput){ + //getById("cameraTipContext1").innerHTML = getTranslation("samsung-a-series"); + miniTranslate(getById("cameraTipContext1"),"samsung-a-series"); + getById("cameraTip1").classList.remove("hidden"); + } + } + if (session.nomirror){ // do not have the camera be mirrored by default, unless using &mirror + session.mirrorExclude = true; + } else { + session.mirrorExclude = mirror; + } + + if (constraints.video && (constraints.video!==true) && (Object.keys(constraints.video).length==0)){ + constraints.video = true; + } else if (constraints.video && (constraints.video!==true) && (Object.keys(constraints.video).length==1) && ("deviceId" in constraints.video) && ("exact" in constraints.video.deviceId) && (constraints.video.deviceId.exact==="default")){ + constraints.video = true; // solves issues with IOS, where no permission yet given - can't request device ID it seems until permissions is given. + } + + log(constraints); + clearTimeout(grabVideoUserMediaTimeout); + getUserMediaRequestID += 1; + var gumMediaID = getUserMediaRequestID; + var delayStart = 100; + if (ChromiumVersion>110){ // aded july 16th; speed up camera switching. + delayStart = 20; + } else if (Firefox){ + delayStart = 500; // cause firefox is buggy as crap + } + grabVideoUserMediaTimeout = setTimeout(function(gumID, callback2) { + if (getUserMediaRequestID !== gumID) {return;} // cancel + navigator.mediaDevices.getUserMedia(constraints).then(function(stream) { + + if (getUserMediaRequestID !== gumID) { + warnlog("GET USER MEDIA CALL HAS EXPIRED"); + stream.getTracks().forEach(function(track) { + stream.removeTrack(track); + track.stop(); + log("stopping old track"); + }); + return; + } + log("adding video tracks 2412"); + + stream.getVideoTracks().forEach(function(track) { + + try{ + if (mirrorcheck){ + try { + var capabilities = track.getCapabilities(); + } catch(e){ + var capabilities = {}; + } + if ("facingMode" in capabilities){ + if (capabilities.facingMode == "environment"){ + session.mirrorExclude = true; + } + } + if ("backgroundBlur" in capabilities){ // Chrome original trial, until v117, and then??? + query('#effectSelector option[value="13"]').classList.remove("hidden"); + query('#effectSelector option[value="13"]').disabled = null; + query('#effectSelector3 option[value="13"]').classList.remove("hidden"); + query('#effectSelector3 option[value="13"]').disabled = null; + } else { + query('#effectSelector option[value="13"]').disabled = true; + query('#effectSelector3 option[value="13"]').disabled = true; + } + } + } catch(e){} + + session.streamSrc.addTrack(track); // tracks previously removed. + + try{ + track.onended = function(e) { // hurrah! + warnlog(e); + refreshVideoDevice(); + } + } catch(e){errorlog(e);} + + if (session.mobile){ + if (!(iPad || iOS || Firefox)){ + try{ + applySavedVideoSettings(track); + } catch(e){errorlog(e);} + } + } + }); + + if (Firefox && !FirefoxEnumerated){ + if (session.streamSrc && session.streamSrc.getTracks().length){ + FirefoxEnumerated=true; + enumerateDevices().then(gotDevices); + } + } + + + updateRenderOutpipe(); + // senderAudioUpdate + + if (wasDisabled && !session.videoMuted){ + var msg = {}; + msg.videoMuted = session.videoMuted; + session.sendMessage(msg); + } + + applyMirror(session.mirrorExclude); + + session.videoElement.play().then(() => { + log("start play doublecheck"); + }); + + if ((eleName == "previewWebcam") && document.getElementById("previewWebcam")){ + if (session.autostart) { + publishWebcam(); + } else { + log("4620"); + if (document.getElementById("gear_webcam")) { + updateStats(obscam); + } + if (document.getElementById("gowebcam")) { + document.getElementById("gowebcam").dataset.ready = "true"; + if (document.getElementById("gowebcam").dataset.audioready == "true"){ + document.getElementById("gowebcam").disabled = false; + //document.getElementById("gowebcam").innerHTML = getTranslation("start"); + miniTranslate(document.getElementById("gowebcam"),"start"); + document.getElementById("gowebcam").focus(); + } + } + } + } else if (getById("gear_webcam3").style.display === "inline-block") { + updateStats(obscam); + } + + // Once crbug.com/711524 is fixed, we won't need to wait anymore. This is + // currently needed because capabilities can only be retrieved after the + // device starts streaming. This happens after and asynchronously w.r.t. + // getUserMedia() returns. + if (grabVideoTimer) { + clearTimeout(grabVideoTimer); + if ((eleName == "previewWebcam") && document.getElementById("previewWebcam")){ + session.videoElement.controls = true; + } + } + if (getById("popupSelector_constraints_video")) { + getById("popupSelector_constraints_video").innerHTML = ""; + } + if (getById("popupSelector_constraints_audio")) { + getById("popupSelector_constraints_audio").innerHTML = ""; + } + if (getById("popupSelector_constraints_loading")) { + getById("popupSelector_constraints_loading").style.display = ""; + } + + if (iOS || iPad){ // TEMPORARY: iOS 15.3 beta fix + toggleSpeakerMute(true); + } + if (!((eleName == "previewWebcam") || document.getElementById("previewWebcam"))){ + updateMixer(); // not with the preview, but after. + } + + pokeIframeAPI('local-camera-event'); + + let grabVideoPostTimeoutValue = 1000; + + if (Firefox || session.mobile){ // wait longer for these; they are more likely to crash if too quick. + grabVideoPostTimeoutValue = 2000; + } + + grabVideoTimer = setTimeout(async function(callback3, gumid) { + + if (getUserMediaRequestID !== gumid) { // new camera selected in this time. + return; + } + makeImages(true); + + if (getById("popupSelector_constraints_loading")) { + getById("popupSelector_constraints_loading").style.display = "none"; + } + if ((eleName == "previewWebcam") && document.getElementById("previewWebcam")){ + session.videoElement.controls = true; + try { + var track0 = session.streamSrc.getVideoTracks(); + if (track0.length) { + track0 = track0[0]; + if (track0.getCapabilities) { + session.cameraConstraints = track0.getCapabilities(); + } else { + session.cameraConstraints = {}; + } + log(session.cameraConstraints); + if (track0.getSettings) { + session.currentCameraConstraints = track0.getSettings(); + + + if (screen && screen.orientation && screen.orientation.type){ + if (screen.orientation.type.includes("portrait")){ + if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ + session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; + } + } + } else if (window.matchMedia("(orientation: portrait)").matches){ + if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ + session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; + } + } + } else { + session.currentCameraConstraints = {}; + } + log(session.currentCameraConstraints); + } + } catch (e) { + errorlog(e); + } + } else if (toggleSettingsState) { + log("16047"); + updateConstraintSliders();//listCameraSettings(); + } + if (callback3){ + try { + var data = {}; + data.UUID = callback3; + data.videoOptions = listVideoSettingsPrep(); + sendMediaDevices(data.UUID); + session.sendMessage(data, data.UUID); + } catch(e){} + } + + if (iOS || iPad){ // TEMPORARY: iOS 15.3 beta fix + toggleSpeakerMute(true); + } + + if (session.forceAspectRatio){ + await updateCameraConstraints("aspectRatio", session.forceAspectRatio); + } + + updateForceRotate(); // this contains session.setResolution(); + + if (iOS || iPad){ + // if we don't do this, portrait videos may be detected as horizontal + if (!document.getElementById("previewWebcam")){ + updateMixer(); // not with the preview, but after. + } + } + + try { + if (session.pip3){ + if (!eleName.pip){ + eleName.pip=true; + toggleSystemPip(session.videoElement, true); + } + } + } catch(e){} + + // this will reset scaling for all viewers of this stream. I also call it when aspect ratio, width, or height is changed via applyConstraints + + dragElement(session.videoElement); + }, grabVideoPostTimeoutValue, callback2, gumID); // focus + + log("DONE - found stream"); + }).catch(function(e) { + + + if (getUserMediaRequestID !== gumID) { + warnlog("the previously selected camera attempted failed, but not a big deal, since its now void"); + return; + } + + warnlog(e); + if (e.name === "OverconstrainedError") { + warnlog(e.message || e); + log("Resolution or frameRate didn't work"); + } else if (e.name === "NotReadableError"){ + if (quality <= 10) { + activatedPreview = false; + grabVideo(quality + 1, eleName, selector); + } else if (session.facingMode){ + session.facingMode = false; + activatedPreview = false; + grabVideo(false, eleName, selector); // restart. + } else { + if (!(session.cleanOutput)) { + if (iOS) { + warnUser("An error occured. Closing existing tabs in Safari may solve this issue."); + } else { + warnUser("Error: Could not start video source.\n\nTypically this means the Camera is already be in use elsewhere. Most webcams can only be accessed by one program at a time.\n\nTry a different camera or perhaps try re-plugging in the device."); + } + } + activatedPreview = true; + if (getById('gowebcam')) { + getById('gowebcam').innerHTML = "Problem with Camera"; + } + + } + return; + } else if (e.name === "NavigatorUserMediaError") { + if (getById('gowebcam')) { + getById('gowebcam').innerHTML = "Problem with Camera"; + } + if (!(session.cleanOutput)) { + warnUser("Unknown error: 'NavigatorUserMediaError'"); + } + return; + } else if (e.name === "timedOut") { + activatedPreview = true; + if (getById('gowebcam')) { + getById('gowebcam').innerHTML = "Problem with Camera"; + } + if (!(session.cleanOutput)) { + warnUser(e.message); + } + return; + } else { + errorlog("An unknown camera error occured"); + } + + if (quality <= 10) { + activatedPreview = false; + grabVideo(quality + 1, eleName, selector); + } else if (session.facingMode){ + session.facingMode = false; + activatedPreview = false; + grabVideo(false, eleName, selector); // restart. + } else { + errorlog("********Camera failed to work"); + activatedPreview = true; + if (getById('gowebcam')) { + getById('gowebcam').innerHTML = "Problem with Camera"; + } + if (!(session.cleanOutput)) { + if (session.width || session.height || session.frameRate) { + warnUser(" Camera failed to load.\n\nPlease ensure your camera supports the resolution and frameRate that has been manually specified. Perhaps use &quality=0 instead.", false, false); + } else { + warnUser(" Camera failed to load.\n\nPlease make sure it is not already in use by another application.\n\nPlease make sure you have accepted the camera permissions.", false, false); + } + } + } + }); + }, delayStart, gumMediaID, callback); + } +} + +function updateRenderOutpipe(){ // video only. + log("updateRenderOutpipe()"); + + if (session.canvasWebGL){ + session.canvasWebGL.remove() + session.canvasWebGL=null; + } + + if (session.canvasSource){ + session.canvasSource.srcObject.getTracks().forEach(function(trk) { + session.canvasSource.srcObject.removeTrack(trk); + //trk.stop(); + }); + } + + if (session.videoElement && session.videoElement.srcObject) { + session.videoElement.srcObject.getVideoTracks().forEach(function(track) { + session.videoElement.srcObject.removeTrack(track); + //track.stop(); + //session.videoElement.load(); + }); + } else { + checkBasicStreamsExist(); + } + + if (session.streamSrc){ + var tracks = session.streamSrc.getVideoTracks(); + + if (!tracks.length || session.videoMuted){ + tracks = setAvatarImage(tracks); + if (tracks.length){ + + if (tracks.length && !session.cleanOutput && !session.cleanish){ + getById("mutevideobutton").classList.remove("hidden"); + } + + tracks.forEach(function(track) { + session.videoElement.srcObject.addTrack(track); + if (session.avatar && session.avatar.tracks){ + var msg = {}; + msg.videoMuted = false; // doesn't matter actual mute state, since its the avatar + session.sendMessage(msg); + } else { + toggleVideoMute(true); + } + pushOutVideoTrack(track); // video only + }); + } else { + var msg = {}; + msg.videoMuted = true; + session.sendMessage(msg); + session.videoElement.load(); + getById("mutevideobutton").classList.add("hidden"); + } + } else if (tracks.length){ + applyMirror(session.mirrorExclude); + tracks.forEach(function(track) { + track = applyEffects(track); // updates with the correct track session.streamSrc + session.videoElement.srcObject.addTrack(track); + toggleVideoMute(true); + pushOutVideoTrack(track); // video only + }); + + if (tracks.length && !session.cleanOutput && !session.cleanish){ + getById("mutevideobutton").classList.remove("hidden"); + } + } + } +} + +function pushOutVideoTrack(track){ + log("pushOutVideoTrack"); + + pokeIframeAPI('push-video-track', track.id, false, session.streamID); // (action, value = null, UUID = null, SID=null) + + if (session.chunked){ + for (UUID in session.pcs) { + session.chunkedStream(UUID); // I need to update chunkedStream with the current track? If sstype=3, then skip this + } + return; + } + + if (session.audioContentHint && (track.kind === "audio")){ // why am I pushing an audio track? + errorlog("this shouldn't occur, since only video tracks are expected"); + try { + track.contentHint = session.audioContentHint; + } catch(e){ + errorlog(e); + } + } + if (session.screenShareState && session.screenshareContentHint && (track.kind === "video")){ // I need to check if this is actually a screenshare before setting the hint (sstype=3) + try { + track.contentHint = session.screenshareContentHint; + } catch(e){ + errorlog(e); + } + } else if (session.contentHint && (track.kind === "video")){ + try { + track.contentHint = session.contentHint; + } catch(e){ + errorlog(e); + } + } + + + if (session.whipOut && session.whipOut.getSenders){ // should only be 0 or 1 video sender, ever. + //var added = false; + session.whipOut.getSenders().forEach((sender) => { // I suppose there could be a race condition between negotiating and updating this. if joining at the same time as changnig streams? + if (sender.track && sender.track.kind == "video") { + errorlog("Replacing track"); + sender.replaceTrack(track); // replace may not be supported by all browsers. eek. + //sender.track.enabled = true; + //added = true; + } + }); + } + + for (UUID in session.pcs){ + var videoAdded = false; + try { + if ("realUUID" in session.pcs[UUID]){continue;} + if ((session.pcs[UUID].guest == true) && (session.roombitrate === 0)) { + log("room rate restriction detected. No videos will be published to other guests"); + } else if (session.pcs[UUID].allowVideo == true) { // allow + + // for any connected peer, update the video they have if connected with a video already. + var added = false; + var senders = getSenders2(UUID); + senders.forEach((sender) => { // I suppose there could be a race condition between negotiating and updating this. if joining at the same time as changnig streams? + if (added) { + return; + } + if (sender.track && sender.track.kind == "video") { + sender.replaceTrack(track); // replace may not be supported by all browsers. eek. + log("Track replaced"); + log(track); + sender.track.enabled = true; + added = true; + } + }); + if (added == false) { + videoAdded = true; + session.pcs[UUID].addTrack(track, session.videoElement.srcObject); // can't replace, so adding + setTimeout(function(uuid){session.optimizeBitrate(uuid);},session.rampUpTime, UUID); // 3 seconds lets us ramp up the quality a bit and figure out the total bandwidth quicker + } + } + } catch (e) { + errorlog(e); + } + + if (iOS || iPad){ ///////// THIS IS A FIX FOR iOS 15.4. When a video is loaded (view/push), the bitrate from iOS devices is stuck low, and resolution needs toggle to fix. + // videoAdded value needs to be deleted from above also + if (SafariVersion && (SafariVersion<=13)){ + // + } else if (videoAdded){ + setTimeout(function(uuid){ + session.setScale(uuid, null); + }, 2000, UUID); + setTimeout(function(uuid){ + var scale = 100;session.setScale + if (session.pcs[uuid].scale){ + scale = session.pcs[uuid].scale; + } + session.setScale(uuid, scale); + },5000, UUID); + } + } + } + if (track.kind === "audio"){ + session.applyIsolatedChat(); + } + session.refreshScale(); +} + + +async function grabAudio(selector = "#audioSource", trackid = null, override = false, callbackUUID = false, callback = false) { // trackid is the excluded track , callback is UUID + + + if (activatedPreview == true) { + log("activated preview return 2"); + return; + } + activatedPreview = true; + log("TRACK EXCLUDED:" + trackid); + + try { + var audioSelect = document.querySelector(selector).querySelectorAll("input"); + var audioExcludeList = []; + for (var i = 0; i < audioSelect.length; i++) { + try { + if ("screen" == audioSelect[i].dataset.type) { // skip already excluded ---------- !!!!!! DOES THIS MAKE SENSE? TODO: CHECK + if (audioSelect[i].checked) { + audioExcludeList.push(audioSelect[i]); + } + } + } catch (e) { + errorlog(e); + } + } + } catch (e) { + errorlog(e); + } + + try { + if (session.videoElement && session.videoElement.srcObject) { + session.videoElement.srcObject.getAudioTracks().forEach(function(track) { // TODO: Confirm that I even need this? + for (var i = 0; i < audioExcludeList.length; i++) { + try { + if (audioExcludeList[i].label == track.label) { + warnlog("DONE"); + return; + } + } catch (e) {errorlog(e);} + } + if (trackid && (track.id == trackid)) { + warnlog("SKIPPED EXCLUDED TRACK?"); + return; + } + session.videoElement.srcObject.removeTrack(track); + track.stop(); // remove then stop. + }); + } else { // if no stream exists + checkBasicStreamsExist(); + } + } catch (e) { + errorlog(e); + } + + try { + if (session.streamSrc){ + session.streamSrc.getAudioTracks().forEach(function(track) { + for (var i = 0; i < audioExcludeList.length; i++) { + try { + if (audioExcludeList[i].label == track.label) { + warnlog("EXCLUDING TRACK; PROBABLY SCREEN SHARE"); + return; + } + } catch (e) {errorlog(e);} + } + if (trackid && (track.id == trackid)) { + warnlog("SKIPPED EXCLUDED TRACK?"); + return; + } + session.streamSrc.removeTrack(track); + track.stop(); + }); + } else { // if no stream exists + checkBasicStreamsExist(); + } + } catch (e) { + errorlog(e); + } + + try { + if (session.streamSrcClone){ + session.streamSrcClone.getAudioTracks().forEach(function(track) { + for (var i = 0; i < audioExcludeList.length; i++) { + try { + if (audioExcludeList[i].label == track.label) { + warnlog("EXCLUDING TRACK; PROBABLY SCREEN SHARE"); + return; + } + } catch (e) {errorlog(e);} + } + if (trackid && (track.id == trackid)) { + warnlog("SKIPPED EXCLUDED TRACK?"); + return; + } + session.streamSrcClone.removeTrack(track); + track.stop(); + }); + } + } catch (e) { + errorlog(e); + } + + var streams = await getAudioOnly(selector, trackid, override); // Get audio streams + + try { + log("STREAMS: "+streams.length); + + for (var i = 0; i < streams.length; i++) { + streams[i].getAudioTracks().forEach(function(track) { + try { + session.streamSrc.addTrack(track); // add video track to the preview video + + track.onended = function(){ + errorlog("Track ended unexpectedly"); + if (!session.cleanOutput){ + toggleSettings(true); // forceshow + } + }; + log("ok?"); + // applySavedAudioSettings(track); ## this doesn't work as echo-cancellation(+) needs to be applied via getuserMedia only. + } catch(e){ + errorlog(e); + } + }); + } + } catch(e){errorlog(e);} + + if (Firefox && !FirefoxEnumerated){ + if (session.streamSrc && session.streamSrc.getTracks().length){ + FirefoxEnumerated=true; + enumerateDevices().then(gotDevices); + } + } + + if (callback){ + callback(); + } + senderAudioUpdate(callbackUUID); +} + +session.toggleSoloChat = function(UUID, event=false){ // ==> applyIsolatedChat -- this should be trigger by the director only I think + + if (session.director){ + if (!session.directorEnabledPPT){ + warnUser("Enable the director's microphone first.",2000); + return false; + } + } + + if (Firefox){ + warnlog("Solo talk support for Firefox is currently experimental"); + } + + var msg = {}; + msg.micIsolate = false; + + if (session.soloChatUUID.includes(UUID)){ // already added, so lets toggle off + session.soloChatUUID.splice(session.soloChatUUID.indexOf(UUID), 1); // Toggles. Adds target to soloChatUUID list + msg.lowerVolume = false; + } else { + session.soloChatUUID.push(UUID); //not added, so lets toggle on + msg.lowerVolume = true; + } + + if (event){ + if ( event.ctrlKey || event.metaKey){ + if (session.soloChatUUID.includes(UUID)){ + msg.micIsolate = 1; + } + } + } + + session.sendRequest(msg, UUID); + + log(session.soloChatUUID); + + var ele = document.querySelector('[data-action-type="solo-chat"][data--u-u-i-d="'+UUID+'"]'); // [data--u-u-i-d="'+UUID+'"] // this all just updates the buttons + log(ele); + + var ret = 0; + + if (session.soloChatUUID.includes(UUID)){ + if (msg.micIsolate){ + ret = 2; + ele.classList.add("altpress"); // we will do this later. + ele.value = 2; + } else { + ret = 1; + ele.value = 1; + } + } else { + ele.classList.remove("pressed"); ele.ariaPressed = "false"; + ele.classList.remove("altpress"); + ele.value = 0; + } + + session.applySoloChat(false); + return ret + +}; +/////////////////////// + +session.togglePrivateChat = function(ele){ + var msg = {}; + warnlog(ele); + if (ele.value == 0) { + msg.micIsolate = true; + ele.value = 1; + ele.classList.add("pressed"); ele.ariaPressed = "true"; + } else { + msg.micIsolate = false; + ele.value = 0; + ele.classList.remove("pressed"); ele.ariaPressed = "false"; + } + session.sendRequest(msg, ele.dataset.UUID); + warnlog(msg); +}; + +// we call this via session.applyIsolatedChat, just in case +session.applyIsolatedVolume = function(){ // mutes outbound mic audio; for guests, and not the director + + var i = session.lowerVolume.length; + while (i--){ + if (!(session.lowerVolume[i] in session.rpcs)){ // clean up dead connections + session.lowerVolume.splice(i, 1); + } + } + + var soloMode = false; + + /* if (!(session.cleanOutput)){ + if (session.lowerVolume.length){ + getById("header").classList.add('orange'); + getById("head6").classList.remove('hidden'); + } else if (session.audioGain === 0){ + // do nothing. + } else { + getById("header").classList.remove('orange'); + getById("head6").classList.add('hidden'); + } + } */ + + if (session.lowerVolume.length){ + soloMode = true; + } + + if (soloMode){ + for (var UUID in session.rpcs){ + if (session.lowerVolume.includes(UUID)){ + if (session.rpcs[UUID].videoElement && (session.rpcs[UUID].savedVolume!==false)){ // isolated + session.rpcs[UUID].videoElement.volume = session.rpcs[UUID].savedVolume; + session.rpcs[UUID].savedVolume = false; + } + continue; + } + if (session.rpcs[UUID].videoElement && (session.rpcs[UUID].savedVolume==false)){ // not isolated + session.rpcs[UUID].savedVolume = session.rpcs[UUID].videoElement.volume; + session.rpcs[UUID].videoElement.volume = session.rpcs[UUID].savedVolume*0.25; + } + } + } else { + for (var UUID in session.rpcs){ + if (session.rpcs[UUID].videoElement && (session.rpcs[UUID].savedVolume!==false)){ // isolated + session.rpcs[UUID].videoElement.volume = session.rpcs[UUID].savedVolume; + session.rpcs[UUID].savedVolume = false; + } + } + } +} + +session.applyIsolatedChat = function(UUID=false){ // mutes outbound mic audio; for guests, and not the director + log("applyIsolatedChat"); + session.applyIsolatedVolume(); // this toggle the speaker output + + var i = session.micIsolated.length; + while (i--){ + if (!(session.micIsolated[i] in session.pcs) && !(session.micIsolated[i] in session.rpcs)){ + session.micIsolated.splice(i, 1); + } + } + + var muteList = [...session.micIsolated]; // one thing I hate about Javascript. Doesn't actually copy arrays. + var soloMode = false; + + if (session.micIsolatedAutoMute){ // session.micIsolatedAutoMute + soloMode = true; + session.micIsolatedAutoMute.forEach(uid =>{ + if (!muteList.includes(uid) && (uid in session.rpcs || uid in session.pcs)){ + muteList.push(uid); + } + }); + } + + if (muteList.length){ + soloMode = true; + } + + if (!(session.cleanOutput)){ + if (soloMode){ + getById("header").classList.add('orange'); + getById("head6").classList.remove('hidden'); + } else if (session.audioGain === 0){ + // do nothing. + } else { + getById("header").classList.remove('orange'); + getById("head6").classList.add('hidden'); + } + } + + ///// + if (session.directorSpeakerMuted!==null){ + for (var uuid in session.rpcs){ + try{ + var receivers = getReceivers2(uuid);//session.rpcs[uuid].getReceivers(); + for (var i=0; i { + if (!sender.track){return;} + if (sender.track.kind !== "audio"){return;} + + var settings = {}; + if (!soloMode){ + settings.active = true; + session.pcs[UUID].audioMutedOverride = false; + } else if (muteList.indexOf(UUID)>=0){ + settings.active = true; + session.pcs[UUID].audioMutedOverride = false; + } else { + log("MUTING via session.applyIsolatedChat"); + settings.active = false; + session.pcs[UUID].audioMutedOverride = true + } + setEncodings(sender, settings); + }); + } catch(e){errorlog(e);} + } else { + for (var UUID in session.pcs){ + try { + var senders = getSenders2(UUID); + senders.forEach((sender) => { + if (!sender.track){return;} + if (sender.track.kind !== "audio"){return;} + + var settings = {}; + if (!soloMode){ + settings.active = true; + session.pcs[UUID].audioMutedOverride = false; + } else if (muteList.indexOf(UUID)>=0){ + settings.active = true; + session.pcs[UUID].audioMutedOverride = false; + } else { + log("MUTING via session.applyIsolatedChat"); + settings.active = false; + session.pcs[UUID].audioMutedOverride = true; + } + setEncodings(sender, settings); + }); + } catch(e){errorlog(e);} + } + } +} + +var FirefoxSenders = {}; + +function setEncodings(sender, settings=null, callback=null, cbarg=null){ + if (!settings){ + if (!(sender.encodingsQueue)){ // not set + return; + } else if (!sender.encodingsQueue.length){ // none left + return; + } + } else if (!("encodingsQueue" in sender)){ + sender.encodingsQueue = [[settings, callback, cbarg]]; + } else { + sender.encodingsQueue.push([settings, callback, cbarg]); + } + + if (sender.encodingsQueueActive){return;} + + try { + sender.encodingsQueueActive = true; // we're now busy. + + var options = sender.encodingsQueue.shift(); + settings = options[0]; + callback = options[1]; + cbarg = options[2]; + + const params = sender.getParameters(); + if (!params.encodings || (params.encodings.length==0)){ + params.encodings = [{}]; + } + var changed = false; + for (var setting in settings){ + if (settings[setting]===null){ + if (setting in params.encodings[0]){ + delete params.encodings[0][setting]; + changed = true + } + } else { + if (setting in params.encodings[0]){ + if (params.encodings[0][setting] !== settings[setting]){ + changed = true + } + } else { + changed = true + } + params.encodings[0][setting] = settings[setting]; + } + } + + log(settings); + + // if old Firefox, see if I can do something other than Active? + + if (!changed && !Firefox && !SafariVersion){ + log("SET ENCODINGS MATCH INPUT; skipping"); + if (callback){ + if (cbarg){ + setTimeout(function(){callback(cbarg);},0); + } else { + setTimeout(function(){callback();},0); + } + } + sender.encodingsQueueActive = false; + setEncodings(sender); + return; + } + + if (Firefox && !(Firefox >=110)){ // https://developer.mozilla.org/en-US/docs/Web/API/RTCRtpEncodingParameters now supported in v110, but old versions will need this function still + if ("active" in settings){ + warnlog("Firefox does not support track active state. We will use enable/disable for that instead."); + if (FirefoxSenders.sender){ + if (FirefoxSenders.sender.lastState === false){ + FirefoxSenders.sender.activeState = settings.active; + // already set to false, so should stay disabled + } else { + FirefoxSenders.sender.activeState = settings.active; + sender.track.enabled = settings.active; // either true or false + } + } else { + FirefoxSenders.sender = {lastState: sender.track.enabled, activeState: settings.active}; + sender.track.enabled = settings.active; + } + + delete settings.active; + if (!Object.keys(settings).length){ + if (callback){ + if (cbarg){ + setTimeout(function(){callback(cbarg);},0); + } else { + setTimeout(function(){callback();},0); + } + } + log("COMPELTED FIREFOX SET ENCODINGS"); + sender.encodingsQueueActive = false; + setEncodings(sender); + return; + } + } + } else if (Firefox){ // Firefox , all versions, don't support active state with audio yet.?? GAhhhhhhhh! + if (("track" in sender) && ("kind" in sender.track) && (sender.track.kind == "audio")){ + if ("active" in settings){ + warnlog("Firefox does not support track active state with AUDIO yet... We will use enable/disable for that instead."); + if (FirefoxSenders.sender){ + if (FirefoxSenders.sender.lastState === false){ + FirefoxSenders.sender.activeState = settings.active; + // already set to false, so should stay disabled + } else { + FirefoxSenders.sender.activeState = settings.active; + sender.track.enabled = settings.active; // either true or false + } + } else { + FirefoxSenders.sender = {lastState: sender.track.enabled, activeState: settings.active}; + sender.track.enabled = settings.active; + } + + delete settings.active; + if (!Object.keys(settings).length){ + if (callback){ + if (cbarg){ + setTimeout(function(){callback(cbarg);},0); + } else { + setTimeout(function(){callback();},0); + } + } + log("COMPELTED FIREFOX SET ENCODINGS"); + sender.encodingsQueueActive = false; + setEncodings(sender); + return; + } + } + + } + } + + sender.setParameters(params).then(() => { + if (callback){ + if (cbarg){ + setTimeout(function(){callback(cbarg);},0); + } else { + setTimeout(function(){callback();},0); + } + } + sender.encodingsQueueActive = false; + setEncodings(sender); + }).catch((e)=>{ + errorlog(e); + sender.encodingsQueueActive = false; + setEncodings(sender); + }); + } catch(e){ + errorlog(e); + sender.encodingsQueueActive = false; + } +} + +session.applySoloChat = function(apply=true){ // mutes outbound mic audio; ;; does the actual solo chat muting for the director + if (session.director===false){ + session.applyIsolatedChat(); + return; + } else if (!session.directorEnabledPPT){ + return; + } + + log("applySoloChat()"); + + var i = session.soloChatUUID.length; + while (i--){ + if (!(session.soloChatUUID[i] in session.pcs)){ + session.soloChatUUID.splice(i, 1); + log("splicing out: "+i); + } + } + + for (var uuid in session.pcs){ // not sure what to do here wrt to screen tracks + try { + var senders = getSenders2(uuid); + senders.forEach((sender) => { + if (!sender.track){return;} + if (sender.track.kind !== "audio"){return;} + + var settings = {}; + + if (session.soloChatUUID.length && (session.soloChatUUID.includes(uuid))){ + settings.active = true; + setEncodings(sender, settings, function(uid){ + log("2: "+uid); + try { + document.querySelectorAll('[data-action-type="solo-chat"][data--u-u-i-d="'+uid+'"]')[0].classList.add("pressed"); + document.querySelectorAll('[data-action-type="solo-chat"][data--u-u-i-d="'+uid+'"]')[0].ariaPressed = "true"; + document.querySelectorAll('[data-action-type="solo-chat"][data--u-u-i-d="'+uid+'"]')[0].classList.remove("hint"); + } catch(e){ + warnlog(e); + } + }, uuid); + + + } else if (session.soloChatUUID.length==0){ + settings.active = true; + setEncodings(sender, settings, function(uid){ + log(uid); + try { + document.querySelectorAll('[data-action-type="solo-chat"][data--u-u-i-d="'+uid+'"]')[0].classList.remove("pressed"); + document.querySelectorAll('[data-action-type="solo-chat"][data--u-u-i-d="'+uid+'"]')[0].ariaPressed = "false"; + document.querySelectorAll('[data-action-type="solo-chat"][data--u-u-i-d="'+uid+'"]')[0].classList.remove("hint"); + } catch(e){ + warnlog(e); + } + }, uuid); + } else { + settings.active = false; + setEncodings(sender, settings, function(uid){ + warnlog("muted the output to:"+ uid); + try { + document.querySelectorAll('[data-action-type="solo-chat"][data--u-u-i-d="'+uid+'"]')[0].classList.remove("pressed"); + document.querySelectorAll('[data-action-type="solo-chat"][data--u-u-i-d="'+uid+'"]')[0].ariaPressed = "false"; + document.querySelectorAll('[data-action-type="solo-chat"][data--u-u-i-d="'+uid+'"]')[0].classList.add("hint"); + } catch(e){ + warnlog(e); + } + }, uuid); + + } + }); + + } catch(e){errorlog(e);} + } + if (apply==false){ + if (session.soloChatUUID.length){ + session.muted_savedState=session.muted; + session.muted=false; + data = {}; + data.muteState = session.muted; + for (var i=0;i{ + try { + trk.contentHint = session.audioContentHint; + } catch(e){ + errorlog(e); + } + }); + } + + if (session.whipOut && session.whipOut.getSenders && tracks.length){ // mixMinus won't work with meshcast, so don't bother. + session.whipOut.getSenders().forEach((sender) => { // I suppose there could be a race condition between negotiating and updating this. if joining at the same time as changnig streams? + if (sender.track && sender.track.kind == "audio") { + tracks.forEach(trk=>{ + sender.replaceTrack(trk); + }) + } + }); + } + + for (UUID in session.pcs) { + if ("realUUID" in session.pcs[UUID]){continue;} // do not process the screen share audio + if (session.pcs[UUID].allowAudio == true) { + var senders = getSenders2(UUID); + if (session.mixMinus){ + log("mixMinus START .."); + var STRM = mixMinusAudio(UUID); + if (!STRM){continue;} + STRM.getAudioTracks().forEach(trk=>{ + + if (session.audioContentHint){ + trk.contentHint = session.audioContentHint; + } + + var added = false; + senders.forEach((sender) => { + if (added) { + if (sender.track && (sender.track.kind == "audio")){ + sender.track.enabled = false; + } + return; + } + if (sender.track && (sender.track.kind == "audio")) { + sender.replaceTrack(trk); + sender.track.enabled = true; + added = true; + warnlog("ADDED 5"); + } + }); + if (added) { + return; + } + session.pcs[UUID].addTrack(trk, STRM); + }); + continue; + } + senders.forEach((sender) => { + var good = false; + if (sender.track && sender.track.id && (sender.track.kind == "audio")) { + tracks.forEach(function(track) { // audio also + if (track.id == sender.track.id) { + good = true; + } + }); + } else { // video or something else; ignore it. + return; + } + if (good) { + return; + } + sender.track.enabled = false; + //session.pcs[UUID].removeTrack(sender); // Apparently removeTrack causes renogiation; also kills send/recv. + }); + + if (tracks.length) { + tracks.forEach(function(track) { + var matched = false; + var senders = getSenders2(UUID); + senders.forEach((sender) => { + if (sender.track && sender.track.id && (sender.track.kind == "audio")) { + warnlog(sender.track.id + " " + track.id); + if (sender.track.id == track.id) { + warnlog("MATCHED 1"); + matched = true; + } + } + }); + if (matched) { + return; + } + var added = false; + var senders = getSenders2(UUID); + senders.forEach((sender) => { + if (added) { + return; + } + if (sender.track && (sender.track.kind == "audio") && (sender.track.enabled == false)) { + sender.replaceTrack(track); + sender.track.enabled = true; + added = true; + warnlog("ADDED 2"); + } + }); + if (added) { + return; + } + var sender = session.pcs[UUID].addTrack(track, session.videoElement.srcObject); + }); + } else { + var senders = getSenders2(UUID); + senders.forEach((sender) => { + if (sender.track && sender.track.kind == "audio") { + sender.track.enabled = false; // (trying this instead) + //session.pcs[UUID].removeTrack(sender); // Apparently removeTrack causes renogiation; also kills send/recv. + } + }); + } + } + } + if (session.director!==false){ + session.applySoloChat(); // mute streams that should be muted if a director + } + session.applyIsolatedChat(); + + try { + if (toggleSettingsState){ + updateConstraintSliders(); + } + } catch(e){} + + if (callback){ + try{ + var data = {}; + data.UUID = callback; + data.audioOptions = listAudioSettingsPrep(); + sendMediaDevices(data.UUID); + session.sendMessage(data, data.UUID); + } catch(e){} + } + } catch (e) { + errorlog(e); + } + if (document.getElementById("gowebcam")) { + document.getElementById("gowebcam").dataset.audioready = true; + if (document.getElementById("gowebcam").dataset.ready && (document.getElementById("gowebcam").dataset.ready=="true")){ + document.getElementById("gowebcam").disabled = false; + miniTranslate(document.getElementById("gowebcam"),"start"); + document.getElementById("gowebcam").focus(); + } + } +} + +async function press2talk(clean = false) { + var ele = getById("press2talk"); + ele.style.minWidth = "127px"; + ele.style.padding = "7px"; + getById("settingsbutton").classList.remove("hidden"); + + if (!document.getElementById("controls_director") && session.showDirector){ + createDirectorOnlyBox(); + } + + if (session.taintedSession){ + var msg = {}; + msg.virtualHangup = false; + session.sendMessage(msg); + } + + log("DIRECTOR STREAM SETUP"); + + if (getById("press2talk").dataset.enabled == true){log("already enabled");return;} + getById("press2talk").dataset.enabled = true; + getById("press2talk").outerHTML = ""; + getById("mutebutton").classList.remove("hidden"); + getById("hangupbutton2").classList.remove("hidden"); + + if (!session.showDirector && (session.recordLocal!==false)){ + getById("recordLocalbutton").classList.remove("hidden"); + } + + if (session.screenshareType===3){ + getById("screenshare3button").className = "float"; + getById("screensharebutton").className = "float hidden"; + getById("screenshare2button").className = "float hidden"; + } else if (session.screenshareType===1){ + getById("screensharebutton").className = "float"; + getById("screenshare3button").className = "float hidden"; + getById("screenshare2button").className = "float hidden"; + } else if (session.screenshareType===2){ + getById("screenshare2button").className = "float"; + getById("screensharebutton").className = "float hidden"; + getById("screenshare3button").className = "float hidden"; + } else if (session.broadcast===null){ + // sstype=1, since in self-broadcast mode + getById("screensharebutton").className = "float"; + getById("screenshare2button").className = "float hidden"; + getById("screenshare3button").className = "float hidden"; + } else { + // sstype=3, since not in broadcast mode + getById("screensharebutton").className = "float hidden"; + getById("screenshare2button").className = "float hidden"; + getById("screenshare3button").className = "float"; + } + + + checkBasicStreamsExist(); + session.videoElement.id = "videosource"; // could be set to UUID in the future + session.videoElement.dataset.menu = "context-menu-video"; + + if (session.streamID){ + session.videoElement.dataset.sid = session.streamID; + } + + + // videosource + session.videoElement.muted = true; + session.videoElement.autoplay = true; + session.videoElement.controls = session.showControls || false; + session.videoElement.setAttribute("playsinline",""); + + if (document.getElementById("videoContainer_director")){ + getById("videoContainer_director").appendChild(session.videoElement); + } else { + getById("miniPerformer").appendChild(session.videoElement); + } + + if (session.screenShareElement && document.getElementById("videoScreenContainer_director")){ + getById("videoScreenContainer_director").appendChild(session.screenShareElement); + } else if (session.screenShareElement){ + getById("miniPerformer").appendChild(session.screenShareElement); + } + + session.videoElement.title = "This is the preview of the Director's audio and video output."; + + session.videoElement.onpause = (event) => { // prevent things from pausing; human or other + + if (!((event.ctrlKey) || (event.metaKey) )){ + log("Video paused; auto playing"); + event.currentTarget.play().then(_ => { + log("playing 9"); + }).catch(warnlog); + } + }; + + session.videoElement.addEventListener('click', function(e) { // show stats of video if double clicked + log("click"); + try { + if ((e.ctrlKey)||(e.metaKey)){ + e.preventDefault(); + + //////////////////////// + + var [menu, innerMenu] = statsMenuCreator(); + + ////////////////////////////////// + + menu.interval = setInterval(printMyStats,session.statsInterval, innerMenu); + printMyStats(innerMenu); + e.stopPropagation(); + + return false; + } + } catch(e){errorlog(e);} + }); + + updatePushId(); + + + /* if (session.directorEnabledPPT){ + enumerateDevices().then(gotDevices).then(async function() { + console.log("done"); + toggleSettings(); + }); + + return; + } */ + //await toggleSettings(); + + var constraint = {video: false, audio: true}; + + if (session.videoDevice){ + constraint.video = true; + } + if (session.audioDevice===0){ + constraint.audio = false; + } + requestBasicPermissions(constraint, function(){ + log("requestBasicPermissions done"); + enumerateDevices().then(gotDevices).then(async function() { + log("enumerateDevices+gotDevices complete"); + + pokeIframeAPI('director-share', true, false, session.streamID); // director has started publishing; even if no audio/video. + + log("session.directorEnabledPPT: " +session.directorEnabledPPT); + + if (session.directorEnabledPPT){ + return; + } + + if (session.audioDevice!==0){ // change from Auto to Selected Audio Device + log("SETTING AUDIO DEVICE!!"); + activatedPreview = false; + await grabAudio("#audioSource3"); + + } + + + + if (session.videoDevice !== 0) { + activatedPreview = false; + if (session.quality !== false) { + await grabVideo(session.quality, 'videosource', "#videoSource3"); + } else { + //session.quality_wb = parseInt(getById("webcamquality").elements.namedItem("resolution").value); + await grabVideo(session.quality_wb || 0, 'videosource', "#videoSource3"); + } + } + + + if (session.videoMutedFlag){ + session.videoMuted = true; + toggleVideoMute(true); + } + + session.directorEnabledPPT = true; + toggleMute(true); + + //await toggleSettings(); + + log("session.seeding: " +session.seeding); + + if (session.seeding){ + setTimeout(function(){meshcast()},1000); + return; + } + + if (session.autorecord || session.autorecordlocal){ + log("AUTO RECORD START"); + setTimeout(function(v){ + + var videoKbps = 4000; + if (session.recordLocal !== false) { + videoKbps = session.recordLocal; + } + + if (session.director){ + recordVideo(document.querySelector("[data-action-type='recorder-local'][data-sid='"+session.streamID+"']"), null, videoKbps) + } else if (v.stopWriter || v.recording){ + + } else if (v.startWriter){ + v.startWriter(); + } else { + recordLocalVideo(null, videoKbps, v) + } + },2000, session.videoElement); + } + + session.seeding=true; + await session.seedStream(); + //meshcast(); + }); + }); +}; // publishdirector + +function statsMenuCreator(){ + if (getById("menuStatsBox")){ + clearInterval(getById("menuStatsBox").interval); + getById("menuStatsBox").remove(); + } + + var menu = document.createElement("div"); + menu.id = "menuStatsBox"; + menu.className = "debugStats remotestats"; + getById('main').appendChild(menu); + + menu.style.left = parseInt(Math.random()*10)+15+"px" + menu.style.top = parseInt(Math.random()*10)+"px" + + menu.innerHTML="

    Statistics

    "; + var menuCloseBtn = document.createElement("button"); + menuCloseBtn.className="close"; + menuCloseBtn.innerHTML="×"; + menu.appendChild(menuCloseBtn); + + var innerMenu = document.createElement("div"); + menu.appendChild(innerMenu); + + menuCloseBtn.addEventListener('click', function(eve) { + clearInterval(menu.interval); + eve.currentTarget.parentNode.remove(); + eve.preventDefault(); + eve.stopPropagation(); + }); + return [menu, innerMenu]; +} + + +// WEBCAM +session.publishStream = function(v){ // stream is used to generated an SDP + log("STREAM SETUP"); + + if (session.transcript){ + setTimeout(function(){setupClosedCaptions();},1000); + } + + if (!session.streamSrc){ + checkBasicStreamsExist(); + } + + session.streamSrc.oninactive = function streamoninactive() { + warnlog('Stream inactive'); + if (session.videoElement.recording){ + session.videoElement.recorder.stop(); + } + }; + + if (session.streamSrc.getVideoTracks().length==0){ + warnlog("NO VIDEO TRACK INCLUDED"); + } + + if (session.streamSrc.getAudioTracks().length==0){ + warnlog("NO AUDIO TRACK INCLUDED"); + } + + + var container = document.createElement("div"); + v.container = container; + container.id = "container"; + + + if (session.cleanOutput){ + container.style.height = "100%"; + v.style.maxWidth = "100%"; + v.style.boxShadow = "none"; + } + + if (session.cover){ + container.style.setProperty('height', '100%', 'important'); + } + + //container.className = "vidcon"; + getById("gridlayout").appendChild(container); + + v.className = "tile"; //"tile task"; TODO: get working (will add task later on instead) + + + v.muted = true; + v.autoplay = true; + if (session.showControls!==null){ + v.controls = session.showControls; + } else if (session.mobile){ + v.controls = true; + } else { + v.controls = session.showControls || false; + } + v.setAttribute("playsinline",""); + v.id = "videosource"; // could be set to UUID in the future + v.oncanplay = null; + + session.videoElement = v; + + container.appendChild(v); + + toggleMute(true); + + if (session.nopreview){ + v.style.display="none"; + container.style.display="none"; + } + + + if (((session.roomid===false || session.roomid==="") && (session.quality===false)) || session.forceMediaSettings){ + try { + if ((session.quality_wb!==false) && (session.quality===false)){ + getById("webcamquality3").elements.namedItem("resolution").value = session.quality_wb; + } else if (session.quality!==false){ + getById("webcamquality3").elements.namedItem("resolution").value = session.quality; + } + getById("gear_webcam3").style.display = "inline-block"; + getById("webcamquality3").onchange = function(event) { + if (parseInt(getById("webcamquality3").elements.namedItem("resolution").value) == 2) { + if (session.maxframeRate===false){ + session.maxframeRate = 30; + session.maxframeRate_q2 = true; + } + } else if (session.maxframeRate_q2){ + session.maxframeRate = false; + session.maxframeRate_q2 = false; + } + activatedPreview = false; + session.quality_wb = parseInt(getById("webcamquality3").elements.namedItem("resolution").value); + grabVideo(session.quality_wb, "videosource", "select#videoSource3"); + }; + } catch (e) {errorlog(e);} + } + + + var bigPlayButton = document.getElementById("bigPlayButton"); + if (bigPlayButton){ + bigPlayButton.parentNode.removeChild(bigPlayButton); + } + + if (session.streamID){ + session.videoElement.dataset.sid = session.streamID; + } + + if (session.statsMenu){ + var [menu, innerMenu] = statsMenuCreator(); + menu.interval = setInterval(printMyStats,session.statsInterval, innerMenu); + printMyStats(innerMenu); + } + + if (session.director){ // the director doesn't load a webcam by default anyways. + // audio is not mucked with + } else if (session.scene!==false){ // it's a scene, and there are no previews in a scene. + //setTimeout(function(){updateMixer();},10); + } else if (session.roomid!==false){ + if (session.roomid===""){ + if (!session.view || (session.view==="")){ + if (session.fullscreen){ + session.windowed = false; + } else { + v.className = "myVideo"; //"myVideo task"; TODO: get working + session.windowed = true; + container.classList.add("vidcon"); + } + getById("mutespeakerbutton").classList.add("hidden"); + + applyMirror(session.mirrorExclude); + + container.style.width="100%"; + //container.style.height="100%"; + + container.style.alignItems = "center"; + container.backgroundColor = "#666"; + + setTimeout(function (){dragElement(v);},1000); + play(); + } else { + session.windowed = false; + applyMirror(session.mirrorExclude); + play(); + //setTimeout(function(){updateMixer();},10); + } + } else { + //session.cbr=0; // we're just going to override it + if (session.stereo==5){ // not a scene or director, so we will assume its a guest. changing to stereo=3 + session.stereo=3; + } + session.windowed = false; + applyMirror(session.mirrorExclude); + + if (session.include.length){ + play(); + } + + //setTimeout(function(){updateMixer();},10); + } + } else { + + if (session.fullscreen){ + session.windowed = false; + } else { + v.className = "myVideo"; //"myVideo task"; TODO: get working + container.classList.add("vidcon"); + session.windowed = true; + } + getById("mutespeakerbutton").classList.add("hidden"); + + applyMirror(session.mirrorExclude); + + container.style.width="100%"; + //container.style.height="100%"; + //container.style.display = "flex"; + + container.style.alignItems = "center"; + container.backgroundColor = "#666"; + + setTimeout(function (){dragElement(v);},1000); + + } + + v.onpause = (event) => { // prevent things from pausing; human or other + if (!((event.ctrlKey) || (event.metaKey) )){ + log("Video paused; auto playing"); + event.currentTarget.play().then(_ => { + log("playing 10"); + }).catch(warnlog); + } + }; + + v.addEventListener('click', function(e) { + log("click"); + try { + if ((e.ctrlKey)||(e.metaKey)){ + e.preventDefault(); + + var [menu, innerMenu] = statsMenuCreator(); + + menu.interval = setInterval(printMyStats,session.statsInterval, innerMenu); + + printMyStats(innerMenu); + e.stopPropagation(); + return false; + } + } catch(e){errorlog(e);} + }); + + v.touchTimeOut = null; + v.touchLastTap = 0; + v.touchCount = 0; + + v.addEventListener('touchend', function(event) { + if (session.disableMouseEvents){return;} + log("touched"); + + //document.ontouchup = null; + //document.onmouseup = null; + document.onmousemove = null; + document.ontouchmove = null; + + var currentTime = new Date().getTime(); + var tapLength = currentTime - v.touchLastTap; + clearTimeout(v.touchTimeOut); + if (tapLength < 500 && tapLength > 0) { + /// + log("double touched"); + v.touchCount+=1; + event.preventDefault(); + if (v.touchCount<5){ + v.touchLastTap = currentTime; + return false; + } + v.touchLastTap = 0; + v.touchCount=0; + + var [menu, innerMenu] = statsMenuCreator(); + + menu.interval = setInterval(printMyStats,session.statsInterval, innerMenu); + + printMyStats(innerMenu); + event.stopPropagation(); + return false; + ////// + } else { + v.touchCount=1; + v.touchLastTap = currentTime; + + v.touchTimeOut = setTimeout(function(vv) { + clearTimeout(vv.touchTimeOut); + vv.touchLastTap = 0; + vv.touchCount=0; + }, 5000, v); + + } + + }); + + updateReshareLink(); + pokeIframeAPI('started-camera'); // depreciated + pokeIframeAPI('camera-share', true); + + if (session.videoMutedFlag){ + session.videoMuted = true; + toggleVideoMute(true); + } + + if (!gotDevices2AlreadyRan){ + enumerateDevices().then(gotDevices2); // this is needed for iOS; was previous set to timeout at 100ms, but would be useful everywhere I think + } + + v.dataset.menu = "context-menu-video"; + if (!session.cleanOutput){ + v.classList.add("task"); // this adds the right-click menu + } + + session.postPublish(); + + if (session.autorecord || session.autorecordlocal){ + log("AUTO RECORD START"); + setTimeout(function(v){ + var videoKbps = 4000; + if (session.recordLocal !== false) { + videoKbps = session.recordLocal; + } + + if (session.director){ + recordVideo(document.querySelector("[data-action-type='recorder-local'][data-sid='"+session.streamID+"']"), null, videoKbps) + } else if (v.stopWriter || v.recording){ + + } else if (v.startWriter){ + v.startWriter(); + } else { + recordLocalVideo(null, videoKbps, v) + } + },2000, v); + } + + setTimeout(function(){updateMixer();},10); + +}; // publishStream + +function stickyMessage(message){ + var textOverlay = getById("stickyMsgs"); + if (textOverlay) { + var spanOverlay = document.createElement("span"); + spanOverlay.innerHTML = message; + var closeBtn = document.createElement("button"); + closeBtn.className = "overlayCloseBtn"; + closeBtn.innerText = "X"; + closeBtn.onclick = function(){this.parentNode.remove();}; + textOverlay.appendChild(spanOverlay); + spanOverlay.appendChild(closeBtn); + textOverlay.classList.remove("hidden"); + } +} + +session.postPublish = async function(){ + log("Post publish"); + if (session.welcomeMessage){ + stickyMessage(session.welcomeMessage); + // getChatMessage(session.welcomeMessage, false, true, true); + } + + if (session.welcomeImage){ + var welcomeoverlay = document.createElement("img"); + welcomeoverlay.src = session.welcomeImage; + welcomeoverlay.className = "welcomeOverlay"; + document.body.appendChild(welcomeoverlay); + await sleep(2000); + setTimeout(function(welcomeoverlay){ + welcomeoverlay.style = "animation: fadeout 1s;" + setTimeout(function(welcomeoverlay){ + welcomeoverlay.remove(); + },990,welcomeoverlay); + }, 1000, welcomeoverlay); + } + + if (session.welcomeHTML){ + var welcomeHTML = document.createElement("div"); + welcomeHTML.innerHTML = session.welcomeHTML; + welcomeHTML.className = "welcomeOverlay"; + document.body.appendChild(welcomeHTML); + setTimeout(function(welcomeHTML){ + welcomeHTML.style = "animation: fadeout 1s;" + setTimeout(function(welcomeHTML){ + welcomeHTML.remove(); + },990,welcomeHTML); + }, 3000, welcomeHTML); + } + + clearInterval(session.updateLocalStatsInterval); + session.updateLocalStatsInterval = setInterval(function(){updateLocalStats();},session.statsInterval); + + pokeIframeAPI("screen-share-state", false); + + session.seeding=true; + session.seedStream(); + + if (session.whipOutput){ + whipOut(); + } + if (session.whepHost){ + whepOut(); + } + +} + + +async function publishScreen2(constraints, audioList=[], audio=true, overrideFramerate=false){ // webcam stream is used to generated an SDP + log("SCREEN SHARE SETUP"); + + if (!navigator.mediaDevices.getDisplayMedia){ + setTimeout(function(){ + if (iOS || iPad){ + warnUser("Sorry, but your iOS browser does not support screen-sharing.\n\nPlease see this guide for an alternative method to do so.", false, false); + } else if (session.mobile){ + warnUser("Sorry, your browser does not support screen-sharing.\n\nThe Android native app should support it though.", false, false); + } else { + warnUser("Sorry, your browser does not support screen-sharing.\n\nPlease use the desktop versions of Firefox or Chrome instead."); + } + },1); + return false; + } + if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1) { + if (!ElectronDesktopCapture){ + if (!(session.cleanOutput && session.cleanish==false)){ + warnUser("Enable Elevated Privileges to allow screen-sharing. (right click this window to see that option)"); + } + return false; + } + } + + var streams = []; + for (var i=1; i{ + if (getUserMediaRequestID !== gumID) { + warnlog("GET USER MEDIA CALL HAS EXPIRED 3"); + stream.getTracks().forEach(function(track) { + stream.removeTrack(track); + track.stop(); + log("stopping old track"); + }); + return; + } + streams.push(stream); + }).catch(errorlog); + } + } + + if (session.audioDevice === 0 ){ + constraints.audio = false; + } + + if (session.screenshareVideoOnly){ + constraints.audio = false; + } + + if ((constraints.video!==false) && (Object.keys(constraints.video).length==0)){ + constraints.video = true; + } + + + log(constraints); + getUserMediaRequestID+=1; + var gumID = getUserMediaRequestID; + return navigator.mediaDevices.getDisplayMedia(constraints).then(async function (stream){ + if (getUserMediaRequestID !== gumID) { + warnlog("GET USER MEDIA CALL HAS EXPIRED 3"); + stream.getTracks().forEach(function(track) { + stream.removeTrack(track); + track.stop(); + log("stopping old track"); + }); + return; + } + + try { + var constraint = {}; + + if (session.forceAspectRatio && (session.forceScreenShareAspectRatio===null)){ + constraint.aspectRatio = parseFloat(session.forceAspectRatio); + } else if (session.forceScreenShareAspectRatio){ + constraint.aspectRatio = parseFloat(session.forceScreenShareAspectRatio); + } + if (overrideFramerate){ + constraint.frameRate = overrideFramerate; + } + if (Object.keys(constraint).length){ + await stream.getVideoTracks()[0].applyConstraints({ + advanced: [constraint] + }); + log({ + advanced: [constraint] + }); + } + } catch(e){errorlog(e);} + + /// RETURN stream for preview? rather than jumping right in. + session.screenShareState=true; + pokeIframeAPI('screen-share-state', session.screenShareState, null, session.streamID); + notifyOfScreenShare(); + + try { + stream.getVideoTracks()[0].onended = function () { + toggleScreenShare(); + }; + } catch(e){log("No Video selected; screensharing?");} + + // OR, jump right in, and let user change from there + if (session.roomid!==false){ + if ((session.roomid==="") && ((!(session.view)) || (session.view===""))){ + + } else { + getById("head3").classList.add('hidden'); + getById("head3a").classList.add('hidden'); + log("ROOMID EANBLED"); + log("Update Mixer Event on REsize SET"); + window.onresize = updateMixer; + window.onorientationchange = function(){ + if (Firefox){ + updateForceRotate(true); + } + setTimeout(async function(){ + if (session.forceAspectRatio){ + await updateCameraConstraints("aspectRatio", session.forceAspectRatio); + } + if (session.effect && (session.effect === "7")){digitalZoom(true);} + updateForceRotate(); + updateMixer(); + }, 200); + }; + joinRoom(session.roomid); + } + } else { + getById("head3").classList.remove('hidden'); + getById("head3a").classList.remove('hidden'); + getById("logoname").style.display = 'none'; + } + + updatePushId(); + + if (stream.getAudioTracks().length){ + screenShareAudioTrack = stream.getAudioTracks()[0]; + } + + log("adding tracks"); + for (var i=0; i{ + stream.addTrack(track); + }); + } + streams = null; + if (!session.screenshareVideoOnly && session.audioDevice !== 0){ + if (stream.getAudioTracks().length==0){ + if (!(session.cleanOutput)){ + if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1){ + // Electron has no audio. + } else { + setTimeout(function(){warnUser(getTranslation("no-audio-source-detected"));},300); + } + } + } + } + + + try { + session.streamSrc = stream; + } catch (e){errorlog(e);} + toggleMute(true); + + var v = createVideoElement(); + session.videoElement = v; + + if (session.streamID){ + session.videoElement.dataset.sid = session.streamID; + } + + var container = document.createElement("div"); + v.container = container; + container.id = "container_screen"; + container.style.height = "100%"; + + if (session.cleanOutput){ + v.style.maxWidth = "100%"; + v.style.boxShadow = "none"; + } + + //container.className = "vidcon"; + getById("gridlayout").appendChild(container); + + if (session.nopreview){ + v.style.display="none"; + container.style.display="none"; + } + + //if (session.cover){ + // container.style.setProperty('height', '100%', 'important'); + //} + + container.appendChild(v); + + v.className = "tile"; + + if (session.director){ + } else if (session.scene!==false){ + setTimeout(function(){updateMixer();},1); + } else if (session.roomid!==false){ + if (session.roomid===""){ + if (!(session.view) || (session.view==="")){ + + getById("mutespeakerbutton").classList.add("hidden"); + + if (session.fullscreen){ + session.windowed = false; + if (session.mirrored && session.flipped){ + v.style.transform = " scaleX(-1) scaleY(-1)"; + v.classList.add("mirrorControl"); + } else if (session.mirrored){ + v.style.transform = "scaleX(-1)"; + v.classList.add("mirrorControl"); + } else if (session.flipped){ + v.style.transform = "scaleY(-1)"; + v.classList.remove("mirrorControl"); + } else { + v.style.transform = ""; + v.classList.remove("mirrorControl"); + } + } else { + v.className = "myVideo"; + session.windowed = true; + if (session.mirrored && session.flipped){ + v.style.transform = " scaleX(-1) scaleY(-1) translate(0, 50%)"; + v.classList.add("mirrorControl"); + } else if (session.mirrored){ + v.style.transform = "scaleX(-1) translate(0, -50%)"; + v.classList.add("mirrorControl"); + } else if (session.flipped){ + v.style.transform = "scaleY(-1) translate(0, 50%)"; + v.classList.remove("mirrorControl"); + } else { + v.style.transform = " translate(0, -50%)"; + v.classList.remove("mirrorControl"); + } + } + + container.style.width="100%"; + //container.style.height="100%"; + container.style.alignItems = "center"; + container.backgroundColor = "#666"; + + setTimeout(function (){dragElement(v);},1000); + play(); + } else { + play(); + setTimeout(function(){updateMixer();},1); + } + } else { + setTimeout(function(){updateMixer();},1); + } + } else { + + getById("mutespeakerbutton").classList.add("hidden"); + if (session.fullscreen){ + session.windowed = false; + if (session.mirrored && session.flipped){ + v.style.transform = " scaleX(-1) scaleY(-1)"; + v.classList.add("mirrorControl"); + } else if (session.mirrored){ + v.style.transform = "scaleX(-1)"; + v.classList.add("mirrorControl"); + } else if (session.flipped){ + v.style.transform = "scaleY(-1)"; + v.classList.remove("mirrorControl"); + } else { + v.style.transform = ""; + v.classList.remove("mirrorControl"); + } + } else { + v.className = "myVideo"; + session.windowed = true; + container.classList.add("vidcon"); + if (session.mirrored && session.flipped){ + v.style.transform = " scaleX(-1) scaleY(-1) translate(0, 50%)"; + v.classList.add("mirrorControl"); + } else if (session.mirrored){ + v.style.transform = "scaleX(-1) translate(0, -50%)"; + v.classList.add("mirrorControl"); + } else if (session.flipped){ + v.style.transform = "scaleY(-1) translate(0, 50%)"; + v.classList.remove("mirrorControl"); + } else { + v.style.transform = " translate(0, -50%)"; + v.classList.remove("mirrorControl"); + } + } + + container.style.width="100%"; + //container.style.height="100%"; + container.style.alignItems = "center"; + container.backgroundColor = "#666"; + } + + if (!session.windowed){ + window.onresize = updateMixer; + window.onorientationchange = function(){ + if (Firefox){ + updateForceRotate(true); + } + setTimeout(async function(){ + if (session.forceAspectRatio){ + await updateCameraConstraints("aspectRatio", session.forceAspectRatio); + } + if (session.effect && (session.effect === "7")){digitalZoom(true);} + updateForceRotate(); + updateMixer(); + }, 200); + }; + } + + v.autoplay = true; + v.controls = session.showControls || false; + v.setAttribute("playsinline",""); + v.muted = true; + v.id = "videosource"; + v.dataset.menu = "context-menu-video"; + + if (!session.cleanOutput){ + v.classList.add("task"); // this adds the right-click menu + } + + //if (!v.srcObject || v.srcObject.id !== stream.id) { + // v.srcObject = stream; + v.srcObject = outboundAudioPipeline(); + //} + + v.onpause = (event) => { // prevent things from pausing; human or other + if (!((event.ctrlKey) || (event.metaKey) )){ + log("Video paused; auto playing"); + event.currentTarget.play().then(_ => { + log("playing 11"); + }).catch(warnlog); + } + }; + + v.addEventListener('click', function(e) { // show stats of video if double clicked + log("click"); + try { + if ((e.ctrlKey)||(e.metaKey)){ + e.preventDefault(); + + var [menu, innerMenu] = statsMenuCreator(); + + menu.interval = setInterval(printMyStats,session.statsInterval, innerMenu); + + printMyStats(innerMenu); + e.stopPropagation(); + return false; + } + } catch(e){errorlog(e);} + }); + + updateReshareLink(); + + if (session.videoMutedFlag){ + session.videoMuted = true; + toggleVideoMute(true); + } + + clearInterval(session.updateLocalStatsInterval); + session.updateLocalStatsInterval = setInterval(function(){updateLocalStats();},session.statsInterval); + + session.seeding=true; + session.seedStream(); + + //pokeIframeAPI('started-screenshare'); // depreciated + pokeIframeAPI('screen-share-state', true, null, session.streamID); // (action, value = null, UUID = null, SID=null) + + if (session.autorecord || session.autorecordlocal){ + log("AUTO RECORD START"); + setTimeout(function(v){ + var videoKbps = 4000; + if (session.recordLocal !== false) { + videoKbps = session.recordLocal; + } + + if (session.director){ + recordVideo(document.querySelector("[data-action-type='recorder-local'][data-sid='"+session.streamID+"']"), null, videoKbps) + } else if (v.stopWriter || v.recording){ + + } else if (v.startWriter){ + v.startWriter(); + } else { + recordLocalVideo(null, videoKbps, v) + } + },2000, v); + } + + return true; + }).catch(function(err){ + errorlog(err); + errorlog(err.name); + if ((err.name == "NotAllowedError") || (err.name == "PermissionDeniedError")){ + // User Stopped it. (is this next part needed??) + session.screenShareState=false; + pokeIframeAPI('screen-share-state', session.screenShareState, null, session.streamID); + notifyOfScreenShare(); + + if (macOS){ + warnUser(getTranslation("screen-permissions-denied"), false, false); + } + return false; + } else { + if (audio==true){ + if (err.name == "NotReadableError"){ + if (!(session.cleanOutput)){ + warnUser(getTranslation("change-audio-output-device"), false, false); + } + return false; + } else { + constraints.audio=false; + if (!(session.cleanOutput)){ + setTimeout(function(){warnUser(err);},1); // TypeError: Failed to execute 'getDisplayMedia' on 'MediaDevices': Audio capture is not supported + } + return publishScreen2(constraints, audioList, false); + } + } else { + if (!(session.cleanOutput)){ + setTimeout(function(){warnUser(err);},1); // TypeError: Failed to execute 'getDisplayMedia' on 'MediaDevices': Audio capture is not supported + } + return false; + } + } + }); +}; // publishStream2 + +var transferList = []; +var msgTransferList = []; + +function cancelFile(ele){ + var idx = ele.dataset.tid; + try{ + transferList[idx].dc.close(); + } catch(e){} + transferList[idx].status = 5; + updateDownloadLink(idx); +} + +function requestFile(ele){ + var idx = ele.dataset.tid; + transferList[idx].status = 1; + + var fid = ele.dataset.fid; + var UUID = ele.dataset.uuid; + var msg = {}; + msg.requestFile = fid; + msg.UUID = UUID; + session.sendRequest(msg, msg.UUID); + + updateDownloadLink(idx); + pokeIframeAPI('request-file', fid, UUID); +} + +function clearDownloadFile(ele){ + var idx = ele.dataset.tid; + transferList[idx].status = 6; + updateDownloadLink(idx); +} + +function addDownloadLink(fileList, UUID, pc){ + if (session.nodownloads){return;} // downloads are blocked + log(fileList); + if (!fileList || !fileList.length){return;} + for (var i = 0; i< fileList.length; i++){ + fileList[i].UUID = UUID; + fileList[i].completed = 0; + fileList[i].status = 0; + fileList[i].time = Date.now(); + fileList[i].pc = pc[UUID]; + transferList.push(fileList[i]); + } + + if (session.chatbutton===false){return;} // messages can still appear as overlays + + updateMessages(); + + if (session.beepToNotify) { + playtone(); + } + + if (session.chat == false) { + getById("chattoggle").className = "las la-comments toggleSize pulsate"; + getById("chatbutton").className = "float"; + + if (getById("chatNotification").value) { + getById("chatNotification").value = getById("chatNotification").value + 1; + } else { + getById("chatNotification").value = 1; + } + getById("chatNotification").classList.add("notification", "red"); + } + + //if (session.broadcastChannel !== false) { + // session.broadcastChannel.postMessage(data); /* send */ + //} +} + +function updateDownloadLink(idx){ + idx = parseInt(idx); + var elements = document.querySelectorAll('[data-tid="'+idx+'"]'); + if (elements[0]) { + if (transferList[idx].status === 0){ + elements[0].innerHTML = "Download it here"; + } else if (transferList[idx].status === 1){ + elements[0].innerHTML = "Requested"; + //elements[0].onclick='cancelFile(this);' + } else if (transferList[idx].status === 2){ + elements[0].innerHTML = "Downloading: "+parseInt(transferList[idx].completed*100)+"%"; + elements[0].onclick = function(){cancelFile(this);} + } else if (transferList[idx].status === 3){ + elements[0].innerHTML = "Completed"; + elements[0].onclick = null; + elements[0].disabled = true; + } else if (transferList[idx].status === 4){ + elements[0].innerHTML = "No longer available"; + elements[0].onclick = null; + elements[0].disabled = true; + } else if (transferList[idx].status === 5){ + elements[0].innerHTML = "Cancelled"; + elements[0].onclick = null; + elements[0].disabled = true; + } else if (transferList[idx].status === 6){ + getById("transfer_"+idx).style.display = "none"; + //delete(transferList[idx]); + } + } +} + +function showDownloadLinks(){ + if (session.nodownloads){return;} // downloads are blocked + msgTransferList=[]; + if (!transferList || !transferList.length){return;} + for (var i = 0; i< transferList.length; i++){ + fileShareMessage(transferList[i], i); + } +} + +function fileShareMessage(fileinfo, idx){ + + fileinfo.name = sanitizeChat(fileinfo.name); // keep it clean. + + var label = false; + if (fileinfo.pc){ + if (fileinfo.pc.label) { + label = sanitizeLabel(fileinfo.pc.label); + } + } + var data = {}; + data.idx = idx; + if (fileinfo.status === 0){ + data.msg = " has a shared a file with you:
    "+fileinfo.name+"
    Do you trust them? "; + } else if (fileinfo.status === 1){ + data.msg = " has a shared a file with you:
    "+fileinfo.name+"
    "; + } else if (fileinfo.status === 2){ + data.msg = " has a shared a file with you:
    "+fileinfo.name+"
    "; + } else if (fileinfo.status === 3){ + data.msg = " has a shared a file with you:
    "+fileinfo.name+"
    "; + transferList[idx].status = 6; + } else if (fileinfo.status === 4){ + data.msg = " has a shared a file with you:
    "+fileinfo.name+"
    "; + } else if (fileinfo.status === 5){ + data.msg = " has a shared a file with you:
    "+fileinfo.name+"
    "; + transferList[idx].status = 6; + } else if (fileinfo.status === 6){ + return; + } + + var director=false; // add back in later. + if (session.directorList.indexOf(fileinfo.UUID)>=0){ + director=true; + } + if (label) { + data.label = label; + if (director) { + data.label = "" + data.label + ""; + } else { + data.label = "" + data.label + ""; + } + } else if (director) { + data.label = "Director"; + } else { + data.label = "Someone"; + } + data.type = "action"; + + msgTransferList.push(data); +} + +session.shareFile = function(ele, UUID=false, event=false){ // webcam stream is used to generated an SDP + if (session.hostedFiles===false){return;} // disabled + + for (var i = 0; i < ele.files.length; i++){ // changing from a FileList to an Array. Arrays are easier to modify later on + ele.files[i].id = session.generateStreamID(8); // can't be too short, else can be brute forced + ele.files[i].state = 1; + ele.files[i].restricted = UUID; + session.hostedFiles.push(ele.files[i]); + } + log(session.hostedFiles); + //for (var in rpcs and pcs .... goes here + if (UUID===false){ + for (UUID in session.pcs){ + session.provideFileList(UUID); + } + for (UUID in session.rpcs){ + if (UUID in session.pcs){continue;} + session.provideFileList(UUID); + } + } else { + session.provideFileList(UUID); + } + pokeIframeAPI('file-share', true); + closeModal(); +} + + +session.hostFile = function(ele, event){ // webcam stream is used to generated an SDP + log("FILE TRANSFER SETUP"); + session.hostedFiles = []; + for (var i = 0; i < ele.files.length; i++){ // changing from a FileList to an Array. Arrays are easier to modify later on + ele.files[i].id = session.generateStreamID(8); // can't be too short, else can be brute forced + ele.files[i].state = 1; + session.hostedFiles.push(ele.files[i]); + } + log(session.hostedFiles); + + var container = document.createElement("div"); + container.id = "container_host"; + getById("gridlayout").appendChild(container); + + if (session.cover){ + container.style.setProperty('height', '100%', 'important'); + } + + if (session.roomid!==false){ + if ((session.roomid==="") && ((!(session.view)) || (session.view===""))){ + + } else { + log("ROOMID EANBLED"); + //log("Update Mixer Event on REsize SET"); + //window.addEventListener("resize", updateMixer);// TODO FIX + //window.addEventListener("orientationchange", updateMixer);// TODO FIX + getById("head3").classList.add('hidden'); + getById("head3a").classList.add('hidden'); + joinRoom(session.roomid); + } + + } else { + getById("head3").classList.remove('hidden'); + getById("head3a").classList.remove('hidden'); + getById("logoname").style.display = 'none'; + } + getById("head1").className = 'hidden'; + + updatePushId() + + getById("head1").className = 'hidden'; + getById("head2").className = 'hidden'; + + if (!(session.cleanOutput)){ + getById("chatbutton").className="float"; + getById('sharefilebutton').classList.remove("hidden"); // we won't override "display:none", if set, though. + getById("hangupbutton").className="float"; + getById("controlButtons").classList.remove("hidden"); + getById("helpbutton").style.display = "inherit"; + getById("reportbutton").style.display = ""; + } else { + getById("controlButtons").classList.add("hidden"); + } + + + updateReshareLink(); + + pokeIframeAPI('file-share', true); + pokeIframeAPI('started-fileshare'); // deprecated + + clearInterval(session.updateLocalStatsInterval); + session.updateLocalStatsInterval = setInterval(function(){updateLocalStats();},session.statsInterval); + + session.seeding=true; + session.seedStream(); +} + +function updateReshareLink(){ + + try{ + var m = getById("mainmenu"); + m.remove(); + document.querySelectorAll(".hidden2").forEach(ele2=>{ + ele2.classList.remove("hidden2"); + }); + } catch (e){} + + var added = ""; + if (session.defaultPassword===false){ + if (session.password!==false){ + added="&pw="+session.password; + } else { + added="&pw=false"; + } + } + + var wss = ""; + if (session.wssSetViaUrl){ + if (session.customWSS && (session.customWSS!==true)){ + wss = "&pie="+session.customWSS; + } else { + wss = "&wss="+session.wss; + } + } + + var shareLink = "https://"+location.host+location.pathname+"?view="+session.streamID+added+wss; + if (document.getElementById("reshare")){ + document.getElementById("reshare").href = shareLink; + document.getElementById("reshare").text = shareLink; + document.getElementById("reshare").style.width = ((document.getElementById("reshare").text.length + 1)*1.15 * 8) + 'px'; + } + pokeIframeAPI('share-link', shareLink); +} + + + +session.changePublishFile = function(ele, event){ // webcam stream is used to generated an SDP + log("FILE VIDEO STREAM CHANGE"); + var files = []; + for (var i = 0; i < ele.files.length; i++){ // changing from a FileList to an Array. Arrays are easier to modify later on + files.push(ele.files[i]); + } + var vid = getById("videosource"); + vid.playlist = files; + nextFilePlaylist(vid); +} + +function nextFilePlaylist(vid) { + log("nextFilePlaylistD"); + var filenext = vid.playlist.shift(); + vid.pause(); + vid.removeAttribute('src'); // empty source + vid.src = URL.createObjectURL(filenext); + + vid.onloadeddata = function(){ + + if (Firefox){ + session.streamSrc = vid.mozCaptureStream(); + } else { + session.streamSrc = vid.captureStream(); // gaaaaaaaaaaaahhhhhhhh! + } + + var tracks = session.streamSrc.getVideoTracks(); + if (tracks.length){ + pushOutVideoTrack(tracks[0]); // video only + } + + var tracks = session.streamSrc.getAudioTracks(); + senderAudioUpdate(false, tracks); // don't apply audio effects + } + + session.applySoloChat(); // mute streams that should be muted if a director + session.applyIsolatedChat(); + + vid.load(); + vid.play().then(_ => { + log("playing 2"); + }).catch(warnlog); +} + +session.publishFile = function(ele, event){ // webcam stream is used to generated an SDP + log("FILE STREAM SETUP"); + + if (session.transcript){ + setTimeout(function(){setupClosedCaptions();},1000); + } + + var files = []; + for (var i = 0; i < ele.files.length; i++){ // changing from a FileList to an Array. Arrays are easier to modify later on + files.push(ele.files[i]); + } + log(files); + //var type = file.type; + + var fileURL = URL.createObjectURL(files[0]); + var container = document.createElement("div"); + container.id = "container"; + //container.className = "vidcon"; + + if (session.cover){ + container.style.setProperty('height', '100%', 'important'); + } + + var v = createVideoElement(); + v.container = container; + + if (session.cleanOutput){ + container.style.height = "100%"; + v.style.maxWidth = "100%"; + v.style.boxShadow = "none"; + } + + if (session.streamID){ + v.dataset.sid = session.streamID; + } + + getById("gridlayout").appendChild(container); + + + if (session.roomid!==false){ + if ((session.roomid==="") && ((!(session.view)) || (session.view===""))){ + + } else { + log("ROOMID EANBLED"); + log("Update Mixer Event on REsize SET"); + //window.addEventListener("resize", updateMixer);// TODO FIX + //window.addEventListener("orientationchange", updateMixer);// TODO FIX + getById("head3").classList.add('hidden'); + getById("head3a").classList.add('hidden'); + joinRoom(session.roomid); + } + + } else { + getById("head3").classList.remove('hidden'); + getById("head3a").classList.remove('hidden'); + getById("logoname").style.display = 'none'; + } + getById("head1").className = 'hidden'; + + updatePushId() + + getById("head1").className = 'hidden'; + getById("head2").className = 'hidden'; + + if (!(session.cleanOutput)){ + getById("chatbutton").className="float"; + getById('sharefilebutton').classList.remove("hidden"); // we won't override "display:none", if set, though. + getById("hangupbutton").className="float"; + getById("controlButtons").classList.remove("hidden"); + getById("helpbutton").style.display = "inherit"; + getById("reportbutton").style.display = ""; + } else { + getById("controlButtons").classList.add("hidden"); + } + + var bigPlayButton = document.getElementById("bigPlayButton"); + if (bigPlayButton){ + bigPlayButton.parentNode.removeChild(bigPlayButton); + } + + v.autoplay = false; + if (session.showControls!==null){ + v.controls = session.showControls; + } else { + v.controls = true; + } + v.muted = false; + + if (files.length ==1){ // we don't want to do the complex logic if there is just one video + v.loop = true; + } else { + v.loop = false; // triggers the complex track/rtc logic. + } + + v.setAttribute("playsinline",""); + v.src = fileURL; + + + try { + if (Firefox){ + session.streamSrc = v.mozCaptureStream(); + } else { + session.streamSrc = v.captureStream(); // gaaaaaaaaaaaahhhhhhhh! + } + toggleMute(true); + } catch (e){ + errorlog(e); + return; + } + + v.id = "videosource"; // could be set to UUID in the future + v.dataset.menu = "context-menu-video"; + v.playlist = files; + v.addEventListener('ended',myHandler,false); // only fires if the video doesn't loop. + + + function myHandler(e) { + log("MY HANDLER TRIGGERED"); + var vid = getById("videosource"); + nextFilePlaylist(vid); + } + + // no preview doesn't work, so just stop it from doing its thing. + + v.className = "tile clean fileshare"; + session.videoElement = v; + + container.appendChild(v); + + session.mirrorExclude=true; + + if (session.director){ + } else if (session.scene!==false){ + + } else if (session.roomid!==false){ + if (session.roomid===""){ + if (!(session.view) || (session.view==="")){ + if (session.fullscreen){ + session.windowed = false; + } else { + v.className = "myVideo clean fileshare"; + container.classList.add("vidcon"); + session.windowed = true; + } + getById("mutespeakerbutton").classList.add("hidden"); + container.style.width="100%"; + container.style.alignItems = "center"; + container.backgroundColor = "#666"; + play(); + } else { + session.windowed = false; + play(); + } + } else { + //session.cbr=0; // we're just going to override it + if (session.stereo==5){ + session.stereo=3; + } + session.windowed = false; + } + applyMirror(session.mirrorExclude); + } else { + if (session.fullscreen){ + session.windowed = false; + } else { + v.className = "myVideo clean fileshare"; + container.classList.add("vidcon"); + session.windowed = true; + } + getById("mutespeakerbutton").classList.add("hidden"); + container.style.width="100%"; + container.style.alignItems = "center"; + container.backgroundColor = "#666"; + applyMirror(session.mirrorExclude); + } + + + v.addEventListener('click', function(e){ + log("click"); + try { + if ((e.ctrlKey)||(e.metaKey)){ + e.preventDefault(); + + var [menu, innerMenu] = statsMenuCreator(); + + menu.interval = setInterval(printMyStats,session.statsInterval, innerMenu); + + printMyStats(innerMenu); + e.stopPropagation(); + return false; + } + } catch(e){errorlog(e);} + }); + + v.touchTimeOut = null; + v.touchLastTap = 0; + v.touchCount = 0; + v.addEventListener('touchend', function(event) { + if (session.disableMouseEvents){return;} + log("touched"); + + //document.ontouchup = null; + //document.onmouseup = null; + document.onmousemove = null; + document.ontouchmove = null; + + var currentTime = new Date().getTime(); + var tapLength = currentTime - v.touchLastTap; + clearTimeout(v.touchTimeOut); + if (tapLength < 500 && tapLength > 0) { + /// + log("double touched"); + v.touchCount+=1; + event.preventDefault(); + if (v.touchCount<5){ + v.touchLastTap = currentTime; + return false; + } + v.touchLastTap = 0; + v.touchCount=0; + + var [menu, innerMenu] = statsMenuCreator(); + + menu.interval = setInterval(printMyStats,session.statsInterval, innerMenu); + + printMyStats(innerMenu); + event.stopPropagation(); + return false; + ////// + } else { + v.touchCount=1; + v.touchTimeOut = setTimeout(function(vv) { + clearTimeout(vv.touchTimeOut); + vv.touchLastTap = 0; + vv.touchCount=0; + }, 5000, v); + v.touchLastTap = currentTime; + } + + }); + + + + updateReshareLink(); + pokeIframeAPI('started-fileshare'); // depreciated + pokeIframeAPI('file-share', true); + + clearInterval(session.updateLocalStatsInterval); + session.updateLocalStatsInterval = setInterval(function(){updateLocalStats();},session.statsInterval); + + session.seeding=true; + + if (session.videoMutedFlag){ + session.videoMuted = true; + toggleVideoMute(true); + } + + session.seedStream(); +}; // publishFile + + +function tryAgain(event) { // audio or video agnostic track reconnect ------------not actually in use,. maybe out of date + log("TRY AGAIN TRIGGERED"); + warnlog(event); +} + + +function enterPressedClick(event, ele) { + if (event.keyCode === 13) { + event.preventDefault(); + ele.click(); + } +} + +function enterPressed(event, callback) { + // Number 13 is the "Enter" key on the keyboard + if (event.keyCode === 13) { + event.preventDefault(); + callback(); + } +} + + +function dragElement(elmnt) { + if (session.disableMouseEvents){return;} + log("dragElement started"); + + function onvideoclick() { + log("onvideoclick"); + log(pos3 + " " + pos4); + //log(pos3o + " " + pos4o); + tapToFocus(parseInt(pos3*100/elmnt.clientWidth), parseInt(pos4/elmnt.clientHeight*100)); + return false; + } + + function elementDrag(e) { + + e = e || window.event; + e.preventDefault(); + // calculate the new cursor position: + log("dragging"); + log(e); + if (Date.now() - millis < 100) { + return; + } + + dragged = true; + millis = Date.now(); + + + + if (e.type == 'touchstart' || e.type == 'touchmove' || e.type == 'touchend' || e.type == 'touchcancel') { + var touch = e.touches[0] || e.originalEvent.touches[0] || e.originalEvent.changedTouches[0]; + pos1 = touch.clientX; + pos2 = touch.clientY; + } else if (e.type == 'mousedown' || e.type == 'mouseup' || e.type == 'mousemove' || e.type == 'mouseover' || e.type == 'mouseout' || e.type == 'mouseenter' || e.type == 'mouseleave') { + pos1 = e.clientX; + pos2 = e.clientY; + } + + if (!zoomable){ + return; + } + + var zoom = parseFloat((pos4 - pos2) * 2 / elmnt.offsetHeight); + + if (zoom > 1) { + zoom = 1.0; + } else if (zoom < -1) { + zoom = -1.0; + } + input.value = zoom * (input.max - input.min) + input.min; + updateCameraConstraints("zoom", input.value, false, false); + + } + function closeDragElement(e) { + + log("closeDragElement"); + log(e); + + // focusable + if (!dragged){ + log("dragged: "+dragged); + onvideoclick(); + } + dragged = false; + + elmnt.removeEventListener('touchend', closeDragElement); + elmnt.removeEventListener('mouseup', closeDragElement); + + /* stop moving when mouse button is released:*/ + //document.ontouchend = null; + //document.onmouseup = null; + document.onmousemove = null; + document.ontouchmove = null; + } + function dragMouseDown(e) { + log("dragMouseDown"); + log(e); + + dragged = false; + millis = Date.now(); + + e = e || window.event; + e.preventDefault(); + + pos0 = input.value; + if (e.type == 'touchstart' || e.type == 'touchmove' || e.type == 'touchend' || e.type == 'touchcancel') { + var touch = e.touches[0] || e.originalEvent.touches[0] || e.originalEvent.changedTouches[0]; + pos3 = touch.clientX; + pos4 = touch.clientY; + //pos3o = touch.offsetX; + //pos4o = touch.offsetX; + } else if (e.type == 'mousedown' || e.type == 'mouseup' || e.type == 'mousemove' || e.type == 'mouseover' || e.type == 'mouseout' || e.type == 'mouseenter' || e.type == 'mouseleave') { + pos3 = e.clientX; + pos4 = e.clientY; + //pos3o = e.offsetX; + //pos4o = e.offsetX; + } + elmnt.addEventListener('touchend', closeDragElement); + elmnt.addEventListener('mouseup', closeDragElement); + document.ontouchmove = elementDrag; + document.onmousemove = elementDrag; + } + + try { + var stream = elmnt.srcObject; + try { + var track0 = stream.getVideoTracks(); + } catch (e) { + return; + } + + if (!(track0.length)) { + return; + } + var focusable = false; + var zoomable = false; + var dragged = false; + var input = getById("zoomSlider"); + track0 = track0[0]; + if (track0.getCapabilities) { + var capabilities = track0.getCapabilities(); + var settings = track0.getSettings(); + + if ("focusDistance" in capabilities){ + log("focusable"); + focusable = true; + } + + if ('zoom' in capabilities) { + if (capabilities.zoom.min !== capabilities.zoom.max){ + log("zoomable;"); + zoomable = true; + input.min = capabilities.zoom.min; + input.max = capabilities.zoom.max; + input.step = capabilities.zoom.step; + input.value = settings.zoom; + } + + } + } + + var millis = Date.now(); + var pos0 = 1; + var pos3 = 0; + var pos4 = 0; + var pos1 = 0; + var pos2 = 0; + //var pos3o = 0; + //var pos4o = 0; + } catch (e) { + errorlog(e); + return; + } + + if (!focusable && !zoomable){return;} // can't be zoomed or focused. + + log("drag on"); + elmnt.onmousedown = dragMouseDown; + elmnt.ontouchstart = dragMouseDown; + + +} + +function previewIframe(iframeSrc) { // this is pretty important if you want to avoid camera permission popup problems. You can also call it automatically via: loadIframe();"> , but don't call it before the page loads. + + var iframe = document.createElement("iframe"); + iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;display-capture;midi;"; + iframe.style.width = "100%"; + iframe.style.height = "100%"; + iframe.style.border = "10px dashed rgb(64 65 62)"; + + iframeSrc = parseURL4Iframe(iframeSrc); + + /* if (typeof iframeSrc == "object"){ // special handler. + iframeSrc = iframeSrc.parsedSrc; + } */ + + iframe.src = iframeSrc; + getById("previewIframe").innerHTML = ""; + getById("previewIframe").style.width = "640px"; + getById("previewIframe").style.height = "360px"; + getById("previewIframe").style.margin = "auto"; + getById("previewIframe").appendChild(iframe); +} + +function loadIframe(iframesrc, UUID) { // this is pretty important if you want to avoid camera permission popup problems. You can also call it automatically via: loadIframe();"> , but don't call it before the page loads. + /* if (document.getElementById("mainmenu")) { + var m = getById("mainmenu"); + m.remove(); + } */ + var iframeID = "iframe_"+UUID; + + var iframe = document.createElement("iframe"); + iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;display-capture;midi;"; + iframe.style.width = "100%"; + iframe.style.height = "100%"; + iframe.style.border = "10px dashed rgb(64 65 62)"; + iframe.id = iframeID; + iframe.dataset.UUID = UUID; + iframe.loadedYoutubeListen = false; + + if (session.director){ + // + } else if (session.scene!==false){ + if (session.view){ // specific video to be played + iframe.style.display="block"; + } else if (session.scene==="0"){ + iframe.style.display="block"; + } else { // group scene I guess; needs to be added manually + iframe.style.display="none"; + } + } else if (session.roomid!==false){ + // + } else { + iframe.style.display="block"; + } + if (iframesrc == "") { + iframesrc = "./"; + iframe.style.border = "0"; + } + + // trusted domains + if (iframesrc.startsWith("https://vdo.ninja/")){ + iframe.style.border = "0"; + } else if (iframesrc.startsWith("https://obs.ninja/")){ + iframe.style.border = "0"; + } else if (iframesrc.startsWith("https://vmix.ninja/")){ + iframe.style.border = "0"; + } else if (iframesrc.startsWith("https://backup.vdo.ninja/")){ + iframe.style.border = "0"; + } else if (iframesrc.startsWith("https://backup.obs.ninja/")){ + iframe.style.border = "0"; + } else if (iframesrc.startsWith("https://www.youtube.com/")){ + iframe.style.border = "0"; + setTimeout(function(iframe_id){YoutubeListen(iframe_id);}, 1000, iframeID); // create stats feedback for the director; syncing. + } else if (iframesrc.startsWith("https://player.twitch.tv/")){ + iframe.style.border = "0"; + } else if (iframesrc.startsWith("https://twitch.tv/")){ + iframe.style.border = "0"; + } else if (iframesrc.startsWith("https://www.twitch.tv/")){ + iframe.style.border = "0"; + } else if (iframesrc.startsWith("https://vimeo.com/")){ + iframe.style.border = "0"; + } else if (iframesrc.startsWith("https://player.vimeo.com/")){ + iframe.style.border = "0"; + } else if (iframesrc.startsWith("https://meshcast.io/")){ + //iframesrc = iframesrc.replace("//meshcast.io/", "//meshcast.vdo.ninja/"); + iframe.style.border = "0"; + // iframe.dataset.meshcast = true; // TODO: this was a bit of a fail + if (document.domain==="backup.vdo.ninja"){ + document.domain = 'vdo.ninja'; + } else if (document.domain==="isolated.vdo.ninja"){ + document.domain = 'vdo.ninja'; + } + } else if (iframesrc.startsWith("https://s10.fun/")){ + iframe.style.border = "0"; + } else if (iframesrc.startsWith("https://play.rozy.tv/")){ + iframe.style.border = "0"; + } + + iframe.src = iframesrc; + + pokeIframeAPI('iframe-loaded', iframesrc); + return iframe +} + +function dropDownButtonAction(ele) { + var ele = getById("dropButton"); + if (ele) { + ele.parentNode.removeChild(ele); + //getById('container-5').classList.remove('hidden'); + //getById('container-8').classList.remove('hidden'); + //getById('container-6').classList.remove('hidden'); + document.querySelectorAll("div.column.card").forEach(child=>{ + child.classList.remove('hidden'); + }); + } +} + +function updateConstraintSliders() { + log("updateConstraintSliders"); + if (session.roomid !== false && session.roomid !== "" && session.director !== true && session.forceMediaSettings == false) { + if (session.controlRoomBitrate !== false) { + listCameraSettings(); + } + if (session.effect!==false){ + //if ((iOS) || (iPad)){ + //} else { + getById("effectsDiv3").style.display = "block"; + getById("effectSelector3").value = session.effect || "0"; + //} + } + } else { + listAudioSettings(); + listCameraSettings(); + + //if ((iOS) || (iPad)){ + // } else { + if (session.effect!==false){ + getById("effectsDiv3").style.display = "block"; + try{ + getById("effectSelector3").value = session.effect || "0"; + } catch(E){} + } + //} + } + //checkIfPIP(); // this doesn't actually work on iOS still, so whatever. +} + +function checkIfPIP() { + try { + if (session.videoElement && ((session.videoElement.webkitSupportsPresentationMode && typeof session.videoElement.webkitSetPresentationMode === "function") || (document.pictureInPictureEnabled || !videoElement.disablePictureInPicture))) { + // Toggle PiP when the user clicks the button. + + getById("pIpStartButton").addEventListener("click", function(event) { + // if ( (document.pictureInPictureEnabled || !videoElement.disablePictureInPicture)){ + //session.videoElement.requestPictureInPicture(); + // } else { + session.videoElement.webkitSetPresentationMode(session.videoElement.webkitPresentationMode === "picture-in-picture" ? "inline" : "picture-in-picture"); + // } + }); + getById("pIpStartButton").style.display = "inline-block"; + } + } catch (e) { + errorlog(e); + } +} + +function togglePictureInPicture(videoElement) { + if (document.pictureInPictureElement) { + if (document.pictureInPictureElement.id == videoElement.id){ + document.exitPictureInPicture(); + pokeIframeAPI('picture-in-picture', false); + return false; + } else { + document.exitPictureInPicture(); + pokeIframeAPI('picture-in-picture', false); + videoElement.requestPictureInPicture(); + pokeIframeAPI('picture-in-picture', true); + } + } else if (document.pictureInPictureEnabled) { + videoElement.requestPictureInPicture(); + pokeIframeAPI('picture-in-picture', true); + } + return true; +} + + +function mixMinusAudio(uid=false){ + var audioContext = new AudioContext(); + + if (session.stereo===false){ + var merger = audioContext.createChannelMerger(1); + } else { + var merger = audioContext.createChannelMerger(2); + } + + if (session.videoElement && session.videoElement.srcObject){ + var tracks = session.videoElement.srcObject.getAudioTracks(); + for (var i=0;i 0) { + log("FINAL:" + Final_transcript); + try { + var data = {}; + data.isFinal = true; + data.transcript = Final_transcript; + data.counter = TranscriptionCounter; + session.sendMessage(data); + TranscriptionCounter += 1; + Final_transcript = ""; + Interim_transcript = ""; + pokeIframeAPI('transcription-text', Final_transcript); + } catch (e) { + errorlog(e); + } + + } else { + try { + var data = {}; + data.isFinal = false; + data.transcript = Interim_transcript; + data.counter = TranscriptionCounter; + session.sendMessage(data); + } catch (e) { + errorlog(e); + Interim_transcript = ""; + } + } + }; + + Recognition.start(); + } else if (!session.cleanOutput){ + warnUser(getTranslation("speech-not-suppoted"), false, false); + } +} + + +async function requestVideoRecord(ele, state=null, bitrate=null) { + var UUID = ele.dataset.UUID; + if (!state && ele.classList.contains("pressed")) { + var msg = {}; + msg.requestVideoRecord = false; + msg.UUID = UUID; + session.sendRequest(msg, msg.UUID); + ele.classList.remove("pressed"); ele.ariaPressed = "false"; + } else if (state==null || state){ + var msg = {}; + msg.requestVideoRecord = true; + msg.UUID = UUID; + if (bitrate===null){ + window.focus(); + bitrate = await promptAlt(getTranslation("what-bitrate"), false, false, 6000); + } + if (bitrate) { + msg.value = bitrate; + session.sendRequest(msg, msg.UUID); + ele.classList.add("pressed"); ele.ariaPressed = "true"; + } + } + pokeIframeAPI('request-video-record', msg.requestVideoRecord, UUID); +} + +function changeOrderDirector(value) { + if (session.order==false){ + session.order=0; + } + session.order += parseInt(value) || 0; + + var elements = document.querySelectorAll('[data-action-type="order-value-director"]'); + //log(elements); + if (elements[0]){ + elements[0].innerText = parseInt(session.order) || 0; + } + + var data = {}; + data = {}; + data.order = session.order; + session.sendPeers(data); + pokeIframeAPI('director-order', data.order); +} + + + +function changeOrder(value, UUID) { + var msg = {}; + msg.changeOrder = value; + msg.UUID = UUID; + session.sendRequest(msg, msg.UUID); + pokeIframeAPI('change-order', value, UUID); +} + +function requestVideoHack(keyname, value, UUID, ctrl=false) { + var msg = {}; + msg.requestVideoHack = true; + msg.keyname = keyname; + msg.value = value; + msg.UUID = UUID; + msg.ctrl = ctrl; + session.sendRequest(msg, msg.UUID); + pokeIframeAPI('request-video-setting', {value:value, keyname:keyname, ctrl:ctrl}, UUID); +} + +function requestAudioHack(keyname, value, UUID, deviceId = "default") { // updateAudioConstraints + var msg = {}; + msg.requestAudioHack = true; + msg.keyname = keyname; + msg.value = value; + msg.UUID = UUID; + msg.deviceId = deviceId; + session.sendRequest(msg, msg.UUID); + pokeIframeAPI('request-audio-setting', {value:value, keyname:keyname, deviceId:deviceId}, UUID); +} + +function requestChangeEQ(keyname, value, UUID, track = 0) { // updateAudioConstraints + var msg = {}; + msg.requestChangeEQ = true; + msg.keyname = keyname; + msg.value = value; + msg.UUID = UUID; + msg.track = track; // pointless atm + session.sendRequest(msg, msg.UUID); + pokeIframeAPI('request-change-eq', {value:value, keyname:keyname, track:track}, UUID); +} + +function requestChangeGating(keyname, value, UUID, track = 0) { // updateAudioConstraints + var msg = {}; + msg.requestChangeGating = true; + msg.keyname = keyname; + msg.value = value; + msg.UUID = UUID; + msg.track = track; // pointless atm + session.sendRequest(msg, msg.UUID); + pokeIframeAPI('request-change-gating', {value:value, keyname:keyname, track:track}, UUID); +} +function requestChangeCompressor(keyname, value, UUID, track = 0) { // updateAudioConstraints + var msg = {}; + msg.requestChangeCompressor = true; + msg.keyname = keyname; + msg.value = value; + msg.UUID = UUID; + msg.track = track; // pointless atm + session.sendRequest(msg, msg.UUID); + pokeIframeAPI('request-change-compressor', {value:value, keyname:keyname, track:track}, UUID); +} +function requestChangeMicDelay(value, UUID, track = 0) { // updateAudioConstraints + var msg = {}; + msg.requestChangeMicDelay = true; + msg.value = value; + msg.UUID = UUID; + msg.track = track; // pointless atm + session.sendRequest(msg, msg.UUID); + pokeIframeAPI('request-change-mic-delay', {value:value, track:track}, UUID); +} + +function requestChangeSubGain(value, UUID, deviceId) { // updateAudioConstraints + var msg = {}; + msg.requestChangeSubGain = true; + msg.value = value; + msg.UUID = UUID; + msg.deviceId = deviceId; // pointless atm + log(msg); + session.sendRequest(msg, msg.UUID); + pokeIframeAPI('request-sub-gain', {value:value, deviceId:deviceId}, UUID); +} + +function requestChangeLowcut(value, UUID, track = 0) { // updateAudioConstraints + var msg = {}; + msg.requestChangeLowcut = true; + msg.value = value; + msg.UUID = UUID; + msg.track = track; // pointless atm + session.sendRequest(msg, msg.UUID); + pokeIframeAPI('request-low-cut', value, UUID); +} + +function toggleSystemPip(vid) { + try{ + if (vid.webkitSupportsPresentationMode && (typeof vid.webkitSetPresentationMode === "function")) { + vid.webkitSetPresentationMode( + vid.webkitPresentationMode === "picture-in-picture" + ? "inline" + : "picture-in-picture" + ); + } else { + if (document.pictureInPictureElemen) { + document.exitPictureInPicture(); + vid.requestPictureInPicture(); + } else { + vid.requestPictureInPicture(); + } + } + } catch(e){ + errorlog(e); + } +} + +function updateDirectorsAudio(dataN, UUID) { + var audioEle = document.createElement("div"); + query("#container_"+UUID+" .advancedAudioSettings").innerHTML = ""; + query("#container_"+UUID+" .advancedAudioSettings").classList.remove("hidden"); + + //log(dataN); + if (!dataN.length) { + return; + } + + for (var n = 0; n < dataN.length; n += 1) { + var data = dataN[n]; + + if (dataN.length==1) { + if (data.trackLabel) { + var label = document.createElement("label"); + label.innerText = data.trackLabel; + label.style.display = "block"; + label.id = "remoteAudioLabel_"+UUID; + label.classList.add("settingsLabel"); + label.dataset.UUID = UUID; + audioEle.appendChild(label); + } + } + //if (n !== 0) { + //var label = document.createElement("span"); + //label.innerText = "Coming Soon"; + //audioEle.appendChild(label); + // continue; // remove to more than one audio device (assuming other fixes are applied) + //} + + if (("micDelay" in data) && n==0) { + var label = document.createElement("label"); + var i = "micDelay"; + var div = document.createElement("div"); + label.id = "label_" + i + "_"+UUID; + label.htmlFor = "constraints_" + i + "_"+UUID; + + var input = document.createElement("input"); + input.min = 0; + input.max = 500; + input.value = data.micDelay || 0; + + input.title = "Previously was: "+input.value; + + input.type = "range"; + input.dataset.keyname = i; + //input.dataset.labelname = "mic delay (ms):"; + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + " (ms):"; + + + var manualInput = document.createElement("input"); + manualInput.type = "number"; + manualInput.dataset.keyname = i; + manualInput.value = parseFloat(input.value); + manualInput.className = "manualInput"; + manualInput.id = "constraints_manual_" + i + "_"+UUID; + manualInput.dataset.UUID = UUID; + manualInput.dataset.track = n; + + input.dataset.track = n; + input.dataset.UUID = UUID; + input.id = "constraints_" + i + "_"+UUID; + input.style = "display:block; width:100%;"; + input.name = "constraints_" + i; + input.style.margin = "2px 0px 5px"; + + manualInput.onchange = function(e) { + getById("constraints_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); + requestChangeMicDelay(parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); + }; + + input.onchange = function(e) { + //e.target.title = e.target.value; + getById("constraints_manual_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); + requestChangeMicDelay(parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); + }; + + input.oninput = function(e) { + getById("constraints_manual_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); + if (Date.now() - remoteSliderTimeout > 100){ + remoteSliderTimeout = Date.now(); + requestChangeMicDelay(parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); + } + }; + + audioEle.appendChild(div) + div.appendChild(label); + div.appendChild(manualInput); + audioEle.appendChild(input); + } + + + if (data.lowcut!==false && n==0) { + var label = document.createElement("label"); + var i = "lowCut"; + label.id = "label_" + i + "_"+UUID; + label.htmlFor = "constraints_" + i + "_"+UUID; + + var input = document.createElement("input"); + input.min = 50; + input.max = 150; + input.value = data.lowcut; + + input.title = "Previously was: "+input.value; + + input.type = "range"; + input.dataset.keyname = i; + //input.dataset.labelname = "low cut:"; + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; + + + var manualInput = document.createElement("input"); + manualInput.type = "number"; + manualInput.dataset.keyname = i; + manualInput.value = parseFloat(input.value); + manualInput.className = "manualInput"; + manualInput.id = "constraints_manual_" + i + "_"+UUID; + manualInput.dataset.UUID = UUID; + manualInput.dataset.track = n; + + input.dataset.track = n; + input.dataset.UUID = UUID; + input.id = "constraints_" + i + "_"+UUID; + input.style = "display:block; width:100%;"; + input.name = "constraints_" + i; + input.style.margin = "2px 0px 5px"; + + manualInput.onchange = function(e) { + getById("constraints_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); + requestChangeLowcut(parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); + }; + + input.onchange = function(e) { + //e.target.title = e.target.value; + getById("constraints_manual_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); + requestChangeLowcut(parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); + }; + + input.oninput = function(e) { + getById("constraints_manual_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); + if (Date.now() - remoteSliderTimeout > 100){ + remoteSliderTimeout = Date.now(); + requestChangeLowcut(parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); + } + }; + + audioEle.appendChild(label); + audioEle.appendChild(manualInput); + audioEle.appendChild(input); + } + + if (data.equalizer && n==0) { + var label = document.createElement("label"); + var i = "Low_EQ"; + //label.id = "label_" +i + "_"+UUID; + label.htmlFor = "constraints_" +i + "_"+UUID; + + + var input = document.createElement("input"); + input.min = -50; + input.max = 50; + input.value = data.lowEQ; + input.title = "Previously was: "+input.value; + input.type = "range"; + input.dataset.keyname = i; + input.dataset.labelname = "low EQ:" + + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; + + var manualInput = document.createElement("input"); + manualInput.type = "number"; + manualInput.dataset.keyname = i; + manualInput.value = parseFloat(input.value); + manualInput.className = "manualInput"; + manualInput.id = "label_" + i + "_"+UUID; + manualInput.dataset.UUID = UUID; + manualInput.dataset.track = n; + + input.dataset.track = n; + input.dataset.UUID = UUID; + input.id = "constraints_" + i + "_"+UUID; + input.style = "display:block; width:100%;"; + input.name = "constraints_" + i; + input.style.margin = "2px 0px 5px"; + + manualInput.onchange = function(e) { + getById("constraints_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); + requestChangeEQ("low", parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); + }; + + input.onchange = function(e) { + getById("label_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); + requestChangeEQ("low", parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); + }; + + input.oninput = function(e) { + getById("label_"+ e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); + if (Date.now() - remoteSliderTimeout > 100){ + remoteSliderTimeout = Date.now(); + requestChangeEQ("low", parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); + } + }; + + audioEle.appendChild(label); + audioEle.appendChild(manualInput); + audioEle.appendChild(input); + + var label = document.createElement("label"); + var i = "midEQ"; + //label.id = "label_" + i + "_"+UUID; + label.htmlFor = "constraints_" + i + "_"+UUID; + + + + var input = document.createElement("input"); + input.min = -50; + input.max = 50; + input.value = data.midEQ; + input.title = "Previously was: "+input.value; + input.type = "range"; + input.dataset.keyname = i; + input.dataset.labelname = "mid EQ:"; + + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; + + var manualInput = document.createElement("input"); + manualInput.type = "number"; + manualInput.dataset.keyname = i; + manualInput.value = parseFloat(input.value); + manualInput.className = "manualInput"; + manualInput.id = "label_" + i + "_"+UUID; + manualInput.dataset.UUID = UUID; + manualInput.dataset.track = n; + + input.dataset.track = n; + input.dataset.UUID = UUID; + input.id = "constraints_" + i + "_"+UUID; + input.style = "display:block; width:100%;"; + input.name = "constraints_" + i; + input.style.margin = "2px 0px 5px"; + + manualInput.onchange = function(e) { + getById("constraints_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); + requestChangeEQ("mid", parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); + }; + + input.onchange = function(e) { + getById("label_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); + requestChangeEQ("mid", parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); + }; + + input.oninput = function(e) { + getById("label_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); + if (Date.now() - remoteSliderTimeout > 100){ + remoteSliderTimeout = Date.now(); + requestChangeEQ("mid", parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); + } + }; + + + audioEle.appendChild(label); + audioEle.appendChild(manualInput); + audioEle.appendChild(input); + + + var label = document.createElement("label"); + var i = "highEQ"; + //label.id = "label_" + i + "_"+UUID; + label.htmlFor = "constraints_" + i + "_"+UUID; + + + var input = document.createElement("input"); + input.min = -50; + input.max = 50; + input.value = data.highEQ; + input.title = "Previously was: "+input.value; + input.type = "range"; + input.dataset.keyname = i; + input.dataset.labelname = "high EQ:"; + + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; + + var manualInput = document.createElement("input"); + manualInput.type = "number"; + manualInput.dataset.keyname = i; + manualInput.value = parseFloat(input.value); + manualInput.className = "manualInput"; + manualInput.id = "label_" + i + "_"+UUID; + manualInput.dataset.UUID = UUID; + manualInput.dataset.track = n; + + input.dataset.track = n; + input.dataset.UUID = UUID; + input.id = "constraints_" + i + "_"+UUID; + input.classList.add("inputConstraint"); + input.name = "constraints_" + i; + + manualInput.onchange = function(e) { + getById("constraints_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); + requestChangeEQ("high", parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); + }; + + input.onchange = function(e) { + getById("label_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); + requestChangeEQ("high", parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); + }; + + input.oninput = function(e) { + getById("label_" + e.target.dataset.keyname+"_"+e.target.dataset.UUID).value = parseFloat(e.target.value); + if (Date.now() - remoteSliderTimeout > 100){ + remoteSliderTimeout = Date.now(); + requestChangeEQ("high", parseInt(e.target.value), e.target.dataset.UUID, parseInt(e.target.dataset.track)); + } + }; + + audioEle.appendChild(label); + audioEle.appendChild(manualInput); + audioEle.appendChild(input); + } + + if (("gating" in data) && n==0) { // only show once. + var label = document.createElement("label"); + var i = "noiseGate"; + var div = document.createElement("div"); + var label = document.createElement("label"); + label.id = "label_" + i + "_"+n + "_"+UUID; + label.htmlFor = "constraints_" + i + "_"+n + "_"+UUID; + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; + label.style = "display:inline-block; padding:0;"; + label.dataset.keyname = i; + label.dataset.track = n; + var input = document.createElement("select"); + var c = document.createElement("option"); + + var opt = new Option("Off", false); + input.options.add(opt); + opt = new Option("On", true); + input.options.add(opt); + + if (data.gating){ + opt.selected = "true"; + } + + input.dataset.deviceId = data.deviceId; + input.id = "constraints_" + i + "_"+n + "_"+UUID; + input.className = "constraintCameraInput"; + input.name = "constraints_" + i + "_"+n; + input.style = "display:inline; padding:2px;"; + input.dataset.keyname = i; + input.dataset.track = n; + input.dataset.UUID = UUID; + input.dataset.chosen = input.value; + input.onchange = function(e) { + this.dataset.chosen = this.value; + //getById("label_"+e.target.dataset.keyname).innerText =e.target.dataset.keyname+": "+e.target.value; + //updateAudioConstraints(e.target.dataset.keyname, e.target.value); + requestChangeGating("gating", e.target.value, e.target.dataset.UUID, parseInt(e.target.dataset.track)); + log(e.target.dataset.keyname, e.target.value); + }; + audioEle.appendChild(div); + div.appendChild(label); + div.appendChild(input); + } + + if (("compressor" in data) && n==0) { + var label = document.createElement("label"); + var i = "compressor"; + var div = document.createElement("div"); + var label = document.createElement("label"); + label.id = "label_" + i + "_"+n+ "_"+UUID; + label.htmlFor = "constraints_" + i + "_"+n+ "_"+UUID; + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; + label.style = "display:inline-block; padding:0;"; + label.dataset.keyname = i; + label.dataset.track = n; + + var input = document.createElement("select"); + var c = document.createElement("option"); + + var opt = new Option("Off", false); + input.options.add(opt); + opt = new Option("On", 1); + input.options.add(opt); + + if (data.compressor==1){ + opt.selected = "true"; + } + opt = new Option("Limiter", 2); + input.options.add(opt); + + if (data.compressor==2){ + opt.selected = "true"; + } + + input.dataset.deviceId = data.deviceId; + input.id = "constraints_" + i + "_"+n+ "_"+UUID; + input.className = "constraintCameraInput"; + input.name = "constraints_" + i + "_"+n; + input.style = "display:inline; padding:2px;"; + input.dataset.keyname = i; + input.dataset.track = n; + input.dataset.UUID = UUID; + input.dataset.chosen = input.value; + input.onchange = function(e) { + this.dataset.chosen = this.value; + //getById("label_"+e.target.dataset.keyname).innerText =e.target.dataset.keyname+": "+e.target.value; + //updateAudioConstraints(e.target.dataset.keyname, e.target.value); + requestChangeCompressor("compressor", e.target.value, e.target.dataset.UUID, parseInt(e.target.dataset.track)); + log(e.target.dataset.keyname, e.target.value); + }; + audioEle.appendChild(div); + div.appendChild(label); + div.appendChild(input); + } + + if (dataN.length>1){ + if (data.trackLabel) { + var label = document.createElement("label"); + label.innerText = data.trackLabel; + label.style.display = "block"; + label.id = "remoteAudioLabel_"+UUID+"_"+n+ "_"+UUID; + label.classList.add("settingsLabel"); + audioEle.appendChild(label); + } + } + + warnlog(data); + + for (var i in data.audioConstraints) { + try { + log(i); + log(data.audioConstraints[i]); + if ((typeof data.audioConstraints[i] === 'object') && (data.audioConstraints[i] !== null) && ("max" in data.audioConstraints[i]) && ("min" in data.audioConstraints[i])) { + if (i === "aspectRatio") { + continue; + } else if (i === "width") { + continue; + } else if (i === "height") { + continue; + } else if (i === "frameRate") { + continue; + } else if (i === "latency") { + // continue; + } else if (i === "sampleRate") { + continue; + } else if (i === "channelCount") { + // continue; + } else if (i === "volume"){ + continue; + } + + if (!("deviceId" in data.audioConstraints)){continue;} // not going to support older versions. + + var label = document.createElement("label"); + //label.id = "label_" + i + "_"+n+ "_"+UUID; + label.htmlFor = "constraints_" + i + "_"+n+ "_"+UUID; + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; + + var input = document.createElement("input"); + input.min = data.audioConstraints[i].min; + input.max = data.audioConstraints[i].max; + + + if (parseFloat(input.min) == parseFloat(input.max)) { + continue; + } + + var manualInput = document.createElement("input"); + manualInput.type = "number"; + + if ("step" in data.audioConstraints[i]) { + input.step = data.audioConstraints[i].step; + manualInput.step = data.audioConstraints[i].step; + } else if ("volume" == i) { + input.step = 0.01; + manualInput.step = 0.01; + } + + manualInput.dataset.keyname = i; + manualInput.className = "manualInput"; + manualInput.id = "label_" + i + "_"+n+ "_"+UUID; + manualInput.max = data.audioConstraints[i].max; + manualInput.min = data.audioConstraints[i].min; + manualInput.dataset.UUID = UUID; + manualInput.dataset.track = n; + manualInput.dataset.keyname = i; + + if (i in data.currentAudioConstraints) { + input.value = data.currentAudioConstraints[i]; + manualInput.value = parseFloat(input.value); + //label.innerText = i + ": " + data.currentAudioConstraints[i]; + label.title = "Previously was: " + data.currentAudioConstraints[i]; + input.title = "Previously was: " + data.currentAudioConstraints[i]; + } else { + label.innerText = i; + } + + if ((i === "height") || (i === "width")){ + input.title = "Hold CTRL (or cmd) to lock width and height together when changing them"; + input.min = 16; + } + + + input.type = "range"; + input.dataset.keyname = i; + input.dataset.track = n; + input.dataset.deviceId = data.deviceId; + input.dataset.UUID = UUID; + input.id = "constraints_" + i + "_"+n+ "_"+UUID; + input.style = "display:block; width:100%;"; + input.name = "constraints_" + i + "_"+n + "_"+ UUID; + + if (i=="channelCount"){ + input.style.display = "none"; + manualInput.style.margin = "5px 0px 9px 10px"; + } + + manualInput.onchange = function(e) { + getById("constraints_" + e.target.dataset.keyname + "_"+e.target.dataset.track + "_"+ e.target.dataset.UUID).value = parseFloat(e.target.value); + requestAudioHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, e.target.dataset.deviceId); + }; + + input.onchange = function(e) { + //e.target.title = e.target.value; + getById("label_" + e.target.dataset.keyname + "_"+e.target.dataset.track + "_"+ e.target.dataset.UUID ).value = parseFloat(e.target.value); + //updateAudioConstraints(e.target.dataset.keyname, e.target.value); + requestAudioHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, e.target.dataset.deviceId); + }; + + audioEle.appendChild(label); + audioEle.appendChild(manualInput); + audioEle.appendChild(input); + } else if ((typeof data.audioConstraints[i] === 'object') && (data.audioConstraints[i] !== null)) { + if (i == "resizeMode") { + continue; + } + + var div = document.createElement("div"); + var label = document.createElement("label"); + label.id = "label_" + i + "_"+n+ "_"+UUID; + label.htmlFor = "constraints_" + i + "_"+n+ "_"+UUID; + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; + label.style = "display:inline-block; padding:0;"; + var input = document.createElement("select"); + var c = document.createElement("option"); + + + if (data.audioConstraints[i].length > 1) { + for (var opts in data.audioConstraints[i]) { + log(opts); + if (data.audioConstraints[i][opts] === false){ + var opt = new Option("Off", data.audioConstraints[i][opts]); + } else if (data.audioConstraints[i][opts] === true){ + var opt = new Option("On", data.audioConstraints[i][opts]); + } else { + var opt = new Option(data.audioConstraints[i][opts], data.audioConstraints[i][opts]); + } + input.options.add(opt); + if (i in data.currentAudioConstraints) { + if (data.audioConstraints[i][opts] == data.currentAudioConstraints[i]) { + opt.selected = "true"; + } + } + } + } else if (i.toLowerCase == "torch") { + var opt = new Option("Off", false); + input.options.add(opt); + opt = new Option("On", true); + input.options.add(opt); + try{ + if (i in data.currentAudioConstraints) { + if (data.audioConstraints[i]['torch'] == true) { + opt.selected = "true"; + } + } + } catch(e){} + } else { + continue; + } + + input.id = "constraints_" + i + "_"+n+ "_"+UUID; + input.className = "constraintCameraInput"; + input.name = input.id; + input.style = "display:inline; padding:2px;"; + input.dataset.keyname = i; + input.dataset.track = n; + input.dataset.deviceId = data.deviceId; + input.dataset.UUID = UUID; + input.dataset.chosen = input.value; + input.onchange = function(e) { + this.dataset.chosen = this.value; + //getById("label_"+e.target.dataset.keyname).innerText =e.target.dataset.keyname+": "+e.target.value; + //updateAudioConstraints(e.target.dataset.keyname, e.target.value); + requestAudioHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, e.target.dataset.deviceId); + log(e.target.dataset.keyname, e.target.value); + }; + audioEle.appendChild(div); + div.appendChild(label); + div.appendChild(input); + } else if (typeof data.audioConstraints[i] === 'boolean') { + + var div = document.createElement("div"); + var label = document.createElement("label"); + label.id = "label_" + i + "_"+n+ "_"+UUID; + label.htmlFor = "constraints_" + i + "_"+n+ "_"+UUID; + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; + label.style = "display:inline-block; padding:0;"; + label.dataset.keyname = i ; + label.dataset.track = n; + var input = document.createElement("select"); + var c = document.createElement("option"); + + var opt = new Option("Off", false); + input.options.add(opt); + opt = new Option("On", true); + input.options.add(opt); + + try{ + if (data.audioConstraints[i] === true){ + opt.selected = "true"; + } + } catch(e){} + + input.dataset.deviceId = data.deviceId; + input.id = "constraints_" + i + "_"+n+ "_"+UUID; + input.className = "constraintCameraInput"; + input.name = input.id; + input.style = "display:inline; padding:2px;"; + input.dataset.keyname = i; + input.dataset.track = n; + input.dataset.UUID = UUID; + input.dataset.chosen = input.value; + input.onchange = function(e) { + this.dataset.chosen = this.value; + //getById("label_"+e.target.dataset.keyname).innerText =e.target.dataset.keyname+": "+e.target.value; + //updateAudioConstraints(e.target.dataset.keyname, e.target.value); + requestAudioHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, e.target.dataset.deviceId); + log(e.target.dataset.keyname, e.target.value); + }; + audioEle.appendChild(div); + div.appendChild(label); + div.appendChild(input); + } + } catch (e) { + errorlog(e); + } + } + + + if (data.subGain!==false) { + var label = document.createElement("label"); + var i = "Gain"; + var div = document.createElement("div"); + label.id = "label_" + i + "_"+n+ "_"+UUID; + label.htmlFor = "constraints_" + i + "_" + n+ "_"+UUID; + + var input = document.createElement("input"); + input.min = 0; + input.max = 200; + input.value = data.subGain*100; + input.title = "Previously was: "+parseInt(input.value); + input.type = "range"; + input.dataset.keyname = i; + input.dataset.track = n; + input.dataset.labelname = "Gain:" + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; + + var manualInput = document.createElement("input"); + manualInput.type = "number"; + manualInput.dataset.keyname = i; + manualInput.value = parseFloat(input.value); + manualInput.className = "manualInput"; + manualInput.id = "label_" + i + "_" + n+ "_"+UUID; + manualInput.dataset.UUID = UUID; + manualInput.dataset.track = n; + + + input.dataset.track = data.deviceId; + input.dataset.UUID = UUID; + input.id = "constraints_" + i + "_" + n+ "_"+UUID; + input.style = "display:block; width:100%;"; + input.name = input.id; + input.style.margin = "2px 0px 5px"; + + manualInput.onchange = function(e) { + getById("constraints_" + e.target.dataset.keyname + "_"+e.target.dataset.track + "_"+ e.target.dataset.UUID).value = parseFloat(e.target.value); + requestChangeSubGain(parseInt(e.target.value), e.target.dataset.UUID, e.target.dataset.track); + }; + + input.onchange = function(e) { + getById("label_" + e.target.dataset.keyname + "_"+e.target.dataset.track + "_"+ e.target.dataset.UUID).value = parseFloat(e.target.value); + requestChangeSubGain(parseInt(e.target.value), e.target.dataset.UUID, e.target.dataset.track); + }; + + input.oninput = function(e) { + getById("label_" + e.target.dataset.keyname + "_"+e.target.dataset.track + "_"+ e.target.dataset.UUID).value = parseFloat(e.target.value); + if (Date.now() - remoteSliderTimeout > 100){ + remoteSliderTimeout = Date.now(); + requestChangeSubGain(parseInt(e.target.value), e.target.dataset.UUID, e.target.dataset.track); + } + }; + + audioEle.appendChild(div) + div.appendChild(label); + div.appendChild(manualInput); + audioEle.appendChild(input); + } + + query("#container_"+UUID+" .advancedAudioSettings").appendChild(audioEle); + } + + if (fixScrollReset){ + clearTimeout(fixScrollReset); + fixScrollReset = null; + getById("directorlayout").scrollTop = fixScrollResetValue; + } +} + +var remoteSliderTimeout = 0; + +function updateDirectorsVideo(data, UUID) { + var videoEle = document.createElement("div"); + if (data.trackLabel) { + var label = document.createElement("label"); + label.innerText = data.trackLabel; + label.style.display = "block"; + label.id = "remoteVideoLabel_"+UUID; + label.dataset.UUID = UUID; + label.classList.add("settingsLabel") + videoEle.appendChild(label); + } + + for (var i in data.cameraConstraints) { + try { + log(i); + log(data.cameraConstraints[i]); + + + if (i === "focusMode") { + continue // I'll handle this with FocusDistance instead + } else if (i === "whiteBalanceMode") { + continue // I'll handle this elsewhere + } else if (i === "exposureMode") { + continue // I'll handle this elsewhere + } + + + if ((typeof data.cameraConstraints[i] === 'object') && (data.cameraConstraints[i] !== null) && ("max" in data.cameraConstraints[i]) && ("min" in data.cameraConstraints[i])){ + if (i === "aspectRatio") { + // continue; + } else if (i === "width") { + // continue; + } else if (i === "height") { + // continue; + } else if (i === "frameRate") { + // continue; + } else if (i === "latency") { + // continue; + } else if (i === "sampleRate") { + continue; + } else if (i === "channelCount") { + // continue; + } + + var manualMode = false; + var manualLabel = false; + if (i ==="exposureTime"){ + if (data.currentCameraConstraints["exposureMode"]){ + manualMode = document.createElement("input"); + manualMode.type = "checkbox"; + manualMode.id = "manual_"+i+"_"+UUID; + manualMode.dataset.UUID = UUID; + manualMode.dataset.keyname = "exposureMode"; + manualMode.onchange = function(e) { + var value = "manual"; + if (e.target.checked){ + value = "continuous"; + } + requestVideoHack(e.target.dataset.keyname, value, e.target.dataset.UUID, true); + //getById("constraints_" + e.target.dataset.keyname + "_" + e.target.dataset.UUID).value = parseFloat(e.target.value); + //getById("label_" + e.target.dataset.keyname+ "_" + e.target.dataset.UUID).value = parseFloat(e.target.value); + }; + + manualLabel = document.createElement("label"); + manualLabel.htmlFor = manualMode.id; + manualLabel.innerHTML = "Auto: "; + manualLabel.style.marginLeft = "20px"; + if (data.currentCameraConstraints["exposureMode"] == "continuous"){ + manualMode.checked = true; + } + } + } else if (i ==="focusDistance"){ + if (data.currentCameraConstraints["focusMode"]){ + manualMode = document.createElement("input"); + manualMode.type = "checkbox"; + manualMode.id = "manual_"+i+"_"+UUID; + manualMode.dataset.UUID = UUID; + manualMode.dataset.keyname = "focusMode"; + manualMode.onchange = function(e) { + var value = "manual"; + if (e.target.checked){ + value = "continuous"; + } + requestVideoHack(e.target.dataset.keyname, value, e.target.dataset.UUID, true); + }; + manualLabel = document.createElement("label"); + manualLabel.htmlFor = manualMode.id; + manualLabel.innerHTML = "Auto: "; + manualLabel.style.marginLeft = "20px"; + if (data.currentCameraConstraints["focusMode"] == "continuous"){ + manualMode.checked = true; + } + } + } else if (i ==="colorTemperature"){ + if (data.currentCameraConstraints["whiteBalanceMode"]){ + manualMode = document.createElement("input"); + manualMode.type = "checkbox"; + manualMode.id = "manual_"+i+"_"+UUID; + manualMode.dataset.UUID = UUID; + manualMode.dataset.keyname = "whiteBalanceMode"; + manualMode.onchange = function(e) { + var value = "manual"; + if (e.target.checked){ + value = "continuous"; + } + requestVideoHack(e.target.dataset.keyname, value, e.target.dataset.UUID, true); + }; + manualLabel = document.createElement("label"); + manualLabel.htmlFor = manualMode.id; + manualLabel.innerHTML = "Auto: "; + manualLabel.style.marginLeft = "20px"; + if (data.currentCameraConstraints["whiteBalanceMode"] == "continuous"){ + manualMode.checked = true; + } + } + } + + + + var label = document.createElement("label"); + //label.id = "label_" + i; + label.htmlFor = "constraints_" + i + " _"+ UUID; + if (i === "colorTemperature"){ + label.innerText = "Color Temp:"; + } else if (i === "exposureCompensation"){ + label.innerText = "Exposure Comp:"; + } else if (i === "exposureTime"){ + label.innerText = "Exposure:"; + } else if (i === "focusDistance"){ + label.innerText = "Focus:"; + } else { + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; + } + + + if (i === "zoom" || i === "pan" || i === "til"){ + label.innerHTML = " "+label.innerText + } + + var input = document.createElement("input"); + + if (i === "aspectRatio") { + input.max = 5; + input.min = 0.2; + input.step = 0.00001 + } else { + input.min = data.cameraConstraints[i].min; + input.max = data.cameraConstraints[i].max; + } + + if (parseFloat(input.min) == parseFloat(input.max)) { + continue; + } + + if (i in data.currentCameraConstraints) { + input.value = data.currentCameraConstraints[i]; + label.title = "Previously was: " + data.currentCameraConstraints[i]; + input.title = "Previously was: " + data.currentCameraConstraints[i]; + } + + input.type = "range"; + input.dataset.keyname = i; + input.dataset.UUID = UUID; + input.id = "constraints_" + i + "_" + UUID; + input.name = input.id; + input.classList.add("inputConstraint"); + input.manualMode = manualMode; + + + if ((i === "height") || (i === "width")){ + input.title = "Hold CTRL (or cmd) to lock width and height together when changing them"; + input.min = 16; + } + + var manualInput = document.createElement("input"); + manualInput.type = "number"; + manualInput.value = parseFloat(input.value); + manualInput.className = "manualInput"; + manualInput.id = "label_" + i + "_" + UUID; + manualInput.name = manualInput.id; + manualInput.dataset.keyname = i; + manualInput.dataset.UUID = UUID; + manualInput.manualMode = manualMode; + + if ("step" in data.cameraConstraints[i]) { + manualInput.step = data.cameraConstraints[i].step; + input.step = data.cameraConstraints[i].step; + } else if (i === "aspectRatio") { + input.step = 0.000001 + manualInput.step = 0.005; + } + + manualInput.onchange = function(e) { + getById("constraints_" + e.target.dataset.keyname + "_" + e.target.dataset.UUID).value = parseFloat(e.target.value); + if (e.target.manualMode){ + requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, true); + } else { + requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, false); + } + }; + + input.onchange = function(e) { + getById("label_" + e.target.dataset.keyname+ "_" + e.target.dataset.UUID).value = parseFloat(e.target.value); + //updateVideoConstraints(e.target.dataset.keyname, e.target.value); + if (CtrlPressed || e.target.manualMode){ + requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, true); + } else { + requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, false); + } + }; + + input.oninput = function(e) { + getById("label_" + e.target.dataset.keyname+ "_" + e.target.dataset.UUID).value = parseFloat(e.target.value); + if (Date.now() - remoteSliderTimeout > 100){ + remoteSliderTimeout = Date.now(); + if (CtrlPressed){ + requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, true); + } else { + requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, false); + } + } + }; + + videoEle.appendChild(label); + videoEle.appendChild(manualInput); + + if (manualMode && manualLabel){ + videoEle.appendChild(manualLabel); + videoEle.appendChild(manualMode); + } + + if (i === "aspectRatio") { + var preSelectButton = document.createElement("button"); + preSelectButton.value = 16/9.0; + preSelectButton.innerText = "16:9"; + preSelectButton.dataset.keyname = i; + preSelectButton.dataset.UUID = UUID; + preSelectButton.className = "preSelectButton"; + preSelectButton.onclick = function(e) { + getById("constraints_" + e.target.dataset.keyname+ "_" + e.target.dataset.UUID).value = parseFloat(e.target.value); + getById("label_" + e.target.dataset.keyname+ "_" + e.target.dataset.UUID).value = parseFloat(e.target.value); + requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, false); + }; + videoEle.appendChild(preSelectButton); + var preSelectButton = document.createElement("button"); + preSelectButton.value = 9/16.0; + preSelectButton.innerText = "9:16"; + preSelectButton.dataset.UUID = UUID; + preSelectButton.className = "preSelectButton"; + preSelectButton.dataset.keyname = i; + preSelectButton.onclick = function(e) { + getById("constraints_" + e.target.dataset.keyname+ "_" + e.target.dataset.UUID).value = parseFloat(e.target.value); + getById("label_" + e.target.dataset.keyname+ "_" + e.target.dataset.UUID).value = parseFloat(e.target.value); + requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, false); + }; + videoEle.appendChild(preSelectButton); + } + + + videoEle.appendChild(input); + } else if ((typeof data.cameraConstraints[i] === 'object') && (data.cameraConstraints[i] !== null)) { + if (i == "resizeMode") { + continue; + } + + var div = document.createElement("div"); + var label = document.createElement("label"); + label.id = "label_" + i + "_"+UUID; + label.name = label.id; + label.htmlFor = "constraints_" + i + "_"+UUID; + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; + label.style = "display:inline-block; padding:0;"; + label.dataset.keyname = i; + label.dataset.UUID = UUID; + var input = document.createElement("select"); + var c = document.createElement("option"); + + if (data.cameraConstraints[i].length > 1) { + for (var opts in data.cameraConstraints[i]) { + log(opts); + + if (data.cameraConstraints[i][opts] === false){ + var opt = new Option("Off", data.cameraConstraints[i][opts]); + } else if (data.cameraConstraints[i][opts] === true){ + var opt = new Option("On", data.cameraConstraints[i][opts]); + } else { + var opt = new Option(data.cameraConstraints[i][opts], data.cameraConstraints[i][opts]); + } + + input.options.add(opt); + if (i in data.currentCameraConstraints) { + if (data.cameraConstraints[i][opts] == data.currentCameraConstraints[i]) { + opt.selected = "true"; + } + } + } + } else if (i.toLowerCase == "torch") { + var opt = new Option("Off", false); + input.options.add(opt); + opt = new Option("On", true); + input.options.add(opt); + try{ + if (i in data.currentCameraConstraints) { + if (data.cameraConstraints[i]['torch'] == true) { + opt.selected = "true"; + } + } + } catch(e){} + } else { + continue; + } + + input.id = "constraints_" + i + "_"+UUID; + input.className = "constraintCameraInput"; + input.name = input.id; + input.dataset.UUID = UUID; + input.style = "display:inline; padding:2px;"; + input.dataset.keyname = i; + input.dataset.chosen = input.value; + input.onchange = function(e) { + this.dataset.chosen = this.value; + //getById("label_"+e.target.dataset.keyname+ "_" + e.target.dataset.UUID).innerText =e.target.dataset.keyname+": "+e.target.value; + //updateVideoConstraints(e.target.dataset.keyname, e.target.value); + if (CtrlPressed){ + requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, true); + } else { + requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, false); + } + log(e.target.dataset.keyname, e.target.value); + }; + videoEle.appendChild(div); + div.appendChild(label); + div.appendChild(input); + } else if (typeof data.cameraConstraints[i] === 'boolean') { + + var div = document.createElement("div"); + var label = document.createElement("label"); + label.id = "label_" + + i + "_"+UUID; + label.name = label.id; + label.htmlFor = "constraints_" + + i + "_"+UUID; + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; + label.style = "display:inline-block; padding:0;"; + label.dataset.keyname = i; + label.dataset.UUID = UUID; + var input = document.createElement("select"); + var c = document.createElement("option"); + + var opt = new Option("Off", false); + input.options.add(opt); + opt = new Option("On", true); + input.options.add(opt); + + try{ + if (data.audioConstraints[i] === true){ + opt.selected = "true"; + } + } catch(e){} + + input.id = "constraints_" + + i + "_"+UUID; + input.className = "constraintCameraInput"; + input.name = input.id; + input.style = "display:inline; padding:2px;"; + input.dataset.UUID = UUID; + input.dataset.keyname = i; + input.dataset.chosen = input.value; + input.onchange = function(e) { + this.dataset.chosen = this.value; + //getById("label_"+e.target.dataset.keyname+ "_" + e.target.dataset.UUID).innerText =e.target.dataset.keyname+": "+e.target.value; + //updateVideoConstraints(e.target.dataset.keyname, e.target.value); + if (CtrlPressed){ + requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, true); + } else { + requestVideoHack(e.target.dataset.keyname, e.target.value, e.target.dataset.UUID, false); + } + log(e.target.dataset.keyname, e.target.value); + }; + videoEle.appendChild(div); + div.appendChild(label); + div.appendChild(input); + } + } catch (e) { + errorlog(e); + } + } + + query("#container_"+UUID+" .advancedVideoSettings").innerHTML = ""; + query("#container_"+UUID+" .advancedVideoSettings").appendChild(videoEle); + query("#container_"+UUID+" .advancedVideoSettings").classList.remove("hidden"); + + if (fixScrollReset){ + clearTimeout(fixScrollReset); + fixScrollReset = null; + getById("directorlayout").scrollTop = fixScrollResetValue; + } +} + +/////// +function capitalizeFirstLetter(string) { + return string.charAt(0).toUpperCase() + string.slice(1); +} + +function listAudioSettings() { + getById("popupSelector_constraints_audio").innerHTML = ""; + + var tracks = session.streamSrc.getAudioTracks(); + if (!tracks.length){ + warnlog("session.streamSrc contains no audio tracks"); + return + } + + for (var ii = 0; ii< tracks.length; ii++){ + track0 = tracks[ii]; + if (track0.getCapabilities) { + session.audioConstraints = track0.getCapabilities(); + } else if (Firefox){ // let's pretend like Firefox doesn't actually suck + session.audioConstraints = { + "autoGainControl": [ + true, + false + ], + // "channelCount": { + // "max": 2, + // "min": 1 + // }, + // "deviceId": "default", + "echoCancellation": [ + true, + false + ], + // "groupId": "a3cbdec54a9b6ed473fd950415626f7e76f9d1b90f8c768faab572175a355a17", + // "latency": { + // "max": 0.01, + // "min": 0.01 + // }, + "noiseSuppression": [ + true, + false + ], + // "sampleRate": { + // "max": 48000, + // "min": 48000 + // }, + // "sampleSize": { + // "max": 16, + // "min": 16 + /// } + }; + } + + try { + if (track0.getSettings) { + session.currentAudioConstraints = track0.getSettings(); + + if (!session.stereo){ + try { + delete session.currentAudioConstraints.channelCount; + delete session.audioConstraints.channelCount; + } catch(e){}; + } else if (session.audioInputChannels && (session.audioInputChannels==1)){ // this is pretty hacky, but it gets around not being able to actually set 1-channel. Not sure why. + session.currentAudioConstraints.channelCount = 1; + } + + } + } catch (e) { + errorlog(e); + } + + ////// + if (ii==0){ + for (var webAudio in session.webAudios) { + if (session.webAudios[webAudio].gainNode ) { + + if (getById("popupSelector_constraints_audio").style.display == "none") { + getById("advancedOptionsAudio").style.display = "inline-flex"; + } + var div = document.createElement("div"); + var label = document.createElement("label"); + var i = "masterGain"; + //label.id = "label_" + i; + label.htmlFor = "constraints_" + i; + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; + label.style = "display:inline-block;"; + + var input = document.createElement("input"); + input.min = 0; + input.max = 200; + + input.dataset.deviceid = track0.id; // pointless + + input.type = "range"; + input.dataset.keyname = i; + input.dataset.labelname = label.innerHTML; + input.id = "constraints_" + i; + input.style = "display:block; width:100%;"; + input.name = "constraints_" + i; + + input.value = session.webAudios[webAudio].gainNode.gain.value * 100; + //label.innerHTML += " " + parseInt(session.webAudios[webAudio].gainNode.gain.value * 100); + input.title = parseInt(input.value); + + var manualInput = document.createElement("input"); + manualInput.type = "number"; + manualInput.dataset.keyname = i; + manualInput.dataset.deviceid = track0.id; + manualInput.dataset.labelname = label.innerHTML; + manualInput.value = parseFloat(input.value); + manualInput.className = "manualInput"; + manualInput.id = "label_" + i; + + manualInput.onchange = function(e) { + getById("constraints_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + changeMainGain(e.target.value); + e.target.title = e.target.value; + pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + + input.oninput = function(e) { + getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + changeMainGain(e.target.value); + e.target.title = e.target.value; + }; + + input.onchange = function(e) { + getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + changeMainGain(e.target.value); + e.target.title = e.target.value; + pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + + getById("popupSelector_constraints_audio").appendChild(div); + div.appendChild(label); + div.appendChild(manualInput); + div.appendChild(input); + break; + } + } + } + + if (session.micDelay!==false && ii==0) { // ii==0 implies only track0 is supported by the web audio pipeline currently (or everything after the mixer node) + if (getById("popupSelector_constraints_audio").style.display == "none") { + getById("advancedOptionsAudio").style.display = "inline-flex"; + } + + var label = document.createElement("label"); + var i = "micDelay"; + label.htmlFor = "constraints_" + i; + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + " (ms):"; + + var input = document.createElement("input"); + input.min = 0; + input.max = 500; + + input.dataset.deviceid = track0.id; // pointless, for now + + input.type = "range"; + input.dataset.keyname = i; + input.dataset.labelname = label.innerHTML; + input.id = "constraints_" + i; + input.style = "display:block; width:100%;"; + input.name = "constraints_" + i; + + for (var webAudio in session.webAudios) { + if (session.webAudios[webAudio].micDelay) { // session.webAudios[waid].micDelay.delayTime.setValueAtTime + input.value = session.webAudios[webAudio].micDelay.delayTime.value*1000; + label.innerHTML += " " + parseInt(session.webAudios[webAudio].micDelay.delayTime.value*1000); + input.title = input.value; + break; + } + } + + var manualInput = document.createElement("input"); + manualInput.type = "number"; + manualInput.dataset.keyname = i; + manualInput.dataset.labelname = label.innerHTML; + manualInput.value = parseFloat(input.value); + manualInput.className = "manualInput"; + manualInput.id = "label_" + i; + + manualInput.onchange = function(e) { + getById("constraints_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + changeMicDelay(e.target.value, e.target.dataset.deviceid); + e.target.title = e.target.value; + pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + + input.oninput = function(e) { + getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + changeMicDelay(e.target.value, e.target.dataset.deviceid); + e.target.title = e.target.value; + }; + + input.onchange = function(e) { + getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + changeMicDelay(e.target.value, e.target.dataset.deviceid); + e.target.title = e.target.value; + pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + + getById("popupSelector_constraints_audio").appendChild(label); + getById("popupSelector_constraints_audio").appendChild(manualInput); + getById("popupSelector_constraints_audio").appendChild(input); + } + + + if (session.lowcut && ii==0) { // ii==0 implies only track0 is supported by the web audio pipeline currently (or everything after the mixer node) + if (getById("popupSelector_constraints_audio").style.display == "none") { + getById("advancedOptionsAudio").style.display = "inline-flex"; + } + + var label = document.createElement("label"); + var i = "Low_Cut"; + label.htmlFor = "constraints_" + i; + label.innerText = "Low Cut:"; + + var input = document.createElement("input"); + input.min = 50; + input.max = 400; + + input.dataset.deviceid = track0.id; // pointless + + input.type = "range"; + input.dataset.keyname = i; + input.dataset.labelname = label.innerHTML; + input.id = "constraints_" + i; + input.style = "display:block; width:100%;"; + input.name = "constraints_" + i; + + for (var webAudio in session.webAudios) { + if (session.webAudios[webAudio].lowcut1) { + input.value = session.webAudios[webAudio].lowcut1.frequency.value; + label.innerHTML += " " + session.webAudios[webAudio].lowcut1.frequency.value; + input.title = input.value; + break; + } + } + + var manualInput = document.createElement("input"); + manualInput.type = "number"; + manualInput.dataset.keyname = i; + manualInput.dataset.labelname = label.innerHTML; + manualInput.value = parseFloat(input.value); + manualInput.className = "manualInput"; + manualInput.id = "label_" + i; + + manualInput.onchange = function(e) { + getById("constraints_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + changeLowCut(e.target.value, e.target.dataset.deviceid); + e.target.title = e.target.value; + pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + + input.oninput = function(e) { + getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + changeLowCut(e.target.value, e.target.dataset.deviceid); + e.target.title = e.target.value; + }; + + input.onchange = function(e) { + getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + changeLowCut(e.target.value, e.target.dataset.deviceid); + e.target.title = e.target.value; + pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + + getById("popupSelector_constraints_audio").appendChild(label); + getById("popupSelector_constraints_audio").appendChild(manualInput); + getById("popupSelector_constraints_audio").appendChild(input); + } + + if (session.equalizer && ii==0) { // ii==0 implies only track0 is supported by the web audio pipeline currently (or everything after the mixer node) + if (getById("popupSelector_constraints_audio").style.display == "none") { + getById("advancedOptionsAudio").style.display = "inline-flex"; + } + + var label = document.createElement("label"); + var i = "Low_EQ"; + //label.id = "label_" + i; + label.htmlFor = "constraints_" + i; + label.innerHTML = "Low EQ:"; + + var input = document.createElement("input"); + input.min = -50; + input.max = 50; + + input.dataset.deviceid = track0.id; // pointless + + input.type = "range"; + input.dataset.keyname = i; + input.dataset.labelname = label.innerHTML; + input.id = "constraints_" + i; + input.style = "display:block; width:100%;"; + input.name = "constraints_" + i; + + for (var webAudio in session.webAudios) { + if (session.webAudios[webAudio].lowEQ) { + input.value = session.webAudios[webAudio].lowEQ.gain.value; + label.innerHTML += " " + session.webAudios[webAudio].lowEQ.gain.value; + input.title = input.value; + } + } + + var manualInput = document.createElement("input"); + manualInput.type = "number"; + manualInput.dataset.keyname = i; + manualInput.dataset.labelname = label.innerHTML; + manualInput.value = parseFloat(input.value); + manualInput.className = "manualInput"; + manualInput.id = "label_" + i; + + manualInput.onchange = function(e) { + getById("constraints_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + changeLowEQ(e.target.value, e.target.dataset.deviceid); + e.target.title = e.target.value; + pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + + input.oninput = function(e) { + getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + changeLowEQ(e.target.value, e.target.dataset.deviceid); + e.target.title = e.target.value; + }; + + input.onchange = function(e) { + getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + changeLowEQ(e.target.value, e.target.dataset.deviceid); + e.target.title = e.target.value; + pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + + getById("popupSelector_constraints_audio").appendChild(label); + getById("popupSelector_constraints_audio").appendChild(manualInput); + getById("popupSelector_constraints_audio").appendChild(input); + // + if (getById("popupSelector_constraints_audio").style.display == "none") { + getById("advancedOptionsAudio").style.display = "inline-flex"; + } + + var label = document.createElement("label"); + var i = "Mid_EQ"; + //label.id = "label_" + i; + label.htmlFor = "constraints_" + i; + label.innerHTML = "Mid EQ:"; + + var input = document.createElement("input"); + input.min = -50; + input.max = 50; + + input.dataset.deviceid = track0.id; // pointless + + input.type = "range"; + input.dataset.keyname = i; + input.dataset.labelname = label.innerHTML; + input.id = "constraints_" + i; + input.style = "display:block; width:100%;"; + input.name = "constraints_" + i; + + + for (var webAudio in session.webAudios) { + if (session.webAudios[webAudio].midEQ) { + input.value = session.webAudios[webAudio].midEQ.gain.value; + label.innerHTML += " " + session.webAudios[webAudio].midEQ.gain.value; + input.title = input.value; + } + } + + var manualInput = document.createElement("input"); + manualInput.type = "number"; + manualInput.dataset.keyname = i; + manualInput.dataset.labelname = label.innerHTML; + manualInput.value = parseFloat(input.value); + manualInput.className = "manualInput"; + manualInput.id = "label_" + i; + + manualInput.onchange = function(e) { + getById("constraints_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + changeMidEQ(e.target.value, e.target.dataset.deviceid); + e.target.title = e.target.value; + pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + + input.oninput = function(e) { + getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + changeMidEQ(e.target.value, e.target.dataset.deviceid); + e.target.title = e.target.value; + }; + + input.onchange = function(e) { + getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + changeMidEQ(e.target.value, e.target.dataset.deviceid); + e.target.title = e.target.value; + pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + + getById("popupSelector_constraints_audio").appendChild(label); + getById("popupSelector_constraints_audio").appendChild(manualInput); + getById("popupSelector_constraints_audio").appendChild(input); + // + if (getById("popupSelector_constraints_audio").style.display == "none") { + getById("advancedOptionsAudio").style.display = "inline-flex"; + } + + var label = document.createElement("label"); + var i = "High_EQ"; + //label.id = "label_" + i; + label.htmlFor = "constraints_" + i; + label.innerHTML = "High EQ:"; + + var input = document.createElement("input"); + input.min = -50; + input.max = 50; + + input.dataset.deviceid = track0.id; // pointless + + input.type = "range"; + input.dataset.keyname = i; + input.dataset.labelname = label.innerHTML; + input.id = "constraints_" + i; + input.style = "display:block; width:100%;"; + input.name = "constraints_" + i; + + for (var webAudio in session.webAudios) { + if (session.webAudios[webAudio].highEQ) { + input.value = session.webAudios[webAudio].highEQ.gain.value; + label.innerHTML += " " + session.webAudios[webAudio].highEQ.gain.value; + input.title = input.value; + } + } + + var manualInput = document.createElement("input"); + manualInput.type = "number"; + manualInput.dataset.keyname = i; + manualInput.dataset.labelname = label.innerHTML; + manualInput.value = parseFloat(input.value); + manualInput.className = "manualInput"; + manualInput.id = "label_" + i; + + manualInput.onchange = function(e) { + getById("constraints_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + changeHighEQ(e.target.value, e.target.dataset.deviceid); + e.target.title = e.target.value; + pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + + input.oninput = function(e) { + getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + changeHighEQ(e.target.value, e.target.dataset.deviceid); + e.target.title = e.target.value; + }; + + input.onchange = function(e) { + getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + changeHighEQ(e.target.value, e.target.dataset.deviceid); + e.target.title = e.target.value; + pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + + getById("popupSelector_constraints_audio").appendChild(label); + getById("popupSelector_constraints_audio").appendChild(manualInput); + getById("popupSelector_constraints_audio").appendChild(input); + } + + if ((session.noisegate!==false) && (ii==0)) { + for (var webAudio in session.webAudios) { + if (session.webAudios[webAudio].gatingNode) { + + var div = document.createElement("div"); + var label = document.createElement("label"); + + var i = "noiseGating"; + + label.id = "label_" + i + "_"+ii; + label.htmlFor = "constraints_" + i + "_"+ii; + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; + label.style = "display:inline-block;"; + label.dataset.keyname = i; + label.title = "This will reduce the gain ~80% when there is no one talking loudly"; + var input = document.createElement("select"); + var c = document.createElement("option"); + + + input.dataset.deviceid = track0.id; + + var opt = new Option("Off", false); + input.options.add(opt); + opt = new Option("On", true); + if (session.noisegate){ + opt.selected = "true"; + } + input.options.add(opt); + + if (getById("popupSelector_constraints_audio").style.display == "none") { + getById("advancedOptionsAudio").style.display = "inline-flex"; + } + + input.id = "constraints_" + i + "_"+ii; + input.className = "constraintCameraInput"; + input.name = "constraints_" + i + "_"+ii; + input.style = "display:inline; padding:2px;"; + input.dataset.keyname = i; + input.dataset.chosen = input.value; + input.onchange = function(e) { + this.dataset.chosen = this.value; + if (e.target.value == "false"){ + session.noisegate = null; + } else if (e.target.value == "true"){ + session.noisegate = true; + } else { + session.noisegate = e.target.value; + } + if (!session.noisegate){ + changeGatingGain(100); + changeGatingGain(100,3100); + } + pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + + getById("popupSelector_constraints_audio").appendChild(div); + div.appendChild(label); + div.appendChild(input); + break; + } + } + } + + + //////// + if (tracks.length>1){ + var label = document.createElement("h4"); + label.innerHTML = track0.label; + label.style = "text-shadow: 0 0 10px #fff3;margin:0px 0 10px 0" + if (ii>0){ + label.style = "text-shadow: 0 0 10px #fff3;margin:40px 0 10px 0" + } + getById("popupSelector_constraints_audio").appendChild(label); + } + + for (var i in session.audioConstraints) { + try { + log(i); + log(session.audioConstraints[i]); + + if ((typeof session.audioConstraints[i] === 'object') && (session.audioConstraints[i] !== null) && ("max" in session.audioConstraints[i]) && ("min" in session.audioConstraints[i])) { + if (i === "aspectRatio") { + continue; + } else if (i === "width") { + continue; + } else if (i === "height") { + continue; + } else if (i === "frameRate") { + continue; + } else if (i === "latency") { + // continue; + } else if (i === "sampleRate") { + //continue; + } else if (i === "sampleSize") { + //continue; + } else if (i === "channelCount") { + if (!session.stereo){ + continue; + } + } else if (!session.disableWebAudio && (i === "volume")){ + continue; + } + + var label = document.createElement("label"); + //label.id = "label_" + i + "_"+ii; + label.htmlFor = "constraints_" + i + "_"+ii; + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; + + + var input = document.createElement("input"); + input.min = session.audioConstraints[i].min; + input.max = session.audioConstraints[i].max; + + input.dataset.deviceid = track0.id; + + + + if (getById("popupSelector_constraints_audio").style.display == "none") { + getById("advancedOptionsAudio").style.display = "inline-flex"; + } + + var manualInput = document.createElement("input"); + manualInput.type = "number"; + + if ("step" in session.audioConstraints[i]) { + input.step = session.audioConstraints[i].step; + manualInput.step = session.audioConstraints[i].step; + } else if ("volume" == i) { + input.step = 0.01; + manualInput.step = 0.01; + } + + if (i in session.currentAudioConstraints) { + input.value = parseFloat(session.currentAudioConstraints[i]); + label.title = "Previously was: " + session.currentAudioConstraints[i]; + input.title = "Previously was: " + session.currentAudioConstraints[i]; + } + + if ((i === "height") || (i === "width")){ + input.title = "Hold CTRL (or cmd) to lock width and height together when changing them"; + input.min = 16; + } + + + input.type = "range"; + input.dataset.keyname = i; + input.dataset.track = ii; + + input.id = "constraints_" + i + "_"+ii; + input.style = "display:block; width:100%;"; + input.name = "constraints_" + i + "_"+ii; + + manualInput.dataset.keyname = i; + manualInput.dataset.track = ii; + manualInput.dataset.deviceid = track0.id; + + manualInput.className = "manualInput"; + manualInput.id = "label_" + i + "_"+ii; + manualInput.max = session.audioConstraints[i].max; + manualInput.min = session.audioConstraints[i].min; + + manualInput.value = parseFloat(session.currentAudioConstraints[i]); + + if (i=="channelCount"){ + input.style.display = "none"; + } + + manualInput.onchange = function(e) { + try { + getById("constraints_" + e.target.dataset.keyname+"_"+ e.target.dataset.track).value = parseFloat(e.target.value); + applyAudioHack(e.target.dataset.keyname, e.target.value, e.target.dataset.deviceid); + e.target.title = e.target.value; + pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }catch(e){errorlog(e);} + }; + + input.onchange = function(e) { + try { + getById("label_" + e.target.dataset.keyname+"_"+ e.target.dataset.track).value = parseFloat(e.target.value); + applyAudioHack(e.target.dataset.keyname, e.target.value, e.target.dataset.deviceid); + e.target.title = e.target.value; + pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }catch(e){errorlog(e);} + }; + + // not sure if I should include "oninput" as well? Probably not needed. + + var div = document.createElement("div"); + if (parseFloat(input.min) == parseFloat(input.max)) { + manualInput.disabled = true + manualInput.title = "Only one option available, so can't be changed"; + label.title = "Only one option available, so can't be changed"; + div.appendChild(label); + div.appendChild(manualInput); + getById("popupSelector_constraints_audio").appendChild(div); + } else { + div.appendChild(label); + div.appendChild(manualInput); + div.appendChild(input); + getById("popupSelector_constraints_audio").appendChild(div); + } + } else if ((typeof session.audioConstraints[i] === 'object') && (session.audioConstraints[i] !== null)) { + if (i == "resizeMode") { + continue; + } + + var div = document.createElement("div"); + var label = document.createElement("label"); + label.id = "label_" + i + "_"+ii; + label.htmlFor = "constraints_" + i + "_"+ii; + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; + label.style = "display:inline-block;"; + label.dataset.keyname = i; + + var input = document.createElement("select"); + var c = document.createElement("option"); + + if (session.audioConstraints[i].length == 2) { + for (var opts in session.audioConstraints[i]) { + log(opts); + if (session.audioConstraints[i][opts]===true){ + var opt = new Option("On", session.audioConstraints[i][opts]); + } else if (session.audioConstraints[i][opts]===false){ + var opt = new Option("Off", session.audioConstraints[i][opts]); + } else { + var opt = new Option(session.audioConstraints[i][opts], session.audioConstraints[i][opts]); + } + input.options.add(opt); + + if (i in session.currentAudioConstraints) { + if (session.audioConstraints[i][opts] == session.currentAudioConstraints[i]) { + opt.selected = "true"; + } + } + } + } else if (session.audioConstraints[i].length > 1) { + for (var opts in session.audioConstraints[i]) { + log(opts); + var opt = new Option(session.audioConstraints[i][opts], session.audioConstraints[i][opts]); + input.options.add(opt); + + if (i in session.currentAudioConstraints) { + if (session.audioConstraints[i][opts] == session.currentAudioConstraints[i]) { + opt.selected = "true"; + } + } + + } + } else if (i.toLowerCase == "torch") { + var opt = new Option("Off", false); + input.options.add(opt); + opt = new Option("On", true); + input.options.add(opt); + } else { + continue; + } + + if (getById("popupSelector_constraints_audio").style.display == "none") { + getById("advancedOptionsAudio").style.display = "inline-flex"; + } + + input.id = "constraints_" + i + "_"+ii; + input.className = "constraintCameraInput"; + input.name = "constraints_" + i + "_"+ii; + input.dataset.deviceid = track0.id; + input.style = "display:inline; padding:2px;"; + input.dataset.keyname = i; + input.dataset.chosen = input.value; + input.onchange = function(e) { + this.dataset.chosen = this.value; + applyAudioHack(e.target.dataset.keyname, e.target.value, e.target.dataset.deviceid); + pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + getById("popupSelector_constraints_audio").appendChild(div); + div.appendChild(label); + div.appendChild(input); + } else if (typeof session.audioConstraints[i] === 'boolean') { + + var div = document.createElement("div"); + var label = document.createElement("label"); + label.id = "label_" + i + "_"+ii; + label.htmlFor = "constraints_" + i + "_"+ii; + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; + label.style = "display:inline-block;"; + label.dataset.keyname = i; + var input = document.createElement("select"); + var c = document.createElement("option"); + + + input.dataset.deviceid = track0.id; + + var opt = new Option("Off", false); + input.options.add(opt); + opt = new Option("On", true); + input.options.add(opt); + + + + if (getById("popupSelector_constraints_audio").style.display == "none") { + getById("advancedOptionsAudio").style.display = "inline-flex"; + } + + input.id = "constraints_" + i + "_"+ii; + input.className = "constraintCameraInput"; + input.name = "constraints_" + i + "_"+ii; + input.style = "display:inline; padding:2px;"; + input.dataset.keyname = i; + input.dataset.chosen = input.value; + input.onchange = function(e) { + this.dataset.chosen = this.value; + //getById("label_"+e.target.dataset.keyname).innerHTML =e.target.dataset.keyname+": "+e.target.value; + //updateAudioConstraints(e.target.dataset.keyname, e.target.value); + applyAudioHack(e.target.dataset.keyname, e.target.value, e.target.dataset.deviceid); + pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + getById("popupSelector_constraints_audio").appendChild(div); + div.appendChild(label); + div.appendChild(input); + } + } catch (e) { + errorlog(e); + } + } + + if (tracks.length>1){ + for (var webAudio in session.webAudios) { + if (session.webAudios[webAudio].subGainNodes && (track0.id in session.webAudios[webAudio].subGainNodes)) { + + if (getById("popupSelector_constraints_audio").style.display == "none") { + getById("advancedOptionsAudio").style.display = "inline-flex"; + } + var div = document.createElement("div"); + var label = document.createElement("label"); + var i = "Gain"; + //label.id = "label_" + i + "_" + track0.id; + label.htmlFor = "constraints_" + i + "_" + track0.id; + label.innerText = "Gain:"; + label.style = "display:inline-block; padding:0;margin-top: 15px"; + + var input = document.createElement("input"); + input.min = 0; + input.max = 200; + + input.dataset.deviceid = track0.id; // pointless + + input.type = "range"; + input.dataset.keyname = i; + input.dataset.labelname = label.innerHTML; + input.id = "constraints_" + i+ "_" + track0.id; + input.style = "display:block; width:100%;"; + input.name = "constraints_" + i + "_" + track0.id; + + input.value = session.webAudios[webAudio].subGainNodes[track0.id].gain.value * 100; + label.innerHTML += " " + parseInt(session.webAudios[webAudio].subGainNodes[track0.id].gain.value * 100); + input.title = parseInt(input.value); + + var manualInput = document.createElement("input"); + manualInput.type = "number"; + manualInput.dataset.keyname = i; + manualInput.dataset.deviceid = track0.id; + manualInput.dataset.labelname = label.innerHTML; + manualInput.value = parseFloat(input.value); + manualInput.className = "manualInput"; + manualInput.id = "label_" + i + "_" + track0.id; + + manualInput.onchange = function(e) { + getById("constraints_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + changeSubGain(e.target.value, e.target.dataset.deviceid); + e.target.title = e.target.value; + pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + + input.oninput = function(e) { + getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + changeSubGain(e.target.value, e.target.dataset.deviceid); + e.target.title = e.target.value; + }; + + input.onchange = function(e) { + getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + changeSubGain(e.target.value, e.target.dataset.deviceid); + e.target.title = e.target.value; + pokeIframeAPI("mic-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + + getById("popupSelector_constraints_audio").appendChild(div); + div.appendChild(label); + div.appendChild(manualInput); + div.appendChild(input); + break; + } + } + } + + } +} + + +function applyAudioHack(constraint, value = null, deviceid="default") { + if (value == parseFloat(value)) { + value = parseFloat(value); + if (constraint == "channelCount"){ + session.audioInputChannels = value; + } + value = { + exact: value + }; + } else if (value == "true") { + value = true; + } else if (value == "false") { + value = false; + } + //////////////// + try { + var tracks = session.streamSrc.getAudioTracks(); + if (tracks.length) { + var track0 = tracks[0]; + for (var ii = 0;ii session.totalRoomBitrate) { + return; + } else { + session.controlRoomBitrate = parseInt(e.target.value); + } + updateMixer(); + pokeIframeAPI("camera-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + + input.onchange = function(e) { + getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + if (e.target.value > session.totalRoomBitrate) { + return; + } else { + session.controlRoomBitrate = parseInt(e.target.value); + } + updateMixer(); + pokeIframeAPI("camera-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + getById("popupSelector_constraints_video").appendChild(label); + getById("popupSelector_constraints_video").appendChild(manualInput); + getById("popupSelector_constraints_video").appendChild(input); + } + try { + var track0 = session.streamSrc.getVideoTracks(); + if (track0.length) { + track0 = track0[0]; + if (track0.getCapabilities) { + session.cameraConstraints = track0.getCapabilities(); + } else { + session.cameraConstraints = {}; // probably firefox... + } + log(session.cameraConstraints); + } + } catch (e) { + errorlog(e); + return; + } + + try { + if (track0.getSettings) { + session.currentCameraConstraints = track0.getSettings(); + + if (screen && screen.orientation && screen.orientation.type){ + if (!screen.orientation.type.includes("portrait")){ + if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ + session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; + } + } + } else if (!window.matchMedia("(orientation: portrait)").matches){ + if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ + session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; + } + } + } else { + session.currentCameraConstraints = {}; + } + } catch (e) { + errorlog(e); + } + + + for (var i in session.cameraConstraints) { + try { + log(i); + log(session.cameraConstraints[i]); + + if (i === "focusMode") { + continue // I'll handle this with FocusDistance instead + } else if (i === "whiteBalanceMode") { + continue // I'll handle this elsewhere + } else if (i === "exposureMode") { + continue // I'll handle this elsewhere + } + + if ((typeof session.cameraConstraints[i] === 'object') && (session.cameraConstraints[i] !== null) && ("max" in session.cameraConstraints[i]) && ("min" in session.cameraConstraints[i])) { + + var manualMode = false; + var manualLabel = false; + if (i ==="exposureTime"){ + if (session.currentCameraConstraints["exposureMode"]){ + manualMode = document.createElement("input"); + manualMode.type = "checkbox"; + manualMode.id = "manual_"+i; + manualMode.dataset.keyname = "exposureMode"; + manualMode.onchange = function(e) { + var value = "manual"; + if (e.target.checked){ + value = "continuous"; + } + if (CtrlPressed){ + updateCameraConstraints(e.target.dataset.keyname, value, true, false); + } else { + updateCameraConstraints(e.target.dataset.keyname, value, false, false); + } + pokeIframeAPI("camera-constraint-changed", {name:e.target.dataset.keyname, value:value}); + }; + + manualLabel = document.createElement("label"); + manualLabel.htmlFor = manualMode.id; + manualLabel.innerHTML = "Auto: "; + manualLabel.style.marginLeft = "20px"; + if (session.currentCameraConstraints["exposureMode"] == "continuous"){ + manualMode.checked = true; + } + } + } else if (i ==="focusDistance"){ + if (session.currentCameraConstraints["focusMode"]){ + manualMode = document.createElement("input"); + manualMode.type = "checkbox"; + manualMode.id = "manual_"+i; + manualMode.dataset.keyname = "focusMode"; + manualMode.onchange = function(e) { + var value = "manual"; + if (e.target.checked){ + value = "continuous"; + } + if (CtrlPressed){ + updateCameraConstraints(e.target.dataset.keyname, value, true, false); + } else { + updateCameraConstraints(e.target.dataset.keyname, value, false, false); + } + pokeIframeAPI("camera-constraint-changed", {name:e.target.dataset.keyname, value:value}); + }; + manualLabel = document.createElement("label"); + manualLabel.htmlFor = manualMode.id; + manualLabel.innerHTML = "Auto: "; + manualLabel.style.marginLeft = "20px"; + if (session.currentCameraConstraints["focusMode"] == "continuous"){ + manualMode.checked = true; + } + } + } else if (i ==="colorTemperature"){ + if (session.currentCameraConstraints["whiteBalanceMode"]){ + manualMode = document.createElement("input"); + manualMode.type = "checkbox"; + manualMode.id = "manual_"+i; + manualMode.dataset.keyname = "whiteBalanceMode"; + manualMode.onchange = function(e) { + var value = "manual"; + if (e.target.checked){ + value = "continuous"; + } + if (CtrlPressed){ + updateCameraConstraints(e.target.dataset.keyname, value, true, false); + } else { + updateCameraConstraints(e.target.dataset.keyname, value, false, false); + } + pokeIframeAPI("camera-constraint-changed", {name:e.target.dataset.keyname, value:value}); + }; + manualLabel = document.createElement("label"); + manualLabel.htmlFor = manualMode.id; + manualLabel.innerHTML = "Auto: "; + manualLabel.style.marginLeft = "20px"; + if (session.currentCameraConstraints["whiteBalanceMode"] == "continuous"){ + manualMode.checked = true; + } + } + } + + + + var label = document.createElement("label"); + label.htmlFor = "constraints_" + i; + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; + + var input = document.createElement("input"); + input.min = parseFloat(session.cameraConstraints[i].min); + + if (i === "aspectRatio") { + input.max = 5; + input.min = 0.2 + } else { + input.min = parseFloat(session.cameraConstraints[i].min); + input.max = parseFloat(session.cameraConstraints[i].max); + } + + if (parseFloat(input.min) == parseFloat(input.max)) { + continue; + } + + if (getById("popupSelector_constraints_video").style.display == "none") { + getById("advancedOptionsCamera").style.display = "inline-flex"; + } + + var manualInput = document.createElement("input"); + manualInput.type = "number"; + manualInput.dataset.keyname = i; + + manualInput.className = "manualInput"; + manualInput.id = "label_" + i; + + if ("step" in session.cameraConstraints[i]) { + input.step = session.cameraConstraints[i].step; + manualInput.step = session.cameraConstraints[i].step; + } else if (i === "aspectRatio") { + input.step = 0.000001 + manualInput.step = 0.005; + } + + if (i in session.currentCameraConstraints) { + input.value = parseFloat(session.currentCameraConstraints[i]); + //label.innerHTML = i + ": " + session.currentCameraConstraints[i]; + manualInput.value = parseFloat(session.currentCameraConstraints[i]); + label.title = "Previously was: " + session.currentCameraConstraints[i]; + input.title = "Previously was: " + session.currentCameraConstraints[i]; + } else { + label.innerHTML = i; + } + if ((i === "height") || (i === "width")){ + input.title = "Hold CTRL (or cmd) to lock width and height together when changing them"; + input.min = 16; + } + + input.type = "range"; + input.dataset.keyname = i; + input.id = "constraints_" + i; + input.style = "display:block; width:100%;"; + input.name = "constraints_" + i; + + // on manualInput.change = .. update the input field! gotta riprocate + + manualInput.onchange = function(e) { + getById("constraints_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + updateCameraConstraints(e.target.dataset.keyname, e.target.value, false, false); + pokeIframeAPI("camera-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + + input.oninput = function(e) { + getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + if (CtrlPressed){ + updateCameraConstraints(e.target.dataset.keyname, e.target.value, true, false, false); + } else { + updateCameraConstraints(e.target.dataset.keyname, e.target.value, false, false, false); + } + }; + + input.onchange = function(e) { + getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + if (CtrlPressed){ + updateCameraConstraints(e.target.dataset.keyname, e.target.value, true, false); + } else { + updateCameraConstraints(e.target.dataset.keyname, e.target.value, false, false); + } + pokeIframeAPI("camera-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + + + getById("popupSelector_constraints_video").appendChild(label); + getById("popupSelector_constraints_video").appendChild(manualInput); + if (manualMode && manualLabel){ + getById("popupSelector_constraints_video").appendChild(manualLabel); + getById("popupSelector_constraints_video").appendChild(manualMode); + } + + if (i === "aspectRatio") { + var preSelectButton = document.createElement("button"); + preSelectButton.value = 16/9.0; + preSelectButton.innerText = "16:9"; + preSelectButton.dataset.keyname = i; + preSelectButton.className = "preSelectButton"; + preSelectButton.onclick = function(e) { + getById("constraints_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + updateCameraConstraints(e.target.dataset.keyname, e.target.value, false, false); + }; + getById("popupSelector_constraints_video").appendChild(preSelectButton); + var preSelectButton = document.createElement("button"); + preSelectButton.value = 9/16.0; + preSelectButton.innerText = "9:16"; + preSelectButton.className = "preSelectButton"; + preSelectButton.dataset.keyname = i; + preSelectButton.onclick = function(e) { + getById("constraints_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + getById("label_" + e.target.dataset.keyname).value = parseFloat(e.target.value); + updateCameraConstraints(e.target.dataset.keyname, e.target.value, false, false); + }; + getById("popupSelector_constraints_video").appendChild(preSelectButton); + } + + getById("popupSelector_constraints_video").appendChild(input); + } else if ((typeof session.cameraConstraints[i] === 'object') && (session.cameraConstraints[i] !== null)) { + if (i == "resizeMode") { + continue; + } + + var div = document.createElement("div"); + var label = document.createElement("label"); + label.id = "label_" + i; + label.htmlFor = "constraints_" + i; + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; + label.style = "display:inline-block;"; + label.dataset.keyname = i; + var input = document.createElement("select"); + + if (session.cameraConstraints[i].length > 1) { + var included = false; + for (var opts in session.cameraConstraints[i]) { + log(opts); + var opt = new Option(session.cameraConstraints[i][opts], session.cameraConstraints[i][opts]); + input.options.add(opt); + if (i in session.currentCameraConstraints) { + if (session.cameraConstraints[i][opts] == session.currentCameraConstraints[i]) { + opt.selected = "true"; + included = true; + } + } + } + if (!included){ + if (i in session.currentCameraConstraints) { + var opt = new Option(session.currentCameraConstraints[i], session.currentCameraConstraints[i]); + input.options.add(opt); + opt.selected = "true"; + } + } + } else if (i.toLowerCase == "torch") { + warnlog("TORCH"); + var opt = new Option("Off", false); + input.options.add(opt); + opt = new Option("On", true); + input.options.add(opt); + try{ + if (session.currentCameraConstraints[i]){ + opt.selected = "selected"; + } + } catch(e){} + } else if (session.cameraConstraints[i].length && ("continuous" == session.cameraConstraints[i][0])){ + var opt = new Option("continuous", "continuous"); + input.options.add(opt); + if (i in session.currentCameraConstraints) { + if ("continuous" == session.currentCameraConstraints[i]) { + opt.selected = "true"; + var opt = new Option("manual", "manual"); + input.options.add(opt); + var opt = new Option("none", "none"); + input.options.add(opt); + } else { + var opt = new Option(session.currentCameraConstraints[i], session.currentCameraConstraints[i]); + input.options.add(opt); + opt.selected = "true"; + if (session.currentCameraConstraints[i]=="none"){ + var opt = new Option("manual", "manual"); + input.options.add(opt); + } else { + var opt = new Option("none", "none"); + input.options.add(opt); + } + } + } else { + opt.selected = "true"; + var opt = new Option("manual", "manual"); + input.options.add(opt); + var opt = new Option("none", "none"); + input.options.add(opt); + } + } else if (session.cameraConstraints[i].length && ("manual" == session.cameraConstraints[i][0])){ + var opt = new Option("manual", "manual"); + input.options.add(opt); + if (i in session.currentCameraConstraints) { + if ("manual" == session.currentCameraConstraints[i]) { + opt.selected = "true"; + var opt = new Option("continuous", "continuous"); + input.options.add(opt); + var opt = new Option("none", "none"); + input.options.add(opt); + } else { + var opt = new Option(session.currentCameraConstraints[i], session.currentCameraConstraints[i]); + input.options.add(opt); + opt.selected = "true"; + if (session.currentCameraConstraints[i]=="none"){ + var opt = new Option("continuous", "continuous"); + input.options.add(opt); + } else { + var opt = new Option("none", "none"); + input.options.add(opt); + } + } + } else { + opt.selected = "true"; + var opt = new Option("continuous", "continuous"); + input.options.add(opt); + var opt = new Option("none", "none"); + input.options.add(opt); + } + } else { + continue; + } + + if (getById("popupSelector_constraints_video").style.display == "none") { + getById("advancedOptionsCamera").style.display = "inline-flex"; + } + + input.id = "constraints_" + i; + input.className = "constraintCameraInput"; + input.name = "constraints_" + i; + input.style = "display:inline; padding:2px;"; + input.dataset.keyname = i; + input.dataset.chosen = input.value; + input.onchange = function(e) { + this.dataset.chosen = this.value; + //getById("label_"+e.target.dataset.keyname).innerHTML =e.target.dataset.keyname+": "+e.target.value; + if (CtrlPressed){ + updateCameraConstraints(e.target.dataset.keyname, e.target.value, true, false, false); + } else { + updateCameraConstraints(e.target.dataset.keyname, e.target.value, false, false, false); + } + pokeIframeAPI("camera-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + getById("popupSelector_constraints_video").appendChild(div); + div.appendChild(label); + div.appendChild(input); + } else if (typeof session.cameraConstraints[i] === 'boolean') { + + var div = document.createElement("div"); + var label = document.createElement("label"); + label.id = "label_" + i; + label.htmlFor = "constraints_" + i; + label.innerText = capitalizeFirstLetter(i).replace(/([a-z])([A-Z])/g, '$1 $2') + ":"; + label.style = "display:inline-block;"; + label.dataset.keyname = i; + var input = document.createElement("select"); + + var opt = new Option("Off", "false"); + input.options.add(opt); + + opt = new Option("On", "true"); + input.options.add(opt); + if (session.currentCameraConstraints[i]){ + opt.selected = "true"; + } + + if (getById("popupSelector_constraints_video").style.display == "none") { + getById("advancedOptionsCamera").style.display = "inline-flex"; + } + + input.id = "constraints_" + i; + input.className = "constraintCameraInput"; + input.name = "constraints_" + i; + input.style = "display:inline; padding:2px;"; + input.dataset.keyname = i; + input.dataset.chosen = input.value; + input.onchange = function(e) { + this.dataset.chosen = this.value; + //getById("label_"+e.target.dataset.keyname).innerHTML =e.target.dataset.keyname+": "+e.target.value; + if (CtrlPressed){ + updateCameraConstraints(e.target.dataset.keyname, e.target.value, true, false, false); + } else { + updateCameraConstraints(e.target.dataset.keyname, e.target.value, false, false, false); + } + pokeIframeAPI("camera-constraint-changed", {name:e.target.dataset.keyname, value:e.target.value}); + }; + getById("popupSelector_constraints_video").appendChild(div); + div.appendChild(label); + div.appendChild(input); + } + } catch (e) { + errorlog(e); + } + } + + if (session.currentCameraConstraints.deviceId){ + if (getStorage("camera_"+session.currentCameraConstraints.deviceId)){ + var button = document.createElement("button"); + button.innerHTML = "Reset video settings to default"; + button.style.display = "block"; + button.style.padding = "10px 5px"; + button.dataset.deviceId = session.currentCameraConstraints.deviceId; + button.onclick = function(){ + var deviceId = this.dataset.deviceId; + var cameraSettings = getStorage("camera_"+deviceId); + var constraints = {}; + var resetResolution = false; + var failed = false; + if (cameraSettings['default']){ + if (cameraSettings['current']){ + for (var i in cameraSettings['default']){ + if (i == "groupId"){ + continue; + } else if (i === "aspectRatio") { // do not load from storage; causes issues + continue; + } else if (i === "width") { + // continue; + } else if (i === "height") { + // continue; + } else { // if I include any of these, it will complain about mixing types and fail + if (i in cameraSettings['current']){ + if (cameraSettings['current'][i] != cameraSettings['default'][i]){ + track0.applyConstraints({ + advanced: [{[i]:cameraSettings['default'][i]}] + }).then(() => {}).catch(e => { + errorlog("Failed to reset to defaults"); + failed = true; + }); + } + } + continue; + } + + if (i in cameraSettings['current']){ + if (cameraSettings['current'][i] != cameraSettings['default'][i]){ + if (i in session.cameraConstraints){ + if ("min" in session.cameraConstraints[i]){ + if (session.cameraConstraints[i].min>cameraSettings['default'][i]){ + continue; + } + } + if ("max" in session.cameraConstraints[i]){ + if (session.cameraConstraints[i].max { + if (!failed){ + removeStorage("camera_"+deviceId); + } + listCameraSettings(); + + if (resetResolution){ + session.setResolution(); // this will reset scaling for all viewers of this stream + } + }).catch(e => { + errorlog("Failed to reset to defaults"); + errorlog(e); + }); + } else if (!failed){ + removeStorage("camera_"+deviceId); + listCameraSettings(); + } + }; + + getById("popupSelector_constraints_video").appendChild(button); + } + } +} + +// applySavedAudioSettings is currently disabled since aec/denoise/etc do not work except with constraints. +function applySavedAudioSettings(track0){ // just applies any saved settings. This then assumes there are already default settings saved, as saved won't be there without the default also. + if (track0.getSettings) { + log("applySavedAudioSettings"); + session.currentAudioConstraints = track0.getSettings(); + if ("deviceId" in session.currentAudioConstraints){ + var deviceId = session.currentAudioConstraints.deviceId; + if (getStorage("audio_"+deviceId)){ + var audioSettings = getStorage("audio_"+deviceId); + var constraints = {}; + if (audioSettings['deviceId']){ + for (var i in session.currentAudioConstraints){ + if (i in audioSettings){ + if (audioSettings[i] != session.currentAudioConstraints[i]){ + if (i == "autoGainControl"){ + } else if (i == "echoCancellation"){ + } else if (i == "noiseSuppression"){ + } else { + continue; + } + constraints[i]=audioSettings[i]; + warnlog("DIFF: "+i); + } + } + } + } + warnlog(constraints); + if (Object.keys(constraints).length){ + track0.applyConstraints({ + advanced: [constraints] // ignore + }).then(() => { + warnlog("audio settings updated for deviceId:"+deviceId); + //removeStorage("audio_"+deviceId); + //listCameraSettings(); + }).catch(e => { + errorlog("Failed to reset to audio defaults"); + }); + } + } + } + } +} + +function applySavedVideoSettings(track0){ // just applies any saved settings. This then assumes there are already default settings saved, as saved won't be there without the default also. + if (track0.getSettings) { + session.currentCameraConstraints = track0.getSettings(); + + if (screen && screen.orientation && screen.orientation.type){ + if (!screen.orientation.type.includes("portrait")){ + if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ + session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; + } + } + } else if (!window.matchMedia("(orientation: portrait)").matches){ + if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ + session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; + } + } + + if ("deviceId" in session.currentCameraConstraints){ + var deviceId = session.currentCameraConstraints.deviceId; + if (getStorage("camera_"+deviceId)){ + var cameraSettings = getStorage("camera_"+deviceId); + var constraints = {}; + if (cameraSettings['current']){ + for (var i in session.currentCameraConstraints){ + if (i in cameraSettings['current']){ + if (cameraSettings['current'][i] != session.currentCameraConstraints[i]){ + if (i == "groupId"){ + continue; + } else if (session.forceAspectRatio && (i === "aspectRatio")){ + log("Skipping saved AspectRatio setting"); + continue; + } + constraints[i]=cameraSettings['current'][i]; + warnlog("DIFF: "+i); + } + } + } + } + + warnlog(constraints); + if (Object.keys(constraints).length){ + track0.applyConstraints({ + advanced: [constraints] // ignore + }).then(() => { + warnlog("video settings updated for deviceId:"+deviceId); + //removeStorage("camera_"+deviceId); + //listCameraSettings(); + }).catch(e => { + errorlog("Failed to reset to defaults"); + }); + } + } + } + } + +} + + +var updateCameraConstraintsBusy = false; +var updateCameraConstraintsNext = false; + +async function updateCameraConstraints(constraint, value = null, ctrl=false, UUID=false, save=true) { + + if ((constraint === "zoom") && (value === 0)){ + log("can't zoom to zero"); + return; + } + + log("updateCameraConstraintsBusy..?"); + + if (updateCameraConstraintsBusy){ + updateCameraConstraintsNext = [constraint, value, ctrl, UUID, save]; + return; + } else { + updateCameraConstraintsBusy = true; + updateCameraConstraintsNext = false; + } + + try { + var track0 = session.streamSrc.getVideoTracks(); + track0 = track0[0]; // shoud only be one video track anyways. + + if (!track0 || (track0.readyState && track0.readyState != 'live') || (!track0.enabled)) { + if (!save){ + errorlog("TRACK IS NOT ENABLED"); + updateCameraConstraintsBusy = false; + updateCameraConstraintsNext = false; + } + } + + + if (value == parseFloat(value)) { + value = parseFloat(value); + } else if (value == "true") { + value = true; + } else if (value == "false") { + value = false; + } + log({ + advanced: [{ + [constraint]: value + }] + }); + + } catch(e){ + errorlog(e); + updateCameraConstraintsBusy = false; + updateCameraConstraintsNext = false; + return e; + } + + log("updateCameraConstraintsNext:"); + log(updateCameraConstraintsNext); + + try { + if (track0.getSettings){ + var cameraSettings = {}; + session.currentCameraConstraints = track0.getSettings(); + + if (screen && screen.orientation && screen.orientation.type){ + if (!screen.orientation.type.includes("portrait")){ + if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ + session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; + } + } + } else if (!window.matchMedia("(orientation: portrait)").matches){ + if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ + session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; + } + } + if (session.currentCameraConstraints.deviceId){ + if (!getStorage("camera_"+session.currentCameraConstraints.deviceId)){ + cameraSettings['default'] = JSON.parse(JSON.stringify(session.currentCameraConstraints)); + log(cameraSettings['default']); + } else { + cameraSettings = getStorage("camera_"+session.currentCameraConstraints.deviceId); + } + } + } + } catch(e){errorlog(e);} + + + if (constraint=="width"){ + var constraits = {"width": value}; + + if (session.currentCameraConstraints && session.currentCameraConstraints.frameRate){ + constraits.frameRate = session.currentCameraConstraints.frameRate; + } + + if (screen && screen.orientation && screen.orientation.type){ + if (!screen.orientation.type.includes("portrait") && session.currentCameraConstraints){ + if (!ctrl && session.currentCameraConstraints.height){ + constraits.height = session.currentCameraConstraints.height; + } + } + } else if (!window.matchMedia("(orientation: portrait)").matches && session.currentCameraConstraints){ + if (!ctrl && session.currentCameraConstraints.height){ + constraits.height = session.currentCameraConstraints.height; + } + } + + } else if (constraint=="height"){ + var constraits = {"height": value}; + + if (session.currentCameraConstraints && session.currentCameraConstraints.frameRate){ + constraits.frameRate = session.currentCameraConstraints.frameRate; + } + + if (screen && screen.orientation && screen.orientation.type){ + if (!screen.orientation.type.includes("portrait") && session.currentCameraConstraints){ + if (!ctrl && session.currentCameraConstraints.width){ + constraits.width = session.currentCameraConstraints.width; + } + } + } else if (!window.matchMedia("(orientation: portrait)").matches && session.currentCameraConstraints){ + + if (!ctrl && session.currentCameraConstraints.width){ + constraits.width = session.currentCameraConstraints.width; + } + } + } else if (!ctrl && (constraint=="frameRate")){ + var constraits = {"frameRate": value}; + + if (screen && screen.orientation && screen.orientation.type){ + if (!screen.orientation.type.includes("portrait") && session.currentCameraConstraints){ + if (session.currentCameraConstraints.height && session.currentCameraConstraints.width){ + constraits.height = session.currentCameraConstraints.height; + constraits.width = session.currentCameraConstraints.width; + } + } + } else if (!window.matchMedia("(orientation: portrait)").matches && session.currentCameraConstraints){ + if (session.currentCameraConstraints.height && session.currentCameraConstraints.width){ + constraits.height = session.currentCameraConstraints.height; + constraits.width = session.currentCameraConstraints.width; + } + } + } else if ((constraint=="exposureMode") && (value=="manual")){ + + var constraits = {}; // try to force the current focus, to get the actual current value. + if (session.currentCameraConstraints && session.currentCameraConstraints.exposureTime){ // just requested a second a go + constraits.exposureTime = session.currentCameraConstraints.exposureTime; // needs the focus set for the manual to activate. + } + await track0.applyConstraints({ // apply what we have on record, to try to force it. + advanced: [constraits] + }) + session.currentCameraConstraints = track0.getSettings(); // now get the actual focus distance; solves a bug + + if (screen && screen.orientation && screen.orientation.type){ + if (!screen.orientation.type.includes("portrait")){ + if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ + session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; + } + } + } else if (!window.matchMedia("(orientation: portrait)").matches){ + if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ + session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; + } + } + + var constraits = {[constraint]: value}; // now we can set things to manual; if we don't set the focusDistance, it won't work otherwise. + if (session.currentCameraConstraints && session.currentCameraConstraints.exposureTime){ + constraits.exposureTime = session.currentCameraConstraints.exposureTime; // needs the focus set for the manual to activate. + } + } else if (constraint=="exposureTime"){ + var constraits = {[constraint]: value}; + constraits.exposureMode = "manual"; + + } else if ((constraint=="focusMode") && (value=="manual")){ + + var constraits = {}; // try to force the current focus, to get the actual current value. + if (session.currentCameraConstraints && session.currentCameraConstraints.focusDistance){ // just requested a second a go + constraits.focusDistance = session.currentCameraConstraints.focusDistance; // needs the focus set for the manual to activate. + } + await track0.applyConstraints({ // apply what we have on record, to try to force it. + advanced: [constraits] + }) + session.currentCameraConstraints = track0.getSettings(); // now get the actual focus distance; solves a bug + + if (screen && screen.orientation && screen.orientation.type){ + if (!screen.orientation.type.includes("portrait")){ + if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ + session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; + } + } + } else if (!window.matchMedia("(orientation: portrait)").matches){ // legacy + if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ + session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; + } + } + + var constraits = {[constraint]: value}; // now we can set things to manual; if we don't set the focusDistance, it won't work otherwise. + if (session.currentCameraConstraints && session.currentCameraConstraints.focusDistance){ + constraits.focusDistance = session.currentCameraConstraints.focusDistance; // needs the focus set for the manual to activate. + } + } else if (constraint=="focusDistance"){ + var constraits = {[constraint]: value}; + constraits.focusMode = "manual"; + + } else if ((constraint=="whiteBalanceMode") && (value=="manual")){ + + var constraits = {}; // try to force the current colorTemperature, to get the actual current value. + if (session.currentCameraConstraints && session.currentCameraConstraints.colorTemperature){ // just requested a second a go + constraits.colorTemperature = session.currentCameraConstraints.colorTemperature; // needs the colorTemperature set for the manual to activate. + } + await track0.applyConstraints({ // apply what we have on record, to try to force it. + advanced: [constraits] + }) + session.currentCameraConstraints = track0.getSettings(); // now get the actual colorTemperature; solves a bug + + if (screen && screen.orientation && screen.orientation.type){ + if (!screen.orientation.type.includes("portrait")){ + if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ + session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; + } + } + } else if (!window.matchMedia("(orientation: portrait)").matches){ // legacy + if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ + session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; + } + } + + var constraits = {[constraint]: value}; + if (session.cameraConstraints.colorTemperature && ("max" in session.cameraConstraints.colorTemperature) && ("min" in session.cameraConstraints.colorTemperature)){ + if (session.currentCameraConstraints && session.currentCameraConstraints.colorTemperature){ + constraits.colorTemperature = session.currentCameraConstraints.colorTemperature; + } else if ((5000>=session.cameraConstraints.colorTemperature.min) && (5000<=session.cameraConstraints.colorTemperature.max)){ + constraits.colorTemperature = 5000; // whiteBalanceMode won't work unless a colorTemperature is set. 5000 is a good default. + } else { + constraits.colorTemperature = session.cameraConstraints.colorTemperature.max; + } + } + } else if ((constraint=="whiteBalanceMode") && (value=="continuous")){ + var constraits = {[constraint]: value}; + + if (session.mobile && ChromiumVersion){ // trying to fix the issue that chrome mobile has. + constraits.colorTemperature = 5000; + } + + } else if (constraint=="colorTemperature"){ + var constraits = {[constraint]: value}; + constraits.whiteBalanceMode = "manual"; + + } else if (constraint=="aspectRatio"){ + var constraits = {[constraint]: value}; + if (session.currentCameraConstraints && session.currentCameraConstraints.frameRate){ + constraits.frameRate = session.currentCameraConstraints.frameRate; + } + } else { + var constraits = {[constraint]: value}; + } + + if (screen && screen.orientation && screen.orientation.type){ + if (screen.orientation.type.includes("portrait")){ + if (constraits.aspectRatio){ + constraits.aspectRatio = 1/constraits.aspectRatio; + } + } + } else if (window.matchMedia("(orientation: portrait)").matches){ // legacy + if (constraits.aspectRatio){ + constraits.aspectRatio = 1/constraits.aspectRatio; + } + } + + log("20788"); + log(constraits); + + await track0.applyConstraints({ + advanced: [constraits] + }).then(() => { + log("applied constraint"); + if (save){ + if (track0.getSettings){ // -- updateCameraConstraints + if (session.currentCameraConstraints.deviceId){ + session.currentCameraConstraints = track0.getSettings(); + + + if (screen && screen.orientation && screen.orientation.type){ + if (!screen.orientation.type.includes("portrait")){ + if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ + session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; + } + } + } else if (!window.matchMedia("(orientation: portrait)").matches){ // legacy + if (session.currentCameraConstraints && session.currentCameraConstraints.aspectRatio){ + session.currentCameraConstraints.aspectRatio = 1/session.currentCameraConstraints.aspectRatio; + } + } + + cameraSettings['current'] = session.currentCameraConstraints; // this won't let failed settings be stored. + //cameraSettings['current'][constraint] = value; // setting value is a problem, as it will allow for failed settings to be stored. + setStorage("camera_"+session.currentCameraConstraints.deviceId, cameraSettings); + if (toggleSettingsState == true) { + listCameraSettings(); + } + } + } + + if (UUID){ + var data = {}; + data.UUID = UUID; + data.videoOptions = listVideoSettingsPrep(); + sendMediaDevices(data.UUID); + session.sendMessage(data, data.UUID); + } + if ((constraint == "width") || (constraint == "height") || (constraint == "aspectRatio")){ + session.setResolution(); // this will reset scaling for all viewers of this stream + } + } + + if (updateCameraConstraintsNext){ + setTimeout(function(){ + updateCameraConstraintsBusy = false; + updateCameraConstraints(updateCameraConstraintsNext[0], updateCameraConstraintsNext[1], updateCameraConstraintsNext[2], updateCameraConstraintsNext[3], updateCameraConstraintsNext[4]); + },30) + } else { + updateCameraConstraintsBusy = false; + } + }).catch(e => { + errorlog(e.message); + errorlog("coulnd't save defaults"); // this doesn't get triggered when a setting fails for some reason. + + window.focus(); + updateCameraConstraintsBusy = false; + updateCameraConstraintsNext = false; + return; + }); + return; +} + +function toggleAudioUser(ele){ + ele.classList.toggle('highlight'); + toggle(getById('popupSelector_constraints_audio'),false,false); + getById('popupSelector_constraints_loading').style.visibility='visible'; + getById('popupSelector_constraints_video').style.display = "none"; + getById('popupSelector_user_settings').style.display = "none"; +} +function toggleVideoUser(ele){ + ele.classList.toggle('highlight'); + toggle(getById('popupSelector_constraints_video'),false,false); + getById('popupSelector_constraints_loading').style.visibility='visible'; + getById('popupSelector_user_settings').style.display = "none"; + getById('popupSelector_constraints_audio').style.display = "none"; +} +function toggleUserUser(ele){ + ele.classList.toggle('highlight'); + toggle(getById('popupSelector_user_settings'),false,false); + getById('popupSelector_user_settings').style.visibility='visible'; + getById('popupSelector_constraints_video').style.display = "none"; + getById('popupSelector_constraints_audio').style.display = "none"; +} + +function setupWebcamSelection(miconly=false) { + log("setupWebcamSelection();"); + + checkBasicStreamsExist(); + + try { + return enumerateDevices().then(function(dInfo){return gotDevices(dInfo, miconly)}).then(function() { + + if (getById("webcamquality").elements && parseInt(getById("webcamquality").elements.namedItem("resolution").value) == 3) { // this is junk?? + if (session.maxframeRate===false){ + session.maxframeRate = 30; + session.maxframeRate_q2 = true; + } + } else if (session.maxframeRate_q2){ + session.maxframeRate = false; + session.maxframeRate_q2 = false; + } + + var audioSelect = getById('audioSource'); + var videoSelect = getById('videoSourceSelect'); + var outputSelect = getById('outputSource'); + + audioSelect.onchange = function() { + + if (document.getElementById("gowebcam")) { + document.getElementById("gowebcam").disabled = true; + document.getElementById("gowebcam").dataset.audioready = "false"; + //document.getElementById("gowebcam").style.backgroundColor = "#DDDDDD"; + document.getElementById("gowebcam").style.fontWeight = "normal"; + document.getElementById("gowebcam").innerHTML = "Waiting for mic to load"; + miniTranslate(document.getElementById("gowebcam"), "waiting-for-mic-to-load"); + } + activatedPreview = false; + grabAudio(); + }; + videoSelect.onchange = function() { + + if (document.getElementById("gowebcam")) { + document.getElementById("gowebcam").disabled = true; + document.getElementById("gowebcam").dataset.ready = "false"; + //document.getElementById("gowebcam").style.backgroundColor = "#DDDDDD"; + document.getElementById("gowebcam").style.fontWeight = "normal"; + document.getElementById("gowebcam").innerHTML = "Waiting for Camera to load"; + miniTranslate(document.getElementById("gowebcam"), "waiting-for-camera-to-load"); + } + warnlog("video source changed"); + + activatedPreview = false; + if (session.quality !== false) { + grabVideo(session.quality); + } else { + session.quality_wb = parseInt(getById("webcamquality").elements.namedItem("resolution").value); + grabVideo(session.quality_wb); + } + }; + + if (Firefox && !session.mobile){ + outputSelect.onclick = function() { + log("on click"); + if (outputSelect.options[outputSelect.selectedIndex].value === "others"){ + + navigator.mediaDevices.selectAudioOutput().then((device) => { + + if (device.kind == "audiooutput"){ + + session.sink = device.deviceId; + + try { + var matched = false; + outputSelect.childNodes.forEach(ele =>{ + if (ele.value === device.deviceId){ + matched = true; + ele.selected = true; + } + }) + if (!matched){ + var option = document.createElement('option'); + option.value = device.deviceId; + option.text = device.label; + outputSelect.appendChild(option); + option.selected = true; + } + + saveSettings(); // we're saving because there was an explicit action to change devices + } catch(e){errorlog(e);} + + if (!session.sink){return;} // Not sure this would ever happen, but whatever. + + resetupAudioOut(); // we'll probalby use session.sink, since outputSelect3 doesn't exist. + } + + }); + } + } + } + + + outputSelect.onchange = function() { + if (iOS || iPad) { + return; + } + if (Firefox && !session.mobile){ + if (outputSelect.options[outputSelect.selectedIndex].value === "others"){ + return; + } + } + + try{ + session.sink = outputSelect.options[outputSelect.selectedIndex].value; + saveSettings(); // we're saving because there was an explicit action to change devices + } catch(e){errorlog(e);} + + if (!session.sink){return;} // Not sure this would ever happen, but whatever. + + resetupAudioOut(); // we'll probalby use session.sink, since outputSelect3 doesn't exist. + } + + getById("webcamquality").onchange = function() { + + if (document.getElementById("gowebcam")) { + document.getElementById("gowebcam").disabled = true; + document.getElementById("gowebcam").dataset.ready = "false"; + // document.getElementById("gowebcam").style.backgroundColor = "#DDDDDD"; + document.getElementById("gowebcam").style.fontWeight = "normal"; + document.getElementById("gowebcam").innerHTML = "Waiting for Camera to load"; + miniTranslate(document.getElementById("gowebcam"), "waiting-for-camera-to-load"); + } + + if (parseInt(getById("webcamquality").elements.namedItem("resolution").value) == 2) { + if (session.maxframeRate===false){ + session.maxframeRate = 30; + session.maxframeRate_q2 = true; + } + } else if (session.maxframeRate_q2){ + session.maxframeRate = false; + session.maxframeRate_q2 = false; + } + + activatedPreview = false; + session.quality_wb = parseInt(getById("webcamquality").elements.namedItem("resolution").value); + grabVideo(session.quality_wb); + }; + + if (session.safemode){ + if (document.getElementById("gowebcam")){ + document.getElementById("gowebcam").disabled = false; + //document.getElementById("gowebcam").innerHTML = getTranslation("start"); + miniTranslate(document.getElementById("gowebcam"),"start"); + document.getElementById("gowebcam").dataset.audioready = "true"; + document.getElementById("gowebcam").dataset.ready = "true"; + document.getElementById("gowebcam").focus(); + setTimeout(function(){updateForceRotate();},1000); + + if (session.autostart) { + publishWebcam(); // no need to mirror as there is no video... + } + return; + } + } + + if (session.audioDevice!==0){ // change from Auto to Selected Audio Device + log("SETTING AUDIO DEVICE!!"); + activatedPreview = false; + grabAudio(); + } else if (document.getElementById("gowebcam")){ + document.getElementById("gowebcam").dataset.audioready = "true"; + } + + if ((session.videoDevice === 0) || miconly) { + if (session.autostart) { + publishWebcam(); // no need to mirror as there is no video... + return; + } else { + if (document.getElementById("gowebcam")) { + document.getElementById("gowebcam").dataset.ready = "true"; + if (document.getElementById("gowebcam").dataset.audioready == "true"){ + document.getElementById("gowebcam").disabled = false; + miniTranslate(document.getElementById("gowebcam"),"start"); + document.getElementById("gowebcam").focus(); + //document.getElementById("gowebcam").innerHTML = getTranslation("start"); + } + } + } + } else { + log("GRabbing video: " + session.quality); + activatedPreview = false; + if (session.quality !== false) { + grabVideo(session.quality); + } else { + session.quality_wb = parseInt(getById("webcamquality").elements.namedItem("resolution").value); + grabVideo(session.quality_wb); + } + } + + if (!(iOS || iPad || session.mobile)) { + try { + if (outputSelect.selectedIndex >= 0) { + session.sink = outputSelect.options[outputSelect.selectedIndex].value; + saveSettings(); + if (session.videoElement && !session.videoElement.paused){ + resetupAudioOut(); + } + } + } catch(e){errorlog(e);} + } + + }).catch(e => { + errorlog(e); + }); + } catch (e) { + errorlog(e); + } +} + +Promise.wait = function(ms) { + return new Promise(function(resolve) { + setTimeout(resolve, ms); + }); +}; + +Promise.prototype.timeout = function(ms) { + return Promise.race([ + this, Promise.wait(ms).then(function() { + if (iOS || iPad){ + var errormsg = new Error("Time Out\nDid you accept camera permissions in time? Please do so first.\n\nIf using an iPhone or iPad, try fully closing your browser and open it again; Safari sometimes jams up the camera."); + errormsg.name = "timedOut"; + errormsg.message = "Time Out\nDid you accept camera permissions in time? Please do so first.\n\nIf using an iPhone or iPad, try fully closing your browser and open it again; Safari sometimes jams up the camera." + throw errormsg; + } else if (session.mobile){ + var errormsg = new Error("Time Out\nDid you accept camera permissions in time? Please do so first.\n\nMake sure no other application is using the camera already and that you are using a compatible browser. If issues persist, maybe try the official native mobile app."); + errormsg.name = "timedOut"; + errormsg.message = "Time Out\nDid you accept camera permissions in time? Please do so first.\n\nMake sure no other application is using the camera already and that you are using a compatible browser. If issues persist, maybe try the official native mobile app." + throw errormsg; + } else { + var errormsg = new Error("Time Out\nDid you accept camera permissions in time? Please do so first.\n\nOtherwise, do you have NDI Tools installed? Maybe try uninstalling it.\n\nPlease also ensure your camera and audio device are correctly connected and not already in use. You may also need to refresh the page."); + errormsg.name = "timedOut"; + errormsg.message = "Time Out\nDid you accept camera permissions in time? Please do so first.\n\nOtherwise, do you have NDI Tools installed? Maybe try uninstalling it.\n\nPlease also ensure your camera and audio device are correctly connected and not already in use. You may also need to refresh the page." + throw errormsg; + } + }) + ]) +}; + + +async function shareWebsite(autostart=false, evt=false){ + + if (session.iframeSrc){ + + if (!session.cleanOutput){ + getById("websitesharebutton2").classList.remove('hidden'); + } + + if (evt && (evt.ctrlKey || evt.metaKey)){ + if (getById("websitesharebutton2").classList.contains("green")){ + var actionMsg = {}; + actionMsg.infocus = false; + + for (var UUID in session.pcs){ + if (session.pcs[UUID].allowIframe===true){ + session.sendMessage(actionMsg, UUID); + } + } + + getById("websitesharebutton2").classList.remove("green"); + getById("websitesharebutton2").ariaPressed = "false"; + getById("websitesharebutton2").title = "Hold CTRL (or CMD) and click to spotlight this shared website" + } else { + if (session.streamID){ + var actionMsg = {}; + actionMsg.infocus = session.streamID; + + for (var UUID in session.pcs){ + if (session.pcs[UUID].allowIframe===true){ + session.sendMessage(actionMsg, UUID); + } + } + + getById("websitesharebutton2").classList.add("green"); + getById("websitesharebutton2").ariaPressed = "true"; + getById("websitesharebutton2").title = "Video is currently spotlighted"; + } + } + return; + } + getById("websitesharebutton2").classList.remove("green"); + getById("websitesharebutton2").ariaPressed = "false"; + session.iframeSrc = false; + + + if (session.director){ + clearDirectorSettings(); + //setStorage("directorWebsiteShare", {"website":session.iframeSrc, "roomid":session.roomid}); + } else if (document.getElementById("container_iframe") || session.iframeEle){ + if (session.iframeEle){ + session.iframeEle.remove(); + session.iframeEle = false; + } + if (document.getElementById("container_iframe")){ + document.getElementById("container_iframe").remove(); + } + + updateMixer(); + } + getById("websitesharebutton2").classList.add("hidden"); + getById("websitesharebutton").classList.remove("hidden"); + + + + var data = {}; + data.iframeSrc = false; + for (var UUID in session.pcs){ + if (session.pcs[UUID].allowIframe===true){ + session.sendMessage(data, UUID); + } + } + getById("websitesharebutton2").title = "Hold CTRL (or CMD) and click to spotlight this shared website" + return + } + getById("websitesharebutton2").classList.remove("green"); + getById("websitesharebutton2").ariaPressed = "false"; + + if (autostart===false){ + window.focus(); + var iframeURL = await promptAlt(getTranslation("enter-website"), false, false, session.defaultIframeSrc); + } else { + var iframeURL = autostart; + } + if (!iframeURL){ + return; + } + if (iframeURL == session.iframeSrc){return;} + session.defaultIframeSrc = iframeURL; + + warnlog(iframeURL); + + session.iframeSrc = parseURL4Iframe(iframeURL); + + if (session.director && !autostart){ + setStorage("directorWebsiteShare", {"website":session.iframeSrc, "roomid":session.roomid}); + } else if (session.iframeEle){ + session.iframeEle.src = session.iframeSrc; + if (session.iframeSrc.startsWith("https://www.youtube.com/")){ // special handler. + setTimeout(function(iframe_id){ YoutubeListen(iframe_id);}, 1000, iframe.id); + } + } else { + var iframe = document.createElement("iframe"); + iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;display-capture;midi;"; + iframe.src = session.iframeSrc; + iframe.id = "iframe_source"; + iframe.loadedYoutubeListen = false; + session.iframeEle = iframe; + + var container = document.createElement("div"); + iframe.container = container; + container.id = "container_iframe"; + + if (session.iframeSrc.startsWith("https://www.youtube.com/")){ // special handler. + setTimeout(function(iframe_id){ YoutubeListen(iframe_id);}, 1000, iframe.id); + } + updateMixer(); + } + + getById("websitesharebutton2").classList.remove("hidden"); + getById("websitesharebutton").classList.add("hidden"); + + var data = {}; + data.iframeSrc = session.iframeSrc; + for (var UUID in session.pcs){ + if (session.pcs[UUID].allowIframe===true){ + session.sendMessage(data, UUID); + } + } +} + + +function screenshareTypeDecider(sstype=1){ + if (session.screenshareType){ + sstype = session.screenshareType; + } + if (sstype==1){ + toggleScreenShare(); + } else if (sstype==2){ + createIframePopup(); + } else if (sstype==3){ + createSecondStream(); + } +} + +function createScreenShareURL(transparent=true){ // iframe.src = + if (session.screenShareElement) { + var iFrameID = session.streamID.substring(0, 12) + "_" + session.generateStreamID(5); + } else if (session.screenshareid) { + var iFrameID = session.screenshareid; + } else { + var iFrameID = session.streamID.substring(0, 12) + "_" + session.generateStreamID(5); + } + + if (session.exclude) { + session.exclude.push(iFrameID); + } else { + session.exclude = []; + session.exclude.push(iFrameID); + } + + var extras = ""; + if (session.password){ + extras += "&password=" + session.password; // encodeURIComponent( + } + + if (session.privacy){ + extras += "&privacy"; + } + + if (session.meshcast){ + extras += "&meshcast"; + } + + if (session.token){ + extras+="&token="+session.token; + } + + if (session.remote){ + if (session.remote===true){ + extras += "&remote"; + } else { + extras += "&remote="+session.remote; + } + } + if (session.salt !== location.hostname){ // this is default. + extras += "&salt="+session.salt; + } + if (session.whipOutVideoBitrate){ + extras += "&wovb="+session.whipOutVideoBitrate; + } + if (session.whipOutScreenShareBitrate){ + extras += "&wossbitrate="+session.whipOutScreenShareBitrate; + } + + if (!session.notifyScreenShare){ + extras += "&smallshare"; + } + + if (session.screenshareContentHint){ + extras += "&sshint="+session.screenshareContentHint; + } else if (session.contentHint){ + extras += "&sshint="+session.contentHint; + } + + if (session.audioContentHint){ + extras += "&audiohint="+session.audioContentHint; + } + + if (session.whipOutScreenShareCodec){ + extras += "&whipoutcodec="+session.whipOutScreenShareCodec; + } else if (session.whipOutCodec){ + extras += "&whipoutcodec="+session.whipOutCodec; + } + + if (session.screensharequality!==false){ + extras += "&q="+session.screensharequality; // &quality works here, since only thing we are doing + } else if (session.quality!==false){ + extras += "&q="+session.quality; + } else if (session.quality_ss!==false){ + extras += "&q="+session.quality_ss; + } else { + extras += "&q=0"; + } + + if (session.screenShareLabel!==false){ + if (session.screenShareLabel){ + extras += "&label="+encodeURIComponent(session.screenShareLabel); + } else if (session.label){ + extras += "&label="+encodeURIComponent(session.label); + } + } + + if (session.screensharefps!==false){ + extras += "&maxframeRate="+parseInt(session.screensharefps*100)/100.0; + } + if (session.screenshareAEC!==false){ + extras += "&aec=1"; + } + if (session.screenshareDenoise!==false){ + extras += "&denoise=1"; + } + if (session.screenshareAutogain!==false){ + extras += "&autogain=1"; + } + if (session.screenshareStereo!==false){ + extras += "&stereo="+session.screenshareStereo; + } + if (session.forceAspectRatio && (session.forceScreenShareAspectRatio===null)){ + extras += "&ar="+session.forceAspectRatio; + } else if (session.forceScreenShareAspectRatio){ + extras += "&ar="+session.forceScreenShareAspectRatio; + } + if (session.muted){ + extras += "&muted"; + } + + if (session.recordLocal){ + extras += "&record="+session.recordLocal; + } + + if (session.autorecordlocal){ + extras += "&autorecordlocal"; + } + + if (transparent){ + extras += "&transparent&cleanish"; + } + // manual, since I don't want to use the auto-mixer. + return "?manual&audiodevice=1&screenshare&cb=0&nvb&nsb&autostart&view&room=" + session.roomid + "&push=" + iFrameID + extras; +} + +function createIframePopup() { + + if (session.screenShareElement) { + postMessageIframe(session.screenShareElement, {"close": true}); + session.screenShareElement.parentNode.removeChild(session.screenShareElement); + session.screenShareElement = false; + updateMixer(); + getById("screenshare2button").classList.remove("green"); + getById("screenshare2button").ariaPressed = "false"; + return; + } + + if ((session.queue && !session.transferred) || (session.screenShareState && !session.queue && session.transferred)){ // if (session.queue || session.transferred){ + //getById("screenshare2button").classList.add("hidden"); + //getById("screensharebutton").classList.remove("hidden"); + toggleScreenShare(); + return; + } // can't secondary-screen share if in a queue. + + var iframe = document.createElement("iframe"); + iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;display-capture;midi;"; + iframe.src = "./"+createScreenShareURL(); + + iframe.style.width = "100%"; + iframe.style.height = "100%"; + iframe.style.overflow = "hidden"; + iframe.id = "screensharesource"; + iframe.dataset.sid = "#screensharesource"; + iframe.style.zIndex = "0"; + + session.screenShareElement = iframe; + session.screenShareElement.dataset.doNotMove = true; + + + document.getElementById("main").appendChild(iframe); + + if (session.screenShareElementHidden){ + session.screenShareElement.style.display = "none"; + } + + updateMixer(); + + getById("screenshare2button").classList.add("green"); + getById("screenshare2button").ariaPressed = "true"; + + return; // ignore the rest. +} + +function previewWebcam(miconly=false) { + + if (session.taintedSession === null) { + log("STILL WAITING ON HASH TO VALIDATE"); + setTimeout(function(miconly) { + previewWebcam(miconly); + }, 1000, miconly); + return; + } else if (session.taintedSession === true) { + warnlog("HASH FAILED; PASSWORD NOT VALID"); + return; + } else { + log("NOT TAINTED"); + } + + if (activatedPreview == true) { + log("activeated preview return 1"); + return; + } + activatedPreview = true; + + + if (miconly){ // this just shares the preview section with the mic-only and video+mic modes + if (!getById("add_camera_inner").cloned){ + getById("add_camera_inner").cloned = true; + insertAfter(getById("add_camera_inner"),getById("add_microphone")); + document.getElementById('videoSourceSelect').innerHTML = ""; + } + } else if (getById("add_camera_inner").cloned){ + getById("add_camera_inner").cloned = false; + insertAfter(getById("add_camera_inner"),getById("add_camera")); + document.getElementById('videoSourceSelect').innerHTML = ""; + } + + + if (session.audioDevice === 0) { // OFF + var constraint = { + audio: false + }; + } else if ((session.echoCancellation !== false) && (session.autoGainControl !== false) && (session.noiseSuppression !== false)) { // AUTO + var constraint = { + audio: true + }; + } else { // Disable Echo Cancellation and stuff for the PREVIEW (DEFAULT CAM/MIC) + var constraint = { + audio: {} + }; + if (session.echoCancellation !== false) { // if not disabled, we assume it's on + constraint.audio.echoCancellation = true; + } else { + constraint.audio.echoCancellation = false; + if (!session.cleanoutput){ + getById("headphoneTip1").classList.remove("hidden"); + miniTranslate(getById("headphoneTipContext1"),"headphones-tip"); + //getById("headphoneTipContext1").innerHTML = getTranslation("headphones-tip"); + } + } + if (session.autoGainControl !== false) { + constraint.audio.autoGainControl = true; + } else { + constraint.audio.autoGainControl = false; + } + if (session.noiseSuppression !== false) { + constraint.audio.noiseSuppression = true; + } else { + constraint.audio.noiseSuppression = false; + } + } + + if ((session.videoDevice === 0) || miconly) { + constraint.video = false; + } else { + constraint.video = true; + } + + window.onorientationchange = function(){ + if (Firefox){ + updateForceRotate(true); + } + setTimeout(async function(){ + if (session.forceAspectRatio){ + await updateCameraConstraints("aspectRatio", session.forceAspectRatio); + } + if (session.effect && (session.effect === "7")){digitalZoom(true);} + updateForceRotate(); + }, 200); + }; + + if ((constraint.video === false) && (constraint.audio === false)){ + if (session.autostart) { + publishWebcam(false, miconly); // no need to mirror as there is no video... + return; + } else { + getById("getPermissions").style.display = "none"; + if (document.getElementById("gowebcam")) { + document.getElementById("gowebcam").dataset.ready = "true"; + document.getElementById("gowebcam").dataset.audioready = "true"; + document.getElementById("gowebcam").disabled = false; + //document.getElementById("gowebcam").innerHTML = getTranslation("start"); + miniTranslate(document.getElementById("gowebcam"),"start"); + document.getElementById("gowebcam").focus(); + } + } + return; + } + + enumerateDevices().then(function(devices) { + log("enumeratated"); + log(devices); + var vtrue = false; + var atrue = false; + devices.forEach(function(device) { + if (device.kind === 'audioinput') { + atrue = true; + } else if (device.kind === 'videoinput') { + vtrue = true; + } + }); + if (atrue === false) { + constraint.audio = false; + } + if (vtrue === false) { + constraint.video = false; + } + setTimeout(function(constraint, miconly) { + requestBasicPermissions(constraint, setupWebcamSelection, miconly); + }, 0, constraint, miconly); + }).catch((error) => { + log("enumeratated failed. Seeking permissions."); + setTimeout(function(constraint, miconly) { + requestBasicPermissions(constraint, setupWebcamSelection, miconly); + }, 0, constraint, miconly); + }); + +} + +async function requestBasicPermissions(constraint = {video: true, audio: true}, callback=setupWebcamSelection, miconly=false) { + if (session.taintedSession === null) { + log("STILL WAITING ON HASH TO VALIDATE"); + setTimeout(function(constraint, callback, miconly) { + requestBasicPermissions(constraint, callback, miconly); + }, 1000, constraint, callback, miconly); + return null; + } else if (session.taintedSession === true) { + warnlog("HASH FAILED; PASSWORD NOT VALID"); + return false; + } else { + log("NOT TAINTED 1"); + } + setTimeout(function() { + getById("getPermissions").style.display = "none"; + getById("gowebcam").style.display = ""; + }, 0); + log("REQUESTING BASIC PERMISSIONS"); + + try { + var timerBasicCheck = null; + if (!(session.cleanOutput)) { + log("Setting Timer for getUserMedia"); + timerBasicCheck = setTimeout(function() { + if (!(session.cleanOutput)) { + if (session.mobile){ + warnUser("Notice: Camera timed out\n\nDid you accept the camera permissions?\n\nThis error may also appear if you are in a phone call or another app is already using the camera or microphone."); + } else { + warnUser("Camera Access Request Timed Out\nDid you accept camera permissions? Please do so first.\n\nOtherwise, do you have NDI Tools installed? Maybe try uninstalling NDI tools.\n\nPlease also ensure that your camera and audio devices are correctly connected and not already in use. You may also need to refresh the page."); + } + } + }, 10000); + } + + if (session.audioInputChannels) { + if (constraint.audio === true) { + constraint.audio = {}; + constraint.audio.channelCount = session.audioInputChannels; + } else if (constraint.audio) { + constraint.audio.channelCount = session.audioInputChannels; + } + } + + + if (session.micSampleRate){ + if (constraint.audio === true) { + constraint.audio = {}; + constraint.audio.sampleRate = parseInt(session.micSampleRate); + } else if (constraint.audio) { + constraint.audio.sampleRate = parseInt(session.micSampleRate); + } + } + if (session.micSampleSize){ + if (constraint.audio === true) { + constraint.audio = {}; + constraint.audio.sampleSize = parseInt(session.micSampleSize); + } else if (constraint.audio) { + constraint.audio.sampleSize = parseInt(session.micSampleSize); + } + } + + if (session.safemode){ + if (constraint.video){ + constraint.video = true; + } + if (constraint.audio){ + constraint.audio = true; + } + } + getUserMediaRequestID +=1 ; + var gumID = getUserMediaRequestID; + log("CONSTRAINT"); + log(constraint); + var timeoutStart = 0; + if (Firefox){ + timeoutStart = 500; + } + setTimeout(function(gumID,constraint, timerBasicCheck, callback, miconly){ + log(gumID); + navigator.mediaDevices.getUserMedia(constraint).then(function(stream) { // Apple needs thi to happen before I can access EnumerateDevices. + + log("got first stream"); + clearTimeout(timerBasicCheck); + if (getUserMediaRequestID !== gumID) { + warnlog("GET USER MEDIA CALL HAS EXPIRED 3"); + stream.getTracks().forEach(function(track) { + stream.removeTrack(track); + track.stop(); + log("stopping old track"); + }); + return; + } + closeModal(); + + log(stream.getTracks()); + + session.streamSrc=stream; + checkBasicStreamsExist(); + updateRenderOutpipe(); + + if (callback){ + callback(miconly); + } + }).catch(function(err) { + clearTimeout(timerBasicCheck); + warnlog("some error with GetUSERMEDIA"); + console.warn(err); /* handle the error */ + if (err.name == "NotFoundError" || err.name == "DevicesNotFoundError") { + //required track is missing + } else if (err.name == "NotReadableError" || err.name == "TrackStartError") { + //webcam or mic are already in use + } else if (err.name == "OverconstrainedError" || err.name == "ConstraintNotSatisfiedError") { + //constraints can not be satisfied by avb. devices + } else if (err.name == "NotAllowedError" || err.name == "PermissionDeniedError") { + //permission denied in browser + if (!(session.cleanOutput)) { + setTimeout(function() { + if (window.obsstudio){ + warnUser("Permissions denied.\n\nTo access the camera or microphone from within OBS, please refer to:\ndocs.vdo.ninja/guides/share-webcam-from-inside-obs.", false, false); + } else if (ChromiumVersion && !session.mobile){ + warnUser("

    Camera/mic permissions denied

    \nPlease ensure you have allowed the mic/camera permissions in your browser, such as like:\n\n\n\nFor further help on how to resolve this issue, please refer to:\n\nhttps://docs.vdo.ninja/common-errors-and-known-issues/enable-camera-microphone-permissions.", false, false); + } else { + warnUser("Permission access to the camera or microphone was denied.\n\nPlease ensure you have allowed the mic/camera permissions in your browser.\n\nFor guides on how to resolve this issue, please refer to:\n\nhttps://docs.vdo.ninja/common-errors-and-known-issues/enable-camera-microphone-permissions.", false, false); + } + }, 1); + } + return; + } else if (err.name == "TypeError" || err.name == "TypeError") { + //empty constraints object + } else { + //permission denied in browser + if (!(session.cleanOutput)) { + setTimeout(function(err) { + warnUser(err); + }, 1,err); + } + } + warnlog("trying to list webcam again"); + + if (callback){ + callback(miconly); + } + + }); + }, timeoutStart, gumID, constraint, timerBasicCheck, callback, miconly); + } catch (e) { + console.warn(e); + if (!(session.cleanOutput)) { + if (window.isSecureContext) { + warnUser("An error has occured when trying to access the webcam or microphone. The reason is not known."); + } else if (iOS || iPad) { + warnUser("iOS version 13.4 and up is generally recommended; older than iOS 11 is not supported."); + } else { + warnUser("Error acessing camera or microphone.\n\nThe website may be loaded in an insecure context.\n\nPlease see: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia"); + } + } + } + return null +} + + +function copyFunction(copyText, evt = false) { + if (evt){ + if ("buttons" in evt) { + if (evt.buttons !== 0){return;} + } else if ("which" in evt){ + if (evt.which !== 0){return;} + } + popupMessage(evt); + evt.preventDefault(); + evt.stopPropagation(); + } + + try { + copyText.select(); + copyText.setSelectionRange(0, 99999); + document.execCommand("copy"); + } catch (e) { + var dummy = document.createElement('input'); + document.body.appendChild(dummy); + dummy.value = copyText; + dummy.select(); + document.execCommand('copy'); + document.body.removeChild(dummy); + } + return false; +} + +function generateQRPage() { + var pass = sanitizePassword(getById("invite_password").value); + if (pass.length) { + return generateHash(pass + session.salt, 4).then(function(hash) { + generateQRPageCallback(hash); + }).catch(errorlog); + } else { + generateQRPageCallback(""); + } +} + +async function updateLinkWelcome(arg, input) { + if (input.checked){ + var response = await promptAlt("Enter the message you'd like the guests to see"); + response = encodeURIComponent(response); + var param = input.dataset.param.split("=")[0]; + input.dataset.param = param + "=" + response; + } + updateLink(arg, input); +} + + +function updateLinkWebP(arg, input) { + if (input.checked){ + if (!((getById("director_block_" + arg).dataset.raw.includes("&broadcast")) || (getById("director_block_" + arg).dataset.raw.includes("?broadcast")))){ + getById("broadcastSlider").checked=true; + updateLink(arg, getById("broadcastSlider")); + } + } + updateLink(arg, input); +} + +var soloLinkAppended = ""; +function updateLink(arg, input, solo=false) { + log("updateLink"); + log(input.dataset.param); + if (input.checked) { + + getById("director_block_" + arg).dataset.raw += input.dataset.param; + + if (solo){ + soloLinkAppended += input.dataset.param; + } + + var string = getById("director_block_" + arg).dataset.raw; + + if ((arg==1) && (getById("obfuscate_director_" + arg).checked)) { + string = obfuscateURL(string); + } + + getById("director_block_" + arg).href = string; + getById("director_block_" + arg).innerText = string; + } else { + var string = getById("director_block_" + arg).dataset.raw + "&"; + string = string.replace(input.dataset.param + "&", "&"); + string = string.substring(0, string.length - 1); + getById("director_block_" + arg).dataset.raw = string; + + if (solo){ + soloLinkAppended += "&"; + soloLinkAppended = soloLinkAppended.replace(input.dataset.param + "&", "&"); + soloLinkAppended = soloLinkAppended.substring(0, soloLinkAppended.length - 1); + } + + if ((arg==1) && (getById("obfuscate_director_" + arg).checked)) { + string = obfuscateURL(string); + } + + // document.querySelector("soloLink") + // soloLink + + getById("director_block_" + arg).href = string; + getById("director_block_" + arg).innerText = string; + } + if (solo){ + document.querySelectorAll("a.soloLink").forEach(ele=>{ + try { + var href = ele.getAttribute("value") + soloLinkAppended; + ele.href = href; + ele.innerHTML = href; + } catch(e){ + errorlog(e); + } + }); + } + + saveDirectorSettings(); +} + +function changeURL(changeURL){ + window.focus(); + if (session.consent){ + hangup(); + window.location.href = changeURL; + } else { + confirmAlt(getTranslation("director-redirect-1")+changeURL+getTranslation("director-redirect-2")).then(res=>{ + if (res){ + hangup(); + window.location.href = changeURL; + }; + }); + } +} + +function updateLinkInverse(arg, input) { + log("updateLinkInverse"); + log(input.dataset.param); + if (!(input.checked)) { + + getById("director_block_" + arg).dataset.raw += input.dataset.param; + + var string = getById("director_block_" + arg).dataset.raw; + + if ((arg==1) && (getById("obfuscate_director_" + arg).checked)) { + string = obfuscateURL(string); + } + + + getById("director_block_" + arg).href = string; + getById("director_block_" + arg).innerText = string; + } else { + var string = getById("director_block_" + arg).dataset.raw + "&"; + string = string.replace(input.dataset.param + "&", "&"); + string = string.substring(0, string.length - 1); + getById("director_block_" + arg).dataset.raw = string; + + if ((arg==1) && (getById("obfuscate_director_" + arg).checked)) { + string = obfuscateURL(string); + } + + getById("director_block_" + arg).href = string; + getById("director_block_" + arg).innerText = string; + } +} + +function updateLinkScene(arg, input) { + log("updateLinkScene"); + var string = getById("director_block_" + arg).dataset.raw; + + if (input.checked) { + string = changeParam(string, "scene", "0"); + } else { + string = changeParam(string, "scene", "1"); + } + getById("director_block_" + arg).dataset.raw = string; + + if ((arg==1) && (getById("obfuscate_director_" + arg).checked)) { + string = obfuscateURL(string); + } + + getById("director_block_" + arg).href = string; + getById("director_block_" + arg).innerText = string; +} + +function fullscreenPageToggle(state=null){ + try { + if (!document.fullscreenElement) { // not currently full screen + if (state!==false){ // if state is false, we are already not full screen + if (document.documentElement.requestFullscreen){ + document.documentElement.requestFullscreen(); + } else if (document.documentElement.webkitRequestFullscreen){ + document.documentElement.webkitRequestFullscreen(); + } + } + } else if (document.exitFullscreen) { + if (!state){ // if toggle mode or state=false + document.exitFullscreen(); + } + } else if (document.webkitExitFullscreen) { + if (!state){ // if toggle mode or state=false + document.webkitExitFullscreen(); + } + } + //updateMixer(); // we will do this on the event for this instead + } catch(e){errorlog(e);} +} + +session.pipWindow = false; +async function PictureInPicturePageToggle(state=null){ + try { + if (typeof documentPictureInPicture === "undefined"){return;} + if (session.pipWindow){ + getById("testtone").parentNode.insertBefore(session.pipWindow.document.getElementById("gridlayout"), getById("testtone")); + session.pipWindow.close(); + session.pipWindow = null; + updateMixer(); + getById("PictureInPicturePage").classList.remove("green"); + } else{ + session.pipWindow = await documentPictureInPicture.requestWindow({width:564,height:346}); // 360 + 30px for the window header + + session.pipWindow.addEventListener("pagehide", (event) => { + if (session.pipWindow){ + getById("testtone").parentNode.insertBefore(session.pipWindow.document.getElementById("gridlayout"), getById("testtone")); + session.pipWindow.close(); + session.pipWindow = null; + updateMixer(); + getById("PictureInPicturePage").classList.remove("green"); + } + }); + + session.pipWindow.document.body.className = "main"; + session.pipWindow.document.head.innerHTML = 'Pop-out Window' + session.pipWindow.document.body.style = document.body.style; + session.pipWindow.document.title = "Pop-out Window"; + session.pipWindow.document.body.append(getById("gridlayout")); + session.pipWindow.onresize = updateMixer; + updateMixer(); // just in case onresize doesn't trigger + getById("PictureInPicturePage").classList.add("green"); + } + } catch(e){errorlog(e);} +} + +function resetGen() { + getById("gencontent").style.display = "block"; + getById("gencontent2").style.display = "none"; + getById("gencontent2").className = ""; //container-inner + getById("gencontent").className = "container-inner"; // + getById("gencontent2").innerHTML = ""; + getById("videoname4").focus(); +} + +function generateQRPageCallback(hash) { + try { + var title = getById("videoname4").value; + if (title.length) { + title = title.replace(/[\W]+/g, "_").replace(/_+/g, '_'); // but not what others might get. TODO: allow for non-alphanumeric characters; santitize, then URL encode instead, + title = "&label=" + title; + } + var sid = session.generateStreamID(); + + var viewstr = ""; + var sendstr = ""; + + if (getById("invite_bitrate").checked) { + viewstr += "&bitrate=20000"; + } + if (getById("invite_vp9").checked) { + viewstr += "&codec=vp9"; + } + if (getById("invite_stereo").checked) { + viewstr += "&stereo"; + sendstr += "&stereo"; + } + if (getById("invite_automic").checked) { + sendstr += "&audiodevice=1"; + } + if (getById("invite_automic").checked) { + sendstr += "&audiodevice=1"; + } + if (getById("invite_effects").checked) { + sendstr += "&effects"; + } + + if (getById("invite_remotecontrol").checked) { // + var remote_gen_id = session.generateStreamID(); + sendstr += "&remote=" + remote_gen_id; // security + viewstr += "&remote=" + remote_gen_id; + } + + if (getById("invite_joinroom").value.trim().length) { + sendstr += "&ssid&room=" + getById("invite_joinroom").value.trim(); + viewstr += "&solo&room=" + getById("invite_joinroom").value.trim(); + } + + if (getById("invite_password").value.trim().length) { + sendstr += "&hash=" + hash; + viewstr += "&password=" + sanitizePassword(getById("invite_password").value.trim()); + } + + if (session.token){ + sendstr += "&token=" + session.token; + viewstr += "&token=" + session.token; + } + + if (getById("invite_group_chat_type").value) { // 0 is default + if (getById("invite_group_chat_type").value == 1) { // no video + sendstr += "&novideo"; + } else if (getById("invite_group_chat_type").value == 2) { // no view or audio + sendstr += "&view"; + } + } + + if (getById("invite_quality").value) { + if (getById("invite_quality").value == 0) { + sendstr += "&quality=0"; + } else if (getById("invite_quality").value == 1) { + sendstr += "&quality=1"; + } else if (getById("invite_quality").value == 2) { + sendstr += "&quality=2"; + } + } + + var wss = ""; + + if (session.wssSetViaUrl){ + if (session.customWSS && (session.customWSS!==true)){ + wss = "&pie="+session.customWSS; + } else { + wss = "&wss="+session.wss; + } + } + + var hoststr = ""; + if (getById("invite_hostlink").checked) { + hoststr = 'https://' + location.host + location.pathname + '?push=' + sid + "_hostlink" + "&view="+ sid + sendstr + "&bitrate=500" + title + wss; + sendstr = 'https://' + location.host + location.pathname + '?push=' + sid + "&view="+sid + "_hostlink" + sendstr + "&bitrate=1200" + title + wss; + } else { + sendstr = 'https://' + location.host + location.pathname + '?push=' + sid + sendstr + title + wss; + } + + if (getById("invite_obfuscate").checked) { + sendstr = obfuscateURL(sendstr); + } + + viewstr = 'https://' + location.host + location.pathname + '?view=' + sid + viewstr + title + wss; + getById("gencontent").style.display = "none"; + getById("gencontent").className = ""; // + getById("gencontent2").style.display = "block"; + getById("gencontent2").className = "container-inner"; + + getById("gencontent2").innerHTML = '
    \ +

    Guest Invite Link:

    \ + ' + sendstr + '

    \ +

    and don\'t forget the

    OBS Browser Source Link:

    ' + viewstr + ' \ + '; + + if (hoststr){ + getById("gencontent2").innerHTML += '

    Host Chat Link:

    ' + hoststr + ' '; + } + + getById("gencontent2").innerHTML += '

    \ + \ +
  • This invite link and OBS ingestion link are reusable.
  • \ +
  • Only one person may use a specific invite at a time.
  • \ +
  • The stream ID can be changed manually to something else; keep it unique and alphanumeric.
  • \ +
  • Nothing is stored server-side; links do not expire, nor is there anything to delete.
  • \ +


    \ + '; + + var qrcode = new QRCode(getById("qrcode"), { + width: 300 + , height: 300 + , colorDark: "#000000" + , colorLight: "#FFFFFF" + , useSVG: false + }); + qrcode.makeCode(sendstr); + getById("qrcode").title = ""; + setTimeout(function() { + getById("qrcode").title = ""; + if (getById("qrcode").getElementsByTagName('img').length) { + getById("qrcode").getElementsByTagName('img')[0].style.cursor = "none"; + getById("qrcode").getElementsByTagName('img')[0].style.margin = "0 auto"; + } + }, 100); // i really hate the title overlay that the qrcode function makes + + } catch (e) { + errorlog(e); + } +} + + +function initSceneList(UUID){ + Object.keys(session.sceneList).forEach((scene, index) => { + if (getById("container_" + UUID).querySelectorAll('[data-scene="'+scene+'"]').length){return;} // already exists. + var newScene = document.createElement("div"); + newScene.innerHTML = ''; + newScene.classList.add("customScene"); + + var added = false; + getById("container_" + UUID).querySelectorAll('.customScene>[data-scene]').forEach(ele=>{ + log(ele); + if (!added && ele.dataset.scene>scene+""){ + ele.parentNode.parentNode.insertBefore(newScene, ele.parentNode); + added = true; + } + }); + if (!added){ + getById("container_" + UUID).appendChild(newScene); + } + }); +} + +function updateSceneList(scene){ // custom scenes only. + if (!session.director){return;} + if (scene in session.sceneList){return;} + + if ((parseInt(scene)+"")===scene){ + if ((parseInt(scene)>=0) && (parseInt(scene)<=8)){ + return; + } + } + + session.sceneList[scene] = true; + for (var UUID in session.rpcs){ + var newScene = document.createElement("div"); + newScene.innerHTML = ''; + newScene.classList.add("customScene"); + var added = false; + getById("container_" + UUID).querySelectorAll('.customScene>[data-scene]').forEach(ele=>{ + log(ele); + if (!added && ele.dataset.scene>scene+""){ + ele.parentNode.parentNode.insertBefore(newScene, ele.parentNode); + added = true; + } + }); + if (!added){ + getById("container_" + UUID).appendChild(newScene); + } + } + + + if (session.showDirector){ + if (document.getElementById("container_director")){ + var newScene = document.createElement("div"); + newScene.innerHTML = ''; + newScene.classList.add("customScene"); + //getById("container_director").appendChild(newScene); + + var added = false; + getById("container_director").querySelectorAll('.customScene>[data-scene]').forEach(ele=>{ + if (!added && ele.dataset.scene>scene+""){ + ele.parentNode.parentNode.insertBefore(newScene, ele.parentNode); + added = true; + } + }); + if (!added){ + getById("container_director").appendChild(newScene); + } + } + } +} + +var vis = (function() { + var stateKey, eventKey, keys = { + hidden: "visibilitychange" + , webkitHidden: "webkitvisibilitychange" + , mozHidden: "mozvisibilitychange" + , msHidden: "msvisibilitychange" + }; + for (stateKey in keys) { + if (stateKey in document) { + eventKey = keys[stateKey]; + break; + } + } + return function(c) { + if (c) { + document.addEventListener(eventKey, c); + //document.addEventListener("blur", c); + //document.addEventListener("focus", c); + } + return !document[stateKey]; + }; +})(); + +function unPauseVideo(videoEle, update=true){ + try { + if (!videoEle){return;} + else if (!(videoEle.dataset.UUID in session.rpcs)){return;} + else if (!("prePausedBandwidth" in session.rpcs[videoEle.dataset.UUID])){return;} // not paused; useless to have, but might as well + session.rpcs[videoEle.dataset.UUID].manualBandwidth = false; + //session.rpcs[videoEle.dataset.UUID].manualAudioBandwidth = false; + + if (session.rpcs[videoEle.dataset.UUID].videoElement){ + session.rpcs[videoEle.dataset.UUID].videoElement.play(); + } + + delete(session.rpcs[videoEle.dataset.UUID].prePausedBandwidth); + session.requestRateLimit(false, videoEle.dataset.UUID, false); // passing a bitrate of false forces the saved existing bitrate to be requested. + videoEle.classList.remove("paused"); + videoEle.classList.remove("partialFadeout"); + if (update){ + updateMixer(); + } + }catch(e){errorlog(e);} +} + +function pauseVideo(videoEle, update=true){ + if (!videoEle){return;} + else if (!(videoEle.dataset.UUID in session.rpcs)){return;} + session.rpcs[videoEle.dataset.UUID].prePausedBandwidth = session.rpcs[videoEle.dataset.UUID].manualBandwidth; // useless, but whatever + session.rpcs[videoEle.dataset.UUID].manualBandwidth = 0; + + if (session.rpcs[videoEle.dataset.UUID].videoElement){ + session.rpcs[videoEle.dataset.UUID].videoElement.pause(); + } + //session.rpcs[videoEle.dataset.UUID].manualAudioBandwidth = 0; + session.requestRateLimit(false, videoEle.dataset.UUID, true); // passing a bitrate of false forces the saved existing bitrate to be requested. + videoEle.classList.add("paused"); + videoEle.classList.add("partialFadeout"); + if (update){ + updateMixer(); + } +} + +(function rightclickmenuthing() { // right click menu + "use strict"; + + function clickInsideElement(e, value="menu") { + + var el = e.srcElement || e.target; + if (el.dataset && (value in el.dataset)) { + return el; + } else { + while (el = el.parentNode) { + if (el.dataset && (value in el.dataset)) { + return el; + } + } + } + return false; + } + + function getPosition(event2) { + var posx = 0; + var posy = 0; + + if (!event2){var event = window.event;} + + if (event2.pageX || event2.pageY){ + posx = event2.pageX; + posy = event2.pageY; + } else if (event2.clientX || event2.clientY) { + posx = event2.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; + posy = event2.clientY + document.body.scrollTop + document.documentElement.scrollTop; + } + + return {x: posx, y: posy}; + } + + var taskItemInContext; + var clickCoordsX; + var clickCoordsY; + var menu; + var menuState = 0; + var lastMenu= false; + var menuWidth; + var menuHeight; + var windowWidth; + var windowHeight; + + + + function contextListener() { + document.addEventListener("contextmenu", function(e) { + + if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1){ + if (e && (!e.ctrlKey && !e.metaKey)){return;} + } else if (e && (e.ctrlKey || e.metaKey)){return;} // allow for development ease + + taskItemInContext = clickInsideElement(e, "menu"); + + if (taskItemInContext) { + e.preventDefault(); + e.stopPropagation(); + if (taskItemInContext.dataset && taskItemInContext.dataset.menu){ + toggleMenuOn(taskItemInContext.dataset.menu, taskItemInContext); + } else { + toggleMenuOn(); + } + positionMenu(e); + return false; + } else { + taskItemInContext = null; + toggleMenuOff(); + } + }); + } + + function menuClickListener(e) { + var clickeElIsLink = clickInsideElement(e, "action"); + if (clickeElIsLink) { + e.preventDefault(); + e.stopPropagation(); + menuItemListener(clickeElIsLink, false, e); + return false; + } else { + var button = e.which || e.button; + if (button === 1) { + toggleMenuOff(); + } + } + } + + function handleInputElement(e){ // for the input range slider version + var clickeElIsLink = clickInsideElement(e, "action"); + if (clickeElIsLink) { + e.preventDefault(); + e.stopPropagation(); + menuItemListener(clickeElIsLink, e.srcElement, e); + return false; + } else { + var button = e.which || e.button; + if (button === 1) { + toggleMenuOff(); + } + } + } + + function toggleMenuOn(menutype=false, ele=false) { + if (lastMenu && (lastMenu !== menutype)){ + try { + menuState = 0; + getById(lastMenu).classList.remove("context-menu--active"); + + document.removeEventListener("click", menuClickListener); + menu.querySelectorAll("input").forEach(ele=>{ + ele.removeEventListener("input", handleInputElement); + }); + } catch(e){} + } + menu = getById(menutype || "context-menu"); + menuItemSyncState(menu); + if (menuState !== 1) { + menuState = 1; + menu.classList.add("context-menu--active"); + document.addEventListener("click", menuClickListener); + menu.querySelectorAll("input").forEach(ele=>{ + ele.addEventListener("input", handleInputElement); + }); + } + + if (ele && ele.classOptions){ + menu.classList.add(ele.classOptions); + } + lastMenu = menutype || "context-menu"; + + } + + function toggleMenuOff() { + if (menuState !== 0) { + menuState = 0; + menu.classList.remove("context-menu--active"); + + document.removeEventListener("click", menuClickListener); + menu.querySelectorAll("input").forEach(ele=>{ + ele.removeEventListener("input", handleInputElement); + }); + } + lastMenu = false; + } + + function positionMenu(e) { + var clickCoords = getPosition(e); + clickCoordsX = clickCoords.x; + clickCoordsY = clickCoords.y; + + menuWidth = menu.offsetWidth + 4; + menuHeight = menu.offsetHeight + 4; + + windowWidth = window.innerWidth; + windowHeight = window.innerHeight; + + if ((windowWidth - clickCoordsX) < menuWidth) { + menu.style.left = windowWidth - menuWidth + "px"; + } else { + menu.style.left = clickCoordsX + "px"; + } + + if ((windowHeight - clickCoordsY) < menuHeight) { + menu.style.top = windowHeight - menuHeight + "px"; + } else { + menu.style.top = clickCoordsY + "px"; + } + } + + async function menuItemListener(link, inputElement=false, e=false) { + if (link.getAttribute("data-action") === "Open") { + window.open(taskItemInContext.href); + } else if (link.getAttribute("data-action") === "Copy") { + copyFunction(taskItemInContext.href); + } else if (link.getAttribute("data-action") === "Mirror") { + if ((taskItemInContext.id == "videosource") || (taskItemInContext.id == "previewWebcam")){ + session.mirrored = !session.mirrored; + applyMirror(false); + log("session.mirrored"); + } else { + if ("mirror" in taskItemInContext){ + taskItemInContext.mirror = !taskItemInContext.mirror; + applyMirrorGuest(taskItemInContext.mirror, taskItemInContext); + } else { + taskItemInContext.mirror = true; + applyMirrorGuest(taskItemInContext.mirror, taskItemInContext); + } + } + } else if (link.getAttribute("data-action") === "FullWindow") { + if ((taskItemInContext.id == "videosource") || (taskItemInContext.id == "previewWebcam")){ + session.infocus=true; + } else { + session.infocus = taskItemInContext.dataset.UUID; + } + updateMixer(); + } else if (link.getAttribute("data-action") === "ShrinkWindow") { + session.infocus=false; + updateMixer(); + } else if (link.getAttribute("data-action") === "Pause") { + pauseVideo(taskItemInContext); + } else if (link.getAttribute("data-action") === "UnPause") { + unPauseVideo(taskItemInContext); + } else if (link.getAttribute("data-action") === "PiP") { + togglePictureInPicture(taskItemInContext); + } else if (link.getAttribute("data-action") === "PiP2") { + PictureInPicturePageToggle(); + } else if (link.getAttribute("data-action") === "Record") { + if (taskItemInContext.stopWriter || taskItemInContext.recording){ + } else if (taskItemInContext.startWriter){ + taskItemInContext.startWriter(); + } else { + var videoKbps = 4000; + if (session.recordLocal !== false) { + videoKbps = session.recordLocal; + } + recordLocalVideo(null, videoKbps, taskItemInContext) + } + } else if (link.getAttribute("data-action") === "StopRecording") { + if (taskItemInContext.stopWriter){ + taskItemInContext.stopWriter(); + } else if (taskItemInContext.recording){ + recordLocalVideo("stop", null, taskItemInContext); + } + } else if (link.getAttribute("data-action") === "CopyFrameAsImage") { + copyVideoFrameToClipboard(taskItemInContext, e); + } else if (link.getAttribute("data-action") === "SaveFrameToDisk") { + saveVideoFrameToClipboard(taskItemInContext, e); + } else if (link.getAttribute("data-action") === "ChangeBuffer") { + toggleBufferSettings(taskItemInContext.dataset.UUID); + } else if (link.getAttribute("data-action") === "Cast") { + //copyFunction(taskItemInContext.href); + } else if (link.getAttribute("data-action") === "Controls") { + taskItemInContext.controls = true; + } else if (link.getAttribute("data-action") === "HideControls") { + taskItemInContext.controls = false; + } else if (link.getAttribute("data-action") === "Edit") { + //copyFunction(taskItemInContext.href); + var response = await promptAlt("Please note, manual edits to the URL may conflict with the toggles", false, false, taskItemInContext.href); + if (response){ + taskItemInContext.href = response; + taskItemInContext.dataset.raw = response; + taskItemInContext.innerHTML = response; + + } + } else if (link.getAttribute("data-action") === "QRCode") { + warnUser("Loading QR Code"); + loadQR(function tt(url){ + getById("alertModalMessage").innerHTML = ""; + var qrcode = new QRCode(getById("alertModalMessage"), { + width: 300 + , height: 300 + , colorDark: "#000000" + , colorLight: "#FFFFFF" + , useSVG: false + }); + qrcode.makeCode(url); + getById("alertModalMessage").title = ""; + setTimeout(function() { + getById("alertModalMessage").title = ""; + if (getById("alertModalMessage").getElementsByTagName('img').length) { + getById("alertModalMessage").getElementsByTagName('img')[0].style.cursor = "none"; + } + }, 100); + }, taskItemInContext.href); + } else if (link.getAttribute("data-action") === "ShowStats"){ + if ((taskItemInContext.id == "videosource") || (taskItemInContext.id == "previewWebcam")){ + var [menu, innerMenu] = statsMenuCreator(); + menu.interval = setInterval(printMyStats,session.statsInterval, innerMenu); + printMyStats(innerMenu); + } else if (taskItemInContext.dataset.UUID && (taskItemInContext.dataset.UUID in session.rpcs)){ + var [menu, innerMenu] = statsMenuCreator(); + printViewStats(innerMenu, taskItemInContext.dataset.UUID ); + menu.interval = setInterval(printViewStats, session.statsInterval, innerMenu, taskItemInContext.dataset.UUID); + } + } else if (link.getAttribute("data-action") === "OutputAudio"){ + enumerateDevices().then(function(deviceInfo){ + var ele = getById(taskItemInContext.id); + + var deviceListElement = gotDevices3(deviceInfo, ele); + if (deviceListElement){ + warnUser("Select the audio playback destination for this media:\n\n"); + getById("alertModalMessage").appendChild(deviceListElement); + } else { + warnUser("No output devices available"); + } + }); + + // + } else if (link.getAttribute("data-action") === "RemoteHangup") { + if (session.rpcs[taskItemInContext.dataset.UUID] && session.rpcs[taskItemInContext.dataset.UUID].stats.info && ("remote" in session.rpcs[taskItemInContext.dataset.UUID].stats.info) && session.rpcs[taskItemInContext.dataset.UUID].stats.info.remote){ + var confirmHangup = confirm(getTranslation("confirm-disconnect-user")); + if (confirmHangup) { + var msg = {}; + msg.hangup = true; + msg.remote = session.remote; + msg = await session.encodeRemote(msg); + session.sendRequest(msg, taskItemInContext.dataset.UUID); + pokeIframeAPI("hungup", "remote", taskItemInContext.dataset.UUID); + } + } + } else if (link.getAttribute("data-action") === "RemoteReload") { + if (session.rpcs[taskItemInContext.dataset.UUID] && session.rpcs[taskItemInContext.dataset.UUID].stats.info && ("remote" in session.rpcs[taskItemInContext.dataset.UUID].stats.info) && session.rpcs[taskItemInContext.dataset.UUID].stats.info.remote){ + var confirmReload = confirm(getTranslation("confirm-reload-user")); + if (confirmReload) { + var msg = {}; + msg.reload = true; + msg.remote = session.remote; + msg = await session.encodeRemote(msg); + session.sendRequest(msg, taskItemInContext.dataset.UUID); + pokeIframeAPI("reload", "remote", taskItemInContext.dataset.UUID); + } + } + } else if (link.getAttribute("data-action") === "SSNewTab") { + var URL = "https://"+window.location.hostname+location.pathname+createScreenShareURL(false); + log(URL); + window.open(URL, '_blank').focus(); + } else if (link.getAttribute("data-action") === "pip-clock") { + popOutClock(taskItemInContext.children[0]); + } else if (link.getAttribute("data-action") === "Publish") { + + var URL = taskItemInContext.href; + URL+="&clean&chroma=000&ssar=landscape&nosettings&prefercurrenttab&selfbrowsersurface=include&displaysurface=browser&np&nopush&publish&whippush&whippushtoken"; + var win = window.open( URL ,'targetWindow', 'toolbar=no,location=no,status=no,scaling=no,menubar=no,scrollbars=no,resizable=no,width=1280,height=720'); + win.focus(); + win.resizeTo(1280,720); + } + + if (inputElement===false){ + log("Task ID - " + taskItemInContext + ", Task action - " + link.getAttribute("data-action")); + toggleMenuOff(); + } + } + + function menuItemSyncState(menu) { + var items = menu.querySelectorAll("[data-action]"); + for (var i=0;i -1){ + items[i].parentNode.classList.add("hidden"); + } else { + items[i].parentNode.classList.remove("hidden"); + } + } else if (items[i].getAttribute("data-action") === "Publish") { + if (taskItemInContext.classList.contains("publish")){ + items[i].parentNode.classList.remove("hidden"); + } else { + items[i].parentNode.classList.add("hidden"); + } + } + } + } + contextListener(); + +})(); + + +function gotDevices3(deviceInfos, vid){ + var audioEle = document.createElement("select"); + log(deviceInfos); + if (!deviceInfos.length) { + return false; + } + for (let i = 0; i !== deviceInfos.length; ++i) { + if (deviceInfos[i].kind === 'audiooutput') { + var opt = document.createElement("option"); + opt.innerText = deviceInfos[i].label; + opt.value = deviceInfos[i].deviceId; + audioEle.appendChild(opt); + audioEle.videoTarget = vid; + if (vid.sinkId){ + if (vid.sinkId == deviceInfos[i].deviceId){ + opt.selected = true; + } + } else if (vid.manualSink){ + if (vid.manualSink == deviceInfos[i].deviceId){ + opt.selected = true; + } + } else if (session.sink){ + if (session.sink == deviceInfos[i].deviceId){ + opt.selected = true; + } + } + } + } + audioEle.onchange = function(){ + vid.manualSink = this.options[this.selectedIndex].value; + if (this.videoTarget && this.videoTarget.dataset.UUID){ + session.audioEffects = true; + updateIncomingAudioElement(this.videoTarget.dataset.UUID); + } + resetupAudioOut(this.videoTarget); + } + return audioEle; +} + +function popupMessage(e, message = "Copied to Clipboard") { // right click menu + + var posx = 0; + var posy = 0; + + if (!e) var e = window.event; + + if (e.pageX || e.pageY) { + posx = e.pageX; + posy = e.pageY; + } else if (e.clientX || e.clientY) { + posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; + posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; + } + + posx += 10; + + + var menu = getById("messagePopup"); + menu.innerHTML = "
    " + message + "
    "; + var menuState = 0; + var menuWidth; + var menuHeight; + var menuPosition; + var menuPositionX; + var menuPositionY; + + var windowWidth; + var windowHeight; + + if (menuState !== 1) { + menuState = 1; + menu.classList.add("context-menu--active"); + } + + menuWidth = menu.offsetWidth + 4; + menuHeight = menu.offsetHeight + 4; + + windowWidth = window.innerWidth; + windowHeight = window.innerHeight; + + if ((windowWidth - posx) < menuWidth) { + menu.style.left = windowWidth - menuWidth + "px"; + } else { + menu.style.left = posx + "px"; + } + + if ((windowHeight - posy) < menuHeight) { + menu.style.top = windowHeight - menuHeight + "px"; + } else { + menu.style.top = posy + "px"; + } + + + function toggleMenuOff() { + if (menuState !== 0) { + menuState = 0; + menu.classList.remove("context-menu--active"); + } + } + menu.classList.remove("fadeout"); + + var showlength = message.length*50 || 500; + + setTimeout(function() { + menu.classList.add("fadeout"); + }, showlength); + + setTimeout(function() { + toggleMenuOff(); + }, showlength+1000); +} + +function timeSince(date) { + + var seconds = Math.floor((new Date() - date) / 1000); + + var interval = seconds / 31536000; + + if (interval > 1) { + return Math.floor(interval) + " years"; + } + interval = seconds / 2592000; + if (interval > 1) { + return Math.floor(interval) + " months"; + } + interval = seconds / 86400; + if (interval > 1) { + return Math.floor(interval) + " days"; + } + interval = seconds / 3600; + if (interval > 1) { + return Math.floor(interval) + " hours"; + } + interval = seconds / 60; + if (interval > 1) { + return Math.floor(interval) + " minutes"; + } + return "Seconds ago"; +} + +var messageList = [] +function sendChatMessage(chatMsg = false, bc = false) { // filtered + visual + var data = {}; + if (chatMsg === false) { + var msg = document.getElementById('chatInput').value; + } else { + var msg = chatMsg; + } + //msg = sanitizeChat(msg); + if (msg == "") { + return false; + } + + msg = convertShortcodes(msg); + + var label = ""; + if (session.label){ + if (session.director){ + label = "" + session.label + ": "; + } else { + label = "" + session.label + ": "; + } + } else if (session.director){ + label = "Director: "; + } + + if (msg.trim()==="/list"){ + var listMsg = null; + for (var UUID in session.rpcs){ + if (session.rpcs[UUID].label){ + listMsg = UUID+": "+session.rpcs[UUID].label + } else if (session.directorList.indexOf(UUID)>=0){ + listMsg = UUID+": Director"; + } else { + listMsg = UUID+": Unknown User"; + } + var data = {}; + data.msg = listMsg; + data.label = false; + data.type = "alert"; + data.time = Date.now(); + messageList.push(data); + } + for (var UUID in session.pcs){ + if (UUID in session.rpcs){continue;} + if (session.pcs[UUID].label){ + listMsg = UUID+"; "+session.pcs[UUID].label + } else if (session.directorList.indexOf(UUID)>=0){ + listMsg = UUID+"; Director"; + } else { + listMsg = UUID+"; Unknown User"; + } + var data = {}; + data.msg = listMsg; + data.label = false; + data.type = "alert"; + data.time = Date.now(); + messageList.push(data); + } + if (listMsg===null){ + data.msg = "No other users are connected to you"; + data.label = false; + data.type = "alert"; + data.time = Date.now(); + messageList.push(data); + } + } else if (msg.startsWith("\/msg ")){ + var msg = msg.split("\/msg ")[1]; + msg = msg.split(" "); + uid = msg.shift().toLowerCase(); + msg = msg.join(" "); + if (msg == ""){return false;} + var sent = false; + for (var UUID in session.rpcs){ + if (UUID.startsWith(uid)){ + sendChat(msg, UUID); // send message to peers + var data = {}; + data.time = Date.now(); + data.msg = sanitizeChat(msg); // this is what the other person should see + data.label = label; + data.type = "sent"; + messageList.push(data); + sent=true; + } else if (session.rpcs[UUID].label && session.rpcs[UUID].label.toLowerCase().startsWith(uid)){ + sendChat(msg, UUID); // send message to peers + var data = {}; + data.time = Date.now(); + data.msg = sanitizeChat(msg); // this is what the other person should see + data.label = label; + data.type = "sent"; + messageList.push(data); + sent=true; + } else if ((session.directorList.indexOf(UUID)>=0) && "director".startsWith(uid)){ + sendChat(msg, UUID); // send message to peers + var data = {}; + data.time = Date.now(); + data.msg = sanitizeChat(msg); // this is what the other person should see + data.label = label; + data.type = "sent"; + messageList.push(data); + sent=true; + } + } + for (var UUID in session.pcs){ + if (UUID in session.rpcs){continue;} + if (UUID.startsWith(uid)){ + sendChat(msg, UUID); // send message to peers + var data = {}; + data.time = Date.now(); + data.msg = sanitizeChat(msg); // this is what the other person should see + data.label = label; + data.type = "sent"; + messageList.push(data); + sent=true; + } else if (session.pcs[UUID].label && session.pcs[UUID].label.toLowerCase().startsWith(uid)){ + sendChat(msg, UUID); // send message to peers + var data = {}; + data.time = Date.now(); + data.msg = sanitizeChat(msg); // this is what the other person should see + data.label = label; + data.type = "sent"; + messageList.push(data); + sent=true; + } else if ((session.directorList.indexOf(UUID)>=0) && "director".startsWith(uid)){ + sendChat(msg, UUID); // send message to peers + var data = {}; + data.time = Date.now(); + data.msg = sanitizeChat(msg); // this is what the other person should see + data.label = label; + data.type = "sent"; + messageList.push(data); + sent=true; + } + } + if (sent == false){ + var data = {}; + data.msg = "No user found. Message not sent."; + data.label = false; + data.type = "alert"; + data.time = Date.now(); + messageList.push(data); + updateMessages(); + return false; + } + } else if (msg.startsWith("\/")){ + data.msg = "Unknown command. Try '/list' or '/msg username message'."; + data.label = false; + data.type = "alert"; + data.time = Date.now(); + messageList.push(data); + updateMessages(); + return false; + } else if (session.directorChat===true){ + if (session.directorList.length){ + for (var i = 0;i{ + ele.click(); + }); +} + +function toggleQualityDirector(bitrate, UUID, ele) { // ele is specific to the button in the director's room + var eles = ele.parentNode.childNodes; + for (var i=0;i/g, ">").replace(/["']/g, ""); // try to sanitize things, just in case. + + var punc = ""; + while (url[url.length-1] === "."){ + url = url.slice(0,-1); + punc += "."; + } + while (url[url.length-1] === ";"){ + url = url.slice(0,-1); + punc += ";"; + } + while (url[url.length-1] === ","){ + url = url.slice(0,-1); + punc += ","; + } + while (url[url.length-1] === "!"){ + url = url.slice(0,-1); + punc += "!"; + } + while (url[url.length-1] === ":"){ + url = url.slice(0,-1); + punc += ":"; + } + while (url[url.length-1] === "*"){ + url = url.slice(0,-1); + punc += "*"; + } + while (url[url.length-1] === ")"){ + url = url.slice(0,-1); + punc += ")"; + } + while (url[url.length-1] === "?"){ + url = url.slice(0,-1); + punc += "?"; + } + + var hyperlink = url; + if (!hyperlink.match('^https?:\/\/')) { + hyperlink = 'http://' + hyperlink; + } + if (url.length>35){ + url = url.substring(0, 35)+"..."; + } + return '' + url + ''+punc; + }); +} + +function getChatMessage(msg, label = false, director = false, overlay = false) { + + msg = sanitizeChat(msg); // keep it clean. + if (msg == "") { + return; + } + + if (label) { + label = sanitizeLabel(label); + } + + data = {}; + data.time = Date.now(); + data.msg = msg; + if (label) { + data.label = label; + if (director) { + data.label = "" + data.label + ": "; + } else { + data.label = "" + data.label + ": "; + } + label = ""+label+":"; // label+":"; + } else if (director) { + data.label = "Director: "; + label = "Director:"; + } else { + if (session.director){ + data.label = "Someone: "; + } else { + data.label = ""; + } + label = ""; + } + data.type = "recv"; + + if (overlay) { + if (!(session.cleanOutput && session.cleanish==false)){ + var textOverlay = getById("overlayMsgs"); + if (textOverlay) { + var spanOverlay = document.createElement("span"); + spanOverlay.innerHTML = "" + label + " " + msg + "
    "; + textOverlay.appendChild(spanOverlay); + textOverlay.style.display = "block"; + var showtime = msg.length * 200 + 3000; + if (showtime > 8000) { + showtime = 8000; + } + setTimeout(function(ele) { + ele.parentNode.removeChild(ele); + }, showtime, spanOverlay); + } + } + } + + if (isIFrame) { + parent.postMessage({ + "gotChat": data + }, session.iframetarget); + } + + if (session.chatbutton===false){return;} // messages can still appear as overlays ^ + + messageList.push(data); + messageList = messageList.slice(-100); + + if (session.beepToNotify) { + playtone(); + showNotification("new message", msg); + } + updateMessages(); + + if (session.chat == false) { + getById("chattoggle").className = "las la-comments toggleSize pulsate"; + getById("chatbutton").className = "float"; + + if (getById("chatNotification").value) { + getById("chatNotification").value = getById("chatNotification").value + 1; + } else { + getById("chatNotification").value = 1; + } + getById("chatNotification").classList.add("notification", "red"); + } + + + if (session.broadcastChannel !== false) { + session.broadcastChannel.postMessage(data); /* send */ + } + +} + +function updateClosedCaptions(msg, label, UUID) { + msg.counter = parseInt(msg.counter); + var temp = document.createElement('div'); + temp.innerText = msg.transcript; + temp.innerText = temp.innerHTML; + var transcript = temp.textContent || temp.innerText || ""; + + if (transcript == "") { + return; + } + + transcript = transcript.charAt(0).toUpperCase() + transcript.slice(1); + //transcript = transcript.substr(-1, 5000); // keep it from being too long + + + if (label && (!(session.view && !session.view_set))) { + label = sanitizeLabel(label); + label = "" + label + ": "; + } else { + label = ""; + } + + var textOverlay = getById("overlayMsgs"); + if (textOverlay) { + if (document.getElementById(UUID + "_" + msg.counter)) { + var spanOverlay = document.getElementById(UUID + "_" + msg.counter); + } else { + var spanOverlay = document.createElement("span"); + spanOverlay.id = UUID + "_" + msg.counter; + textOverlay.appendChild(spanOverlay); + textOverlay.style.height = "unset"; + textOverlay.style.textAlign = "left"; + textOverlay.style.display = "block"; + textOverlay.style.position = "fixed"; + textOverlay.style.bottom = "0"; + + } + spanOverlay.innerHTML = label + transcript + "
    "; + spanOverlay.style.fontSize = (parseInt(session.labelsize || 100) / 100.0 * 4.5) + "vh"; + spanOverlay.style.lineHeight = (parseInt(session.labelsize || 100) / 100 * 6) + "vh"; + spanOverlay.style.margin = (parseInt(session.labelsize || 100) / 100.0 * 0.75) + "vh"; + + if (msg.isFinal) { + var showtime = 3000; + clearTimeout(spanOverlay.timeout); + spanOverlay.timeout = setTimeout(function(ele) { + ele.parentNode.removeChild(ele); + }, showtime, spanOverlay); + } else { + clearTimeout(spanOverlay.timeout); + spanOverlay.timeout = setTimeout(function(ele) { + ele.parentNode.removeChild(ele); + }, 30000, spanOverlay); + } + + } +} + +var chatUpdateTimeout = null; +function updateMessages(){ + if (session.chatbutton===false){return;} + document.getElementById("chatBody").innerHTML = ""; + for (var i in messageList) { + + var time = timeSince(messageList[i].time) || ""; + time = " - "+time+""; + var msg = document.createElement("div"); + var message = replaceURLs(messageList[i].msg); + + if (messageList[i].type == "sent") { + msg.innerHTML = message + "" + time + ""; + msg.classList.add("outMessage"); + } else if ((messageList[i].type == "recv") || (messageList[i].type == "action")) { + var label = ""; + if (messageList[i].label) { + label = messageList[i].label; + } + msg.innerHTML = label + message + "" + time + ""; + msg.classList.add("inMessage"); + } else if (messageList[i].type == "alert") { + msg.innerHTML = message + "" + time + ""; + msg.classList.add("inMessage"); + } else { + msg.innerHTML = message; + msg.classList.add("outMessage"); + } + + document.getElementById("chatBody").appendChild(msg); + } + showDownloadLinks(); + for (var i in msgTransferList) { + var time = timeSince(msgTransferList[i].time) || ""; + time = " - "+time+""; + + var msg = document.createElement("div"); + if ("idx" in msgTransferList[i]){ + msg.id = "transfer_"+msgTransferList[i].idx; + msg.classList.add("transfer"); + } + if (msgTransferList[i].type == "sent") { + msg.innerHTML = msgTransferList[i].msg + "" + time + ""; + msg.classList.add("outMessage"); + } else if ((msgTransferList[i].type == "recv") || (msgTransferList[i].type == "action")) { + var label = ""; + if (msgTransferList[i].label) { + label = msgTransferList[i].label; + } + msg.innerHTML = label + msgTransferList[i].msg + "" + time + ""; + msg.classList.add("inMessage"); + } else if (msgTransferList[i].type == "alert") { + msg.innerHTML = msgTransferList[i].msg + "" + time + ""; + msg.classList.add("inMessage"); + } else { + msg.innerHTML = msgTransferList[i].msg; + msg.classList.add("outMessage"); + } + + if (msg.id && document.getElementById(msg.id)){ + document.getElementById(msg.id).innerHTML = msg.innerHTML; + } else { + getById("chatBody").appendChild(msg); + } + } + if (chatUpdateTimeout) { + clearInterval(chatUpdateTimeout); + } + document.getElementById("chatBody").scrollTop = document.getElementById("chatBody").scrollHeight; + chatUpdateTimeout = setTimeout(function() { + updateMessages(); + }, 60000); +} + +function EnterButtonChat(event) { + // Number 13 is the "Enter" key on the keyboard + var key = event.which || event.keyCode; + if (key === 13) { + // Cancel the default action, if needed + event.preventDefault(); + // Trigger the button element with a click + sendChatMessage(); + } +} + +function showCustomizer(arg, ele) { + //getById("directorLinksButton").innerHTML=' LINKS (GUEST INVITES & SCENES)' + getById("showCustomizerButton1").style.backgroundColor = ""; + getById("showCustomizerButton2").style.backgroundColor = ""; + getById("showCustomizerButton3").style.backgroundColor = ""; + getById("showCustomizerButton4").style.backgroundColor = ""; + getById("showCustomizerButton1").style.boxShadow = ""; + getById("showCustomizerButton2").style.boxShadow = ""; + getById("showCustomizerButton3").style.boxShadow = ""; + getById("showCustomizerButton4").style.boxShadow = ""; + + + if (getById("customizeLinks" + arg).style.display != "none") { + getById("customizeLinks").style.display = "none"; + getById("customizeLinks" + arg).style.display = "none"; + } else { + //directorLinks").style.display="none"; + getById("showCustomizerButton" + arg).style.backgroundColor = "#1e0000"; + getById("showCustomizerButton" + arg).style.boxShadow = "inset 0px 0px 1px #b90000"; + getById("customizeLinks1").style.display = "none"; + getById("customizeLinks3").style.display = "none"; + getById("customizeLinks").style.display = "block"; + getById("customizeLinks" + arg).style.display = "block"; + } +} + +var PPTHotkey = getStorage("PPTHotkey") || false; +if (PPTHotkey){ + var key = ""; + if (PPTHotkey.ctrl){ + key += "Control"; + } + if (PPTHotkey.meta){ + if (key){ + key += " + "; + } + key += "Meta"; + } + if (PPTHotkey.alt){ + if (key){ + key += " + "; + } + key += "Alt"; + } + + if (PPTHotkey.key=="Control"){ + // + } else if (PPTHotkey.key=="Alt"){ + // + } else if (PPTHotkey.key=="Meta"){ + // + } else if (PPTHotkey.key !== false){ + if (key){ + key += " + "; + } + if (PPTHotkey.key === " "){ + key += "Space" + } else { + key += PPTHotkey.key; + } + } else if (key && (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1)){ + getById("pptHotKey").title = "Note: Global hot-keys can't simply be Control, Alt, or Meta keys."; + } + getById("pptHotKey").value = key; + + try { + if (window.electronApi && window.electronApi.updatePPT){ + window.electronApi.updatePPT(PPTHotkey); + } else if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1) { + if (!ipcRenderer){ + ipcRenderer = require('electron').ipcRenderer; + } + if (ipcRenderer){ + ipcRenderer.send('PPTHotkey', PPTHotkey); + } + } + } catch(e){errorlog(e);} +} + +function setHotKey(keyinput=true){ + if (!keyinput){ // clears if false + getById("pptHotKey").value = ""; + PPTHotkey = false; + removeStorage("PPTHotkey"); + + try { + if (window.electronApi && window.electronApi.updatePPT){ + window.electronApi.updatePPT(PPTHotkey); + } else if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1) { + if (!ipcRenderer){ + ipcRenderer = require('electron').ipcRenderer; + } + if (ipcRenderer){ + ipcRenderer.send('PPTHotkey', PPTHotkey); + } + } + } catch(e){errorlog(e);} + + return; + } + + PPTHotkey = { + ctrl:false, + alt: false, + meta: false, + key: false + }; + + log(event); + var key = ""; + if (event.ctrlKey){ + key += "Control"; + PPTHotkey.ctrl = true; + } + if (event.metaKey){ + if (key){ + key += " + "; + } + key += "Meta"; + PPTHotkey.meta = true; + } + if (event.altKey){ + if (key){ + key += " + "; + } + key += "Alt"; + PPTHotkey.alt = true; + } + + if (event.key=="Control"){ + // + } else if (event.key=="Alt"){ + // + } else if (event.key=="Meta"){ + // + } else if (event.key || (event.key === " " || (event.key===0))){ + if (key){ + key += " + "; + } + if (event.key === " "){ + key += "Space" + } else { + key += event.key; + } + PPTHotkey.key = event.key; + } + setStorage("PPTHotkey", PPTHotkey, 99999); + event.target.value = key; + + try { + if (window.electronApi && window.electronApi.updatePPT){ + window.electronApi.updatePPT(PPTHotkey); + } else if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1) { + if (!ipcRenderer){ + ipcRenderer = require('electron').ipcRenderer; + } + if (ipcRenderer){ + ipcRenderer.send('PPTHotkey', PPTHotkey); + } + } + } catch(e){errorlog(e);} + + event.preventDefault(); + event.stopPropagation(); + return false; +} + + +async function streamVideoToDropbox(filename) { + + if (!session.dbx){return;} + + return await session.dbx.filesUploadSessionStart({ close: false }).then(function (response) { + var sessionId = response.result.session_id; + var offset = 0; + var queue = []; + + console.log(response); + + function uploadChunk(chunk, oldCursor = false) { + + if (queue.length){ // still uploading + queue.push(chunk); + return; + } + + queue.push(chunk); + + if (chunk===false){ + + console.log("DONE UPLOADING.. closing dropbox file: "+offset); + console.log(oldCursor); + + session.dbx.filesUploadSessionFinish({ cursor: { session_id: sessionId, offset: offset }, commit: { path: '/' + filename } }).then(function (response) { + console.log(response); + console.log('File uploaded to Dropbox:', response.path_display); + }) + .catch(function (error) { + console.error('Error uploading file:', error); + }); + + } else { + + var currentOffset = offset; + offset += chunk.size; + + var cursor = { session_id: sessionId, offset: currentOffset }; + + console.log(cursor); + + session.dbx.filesUploadSessionAppendV2({ cursor: cursor, close: false, contents: chunk }).then(function () { + console.log("uploaded"); + console.log(queue); + var x = queue.shift(); + if (queue.length){ + uploadChunk(queue.shift(), cursor) + } + }).catch(function (error) { + console.error('Error appending chunk:', error); + }); + } + } + + return uploadChunk; + + }).catch(function (error) { + console.error('Error starting upload session:', error); + }); +} + +var recordingBitratePromise = false; +var defaultRecordingBitrate = false; +async function recordVideo(target, event = null, videoKbps = false) { // event.currentTarget,this.parentNode.parentNode.dataset.UUID + + if (session.record === false){warnlog("recordings are disabled by decree of thy host magistrate");} + + var UUID = target.dataset.UUID; + + if (!UUID){return;} + + var video = session.rpcs[UUID].videoElement; + + if (!video){return;} + + if (video.stopWriter){ + video.stopWriter(); + updateLocalRecordButton(UUID, -1); + return; + } else if (video.startWriter){ + await video.startWriter(); + updateLocalRecordButton(UUID, 0); + return; + } + + + var audioKbps = false; + + if (event === null) { + if (defaultRecordingBitrate === null) { + updateLocalRecordButton(UUID, -1); + return; + } + } else if ((event.ctrlKey) || (event.metaKey)) { + updateLocalRecordButton(UUID, -3); + Callbacks.push([recordVideo, target, null, false]); + log("Record Video queued"); + defaultRecordingBitrate = false; + recordingBitratePromise = false; + return; + } else { + defaultRecordingBitrate = false; + recordingBitratePromise = false; + } + + log("Record Video Clicked"); + if ("recording" in video) { + log("ALREADY RECORDING!"); + updateLocalRecordButton(UUID, -2); + video.recorder.stop(); + session.requestRateLimit(35, UUID); // 100kbps + if (session.audiobitrate===false){ + session.requestAudioRateLimit(-1,UUID); + } + + var elements = document.querySelectorAll('[data-action-type="change-quality2"][data--u-u-i-d="' + UUID + '"]'); + if (elements[0]) { + elements[0].classList.add("pressed"); elements[0].ariaPressed = "true"; + } + var elements = document.querySelectorAll('[data-action-type="change-quality1"][data--u-u-i-d="' + UUID + '"]'); + if (elements[0]) { + elements[0].classList.remove("pressed"); elements[0].ariaPressed = "false"; + } + var elements = document.querySelectorAll('[data-action-type="change-quality3"][data--u-u-i-d="' + UUID + '"]'); + if (elements[0]) { + elements[0].classList.remove("pressed"); elements[0].ariaPressed = "false"; + } + return; + } else { + updateLocalRecordButton(UUID, 0); + //target.style.backgroundColor = "#FCC"; + //target.innerHTML = " Download"; + video.recording = true; + } + + video.recorder = {}; + + if (videoKbps === false) { + if (defaultRecordingBitrate == false) { + + videoKbps = 4000; + if (session.recordLocal !== false) { + videoKbps = session.recordLocal; + } + + if (!recordingBitratePromise){ + window.focus(); + recordingBitratePromise = promptAlt(getTranslation("press-ok-to-record"), false, false, videoKbps); + } + videoKbps = await recordingBitratePromise; + log("videoKbps: "+videoKbps+", UUID:"+UUID); + if (videoKbps === null) { + //target.style.backgroundColor = null; + //target.innerHTML = ' record local'; + updateLocalRecordButton(UUID, -1); + target.style.backgroundColor = ""; + delete(video.recorder); + delete(video.recording); + defaultRecordingBitrate = null; + return; + } + videoKbps = parseInt(videoKbps); + defaultRecordingBitrate = videoKbps; + } else { + videoKbps = defaultRecordingBitrate; + } + } + + if (videoKbps <= 0) { + audioKbps = videoKbps * (-1); + videoKbps = false; + if (session.audiobitrate===false){ + if ((audioKbps>0) && (audioKbps>=128)){ + session.requestAudioRateLimit(128,UUID); // no point going higher + } else if (audioKbps==0){ + session.requestAudioRateLimit(256,UUID); // PCM + } else { + session.requestAudioRateLimit(parseInt(audioKbps),UUID); // exact? sure. why not. + } + } + } else if (videoKbps < 50) { // this just makes sure you can't set 0 on the record bitrate. + videoKbps = 50; + session.requestRateLimit(parseInt(videoKbps * 0.8), UUID); // 3200kbps transfer bitrate. Less than the recording bitrate, to avoid waste. + } else { + session.requestRateLimit(parseInt(videoKbps * 0.8), UUID); // 3200kbps transfer bitrate. Less than the recording bitrate, to avoid waste. + + if (videoKbps>4000){ + if (session.audiobitrate===false){ + if (session.pcm){ + session.requestAudioRateLimit(256,UUID); + } else { + session.requestAudioRateLimit(128,UUID); + } + } + } else if (videoKbps>2500){ + if (session.audiobitrate===false){ + if (session.pcm){ + session.requestAudioRateLimit(256,UUID); + } else { + session.requestAudioRateLimit(80,UUID); + } + } + } + + } + + var timestamp = Date.now(); + var filename = ""; + if (session.rpcs[UUID].label || session.rpcs[UUID].streamID) { + filename = session.rpcs[UUID].label || session.rpcs[UUID].streamID; + filename = filename.replace(/[\W]+/g, "_"); + filename = filename.substring(0, 200); + } + + filename += "_" + timestamp.toString(); + + var cancell = false; + if (typeof video.srcObject === "undefined" || !video.srcObject) { + return; + } + + video.recorder.stop = function(restart = false, notify = false) { + + if (session.dbx && video.recorder && video.recorder.dropbox){ + video.recorder.dropbox(false); + } + + if (!video.recording) { + errorlog("ALREADY STOPPED"); + updateLocalRecordButton(UUID, -1); + return; + } + + if (notify){ + if (!session.cleanOutput){ + warnUser("A local recording has stopped unexpectedly."); + } + if (session.beepToNotify){ + playtone(); + + } + target.classList.remove("shake"); + setTimeout(function(target){target.classList.add("shake");},10, target); + } + + video.recording = false; + updateLocalRecordButton(UUID, -2); + try { + if (video.recorder.mediaRecorder.state !== "inactive") { + video.recorder.mediaRecorder.stop(); + } + } catch (e) { + errorlog(e); + } + + session.requestRateLimit(35, UUID); // 100kbps + if (session.audiobitrate===false){ + session.requestAudioRateLimit(-1,UUID); + } + var elements = document.querySelectorAll('[data-action-type="change-quality2"][data--u-u-i-d="' + UUID + '"]'); + if (elements[0]) { + elements[0].classList.add("pressed"); elements[0].ariaPressed = "true"; + } + var elements = document.querySelectorAll('[data-action-type="change-quality1"][data--u-u-i-d="' + UUID + '"]'); + if (elements[0]) { + elements[0].classList.remove("pressed"); elements[0].ariaPressed = "false"; + } + var elements = document.querySelectorAll('[data-action-type="change-quality3"][data--u-u-i-d="' + UUID + '"]'); + if (elements[0]) { + elements[0].classList.remove("pressed"); elements[0].ariaPressed = "false"; + } + + cancell = true; + // log('Recorded Blobs: ', recordedBlobs); + // download(); + setTimeout((writer1,UUID1,video1) => { + try{ + writer1.close(); + } catch(e){} + updateLocalRecordButton(UUID1, -1); + delete(video1.recorder); + delete(video1.recording); + }, 1200, writer, UUID, video); + }; + + const {readable, writable} = new TransformStream({ + transform: (chunk, ctrl) => chunk.arrayBuffer().then(b => ctrl.enqueue(new Uint8Array(b))) + }); + var writer = writable.getWriter(); + readable.pipeTo(streamSaver.createWriteStream(filename.toString() + '.webm', video.recorder.stop)); + video.recorder.writer = writer; + pokeIframeAPI("recording-started"); + + let options = {}; + + if (videoKbps) { + + var tryCodec = false; + if (session.recordingVideoCodec){ + tryCodec = session.recordingVideoCodec; + } + if (tryCodec && MediaRecorder.isTypeSupported('video/webm;codecs='+tryCodec)) { + if (!session.cleanOutput){ + warnUser("The browser 'says' it supports "+tryCodec); + } + options.mimeType = 'video/webm;codecs='+tryCodec; + if (session.pcm){ + if (MediaRecorder.isTypeSupported('video/webm;codecs="'+tryCodec+', pcm"')){ + options.mimeType = 'video/webm;codecs="'+tryCodec+', pcm"'; + } else { + options.mimeType = "video/webm;codecs=pcm"; + } + } + } else { + if (session.pcm){ + if (MediaRecorder.isTypeSupported("video/webm;codecs=pcm")) { + options.mimeType = "video/webm;codecs=pcm"; + } else { + options.mimeType = "video/webm"; + } + } else { + options.mimeType = "video/webm"; + } + } + if (videoKbps < 1000) { + options.videoBitsPerSecond = parseInt(videoKbps * 1024); // 100 kbps audio + } else { + options.bitsPerSecond = parseInt(videoKbps * 1024); // 100 to 132 kbps audio + } + video.recorder.mediaRecorder = new MediaRecorder(video.srcObject, options); + + //if (session.dbx){ + // video.recorder.dropbox = await streamVideoToDropbox(); // i don't want to upload to dropbox remote streams; just local + //} + + } else { + options.mimeType = 'audio/webm'; + if (audioKbps == 0) { + if (MediaRecorder.isTypeSupported("audio/webm;codecs=pcm")) { + options.mimeType = "audio/webm;codecs=pcm"; + } + } else { + options.bitsPerSecond = parseInt(audioKbps * 1024); + } + var stream = createMediaStream(); + video.srcObject.getAudioTracks().forEach((track) => { + stream.addTrack(track, video.srcObject); + }); + video.recorder.mediaRecorder = new MediaRecorder(stream, options); + + //if (session.dbx){ + // video.recorder.dropbox = await streamVideoToDropbox(); + //} + } + + log(options); + + function download() { + const blob = new Blob(recordedBlobs, { + type: "video/webm" + }); + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.style.display = 'none'; + a.href = url; + a.download = filename + ".webm"; + document.body.appendChild(a); + a.click(); + setTimeout(function(uu,aa){ + document.body.removeChild(aa); + window.URL.revokeObjectURL(uu); + }, 100, url,a); + } + + function handleDataAvailable(event) { + if (event.data && event.data.size > 0) { + //recordedBlobs.push(event.data); + try{ + writer.write(event.data); //////////// + if (video.recording) { + updateLocalRecordButton(UUID, (parseInt((Date.now() - timestamp) / 1000) || 0)); + } + } catch(e){warnlog("Stream recording error or ended");} + + try { + if (session.dbx && video.recorder && video.recorder.dropbox){ + video.recorder.dropbox(event.data); + } + + } catch(e){ + errorlog(e); + } + } + } + + video.recorder.mediaRecorder.ondataavailable = handleDataAvailable; + + video.recorder.mediaRecorder.onerror = function(event) { + errorlog(event); + video.recorder.stop(); + session.requestRateLimit(35, UUID); + if (!(session.cleanOutput)) { + setTimeout(function() { + warnUser("an error occured with the media recorder; stopping recording"); + }, 1); + } + }; + + video.srcObject.ended = function(event) { + video.recorder.stop(); + session.requestRateLimit(35, UUID); + if (!(session.cleanOutput)) { + setTimeout(function() { + warnUser("stream ended! stopping recording"); + }, 1); + } + }; + + + setTimeout(function(v) { + v.recorder.mediaRecorder.start(1000); + }, 500, video); // 100ms chunks + + return; +} + +function updateRemoteRecordButton(UUID, recorder) { + var elements = document.querySelectorAll('[data-action-type="recorder-remote"][data--u-u-i-d="' + UUID + '"]'); + if (elements[0]) { + var time = parseInt(recorder) || 0; + if (time == -4) { + if (!session.cleanOutput){ + warnUser("A remote recording has stopped unexpectedly.\n\nDid a user cancel the file downlaod?"); + } + if (session.beepToNotify){ + playtone(); + } + elements[0].classList.add("pressed"); elements[0].ariaPressed = "true"; + elements[0].classList.remove("shake"); + elements[0].innerHTML = ' stopping...'; + setTimeout(function(ele){ele.classList.add("shake");},10,elements[0]); + } else if (time == -3) { + elements[0].classList.remove("pressed"); elements[0].ariaPressed = "false"; + elements[0].disabled = true; + elements[0].innerHTML = ' Not Supported'; + if (!(session.cleanOutput)) { + setTimeout(function() { + warnUser('The remote browser does not support recording.\n\nPerhaps try local recording instead.'); + }, 0); + } + } else if (time == -5) { + if (!(session.cleanOutput)) { + setTimeout(function() { + warnUser('The remote browser has only experimental support for media recording.\n\nAlso, when this download stops, the remote user may be asked to download the file for it to save.'); + }, 0); + } + } else if (time == -2) { + elements[0].classList.add("pressed"); elements[0].ariaPressed = "true"; + elements[0].innerHTML = ' stopping...'; + } else if (time == -1) { + elements[0].classList.remove("pressed"); elements[0].ariaPressed = "false"; + elements[0].innerHTML = ' Record Remote'; + } else { + var minutes = Math.floor(time / 60); + var seconds = time - minutes * 60; + elements[0].classList.add("pressed"); elements[0].ariaPressed = "true"; + elements[0].innerHTML = ' ' + (minutes) + "m : " + zpadTime(seconds) + "s"; + } + } +} + +function updateLocalRecordButton(UUID, recorder) { + var elements = document.querySelectorAll('[data-action-type="recorder-local"][data--u-u-i-d="' + UUID + '"]'); + if (elements[0]) { + var time = parseInt(recorder) || 0; + + //target.innerHTML = ' ARMED'; + // + if (time == -3) { + elements[0].classList.add("pressed"); elements[0].ariaPressed = "true"; + elements[0].innerHTML = ' ARMED'; + elements[0].style.backgroundColor = "#BF3F3F"; + } else if (time == -2) { + elements[0].classList.add("pressed"); elements[0].ariaPressed = "true"; + elements[0].innerHTML = ' stopping...'; + elements[0].style.backgroundColor = ""; + } else if (time == -1) { + elements[0].classList.remove("pressed"); elements[0].ariaPressed = "false"; + elements[0].innerHTML = ' Record Local'; + elements[0].style.backgroundColor = ""; + } else { + var minutes = Math.floor(time / 60); + var seconds = time - minutes * 60; + elements[0].classList.add("pressed"); elements[0].ariaPressed = "true"; + elements[0].innerHTML = ' ' + (minutes) + "m : " + zpadTime(seconds) + "s"; + elements[0].style.backgroundColor = ""; + } + } +} + +function recordLocalVideoToggle() { + if (!session.videoElement){return;} + log("recordLocalVideoToggle()"); + + var ele = getById("recordLocalbutton"); + if (ele.dataset.state == "0") { + ele.dataset.state = "1"; + ele.style.backgroundColor = "red"; + ele.innerHTML = ''; + if ("recording" in session.videoElement) { + + } else { + recordLocalVideo("start"); + } + + if (session.director){ + var elements = document.querySelectorAll('[data-action-type="recorder-local"][data-sid="' + session.streamID + '"]'); + if (elements[0]) { + elements[0].classList.add("pressed"); elements[0].ariaPressed = "true"; + elements[0].innerHTML = ' Record'; + } + } + return true; + } else { + if ("recording" in session.videoElement) { + recordLocalVideo("stop"); + } + ele.dataset.state = "0"; + ele.style.backgroundColor = ""; + ele.innerHTML = ''; + + if (session.director){ + var elements = document.querySelectorAll('[data-action-type="recorder-local"][data-sid="' + session.streamID + '"]'); + if (elements[0]) { + elements[0].classList.remove("pressed"); elements[0].ariaPressed = "false"; + elements[0].innerHTML = ' Record'; + } + } + return false; + } +} + +function setupSensorData(pollrate = 30) { + session.sensors = {}; + session.sensors.data = {}; + + if (window.Accelerometer && session.sensorDataFilter.includes("acc")) { + session.sensors.data.acc = {}; + session.sensors.Accelerometer = new Accelerometer({ + frequency: pollrate + }); + session.sensors.Accelerometer.addEventListener('reading', e => { + session.sensors.data.acc.x = session.sensors.Accelerometer.x.toFixed(5); + session.sensors.data.acc.y = session.sensors.Accelerometer.y.toFixed(5); + session.sensors.data.acc.z = session.sensors.Accelerometer.z.toFixed(5); + session.sensors.data.acc.t = parseInt(Math.round(session.sensors.Accelerometer.timestamp)); + }); + session.sensors.Accelerometer.start(); + } + if (window.Gyroscope && session.sensorDataFilter.includes("gyro")) { + session.sensors.data.gyro = {}; + session.sensors.Gyroscope = new Gyroscope({ + frequency: pollrate + }); + session.sensors.Gyroscope.addEventListener('reading', e => { + session.sensors.data.gyro.x = session.sensors.Gyroscope.x.toFixed(5); + session.sensors.data.gyro.y = session.sensors.Gyroscope.y.toFixed(5); + session.sensors.data.gyro.z = session.sensors.Gyroscope.z.toFixed(5); + session.sensors.data.gyro.t = parseInt(Math.round(session.sensors.Gyroscope.timestamp)); + }); + session.sensors.Gyroscope.start(); + } + if (window.Magnetometer && session.sensorDataFilter.includes("mag")) { + session.sensors.data.mag = {}; + session.sensors.Magnetometer = new Magnetometer({ + frequency: pollrate + }); + session.sensors.Magnetometer.addEventListener('reading', e => { + session.sensors.data.mag.x = session.sensors.Magnetometer.x.toFixed(5); + session.sensors.data.mag.y = session.sensors.Magnetometer.y.toFixed(5); + session.sensors.data.mag.z = session.sensors.Magnetometer.z.toFixed(5); + session.sensors.data.mag.t = parseInt(Math.round(session.sensors.Magnetometer.timestamp)); + + }); + session.sensors.Magnetometer.start(); + session.sensors.deviceorientation = false; + } else if (session.sensorDataFilter.includes("ori")){ + try{ + window.addEventListener('deviceorientation', e => { + session.sensors.data.ori = {}; + try{ + session.sensors.data.ori.d = e.absolute; + } catch(event){} + session.sensors.data.ori.a = e.alpha.toFixed(5); + session.sensors.data.ori.b = e.beta.toFixed(5); + session.sensors.data.ori.g = e.gamma.toFixed(5); + session.sensors.data.ori.t = parseInt(Math.round(e.timestamp)) || Date.now(); + }); + session.sensors.deviceorientation = true; + } catch(e){ + session.sensors.deviceorientation = false; + } + } + if (window.LinearAccelerationSensor && session.sensorDataFilter.includes("lin")) { + session.sensors.data.lin = {}; + session.sensors.LinearAccelerationSensor = new LinearAccelerationSensor({ + frequency: pollrate + }); + session.sensors.LinearAccelerationSensor.addEventListener('reading', e => { + session.sensors.data.lin.x = session.sensors.LinearAccelerationSensor.x.toFixed(5); + session.sensors.data.lin.y = session.sensors.LinearAccelerationSensor.y.toFixed(5); + session.sensors.data.lin.z = session.sensors.LinearAccelerationSensor.z.toFixed(5); + session.sensors.data.lin.t = parseInt(Math.round(session.sensors.LinearAccelerationSensor.timestamp)); + }); + session.sensors.LinearAccelerationSensor.start(); + } + + if (navigator.geolocation && session.sensorDataFilter.includes("pos")){ + navigator.geolocation.watchPosition(function(pos){ + try { + session.sensors.data.pos = {}; + session.sensors.data.pos.speed = pos.coords.speed.toFixed(3); + session.sensors.data.pos.alt = pos.coords.altitude.toFixed(3); + session.sensors.data.pos.t = pos.timestamp; + }catch(e){} + }, errorlog, { + enableHighAccuracy: true, + timeout: 5000, + maximumAge: 0 + }); + } + + setInterval(function() { + session.sendMessage({sensors: session.sensors.data}); + }, parseInt(1000 / pollrate)); +} + + +async function recordLocalVideo(action = null, videoKbps = false, remote=false) { // event.currentTarget,this.parentNode.parentNode.dataset.UUID + + + if (session.record === false){warnlog("recordings are disabled by decree of thy host magistrate");} + + var audioKbps = false; + if (remote){ + var video = remote; + if (remote.id === "videosource"){ + remote = false; + } + } else { + var video = session.videoElement; + } + log(video.id); + + if (!video){return;} + + if ("recording" in video) { + if (action == "estop") { + video.recorder.eStop(); + log("EMERGENCY Stopping RECORDING!"); + video.recorder.stop(); + return; + } else if (action == "stop") { + log("Stopping RECORDING!"); + video.recorder.stop(); + return; + } else if (action == "start") { + log("ALREADY RECORDING!"); + if (remote){ + getById("recordLocalbutton").dataset.state = "1"; + getById("recordLocalbutton").style.backgroundColor = "red"; + getById("recordLocalbutton").innerHTML = ''; + } + return; + } else { + log("STOPPING RECORDING by default toggle!"); + video.recorder.stop(); + return; + } + return; + } else if (action == "start") { + if (!MediaRecorder) { + var msg = {}; + msg.recorder = -3; + for (var i = 0;i { + try { + video.recorder.writer.close(); + } catch(e){} + pokeIframeAPI("recording-stopped"); + if (!remote){ + try { + if (session.directorUUID) { + var msg = {}; + msg.recorder = -1; + for (var i = 0;i { + audioTrack = true; + stream.addTrack(track, video.srcObject); + }); + + if (!audioTrack){ + errorlog("Failing the recording; no audio track"); + try { + video.recorder.writer.close(); + } catch(e){} + try { + delete(video.recorder); + delete(video.recording); + } catch(e){} + var msg = {}; + msg.recorder = -3; + for (var i = 0;i 0) { + writer.write(event.data); + if (session.directorList.length) { + if (video.recording) { + var msg = {}; + msg.recorder = parseInt((Date.now() - timestamp) / 1000) || 0; + for (var i =0;i chunk.arrayBuffer().then(b => ctrl.enqueue(new Uint8Array(b))) + }); + var writer = writable.getWriter(); + readable.pipeTo(streamSaver.createWriteStream(filename.toString() + '.webm', video.recorder.stop)); + video.recorder.writer = writer; + + video.recorder.mediaRecorder.start(1000); // 100ms chunks + + pokeIframeAPI("recording-started"); + + getById("recordLocalbutton").dataset.state = "1"; + getById("recordLocalbutton").style.backgroundColor = "red"; + getById("recordLocalbutton").innerHTML = ''; + + if (session.directorList.length) { + var msg = {}; + + msg.recorder = 0; + for (var i =0;i{ + var UUID = target.dataset.UUID; + if (!UUID){return;} + var video = session.rpcs[UUID].videoElement; + if (!video){return;} + if (!video.stopWriter){ + recordVideo(target); // if not started, start + } + }); + recordLocalVideo("start"); // self +} +function localGlobalRecordStop(){ + document.querySelectorAll('[data-action-type=\'recorder-local\']').forEach(target=>{ + var UUID = target.dataset.UUID; + if (!UUID){return;} + var video = session.rpcs[UUID].videoElement; + if (!video){return;} + if (video.stopWriter){ + recordVideo(target); // if started, stop + } + }); + recordLocalVideo("stop"); // self +} +async function remoteGlobalRecordStart(){ + window.focus(); + var bitrate = await promptAlt(miscTranslations['what-bitrate'], false, false, 6000); + document.querySelectorAll('[data-action-type=\'recorder-remote\']').forEach(target=>{ + requestVideoRecord(target, true, bitrate); + }); +} +function remoteGlobalRecordStop(){ + document.querySelectorAll('[data-action-type=\'recorder-remote\']').forEach(target=>{ + if (target.classList.contains('pressed')){ + requestVideoRecord(target, false); + } + }); +} +session.onTrack = function(event, UUID){ + + if (session.badStreamList.includes(session.rpcs[UUID].streamID)){ + errorlog("new connection is contained in badStreamList 2! This shouldn't happen"); + // we will have none of this. + return; + } + + var newTracks = []; + var newStream = false; + if (event.streams && event.streams[0]){ + newStream = event.streams[0]; + newTracks = newStream.getTracks(); + } else if (event.track){ + newTracks.push(event.track); + } else { + errorlog("Something went wrong with incoming track.."); + return; + } + + if (session.rpcs[UUID].streamSrc){ + var tracks = session.rpcs[UUID].streamSrc.getTracks(); + newTracks.forEach(function(trk){ + tracks.forEach(function(trk2){ + if ((trk.id == trk2.id) && (trk.kind == trk2.kind)){ + var index = newTracks.indexOf(trk); + if (index > -1) { + newTracks.splice(index, 1); + } + } + }); + }); + } + + var screenshare = false; + if (session.rpcs[UUID].screenIndexes && session.rpcs[UUID].screenIndexes.length){ + log("session.rpcs[UUID].screenIndexes: " + session.rpcs[UUID].screenIndexes); + var receievers = session.rpcs[UUID].getReceivers(); // excluded + for (var i=0;i{ + if ((trk.id == e1.track.id) && (trk.kind == e1.track.kind)){ + session.rpcs[UUID].streamSrc.removeTrack(trk); + } + }); + if ( e1.track.kind=="video"){ + updateIncomingVideoElement(UUID, true, false); + } else { + updateIncomingVideoElement(UUID, false, true); + } + // updateIncomingVideoElement(UUID); // session.rpcs[UUID].videoElement.srcObject = session.rpcs[UUID].streamSrc; + setTimeout(function(){updateMixer();},1); + } catch(e){} + }; + + newStream.onerror = function(e1){ + errorlog(e1); + try{ + warnlog("Track threw an error; going to reconnect it"); + session.rpcs[UUID].streamSrc.getTracks().forEach((trk)=>{ + try{ + if ((trk.id == e1.track.id) && (trk.kind == e1.track.kind)){ + session.rpcs[UUID].streamSrc.removeTrack(trk); + } + } catch(e){} + }); + if ( e1.track.kind=="video"){ + updateIncomingVideoElement(UUID, true, false); + } else { + updateIncomingVideoElement(UUID, false, true); + } + setTimeout(function(){updateMixer();},1); + } catch(e){errorlog(e);} + }; + } + + createRichVideoElement(UUID); + + if (!session.rpcs[UUID].streamSrc) { + session.rpcs[UUID].streamSrc = createMediaStream(); + mediaSourceUpdated(UUID, session.rpcs[UUID].streamID); + } + + + var videoAdded=false; + var audioAdded=false; + + newTracks.forEach((trk)=>{ + if (trk.kind=="video"){ + videoAdded=true; + } else if (trk.kind=="audio"){ + audioAdded=true; + } + log("adding track"); + session.rpcs[UUID].streamSrc.addTrack(trk); + }); + + if (newTracks.length > session.rpcs[UUID].streamSrc.getTracks().length){ + errorlog("Not all the tracks were added to the local stream; are the tracks' IDs not unique?"); + console.log("streamSrc total tracks: "+session.rpcs[UUID].streamSrc.getTracks().length); + } + + if (isIFrame && session.sendframes){ + newTracks.forEach((trk)=>{ + if (trk.kind==="video"){ + log("STARTING NEW VIDEO TRACK"); + + trk.frameReader = new MediaStreamTrackProcessor(trk).readable.getReader(); + trk.frameReader.read().then(function processFrame2({done, value}) { + if (done) { + if (value){ + value.close(); + } + return; + } + try { + parent.postMessage({"frame":value, UUID:UUID, streamID:session.rpcs[UUID].streamID, trackID: trk.id, kind: "video"}, session.sendframes, [value]); + } catch(e){ + value.close(); + return; + } + value.close(); + trk.frameReader.read().then(processFrame2); + + }); + } else if (trk.kind==="audio"){ + log("STARTING NEW AUDIO TRACK"); + + trk.frameReader = new MediaStreamTrackProcessor(trk).readable.getReader(); + trk.frameReader.read().then(function processFrameAudio2({done, value}) { + if (done) { + if (value){ + value.close(); + } + return; + } + try { + parent.postMessage({"frame":value, UUID:UUID, streamID:session.rpcs[UUID].streamID, trackID: trk.id, kind: "audio"}, session.sendframes, [new ArrayBuffer(value)]); + } catch(e){ + value.close(); + return; + } + value.close(); + trk.frameReader.read().then(processFrameAudio2); + + }); + } + }); + } + + + + if (audioAdded && videoAdded){ + updateIncomingVideoElement(UUID); + } else if (videoAdded){ + updateIncomingVideoElement(UUID, true, false); + } else if (audioAdded){ + try { + if (session.audioCodec == "lyra"){ // not supported currently + lyraDecode(event.receiver); + } + } catch(e){errorlog(e);} + updateIncomingVideoElement(UUID, false, true); + if (!session.roomid && session.view && !session.permaid){ + setTimeout(function(){updateMixer();},10); // video already has an auto-start, with aspect ratio size change. audio doesn't. + } + } + + return session; + +}; + + +function updateIncomingVideoElement(UUID, video=true, audio=true){ + + if (!session.rpcs[UUID].videoElement){ + return;} + if (!session.rpcs[UUID].streamSrc){ + return;} + + if (!session.rpcs[UUID].videoElement.srcObject) { + session.rpcs[UUID].videoElement.srcObject = createMediaStream(); + } + + if (video){ + var tracks = session.rpcs[UUID].videoElement.srcObject.getVideoTracks(); // add video track + + session.rpcs[UUID].streamSrc.getVideoTracks().forEach((trk)=>{ + var added = false; + tracks.forEach(trk2 =>{ + if ((trk.id == trk2.id) && (trk.kind == trk2.kind)){ + added=true; + } + }); + if (!added){ + session.rpcs[UUID].videoElement.srcObject.getVideoTracks().forEach((trk2)=>{ // make sure only one video track is added at a time. + session.rpcs[UUID].videoElement.srcObject.removeTrack(trk2); + }); + + if (trk.muted && (trk.kind=="video") && session.director){ + trk.onunmute = function(e){ + if (!session.rpcs[UUID]){return;} + this.onunmute = null; + warnlog("ON UN-MUTE"); + updateIncomingVideoElement(UUID, true, false); + }; + } else { + if (session.rpcs[UUID].videoElement.controls){ + session.rpcs[UUID].videoElement.controls = session.showControls || false; + if (session.showControls===null){ + setTimeout(function(ele){ + if (ele){ + ele.controls = true; + } + },500, session.rpcs[UUID].videoElement); + } + } + session.rpcs[UUID].videoElement.srcObject.addTrack(trk); + mediaVideoTrackUpdated(UUID, session.rpcs[UUID].streamID); + } + } + }); + + if (session.motionSwitch && !session.rpcs[UUID].motionDetectionInterval){ + session.rpcs[UUID].motionDetectionInterval = setTimeout(function(){ + setInterval(function(){ + motionDetection(session.rpcs[UUID].videoElement, session.motionSwitch); + },400); + },2000); + } + + } + if (audio){ + updateIncomingAudioElement(UUID) // do the same for audio now. + } +} + +function updateIncomingAudioElement(UUID){ // this can be called when turning on/off inbound audio processing. + if (!session.rpcs[UUID] || !session.rpcs[UUID].videoElement || !session.rpcs[UUID].streamSrc){return;} + + if (!session.rpcs[UUID].videoElement.srcObject) { + session.rpcs[UUID].videoElement.srcObject = createMediaStream(); + } + + log("updateIncomingAudioElement: "+UUID); + if ((session.audioEffects===true) || session.pushLoudness){ + var tracks = session.rpcs[UUID].streamSrc.getAudioTracks(); + if (tracks.length){ + var track = tracks[0]; + track = addAudioPipeline(UUID, track); + log(track); + var added = false; + var tracks2 = session.rpcs[UUID].videoElement.srcObject.getAudioTracks(); + log(tracks2); + tracks2.forEach(trk2 =>{ + if (trk2.label && (trk2.label == "MediaStreamAudioDestinationNode")){ // an old morphed node; delete it. + session.rpcs[UUID].videoElement.srcObject.removeTrack(trk2); + } else if ((track.id == trk2.id) && (track.kind == trk2.kind)){ // maybe it didn't morph; already added either way + added = true; + } else if ((tracks[0].id == trk2.id) && (tracks[0].kind == trk2.kind) && (track.id != tracks[0].id)){ // remove original audio track that is now morphed + session.rpcs[UUID].videoElement.srcObject.removeTrack(trk2); + } + }); + if (!added){ + session.rpcs[UUID].videoElement.srcObject.addTrack(track); + mediaAudioTrackUpdated(UUID, session.rpcs[UUID].streamID); + } + + } else { + session.rpcs[UUID].videoElement.srcObject.getAudioTracks().forEach(trk=>{ // make sure to remove all tracks. + session.rpcs[UUID].videoElement.srcObject.remove(trk); + }); + } + } else { + var expected = []; + tracks = session.rpcs[UUID].videoElement.srcObject.getAudioTracks(); // add audio tracks + session.rpcs[UUID].streamSrc.getAudioTracks().forEach((trk)=>{ + var added = false; + tracks.forEach(trk2 =>{ + if ((trk.id == trk2.id) && (trk.kind == trk2.kind)){ + added=true; + expected.push(trk2); // + } + }); + if (!added){ + session.rpcs[UUID].videoElement.srcObject.addTrack(trk); + mediaAudioTrackUpdated(UUID, session.rpcs[UUID].streamID); + } + }); + tracks.forEach((trk)=>{ + var added = false; + expected.forEach((trk2)=>{ + if ((trk.id == trk2.id) && (trk.kind == trk2.kind)){ + added=true; + } + }); + if (!added){ // not expected. so lets delete. + warnlog("this shouldn't happen that often, audio track orphaned. removing it"); + session.rpcs[UUID].videoElement.srcObject.removeTrack(trk); + } + }); + } + + + if (session.mixMinus){ + stream = mixMinusAudio(UUID); // only works with p2p; no chunked mode. + } + +} + + +function cycleStyleOptions(){ + session.style +=1; + if (session.style >6 ){ + session.style = 1; + } else if (session.style == 4 ){ + session.style = 5; + } + + for (var UUID in session.rpcs){ + if (session.rpcs[UUID].canvas){ + try{ + if (session.rpcs[UUID].canvas){ + session.rpcs[UUID].canvas.remove(); + } + } catch(e){} + session.rpcs[UUID].canvas = null; + } + updateIncomingAudioElement(UUID); + } + updateMixer(); +} + +function addAudioPipeline(UUID, track){ // INBOUND AUDIO EFFECTS ; audio tracks only + try{ + + if (session.disableViewerWebAudioPipeline){ + log("ignoring addAudioPipeline - disableViewerWebAudioPipeline is enabled (noap)"); + return track; + } + + log("Triggered webaudio effects path"); + + for (var tid in session.rpcs[UUID].inboundAudioPipeline){ + delete session.rpcs[UUID].inboundAudioPipeline[tid]; // get rid of old nodes. + } + var trackid = track.id; // this is an audio track, or should be. + + session.rpcs[UUID].inboundAudioPipeline[trackid] = {}; + + session.rpcs[UUID].inboundAudioPipeline[trackid].mediaStream = createMediaStream(); + session.rpcs[UUID].inboundAudioPipeline[trackid].mediaStream.addTrack(track); + + if (ChromiumVersion && session.audioEffects){ // I'm going to deprecate this. + session.rpcs[UUID].inboundAudioPipeline[trackid].mutedAudio = createAudioElement(); // TODO: I don't know if this mutedAudio thing matters any more, in recent versions of Chrome, since it won't play even if muted. + session.rpcs[UUID].inboundAudioPipeline[trackid].mutedAudio.muted = true; + session.rpcs[UUID].inboundAudioPipeline[trackid].mutedAudio.playsinline = true; // ## Added Oct 9th 2022. Not sure it's does anything, but might help with iPhones? + session.rpcs[UUID].inboundAudioPipeline[trackid].mutedAudio.srcObject = session.rpcs[UUID].inboundAudioPipeline[trackid].mediaStream; // needs to be added as an streamed element to be usable, even if its hidden + session.rpcs[UUID].inboundAudioPipeline[trackid].mutedAudio.muted = true; + //session.rpcs[UUID].inboundAudioPipeline[trackid].mutedAudio.volume = 0.01; + session.rpcs[UUID].inboundAudioPipeline[trackid].mutedAudio.play().then(_ => { + //session.rpcs[UUID].inboundAudioPipeline[trackid].mutedAudio.muted = false; + log("playing 1"); + }).catch(warnlog); + } + + // https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/createMediaStreamTrackSource + var source = session.audioCtx.createMediaStreamSource(session.rpcs[UUID].inboundAudioPipeline[trackid].mediaStream); + + ////////////////// + + var screwedUp = false; + session.rpcs[UUID].inboundAudioPipeline[trackid].destination = false; + + if (session.sync!==false){ + log("adding a delay node to audio"); + source = addDelayNode( source, UUID, trackid); + screwedUp = true; + } + if (session.style===2){ + log("adding a fftwave node to audio"); + try { + if (session.rpcs[UUID].inboundAudioPipeline[trackid]){ // clear audioMeterGuest, if active. + clearTimeout(session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.interval); + } + } catch(e){ } + source = fftWaveform( source, UUID, trackid); + } else if (session.style===3 || session.meterStyle){ + log("adding a loudness meter node to audio"); + source = audioMeterGuest(source, UUID, trackid); + } else if (session.audioMeterGuest){ + log("adding a loudness meter node to audio"); + source = audioMeterGuest(source, UUID, trackid); + } else if (session.activeSpeaker){ + log("adding a loudness meter node to audio"); + source = audioMeterGuest(source, UUID, trackid); + } else if (session.quietOthers){ + log("adding a loudness meter node to audio"); + source = audioMeterGuest(source, UUID, trackid); + } else if (session.pushLoudness){ + source = audioMeterGuest(source, UUID, trackid); + } else { + try { + if (session.rpcs[UUID].inboundAudioPipeline[trackid]){ // nothign active, so clear + clearTimeout(session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.interval); + } + } catch(e){ } + } + + if (session.playChannel){ + session.rpcs[UUID].inboundAudioPipeline[trackid].destination = session.audioCtx.createMediaStreamDestination(); + source = selectChannel( session.rpcs[UUID].inboundAudioPipeline[trackid].destination, source, session.playChannel); + screwedUp = true; + } else if (session.rpcs[UUID].channelOffset !== false){ + log("custom offset set"); + session.rpcs[UUID].inboundAudioPipeline[trackid].destination = session.audioCtx.createMediaStreamDestination(); + source = offsetChannel( session.rpcs[UUID].inboundAudioPipeline[trackid].destination, source, session.rpcs[UUID].channelOffset, session.rpcs[UUID].channelWidth); + screwedUp = true; + } else if (session.offsetChannel !== false){ // proably better to do this last. + log("adding offset channels"); + session.rpcs[UUID].inboundAudioPipeline[trackid].destination = session.audioCtx.createMediaStreamDestination(); + source = offsetChannel( session.rpcs[UUID].inboundAudioPipeline[trackid].destination, source, session.offsetChannel, session.channelWidth); + screwedUp = true; + } else if (session.panning !== false){ // proably better to do this last. + log("adding offset channels"); + session.rpcs[UUID].inboundAudioPipeline[trackid].destination = session.audioCtx.createMediaStreamDestination(); + source = stereoPanning( source, UUID, trackid, session.panning); + screwedUp = true; + } else if (session.rpcs[UUID].videoElement && session.rpcs[UUID].videoElement.manualSink){ + screwedUp = true; // added June-3-22 to allow for custom outputs to different audio output destinations. + } + + if (screwedUp){ + warnlog("screwedUp mode activated. dun dun"); + if (session.rpcs[UUID].inboundAudioPipeline[trackid].destination===false){ + session.rpcs[UUID].inboundAudioPipeline[trackid].destination = session.audioCtx.createMediaStreamDestination(); + } + source.connect(session.rpcs[UUID].inboundAudioPipeline[trackid].destination); + + try { + if (session.firstPlayTriggered && (session.audioCtx.state == "suspended")){ + log("trying to resume.."); + session.audioCtx.resume(); + } + } catch(e){warnlog("session.audioCtx.resume(); failed");} + + return session.rpcs[UUID].inboundAudioPipeline[trackid].destination.stream.getAudioTracks()[0]; + } + + try { + if (session.firstPlayTriggered && (session.audioCtx.state == "suspended")){ + session.audioCtx.resume(); + } + } catch(e){warnlog("session.audioCtx.resume(); failed 2");} + + return track; + } catch(e) {errorlog(e);} + return track; +} + +function processMiniInfoUpdate(miniInfo, UUID){ + if ("qlr" in miniInfo){ + session.rpcs[UUID].stats.info.quality_limitation_reason = miniInfo.qlr; + } + + if ("con" in miniInfo){ + session.rpcs[UUID].stats.info.conn_type = miniInfo.con; + } + + if ("cpu" in miniInfo){ + session.rpcs[UUID].stats.info.cpuLimited = miniInfo.cpu; + if (session.rpcs[UUID].signalMeter){ + if (miniInfo.cpu){ + session.rpcs[UUID].signalMeter.dataset.cpu = "1"; + } else if ("cpu" in miniInfo){ + session.rpcs[UUID].signalMeter.dataset.cpu = "0"; + } + } + } + + if ("hw_enc" in miniInfo){ + session.rpcs[UUID].stats.info.hardware_video_encoder = miniInfo.hw_enc; + } + + if ("bat" in miniInfo){ + if (typeof miniInfo.bat == "number"){ + session.rpcs[UUID].stats.info.power_level = miniInfo.bat*100; + } else { + session.rpcs[UUID].stats.info.power_level = null; + } + } + if ("chrg" in miniInfo){ + session.rpcs[UUID].stats.info.plugged_in = miniInfo.chrg; + } + + if (("out" in miniInfo) && ("c" in miniInfo.out)){ + session.rpcs[UUID].stats.info.total_outbound_p2p_connections = miniInfo.out.c; + if (session.showConnections && session.rpcs[UUID].connectionDetails){ + session.rpcs[UUID].connectionDetails.innerText = "🔗"+session.rpcs[UUID].stats.info.total_outbound_p2p_connections; + session.rpcs[UUID].connectionDetails.dataset.value = session.rpcs[UUID].stats.info.total_outbound_p2p_connections; + } + } + + if (session.rpcs[UUID].batteryMeter){ + batteryMeterInfoUpdate(UUID); + } +} + + +function batteryMeterInfoUpdate(UUID){ + if (session.rpcs[UUID].stats.info && (session.rpcs[UUID].stats.info.power_level!==null)){ + var level = session.rpcs[UUID].batteryMeter.querySelector(".battery-level"); + if (level){ + var value = session.rpcs[UUID].stats.info.power_level; + if (value > 100){value = 100;} + if (value < 0){ value = 0;} + level.style.height = parseInt(value)+"%"; + if (value<15){ + session.rpcs[UUID].batteryMeter.classList.remove("warn"); + session.rpcs[UUID].batteryMeter.classList.add("alert"); + } else if (value<25){ + session.rpcs[UUID].batteryMeter.classList.remove("alert"); + session.rpcs[UUID].batteryMeter.classList.add("warn"); + } else { + session.rpcs[UUID].batteryMeter.classList.remove("alert"); + session.rpcs[UUID].batteryMeter.classList.remove("warn"); + } + if (value<100){ + session.rpcs[UUID].batteryMeter.classList.remove("hidden"); + } + //session.rpcs[UUID].batteryMeter.title = value+"% battery remaining"; + session.rpcs[UUID].batteryMeter.title = parseInt(value)+"% battery remaining"; + } + } + + if (session.rpcs[UUID].stats.info && ("plugged_in" in session.rpcs[UUID].stats.info) && (session.rpcs[UUID].stats.info.plugged_in===false)){ + session.rpcs[UUID].batteryMeter.dataset.plugged = "0"; + session.rpcs[UUID].batteryMeter.classList.remove("hidden"); + } else { + session.rpcs[UUID].batteryMeter.dataset.plugged = "1"; + // add on + session.rpcs[UUID].batteryMeter.title = parseInt(value)+"% charging"; + session.rpcs[UUID].batteryMeter.classList.add("hidden"); + } +} + +function setupGuestLabelControl(UUID){ + var labelID = getById("label_"+UUID); + if (labelID){ + labelID.classList.add("contolboxLabel"); + labelID.dataset.UUID = UUID; + if (session.rpcs[UUID].label){ + labelID.innerText = session.rpcs[UUID].label; // Replace underscores with a Space when publishing to HTML. No Double spaces. + labelID.classList.remove("addALabel"); + } else if (session.directorUUID === UUID){ + miniTranslate(labelID,"main-director"); + //labelID.innerHTML = getTranslation("main-director"); + labelID.classList.remove("addALabel"); + } else { + miniTranslate(labelID,"add-a-label"); + //labelID.innerHTML = getTranslation("add-a-label"); // Replace underscores with a Space when publishing to HTML. No Double spaces. + labelID.classList.add("addALabel"); + } + labelID.onclick = async function(ee){ + var oldlabel = ee.target.innerText; + if (session.rpcs[ee.target.dataset.UUID].label===false){ + oldlabel = ""; + } + window.focus(); + var newlabel = await promptAlt(getTranslation("new-display-name"), false, false, oldlabel); + if (newlabel!==null){ + newlabel = newlabel.trim(); + if (newlabel == ""){ + newlabel = false; + if (session.directorUUID === UUID){ + miniTranslate(ee.target,"main-director"); + //ee.target.innerHTML = getTranslation("main-director"); + ee.target.classList.remove("addALabel"); + } else { + miniTranslate(ee.target,"add-a-label"); + //ee.target.innerHTML = getTranslation("add-a-label"); + ee.target.classList.add("addALabel"); + } + } else { + ee.target.innerText = newlabel; + ee.target.classList.remove("addALabel"); + } + var data = {}; + data.UUID = ee.target.dataset.UUID; + data.changeLabel = true; + data.value = newlabel; + session.sendRequest(data, data.UUID); + } + } + } +} + +function updateLabelDirectors(UUID){ + var elements = getById("label_"+UUID); + if (session.rpcs[UUID].label){ + elements.innerText = session.rpcs[UUID].label; + elements.classList.remove("addALabel"); + } else if (session.directorUUID === UUID){ + //elements.innerHTML = getTranslation("main-director"); + miniTranslate(elements,"main-director"); + elements.classList.remove("addALabel"); + } else { + //elements.innerHTML = getTranslation("add-a-label"); + miniTranslate(elements,"add-a-label"); + elements.classList.add("addALabel"); + } +} +function updateLabelDirectors2(UUID){ + var elements = getById("label_"+UUID); + if (session.directorUUID === UUID){ + //elements.innerHTML = getTranslation("main-director"); + miniTranslate(elements,"main-director"); + elements.classList.remove("addALabel"); + } else { + //elements.innerHTML = getTranslation("add-a-label"); + miniTranslate(elements,"add-a-label"); + elements.classList.add("addALabel"); + } +} + +function directorCoDirectorColoring(UUID){ + if (UUID === session.directorUUID){ + try{ + session.rpcs[UUID].stats.info.director = true; + getById("container_"+UUID).classList.add("directorBox"); + } catch(e){} + } else if (session.directorList.indexOf(UUID)>=0){ + try{ + session.rpcs[UUID].stats.info.coDirector = true; + addDirectorBlue(UUID); + + } catch(e){} + } +} + +function addDirectorBlue(UUID){ + getById("container_"+UUID).classList.add("directorBlue"); +} + +function soloLinkGeneratorInit(UUID){ + document.querySelectorAll("container_"+UUID).forEach(ele=>{ + ele.querySelectorAll("[data-sololink]").forEach(ele2=>{ // value='" + soloLink + "' href='" + soloLink + "'/>" + soloLink + " + var soloLink = soloLinkGenerator(session.rpcs[UUID].streamID, false); + ele2.value = soloLink; + ele2.href = soloLink; + ele2.innerText = soloLink; + }); + }); +} + +function initRecordingImpossible(UUID){ + var ele = document.querySelectorAll('[data-action-type="mute-guest"][data--u-u-i-d="'+UUID+'"]'); + if (ele){ + ele.disabled = true; + ele.title = getTranslation("Audio processing is disabled with this guest. Can't mute or change volume"); + } + var ele = document.querySelectorAll('[data-action-type="volume"][data--u-u-i-d="'+UUID+'"]'); + if (ele){ + ele.disabled = true; + ele.title = title = getTranslation("Audio processing is disabled with this guest. Can't mute or change volume"); + ele.style.opacity = 0.2; + } +} + +function initAudioButtons(audioGain, UUID){ + if (audioGain===0){ + var ele = document.querySelector('[data-action-type="mute-guest"][data--u-u-i-d="'+UUID+'"]'); + if (ele){ + ele.value = 1; + ele.classList.add("pressed"); ele.ariaPressed = "true"; + miniTranslate(ele.children[1],"unmute"); + session.rpcs[UUID].directorMutedState = 1; + } + pokeIframeAPI("director-mute-state", true, UUID); + } else { + var ele = document.querySelector('[data-action-type="volume"][data--u-u-i-d="'+UUID+'"]'); + if (ele){ + ele.value = audioGain; + session.rpcs[UUID].directorVolumeState = audioGain; + remoteVolumeUI(ele); + } + } +} + +function initGroupButtons(UUID){ + var elements = document.querySelectorAll('[data-action-type="toggle-group"][data--u-u-i-d="'+UUID+'"]'); + for (var i=0;i -1){ + session.group.splice(index, 1); + change=true; + } + } else if (ele.classList.contains("pressed")){ + ele.classList.remove("pressed"); ele.ariaPressed = "false"; + if (index > -1){ + session.group.splice(index, 1); + change=true; + } + } else { + ele.classList.add("pressed"); ele.ariaPressed = "true"; + if (index === -1){ + session.group.push(group); + change=true; + } + } + + if (session.group.length || session.allowNoGroup){ + session.sendMessage({"group":session.group.join(",")}); + } else { + session.sendMessage({"group":false}); + } + if (change){ + pokeIframeAPI("group-set-updated", session.group); + } + + if (session.group.indexOf(group)===-1){ + return false; + } else { + return true; + } +} + +function changeGroupDirectorAPI(group, state=null, update=true){ + log("changeGroupDirectorAPI()"); + group = sanitizeLabel(group); + + if (document.getElementById("container_director")){ + var ele = getById("container_director").querySelector('[data-action-type="toggle-group"][data-group="'+group+'"]'); + if (ele){ + if (update){ + ele.click(); + } else if (state===true){ + ele.classList.add("pressed"); ele.ariaPressed = "true"; + } + if (session.group.indexOf(group)===-1){ + return false; + } else { + return true; + } + } + } + + var index = session.group.indexOf(group); + + var eleGroup = getById("groups"); + eleGroup.classList.remove("hidden"); + var ele = eleGroup.querySelector('[data-action-type="toggle-group"][data-group="'+group+'"'); + + if (eleGroup.showDirector){ + if (!ele){ + ele = htmlToElement(''); + + var added = false; + eleGroup.querySelectorAll('[data-group]').forEach(ele2=>{ + log(ele2); + if (!added && ele2.dataset.group>group+""){ + ele2.parentNode.insertBefore(ele, ele2); + added = true; + } + }); + if (!added){ + eleGroup.appendChild(ele); + } + } + } else if (!ele){ + ele = document.createElement("div"); + ele.dataset.actionType = "toggle-group"; + ele.dataset.group = group; + ele.classList.add('float'); + ele.style.display = "inline-block"; + ele.role = "button"; + ele.innerHTML = '
    '+group; + eleGroup.appendChild(ele); + ele.onclick = function(){ + changeGroupDirectorAPI(this.dataset.group); + } + } + var changed = false; + + if (state===true){ + if (eleGroup.showDirector){ + ele.classList.add("pressed"); ele.ariaPressed = "true"; + } else { + ele.classList.add("green"); ele.ariaPressed = "true"; + } + if (index === -1){ + session.group.push(group); + changed=true; + } + } else if (state === false){ + if (eleGroup.showDirector){ + ele.classList.remove("pressed"); ele.ariaPressed = "false"; + } else { + ele.classList.remove("green"); ele.ariaPressed = "false"; + } + if (index > -1){ + session.group.splice(index, 1); + changed=true; + } + } else if (ele.classList.contains("green")){ + if (eleGroup.showDirector){ + ele.classList.remove("pressed"); ele.ariaPressed = "false"; + } else { + ele.classList.remove("green"); ele.ariaPressed = "false"; + } + if (index > -1){ + session.group.splice(index, 1); + changed=true; + } + } else { + if (eleGroup.showDirector){ + ele.classList.add("pressed"); ele.ariaPressed = "true"; + } else { + ele.classList.add("green"); ele.ariaPressed = "true"; + } + if (index === -1){ + session.group.push(group); + changed=true; + } + } + if (update){ + if (session.group.length || session.allowNoGroup){ + session.sendMessage({"group":session.group.join(",")}); + } else { + session.sendMessage({"group":false}); + } + } + if (changed){ + pokeIframeAPI("group-set-updated", session.group); + } + + if (state!==null){ + return true; + } else if (session.group.indexOf(group)===-1){ + return false; + } else { + return true; + } +} + +function changeGroupViewDirectorAPI(group, state=null){ + log("changeGroupViewDirectorAPI()"); + group = sanitizeLabel(group); + + var index = session.groupView.indexOf(group); + + var changed = false; + + if (state===true){ + if (index === -1){ + session.groupView.push(group); + changed=true; + } + } else if (state === false){ + if (index > -1){ + session.groupView.splice(index, 1); + changed=true; + } + } else { + if (index > -1){ + session.groupView.splice(index, 1); + } else { + session.groupView.push(group); + } + changed=true; + } + + + if (changed){ + pokeIframeAPI("group-view-set-updated", session.groupView); + } + + if (state!==null){ + return true; + } else if (session.groupView.indexOf(group)===-1){ + return false; + } else { + return true; + } +} + + +function changeGroup(ele, state=null){ + + var group = ele.dataset.group; + + var index = session.rpcs[ele.dataset.UUID].group.indexOf(group); + + if (state===true){ + ele.classList.add("pressed"); ele.ariaPressed = "true"; + if (index === -1){ + session.rpcs[ele.dataset.UUID].group.push(group); + } + } else if (state === false){ + ele.classList.remove("pressed"); ele.ariaPressed = "false"; + if (index > -1){ + session.rpcs[ele.dataset.UUID].group.splice(index, 1); + } + } else if (ele.classList.contains("pressed")){ + ele.classList.remove("pressed"); ele.ariaPressed = "false"; + if (index > -1){ + session.rpcs[ele.dataset.UUID].group.splice(index, 1); + } + } else { + ele.classList.add("pressed"); ele.ariaPressed = "true"; + if (index === -1){ + session.rpcs[ele.dataset.UUID].group.push(group); + } + } + if (session.rpcs[ele.dataset.UUID].group.length){ + session.sendRequest({"group":session.rpcs[ele.dataset.UUID].group.join(",")}, ele.dataset.UUID); + } else { + session.sendRequest({"group":false}, ele.dataset.UUID); + } + syncDirectorState(ele); + + if (session.rpcs[ele.dataset.UUID].group.indexOf(group)===-1){ + return false; + } else { + return true; + } +} + +function changeChannelOffset(UUID, channel){ + var ele = document.querySelectorAll('[data-action-type="add-channel"][data--u-u-i-d="' + UUID + '"]'); + for (var i=0;i1){value=1;} + } + //// some reverb logic goes here... + ///var reverbNode = session.audioCtx.createStereoPanner(); + ///session.rpcs[UUID].inboundAudioPipeline[trackid].reverbNode = reverbNode; + //// + + source.connect(reverbNode); + return reverbNode; +} + +function stereoPanning(source, UUID, trackid, value){ + if (parseInt(value) === -1){ + value = Math.random() * (Math.random()*2-1); + warnlog(value); + } else if (value === false){ + return source; + } else if (value === true){ + value = 90; + } else { + value = parseFloat(value/90) -1 || 0; + if (value<-1){value=-1;} + if (value>1){value=1;} + } + + var gainNode = session.audioCtx.createGain(); + session.rpcs[UUID].inboundAudioPipeline[trackid].gainPanNode = gainNode; + gainNode.value = (1-Math.abs(value)/2); // the stereo panner seems to make things extra loud, so they clip. REDUCE IT. + source.connect(gainNode); + + var panNode = session.audioCtx.createStereoPanner(); + session.rpcs[UUID].inboundAudioPipeline[trackid].panNode = panNode; + panNode.pan.value = value; + gainNode.connect(panNode); + return panNode; +} + +function adjustPan(UUID, value){ + + if (value === true){ + value = Math.random() * (Math.random()*2-1); + } else if (value === false){ + value=0; + } else { + value = parseFloat(value/90) -1 || 0; + if (value<-1){value=-1;} + if (value>1){value=1;} + } + + for (var trackid in session.rpcs[UUID].inboundAudioPipeline){ + if ("panNode" in session.rpcs[UUID].inboundAudioPipeline[trackid]){ + session.rpcs[UUID].inboundAudioPipeline[trackid].panNode.pan.setValueAtTime(value, session.audioCtx.currentTime); + } + if ("gainPanNode" in session.rpcs[UUID].inboundAudioPipeline[trackid]){ + session.rpcs[UUID].inboundAudioPipeline[trackid].gainPanNode.setValueAtTime((1-Math.abs(value)/2), session.audioCtx.currentTime); + } + } +} + +function addDelayNode(source, UUID, trackid){ // append the delay Node to the track??? WOULD THIS WORK? + + var delay = parseFloat(session.sync) || 0; + if (delay<0){delay=0;} + + if (session.buffer && (session.buffer>0)){ + delay += parseFloat(session.buffer); + } + + delay = delay/1000; + + session.rpcs[UUID].inboundAudioPipeline[trackid].delayNode = session.audioCtx.createDelay(delay+5);// 5 seconds additionally added for the purpose of flexibility + + session.rpcs[UUID].inboundAudioPipeline[trackid].delayNode.delayTime.value = delay; // delayTime takes it in seconds. + source.connect(session.rpcs[UUID].inboundAudioPipeline[trackid].delayNode); + log("added new delay node"); + return session.rpcs[UUID].inboundAudioPipeline[trackid].delayNode; +} + + +function createStyleCanvas(UUID){ // append the delay Node to the track??? WOULD THIS WORK? + if (!session.rpcs[UUID].canvas){ // just make sure that if using &effects or something, to null the canvas after use, else this won't trigger. + session.rpcs[UUID].canvas = document.createElement("canvas"); + session.rpcs[UUID].canvas.dataset.UUID = UUID + if (session.rpcs[UUID].streamID){ + session.rpcs[UUID].canvas.dataset.sid = session.rpcs[UUID].streamID; + } + session.rpcs[UUID].canvas.style.pointerEvents = "auto"; + session.rpcs[UUID].canvasCtx = session.rpcs[UUID].canvas.getContext('2d', { alpha: session.alpha }); + // + session.rpcs[UUID].canvas.addEventListener('click', function(e) { // show stats of video if double clicked + log("clicked"); + try { + var uid = e.currentTarget.dataset.UUID; + if ((e.ctrlKey)||(e.metaKey)){ + e.preventDefault(); + if (session.statsMenu !==false){ + if ("stats" in session.rpcs[uid]){ + var [menu, innerMenu] = statsMenuCreator(); + printViewStats(innerMenu, uid ); + menu.interval = setInterval(printViewStats, session.statsInterval, innerMenu, uid); + } + } + e.stopPropagation(); + return false; + } else if ("prePausedBandwidth" in session.rpcs[uid]){ + unPauseVideo(e.currentTarget); + } + + } catch(e){errorlog(e);} + }); + + if (session.statsMenu){ + if ("stats" in session.rpcs[UUID]){ + var [menu, innerMenu] = statsMenuCreator(); + printViewStats(innerMenu, UUID ); + menu.interval = setInterval(printViewStats, session.statsInterval, innerMenu, UUID); + } + } + + if (session.aspectRatio){ + if (session.aspectRatio==1){ + session.rpcs[UUID].canvas.width="720"; + session.rpcs[UUID].canvas.height="1280"; + } else if (session.aspectRatio==2){ + session.rpcs[UUID].canvas.width="960"; + session.rpcs[UUID].canvas.height="960"; + } else if (session.aspectRatio==3){ + session.rpcs[UUID].canvas.width="1280"; + session.rpcs[UUID].canvas.height="960"; + } + } else { + session.rpcs[UUID].canvas.width="1280"; + session.rpcs[UUID].canvas.height="720"; + } + + updateMixer(); + return true; + } else { + return false; + } +} + + +function applyStyleEffect(UUID){ + if (!session.rpcs[UUID].canvas || !session.rpcs[UUID].canvasCtx){return;} + + /* session.rpcs[UUID].canvasContainer = document.createElement("div"); + session.rpcs[UUID].canvasContainer.appendChild(session.rpcs[UUID].canvas); + session.rpcs[UUID].canvas.style = "width:100%;height:100%;display:block;"; + session.rpcs[UUID].canvasContainer.appendChild(session.rpcs[UUID].videoElement); */ + + if (session.style==3){ // black + session.rpcs[UUID].canvasCtx.fillStyle = "rgb(0, 0, 0)"; + session.rpcs[UUID].canvasCtx.fillRect(0, 0, session.rpcs[UUID].canvas.width, session.rpcs[UUID].canvas.height); + } else if (session.style==4){ + session.rpcs[UUID].canvasCtx.fillStyle = "rgb(0, 0, 0)"; + session.rpcs[UUID].canvasCtx.fillRect(0, 0, session.rpcs[UUID].canvas.width, session.rpcs[UUID].canvas.height); + } else if (session.style==5){ + var r = Math.random()*255; + var g = Math.random()*255; + var b = Math.random()*255; + session.rpcs[UUID].canvasCtx.fillStyle = "rgb("+r+", "+g+", "+b+")"; + session.rpcs[UUID].canvasCtx.fillRect(0, 0, session.rpcs[UUID].canvas.width, session.rpcs[UUID].canvas.height); + } else if (session.style==6){ + + session.rpcs[UUID].canvasCtx.fillStyle = "rgb(0,0,0)"; + session.rpcs[UUID].canvasCtx.fillRect(0, 0, session.rpcs[UUID].canvas.width, session.rpcs[UUID].canvas.height); + + var r = Math.random()*150+50; + var g = Math.random()*150+50; + var b = Math.random()*150+50; + session.rpcs[UUID].canvasCtx.fillStyle = "rgb("+r+", "+g+", "+b+")"; + session.rpcs[UUID].canvasCtx.beginPath(); + session.rpcs[UUID].canvasCtx.arc(parseInt(session.rpcs[UUID].canvas.width/2), parseInt(session.rpcs[UUID].canvas.height/2), parseInt(session.rpcs[UUID].canvas.height/4), 0, 2 * Math.PI, false); + session.rpcs[UUID].canvasCtx.fill(); + + if (session.rpcs[UUID].label){ + session.rpcs[UUID].canvasCtx.fillStyle = "rgb(0,0,0)"; + session.rpcs[UUID].canvasCtx.textAlign = "center"; + session.rpcs[UUID].canvasCtx.font = parseInt(session.rpcs[UUID].canvas.height/2.11)+"px Arial"; + session.rpcs[UUID].canvasCtx.fillText(session.rpcs[UUID].label[0].toUpperCase(), parseInt(session.rpcs[UUID].canvas.width/2), parseInt(session.rpcs[UUID].canvas.height*2/3)); + } else { + var tmp = getComputedStyle(document.querySelector(':root')).getPropertyValue('--video-background-image').split('"'); + if (tmp.length===3){ + var img = new Image(); + img.onload = function() { + session.rpcs[UUID].canvasCtx.fillStyle = "rgb(25,0,0)"; + session.rpcs[UUID].canvasCtx.drawImage(img, parseInt(session.rpcs[UUID].canvas.width/2-110), parseInt(session.rpcs[UUID].canvas.height/2-110),220,220); + } + img.src = tmp[1]; + } + } + } +} + +function capitalizeFirstLetter(string) { + return string.charAt(0).toUpperCase() + string.slice(1); +} + +function fftWaveform( source, UUID, trackid){ // append the delay Node to the track??? WOULD THIS WORK? + // https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode + session.rpcs[UUID].inboundAudioPipeline[trackid].analyser = session.audioCtx.createAnalyser(); + session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.fftSize = 512; + var bufferLength = session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.frequencyBinCount; + var dataArray = new Uint8Array(bufferLength); + session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.getByteTimeDomainData(dataArray); + // analyser.getByteTimeDomainData(dataArray); + source.connect(session.rpcs[UUID].inboundAudioPipeline[trackid].analyser); + + createStyleCanvas(UUID); + clearInterval(session.rpcs[UUID].canvasIntervalAction); + var canvasIntervalAction = setInterval(function(uuid){ + + if (session.style!==2){ + clearInterval(canvasIntervalAction); // this is FFT only, so okay to kill. + return; + } + + try{ + session.rpcs[uuid].inboundAudioPipeline[trackid].analyser.getByteTimeDomainData(dataArray); + session.rpcs[uuid].canvasCtx.fillStyle = "rgba(0, 0, 0, 0.1)"; + session.rpcs[uuid].canvasCtx.fillRect(0, 0, session.rpcs[uuid].canvas.width, session.rpcs[uuid].canvas.height); + session.rpcs[uuid].canvasCtx.lineWidth = 10; + session.rpcs[uuid].canvasCtx.strokeStyle = "rgb(111, 255, 111)"; + + var sliceWidth = session.rpcs[uuid].canvas.width * 1.0 / bufferLength; + + var loudness = dataArray; + var Squares = loudness.map((val) => ((val-128.0)*(val-128.0))); + var Sum = Squares.reduce((acum, val) => (acum + val)); + var Mean = Sum/loudness.length; + loudness = Math.sqrt(Mean)*10; + session.rpcs[uuid].stats.Audio_Loudness = parseInt(loudness); + + if (session.pushLoudness==true){ + var loudnessObj = {}; + loudnessObj[session.rpcs[uuid].streamID] = session.rpcs[uuid].stats.Audio_Loudness; + + if (isIFrame){ + parent.postMessage({"loudness": loudnessObj, "action":"loudness", "value":loudness, "UUID":uuid}, session.iframetarget); + } + } + + if (loudness<2){return;} + + //log(bufferLength); + session.rpcs[uuid].canvasCtx.beginPath(); + var m = session.rpcs[uuid].canvas.height / 256.0; + session.rpcs[uuid].canvasCtx.moveTo(0, dataArray[0]*m); + var x = 0; + for (var i = 1; i < bufferLength; i++){ + var y = dataArray[i] * m; + session.rpcs[uuid].canvasCtx.lineTo(x, y); + x += sliceWidth; + } + session.rpcs[uuid].canvasCtx.lineTo(session.rpcs[uuid].canvas.width, session.rpcs[uuid].canvas.height / 2); + session.rpcs[uuid].canvasCtx.stroke(); + } catch(e){ + warnlog(e); + warnlog("Did the remote source disconnect?"); + clearInterval(canvasIntervalAction); + warnlog(session.rpcs[uuid]); + } + },50, UUID); + session.rpcs[UUID].canvasIntervalAction = canvasIntervalAction; + return session.rpcs[UUID].inboundAudioPipeline[trackid].analyser; +} + +function audioMeterGuest(mediaStreamSource, UUID, trackid){ + log("audioMeterGuest started"); + session.rpcs[UUID].inboundAudioPipeline[trackid].analyser = session.audioCtx.createAnalyser(); + mediaStreamSource.connect(session.rpcs[UUID].inboundAudioPipeline[trackid].analyser); + session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.fftSize = 256; + session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.smoothingTimeConstant = 0.05; + + var bufferLength = session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.frequencyBinCount; + var dataArray = new Uint8Array(bufferLength); + + function updateLevels() { + + try { + session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.getByteFrequencyData(dataArray); + var total = 0; + for (var i = 0; i < dataArray.length; i++){ + total += dataArray[i]; + } + total = parseInt(total/150); + session.rpcs[UUID].stats.Audio_Loudness = total; + + if (session.pushLoudness==true){ + var loudnessObj = {}; + loudnessObj[session.rpcs[UUID].streamID] = session.rpcs[UUID].stats.Audio_Loudness; + + if (isIFrame){ + parent.postMessage({"loudness": loudnessObj, "action":"loudness", "value":session.rpcs[UUID].stats.Audio_Loudness, "UUID":UUID}, session.iframetarget); + } + } + + try{ + clearTimeout(session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.interval); + session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.interval = setTimeout(function(){updateLevels();},100); + } catch(e){ + log("closing old inaudio pipeline"); + } + + if (session.style==3 || session.meterStyle){ // overrides style + if (session.rpcs[UUID].videoElement){ + if (total>40){ + session.rpcs[UUID].videoElement.dataset.speaking = "2"; + } else if (total>10){ + session.rpcs[UUID].videoElement.dataset.speaking = "1"; + } else { + session.rpcs[UUID].videoElement.dataset.speaking = "0"; + } + + if (session.meterStyle==4){ + session.rpcs[UUID].videoElement.dataset.loudness = total; + return; // this is cause we are using the data-loudness + } + } else if (session.meterStyle==4){ + return; + } + } else if (session.scene!==false){ // if a scene, cancel + return; + } else if (session.audioMeterGuest===false){ // don't show if we just want the volume levels + return; + } + + if (session.rpcs[UUID].voiceMeter){ + session.rpcs[UUID].voiceMeter.dataset.level = total; + if (session.meterStyle==1){ + var perct = Math.min(total,100); + + session.rpcs[UUID].voiceMeter.style.height = perct + "%"; + if (total>80){ + var R = parseInt(255 * perct/100).toString(16).padStart(2, '0'); + var G = parseInt(255 - 255 * perct /100).toString(16).padStart(2, '0'); + session.rpcs[UUID].voiceMeter.style.backgroundColor = "#" + R + G + "00"; + } else { + session.rpcs[UUID].voiceMeter.style.backgroundColor = "#00FF00"; + } + + } else { + if (total>15){ + session.rpcs[UUID].voiceMeter.style.opacity = 100; // temporary + } else { + session.rpcs[UUID].voiceMeter.style.opacity = 0; // temporary + } + } + + } else { + session.rpcs[UUID].voiceMeter = document.createElement("div"); + session.rpcs[UUID].voiceMeter.id = "voiceMeter_"+UUID; + session.rpcs[UUID].voiceMeter.dataset.level = total; + if (session.meterStyle==1){ + session.rpcs[UUID].voiceMeter.classList.add("video-meter2"); + } else { + if (total>15){ + session.rpcs[UUID].voiceMeter.style.opacity = 100; // temporary + } else { + session.rpcs[UUID].voiceMeter.style.opacity = 0; // temporary + } + if (session.meterStyle==2){ + session.rpcs[UUID].voiceMeter.classList.add("video-meter-2"); + } else { + session.rpcs[UUID].voiceMeter.classList.add("video-meter"); + } + } + updateMixer(); + } + + } catch(e){ + warnlog(e); + // fail as an exception; this is a control close. + return; + } + }; + clearTimeout(session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.interval); + session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.interval = setTimeout(function(){updateLevels();},100); + return session.rpcs[UUID].inboundAudioPipeline[trackid].analyser; +} + +function effectsDynamicallyUpdate(event, ele){ + log("effectsDynamicallyUpdate"); + session.effect = ele.options[ele.selectedIndex].value; + + getById("selectImageTFLITE").style.display = "none"; + getById("selectImageTFLITE3").style.display = "none"; + getById("selectEffectAmount").style.display = "none"; + getById("selectEffectAmount3").style.display = "none"; + + if (session.effect === "1"){ + updateRenderOutpipe(); + return; + } + + if (session.effect === "7"){ // digitalZoom + getById("selectEffectAmount").style.display = "block"; + getById("selectEffectAmount3").style.display = "block"; + session.effectValue = 1.0; + getById("selectEffectAmountInput").min = 1; + getById("selectEffectAmountInput").max = 1.99; + getById("selectEffectAmountInput").step = 0.01 + getById("selectEffectAmountInput3").min = 1; + getById("selectEffectAmountInput3").max = 1.99; + getById("selectEffectAmountInput3").step = 0.01 + + getById("selectEffectAmountInput").value = session.effectValue; + getById("selectEffectAmountInput3").value = session.effectValue; + updateRenderOutpipe(); + return; + } + + if (session.effect === "8"){ // like zoom but none + updateRenderOutpipe(); + return; + } + + if (session.effect == "3a"){ + session.effect = "3"; + session.effectValue = 5; + } + if ((session.effectValue_default===false) && (session.effect=="3")){ + session.effectValue = 2; + } else { + session.effectValue = session.effectValue_default; + } + + if (session.effect == "0" || !session.effect){ + updateRenderOutpipe(); + return; + } else if (session.effect === "3" || session.effect === "4"){ + attemptTFLiteJsFileLoad(); + if (!session.tfliteModule.looping){ + updateRenderOutpipe(); + } + if ((session.effect === "3") && (session.effectValue_default==false)){ + getById("selectEffectAmount").style.display = "block"; + getById("selectEffectAmount3").style.display = "block"; + + getById("selectEffectAmountInput").min = 0; + getById("selectEffectAmountInput").max = 20; + getById("selectEffectAmountInput").step = 1; + getById("selectEffectAmountInput3").min = 0; + getById("selectEffectAmountInput3").max = 20; + getById("selectEffectAmountInput3").step = 1; + + getById("selectEffectAmountInput").value = session.effectValue; + getById("selectEffectAmountInput3").value = session.effectValue; + } + } else if (session.effect === "5"){ + attemptTFLiteJsFileLoad(); + if (!session.tfliteModule.looping){ + updateRenderOutpipe(); + } + loadTFLITEImages(); + } else if (session.effect === "6"){ + if (!gpgpuSupport){ + if (!session.cleanOutput){ + warnUser("Hardware acceleration isn't detected.

    This effect will not work",4000,false); + return; + } + } else if (gpgpuSupport == "Google SwiftShader"){ + if (!session.cleanOutput){ + warnUser("Hardware acceleration isn't detected.

    Please enable it for this effect to work correctly.

    Settings -> Advanced -> System -> Use hardware-accleration", false, false); + } + return; + } + loadTensorflowJS(); + updateRenderOutpipe(); + //mainMeshMask(); + } else { + //loadEffect(session.effect); + updateRenderOutpipe(); + } + + if ((session.permaid===false) && (session.roomid===false) && (session.view===false) && (session.director===false)){ + updateURL("effects"); + } +} + +function loadTFLITEImages(){ + if (session.effect!=="5"){return;} // only load if effects 5 is set. + + if (session.defaultBackgroundImages){ + try { + session.defaultBackgroundImages.reverse(); + }catch(e){ + errorlog("Could not process image list"); + session.defaultBackgroundImages = false; + session.selectImageTFLITE_contents = getById("selectImageTFLITE_contents"); + return; + } + session.defaultBackgroundImages.forEach(imgSrc=>{ + try { + var img = document.createElement("img"); + img.onerror = function(){this.style.display="none";}; // hide images that fail to load + img.crossOrigin = "Anonymous"; + img.src = imgSrc; + img.style="max-width:130px;max-height:73.5px;display:inline-block;margin:10px;cursor:pointer;"; + img.onclick=function(event){changeTFLiteImage(event, this);}; + getById("selectImageTFLITE_contents").prepend(img); + } catch(e){}; + }); + session.defaultBackgroundImages = false; + session.selectImageTFLITE_contents = getById("selectImageTFLITE_contents"); + } else if (!session.selectImageTFLITE_contents){ + session.selectImageTFLITE_contents = getById("selectImageTFLITE_contents"); + } + if (document.getElementById("selectImageTFLITE")){ + document.getElementById("selectImageTFLITE").style.display = "block"; + document.getElementById("selectImageTFLITE").appendChild(session.selectImageTFLITE_contents); + session.selectImageTFLITE_contents.classList.remove("hidden"); + } else if (document.getElementById("selectImageTFLITE3")){ + document.getElementById("selectImageTFLITE3").style.display = "block"; + document.getElementById("selectImageTFLITE3").appendChild(session.selectImageTFLITE_contents); + session.selectImageTFLITE_contents.classList.remove("hidden"); + } +} + +var effectsLoaded = {}; +var JEELIZFACEFILTER = null; +async function loadEffect(effect){ + warnlog("effect:"+effect); + var filename = effect.replace(/\W/g, ''); + if (effectsLoaded[filename]){ + effectsLoaded[filename](); + return; + } else { + effectsLoaded[filename] = function(){}; + } + warnlog("Loading Effect: "+effect); + var script = document.createElement('script'); + script.onload = async function() { + log("LOADED EFFECT"); + effectsLoaded[filename] = await effectsEngine(filename); + log("effectsEngine();"); + if (gpgpuSupport == "Google SwiftShader"){ + if (!session.cleanOutput){ + warnUser("Hardware acceleration isn't detected.

    Please enable it for better performance.

    Settings -> Advanced -> System -> Use hardware-accleration", false, false); + } + } + effectsLoaded[filename](); + } + script.src = "./filters/"+filename+".js?"+parseInt(1000*Math.random()); + document.head.appendChild(script); + warnUser("Loading custom effects model...",1000); +} + +async function loadScript(url, callback=false){ + var res = null; + var rej = null; + var promise = new Promise((resolve, reject) => { + res = resolve; + rej = reject; + }); + + var check = document.querySelector("script[src='"+url+"']"); + if (check){ + if(callback){callback();} + } else { + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = url; + script.onload = function(){ + res(); + if(callback){ + callback(); + } + }; + document.head.appendChild(script); + } + return await promise; +} + +var tokenClient=false; +function YoutubeChatInterface(remote=false){ // this lets us query Youtube for chat messages, but its quota limited :( + if (!tokenClient){ + tokenClient=true; + } else { + return; + } + + var gisInited = false; + var gapiInited = false; + var busy = 0; + + function handleAuthClick() { + tokenClient.callback = async (resp) => { + if (resp.error){ + errorlog(resp.error); + } + closeModal(); + var auths = gapi.client.getToken(); + if (auths){ + setStorage("YoutubeAuth", JSON.stringify(auths), auths.expires_in || 3600); + } + listBroadcasts(); + }; + var saved = getStorage("YoutubeAuth"); + + if (saved){ + gapi.client.setToken(JSON.parse(saved)); + listBroadcasts(); + } else if (gapi.client.getToken() === null) { + if (remote){ + tokenClient.requestAccessToken({prompt:"consent"}); + } else { + warnUser("", false, false); + } + } else { + if (remote){ + tokenClient.requestAccessToken({prompt: ""}); + } else { + warnUser("", false, false); + } + } + } + + function maybeEnableButtons() { + if (gapiInited && gisInited){ + handleAuthClick(); + } + } + + async function initializeGapiClient() { + await gapi.client.init({ + apiKey: session.youtubeKey.split(",")[1], + discoveryDocs: ['https://www.googleapis.com/discovery/v1/apis/youtube/v3/rest'], + }); + gapiInited = true; + maybeEnableButtons(); + } + + function handleSignoutClick() { + let token = gapi.client.getToken(); + if (token !== null) { + google.accounts.oauth2.revoke(token.access_token); + gapi.client.setToken(''); + } + } + + async function listBroadcasts() { + try { + var response = await gapi.client.youtube.liveBroadcasts.list({ + "broadcastStatus": "active" + }); + } catch (err) { + errorlog(err); + return; + } + + let broadcasts = response.result.items; + if (!broadcasts || broadcasts.length == 0) { + return; + } + broadcasts.forEach(broadcast=>{ + setTimeout(function(liveChatId){ + listMessages(liveChatId); + busy+=1; + },1000, broadcast.snippet.liveChatId); + }); + } + + async function listMessages(liveChatId, pageToken = false) { + try { + if (pageToken){ + var response = await gapi.client.youtube.liveChatMessages.list({ + "liveChatId": liveChatId, + "part": ["id", "snippet", "authorDetails"], + "pageToken": pageToken + }); + } else { + var response = await gapi.client.youtube.liveChatMessages.list({ + "liveChatId": liveChatId, + "part": ["id", "snippet", "authorDetails"] + }); + } + + var messages = response.result.items; + messages.forEach(msg =>{ + pokeIframeAPI("YoutubeChat",msg); + }); + + var polling = response.result.pollingIntervalMillis; + var pageToken = response.result.nextPageToken; + + if (busy>1){ + // popular eh? Lets quickly check for more. + } else if (busy>0){ // a message ! hurrah + if (polling<2000){polling=2000;} // Was it just luck? + } else if (polling<5000){ + polling=5000; // let's not spam the api, cause we know there isn't anything waiting.. + } + busy=0; // reset + setTimeout(function(liveChatId,pageToken){ + listMessages(liveChatId, pageToken); + }, polling, liveChatId, pageToken) + + } catch (err) { + return; + } + } + + function gisLoaded() { + tokenClient = google.accounts.oauth2.initTokenClient({ + client_id: session.youtubeKey.split(",")[0], + scope: 'https://www.googleapis.com/auth/youtube', + callback: '', + }); + gisInited = true; + maybeEnableButtons(); + } + function gapiLoaded() { + gapi.load('client', initializeGapiClient); + } + + loadScript("https://apis.google.com/js/api.js",gapiLoaded); + loadScript("https://accounts.google.com/gsi/client",gisLoaded); +} + +function loadTensorflowJS(){ + if (session.TFJSModel!=null){ + return; + } + log("loadTensorflowJS()"); + session.TFJSModel=true; + var script = document.createElement('script'); + var script2 = document.createElement('script'); + var script3 = document.createElement('script'); + var script4 = document.createElement('script'); + script.onload = function() { + document.head.appendChild(script2); + } + script2.onload = function() { + document.head.appendChild(script3); + } + script3.onload = function() { + document.head.appendChild(script4); + } + script4.onload = function() { + async function loadModel(){ + session.TFJSModel = await faceLandmarksDetection.load(faceLandmarksDetection.SupportedPackages.mediapipeFacemesh); + closeModal(); + warnUser("Almost done loading model...",3000); + } + loadModel(); + + } + script.src = "./thirdparty/tfjs/tf-core.js"; + script2.src = "./thirdparty/tfjs/tf-converter.js"; + script3.src = "./thirdparty/tfjs/tf-backend-webgl.js"; + script4.src = "./thirdparty/tfjs/face-landmarks-detection.js"; + warnUser("Downloading a big effects model... may take a minute",15000); + + script.type = 'text/javascript';script2.type = 'text/javascript';script3.type = 'text/javascript';script4.type = 'text/javascript'; + document.head.appendChild(script); +} + + + +var TFLITELOADING = false; +function attemptTFLiteJsFileLoad(){ + if (session.tfliteModule!==false){ + return true; + } + warnUser("Loading effects model..."); + TFLITELOADING=true; + session.tfliteModule={}; + + if (!document.getElementById("tflitesimdjs")){ + var tmpScript = document.createElement('script'); + tmpScript.onload = loadTFLiteModel; + tmpScript.type = 'text/javascript'; + tmpScript.src = "./thirdparty/tflite/tflite-simd.js?ver=2"; + tmpScript.id = "tflitesimdjs"; + document.head.appendChild(tmpScript); + } + + return false; +} +async function changeTFLiteImage(ev, ele){ + if (ele.files && ele.files[0]) { + if (session.tfliteModule.img){ + session.tfliteModule.img.classList.remove("selectedTFImage"); + } + session.tfliteModule.img = document.createElement("img"); + session.tfliteModule.img.style="max-width:130px;max-height:73.5px;display:inline-block;margin:10px;cursor:pointer;"; + session.tfliteModule.img.onclick=function(event){changeTFLiteImage(event, this);}; + ele.parentNode.parentNode.insertBefore(session.tfliteModule.img, ele.parentNode); + session.tfliteModule.img.onload = () => { + URL.revokeObjectURL(session.tfliteModule.img.src); // no longer needed, free memory + } + session.tfliteModule.img.src = URL.createObjectURL(ele.files[0]); // set src to blob url + session.tfliteModule.img.classList.add("selectedTFImage"); + + } else if (ele.tagName.toLowerCase() == "img"){ + session.tfliteModule.img.classList.remove("selectedTFImage"); + session.tfliteModule.img = ele + session.tfliteModule.img.classList.add("selectedTFImage"); + } +} +async function changeEffectAmount(ev, ele){ + session.effectValue = ele.value; + if (ele.id === "selectEffectAmountInput"){ + getById("selectEffectAmountInput3").value = ele.value + } + log("session.effectValue: "+session.effectValue); +} +async function loadTFLiteModel(){ + try { + + if (session.tfliteModule && (session.tfliteModule.img)){ + var img = session.tfliteModule.img; + session.tfliteModule = await createTFLiteSIMDModule(); + session.tfliteModule.img = img; + } else { + session.tfliteModule = {}; + session.tfliteModule = await createTFLiteSIMDModule(); + } + if (!session.tfliteModule.simd){ + var elements = document.querySelectorAll('[data-warnSimdNotice]') + for (let i = 0; i < elements.length; i++) { + elements[i].style.display = "inline-block"; + } + } + } catch(e){ + warnlog("TF-LITE FAILED TO LOAD"); + closeModal(); + return; + } + const modelResponse = await fetch("./thirdparty/tflite/segm_full_v679.tflite"); + session.tfliteModule.model = await modelResponse.arrayBuffer(); + + session.tfliteModule.HEAPU8.set(new Uint8Array(session.tfliteModule.model), session.tfliteModule._getModelBufferMemoryOffset()); + session.tfliteModule._loadModel(session.tfliteModule.model.byteLength); + session.tfliteModule.activelyProcessing = false; + TFLITELOADING = false; + closeModal(); + if (LaunchTFWorkerCallback){TFLiteWorker();} +} +function smdInfo(){ + warnUser("For improved performance, use Chrome v87 or newer with SIMD support enabled.
    Enable SIMD here: chrome://flags/#enable-webassembly-simd", false, false); +} + +function getGuestTarget(type, id){ + var element = document.querySelectorAll('[data-action-type="'+type+'"][data-sid="'+id+'"]'); // data-sid="P5MQpia" + if (!element.length){ + return element = getRightOrderedElement('[data-action-type="'+type+'"][data--u-u-i-d]', id); + } else { + element = element[0]; + } + return element; +} + +function getGuestTargetScene(scene, id){ + var element = document.querySelectorAll('[data-action-type="addToScene"][data-scene="'+scene+'"][data-sid="'+id+'"]'); // data-sid="P5MQpia" + if (!element.length){ + return element = getRightOrderedElement('[data-action-type="addToScene"][data-scene="'+scene+'"][data--u-u-i-d]', id); + } else { + element = element[0]; + } + return element; +} +function getGuestTargetGroup(group, id){ + var element = document.querySelectorAll('[data-action-type="toggle-group"][data-group="'+group+'"][data-sid="'+id+'"]'); // data-sid="P5MQpia" + if (!element.length){ + return getRightOrderedElement('[data-action-type="toggle-group"][data-group="'+group+'"][data--u-u-i-d]', id); + } else { + element = element[0]; + } + return element; +} + +function targetGuest(target, action, value=null){ + if (target){ + if ((target == (parseInt(target)+"")) && target<100){ + target -=1; + } + } else { + target=1; + } + warnlog("target "+target); + warnlog("action "+action); + warnlog("value "+value); + if ((action == 0) || (action == "forward")) { + var element = getGuestTarget("forward", target); + if (element) { + directMigrate(element, true, value); // if value is set, it will auto transfer the guest to that room. + } + } else if ((action == 1) || (action == "addScene")) { + var scene = 1; + if (value == "null" || value == null || value == "toggle"){ + scene = 1; + } else if ((value !== true) && (value !== false)){ + scene = value; + } + var element = getGuestTargetScene(scene, target); // oscid/action/target/value 1/1/scene + if (element) { + if (value===true){ + element.value = 1; + } else if (value===false){ + element.value = 0; + } + return directEnable(element, true); // false or true return + } + } else if ((action == 2) || (action == "muteScene")) { + var element = getGuestTarget("mute-scene", target); + if (element) { + if (value===true){ + element.value = 1; + } else if (value===false){ + element.value = 0; + } + return directMute(element, true); // false/true + } + } else if ((action == 3) || (action == "mic")) { + var element = getGuestTarget("mute-guest", target); + if (element) { + if (value===true){ + element.value = 1; + } else if (value===false){ + element.value = 0; + } + return remoteMute(element, true); // false/true + } + } else if ((action == 4) || (action == "hangup")) { + var element = getGuestTarget("hangup", target); + if (element) { + return directHangup(element, true); // false or true; false if confirmed no + } + } else if ((action == 5) || (action == "soloChat")) { // see soloChatBidirectional action=9 for two-way + var element = getGuestTarget("solo-chat", target); + if (element) { + if (value===true){ + element.value = 1; + } else if (value===false){ + element.value = 0; + } + return session.toggleSoloChat(element.dataset.UUID); + } + } else if ((action == 6) || (action == "speaker")) { + var element = getGuestTarget("toggle-remote-speaker", target); + if (element) { + if (value===true){ + element.value = 1; + } else if (value===false){ + element.value = 0; + } + return remoteSpeakerMute(element); + + } + } else if ((action == 7) || (action == "display")) { + var element = getGuestTarget("toggle-remote-display", target); + if (element) { + if (value===true){ + element.value = 1; + } else if (value===false){ + element.value = 0; + } + return remoteDisplayMute(element); + } + } else if ((action == 8) || (action == "group")) { + if (value == "null" || value == null){ + value = 1; + } + var element = getGuestTargetGroup(value, target); + if (element) { + return changeGroup(element, null, value); + } + } else if ((action == 9) || (action == "soloChatBidirectional")) { + var element = getGuestTarget("solo-chat", target); + if (element) { + var ctrl = {}; + ctrl.ctrlKey = true; + if (value===true){ + element.value = 1; + } else if (value===false){ + element.value = 0; + } + return session.toggleSoloChat(element.dataset.UUID, ctrl); + + } + } else if ((action == 12) || (action == "addScene2")) { + var element = getGuestTargetScene(2, target); + if (element) { + if (value===true){ + element.value = 1; + } else if (value===false){ + element.value = 0; + } + return directEnable(element, true) + } + } else if ((action == 13) || (action == "addScene3")) { + var element = getGuestTargetScene(3, target); + if (element) { + if (value===true){ + element.value = 1; + } else if (value===false){ + element.value = 0; + } + return directEnable(element, true) + } + } else if ((action == 14) || (action == "addScene4")) { + var element = getGuestTargetScene(4, target); + if (element) { + if (value===true){ + element.value = 1; + } else if (value===false){ + element.value = 0; + } + return directEnable(element, true) + } + } else if ((action == 15) || (action == "addScene5")) { + var element = getGuestTargetScene(5, target); + if (element) { + if (value===true){ + element.value = 1; + } else if (value===false){ + element.value = 0; + } + return directEnable(element, true) + } + } else if ((action == 16) || (action == "addScene6")) { + var element = getGuestTargetScene(6, target); + if (element) { + if (value===true){ + element.value = 1; + } else if (value===false){ + element.value = 0; + } + return directEnable(element, true) + } + } else if ((action == 17) || (action == "addScene7")) { + var element = getGuestTargetScene(7, target); + if (element) { + if (value===true){ + element.value = 1; + } else if (value===false){ + element.value = 0; + } + return directEnable(element, true) + } + } else if ((action == 18) || (action == "addScene8")) { + var element = getGuestTargetScene(8, target); + if (element) { + if (value===true){ + element.value = 1; + } else if (value===false){ + element.value = 0; + } + return directEnable(element, true) + } + } else if ((action == 19) || (action == "forceKeyframe")) { + var element = getGuestTarget("force-keyframe", target); + if (element) { + return requestKeyframeScene(element); + } + } else if ((action == 20) || (action == "soloVideo")) { + var element = getGuestTarget("solo-video", target); + if (element) { + if (value===true){ + element.value = 1; + } else if (value===false){ + element.value = 0; + } + return requestInfocus(element); + } + } else if ((action == 21) || (action == "sendChat")) { + var element = getGuestTarget("solo-video", target); // just something that probably exists. + if (element) { + return sendChat(value, element.dataset.UUID); + } + } else if ((action == 22) || (action == "sendDirectorChat")) { + var element = getGuestTarget("solo-video", target); // just something that probably exists. + if (element) { + return sendChat(value, element.dataset.UUID, true); + } + } else if ((action == 27) || (action == "volume")){ + var element = getGuestTarget("volume", target); + if (element) { + element.value = parseInt(value) || 0; + return remoteVolume(element); + } + } else if (action == "startRoomTimer"){ + var element = getGuestTarget("create-timer", target); + if (element) { + element.value = 0; + return directTimer(element, false, value); + } + } else if (action == "pauseRoomTimer"){ + var element = getGuestTarget("create-timer", target); + if (element) { + if (element.value == 3){ + return directTimer(element, {ctrlKey:true}); + } else { + return directTimer(element, {ctrlKey:true}); + } + } + } else if (action == "stopRoomTimer"){ + var element = getGuestTarget("create-timer", target); + if (element) { + element.value = 1; + return directTimer(element); + } + } + return false; +} +async function startPublishing(){ + + if (query("#publishOutURL input[type='text']").dataset.twitch=="true"){ + session.whipOutput = "https://g.webrtc.live-video.net:4443/v2/offer"; + } else { + session.whipOutput = query("#publishOutURL input[type='text']").value || session.whipOutput || null; + } + + if (!session.whipOutput){ + warnUser("Please first provided an output destination",2500); + return; + } + + if (!session.whipOutputToken){ + session.whipOutputToken = query("#publishOutToken input[type='password']").value || false; + } + + if (!session.whipOutputToken && query("#publishOutURL input[type='text']").dataset.twitch=="true"){ + warnUser("Please enter a Twitch stream token first",2000); + return; + } + getById("publishSettings").classList.add("hidden"); + + + if (!getById("whipoutvbrcbr").classList.contains("hidden")){ + if (getById("whipoutvbrcbr").value ==="cbr"){ + session.cbr = 1; + } else { + session.cbr = 0; + } + } + if (!getById("whipoutdenoise").classList.contains("hidden")){ + if (getById("whipoutdenoise").value ==="1"){ + session.noiseSuppression = true; + } else { + session.noiseSuppression = false; + } + } + if (!getById("whipoutautogain").classList.contains("hidden")){ + if (getById("whipoutautogain").value ==="1"){ + session.autoGainControl = true; + } else { + session.autoGainControl = false; + } + } + if (!getById("whipoutstereo").classList.contains("hidden")){ + if (getById("whipoutstereo").value ==="1"){ + session.stereo = 1; + } else { + session.stereo = 0; + } + } + if (!getById("whipoutbitrateGroupFlag").classList.contains("hidden")){ + session.whipOutVideoBitrate = parseInt(getById("whipoutbitrateGroupFlag").value); + } + if (!getById("whipoutaudiobitrate").classList.contains("hidden")){ + session.whipOutAudioBitrate = parseInt(getById("whipoutaudiobitrate").value); + } + + var ret = await publishScreen(); + if (ret){ + getById("publishSettings").classList.add("hidden"); + resizeWindow(1280,720); + document.title="PUBLISHING🔴"+document.title; + } else { + getById("publishSettings").classList.remove("hidden"); + } +} + +function twitchSelect(ele){ + if (ele.checked){ + //query("#publishOutURL input[type='text']").value = + query("#publishOutURL input[type='text']").disabled = true; + query("#publishOutURL input[type='text']").classList.add("disable"); + query("#publishOutURL input[type='text']").dataset.twitch = "true"; + + query("#publishOutToken input[type='password']").placeholder = "Twitch stream token here"; + } else { + query("#publishOutURL input[type='text']").disabled = null; + query("#publishOutURL input[type='text']").classList.remove("disable"); + delete getById("publishOutURL").disabled; + query("#publishOutURL input[type='text']").dataset.twitch = "false"; + query("#publishOutToken input[type='password']").placeholder = "WHIP auth token here"; + } +} + +function resizeWindow(width, height){ + if (window.outerWidth) { + window.resizeTo( + width + (window.outerWidth - window.innerWidth), + height + (window.outerHeight - window.innerHeight) + ); + } else { + window.resizeTo(500, 500); + window.resizeTo( + width + (500 - document.body.offsetWidth), + height + (500 - document.body.offsetHeight) + ); + } + + setInterval(function(){ + if ((window.innerWidth/window.innerHeight > 17/9) && (window.innerWidth/window.innerHeight < 15/9)){ + return; + } + if (window.outerWidth) { + window.resizeTo( + width + (window.outerWidth - window.innerWidth), + height + (window.outerHeight - window.innerHeight) + ); + } else { + window.resizeTo(500, 500); + window.resizeTo( + width + (500 - document.body.offsetWidth), + height + (500 - document.body.offsetHeight) + ); + } + },5000); +} + +function configureWhipOutSDP(description){ // THIS IS FOR WHIP-OUTPUT; it has + + var configs = false; + + if (SafariVersion && (SafariVersion<=13) && (iOS || iPad)){ + // skip. Not going to try to tinker with older iOS SDPs + } else if ((session.stereo==3) || (session.stereo==5) || (session.stereo==6) || (session.stereo==1)){ // stereo out + configs = { + 'stereo': 1, + 'useinbandfec': session.noFEC ? 0 : 1, + 'maxptime': session.maxptime, + 'minptime': session.minptime, + 'ptime': session.ptime, + 'dtx': session.dtx // "usedtx", if no loud audio, stops sending audio for 400ms. default. + }; + log("stereo enabled"); + } else if (iOS || iPad){ // iOS doesn't have multichannel, so why even bother + configs = { + 'useinbandfec': session.noFEC ? 0 : 1, + 'maxptime': session.maxptime, + 'minptime': session.minptime, + 'ptime': session.ptime, + 'dtx': session.dtx // "usedtx", if no loud audio, stops sending audio for 400ms. default. + }; + + } else if (session.stereo==4){ + configs = { + 'stereo': 2, + 'useinbandfec': session.noFEC ? 0 : 1, + 'maxptime': session.maxptime, + 'minptime': session.minptime, + 'ptime': session.ptime, + 'dtx': session.dtx // "usedtx", if no loud audio, stops sending audio for 400ms. default. + }; + log("stereo enabled"); + } else { + configs = { + 'stereo': 0, + 'useinbandfec': session.noFEC ? 0 : 1, + 'maxptime': session.maxptime, + 'minptime': session.minptime, + 'ptime': session.ptime, + 'dtx': session.dtx // "usedtx", if no loud audio, stops sending audio for 400ms. default. + }; + } + + if (session.whipOutAudioBitrate){ + if (!configs){ + configs = { + 'maxaveragebitrate': session.whipOutAudioBitrate * 1024, + 'cbr': session.cbr + }; + } else{ + configs.maxaveragebitrate = session.whipOutAudioBitrate * 1024; + configs.cbr = session.cbr; + } + } + + if (configs){ + log("Processing sdp of type: "+description.type+ " ..."); + description.sdp = CodecsHandler.setOpusAttributes(description.sdp, configs, true); + } + + if (iOS || iPad){ // solves issues with iOS rotation not being correct + if (session.removeOrientationFlag && description.sdp.includes("a=extmap:3 urn:3gpp:video-orientation\r\n")){ + description.sdp = description.sdp.replace('a=extmap:3 urn:3gpp:video-orientation\r\n', ''); + } + } + + if (session.screenShareState && (typeof session.whipOutScreenShareCodec === "object")){ + session.whipOutScreenShareCodec.reverse().forEach(codec=>{ + description.sdp = CodecsHandler.preferCodec(description.sdp, codec); + + if (session.whipOutScreenShareBitrate || session.whipOutVideoBitrate){ + description.sdp = CodecsHandler.setVideoBitrates(description.sdp , { + min: parseInt((session.whipOutScreenShareBitrate || session.whipOutVideoBitrate)/10) || 1, + max: session.whipOutScreenShareBitrate || session.whipOutVideoBitrate || 1 + }, codec); + } + }); + } else if (session.screenShareState && session.whipOutScreenShareCodec){ + description.sdp = CodecsHandler.preferCodec(description.sdp, session.whipOutScreenShareCodec); + + if (session.whipOutScreenShareBitrate || session.whipOutVideoBitrate){ + description.sdp = CodecsHandler.setVideoBitrates(description.sdp , { + min: parseInt((session.whipOutScreenShareBitrate || session.whipOutVideoBitrate)/10) || 1, + max: session.whipOutScreenShareBitrate || session.whipOutVideoBitrate || 1 + }, session.whipOutScreenShareCodec); + } + } else if (typeof session.whipOutCodec === "object"){ + session.whipOutCodec.reverse().forEach(codec=>{ + description.sdp = CodecsHandler.preferCodec(description.sdp, codec); + + if (session.whipOutVideoBitrate){ + description.sdp = CodecsHandler.setVideoBitrates(description.sdp , { + min: parseInt(session.whipOutVideoBitrate/10) || 1, + max: session.whipOutVideoBitrate || 1 + }, codec); + } + }); + } else if (session.whipOutCodec){ + description.sdp = CodecsHandler.preferCodec(description.sdp, session.whipOutCodec); + if (session.whipOutVideoBitrate){ + description.sdp = CodecsHandler.setVideoBitrates(description.sdp , { + min: parseInt(session.whipOutVideoBitrate/10) || 1, + max: session.whipOutVideoBitrate || 1 + }, session.whipOutCodec); + } + } else { + if (iOS || iPad){ + jsep.sdp = jsep.sdp.replace(/42e01f/gi,"42e01f"); // openH264 + jsep.sdp = jsep.sdp.replace(/42001f/gi,"42e01f"); // external encoder + jsep.sdp = jsep.sdp.replace(/420029/gi,"42e01f"); // external encoder + jsep.sdp = jsep.sdp.replace(/42a01e/gi,"42e01f"); // external encoder + jsep.sdp = jsep.sdp.replace(/42a014/gi,"42e01f"); // external encoder + jsep.sdp = jsep.sdp.replace(/42a00b/gi,"42e01f"); // external encoder + jsep.sdp = jsep.sdp.replace(/640c1f/gi,"42e01f"); // will not work + } else { + description.sdp = CodecsHandler.preferCodec(description.sdp,"h264"); // default + description.sdp = description.sdp.replace(/42001f/gi,"42e01f"); // openh264 set as default. + description.sdp = description.sdp.replace(/420029/gi,"42e01f"); + } + if (session.whipOutVideoBitrate){ + description.sdp = CodecsHandler.setVideoBitrates(description.sdp , { + min: parseInt(session.whipOutVideoBitrate/10) || 1, + max: session.whipOutVideoBitrate || 1 + }, "h264"); + } + } + + var bitrate = 2500; + if (session.whipOutVideoBitrate!==false){ + bitrate = session.whipOutVideoBitrate; + } + if (session.screenShareState && (session.whipOutScreenShareBitrate!==false)){ + bitrate = session.whipOutScreenShareBitrate; + } + + session.whipOut.savedBitrate = bitrate; // actual target + session.whipOut.setBitrate = bitrate; // max + + return description; +} + + +function whipOut(){ + log("whipOut"); + var candidates = []; + var codec = false; + var keyframe = false; + async function whipConnect(){ + + try { + + if (!session.configuration){ + await chooseBestTURN(); + } + + session.whipOut = new RTCPeerConnection(session.configuration); + session.whipOut.stats = {}; + session.whipOut.maxBandwidth = null; // based on max available bitrate + session.whipOut.scale = false; + + session.whipOut.offerToReceiveAudio = false; + session.whipOut.offerToReceiveVideo = false; + + + } catch(err){ + errorlog(err); + if (!session.cleanOutput){ + warnUser("An RTC error occured"); + } + } + + try { + var tracks = false; + if (session.videoElement && session.videoElement.srcObject){ + tracks = session.videoElement.srcObject.getAudioTracks(); + } + var streamsource = false; + if (!tracks || !tracks.length){ + var audioCtx = new AudioContext(); + warnlog("No audio track; using a webaudio node instead"); + var destination = audioCtx.createMediaStreamDestination(); + streamsource = destination.stream; + destination.stream.getAudioTracks().forEach(trk=>{ + tracks = trk; + }); + } else { + tracks = tracks[0]; + streamsource = session.videoElement.srcObject; + } + + if (session.audioContentHint && (tracks.kind === "audio")){ + try { + tracks.contentHint = session.audioContentHint; + } catch(e){ + errorlog(e); + } + } + + if (tracks){ + try { + session.whipOut.addTransceiver(tracks, { + streams: [ streamsource ], + direction: 'sendonly' + }); + } catch(e){ + errorlog(e); + session.whipOut.addTrack(tracks); + } + } + //} + /////// + + //// video tracks + var tracks = false; + if (session.videoElement && session.videoElement.srcObject){ + tracks = session.videoElement.srcObject.getVideoTracks(); + } + //// + + //if (!tracks || !tracks.length){ + // tracks = getMeshcastCanvasTrack(); + //} else { + tracks = tracks[0]; + //} + + if (session.screenShareState && session.screenshareContentHint && (tracks.kind === "video")){ + try { + tracks.contentHint = session.screenshareContentHint; + } catch(e){ + errorlog(e); + } + } else if (session.contentHint && (tracks.kind === "video")){ + try { + tracks.contentHint = session.contentHint; + } catch(e){ + errorlog(e); + } + } + if (tracks){ + try { + session.whipOut.addTransceiver(tracks, { + streams: [ session.videoElement.srcObject ], + direction: 'sendonly' + }); + } catch(e){ + errorlog(e); + session.whipOut.addTrack(tracks); + } + } + //} + + session.whipOut.onnegotiationneeded = publish; // bug: https://groups.google.com/forum/#!topic/discuss-webrtc/3-TmyjQ2SeE + + session.whipOut.onicecandidate = function(event){ //event + if (event.candidate==null){ + log("END OF ICE CANDIDATES"); + return; + } + //log(event.candidate); + candidates.push(event.candidate); + }; + + } catch(e){errorlog(e);} + } + var publishing = false; + + + function publish(event){ + if (publishing){ + log(event); + errorlog("onnegotiationneeded again?"); + return; + } + publishing = true; + warnlog("ON NEGO NEEDED"); + warnlog(event); + try { + session.whipOut.createOffer().then(function(description){ + + + try { + description = configureWhipOutSDP(description); + } catch(e){ + errorlog(e); + } + + return session.whipOut.setLocalDescription(description); + }).then(function() { + warnlog(session.whipOut.localDescription.sdp); + var sdp = session.whipOut.localDescription.sdp; + + if (sdp.includes("sendrecv")){ + errorlog("Should not include sendrecv"); + sdp = sdp.replace("a=sendrecv","a=sendonly"); + sdp = sdp.replace("v=sendrecv","v=sendonly"); + } + ajax(sdp, "sdp"); + }).catch(function(err){}); + } catch(e){errorlog(e);} + } + + function ajax(data, type, callback=false){ + log("AJAX: "+type); + //log(data); + try { + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState == 4 && (this.status == 200 || this.status == 201)) { + + var contentType = this.getResponseHeader('content-type'); + + if (contentType.startsWith("text/plain")){ + warnlog("The WHIP output destination responded with an incorrect content type; will attempt to continue still."); + } + + if (!session.whipOut.stats){ + session.whipOut.stats = {}; + } + session.whipOut.stats.whipHost = "generic"; + session.whipOut.stats.whep_URL = false + session.whipOut.stats.watch_URL = false; // cloudflare and meshcast have this, but meh. aec is an issue, so won't bother for now. + var WHELPlaybackURL = false; // we will try to determine the WHEP address, if we need to share it with other viewers. + try { + log(this.getAllResponseHeaders()); + if (this.getAllResponseHeaders().indexOf("whep") >= 0) { + WHELPlaybackURL = this.getResponseHeader('whep') || false; + } + if (!WHELPlaybackURL && session.whipOutput){ + var targetDomain = session.whipOutput.split("/"); + if (targetDomain[2].endsWith(".cloudflarestream.com") && (targetDomain[3].length == 65)){ + WHELPlaybackURL = "https://"+targetDomain[2]+"/"+targetDomain[3].slice(33,65)+"/webRTC/play"; + session.whipOut.stats.whipHost = "Cloudflare"; + } + } + log("WHELPlaybackURL: "+WHELPlaybackURL); + session.whipOut.stats.whep_URL = WHELPlaybackURL + + } catch(e){errorlog(e);} + + if (WHELPlaybackURL){ + session.whipoutSettings = {type:"whep", "url": WHELPlaybackURL}; + } + + if (contentType.startsWith("application/sdp") || contentType.startsWith("text/plain")){ + var jsep = {}; + jsep.sdp = this.responseText; + jsep.type = "answer"; + + try { + jsep = configureWhipOutSDP(jsep); + } catch(e){ + errorlog(e); + } + + warnlog("Processing answer:"); + warnlog(jsep); + + session.whipOut.setRemoteDescription(jsep).then(async function(){ + warnlog("SHOULD BE CONNECTED?"); + var content = ""; + while (candidates.length){ + var candidate = candidates.pop(); + content += candidate.candidate; + + } + warnlog("Content: "+content.length) + //if (content){ + // warnlog("SENDING TRICKLE"); //.. I should, but I'm not, since most sites don't support it still. + if (!keyframe){ + keyframe = setInterval(function(){GOP();},6000); // ensure GOP no longer than 6s + } + session.whipOutSetScale(); + + await sleep(1000); // give whip server a moment to setup I guess. + if (session.whipoutSettings){ + for (var UUID in session.pcs){ + if (session.pcs[UUID].whipout===null){ + var data = {} + data.whepSettings = session.whipoutSettings; + if (session.sendMessage(data, UUID)){ + session.pcs[UUID].whipout = true; + } + } + } + } + //} + + }).catch(function(e){log(e);}); + + } else if (contentType == "application/error"){ + if (this.responseText==432){ + warnUser("Whip out error: 432"); + } else { + warnUser("Unknown Whipe Out error"); + } + } else if (callback){ + callback(); + } + } + }; + if (type==="trickle-ice-sdpfrag"){ + xhttp.open("PATCH", session.whipOutput, true); // Not supported by most sites yet + } else { + xhttp.open("POST", session.whipOutput, true); + } + + if (session.whipOutputToken){ + xhttp.setRequestHeader('Authorization', 'Bearer ' + session.whipOutputToken); + } + + xhttp.setRequestHeader('Content-Type', 'application/'+type); + + xhttp.onerror = function(e) { + errorlog(e); + warnUser("Whip out failed."); + }; + xhttp.send(data); + + } catch(e){errorlog(e);} + } + function GOP(){ + log("Sending keyframe"); + try { + if (!session.whipOut){return;} + + var senders = session.whipOut.getSenders(); + var sender = false; + senders.forEach((senderVideo)=>{ + if (senderVideo.track && senderVideo.track.id && (senderVideo.track.kind == "video")){ + sender = senderVideo; + } + }); + + if (!sender){ + warnlog("can't change bitrate; no video sender found"); + return false; + } + + var settings = {}; + settings.scaleResolutionDownBy = 10; // 50% of default max + + + setEncodings(sender, settings, function(sendr){ + var settings = {}; + + var chromeVersion = getChromiumVersion(); + if (chromeVersion>80){ // just because + settings.scaleResolutionDownBy = null; + } else { + settings.scaleResolutionDownBy = 1.0; + } + + setEncodings(sendr, settings, function(){ + //log("scaleResolutionDownBy set 3b!"); + }); + }, sender); + + return true; + } catch(e){ + errorlog(e); + } + } + whipConnect(); +} + +function whipClient(){ // publish to whip.vdo.ninja with obs, to use. experimental + if (!session.whipView){return;} + warnlog("WHIP Client started"); + + var socket = null; + var connecting = false; + var failedCount = 0; + + function connect(){ + clearTimeout(connecting); + if (socket){ + if (socket.readyState === socket.OPEN){return;} + try{ + socket.close(); + } catch(e){} + } + log("Trying to load whip websocket..."); + + socket = new WebSocket("wss://whip.vdo.ninja"); + + socket.onclose = function (){ + failedCount+=1; + clearTimeout(connecting); + connecting = setTimeout(function(){connect();},100*(failedCount-1)); + + }; + + socket.onerror = function (e){ + console.error(e); + failedCount+=1; + clearTimeout(connecting); + connecting = setTimeout(function(){connect();},100*failedCount); + }; + + socket.onopen = function (){ + failedCount = 0; + try{ + var settings = {}; + socket.send(JSON.stringify({"join":session.whipView})); + } catch(e){ + connecting = setTimeout(function(){connect();},1); + } + }; + + socket.addEventListener('message', async function (event) { + if (event.data){ + + var data = JSON.parse(event.data); + + if ("sdp" in data){ + var resp = await processWHIP(data); + if (resp){ + var ret = {}; + var get = data.get; + data = {}; + if (get){ + data.get = get; + data.result = resp; + ret.callback = data; + log(ret); + socket.send(JSON.stringify(ret)); + } + } + } else if ("delete" in data){ + warnlog("WHIP Client is actively disconnecting"); + // session.closeRPC(i, true); + } + } + }); + } + connect(); +} + +async function processWHIP(data){ // LISTEN FOR REMOTE WHIP + var msg = {}; + msg.description = {}; + msg.description.type = "offer"; + msg.description.sdp = data.sdp; + // msg.session = session.generateRandomString(5); + msg.UUID = session.generateRandomString(25); // fake + + if (data.streamID){ + msg.streamID = data.streamID; + } else { + msg.streamID = session.generateRandomString(15); // fake + } + log("setupIncoming"); + await session.setupIncoming(msg); // could end up setting up the peer the wrong way. + + try { + // session.rpcs[msg.UUID].addTransceiver('video', {direction: 'recvonly'}); + // session.rpcs[msg.UUID].addTransceiver('audio', {direction: 'recvonly'}); + } catch(e){errorlog(e);} + + session.rpcs[msg.UUID].whip = true; + var callback = null; + var promise = new Promise((resolve, reject) => { + callback = resolve; + }); + session.rpcs[msg.UUID].whipCallback = callback; + + var callback2 = null; + var promise2 = new Promise((resolve, reject) => { + callback2 = resolve; + }); + session.rpcs[msg.UUID].whipCallback2 = callback2; + + log("CONNECT PEEER"); + session.connectPeer(msg); + log("CONNECT PEEER DONE"); + + if (!session.manual || !session.director){ + window.onresize = updateMixer; + window.onorientationchange = function(){ + setTimeout(updateMixer, 200); + }; + } + + if ((session.roomid === false) && !session.permaid){ + getById("header").classList.add("hidden"); + } + + log("ICE BUNDLE PROMISE"); + setTimeout(function(UUID){ + log("ICE BUNDLE PROMISE TIMEOUT"); + if (session.rpcs[UUID].whipCallback2){ + session.rpcs[UUID].whipCallback2([...session.rpcs[UUID].iceBundle]); + clearTimeout(session.rpcs[UUID].iceTimer); + session.rpcs[UUID].iceTimer = null; + session.rpcs[UUID].iceBundle = [] + session.rpcs[UUID].whipCallback2 = null; + + } + },3000,msg.UUID); + var iceBundle = await promise2; // waiting for ICE GATHER COMPLETE + session.rpcs[msg.UUID].whipCallback2 = null; + + log("ICE BUNDLE DONE"); + log(iceBundle); + + await promise; + session.rpcs[msg.UUID].whipCallback = null; + sdpAnswer = session.rpcs[msg.UUID].localDescription.sdp; + + var insertIce = ""; + iceBundle.forEach(ice=>{ + if (ice.candidate){ + insertIce += "a="+ice.candidate+"\r\n"; + } + }); + sdpAnswer = sdpAnswer.replace("a=ice-ufrag", insertIce+"a=ice-ufrag"); + + //if (session.stereo){ + // sdpAnswer = CodecsHandler.setOpusAttributes(sdpAnswer, {stereo:1}, true); + //} + + if (sdpAnswer.includes("sendrecv")){ + errorlog("Should not include sendrecv"); + sdpAnswer = sdpAnswer.replace("a=sendrecv","a=recvonly"); + sdpAnswer = sdpAnswer.replace("v=sendrecv","v=recvonly"); + } + + log("completed"); + warnlog(sdpAnswer); + + return sdpAnswer; // return SDP answer for the remote WHIP request +} + +async function whepIn(whepInput=false,whepInputToken=false, UUID=false){ // PLAY WHEP + var candidates = []; + var responseLocation = false; + if (!UUID){ + UUID = "whep_"+session.generateRandomString(25); // fake + } + + whepInput = whepInput || session.whepInput; + if (!whepInput){ + errorlog("no whepInput"); + return; + } + whepInputToken = whepInputToken || session.whepInputToken; + + async function whepConnect(){ + try { + + if (!session.configuration){ + await chooseBestTURN(); + } + + if (!(UUID in session.rpcs)){ + session.rpcs[UUID] = {}; + session.rpcs[UUID].stats = {}; + session.rpcs[UUID].allowGraphs = false; + session.rpcs[UUID].inboundAudioPipeline = {}; + session.rpcs[UUID].channelOffset = false; + session.rpcs[UUID].channelWidth = false; + session.rpcs[UUID].settings = false; + session.rpcs[UUID].lockedVideoBitrate = false; // doesn't do anything + session.rpcs[UUID].lockedAudioBitrate = false; + session.rpcs[UUID].manualBandwidth = false; // doesn't do anything, except maybe help keep track of pause/play states + session.rpcs[UUID].motionDetectionInterval = false; + } + var config = {...session.configuration}; + + if (whepInput.includes("cloudflare")){ + config.iceTransportPolicy = "relay"; // oof. Doesn't work with Cloudflare without this? + } + + try { + session.rpcs[UUID].whep = new RTCPeerConnection(config); + } catch(err){ + errorlog(err); + if (!session.cleanOutput){ + warnUser("An RTC error occured"); + } + } + + var video = true; + var audio = true; + + if ((session.novideo !== false) && (!session.novideo.includes(session.rpcs[UUID].streamID))){ + video = false; + } else if (session.rpcs[UUID].settings && !session.rpcs[UUID].settings.video){ + video = false; + } + if ((session.noaudio !== false) && (!session.noaudio.includes(session.rpcs[UUID].streamID))){ + audio = false; + } else if (session.rpcs[UUID].settings && !session.rpcs[UUID].settings.audio){ + audio = false; + } + + if (!audio && !video){ + errorlog("We will not request the whep source as no audio or video is requested"); + return; + } + + if (!session.manual || !session.director){ + window.onresize = updateMixer; + window.onorientationchange = function(){ + setTimeout(updateMixer, 200); + }; + } + + try { + if (video){ + session.rpcs[UUID].whep.addTransceiver('video', {direction: 'recvonly'}); + } + if (audio){ + session.rpcs[UUID].whep.addTransceiver('audio', {direction: 'recvonly'}); + } + } catch(e){errorlog(e);} + + session.rpcs[UUID].whep.ontrack = function(event) { + warnlog("TRACK INBOUND!"); + warnlog(event); + session.onTrack(event, UUID); + }; + + } catch(err){ + errorlog(err); + if (!session.cleanOutput){ + warnUser("An RTC error occured"); + } + } + + session.rpcs[UUID].whep.onnegotiationneeded = requestStream; // bug: https://groups.google.com/forum/#!topic/discuss-webrtc/3-TmyjQ2SeE + + session.rpcs[UUID].whep.onicecandidate = function(event){ //event + if (event.candidate==null){ + log("END OF ICE CANDIDATES"); + return; + } + //log(event.candidate); + candidates.push(event.candidate); + }; + + log("onnegotiationneeded event setup"); + } + + var requestingStream = false; + function requestStream(event){ + if (requestingStream){ + log(event); + errorlog("onnegotiationneeded again?"); + return; + } + requestingStream = true; + warnlog("ON NEGO NEEDED"); + warnlog(event); + + try { + session.rpcs[UUID].whep.createOffer().then(async function(offer){ + return session.rpcs[UUID].whep.setLocalDescription(offer); + }).then(async function() { + //log(session.rpcs[UUID].whep.localDescription); + await sleep(6000); + var sdp = session.rpcs[UUID].whep.localDescription.sdp; + if (sdp.includes("sendrecv")){ + errorlog("Should not include sendrecv"); + sdp = sdp.replace("a=sendrecv","a=recvonly"); + sdp = sdp.replace("v=sendrecv","v=recvonly"); + } + ajax(sdp, "sdp"); + }).catch(function(err){}); + } catch(e){errorlog(e);} + } + + function ajax(dataPayload, type, callback=false){ + //log(dataPayload); + try { + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState == 4 && (this.status == 200 || this.status == 201)) { + + var contentType = this.getResponseHeader('content-type'); + responseLocation = this.getResponseHeader('location'); + + + if (contentType.startsWith("application/sdp")){ + var jsep = {}; + jsep.sdp = this.responseText; + jsep.type = "answer"; + + warnlog("Processing answer:"); + + session.rpcs[UUID].whep.setRemoteDescription(jsep).then(function(){ + warnlog("SHOULD BE CONNECTED?"); + /* var content = ""; + while (candidates.length){ + var candidate = candidates.pop(); + content += candidate.candidate; + } + if (content){ + ajax(content, "trickle-ice-sdpfrag", function(){ + }); + } */ + }).catch(function(e){log(e);}); + + } else if (contentType == "application/error"){ + if (this.responseText==432){ + warnUser("Whep in error: 432"); + } else { + warnUser("Unknown Whep In error"); + } + } else if (callback){ + callback(); + } + } + }; + if (type==="trickle-ice-sdpfrag"){ + if (responseLocation){ + xhttp.open("PATCH", whepInput, true); + } else { + xhttp.open("PATCH", whepInput, true); + } + } else { + xhttp.open("POST", whepInput, true); + } + xhttp.setRequestHeader('Content-Type', 'application/'+type); + + if (whepInputToken){ + xhttp.setRequestHeader('Authorization', 'Bearer ' + whepInputToken); + } + xhttp.onerror = function(e) { + errorlog(e); + warnUser("Whep in failed."); + }; + + xhttp.send(dataPayload); + + } catch(e){errorlog(e);} + } + + whepConnect(); + return UUID; +} +//////// +function whepOut(){ // publish to whep.vdo.ninja with obs, to use. experimental + if (!session.whepHost){return;} + warnlog("WHEP Client started"); + + var socket = null; + var connecting = false; + var failedCount = 0; + + function connect(){ + clearTimeout(connecting); + if (socket){ + if (socket.readyState === socket.OPEN){return;} + try{ + socket.close(); + } catch(e){} + } + log("Trying to load whep websocket..."); + + socket = new WebSocket("wss://whep.vdo.ninja:81"); + + socket.onclose = function (){ + failedCount+=1; + clearTimeout(connecting); + connecting = setTimeout(function(){connect();},100*(failedCount-1)); + + }; + + socket.onerror = function (e){ + console.error(e); + failedCount+=1; + clearTimeout(connecting); + connecting = setTimeout(function(){connect();},100*failedCount); + }; + + socket.onopen = function (){ + failedCount = 0; + try{ + var settings = {}; + socket.send(JSON.stringify({"join":session.whepHost})); + } catch(e){ + connecting = setTimeout(function(){connect();},1); + } + }; + + socket.addEventListener('message', async function (event) { + if (event.data){ + + var data = JSON.parse(event.data); + + if ("sdp" in data){ + var resp = await processWHEPout(data); + if (resp){ + var ret = {}; + var get = data.get; + data = {}; + if (get){ + data.get = get; + data.result = resp; + ret.callback = data; + log(ret); + socket.send(JSON.stringify(ret)); + } + } + } else if ("delete" in data){ + warnlog("WHIP Client is actively disconnecting"); + // session.closeRPC(i, true); + } + } + }); + } + connect(); +} + +async function processWHEPout(data){ // LISTEN FOR REMOTE WHIP + var msg = {}; + msg.description = {}; + msg.description.type = "offer"; + msg.description.sdp = data.sdp; + // msg.session = session.generateRandomString(5); + msg.UUID = session.generateRandomString(25); // fake + + log("setupoutgoing"); + + try { + //if (session.meshcast!=="video"){ + var tracks = false; + if (session.videoElement && session.videoElement.srcObject){ + tracks = session.videoElement.srcObject.getAudioTracks(); + } + var streamsource = false; + if (!tracks || !tracks.length){ + var audioCtx = new AudioContext(); + streamsource = destination.stream; + var destination = audioCtx.createMediaStreamDestination(); + destination.stream.getAudioTracks().forEach(trk=>{ + tracks = trk; + }); + } else { + tracks = tracks[0]; + streamsource = session.videoElement.srcObject; + } + + if (session.audioContentHint && (tracks.kind === "audio")){ + try { + tracks.contentHint = session.audioContentHint; + } catch(e){ + errorlog(e); + } + } + + if (tracks){ + try { + session.whipOut.addTransceiver(tracks, { + streams: [ streamsource ], + direction: 'sendonly' + }); + } catch(e){ + errorlog(e); + session.whipOut.addTrack(tracks); + } + } + //} + /////// + + //// video tracks + //if (session.meshcast!=="audio"){ + var tracks = false; + if (session.videoElement && session.videoElement.srcObject){ + tracks = session.videoElement.srcObject.getVideoTracks(); + } + //// + + //if (!tracks || !tracks.length){ + // tracks = getMeshcastCanvasTrack(); + //} else { + tracks = tracks[0]; + //} + + if (session.screenShareState && session.screenshareContentHint && (tracks.kind === "video")){ + try { + tracks.contentHint = session.screenshareContentHint; + } catch(e){ + errorlog(e); + } + } else if (session.contentHint && (tracks.kind === "video")){ + try { + tracks.contentHint = session.contentHint; + } catch(e){ + errorlog(e); + } + } + if (tracks){ + try { + session.whipOut.addTransceiver(tracks, { + streams: [ session.videoElement.srcObject ], + direction: 'sendonly' + }); + } catch(e){ + errorlog(e); + session.whipOut.addTrack(tracks); + } + } + //} + + session.whipOut.onnegotiationneeded = publish; // bug: https://groups.google.com/forum/#!topic/discuss-webrtc/3-TmyjQ2SeE + + session.whipOut.onicecandidate = function(event){ //event + if (event.candidate==null){ + log("END OF ICE CANDIDATES"); + return; + } + //log(event.candidate); + candidates.push(event.candidate); + }; + + } catch(e){errorlog(e);} + + session.rpcs[msg.UUID].whip = true; + var callback = null; + var promise = new Promise((resolve, reject) => { + callback = resolve; + }); + session.rpcs[msg.UUID].whipCallback = callback; + + var callback2 = null; + var promise2 = new Promise((resolve, reject) => { + callback2 = resolve; + }); + session.rpcs[msg.UUID].whipCallback2 = callback2; + + log("CONNECT PEEER"); + session.connectPeer(msg); + log("CONNECT PEEER DONE"); + + if (!session.manual || !session.director){ + window.onresize = updateMixer; + window.onorientationchange = function(){ + setTimeout(updateMixer, 200); + }; + } + + log("ICE BUNDLE PROMISE"); + setTimeout(function(UUID){ + log("ICE BUNDLE PROMISE TIMEOUT"); + if (session.rpcs[UUID].whipCallback2){ + session.rpcs[UUID].whipCallback2([...session.rpcs[UUID].iceBundle]); + clearTimeout(session.rpcs[UUID].iceTimer); + session.rpcs[UUID].iceTimer = null; + session.rpcs[UUID].iceBundle = [] + session.rpcs[UUID].whipCallback2 = null; + + } + },3000,msg.UUID); + var iceBundle = await promise2; // waiting for ICE GATHER COMPLETE + session.rpcs[msg.UUID].whipCallback2 = null; + + log("ICE BUNDLE DONE"); + log(iceBundle); + + await promise; + session.rpcs[msg.UUID].whipCallback = null; + sdpAnswer = session.rpcs[msg.UUID].localDescription.sdp; + + var insertIce = ""; + iceBundle.forEach(ice=>{ + if (ice.candidate){ + insertIce += "a="+ice.candidate+"\r\n"; + } + }); + sdpAnswer = sdpAnswer.replace("a=ice-ufrag", insertIce+"a=ice-ufrag"); + + if (sdpAnswer.includes("sendrecv")){ + errorlog("Should not include sendrecv"); + sdpAnswer = sdpAnswer.replace("a=sendrecv","a=recvonly"); + sdpAnswer = sdpAnswer.replace("v=sendrecv","v=recvonly"); + } + + log("completed"); + warnlog(sdpAnswer); + + return sdpAnswer; // return SDP answer for the remote WHIP request +} +///// +function pokePostAPI(action, data, streamID){ + var msg = {}; + msg.update = {}; + msg.update.streamID = streamID || session.streamID || null; + msg.update.action = action; + msg.update.value = data; + + try { + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState == 4 && (this.status == 200 || this.status == 201)) { + log("good"); + } else { + warnlog("post api didn't work?"); + } + }; + xhttp.open("POST", session.postApi, true); + xhttp.setRequestHeader('Content-type', 'application/json'); + + xhttp.onerror = function(e) { + errorlog(e); + }; + xhttp.send(JSON.stringify(msg)); + + } catch(e){errorlog(e);} +} + + +var queuedSendingAPIMsgs = []; +function pokeAPI(action, data, streamID = null){ + + if (session.postApi){ + pokePostAPI(action, data, streamID); + } + + if (!session.api){return;} + + if (session.apiSocket){ + try { + var msg = {}; + msg.update = {}; + msg.update.streamID = streamID || session.streamID || null; + msg.update.action = action; + msg.update.value = data; + session.apiSocket.send(JSON.stringify(msg)); + } catch(e){ + errorlog(e); + } + } else if (session.apiSocket!==null){ + queuedSendingAPIMsgs.push([action, data, streamID]); + if (queuedSendingAPIMsgs.length>20){ + queuedSendingAPIMsgs.shift(); + } + } +} + +function oscClient(){ // api.vdo.ninja api OSC (websocket / https API hotkey support). The iFrame API method provides greater customization. + if (!session.api){return;} + warnlog("oscClient started"); + + var socket = null; + var connecting = false; + var failedCount = 0; + + function connect(){ + clearTimeout(connecting); + if (socket){ + if (socket.readyState === socket.OPEN){return;} + try{ + socket.close(); + } catch(e){} + } + socket = new WebSocket(session.apiserver); + + socket.onclose = function (){ + session.apiSocket = false; + failedCount+=1; + clearTimeout(connecting); + connecting = setTimeout(function(){connect();},100*(failedCount-1)); + + }; + + socket.onerror = function (){ + failedCount+=1; + clearTimeout(connecting); + connecting = setTimeout(function(){connect();},100*failedCount); + }; + + socket.onopen = function (){ + failedCount = 0; + try{ + socket.send(JSON.stringify({"join":session.api})); + session.apiSocket = socket; + if (queuedSendingAPIMsgs.length){ + queuedSendingAPIMsgs.forEach(msg=>{ + pokeAPI(msg[0],msg[1], msg[2]); + }); + queuedSendingAPIMsgs = []; + } + pokeAPI("details", getDetailedState(session.streamID)); + } catch(e){ + connecting = setTimeout(function(){connect();},1); + } + + }; + + socket.addEventListener('message', async function (event) { + if (event.data){ + + var data = JSON.parse(event.data); + + if ("msg" in data){ + data = data.msg + } + + if ("value" in data){ + if (("action" in data) && (data.action == "layout")){ + try { + data.value = JSON.parse(data.value) || data.value; + } catch(e){} + } + } + + var resp = processMessage(data); + if (resp!==null){ + var ret = {}; + data.result = resp; + ret.callback = data; + log(ret); + socket.send(JSON.stringify(ret)); + } + } + }); + } + connect(); +} + +function setupCommands(){ + var commands = {} + + commands.raisehand = function(value=null,value2=null){ + return raisehand(); + }; + commands.togglehand = function(value=null,value2=null){ + return raisehand(); + }; + commands.togglescreenshare = function(value=null,value2=null){ + toggleScreenShare(); + return session.screenShareState; + }; + commands.chat = function(value=null,value2=null){ + toggleChat(value); + return session.chat; + }; + commands.speaker = function(value=null,value2=null){ + if (value === true) { // unmute + session.speakerMuted = false; // set + toggleSpeakerMute(true); // apply + } else if (value === false) { // mute + session.speakerMuted = true; // set + toggleSpeakerMute(true); // apply + } else if (value === "toggle") { // toggle + toggleSpeakerMute(); + } + return session.speakerMuted; + }; // mute speaker + commands.mic = function(value=null,value2=null){ + if (value === true) { // unmute + session.muted = false; // set + log(session.muted); + toggleMute(true); // apply + } else if (value === false) { // mute + session.muted = true; // set + log(session.muted); + toggleMute(true); // apply + } else if (value === "toggle") { // toggle + toggleMute(); + } + return session.muted; + }; + commands.camera = function(value=null,value2=null){ + if (value === true) { // unmute + session.videoMuted = false; // set + log(session.videoMuted); + toggleVideoMute(true); // apply + } else if (value === false) { // mute + session.videoMuted = true; // set + log(session.videoMuted); + toggleVideoMute(true); // apply + } else if (value === "toggle") { // toggle + toggleVideoMute(); + } + return session.videoMuted; + } + commands.hangup = function(value=null,value2=null){ + hangup(); + return true; + }; + commands.bitrate = function(value=null,value2=null){ + if (value===false){ + value = 0; + } else if (value===true){ + value = -1; + } else { + value = parseInt(value) || 0; + } + for (var i in session.rpcs) { + try { + session.requestRateLimit(value, i); + } catch (e) { + errorlog(e); + } + } + return value; + }; + + commands.getDetails = function(value=null,value2=null){ + return getDetailedState(); + } + + commands.getGuestList = function(value=null,value2=null){ + return getGuestList(); + } + + commands.reload = function(value=null,value2=null){ + reloadRequested(); + return true; + }; + commands.volume = function(value=null,value2=null){ + if (value===false){ + value = 0; + } else if (value===true){ + value = 100 + } else { + value = parseInt(value) || 0; + } + value = parseFloat(value/100); + for (var i in session.rpcs) { + try { + session.rpcs[i].videoElement.volume = parseFloat(value); + } catch (e) { + errorlog(e); + } + } + return value; + }; + + commands.forceKeyframe = function(value=null,value2=null){ + return session.forcePLI(); + }; + + commands.panning = function(value=null,value2=null){ + if (value===false){ + value = 90; + } else if (value===true){ + value = 90 + } else { + value = parseInt(value); + } + for (var uuid in session.rpcs) { + try { + adjustPan(uuid, value); // &panning needs to be added to enable. playback only; not mic out. + } catch (e) { + errorlog(e); + } + } + return value; + }; + + commands.record = function(value=null,value2=null){ + + if (!session.videoElement){return;} + + if (value === false) { // mute + if ("recording" in session.videoElement) { + recordLocalVideo("stop"); + } + } else if (value === true){ + if ("recording" in session.videoElement) { + // already recording + } else { + recordLocalVideo("start"); + } + } + return value; + }; + + commands.group = function(value=null,value2=null){ + if (value && (value !== "null")){ + return changeGroupDirectorAPI(value); + } + return false; + }; + + commands.joinGroup = function(value=null,value2=null){ + if (value && (value !== "null")){ + return changeGroupDirectorAPI(value, true); + } + return false; + }; + + commands.leaveGroup = function(value=null,value2=null){ + if (value && (value !== "null")){ + return changeGroupDirectorAPI(value, false); + } + return false; + }; + + commands.viewGroup = function(value=null,value2=null){ + if (value && (value !== "null")){ + return changeGroupViewDirectorAPI(value); + } + return false; + }; + + commands.joinViewGroup = function(value=null,value2=null){ + if (value && (value !== "null")){ + return changeGroupViewDirectorAPI(value, true); + } + return false; + }; + + commands.leaveViewGroup = function(value=null,value2=null){ + if (value && (value !== "null")){ + return changeGroupViewDirectorAPI(value, false); + } + return false; + }; + + commands.sendChat = function(value=null,value2=null){ + sendChat(value); + // sendChatMessage // this would add it to the chat message + return true; + }; + + commands.startRoomTimer = function(value=null,value2=null){ + getById("globalTimerDirectorToggle").value = 0; // reset + directRoomTimer(getById("globalTimerDirectorToggle"), false, value); + return true; + }; + + commands.pauseRoomTimer = function(value=null,value2=null){ + if (getById("globalTimerDirectorToggle").value == 3){ + directRoomTimer(getById("globalTimerDirectorToggle"), {ctrlKey:true}, value); + } else { + directRoomTimer(getById("globalTimerDirectorToggle"), {ctrlKey:true}, value); + } + return true; + }; + + commands.stopRoomTimer = function(value=null,value2=null){ + getById("globalTimerDirectorToggle").value = 1; // pause + directRoomTimer(getById("globalTimerDirectorToggle"), false, value); + return true; + }; + + commands.prevSlide = function(value=null,value2=null){ + var data = {}; + data.d = [176, 110, 10]; + playbackMIDI(data); + return true; + }; + + commands.nextSlide = function(value=null,value2=null){ + var data = {}; + data.d = [176, 110, 11]; + playbackMIDI(data); + return true; + }; + + commands.nextSlide = function(value=null,value2=null){ + var data = {}; + data.d = [176, 110, 11]; + playbackMIDI(data); + return true; + }; + + commands.soloVideo = function(value=null,value2=null){ + var element = getById("highlightDirector"); + if (value && (value == "toggle")){ + return requestInfocus(element); + } else if (value && (value !== "null")){ + return requestInfocus(element, null, true); + } else if (value && (value === "null")){ + return requestInfocus(element); + } else { + return requestInfocus(element, null, false); + } + return false; + }; + commands.highlight = function(value=null,value2=null){ + return commands.soloVideo(value, value2); + }; + + commands.layout = function(value=null,value2=null){ + try { + if (parseInt(value)==value){ + value = parseInt(value); + if (value ==0){ + value = false; + } else { + value -= 1; + } + } else if (typeof value === "object"){ + session.layout = value; + pokeIframeAPI("layout-updated", session.layout); + if (session.director){ + var combined = {}; + for (var i=0;i 110){ + var guestslot = command-111; + if (value == 0) { + var ele = getRightOrderedElement('[data-action-type="forward"][data--u-u-i-d]', guestslot); + if (ele) { + directMigrate(ele, true); + } + } else if (value == 1) { + var ele = getRightOrderedElement('[data-action-type="addToScene"][data-scene="1"][data--u-u-i-d]', guestslot); + if (ele) { + directEnable(ele, true); + } + } else if (value == 2) { + var ele = getRightOrderedElement('[data-action-type="mute-scene"][data--u-u-i-d]', guestslot); + if (ele) { + directMute(ele, true); + } + } else if (value == 3) { + var ele = getRightOrderedElement('[data-action-type="mute-guest"][data--u-u-i-d]', guestslot); + if (ele) { + remoteMute(ele, true); + } + } else if (value == 4) { + var ele = getRightOrderedElement('[data-action-type="hangup"][data--u-u-i-d]', guestslot); + if (ele) { + directHangup(ele, true); + } + } else if (value == 5) { + var ele = getRightOrderedElement('[data-action-type="solo-chat"][data--u-u-i-d]', guestslot); + if (ele) { + session.toggleSoloChat(ele.dataset.UUID); + } + } else if (value == 6) { + var ele = getRightOrderedElement('[data-action-type="toggle-remote-speaker"][data--u-u-i-d]', guestslot); + if (ele) { + remoteSpeakerMute(ele); + } + } else if (value == 7) { + var ele = getRightOrderedElement('[data-action-type="toggle-remote-display"][data--u-u-i-d]', guestslot); + if (ele) { + remoteDisplayMute(ele); + } + } else if (value == 8) { + var ele = getRightOrderedElement('[data-action-type="force-keyframe"][data--u-u-i-d]', guestslot); + if (ele) { + requestKeyframeScene(ele); + } + } else if (value == 12) { + var ele = getRightOrderedElement('[data-action-type="addToScene"][data-scene="2"][data--u-u-i-d]', guestslot); + if (ele) { + directEnable(ele, true); + } + } else if (value == 13) { + var ele = getRightOrderedElement('[data-action-type="addToScene"][data-scene="3"][data--u-u-i-d]', guestslot); + if (ele) { + directEnable(ele, true); + } + } else if (value == 14) { + var ele = getRightOrderedElement('[data-action-type="addToScene"][data-scene="4"][data--u-u-i-d]', guestslot); + if (ele) { + directEnable(ele, true); + } + } else if (value == 15) { + var ele = getRightOrderedElement('[data-action-type="addToScene"][data-scene="5"][data--u-u-i-d]', guestslot); + if (ele) { + directEnable(ele, true); + } + } else if (value == 16) { + var ele = getRightOrderedElement('[data-action-type="addToScene"][data-scene="6"][data--u-u-i-d]', guestslot); + if (ele) { + directEnable(ele, true); + } + } else if (value == 17) { + var ele = getRightOrderedElement('[data-action-type="addToScene"][data-scene="7"][data--u-u-i-d]', guestslot); + if (ele) { + directEnable(ele, true); + } + } else if (value == 18) { + var ele = getRightOrderedElement('[data-action-type="addToScene"][data-scene="8"][data--u-u-i-d]', guestslot); + if (ele) { + directEnable(ele, true); + } + } else if ((value => 27)) { + var ele = getRightOrderedElement('[data-action-type="volume"][data--u-u-i-d]', guestslot); + if (ele) { + ele.value = parseInt(value-27); + remoteVolume(ele); + } + } + } +} + +function sendRawMIDI(input, UUID=false, streamID=false){ + // session.sendRawMIDI(e.data.sendRawMIDI); + var msg = {}; + msg.midi = {}; + msg.midi.d = input.data; + + if ("timestamp" in input){ + msg.midi.s = input.timestamp; + } else { + msg.midi.s = Date.now(); // unix timestamp + } + + if (input.message && input.message.channel){ + msg.midi.c = input.message.channel; + } else if (input && input.channel){ + msg.midi.c = input.channel; + } + + if (UUID && session.pcs[UUID] && session.pcs[UUID].allowMIDI){ + session.sendMessage(msg, UUID); + } else if (UUID && session.rpcs[UUID] && session.rpcs[UUID].allowMIDI){ + session.sendRequest(msg, UUID); + } else if (streamID){ + for (var UID in session.rpcs){ + if (session.rpcs[UID].allowMIDI && (session.rpcs[UID].streamID === streamID)){ // specific to gstreamer code aplication + session.sendRequest(msg, UID) + return; // only one stream ID should match + } + } + } else { + var list = []; + for (var UID in session.pcs){ + if (session.pcs[UID].allowMIDI){ + if (session.sendMessage(msg, UID)){ + list.push(UID); + } + } + } + for (var UID in session.rpcs){ + if (session.rpcs[UID].allowMIDI){ // specific to gstreamer code aplication + if (!list.includes(UID)){ + session.sendRequest(msg, UID) + } + } + } + } +} +function playOutMidi(msg){ + console.log("Playing out remotely sourced MIDI"); + if (session.midiIn===true){ + if ("d" in msg){ + for (var i in WebMidi.outputs){ + try { + if ("c" in msg){ + WebMidi.outputs[i].channels[msg.c].send(msg.d); + } else { + WebMidi.outputs[i].send(msg.d); + } + } catch(e){errorlog(e);} + } + } + } else if (session.midiIn==parseInt(session.midiIn)){ + try { + var i = parseInt(session.midiIn)-1; + if ("d" in msg){ + if ("c" in msg){ + WebMidi.outputs[i].channels[msg.c].send(msg.d); + } else { + WebMidi.outputs[i].send(msg.d); + } + } + } catch(e){errorlog(e);}; + } +} +function playbackMIDI(msg, unsafe=false){ + if (session.midiIn===false && session.midiRemote===false){return;} // just in case; security + else if ((session.midiOut===session.midiIn) && (session.midiRemote===false)){return;} // avoid feedback loops + + //msg.midi.d = e.data; + //msg.midi.s = e.timestamp; + //msg.midi.t = e.type; + if (session.midiDelay && ("t" in msg)){ + var timeDelay = session.midiDelay - (Date.now() - msg.t); + if (timeDelay<=0){ + playOutMidi(msg); + } else { + setTimeout(function(msg){playOutMidi(msg)},timeDelay,msg); + } + } else { + playOutMidi(msg); + } + + if (unsafe){return;} // I don't know how midi remote works in reverse, so lets ignore it + + if (session.midiRemote==4){ + if (msg.d[0] == 176){ + midiHotkeysCommand(msg.d[1], msg.d[2]); + } + } else if (session.midiRemote==1 || session.midiRemote==2 || session.midiRemote==3){ + if (msg.d[0] == 156){ + if (msg.d[1] == 33){ + midiHotkeysNote("A1", msg.d[2]); + } else if (msg.d[1] == 55){ + midiHotkeysNote("G3", msg.d[2]); + } else if (msg.d[1] == 57){ + midiHotkeysNote("A3", msg.d[2]); + } else if (msg.d[1] == 59){ + midiHotkeysNote("B3", msg.d[2]); + } else if (msg.d[1] == 60){ + midiHotkeysNote("C4", msg.d[2]); + } else if (msg.d[1] == 62){ + midiHotkeysNote("D4", msg.d[2]); + } else if (msg.d[1] == 64){ + midiHotkeysNote("E4", msg.d[2]); + } else if (msg.d[1] == 65){ + midiHotkeysNote("F4", msg.d[2]); + } else if (msg.d[1] == 67){ + midiHotkeysNote("G4", msg.d[2]); + } else if (msg.d[1] == 69){ + midiHotkeysNote("A4", msg.d[2]); + } else if (msg.d[1] == 43){ + midiHotkeysNote("G2", msg.d[2]); + } else if (msg.d[1] == 35){ + midiHotkeysNote("B1", msg.d[2]); + } else if (msg.d[1] == 36){ + midiHotkeysNote("C2", msg.d[2]); + } else if (msg.d[1] == 38){ + midiHotkeysNote("D2", msg.d[2]); + } else if (msg.d[1] == 40){ + midiHotkeysNote("E2", msg.d[2]); + } else if (msg.d[1] == 41){ + midiHotkeysNote("F2", msg.d[2]); + } else if (msg.d[1] == 24){ + midiHotkeysNote("C1", msg.d[2]); + } + } + } + //var output = WebMidi.getOutputById("123456789"); + //output = WebMidi.getOutputByName("Axiom Pro 25 Ext Out"); + //output = WebMidi.outputs[0]; +} + +function addEventToAll(targets, trigger, callback) { // js helper + const target = document.querySelectorAll(targets); + var triggers = trigger.split(" "); + for (let i = 0; i < target.length; i++) { + for (let j = 0; j < triggers.length; j++) { + setTimeout(function(t1,t2){ + t1.addEventListener(t2, function(e) { + callback(e, t1); + }); + },0,target[i],triggers[j]); + } + } +} + +function insertAfter(newNode, existingNode) { + existingNode.parentNode.insertBefore(newNode, existingNode.nextSibling); +} +addEventToAll(".column", 'click', function(e, ele) { + if (ele.classList.contains("skip-animation")) { + return; + } + try { + var bounding_box = ele.getBoundingClientRect(); + } catch(e){ + return; + } + ele.style.top = bounding_box.top + "px"; + ele.style.left = (bounding_box.left - 20) + "px"; + ele.classList.add('in-animation'); + ele.classList.remove('pointer'); + ele.classList.remove('rounded'); + + if (document.getElementById("empty-container")) { + getById("empty-container").parentNode.removeChild(getById("empty-container")); + } + var empty = document.createElement("DIV"); + empty.id = "empty-container"; + empty.className = "column"; + ele.parentNode.insertBefore(empty, ele.nextSibling); + const styles = "\ + @keyframes outlightbox {\ + 0% {\ + height: 100%;\ + width: 100%;\ + top: 0px;\ + left: 0px;\ + }\ + 50% {\ + height: 200px;\ + top: " + bounding_box.y + "px;\ + }\ + 100% {\ + height: 200px;\ + width: " + bounding_box.width + "px;\ + top: " + bounding_box.y + "px;\ + left: " + bounding_box.x + "px;\ + }\ + }\ + "; + if (document.getElementById('lightbox-animations')) { + getById("lightbox-animations").innerHTML = styles; + } + document.body.style.overflow = "hidden"; +}); +addEventToAll(".close", 'click', function(e, ele) { + cleanupMediaTracks(); + + document.querySelectorAll(".hidden2").forEach(ele2=>{ + ele2.classList.remove("hidden2"); + }); + + ele.style.display = "none"; + mapToAll(".container-inner", function(target) { + target.style.display = "none"; + }); + document.body.style.overflow = "auto"; + var bounding_box = getById("empty-container").parentNode.getBoundingClientRect(); + setTimeout(function() { // just smoothes things out; breathing room to clean up things first. + ele.parentNode.classList.add('out-animation'); + }, 1); + ele.parentNode.style.top = bounding_box.top + 'px'; + ele.parentNode.style.left = bounding_box.left + 'px'; + e.stopPropagation(); +}); +addEventToAll(".column", 'animationend', function(e, ele) { + if (e.animationName == 'inlightbox') { + ele.classList.add("skip-animation"); + mapToAll(".close", function(target) { + target.style.display = "block"; + }, ele); + document.querySelectorAll("#header, #miniTaskBarm, #credits, .columnfade").forEach(ele2=>{ + if (ele2!==ele){ + ele2.classList.add("hidden2"); + } + }); + mapToAll(".container-inner", function(target) { + target.style.display = "block"; + }, ele); + } else if (e.animationName == 'outlightbox') { + ele.classList.remove('in-animation'); + ele.classList.remove('out-animation'); + ele.classList.remove("skip-animation"); + ele.classList.remove('columnfade'); + ele.classList.add('pointer'); + ele.classList.add('rounded'); + getById("empty-container").parentNode.removeChild(getById("empty-container")); + getById("lightbox-animations").sheet.deleteRule(0); + } +}); +addEventToAll("#audioSource", 'mousedown touchend focusin focusout', function(e, ele) { + var state = getById('multiselect-trigger').dataset.state || 0; // Does this return TRU instead??. GAH. #TODO: + if (state == 0) { + getById('multiselect-trigger').dataset.state = 1; + getById('multiselect-trigger').classList.add('open'); + getById('multiselect-trigger').classList.remove('closed'); + mapToAll('.chevron', function(ele) { + ele.classList.remove('bottom'); + }, parentElement = getById('multiselect-trigger')); + mapToAll('.multiselect-contents', function(ele) { + ele.style.display = "block"; + mapToAll('input[type="checkbox"]', function(ele2) { + ele2.parentNode.style.display = "block"; + ele2.style.display = "inline-block"; + }, ele); + }, parentElement = getById('multiselect-trigger').parentNode); + } + e.stopPropagation(); + //e.preventDefault(); +}); +addEventToAll("#audioSource3", 'mousedown touchend focusin focusout', function(e, ele) { + var state = getById('multiselect-trigger3').dataset.state || 0; // Does this return TRU instead??. GAH. #TODO: + if (state == 0) { + getById('multiselect-trigger3').dataset.state = 1; + getById('multiselect-trigger3').classList.add('open'); + getById('multiselect-trigger3').classList.remove('closed'); + mapToAll(".chevron", function(target) { + target.classList.remove('bottom'); + }, getById('multiselect-trigger3')); + mapToAll(".multiselect-contents", function(target) { + target.style.display = "block"; + }, getById('multiselect-trigger3').parentNode); + mapToAll(".multiselect-contents", function(target) { + mapToAll('input[type="checkbox"]', function(target2) { + target2.style.display = "inline-block"; + target2.parentNode.style.display = "block"; + }, target); + }, getById('multiselect-trigger3').parentNode); + } + e.stopPropagation(); + //e.preventDefault(); +}); +addEventToAll("#multiselect-trigger", 'mousedown touchend focusin focusout', function(e, ele) { + var state = ele.dataset.state || 0; // Does this return TRU instead??. GAH. #TODO: + if (state == 0) { // open the dropdown + ele.dataset.state = 1; + ele.classList.add('open'); + ele.classList.remove('closed'); + mapToAll(".chevron", function(target) { + target.classList.remove('bottom'); + }, getById('multiselect-trigger')); + mapToAll(".multiselect-contents", function(target) { + target.style.display = "block"; + }, ele.parentNode); + mapToAll(".multiselect-contents", function(target) { + mapToAll('input[type="checkbox"]', function(target2) { + target2.style.display = "inline-block"; + target2.parentNode.style.display = "block"; + }, target); + }, ele.parentNode); + } else { // close the dropdown + ele.dataset.state = 0; + ele.classList.add('closed'); + ele.classList.remove('open'); + mapToAll(".chevron", function(target) { + target.classList.add('bottom'); + }, ele); + mapToAll(".multiselect-contents", function(target) { + mapToAll('input[type="checkbox"]', function(target2) { + target2.style.display = "none"; + if (!target2.checked) { + target2.parentNode.style.display = "none"; + } + }, target); + }, ele.parentNode); + } + e.preventDefault(); + e.stopPropagation(); +}); +addEventToAll("#multiselect-trigger3", 'mousedown touchend focusin focusout', function(e, ele) { + var state = ele.dataset.state || 0; // Does this return TRU instead??. GAH. #TODO: + if (state == 0) { // open the dropdown + ele.dataset.state = 1; + ele.classList.add('open'); + ele.classList.remove('closed'); + mapToAll(".chevron", function(target) { + target.classList.remove('bottom'); + }, ele); + mapToAll(".multiselect-contents", function(target) { + target.style.display = "block"; + }, ele.parentNode); + mapToAll(".multiselect-contents", function(target) { + mapToAll('input[type="checkbox"]', function(target2) { + target2.style.display = "inline-block"; + target2.parentNode.style.display = "block"; + }, target); + }, ele.parentNode); + } else { // close the dropdown + ele.dataset.state = 0; + ele.classList.add('closed'); + ele.classList.remove('open'); + mapToAll(".chevron", function(target) { + target.classList.add('bottom'); + }, ele); + mapToAll(".multiselect-contents", function(target) { + mapToAll('input[type="checkbox"]', function(target2) { + target2.style.display = "none"; + if (!target2.checked) { + target2.parentNode.style.display = "none"; + } + }, target); + }, ele.parentNode); + } + e.preventDefault(); + e.stopPropagation(); +}); + + +function getSenders2(UUID){ + var fixedSenders = []; + var isAlt = false; + if (!(UUID in session.pcs)){return fixedSenders;} + if ("realUUID" in session.pcs[UUID]){ + isAlt=true; + UUID = session.pcs[UUID].realUUID; + if (!(UUID in session.pcs)){return fixedSenders;} + } + var senders = session.pcs[UUID].getSenders(); + + if (isAlt){ + senders.forEach((sender)=>{ + if (sender.track && sender.track.id){ + if (sender.track.id in screenshareTracks) { // I'm not going to change track.kind, since OBS isn't part of this list + fixedSenders.push(sender); + } + } + }); + } else { + senders.forEach((sender)=>{ + if (sender.track && sender.track.id){ + if (!(sender.track.id in screenshareTracks)){ + fixedSenders.push(sender); + } + } + }); + } + + return fixedSenders; +} + +function getReceivers2(UUID){ + var fixedReceivers = []; + var isAlt = false; + var ssTracks = []; + if ("realUUID" in session.rpcs[UUID]){ + isAlt=true; + UUID = session.rpcs[UUID].realUUID; + if (!("screenIndexes" in session.rpcs[UUID])){ + errorlog("this is supposed to be a screen share, but no screen share index was found"); + return; + } + ssTracks = session.rpcs[UUID].screenIndexes; + } else if (("screenIndexes" in session.rpcs[UUID]) && session.rpcs[UUID].screenIndexes){ + ssTracks = session.rpcs[UUID].screenIndexes; + } + + var receivers = session.rpcs[UUID].getReceivers(); + + if (isAlt){ + for (var i=0;i { + resolve([]); + }); + } + } + + /* if (session.audioContentHint && tracks.length){ + tracks.forEach(trk=>{ + try { + + trk.contentHint = session.audioContentHint; + } catch(e){ + errorlog(e); + } + }); + } */ + + var senders = getSenders2(UUID+"_screen"); + var tracks = session.screenStream.getTracks(); + + for (var i=0;i= 3) { // lowest + video.width = { + ideal: 320 + }; + video.height = { + ideal: 180 + }; + } + + if (session.width) { + video.width = { + ideal: session.width + }; + } + if (session.height) { + video.height = { + ideal: session.height + }; + } + + var constraints = { // this part is a bit annoying. Do I use the same settings? I can add custom setting controls here later + audio: { + echoCancellation: true, // we want to cancel echo, since this is a secondary stream + autoGainControl: false, + noiseSuppression: false + }, + video: video + //,cursor: {exact: "none"} + }; + + try { + let supportedConstraints = navigator.mediaDevices.getSupportedConstraints(); + if (supportedConstraints.cursor) { + if (session.screensharecursor){ + constraints.video.cursor = ["always", "motion"]; + } else { + constraints.video.cursor = "never"; + } + } + if (session.suppressLocalAudioPlayback && supportedConstraints.suppressLocalAudioPlayback){ + constraints.audio.suppressLocalAudioPlayback = true; + } + // + if (session.preferCurrentTab){ + constraints.preferCurrentTab = true; + } + if (session.selfBrowserSurface){ + constraints.selfBrowserSurface = session.selfBrowserSurface; // exclude or include + } + if (session.surfaceSwitching){ + constraints.surfaceSwitching = session.surfaceSwitching; // exclude or include + } + if (session.systemAudio){ + constraints.systemAudio = session.systemAudio; // exclude or include + } + if (session.displaySurface && supportedConstraints.displaySurface){ + constraints.video.displaySurface = session.displaySurface; // monitor, window, or browser + } + } catch(e){ + warnlog("navigator.mediaDevices.getSupportedConstraints() not supported"); + } + + if (session.echoCancellation === false) { + constraints.audio.echoCancellation = false; + } + if (session.autoGainControl === true) { + constraints.audio.autoGainControl = true; + } + if (session.noiseSuppression === true) { + constraints.audio.noiseSuppression = true; + } + //if (audio == false) { + // constraints.audio = false; + //} + + + var overrideFramerate = false; + if ((session.frameRate !== false) && (session.maxframeRate != false)){ + overrideFramerate = session.frameRate; + constraints.video.frameRate = { + ideal: session.maxframeRate, + max: session.maxframeRate + }; + } else if (session.frameRate !== false) { + constraints.video.frameRate = session.frameRate; + } else if (session.maxframeRate != false){ + constraints.video.frameRate = { + ideal: session.maxframeRate, + max: session.maxframeRate + }; + } else { + constraints.video.frameRate = { + ideal: 60 + }; + } + + if (session.screenshareVideoOnly){ + constraints.audio = false; + } + + if (session.forceAspectRatio){ // await updateCameraConstraints("aspectRatio", session.forceAspectRatio); + if (constraints.video && constraints.video!==true){ + + + constraints.video.aspectRatio = { ideal: parseFloat(session.forceAspectRatio)}; + + + if (constraints.video.width && !session.width){ + delete constraints.video.width; + } else if (constraints.video.height && !session.height){ + delete constraints.video.height; + } + } + } + + if ((constraints.video!==false) && (Object.keys(constraints.video).length==0)){ + constraints.video = true; + } + + if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1) { + if (!ElectronDesktopCapture){ + if (!(session.cleanOutput)) { + warnUser("Enable Elevated Privileges to allow screen-sharing. (right click this window to see that option)"); + } + return false; + } + } + + log("sstype3 screen share"); + log(constraints); + + navigator.mediaDevices.getDisplayMedia(constraints).then(async function(stream) { + + + try { + var constraint = {}; + if (session.forceAspectRatio && (session.forceScreenShareAspectRatio===null)){ + constraint.aspectRatio = parseFloat(session.forceAspectRatio); + } else if (session.forceScreenShareAspectRatio){ + constraint.aspectRatio = parseFloat(session.forceScreenShareAspectRatio); + } + if (overrideFramerate){ + constraint.frameRate = overrideFramerate; + } + if (Object.keys(constraint).length){ + await stream.getVideoTracks()[0].applyConstraints({ + advanced: [constraint] + }); + log({ + advanced: [constraint] + }); + } + } catch(e){errorlog(e);} + + + session.screenShareState = true; + session.screenStream = stream; + pokeIframeAPI('screen-share-state', session.screenShareState, null, session.streamID); + + //if (!session.screenVideoElement){ + // session.screenVideoElement = createVideoElement() + //} + try { + stream.getVideoTracks()[0].onended = function () { + stopSecondScreenshare(); + }; + } catch(e){log("No Video selected; screensharing?");} + + session.screenStream.getTracks().forEach(function(track){ + screenshareTracks[track.id] = true; // obs isn't included, so no point to check track.kind + }); + for (UUID in session.pcs){ + createSecondStream2(UUID); + } + + if (!firsttime){ + var msg = {}; + msg.screenStopped = false; + session.sendMessage(msg); + } else if (!session.screenShareElement){ + session.screenShareElement = createVideoElement(); + session.screenShareElement.muted = true; + session.screenShareElement.autoplay = true; + session.screenShareElement.controls = session.showControls || false; + + session.screenShareElement.id = "screensharesource"; + session.screenShareElement.dataset.sid = session.streamID + ":s"; + + if (typeof session.volume == "number"){ + session.screenShareElement.volume = session.volume; + } else { + session.screenShareElement.volume = 1.0; // play audio automatically + } + session.screenShareElement.classList.add("tile"); + session.screenShareElement.setAttribute("playsinline",""); + session.screenShareElement.controlTimer = null; + + session.screenShareElement.dataset.menu = "context-menu-video"; + if (!session.cleanOutput){ + session.screenShareElement.classList.add("task"); // this adds the right-click menu + } + createDirectorScreenshareOnlyBox(); + + if (document.getElementById("videoScreenContainer_director")){ + getById("videoScreenContainer_director").appendChild(session.screenShareElement); + } + + session.screenShareElement.onpause = (event) => { // prevent things from pausing; human or other + if (!((event.ctrlKey) || (event.metaKey) )){ + log("Video paused; auto playing"); + event.currentTarget.play().then(_ => { + log("playing 10"); + }).catch(warnlog); + } + } + + session.screenShareElement.addEventListener('click', function(e) { + log("click"); + try { + if ((e.ctrlKey)||(e.metaKey)){ + e.preventDefault(); + + var [menu, innerMenu] = statsMenuCreator(); + + menu.interval = setInterval(printMyStats,session.statsInterval, innerMenu, true); + + printMyStats(innerMenu, true); + e.stopPropagation(); + return false; + } + } catch(e){errorlog(e);} + }); + + session.screenShareElement.touchTimeOut = null; + session.screenShareElement.touchLastTap = 0; + session.screenShareElement.touchCount = 0; + + session.screenShareElement.addEventListener('touchend', function(event) { + if (session.disableMouseEvents){return;} + log("touched"); + + //document.ontouchup = null; + //document.onmouseup = null; + document.onmousemove = null; + document.ontouchmove = null; + + var currentTime = new Date().getTime(); + var tapLength = currentTime - session.screenShareElement.touchLastTap; + clearTimeout(session.screenShareElement.touchTimeOut); + if (tapLength < 500 && tapLength > 0) { + /// + log("double touched"); + session.screenShareElement.touchCount+=1; + event.preventDefault(); + if (session.screenShareElement.touchCount<5){ + session.screenShareElement.touchLastTap = currentTime; + return false; + } + session.screenShareElement.touchLastTap = 0; + session.screenShareElement.touchCount=0; + + var [menu, innerMenu] = statsMenuCreator(); + + menu.interval = setInterval(printMyStats,session.statsInterval, innerMenu, true); + + printMyStats(innerMenu, true); + event.stopPropagation(); + return false; + ////// + } else { + session.screenShareElement.touchCount=1; + session.screenShareElement.touchLastTap = currentTime; + + session.screenShareElement.touchTimeOut = setTimeout(function(vv) { + clearTimeout(vv.touchTimeOut); + vv.touchLastTap = 0; + vv.touchCount=0; + }, 5000, session.screenShareElement); + + } + }); + } + + firsttime=false + + session.screenShareElement.srcObject = session.screenStream; + + getById("screensharebutton").classList.add("green"); + getById("screensharebutton").ariaPressed = "true"; + getById("screensharebutton").title = getTranslation("stop-screen-sharing"); + + getById("screenshare2button").classList.add("green"); + getById("screenshare2button").ariaPressed = "true"; + getById("screenshare2button").title = getTranslation("stop-screen-sharing"); + + getById("screenshare3button").classList.add("green"); + getById("screenshare3button").ariaPressed = "true"; + getById("screenshare3button").title = getTranslation("stop-screen-sharing"); + + + if (session.autorecord || session.autorecordlocal){ + log("AUTO RECORD START SSTYPE3"); + setTimeout(function(s){ + if (!session.screenStream){return;} + try { + var ele = document.getElementById("recordLocalScreenbutton"); + if (ele){ + ele.classList.add("red"); + ele.classList.remove("hidden"); + if (!ele.vid){ + var v = createVideoElement(); + v.muted = true; + v.srcObject = s; + ele.vid = v; + } + if (ele.vid.recorder || ele.vid.recording){ + ele.vid.recorder.stop(); + ele.classList.remove("red"); + ele.classList.add("hidden"); + ele.vid = null; + } else { + var videoKbps = 4000; + if (session.recordLocal !== false) { + videoKbps = session.recordLocal; + } + recordLocalVideo(null, videoKbps, ele.vid) + } + } + } catch(e){errorlog(e);} + },2000, session.screenStream); + } + + setTimeout(function(){ + updateMixer(); + },100); + + setTimeout(function(){ + updateMixer(); + },1000); + + }).catch(function(err) { + errorlog(err); + }); + } else { // removing a screen + stopSecondScreenshare(); + } +} +function recordLocalScreenStopRecord(){ + var ele = document.getElementById("recordLocalScreenbutton"); + if (ele){ + try { + ele.classList.remove("red"); + ele.classList.add("hidden"); + if (ele.vid){ + if (ele.vid.recorder || ele.vid.recording){ + ele.vid.recorder.stop(); + } + ele.vid = null; + } + }catch(e){errorlog(e);} + } +} +function stopSecondScreenshare(){ + var msg = {}; + msg.screenStopped = true; + session.sendMessage(msg); + + var ele = document.getElementById("recordLocalScreenbutton"); + if (ele){ + try { + ele.classList.remove("red"); + ele.classList.add("hidden"); + if (ele.vid){ + if (ele.vid.recorder || ele.vid.recording){ + ele.vid.recorder.stop(); + } + ele.vid = null; + } + }catch(e){errorlog(e);} + } + + session.screenStream.getTracks().forEach(function(track) { // previous video track; saving it. Must remove the track at some point. + for (UUID in session.pcs){ + if (!("realUUID" in session.pcs[UUID])){continue;} // not a screen share, so skip + var senders = getSenders2(UUID); + senders.forEach((sender) => { // I suppose there could be a race condition between negotiating and updating this. if joining at the same time as changnig streams? + if (sender.track && sender.track.kind == "video") { + sender.track.enabled = false; + } + }); + } + if (track.id in screenshareTracks) { // obs isn't included, so no point to check track.kind + session.screenStream.removeTrack(track); + track.stop(); + screenshareTracks[track.id] = false; + } + }); + session.screenStream = false; + session.screenShareState = false; + pokeIframeAPI('screen-share-state', session.screenShareState, null, session.streamID); + + getById("screensharebutton").classList.remove("green"); + getById("screensharebutton").ariaPressed = "false"; + getById("screensharebutton").title = getTranslation("share-a-screen"); + + getById("screenshare2button").classList.remove("green"); + getById("screenshare2button").ariaPressed = "false"; + getById("screenshare2button").title = getTranslation("share-a-screen"); + + getById("screenshare3button").classList.remove("green"); + getById("screenshare3button").ariaPressed = "false"; + getById("screenshare3button").title = getTranslation("share-a-screen"); + + setTimeout(function(){ + updateMixer(); + },100); + + setTimeout(function(){ + updateMixer(); + },1000); +} + +function createControlBoxScreenshare(UUID, soloLink, streamID) { + if (document.getElementById("deleteme")) { + getById("deleteme").parentNode.removeChild(getById("deleteme")); + } + var controls = getById("controls_blank").cloneNode(true); + controls.classList.remove("hidden"); + controls.id = "controls_" + UUID; + + var container = document.createElement("div"); + container.className = "vidcon directorMargins"; + container.id = "container_" + UUID; // needed to delete on user disconnect + container.UUID = UUID; + container.dataset.UUID = UUID; + + if (session.orderby){ + try { + var added = false; + for (var i=0;i streamID.toLowerCase()){ + getById("guestFeeds").insertBefore(container, getById("guestFeeds").children[i]); + added = true; + break; + } + } + } + + } + if (!added){ + getById("guestFeeds").appendChild(container); + } + } catch(e){ + getById("guestFeeds").appendChild(container); + } + } else { + getById("guestFeeds").appendChild(container); + } + + controls.querySelector(".controlsGrid").classList.add("notmain"); + + if (!session.rpcs[UUID].voiceMeter) { + if (session.meterStyle==1){ + session.rpcs[UUID].voiceMeter = getById("voiceMeterTemplate2").cloneNode(true); + } else { + session.rpcs[UUID].voiceMeter = getById("voiceMeterTemplate").cloneNode(true); + session.rpcs[UUID].voiceMeter.style.opacity = 0; + if (session.meterStyle==2){ + session.rpcs[UUID].voiceMeter.classList.add("video-meter-2"); + session.rpcs[UUID].voiceMeter.classList.remove("video-meter"); + } else { + session.rpcs[UUID].voiceMeter.classList.add("video-meter-director"); + } + } + session.rpcs[UUID].voiceMeter.id = "voiceMeter_" + UUID; + session.rpcs[UUID].voiceMeter.dataset.level = 0; + session.rpcs[UUID].voiceMeter.classList.remove("hidden"); + } + + session.rpcs[UUID].remoteMuteElement = getById("muteStateTemplate").cloneNode(true); + session.rpcs[UUID].remoteMuteElement.id = ""; + session.rpcs[UUID].remoteMuteElement.style.top = "5px"; + session.rpcs[UUID].remoteMuteElement.style.right = "7px"; + + session.rpcs[UUID].remoteVideoMuteElement = getById("videoMuteStateTemplate").cloneNode(true); + session.rpcs[UUID].remoteVideoMuteElement.id = ""; + session.rpcs[UUID].remoteVideoMuteElement.style.top = "5px"; + session.rpcs[UUID].remoteVideoMuteElement.style.right = "28px"; + + session.rpcs[UUID].remoteRaisedHandElement = getById("raisedHandTemplate").cloneNode(true); + session.rpcs[UUID].remoteRaisedHandElement.id = ""; + session.rpcs[UUID].remoteRaisedHandElement.style.top = "5px"; + session.rpcs[UUID].remoteRaisedHandElement.style.right = "49px"; + + var videoContainer = document.createElement("div"); + videoContainer.id = "videoContainer_" + UUID; // needed to delete on user disconnect + videoContainer.style.margin = "0"; + videoContainer.style.position = "relative"; + videoContainer.style.minHeight = "30px"; + + var iframeDetails = document.createElement("div"); + iframeDetails.id = "iframeDetails_" + UUID; // needed to delete on user disconnect + iframeDetails.className = "iframeDetails hidden"; + + //controls.innerHTML += ""; + //controls.innerHTML += ""; + + var handsID = "hands_" + UUID; + + controls.innerHTML += "
    "; + + if (session.hidesololinks==false){ + controls.innerHTML += "
    \ + " + sanitizeChat(soloLink) + "\ + \ +
    "; + } + + controls.innerHTML += "\ +
    "; + + controls.querySelectorAll('[data-action-type]').forEach((ele) => { // give action buttons some self-reference + ele.dataset.UUID = UUID; + ele.dataset.sid = streamID; + }); + + + var buttons = ""; + if (session.slotmode){ + var slots = document.querySelectorAll("div.slotsbar[data-slot]"); + var biggestSlot=0; + var slotDefault = null; + if (streamID in session.pastSlots){ + slotDefault = session.pastSlots[streamID]; + } + var allSlots = []; + if (session.slotmode==1){ + for (var i=0;ibiggestSlot){ + biggestSlot = parseInt(slots[i].dataset.slot); + } + if (slotDefault===parseInt(slots[i].dataset.slot)){ + slotDefault = null; + } + allSlots.push(parseInt(slots[i].dataset.slot)); + } + biggestSlot+=1; + } + if (slotDefault!==null){ + biggestSlot = slotDefault; + } else if (session.slotmode==1){ + var bestfree = 0; + for (var i =1; i<=biggestSlot; i++){ + if (allSlots.includes(i)){ + continue; + } else { + bestfree = i; + break; + } + } + biggestSlot = bestfree; + } + var slotName = "slot: "+biggestSlot; + if (!biggestSlot){ + slotName = "unset"; + } + session.pastSlots[streamID] = biggestSlot; + + buttons += "
    \ +
    "; + } + + buttons += "
    ID: " + streamID + "\ + \ + \ + \ +
    "; + + container.innerHTML = buttons; + updateLockedElements(); + + + var videoContainerControlBox = document.createElement("div"); + videoContainerControlBox.className = "controlVideoBox"; + container.containerControlBox = videoContainerControlBox + + container.appendChild(videoContainerControlBox); + videoContainerControlBox.appendChild(videoContainer); + + if (session.signalMeter){ + if (!session.rpcs[UUID].signalMeter){ + session.rpcs[UUID].signalMeter = getById("signalMeterTemplate").cloneNode(true); + session.rpcs[UUID].signalMeter.id = "signalMeter_" + UUID; + session.rpcs[UUID].signalMeter.dataset.level = 0; + session.rpcs[UUID].signalMeter.classList.remove("hidden"); + session.rpcs[UUID].signalMeter.dataset.UUID = UUID; + session.rpcs[UUID].signalMeter.title = getTranslation("signal-meter"); + + //if (session.rpcs[UUID].stats.info && session.rpcs[UUID].stats.info.cpu_maxed){ + // session.rpcs[UUID].signalMeter.dataset.cpu = "1"; + //} + + session.rpcs[UUID].signalMeter.addEventListener('click', function(e) { // show stats of video if double clicked + log("clicked signal meter"); + try { + e.preventDefault(); + if (session.statsMenu !==false){ + var uid = e.currentTarget.dataset.UUID; + if ("stats" in session.rpcs[uid]){ + + var [menu, innerMenu] = statsMenuCreator(); + printViewStats(innerMenu, uid ); + menu.interval = setInterval(printViewStats, session.statsInterval, innerMenu, uid); + + } + } + e.stopPropagation(); + return false; + + } catch(e){errorlog(e);} + }); + } + videoContainer.appendChild(session.rpcs[UUID].signalMeter); + } + + if (session.batteryMeter){ + //////// + if (!session.rpcs[UUID].batteryMeter){ + session.rpcs[UUID].batteryMeter = getById("batteryMeterTemplate").cloneNode(true); + session.rpcs[UUID].batteryMeter.id = "batteryMeter_" + UUID; + /* + if (session.rpcs[UUID].stats.info && (session.rpcs[UUID].stats.info.power_level!==null)){ + var level = session.rpcs[UUID].batteryMeter.querySelector(".battery-level"); + if (level){ + var value = session.rpcs[UUID].stats.info.power_level; + if (value > 100){value = 100;} + else if (value < 0){ value = 0;} + level.style.height = parseInt(value)+"%"; + if (value<10){ + session.rpcs[UUID].batteryMeter.classList.add("alert"); + } else if (value<25){ + session.rpcs[UUID].batteryMeter.classList.add("warn"); + } + if (value<100){ + session.rpcs[UUID].batteryMeter.classList.remove("hidden"); + } + session.rpcs[UUID].batteryMeter.title = (Math.round(value*10)/10)+"% battery remaining"; + } + } + if (session.rpcs[UUID].stats.info && ("plugged_in" in session.rpcs[UUID].stats.info) && (session.rpcs[UUID].stats.info.plugged_in===false)){ + session.rpcs[UUID].batteryMeter.dataset.plugged = "0"; + session.rpcs[UUID].batteryMeter.classList.remove("hidden"); + } else { + session.rpcs[UUID].batteryMeter.dataset.plugged = "1"; + } + */ + batteryMeterInfoUpdate(UUID); + + } + videoContainer.appendChild(session.rpcs[UUID].batteryMeter); + } + + if (session.showConnections){ + if (!session.rpcs[UUID].connectionDetails){ + createConnectionDetailsEle(UUID); + } + videoContainer.appendChild(session.rpcs[UUID].connectionDetails); + } + + videoContainer.appendChild(session.rpcs[UUID].voiceMeter); + videoContainer.appendChild(session.rpcs[UUID].remoteMuteElement); + videoContainer.appendChild(session.rpcs[UUID].remoteVideoMuteElement); + videoContainer.appendChild(session.rpcs[UUID].remoteRaisedHandElement); + videoContainer.appendChild(iframeDetails); + videoContainer.appendChild(session.rpcs[UUID].videoElement); + container.appendChild(controls); + + session.group.forEach(group=>{ + var ele = controls.querySelector('[data-action-type="toggle-group"][data--u-u-i-d="'+UUID+'"][data-group="'+group+'"]'); + if (!ele){ + var newGroup = htmlToElement(''); + + var added = false; + container.querySelectorAll('.customGroup>[data-group]').forEach(ele=>{ + log(ele); + if (!added && ele.dataset.group>group+""){ + ele.parentNode.insertBefore(newGroup, ele); + added = true; + } + }); + if (!added){ + var newGroupCon = container.querySelector(".customGroup"); + if (!newGroupCon){ + newGroupCon = document.createElement("div"); + newGroupCon.classList.add("customGroup"); + container.appendChild(newGroupCon); + } + newGroupCon.appendChild(newGroup); + } + } + }); + + initSceneList(UUID); + pokeIframeAPI("control-box", true, UUID); +} diff --git a/lineawesome/LICENSE.txt b/app/static/lineawesome/LICENSE.txt similarity index 100% rename from lineawesome/LICENSE.txt rename to app/static/lineawesome/LICENSE.txt diff --git a/lineawesome/Readme.md b/app/static/lineawesome/Readme.md similarity index 100% rename from lineawesome/Readme.md rename to app/static/lineawesome/Readme.md diff --git a/lineawesome/css/line-awesome.css b/app/static/lineawesome/css/line-awesome.css similarity index 99% rename from lineawesome/css/line-awesome.css rename to app/static/lineawesome/css/line-awesome.css index 3e9d858..41074f7 100644 --- a/lineawesome/css/line-awesome.css +++ b/app/static/lineawesome/css/line-awesome.css @@ -4410,8 +4410,8 @@ readers do not read off random characters that represent icons */ font-style: normal; font-weight: 900; font-display: auto; - src: url("../fonts/la-solid-900.eot"); - src: url("../fonts/la-solid-900.eot?#iefix") format("embedded-opentype"), url("../fonts/la-solid-900.woff2") format("woff2"), url("../fonts/la-solid-900.woff") format("woff"), url("../fonts/la-solid-900.ttf") format("truetype"), url("../fonts/la-solid-900.svg#lineawesome") format("svg"); } + src: url("la-solid-900.eot"); + src: url("la-solid-900.eot?#iefix") format("embedded-opentype"), url("la-solid-900.woff2") format("woff2"), url("la-solid-900.woff") format("woff"), url("la-solid-900.ttf") format("truetype"), url("la-solid-900.svg#lineawesome") format("svg"); } .la, .las { diff --git a/lineawesome/css/line-awesome.min.css b/app/static/lineawesome/css/line-awesome.min.css similarity index 62% rename from lineawesome/css/line-awesome.min.css rename to app/static/lineawesome/css/line-awesome.min.css index 5636d52..234f304 100644 --- a/lineawesome/css/line-awesome.min.css +++ b/app/static/lineawesome/css/line-awesome.min.css @@ -1 +1 @@ -.la,.lab,.lad,.lal,.lar,.las{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.la-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.la-xs{font-size:.75em}.la-sm{font-size:.875em}.la-1x{font-size:1em}.la-2x{font-size:2em}.la-3x{font-size:3em}.la-4x{font-size:4em}.la-5x{font-size:5em}.la-6x{font-size:6em}.la-7x{font-size:7em}.la-8x{font-size:8em}.la-9x{font-size:9em}.la-10x{font-size:10em}.la-fw{text-align:center;width:1.25em}.la-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.la-ul>li{position:relative}.la-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.la-border{border:solid .08em #eee;border-radius:.1em;padding:.2em .25em .15em}.la-pull-left{float:left}.la-pull-right{float:right}.la.la-pull-left,.lab.la-pull-left,.lal.la-pull-left,.lar.la-pull-left,.las.la-pull-left{margin-right:.3em}.la.la-pull-right,.lab.la-pull-right,.lal.la-pull-right,.lar.la-pull-right,.las.la-pull-right{margin-left:.3em}.la-spin{-webkit-animation:la-spin 2s infinite linear;animation:la-spin 2s infinite linear}.la-pulse{-webkit-animation:la-spin 1s infinite steps(8);animation:la-spin 1s infinite steps(8)}@-webkit-keyframes la-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes la-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.la-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.la-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.la-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.la-flip-horizontal{-webkit-transform:scale(-1,1);transform:scale(-1,1)}.la-flip-vertical{-webkit-transform:scale(1,-1);transform:scale(1,-1)}.la-flip-both,.la-flip-horizontal.la-flip-vertical{-webkit-transform:scale(-1,-1);transform:scale(-1,-1)}:root .la-flip-both,:root .la-flip-horizontal,:root .la-flip-vertical,:root .la-rotate-180,:root .la-rotate-270,:root .la-rotate-90{-webkit-filter:none;filter:none}.la-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.la-stack-1x,.la-stack-2x{left:0;position:absolute;text-align:center;width:100%}.la-stack-1x{line-height:inherit}.la-stack-2x{font-size:2em}.la-inverse{color:#fff}.la-500px:before{content:"\f26e"}.la-accessible-icon:before{content:"\f368"}.la-accusoft:before{content:"\f369"}.la-acquisitions-incorporated:before{content:"\f6af"}.la-ad:before{content:"\f641"}.la-address-book:before{content:"\f2b9"}.la-address-card:before{content:"\f2bb"}.la-adjust:before{content:"\f042"}.la-adn:before{content:"\f170"}.la-adobe:before{content:"\f778"}.la-adversal:before{content:"\f36a"}.la-affiliatetheme:before{content:"\f36b"}.la-air-freshener:before{content:"\f5d0"}.la-airbnb:before{content:"\f834"}.la-algolia:before{content:"\f36c"}.la-align-center:before{content:"\f037"}.la-align-justify:before{content:"\f039"}.la-align-left:before{content:"\f036"}.la-align-right:before{content:"\f038"}.la-alipay:before{content:"\f642"}.la-allergies:before{content:"\f461"}.la-amazon:before{content:"\f270"}.la-amazon-pay:before{content:"\f42c"}.la-ambulance:before{content:"\f0f9"}.la-american-sign-language-interpreting:before{content:"\f2a3"}.la-amilia:before{content:"\f36d"}.la-anchor:before{content:"\f13d"}.la-android:before{content:"\f17b"}.la-angellist:before{content:"\f209"}.la-angle-double-down:before{content:"\f103"}.la-angle-double-left:before{content:"\f100"}.la-angle-double-right:before{content:"\f101"}.la-angle-double-up:before{content:"\f102"}.la-angle-down:before{content:"\f107"}.la-angle-left:before{content:"\f104"}.la-angle-right:before{content:"\f105"}.la-angle-up:before{content:"\f106"}.la-angry:before{content:"\f556"}.la-angrycreative:before{content:"\f36e"}.la-angular:before{content:"\f420"}.la-ankh:before{content:"\f644"}.la-app-store:before{content:"\f36f"}.la-app-store-ios:before{content:"\f370"}.la-apper:before{content:"\f371"}.la-apple:before{content:"\f179"}.la-apple-alt:before{content:"\f5d1"}.la-apple-pay:before{content:"\f415"}.la-archive:before{content:"\f187"}.la-archway:before{content:"\f557"}.la-arrow-alt-circle-down:before{content:"\f358"}.la-arrow-alt-circle-left:before{content:"\f359"}.la-arrow-alt-circle-right:before{content:"\f35a"}.la-arrow-alt-circle-up:before{content:"\f35b"}.la-arrow-circle-down:before{content:"\f0ab"}.la-arrow-circle-left:before{content:"\f0a8"}.la-arrow-circle-right:before{content:"\f0a9"}.la-arrow-circle-up:before{content:"\f0aa"}.la-arrow-down:before{content:"\f063"}.la-arrow-left:before{content:"\f060"}.la-arrow-right:before{content:"\f061"}.la-arrow-up:before{content:"\f062"}.la-arrows-alt:before{content:"\f0b2"}.la-arrows-alt-h:before{content:"\f337"}.la-arrows-alt-v:before{content:"\f338"}.la-artstation:before{content:"\f77a"}.la-assistive-listening-systems:before{content:"\f2a2"}.la-asterisk:before{content:"\f069"}.la-asymmetrik:before{content:"\f372"}.la-at:before{content:"\f1fa"}.la-atlas:before{content:"\f558"}.la-atlassian:before{content:"\f77b"}.la-atom:before{content:"\f5d2"}.la-audible:before{content:"\f373"}.la-audio-description:before{content:"\f29e"}.la-autoprefixer:before{content:"\f41c"}.la-avianex:before{content:"\f374"}.la-aviato:before{content:"\f421"}.la-award:before{content:"\f559"}.la-aws:before{content:"\f375"}.la-baby:before{content:"\f77c"}.la-baby-carriage:before{content:"\f77d"}.la-backspace:before{content:"\f55a"}.la-backward:before{content:"\f04a"}.la-bacon:before{content:"\f7e5"}.la-balance-scale:before{content:"\f24e"}.la-balance-scale-left:before{content:"\f515"}.la-balance-scale-right:before{content:"\f516"}.la-ban:before{content:"\f05e"}.la-band-aid:before{content:"\f462"}.la-bandcamp:before{content:"\f2d5"}.la-barcode:before{content:"\f02a"}.la-bars:before{content:"\f0c9"}.la-baseball-ball:before{content:"\f433"}.la-basketball-ball:before{content:"\f434"}.la-bath:before{content:"\f2cd"}.la-battery-empty:before{content:"\f244"}.la-battery-full:before{content:"\f240"}.la-battery-half:before{content:"\f242"}.la-battery-quarter:before{content:"\f243"}.la-battery-three-quarters:before{content:"\f241"}.la-battle-net:before{content:"\f835"}.la-bed:before{content:"\f236"}.la-beer:before{content:"\f0fc"}.la-behance:before{content:"\f1b4"}.la-behance-square:before{content:"\f1b5"}.la-bell:before{content:"\f0f3"}.la-bell-slash:before{content:"\f1f6"}.la-bezier-curve:before{content:"\f55b"}.la-bible:before{content:"\f647"}.la-bicycle:before{content:"\f206"}.la-biking:before{content:"\f84a"}.la-bimobject:before{content:"\f378"}.la-binoculars:before{content:"\f1e5"}.la-biohazard:before{content:"\f780"}.la-birthday-cake:before{content:"\f1fd"}.la-bitbucket:before{content:"\f171"}.la-bitcoin:before{content:"\f379"}.la-bity:before{content:"\f37a"}.la-black-tie:before{content:"\f27e"}.la-blackberry:before{content:"\f37b"}.la-blender:before{content:"\f517"}.la-blender-phone:before{content:"\f6b6"}.la-blind:before{content:"\f29d"}.la-blog:before{content:"\f781"}.la-blogger:before{content:"\f37c"}.la-blogger-b:before{content:"\f37d"}.la-bluetooth:before{content:"\f293"}.la-bluetooth-b:before{content:"\f294"}.la-bold:before{content:"\f032"}.la-bolt:before{content:"\f0e7"}.la-bomb:before{content:"\f1e2"}.la-bone:before{content:"\f5d7"}.la-bong:before{content:"\f55c"}.la-book:before{content:"\f02d"}.la-book-dead:before{content:"\f6b7"}.la-book-medical:before{content:"\f7e6"}.la-book-open:before{content:"\f518"}.la-book-reader:before{content:"\f5da"}.la-bookmark:before{content:"\f02e"}.la-bootstrap:before{content:"\f836"}.la-border-all:before{content:"\f84c"}.la-border-none:before{content:"\f850"}.la-border-style:before{content:"\f853"}.la-bowling-ball:before{content:"\f436"}.la-box:before{content:"\f466"}.la-box-open:before{content:"\f49e"}.la-boxes:before{content:"\f468"}.la-braille:before{content:"\f2a1"}.la-brain:before{content:"\f5dc"}.la-bread-slice:before{content:"\f7ec"}.la-briefcase:before{content:"\f0b1"}.la-briefcase-medical:before{content:"\f469"}.la-broadcast-tower:before{content:"\f519"}.la-broom:before{content:"\f51a"}.la-brush:before{content:"\f55d"}.la-btc:before{content:"\f15a"}.la-buffer:before{content:"\f837"}.la-bug:before{content:"\f188"}.la-building:before{content:"\f1ad"}.la-bullhorn:before{content:"\f0a1"}.la-bullseye:before{content:"\f140"}.la-burn:before{content:"\f46a"}.la-buromobelexperte:before{content:"\f37f"}.la-bus:before{content:"\f207"}.la-bus-alt:before{content:"\f55e"}.la-business-time:before{content:"\f64a"}.la-buy-n-large:before{content:"\f8a6"}.la-buysellads:before{content:"\f20d"}.la-calculator:before{content:"\f1ec"}.la-calendar:before{content:"\f133"}.la-calendar-alt:before{content:"\f073"}.la-calendar-check:before{content:"\f274"}.la-calendar-day:before{content:"\f783"}.la-calendar-minus:before{content:"\f272"}.la-calendar-plus:before{content:"\f271"}.la-calendar-times:before{content:"\f273"}.la-calendar-week:before{content:"\f784"}.la-camera:before{content:"\f030"}.la-camera-retro:before{content:"\f083"}.la-campground:before{content:"\f6bb"}.la-canadian-maple-leaf:before{content:"\f785"}.la-candy-cane:before{content:"\f786"}.la-cannabis:before{content:"\f55f"}.la-capsules:before{content:"\f46b"}.la-car:before{content:"\f1b9"}.la-car-alt:before{content:"\f5de"}.la-car-battery:before{content:"\f5df"}.la-car-crash:before{content:"\f5e1"}.la-car-side:before{content:"\f5e4"}.la-caret-down:before{content:"\f0d7"}.la-caret-left:before{content:"\f0d9"}.la-caret-right:before{content:"\f0da"}.la-caret-square-down:before{content:"\f150"}.la-caret-square-left:before{content:"\f191"}.la-caret-square-right:before{content:"\f152"}.la-caret-square-up:before{content:"\f151"}.la-caret-up:before{content:"\f0d8"}.la-carrot:before{content:"\f787"}.la-cart-arrow-down:before{content:"\f218"}.la-cart-plus:before{content:"\f217"}.la-cash-register:before{content:"\f788"}.la-cat:before{content:"\f6be"}.la-cc-amazon-pay:before{content:"\f42d"}.la-cc-amex:before{content:"\f1f3"}.la-cc-apple-pay:before{content:"\f416"}.la-cc-diners-club:before{content:"\f24c"}.la-cc-discover:before{content:"\f1f2"}.la-cc-jcb:before{content:"\f24b"}.la-cc-mastercard:before{content:"\f1f1"}.la-cc-paypal:before{content:"\f1f4"}.la-cc-stripe:before{content:"\f1f5"}.la-cc-visa:before{content:"\f1f0"}.la-centercode:before{content:"\f380"}.la-centos:before{content:"\f789"}.la-certificate:before{content:"\f0a3"}.la-chair:before{content:"\f6c0"}.la-chalkboard:before{content:"\f51b"}.la-chalkboard-teacher:before{content:"\f51c"}.la-charging-station:before{content:"\f5e7"}.la-chart-area:before{content:"\f1fe"}.la-chart-bar:before{content:"\f080"}.la-chart-line:before{content:"\f201"}.la-chart-pie:before{content:"\f200"}.la-check:before{content:"\f00c"}.la-check-circle:before{content:"\f058"}.la-check-double:before{content:"\f560"}.la-check-square:before{content:"\f14a"}.la-cheese:before{content:"\f7ef"}.la-chess:before{content:"\f439"}.la-chess-bishop:before{content:"\f43a"}.la-chess-board:before{content:"\f43c"}.la-chess-king:before{content:"\f43f"}.la-chess-knight:before{content:"\f441"}.la-chess-pawn:before{content:"\f443"}.la-chess-queen:before{content:"\f445"}.la-chess-rook:before{content:"\f447"}.la-chevron-circle-down:before{content:"\f13a"}.la-chevron-circle-left:before{content:"\f137"}.la-chevron-circle-right:before{content:"\f138"}.la-chevron-circle-up:before{content:"\f139"}.la-chevron-down:before{content:"\f078"}.la-chevron-left:before{content:"\f053"}.la-chevron-right:before{content:"\f054"}.la-chevron-up:before{content:"\f077"}.la-child:before{content:"\f1ae"}.la-chrome:before{content:"\f268"}.la-chromecast:before{content:"\f838"}.la-church:before{content:"\f51d"}.la-circle:before{content:"\f111"}.la-circle-notch:before{content:"\f1ce"}.la-city:before{content:"\f64f"}.la-clinic-medical:before{content:"\f7f2"}.la-clipboard:before{content:"\f328"}.la-clipboard-check:before{content:"\f46c"}.la-clipboard-list:before{content:"\f46d"}.la-clock:before{content:"\f017"}.la-clone:before{content:"\f24d"}.la-closed-captioning:before{content:"\f20a"}.la-cloud:before{content:"\f0c2"}.la-cloud-download-alt:before{content:"\f381"}.la-cloud-meatball:before{content:"\f73b"}.la-cloud-moon:before{content:"\f6c3"}.la-cloud-moon-rain:before{content:"\f73c"}.la-cloud-rain:before{content:"\f73d"}.la-cloud-showers-heavy:before{content:"\f740"}.la-cloud-sun:before{content:"\f6c4"}.la-cloud-sun-rain:before{content:"\f743"}.la-cloud-upload-alt:before{content:"\f382"}.la-cloudscale:before{content:"\f383"}.la-cloudsmith:before{content:"\f384"}.la-cloudversify:before{content:"\f385"}.la-cocktail:before{content:"\f561"}.la-code:before{content:"\f121"}.la-code-branch:before{content:"\f126"}.la-codepen:before{content:"\f1cb"}.la-codiepie:before{content:"\f284"}.la-coffee:before{content:"\f0f4"}.la-cog:before{content:"\f013"}.la-cogs:before{content:"\f085"}.la-coins:before{content:"\f51e"}.la-columns:before{content:"\f0db"}.la-comment:before{content:"\f075"}.la-comment-alt:before{content:"\f27a"}.la-comment-dollar:before{content:"\f651"}.la-comment-dots:before{content:"\f4ad"}.la-comment-medical:before{content:"\f7f5"}.la-comment-slash:before{content:"\f4b3"}.la-comments:before{content:"\f086"}.la-comments-dollar:before{content:"\f653"}.la-compact-disc:before{content:"\f51f"}.la-compass:before{content:"\f14e"}.la-compress:before{content:"\f066"}.la-compress-arrows-alt:before{content:"\f78c"}.la-concierge-bell:before{content:"\f562"}.la-confluence:before{content:"\f78d"}.la-connectdevelop:before{content:"\f20e"}.la-contao:before{content:"\f26d"}.la-cookie:before{content:"\f563"}.la-cookie-bite:before{content:"\f564"}.la-copy:before{content:"\f0c5"}.la-copyright:before{content:"\f1f9"}.la-cotton-bureau:before{content:"\f89e"}.la-couch:before{content:"\f4b8"}.la-cpanel:before{content:"\f388"}.la-creative-commons:before{content:"\f25e"}.la-creative-commons-by:before{content:"\f4e7"}.la-creative-commons-nc:before{content:"\f4e8"}.la-creative-commons-nc-eu:before{content:"\f4e9"}.la-creative-commons-nc-jp:before{content:"\f4ea"}.la-creative-commons-nd:before{content:"\f4eb"}.la-creative-commons-pd:before{content:"\f4ec"}.la-creative-commons-pd-alt:before{content:"\f4ed"}.la-creative-commons-remix:before{content:"\f4ee"}.la-creative-commons-sa:before{content:"\f4ef"}.la-creative-commons-sampling:before{content:"\f4f0"}.la-creative-commons-sampling-plus:before{content:"\f4f1"}.la-creative-commons-share:before{content:"\f4f2"}.la-creative-commons-zero:before{content:"\f4f3"}.la-credit-card:before{content:"\f09d"}.la-critical-role:before{content:"\f6c9"}.la-crop:before{content:"\f125"}.la-crop-alt:before{content:"\f565"}.la-cross:before{content:"\f654"}.la-crosshairs:before{content:"\f05b"}.la-crow:before{content:"\f520"}.la-crown:before{content:"\f521"}.la-crutch:before{content:"\f7f7"}.la-css3:before{content:"\f13c"}.la-css3-alt:before{content:"\f38b"}.la-cube:before{content:"\f1b2"}.la-cubes:before{content:"\f1b3"}.la-cut:before{content:"\f0c4"}.la-cuttlefish:before{content:"\f38c"}.la-d-and-d:before{content:"\f38d"}.la-d-and-d-beyond:before{content:"\f6ca"}.la-dashcube:before{content:"\f210"}.la-database:before{content:"\f1c0"}.la-deaf:before{content:"\f2a4"}.la-delicious:before{content:"\f1a5"}.la-democrat:before{content:"\f747"}.la-deploydog:before{content:"\f38e"}.la-deskpro:before{content:"\f38f"}.la-desktop:before{content:"\f108"}.la-dev:before{content:"\f6cc"}.la-deviantart:before{content:"\f1bd"}.la-dharmachakra:before{content:"\f655"}.la-dhl:before{content:"\f790"}.la-diagnoses:before{content:"\f470"}.la-diaspora:before{content:"\f791"}.la-dice:before{content:"\f522"}.la-dice-d20:before{content:"\f6cf"}.la-dice-d6:before{content:"\f6d1"}.la-dice-five:before{content:"\f523"}.la-dice-four:before{content:"\f524"}.la-dice-one:before{content:"\f525"}.la-dice-six:before{content:"\f526"}.la-dice-three:before{content:"\f527"}.la-dice-two:before{content:"\f528"}.la-digg:before{content:"\f1a6"}.la-digital-ocean:before{content:"\f391"}.la-digital-tachograph:before{content:"\f566"}.la-directions:before{content:"\f5eb"}.la-discord:before{content:"\f392"}.la-discourse:before{content:"\f393"}.la-divide:before{content:"\f529"}.la-dizzy:before{content:"\f567"}.la-dna:before{content:"\f471"}.la-dochub:before{content:"\f394"}.la-docker:before{content:"\f395"}.la-dog:before{content:"\f6d3"}.la-dollar-sign:before{content:"\f155"}.la-dolly:before{content:"\f472"}.la-dolly-flatbed:before{content:"\f474"}.la-donate:before{content:"\f4b9"}.la-door-closed:before{content:"\f52a"}.la-door-open:before{content:"\f52b"}.la-dot-circle:before{content:"\f192"}.la-dove:before{content:"\f4ba"}.la-download:before{content:"\f019"}.la-draft2digital:before{content:"\f396"}.la-drafting-compass:before{content:"\f568"}.la-dragon:before{content:"\f6d5"}.la-draw-polygon:before{content:"\f5ee"}.la-dribbble:before{content:"\f17d"}.la-dribbble-square:before{content:"\f397"}.la-dropbox:before{content:"\f16b"}.la-drum:before{content:"\f569"}.la-drum-steelpan:before{content:"\f56a"}.la-drumstick-bite:before{content:"\f6d7"}.la-drupal:before{content:"\f1a9"}.la-dumbbell:before{content:"\f44b"}.la-dumpster:before{content:"\f793"}.la-dumpster-fire:before{content:"\f794"}.la-dungeon:before{content:"\f6d9"}.la-dyalog:before{content:"\f399"}.la-earlybirds:before{content:"\f39a"}.la-ebay:before{content:"\f4f4"}.la-edge:before{content:"\f282"}.la-edit:before{content:"\f044"}.la-egg:before{content:"\f7fb"}.la-eject:before{content:"\f052"}.la-elementor:before{content:"\f430"}.la-ellipsis-h:before{content:"\f141"}.la-ellipsis-v:before{content:"\f142"}.la-ello:before{content:"\f5f1"}.la-ember:before{content:"\f423"}.la-empire:before{content:"\f1d1"}.la-envelope:before{content:"\f0e0"}.la-envelope-open:before{content:"\f2b6"}.la-envelope-open-text:before{content:"\f658"}.la-envelope-square:before{content:"\f199"}.la-envira:before{content:"\f299"}.la-equals:before{content:"\f52c"}.la-eraser:before{content:"\f12d"}.la-erlang:before{content:"\f39d"}.la-ethereum:before{content:"\f42e"}.la-ethernet:before{content:"\f796"}.la-etsy:before{content:"\f2d7"}.la-euro-sign:before{content:"\f153"}.la-evernote:before{content:"\f839"}.la-exchange-alt:before{content:"\f362"}.la-exclamation:before{content:"\f12a"}.la-exclamation-circle:before{content:"\f06a"}.la-exclamation-triangle:before{content:"\f071"}.la-expand:before{content:"\f065"}.la-expand-arrows-alt:before{content:"\f31e"}.la-expeditedssl:before{content:"\f23e"}.la-external-link-alt:before{content:"\f35d"}.la-external-link-square-alt:before{content:"\f360"}.la-eye:before{content:"\f06e"}.la-eye-dropper:before{content:"\f1fb"}.la-eye-slash:before{content:"\f070"}.la-facebook:before{content:"\f09a"}.la-facebook-f:before{content:"\f39e"}.la-facebook-messenger:before{content:"\f39f"}.la-facebook-square:before{content:"\f082"}.la-fan:before{content:"\f863"}.la-fantasy-flight-games:before{content:"\f6dc"}.la-fast-backward:before{content:"\f049"}.la-fast-forward:before{content:"\f050"}.la-fax:before{content:"\f1ac"}.la-feather:before{content:"\f52d"}.la-feather-alt:before{content:"\f56b"}.la-fedex:before{content:"\f797"}.la-fedora:before{content:"\f798"}.la-female:before{content:"\f182"}.la-fighter-jet:before{content:"\f0fb"}.la-figma:before{content:"\f799"}.la-file:before{content:"\f15b"}.la-file-alt:before{content:"\f15c"}.la-file-archive:before{content:"\f1c6"}.la-file-audio:before{content:"\f1c7"}.la-file-code:before{content:"\f1c9"}.la-file-contract:before{content:"\f56c"}.la-file-csv:before{content:"\f6dd"}.la-file-download:before{content:"\f56d"}.la-file-excel:before{content:"\f1c3"}.la-file-export:before{content:"\f56e"}.la-file-image:before{content:"\f1c5"}.la-file-import:before{content:"\f56f"}.la-file-invoice:before{content:"\f570"}.la-file-invoice-dollar:before{content:"\f571"}.la-file-medical:before{content:"\f477"}.la-file-medical-alt:before{content:"\f478"}.la-file-pdf:before{content:"\f1c1"}.la-file-powerpoint:before{content:"\f1c4"}.la-file-prescription:before{content:"\f572"}.la-file-signature:before{content:"\f573"}.la-file-upload:before{content:"\f574"}.la-file-video:before{content:"\f1c8"}.la-file-word:before{content:"\f1c2"}.la-fill:before{content:"\f575"}.la-fill-drip:before{content:"\f576"}.la-film:before{content:"\f008"}.la-filter:before{content:"\f0b0"}.la-fingerprint:before{content:"\f577"}.la-fire:before{content:"\f06d"}.la-fire-alt:before{content:"\f7e4"}.la-fire-extinguisher:before{content:"\f134"}.la-firefox:before{content:"\f269"}.la-first-aid:before{content:"\f479"}.la-first-order:before{content:"\f2b0"}.la-first-order-alt:before{content:"\f50a"}.la-firstdraft:before{content:"\f3a1"}.la-fish:before{content:"\f578"}.la-fist-raised:before{content:"\f6de"}.la-flag:before{content:"\f024"}.la-flag-checkered:before{content:"\f11e"}.la-flag-usa:before{content:"\f74d"}.la-flask:before{content:"\f0c3"}.la-flickr:before{content:"\f16e"}.la-flipboard:before{content:"\f44d"}.la-flushed:before{content:"\f579"}.la-fly:before{content:"\f417"}.la-folder:before{content:"\f07b"}.la-folder-minus:before{content:"\f65d"}.la-folder-open:before{content:"\f07c"}.la-folder-plus:before{content:"\f65e"}.la-font:before{content:"\f031"}.la-font-awesome:before{content:"\f2b4"}.la-font-awesome-alt:before{content:"\f35c"}.la-font-awesome-flag:before{content:"\f425"}.la-font-awesome-logo-full:before{content:"\f4e6"}.la-fonticons:before{content:"\f280"}.la-fonticons-fi:before{content:"\f3a2"}.la-football-ball:before{content:"\f44e"}.la-fort-awesome:before{content:"\f286"}.la-fort-awesome-alt:before{content:"\f3a3"}.la-forumbee:before{content:"\f211"}.la-forward:before{content:"\f04e"}.la-foursquare:before{content:"\f180"}.la-free-code-camp:before{content:"\f2c5"}.la-freebsd:before{content:"\f3a4"}.la-frog:before{content:"\f52e"}.la-frown:before{content:"\f119"}.la-frown-open:before{content:"\f57a"}.la-fulcrum:before{content:"\f50b"}.la-funnel-dollar:before{content:"\f662"}.la-futbol:before{content:"\f1e3"}.la-galactic-republic:before{content:"\f50c"}.la-galactic-senate:before{content:"\f50d"}.la-gamepad:before{content:"\f11b"}.la-gas-pump:before{content:"\f52f"}.la-gavel:before{content:"\f0e3"}.la-gem:before{content:"\f3a5"}.la-genderless:before{content:"\f22d"}.la-get-pocket:before{content:"\f265"}.la-gg:before{content:"\f260"}.la-gg-circle:before{content:"\f261"}.la-ghost:before{content:"\f6e2"}.la-gift:before{content:"\f06b"}.la-gifts:before{content:"\f79c"}.la-git:before{content:"\f1d3"}.la-git-alt:before{content:"\f841"}.la-git-square:before{content:"\f1d2"}.la-github:before{content:"\f09b"}.la-github-alt:before{content:"\f113"}.la-github-square:before{content:"\f092"}.la-gitkraken:before{content:"\f3a6"}.la-gitlab:before{content:"\f296"}.la-gitter:before{content:"\f426"}.la-glass-cheers:before{content:"\f79f"}.la-glass-martini:before{content:"\f000"}.la-glass-martini-alt:before{content:"\f57b"}.la-glass-whiskey:before{content:"\f7a0"}.la-glasses:before{content:"\f530"}.la-glide:before{content:"\f2a5"}.la-glide-g:before{content:"\f2a6"}.la-globe:before{content:"\f0ac"}.la-globe-africa:before{content:"\f57c"}.la-globe-americas:before{content:"\f57d"}.la-globe-asia:before{content:"\f57e"}.la-globe-europe:before{content:"\f7a2"}.la-gofore:before{content:"\f3a7"}.la-golf-ball:before{content:"\f450"}.la-goodreads:before{content:"\f3a8"}.la-goodreads-g:before{content:"\f3a9"}.la-google:before{content:"\f1a0"}.la-google-drive:before{content:"\f3aa"}.la-google-play:before{content:"\f3ab"}.la-google-plus:before{content:"\f2b3"}.la-google-plus-g:before{content:"\f0d5"}.la-google-plus-square:before{content:"\f0d4"}.la-google-wallet:before{content:"\f1ee"}.la-gopuram:before{content:"\f664"}.la-graduation-cap:before{content:"\f19d"}.la-gratipay:before{content:"\f184"}.la-grav:before{content:"\f2d6"}.la-greater-than:before{content:"\f531"}.la-greater-than-equal:before{content:"\f532"}.la-grimace:before{content:"\f57f"}.la-grin:before{content:"\f580"}.la-grin-alt:before{content:"\f581"}.la-grin-beam:before{content:"\f582"}.la-grin-beam-sweat:before{content:"\f583"}.la-grin-hearts:before{content:"\f584"}.la-grin-squint:before{content:"\f585"}.la-grin-squint-tears:before{content:"\f586"}.la-grin-stars:before{content:"\f587"}.la-grin-tears:before{content:"\f588"}.la-grin-tongue:before{content:"\f589"}.la-grin-tongue-squint:before{content:"\f58a"}.la-grin-tongue-wink:before{content:"\f58b"}.la-grin-wink:before{content:"\f58c"}.la-grip-horizontal:before{content:"\f58d"}.la-grip-lines:before{content:"\f7a4"}.la-grip-lines-vertical:before{content:"\f7a5"}.la-grip-vertical:before{content:"\f58e"}.la-gripfire:before{content:"\f3ac"}.la-grunt:before{content:"\f3ad"}.la-guitar:before{content:"\f7a6"}.la-gulp:before{content:"\f3ae"}.la-h-square:before{content:"\f0fd"}.la-hacker-news:before{content:"\f1d4"}.la-hacker-news-square:before{content:"\f3af"}.la-hackerrank:before{content:"\f5f7"}.la-hamburger:before{content:"\f805"}.la-hammer:before{content:"\f6e3"}.la-hamsa:before{content:"\f665"}.la-hand-holding:before{content:"\f4bd"}.la-hand-holding-heart:before{content:"\f4be"}.la-hand-holding-usd:before{content:"\f4c0"}.la-hand-lizard:before{content:"\f258"}.la-hand-middle-finger:before{content:"\f806"}.la-hand-paper:before{content:"\f256"}.la-hand-peace:before{content:"\f25b"}.la-hand-point-down:before{content:"\f0a7"}.la-hand-point-left:before{content:"\f0a5"}.la-hand-point-right:before{content:"\f0a4"}.la-hand-point-up:before{content:"\f0a6"}.la-hand-pointer:before{content:"\f25a"}.la-hand-rock:before{content:"\f255"}.la-hand-scissors:before{content:"\f257"}.la-hand-spock:before{content:"\f259"}.la-hands:before{content:"\f4c2"}.la-hands-helping:before{content:"\f4c4"}.la-handshake:before{content:"\f2b5"}.la-hanukiah:before{content:"\f6e6"}.la-hard-hat:before{content:"\f807"}.la-hashtag:before{content:"\f292"}.la-hat-cowboy:before{content:"\f8c0"}.la-hat-cowboy-side:before{content:"\f8c1"}.la-hat-wizard:before{content:"\f6e8"}.la-haykal:before{content:"\f666"}.la-hdd:before{content:"\f0a0"}.la-heading:before{content:"\f1dc"}.la-headphones:before{content:"\f025"}.la-headphones-alt:before{content:"\f58f"}.la-headset:before{content:"\f590"}.la-heart:before{content:"\f004"}.la-heart-broken:before{content:"\f7a9"}.la-heartbeat:before{content:"\f21e"}.la-helicopter:before{content:"\f533"}.la-highlighter:before{content:"\f591"}.la-hiking:before{content:"\f6ec"}.la-hippo:before{content:"\f6ed"}.la-hips:before{content:"\f452"}.la-hire-a-helper:before{content:"\f3b0"}.la-history:before{content:"\f1da"}.la-hockey-puck:before{content:"\f453"}.la-holly-berry:before{content:"\f7aa"}.la-home:before{content:"\f015"}.la-hooli:before{content:"\f427"}.la-hornbill:before{content:"\f592"}.la-horse:before{content:"\f6f0"}.la-horse-head:before{content:"\f7ab"}.la-hospital:before{content:"\f0f8"}.la-hospital-alt:before{content:"\f47d"}.la-hospital-symbol:before{content:"\f47e"}.la-hot-tub:before{content:"\f593"}.la-hotdog:before{content:"\f80f"}.la-hotel:before{content:"\f594"}.la-hotjar:before{content:"\f3b1"}.la-hourglass:before{content:"\f254"}.la-hourglass-end:before{content:"\f253"}.la-hourglass-half:before{content:"\f252"}.la-hourglass-start:before{content:"\f251"}.la-house-damage:before{content:"\f6f1"}.la-houzz:before{content:"\f27c"}.la-hryvnia:before{content:"\f6f2"}.la-html5:before{content:"\f13b"}.la-hubspot:before{content:"\f3b2"}.la-i-cursor:before{content:"\f246"}.la-ice-cream:before{content:"\f810"}.la-icicles:before{content:"\f7ad"}.la-icons:before{content:"\f86d"}.la-id-badge:before{content:"\f2c1"}.la-id-card:before{content:"\f2c2"}.la-id-card-alt:before{content:"\f47f"}.la-igloo:before{content:"\f7ae"}.la-image:before{content:"\f03e"}.la-images:before{content:"\f302"}.la-imdb:before{content:"\f2d8"}.la-inbox:before{content:"\f01c"}.la-indent:before{content:"\f03c"}.la-industry:before{content:"\f275"}.la-infinity:before{content:"\f534"}.la-info:before{content:"\f129"}.la-info-circle:before{content:"\f05a"}.la-instagram:before{content:"\f16d"}.la-intercom:before{content:"\f7af"}.la-internet-explorer:before{content:"\f26b"}.la-invision:before{content:"\f7b0"}.la-ioxhost:before{content:"\f208"}.la-italic:before{content:"\f033"}.la-itch-io:before{content:"\f83a"}.la-itunes:before{content:"\f3b4"}.la-itunes-note:before{content:"\f3b5"}.la-java:before{content:"\f4e4"}.la-jedi:before{content:"\f669"}.la-jedi-order:before{content:"\f50e"}.la-jenkins:before{content:"\f3b6"}.la-jira:before{content:"\f7b1"}.la-joget:before{content:"\f3b7"}.la-joint:before{content:"\f595"}.la-joomla:before{content:"\f1aa"}.la-journal-whills:before{content:"\f66a"}.la-js:before{content:"\f3b8"}.la-js-square:before{content:"\f3b9"}.la-jsfiddle:before{content:"\f1cc"}.la-kaaba:before{content:"\f66b"}.la-kaggle:before{content:"\f5fa"}.la-key:before{content:"\f084"}.la-keybase:before{content:"\f4f5"}.la-keyboard:before{content:"\f11c"}.la-keycdn:before{content:"\f3ba"}.la-khanda:before{content:"\f66d"}.la-kickstarter:before{content:"\f3bb"}.la-kickstarter-k:before{content:"\f3bc"}.la-kiss:before{content:"\f596"}.la-kiss-beam:before{content:"\f597"}.la-kiss-wink-heart:before{content:"\f598"}.la-kiwi-bird:before{content:"\f535"}.la-korvue:before{content:"\f42f"}.la-landmark:before{content:"\f66f"}.la-language:before{content:"\f1ab"}.la-laptop:before{content:"\f109"}.la-laptop-code:before{content:"\f5fc"}.la-laptop-medical:before{content:"\f812"}.la-laravel:before{content:"\f3bd"}.la-lastfm:before{content:"\f202"}.la-lastfm-square:before{content:"\f203"}.la-laugh:before{content:"\f599"}.la-laugh-beam:before{content:"\f59a"}.la-laugh-squint:before{content:"\f59b"}.la-laugh-wink:before{content:"\f59c"}.la-layer-group:before{content:"\f5fd"}.la-leaf:before{content:"\f06c"}.la-leanpub:before{content:"\f212"}.la-lemon:before{content:"\f094"}.la-less:before{content:"\f41d"}.la-less-than:before{content:"\f536"}.la-less-than-equal:before{content:"\f537"}.la-level-down-alt:before{content:"\f3be"}.la-level-up-alt:before{content:"\f3bf"}.la-life-ring:before{content:"\f1cd"}.la-lightbulb:before{content:"\f0eb"}.la-line:before{content:"\f3c0"}.la-link:before{content:"\f0c1"}.la-linkedin:before{content:"\f08c"}.la-linkedin-in:before{content:"\f0e1"}.la-linode:before{content:"\f2b8"}.la-linux:before{content:"\f17c"}.la-lira-sign:before{content:"\f195"}.la-list:before{content:"\f03a"}.la-list-alt:before{content:"\f022"}.la-list-ol:before{content:"\f0cb"}.la-list-ul:before{content:"\f0ca"}.la-location-arrow:before{content:"\f124"}.la-lock:before{content:"\f023"}.la-lock-open:before{content:"\f3c1"}.la-long-arrow-alt-down:before{content:"\f309"}.la-long-arrow-alt-left:before{content:"\f30a"}.la-long-arrow-alt-right:before{content:"\f30b"}.la-long-arrow-alt-up:before{content:"\f30c"}.la-low-vision:before{content:"\f2a8"}.la-luggage-cart:before{content:"\f59d"}.la-lyft:before{content:"\f3c3"}.la-magento:before{content:"\f3c4"}.la-magic:before{content:"\f0d0"}.la-magnet:before{content:"\f076"}.la-mail-bulk:before{content:"\f674"}.la-mailchimp:before{content:"\f59e"}.la-male:before{content:"\f183"}.la-mandalorian:before{content:"\f50f"}.la-map:before{content:"\f279"}.la-map-marked:before{content:"\f59f"}.la-map-marked-alt:before{content:"\f5a0"}.la-map-marker:before{content:"\f041"}.la-map-marker-alt:before{content:"\f3c5"}.la-map-pin:before{content:"\f276"}.la-map-signs:before{content:"\f277"}.la-markdown:before{content:"\f60f"}.la-marker:before{content:"\f5a1"}.la-mars:before{content:"\f222"}.la-mars-double:before{content:"\f227"}.la-mars-stroke:before{content:"\f229"}.la-mars-stroke-h:before{content:"\f22b"}.la-mars-stroke-v:before{content:"\f22a"}.la-mask:before{content:"\f6fa"}.la-mastodon:before{content:"\f4f6"}.la-maxcdn:before{content:"\f136"}.la-mdb:before{content:"\f8ca"}.la-medal:before{content:"\f5a2"}.la-medapps:before{content:"\f3c6"}.la-medium:before{content:"\f23a"}.la-medium-m:before{content:"\f3c7"}.la-medkit:before{content:"\f0fa"}.la-medrt:before{content:"\f3c8"}.la-meetup:before{content:"\f2e0"}.la-megaport:before{content:"\f5a3"}.la-meh:before{content:"\f11a"}.la-meh-blank:before{content:"\f5a4"}.la-meh-rolling-eyes:before{content:"\f5a5"}.la-memory:before{content:"\f538"}.la-mendeley:before{content:"\f7b3"}.la-menorah:before{content:"\f676"}.la-mercury:before{content:"\f223"}.la-meteor:before{content:"\f753"}.la-microchip:before{content:"\f2db"}.la-microphone:before{content:"\f130"}.la-microphone-alt:before{content:"\f3c9"}.la-microphone-alt-slash:before{content:"\f539"}.la-microphone-slash:before{content:"\f131"}.la-microscope:before{content:"\f610"}.la-microsoft:before{content:"\f3ca"}.la-minus:before{content:"\f068"}.la-minus-circle:before{content:"\f056"}.la-minus-square:before{content:"\f146"}.la-mitten:before{content:"\f7b5"}.la-mix:before{content:"\f3cb"}.la-mixcloud:before{content:"\f289"}.la-mizuni:before{content:"\f3cc"}.la-mobile:before{content:"\f10b"}.la-mobile-alt:before{content:"\f3cd"}.la-modx:before{content:"\f285"}.la-monero:before{content:"\f3d0"}.la-money-bill:before{content:"\f0d6"}.la-money-bill-alt:before{content:"\f3d1"}.la-money-bill-wave:before{content:"\f53a"}.la-money-bill-wave-alt:before{content:"\f53b"}.la-money-check:before{content:"\f53c"}.la-money-check-alt:before{content:"\f53d"}.la-monument:before{content:"\f5a6"}.la-moon:before{content:"\f186"}.la-mortar-pestle:before{content:"\f5a7"}.la-mosque:before{content:"\f678"}.la-motorcycle:before{content:"\f21c"}.la-mountain:before{content:"\f6fc"}.la-mouse:before{content:"\f8cc"}.la-mouse-pointer:before{content:"\f245"}.la-mug-hot:before{content:"\f7b6"}.la-music:before{content:"\f001"}.la-napster:before{content:"\f3d2"}.la-neos:before{content:"\f612"}.la-network-wired:before{content:"\f6ff"}.la-neuter:before{content:"\f22c"}.la-newspaper:before{content:"\f1ea"}.la-nimblr:before{content:"\f5a8"}.la-node:before{content:"\f419"}.la-node-js:before{content:"\f3d3"}.la-not-equal:before{content:"\f53e"}.la-notes-medical:before{content:"\f481"}.la-npm:before{content:"\f3d4"}.la-ns8:before{content:"\f3d5"}.la-nutritionix:before{content:"\f3d6"}.la-object-group:before{content:"\f247"}.la-object-ungroup:before{content:"\f248"}.la-odnoklassniki:before{content:"\f263"}.la-odnoklassniki-square:before{content:"\f264"}.la-oil-can:before{content:"\f613"}.la-old-republic:before{content:"\f510"}.la-om:before{content:"\f679"}.la-opencart:before{content:"\f23d"}.la-openid:before{content:"\f19b"}.la-opera:before{content:"\f26a"}.la-optin-monster:before{content:"\f23c"}.la-orcid:before{content:"\f8d2"}.la-osi:before{content:"\f41a"}.la-otter:before{content:"\f700"}.la-outdent:before{content:"\f03b"}.la-page4:before{content:"\f3d7"}.la-pagelines:before{content:"\f18c"}.la-pager:before{content:"\f815"}.la-paint-brush:before{content:"\f1fc"}.la-paint-roller:before{content:"\f5aa"}.la-palette:before{content:"\f53f"}.la-palfed:before{content:"\f3d8"}.la-pallet:before{content:"\f482"}.la-paper-plane:before{content:"\f1d8"}.la-paperclip:before{content:"\f0c6"}.la-parachute-box:before{content:"\f4cd"}.la-paragraph:before{content:"\f1dd"}.la-parking:before{content:"\f540"}.la-passport:before{content:"\f5ab"}.la-pastafarianism:before{content:"\f67b"}.la-paste:before{content:"\f0ea"}.la-patreon:before{content:"\f3d9"}.la-pause:before{content:"\f04c"}.la-pause-circle:before{content:"\f28b"}.la-paw:before{content:"\f1b0"}.la-paypal:before{content:"\f1ed"}.la-peace:before{content:"\f67c"}.la-pen:before{content:"\f304"}.la-pen-alt:before{content:"\f305"}.la-pen-fancy:before{content:"\f5ac"}.la-pen-nib:before{content:"\f5ad"}.la-pen-square:before{content:"\f14b"}.la-pencil-alt:before{content:"\f303"}.la-pencil-ruler:before{content:"\f5ae"}.la-penny-arcade:before{content:"\f704"}.la-people-carry:before{content:"\f4ce"}.la-pepper-hot:before{content:"\f816"}.la-percent:before{content:"\f295"}.la-percentage:before{content:"\f541"}.la-periscope:before{content:"\f3da"}.la-person-booth:before{content:"\f756"}.la-phabricator:before{content:"\f3db"}.la-phoenix-framework:before{content:"\f3dc"}.la-phoenix-squadron:before{content:"\f511"}.la-phone:before{content:"\f095"}.la-phone-alt:before{content:"\f879"}.la-phone-slash:before{content:"\f3dd"}.la-phone-square:before{content:"\f098"}.la-phone-square-alt:before{content:"\f87b"}.la-phone-volume:before{content:"\f2a0"}.la-photo-video:before{content:"\f87c"}.la-php:before{content:"\f457"}.la-pied-piper:before{content:"\f2ae"}.la-pied-piper-alt:before{content:"\f1a8"}.la-pied-piper-hat:before{content:"\f4e5"}.la-pied-piper-pp:before{content:"\f1a7"}.la-piggy-bank:before{content:"\f4d3"}.la-pills:before{content:"\f484"}.la-pinterest:before{content:"\f0d2"}.la-pinterest-p:before{content:"\f231"}.la-pinterest-square:before{content:"\f0d3"}.la-pizza-slice:before{content:"\f818"}.la-place-of-worship:before{content:"\f67f"}.la-plane:before{content:"\f072"}.la-plane-arrival:before{content:"\f5af"}.la-plane-departure:before{content:"\f5b0"}.la-play:before{content:"\f04b"}.la-play-circle:before{content:"\f144"}.la-playstation:before{content:"\f3df"}.la-plug:before{content:"\f1e6"}.la-plus:before{content:"\f067"}.la-plus-circle:before{content:"\f055"}.la-plus-square:before{content:"\f0fe"}.la-podcast:before{content:"\f2ce"}.la-poll:before{content:"\f681"}.la-poll-h:before{content:"\f682"}.la-poo:before{content:"\f2fe"}.la-poo-storm:before{content:"\f75a"}.la-poop:before{content:"\f619"}.la-portrait:before{content:"\f3e0"}.la-pound-sign:before{content:"\f154"}.la-power-off:before{content:"\f011"}.la-pray:before{content:"\f683"}.la-praying-hands:before{content:"\f684"}.la-prescription:before{content:"\f5b1"}.la-prescription-bottle:before{content:"\f485"}.la-prescription-bottle-alt:before{content:"\f486"}.la-print:before{content:"\f02f"}.la-procedures:before{content:"\f487"}.la-product-hunt:before{content:"\f288"}.la-project-diagram:before{content:"\f542"}.la-pushed:before{content:"\f3e1"}.la-puzzle-piece:before{content:"\f12e"}.la-python:before{content:"\f3e2"}.la-qq:before{content:"\f1d6"}.la-qrcode:before{content:"\f029"}.la-question:before{content:"\f128"}.la-question-circle:before{content:"\f059"}.la-quidditch:before{content:"\f458"}.la-quinscape:before{content:"\f459"}.la-quora:before{content:"\f2c4"}.la-quote-left:before{content:"\f10d"}.la-quote-right:before{content:"\f10e"}.la-quran:before{content:"\f687"}.la-r-project:before{content:"\f4f7"}.la-radiation:before{content:"\f7b9"}.la-radiation-alt:before{content:"\f7ba"}.la-rainbow:before{content:"\f75b"}.la-random:before{content:"\f074"}.la-raspberry-pi:before{content:"\f7bb"}.la-ravelry:before{content:"\f2d9"}.la-react:before{content:"\f41b"}.la-reacteurope:before{content:"\f75d"}.la-readme:before{content:"\f4d5"}.la-rebel:before{content:"\f1d0"}.la-receipt:before{content:"\f543"}.la-record-vinyl:before{content:"\f8d9"}.la-recycle:before{content:"\f1b8"}.la-red-river:before{content:"\f3e3"}.la-reddit:before{content:"\f1a1"}.la-reddit-alien:before{content:"\f281"}.la-reddit-square:before{content:"\f1a2"}.la-redhat:before{content:"\f7bc"}.la-redo:before{content:"\f01e"}.la-redo-alt:before{content:"\f2f9"}.la-registered:before{content:"\f25d"}.la-remove-format:before{content:"\f87d"}.la-renren:before{content:"\f18b"}.la-reply:before{content:"\f3e5"}.la-reply-all:before{content:"\f122"}.la-replyd:before{content:"\f3e6"}.la-republican:before{content:"\f75e"}.la-researchgate:before{content:"\f4f8"}.la-resolving:before{content:"\f3e7"}.la-restroom:before{content:"\f7bd"}.la-retweet:before{content:"\f079"}.la-rev:before{content:"\f5b2"}.la-ribbon:before{content:"\f4d6"}.la-ring:before{content:"\f70b"}.la-road:before{content:"\f018"}.la-robot:before{content:"\f544"}.la-rocket:before{content:"\f135"}.la-rocketchat:before{content:"\f3e8"}.la-rockrms:before{content:"\f3e9"}.la-route:before{content:"\f4d7"}.la-rss:before{content:"\f09e"}.la-rss-square:before{content:"\f143"}.la-ruble-sign:before{content:"\f158"}.la-ruler:before{content:"\f545"}.la-ruler-combined:before{content:"\f546"}.la-ruler-horizontal:before{content:"\f547"}.la-ruler-vertical:before{content:"\f548"}.la-running:before{content:"\f70c"}.la-rupee-sign:before{content:"\f156"}.la-sad-cry:before{content:"\f5b3"}.la-sad-tear:before{content:"\f5b4"}.la-safari:before{content:"\f267"}.la-salesforce:before{content:"\f83b"}.la-sass:before{content:"\f41e"}.la-satellite:before{content:"\f7bf"}.la-satellite-dish:before{content:"\f7c0"}.la-save:before{content:"\f0c7"}.la-schlix:before{content:"\f3ea"}.la-school:before{content:"\f549"}.la-screwdriver:before{content:"\f54a"}.la-scribd:before{content:"\f28a"}.la-scroll:before{content:"\f70e"}.la-sd-card:before{content:"\f7c2"}.la-search:before{content:"\f002"}.la-search-dollar:before{content:"\f688"}.la-search-location:before{content:"\f689"}.la-search-minus:before{content:"\f010"}.la-search-plus:before{content:"\f00e"}.la-searchengin:before{content:"\f3eb"}.la-seedling:before{content:"\f4d8"}.la-sellcast:before{content:"\f2da"}.la-sellsy:before{content:"\f213"}.la-server:before{content:"\f233"}.la-servicestack:before{content:"\f3ec"}.la-shapes:before{content:"\f61f"}.la-share:before{content:"\f064"}.la-share-alt:before{content:"\f1e0"}.la-share-alt-square:before{content:"\f1e1"}.la-share-square:before{content:"\f14d"}.la-shekel-sign:before{content:"\f20b"}.la-shield-alt:before{content:"\f3ed"}.la-ship:before{content:"\f21a"}.la-shipping-fast:before{content:"\f48b"}.la-shirtsinbulk:before{content:"\f214"}.la-shoe-prints:before{content:"\f54b"}.la-shopping-bag:before{content:"\f290"}.la-shopping-basket:before{content:"\f291"}.la-shopping-cart:before{content:"\f07a"}.la-shopware:before{content:"\f5b5"}.la-shower:before{content:"\f2cc"}.la-shuttle-van:before{content:"\f5b6"}.la-sign:before{content:"\f4d9"}.la-sign-in-alt:before{content:"\f2f6"}.la-sign-language:before{content:"\f2a7"}.la-sign-out-alt:before{content:"\f2f5"}.la-signal:before{content:"\f012"}.la-signature:before{content:"\f5b7"}.la-sim-card:before{content:"\f7c4"}.la-simplybuilt:before{content:"\f215"}.la-sistrix:before{content:"\f3ee"}.la-sitemap:before{content:"\f0e8"}.la-sith:before{content:"\f512"}.la-skating:before{content:"\f7c5"}.la-sketch:before{content:"\f7c6"}.la-skiing:before{content:"\f7c9"}.la-skiing-nordic:before{content:"\f7ca"}.la-skull:before{content:"\f54c"}.la-skull-crossbones:before{content:"\f714"}.la-skyatlas:before{content:"\f216"}.la-skype:before{content:"\f17e"}.la-slack:before{content:"\f198"}.la-slack-hash:before{content:"\f3ef"}.la-slash:before{content:"\f715"}.la-sleigh:before{content:"\f7cc"}.la-sliders-h:before{content:"\f1de"}.la-slideshare:before{content:"\f1e7"}.la-smile:before{content:"\f118"}.la-smile-beam:before{content:"\f5b8"}.la-smile-wink:before{content:"\f4da"}.la-smog:before{content:"\f75f"}.la-smoking:before{content:"\f48d"}.la-smoking-ban:before{content:"\f54d"}.la-sms:before{content:"\f7cd"}.la-snapchat:before{content:"\f2ab"}.la-snapchat-ghost:before{content:"\f2ac"}.la-snapchat-square:before{content:"\f2ad"}.la-snowboarding:before{content:"\f7ce"}.la-snowflake:before{content:"\f2dc"}.la-snowman:before{content:"\f7d0"}.la-snowplow:before{content:"\f7d2"}.la-socks:before{content:"\f696"}.la-solar-panel:before{content:"\f5ba"}.la-sort:before{content:"\f0dc"}.la-sort-alpha-down:before{content:"\f15d"}.la-sort-alpha-down-alt:before{content:"\f881"}.la-sort-alpha-up:before{content:"\f15e"}.la-sort-alpha-up-alt:before{content:"\f882"}.la-sort-amount-down:before{content:"\f160"}.la-sort-amount-down-alt:before{content:"\f884"}.la-sort-amount-up:before{content:"\f161"}.la-sort-amount-up-alt:before{content:"\f885"}.la-sort-down:before{content:"\f0dd"}.la-sort-numeric-down:before{content:"\f162"}.la-sort-numeric-down-alt:before{content:"\f886"}.la-sort-numeric-up:before{content:"\f163"}.la-sort-numeric-up-alt:before{content:"\f887"}.la-sort-up:before{content:"\f0de"}.la-soundcloud:before{content:"\f1be"}.la-sourcetree:before{content:"\f7d3"}.la-spa:before{content:"\f5bb"}.la-space-shuttle:before{content:"\f197"}.la-speakap:before{content:"\f3f3"}.la-speaker-deck:before{content:"\f83c"}.la-spell-check:before{content:"\f891"}.la-spider:before{content:"\f717"}.la-spinner:before{content:"\f110"}.la-splotch:before{content:"\f5bc"}.la-spotify:before{content:"\f1bc"}.la-spray-can:before{content:"\f5bd"}.la-square:before{content:"\f0c8"}.la-square-full:before{content:"\f45c"}.la-square-root-alt:before{content:"\f698"}.la-squarespace:before{content:"\f5be"}.la-stack-exchange:before{content:"\f18d"}.la-stack-overflow:before{content:"\f16c"}.la-stackpath:before{content:"\f842"}.la-stamp:before{content:"\f5bf"}.la-star:before{content:"\f005"}.la-star-and-crescent:before{content:"\f699"}.la-star-half:before{content:"\f089"}.la-star-half-alt:before{content:"\f5c0"}.la-star-of-david:before{content:"\f69a"}.la-star-of-life:before{content:"\f621"}.la-staylinked:before{content:"\f3f5"}.la-steam:before{content:"\f1b6"}.la-steam-square:before{content:"\f1b7"}.la-steam-symbol:before{content:"\f3f6"}.la-step-backward:before{content:"\f048"}.la-step-forward:before{content:"\f051"}.la-stethoscope:before{content:"\f0f1"}.la-sticker-mule:before{content:"\f3f7"}.la-sticky-note:before{content:"\f249"}.la-stop:before{content:"\f04d"}.la-stop-circle:before{content:"\f28d"}.la-stopwatch:before{content:"\f2f2"}.la-store:before{content:"\f54e"}.la-store-alt:before{content:"\f54f"}.la-strava:before{content:"\f428"}.la-stream:before{content:"\f550"}.la-street-view:before{content:"\f21d"}.la-strikethrough:before{content:"\f0cc"}.la-stripe:before{content:"\f429"}.la-stripe-s:before{content:"\f42a"}.la-stroopwafel:before{content:"\f551"}.la-studiovinari:before{content:"\f3f8"}.la-stumbleupon:before{content:"\f1a4"}.la-stumbleupon-circle:before{content:"\f1a3"}.la-subscript:before{content:"\f12c"}.la-subway:before{content:"\f239"}.la-suitcase:before{content:"\f0f2"}.la-suitcase-rolling:before{content:"\f5c1"}.la-sun:before{content:"\f185"}.la-superpowers:before{content:"\f2dd"}.la-superscript:before{content:"\f12b"}.la-supple:before{content:"\f3f9"}.la-surprise:before{content:"\f5c2"}.la-suse:before{content:"\f7d6"}.la-swatchbook:before{content:"\f5c3"}.la-swift:before{content:"\f8e1"}.la-swimmer:before{content:"\f5c4"}.la-swimming-pool:before{content:"\f5c5"}.la-symfony:before{content:"\f83d"}.la-synagogue:before{content:"\f69b"}.la-sync:before{content:"\f021"}.la-sync-alt:before{content:"\f2f1"}.la-syringe:before{content:"\f48e"}.la-table:before{content:"\f0ce"}.la-table-tennis:before{content:"\f45d"}.la-tablet:before{content:"\f10a"}.la-tablet-alt:before{content:"\f3fa"}.la-tablets:before{content:"\f490"}.la-tachometer-alt:before{content:"\f3fd"}.la-tag:before{content:"\f02b"}.la-tags:before{content:"\f02c"}.la-tape:before{content:"\f4db"}.la-tasks:before{content:"\f0ae"}.la-taxi:before{content:"\f1ba"}.la-teamspeak:before{content:"\f4f9"}.la-teeth:before{content:"\f62e"}.la-teeth-open:before{content:"\f62f"}.la-telegram:before{content:"\f2c6"}.la-telegram-plane:before{content:"\f3fe"}.la-temperature-high:before{content:"\f769"}.la-temperature-low:before{content:"\f76b"}.la-tencent-weibo:before{content:"\f1d5"}.la-tenge:before{content:"\f7d7"}.la-terminal:before{content:"\f120"}.la-text-height:before{content:"\f034"}.la-text-width:before{content:"\f035"}.la-th:before{content:"\f00a"}.la-th-large:before{content:"\f009"}.la-th-list:before{content:"\f00b"}.la-the-red-yeti:before{content:"\f69d"}.la-theater-masks:before{content:"\f630"}.la-themeco:before{content:"\f5c6"}.la-themeisle:before{content:"\f2b2"}.la-thermometer:before{content:"\f491"}.la-thermometer-empty:before{content:"\f2cb"}.la-thermometer-full:before{content:"\f2c7"}.la-thermometer-half:before{content:"\f2c9"}.la-thermometer-quarter:before{content:"\f2ca"}.la-thermometer-three-quarters:before{content:"\f2c8"}.la-think-peaks:before{content:"\f731"}.la-thumbs-down:before{content:"\f165"}.la-thumbs-up:before{content:"\f164"}.la-thumbtack:before{content:"\f08d"}.la-ticket-alt:before{content:"\f3ff"}.la-times:before{content:"\f00d"}.la-times-circle:before{content:"\f057"}.la-tint:before{content:"\f043"}.la-tint-slash:before{content:"\f5c7"}.la-tired:before{content:"\f5c8"}.la-toggle-off:before{content:"\f204"}.la-toggle-on:before{content:"\f205"}.la-toilet:before{content:"\f7d8"}.la-toilet-paper:before{content:"\f71e"}.la-toolbox:before{content:"\f552"}.la-tools:before{content:"\f7d9"}.la-tooth:before{content:"\f5c9"}.la-torah:before{content:"\f6a0"}.la-torii-gate:before{content:"\f6a1"}.la-tractor:before{content:"\f722"}.la-trade-federation:before{content:"\f513"}.la-trademark:before{content:"\f25c"}.la-traffic-light:before{content:"\f637"}.la-train:before{content:"\f238"}.la-tram:before{content:"\f7da"}.la-transgender:before{content:"\f224"}.la-transgender-alt:before{content:"\f225"}.la-trash:before{content:"\f1f8"}.la-trash-alt:before{content:"\f2ed"}.la-trash-restore:before{content:"\f829"}.la-trash-restore-alt:before{content:"\f82a"}.la-tree:before{content:"\f1bb"}.la-trello:before{content:"\f181"}.la-tripadvisor:before{content:"\f262"}.la-trophy:before{content:"\f091"}.la-truck:before{content:"\f0d1"}.la-truck-loading:before{content:"\f4de"}.la-truck-monster:before{content:"\f63b"}.la-truck-moving:before{content:"\f4df"}.la-truck-pickup:before{content:"\f63c"}.la-tshirt:before{content:"\f553"}.la-tty:before{content:"\f1e4"}.la-tumblr:before{content:"\f173"}.la-tumblr-square:before{content:"\f174"}.la-tv:before{content:"\f26c"}.la-twitch:before{content:"\f1e8"}.la-twitter:before{content:"\f099"}.la-twitter-square:before{content:"\f081"}.la-typo3:before{content:"\f42b"}.la-uber:before{content:"\f402"}.la-ubuntu:before{content:"\f7df"}.la-uikit:before{content:"\f403"}.la-umbraco:before{content:"\f8e8"}.la-umbrella:before{content:"\f0e9"}.la-umbrella-beach:before{content:"\f5ca"}.la-underline:before{content:"\f0cd"}.la-undo:before{content:"\f0e2"}.la-undo-alt:before{content:"\f2ea"}.la-uniregistry:before{content:"\f404"}.la-universal-access:before{content:"\f29a"}.la-university:before{content:"\f19c"}.la-unlink:before{content:"\f127"}.la-unlock:before{content:"\f09c"}.la-unlock-alt:before{content:"\f13e"}.la-untappd:before{content:"\f405"}.la-upload:before{content:"\f093"}.la-ups:before{content:"\f7e0"}.la-usb:before{content:"\f287"}.la-user:before{content:"\f007"}.la-user-alt:before{content:"\f406"}.la-user-alt-slash:before{content:"\f4fa"}.la-user-astronaut:before{content:"\f4fb"}.la-user-check:before{content:"\f4fc"}.la-user-circle:before{content:"\f2bd"}.la-user-clock:before{content:"\f4fd"}.la-user-cog:before{content:"\f4fe"}.la-user-edit:before{content:"\f4ff"}.la-user-friends:before{content:"\f500"}.la-user-graduate:before{content:"\f501"}.la-user-injured:before{content:"\f728"}.la-user-lock:before{content:"\f502"}.la-user-md:before{content:"\f0f0"}.la-user-minus:before{content:"\f503"}.la-user-ninja:before{content:"\f504"}.la-user-nurse:before{content:"\f82f"}.la-user-plus:before{content:"\f234"}.la-user-secret:before{content:"\f21b"}.la-user-shield:before{content:"\f505"}.la-user-slash:before{content:"\f506"}.la-user-tag:before{content:"\f507"}.la-user-tie:before{content:"\f508"}.la-user-times:before{content:"\f235"}.la-users:before{content:"\f0c0"}.la-users-cog:before{content:"\f509"}.la-usps:before{content:"\f7e1"}.la-ussunnah:before{content:"\f407"}.la-utensil-spoon:before{content:"\f2e5"}.la-utensils:before{content:"\f2e7"}.la-vaadin:before{content:"\f408"}.la-vector-square:before{content:"\f5cb"}.la-venus:before{content:"\f221"}.la-venus-double:before{content:"\f226"}.la-venus-mars:before{content:"\f228"}.la-viacoin:before{content:"\f237"}.la-viadeo:before{content:"\f2a9"}.la-viadeo-square:before{content:"\f2aa"}.la-vial:before{content:"\f492"}.la-vials:before{content:"\f493"}.la-viber:before{content:"\f409"}.la-video:before{content:"\f03d"}.la-video-slash:before{content:"\f4e2"}.la-vihara:before{content:"\f6a7"}.la-vimeo:before{content:"\f40a"}.la-vimeo-square:before{content:"\f194"}.la-vimeo-v:before{content:"\f27d"}.la-vine:before{content:"\f1ca"}.la-vk:before{content:"\f189"}.la-vnv:before{content:"\f40b"}.la-voicemail:before{content:"\f897"}.la-volleyball-ball:before{content:"\f45f"}.la-volume-down:before{content:"\f027"}.la-volume-mute:before{content:"\f6a9"}.la-volume-off:before{content:"\f026"}.la-volume-up:before{content:"\f028"}.la-vote-yea:before{content:"\f772"}.la-vr-cardboard:before{content:"\f729"}.la-vuejs:before{content:"\f41f"}.la-walking:before{content:"\f554"}.la-wallet:before{content:"\f555"}.la-warehouse:before{content:"\f494"}.la-water:before{content:"\f773"}.la-wave-square:before{content:"\f83e"}.la-waze:before{content:"\f83f"}.la-weebly:before{content:"\f5cc"}.la-weibo:before{content:"\f18a"}.la-weight:before{content:"\f496"}.la-weight-hanging:before{content:"\f5cd"}.la-weixin:before{content:"\f1d7"}.la-whatsapp:before{content:"\f232"}.la-whatsapp-square:before{content:"\f40c"}.la-wheelchair:before{content:"\f193"}.la-whmcs:before{content:"\f40d"}.la-wifi:before{content:"\f1eb"}.la-wikipedia-w:before{content:"\f266"}.la-wind:before{content:"\f72e"}.la-window-close:before{content:"\f410"}.la-window-maximize:before{content:"\f2d0"}.la-window-minimize:before{content:"\f2d1"}.la-window-restore:before{content:"\f2d2"}.la-windows:before{content:"\f17a"}.la-wine-bottle:before{content:"\f72f"}.la-wine-glass:before{content:"\f4e3"}.la-wine-glass-alt:before{content:"\f5ce"}.la-wix:before{content:"\f5cf"}.la-wizards-of-the-coast:before{content:"\f730"}.la-wolf-pack-battalion:before{content:"\f514"}.la-won-sign:before{content:"\f159"}.la-wordpress:before{content:"\f19a"}.la-wordpress-simple:before{content:"\f411"}.la-wpbeginner:before{content:"\f297"}.la-wpexplorer:before{content:"\f2de"}.la-wpforms:before{content:"\f298"}.la-wpressr:before{content:"\f3e4"}.la-wrench:before{content:"\f0ad"}.la-x-ray:before{content:"\f497"}.la-xbox:before{content:"\f412"}.la-xing:before{content:"\f168"}.la-xing-square:before{content:"\f169"}.la-y-combinator:before{content:"\f23b"}.la-yahoo:before{content:"\f19e"}.la-yammer:before{content:"\f840"}.la-yandex:before{content:"\f413"}.la-yandex-international:before{content:"\f414"}.la-yarn:before{content:"\f7e3"}.la-yelp:before{content:"\f1e9"}.la-yen-sign:before{content:"\f157"}.la-yin-yang:before{content:"\f6ad"}.la-yoast:before{content:"\f2b1"}.la-youtube:before{content:"\f167"}.la-youtube-square:before{content:"\f431"}.la-zhihu:before{content:"\f63f"}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}@font-face{font-family:'Line Awesome Brands';font-style:normal;font-weight:400;font-display:auto;src:url(../fonts/la-brands-400.eot);src:url(../fonts/la-brands-400.eot?#iefix) format("embedded-opentype"),url(../fonts/la-brands-400.woff2) format("woff2"),url(../fonts/la-brands-400.woff) format("woff"),url(../fonts/la-brands-400.ttf) format("truetype"),url(../fonts/la-brands-400.svg#lineawesome) format("svg")}.lab{font-family:'Line Awesome Brands'}@font-face{font-family:'Line Awesome Free';font-style:normal;font-weight:400;font-display:auto;src:url(../fonts/la-regular-400.eot);src:url(../fonts/la-regular-400.eot?#iefix) format("embedded-opentype"),url(../fonts/la-regular-400.woff2) format("woff2"),url(../fonts/la-regular-400.woff) format("woff"),url(../fonts/la-regular-400.ttf) format("truetype"),url(../fonts/la-regular-400.svg#lineawesome) format("svg")}.lar{font-family:'Line Awesome Free';font-weight:400}@font-face{font-family:'Line Awesome Free';font-style:normal;font-weight:900;font-display:auto;src:url(../fonts/la-solid-900.eot);src:url(../fonts/la-solid-900.eot?#iefix) format("embedded-opentype"),url(../fonts/la-solid-900.woff2) format("woff2"),url(../fonts/la-solid-900.woff) format("woff"),url(../fonts/la-solid-900.ttf) format("truetype"),url(../fonts/la-solid-900.svg#lineawesome) format("svg")}.la,.las{font-family:'Line Awesome Free';font-weight:900}.la.la-glass:before{content:"\f000"}.la.la-meetup{font-family:'Line Awesome Brands';font-weight:400}.la.la-star-o{font-family:'Line Awesome Free';font-weight:400}.la.la-star-o:before{content:"\f005"}.la.la-remove:before{content:"\f00d"}.la.la-close:before{content:"\f00d"}.la.la-gear:before{content:"\f013"}.la.la-trash-o{font-family:'Line Awesome Free';font-weight:400}.la.la-trash-o:before{content:"\f2ed"}.la.la-file-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-o:before{content:"\f15b"}.la.la-clock-o{font-family:'Line Awesome Free';font-weight:400}.la.la-clock-o:before{content:"\f017"}.la.la-arrow-circle-o-down{font-family:'Line Awesome Free';font-weight:400}.la.la-arrow-circle-o-down:before{content:"\f358"}.la.la-arrow-circle-o-up{font-family:'Line Awesome Free';font-weight:400}.la.la-arrow-circle-o-up:before{content:"\f35b"}.la.la-play-circle-o{font-family:'Line Awesome Free';font-weight:400}.la.la-play-circle-o:before{content:"\f144"}.la.la-repeat:before{content:"\f01e"}.la.la-rotate-right:before{content:"\f01e"}.la.la-refresh:before{content:"\f021"}.la.la-list-alt{font-family:'Line Awesome Free';font-weight:400}.la.la-dedent:before{content:"\f03b"}.la.la-video-camera:before{content:"\f03d"}.la.la-picture-o{font-family:'Line Awesome Free';font-weight:400}.la.la-picture-o:before{content:"\f03e"}.la.la-photo{font-family:'Line Awesome Free';font-weight:400}.la.la-photo:before{content:"\f03e"}.la.la-image{font-family:'Line Awesome Free';font-weight:400}.la.la-image:before{content:"\f03e"}.la.la-pencil:before{content:"\f303"}.la.la-map-marker:before{content:"\f3c5"}.la.la-pencil-square-o{font-family:'Line Awesome Free';font-weight:400}.la.la-pencil-square-o:before{content:"\f044"}.la.la-share-square-o{font-family:'Line Awesome Free';font-weight:400}.la.la-share-square-o:before{content:"\f14d"}.la.la-check-square-o{font-family:'Line Awesome Free';font-weight:400}.la.la-check-square-o:before{content:"\f14a"}.la.la-arrows:before{content:"\f0b2"}.la.la-times-circle-o{font-family:'Line Awesome Free';font-weight:400}.la.la-times-circle-o:before{content:"\f057"}.la.la-check-circle-o{font-family:'Line Awesome Free';font-weight:400}.la.la-check-circle-o:before{content:"\f058"}.la.la-mail-forward:before{content:"\f064"}.la.la-eye{font-family:'Line Awesome Free';font-weight:400}.la.la-eye-slash{font-family:'Line Awesome Free';font-weight:400}.la.la-warning:before{content:"\f071"}.la.la-calendar:before{content:"\f073"}.la.la-arrows-v:before{content:"\f338"}.la.la-arrows-h:before{content:"\f337"}.la.la-bar-chart{font-family:'Line Awesome Free';font-weight:400}.la.la-bar-chart:before{content:"\f080"}.la.la-bar-chart-o{font-family:'Line Awesome Free';font-weight:400}.la.la-bar-chart-o:before{content:"\f080"}.la.la-twitter-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-facebook-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-gears:before{content:"\f085"}.la.la-thumbs-o-up{font-family:'Line Awesome Free';font-weight:400}.la.la-thumbs-o-up:before{content:"\f164"}.la.la-thumbs-o-down{font-family:'Line Awesome Free';font-weight:400}.la.la-thumbs-o-down:before{content:"\f165"}.la.la-heart-o{font-family:'Line Awesome Free';font-weight:400}.la.la-heart-o:before{content:"\f004"}.la.la-sign-out:before{content:"\f2f5"}.la.la-linkedin-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-linkedin-square:before{content:"\f08c"}.la.la-thumb-tack:before{content:"\f08d"}.la.la-external-link:before{content:"\f35d"}.la.la-sign-in:before{content:"\f2f6"}.la.la-github-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-lemon-o{font-family:'Line Awesome Free';font-weight:400}.la.la-lemon-o:before{content:"\f094"}.la.la-square-o{font-family:'Line Awesome Free';font-weight:400}.la.la-square-o:before{content:"\f0c8"}.la.la-bookmark-o{font-family:'Line Awesome Free';font-weight:400}.la.la-bookmark-o:before{content:"\f02e"}.la.la-twitter{font-family:'Line Awesome Brands';font-weight:400}.la.la-facebook{font-family:'Line Awesome Brands';font-weight:400}.la.la-facebook:before{content:"\f39e"}.la.la-facebook-f{font-family:'Line Awesome Brands';font-weight:400}.la.la-facebook-f:before{content:"\f39e"}.la.la-github{font-family:'Line Awesome Brands';font-weight:400}.la.la-credit-card{font-family:'Line Awesome Free';font-weight:400}.la.la-feed:before{content:"\f09e"}.la.la-hdd-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hdd-o:before{content:"\f0a0"}.la.la-hand-o-right{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-o-right:before{content:"\f0a4"}.la.la-hand-o-left{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-o-left:before{content:"\f0a5"}.la.la-hand-o-up{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-o-up:before{content:"\f0a6"}.la.la-hand-o-down{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-o-down:before{content:"\f0a7"}.la.la-arrows-alt:before{content:"\f31e"}.la.la-group:before{content:"\f0c0"}.la.la-chain:before{content:"\f0c1"}.la.la-scissors:before{content:"\f0c4"}.la.la-files-o{font-family:'Line Awesome Free';font-weight:400}.la.la-files-o:before{content:"\f0c5"}.la.la-floppy-o{font-family:'Line Awesome Free';font-weight:400}.la.la-floppy-o:before{content:"\f0c7"}.la.la-navicon:before{content:"\f0c9"}.la.la-reorder:before{content:"\f0c9"}.la.la-pinterest{font-family:'Line Awesome Brands';font-weight:400}.la.la-pinterest-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-google-plus-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-google-plus{font-family:'Line Awesome Brands';font-weight:400}.la.la-google-plus:before{content:"\f0d5"}.la.la-money{font-family:'Line Awesome Free';font-weight:400}.la.la-money:before{content:"\f3d1"}.la.la-unsorted:before{content:"\f0dc"}.la.la-sort-desc:before{content:"\f0dd"}.la.la-sort-asc:before{content:"\f0de"}.la.la-linkedin{font-family:'Line Awesome Brands';font-weight:400}.la.la-linkedin:before{content:"\f0e1"}.la.la-rotate-left:before{content:"\f0e2"}.la.la-legal:before{content:"\f0e3"}.la.la-tachometer:before{content:"\f3fd"}.la.la-dashboard:before{content:"\f3fd"}.la.la-comment-o{font-family:'Line Awesome Free';font-weight:400}.la.la-comment-o:before{content:"\f075"}.la.la-comments-o{font-family:'Line Awesome Free';font-weight:400}.la.la-comments-o:before{content:"\f086"}.la.la-flash:before{content:"\f0e7"}.la.la-clipboard{font-family:'Line Awesome Free';font-weight:400}.la.la-paste{font-family:'Line Awesome Free';font-weight:400}.la.la-paste:before{content:"\f328"}.la.la-lightbulb-o{font-family:'Line Awesome Free';font-weight:400}.la.la-lightbulb-o:before{content:"\f0eb"}.la.la-exchange:before{content:"\f362"}.la.la-cloud-download:before{content:"\f381"}.la.la-cloud-upload:before{content:"\f382"}.la.la-bell-o{font-family:'Line Awesome Free';font-weight:400}.la.la-bell-o:before{content:"\f0f3"}.la.la-cutlery:before{content:"\f2e7"}.la.la-file-text-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-text-o:before{content:"\f15c"}.la.la-building-o{font-family:'Line Awesome Free';font-weight:400}.la.la-building-o:before{content:"\f1ad"}.la.la-hospital-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hospital-o:before{content:"\f0f8"}.la.la-tablet:before{content:"\f3fa"}.la.la-mobile:before{content:"\f3cd"}.la.la-mobile-phone:before{content:"\f3cd"}.la.la-circle-o{font-family:'Line Awesome Free';font-weight:400}.la.la-circle-o:before{content:"\f111"}.la.la-mail-reply:before{content:"\f3e5"}.la.la-github-alt{font-family:'Line Awesome Brands';font-weight:400}.la.la-folder-o{font-family:'Line Awesome Free';font-weight:400}.la.la-folder-o:before{content:"\f07b"}.la.la-folder-open-o{font-family:'Line Awesome Free';font-weight:400}.la.la-folder-open-o:before{content:"\f07c"}.la.la-smile-o{font-family:'Line Awesome Free';font-weight:400}.la.la-smile-o:before{content:"\f118"}.la.la-frown-o{font-family:'Line Awesome Free';font-weight:400}.la.la-frown-o:before{content:"\f119"}.la.la-meh-o{font-family:'Line Awesome Free';font-weight:400}.la.la-meh-o:before{content:"\f11a"}.la.la-keyboard-o{font-family:'Line Awesome Free';font-weight:400}.la.la-keyboard-o:before{content:"\f11c"}.la.la-flag-o{font-family:'Line Awesome Free';font-weight:400}.la.la-flag-o:before{content:"\f024"}.la.la-mail-reply-all:before{content:"\f122"}.la.la-star-half-o{font-family:'Line Awesome Free';font-weight:400}.la.la-star-half-o:before{content:"\f089"}.la.la-star-half-empty{font-family:'Line Awesome Free';font-weight:400}.la.la-star-half-empty:before{content:"\f089"}.la.la-star-half-full{font-family:'Line Awesome Free';font-weight:400}.la.la-star-half-full:before{content:"\f089"}.la.la-code-fork:before{content:"\f126"}.la.la-chain-broken:before{content:"\f127"}.la.la-shield:before{content:"\f3ed"}.la.la-calendar-o{font-family:'Line Awesome Free';font-weight:400}.la.la-calendar-o:before{content:"\f133"}.la.la-maxcdn{font-family:'Line Awesome Brands';font-weight:400}.la.la-html5{font-family:'Line Awesome Brands';font-weight:400}.la.la-css3{font-family:'Line Awesome Brands';font-weight:400}.la.la-ticket:before{content:"\f3ff"}.la.la-minus-square-o{font-family:'Line Awesome Free';font-weight:400}.la.la-minus-square-o:before{content:"\f146"}.la.la-level-up:before{content:"\f3bf"}.la.la-level-down:before{content:"\f3be"}.la.la-pencil-square:before{content:"\f14b"}.la.la-external-link-square:before{content:"\f360"}.la.la-compass{font-family:'Line Awesome Free';font-weight:400}.la.la-caret-square-o-down{font-family:'Line Awesome Free';font-weight:400}.la.la-caret-square-o-down:before{content:"\f150"}.la.la-toggle-down{font-family:'Line Awesome Free';font-weight:400}.la.la-toggle-down:before{content:"\f150"}.la.la-caret-square-o-up{font-family:'Line Awesome Free';font-weight:400}.la.la-caret-square-o-up:before{content:"\f151"}.la.la-toggle-up{font-family:'Line Awesome Free';font-weight:400}.la.la-toggle-up:before{content:"\f151"}.la.la-caret-square-o-right{font-family:'Line Awesome Free';font-weight:400}.la.la-caret-square-o-right:before{content:"\f152"}.la.la-toggle-right{font-family:'Line Awesome Free';font-weight:400}.la.la-toggle-right:before{content:"\f152"}.la.la-eur:before{content:"\f153"}.la.la-euro:before{content:"\f153"}.la.la-gbp:before{content:"\f154"}.la.la-usd:before{content:"\f155"}.la.la-dollar:before{content:"\f155"}.la.la-inr:before{content:"\f156"}.la.la-rupee:before{content:"\f156"}.la.la-jpy:before{content:"\f157"}.la.la-cny:before{content:"\f157"}.la.la-rmb:before{content:"\f157"}.la.la-yen:before{content:"\f157"}.la.la-rub:before{content:"\f158"}.la.la-ruble:before{content:"\f158"}.la.la-rouble:before{content:"\f158"}.la.la-krw:before{content:"\f159"}.la.la-won:before{content:"\f159"}.la.la-btc{font-family:'Line Awesome Brands';font-weight:400}.la.la-bitcoin{font-family:'Line Awesome Brands';font-weight:400}.la.la-bitcoin:before{content:"\f15a"}.la.la-file-text:before{content:"\f15c"}.la.la-sort-alpha-asc:before{content:"\f15d"}.la.la-sort-alpha-desc:before{content:"\f881"}.la.la-sort-amount-asc:before{content:"\f160"}.la.la-sort-amount-desc:before{content:"\f884"}.la.la-sort-numeric-asc:before{content:"\f162"}.la.la-sort-numeric-desc:before{content:"\f886"}.la.la-youtube-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-youtube{font-family:'Line Awesome Brands';font-weight:400}.la.la-xing{font-family:'Line Awesome Brands';font-weight:400}.la.la-xing-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-youtube-play{font-family:'Line Awesome Brands';font-weight:400}.la.la-youtube-play:before{content:"\f167"}.la.la-dropbox{font-family:'Line Awesome Brands';font-weight:400}.la.la-stack-overflow{font-family:'Line Awesome Brands';font-weight:400}.la.la-instagram{font-family:'Line Awesome Brands';font-weight:400}.la.la-flickr{font-family:'Line Awesome Brands';font-weight:400}.la.la-adn{font-family:'Line Awesome Brands';font-weight:400}.la.la-bitbucket{font-family:'Line Awesome Brands';font-weight:400}.la.la-bitbucket-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-bitbucket-square:before{content:"\f171"}.la.la-tumblr{font-family:'Line Awesome Brands';font-weight:400}.la.la-tumblr-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-long-arrow-down:before{content:"\f309"}.la.la-long-arrow-up:before{content:"\f30c"}.la.la-long-arrow-left:before{content:"\f30a"}.la.la-long-arrow-right:before{content:"\f30b"}.la.la-apple{font-family:'Line Awesome Brands';font-weight:400}.la.la-windows{font-family:'Line Awesome Brands';font-weight:400}.la.la-android{font-family:'Line Awesome Brands';font-weight:400}.la.la-linux{font-family:'Line Awesome Brands';font-weight:400}.la.la-dribbble{font-family:'Line Awesome Brands';font-weight:400}.la.la-skype{font-family:'Line Awesome Brands';font-weight:400}.la.la-foursquare{font-family:'Line Awesome Brands';font-weight:400}.la.la-trello{font-family:'Line Awesome Brands';font-weight:400}.la.la-gratipay{font-family:'Line Awesome Brands';font-weight:400}.la.la-gittip{font-family:'Line Awesome Brands';font-weight:400}.la.la-gittip:before{content:"\f184"}.la.la-sun-o{font-family:'Line Awesome Free';font-weight:400}.la.la-sun-o:before{content:"\f185"}.la.la-moon-o{font-family:'Line Awesome Free';font-weight:400}.la.la-moon-o:before{content:"\f186"}.la.la-vk{font-family:'Line Awesome Brands';font-weight:400}.la.la-weibo{font-family:'Line Awesome Brands';font-weight:400}.la.la-renren{font-family:'Line Awesome Brands';font-weight:400}.la.la-pagelines{font-family:'Line Awesome Brands';font-weight:400}.la.la-stack-exchange{font-family:'Line Awesome Brands';font-weight:400}.la.la-arrow-circle-o-right{font-family:'Line Awesome Free';font-weight:400}.la.la-arrow-circle-o-right:before{content:"\f35a"}.la.la-arrow-circle-o-left{font-family:'Line Awesome Free';font-weight:400}.la.la-arrow-circle-o-left:before{content:"\f359"}.la.la-caret-square-o-left{font-family:'Line Awesome Free';font-weight:400}.la.la-caret-square-o-left:before{content:"\f191"}.la.la-toggle-left{font-family:'Line Awesome Free';font-weight:400}.la.la-toggle-left:before{content:"\f191"}.la.la-dot-circle-o{font-family:'Line Awesome Free';font-weight:400}.la.la-dot-circle-o:before{content:"\f192"}.la.la-vimeo-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-try:before{content:"\f195"}.la.la-turkish-lira:before{content:"\f195"}.la.la-plus-square-o{font-family:'Line Awesome Free';font-weight:400}.la.la-plus-square-o:before{content:"\f0fe"}.la.la-slack{font-family:'Line Awesome Brands';font-weight:400}.la.la-wordpress{font-family:'Line Awesome Brands';font-weight:400}.la.la-openid{font-family:'Line Awesome Brands';font-weight:400}.la.la-institution:before{content:"\f19c"}.la.la-bank:before{content:"\f19c"}.la.la-mortar-board:before{content:"\f19d"}.la.la-yahoo{font-family:'Line Awesome Brands';font-weight:400}.la.la-google{font-family:'Line Awesome Brands';font-weight:400}.la.la-reddit{font-family:'Line Awesome Brands';font-weight:400}.la.la-reddit-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-stumbleupon-circle{font-family:'Line Awesome Brands';font-weight:400}.la.la-stumbleupon{font-family:'Line Awesome Brands';font-weight:400}.la.la-delicious{font-family:'Line Awesome Brands';font-weight:400}.la.la-digg{font-family:'Line Awesome Brands';font-weight:400}.la.la-pied-piper-pp{font-family:'Line Awesome Brands';font-weight:400}.la.la-pied-piper-alt{font-family:'Line Awesome Brands';font-weight:400}.la.la-drupal{font-family:'Line Awesome Brands';font-weight:400}.la.la-joomla{font-family:'Line Awesome Brands';font-weight:400}.la.la-spoon:before{content:"\f2e5"}.la.la-behance{font-family:'Line Awesome Brands';font-weight:400}.la.la-behance-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-steam{font-family:'Line Awesome Brands';font-weight:400}.la.la-steam-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-automobile:before{content:"\f1b9"}.la.la-cab:before{content:"\f1ba"}.la.la-envelope-o{font-family:'Line Awesome Free';font-weight:400}.la.la-envelope-o:before{content:"\f0e0"}.la.la-deviantart{font-family:'Line Awesome Brands';font-weight:400}.la.la-soundcloud{font-family:'Line Awesome Brands';font-weight:400}.la.la-file-pdf-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-pdf-o:before{content:"\f1c1"}.la.la-file-word-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-word-o:before{content:"\f1c2"}.la.la-file-excel-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-excel-o:before{content:"\f1c3"}.la.la-file-powerpoint-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-powerpoint-o:before{content:"\f1c4"}.la.la-file-image-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-image-o:before{content:"\f1c5"}.la.la-file-photo-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-photo-o:before{content:"\f1c5"}.la.la-file-picture-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-picture-o:before{content:"\f1c5"}.la.la-file-archive-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-archive-o:before{content:"\f1c6"}.la.la-file-zip-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-zip-o:before{content:"\f1c6"}.la.la-file-audio-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-audio-o:before{content:"\f1c7"}.la.la-file-sound-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-sound-o:before{content:"\f1c7"}.la.la-file-video-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-video-o:before{content:"\f1c8"}.la.la-file-movie-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-movie-o:before{content:"\f1c8"}.la.la-file-code-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-code-o:before{content:"\f1c9"}.la.la-vine{font-family:'Line Awesome Brands';font-weight:400}.la.la-codepen{font-family:'Line Awesome Brands';font-weight:400}.la.la-jsfiddle{font-family:'Line Awesome Brands';font-weight:400}.la.la-life-ring{font-family:'Line Awesome Free';font-weight:400}.la.la-life-bouy{font-family:'Line Awesome Free';font-weight:400}.la.la-life-bouy:before{content:"\f1cd"}.la.la-life-buoy{font-family:'Line Awesome Free';font-weight:400}.la.la-life-buoy:before{content:"\f1cd"}.la.la-life-saver{font-family:'Line Awesome Free';font-weight:400}.la.la-life-saver:before{content:"\f1cd"}.la.la-support{font-family:'Line Awesome Free';font-weight:400}.la.la-support:before{content:"\f1cd"}.la.la-circle-o-notch:before{content:"\f1ce"}.la.la-rebel{font-family:'Line Awesome Brands';font-weight:400}.la.la-ra{font-family:'Line Awesome Brands';font-weight:400}.la.la-ra:before{content:"\f1d0"}.la.la-resistance{font-family:'Line Awesome Brands';font-weight:400}.la.la-resistance:before{content:"\f1d0"}.la.la-empire{font-family:'Line Awesome Brands';font-weight:400}.la.la-ge{font-family:'Line Awesome Brands';font-weight:400}.la.la-ge:before{content:"\f1d1"}.la.la-git-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-git{font-family:'Line Awesome Brands';font-weight:400}.la.la-hacker-news{font-family:'Line Awesome Brands';font-weight:400}.la.la-y-combinator-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-y-combinator-square:before{content:"\f1d4"}.la.la-yc-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-yc-square:before{content:"\f1d4"}.la.la-tencent-weibo{font-family:'Line Awesome Brands';font-weight:400}.la.la-qq{font-family:'Line Awesome Brands';font-weight:400}.la.la-weixin{font-family:'Line Awesome Brands';font-weight:400}.la.la-wechat{font-family:'Line Awesome Brands';font-weight:400}.la.la-wechat:before{content:"\f1d7"}.la.la-send:before{content:"\f1d8"}.la.la-paper-plane-o{font-family:'Line Awesome Free';font-weight:400}.la.la-paper-plane-o:before{content:"\f1d8"}.la.la-send-o{font-family:'Line Awesome Free';font-weight:400}.la.la-send-o:before{content:"\f1d8"}.la.la-circle-thin{font-family:'Line Awesome Free';font-weight:400}.la.la-circle-thin:before{content:"\f111"}.la.la-header:before{content:"\f1dc"}.la.la-sliders:before{content:"\f1de"}.la.la-futbol-o{font-family:'Line Awesome Free';font-weight:400}.la.la-futbol-o:before{content:"\f1e3"}.la.la-soccer-ball-o{font-family:'Line Awesome Free';font-weight:400}.la.la-soccer-ball-o:before{content:"\f1e3"}.la.la-slideshare{font-family:'Line Awesome Brands';font-weight:400}.la.la-twitch{font-family:'Line Awesome Brands';font-weight:400}.la.la-yelp{font-family:'Line Awesome Brands';font-weight:400}.la.la-newspaper-o{font-family:'Line Awesome Free';font-weight:400}.la.la-newspaper-o:before{content:"\f1ea"}.la.la-paypal{font-family:'Line Awesome Brands';font-weight:400}.la.la-google-wallet{font-family:'Line Awesome Brands';font-weight:400}.la.la-cc-visa{font-family:'Line Awesome Brands';font-weight:400}.la.la-cc-mastercard{font-family:'Line Awesome Brands';font-weight:400}.la.la-cc-discover{font-family:'Line Awesome Brands';font-weight:400}.la.la-cc-amex{font-family:'Line Awesome Brands';font-weight:400}.la.la-cc-paypal{font-family:'Line Awesome Brands';font-weight:400}.la.la-cc-stripe{font-family:'Line Awesome Brands';font-weight:400}.la.la-bell-slash-o{font-family:'Line Awesome Free';font-weight:400}.la.la-bell-slash-o:before{content:"\f1f6"}.la.la-trash:before{content:"\f2ed"}.la.la-copyright{font-family:'Line Awesome Free';font-weight:400}.la.la-eyedropper:before{content:"\f1fb"}.la.la-area-chart:before{content:"\f1fe"}.la.la-pie-chart:before{content:"\f200"}.la.la-line-chart:before{content:"\f201"}.la.la-lastfm{font-family:'Line Awesome Brands';font-weight:400}.la.la-lastfm-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-ioxhost{font-family:'Line Awesome Brands';font-weight:400}.la.la-angellist{font-family:'Line Awesome Brands';font-weight:400}.la.la-cc{font-family:'Line Awesome Free';font-weight:400}.la.la-cc:before{content:"\f20a"}.la.la-ils:before{content:"\f20b"}.la.la-shekel:before{content:"\f20b"}.la.la-sheqel:before{content:"\f20b"}.la.la-meanpath{font-family:'Line Awesome Brands';font-weight:400}.la.la-meanpath:before{content:"\f2b4"}.la.la-buysellads{font-family:'Line Awesome Brands';font-weight:400}.la.la-connectdevelop{font-family:'Line Awesome Brands';font-weight:400}.la.la-dashcube{font-family:'Line Awesome Brands';font-weight:400}.la.la-forumbee{font-family:'Line Awesome Brands';font-weight:400}.la.la-leanpub{font-family:'Line Awesome Brands';font-weight:400}.la.la-sellsy{font-family:'Line Awesome Brands';font-weight:400}.la.la-shirtsinbulk{font-family:'Line Awesome Brands';font-weight:400}.la.la-simplybuilt{font-family:'Line Awesome Brands';font-weight:400}.la.la-skyatlas{font-family:'Line Awesome Brands';font-weight:400}.la.la-diamond{font-family:'Line Awesome Free';font-weight:400}.la.la-diamond:before{content:"\f3a5"}.la.la-intersex:before{content:"\f224"}.la.la-facebook-official{font-family:'Line Awesome Brands';font-weight:400}.la.la-facebook-official:before{content:"\f09a"}.la.la-pinterest-p{font-family:'Line Awesome Brands';font-weight:400}.la.la-whatsapp{font-family:'Line Awesome Brands';font-weight:400}.la.la-hotel:before{content:"\f236"}.la.la-viacoin{font-family:'Line Awesome Brands';font-weight:400}.la.la-medium{font-family:'Line Awesome Brands';font-weight:400}.la.la-y-combinator{font-family:'Line Awesome Brands';font-weight:400}.la.la-yc{font-family:'Line Awesome Brands';font-weight:400}.la.la-yc:before{content:"\f23b"}.la.la-optin-monster{font-family:'Line Awesome Brands';font-weight:400}.la.la-opencart{font-family:'Line Awesome Brands';font-weight:400}.la.la-expeditedssl{font-family:'Line Awesome Brands';font-weight:400}.la.la-battery-4:before{content:"\f240"}.la.la-battery:before{content:"\f240"}.la.la-battery-3:before{content:"\f241"}.la.la-battery-2:before{content:"\f242"}.la.la-battery-1:before{content:"\f243"}.la.la-battery-0:before{content:"\f244"}.la.la-object-group{font-family:'Line Awesome Free';font-weight:400}.la.la-object-ungroup{font-family:'Line Awesome Free';font-weight:400}.la.la-sticky-note-o{font-family:'Line Awesome Free';font-weight:400}.la.la-sticky-note-o:before{content:"\f249"}.la.la-cc-jcb{font-family:'Line Awesome Brands';font-weight:400}.la.la-cc-diners-club{font-family:'Line Awesome Brands';font-weight:400}.la.la-clone{font-family:'Line Awesome Free';font-weight:400}.la.la-hourglass-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hourglass-o:before{content:"\f254"}.la.la-hourglass-1:before{content:"\f251"}.la.la-hourglass-2:before{content:"\f252"}.la.la-hourglass-3:before{content:"\f253"}.la.la-hand-rock-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-rock-o:before{content:"\f255"}.la.la-hand-grab-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-grab-o:before{content:"\f255"}.la.la-hand-paper-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-paper-o:before{content:"\f256"}.la.la-hand-stop-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-stop-o:before{content:"\f256"}.la.la-hand-scissors-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-scissors-o:before{content:"\f257"}.la.la-hand-lizard-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-lizard-o:before{content:"\f258"}.la.la-hand-spock-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-spock-o:before{content:"\f259"}.la.la-hand-pointer-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-pointer-o:before{content:"\f25a"}.la.la-hand-peace-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-peace-o:before{content:"\f25b"}.la.la-registered{font-family:'Line Awesome Free';font-weight:400}.la.la-creative-commons{font-family:'Line Awesome Brands';font-weight:400}.la.la-gg{font-family:'Line Awesome Brands';font-weight:400}.la.la-gg-circle{font-family:'Line Awesome Brands';font-weight:400}.la.la-tripadvisor{font-family:'Line Awesome Brands';font-weight:400}.la.la-odnoklassniki{font-family:'Line Awesome Brands';font-weight:400}.la.la-odnoklassniki-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-get-pocket{font-family:'Line Awesome Brands';font-weight:400}.la.la-wikipedia-w{font-family:'Line Awesome Brands';font-weight:400}.la.la-safari{font-family:'Line Awesome Brands';font-weight:400}.la.la-chrome{font-family:'Line Awesome Brands';font-weight:400}.la.la-firefox{font-family:'Line Awesome Brands';font-weight:400}.la.la-opera{font-family:'Line Awesome Brands';font-weight:400}.la.la-internet-explorer{font-family:'Line Awesome Brands';font-weight:400}.la.la-television:before{content:"\f26c"}.la.la-contao{font-family:'Line Awesome Brands';font-weight:400}.la.la-500px{font-family:'Line Awesome Brands';font-weight:400}.la.la-amazon{font-family:'Line Awesome Brands';font-weight:400}.la.la-calendar-plus-o{font-family:'Line Awesome Free';font-weight:400}.la.la-calendar-plus-o:before{content:"\f271"}.la.la-calendar-minus-o{font-family:'Line Awesome Free';font-weight:400}.la.la-calendar-minus-o:before{content:"\f272"}.la.la-calendar-times-o{font-family:'Line Awesome Free';font-weight:400}.la.la-calendar-times-o:before{content:"\f273"}.la.la-calendar-check-o{font-family:'Line Awesome Free';font-weight:400}.la.la-calendar-check-o:before{content:"\f274"}.la.la-map-o{font-family:'Line Awesome Free';font-weight:400}.la.la-map-o:before{content:"\f279"}.la.la-commenting:before{content:"\f4ad"}.la.la-commenting-o{font-family:'Line Awesome Free';font-weight:400}.la.la-commenting-o:before{content:"\f4ad"}.la.la-houzz{font-family:'Line Awesome Brands';font-weight:400}.la.la-vimeo{font-family:'Line Awesome Brands';font-weight:400}.la.la-vimeo:before{content:"\f27d"}.la.la-black-tie{font-family:'Line Awesome Brands';font-weight:400}.la.la-fonticons{font-family:'Line Awesome Brands';font-weight:400}.la.la-reddit-alien{font-family:'Line Awesome Brands';font-weight:400}.la.la-edge{font-family:'Line Awesome Brands';font-weight:400}.la.la-credit-card-alt:before{content:"\f09d"}.la.la-codiepie{font-family:'Line Awesome Brands';font-weight:400}.la.la-modx{font-family:'Line Awesome Brands';font-weight:400}.la.la-fort-awesome{font-family:'Line Awesome Brands';font-weight:400}.la.la-usb{font-family:'Line Awesome Brands';font-weight:400}.la.la-product-hunt{font-family:'Line Awesome Brands';font-weight:400}.la.la-mixcloud{font-family:'Line Awesome Brands';font-weight:400}.la.la-scribd{font-family:'Line Awesome Brands';font-weight:400}.la.la-pause-circle-o{font-family:'Line Awesome Free';font-weight:400}.la.la-pause-circle-o:before{content:"\f28b"}.la.la-stop-circle-o{font-family:'Line Awesome Free';font-weight:400}.la.la-stop-circle-o:before{content:"\f28d"}.la.la-bluetooth{font-family:'Line Awesome Brands';font-weight:400}.la.la-bluetooth-b{font-family:'Line Awesome Brands';font-weight:400}.la.la-gitlab{font-family:'Line Awesome Brands';font-weight:400}.la.la-wpbeginner{font-family:'Line Awesome Brands';font-weight:400}.la.la-wpforms{font-family:'Line Awesome Brands';font-weight:400}.la.la-envira{font-family:'Line Awesome Brands';font-weight:400}.la.la-wheelchair-alt{font-family:'Line Awesome Brands';font-weight:400}.la.la-wheelchair-alt:before{content:"\f368"}.la.la-question-circle-o{font-family:'Line Awesome Free';font-weight:400}.la.la-question-circle-o:before{content:"\f059"}.la.la-volume-control-phone:before{content:"\f2a0"}.la.la-asl-interpreting:before{content:"\f2a3"}.la.la-deafness:before{content:"\f2a4"}.la.la-hard-of-hearing:before{content:"\f2a4"}.la.la-glide{font-family:'Line Awesome Brands';font-weight:400}.la.la-glide-g{font-family:'Line Awesome Brands';font-weight:400}.la.la-signing:before{content:"\f2a7"}.la.la-viadeo{font-family:'Line Awesome Brands';font-weight:400}.la.la-viadeo-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-snapchat{font-family:'Line Awesome Brands';font-weight:400}.la.la-snapchat-ghost{font-family:'Line Awesome Brands';font-weight:400}.la.la-snapchat-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-pied-piper{font-family:'Line Awesome Brands';font-weight:400}.la.la-first-order{font-family:'Line Awesome Brands';font-weight:400}.la.la-yoast{font-family:'Line Awesome Brands';font-weight:400}.la.la-themeisle{font-family:'Line Awesome Brands';font-weight:400}.la.la-google-plus-official{font-family:'Line Awesome Brands';font-weight:400}.la.la-google-plus-official:before{content:"\f2b3"}.la.la-google-plus-circle{font-family:'Line Awesome Brands';font-weight:400}.la.la-google-plus-circle:before{content:"\f2b3"}.la.la-font-awesome{font-family:'Line Awesome Brands';font-weight:400}.la.la-fa{font-family:'Line Awesome Brands';font-weight:400}.la.la-fa:before{content:"\f2b4"}.la.la-handshake-o{font-family:'Line Awesome Free';font-weight:400}.la.la-handshake-o:before{content:"\f2b5"}.la.la-envelope-open-o{font-family:'Line Awesome Free';font-weight:400}.la.la-envelope-open-o:before{content:"\f2b6"}.la.la-linode{font-family:'Line Awesome Brands';font-weight:400}.la.la-address-book-o{font-family:'Line Awesome Free';font-weight:400}.la.la-address-book-o:before{content:"\f2b9"}.la.la-vcard:before{content:"\f2bb"}.la.la-address-card-o{font-family:'Line Awesome Free';font-weight:400}.la.la-address-card-o:before{content:"\f2bb"}.la.la-vcard-o{font-family:'Line Awesome Free';font-weight:400}.la.la-vcard-o:before{content:"\f2bb"}.la.la-user-circle-o{font-family:'Line Awesome Free';font-weight:400}.la.la-user-circle-o:before{content:"\f2bd"}.la.la-user-o{font-family:'Line Awesome Free';font-weight:400}.la.la-user-o:before{content:"\f007"}.la.la-id-badge{font-family:'Line Awesome Free';font-weight:400}.la.la-drivers-license:before{content:"\f2c2"}.la.la-id-card-o{font-family:'Line Awesome Free';font-weight:400}.la.la-id-card-o:before{content:"\f2c2"}.la.la-drivers-license-o{font-family:'Line Awesome Free';font-weight:400}.la.la-drivers-license-o:before{content:"\f2c2"}.la.la-quora{font-family:'Line Awesome Brands';font-weight:400}.la.la-free-code-camp{font-family:'Line Awesome Brands';font-weight:400}.la.la-telegram{font-family:'Line Awesome Brands';font-weight:400}.la.la-thermometer-4:before{content:"\f2c7"}.la.la-thermometer:before{content:"\f2c7"}.la.la-thermometer-3:before{content:"\f2c8"}.la.la-thermometer-2:before{content:"\f2c9"}.la.la-thermometer-1:before{content:"\f2ca"}.la.la-thermometer-0:before{content:"\f2cb"}.la.la-bathtub:before{content:"\f2cd"}.la.la-s15:before{content:"\f2cd"}.la.la-window-maximize{font-family:'Line Awesome Free';font-weight:400}.la.la-window-restore{font-family:'Line Awesome Free';font-weight:400}.la.la-times-rectangle:before{content:"\f410"}.la.la-window-close-o{font-family:'Line Awesome Free';font-weight:400}.la.la-window-close-o:before{content:"\f410"}.la.la-times-rectangle-o{font-family:'Line Awesome Free';font-weight:400}.la.la-times-rectangle-o:before{content:"\f410"}.la.la-bandcamp{font-family:'Line Awesome Brands';font-weight:400}.la.la-grav{font-family:'Line Awesome Brands';font-weight:400}.la.la-etsy{font-family:'Line Awesome Brands';font-weight:400}.la.la-imdb{font-family:'Line Awesome Brands';font-weight:400}.la.la-ravelry{font-family:'Line Awesome Brands';font-weight:400}.la.la-eercast{font-family:'Line Awesome Brands';font-weight:400}.la.la-eercast:before{content:"\f2da"}.la.la-snowflake-o{font-family:'Line Awesome Free';font-weight:400}.la.la-snowflake-o:before{content:"\f2dc"}.la.la-superpowers{font-family:'Line Awesome Brands';font-weight:400}.la.la-wpexplorer{font-family:'Line Awesome Brands';font-weight:400}.la.la-spotify{font-family:'Line Awesome Brands';font-weight:400} +.la,.lab,.lad,.lal,.lar,.las{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.la-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.la-xs{font-size:.75em}.la-sm{font-size:.875em}.la-1x{font-size:1em}.la-2x{font-size:2em}.la-3x{font-size:3em}.la-4x{font-size:4em}.la-5x{font-size:5em}.la-6x{font-size:6em}.la-7x{font-size:7em}.la-8x{font-size:8em}.la-9x{font-size:9em}.la-10x{font-size:10em}.la-fw{text-align:center;width:1.25em}.la-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.la-ul>li{position:relative}.la-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.la-border{border:solid .08em #eee;border-radius:.1em;padding:.2em .25em .15em}.la-pull-left{float:left}.la-pull-right{float:right}.la.la-pull-left,.lab.la-pull-left,.lal.la-pull-left,.lar.la-pull-left,.las.la-pull-left{margin-right:.3em}.la.la-pull-right,.lab.la-pull-right,.lal.la-pull-right,.lar.la-pull-right,.las.la-pull-right{margin-left:.3em}.la-spin{-webkit-animation:la-spin 2s infinite linear;animation:la-spin 2s infinite linear}.la-pulse{-webkit-animation:la-spin 1s infinite steps(8);animation:la-spin 1s infinite steps(8)}@-webkit-keyframes la-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes la-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.la-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.la-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.la-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.la-flip-horizontal{-webkit-transform:scale(-1,1);transform:scale(-1,1)}.la-flip-vertical{-webkit-transform:scale(1,-1);transform:scale(1,-1)}.la-flip-both,.la-flip-horizontal.la-flip-vertical{-webkit-transform:scale(-1,-1);transform:scale(-1,-1)}:root .la-flip-both,:root .la-flip-horizontal,:root .la-flip-vertical,:root .la-rotate-180,:root .la-rotate-270,:root .la-rotate-90{-webkit-filter:none;filter:none}.la-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.la-stack-1x,.la-stack-2x{left:0;position:absolute;text-align:center;width:100%}.la-stack-1x{line-height:inherit}.la-stack-2x{font-size:2em}.la-inverse{color:#fff}.la-500px:before{content:"\f26e"}.la-accessible-icon:before{content:"\f368"}.la-accusoft:before{content:"\f369"}.la-acquisitions-incorporated:before{content:"\f6af"}.la-ad:before{content:"\f641"}.la-address-book:before{content:"\f2b9"}.la-address-card:before{content:"\f2bb"}.la-adjust:before{content:"\f042"}.la-adn:before{content:"\f170"}.la-adobe:before{content:"\f778"}.la-adversal:before{content:"\f36a"}.la-affiliatetheme:before{content:"\f36b"}.la-air-freshener:before{content:"\f5d0"}.la-airbnb:before{content:"\f834"}.la-algolia:before{content:"\f36c"}.la-align-center:before{content:"\f037"}.la-align-justify:before{content:"\f039"}.la-align-left:before{content:"\f036"}.la-align-right:before{content:"\f038"}.la-alipay:before{content:"\f642"}.la-allergies:before{content:"\f461"}.la-amazon:before{content:"\f270"}.la-amazon-pay:before{content:"\f42c"}.la-ambulance:before{content:"\f0f9"}.la-american-sign-language-interpreting:before{content:"\f2a3"}.la-amilia:before{content:"\f36d"}.la-anchor:before{content:"\f13d"}.la-android:before{content:"\f17b"}.la-angellist:before{content:"\f209"}.la-angle-double-down:before{content:"\f103"}.la-angle-double-left:before{content:"\f100"}.la-angle-double-right:before{content:"\f101"}.la-angle-double-up:before{content:"\f102"}.la-angle-down:before{content:"\f107"}.la-angle-left:before{content:"\f104"}.la-angle-right:before{content:"\f105"}.la-angle-up:before{content:"\f106"}.la-angry:before{content:"\f556"}.la-angrycreative:before{content:"\f36e"}.la-angular:before{content:"\f420"}.la-ankh:before{content:"\f644"}.la-app-store:before{content:"\f36f"}.la-app-store-ios:before{content:"\f370"}.la-apper:before{content:"\f371"}.la-apple:before{content:"\f179"}.la-apple-alt:before{content:"\f5d1"}.la-apple-pay:before{content:"\f415"}.la-archive:before{content:"\f187"}.la-archway:before{content:"\f557"}.la-arrow-alt-circle-down:before{content:"\f358"}.la-arrow-alt-circle-left:before{content:"\f359"}.la-arrow-alt-circle-right:before{content:"\f35a"}.la-arrow-alt-circle-up:before{content:"\f35b"}.la-arrow-circle-down:before{content:"\f0ab"}.la-arrow-circle-left:before{content:"\f0a8"}.la-arrow-circle-right:before{content:"\f0a9"}.la-arrow-circle-up:before{content:"\f0aa"}.la-arrow-down:before{content:"\f063"}.la-arrow-left:before{content:"\f060"}.la-arrow-right:before{content:"\f061"}.la-arrow-up:before{content:"\f062"}.la-arrows-alt:before{content:"\f0b2"}.la-arrows-alt-h:before{content:"\f337"}.la-arrows-alt-v:before{content:"\f338"}.la-artstation:before{content:"\f77a"}.la-assistive-listening-systems:before{content:"\f2a2"}.la-asterisk:before{content:"\f069"}.la-asymmetrik:before{content:"\f372"}.la-at:before{content:"\f1fa"}.la-atlas:before{content:"\f558"}.la-atlassian:before{content:"\f77b"}.la-atom:before{content:"\f5d2"}.la-audible:before{content:"\f373"}.la-audio-description:before{content:"\f29e"}.la-autoprefixer:before{content:"\f41c"}.la-avianex:before{content:"\f374"}.la-aviato:before{content:"\f421"}.la-award:before{content:"\f559"}.la-aws:before{content:"\f375"}.la-baby:before{content:"\f77c"}.la-baby-carriage:before{content:"\f77d"}.la-backspace:before{content:"\f55a"}.la-backward:before{content:"\f04a"}.la-bacon:before{content:"\f7e5"}.la-balance-scale:before{content:"\f24e"}.la-balance-scale-left:before{content:"\f515"}.la-balance-scale-right:before{content:"\f516"}.la-ban:before{content:"\f05e"}.la-band-aid:before{content:"\f462"}.la-bandcamp:before{content:"\f2d5"}.la-barcode:before{content:"\f02a"}.la-bars:before{content:"\f0c9"}.la-baseball-ball:before{content:"\f433"}.la-basketball-ball:before{content:"\f434"}.la-bath:before{content:"\f2cd"}.la-battery-empty:before{content:"\f244"}.la-battery-full:before{content:"\f240"}.la-battery-half:before{content:"\f242"}.la-battery-quarter:before{content:"\f243"}.la-battery-three-quarters:before{content:"\f241"}.la-battle-net:before{content:"\f835"}.la-bed:before{content:"\f236"}.la-beer:before{content:"\f0fc"}.la-behance:before{content:"\f1b4"}.la-behance-square:before{content:"\f1b5"}.la-bell:before{content:"\f0f3"}.la-bell-slash:before{content:"\f1f6"}.la-bezier-curve:before{content:"\f55b"}.la-bible:before{content:"\f647"}.la-bicycle:before{content:"\f206"}.la-biking:before{content:"\f84a"}.la-bimobject:before{content:"\f378"}.la-binoculars:before{content:"\f1e5"}.la-biohazard:before{content:"\f780"}.la-birthday-cake:before{content:"\f1fd"}.la-bitbucket:before{content:"\f171"}.la-bitcoin:before{content:"\f379"}.la-bity:before{content:"\f37a"}.la-black-tie:before{content:"\f27e"}.la-blackberry:before{content:"\f37b"}.la-blender:before{content:"\f517"}.la-blender-phone:before{content:"\f6b6"}.la-blind:before{content:"\f29d"}.la-blog:before{content:"\f781"}.la-blogger:before{content:"\f37c"}.la-blogger-b:before{content:"\f37d"}.la-bluetooth:before{content:"\f293"}.la-bluetooth-b:before{content:"\f294"}.la-bold:before{content:"\f032"}.la-bolt:before{content:"\f0e7"}.la-bomb:before{content:"\f1e2"}.la-bone:before{content:"\f5d7"}.la-bong:before{content:"\f55c"}.la-book:before{content:"\f02d"}.la-book-dead:before{content:"\f6b7"}.la-book-medical:before{content:"\f7e6"}.la-book-open:before{content:"\f518"}.la-book-reader:before{content:"\f5da"}.la-bookmark:before{content:"\f02e"}.la-bootstrap:before{content:"\f836"}.la-border-all:before{content:"\f84c"}.la-border-none:before{content:"\f850"}.la-border-style:before{content:"\f853"}.la-bowling-ball:before{content:"\f436"}.la-box:before{content:"\f466"}.la-box-open:before{content:"\f49e"}.la-boxes:before{content:"\f468"}.la-braille:before{content:"\f2a1"}.la-brain:before{content:"\f5dc"}.la-bread-slice:before{content:"\f7ec"}.la-briefcase:before{content:"\f0b1"}.la-briefcase-medical:before{content:"\f469"}.la-broadcast-tower:before{content:"\f519"}.la-broom:before{content:"\f51a"}.la-brush:before{content:"\f55d"}.la-btc:before{content:"\f15a"}.la-buffer:before{content:"\f837"}.la-bug:before{content:"\f188"}.la-building:before{content:"\f1ad"}.la-bullhorn:before{content:"\f0a1"}.la-bullseye:before{content:"\f140"}.la-burn:before{content:"\f46a"}.la-buromobelexperte:before{content:"\f37f"}.la-bus:before{content:"\f207"}.la-bus-alt:before{content:"\f55e"}.la-business-time:before{content:"\f64a"}.la-buy-n-large:before{content:"\f8a6"}.la-buysellads:before{content:"\f20d"}.la-calculator:before{content:"\f1ec"}.la-calendar:before{content:"\f133"}.la-calendar-alt:before{content:"\f073"}.la-calendar-check:before{content:"\f274"}.la-calendar-day:before{content:"\f783"}.la-calendar-minus:before{content:"\f272"}.la-calendar-plus:before{content:"\f271"}.la-calendar-times:before{content:"\f273"}.la-calendar-week:before{content:"\f784"}.la-camera:before{content:"\f030"}.la-camera-retro:before{content:"\f083"}.la-campground:before{content:"\f6bb"}.la-canadian-maple-leaf:before{content:"\f785"}.la-candy-cane:before{content:"\f786"}.la-cannabis:before{content:"\f55f"}.la-capsules:before{content:"\f46b"}.la-car:before{content:"\f1b9"}.la-car-alt:before{content:"\f5de"}.la-car-battery:before{content:"\f5df"}.la-car-crash:before{content:"\f5e1"}.la-car-side:before{content:"\f5e4"}.la-caret-down:before{content:"\f0d7"}.la-caret-left:before{content:"\f0d9"}.la-caret-right:before{content:"\f0da"}.la-caret-square-down:before{content:"\f150"}.la-caret-square-left:before{content:"\f191"}.la-caret-square-right:before{content:"\f152"}.la-caret-square-up:before{content:"\f151"}.la-caret-up:before{content:"\f0d8"}.la-carrot:before{content:"\f787"}.la-cart-arrow-down:before{content:"\f218"}.la-cart-plus:before{content:"\f217"}.la-cash-register:before{content:"\f788"}.la-cat:before{content:"\f6be"}.la-cc-amazon-pay:before{content:"\f42d"}.la-cc-amex:before{content:"\f1f3"}.la-cc-apple-pay:before{content:"\f416"}.la-cc-diners-club:before{content:"\f24c"}.la-cc-discover:before{content:"\f1f2"}.la-cc-jcb:before{content:"\f24b"}.la-cc-mastercard:before{content:"\f1f1"}.la-cc-paypal:before{content:"\f1f4"}.la-cc-stripe:before{content:"\f1f5"}.la-cc-visa:before{content:"\f1f0"}.la-centercode:before{content:"\f380"}.la-centos:before{content:"\f789"}.la-certificate:before{content:"\f0a3"}.la-chair:before{content:"\f6c0"}.la-chalkboard:before{content:"\f51b"}.la-chalkboard-teacher:before{content:"\f51c"}.la-charging-station:before{content:"\f5e7"}.la-chart-area:before{content:"\f1fe"}.la-chart-bar:before{content:"\f080"}.la-chart-line:before{content:"\f201"}.la-chart-pie:before{content:"\f200"}.la-check:before{content:"\f00c"}.la-check-circle:before{content:"\f058"}.la-check-double:before{content:"\f560"}.la-check-square:before{content:"\f14a"}.la-cheese:before{content:"\f7ef"}.la-chess:before{content:"\f439"}.la-chess-bishop:before{content:"\f43a"}.la-chess-board:before{content:"\f43c"}.la-chess-king:before{content:"\f43f"}.la-chess-knight:before{content:"\f441"}.la-chess-pawn:before{content:"\f443"}.la-chess-queen:before{content:"\f445"}.la-chess-rook:before{content:"\f447"}.la-chevron-circle-down:before{content:"\f13a"}.la-chevron-circle-left:before{content:"\f137"}.la-chevron-circle-right:before{content:"\f138"}.la-chevron-circle-up:before{content:"\f139"}.la-chevron-down:before{content:"\f078"}.la-chevron-left:before{content:"\f053"}.la-chevron-right:before{content:"\f054"}.la-chevron-up:before{content:"\f077"}.la-child:before{content:"\f1ae"}.la-chrome:before{content:"\f268"}.la-chromecast:before{content:"\f838"}.la-church:before{content:"\f51d"}.la-circle:before{content:"\f111"}.la-circle-notch:before{content:"\f1ce"}.la-city:before{content:"\f64f"}.la-clinic-medical:before{content:"\f7f2"}.la-clipboard:before{content:"\f328"}.la-clipboard-check:before{content:"\f46c"}.la-clipboard-list:before{content:"\f46d"}.la-clock:before{content:"\f017"}.la-clone:before{content:"\f24d"}.la-closed-captioning:before{content:"\f20a"}.la-cloud:before{content:"\f0c2"}.la-cloud-download-alt:before{content:"\f381"}.la-cloud-meatball:before{content:"\f73b"}.la-cloud-moon:before{content:"\f6c3"}.la-cloud-moon-rain:before{content:"\f73c"}.la-cloud-rain:before{content:"\f73d"}.la-cloud-showers-heavy:before{content:"\f740"}.la-cloud-sun:before{content:"\f6c4"}.la-cloud-sun-rain:before{content:"\f743"}.la-cloud-upload-alt:before{content:"\f382"}.la-cloudscale:before{content:"\f383"}.la-cloudsmith:before{content:"\f384"}.la-cloudversify:before{content:"\f385"}.la-cocktail:before{content:"\f561"}.la-code:before{content:"\f121"}.la-code-branch:before{content:"\f126"}.la-codepen:before{content:"\f1cb"}.la-codiepie:before{content:"\f284"}.la-coffee:before{content:"\f0f4"}.la-cog:before{content:"\f013"}.la-cogs:before{content:"\f085"}.la-coins:before{content:"\f51e"}.la-columns:before{content:"\f0db"}.la-comment:before{content:"\f075"}.la-comment-alt:before{content:"\f27a"}.la-comment-dollar:before{content:"\f651"}.la-comment-dots:before{content:"\f4ad"}.la-comment-medical:before{content:"\f7f5"}.la-comment-slash:before{content:"\f4b3"}.la-comments:before{content:"\f086"}.la-comments-dollar:before{content:"\f653"}.la-compact-disc:before{content:"\f51f"}.la-compass:before{content:"\f14e"}.la-compress:before{content:"\f066"}.la-compress-arrows-alt:before{content:"\f78c"}.la-concierge-bell:before{content:"\f562"}.la-confluence:before{content:"\f78d"}.la-connectdevelop:before{content:"\f20e"}.la-contao:before{content:"\f26d"}.la-cookie:before{content:"\f563"}.la-cookie-bite:before{content:"\f564"}.la-copy:before{content:"\f0c5"}.la-copyright:before{content:"\f1f9"}.la-cotton-bureau:before{content:"\f89e"}.la-couch:before{content:"\f4b8"}.la-cpanel:before{content:"\f388"}.la-creative-commons:before{content:"\f25e"}.la-creative-commons-by:before{content:"\f4e7"}.la-creative-commons-nc:before{content:"\f4e8"}.la-creative-commons-nc-eu:before{content:"\f4e9"}.la-creative-commons-nc-jp:before{content:"\f4ea"}.la-creative-commons-nd:before{content:"\f4eb"}.la-creative-commons-pd:before{content:"\f4ec"}.la-creative-commons-pd-alt:before{content:"\f4ed"}.la-creative-commons-remix:before{content:"\f4ee"}.la-creative-commons-sa:before{content:"\f4ef"}.la-creative-commons-sampling:before{content:"\f4f0"}.la-creative-commons-sampling-plus:before{content:"\f4f1"}.la-creative-commons-share:before{content:"\f4f2"}.la-creative-commons-zero:before{content:"\f4f3"}.la-credit-card:before{content:"\f09d"}.la-critical-role:before{content:"\f6c9"}.la-crop:before{content:"\f125"}.la-crop-alt:before{content:"\f565"}.la-cross:before{content:"\f654"}.la-crosshairs:before{content:"\f05b"}.la-crow:before{content:"\f520"}.la-crown:before{content:"\f521"}.la-crutch:before{content:"\f7f7"}.la-css3:before{content:"\f13c"}.la-css3-alt:before{content:"\f38b"}.la-cube:before{content:"\f1b2"}.la-cubes:before{content:"\f1b3"}.la-cut:before{content:"\f0c4"}.la-cuttlefish:before{content:"\f38c"}.la-d-and-d:before{content:"\f38d"}.la-d-and-d-beyond:before{content:"\f6ca"}.la-dashcube:before{content:"\f210"}.la-database:before{content:"\f1c0"}.la-deaf:before{content:"\f2a4"}.la-delicious:before{content:"\f1a5"}.la-democrat:before{content:"\f747"}.la-deploydog:before{content:"\f38e"}.la-deskpro:before{content:"\f38f"}.la-desktop:before{content:"\f108"}.la-dev:before{content:"\f6cc"}.la-deviantart:before{content:"\f1bd"}.la-dharmachakra:before{content:"\f655"}.la-dhl:before{content:"\f790"}.la-diagnoses:before{content:"\f470"}.la-diaspora:before{content:"\f791"}.la-dice:before{content:"\f522"}.la-dice-d20:before{content:"\f6cf"}.la-dice-d6:before{content:"\f6d1"}.la-dice-five:before{content:"\f523"}.la-dice-four:before{content:"\f524"}.la-dice-one:before{content:"\f525"}.la-dice-six:before{content:"\f526"}.la-dice-three:before{content:"\f527"}.la-dice-two:before{content:"\f528"}.la-digg:before{content:"\f1a6"}.la-digital-ocean:before{content:"\f391"}.la-digital-tachograph:before{content:"\f566"}.la-directions:before{content:"\f5eb"}.la-discord:before{content:"\f392"}.la-discourse:before{content:"\f393"}.la-divide:before{content:"\f529"}.la-dizzy:before{content:"\f567"}.la-dna:before{content:"\f471"}.la-dochub:before{content:"\f394"}.la-docker:before{content:"\f395"}.la-dog:before{content:"\f6d3"}.la-dollar-sign:before{content:"\f155"}.la-dolly:before{content:"\f472"}.la-dolly-flatbed:before{content:"\f474"}.la-donate:before{content:"\f4b9"}.la-door-closed:before{content:"\f52a"}.la-door-open:before{content:"\f52b"}.la-dot-circle:before{content:"\f192"}.la-dove:before{content:"\f4ba"}.la-download:before{content:"\f019"}.la-draft2digital:before{content:"\f396"}.la-drafting-compass:before{content:"\f568"}.la-dragon:before{content:"\f6d5"}.la-draw-polygon:before{content:"\f5ee"}.la-dribbble:before{content:"\f17d"}.la-dribbble-square:before{content:"\f397"}.la-dropbox:before{content:"\f16b"}.la-drum:before{content:"\f569"}.la-drum-steelpan:before{content:"\f56a"}.la-drumstick-bite:before{content:"\f6d7"}.la-drupal:before{content:"\f1a9"}.la-dumbbell:before{content:"\f44b"}.la-dumpster:before{content:"\f793"}.la-dumpster-fire:before{content:"\f794"}.la-dungeon:before{content:"\f6d9"}.la-dyalog:before{content:"\f399"}.la-earlybirds:before{content:"\f39a"}.la-ebay:before{content:"\f4f4"}.la-edge:before{content:"\f282"}.la-edit:before{content:"\f044"}.la-egg:before{content:"\f7fb"}.la-eject:before{content:"\f052"}.la-elementor:before{content:"\f430"}.la-ellipsis-h:before{content:"\f141"}.la-ellipsis-v:before{content:"\f142"}.la-ello:before{content:"\f5f1"}.la-ember:before{content:"\f423"}.la-empire:before{content:"\f1d1"}.la-envelope:before{content:"\f0e0"}.la-envelope-open:before{content:"\f2b6"}.la-envelope-open-text:before{content:"\f658"}.la-envelope-square:before{content:"\f199"}.la-envira:before{content:"\f299"}.la-equals:before{content:"\f52c"}.la-eraser:before{content:"\f12d"}.la-erlang:before{content:"\f39d"}.la-ethereum:before{content:"\f42e"}.la-ethernet:before{content:"\f796"}.la-etsy:before{content:"\f2d7"}.la-euro-sign:before{content:"\f153"}.la-evernote:before{content:"\f839"}.la-exchange-alt:before{content:"\f362"}.la-exclamation:before{content:"\f12a"}.la-exclamation-circle:before{content:"\f06a"}.la-exclamation-triangle:before{content:"\f071"}.la-expand:before{content:"\f065"}.la-expand-arrows-alt:before{content:"\f31e"}.la-expeditedssl:before{content:"\f23e"}.la-external-link-alt:before{content:"\f35d"}.la-external-link-square-alt:before{content:"\f360"}.la-eye:before{content:"\f06e"}.la-eye-dropper:before{content:"\f1fb"}.la-eye-slash:before{content:"\f070"}.la-facebook:before{content:"\f09a"}.la-facebook-f:before{content:"\f39e"}.la-facebook-messenger:before{content:"\f39f"}.la-facebook-square:before{content:"\f082"}.la-fan:before{content:"\f863"}.la-fantasy-flight-games:before{content:"\f6dc"}.la-fast-backward:before{content:"\f049"}.la-fast-forward:before{content:"\f050"}.la-fax:before{content:"\f1ac"}.la-feather:before{content:"\f52d"}.la-feather-alt:before{content:"\f56b"}.la-fedex:before{content:"\f797"}.la-fedora:before{content:"\f798"}.la-female:before{content:"\f182"}.la-fighter-jet:before{content:"\f0fb"}.la-figma:before{content:"\f799"}.la-file:before{content:"\f15b"}.la-file-alt:before{content:"\f15c"}.la-file-archive:before{content:"\f1c6"}.la-file-audio:before{content:"\f1c7"}.la-file-code:before{content:"\f1c9"}.la-file-contract:before{content:"\f56c"}.la-file-csv:before{content:"\f6dd"}.la-file-download:before{content:"\f56d"}.la-file-excel:before{content:"\f1c3"}.la-file-export:before{content:"\f56e"}.la-file-image:before{content:"\f1c5"}.la-file-import:before{content:"\f56f"}.la-file-invoice:before{content:"\f570"}.la-file-invoice-dollar:before{content:"\f571"}.la-file-medical:before{content:"\f477"}.la-file-medical-alt:before{content:"\f478"}.la-file-pdf:before{content:"\f1c1"}.la-file-powerpoint:before{content:"\f1c4"}.la-file-prescription:before{content:"\f572"}.la-file-signature:before{content:"\f573"}.la-file-upload:before{content:"\f574"}.la-file-video:before{content:"\f1c8"}.la-file-word:before{content:"\f1c2"}.la-fill:before{content:"\f575"}.la-fill-drip:before{content:"\f576"}.la-film:before{content:"\f008"}.la-filter:before{content:"\f0b0"}.la-fingerprint:before{content:"\f577"}.la-fire:before{content:"\f06d"}.la-fire-alt:before{content:"\f7e4"}.la-fire-extinguisher:before{content:"\f134"}.la-firefox:before{content:"\f269"}.la-first-aid:before{content:"\f479"}.la-first-order:before{content:"\f2b0"}.la-first-order-alt:before{content:"\f50a"}.la-firstdraft:before{content:"\f3a1"}.la-fish:before{content:"\f578"}.la-fist-raised:before{content:"\f6de"}.la-flag:before{content:"\f024"}.la-flag-checkered:before{content:"\f11e"}.la-flag-usa:before{content:"\f74d"}.la-flask:before{content:"\f0c3"}.la-flickr:before{content:"\f16e"}.la-flipboard:before{content:"\f44d"}.la-flushed:before{content:"\f579"}.la-fly:before{content:"\f417"}.la-folder:before{content:"\f07b"}.la-folder-minus:before{content:"\f65d"}.la-folder-open:before{content:"\f07c"}.la-folder-plus:before{content:"\f65e"}.la-font:before{content:"\f031"}.la-font-awesome:before{content:"\f2b4"}.la-font-awesome-alt:before{content:"\f35c"}.la-font-awesome-flag:before{content:"\f425"}.la-font-awesome-logo-full:before{content:"\f4e6"}.la-fonticons:before{content:"\f280"}.la-fonticons-fi:before{content:"\f3a2"}.la-football-ball:before{content:"\f44e"}.la-fort-awesome:before{content:"\f286"}.la-fort-awesome-alt:before{content:"\f3a3"}.la-forumbee:before{content:"\f211"}.la-forward:before{content:"\f04e"}.la-foursquare:before{content:"\f180"}.la-free-code-camp:before{content:"\f2c5"}.la-freebsd:before{content:"\f3a4"}.la-frog:before{content:"\f52e"}.la-frown:before{content:"\f119"}.la-frown-open:before{content:"\f57a"}.la-fulcrum:before{content:"\f50b"}.la-funnel-dollar:before{content:"\f662"}.la-futbol:before{content:"\f1e3"}.la-galactic-republic:before{content:"\f50c"}.la-galactic-senate:before{content:"\f50d"}.la-gamepad:before{content:"\f11b"}.la-gas-pump:before{content:"\f52f"}.la-gavel:before{content:"\f0e3"}.la-gem:before{content:"\f3a5"}.la-genderless:before{content:"\f22d"}.la-get-pocket:before{content:"\f265"}.la-gg:before{content:"\f260"}.la-gg-circle:before{content:"\f261"}.la-ghost:before{content:"\f6e2"}.la-gift:before{content:"\f06b"}.la-gifts:before{content:"\f79c"}.la-git:before{content:"\f1d3"}.la-git-alt:before{content:"\f841"}.la-git-square:before{content:"\f1d2"}.la-github:before{content:"\f09b"}.la-github-alt:before{content:"\f113"}.la-github-square:before{content:"\f092"}.la-gitkraken:before{content:"\f3a6"}.la-gitlab:before{content:"\f296"}.la-gitter:before{content:"\f426"}.la-glass-cheers:before{content:"\f79f"}.la-glass-martini:before{content:"\f000"}.la-glass-martini-alt:before{content:"\f57b"}.la-glass-whiskey:before{content:"\f7a0"}.la-glasses:before{content:"\f530"}.la-glide:before{content:"\f2a5"}.la-glide-g:before{content:"\f2a6"}.la-globe:before{content:"\f0ac"}.la-globe-africa:before{content:"\f57c"}.la-globe-americas:before{content:"\f57d"}.la-globe-asia:before{content:"\f57e"}.la-globe-europe:before{content:"\f7a2"}.la-gofore:before{content:"\f3a7"}.la-golf-ball:before{content:"\f450"}.la-goodreads:before{content:"\f3a8"}.la-goodreads-g:before{content:"\f3a9"}.la-google:before{content:"\f1a0"}.la-google-drive:before{content:"\f3aa"}.la-google-play:before{content:"\f3ab"}.la-google-plus:before{content:"\f2b3"}.la-google-plus-g:before{content:"\f0d5"}.la-google-plus-square:before{content:"\f0d4"}.la-google-wallet:before{content:"\f1ee"}.la-gopuram:before{content:"\f664"}.la-graduation-cap:before{content:"\f19d"}.la-gratipay:before{content:"\f184"}.la-grav:before{content:"\f2d6"}.la-greater-than:before{content:"\f531"}.la-greater-than-equal:before{content:"\f532"}.la-grimace:before{content:"\f57f"}.la-grin:before{content:"\f580"}.la-grin-alt:before{content:"\f581"}.la-grin-beam:before{content:"\f582"}.la-grin-beam-sweat:before{content:"\f583"}.la-grin-hearts:before{content:"\f584"}.la-grin-squint:before{content:"\f585"}.la-grin-squint-tears:before{content:"\f586"}.la-grin-stars:before{content:"\f587"}.la-grin-tears:before{content:"\f588"}.la-grin-tongue:before{content:"\f589"}.la-grin-tongue-squint:before{content:"\f58a"}.la-grin-tongue-wink:before{content:"\f58b"}.la-grin-wink:before{content:"\f58c"}.la-grip-horizontal:before{content:"\f58d"}.la-grip-lines:before{content:"\f7a4"}.la-grip-lines-vertical:before{content:"\f7a5"}.la-grip-vertical:before{content:"\f58e"}.la-gripfire:before{content:"\f3ac"}.la-grunt:before{content:"\f3ad"}.la-guitar:before{content:"\f7a6"}.la-gulp:before{content:"\f3ae"}.la-h-square:before{content:"\f0fd"}.la-hacker-news:before{content:"\f1d4"}.la-hacker-news-square:before{content:"\f3af"}.la-hackerrank:before{content:"\f5f7"}.la-hamburger:before{content:"\f805"}.la-hammer:before{content:"\f6e3"}.la-hamsa:before{content:"\f665"}.la-hand-holding:before{content:"\f4bd"}.la-hand-holding-heart:before{content:"\f4be"}.la-hand-holding-usd:before{content:"\f4c0"}.la-hand-lizard:before{content:"\f258"}.la-hand-middle-finger:before{content:"\f806"}.la-hand-paper:before{content:"\f256"}.la-hand-peace:before{content:"\f25b"}.la-hand-point-down:before{content:"\f0a7"}.la-hand-point-left:before{content:"\f0a5"}.la-hand-point-right:before{content:"\f0a4"}.la-hand-point-up:before{content:"\f0a6"}.la-hand-pointer:before{content:"\f25a"}.la-hand-rock:before{content:"\f255"}.la-hand-scissors:before{content:"\f257"}.la-hand-spock:before{content:"\f259"}.la-hands:before{content:"\f4c2"}.la-hands-helping:before{content:"\f4c4"}.la-handshake:before{content:"\f2b5"}.la-hanukiah:before{content:"\f6e6"}.la-hard-hat:before{content:"\f807"}.la-hashtag:before{content:"\f292"}.la-hat-cowboy:before{content:"\f8c0"}.la-hat-cowboy-side:before{content:"\f8c1"}.la-hat-wizard:before{content:"\f6e8"}.la-haykal:before{content:"\f666"}.la-hdd:before{content:"\f0a0"}.la-heading:before{content:"\f1dc"}.la-headphones:before{content:"\f025"}.la-headphones-alt:before{content:"\f58f"}.la-headset:before{content:"\f590"}.la-heart:before{content:"\f004"}.la-heart-broken:before{content:"\f7a9"}.la-heartbeat:before{content:"\f21e"}.la-helicopter:before{content:"\f533"}.la-highlighter:before{content:"\f591"}.la-hiking:before{content:"\f6ec"}.la-hippo:before{content:"\f6ed"}.la-hips:before{content:"\f452"}.la-hire-a-helper:before{content:"\f3b0"}.la-history:before{content:"\f1da"}.la-hockey-puck:before{content:"\f453"}.la-holly-berry:before{content:"\f7aa"}.la-home:before{content:"\f015"}.la-hooli:before{content:"\f427"}.la-hornbill:before{content:"\f592"}.la-horse:before{content:"\f6f0"}.la-horse-head:before{content:"\f7ab"}.la-hospital:before{content:"\f0f8"}.la-hospital-alt:before{content:"\f47d"}.la-hospital-symbol:before{content:"\f47e"}.la-hot-tub:before{content:"\f593"}.la-hotdog:before{content:"\f80f"}.la-hotel:before{content:"\f594"}.la-hotjar:before{content:"\f3b1"}.la-hourglass:before{content:"\f254"}.la-hourglass-end:before{content:"\f253"}.la-hourglass-half:before{content:"\f252"}.la-hourglass-start:before{content:"\f251"}.la-house-damage:before{content:"\f6f1"}.la-houzz:before{content:"\f27c"}.la-hryvnia:before{content:"\f6f2"}.la-html5:before{content:"\f13b"}.la-hubspot:before{content:"\f3b2"}.la-i-cursor:before{content:"\f246"}.la-ice-cream:before{content:"\f810"}.la-icicles:before{content:"\f7ad"}.la-icons:before{content:"\f86d"}.la-id-badge:before{content:"\f2c1"}.la-id-card:before{content:"\f2c2"}.la-id-card-alt:before{content:"\f47f"}.la-igloo:before{content:"\f7ae"}.la-image:before{content:"\f03e"}.la-images:before{content:"\f302"}.la-imdb:before{content:"\f2d8"}.la-inbox:before{content:"\f01c"}.la-indent:before{content:"\f03c"}.la-industry:before{content:"\f275"}.la-infinity:before{content:"\f534"}.la-info:before{content:"\f129"}.la-info-circle:before{content:"\f05a"}.la-instagram:before{content:"\f16d"}.la-intercom:before{content:"\f7af"}.la-internet-explorer:before{content:"\f26b"}.la-invision:before{content:"\f7b0"}.la-ioxhost:before{content:"\f208"}.la-italic:before{content:"\f033"}.la-itch-io:before{content:"\f83a"}.la-itunes:before{content:"\f3b4"}.la-itunes-note:before{content:"\f3b5"}.la-java:before{content:"\f4e4"}.la-jedi:before{content:"\f669"}.la-jedi-order:before{content:"\f50e"}.la-jenkins:before{content:"\f3b6"}.la-jira:before{content:"\f7b1"}.la-joget:before{content:"\f3b7"}.la-joint:before{content:"\f595"}.la-joomla:before{content:"\f1aa"}.la-journal-whills:before{content:"\f66a"}.la-js:before{content:"\f3b8"}.la-js-square:before{content:"\f3b9"}.la-jsfiddle:before{content:"\f1cc"}.la-kaaba:before{content:"\f66b"}.la-kaggle:before{content:"\f5fa"}.la-key:before{content:"\f084"}.la-keybase:before{content:"\f4f5"}.la-keyboard:before{content:"\f11c"}.la-keycdn:before{content:"\f3ba"}.la-khanda:before{content:"\f66d"}.la-kickstarter:before{content:"\f3bb"}.la-kickstarter-k:before{content:"\f3bc"}.la-kiss:before{content:"\f596"}.la-kiss-beam:before{content:"\f597"}.la-kiss-wink-heart:before{content:"\f598"}.la-kiwi-bird:before{content:"\f535"}.la-korvue:before{content:"\f42f"}.la-landmark:before{content:"\f66f"}.la-language:before{content:"\f1ab"}.la-laptop:before{content:"\f109"}.la-laptop-code:before{content:"\f5fc"}.la-laptop-medical:before{content:"\f812"}.la-laravel:before{content:"\f3bd"}.la-lastfm:before{content:"\f202"}.la-lastfm-square:before{content:"\f203"}.la-laugh:before{content:"\f599"}.la-laugh-beam:before{content:"\f59a"}.la-laugh-squint:before{content:"\f59b"}.la-laugh-wink:before{content:"\f59c"}.la-layer-group:before{content:"\f5fd"}.la-leaf:before{content:"\f06c"}.la-leanpub:before{content:"\f212"}.la-lemon:before{content:"\f094"}.la-less:before{content:"\f41d"}.la-less-than:before{content:"\f536"}.la-less-than-equal:before{content:"\f537"}.la-level-down-alt:before{content:"\f3be"}.la-level-up-alt:before{content:"\f3bf"}.la-life-ring:before{content:"\f1cd"}.la-lightbulb:before{content:"\f0eb"}.la-line:before{content:"\f3c0"}.la-link:before{content:"\f0c1"}.la-linkedin:before{content:"\f08c"}.la-linkedin-in:before{content:"\f0e1"}.la-linode:before{content:"\f2b8"}.la-linux:before{content:"\f17c"}.la-lira-sign:before{content:"\f195"}.la-list:before{content:"\f03a"}.la-list-alt:before{content:"\f022"}.la-list-ol:before{content:"\f0cb"}.la-list-ul:before{content:"\f0ca"}.la-location-arrow:before{content:"\f124"}.la-lock:before{content:"\f023"}.la-lock-open:before{content:"\f3c1"}.la-long-arrow-alt-down:before{content:"\f309"}.la-long-arrow-alt-left:before{content:"\f30a"}.la-long-arrow-alt-right:before{content:"\f30b"}.la-long-arrow-alt-up:before{content:"\f30c"}.la-low-vision:before{content:"\f2a8"}.la-luggage-cart:before{content:"\f59d"}.la-lyft:before{content:"\f3c3"}.la-magento:before{content:"\f3c4"}.la-magic:before{content:"\f0d0"}.la-magnet:before{content:"\f076"}.la-mail-bulk:before{content:"\f674"}.la-mailchimp:before{content:"\f59e"}.la-male:before{content:"\f183"}.la-mandalorian:before{content:"\f50f"}.la-map:before{content:"\f279"}.la-map-marked:before{content:"\f59f"}.la-map-marked-alt:before{content:"\f5a0"}.la-map-marker:before{content:"\f041"}.la-map-marker-alt:before{content:"\f3c5"}.la-map-pin:before{content:"\f276"}.la-map-signs:before{content:"\f277"}.la-markdown:before{content:"\f60f"}.la-marker:before{content:"\f5a1"}.la-mars:before{content:"\f222"}.la-mars-double:before{content:"\f227"}.la-mars-stroke:before{content:"\f229"}.la-mars-stroke-h:before{content:"\f22b"}.la-mars-stroke-v:before{content:"\f22a"}.la-mask:before{content:"\f6fa"}.la-mastodon:before{content:"\f4f6"}.la-maxcdn:before{content:"\f136"}.la-mdb:before{content:"\f8ca"}.la-medal:before{content:"\f5a2"}.la-medapps:before{content:"\f3c6"}.la-medium:before{content:"\f23a"}.la-medium-m:before{content:"\f3c7"}.la-medkit:before{content:"\f0fa"}.la-medrt:before{content:"\f3c8"}.la-meetup:before{content:"\f2e0"}.la-megaport:before{content:"\f5a3"}.la-meh:before{content:"\f11a"}.la-meh-blank:before{content:"\f5a4"}.la-meh-rolling-eyes:before{content:"\f5a5"}.la-memory:before{content:"\f538"}.la-mendeley:before{content:"\f7b3"}.la-menorah:before{content:"\f676"}.la-mercury:before{content:"\f223"}.la-meteor:before{content:"\f753"}.la-microchip:before{content:"\f2db"}.la-microphone:before{content:"\f130"}.la-microphone-alt:before{content:"\f3c9"}.la-microphone-alt-slash:before{content:"\f539"}.la-microphone-slash:before{content:"\f131"}.la-microscope:before{content:"\f610"}.la-microsoft:before{content:"\f3ca"}.la-minus:before{content:"\f068"}.la-minus-circle:before{content:"\f056"}.la-minus-square:before{content:"\f146"}.la-mitten:before{content:"\f7b5"}.la-mix:before{content:"\f3cb"}.la-mixcloud:before{content:"\f289"}.la-mizuni:before{content:"\f3cc"}.la-mobile:before{content:"\f10b"}.la-mobile-alt:before{content:"\f3cd"}.la-modx:before{content:"\f285"}.la-monero:before{content:"\f3d0"}.la-money-bill:before{content:"\f0d6"}.la-money-bill-alt:before{content:"\f3d1"}.la-money-bill-wave:before{content:"\f53a"}.la-money-bill-wave-alt:before{content:"\f53b"}.la-money-check:before{content:"\f53c"}.la-money-check-alt:before{content:"\f53d"}.la-monument:before{content:"\f5a6"}.la-moon:before{content:"\f186"}.la-mortar-pestle:before{content:"\f5a7"}.la-mosque:before{content:"\f678"}.la-motorcycle:before{content:"\f21c"}.la-mountain:before{content:"\f6fc"}.la-mouse:before{content:"\f8cc"}.la-mouse-pointer:before{content:"\f245"}.la-mug-hot:before{content:"\f7b6"}.la-music:before{content:"\f001"}.la-napster:before{content:"\f3d2"}.la-neos:before{content:"\f612"}.la-network-wired:before{content:"\f6ff"}.la-neuter:before{content:"\f22c"}.la-newspaper:before{content:"\f1ea"}.la-nimblr:before{content:"\f5a8"}.la-node:before{content:"\f419"}.la-node-js:before{content:"\f3d3"}.la-not-equal:before{content:"\f53e"}.la-notes-medical:before{content:"\f481"}.la-npm:before{content:"\f3d4"}.la-ns8:before{content:"\f3d5"}.la-nutritionix:before{content:"\f3d6"}.la-object-group:before{content:"\f247"}.la-object-ungroup:before{content:"\f248"}.la-odnoklassniki:before{content:"\f263"}.la-odnoklassniki-square:before{content:"\f264"}.la-oil-can:before{content:"\f613"}.la-old-republic:before{content:"\f510"}.la-om:before{content:"\f679"}.la-opencart:before{content:"\f23d"}.la-openid:before{content:"\f19b"}.la-opera:before{content:"\f26a"}.la-optin-monster:before{content:"\f23c"}.la-orcid:before{content:"\f8d2"}.la-osi:before{content:"\f41a"}.la-otter:before{content:"\f700"}.la-outdent:before{content:"\f03b"}.la-page4:before{content:"\f3d7"}.la-pagelines:before{content:"\f18c"}.la-pager:before{content:"\f815"}.la-paint-brush:before{content:"\f1fc"}.la-paint-roller:before{content:"\f5aa"}.la-palette:before{content:"\f53f"}.la-palfed:before{content:"\f3d8"}.la-pallet:before{content:"\f482"}.la-paper-plane:before{content:"\f1d8"}.la-paperclip:before{content:"\f0c6"}.la-parachute-box:before{content:"\f4cd"}.la-paragraph:before{content:"\f1dd"}.la-parking:before{content:"\f540"}.la-passport:before{content:"\f5ab"}.la-pastafarianism:before{content:"\f67b"}.la-paste:before{content:"\f0ea"}.la-patreon:before{content:"\f3d9"}.la-pause:before{content:"\f04c"}.la-pause-circle:before{content:"\f28b"}.la-paw:before{content:"\f1b0"}.la-paypal:before{content:"\f1ed"}.la-peace:before{content:"\f67c"}.la-pen:before{content:"\f304"}.la-pen-alt:before{content:"\f305"}.la-pen-fancy:before{content:"\f5ac"}.la-pen-nib:before{content:"\f5ad"}.la-pen-square:before{content:"\f14b"}.la-pencil-alt:before{content:"\f303"}.la-pencil-ruler:before{content:"\f5ae"}.la-penny-arcade:before{content:"\f704"}.la-people-carry:before{content:"\f4ce"}.la-pepper-hot:before{content:"\f816"}.la-percent:before{content:"\f295"}.la-percentage:before{content:"\f541"}.la-periscope:before{content:"\f3da"}.la-person-booth:before{content:"\f756"}.la-phabricator:before{content:"\f3db"}.la-phoenix-framework:before{content:"\f3dc"}.la-phoenix-squadron:before{content:"\f511"}.la-phone:before{content:"\f095"}.la-phone-alt:before{content:"\f879"}.la-phone-slash:before{content:"\f3dd"}.la-phone-square:before{content:"\f098"}.la-phone-square-alt:before{content:"\f87b"}.la-phone-volume:before{content:"\f2a0"}.la-photo-video:before{content:"\f87c"}.la-php:before{content:"\f457"}.la-pied-piper:before{content:"\f2ae"}.la-pied-piper-alt:before{content:"\f1a8"}.la-pied-piper-hat:before{content:"\f4e5"}.la-pied-piper-pp:before{content:"\f1a7"}.la-piggy-bank:before{content:"\f4d3"}.la-pills:before{content:"\f484"}.la-pinterest:before{content:"\f0d2"}.la-pinterest-p:before{content:"\f231"}.la-pinterest-square:before{content:"\f0d3"}.la-pizza-slice:before{content:"\f818"}.la-place-of-worship:before{content:"\f67f"}.la-plane:before{content:"\f072"}.la-plane-arrival:before{content:"\f5af"}.la-plane-departure:before{content:"\f5b0"}.la-play:before{content:"\f04b"}.la-play-circle:before{content:"\f144"}.la-playstation:before{content:"\f3df"}.la-plug:before{content:"\f1e6"}.la-plus:before{content:"\f067"}.la-plus-circle:before{content:"\f055"}.la-plus-square:before{content:"\f0fe"}.la-podcast:before{content:"\f2ce"}.la-poll:before{content:"\f681"}.la-poll-h:before{content:"\f682"}.la-poo:before{content:"\f2fe"}.la-poo-storm:before{content:"\f75a"}.la-poop:before{content:"\f619"}.la-portrait:before{content:"\f3e0"}.la-pound-sign:before{content:"\f154"}.la-power-off:before{content:"\f011"}.la-pray:before{content:"\f683"}.la-praying-hands:before{content:"\f684"}.la-prescription:before{content:"\f5b1"}.la-prescription-bottle:before{content:"\f485"}.la-prescription-bottle-alt:before{content:"\f486"}.la-print:before{content:"\f02f"}.la-procedures:before{content:"\f487"}.la-product-hunt:before{content:"\f288"}.la-project-diagram:before{content:"\f542"}.la-pushed:before{content:"\f3e1"}.la-puzzle-piece:before{content:"\f12e"}.la-python:before{content:"\f3e2"}.la-qq:before{content:"\f1d6"}.la-qrcode:before{content:"\f029"}.la-question:before{content:"\f128"}.la-question-circle:before{content:"\f059"}.la-quidditch:before{content:"\f458"}.la-quinscape:before{content:"\f459"}.la-quora:before{content:"\f2c4"}.la-quote-left:before{content:"\f10d"}.la-quote-right:before{content:"\f10e"}.la-quran:before{content:"\f687"}.la-r-project:before{content:"\f4f7"}.la-radiation:before{content:"\f7b9"}.la-radiation-alt:before{content:"\f7ba"}.la-rainbow:before{content:"\f75b"}.la-random:before{content:"\f074"}.la-raspberry-pi:before{content:"\f7bb"}.la-ravelry:before{content:"\f2d9"}.la-react:before{content:"\f41b"}.la-reacteurope:before{content:"\f75d"}.la-readme:before{content:"\f4d5"}.la-rebel:before{content:"\f1d0"}.la-receipt:before{content:"\f543"}.la-record-vinyl:before{content:"\f8d9"}.la-recycle:before{content:"\f1b8"}.la-red-river:before{content:"\f3e3"}.la-reddit:before{content:"\f1a1"}.la-reddit-alien:before{content:"\f281"}.la-reddit-square:before{content:"\f1a2"}.la-redhat:before{content:"\f7bc"}.la-redo:before{content:"\f01e"}.la-redo-alt:before{content:"\f2f9"}.la-registered:before{content:"\f25d"}.la-remove-format:before{content:"\f87d"}.la-renren:before{content:"\f18b"}.la-reply:before{content:"\f3e5"}.la-reply-all:before{content:"\f122"}.la-replyd:before{content:"\f3e6"}.la-republican:before{content:"\f75e"}.la-researchgate:before{content:"\f4f8"}.la-resolving:before{content:"\f3e7"}.la-restroom:before{content:"\f7bd"}.la-retweet:before{content:"\f079"}.la-rev:before{content:"\f5b2"}.la-ribbon:before{content:"\f4d6"}.la-ring:before{content:"\f70b"}.la-road:before{content:"\f018"}.la-robot:before{content:"\f544"}.la-rocket:before{content:"\f135"}.la-rocketchat:before{content:"\f3e8"}.la-rockrms:before{content:"\f3e9"}.la-route:before{content:"\f4d7"}.la-rss:before{content:"\f09e"}.la-rss-square:before{content:"\f143"}.la-ruble-sign:before{content:"\f158"}.la-ruler:before{content:"\f545"}.la-ruler-combined:before{content:"\f546"}.la-ruler-horizontal:before{content:"\f547"}.la-ruler-vertical:before{content:"\f548"}.la-running:before{content:"\f70c"}.la-rupee-sign:before{content:"\f156"}.la-sad-cry:before{content:"\f5b3"}.la-sad-tear:before{content:"\f5b4"}.la-safari:before{content:"\f267"}.la-salesforce:before{content:"\f83b"}.la-sass:before{content:"\f41e"}.la-satellite:before{content:"\f7bf"}.la-satellite-dish:before{content:"\f7c0"}.la-save:before{content:"\f0c7"}.la-schlix:before{content:"\f3ea"}.la-school:before{content:"\f549"}.la-screwdriver:before{content:"\f54a"}.la-scribd:before{content:"\f28a"}.la-scroll:before{content:"\f70e"}.la-sd-card:before{content:"\f7c2"}.la-search:before{content:"\f002"}.la-search-dollar:before{content:"\f688"}.la-search-location:before{content:"\f689"}.la-search-minus:before{content:"\f010"}.la-search-plus:before{content:"\f00e"}.la-searchengin:before{content:"\f3eb"}.la-seedling:before{content:"\f4d8"}.la-sellcast:before{content:"\f2da"}.la-sellsy:before{content:"\f213"}.la-server:before{content:"\f233"}.la-servicestack:before{content:"\f3ec"}.la-shapes:before{content:"\f61f"}.la-share:before{content:"\f064"}.la-share-alt:before{content:"\f1e0"}.la-share-alt-square:before{content:"\f1e1"}.la-share-square:before{content:"\f14d"}.la-shekel-sign:before{content:"\f20b"}.la-shield-alt:before{content:"\f3ed"}.la-ship:before{content:"\f21a"}.la-shipping-fast:before{content:"\f48b"}.la-shirtsinbulk:before{content:"\f214"}.la-shoe-prints:before{content:"\f54b"}.la-shopping-bag:before{content:"\f290"}.la-shopping-basket:before{content:"\f291"}.la-shopping-cart:before{content:"\f07a"}.la-shopware:before{content:"\f5b5"}.la-shower:before{content:"\f2cc"}.la-shuttle-van:before{content:"\f5b6"}.la-sign:before{content:"\f4d9"}.la-sign-in-alt:before{content:"\f2f6"}.la-sign-language:before{content:"\f2a7"}.la-sign-out-alt:before{content:"\f2f5"}.la-signal:before{content:"\f012"}.la-signature:before{content:"\f5b7"}.la-sim-card:before{content:"\f7c4"}.la-simplybuilt:before{content:"\f215"}.la-sistrix:before{content:"\f3ee"}.la-sitemap:before{content:"\f0e8"}.la-sith:before{content:"\f512"}.la-skating:before{content:"\f7c5"}.la-sketch:before{content:"\f7c6"}.la-skiing:before{content:"\f7c9"}.la-skiing-nordic:before{content:"\f7ca"}.la-skull:before{content:"\f54c"}.la-skull-crossbones:before{content:"\f714"}.la-skyatlas:before{content:"\f216"}.la-skype:before{content:"\f17e"}.la-slack:before{content:"\f198"}.la-slack-hash:before{content:"\f3ef"}.la-slash:before{content:"\f715"}.la-sleigh:before{content:"\f7cc"}.la-sliders-h:before{content:"\f1de"}.la-slideshare:before{content:"\f1e7"}.la-smile:before{content:"\f118"}.la-smile-beam:before{content:"\f5b8"}.la-smile-wink:before{content:"\f4da"}.la-smog:before{content:"\f75f"}.la-smoking:before{content:"\f48d"}.la-smoking-ban:before{content:"\f54d"}.la-sms:before{content:"\f7cd"}.la-snapchat:before{content:"\f2ab"}.la-snapchat-ghost:before{content:"\f2ac"}.la-snapchat-square:before{content:"\f2ad"}.la-snowboarding:before{content:"\f7ce"}.la-snowflake:before{content:"\f2dc"}.la-snowman:before{content:"\f7d0"}.la-snowplow:before{content:"\f7d2"}.la-socks:before{content:"\f696"}.la-solar-panel:before{content:"\f5ba"}.la-sort:before{content:"\f0dc"}.la-sort-alpha-down:before{content:"\f15d"}.la-sort-alpha-down-alt:before{content:"\f881"}.la-sort-alpha-up:before{content:"\f15e"}.la-sort-alpha-up-alt:before{content:"\f882"}.la-sort-amount-down:before{content:"\f160"}.la-sort-amount-down-alt:before{content:"\f884"}.la-sort-amount-up:before{content:"\f161"}.la-sort-amount-up-alt:before{content:"\f885"}.la-sort-down:before{content:"\f0dd"}.la-sort-numeric-down:before{content:"\f162"}.la-sort-numeric-down-alt:before{content:"\f886"}.la-sort-numeric-up:before{content:"\f163"}.la-sort-numeric-up-alt:before{content:"\f887"}.la-sort-up:before{content:"\f0de"}.la-soundcloud:before{content:"\f1be"}.la-sourcetree:before{content:"\f7d3"}.la-spa:before{content:"\f5bb"}.la-space-shuttle:before{content:"\f197"}.la-speakap:before{content:"\f3f3"}.la-speaker-deck:before{content:"\f83c"}.la-spell-check:before{content:"\f891"}.la-spider:before{content:"\f717"}.la-spinner:before{content:"\f110"}.la-splotch:before{content:"\f5bc"}.la-spotify:before{content:"\f1bc"}.la-spray-can:before{content:"\f5bd"}.la-square:before{content:"\f0c8"}.la-square-full:before{content:"\f45c"}.la-square-root-alt:before{content:"\f698"}.la-squarespace:before{content:"\f5be"}.la-stack-exchange:before{content:"\f18d"}.la-stack-overflow:before{content:"\f16c"}.la-stackpath:before{content:"\f842"}.la-stamp:before{content:"\f5bf"}.la-star:before{content:"\f005"}.la-star-and-crescent:before{content:"\f699"}.la-star-half:before{content:"\f089"}.la-star-half-alt:before{content:"\f5c0"}.la-star-of-david:before{content:"\f69a"}.la-star-of-life:before{content:"\f621"}.la-staylinked:before{content:"\f3f5"}.la-steam:before{content:"\f1b6"}.la-steam-square:before{content:"\f1b7"}.la-steam-symbol:before{content:"\f3f6"}.la-step-backward:before{content:"\f048"}.la-step-forward:before{content:"\f051"}.la-stethoscope:before{content:"\f0f1"}.la-sticker-mule:before{content:"\f3f7"}.la-sticky-note:before{content:"\f249"}.la-stop:before{content:"\f04d"}.la-stop-circle:before{content:"\f28d"}.la-stopwatch:before{content:"\f2f2"}.la-store:before{content:"\f54e"}.la-store-alt:before{content:"\f54f"}.la-strava:before{content:"\f428"}.la-stream:before{content:"\f550"}.la-street-view:before{content:"\f21d"}.la-strikethrough:before{content:"\f0cc"}.la-stripe:before{content:"\f429"}.la-stripe-s:before{content:"\f42a"}.la-stroopwafel:before{content:"\f551"}.la-studiovinari:before{content:"\f3f8"}.la-stumbleupon:before{content:"\f1a4"}.la-stumbleupon-circle:before{content:"\f1a3"}.la-subscript:before{content:"\f12c"}.la-subway:before{content:"\f239"}.la-suitcase:before{content:"\f0f2"}.la-suitcase-rolling:before{content:"\f5c1"}.la-sun:before{content:"\f185"}.la-superpowers:before{content:"\f2dd"}.la-superscript:before{content:"\f12b"}.la-supple:before{content:"\f3f9"}.la-surprise:before{content:"\f5c2"}.la-suse:before{content:"\f7d6"}.la-swatchbook:before{content:"\f5c3"}.la-swift:before{content:"\f8e1"}.la-swimmer:before{content:"\f5c4"}.la-swimming-pool:before{content:"\f5c5"}.la-symfony:before{content:"\f83d"}.la-synagogue:before{content:"\f69b"}.la-sync:before{content:"\f021"}.la-sync-alt:before{content:"\f2f1"}.la-syringe:before{content:"\f48e"}.la-table:before{content:"\f0ce"}.la-table-tennis:before{content:"\f45d"}.la-tablet:before{content:"\f10a"}.la-tablet-alt:before{content:"\f3fa"}.la-tablets:before{content:"\f490"}.la-tachometer-alt:before{content:"\f3fd"}.la-tag:before{content:"\f02b"}.la-tags:before{content:"\f02c"}.la-tape:before{content:"\f4db"}.la-tasks:before{content:"\f0ae"}.la-taxi:before{content:"\f1ba"}.la-teamspeak:before{content:"\f4f9"}.la-teeth:before{content:"\f62e"}.la-teeth-open:before{content:"\f62f"}.la-telegram:before{content:"\f2c6"}.la-telegram-plane:before{content:"\f3fe"}.la-temperature-high:before{content:"\f769"}.la-temperature-low:before{content:"\f76b"}.la-tencent-weibo:before{content:"\f1d5"}.la-tenge:before{content:"\f7d7"}.la-terminal:before{content:"\f120"}.la-text-height:before{content:"\f034"}.la-text-width:before{content:"\f035"}.la-th:before{content:"\f00a"}.la-th-large:before{content:"\f009"}.la-th-list:before{content:"\f00b"}.la-the-red-yeti:before{content:"\f69d"}.la-theater-masks:before{content:"\f630"}.la-themeco:before{content:"\f5c6"}.la-themeisle:before{content:"\f2b2"}.la-thermometer:before{content:"\f491"}.la-thermometer-empty:before{content:"\f2cb"}.la-thermometer-full:before{content:"\f2c7"}.la-thermometer-half:before{content:"\f2c9"}.la-thermometer-quarter:before{content:"\f2ca"}.la-thermometer-three-quarters:before{content:"\f2c8"}.la-think-peaks:before{content:"\f731"}.la-thumbs-down:before{content:"\f165"}.la-thumbs-up:before{content:"\f164"}.la-thumbtack:before{content:"\f08d"}.la-ticket-alt:before{content:"\f3ff"}.la-times:before{content:"\f00d"}.la-times-circle:before{content:"\f057"}.la-tint:before{content:"\f043"}.la-tint-slash:before{content:"\f5c7"}.la-tired:before{content:"\f5c8"}.la-toggle-off:before{content:"\f204"}.la-toggle-on:before{content:"\f205"}.la-toilet:before{content:"\f7d8"}.la-toilet-paper:before{content:"\f71e"}.la-toolbox:before{content:"\f552"}.la-tools:before{content:"\f7d9"}.la-tooth:before{content:"\f5c9"}.la-torah:before{content:"\f6a0"}.la-torii-gate:before{content:"\f6a1"}.la-tractor:before{content:"\f722"}.la-trade-federation:before{content:"\f513"}.la-trademark:before{content:"\f25c"}.la-traffic-light:before{content:"\f637"}.la-train:before{content:"\f238"}.la-tram:before{content:"\f7da"}.la-transgender:before{content:"\f224"}.la-transgender-alt:before{content:"\f225"}.la-trash:before{content:"\f1f8"}.la-trash-alt:before{content:"\f2ed"}.la-trash-restore:before{content:"\f829"}.la-trash-restore-alt:before{content:"\f82a"}.la-tree:before{content:"\f1bb"}.la-trello:before{content:"\f181"}.la-tripadvisor:before{content:"\f262"}.la-trophy:before{content:"\f091"}.la-truck:before{content:"\f0d1"}.la-truck-loading:before{content:"\f4de"}.la-truck-monster:before{content:"\f63b"}.la-truck-moving:before{content:"\f4df"}.la-truck-pickup:before{content:"\f63c"}.la-tshirt:before{content:"\f553"}.la-tty:before{content:"\f1e4"}.la-tumblr:before{content:"\f173"}.la-tumblr-square:before{content:"\f174"}.la-tv:before{content:"\f26c"}.la-twitch:before{content:"\f1e8"}.la-twitter:before{content:"\f099"}.la-twitter-square:before{content:"\f081"}.la-typo3:before{content:"\f42b"}.la-uber:before{content:"\f402"}.la-ubuntu:before{content:"\f7df"}.la-uikit:before{content:"\f403"}.la-umbraco:before{content:"\f8e8"}.la-umbrella:before{content:"\f0e9"}.la-umbrella-beach:before{content:"\f5ca"}.la-underline:before{content:"\f0cd"}.la-undo:before{content:"\f0e2"}.la-undo-alt:before{content:"\f2ea"}.la-uniregistry:before{content:"\f404"}.la-universal-access:before{content:"\f29a"}.la-university:before{content:"\f19c"}.la-unlink:before{content:"\f127"}.la-unlock:before{content:"\f09c"}.la-unlock-alt:before{content:"\f13e"}.la-untappd:before{content:"\f405"}.la-upload:before{content:"\f093"}.la-ups:before{content:"\f7e0"}.la-usb:before{content:"\f287"}.la-user:before{content:"\f007"}.la-user-alt:before{content:"\f406"}.la-user-alt-slash:before{content:"\f4fa"}.la-user-astronaut:before{content:"\f4fb"}.la-user-check:before{content:"\f4fc"}.la-user-circle:before{content:"\f2bd"}.la-user-clock:before{content:"\f4fd"}.la-user-cog:before{content:"\f4fe"}.la-user-edit:before{content:"\f4ff"}.la-user-friends:before{content:"\f500"}.la-user-graduate:before{content:"\f501"}.la-user-injured:before{content:"\f728"}.la-user-lock:before{content:"\f502"}.la-user-md:before{content:"\f0f0"}.la-user-minus:before{content:"\f503"}.la-user-ninja:before{content:"\f504"}.la-user-nurse:before{content:"\f82f"}.la-user-plus:before{content:"\f234"}.la-user-secret:before{content:"\f21b"}.la-user-shield:before{content:"\f505"}.la-user-slash:before{content:"\f506"}.la-user-tag:before{content:"\f507"}.la-user-tie:before{content:"\f508"}.la-user-times:before{content:"\f235"}.la-users:before{content:"\f0c0"}.la-users-cog:before{content:"\f509"}.la-usps:before{content:"\f7e1"}.la-ussunnah:before{content:"\f407"}.la-utensil-spoon:before{content:"\f2e5"}.la-utensils:before{content:"\f2e7"}.la-vaadin:before{content:"\f408"}.la-vector-square:before{content:"\f5cb"}.la-venus:before{content:"\f221"}.la-venus-double:before{content:"\f226"}.la-venus-mars:before{content:"\f228"}.la-viacoin:before{content:"\f237"}.la-viadeo:before{content:"\f2a9"}.la-viadeo-square:before{content:"\f2aa"}.la-vial:before{content:"\f492"}.la-vials:before{content:"\f493"}.la-viber:before{content:"\f409"}.la-video:before{content:"\f03d"}.la-video-slash:before{content:"\f4e2"}.la-vihara:before{content:"\f6a7"}.la-vimeo:before{content:"\f40a"}.la-vimeo-square:before{content:"\f194"}.la-vimeo-v:before{content:"\f27d"}.la-vine:before{content:"\f1ca"}.la-vk:before{content:"\f189"}.la-vnv:before{content:"\f40b"}.la-voicemail:before{content:"\f897"}.la-volleyball-ball:before{content:"\f45f"}.la-volume-down:before{content:"\f027"}.la-volume-mute:before{content:"\f6a9"}.la-volume-off:before{content:"\f026"}.la-volume-up:before{content:"\f028"}.la-vote-yea:before{content:"\f772"}.la-vr-cardboard:before{content:"\f729"}.la-vuejs:before{content:"\f41f"}.la-walking:before{content:"\f554"}.la-wallet:before{content:"\f555"}.la-warehouse:before{content:"\f494"}.la-water:before{content:"\f773"}.la-wave-square:before{content:"\f83e"}.la-waze:before{content:"\f83f"}.la-weebly:before{content:"\f5cc"}.la-weibo:before{content:"\f18a"}.la-weight:before{content:"\f496"}.la-weight-hanging:before{content:"\f5cd"}.la-weixin:before{content:"\f1d7"}.la-whatsapp:before{content:"\f232"}.la-whatsapp-square:before{content:"\f40c"}.la-wheelchair:before{content:"\f193"}.la-whmcs:before{content:"\f40d"}.la-wifi:before{content:"\f1eb"}.la-wikipedia-w:before{content:"\f266"}.la-wind:before{content:"\f72e"}.la-window-close:before{content:"\f410"}.la-window-maximize:before{content:"\f2d0"}.la-window-minimize:before{content:"\f2d1"}.la-window-restore:before{content:"\f2d2"}.la-windows:before{content:"\f17a"}.la-wine-bottle:before{content:"\f72f"}.la-wine-glass:before{content:"\f4e3"}.la-wine-glass-alt:before{content:"\f5ce"}.la-wix:before{content:"\f5cf"}.la-wizards-of-the-coast:before{content:"\f730"}.la-wolf-pack-battalion:before{content:"\f514"}.la-won-sign:before{content:"\f159"}.la-wordpress:before{content:"\f19a"}.la-wordpress-simple:before{content:"\f411"}.la-wpbeginner:before{content:"\f297"}.la-wpexplorer:before{content:"\f2de"}.la-wpforms:before{content:"\f298"}.la-wpressr:before{content:"\f3e4"}.la-wrench:before{content:"\f0ad"}.la-x-ray:before{content:"\f497"}.la-xbox:before{content:"\f412"}.la-xing:before{content:"\f168"}.la-xing-square:before{content:"\f169"}.la-y-combinator:before{content:"\f23b"}.la-yahoo:before{content:"\f19e"}.la-yammer:before{content:"\f840"}.la-yandex:before{content:"\f413"}.la-yandex-international:before{content:"\f414"}.la-yarn:before{content:"\f7e3"}.la-yelp:before{content:"\f1e9"}.la-yen-sign:before{content:"\f157"}.la-yin-yang:before{content:"\f6ad"}.la-yoast:before{content:"\f2b1"}.la-youtube:before{content:"\f167"}.la-youtube-square:before{content:"\f431"}.la-zhihu:before{content:"\f63f"}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}@font-face{font-family:'Line Awesome Brands';font-style:normal;font-weight:400;font-display:auto;src:url(../fonts/la-brands-400.eot);src:url(../fonts/la-brands-400.eot?#iefix) format("embedded-opentype"),url(../fonts/la-brands-400.woff2) format("woff2"),url(../fonts/la-brands-400.woff) format("woff"),url(../fonts/la-brands-400.ttf) format("truetype"),url(../fonts/la-brands-400.svg#lineawesome) format("svg")}.lab{font-family:'Line Awesome Brands'}@font-face{font-family:'Line Awesome Free';font-style:normal;font-weight:400;font-display:auto;src:url(../fonts/la-regular-400.eot);src:url(../fonts/la-regular-400.eot?#iefix) format("embedded-opentype"),url(../fonts/la-regular-400.woff2) format("woff2"),url(../fonts/la-regular-400.woff) format("woff"),url(../fonts/la-regular-400.ttf) format("truetype"),url(../fonts/la-regular-400.svg#lineawesome) format("svg")}.lar{font-family:'Line Awesome Free';font-weight:400}@font-face{font-family:'Line Awesome Free';font-style:normal;font-weight:900;font-display:auto;src:url(la-solid-900.eot);src:url(la-solid-900.eot?#iefix) format("embedded-opentype"),url(la-solid-900.woff2) format("woff2"),url(la-solid-900.woff) format("woff"),url(la-solid-900.ttf) format("truetype"),url(la-solid-900.svg#lineawesome) format("svg")}.la,.las{font-family:'Line Awesome Free';font-weight:900}.la.la-glass:before{content:"\f000"}.la.la-meetup{font-family:'Line Awesome Brands';font-weight:400}.la.la-star-o{font-family:'Line Awesome Free';font-weight:400}.la.la-star-o:before{content:"\f005"}.la.la-remove:before{content:"\f00d"}.la.la-close:before{content:"\f00d"}.la.la-gear:before{content:"\f013"}.la.la-trash-o{font-family:'Line Awesome Free';font-weight:400}.la.la-trash-o:before{content:"\f2ed"}.la.la-file-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-o:before{content:"\f15b"}.la.la-clock-o{font-family:'Line Awesome Free';font-weight:400}.la.la-clock-o:before{content:"\f017"}.la.la-arrow-circle-o-down{font-family:'Line Awesome Free';font-weight:400}.la.la-arrow-circle-o-down:before{content:"\f358"}.la.la-arrow-circle-o-up{font-family:'Line Awesome Free';font-weight:400}.la.la-arrow-circle-o-up:before{content:"\f35b"}.la.la-play-circle-o{font-family:'Line Awesome Free';font-weight:400}.la.la-play-circle-o:before{content:"\f144"}.la.la-repeat:before{content:"\f01e"}.la.la-rotate-right:before{content:"\f01e"}.la.la-refresh:before{content:"\f021"}.la.la-list-alt{font-family:'Line Awesome Free';font-weight:400}.la.la-dedent:before{content:"\f03b"}.la.la-video-camera:before{content:"\f03d"}.la.la-picture-o{font-family:'Line Awesome Free';font-weight:400}.la.la-picture-o:before{content:"\f03e"}.la.la-photo{font-family:'Line Awesome Free';font-weight:400}.la.la-photo:before{content:"\f03e"}.la.la-image{font-family:'Line Awesome Free';font-weight:400}.la.la-image:before{content:"\f03e"}.la.la-pencil:before{content:"\f303"}.la.la-map-marker:before{content:"\f3c5"}.la.la-pencil-square-o{font-family:'Line Awesome Free';font-weight:400}.la.la-pencil-square-o:before{content:"\f044"}.la.la-share-square-o{font-family:'Line Awesome Free';font-weight:400}.la.la-share-square-o:before{content:"\f14d"}.la.la-check-square-o{font-family:'Line Awesome Free';font-weight:400}.la.la-check-square-o:before{content:"\f14a"}.la.la-arrows:before{content:"\f0b2"}.la.la-times-circle-o{font-family:'Line Awesome Free';font-weight:400}.la.la-times-circle-o:before{content:"\f057"}.la.la-check-circle-o{font-family:'Line Awesome Free';font-weight:400}.la.la-check-circle-o:before{content:"\f058"}.la.la-mail-forward:before{content:"\f064"}.la.la-eye{font-family:'Line Awesome Free';font-weight:400}.la.la-eye-slash{font-family:'Line Awesome Free';font-weight:400}.la.la-warning:before{content:"\f071"}.la.la-calendar:before{content:"\f073"}.la.la-arrows-v:before{content:"\f338"}.la.la-arrows-h:before{content:"\f337"}.la.la-bar-chart{font-family:'Line Awesome Free';font-weight:400}.la.la-bar-chart:before{content:"\f080"}.la.la-bar-chart-o{font-family:'Line Awesome Free';font-weight:400}.la.la-bar-chart-o:before{content:"\f080"}.la.la-twitter-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-facebook-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-gears:before{content:"\f085"}.la.la-thumbs-o-up{font-family:'Line Awesome Free';font-weight:400}.la.la-thumbs-o-up:before{content:"\f164"}.la.la-thumbs-o-down{font-family:'Line Awesome Free';font-weight:400}.la.la-thumbs-o-down:before{content:"\f165"}.la.la-heart-o{font-family:'Line Awesome Free';font-weight:400}.la.la-heart-o:before{content:"\f004"}.la.la-sign-out:before{content:"\f2f5"}.la.la-linkedin-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-linkedin-square:before{content:"\f08c"}.la.la-thumb-tack:before{content:"\f08d"}.la.la-external-link:before{content:"\f35d"}.la.la-sign-in:before{content:"\f2f6"}.la.la-github-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-lemon-o{font-family:'Line Awesome Free';font-weight:400}.la.la-lemon-o:before{content:"\f094"}.la.la-square-o{font-family:'Line Awesome Free';font-weight:400}.la.la-square-o:before{content:"\f0c8"}.la.la-bookmark-o{font-family:'Line Awesome Free';font-weight:400}.la.la-bookmark-o:before{content:"\f02e"}.la.la-twitter{font-family:'Line Awesome Brands';font-weight:400}.la.la-facebook{font-family:'Line Awesome Brands';font-weight:400}.la.la-facebook:before{content:"\f39e"}.la.la-facebook-f{font-family:'Line Awesome Brands';font-weight:400}.la.la-facebook-f:before{content:"\f39e"}.la.la-github{font-family:'Line Awesome Brands';font-weight:400}.la.la-credit-card{font-family:'Line Awesome Free';font-weight:400}.la.la-feed:before{content:"\f09e"}.la.la-hdd-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hdd-o:before{content:"\f0a0"}.la.la-hand-o-right{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-o-right:before{content:"\f0a4"}.la.la-hand-o-left{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-o-left:before{content:"\f0a5"}.la.la-hand-o-up{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-o-up:before{content:"\f0a6"}.la.la-hand-o-down{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-o-down:before{content:"\f0a7"}.la.la-arrows-alt:before{content:"\f31e"}.la.la-group:before{content:"\f0c0"}.la.la-chain:before{content:"\f0c1"}.la.la-scissors:before{content:"\f0c4"}.la.la-files-o{font-family:'Line Awesome Free';font-weight:400}.la.la-files-o:before{content:"\f0c5"}.la.la-floppy-o{font-family:'Line Awesome Free';font-weight:400}.la.la-floppy-o:before{content:"\f0c7"}.la.la-navicon:before{content:"\f0c9"}.la.la-reorder:before{content:"\f0c9"}.la.la-pinterest{font-family:'Line Awesome Brands';font-weight:400}.la.la-pinterest-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-google-plus-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-google-plus{font-family:'Line Awesome Brands';font-weight:400}.la.la-google-plus:before{content:"\f0d5"}.la.la-money{font-family:'Line Awesome Free';font-weight:400}.la.la-money:before{content:"\f3d1"}.la.la-unsorted:before{content:"\f0dc"}.la.la-sort-desc:before{content:"\f0dd"}.la.la-sort-asc:before{content:"\f0de"}.la.la-linkedin{font-family:'Line Awesome Brands';font-weight:400}.la.la-linkedin:before{content:"\f0e1"}.la.la-rotate-left:before{content:"\f0e2"}.la.la-legal:before{content:"\f0e3"}.la.la-tachometer:before{content:"\f3fd"}.la.la-dashboard:before{content:"\f3fd"}.la.la-comment-o{font-family:'Line Awesome Free';font-weight:400}.la.la-comment-o:before{content:"\f075"}.la.la-comments-o{font-family:'Line Awesome Free';font-weight:400}.la.la-comments-o:before{content:"\f086"}.la.la-flash:before{content:"\f0e7"}.la.la-clipboard{font-family:'Line Awesome Free';font-weight:400}.la.la-paste{font-family:'Line Awesome Free';font-weight:400}.la.la-paste:before{content:"\f328"}.la.la-lightbulb-o{font-family:'Line Awesome Free';font-weight:400}.la.la-lightbulb-o:before{content:"\f0eb"}.la.la-exchange:before{content:"\f362"}.la.la-cloud-download:before{content:"\f381"}.la.la-cloud-upload:before{content:"\f382"}.la.la-bell-o{font-family:'Line Awesome Free';font-weight:400}.la.la-bell-o:before{content:"\f0f3"}.la.la-cutlery:before{content:"\f2e7"}.la.la-file-text-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-text-o:before{content:"\f15c"}.la.la-building-o{font-family:'Line Awesome Free';font-weight:400}.la.la-building-o:before{content:"\f1ad"}.la.la-hospital-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hospital-o:before{content:"\f0f8"}.la.la-tablet:before{content:"\f3fa"}.la.la-mobile:before{content:"\f3cd"}.la.la-mobile-phone:before{content:"\f3cd"}.la.la-circle-o{font-family:'Line Awesome Free';font-weight:400}.la.la-circle-o:before{content:"\f111"}.la.la-mail-reply:before{content:"\f3e5"}.la.la-github-alt{font-family:'Line Awesome Brands';font-weight:400}.la.la-folder-o{font-family:'Line Awesome Free';font-weight:400}.la.la-folder-o:before{content:"\f07b"}.la.la-folder-open-o{font-family:'Line Awesome Free';font-weight:400}.la.la-folder-open-o:before{content:"\f07c"}.la.la-smile-o{font-family:'Line Awesome Free';font-weight:400}.la.la-smile-o:before{content:"\f118"}.la.la-frown-o{font-family:'Line Awesome Free';font-weight:400}.la.la-frown-o:before{content:"\f119"}.la.la-meh-o{font-family:'Line Awesome Free';font-weight:400}.la.la-meh-o:before{content:"\f11a"}.la.la-keyboard-o{font-family:'Line Awesome Free';font-weight:400}.la.la-keyboard-o:before{content:"\f11c"}.la.la-flag-o{font-family:'Line Awesome Free';font-weight:400}.la.la-flag-o:before{content:"\f024"}.la.la-mail-reply-all:before{content:"\f122"}.la.la-star-half-o{font-family:'Line Awesome Free';font-weight:400}.la.la-star-half-o:before{content:"\f089"}.la.la-star-half-empty{font-family:'Line Awesome Free';font-weight:400}.la.la-star-half-empty:before{content:"\f089"}.la.la-star-half-full{font-family:'Line Awesome Free';font-weight:400}.la.la-star-half-full:before{content:"\f089"}.la.la-code-fork:before{content:"\f126"}.la.la-chain-broken:before{content:"\f127"}.la.la-shield:before{content:"\f3ed"}.la.la-calendar-o{font-family:'Line Awesome Free';font-weight:400}.la.la-calendar-o:before{content:"\f133"}.la.la-maxcdn{font-family:'Line Awesome Brands';font-weight:400}.la.la-html5{font-family:'Line Awesome Brands';font-weight:400}.la.la-css3{font-family:'Line Awesome Brands';font-weight:400}.la.la-ticket:before{content:"\f3ff"}.la.la-minus-square-o{font-family:'Line Awesome Free';font-weight:400}.la.la-minus-square-o:before{content:"\f146"}.la.la-level-up:before{content:"\f3bf"}.la.la-level-down:before{content:"\f3be"}.la.la-pencil-square:before{content:"\f14b"}.la.la-external-link-square:before{content:"\f360"}.la.la-compass{font-family:'Line Awesome Free';font-weight:400}.la.la-caret-square-o-down{font-family:'Line Awesome Free';font-weight:400}.la.la-caret-square-o-down:before{content:"\f150"}.la.la-toggle-down{font-family:'Line Awesome Free';font-weight:400}.la.la-toggle-down:before{content:"\f150"}.la.la-caret-square-o-up{font-family:'Line Awesome Free';font-weight:400}.la.la-caret-square-o-up:before{content:"\f151"}.la.la-toggle-up{font-family:'Line Awesome Free';font-weight:400}.la.la-toggle-up:before{content:"\f151"}.la.la-caret-square-o-right{font-family:'Line Awesome Free';font-weight:400}.la.la-caret-square-o-right:before{content:"\f152"}.la.la-toggle-right{font-family:'Line Awesome Free';font-weight:400}.la.la-toggle-right:before{content:"\f152"}.la.la-eur:before{content:"\f153"}.la.la-euro:before{content:"\f153"}.la.la-gbp:before{content:"\f154"}.la.la-usd:before{content:"\f155"}.la.la-dollar:before{content:"\f155"}.la.la-inr:before{content:"\f156"}.la.la-rupee:before{content:"\f156"}.la.la-jpy:before{content:"\f157"}.la.la-cny:before{content:"\f157"}.la.la-rmb:before{content:"\f157"}.la.la-yen:before{content:"\f157"}.la.la-rub:before{content:"\f158"}.la.la-ruble:before{content:"\f158"}.la.la-rouble:before{content:"\f158"}.la.la-krw:before{content:"\f159"}.la.la-won:before{content:"\f159"}.la.la-btc{font-family:'Line Awesome Brands';font-weight:400}.la.la-bitcoin{font-family:'Line Awesome Brands';font-weight:400}.la.la-bitcoin:before{content:"\f15a"}.la.la-file-text:before{content:"\f15c"}.la.la-sort-alpha-asc:before{content:"\f15d"}.la.la-sort-alpha-desc:before{content:"\f881"}.la.la-sort-amount-asc:before{content:"\f160"}.la.la-sort-amount-desc:before{content:"\f884"}.la.la-sort-numeric-asc:before{content:"\f162"}.la.la-sort-numeric-desc:before{content:"\f886"}.la.la-youtube-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-youtube{font-family:'Line Awesome Brands';font-weight:400}.la.la-xing{font-family:'Line Awesome Brands';font-weight:400}.la.la-xing-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-youtube-play{font-family:'Line Awesome Brands';font-weight:400}.la.la-youtube-play:before{content:"\f167"}.la.la-dropbox{font-family:'Line Awesome Brands';font-weight:400}.la.la-stack-overflow{font-family:'Line Awesome Brands';font-weight:400}.la.la-instagram{font-family:'Line Awesome Brands';font-weight:400}.la.la-flickr{font-family:'Line Awesome Brands';font-weight:400}.la.la-adn{font-family:'Line Awesome Brands';font-weight:400}.la.la-bitbucket{font-family:'Line Awesome Brands';font-weight:400}.la.la-bitbucket-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-bitbucket-square:before{content:"\f171"}.la.la-tumblr{font-family:'Line Awesome Brands';font-weight:400}.la.la-tumblr-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-long-arrow-down:before{content:"\f309"}.la.la-long-arrow-up:before{content:"\f30c"}.la.la-long-arrow-left:before{content:"\f30a"}.la.la-long-arrow-right:before{content:"\f30b"}.la.la-apple{font-family:'Line Awesome Brands';font-weight:400}.la.la-windows{font-family:'Line Awesome Brands';font-weight:400}.la.la-android{font-family:'Line Awesome Brands';font-weight:400}.la.la-linux{font-family:'Line Awesome Brands';font-weight:400}.la.la-dribbble{font-family:'Line Awesome Brands';font-weight:400}.la.la-skype{font-family:'Line Awesome Brands';font-weight:400}.la.la-foursquare{font-family:'Line Awesome Brands';font-weight:400}.la.la-trello{font-family:'Line Awesome Brands';font-weight:400}.la.la-gratipay{font-family:'Line Awesome Brands';font-weight:400}.la.la-gittip{font-family:'Line Awesome Brands';font-weight:400}.la.la-gittip:before{content:"\f184"}.la.la-sun-o{font-family:'Line Awesome Free';font-weight:400}.la.la-sun-o:before{content:"\f185"}.la.la-moon-o{font-family:'Line Awesome Free';font-weight:400}.la.la-moon-o:before{content:"\f186"}.la.la-vk{font-family:'Line Awesome Brands';font-weight:400}.la.la-weibo{font-family:'Line Awesome Brands';font-weight:400}.la.la-renren{font-family:'Line Awesome Brands';font-weight:400}.la.la-pagelines{font-family:'Line Awesome Brands';font-weight:400}.la.la-stack-exchange{font-family:'Line Awesome Brands';font-weight:400}.la.la-arrow-circle-o-right{font-family:'Line Awesome Free';font-weight:400}.la.la-arrow-circle-o-right:before{content:"\f35a"}.la.la-arrow-circle-o-left{font-family:'Line Awesome Free';font-weight:400}.la.la-arrow-circle-o-left:before{content:"\f359"}.la.la-caret-square-o-left{font-family:'Line Awesome Free';font-weight:400}.la.la-caret-square-o-left:before{content:"\f191"}.la.la-toggle-left{font-family:'Line Awesome Free';font-weight:400}.la.la-toggle-left:before{content:"\f191"}.la.la-dot-circle-o{font-family:'Line Awesome Free';font-weight:400}.la.la-dot-circle-o:before{content:"\f192"}.la.la-vimeo-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-try:before{content:"\f195"}.la.la-turkish-lira:before{content:"\f195"}.la.la-plus-square-o{font-family:'Line Awesome Free';font-weight:400}.la.la-plus-square-o:before{content:"\f0fe"}.la.la-slack{font-family:'Line Awesome Brands';font-weight:400}.la.la-wordpress{font-family:'Line Awesome Brands';font-weight:400}.la.la-openid{font-family:'Line Awesome Brands';font-weight:400}.la.la-institution:before{content:"\f19c"}.la.la-bank:before{content:"\f19c"}.la.la-mortar-board:before{content:"\f19d"}.la.la-yahoo{font-family:'Line Awesome Brands';font-weight:400}.la.la-google{font-family:'Line Awesome Brands';font-weight:400}.la.la-reddit{font-family:'Line Awesome Brands';font-weight:400}.la.la-reddit-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-stumbleupon-circle{font-family:'Line Awesome Brands';font-weight:400}.la.la-stumbleupon{font-family:'Line Awesome Brands';font-weight:400}.la.la-delicious{font-family:'Line Awesome Brands';font-weight:400}.la.la-digg{font-family:'Line Awesome Brands';font-weight:400}.la.la-pied-piper-pp{font-family:'Line Awesome Brands';font-weight:400}.la.la-pied-piper-alt{font-family:'Line Awesome Brands';font-weight:400}.la.la-drupal{font-family:'Line Awesome Brands';font-weight:400}.la.la-joomla{font-family:'Line Awesome Brands';font-weight:400}.la.la-spoon:before{content:"\f2e5"}.la.la-behance{font-family:'Line Awesome Brands';font-weight:400}.la.la-behance-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-steam{font-family:'Line Awesome Brands';font-weight:400}.la.la-steam-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-automobile:before{content:"\f1b9"}.la.la-cab:before{content:"\f1ba"}.la.la-envelope-o{font-family:'Line Awesome Free';font-weight:400}.la.la-envelope-o:before{content:"\f0e0"}.la.la-deviantart{font-family:'Line Awesome Brands';font-weight:400}.la.la-soundcloud{font-family:'Line Awesome Brands';font-weight:400}.la.la-file-pdf-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-pdf-o:before{content:"\f1c1"}.la.la-file-word-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-word-o:before{content:"\f1c2"}.la.la-file-excel-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-excel-o:before{content:"\f1c3"}.la.la-file-powerpoint-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-powerpoint-o:before{content:"\f1c4"}.la.la-file-image-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-image-o:before{content:"\f1c5"}.la.la-file-photo-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-photo-o:before{content:"\f1c5"}.la.la-file-picture-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-picture-o:before{content:"\f1c5"}.la.la-file-archive-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-archive-o:before{content:"\f1c6"}.la.la-file-zip-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-zip-o:before{content:"\f1c6"}.la.la-file-audio-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-audio-o:before{content:"\f1c7"}.la.la-file-sound-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-sound-o:before{content:"\f1c7"}.la.la-file-video-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-video-o:before{content:"\f1c8"}.la.la-file-movie-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-movie-o:before{content:"\f1c8"}.la.la-file-code-o{font-family:'Line Awesome Free';font-weight:400}.la.la-file-code-o:before{content:"\f1c9"}.la.la-vine{font-family:'Line Awesome Brands';font-weight:400}.la.la-codepen{font-family:'Line Awesome Brands';font-weight:400}.la.la-jsfiddle{font-family:'Line Awesome Brands';font-weight:400}.la.la-life-ring{font-family:'Line Awesome Free';font-weight:400}.la.la-life-bouy{font-family:'Line Awesome Free';font-weight:400}.la.la-life-bouy:before{content:"\f1cd"}.la.la-life-buoy{font-family:'Line Awesome Free';font-weight:400}.la.la-life-buoy:before{content:"\f1cd"}.la.la-life-saver{font-family:'Line Awesome Free';font-weight:400}.la.la-life-saver:before{content:"\f1cd"}.la.la-support{font-family:'Line Awesome Free';font-weight:400}.la.la-support:before{content:"\f1cd"}.la.la-circle-o-notch:before{content:"\f1ce"}.la.la-rebel{font-family:'Line Awesome Brands';font-weight:400}.la.la-ra{font-family:'Line Awesome Brands';font-weight:400}.la.la-ra:before{content:"\f1d0"}.la.la-resistance{font-family:'Line Awesome Brands';font-weight:400}.la.la-resistance:before{content:"\f1d0"}.la.la-empire{font-family:'Line Awesome Brands';font-weight:400}.la.la-ge{font-family:'Line Awesome Brands';font-weight:400}.la.la-ge:before{content:"\f1d1"}.la.la-git-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-git{font-family:'Line Awesome Brands';font-weight:400}.la.la-hacker-news{font-family:'Line Awesome Brands';font-weight:400}.la.la-y-combinator-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-y-combinator-square:before{content:"\f1d4"}.la.la-yc-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-yc-square:before{content:"\f1d4"}.la.la-tencent-weibo{font-family:'Line Awesome Brands';font-weight:400}.la.la-qq{font-family:'Line Awesome Brands';font-weight:400}.la.la-weixin{font-family:'Line Awesome Brands';font-weight:400}.la.la-wechat{font-family:'Line Awesome Brands';font-weight:400}.la.la-wechat:before{content:"\f1d7"}.la.la-send:before{content:"\f1d8"}.la.la-paper-plane-o{font-family:'Line Awesome Free';font-weight:400}.la.la-paper-plane-o:before{content:"\f1d8"}.la.la-send-o{font-family:'Line Awesome Free';font-weight:400}.la.la-send-o:before{content:"\f1d8"}.la.la-circle-thin{font-family:'Line Awesome Free';font-weight:400}.la.la-circle-thin:before{content:"\f111"}.la.la-header:before{content:"\f1dc"}.la.la-sliders:before{content:"\f1de"}.la.la-futbol-o{font-family:'Line Awesome Free';font-weight:400}.la.la-futbol-o:before{content:"\f1e3"}.la.la-soccer-ball-o{font-family:'Line Awesome Free';font-weight:400}.la.la-soccer-ball-o:before{content:"\f1e3"}.la.la-slideshare{font-family:'Line Awesome Brands';font-weight:400}.la.la-twitch{font-family:'Line Awesome Brands';font-weight:400}.la.la-yelp{font-family:'Line Awesome Brands';font-weight:400}.la.la-newspaper-o{font-family:'Line Awesome Free';font-weight:400}.la.la-newspaper-o:before{content:"\f1ea"}.la.la-paypal{font-family:'Line Awesome Brands';font-weight:400}.la.la-google-wallet{font-family:'Line Awesome Brands';font-weight:400}.la.la-cc-visa{font-family:'Line Awesome Brands';font-weight:400}.la.la-cc-mastercard{font-family:'Line Awesome Brands';font-weight:400}.la.la-cc-discover{font-family:'Line Awesome Brands';font-weight:400}.la.la-cc-amex{font-family:'Line Awesome Brands';font-weight:400}.la.la-cc-paypal{font-family:'Line Awesome Brands';font-weight:400}.la.la-cc-stripe{font-family:'Line Awesome Brands';font-weight:400}.la.la-bell-slash-o{font-family:'Line Awesome Free';font-weight:400}.la.la-bell-slash-o:before{content:"\f1f6"}.la.la-trash:before{content:"\f2ed"}.la.la-copyright{font-family:'Line Awesome Free';font-weight:400}.la.la-eyedropper:before{content:"\f1fb"}.la.la-area-chart:before{content:"\f1fe"}.la.la-pie-chart:before{content:"\f200"}.la.la-line-chart:before{content:"\f201"}.la.la-lastfm{font-family:'Line Awesome Brands';font-weight:400}.la.la-lastfm-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-ioxhost{font-family:'Line Awesome Brands';font-weight:400}.la.la-angellist{font-family:'Line Awesome Brands';font-weight:400}.la.la-cc{font-family:'Line Awesome Free';font-weight:400}.la.la-cc:before{content:"\f20a"}.la.la-ils:before{content:"\f20b"}.la.la-shekel:before{content:"\f20b"}.la.la-sheqel:before{content:"\f20b"}.la.la-meanpath{font-family:'Line Awesome Brands';font-weight:400}.la.la-meanpath:before{content:"\f2b4"}.la.la-buysellads{font-family:'Line Awesome Brands';font-weight:400}.la.la-connectdevelop{font-family:'Line Awesome Brands';font-weight:400}.la.la-dashcube{font-family:'Line Awesome Brands';font-weight:400}.la.la-forumbee{font-family:'Line Awesome Brands';font-weight:400}.la.la-leanpub{font-family:'Line Awesome Brands';font-weight:400}.la.la-sellsy{font-family:'Line Awesome Brands';font-weight:400}.la.la-shirtsinbulk{font-family:'Line Awesome Brands';font-weight:400}.la.la-simplybuilt{font-family:'Line Awesome Brands';font-weight:400}.la.la-skyatlas{font-family:'Line Awesome Brands';font-weight:400}.la.la-diamond{font-family:'Line Awesome Free';font-weight:400}.la.la-diamond:before{content:"\f3a5"}.la.la-intersex:before{content:"\f224"}.la.la-facebook-official{font-family:'Line Awesome Brands';font-weight:400}.la.la-facebook-official:before{content:"\f09a"}.la.la-pinterest-p{font-family:'Line Awesome Brands';font-weight:400}.la.la-whatsapp{font-family:'Line Awesome Brands';font-weight:400}.la.la-hotel:before{content:"\f236"}.la.la-viacoin{font-family:'Line Awesome Brands';font-weight:400}.la.la-medium{font-family:'Line Awesome Brands';font-weight:400}.la.la-y-combinator{font-family:'Line Awesome Brands';font-weight:400}.la.la-yc{font-family:'Line Awesome Brands';font-weight:400}.la.la-yc:before{content:"\f23b"}.la.la-optin-monster{font-family:'Line Awesome Brands';font-weight:400}.la.la-opencart{font-family:'Line Awesome Brands';font-weight:400}.la.la-expeditedssl{font-family:'Line Awesome Brands';font-weight:400}.la.la-battery-4:before{content:"\f240"}.la.la-battery:before{content:"\f240"}.la.la-battery-3:before{content:"\f241"}.la.la-battery-2:before{content:"\f242"}.la.la-battery-1:before{content:"\f243"}.la.la-battery-0:before{content:"\f244"}.la.la-object-group{font-family:'Line Awesome Free';font-weight:400}.la.la-object-ungroup{font-family:'Line Awesome Free';font-weight:400}.la.la-sticky-note-o{font-family:'Line Awesome Free';font-weight:400}.la.la-sticky-note-o:before{content:"\f249"}.la.la-cc-jcb{font-family:'Line Awesome Brands';font-weight:400}.la.la-cc-diners-club{font-family:'Line Awesome Brands';font-weight:400}.la.la-clone{font-family:'Line Awesome Free';font-weight:400}.la.la-hourglass-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hourglass-o:before{content:"\f254"}.la.la-hourglass-1:before{content:"\f251"}.la.la-hourglass-2:before{content:"\f252"}.la.la-hourglass-3:before{content:"\f253"}.la.la-hand-rock-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-rock-o:before{content:"\f255"}.la.la-hand-grab-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-grab-o:before{content:"\f255"}.la.la-hand-paper-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-paper-o:before{content:"\f256"}.la.la-hand-stop-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-stop-o:before{content:"\f256"}.la.la-hand-scissors-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-scissors-o:before{content:"\f257"}.la.la-hand-lizard-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-lizard-o:before{content:"\f258"}.la.la-hand-spock-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-spock-o:before{content:"\f259"}.la.la-hand-pointer-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-pointer-o:before{content:"\f25a"}.la.la-hand-peace-o{font-family:'Line Awesome Free';font-weight:400}.la.la-hand-peace-o:before{content:"\f25b"}.la.la-registered{font-family:'Line Awesome Free';font-weight:400}.la.la-creative-commons{font-family:'Line Awesome Brands';font-weight:400}.la.la-gg{font-family:'Line Awesome Brands';font-weight:400}.la.la-gg-circle{font-family:'Line Awesome Brands';font-weight:400}.la.la-tripadvisor{font-family:'Line Awesome Brands';font-weight:400}.la.la-odnoklassniki{font-family:'Line Awesome Brands';font-weight:400}.la.la-odnoklassniki-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-get-pocket{font-family:'Line Awesome Brands';font-weight:400}.la.la-wikipedia-w{font-family:'Line Awesome Brands';font-weight:400}.la.la-safari{font-family:'Line Awesome Brands';font-weight:400}.la.la-chrome{font-family:'Line Awesome Brands';font-weight:400}.la.la-firefox{font-family:'Line Awesome Brands';font-weight:400}.la.la-opera{font-family:'Line Awesome Brands';font-weight:400}.la.la-internet-explorer{font-family:'Line Awesome Brands';font-weight:400}.la.la-television:before{content:"\f26c"}.la.la-contao{font-family:'Line Awesome Brands';font-weight:400}.la.la-500px{font-family:'Line Awesome Brands';font-weight:400}.la.la-amazon{font-family:'Line Awesome Brands';font-weight:400}.la.la-calendar-plus-o{font-family:'Line Awesome Free';font-weight:400}.la.la-calendar-plus-o:before{content:"\f271"}.la.la-calendar-minus-o{font-family:'Line Awesome Free';font-weight:400}.la.la-calendar-minus-o:before{content:"\f272"}.la.la-calendar-times-o{font-family:'Line Awesome Free';font-weight:400}.la.la-calendar-times-o:before{content:"\f273"}.la.la-calendar-check-o{font-family:'Line Awesome Free';font-weight:400}.la.la-calendar-check-o:before{content:"\f274"}.la.la-map-o{font-family:'Line Awesome Free';font-weight:400}.la.la-map-o:before{content:"\f279"}.la.la-commenting:before{content:"\f4ad"}.la.la-commenting-o{font-family:'Line Awesome Free';font-weight:400}.la.la-commenting-o:before{content:"\f4ad"}.la.la-houzz{font-family:'Line Awesome Brands';font-weight:400}.la.la-vimeo{font-family:'Line Awesome Brands';font-weight:400}.la.la-vimeo:before{content:"\f27d"}.la.la-black-tie{font-family:'Line Awesome Brands';font-weight:400}.la.la-fonticons{font-family:'Line Awesome Brands';font-weight:400}.la.la-reddit-alien{font-family:'Line Awesome Brands';font-weight:400}.la.la-edge{font-family:'Line Awesome Brands';font-weight:400}.la.la-credit-card-alt:before{content:"\f09d"}.la.la-codiepie{font-family:'Line Awesome Brands';font-weight:400}.la.la-modx{font-family:'Line Awesome Brands';font-weight:400}.la.la-fort-awesome{font-family:'Line Awesome Brands';font-weight:400}.la.la-usb{font-family:'Line Awesome Brands';font-weight:400}.la.la-product-hunt{font-family:'Line Awesome Brands';font-weight:400}.la.la-mixcloud{font-family:'Line Awesome Brands';font-weight:400}.la.la-scribd{font-family:'Line Awesome Brands';font-weight:400}.la.la-pause-circle-o{font-family:'Line Awesome Free';font-weight:400}.la.la-pause-circle-o:before{content:"\f28b"}.la.la-stop-circle-o{font-family:'Line Awesome Free';font-weight:400}.la.la-stop-circle-o:before{content:"\f28d"}.la.la-bluetooth{font-family:'Line Awesome Brands';font-weight:400}.la.la-bluetooth-b{font-family:'Line Awesome Brands';font-weight:400}.la.la-gitlab{font-family:'Line Awesome Brands';font-weight:400}.la.la-wpbeginner{font-family:'Line Awesome Brands';font-weight:400}.la.la-wpforms{font-family:'Line Awesome Brands';font-weight:400}.la.la-envira{font-family:'Line Awesome Brands';font-weight:400}.la.la-wheelchair-alt{font-family:'Line Awesome Brands';font-weight:400}.la.la-wheelchair-alt:before{content:"\f368"}.la.la-question-circle-o{font-family:'Line Awesome Free';font-weight:400}.la.la-question-circle-o:before{content:"\f059"}.la.la-volume-control-phone:before{content:"\f2a0"}.la.la-asl-interpreting:before{content:"\f2a3"}.la.la-deafness:before{content:"\f2a4"}.la.la-hard-of-hearing:before{content:"\f2a4"}.la.la-glide{font-family:'Line Awesome Brands';font-weight:400}.la.la-glide-g{font-family:'Line Awesome Brands';font-weight:400}.la.la-signing:before{content:"\f2a7"}.la.la-viadeo{font-family:'Line Awesome Brands';font-weight:400}.la.la-viadeo-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-snapchat{font-family:'Line Awesome Brands';font-weight:400}.la.la-snapchat-ghost{font-family:'Line Awesome Brands';font-weight:400}.la.la-snapchat-square{font-family:'Line Awesome Brands';font-weight:400}.la.la-pied-piper{font-family:'Line Awesome Brands';font-weight:400}.la.la-first-order{font-family:'Line Awesome Brands';font-weight:400}.la.la-yoast{font-family:'Line Awesome Brands';font-weight:400}.la.la-themeisle{font-family:'Line Awesome Brands';font-weight:400}.la.la-google-plus-official{font-family:'Line Awesome Brands';font-weight:400}.la.la-google-plus-official:before{content:"\f2b3"}.la.la-google-plus-circle{font-family:'Line Awesome Brands';font-weight:400}.la.la-google-plus-circle:before{content:"\f2b3"}.la.la-font-awesome{font-family:'Line Awesome Brands';font-weight:400}.la.la-fa{font-family:'Line Awesome Brands';font-weight:400}.la.la-fa:before{content:"\f2b4"}.la.la-handshake-o{font-family:'Line Awesome Free';font-weight:400}.la.la-handshake-o:before{content:"\f2b5"}.la.la-envelope-open-o{font-family:'Line Awesome Free';font-weight:400}.la.la-envelope-open-o:before{content:"\f2b6"}.la.la-linode{font-family:'Line Awesome Brands';font-weight:400}.la.la-address-book-o{font-family:'Line Awesome Free';font-weight:400}.la.la-address-book-o:before{content:"\f2b9"}.la.la-vcard:before{content:"\f2bb"}.la.la-address-card-o{font-family:'Line Awesome Free';font-weight:400}.la.la-address-card-o:before{content:"\f2bb"}.la.la-vcard-o{font-family:'Line Awesome Free';font-weight:400}.la.la-vcard-o:before{content:"\f2bb"}.la.la-user-circle-o{font-family:'Line Awesome Free';font-weight:400}.la.la-user-circle-o:before{content:"\f2bd"}.la.la-user-o{font-family:'Line Awesome Free';font-weight:400}.la.la-user-o:before{content:"\f007"}.la.la-id-badge{font-family:'Line Awesome Free';font-weight:400}.la.la-drivers-license:before{content:"\f2c2"}.la.la-id-card-o{font-family:'Line Awesome Free';font-weight:400}.la.la-id-card-o:before{content:"\f2c2"}.la.la-drivers-license-o{font-family:'Line Awesome Free';font-weight:400}.la.la-drivers-license-o:before{content:"\f2c2"}.la.la-quora{font-family:'Line Awesome Brands';font-weight:400}.la.la-free-code-camp{font-family:'Line Awesome Brands';font-weight:400}.la.la-telegram{font-family:'Line Awesome Brands';font-weight:400}.la.la-thermometer-4:before{content:"\f2c7"}.la.la-thermometer:before{content:"\f2c7"}.la.la-thermometer-3:before{content:"\f2c8"}.la.la-thermometer-2:before{content:"\f2c9"}.la.la-thermometer-1:before{content:"\f2ca"}.la.la-thermometer-0:before{content:"\f2cb"}.la.la-bathtub:before{content:"\f2cd"}.la.la-s15:before{content:"\f2cd"}.la.la-window-maximize{font-family:'Line Awesome Free';font-weight:400}.la.la-window-restore{font-family:'Line Awesome Free';font-weight:400}.la.la-times-rectangle:before{content:"\f410"}.la.la-window-close-o{font-family:'Line Awesome Free';font-weight:400}.la.la-window-close-o:before{content:"\f410"}.la.la-times-rectangle-o{font-family:'Line Awesome Free';font-weight:400}.la.la-times-rectangle-o:before{content:"\f410"}.la.la-bandcamp{font-family:'Line Awesome Brands';font-weight:400}.la.la-grav{font-family:'Line Awesome Brands';font-weight:400}.la.la-etsy{font-family:'Line Awesome Brands';font-weight:400}.la.la-imdb{font-family:'Line Awesome Brands';font-weight:400}.la.la-ravelry{font-family:'Line Awesome Brands';font-weight:400}.la.la-eercast{font-family:'Line Awesome Brands';font-weight:400}.la.la-eercast:before{content:"\f2da"}.la.la-snowflake-o{font-family:'Line Awesome Free';font-weight:400}.la.la-snowflake-o:before{content:"\f2dc"}.la.la-superpowers{font-family:'Line Awesome Brands';font-weight:400}.la.la-wpexplorer{font-family:'Line Awesome Brands';font-weight:400}.la.la-spotify{font-family:'Line Awesome Brands';font-weight:400} diff --git a/lineawesome/fonts/la-solid-900.eot b/app/static/lineawesome/fonts/la-solid-900.eot similarity index 100% rename from lineawesome/fonts/la-solid-900.eot rename to app/static/lineawesome/fonts/la-solid-900.eot diff --git a/lineawesome/fonts/la-solid-900.svg b/app/static/lineawesome/fonts/la-solid-900.svg similarity index 100% rename from lineawesome/fonts/la-solid-900.svg rename to app/static/lineawesome/fonts/la-solid-900.svg diff --git a/lineawesome/fonts/la-solid-900.ttf b/app/static/lineawesome/fonts/la-solid-900.ttf similarity index 100% rename from lineawesome/fonts/la-solid-900.ttf rename to app/static/lineawesome/fonts/la-solid-900.ttf diff --git a/lineawesome/fonts/la-solid-900.woff b/app/static/lineawesome/fonts/la-solid-900.woff similarity index 100% rename from lineawesome/fonts/la-solid-900.woff rename to app/static/lineawesome/fonts/la-solid-900.woff diff --git a/lineawesome/fonts/la-solid-900.woff2 b/app/static/lineawesome/fonts/la-solid-900.woff2 similarity index 100% rename from lineawesome/fonts/la-solid-900.woff2 rename to app/static/lineawesome/fonts/la-solid-900.woff2 diff --git a/main.css b/app/static/main.css similarity index 99% rename from main.css rename to app/static/main.css index 8f8b5b1..284d6d5 100644 --- a/main.css +++ b/app/static/main.css @@ -4715,8 +4715,8 @@ input:checked + .slider:before { font-style: normal; font-weight: 900; font-display: auto; - src: url("./lineawesome/fonts/la-solid-900.eot"); - src: url("./lineawesome/fonts/la-solid-900.eot?#iefix") format("embedded-opentype"), url("./lineawesome/fonts/la-solid-900.woff2") format("woff2"), url("./lineawesome/fonts/la-solid-900.woff") format("woff"), url("./lineawesome/fonts/la-solid-900.ttf") format("truetype"), url("./lineawesome/fonts/la-solid-900.svg#lineawesome") format("svg"); } + src: url("la-solid-900.eot"); + src: url("la-solid-900.eot?#iefix") format("embedded-opentype"), url("la-solid-900.woff2") format("woff2"), url("la-solid-900.woff") format("woff"), url("la-solid-900.ttf") format("truetype"), url("la-solid-900.svg#lineawesome") format("svg"); } .las { -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; diff --git a/main.js b/app/static/main.js similarity index 97% rename from main.js rename to app/static/main.js index 874d992..0fa64c3 100644 --- a/main.js +++ b/app/static/main.js @@ -1,6467 +1,6467 @@ -/* -* Copyright (c) 2022 Steve Seguin. All Rights Reserved. -* -* Use of this source code is governed by the APGLv3 open-source license -* that can be found in the LICENSE file in the root of the source -* tree. Alternative licencing options can be made available on request. -* -*/ -/*jshint esversion: 6 */ -async function main(){ // main asyncronous thread; mostly initializes the user settings. - - var delayedStartupFuncs = []; - // translation stuff start //// - - var ConfigSettings = getById("main-js"); - var ln_template = false; - - try { - if (ConfigSettings) { - ln_template = ConfigSettings.getAttribute('data-translation'); // Translations - if (typeof ln_template === "undefined") { - ln_template = false; - } else if (ln_template === null) { - ln_template = false; - } - } - - if (urlParams.has('ln') || urlParams.has('language')) { - ln_template = urlParams.get('ln') || urlParams.get('language') || null; - } - } catch (e) { - errorlog(e); - } - if (ln_template===null) { - getById("mainmenu").style.opacity = 1; - } else if (ln_template!==false) { // checking if manual lanuage override enabled - try { - log("Lang Template: " + ln_template); - await changeLg(ln_template); - //getById("mainmenu").style.opacity = 1; - } catch (error) { - errorlog(error); - getById("mainmenu").style.opacity = 1; - } - } - if (location.hostname !== "vdo.ninja" && location.hostname !== "backup.vdo.ninja" && location.hostname !== "proxy.vdo.ninja" && location.hostname !== "obs.ninja") { - - errorReport = false; - - if (location.hostname === "rtc.ninja"){ - try { - if (session.label === false) { - document.title = "RTC.Ninja"; - } - getById("qos").innerHTML = ""; - getById("logoname").innerHTML = ""; - getById("helpbutton").style.display = "none"; - getById("helpbutton").style.opacity = 0; - getById("reportbutton").style.display = "none"; - getById("reportbutton").style.opacity = 0; - getById("dropButton").classList.add("hidden"); - getById("container-4").classList.add("hidden"); - if (!(urlParams.has('screenshare') || urlParams.has('ss'))){ - getById("container-2").classList.add("hidden"); - } - //getById("mainmenu").style.opacity = 1; - getById("mainmenu").style.margin = "30px 0"; - getById("translateButton").style.display = "none"; - getById("translateButton").style.opacity = 0; - getById("info").style.display = "none"; - getById("info").style.opacity = 0; - getById("chatBody").innerHTML = ""; - } catch (e) {} - } else if (session.label === false) { - document.title = location.hostname; - } - try { - if (ln_template===false){ - changeLg("blank"); - } - //getById("mainmenu").style.opacity = 1; - - getById("qos").innerHTML = '' - getById("logoname").innerHTML = getById("qos").outerHTML; - getById("helpbutton").style.display = "none"; - getById("reportbutton").style.display = "none"; - getById("chatBody").innerHTML = ""; - getById("qos").style.color = "#FFF0"; - getById("qos").style.fontSize = "70%"; - getById("logoname").style.display = "none"; - getById("logoname").style.margin = "0 0 0 5px"; - } catch (error) { - getById("mainmenu").style.opacity = 1; - errorlog(error); - } - } else { // check if automatic language translation is available - getById("mainmenu").style.opacity = 1; - } - - //// translation stuff ends //// - - if (urlParams.has('cleanoutput') || urlParams.has('clean') || urlParams.has('cleanish')) { - session.cleanOutput = true; - } - - if (urlParams.has('cleanviewer') || urlParams.has('cv')) { - session.cleanViewer = true; - } - - if (session.cleanOutput || session.cleanViewer){ - session.audioMeterGuest = false; - } - - if (urlParams.has('hidehome')){ - session.hidehome = true; - } - hideHomeCheck(); - - if (urlParams.has('previewmode')){ - session.switchMode = true; - } - - if (urlParams.has('director') || urlParams.has('dir')) { - session.director = urlParams.get('director') || urlParams.get('dir') || session.roomid || urlParams.get('roomid') || urlParams.get('r') || urlParams.get('room') || filename || true; - session.effect = null; // so the director can see the effects after a page refresh - getById("avatarDiv3").classList.remove("hidden"); // lets the director see the avatar option - } - - if (urlParams.has('controls') || urlParams.has('videocontrols')) { - session.showControls = true; // show the video control bar - - if (urlParams.get('controls') === "false"){ - session.showControls = false; - } else if (urlParams.get('controls') === "0"){ - session.showControls = false; - } else if (urlParams.get('controls') === "off"){ - session.showControls = false; - } - } - if (urlParams.has('nocontrols')) { - session.showControls = false; // show the video control bar - } - - if (!isIFrame && !window.obsstudio){ - if (ChromiumVersion===65){ - // pass, since probably manycam and that's bugged - } else if (getStorage("redirect") == "yes") { - setStorage("redirect", "", 0); - session.sticky = true; - } else if (getStorage("settings") != "") { - var URLGOTO = getStorage("settings"); - if (URLGOTO && URLGOTO.startsWith("https://")){ - if (URLGOTO === window.location.href) { - // continue, as its already matched - } else if (!(session.cleanOutput)){ - - window.focus(); - document.body.classList.remove("hidden"); - - session.sticky = await confirmAlt("Would you like to load your previous session?\n\nThis will redirect you to:\n\n"+URLGOTO, true); - if (!session.sticky) { - setStorage("settings", "", 0); - log("deleting cookie as user said no"); - } else { - var cookieSettings = decodeURI(URLGOTO); - setStorage("redirect", "yes", 1); - window.location.replace(cookieSettings); - } - } else { - var cookieSettings = decodeURI(URLGOTO); - setStorage("redirect", "yes", 1); - window.location.replace(cookieSettings); - } - } - } - - if (urlParams.has('sticky')){ // won't work with iframes. - - //if (getStorage("permission") == "") { - // session.sticky = confirm("Would you allow us to store a cookie to keep your session settings persistent?"); - //} else { - session.sticky = true; - - getById("saveRoom").style.display = "none"; - //} - //if (session.sticky) { - setStorage("permission", "yes", 999); - setStorage("settings", encodeURI(window.location.href), 90); - //} - } - } - - - if (urlParams.has('safemode')) { - session.safemode = true; // load defa - } else { - session.store = {}; - try { - loadSettings(); - } catch(e){ - errorlog(e); - } - } - - if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1) { - try { - //getById("electronDragZone").style.cursor="grab"; - if (!ipcRenderer){ - ipcRenderer = require('electron').ipcRenderer; - } - if (ipcRenderer){ - window.prompt = function(title, val){ - return ipcRenderer.sendSync('prompt', {title, val}); // call if needed in the future - }; - } - //ipcRenderer.sendSync('prompt', {title, val}); // call now -- but why? - } catch(e){} - } - - if (window.electronApi && window.electronApi.exposeDoSomethingInWebApp){ - window.electronApi.exposeDoSomethingInWebApp(function (fauxEventData) { - //alert("WORKS"); - //errorlog("WORKS!"); - session.remoteInterfaceAPI(fauxEventData); - }); - window.electronApi.updateVersion(session.version); - } - - if (urlParams.has('retrytimeout')) { - session.retryTimeout = parseInt(urlParams.get('retrytimeout')) || 5000; - if (session.retryTimeout<5000){ - session.retryTimeout = 5000; - } - } - - if (urlParams.has('ptz')){ - session.ptz=true; - } - - if (urlParams.has('notios')){ - iOS=false; - iPad=false; - } - - if (urlParams.has('optimize')) { - session.optimize = parseInt(urlParams.get('optimize')) || 0; - } - - if (urlParams.has('nosettings') || urlParams.has('ns')) { - session.screensharebutton = false; - session.showSettings = false; - } - - if (urlParams.has('nomicbutton') || urlParams.has('nmb')) { - getById("mutebutton").style.setProperty("display", "none", "important"); - } - - if (urlParams.has('novice') ) { - // Hiding advanced items - document.querySelectorAll(".advanced").forEach(element => { - element.classList.add("hide"); - }) - } - - if (urlParams.has('avatarimg') || urlParams.has('bgimage') || urlParams.has('bgimg')) { // URL or data:base64 image. Becomes local to this viewer only. This is like &avatar, but slightly different. Just CSS in this case - var avatarImg = urlParams.get('avatarimg') || urlParams.get('bgimage') || urlParams.get('bgimg') || "./media/avatar1.png"; - if (avatarImg){ - try { - avatarImg = decodeURIComponent(avatarImg); - } catch(e){} - try { - let fallbackImage = new Image(); - fallbackImage.src = avatarImg; - session.style = -1; - fallbackImage.onload = function(){ - document.documentElement.style.setProperty('--video-background-image', 'url("'+avatarImg+'")'); - if (session.meterStyle!==5){ - document.documentElement.style.setProperty('--video-background-image-size', "contain"); - } - } - } catch(e){} - } - } - if (urlParams.has('avatarimg2') || urlParams.has('bgimage2') || urlParams.has('bgimg2')) { // URL or data:base64 image. Becomes local to this viewer only. This is like &avatar, but slightly different. Just CSS in this case - var avatarImg2 = urlParams.get('avatarimg2') || urlParams.get('bgimage2') || urlParams.get('bgimg2') || "./media/avatar2.png"; - if (avatarImg2){ - try { - avatarImg2 = decodeURIComponent(avatarImg2); - } catch(e){} - try { - let fallbackImage2 = new Image(); - fallbackImage2.src = avatarImg2; - fallbackImage2.onload = function(){ - document.documentElement.style.setProperty('--video-background-image-talking', 'url("'+avatarImg2+'")'); - if (session.meterStyle!==5){ - document.documentElement.style.setProperty('--video-background-image-size', "contain"); - } - } - session.audioEffects = true; - session.meterStyle = 4; - session.style = -1; - if (session.showControls===null){ - session.showControls = false; - } - - } catch(e){} - } - } - - if (urlParams.has('avatarimg3') || urlParams.has('bgimage3') || urlParams.has('bgimg3')) { // URL or data:base64 image. Becomes local to this viewer only. This is like &avatar, but slightly different. Just CSS in this case - var avatarImg3 = urlParams.get('avatarimg3') || urlParams.get('bgimage3') || urlParams.get('bgimg3') || "./media/avatar3.png"; - if (avatarImg3){ - try { - avatarImg3 = decodeURIComponent(avatarImg3); - } catch(e){} - try { - - let fallbackImage3 = new Image(); - fallbackImage3.src = avatarImg3; - fallbackImage3.onload = function(){ - document.documentElement.style.setProperty('--video-background-image-screaming', 'url("'+avatarImg3+'")'); - if (session.meterStyle!==5){ - document.documentElement.style.setProperty('--video-background-image-size', "contain"); - } - } - session.audioEffects = true; - session.meterStyle = 4; - session.style = -1; - if (session.showControls===null){ - session.showControls = false; - } - } catch(e){} - } - } - - if (urlParams.has('background') || urlParams.has('appbg')) { // URL or data:base64 image. Use &chroma if you want to use a color instead of image. - var background = urlParams.get('background') || urlParams.get('appbg') || false; - if (background){ - try { - background = decodeURIComponent(background); - } catch(e){} - try { - background = 'url("'+background+'")'; - document.documentElement.style.setProperty('--background-main-image', background); - - } catch(e){} - } - } - - if (urlParams.has('poster')) { // URL or data:base64 image. Becomes local to this viewer only. This is like &avatar, but slightly different. Just CSS in this case - var posterImage = urlParams.get('poster') || "./media/avatar.webp"; - if (posterImage){ - try { - posterImage = decodeURIComponent(posterImage); - session.posterImage = posterImage; - } catch(e){} - } - } - - if (urlParams.has('hideplaybutton') || urlParams.has('hpb')) { // URL or data:base64 image. Becomes local to this viewer only. This is like &avatar, but slightly different. Just CSS in this case - try { - document.getElementById("bigPlayButton").classList.add("hidden"); - } catch(e){ - - } - } - - if (urlParams.has('whip') || urlParams.has('whipview')) { - session.whipView = urlParams.get('whip') || urlParams.get('whipview') || false; - if (session.whipView){ - setTimeout(function(){whipClient();},1000); - } - } - - if (urlParams.has('cftoken') || urlParams.has('cft')){ - session.whipOutput = urlParams.get('cftoken') || urlParams.get('cft') || false; - if (session.whipOutput){ - session.whipOutput = "https://cloudflare.vdo.ninja/"+session.whipOutput; - } - } - - if (urlParams.has('whippush') || urlParams.has('whipout') || urlParams.has('pushwhip')) { // URL or data:base64 image. Becomes local to this viewer only. This is like &avatar, but slightly different. Just CSS in this case - session.whipOutput = urlParams.get('whippush') || urlParams.get('whipout') || urlParams.get('pushwhip') || null; - if (session.whipOutput){ - try { - if (session.whipOutput == 'twitch'){ - session.whipOutput = "https://g.webrtc.live-video.net:4443/v2/offer"; - query("#publishOutToken input[type='password']").placeholder = "Twitch stream token here"; - } else { - session.whipOutput = decodeURIComponent(session.whipOutput); - } - } catch(e){ - errorlog(e); - } - } else { - getById("publishOutURL").classList.remove("hidden"); - } - - if (urlParams.has('whippushtoken') || urlParams.has('whipouttoken') || urlParams.has('pushwhiptoken')) {// URL or data:base64 image. Becomes local to this viewer only. This is like &avatar, but slightly different. Just CSS in this case - session.whipOutputToken = urlParams.get('whippushtoken') || urlParams.get('whipouttoken') || urlParams.get('pushwhiptoken') || false; - if (!session.whipOutputToken){ - getById("publishOutToken").classList.remove("hidden"); - } - } else if (session.whipOutput!==false){ - if (!session.whipOutputToken){ - getById("publishOutToken").classList.remove("hidden"); - } - } - } - - - - if (urlParams.has('whepplay')) { // URL or data:base64 image. Becomes local to this viewer only. This is like &avatar, but slightly different. Just CSS in this case - if (urlParams.get('whepplay')){ - try { - session.whepInput = decodeURIComponent(urlParams.get('whepplay')); - if (session.whepInput){ - setTimeout(function(){whepIn();},1000); - } - } catch(e){ - errorlog(e); - } - } - } - if (urlParams.has('whepplaytoken')) { // URL or data:base64 image. Becomes local to this viewer only. This is like &avatar, but slightly different. Just CSS in this case - if (urlParams.get('whepplaytoken')){ - try { - session.whepInputToken = urlParams.get('whepplaytoken') - } catch(e){ - errorlog(e); - } - } - } - - if (urlParams.has("hostwhep")){ - session.whepHost = urlParams.get("hostwhep") || session.streamID || false; - } - - if (urlParams.has('nomouseevents') || urlParams.has('nme')) { - session.disableMouseEvents = true; - } - if (urlParams.has('overlaycontrols')) { - session.overlayControls = true; - } - - if (urlParams.has('novideobutton') || urlParams.has('nvb')) { - getById("mutevideobutton").style.setProperty("display", "none", "important"); - } - - if (urlParams.has('nospeakerbutton') || urlParams.has('nsb')) { - getById("mutespeakerbutton").style.setProperty("display", "none", "important"); - } - - if (urlParams.has('noscale') || urlParams.has('noscaling')) { - session.noScaling = true; - } - - if (urlParams.has('pusheffectsdata') ) { - session.pushEffectsData=true; - } - - if (urlParams.has('pushloudness') || urlParams.has('getloudness')) { // this sets the loudness IFRAME API output, if available. - session.pushLoudness = true; - } - - if (urlParams.has('pushfaces') || urlParams.has('getfaces')) { - session.grabFaceData = true; - setTimeout(function(){ // give the app some time to load - getFaces(); - }, 2000); - } - - if (urlParams.has('notmobile')){ - session.mobile = false; - } else if (urlParams.has('mobile')){ - session.mobile = true; - session.audioEffects = false; // disable audio inbound effects also. - session.audioMeterGuest = false; - } else if (iOS || iPad) { - if (SafariVersion && SafariVersion<16){ - getById("oldiOSWarning").classList.remove('hidden'); - } - session.mobile = true; - session.audioEffects = false; // disable audio inbound effects also. - session.audioMeterGuest = false; - window.addEventListener('resize', function() { // Safari is the new IE. - - if (session.ws){ - var msg = {}; - msg.requestSceneUpdate = true; - session.sendMessage(msg); - } - if (screen && screen.orientation && screen.orientation.type){ - if (screen.orientation.type.includes("portrait")){ - document.getElementsByTagName("html")[0].style.height = "100vh"; - setTimeout(function(){ - document.getElementsByTagName("html")[0].style.height = "100%"; - }, 1000); - } else if (screen.orientation.type.includes("landscape")){ - document.getElementsByTagName("html")[0].style.height = "100vh"; - setTimeout(function(){ - document.getElementsByTagName("html")[0].style.height = "100%"; - }, 1000); - } - } else if ( window.matchMedia("(orientation: portrait)").matches ) { - document.getElementsByTagName("html")[0].style.height = "100vh"; - setTimeout(function(){ - document.getElementsByTagName("html")[0].style.height = "100%"; - }, 1000); - } else if ( window.matchMedia("(orientation: landscape)").matches ) { - document.getElementsByTagName("html")[0].style.height = "100vh"; - setTimeout(function(){ - document.getElementsByTagName("html")[0].style.height = "100%"; - }, 1000); - } - }); - - if (/CriOS/i.test(navigator.userAgent)) { // if runngin Chrome on iOS - if (!(session.cleanOutput)) { - try { - navigator.mediaDevices.getUserMedia; - } catch (e) { - warnUser("Chrome on this device does not support the required technology to use this site.\n\nPlease use Safari instead or update your iOS and browser version."); - } - } - } - } else if (/Android|Pixel|webOS|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { // not sure how accurate this is. - session.mobile = true; - session.audioEffects = false; // disable audio inbound effects also. - session.audioMeterGuest = false; - } else { - log("MAKE DRAGGABLE"); - delayedStartupFuncs.push([makeDraggableElement, document.getElementById("subControlButtons")]); - if (SafariVersion && !ChromiumVersion){ // if desktop Safari, so macOS, give a note saying it sucks - getById("SafariWarning").classList.remove("hidden"); - } - } - - - if (urlParams.has('broadcasttransfer') || urlParams.has('bct')) { - log("Broadcast transfer flag set"); - session.broadcastTransfer = urlParams.get('broadcasttransfer') || urlParams.get('bct') || null; - if (session.broadcastTransfer === "false") { - session.broadcastTransfer = false; - } else if (session.broadcastTransfer=== "0") { - session.broadcastTransfer = false; - } else if (session.broadcastTransfer === "no") { - session.broadcastTransfer = false; - } else if (session.broadcastTransfer === "off") { - session.broadcastTransfer = false; - } else { - session.broadcastTransfer = true; - } - if (transferSettings){ - transferSettings.broadcast = session.broadcastTransfer; - } - } - - if (urlParams.has('queuetransfer') || urlParams.has('qt')) { - log("Broadcast transfer flag set"); - session.queueTransfer = urlParams.get('queuetransfer') || urlParams.get('qt') || null; - if (session.queueTransfer === "false") { - session.queueTransfer = false; - } else if (session.queueTransfer=== "0") { - session.queueTransfer = false; - } else if (session.queueTransfer === "no") { - session.queueTransfer = false; - } else if (session.queueTransfer === "off") { - session.queueTransfer = false; - } else { - session.queueTransfer = true; - } - if (transferSettings){ - transferSettings.queue = session.queueTransfer; - } - } - - if (urlParams.has('broadcast') || urlParams.has('bc')) { - log("Broadcast flag set"); - session.broadcast = urlParams.get('broadcast') || urlParams.get('bc') || null; - - if (session.broadcast === "false") { - session.broadcast = false; - } else if (session.broadcast=== "0") { - session.broadcast = false; - } else if (session.broadcast === "no") { - session.broadcast = false; - } else if (session.broadcast === "off") { - session.broadcast = false; - } - - //if ((iOS) || (iPad)) { - // session.nopreview = false; - //} else { - // session.nopreview = true; - //} - session.minipreview = 2; // full screen if nothing else on screen. - session.style = 1; - //getById("header").style.display = "none"; - //getById("header").style.opacity = 0; - session.showList=true; - } - - if (urlParams.has('showlist')) { - session.showList = urlParams.get('showlist'); - if (session.showList === "false") { - session.showList = false; - } else if (session.showList=== "0") { - session.showList = false; - } else if (session.showList === "no") { - session.showList = false; - } else if (session.showList === "off") { - session.showList = false; - } else { - session.showList = true; - } - } - //if (session.showList===true){ - // getById("hideusers").classList.add("hidden"); - //} - - if (urlParams.has('meshcast')) { - session.meshcast = urlParams.get('meshcast') || "any"; - meshcast(true); - } - if (urlParams.has('meshcastcode') || urlParams.has('mccode')) { - session.meshcastCode = urlParams.get('meshcastcode') || urlParams.get('mccode') || false - } - - if (urlParams.has('nomeshcast')) { - session.noMeshcast = urlParams.get('nomeshcast') || true; - } - - - var filename = false; - try { - if (!session.decrypted){ - filename = window.location.pathname.substring(window.location.pathname.lastIndexOf('/') + 1); - filename = filename.replace("??", "?"); - filename2 = filename.split("?")[0]; - // split at ??? - if (filename.split(".").length == 1) { - if (filename2.length < 2) { // easy win - filename = false; - } else if (filename.startsWith("&")) { // easy win - var tmpHref = window.location.href.substring(0, window.location.href.lastIndexOf('/')) + "/?" + filename.split("&").slice(1).join("&"); - log("TMP " + tmpHref); - updateURL(filename.split("&")[1], true, tmpHref); - filename = false; - } else if (filename2.split("&")[0].includes("=")) { - log("asdf " + filename.split("&")[0]); - if (history.pushState) { - var tmpHref = window.location.href.substring(0, window.location.href.lastIndexOf('/')); - tmpHref = tmpHref + "/?" + filename; - filename = false; - //warnUser("Please ensure your URL is correctly formatted."); - window.history.pushState({path: tmpHref.toString()}, '', tmpHref.toString()); - } - } else { - filename = filename2.split("&")[0]; - if (filename2 != filename) { - warnUser("Warning: Please ensure your URL is correctly formatted."); - } - } - } else { - filename = false; - } - log(filename); - } - } catch (e) { - errorlog(e); - } - - - var directorLanding = false; - if (session.director) { - if (session.director===true){ // room not specified. - directorLanding = true; - } - session.meterStyle = 1; - session.signalMeter = true; - session.batteryMeter = true; - } else if (filename === "director") { - directorLanding = true; - filename = false; - session.meterStyle = 1; - session.signalMeter = true; - session.batteryMeter = true; - } - - session.slotmode = false; // temporary; remove in the future TODO: ## ----------------------- - if (urlParams.has('slotmode') || urlParams.has('slotsmode')){ - session.slotmode = parseInt(urlParams.get('slotmode')) || parseInt(urlParams.get('slotsmode')) || 1; - } - - - if (urlParams.has('signalmeter')) { - session.signalMeter = urlParams.get('signalmeter'); - if (session.signalMeter === "false") { - session.signalMeter = false; - } else if (session.signalMeter=== "0") { - session.signalMeter = false; - } else if (session.signalMeter === "no") { - session.signalMeter = false; - } else if (session.signalMeter === "off") { - session.signalMeter = false; - } else { - session.signalMeter = true; - } - } - - if (urlParams.has('batterymeter')) { - session.batteryMeter = urlParams.get('batterymeter'); - if (session.batteryMeter === "false") { - session.batteryMeter = false; - } else if (session.batteryMeter=== "0") { - session.batteryMeter = false; - } else if (session.batteryMeter === "no") { - session.batteryMeter = false; - } else if (session.batteryMeter === "off") { - session.batteryMeter = false; - } else { - session.batteryMeter = true; - } - } - - if (urlParams.has('rooms')) { - session.rooms = urlParams.get('rooms').split(",").map(function(e) { - return sanitizeRoomName(e); - }); - getById("rooms").classList.remove('hidden'); - } - - if (urlParams.has('leaveorientationflag')) { - session.removeOrientationFlag = false; // leave `a=extmap:3 urn:3gpp:video-orientation\r\n` alone - } - - if (urlParams.has('showdirector') || urlParams.has('sd')) { - session.showDirector = parseInt(urlParams.get('showdirector')) || parseInt(urlParams.get('sd')) || true; // if 2, video only allowed. True or 1 will be video + audio allowed. - // fyi, true is the same as 1 when == is used, so assert(1==true) is true. - } - - if (urlParams.has('bitratecutoff') || urlParams.has('bitcut')) { - session.lowBitrateCutoff = parseInt(urlParams.get('bitratecutoff')) || parseInt(urlParams.get('bitcut')) || 300; // low bitrate cut off. - } - - if (urlParams.has('motionswitch') || urlParams.has('motiondetection')) { // switch OBS to this scene when there is motion, and "solo view" this video in the VDO.Ninja auto-mixer, if used - session.motionSwitch = parseInt(urlParams.get('motionswitch')) || parseInt(urlParams.get('motiondetection')) || 15; // threshold of motion needed to trigger - } - - - if (urlParams.has('locked')) { - session.locked = urlParams.get('locked'); - - if ((session.locked == 'portrait') || (session.locked == 'vertical')){ - session.locked = 9.0/16.0; - } else if (session.locked == 'landscape'){ - session.locked = 16.0/9.0; - } else if (session.locked == 'square'){ - session.locked = 1.0; - } else { - session.locked = parseFloat(session.locked) || 16/9.0; - } - } - - - if (urlParams.has('lowbitratescene') || urlParams.has('cutscene')) { - session.lowBitrateSceneChange = urlParams.get('lowbitratescene') || urlParams.get('cutscene') || "cutscene"; // low bitrate cut off. - if (session.lowBitrateCutoff===false){ - session.lowBitrateCutoff=300; - } - } - - if (urlParams.has('rotate') ) { - session.rotate = urlParams.get('rotate') || 90; - session.rotate = parseInt(session.rotate); - } - - if (urlParams.has("rotatewindow") || urlParams.has("rotatepage")){ - let rotateThis = parseInt(urlParams.get("rotatewindow")) || parseInt(urlParams.get("rotatepage")) || 90; - updateForceRotatedCSS(rotateThis); - } - - if (urlParams.has('facing') ) { - session.facingMode = urlParams.get('facing') || false; - } - if (session.facingMode){ - session.facingMode = session.facingMode.toLowerCase(); - if (session.facingMode == "user"){ - // - } else if (session.facingMode == "environment"){ - // - } else if (session.facingMode == "rear"){ - session.facingMode = "environment"; - } else if (session.facingMode == "back"){ - session.facingMode = "environment"; - } else if (session.facingMode == "front"){ - session.facingMode = "user"; - } else { - session.facingMode = false; - } - } - - // session.facingMode }; // user or environment - - if (urlParams.has('forcelandscape') || urlParams.has('forcedlandscape') || urlParams.has('fl')){ - session.orientation = "landscape"; - if (Firefox){ - session.fullscreen = true; // windowed mode complicates things in this mode - } - } else if (urlParams.has('forceportrait') || urlParams.has('forcedportrait')|| urlParams.has('fp')){ - session.orientation = "portrait"; - if (Firefox){ - session.fullscreen = true; // windowed mode complicates things in this mode - } - } - - if (urlParams.has('forceviewerlandscape')){ - session.keepIncomingVideosInLandscape = parseInt(urlParams.get('forceviewerlandscape')) || 270; - } - - - document.addEventListener('fullscreenchange', event => { - log("full screen change event"); - log(event); - - if (document.getElementById("previewWebcam")){ - return; - } - - if (session.orientation && session.mobile){ - if (document.fullscreenElement) { - document.exitFullscreen(); - getById("fullscreenPageToggle").classList.add("la-expand-arrows-alt"); - getById("fullscreenPageToggle").classList.remove("la-compress-arrows-alt"); - } - return; - } - if (document.fullscreenElement) { - getById("fullscreenPageToggle").classList.remove("la-expand-arrows-alt"); - getById("fullscreenPageToggle").classList.add("la-compress-arrows-alt"); - } else { - getById("fullscreenPageToggle").classList.add("la-expand-arrows-alt"); - getById("fullscreenPageToggle").classList.remove("la-compress-arrows-alt"); - - } - updateMixer(); - }); - - if (urlParams.has('fullscreenbutton') || urlParams.has('fsb')){ // just an alternative; might be compoundable - if (!(iOS || iPad)){ - session.fullscreenButton = true; - document.documentElement.style.setProperty('--full-screen-button', 'none'); - getById("fullscreenPage").classList.remove("hidden"); - } - } - - if (urlParams.has('pip2') || urlParams.has('pipall')){ // just an alternative; might be compoundable - if (typeof documentPictureInPicture !== "undefined"){ - getById("PictureInPicturePage").classList.remove("hidden"); - } - } - - if (urlParams.has('midi') || urlParams.has('hotkeys')) { - session.midiHotkeys = urlParams.get('midi') || urlParams.get ('hotkeys') || 1; - session.midiHotkeys = parseInt(session.midiHotkeys); - } - - if (urlParams.has('disablehotkeys')){ - session.disableHotKeys = true; - } - - if (urlParams.has('nohangupbutton') || urlParams.has('nohub')){ - getById("hangupbutton").style.display = "none"; - session.hangupbutton = false; - } - - if (urlParams.has('hangupbutton') || urlParams.has('hub') || urlParams.has('humb64')){ - session.hangupbutton = true; - } - if (urlParams.has('hangupmessage') || urlParams.has('hum') || urlParams.has('humb64')){ - let htmlmessage = urlParams.get("hangupmessage") || urlParams.get("hum") || urlParams.get('humb64'); - - if (urlParams.get('humb64')){ - try { - htmlmessage = atob(htmlmessage); - } catch(e){} - } - - try { - htmlmessage = htmlmessage.replace(/(\r\n|\n|\r)/gm, ''); - htmlmessage = decodeURIComponent(htmlmessage); - } catch(e){console.error(e);} - getById("hangupContainer").innerHTML = htmlmessage; - - - } - - if (urlParams.has('socialstream')){ - session.socialstream = urlParams.get('socialstream') || false; - } - - if (urlParams.has('midioffset')){ - session.midiOffset = urlParams.get('midioffset') || 0; - session.midiOffset = parseInt(session.midiOffset); - } - - if (urlParams.has('midiremote') || urlParams.has('remotemidi')){ - if (session.director!==false){ - session.midiRemote = parseInt(urlParams.get('midiremote')) || parseInt(urlParams.get ('remotemidi')) || 4; - } else { - session.midiRemote = parseInt(urlParams.get('midiremote')) || parseInt(urlParams.get ('remotemidi')) || 1; - } - } - - if (urlParams.has('midipush') || urlParams.has('midiout') || urlParams.has('mo')){ - session.midiOut = parseInt(urlParams.get('midipush')) || parseInt(urlParams.get('midiout')) || parseInt(urlParams.get('mo')) || true; - } - - if (urlParams.has('midipull') || urlParams.has('midiin') || urlParams.has('midin') || urlParams.has('mi')){ - session.midiIn = parseInt(urlParams.get('midipull')) || parseInt(urlParams.get('midiin')) || parseInt(urlParams.get('midin')) || parseInt(urlParams.get('mi')) || true; - } - - if (urlParams.has('mididelay')){ // midi-in delay - session.midiDelay = parseInt(urlParams.get('mididelay')) || 1000; // 1 second playout delay? acts as a buffer as well I guess. - } - - if (urlParams.has('midichannel')){ - session.midiChannel = parseInt(urlParams.get('midichannel')) || false; - } - if (session.midiChannel){ - session.midiChannel = parseInt(session.midiChannel); - if (session.midiChannel>16){session.midiChannel=false;} - if (session.midiChannel<1){session.midiChannel=false;} - } - if (urlParams.has('mididevice')){ - session.midiDevice = parseInt(urlParams.get('mididevice')) || false; - } - if (session.midiDevice){ - session.midiDevice = parseInt(session.midiDevice); - } - - if (directorLanding) { - getById("container-1").classList.remove('hidden'); - getById("container-1").classList.add("skip-animation"); - getById("container-1").classList.remove('pointer'); - } else if (session.director){ - // if a director, webcam/screenshare/iframe auto-defaults shouldn't work - } else if (urlParams.has('webcam') || urlParams.has('wc') || urlParams.has('miconly')) { - session.webcamonly = true; - session.screensharebutton = false; - if (urlParams.has('miconly')){ - session.videoDevice=0; - session.miconly = true; - miniTranslate(getById("add_camera"), "share-your-mic", "Share your Microphone"); - getById("container-3").title = getById("add_camera").innerText; - - getById("videoMenu").style.display = "none"; - getById("container-3").classList.add("microphoneBackground"); - getById("flipcamerabutton").style.setProperty("display", "none", "important"); - getById("mutevideobutton").style.setProperty("display", "none", "important"); - getById("videoMenu3").style.setProperty("display", "none", "important"); - getById("previewWebcam").classList.add("miconly"); - //if (session.consent){ - // setTimeout(function(){ - // warnUser("⚠ Privacy warning: The director of this room can remotely switch your camera or microphone without permission.", 8000); - // }, 1500); - //} - } - } else if (urlParams.has('screenshare') || urlParams.has('ss')) { - session.screenshare = true; - if (urlParams.get('screenshare') || urlParams.get('ss')){ - session.screenshare = urlParams.get('screenshare') || urlParams.get('ss'); - } - } else if (urlParams.has('fileshare') || urlParams.has('fs')) { - getById("container-5").classList.remove('hidden'); - getById("container-5").classList.add("skip-animation"); - getById("container-5").classList.remove('pointer'); - - getById("sharefilebutton").style.display = "flex"; // this might be obsolete? - getById("mediafileshare").classList.remove("hidden"); - - - if (SafariVersion){ - getById("safari_warning_fileshare").classList.remove('hidden'); - } else if (!Firefox){ - getById("chrome_warning_fileshare").classList.remove('hidden'); - } - - } else if (!session.director && (urlParams.has('website') || urlParams.has('iframe'))){ - getById("container-6").classList.remove('hidden'); - getById("container-6").classList.add("skip-animation"); - getById("container-6").classList.remove('pointer'); - session.website = urlParams.get('website') || urlParams.get('iframe') || false; - if (session.website){ - session.website = decodeURI(session.website); - delayedStartupFuncs.push([session.publishIFrame, session.website]); - } - } else if (urlParams.has('webcam2') || urlParams.has('wc2')) { - session.webcamonly = true; - session.screensharebutton = false; - session.introButton = true; - } else if (urlParams.has('screenshare2') || urlParams.has('ss2')) { - session.screenshare = true; - session.introButton = true; - if (urlParams.get('screenshare2') || urlParams.get('ss2')){ - session.screenshare = urlParams.get('screenshare2') || urlParams.get('ss2'); - } - } - - if (session.director && (urlParams.has('website') || urlParams.has('iframe'))){ - getById("container-6").classList.remove('hidden'); - getById("container-6").classList.add("skip-animation"); - getById("container-6").classList.remove('pointer'); - session.website = urlParams.get('website') || urlParams.get('iframe') || false; - if (session.website){ - session.website = decodeURI(session.website); - delayedStartupFuncs.push([shareWebsite, session.website]); - } - } - - if (urlParams.has('sstype') || urlParams.has('screensharetype')) { // wha type of screen sharing is used; track replace, iframe, or secondary try - session.screenshareType = urlParams.get('sstype') || urlParams.get('screensharetype'); - session.screenshareType = parseInt(session.screenshareType) || false; - } - - if (urlParams.has('suppresslocalaudio')){ - session.suppressLocalAudioPlayback = true; - } - if (urlParams.has('prefercurrenttab')){ - session.preferCurrentTab = true; - } - if (urlParams.has('selfbrowsersurface')){ // exclude - session.selfBrowserSurface = urlParams.get('selfbrowsersurface') || "exclude"; - } - if (urlParams.has('surfaceswitching')){ - session.surfaceSwitching = urlParams.get('surfaceswitching') || "exclude"; - } - if (urlParams.has('systemaudio')){ // exclude or exclude - session.systemAudio = urlParams.get('systemaudio') || "exclude"; - } - if (urlParams.has('displaysurface')){ // browser, window, or monitor (which is default selected) - session.displaySurface = urlParams.get('displaysurface') || "monitor"; - } - - if (urlParams.has('locksize')){ // browser, window, or monitor (which is default selected) - session.lockWindowSize = urlParams.get('locksize') || true; - } - - if (urlParams.has('intro') || urlParams.has('ib')) { - session.introButton = true; - } - - if (urlParams.has('volumecontrol') || urlParams.has('volumecontrols') || urlParams.has('vc')) { - if (!(iOS || iPad)){ - session.volumeControl = true; - } - } - if (urlParams.has('controlbarspace')){ - session.dedicatedControlBarSpace = true; - } else if (urlParams.has('nocontrolbarspace')){ - session.dedicatedControlBarSpace = false; - } - - if (urlParams.has('hidesolo') || urlParams.has('hs')){ - session.hidesololinks=true; - } - - if (urlParams.has('mute') || urlParams.has('muted') || urlParams.has('m')) { - session.muted = true; - } - - if (urlParams.has('hideguest') || urlParams.has('hidden')) { - session.directorVideoMuted = true; - } - - if (urlParams.has('videomute') || urlParams.has('videomuted') || urlParams.has('vm')) { - session.videoMutedFlag = true; - } - - if (urlParams.has('layout')) { - session.accept_layouts = true; - try { - session.layout = JSON.parse(decodeURIComponent(urlParams.get('layout'))) || JSON.parse(urlParams.get('layout')) || {}; - } catch(e){ - try { - session.layout = JSON.parse(urlParams.get('layout')) || {}; - } catch(e){ - session.layout = {}; - } - } - console.warn("Warning: If using &layout with &broadcast, only the director's video will appear in the custom layout, which is likely not intended."); - } - - if (urlParams.has('layouts')) { // an ordered array of layouts, which can be used to switch between using the API layouts action. - // ie: ?layouts=[[{"x":0,"y":0,"w":100,"h":100,"slot":0}],[{"x":0,"y":0,"w":100,"h":100,"slot":1}],[{"x":0,"y":0,"w":100,"h":100,"slot":2}],[{"x":0,"y":0,"w":100,"h":100,"slot":3}],[{"x":0,"y":0,"w":50,"h":100,"c":false,"slot":0},{"x":50,"y":0,"w":50,"h":100,"c":false,"slot":1}],[{"x":0,"y":0,"w":100,"h":100,"z":0,"c":false,"slot":1},{"x":70,"y":70,"w":30,"h":30,"z":1,"c":true,"slot":0}],[{"x":0,"y":0,"w":50,"h":50,"c":true,"slot":0},{"x":50,"y":0,"w":50,"h":50,"c":true,"slot":1},{"x":0,"y":50,"w":50,"h":50,"c":true,"slot":2},{"x":50,"y":50,"w":50,"h":50,"c":true,"slot":3}],[{"x":0,"y":16.667,"w":66.667,"h":66.667,"c":true,"slot":0},{"x":66.667,"y":0,"w":33.333,"h":33.333,"c":true,"slot":1},{"x":66.667,"y":33.333,"w":33.333,"h":33.333,"c":true,"slot":2},{"x":66.667,"y":66.667,"w":33.333,"h":33.333,"c":true,"slot":3}]] - try { - session.layouts = JSON.parse(decodeURIComponent(urlParams.get('layouts'))) || JSON.parse(urlParams.get('layouts')) || {}; - } catch(e){ - try { - session.layouts = JSON.parse(urlParams.get('layouts')) || false; - } catch(e){ - session.layouts = false; - } - } - } - - /* if (session.layout && session.layouts && (typeof session.layout !== "object") && parseInt(session.layout) && (session.layout == parseInt(session.layout))){ - try { - session.layout = session.layouts[session.layout-1]; - } catch(e){ - session.layout= false; - } - } */ - - if (urlParams.has('deaf') || urlParams.has('deafen')) { - session.directorSpeakerMuted=true; // false == true in this case. - } - - if (urlParams.has('blind')) { - session.directorDisplayMuted=true; // false == true in this case. - } - - if (urlParams.has('blindall')) { - session.directorBlindButton=true; // false == true in this case. - } - if (session.directorBlindButton){ - getById("blindAllGuests").classList.remove("hidden"); - } - - if (urlParams.has('dpi') || urlParams.has('dpr') || urlParams.has('sharper') || urlParams.has('sharpen')) { - session.devicePixelRatio = urlParams.get('dpi') || urlParams.get('dpr') || 2.0; - session.devicePixelRatio = parseFloat(session.devicePixelRatio); - } //else if (window.devicePixelRatio && window.devicePixelRatio!==1){ - // session.devicePixelRatio = window.devicePixelRatio; // this annoys me to no end. - //} - - if (urlParams.has('speakermute') || urlParams.has('speakermuted') || urlParams.has('mutespeaker') || urlParams.has('sm') || urlParams.has('ms')) { - - var checkState = urlParams.get('speakermute') || urlParams.get('speakermuted') || urlParams.get('mutespeaker') || urlParams.get('sm') || urlParams.get('ms') || true; - - if ( checkState === "false") { - session.speakerMuted = false; - } else if (checkState === "0") { - session.speakerMuted = false; - } else if (checkState === "no") { - session.speakerMuted = false; - } else if (checkState === "off") { - session.speakerMuted = false; - } else { - session.speakerMuted = true; - } - - session.speakerMuted_default = session.speakerMuted; - - if (session.speakerMuted){ - getById("mutespeakertoggle").className = "las la-volume-mute toggleSize"; - //getById("mutespeakerbutton").className="hidden float2 red"; - getById("mutespeakerbutton").classList.add("red"); - getById("mutespeakerbutton").classList.add("float2"); - getById("mutespeakerbutton").classList.remove("float"); - - var sounds = document.getElementsByTagName("video"); - for (var i = 0; i < sounds.length; ++i) { - sounds[i].muted = session.speakerMuted; - } - } - } - - if (urlParams.has('chatbutton') || urlParams.has('chat') || urlParams.has('cb')) { - session.chatbutton = urlParams.get('chatbutton') || urlParams.get('chat') || urlParams.get('cb') || null; - if (session.chatbutton === "false") { - session.chatbutton = false; - } else if (session.chatbutton === "0") { - session.chatbutton = false; - } else if (session.chatbutton === "no") { - session.chatbutton = false; - } else if (session.chatbutton === "off") { - session.chatbutton = false; - } else { - session.chatbutton = true; - getById("chatbutton").classList.remove("hidden"); - } - } - - if (urlParams.has('app')){ // midi-in delay - session.screenshare = false; - getById("container-2").classList.add('hidden'); - getById("logoname").classList.add('hidden'); - getById("head1a").classList.remove('hidden'); - getById("main").classList.add('appmode'); - getById("jumptoroomButton").innerText = "Join Room"; - - if (getStorage("jumptoURL")){ - getById('joinbyURL').value = getStorage("jumptoURL"); - } - } - - if (session.screenshare !== false) { - if (session.introButton){ - getById("container-3").className = 'column columnfade hidden'; // Hide screen share - getById("head1").className = 'hidden'; - } else { - getById("container-3").className = 'column columnfade hidden'; // Hide webcam - getById("container-2").classList.add("skip-animation"); - getById("container-2").classList.remove('pointer'); - } - } - - if (urlParams.has('manual')) { - session.manual = true; - } - - if (urlParams.has('hands') || urlParams.has('hand')) { - session.raisehands = true; - } - - if (urlParams.has('portrait') || urlParams.has('916') || urlParams.has('vertical')) { // playback aspect ratio - session.aspectRatio = 1; // 9:16 (default of 0 is 16:9) - } else if (urlParams.has('square') || urlParams.has('11')) { - session.aspectRatio = 2; //1:1 ? - } else if (urlParams.has('43')) { - session.aspectRatio = 3; //1:1 ? - } - - if (urlParams.has('structure')) { - session.structure = true; - } - - - - if (urlParams.has('aspectratio') || urlParams.has('ar')) { // capture aspect ratio - session.forceAspectRatio = urlParams.get('aspectratio') || urlParams.get('ar') || false; - if (session.forceAspectRatio){ - if ((session.forceAspectRatio == 'portrait') || (session.forceAspectRatio == 'vertical')){ - session.forceAspectRatio = 9.0/16.0; - } else if (session.forceAspectRatio == 'landscape'){ - session.forceAspectRatio = 16.0/9.0; - } else if (session.forceAspectRatio == 'square'){ - session.forceAspectRatio = 1.0; - } else { - session.forceAspectRatio = parseFloat(session.forceAspectRatio) || false; - } - } - } - if (urlParams.has('screenshareaspectratio') || urlParams.has('ssar')) { // capture aspect ratio - session.forceScreenShareAspectRatio = urlParams.get('screenshareaspectratio') || urlParams.get('ssar') || 16.0/9.0; - if (session.forceScreenShareAspectRatio){ - if ((session.forceScreenShareAspectRatio == 'portrait') || (session.forceScreenShareAspectRatio == 'vertical')){ - session.forceScreenShareAspectRatio = 9.0/16.0; - } else if (session.forceScreenShareAspectRatio == 'landscape'){ - session.forceScreenShareAspectRatio = 16.0/9.0; - } else if (session.forceScreenShareAspectRatio == 'square'){ - session.forceScreenShareAspectRatio = 1.0; - } else { - session.forceScreenShareAspectRatio = parseFloat(session.forceScreenShareAspectRatio) || false; - } - } - } - - if (urlParams.has('crop')){ - var crop = parseFloat(urlParams.get('crop')) || 0; - if (crop>0){ - session.forceAspectRatio = 1.7777777778 * (crop/100); - } else if (crop<0){ - session.forceAspectRatio = 1.7777777778 / (crop/100); - } else { - session.forceAspectRatio = 1.3333333333; - } - } - - if (urlParams.has('cover')) { - session.cover = true; - document.documentElement.style.setProperty('--fit-style', 'cover'); - document.documentElement.style.setProperty('--myvideo-max-width', '100vw'); - document.documentElement.style.setProperty('--myvideo-width', '100vw'); - document.documentElement.style.setProperty('--myvideo-height', '100vh'); - } - - if (urlParams.has('record')) { - if (!(session.cleanOutput)) { - if (SafariVersion && !MediaRecorder) { - if (macOS){ - warnUser("Your browser may not support local media recording.\n\nTry Chrome instead if on macOS."); - } else { - warnUser("Your browser or device may not support local media recording.\n\nSafari sometimes allows the feature to be enabled via its experimental settings."); - } - } else if (SafariVersion){ - if (macOS){ - warnUser("It is recommended to use Chrome instead of Safari if doing local media recordings."); - } else { - warnUser("Local media recordings are an experimental feature on Apple devices.\n\nPlease at least test it out a few times first."); - } - } - } - session.recordLocal = urlParams.get('record'); - - if ((session.recordLocal==="false") || (session.recordLocal==="off")){ - session.record = false; - session.recordLocal = false; - } else if (session.recordLocal != parseInt(session.recordLocal)) { - session.recordLocal = 6000; - } else { - session.recordLocal = parseInt(session.recordLocal); - } - } - - if (session.record === false){ - getById("recordLocalbutton").classList.add("hidden"); - getById("recordLocalScreenbutton").classList.add("hidden"); - try{ - document.querySelectorAll('[data-action-type^="record"]').forEach(ele=>{ele.remove();delete ele;}); - document.querySelectorAll('[data-action="Record"]').forEach(ele=>{ele.parentNode.remove();delete ele.parentNode;}); - } catch(e){ - errorlog(e); - } - } - - if (urlParams.has('autorecord')) { - session.autorecord=true; - if (session.recordLocal===false){ - let bitautorec = urlParams.get('autorecord'); - if (bitautorec!==null){ - session.recordLocal = parseInt(bitautorec); - } else { - session.recordLocal = 6000; - } - } - } - if (urlParams.has('autorecordlocal')) { - session.autorecordlocal=true; - if (session.recordLocal===false){ - session.recordLocal = urlParams.get('autorecordlocal'); - if (session.recordLocal != parseInt(session.recordLocal)) { - session.recordLocal = 6000; - } else { - session.recordLocal = parseInt(session.recordLocal); - } - } - } - if (urlParams.has('autorecordremote')) { - session.autorecordremote=true; - if (session.recordLocal===false){ - session.recordLocal = urlParams.get('autorecordremote'); - if (session.recordLocal != parseInt(session.recordLocal)) { - session.recordLocal = 6000; - } else { - session.recordLocal = parseInt(session.recordLocal); - } - } - } - - if (urlParams.has('pcm')) { - session.pcm = true; - } - if (urlParams.has('recordcodec') || urlParams.has('rc')) { - session.recordingVideoCodec = urlParams.get('recordcodec') || urlParams.get('rc') || false; - } - - if (urlParams.has('bigbutton')) { - session.bigmutebutton = true; - getById("mutebutton").style.bottom = "100px"; - getById("mutebutton").style.padding = "100px"; - getById("mutebutton").style.position = "fixed"; - getById("mutetoggle").style.bottom = "20px"; - getById("mutetoggle").style.right = "0"; - getById("mutetoggle").style.top = "unset"; - - } - - if (urlParams.has('nosettings')){ - session.nosettings = true; - getById("settingsbutton").classList.add("hidden"); - } - - if (urlParams.has('publish')){ - session.publish = true; - getById("publishSettings").style.display = "block"; - } - - if (urlParams.has('nopush') || urlParams.has('noseed') || urlParams.has('viewonly') || urlParams.has('viewmode')) { // this is like a scene; Seeding is disabled. Can be used with &showall to show all videos on load - session.doNotSeed=true; - - if (session.scene===false){ - session.scene = null; // not a scene, but sorta. false vs null makes a difference here. - } - - session.dataMode = true; // thios will let us connect - // session.showall = true; // this can be used to SHOW the videos. (&showall) - } - - if (urlParams.has('scene') || urlParams.has('scn')) { - session.scene = urlParams.get('scene') || urlParams.get('scn') || 0; - if (typeof session.scene === "string"){ - session.scene = session.scene.replace(/[\W]+/g, "_"); - } else { - session.scene = (parseInt(session.scene) || 0) + ""; - } - } - - if (urlParams.has('solo')){ - if (session.scene===false){ - session.scene = "0"; - } - session.solo = true; - } - - if (session.scene!==false){ - session.disableWebAudio = true; - if (session.audioEffects===null){ - session.audioEffects = false; - } - session.audioMeterGuest = false; - } - - - if (urlParams.has('fakeuser')) { - log("ICE FILTER ENABLED"); - session.fakeUser = true; - session.dataMode = true; - session.autostart = true; - session.novideo = []; - session.noaudio = []; - session.noiframe = []; - session.cleanOutput=true; - } - - if (urlParams.has('datamode') || urlParams.has('dataonly')) { // this disables all media in/out. - session.dataMode = true; - } - - if (session.dataMode){ - - if (!(session.meshcast || (session.whipOutput!==false) || session.screenshare)){ - session.videoDevice = 0; - session.audioDevice = 0; - } - - getById("mainmenu").classList.add("hidden"); - //session.autohide = true; - //session.autostart = true; - //session.novideo = []; - //session.noaudio = []; - //session.noiframe = []; - //session.webcamonly = true; - } - - - - if (urlParams.has('autoadd')) { // the streams we want to view; if set, but let blank, we will request no streams to watch. - session.autoadd = urlParams.get('autoadd') || null; // this value can be comma seperated for multiple streams to pull - - if (session.autoadd == null) { - session.autoadd = false; - } - if (session.autoadd) { - session.autoadd = session.autoadd.split(","); - } - } - - //if (session.scene!=="1"){ // scene =0 and 1 should load instantly. - // session.hiddenSceneViewBitrate = 0; // By default this is ~ 400kbps, but if you have 10 scenes, i don't want to kill things. - //} - - if (urlParams.has('hiddenscenebitrate')) { - session.hiddenSceneViewBitrate = parseInt(urlParams.get('hiddenscenebitrate')) || 0; - } - - if (urlParams.has('preloadbitrate')) { - session.preloadbitrate = parseInt(urlParams.get('preloadbitrate')) || 0; // 1000 - } - - if (urlParams.has('rampuptime')) { - session.rampUpTime = parseInt(urlParams.get('rampuptime')) || 10000; - } - - if (urlParams.has('scenetype') || urlParams.has('type')) { - session.sceneType = parseInt(urlParams.get('scenetype')) || parseInt(urlParams.get('type')) || false; - } - - if (urlParams.has('mediasettings')) { - session.forceMediaSettings = true; - } - - if (urlParams.has('transcript') || urlParams.has('transcribe') || urlParams.has('trans')) { - session.transcript = urlParams.get('transcript') || urlParams.get('transcribe') || urlParams.get('trans') || "en-US"; - } - - - if (urlParams.has('cc') || urlParams.has('closedcaptions') || urlParams.has('captions')) { - session.closedCaptions = true; - } - - if (urlParams.has('css')){ - var cssURL = urlParams.get('css'); - cssURL = decodeURI(cssURL); - log(cssURL); - var cssStylesheet = document.createElement('link'); - cssStylesheet.rel = 'stylesheet'; - cssStylesheet.type = 'text/css'; - cssStylesheet.media = 'screen'; - cssStylesheet.href = cssURL; - document.getElementsByTagName('head')[0].appendChild(cssStylesheet); - - cssStylesheet.onload = function() { - getById("main").classList.remove('hidden'); - log("loaded remote style sheet"); - }; - - cssStylesheet.onerror = function() { - getById("main").classList.remove('hidden'); - errorlog("REMOTE STYLE SHEET HAD ERROR"); - }; - - } else { - getById("main").classList.remove('hidden'); - } - - if (urlParams.has('avatar')){ - var avatar = urlParams.get('avatar') || false; - if (avatar && (avatar=="default")){ - session.avatar = document.getElementById("defaultAvatar2"); - session.avatar.ready=false; - session.avatar.onload = () => { - session.avatar.ready = true; - getById("noAvatarSelected3").classList.remove("selected"); - getById("noAvatarSelected").classList.remove("selected"); - getById("defaultAvatar1").classList.add("selected"); - getById("defaultAvatar2").classList.add("selected"); - }; - if (session.avatar.complete){ - session.avatar.ready = true; - getById("noAvatarSelected3").classList.remove("selected"); - getById("noAvatarSelected").classList.remove("selected"); - getById("defaultAvatar1").classList.add("selected"); - getById("defaultAvatar2").classList.add("selected"); - } - } else if (avatar){ - try { - avatar = decodeURIComponent(avatar); - }catch(e){} - - session.avatar = getById("defaultAvatar2"); - session.avatar.ready = false; - session.avatar.onload = () => { - session.avatar.ready = true; - getById("noAvatarSelected3").classList.remove("selected"); - getById("noAvatarSelected").classList.remove("selected"); - getById("defaultAvatar1").classList.add("selected"); - getById("defaultAvatar2").classList.add("selected"); - }; - getById("defaultAvatar1").src = avatar; - getById("defaultAvatar2").src = avatar; - - } - getById("avatarDiv3").classList.remove("hidden"); - getById("avatarDiv").classList.remove("hidden"); - } - - if (urlParams.has('prompt') || urlParams.has('validate') || urlParams.has('approve')){ - session.promptAccess = true; - } - - if (urlParams.has('js')){ // ie: &js=https%3A%2F%2Fvdo.ninja%2Fexamples%2Ftestjs.js - console.warn("Third-party Javascript has been injected into the code. Security cannot be ensured."); - var jsURL = urlParams.get('js'); - jsURL = decodeURI(jsURL); - log(jsURL); - // type="text/javascript" crossorigin="anonymous" - var externalJavaascript = document.createElement('script'); - externalJavaascript.type = 'text/javascript'; - externalJavaascript.crossorigin = 'anonymous'; - externalJavaascript.src = jsURL; - externalJavaascript.onerror = function() { - warnlog("Third-party Javascript failed to load"); - }; - externalJavaascript.onload = function() { - log("Third-party Javascript loaded"); - }; - document.head.appendChild(externalJavaascript); - } - - if (urlParams.has("base64js") || urlParams.has("b64js") || urlParams.has("jsbase64") || urlParams.has("jsb64")) { - try { - var base64js = urlParams.get("base64js") || urlParams.get("b64js") || urlParams.get("jsbase64") || urlParams.get("jsb64"); - base64js = decodeURIComponent(atob(base64js)); // window.btoa(encodeURIComponent("alert('hi')")); // ?jsb64=YWxlcnQoJ2hpJyk7 - var externalJavaascript = document.createElement('script'); - externalJavaascript.type = 'text/javascript'; - externalJavaascript.crossorigin = 'anonymous'; - externalJavaascript.innerHTML = base64js; - externalJavaascript.onerror = function() { - errorlog("Third-party Javascript failed to load"); - }; - externalJavaascript.onload = function() { - log("Third-party Javascript loaded"); - }; - document.head.appendChild(externalJavaascript); - } catch(e){console.error(e);} - }; - - if (urlParams.has("base64css") || urlParams.has("b64css") || urlParams.has("cssbase64") || urlParams.has("cssb64")) { - try { - var base64Css = urlParams.get("base64css") || urlParams.get("b64css") || urlParams.get("cssbase64") || urlParams.get("cssb64"); - var css = decodeURIComponent(atob(base64Css)); // window.btoa(encodeURIComponent("#mainmenu{background-color: pink; ❤" )); - var cssStyleSheet = document.createElement("style"); - cssStyleSheet.innerText = css; - document.querySelector("head").appendChild(cssStyleSheet); - } catch(e){console.error(e);} - }; - - session.sitePassword = session.defaultPassword; - if (urlParams.has('password') || urlParams.has('pass') || urlParams.has('pw') || urlParams.has('p')) { - session.password = urlParams.get('password') || urlParams.get('pass') || urlParams.get('pw') || urlParams.get('p'); - - if (!session.password) { - window.focus(); - session.password = await promptAlt(getTranslation("enter-password"), true, true); - if (session.password){ - session.password = session.password.trim(); - } - } else if (session.password === "false") { - session.password = false; - session.defaultPassword = false; - } else if (session.password === "0") { - session.password = false; - session.defaultPassword = false; - } else if (session.password === "off") { - session.password = false; - session.defaultPassword = false; - } else { - try { - session.password = decodeURIComponent(session.password); // will be re-encoded in a moment. - } catch(e){errorlog(e);} - } - } else if (urlParams.has('nopassword') || urlParams.has('nopass') || urlParams.has('nopw') || urlParams.has('p0')) { - session.password = false; - session.defaultPassword = false; - } - - if (session.password) { - getById("passwordRoom").value = session.password; - session.password = sanitizePassword(session.password); - session.defaultPassword = false; - getById("addPasswordBasic").style.display = "none"; - } - - if (urlParams.has('salt') && urlParams.get('salt')){ - session.salt = urlParams.get('salt'); - } - - if (urlParams.has('showconnections')){ - session.showConnections = true; // shows remote guest connections as a stat - } - - if (urlParams.has('hash') || urlParams.has('crc') || urlParams.has('check')) { // could be brute forced in theory, so not as safe as just not using a hash check. - session.taintedSession = null; // waiting to see if valid or not. - var hash_input = urlParams.get('hash') || urlParams.get('crc') || urlParams.get('check'); - if (session.password === false) { - window.focus(); - session.password = await promptAlt(getTranslation("enter-password-2"), true, true); - session.password = sanitizePassword(session.password); - getById("passwordRoom").value = session.password; - session.defaultPassword = false; - } - - generateHash(session.password + session.salt, 6).then(function(hash) { // million to one error. - log("hash is " + hash); - if (hash.substring(0, 4) !== hash_input) { // hash crc checks are just first 4 characters. - generateHash(session.password + "obs.ninja", 6).then(function(hash2) { // million to one error. - log("hash2 is " + hash2); - if (hash2.substring(0, 4) !== hash_input) { // hash crc checks are just first 4 characters. - session.taintedSession = true; - if (!(session.cleanOutput)) { - miniTranslate(getById("request_info_prompt"),"password-incorrect"); - //getById("request_info_prompt").innerHTML = getTranslation("password-incorrect"); - getById("request_info_prompt").style.display = "block"; - getById("mainmenu").style.display = "none"; - getById("head1").style.display = "none"; - session.cleanOutput = true; - - } else { - getById("request_info_prompt").innerHTML = ""; - getById("request_info_prompt").style.display = "block"; - getById("mainmenu").style.display = "none"; - getById("head1").style.display = "none"; - } - } else { - session.taintedSession = false; - session.hash = hash; - } - }).catch(errorlog); - } else { - session.taintedSession = false; - session.hash = hash; - } - }).catch(errorlog); - } - - if (session.defaultPassword !== false) { - session.password = session.defaultPassword; // no user entered password; let's use the default password if its not disabled. - } - - if (urlParams.has('showlabels') || urlParams.has('showlabel') || urlParams.has('sl')) { - session.showlabels = urlParams.get('showlabels') || urlParams.get('showlabel') || urlParams.get('sl') || ""; - session.showlabels = sanitizeLabel(session.showlabels.replace(/[\W]+/g, "_").replace(/_+/g, '_')); - //session.style = 6; - - if (session.showlabels == "") { - session.labelstyle = false; - } else { - session.labelstyle = session.showlabels; - } - - session.showlabels = true; - } - - if (urlParams.has('sizelabel') || urlParams.has('labelsize') || urlParams.has('fontsize')) { - session.labelsize = urlParams.get('sizelabel') || urlParams.get('labelsize') || urlParams.get('fontsize') || 100; - session.labelsize = parseInt(session.labelsize); - } - - if (urlParams.has('label') || urlParams.has('l')) { - session.label = urlParams.get('label') || urlParams.get('l') || null; - var updateURLAsNeed = true; - if (session.label == null || session.label.length == 0) { - window.focus(); - session.label = await promptAlt(getTranslation("enter-display-name"), true); - } else { - var updateURLAsNeed = false; - try { - session.label = decodeURIComponent(session.label); - } catch(e){errorlog(e);} - session.label = session.label.replace(/_/g, " ") - } - if (session.label != null) { - session.label = sanitizeLabel(session.label); // alphanumeric was too strict. - document.title = session.label; // what the result is. - - if (updateURLAsNeed) { - var label = encodeURIComponent(session.label); - if (urlParams.has('l')) { - updateURL("l=" + label, true, false); - } else { - updateURL("label=" + label, true, false); - } - } - } - } else if (urlParams.has('defaultlabel') || urlParams.has('labelsuggestion') || urlParams.has('ls')) { - session.label = urlParams.get('defaultlabel') || urlParams.get('labelsuggestion') || urlParams.get('ls') || null; - var updateURLAsNeed = true; - window.focus(); - var label = await promptAlt(getTranslation("enter-display-name"), true); - if (label) { - session.label = sanitizeLabel(label); // alphanumeric was too strict. - } else { - session.label = sanitizeLabel(session.label); - updateURLAsNeed = false; - } - - document.title = session.label; // what the result is. - - if (updateURLAsNeed) { - var label = encodeURIComponent(session.label); - if (urlParams.has('l')) { - updateURL("l=" + label, true, false); - } else { - updateURL("label=" + label, true, false); - } - } - } - - - if (urlParams.has('transparent') || urlParams.has('transparency')) { // sets the window to be transparent - useful for IFRAMES? - session.transparent=true; - } - - if (session.transparent){ - getById("main").style.backgroundColor = "rgba(0,0,0,0)"; - document.documentElement.style.setProperty('--container-color', '#0000'); - document.documentElement.style.setProperty('--background-color', '#0000'); - document.documentElement.style.setProperty('--regular-margin', '0'); - document.documentElement.style.setProperty('--director-margin', '0 25px 0 0'); - document.documentElement.style.setProperty('--discord-grey-1a', '#0000'); - getById("directorLinksButton").style.color = "black"; - getById("main").style.overflow = "hidden"; - } - - if (urlParams.has('stereo') || urlParams.has('s') || urlParams.has('proaudio')) { // both peers need this enabled for HD stereo to be on. If just pub, you get no echo/noise cancellation. if just viewer, you get high bitrate mono - log("STEREO ENABLED"); - session.stereo = urlParams.get('stereo') || urlParams.get('s') || urlParams.get('proaudio'); - - if (session.stereo) { - session.stereo = session.stereo.toLowerCase(); - } - - //var supportedConstraints = navigator.mediaDevices.getSupportedConstraints(); - //supportedConstraints.channelCount; - - if (session.stereo === "false") { - session.stereo = 0; - session.audioInputChannels = 1; - } else if (session.stereo === "0") { - session.stereo = 0; - session.audioInputChannels = 1; - } else if (session.stereo === "no") { - session.stereo = 0; - session.audioInputChannels = 1; - } else if (session.stereo === "off") { - session.stereo = 0; - session.audioInputChannels = 1; - } else if (session.stereo === "1") { - session.stereo = 1; - } else if (session.stereo === "both") { - session.stereo = 1; - } else if (session.stereo === "3") { - session.stereo = 3; - } else if (session.stereo === "out") { - session.stereo = 3; - } else if (session.stereo === "mono") { - session.stereo = 3; - session.audiobitrate = 128; - } else if (session.stereo === "4") { - session.stereo = 4; - } else if (session.stereo === "multi") { - session.stereo = 4; - } else if (session.stereo === "2") { - session.stereo = 2; - } else if (session.stereo === "6") { - session.stereo = 6; - } else if (session.stereo === "in") { - session.stereo = 2; - } else { - session.stereo = 5; // guests; no stereo in, no high bitrate in, but otherwise like stereo=1 - } - - getById("whipoutstereo").classList.add("hidden"); - } - - if (urlParams.has('screensharestereo') || urlParams.has('sss') || urlParams.has('ssproaudio')) { // both peers need this enabled for HD stereo to be on. If just pub, you get no echo/noise cancellation. if just viewer, you get high bitrate mono - log("screenshare stereo ENABLED"); - session.screenshareStereo = urlParams.get('screensharestereo') || urlParams.get('sss') || urlParams.get('ssproaudio'); - - if (session.screenshareStereo) { - session.screenshareStereo = session.screenshareStereo.toLowerCase(); - } - - if (session.screenshareStereo === "false") { - session.screenshareStereo = 0; - } else if (session.screenshareStereo === "0") { - session.screenshareStereo = 0; - } else if (session.screenshareStereo === "no") { - session.screenshareStereo = 0; - } else if (session.screenshareStereo === "off") { - session.screenshareStereo = 0; - } else if (session.screenshareStereo === "1") { - session.screenshareStereo = 1; - } else if (session.screenshareStereo === "both") { - session.screenshareStereo = 1; - } else if (session.screenshareStereo === "3") { - session.screenshareStereo = 3; - } else if (session.screenshareStereo === "out") { - session.screenshareStereo = 3; - } else if (session.screenshareStereo === "mono") { - session.screenshareStereo = 3; - } else if (session.screenshareStereo === "4") { - session.screenshareStereo = 4; - } else if (session.screenshareStereo === "multi") { - session.screenshareStereo = 4; - } else if (session.screenshareStereo === "2") { - session.screenshareStereo = 2; - } else if (session.screenshareStereo === "in") { - session.screenshareStereo = 2; - } else { - session.screenshareStereo = 5; // guests; no stereo in, no high bitrate in, but otherwise like stereo=1 - } - } - - - // Deploy your own handshake server for free; see: https://github.com/steveseguin/websocket_server - if (urlParams.has('pie')){ // piesocket.com support is to be deprecated after dec/19/21, since piesocket is no longer a free service. - session.customWSS = urlParams.get('pie') || true; // If session.customWSS == true, then there is no need to set parameters via URL - session.wssSetViaUrl = true; - if (session.customWSS && (session.customWSS!==true)){ - session.wss = "wss://free3.piesocket.com/v3/1?api_key="+session.customWSS; // if URL param is set, it will use the API key. - } - } - - - - if (Firefox && !session.stereo || (session.stereo === 3)){ - session.mono = true; // this will set the SDP to mono if firefox - } - - if (urlParams.has('mono')) { - session.mono = true; - if ((session.stereo == 1) || (session.stereo == 4)) { - session.stereo = 3; - session.audiobitrate = 128; - } else if (session.stereo == 5) { - session.stereo = 3; // stereo out only - session.audiobitrate = 128; - } else if (session.stereo == 2) { - session.stereo = 0; - session.audiobitrate = 128; - } - } - - if ((session.stereo == 1) || (session.stereo == 3) || (session.stereo == 4) || (session.stereo == 5)) { - session.echoCancellation = false; - session.autoGainControl = false; - session.noiseSuppression = false; - } - - if (urlParams.has("channelcount") || urlParams.has("ac") || urlParams.has("inputchannels")) { // if updates to this, see also function toggleMonoStereoMic() - session.audioInputChannels = urlParams.get('channelcount') || urlParams.get('ac') || urlParams.get('inputchannels') || 0; - session.audioInputChannels = parseInt(session.audioInputChannels); - if (!session.audioInputChannels) { - session.audioInputChannels = false; - } - } else if (urlParams.has("monomic")){ - session.audioInputChannels = 1; - } - - if ((session.stereo === 5) && !session.audioInputChannels){ // allow the guest to set their mic to mono. - document.querySelectorAll(".gear_microphone").forEach(ele=>{ - ele.classList.remove("hidden"); - }); - } - - - if (urlParams.has("echocancellation") || urlParams.has("aec") || urlParams.has("ec")) { - - session.echoCancellation = urlParams.get("echocancellation") || urlParams.get('aec') || urlParams.get('ec'); - - if (session.echoCancellation) { - session.echoCancellation = session.echoCancellation.toLowerCase(); - } - if (session.echoCancellation == "false") { - session.echoCancellation = false; - } else if (session.echoCancellation == "0") { - session.echoCancellation = false; - } else if (session.echoCancellation == "no") { - session.echoCancellation = false; - } else if (session.echoCancellation == "off") { - session.echoCancellation = false; - } else { - session.echoCancellation = true; - } - } - - - if (urlParams.has("autogain") || urlParams.has("ag") || urlParams.has("agc")) { - - session.autoGainControl = urlParams.get('autogain') || urlParams.get('ag') || urlParams.get('agc'); - if (session.autoGainControl) { - session.autoGainControl = session.autoGainControl.toLowerCase(); - } - if (session.autoGainControl == "false") { - session.autoGainControl = false; - } else if (session.autoGainControl == "0") { - session.autoGainControl = false; - } else if (session.autoGainControl == "no") { - session.autoGainControl = false; - } else if (session.autoGainControl == "off") { - session.autoGainControl = false; - } else { - session.autoGainControl = true; - } - } - - if (urlParams.has("denoise") || urlParams.has("dn")) { - - session.noiseSuppression = urlParams.get('denoise') || urlParams.get('dn'); - - if (session.noiseSuppression) { - session.noiseSuppression = session.noiseSuppression.toLowerCase(); - } - if (session.noiseSuppression == "false") { - session.noiseSuppression = false; - } else if (session.noiseSuppression == "0") { - session.noiseSuppression = false; - } else if (session.noiseSuppression == "no") { - session.noiseSuppression = false; - } else if (session.noiseSuppression == "off") { - session.noiseSuppression = false; - } else { - session.noiseSuppression = true; - } - } - - if (session.noiseSuppression!==null){ - getById("whipoutdenoise").classList.add("hidden"); - } - if (session.autoGainControl!==null){ // should be the last - getById("whipoutautogain").classList.add("hidden"); - } - - if (urlParams.has("screenshareaec") || urlParams.has("ssec") || urlParams.has("ssaec")) { - - session.screenshareAEC = urlParams.get('screenshareaec') || urlParams.get('ssec') || urlParams.get("ssaec"); - - if (session.screenshareAEC) { - session.screenshareAEC = session.screenshareAEC.toLowerCase(); - } - if (session.screenshareAEC == "false") { - session.screenshareAEC = false; - } else if (session.screenshareAEC == "0") { - session.screenshareAEC = false; - } else if (session.screenshareAEC == "no") { - session.screenshareAEC = false; - } else if (session.screenshareAEC == "off") { - session.screenshareAEC = false; - } else { - session.screenshareAEC = true; - } - } - if (urlParams.has("screenshareautogain") || urlParams.has("ssag") || urlParams.has("ssagc")) { - - session.screenshareAutogain = urlParams.get('screenshareautogain') || urlParams.get('ssag') || urlParams.get('ssagc'); - if (session.screenshareAutogain) { - session.screenshareAutogain = session.screenshareAutogain.toLowerCase(); - } - if (session.screenshareAutogain == "false") { - session.screenshareAutogain = false; - } else if (session.screenshareAutogain == "0") { - session.screenshareAutogain = false; - } else if (session.screenshareAutogain == "no") { - session.screenshareAutogain = false; - } else if (session.screenshareAutogain == "off") { - session.screenshareAutogain = false; - } else { - session.screenshareAutogain = true; - } - } - if (urlParams.has("screensharedenoise") || urlParams.has("ssdn")) { - - session.screenshareDenoise = urlParams.get('screensharedenoise') || urlParams.get('ssdn'); - - if (session.screenshareDenoise) { - session.screenshareDenoise = session.screenshareDenoise.toLowerCase(); - } - if (session.screenshareDenoise == "false") { - session.screenshareDenoise = false; - } else if (session.screenshareDenoise == "0") { - session.screenshareDenoise = false; - } else if (session.screenshareDenoise == "no") { - session.screenshareDenoise = false; - } else if (session.screenshareDenoise == "off") { - session.screenshareDenoise = false; - } else { - session.screenshareDenoise = true; - } - } - - - if (urlParams.has('roombitrate') || urlParams.has('roomvideobitrate') || urlParams.has('rbr')) { - log("Room BITRATE SET"); - session.roombitrate = urlParams.get('roombitrate') || urlParams.get('rbr') || urlParams.get('roomvideobitrate'); - session.roombitrate = parseInt(session.roombitrate); - if (session.roombitrate < 1) { - session.roombitrate = 0; - } - } - - if ( urlParams.has('outboundaudiobitrate') || urlParams.has('oab')) { - session.outboundAudioBitrate = parseInt(urlParams.get('outboundaudiobitrate')) || parseInt(urlParams.get('oab')) || false; - } - if (urlParams.has('outboundvideobitrate') || urlParams.has('outboundbitrate') || urlParams.has('ovb')) { - session.outboundVideoBitrate = parseInt(urlParams.get('outboundvideobitrate')) || parseInt(urlParams.get('outboundbitrate')) || parseInt(urlParams.get('ovb')) || false; - } - - if (urlParams.has('webp') || urlParams.has('images')){ // deprecicating this. chunked mode will replace it. - session.webp = urlParams.get('webp') || urlParams.get('images') || "webp"; - } - - if (urlParams.has('webpquality') || urlParams.has('webpq') || urlParams.has('wq')){ - session.webPquality = parseInt(urlParams.get('webpquality')) || parseInt(urlParams.get('webpq')) || parseInt(urlParams.get('wq')) || 4; - } - - - if (urlParams.has('audiobitrate') || urlParams.has('ab')) { // both peers need this enabled for HD stereo to be on. If just pub, you get no echo/noise cancellation. if just viewer, you get high bitrate mono - log("AUDIO BITRATE SET"); - session.audiobitrate = urlParams.get('audiobitrate') || urlParams.get('ab'); - session.audiobitrate = parseInt(session.audiobitrate); - if (session.audiobitrate < 1) { - session.audiobitrate = false; - } else if (session.audiobitrate > 510) { - session.audiobitrate = 510; - } // this is to just prevent abuse - } - if ((iOS) || (iPad)) { - session.audiobitrate = false; // iOS devices seem to get distortion with custom audio bitrates. Disable for now. - } - - /* if (urlParams.has('whitebalance') || urlParams.has('temp')){ // Need to be applied after the camera is selected. bleh. not enforcible. remove for now. - var temperature = urlParams.get('whitebalance') || urlParams.get('temp'); - try{ - updateCameraConstraints('colorTemperature', parseFloat(temperature)); - } catch (e){errorlog(e);} - } */ - - - - if (urlParams.has('streamid') || urlParams.has('view') || urlParams.has('v') || urlParams.has('pull')) { // the streams we want to view; if set, but let blank, we will request no streams to watch. - session.view = urlParams.get('streamid') || urlParams.get('view') || urlParams.get('v') || urlParams.get('pull') || null; // this value can be comma seperated for multiple streams to pull - - getById("headphonesDiv2").style.display = "inline-block"; - getById("headphonesDiv").style.display = "inline-block"; - getById("addPasswordBasic").style.display = "none"; - - - if (session.view == null) { - session.view = ""; - } - - /* if (session.view){ - if (urlParams.has('include') && urlParams.get('include')){ - session.view += ","+urlParams.get('include'); - } - } */ - if ((session.scene !== false) && (session.style === false) && window.obsstudio){ - session.style = 1; - } - } - - if (urlParams.has('fakeguests') || urlParams.has('fakefeeds')) { - var total = parseInt(urlParams.get('fakeguests')) || parseInt(urlParams.get('fakefeeds')) || 4; - session.fakeFeeds = []; - log("Creating "+total+" fake feeds"); - for (var i=0;i1){ - session.allowScreen.push(split[0]); - session.view_set.splice(i, 1); - if (!(split[0] in session.view_set)){ - session.view_set.push(split[0]); - } - } else if (split[0]){ - session.allowVideos.push(split[0]); - } else { - session.view_set.splice(i, 1); - } - } - } - - if (urlParams.has('include') && urlParams.get('include')){ - urlParams.get('include').split(",").forEach(sid =>{ - var sidd = sid.split(":s")[0]; - if (sidd && !session.include.includes(sidd)){ - session.include.push(sidd); - } - }); - - } - - if (urlParams.has('directorview') || urlParams.has('dv')){ - session.directorView = true; - } - if (urlParams.has('graphs')){ - session.allowGraphs = true; - } - - if (urlParams.has('ruler') || urlParams.has('grid') || urlParams.has('thirds')) { - session.fullscreen = true; - if (!session.manual){ - session.manual = false; - } - session.ruleOfThirds = urlParams.get('ruler') || urlParams.get('grid') || urlParams.get('thirds') || "./media/thirds.svg"; - session.ruleOfThirds = decodeURIComponent(session.ruleOfThirds); - } - - if (urlParams.has('smallshare')){ - session.notifyScreenShare = false; - } - - if (urlParams.has('proxy')) { // routes the wss traffic via an alternative network path. Not - session.proxy=true; // only works if session.wss is set to false - } else if (location.hostname === "proxy.vdo.ninja"){ - session.proxy=true; - } - - - if (urlParams.has('nopreview') || urlParams.has('np')) { - log("preview OFF"); - session.nopreview = true; - if ((iOS) || (iPad)) { - session.nopreview = false; - session.minipreview = 3; // - } - } else if ((urlParams.has('preview')) || (urlParams.has('showpreview'))) { - log("preview ON"); - session.nopreview = false; - } else if ((urlParams.has('minipreview')) || (urlParams.has('mini'))) { - var mini = urlParams.has('minipreview') || urlParams.has('mini') || true; // 2 is a valid option. (3 is for iPhone with a hidden preview) - log("preview ON"); - session.nopreview = false; - session.minipreview = mini; - } - - if (urlParams.has('minipreviewoffset') || urlParams.has('mpo')){ // 40 would be centered - session.leftMiniPreview = urlParams.get('minipreviewoffset') || urlParams.get('mpo') || 0; - session.leftMiniPreview = parseInt(session.leftMiniPreview) || 0; - if (session.leftMiniPreview<-20){ - session.leftMiniPreview = -20; - } else if (session.leftMiniPreview>120){ - session.leftMiniPreview = 120; - } - } - - - if (urlParams.has('obsfix')) { - session.obsfix = urlParams.get('obsfix'); - if (session.obsfix) { - session.obsfix = session.obsfix.toLowerCase(); - } - if (session.obsfix == "false") { - session.obsfix = false; - } else if (session.obsfix == "0") { - session.obsfix = false; - } else if (session.obsfix == "no") { - session.obsfix = false; - } else if (session.obsfix == "off") { - session.obsfix = false; - } else if (parseInt(session.obsfix) > 0) { - session.obsfix = parseInt(session.obsfix); - } else { - session.obsfix = 1; // aggressive. - } - } - - if (urlParams.has('controlroombitrate') || urlParams.has('crb')) { - session.controlRoomBitrate = true; - } - - if (urlParams.has('minroombitrate') || urlParams.has('mrb')) { - session.minimumRoomBitrate = urlParams.get('minroombitrate') || urlParams.get('mrb') || false; - session.minimumRoomBitrate = parseInt(session.minimumRoomBitrate) || false; - } - - if (urlParams.has('remote') || urlParams.has('rem')) { - log("remote ENABLED"); - session.remote = urlParams.get('remote') || urlParams.get('rem') || true; - } - - if (urlParams.has("slideshow")){ // stream labs mobile fix ? - var ssinterval = parseInt(urlParams.get("slideshow")) || 25; - ssinterval = 1000/ssinterval; - session.manual = true; - session.dynamicScale = false; - setInterval(function(){ - try { - slideshowHack(); - } catch(e){errorlog(e);} - },ssinterval); - } - - if (urlParams.has('latency') || urlParams.has('al') || urlParams.has('audiolatency')) { - log("latency ENABLED"); - session.audioLatency = urlParams.get('latency') || urlParams.get('al') || urlParams.get('audiolatency'); - session.audioLatency = parseInt(session.audioLatency) || 0; - session.disableWebAudio = false; - } - - if (urlParams.has('micdelay') || urlParams.has('delay') || urlParams.has('md')) { - log("audio gain ENABLED"); - session.micDelay = urlParams.get('micdelay') || urlParams.get('delay') || urlParams.get('md') || 0; - session.micDelay = parseInt(session.micDelay) || 0; - session.disableWebAudio = false; - } - - if (urlParams.has('tips')){ - getById("guestTips").style.display="flex"; - } - - if (urlParams.has('audiogain') || urlParams.has('gain') || urlParams.has('g')) { - log("audio gain ENABLED"); - session.audioGain = urlParams.get('audiogain') || urlParams.get('gain') || urlParams.get('g'); - session.audioGain = parseInt(session.audioGain) || 0; - session.disableWebAudio = false; - } - if (urlParams.has('volume') || urlParams.has('vol') ) { // This sets the default volume for all new video playback elements; 0 to 100. - log("setting default volume for playback"); - session.volume = urlParams.get('volume') || urlParams.get('vol') || 100; - session.volume = parseInt(session.volume) || 0; - session.volume = session.volume/100; // 0 to 1.0 - } - if (urlParams.has('compressor') || urlParams.has('comp')) { - log("audio gain ENABLED"); - session.compressor = 1; - session.disableWebAudio = false; - } else if (urlParams.has('limiter')) { - log("audio gain ENABLED"); - session.compressor = 2; - session.disableWebAudio = false; - } - if ((urlParams.has('equalizer')) || (urlParams.has('eq'))) { - session.equalizer = true; - session.disableWebAudio = false; - } - if ((urlParams.has('lowcut')) || (urlParams.has('lc')) || (urlParams.has('higpass'))) { - session.lowcut = urlParams.get('lowcut') || urlParams.get('lc') || urlParams.get('higpass') || 100; - session.lowcut = parseInt(session.lowcut); - session.disableWebAudio = false; - } - - if (urlParams.has('pip')) { - session.pip = true; // togglePip - //session.manual=true; - //innerHTML = - } - if (urlParams.has('pip3') || urlParams.has('mypip') || urlParams.has('pipme')){ - session.pip3 = true; - } - - if (urlParams.has('keyframeinterval') || urlParams.has('keyframerate') || urlParams.has('keyframe') || urlParams.has('fki')) { - log("keyframeRate ENABLED"); - session.keyframeRate = parseInt(urlParams.get('keyframeinterval') || urlParams.get('keyframerate') || urlParams.get('keyframe') || urlParams.get('fki')) || 0; - } - - if (urlParams.has('obsoff') || urlParams.has('oo') || urlParams.has('disableobs')) { - log("OBS feedback disabled"); - session.disableOBS = true; - getById("obsState").style.setProperty("display", "none", "important"); - } - - if (urlParams.has('hidecodirectors') || urlParams.has('hidecodirector') || urlParams.has('hidedirector') || urlParams.has('hidedirectors') || urlParams.has('hd')){ - document.querySelector(':root').style.setProperty("--show-codirectors", "none", "important"); - session.hideDirector = true; - } - - if (urlParams.has('pptcontrols') || urlParams.has('slides') || urlParams.has('ppt') || urlParams.has('powerpoint')){ - session.pptControls = true; // shows powerpoint controls to remotely control a powerpoint slide. Requires additional remote setup. - } - - if (urlParams.has('obscontrols') || urlParams.has('remoteobs') || urlParams.has('obsremote') || urlParams.has('obs') || urlParams.has('controlobs')) { - session.obsControls = urlParams.get('obscontrols') || urlParams.get('remoteobs') || urlParams.get('obsremote') || urlParams.get('obs') || urlParams.get('controlobs'); - if (session.obsControls) { // whether to show the button or not; that's it. - session.obsControls = session.obsControls.toLowerCase(); - } - if (session.obsControls == "false") { - session.obsControls = false; - } else if (session.obsControls == "0") { - session.obsControls = false; - } else if (session.obsControls == "no") { - session.obsControls = false; - } else if (session.obsControls == "off") { - session.obsControls = false; - } else if (session.obsControls){ - session.obsControls = session.obsControls.toLowerCase(); - } else { - session.obsControls = true; - } - } - - if (urlParams.has('allowedscenes')){ - session.filterOBSscenes = urlParams.get('allowedscenes'); - if (session.filterOBSscenes){ - session.filterOBSscenes = session.filterOBSscenes.split(","); - } else { - session.filterOBSscenes = true; - } - } - - - if (urlParams.has('tallyoff') || urlParams.has('notally') || urlParams.has('disabletally') || urlParams.has('to')) { - log("Tally Light off"); - getById("obsState").style.setProperty("display", "none", "important"); - } else if (urlParams.has('tally')) { - session.tallyStyle = 1; - getById("obsState").classList.add("larger"); - } - - if (urlParams.has('automute') || urlParams.has('am')){ - session.automute = urlParams.get('automute') || true; - session.micIsolatedAutoMute = []; // default auto mutes - } - - if (window.obsstudio) { - session.disableWebAudio = true; // default true; might be useful to disable on slow or old computers? - session.audioMeterGuest = false; - if (session.audioEffects===null){ - session.audioEffects = false; - } - if (window.obsstudio.pluginVersion){ - if (macOS){ // if mac, no fix - //session.obsfix = false; - } else if (window.obsstudio.pluginVersion=="2.17.4"){ // if obs v27.2 beta, no fix - //session.obsfix = false; - } else { - var ver = window.obsstudio.pluginVersion.split("."); - if (ver.length >= 2){ - if (parseInt(ver[0])<=2){ - if (parseInt(ver[0])==2){ - if (parseInt(ver[1])<=16){ - session.obsfix = 15; - } - } else { - session.obsfix = 15; - } - } - } - } - } - try { - log("OBS VERSION:" + window.obsstudio.pluginVersion); - log("macOS: " + macOS); - log(window.obsstudio); - - if (!(urlParams.has('streamlabs'))) { - - var ver1 = window.obsstudio.pluginVersion.split("."); - - if (ver1.length == 3) { // Should be 3, but disabled3 - if ((ver1.length == 3) && (parseInt(ver1[0]) == 2) && (ChromiumVersion < 76) && (macOS)) { - updateURL("streamlabs"); - getById("main").innerHTML = "

    Update OBS Studio to v26.1.2 or newer; older versions and StreamLabs OBS are not supported on macOS.\ -
    download here: https://github.com/obsproject/obs-studio/releases\ -



    \ -

    Please use the Electron Capture app if there are further problems or if you wish to use StreamLabs on macOS still.

    \ -
    You can bypass this error message by refreshing, Clicking Here, or by adding &streamlabs to the URL, but it may still not actually work.\ - \ -
    Please report this problem to steve@seguin.email if you feel it is an error.\ -
    "; - } - } - } - - //if (navigator.userAgent.indexOf('Mac OS X') != -1) { - // session.codec = "h264"; // default the codec to h264 if OBS is on macOS (that's all it supports with hardware) // oct 2021, OBS now supports vp8 and actually breaks with h264 android devices. - //} - - if (session.disableOBS===false){ - if (typeof document.visibilityState !== "undefined"){ - session.obsState.visibility = document.visibilityState==="visible"; - } - - getOBSDetails(); - - window.addEventListener("obsSourceVisibleChanged", obsSourceVisibleChanged); - window.addEventListener("obsSourceActiveChanged", obsSourceActiveChanged); - window.addEventListener("obsSceneChanged", obsSceneChanged); - window.addEventListener("obsStreamingStarted", obsStreamingStarted); - window.addEventListener("obsStreamingStopped", obsStreamingStopped); - window.addEventListener("obsRecordingStarted", obsRecordingStarted); - window.addEventListener("obsRecordingStopped", obsRecordingStopped); - window.addEventListener("obsVirtualcamStarted", obsVirtualcamStarted); - window.addEventListener("obsVirtualcamStopped", obsVirtualcamStopped); - } - - } catch (e) { - errorlog(e); - } - } - - if (urlParams.has('chroma')) { - log("Chroma ENABLED"); - getById("main").style.backgroundColor = "#" + (urlParams.get('chroma') || "0F0"); - //try { - // document.querySelector('meta[name="theme-color"]')?.setAttribute('content', "#" + (urlParams.get('chroma') || "0F0")); .. meh - //} catch(e){} - //const ogColor = document.querySelector('meta[name="theme-color"]')?.getAttribute('content'); - } // else if (window.obsstudio || (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1)){ - // getById("main").style.backgroundColor = "rgba(0,0,0,0)"; - //} - - //if (urlParams.has('bgimg')){ - // - // getById("main").style.background = "#" + (urlParams.get('chroma') || "0F0"); - //} - - if (urlParams.has('margin')) { - try { - session.videoMargin = urlParams.get('margin') || 10; - session.videoMargin = parseInt(session.videoMargin); - //document.querySelector(':root').style.setProperty('--video-margin', session.videoMargin+"px"); - } catch(e){errorlog("variable css failed");} - } - - if (urlParams.has('rounded') || urlParams.has('round')) { - try { - session.borderRadius = urlParams.get('rounded') || urlParams.get('round') || 50; - session.borderRadius = parseInt(session.borderRadius); - document.querySelector(':root').style.setProperty('--video-rounded', session.borderRadius+"px"); - } catch(e){errorlog("variable css failed");} - - } - - if (urlParams.has('border')) { - try { - var videoBorder = urlParams.get('border') || 10; - videoBorder = parseInt(videoBorder); - session.border = videoBorder; - videoBorder+="px"; - document.querySelector(':root').style.setProperty('--video-border-color', "#000"); - document.querySelector(':root').style.setProperty('--video-border', videoBorder); - - } catch(e){errorlog("variable css failed");} - - } - - if (urlParams.has('bordercolor')) { - try { - session.borderColor = urlParams.get('bordercolor') || "#000"; - document.querySelector(':root').style.setProperty('--video-border-color', session.borderColor); - } catch(e){errorlog("variable css failed");} - } - - if (urlParams.has('color')) { - session.colorVideosBackground = urlParams.get('color') || session.borderColor || "#000"; - } - - if (urlParams.has('retry')) { - session.forceRetry = parseInt(urlParams.get('retry')) || 30; - } - - if (session.forceRetry){ - clearInterval(session.forceRetryTimeout); - session.forceRetryTimeout = setTimeout(function(){ - try { - session.retryWatchInterval(); - } catch(e){ - log(e); - clearTimeout(this); - } - }, session.forceRetry*1000); - } - - session.dbx = false; - if (urlParams.get('dropbox')){ - loadScript("https://cdnjs.cloudflare.com/ajax/libs/dropbox.js/10.34.0/Dropbox-sdk.min.js", ()=>{ - log("Loaded dropbox SDK"); - var accessToken = urlParams.get('dropbox'); - session.dbx = new Dropbox.Dropbox({ accessToken: accessToken }); - }); - } - - try { - if (urlParams.has("darkmode") || urlParams.has("nightmode")){ - session.darkmode = urlParams.get("darkmode") || urlParams.get("nightmode") || null; - if ((session.darkmode===null) || (session.darkmode === "")){ - session.darkmode=true; - } else if ((darkmode=="false") || (darkmode == "0") || (darkmode == 0) || (darkmode == "off")){ - session.darkmode=false; - } - } else if (urlParams.has("lightmode") || urlParams.has("lightmode")){ - session.darkmode = false; - } else if (window.obsstudio){ - session.darkmode = false; // prevent OBS from defaulting to dark mode, avoiding possible overlooked bugs. - } else if (session.darkmode===null){ - session.darkmode = getComputedStyle(document.querySelector(':root')).getPropertyValue('--color-mode').trim(); - if (session.darkmode == "dark"){ - session.darkmode = true; - } else { - session.darkmode = false; - } - } - - if (session.darkmode){ - document.body.classList.add("darktheme"); - //document.querySelector(':root').style.setProperty('--background-color',"#02050c" ); - } else { - document.body.classList.remove("darktheme"); - //document.querySelector(':root').style.setProperty('--background-color',"#141926" ); // already set as default. - } - } catch(e){errorlog(e);} - - if (urlParams.has("videodevice") || urlParams.has("vdevice") || urlParams.has('vd') || urlParams.has('device') || urlParams.has('d') || urlParams.has('vdo')) { - - session.videoDevice = urlParams.get("videodevice") || urlParams.get("vdevice") || urlParams.get("vd") || urlParams.get("device") || urlParams.get("d") || urlParams.get("vdo"); - - if (session.videoDevice === null) { - session.videoDevice = "1"; - } else if (session.videoDevice) { - session.videoDevice = session.videoDevice.toLowerCase().replace(/[\W]+/g, "_"); - } - - if (session.videoDevice == "false") { - session.videoDevice = 0; - } else if (session.videoDevice == "0") { - session.videoDevice = 0; - } else if (session.videoDevice == "no") { - session.videoDevice = 0; - } else if (session.videoDevice == "off") { - session.videoDevice = 0; - } else if (session.videoDevice == "snapcam") { - session.videoDevice = "snap_camera"; - } else if (session.videoDevice == "canon") { - session.videoDevice = "eos"; - } else if (session.videoDevice == "camlink") { - session.videoDevice = "cam_link"; - } else if (session.videoDevice == "ndi") { - session.videoDevice = "newtek_ndi_video"; - } else if (session.videoDevice == "") { - session.videoDevice = 1; - } else if (session.videoDevice == "1") { - session.videoDevice = 1; - } else if (session.videoDevice == "default") { - session.videoDevice = 1; - } - - if (!urlParams.has('vdo')){ - getById("videoMenu").style.display = "none"; - } - log("session.videoDevice:" + session.videoDevice); - } - - - // audioDevice - if (urlParams.has('audiodevice') || urlParams.has('adevice') || urlParams.has('ad') || urlParams.has('device') || urlParams.has('d')) { - - session.audioDevice = urlParams.get("audiodevice") || urlParams.get("adevice") || urlParams.get("ad") || urlParams.get("device") || urlParams.get("d"); - - if (session.audioDevice === null) { - session.audioDevice = "1"; - } else if (session.audioDevice) { - session.audioDevice = session.audioDevice.toLowerCase().replace(/[^-,'A-Za-z0-9]+/g,"_"); - } - - if (session.audioDevice == "false") { - session.audioDevice = 0; - } else if (session.audioDevice == "0") { - session.audioDevice = 0; - } else if (session.audioDevice == "no") { - session.audioDevice = 0; - } else if (session.audioDevice == "off") { - session.audioDevice = 0; - } else if (session.audioDevice == "") { - session.audioDevice = 1; - } else if (session.audioDevice == "1") { - session.audioDevice = 1; - } else if (session.audioDevice == "default") { - session.audioDevice = 1; - } else if (session.audioDevice == "ndi") { - session.audioDevice = ["line_newtek_ndi_audio"]; - } else { - session.audioDevice = session.audioDevice.split(","); - } - getById("headphonesDiv").style.display = "none"; - getById("headphonesDiv2").style.display = "none"; - - if (typeof session.audioDevice !== "object"){ - getById("audioMenu").style.display = "none"; - getById("audioScreenShare1").style.display = "none"; - } - - if (session.audioDevice){ // 0 or false, do not triger - log("requestAudioStream..()"); - try { - await requestAudioStream(); - } catch(e){errorlog(e);} - } - } - - if (session.videoDevice === 0) { - getById("previewWebcam").classList.add("miconly"); - if (session.audioDevice === 0) { - miniTranslate(getById("add_camera"), "click-start-to-join", "Click Start to Join"); - getById("container-2").className = 'column columnfade hidden'; - getById("container-3").classList.add("skip-animation"); - getById("container-3").classList.remove('pointer'); - delayedStartupFuncs.push([previewWebcam]); - session.webcamonly = true; - } else { - miniTranslate(getById("add_camera"), "share-your-mic", "Share your Microphone"); - getById("container-3").classList.add("microphoneBackground"); - } - getById("container-3").title = getById("add_camera").innerText; - } - - if (session.mobile){ - getById("shareScreenGear").style.display = "none"; - getById("dropButton").style.display = "none"; - //getById("container-2").className = 'column columnfade hidden'; // Hide screen share on mobile - session.screensharebutton = false; - screensharesupport = false; - - if (session.audioDevice!==0){ - getById("flipcamerabutton").classList.remove("hidden"); - } - } - - if (urlParams.has('androidfix')){ - session.AndroidFix = true; - } - - - - if (urlParams.has('consent')){ - session.consent = true; - getById("consentWarning").classList.remove("hidden"); - getById("consentWarning2").classList.remove("hidden"); - } - - if (urlParams.has('autojoin') || urlParams.has('autostart') || urlParams.has('aj') || urlParams.has('as')) { - session.autostart = true; - } - - if (session.dataMode){ - delayedStartupFuncs.push([joinDataMode]); - } else if (session.autostart){ - if (session.screenshare!==false) { - delayedStartupFuncs.push([publishScreen]); - } - if (session.consent){ - setTimeout(function(){ - warnUser("⚠ Privacy warning: The director of this room can remotely switch your camera or microphone without permission.", 8000); - }, 1500); - } - } - - if (urlParams.has('noiframe') || urlParams.has('noiframes') || urlParams.has('nif') || urlParams.has('nowebsite') ) { - - session.noiframe = urlParams.get('noiframe') || urlParams.get('noiframes') || urlParams.get('nif') || urlParams.get('nowebsite'); - - if (!(session.noiframe)) { - session.noiframe = []; - } else { - session.noiframe = session.noiframe.split(","); - } - log("disable iframe playback"); - log(session.noiframe); - } - - - if (urlParams.has('exclude') || urlParams.has('ex')) { - - session.exclude = urlParams.get('exclude') || urlParams.get('ex'); - - if (!(session.exclude)) { - session.exclude = false; - } else { - session.exclude = session.exclude.split(","); - } - log("exclude video playback"); - log(session.exclude); - } - - if (urlParams.has('novideo') || urlParams.has('nv') || urlParams.has('hidevideo') || urlParams.has('showonly')) { - - session.novideo = urlParams.get('novideo') || urlParams.get('nv') || urlParams.get('hidevideo') || urlParams.get('showonly'); - - if (!(session.novideo)) { - session.novideo = []; - } else { - session.novideo = session.novideo.split(","); - } - log("disable video playback"); - log(session.novideo); - } - - if (urlParams.has('noaudio') || urlParams.has('na') || urlParams.has('hideaudio')) { - - session.noaudio = urlParams.get('noaudio') || urlParams.get('na') || urlParams.get('hideaudio'); - - if (!session.noaudio) { - session.noaudio = []; - } else { - session.noaudio = session.noaudio.split(","); - } - log("disable audio playback"); - } - - if (urlParams.has('forceios')) { - log("allow iOS to work in video group chat; for this user at least"); - session.forceios = true; - } - - if (urlParams.has('nocursor')) { // on the screen, not in screen share - session.nocursor = true; - log("DISABLE CURSOR"); - var styletmp = document.createElement('style'); - styletmp.innerHTML = ` - video { - margin: 0; - padding: 0; - overflow: hidden; - cursor: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=), none; - user-select: none; - } - `; - document.head.appendChild(styletmp); - } - - if (urlParams.has('cursor') || urlParams.has('screensharecursor')) { - session.screensharecursor = true; - } - - if (urlParams.has('distort')) { - session.voicechanger = 1; - } - - if (urlParams.has('dtx') || urlParams.has('usedtx')) { - session.dtx = true; - session.cbr = 0; // no point dtx on if cbr is on, right? - } - - if (urlParams.has('youtube')) { // Set with a Youtube v3 clientID + "," + API key, then run YoutubeChatInterface(); - session.youtubeKey = urlParams.get('youtube') || ""; - //YoutubeChatInterface(); // queries Youtube for chat messages. Forwards them to the parent IFRAME only at the moment. - } - - if (urlParams.has('vbr')) { - session.cbr = 0; - getById("whipoutvbrcbr").classList.add("hidden"); - } else if (urlParams.has('cbr')) { - session.cbr = 1; - getById("whipoutvbrcbr").classList.add("hidden"); - } - - if (urlParams.has('order')) { - session.order = parseInt(urlParams.get('order')) || 1; - } - - if (urlParams.has('orderby')) { - session.orderby = urlParams.get('orderby') || "id"; // "label" also an option; the default is stream ID tho. - } - - if (urlParams.has('slot')) { - session.slot = parseInt(urlParams.get('slot')) || 0; - } - - if (urlParams.has('slots')) { - session.slots = parseInt(urlParams.get('slots')) || 4; - } - - if (urlParams.has('alpha')) { - session.alpha = true; - } - - if (urlParams.has('chunked')) { - session.chunked = parseInt(urlParams.get('chunked')) || 2500; // sender side; enables to allows. - // session.alpha = true; - } - if (urlParams.has('nochunk') || urlParams.has('nochunked')) { // viewer side - session.nochunk = true; - } - //if (urlParams.has('viewchunked') || urlParams.has('viewchunk') || urlParams.has('allowchunked') || urlParams.has('allowchunk')) { // viewer side - // session.forceChunked = true; - //} - - if (urlParams.has('token')) { - session.token = urlParams.get('token') || false; - // checkToken(); // this is sycnhonous - } - - if (urlParams.has('maindirectorpassword') || urlParams.has('maindirpass')) { - session.mainDirectorPassword = urlParams.get('maindirectorpassword') || urlParams.get('maindirpass') || false; - if (!session.mainDirectorPassword) { - window.focus(); - session.mainDirectorPassword = await promptAlt(getTranslation("director-password"), true, true); - if (session.mainDirectorPassword){ - session.mainDirectorPassword = session.mainDirectorPassword.trim(); - try { - session.mainDirectorPassword = decodeURIComponent(session.mainDirectorPassword); - } catch(e){errorlog(e);} - } - } - // registerToken(); - } - - - - if (urlParams.has('debug')){ - session.debug=true; - debugStart(); - } - - if (urlParams.has('group') || urlParams.has('groups')) { - session.group = urlParams.get('group') || urlParams.get('groups') || ""; - session.group = session.group.split(","); - } - - if (urlParams.has('groupview') || urlParams.has('viewgroup') || urlParams.has('gv')) { - session.groupView = urlParams.get('groupview') || urlParams.get('viewgroup') || urlParams.get('gv') || ""; - session.groupView = session.groupView.split(","); - } - - if (urlParams.has('groupaudio') || urlParams.has('ga')) { - session.groupAudio = true; - } - - if (urlParams.has('groupmode') || urlParams.has('gm')) { - session.allowNoGroup = true; - } - - if (urlParams.has('host')) { - session.roomhost = true; - } - - if (urlParams.has('sensors') || urlParams.has('sensor') || urlParams.has('gyro') || urlParams.has('gyros') || urlParams.has('accelerometer')) { - session.sensorData = urlParams.get('sensors') || urlParams.get('sensor') || urlParams.get('gyro') || urlParams.get('gyros') || urlParams.get('accelerometer') || 30; - session.sensorData = parseInt(session.sensorData); - } - if (urlParams.has('sensorfilter') || urlParams.has('sensorsfilter') || urlParams.has('filtersensor') || urlParams.has('filtersensors')) { - session.sensorDataFilter = urlParams.get('sensorfilter') || urlParams.get('sensorsfilter') || urlParams.get('filtersensor') || urlParams.get('filtersensors') || ""; - session.sensorDataFilter = session.sensorDataFilter.split(","); // ["pos","lin","ori","mag","gyro","acc"]; - } - - if (urlParams.has('ptime')) { - session.ptime = parseInt(urlParams.get('ptime')) || 20; - if (session.ptime<10){ - session.ptime = 10; - } - } - - if (urlParams.has('minptime')) { - session.minptime = parseInt(urlParams.get('minptime')) || 10; - if (session.minptime < 10) { - session.minptime = 10; - } - if (session.minptime > 300) { - session.minptime = 300; - } - } - - if (urlParams.has('maxptime')) { - session.maxptime = parseInt(urlParams.get('maxptime')) || 60; - if (session.maxptime < 10) { - session.maxptime = 10; - } - if (session.maxptime > 300) { - session.maxptime = 300; - } - } - - if (urlParams.has('contenthint') || urlParams.has('contenttype') || urlParams.has('content') || urlParams.has('hint')) { - session.contentHint = urlParams.get('contenthint') || urlParams.get('contenttype') || urlParams.get('content') || urlParams.get('hint') || "detail"; - } - - if (urlParams.has('audiocontenthint') || urlParams.has('audiocontenttype') || urlParams.has('audiocontent') || urlParams.has('audiohint')) { - session.audioContentHint = urlParams.get('audiocontenthint') || urlParams.get('audiocontenttype') || urlParams.get('audiocontent') || urlParams.get('audiohint') || "music"; - } - - if (urlParams.has('screensharecontenthint') || urlParams.has('sscontenthint') || urlParams.has('screensharecontenttype') || urlParams.has('sscontent') || urlParams.has('sshint')) { - session.screenshareContentHint = urlParams.get('screensharecontenthint') || urlParams.get('sscontenthint') || urlParams.get('screensharecontenttype') || urlParams.get('sscontent') || urlParams.get('sshint') || "detail"; - } - - if (urlParams.has('codec') || urlParams.has('codecs') || urlParams.has('videocodec')) { - log("codecs CHANGED"); - session.codecs = urlParams.get('codec') || urlParams.get('codecs') || urlParams.get('videocodec') || false; - if (session.codecs){ - session.codecs = session.codecs.toLowerCase(); - session.codecs = session.codecs.split(","); - if (session.codecs.length){ - session.codec = session.codecs.shift(); - if (!session.codec){ - session.codec = false; - session.codecs = false; - } - if (!session.codecs.length){ - session.codecs = false; - } - } else { - session.codecs = false; - } - } - } else if (OperaGx){ - session.codec = "vp8"; - warnlog("Defaulting to VP8 manually, as H264 with remote iOS devices is not supported"); - } - - - - if (urlParams.has('audiocodec')) { - log("CODEC CHANGED"); - session.audioCodec = urlParams.get('audiocodec') || false; - if (session.audioCodec){ - session.audioCodec = session.audioCodec.toLowerCase(); - } - } - - if (urlParams.has('scenelinkcodec')){ // this is mainly for a niche iframe API use - log("codecGroupFlag CHANGED"); - session.codecGroupFlag = urlParams.get('scenelinkcodec') || false; - if (session.codecGroupFlag){ - session.codecGroupFlag = "&codec="+session.codecGroupFlag.toLowerCase(); - } - } - if (urlParams.has('scenelinkbitrate')){ // this is mainly for a niche iframe API use - log("bitrateGroupFlag CHANGED"); - session.bitrateGroupFlag = urlParams.get('scenelinkbitrate') || false; - if (session.bitrateGroupFlag){ - session.bitrateGroupFlag = "&totalbitrate="+parseInt(session.bitrateGroupFlag); - } - } - - - if (urlParams.has('h264profile')) { - session.h264profile = urlParams.get('h264profile') || "42e01f"; // 42001f - session.h264profile = session.h264profile.substring(0, 6); - session.h264profile = session.h264profile.toLowerCase(); - if (session.h264profile=="0"){ - session.h264profile = false; - } else if (session.h264profile=="off"){ - session.h264profile = false; - } else if (session.h264profile=="disabl"){ - session.h264profile = false; - } else if (session.h264profile=="defaul"){ - session.h264profile = false; - } else if (session.h264profile=="false"){ - session.h264profile = false; - } - } else if ((session.codec==="hardware") && Android){ // same as &h264profile, but easier for me to remember. I'll try to automate this in the future. - session.codec = "h264"; - session.h264profile = "42e01f"; - } - - if (urlParams.has('nofec')){ // disables error control / throttling -- currently on audio - session.noFEC = true; - } - if (urlParams.has('nonacks') || urlParams.has('nonack')){ // disables error control / throttling. - session.noNacks = true; - } - if (urlParams.has('nopli')){ // disables error control / throttling. - session.noPLIs = true; - } - if (urlParams.has('noremb')){ // disables Receiver Estimated Maximum Bitrate (throttling) - session.noREMB = true; - } - - if (urlParams.has('scale')) { - if (urlParams.get('scale') == "false") {} else if (urlParams.get('scale') == "0") {} else if (urlParams.get('scale') == "no") {} else if (urlParams.get('scale') == "off") {} else { - log("Resolution scale requested"); - session.scale = parseFloat(urlParams.get('scale')) || 100; - } - session.dynamicScale = false; // default true - } else { - if (urlParams.has('viewwidth') || urlParams.has('vw')) { - session.viewwidth = urlParams.get('viewwidth') || urlParams.get('vw') || false; - if (session.viewwidth){ - session.viewwidth = parseInt(session.viewwidth); - } - session.dynamicScale = false; // default true - } - if (urlParams.has('viewheight') || urlParams.has('vh')) { - session.viewheight = urlParams.get('viewheight') || urlParams.get('vh') || false; - session.dynamicScale = false; // default true - if (session.viewheight){ - session.viewheight = parseInt(session.viewheight); - } - } - } - - if (urlParams.has('sharperscreen')) { // sets scale to 100 for inbound screenshares only - session.sharperScreen = true; - } - - if (urlParams.has('mcscale') || urlParams.has('meshcastscale') || urlParams.has('woscale') || urlParams.has('whipoutscale')) { - session.whipOutScale = parseFloat(urlParams.get('mcscale')) || parseFloat(urlParams.get('meshcastscale')) || parseFloat(urlParams.get('woscale')) || parseFloat(urlParams.get('whipoutscale')) || 100; - } - - - if (isIFrame) { - getById("helpbutton").style.display = "none"; - getById("helpbutton").style.opacity = 0; - getById("reportbutton").style.display = "none"; - getById("reportbutton").style.opacity = 0; - getById("calendarButton").style.display = "none"; - getById("calendarButton").style.opacity = 0; - getById("chatBody").innerHTML = ""; - } - - if (urlParams.has('beep') || urlParams.has('notify') || urlParams.has('tone')) { - session.beepToNotify = true; - var addtone = createAudioElement(); - addtone.id = "jointone"; - addtone.src = "./media/join.mp3"; - getById("testtone").parentNode.insertBefore(addtone, getById("testtone").nextSibling) - var addtone = createAudioElement(); - addtone.id = "leavetone"; - addtone.src = "./media/leave.mp3"; - getById("testtone").parentNode.insertBefore(addtone, getById("testtone").nextSibling) - - if (!Notification) { - warnlog('Desktop notifications are not available in your browser.'); - } else if (Notification.permission !== 'granted') { - Notification.requestPermission(); - } - - } - if (urlParams.has('r2d2')) { - /* var addtone = createAudioElement(); - addtone.id = "jointone"; - addtone.src = "./media/join.mp3"; - getById("testtone").parentNode.insertBefore(addtone, getById("testtone").nextSibling) - var addtone = createAudioElement(); - addtone.id = "leavetone"; - addtone.src = "./media/leave.mp3"; - getById("testtone").parentNode.insertBefore(addtone, getById("testtone").nextSibling) */ - getById("testtone").innerHTML = ""; - getById("testtone").src = "./media/robot.mp3"; - session.beepToNotify = true; - } - - if (urlParams.has('easyexit') || urlParams.has('ee')) { - session.noExitPrompt = true; - } - - if (urlParams.has('entrymsg') || urlParams.has('welcome') || urlParams.has('welcomeb64')) { - session.welcomeMessage = urlParams.get('entrymsg') || urlParams.get('welcome') || urlParams.get('welcomeb64'); - - if (urlParams.get('welcomeb64')){ - try { - session.welcomeMessage = atob(session.welcomeMessage); - } catch(e){} - } - try { - session.welcomeMessage = session.welcomeMessage.replace(/(\r\n|\n|\r)/gm, ' '); - session.welcomeMessage = decodeURIComponent(session.welcomeMessage); - } catch(e){} - } - - if (urlParams.has('welcomehtml')) { - session.welcomeHTML = urlParams.get('welcomehtml'); - - try { - session.welcomeHTML = atob(session.welcomeHTML); - } catch(e){} - try { - session.welcomeHTML = session.welcomeHTML.replace(/(\r\n|\n|\r)/gm, ' '); - session.welcomeHTML = decodeURIComponent(session.welcomeHTML); - - } catch(e){} - } - - if (urlParams.has('welcomeimage') || urlParams.has('welcomeimg')) { - session.welcomeImage = urlParams.get('welcomeimage') || urlParams.get('welcomeimg'); - try { - session.welcomeImage = decodeURIComponent(session.welcomeImage); - } catch(e){} - } - - if (urlParams.has('mixminus')){ - session.mixMinus = true; - } - - if (urlParams.has('clearstorage') || urlParams.has('clear')){ - clearStorage(); - } - - if (urlParams.has('videobitrate') || urlParams.has('bitrate') || urlParams.has('vb')) { - session.bitrate = urlParams.get('videobitrate') || urlParams.get('bitrate') || urlParams.get('vb'); - if (session.bitrate) { - if ((session.view_set) && (session.bitrate.split(",").length > 1)) { - session.bitrate_set = session.bitrate.split(","); - session.bitrate = parseInt(session.bitrate_set[0]); - } else { - session.bitrate = parseInt(session.bitrate); - } - if (session.bitrate < 1) { - session.bitrate = false; - } - log("BITRATE ENABLED"); - log(session.bitrate); - - } - } - - if (urlParams.has('maxvideobitrate') || urlParams.has('maxbitrate') || urlParams.has('mvb')) { - session.maxvideobitrate = urlParams.get('maxvideobitrate') || urlParams.get('maxbitrate') || urlParams.get('mvb'); - session.maxvideobitrate = parseInt(session.maxvideobitrate); - - if (session.maxvideobitrate < 1) { - session.maxvideobitrate = false; - } - log("maxvideobitrate ENABLED"); - log(session.maxvideobitrate); - } - - if (urlParams.has('totalroombitrate') || urlParams.has('totalroomvideobitrate') || urlParams.has('trb') || urlParams.has('totalbitrate') || urlParams.has('tb')) { - session.totalRoomBitrate = urlParams.get('totalroombitrate') || urlParams.get('totalroomvideobitrate') || urlParams.get('trb') || urlParams.get('totalbitrate') || urlParams.get('tb') || 0; - session.totalRoomBitrate = parseInt(session.totalRoomBitrate); - - if (session.totalRoomBitrate < 1) { - session.totalRoomBitrate = 0; - } - log("totalRoomBitrate ENABLED"); - log(session.totalRoomBitrate); - } - - if (session.totalRoomBitrate===false){ - session.totalRoomBitrate = session.bitrate || session.totalRoomBitrate_default; // sneaky sneaky - } else { - session.totalRoomBitrate_default = session.totalRoomBitrate; // trb_default doesn't change dynamically, but trb can (per director I guess) - } - - if (session.totalRoomBitrate_default>4000){ - getById("trbSettingInput").max = Math.ceil(session.totalRoomBitrate_default); - } - - if (urlParams.has('maxtotalscenebitrate') || urlParams.has('totalscenebitrate') || urlParams.has('mtsb') || urlParams.has('tsb') || urlParams.has('totalbitrate') || urlParams.has('tb')) { - session.totalSceneBitrate = urlParams.get('maxtotalscenebitrate') || urlParams.get('totalscenebitrate') || urlParams.get('mtsb') || urlParams.get('tsb') || urlParams.get('totalbitrate') || urlParams.get('tb') || false; - if (session.totalSceneBitrate){ - session.totalSceneBitrate = parseInt(session.totalSceneBitrate); - } - } - - if (urlParams.has('blur')){ - session.blurBackground = urlParams.get('blur') || 10; - session.blurBackground = parseInt(session.blurBackground) || 10; - if (session.blurBackground<0){session.blurBackground=false;} - session.structure=true; - } - - if (urlParams.has('limittotalbitrate') || urlParams.has('ltb')){ - session.limitTotalBitrate = urlParams.get('limittotalbitrate') || urlParams.get('ltb') || 2500; - session.limitTotalBitrate = parseInt(session.limitTotalBitrate); - } - - if (urlParams.has('mcscreensharebitrate') || urlParams.has('mcssbitrate') || urlParams.has('whipoutscreensharebitrate') || urlParams.has('wossbitrate')){ - session.whipOutScreenShareBitrate = urlParams.get('mcscreensharebitrate') || urlParams.get('mcssbitrate') || urlParams.get('whipoutscreensharebitrate') || urlParams.get('wossbitrate') || 2500; - session.whipOutScreenShareBitrate = parseInt(session.whipOutScreenShareBitrate); - } - - if (urlParams.has('mcscreensharecodec') || urlParams.has('mcsscodec') || urlParams.has('whipoutscreensharecodec') || urlParams.has('wosscodec')){ - session.whipOutScreenShareCodec = urlParams.get('mcscreensharecodec') || urlParams.get('mcsscodec') || urlParams.get('whipoutscreensharecodec') || urlParams.get('wosscodec') || false; - } - if (session.whipOutScreenShareCodec){ - session.whipOutScreenShareCodec = session.whipOutScreenShareCodec.toLowerCase(); - } - - if (urlParams.has('mccodec') || urlParams.has('meshcastcodec') || urlParams.has('whipoutcodec') || urlParams.has('woc')){ - session.whipOutCodec = urlParams.get('mccodec') || urlParams.get('meshcastcodec') || urlParams.get('whipoutcodec') || urlParams.get('woc') || false; - getById("whipoutcodecGroupFlag").classList.add("hidden"); - } - - if (session.whipOutCodec){ - session.whipOutCodec = session.whipOutCodec.toLowerCase(); - if (session.whipOutCodec == "h264"){ - if (Firefox){ - session.whipOutCodec = false; - } - } - if (session.whipOutCodec){ - session.whipOutCodec = session.whipOutCodec.split(','); - } - getById("whipoutcodecGroupFlag").classList.add("hidden"); - } - - if (urlParams.has('mcab') || urlParams.has('mcaudiobitrate') || urlParams.has('meshcastab') || urlParams.has('meshcastaudiobitrate ') || urlParams.has('whipoutaudiobitrate') || urlParams.has('woab')){ - session.whipOutAudioBitrate = urlParams.get('mcab') || urlParams.get('mcaudiobitrate') || urlParams.get('meshcastab') || urlParams.get('meshcastaudiobitrate ') || urlParams.get('whipoutaudiobitrate') || urlParams.get('woab') || false; - if (session.whipOutAudioBitrate ){ - session.whipOutAudioBitrate = parseInt(session.whipOutAudioBitrate ); - } - getById("whipoutaudiobitrate").classList.add("hidden"); - } - - if (urlParams.has('mcb') || urlParams.has('mcbitrate') || urlParams.has('meshcastbitrate') || urlParams.has('whipoutvideobitrate') || urlParams.has('wovb')){ - session.whipOutVideoBitrate = urlParams.get('mcb') || urlParams.get('mcbitrate') || urlParams.get('meshcastbitrate') || urlParams.get('whipoutvideobitrate') || urlParams.get('wovb') || false; - if (session.whipOutVideoBitrate){ - session.whipOutVideoBitrate = parseInt(session.whipOutVideoBitrate); - } - getById("whipoutbitrateGroupFlag").classList.add("hidden"); - getById("whipoutvbrcbr").classList.add("hidden"); - } - - if (urlParams.has('height') || urlParams.has('h')) { - session.height = urlParams.get('height') || urlParams.get('h'); - session.height = parseInt(session.height); - } - - if (urlParams.has('width') || urlParams.has('w')) { - session.width = urlParams.get('width') || urlParams.get('w'); - session.width = parseInt(session.width); - } - - if (urlParams.has('quality') || urlParams.has('q')) { - try { - session.quality = urlParams.get('quality') || urlParams.get('q') || 0; - session.quality = parseInt(session.quality); - getById("gear_screen").parentNode.removeChild(getById("gear_screen")); - getById("gear_webcam").parentNode.removeChild(getById("gear_webcam")); - } catch (e) { - errorlog(e); - } - } - - if (urlParams.has('sink')) { - session.sink = urlParams.get('sink'); - } else if (urlParams.has('outputdevice') || urlParams.has('od') || urlParams.has('audiooutput')) { - session.outputDevice = urlParams.get('outputdevice') || urlParams.get('od') || urlParams.get('audiooutput') || null; - - if (session.outputDevice) { - session.outputDevice = session.outputDevice.toLowerCase().replace(/[\W]+/g, "_"); - } else { - session.outputDevice = null; - getById("headphonesDiv3").style.display = "none"; // - } - - if (session.outputDevice) { - try { - enumerateDevices().then(function(deviceInfos) { - for (let i = 0; i !== deviceInfos.length; ++i) { - if (deviceInfos[i].kind === 'audiooutput') { - if (deviceInfos[i].label.replace(/[\W]+/g, "_").toLowerCase().includes(session.outputDevice)) { - session.sink = deviceInfos[i].deviceId; - log("AUDIO OUT DEVICE: " + deviceInfos[i].deviceId); - break; - } - } - } - }); - } catch (e) {} - } - - getById("headphonesDiv").style.display = "none"; - getById("headphonesDiv2").style.display = "none"; - } else if (session.sink){ - if (session.sink == "default"){session.sink = false;} - else { - enumerateDevices().then(function(deviceInfos) { - var matched = false; - for (let i = 0; i !== deviceInfos.length; ++i) { - if (deviceInfos[i].kind === 'audiooutput') { - if (deviceInfos[i].deviceId == session.sink) { - matched = true; - break; - } - } - } - if (!matched){ - session.sink = false; // make sure any saved output device exists. - } - }); - } - } - - if (window.obsstudio || (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1)){ - session.fullscreen = true; - } else if (urlParams.has('fullscreen')) { - session.fullscreen = true; - } - - if (urlParams.has('stats')) { - if (urlParams.get('stats') == "0") { - session.statsMenu = false; - } else if (urlParams.get('stats') == "false") { - session.statsMenu = false; - } else if (urlParams.get('stats') == "off") { - session.statsMenu = false; - } else { - session.statsMenu = true; - } - } else if (urlParams.has('nostats')) { - session.statsMenu = false; - } - - if (session.statsMenu === false){ // hide menu option - try { - document.queryselector('[data-action="ShowStats"]').parentNode.classList.add("hidden"); - } catch(e){} - } - - if (urlParams.has("statsinterval")){ - session.statsInterval = parseInt(urlParams.get("statsinterval")) || 3000; // milliseconds. interval of requesting stats of remote guests - } - - - if (urlParams.has('cleandirector') || urlParams.has('cdv')) { - session.cleanDirector = true; - } - - if (urlParams.has('hidetranslate')) { - getById("translateButton").style.display = "none"; - } - - if (session.cleanOutput){ - session.screensharebutton = false; - getById("translateButton").style.display = "none"; - getById("credits").style.display = "none"; - getById("header").style.display = "none"; - getById("controlButtons").classList.add("hidden"); - getById("helpbutton").style.display = "none"; - getById("helpbutton").style.opacity = 0; - getById("reportbutton").style.display = "none"; - getById("reportbutton").style.opacity = 0; - getById("calendarButton").style.display = "none"; - getById("calendarButton").style.opacity = 0; - document.documentElement.style.setProperty('--myvideo-background', '#0000'); - var styleTmp = document.createElement('style'); - styleTmp.innerHTML = ` - video { - background-image: none; - } - `; - document.head.appendChild(styleTmp); - } - getById("credits").innerHTML = "Version: " + session.version + " - " + getById("credits").innerHTML; - - if (urlParams.has('ssb') || urlParams.has('screensharebutton')) { - session.screensharebutton = true; - } - - if (urlParams.has('hideheader') || urlParams.has('noheader') || urlParams.has('hh')) { // needs to happen the room and permaid applications - getById("header").style.display = "none"; - getById("header").style.opacity = 0; - } else if (urlParams.has('showheader')) { // needs to happen the room and permaid applications - getById("header").style.display = "inherit"; - getById("header").style.opacity = 1; - } - - if (urlParams.has('minidirector')) { - try { - var cssStylesheet = document.createElement('link'); - cssStylesheet.rel = 'stylesheet'; - cssStylesheet.type = 'text/css'; - cssStylesheet.media = 'screen'; - cssStylesheet.href = 'minidirector.css'; - document.getElementsByTagName('head')[0].appendChild(cssStylesheet); - } catch (e) { - errorlog(e); - } - } - - - if (urlParams.has('postinterval')){ // interval to post snapimage images - session.postInterval = urlParams.get('postinterval') || session.postInterval; - session.postInterval = parseInt(session.postInterval) || 60; - if (session.postInterval<5){ - session.postInterval = 5; - } - } - if (urlParams.has('postimage')){ - var postURL = decodeURIComponent(urlParams.get('postimage')) || session.postURL; // default will post to https://temp.vdo.ninja/images/STREAMIDHERE.jpg - setInterval(function(postURL){ - try { - uploadImageSnapshot(postURL); - } catch(e){} - }, session.postInterval*1000 , postURL); - } - - if (urlParams.has('cleanish')) { - session.cleanish = true; - } - - - if (session.cleanish || !session.cleanOutput){ - if (session.obsControls){ - getById("obscontrolbutton").classList.remove("hidden"); - getById("controlButtons").classList.remove("hidden"); - } - } - - if (urlParams.has('channels')) { // must be loaded before channelOffset - session.audioChannels = parseInt(urlParams.get('channels')); // for audio output ; not input. see: &channelcount instead. - session.offsetChannel = 0; - log("max channels is 32; channels offset"); - session.audioEffects = true; - } - if (urlParams.has('channeloffset')) { - session.offsetChannel = parseInt(urlParams.get('channeloffset')); - log("max channels is 32; channels offset"); - session.audioEffects = true; - } - if (urlParams.get('playchannel')) { // must be loaded before channelOffset - session.playChannel = parseInt(urlParams.get('playchannel')); // for audio output ; not input. see: &channelcount instead. - session.audioEffects = true; - } - if (urlParams.has('enhance')) { - //if (parseInt(urlParams.get('enhance')>0){ - session.enhance = true; //parseInt(urlParams.get('enhance')); - //} - } - - if (urlParams.has('degrade')) { - session.degrade = urlParams.get('degrade') || true; // Firefox, and maybe Safari, supported I think. - // the possible values are maintain-framerate, maintain-resolution, or balanced. The default value is balanced - } - - if (urlParams.has('maxviewers') || urlParams.has('mv')) { - - session.maxviewers = urlParams.get('maxviewers') || urlParams.get('mv'); - if (session.maxviewers.length == 0) { - session.maxviewers = 1; - } else { - session.maxviewers = parseInt(session.maxviewers); - } - log("maxviewers set"); - } - - if (urlParams.has('maxpublishers') || urlParams.has('mp')) { - - session.maxpublishers = urlParams.get('maxpublishers') || urlParams.get('mp'); - if (session.maxpublishers.length == 0) { - session.maxpublishers = 1; - } else { - session.maxpublishers = parseInt(session.maxpublishers); - } - log("maxpublishers set"); - } - - if (urlParams.has('maxconnections') || urlParams.has('mc')) { - - session.maxconnections = urlParams.get('maxconnections') || urlParams.get('maxconnections'); - if (session.maxconnections.length == 0) { - session.maxconnections = 1; - } else { - session.maxconnections = parseInt(session.maxconnections); - } - - log("maxconnections set"); - } - - - if (urlParams.has('secure')) { - session.security = true; - if (!(session.cleanOutput)) { - delayedStartupFuncs.push([warnUser, "Enhanced Security Mode Enabled."]); - } - } - - if (urlParams.has('random') || urlParams.has('randomize')) { - session.randomize = true; - } - - if (urlParams.has('frameRate') || urlParams.has('fr') || urlParams.has('fps')) { - session.frameRate = urlParams.get('frameRate') || urlParams.get('fr') || urlParams.get('fps'); - session.frameRate = parseInt(session.frameRate); - log("frameRate Changed"); - log(session.frameRate); - } - - if (urlParams.has('tz')){ // being depreciated, but still works with meshcast (no longer turn) - session.tz = urlParams.get('tz'); - if ((session.tz === null) || (session.tz === "")){ - session.tz = false; - } else { - session.tz = parseInt(session.tz); - } - } - - if (urlParams.has('maxframerate') || urlParams.has('mfr') || urlParams.has('mfps')) { - session.maxframeRate = urlParams.get('maxframerate') || urlParams.get('mfr') || urlParams.get('mfps'); - session.maxframeRate = parseInt(session.maxframeRate); - log("max frameRate assigned"); - log(session.maxframeRate); - } - - if (urlParams.has('buffer') || urlParams.has('buffer2')) { // needs to be before sync - if ((ChromiumVersion > 50) && (ChromiumVersion< 78)){ - } else { - session.buffer = parseFloat(urlParams.get('buffer')) || parseFloat(urlParams.get('buffer2')) || 0; - log("buffer Changed: " + session.buffer); - } - if (urlParams.has('buffer2')){ - session.includeRTT = true; - } - } - - - if (urlParams.has('panning') || urlParams.has('pan')) { - session.panning = urlParams.get('panning') || urlParams.get('pan'); - if (session.panning===""){ - session.panning=true - } - session.audioEffects = true; - } - - if (urlParams.has('sync')) { - if ((ChromiumVersion > 50) && (ChromiumVersion< 78)){ - - } else { - session.sync = parseFloat(urlParams.get('sync')); - log("sync Changed; in milliseconds. If not set, defaults to auto."); - log(session.sync); - session.audioEffects = true; - if (session.buffer === false) { - session.buffer = 0; - } - } - } - - if (urlParams.has('nomirror')) { - session.nomirror = true; - } - - if (urlParams.has('mirror')) { - if (urlParams.get('mirror') == "3") { - getById("main").classList.add("mirror"); - } else if (urlParams.get('mirror') == "2") { - session.mirrored = 2; - } else if (urlParams.get('mirror') == "0") { - session.mirrored = 0; - } else if (urlParams.get('mirror') == "false") { - session.mirrored = 0; - } else if (urlParams.get('mirror') == "off") { - session.mirrored = 0; - } else { - session.mirrored = 1; - } - } - - if (urlParams.has('flip')) { - if (urlParams.get('flip') == "0") { - session.flipped = false; - } else if (urlParams.get('flip') == "false") { - session.flipped = false; - } else if (urlParams.get('flip') == "off") { - session.flipped = false; - } else { - session.flipped = true; - } - } - - if ((session.mirrored) && (session.flipped)) { - try { - log("Mirror all videos"); - var mirrorStyle = document.createElement('style'); - mirrorStyle.innerHTML = "video {transform: scaleX(-1) scaleY(-1); }"; - document.getElementsByTagName("head")[0].appendChild(mirrorStyle); - } catch (e) { - errorlog(e); - } - } else if (session.mirrored) { // mirror the video horizontally - try { - log("Mirror all videos"); - var mirrorStyle = document.createElement('style'); - mirrorStyle.innerHTML = "video {transform: scaleX(-1);}"; - document.getElementsByTagName("head")[0].appendChild(mirrorStyle); - } catch (e) { - errorlog(e); - } - } else if (session.flipped) { // mirror the video vertically - try { - log("Mirror all videos"); - var mirrorStyle = document.createElement('style'); - mirrorStyle.innerHTML = "video {transform: scaleY(-1);}"; - document.getElementsByTagName("head")[0].appendChild(mirrorStyle); - } catch (e) { - errorlog(e); - } - } - - - if (urlParams.has('icefilter')) { - log("ICE FILTER ENABLED"); - session.icefilter = urlParams.get('icefilter'); - } - - - - //if (!(ChromiumVersion>=57)){ - // getById("effectSelector").disabled=true; - // getById("effectSelector3").disabled=true; - // getById("effectSelector").title = "Effects are only support on Chromium-based browsers"; - // getById("effectSelector3").title = "Effects are only support on Chromium-based browsers"; - // var elementsTmp = document.querySelectorAll('[data-effectsNotice]'); - // for (let i = 0; i < elementsTmp.length; i++) { - // elementsTmp[i].style.display = "inline-block"; - // } - //} - - - if (urlParams.has('viewereffect') || urlParams.has('viewereffects') || urlParams.has('ve')) { - session.viewereffects = parseInt(urlParams.get('viewereffect')) || parseInt(urlParams.get('ve')) || false; - } - - if (urlParams.has('activespeaker') || urlParams.has('speakerview') || urlParams.has('sas')){ - session.activeSpeaker = urlParams.get('activespeaker') || urlParams.get('speakerview') || urlParams.get('sas') || 1; - session.activeSpeaker = parseInt(session.activeSpeaker); - session.style=6; - session.audioEffects = true; - //session.audioMeterGuest = true; - session.minipreview = 2; - if ((session.activeSpeaker==1) || (session.activeSpeaker==3)){ - session.animatedMoves = false; - } - session.fadein=true; - document.querySelector(':root').style.setProperty('--fadein-speed', 0.5); - setInterval(function(){activeSpeaker(false);},100); - - } else if (urlParams.has('noisegate') || urlParams.has('gating') || urlParams.has('gate') ||urlParams.has('ng')){ - session.quietOthers = urlParams.get('noisegate') || urlParams.get('gating') || urlParams.get('gate') || urlParams.get('ng') || 1; - session.quietOthers = parseInt(session.quietOthers); - - if (session.quietOthers == 1){ - session.quietOthers = false; - session.noisegate = true; - session.audioEffects = true; - //session.audioMeterGuest = true; - } else if (session.quietOthers == 4){ - session.quietOthers = 1; - session.audioEffects = true; - //session.audioMeterGuest = true; - setInterval(function(){activeSpeaker(false);},100); - } else if (!session.quietOthers){ - session.noisegate = false; - session.quietOthers = false; - } else { - session.audioEffects = true; - //session.audioMeterGuest = true; - setInterval(function(){activeSpeaker(false);},100); - } - } - - if (urlParams.has('noisegatesettings')){ - session.noisegateSettings = urlParams.get('noisegatesettings'); - session.noisegateSettings = session.noisegateSettings.split(","); - } - - if (urlParams.has('fadein')) { - session.fadein=true; - if (urlParams.get('fadein') || 0){ - try { - var fadeinspeed = parseInt(urlParams.get('fadein') || 0)/1000.0; - fadeinspeed+="s"; - document.querySelector(':root').style.setProperty('--fadein-speed', fadeinspeed); - } catch(e){errorlog("variable css failed");} - } else { - try { - var fadeinspeed = 0.5; - fadeinspeed+="s"; - document.querySelector(':root').style.setProperty('--fadein-speed', fadeinspeed); - } catch(e){errorlog("variable css failed");} - } - } - - - if (urlParams.has('widget')){ - session.widget = urlParams.get('widget') || false; - - if ((session.widget === "false") || (session.widget === "0") || (session.widget === "off")){ - session.noWidget=true; - session.widget = false; - } else if (session.widget){ - session.widget = decodeURI(session.widget) || false; - log(session.widget); - } - } - - if (urlParams.has('animated') || urlParams.has('animate')){ - session.animatedMoves = urlParams.get('animated') || urlParams.get('animate'); - if (session.animatedMoves === "false") { - session.animatedMoves = false; - } else if (session.animatedMoves === "0") { - session.animatedMoves = false; - } else if (session.animatedMoves === "no") { - session.animatedMoves = false; - } else if (session.animatedMoves === "off") { - session.animatedMoves = false; - } else { - session.animatedMoves = parseInt(session.animatedMoves) || 100; - } - if (session.animatedMoves>200){ - session.animatedMoves = 200; - } - } else if (session.mobile){ - session.animatedMoves=false; - } - - if (urlParams.has('meter') || urlParams.has('meterstyle')){ // same as also adding &style=3 - session.meterStyle = urlParams.get('meter') || urlParams.get('meterstyle') || 1; - session.meterStyle = parseInt(session.meterStyle); - if (session.meterStyle<4){ - session.style=3; // black canvas - } else { - session.style = -1; // no canvas - } - session.audioEffects = true; - } - - if (session.meterStyle==5){ - document.documentElement.style.setProperty('--video-background-image-size-talking', 'auto 35%'); - document.documentElement.style.setProperty('--video-background-image-size-screaming', 'auto 45%'); - } - - if (urlParams.has('directorchat') || urlParams.has('dc')){ - session.directorChat = true; - } - - - if (urlParams.has('style') || urlParams.has('st')) { - session.style = urlParams.get('style') || urlParams.get('st'); - if ((parseInt(session.style) === 0) || (session.style == "controls")) { // no audio only - session.style = 0; - } else if ((parseInt(session.style) == 1) || (session.style == "justvideo")) { // no audio only - session.style = 1; - } else if ((parseInt(session.style) == 2) || (session.style == "waveform")) { // audio waveform - session.style = 2; - session.audioEffects = true; ////!!!!!!! Do I want to enable the audioEffects myself? or do it here? - } else if ((parseInt(session.style) == 3) || (session.style == "volume")) { // audio meter ; see &meterstyle , where optios include default(false), 1, and 2. - session.style = 3; - session.audioEffects = true; - } else if (parseInt(session.style) == 4) { // black background - session.style = 4; - } else if (parseInt(session.style) == 5) { // random colored background - session.style = 5; - } else if (parseInt(session.style) == 7) { // shows video elements for all connections; even those without video/audio - session.style = parseInt(session.style); - session.showall = true; - } else if (parseInt(session.style)) { // 6 is the first letter of the name, surrounded with a colored circle - session.style = parseInt(session.style); - } else { - session.style = 1; - } - } - //if (session.style){ - // getById("toggleWaveformButton").classList.remove("hidden"); - //} - - if (urlParams.has('showall')){ // just an alternative; might be compoundable - session.showall = true; - } - - - - if (urlParams.has('samplerate') || urlParams.has('sr')) { - session.sampleRate = parseInt(urlParams.get('samplerate')) || parseInt(urlParams.get('samplerate')) || 48000; - if (session.audioCtx) { - session.audioCtx.close(); // close the default audio context. - } - session.audioCtx = new AudioContext({ // create a new audio context with a higher sample rate. - sampleRate: session.sampleRate - }); - session.audioEffects = true; - } - - - - // if (session.audioCodec === "lyra"){ // WIP. does not work - // try { - // var { default: Module } = await import('./thirdparty/lyra/webassembly_codec_wrapper.js'); - // await Module().then((module) => { - // console.log("Initialized codec's wasmModule."); - // session.lyraCodecModule = module; - // }).catch(e => { - // console.log(`Module() error: ${e.name} message: ${e.message}`); - // }); - // } catch(e){ - // errorlog(e); - // } - // if (session.lyraCodecModule){ - // console.log("Lyra module loaded"); - // session.micSampleRate = 16000; - // session.encodedInsertableStreams = true; - // } else { - // console.log("Lyra module failed to load"); - // } - // } - - if (urlParams.has("insertablestreams")){ - session.encodedInsertableStreams = true; - } - - if (urlParams.has('micsamplerate') || urlParams.has('msr')) { - session.micSampleRate = parseInt(urlParams.get('micsamplerate')) || parseInt(urlParams.get('msr')) || 48000; - } - - if (urlParams.has('micsamplesize')) { - session.micSampleSize = parseInt(urlParams.get('micsamplesize')) || 16; - } - - if (urlParams.has('noaudioprocessing') || urlParams.has('noap')) { - session.disableWebAudio = true; // default true; might be useful to disable on slow or old computers? - session.disableViewerWebAudioPipeline = true; // this has the potential to break things. - session.audioEffects = false; // disable audio inbound effects also. - session.audioMeterGuest = false; - if (session.noisegate===null){ - session.noisegate = false; - } - } - - // For info, see this: https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidatePairStats/availableOutgoingBitrate - if (urlParams.has('maxbandwidth')) { // limits the bitrate based on the outbound total available bandwidth; chromium-based - session.maxBandwidth = urlParams.get('maxbandwidth') || 80; // 0 to 100; will reduce bitrate as a percentage of available - session.maxBandwidth = parseInt(session.maxBandwidth); - if (session.maxBandwidth > 200){ // will over ride default 2500kbps if no bitrate is specified - session.maxBandwidth = 200; - } else if (session.maxBandwidth<0){ - session.maxBandwidth = 0; - } - } - - if (urlParams.has('iframetarget')) { - session.iframetarget = urlParams.get('iframetarget'); // speciifies the IFRAME Hostname target - if (session.iframetarget){ - session.iframetarget = decodeURIComponent(session.iframetarget); - } else { - session.iframetarget = window.location.hostname; - } - } - - - if (urlParams.has('sendframes')) { - session.sendframes = urlParams.get('sendframes'); - if (session.sendframes){ - try { - session.sendframes = decodeURIComponent(session.sendframes); - } catch(e){} - } else { - session.sendframes = session.iframetarget || "*"; - } - } - - if (urlParams.has('tcp')){ // forces the TURN servers to use TCP mode; still need to add &private to force TURN also tho - session.forceTcpMode = true; - } - - if (urlParams.has('stun')) { - var stunstring = urlParams.get('stun'); - stunstring = stunstring.split(";"); - if (stunstring[0] !== "false") { // false disables the TURN server. Useful for debuggin - var stun = {}; - if (stunstring.length==3){ - stun.username = stunstring[0]; // myusername - stun.credential = stunstring[1]; //mypassword - stun.urls = [stunstring[2]]; // ["turn:turn.obs.ninja:443"]; - } else if (stunstring.length==1){ - stun.urls = [stunstring[0]]; - } - session.stunServers = [stun]; - } else { - session.stunServers = []; - } - } - if (urlParams.has('addstun')) { - var stunstring = urlParams.get('addstun'); - stunstring = stunstring.split(";"); - var stun = {}; - if (stunstring.length==3){ - stun.username = stunstring[0]; // myusername - stun.credential = stunstring[1]; //mypassword - stun.urls = [stunstring[2]]; // ["turn:turn.obs.ninja:443"]; - } else if (stunstring.length==1){ - stun.urls = [stunstring[0]]; - } - session.stunServers = session.stunServers.concat(stun); - } - - if (urlParams.has('bundle')){ - session.bundlePolicy = urlParams.get('bundle') || "MaxBundle"; // default is browser default. - } - - if (urlParams.has('turn')) { - var turnstring = urlParams.get('turn'); - - if (turnstring == "twilio") { // a sample function on loading remote credentials for TURN servers. - try { - session.ws = false; // prevents connection - var twillioRequest = new XMLHttpRequest(); - twillioRequest.onload = function() { - if (this.status === 200) { - try{ - var res = JSON.parse(this.responseText); - } catch(e){ - console.error(e); - return; - } - session.configuration = { - iceServers: [{ - "username": res["1"], - "credential": res["2"], - "url": "turn:global.turn.twilio.com:3478?transport=tcp", - "urls": "turn:global.turn.twilio.com:3478?transport=tcp" - }, - { - "username": res["1"], - "credential": res["2"], - "url": "turn:global.turn.twilio.com:443?transport=tcp", - "urls": "turn:global.turn.twilio.com:443?transport=tcp" - } - ], - sdpSemantics: 'unified-plan' // future-proofing - }; - if (session.ws===false){ - session.ws=null; // allows connection (clears state) - session.connect(); // connect if not already connected. - } - } - // system does not connect if twilio API does not respond. - }; - twillioRequest.open('GET', 'https://turn.example.com:443/twilio', true); // `false` makes the request synchronous - twillioRequest.send(); - } catch (e) { - errorlog("Twilio Failed"); - } - - } else if (turnstring == "nostun") { // disable TURN servers - session.configuration = { - sdpSemantics: 'unified-plan' // future-proofing - }; - - } else if ((turnstring == "false") || (turnstring == "off") || (turnstring == "0")) { // disable TURN servers - session.configuration = { - iceServers: session.stunServers, - sdpSemantics: 'unified-plan' // future-proofing - }; - } else { - try { - //session.configuration = {iceServers: [], sdpSemantics: 'unified-plan'}; - turnstring = turnstring.split(";"); - if (turnstring !== "false") { // false disables the TURN server. Useful for debuggin - var turn = {}; - if (turnstring.length==3){ - turn.username = turnstring[0]; // myusername - turn.credential = turnstring[1]; //mypassword - turn.urls = [turnstring[2]]; // ["turn:turn.obs.ninja:443"]; - } else if (turnstring.length==1){ - turn.urls = [turnstring[0]]; - } - session.configuration = { - iceServers: session.stunServers, - sdpSemantics: 'unified-plan' // future-proofing - }; - - session.configuration.iceServers.push(turn); - } - } catch (e) { - if (!(session.cleanOutput)) { - warnUser("TURN server parameters were wrong."); - } - errorlog(e); - } - } - } - - if (urlParams.has('apiserver') && urlParams.get('apiserver')){ // must set this after any custom TURN / STUN settings, else it might over-ride them. - session.apiserver = urlParams.get('apiserver'); - } - - if (urlParams.has('speedtest')){ // must set this after any custom TURN / STUN settings, else it might over-ride them. - session.speedtest = true; - if (urlParams.get('speedtest')){ // forces essentially UDP mode, unless TCP is specified, and some other stuff - session.speedtest = urlParams.get('speedtest').toLowerCase(); // also limits bitrate - } - setupSpeedtest(); - } - - if (urlParams.has('privacy') || urlParams.has('private') || urlParams.has('relay')) { // please only use if you are also using your own TURN service. - session.privacy = urlParams.get('privacy') || urlParams.get('private') || urlParams.get('relay') || true; - - try { // I'll re-apply this in the setupSpeedtest() promise callback, just to be case. - if (session.configuration){ // this needs to set last, otherwise it might be overridden - session.configuration.iceTransportPolicy = "relay"; // https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate/address - } - } catch (e) { - if (!(session.cleanOutput)) { - warnUser("Privacy mode failed to configure."); - } - errorlog(e); - } - - if (session.speedtest){ - warnlog("Bitrate being throttled to max of 6000 kbps"); - if (session.maxvideobitrate !== false) { - if (session.maxvideobitrate > 6000) { - session.maxvideobitrate = 6000; // Please feel free to get rid of this if using your own TURN servers... - } - } else { - session.maxvideobitrate = 6000; // don't let people pull more than 6000 from you - } - if (session.bitrate !== false) { - if (session.bitrate > 6000) { - session.bitrate = 6000; // Please feel free to get rid of this if using your own TURN servers... - } - } - } else { - warnlog("Bitrate being throttled to max of 4000 kbps"); - if (session.maxvideobitrate !== false) { - if (session.maxvideobitrate > 4000) { - session.maxvideobitrate = 4000; // Please feel free to get rid of this if using your own TURN servers... - } - } else { - session.maxvideobitrate = 4000; // don't let people pull more than 4000 from you - } - if (session.bitrate !== false) { - if (session.bitrate > 4000) { - session.bitrate = 4000; // Please feel free to get rid of this if using your own TURN servers... - } - } - } - } - - if (urlParams.has('wss')) { - session.customWSS = true; - session.wssSetViaUrl = true; - if (urlParams.get('wss')) { - session.wss = urlParams.get('wss'); - if (!session.wss.startsWith("wss://")){ - session.wss = "wss://" + session.wss; - } - } - } else if (urlParams.has('wss2')) { - session.wssSetViaUrl = true; - if (urlParams.get('wss2')) { - session.wss = urlParams.get('wss2'); - if (!session.wss.startsWith("wss://")){ - session.wss = "wss://" + session.wss; - } - } - } - - if (urlParams.has("bypass")){ - session.bypass = true; - session.customWSS = true; - } - - if (urlParams.has('osc') || urlParams.has('api')) { - if (urlParams.get('osc') || urlParams.get('api')) { - session.api = urlParams.get('osc') || urlParams.get('api') || false; - if (session.api){ - setTimeout(function(){oscClient();},1000); - } - } - } - - if (urlParams.has('postapi') || urlParams.has('posturl')) { - session.postApi = urlParams.get('postapi') || urlParams.get('posturl') || false; // ie: &postapi=https%3A%2F%2Fwebhook.site%2Fb190f5bf-e4f8-454a-bd51-78b5807df9c1 - if (session.postApi){ - try { - session.postApi = decodeURI(session.postApi) || session.postApi ; // needs to be SSL enabled. - } catch(e){ - console.error(e); - } - } - } - - if (urlParams.has('queue')) { - session.queue = true; - if (urlParams.get('queue') === "false"){ - session.queue = false; - } else if (urlParams.get('queue') === "0"){ - session.queue = false; - } else if (urlParams.get('queue') === "off"){ - session.queue = false; - } - } - - - - if (urlParams.has('push') || urlParams.has('id') || urlParams.has('permaid') ) { - session.permaid = urlParams.get('push') || urlParams.get('id') || urlParams.get('permaid'); - - if (session.permaid) { - session.permaid = sanitizeStreamID(session.permaid) || null; - session.streamID = session.permaid || session.streamID; - } else if (urlParams.has('permaid') && getStorage("permaid")){ - session.streamID = sanitizeStreamID(getStorage("permaid")) || session.streamID; - session.permaid = null; - } else { - session.permaid = null; - } - - if (urlParams.has('permaid')){ - setStorage("permaid", session.streamID, 99999) - } - - if (urlParams.has('push')){ - updateURL("push="+session.streamID, true, false); - } else if (urlParams.has('id')){ - updateURL("id="+session.streamID, true, false); // not 'officially' supporting this yet; we'll see. - } else if (urlParams.has('permaid')){ - updateURL("permaid="+session.streamID, true, false); - } else { - updateURL("push="+session.streamID, true, false); - } - - if (session.director) { // if I do a short form of this, it will cause duplications in the code elsewhere. - //var director_room_input = urlParams.get('director'); - //director_room_input = sanitizeRoomName(director_room_input); - //createRoom(director_room_input); - session.permaid = false; // used to avoid a trigger later on. - } else { - getById("container-1").className = 'column columnfade hidden'; - getById("container-4").className = 'column columnfade hidden'; - getById("dropButton").className = 'column columnfade hidden'; - - getById("info").innerHTML = ""; - if (session.videoDevice === 0) { - miniTranslate(getById("add_camera"), "share-your-mic", "Share your Microphone"); - } else { - miniTranslate(getById("add_camera"), "share-your-camera", "Share your Camera"); - } - miniTranslate(getById("add_screen"), "share-your-screen", "Share your Screen"); - getById("container-2").title = getById("add_screen").innerText; - getById("container-3").title = getById("add_camera").innerText; - - getById("passwordRoom").value = ""; - getById("videoname1").value = ""; - getById("dirroomid").innerHTML = ""; - getById("roomid").innerHTML = ""; - - getById("mainmenu").style.alignSelf = "center"; - getById("mainmenu").classList.add("mainmenuclass"); - getById("header").style.alignSelf = "center"; - - //if ((iOS) || (iPad)) { - //getById("header").style.display = "none"; // just trying to free up space. - //} - - if (session.webcamonly == true) { // mobile or manual flag 'webcam' pflag set - getById("head1").innerHTML = '- Please accept any camera permissions'; - } else { - getById("head1").innerHTML = '
    - Please select which you wish to share'; - } - - if (!session.cleanOutput){ - try { - if (window.obsstudio){ - getById("unexpectedPushLink").classList.remove("hidden"); - } - } catch(e){} - } - - } - } - if (session.roomid || urlParams.has('roomid') || urlParams.has('r') || urlParams.has('room') || filename || (session.permaid !== false)) { - var roomid = ""; - if (urlParams.has('room')) { // needs to be first; takes priority - roomid = urlParams.get('room'); - } else if (urlParams.has('roomid')) { - roomid = urlParams.get('roomid'); - } else if (urlParams.has('r')) { - roomid = urlParams.get('r'); - } else if (session.roomid) { - roomid = session.roomid; - } else if (filename) { - roomid = filename; - } - session.roomid = sanitizeRoomName(roomid); - if (session.director){ - if (session.director !== session.roomid){ - if (!session.cleanOutput){ - warnUser("Conflicting director and room values were provided.\n\n Check your URL parameters; there should be only &director OR &room",5000); - } - } - session.roomid = false; - } - } - - if ((session.permaid===false) && (session.roomid===false) && (session.view===false) && (session.effect===false) && (session.director===false)){ - session.effect = null; - } - - if (urlParams.has('effects') || urlParams.has('effect')) { - session.effect = urlParams.get('effects') || urlParams.get('effect') || null; - } else if (urlParams.has('zoom')){ - session.effect = "7"; - } - - if (window.FaceDetector !== undefined){ - document.querySelectorAll(".facetracker").forEach(ele=>{ - ele.disabled = null; - ele.removeAttribute("disabled"); - ele.title = "Will slowly pan, tilt, and zoom in on the first face detected"; - }); - } - - - if (urlParams.has('imagelist')){ // "&imagelist="+encodeURIComponent(JSON.stringify(["./media/bg_sample.webp", "./media/bg_sample2.webp"])) - var imageList = urlParams.get('imagelist'); // - if (imageList){ - try { - imageList = JSON.parse(decodeURIComponent(imageList)); - } catch(e){ - console.error(e); - } - if (imageList.length){ - session.defaultBackgroundImages = imageList; // ["./media/bg_sample.webp", "./media/bg_sample2.webp"] - } else { - warnlog("empty image array; skipping"); - } - } - } - - if (session.effect!==false){ - if (session.effect === null){ - getById("effectsDiv").style.display = "inline-block"; - session.effect = "0"; - } else if (session.effect === "0" || session.effect === "false" || session.effect === "off" || session.effect === 0){ - session.effect = false; - getById("effectSelector3").style.display = "none"; - getById("effectsDiv3").style.display = "none"; - getById("effectSelector").style.display = "none"; - getById("effectsDiv").style.display = "none"; - } - - if (session.effect === "5"){ - - loadTFLITEImages(); - - getById("effectSelector").style.display = "none"; - getById("effectsDiv").style.display = "inline-block"; - - } - if (session.effect === "3a"){ // heavier blur - session.effectValue = 5; - session.effect = "3"; - } else if (session.effect === "3"){ - session.effectValue = 2; - } else if (session.effect === "7"){ - session.effectValue = 1; - } - // mirror == 2 - // face == 1 - // blur = 3 - // green = 4 - // image = 5 - } - - - if (urlParams.has('effectvalue') || urlParams.has('ev')) { - session.effectValue = parseInt(urlParams.get('effectvalue')) || parseInt(urlParams.get('ev')) || 0; - session.effectValue_default = session.effectValue; - } - - if (session.webcamonly == true) { - if (session.introButton){ - getById("container-2").className = 'column columnfade hidden'; // Hide screen share - getById("head3").classList.add('hidden'); - getById("head3a").classList.add('hidden'); - } else { - getById("container-2").className = 'column columnfade hidden'; // Hide screen share - getById("container-3").classList.add("skip-animation"); - getById("container-3").classList.remove('pointer'); - delayedStartupFuncs.push([previewWebcam]); - } - } - if (session.introOnClean && (session.permaid===false) && (session.roomid===false)){ - //getById("container-2").className = 'column columnfade hidden'; // Hide screen share - getById("head3").classList.add('hidden'); - getById("head3a").classList.add('hidden'); - } else if (session.introOnClean && (session.scene===false) && ((session.permaid!==false || session.roomid!==false))){ - getById("container-2").className = 'column columnfade hidden'; // Hide screen share - getById("container-3").classList.add("skip-animation"); - getById("container-3").classList.remove('pointer'); - delayedStartupFuncs.push([previewWebcam]); - } - - //if (!session.director && ((ChromiumVersion == 86) || (ChromiumVersion == 77) || (ChromiumVersion == 62) || (ChromiumVersion == 51)) && (((session.permaid===false) && session.view) || (session.scene!==false))){ - // session.studioSoftware = true; // vmix - if (window.obsstudio){ - session.studioSoftware = true; - getById("saveRoom").style.display = "none"; // don't let the user save the room if in OBS - } - if (session.cleanViewer){ - if (session.view && !session.director && session.permaid===false){ - session.cleanOutput = true; - } - } - if (urlParams.has('clock') || urlParams.has('clock24')){ - let urlClock = urlParams.get('clock') || urlParams.get('clock24'); - if (urlParams.has('clock24')){ - session.clock24 = true; - } - session.showTime = true; - if (urlClock === "false"){ - session.showTime = false; - } else if (urlClock === "0"){ - session.showTime = false; - } else if (urlClock === "1"){ - getById("overlayClockContainer2").classList.add("top"); - getById("overlayClockContainer2").classList.add("left"); - } else if (urlClock === "7"){ - getById("overlayClockContainer2").classList.add("bottom"); - getById("overlayClockContainer2").classList.add("left"); - } else if (urlClock === "4"){ - getById("overlayClockContainer2").classList.add("vmiddle"); - getById("overlayClockContainer2").classList.add("left"); - } else if (urlClock === "2"){ - getById("overlayClockContainer2").classList.add("top"); - getById("overlayClockContainer2").classList.add("hmiddle"); - } else if (urlClock === "8"){ - getById("overlayClockContainer2").classList.add("bottom"); - getById("overlayClockContainer2").classList.add("hmiddle"); - } else if (urlClock === "5"){ - getById("overlayClockContainer2").classList.add("vmiddle"); - getById("overlayClockContainer2").classList.add("hmiddle"); - } else if (urlClock === "3"){ - getById("overlayClockContainer2").classList.add("top"); - getById("overlayClockContainer2").classList.add("right"); - } else if (urlClock === "9"){ - getById("overlayClockContainer2").classList.add("bottom"); - getById("overlayClockContainer2").classList.add("right"); - } else if (urlClock === "6"){ - getById("overlayClockContainer2").classList.add("vmiddle"); - getById("overlayClockContainer2").classList.add("right"); - } - - } else if (session.cleanOutput){ - session.showTime = false; - } - - if (urlParams.has('timer')){ - if (urlParams.get('timer') === "1"){ - getById("overlayClockContainer").classList.add("top"); - getById("overlayClockContainer").classList.add("left"); - } else if (urlParams.get('timer') === "7"){ - getById("overlayClockContainer").classList.add("bottom"); - getById("overlayClockContainer").classList.add("left"); - } else if (urlParams.get('timer') === "4"){ - getById("overlayClockContainer").classList.add("vmiddle"); - getById("overlayClockContainer").classList.add("left"); - } else if (urlParams.get('timer') === "2"){ - getById("overlayClockContainer").classList.add("top"); - getById("overlayClockContainer").classList.add("hmiddle"); - } else if (urlParams.get('timer') === "8"){ - getById("overlayClockContainer").classList.add("bottom"); - getById("overlayClockContainer").classList.add("hmiddle"); - } else if (urlParams.get('timer') === "5"){ - getById("overlayClockContainer").classList.add("vmiddle"); - getById("overlayClockContainer").classList.add("hmiddle"); - } else if (urlParams.get('timer') === "3"){ - getById("overlayClockContainer").classList.add("top"); - getById("overlayClockContainer").classList.add("right"); - } else if (urlParams.get('timer') === "9"){ - getById("overlayClockContainer").classList.add("bottom"); - getById("overlayClockContainer").classList.add("right"); - } else if (urlParams.get('timer') === "6"){ - getById("overlayClockContainer").classList.add("vmiddle"); - getById("overlayClockContainer").classList.add("right"); - } else { - getById("overlayClockContainer").classList.add("top"); - getById("overlayClockContainer").classList.add("hmiddle"); - } - } - - if (urlParams.has('miconlyoption') || urlParams.has('moo')){ - session.optionalMicOnly = true; - } - - if (urlParams.has('hidescreenshare') || urlParams.has('hidess') || urlParams.has('sshide') || urlParams.has('screensharehide')) { // this way I don't need to remember what it's called. I can just guess. :D - session.screenShareElementHidden = true; - } - - if (urlParams.has('sspaused') || urlParams.has('sspause') || urlParams.has('ssp')) { // this way I don't need to remember what it's called. I can just guess. :D - session.screenShareStartPaused = true; - } - - if (urlParams.has('zoomedbitrate') || urlParams.has('zb')) { // this way I don't need to remember what it's called. I can just guess. :D - session.zoomedBitrate = urlParams.get('zoomedbitrate') || urlParams.get('zb') || 2500; - session.zoomedBitrate = parseInt(session.zoomedBitrate) ; - } - - if (urlParams.has('screenshareid') || urlParams.has('ssid')) { - if (urlParams.get('screenshareid') || urlParams.get('ssid')) { - session.screenshareid = urlParams.get('screenshareid') || urlParams.get('ssid'); - session.screenshareid = sanitizeStreamID(session.screenshareid); - } else { - session.screenshareid = session.streamID + "_ss"; - } - } - - if (urlParams.has('screensharevideoonly') || urlParams.has('ssvideoonly') || urlParams.has('ssvo')) { - session.screenshareVideoOnly = true; - getById("audioScreenShare1").classList.add("hidden"); - } - - if (urlParams.has('screensharefps') || urlParams.has('ssfps')) { - if (urlParams.get('screensharefps') || urlParams.get('ssfps')) { - session.screensharefps = urlParams.get('screensharefps') || urlParams.get('ssfps'); - session.screensharefps = parseInt(session.screensharefps) || 2; - } - } - - if (urlParams.has('screensharequality') || urlParams.has('ssq')) { - if (urlParams.get('screensharequality') || urlParams.get('ssq')) { - session.screensharequality = urlParams.get('screensharequality') || urlParams.get('ssq'); - session.screensharequality = parseInt(session.screensharequality) || 0; - try { - getById("gear_screen").parentNode.removeChild(getById("gear_screen")); - } catch(e){} - } - } - - if (urlParams.has('screensharebitrate') || urlParams.has('ssbitrate')) { - session.screenShareBitrate = urlParams.get('screensharebitrate') || urlParams.get('ssbitrate'); - session.screenShareBitrate = parseInt(session.screenShareBitrate) || 2500; - } - - if (urlParams.has('screensharelabel') || urlParams.has('sslabel')) { - session.screenShareLabel = urlParams.get('screensharelabel') || urlParams.get('sslabel'); - try { - session.screenShareLabel = decodeURIComponent(session.screenShareLabel); - } catch(e){} - session.screenShareLabel = session.screenShareLabel.replace(/_/g, " ") - } - - if (urlParams.has('whepshare') || urlParams.has('whepsrc')) { - try { - session.whepSrc = urlParams.get('whepshare') || urlParams.get('whepsrc') || null; - log("WHEP SRC: "+session.whepSrc); - if (session.whepSrc){ - try { - session.whepSrc = decodeURIComponent(session.whepSrc); - } catch(e){ - session.whepSrc = session.whepSrc; - } - } else { - session.whepSrc = await promptAlt("Enter the WHEP source as a URL"); - } - if (session.whepSrc){ - session.whipoutSettings = {type:"whep", "url": session.whepSrc}; - } - - } catch(e){ - errorlog(e); - } - } - - if (session.roomid!==false){ - if (!(session.cleanOutput)) { - if (session.roomid === "test") { - if (session.password === session.defaultPassword) { - window.focus(); - var testRoomResponse = confirm(getTranslation("room-test-not-good")); - if (testRoomResponse == false) { - hangup(); - throw new Error("User requested to not enter room 'room'."); - } - } - } - } - - if (session.audioDevice === false && session.outputDevice === false) { - getById("headphonesDiv2").style.display = "inline-block"; - getById("headphonesDiv").style.display = "inline-block"; - } - getById("addPasswordBasic").style.display = "none"; - - getById("info").innerHTML = ""; - getById("info").style.color = "#CCC"; - getById("videoname1").value = session.roomid; - getById("dirroomid").innerText = session.roomid; - getById("roomid").innerText = session.roomid; - getById("container-1").className = 'column columnfade hidden'; - getById("container-4").className = 'column columnfade hidden'; - // container 5 is share media file; 6 is share website - getById("container-7").style.display = 'none'; - getById("container-8").style.display = 'none'; - getById("container-9").style.display = 'none'; - getById("container-10").style.display = 'none'; - getById("container-11").style.display = 'none'; - getById("container-12").style.display = 'none'; - getById("container-13").style.display = 'none'; - getById("container-14").style.display = 'none'; - getById("container-15").style.display = 'none'; - getById("mainmenu").style.alignSelf = "center"; - getById("mainmenu").classList.add("mainmenuclass"); - getById("header").style.alignSelf = "center"; - - if (session.webcamonly == true) { // mobile or manual flag 'webcam' pflag set - getById("head1").innerHTML = ''; - } else { - getById("head1").innerHTML = 'Please select an option to join.'; - } - - if (session.roomid.length > 0) { - if (session.videoDevice === 0) { - if (session.audioDevice === 0) { - miniTranslate(getById("add_camera"), "join-room", "Join Room"); - } else { - miniTranslate(getById("add_camera"), "join-room-with-mic", "Join Room with Microphone"); - } - } else if (session.audioDevice === 0) { - miniTranslate(getById("add_camera"), "join-room-with-camera", "Join Room with Camera"); - } else if (session.optionalMicOnly){ - miniTranslate(getById("add_camera"), "join-room-with-video", "Join Room with Video"); - miniTranslate(getById("add_microphone"), "join-room-with-mic-only", "Join Room with just Microphone"); - getById("container-3a").classList.remove("hidden"); - - } else{ - miniTranslate(getById("add_camera"), "join-room-with-camera", "Join Room with Camera"); - } - miniTranslate(getById("add_screen"), "share-screen-with-room", "Screenshare with Room"); - } else { - if (session.videoDevice === 0) { - miniTranslate(getById("add_camera"), "share-your-mic", "Share your Microphone"); - } else { - miniTranslate(getById("add_camera"), "share-your-camera", "Share your Camera"); - } - miniTranslate(getById("add_screen"), "share-your-screen", "Share your Screen"); - } - getById("head3").classList.add('hidden'); - getById("head3a").classList.add('hidden'); - getById("container-2").title = getById("add_screen").innerText; - getById("container-3").title = getById("add_camera").innerText; - - if (session.scene !== false) { - getById("container-4").className = 'column columnfade'; - getById("container-3").className = 'column columnfade'; - getById("container-2").className = 'column columnfade'; - getById("container-1").className = 'column columnfade'; - getById("header").className = 'hidden'; - getById("info").className = 'hidden'; - getById("head1").className = 'hidden'; - getById("head2").className = 'hidden'; - getById("mainmenu").style.display = "none"; - getById("translateButton").style.display = "none"; - log("Update Mixer Event on REsize SET"); - window.onresize = updateMixer; - window.onorientationchange = function(){ - setTimeout(updateMixer, 200); - - }; - joinRoom(session.roomid); // this is a scene, so we want high resolutions - getById("main").style.overflow = "hidden"; - - if (session.chatbutton === true) { - getById("chatbutton").classList.remove("hidden"); - getById("controlButtons").classList.remove("hidden"); - } else if (session.chatbutton === false) { - getById("chatbutton").classList.add("hidden"); - } - } else if ((session.permaid === null) && (session.roomid == "")) { - if (!(session.cleanOutput)) { - getById("head3").classList.remove('hidden'); - getById("head3a").classList.remove('hidden'); - } - } else if ((window.obsstudio) && (session.permaid === false) && (session.director === false) && (session.view) &&(session.roomid.length>0)) { // we already know roomid !== false - updateURL("scene", true, false); // we also know it's not a scene, but we will assume it is in this specific case. - } - - - } else if (session.director) { // if I do a short form of this, it will cause duplications in the code elsewhere. - if (directorLanding == false){ // implies director is not true or false, but a string - try{ - var director_room_input = sanitizeRoomName(session.director); - log("director_room_input:" + director_room_input); - - if (urlParams.has('codirector') || urlParams.has('directorpassword') || urlParams.has('dirpass') || urlParams.has('dp')) { - session.directorPassword = urlParams.get('codirector') || urlParams.get('directorpassword') || urlParams.get('dirpass') || urlParams.get('dp'); - if (!session.directorPassword) { - window.focus(); - session.directorPassword = await promptAlt(getTranslation("enter-director-password"), true); - } else { - try { - session.directorPassword = decodeURIComponent(session.directorPassword); - } catch(e){} - } - if (session.directorPassword){ - session.directorPassword = sanitizePassword(session.directorPassword) - await generateHash(session.directorPassword + session.salt + "abc123", 12).then(function(hash) { // million to one error. - log("dir room hash is " + hash); - session.directorHash = hash; - return; - }).catch(errorlog); - } else { - session.directorPassword = false; - } - } - - setTimeout(function(director_room_input){createRoom(director_room_input);},20, director_room_input); - } catch(e){ - directorLanding = true; - session.director = true; - } - } - if (session.chatbutton === true) { - getById("chatbutton").classList.remove("hidden"); - getById("controlButtons").classList.remove("hidden"); - } else if (session.chatbutton === false) { - getById("chatbutton").classList.add("hidden"); - } - } else if (session.view && (session.permaid === false)) { - //if (!session.activeSpeaker){ - session.audioMeterGuest = false; - //} - if ((session.style===false) && window.obsstudio){ - session.style = 1; - } - if (session.audioEffects === null) { - session.audioEffects = false; - } - log("Update Mixer Event on REsize SET"); - getById("translateButton").style.display = "none"; - window.onresize = updateMixer; - window.onorientationchange = function(){setTimeout(function(){ - updateMixer(); - }, 200);}; - getById("main").style.overflow = "hidden"; - - if (session.chatbutton === true) { - getById("chatbutton").classList.remove("hidden"); - getById("controlButtons").classList.remove("hidden"); - } else if (session.chatbutton === false) { - getById("chatbutton").classList.add("hidden"); - } - } - - if (urlParams.has('nofileshare') || urlParams.has('nodownloads') || urlParams.has('nofiles')){ - session.hostedFiles = false; - session.nodownloads = true; - getById('sharefilebutton').style.display = "none"; - getById('sharefilebutton').classList.add("hidden"); - } else if (session.mobile){ - getById('sharefilebutton').style.display = "none"; - getById('sharefilebutton').classList.add("hidden"); - } else if (session.roomid==false){ - getById('sharefilebutton').style.display = "none"; - getById('sharefilebutton').classList.add("hidden"); - } else if (session.scene!==false){ - getById('sharefilebutton').style.display = "none"; - getById('sharefilebutton').classList.add("hidden"); - } else if (session.cleanOutput){ - getById('sharefilebutton').style.display = "none"; - getById('sharefilebutton').classList.add("hidden"); - } - - if (session.audioEffects === null) { - session.audioEffects = true; - } - - if (session.audioEffects) { - getById("channelGroup1").style.display = "block"; - getById("channelGroup2").style.display = "block"; - } - - if (urlParams.has('hidemenu') || urlParams.has('hm')) { // needs to happen the room and permaid applications - getById("mainmenu").style.display = "none"; - getById("header").style.display = "none"; - getById("mainmenu").style.opacity = 0; - getById("header").style.opacity = 0; - } - - if (session.view) { - getById("main").className = ""; - getById("credits").style.display = 'none'; - try { - if (session.label === false) { - if (document.title == "") { - document.title = "View=" + session.view.toString(); - } else { - document.title += ", View=" + session.view.toString(); - } - } - } catch (e) { - errorlog(e); - }; - } - - - if (urlParams.get('auth')) { - session.auth = urlParams.get('auth'); - } - - if (urlParams.has('waitimage')){ - session.waitImage = urlParams.get('waitimage') || false; - } - - if (((session.view) && (session.roomid === false)) || (session.waitImage && (session.scene!==false))) { - - getById("container-4").className = 'column columnfade'; - getById("container-3").className = 'column columnfade'; - getById("container-2").className = 'column columnfade'; - getById("container-1").className = 'column columnfade'; - //getById("header").className = 'hidden'; - getById("info").className = 'hidden'; - getById("header").className = 'hidden'; - getById("head1").className = 'hidden'; - getById("head2").className = 'hidden'; - getById("head3").classList.add('hidden'); - getById("head3a").classList.add('hidden'); - - - getById("mainmenu").style.backgroundRepeat = "no-repeat"; - getById("mainmenu").style.backgroundPosition = "bottom center"; - getById("mainmenu").style.minHeight = "100%"; - getById("mainmenu").style.minWidth = "100%"; - getById("mainmenu").style.backgroundSize = "100px 100px"; - getById("mainmenu").innerHTML = ''; - getById("mainmenu").classList.remove("row"); - getById("mainmenu").style.display = "block"; - - if (urlParams.has('waittimeout')){ - session.waitImageTimeout = parseInt(urlParams.get('waittimeout')) || 0; - } - if (!session.fakeFeeds){ - session.waitImageTimeoutObject = setTimeout(function() { - session.waitImageTimeoutObject = true; - try { - if ((session.view)) { - if (document.getElementById("mainmenu")) { - if (session.waitImage){ - getById("mainmenu").innerHTML += ''; - getById("retryimage").src = decodeURIComponent(session.waitImage); - getById("retryimage").onerror = function(){this.style.display='none';}; - - if (session.cover) { - getById("retryimage").style.objectFit = "cover"; - } - - } else if (!(session.cleanOutput)){ - getById("mainmenu").innerHTML += '
    '; - getById("retrySpinner").onclick = function(){ - updateURL("cleanoutput"); - location.reload(); - } - getById("retrySpinner").title = getTranslation("waiting-for-the-stream"); - } - if (urlParams.has('waitmessage')){ - getById("mainmenu").innerHTML += '
    '; - getById("retrymessage").innerText = urlParams.get('waitmessage'); - getById("retrySpinner").title = urlParams.get('waitmessage'); - } - } - } - } catch (e) { - errorlog(e); - } - }, session.waitImageTimeout); - } - - log("auto request videos"); - if ((iPad || iOS) && navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1 && SafariVersion > 13) { // Modern iOS doesn't need pop up - play(); - } else if (navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) { // Safari on Desktop does require pop up - if (!(session.cleanOutput)) { - warnUser("Safari requires us to ask for an audio permission to use peer-to-peer technology. You will need to accept it in a moment if asked to view this live video", 20000); - } - navigator.mediaDevices.getUserMedia({ - audio: true - }).then(function() { - closeModal(); - play(); - }).catch(function() { - play(); - }); - } else { // everything else is OK. - play(); - } - } else if (session.roomid) { - try { - if (session.label === false) { - if (document.title == "") { - document.title = "Room=" + session.roomid.toString(); - } else { - document.title += ": " + session.roomid.toString(); - } - } - } catch (e) { - errorlog(e); - }; - } - - hideHomeCheck(); - - setTimeout(function(){ - for (var i in delayedStartupFuncs) { - var cb = delayedStartupFuncs[i]; - log(cb.slice(1)); - cb[0](...cb.slice(1)); // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#A_better_apply - } - delayedStartupFuncs = []; - },50); - - if ((session.effect=="3") || (session.effect=="4") || (session.effect=="5")){ - attemptTFLiteJsFileLoad(); - } else if (session.effect=="6"){ - loadTensorflowJS(); - } else if (session.effect=="9"){ - session.effect="sample"; - //loadEffect(session.effect); - warnUser("Loading custom effects model...",1000); - } else if (session.effect=="10"){ - session.effect="dog"; - //loadEffect(session.effect); - warnUser("Loading custom effects model...",1000); - } else if (session.effect=="11"){ - session.effect="anon"; - //loadEffect(session.effect); - warnUser("Loading custom effects model...",1000); - } else if (session.effect=="12"){ - session.effect="sample"; - //loadEffect(session.effect); - warnUser("Loading custom effects model...",1000); - } else if (session.effect=="facetracking"){ - session.effect="1"; - } else if (session.effect=="zoom"){ - session.effect="7"; - } - - if (session.effect === "3"){ - getById("selectEffectAmount").style.display = "block"; - getById("selectEffectAmount3").style.display = "block"; - getById("selectEffectAmountInput").value = session.effectValue; - getById("selectEffectAmountInput3").value = session.effectValue; - } else if (session.effect === "7"){ - getById("selectEffectAmount").style.display = "block"; - getById("selectEffectAmount3").style.display = "block"; - session.effectValue = 1.0; - getById("selectEffectAmountInput").min = 1; - getById("selectEffectAmountInput").max = 1.99; - getById("selectEffectAmountInput").step = 0.01 - getById("selectEffectAmountInput3").min = 1; - getById("selectEffectAmountInput3").max = 1.99; - getById("selectEffectAmountInput3").step = 0.01 - - getById("selectEffectAmountInput").value = session.effectValue; - getById("selectEffectAmountInput3").value = session.effectValue; - } - - if (location.protocol !== 'https:') { - if (!(session.cleanOutput)) { - warnUser("SSL (https) is not enabled. This site will not work without it!

    Try accessing the site from here instead.", false, false); - } - } - - if (session.sensorData) { - setupSensorData(parseInt(session.sensorData)); - } - - try { - navigator.mediaDevices.ondevicechange = reconnectDevices; - } catch (e) { - errorlog(e); - } - - if (urlParams.has('autohide')) { - session.autohide=true; - } - if (session.autohide && (session.scene===false)){// && (session.roomid!==false)){ - getById("main").onmouseover = showControl; // this is correct. (it's not session.showControls) - document.ontouchstart = showControl; // this is correct. (it's not session.showControls) - getById("gridlayout").classList.add("nocontrolbar"); - } - - if (urlParams.has('experimental')) { - session.experimental = true; - } - - if (urlParams.has('flagship')) { - session.flagship = true; - } - //if (!session.flagship && session.mobile && (session.limitTotalBitrate===false)){ - // session.limitTotalBitrate = session.totalRoomBitrate_default; // 500, with the max per guest stream out at maxMobileBitrate (350kbps) or 35-kbps if more than X in the room. - //} - - if (urlParams.has('maxmobilebitrate')) { - session.maxMobileBitrate = parseInt(urlParams.has('maxmobilebitrate')) || 0; - } - if (urlParams.has('lowmobilebitrate')) { - session.lowMobileBitrate = parseInt(urlParams.has('lowmobilebitrate')) || 0; - } - - // Please contact steve on discord.vdo.ninja if you'd like this iFRAME tweaked, expanded, etc -- it's updated based on user request - - session.remoteInterfaceAPI = function(e) { // iFRAME api support - if (!e.data || (typeof e.data !== "object")){ - warnlog(e); - return; - } - log(e); - try { - if ("function" in e.data) { // these are calling in-app functions, with perhaps a callback -- TODO: add callbacks - var ret = null; - if (e.data.function === "previewWebcam") { - ret = previewWebcam(); - } else if (e.data.function === "changeHTML") { - ret = getById(e.data.target); - ret.innerHTML = e.data.value; - } else if (e.data.function === "publishScreen") { - ret = publishScreen(); - } else if (e.data.function === "routeMessage"){ - try { - session.ws.onmessage({data: e.data.value}); - } catch(e){warnlog("handshake not yet setup");} - } else if (e.data.function === "eval") { - eval(e.data.value); // eval == evil ; feedback welcomed - } - } - } catch (err) { - errorlog(err); - } - - if ("sendData" in e.data) { // send generic data via p2p. Send whatever you want I guess; there is a max chunk size of course. Use filetransfer for large files? - var UUID = false; - var streamID = false; - var type = false; - if (e.data.UUID){ - UUID = e.data.UUID; - } else if (e.data.streamID){ - streamID = e.data.streamID; - } - if (e.data.type){ - type = e.data.type; - } - var ret = session.sendGenericData(e.data.sendData, UUID, streamID, type); // comes out the other side as: ("dataReceived", data, UUID); - if (!ret){warnlog("Not connected yet or no peers available");} - return; - } - - if ("PPT" in e.data){ - log("PTT activated-webmain"); - if (e.data.PPT === true) { // unmute - session.muted = false; // set - getById("mutebutton").classList.add("PPTActive"); - log(session.muted); - toggleMute(true); // apply - - } else if (e.data.PPT === false) { // mute - session.muted = true; // set - getById("mutebutton").classList.remove("PPTActive"); - log(session.muted); - toggleMute(true); // apply - } else if (e.data.PPT === "toggle") { // toggle - toggleMute(); - } - return; // this is a high-load call, so lets skip the rest of the checks to save cpu. - } - - if ("sendChat" in e.data) { - sendChat(e.data.sendChat); // sends to all peers; more options down the road - return; - } - // Chat out gets called via getChatMessage function - // Related code: parent.postMessage({"chat": {"msg":-----,"type":----,"time":---} }, "*"); - - // session.requestResolution(vid.dataset.UUID, wrw*window.devicePixelRatio, hrh*window.devicePixelRatio); - - if ("mic" in e.data) { // this should work for the director's mic mute button as well. Needs to be manually enabled the first time still tho. - if (e.data.mic === true) { // unmute - session.muted = false; // set - log(session.muted); - toggleMute(true); // apply - } else if (e.data.mic === false) { // mute - session.muted = true; // set - log(session.muted); - toggleMute(true); // apply - } else if (e.data.mic === "toggle") { // toggle - toggleMute(); - } - } - - if ("toggleSettings" in e.data) { // this should work for the director's mic mute button as well. Needs to be manually enabled the first time still tho. - - if (e.data.toggleSettings && !toggleSettingsState){ - toggleSettings(); - } else if (e.data.toggleSettings=="toggle"){ - toggleSettings(); - } else if (toggleSettingsState){ - toggleSettings(); - } - } - - - if ("camera" in e.data) { // this should work for the director's mic mute button as well. Needs to be manually enabled the first time still tho. - if (e.data.camera === true) { // unmute - session.videoMuted = false; // set - log(session.videoMuted); - toggleVideoMute(true); // apply - } else if (e.data.camera === false) { // mute - session.videoMuted = true; // set - log(session.videoMuted); - toggleVideoMute(true); // apply - } else if (e.data.camera === "toggle") { // toggle - toggleVideoMute(); - } - } - - if ("keyframe" in e.data) { - session.sendKeyFrameScenes(); - } - - - if ("groups" in e.data) { - if (typeof e.data.groups == "object"){ - session.group = e.data.groups || []; - } else if (!e.data.group){ - session.group = []; - } else { - session.group = e.data.groups.split(","); - } - var eleGroup = getById("groups"); - eleGroup.querySelectorAll('[data-action-type="toggle-group"][data-group]').forEach(group=>{ - if (!(session.group && session.group.includes(group))){ - group.remove("green"); - } - }); - - if (session.group){ - session.group.forEach(group =>{ - var ele = eleGroup.querySelector('[data-action-type="toggle-group"][data-group="'+group+'"'); - if (!ele){ - ele = document.createElement("div"); - ele.dataset.actionType = "toggle-group"; - ele.dataset.group = group; - ele.classList.add('float'); - ele.style.display = "inline-block"; - ele.role = "button"; - ele.innerHTML = '
    '+group; - eleGroup.appendChild(ele); - ele.onclick = function(){ - changeGroupDirectorAPI(this.dataset.group); - } - } - ele.classList.add("green"); - }); - } - - updateMixer(); - - if (session.group.length || session.allowNoGroup){ - session.sendMessage({"group":session.group.join(",")}); - if (session.screenShareState && (session.screenshareType ===3)){ - session.sendMessage({"group":session.group.join(","), altUUID:true}); - } - } else { - session.sendMessage({"group":false}); - if (session.screenShareState && (session.screenshareType ===3)){ - session.sendMessage({"group":false, altUUID:true}); - } - } - - } - - if ("groupView" in e.data) { - if (typeof e.data.groupView == "object"){ - session.groupView = e.data.groupView || []; - } else if (!e.data.groupView){ - session.groupView = []; - } else { - session.groupView = e.data.groupView.split(","); - } - updateMixer(); - } - - - if ("mute" in e.data) { - if (e.data.mute === true) { // unmute - session.speakerMuted = true; // set - toggleSpeakerMute(true); // apply - } else if (e.data.mute === false) { // mute - session.speakerMuted = false; // set - toggleSpeakerMute(true); // apply - } else if (e.data.mute === "toggle") { // toggle - toggleSpeakerMute(); - } - } else if ("speaker" in e.data) { // same thing as mute. - if (e.data.speaker === true) { // unmute - session.speakerMuted = false; // set - toggleSpeakerMute(true); // apply - } else if (e.data.speaker === false) { // mute - session.speakerMuted = true; // set - toggleSpeakerMute(true); // apply - } else if (e.data.speaker === "toggle") { // toggle - toggleSpeakerMute(); - } - } - - if ("record" in e.data) { - if (e.data.record == false) { // mute - if ("recording" in session.videoElement) { - recordLocalVideo("stop"); - } - } else if (e.data.record === true){ - if ("recording" in session.videoElement) { - // already recording - } else { - recordLocalVideo("start"); - } - } else if (e.data.record){ - var video = document.getElementById(e.data.record); - if (video){ - var videoKbps = 4000; - if (session.recordLocal !== false) { - videoKbps = session.recordLocal; - } - recordLocalVideo(null, videoKbps, video); - } - } - } - - - if ("volume" in e.data) { // might not work with iframes or meshcast currently. - session.volume = parseFloat(e.data.volume) || 0; - if (session.volume > 1.0){ // this is a bit quasi improper. But the API is official 0 to 1.0; not 0 to 100, so this is mainly a catch for those not using the API right. - session.volume = session.volume/100.0; - } - if (!("target" in e.data) || (e.data.target == "*")){ - if (session.videoElement){ - session.videoElement.volume = session.volume; - } - } - for (var i in session.rpcs) { - try { - if (!session.rpcs[i].videoElement){continue;} - if ("streamID" in session.rpcs[i]) { - if ("target" in e.data) { - if ((session.rpcs[i].streamID == e.data.target) || (e.data.target == "*")) { // specify a stream ID or let it apply to all videos - session.rpcs[i].videoElement.volume = session.volume; - } - } else { - session.rpcs[i].videoElement.volume = session.volume; - } - } - } catch (e) { - errorlog(e); - } - } - } - - if ("enableYouTube" in e.data){ // panning adjusts the stereo pan , although current its UUID based. can add stream ID based if requested. - if (typeof e.data.enableYouTube == "string"){ - session.youtubeKey = e.data.enableYouTube; - } else if (!session.youtubeKey){ - errorlog("No Youtube Key provided"); - } - console.log(session.youtubeKey); - YoutubeChatInterface(true); - } - - if ("nextSlide" in e.data){ // panning adjusts the stereo pan , although current its UUID based. can add stream ID based if requested. - nextSlide(); - } - - if ("prevSlide" in e.data){ // panning adjusts the stereo pan , although current its UUID based. can add stream ID based if requested. - gobackSlide(); - } - - if ("panning" in e.data){ // panning adjusts the stereo pan , although current its UUID based. can add stream ID based if requested. - if ("UUID" in e.data){ - try { - adjustPan(UUID, e.data.panning); - } catch (e) { - errorlog(e); - } - } else { - for (var i in session.rpcs) { - try { - adjustPan(i, e.data.panning); - } catch (e) { - errorlog(e); - } - } - } - } - - if (("targetBitrate" in e.data) || ("targetAudioBitrate" in e.data)) { // this sets the fundemental bitrate target, but does not necessarily "lock" . - - var msg = {}; - if ("targetBitrate" in e.data){ - msg.targetBitrate = e.data.targetBitrate; - } - if ("targetAudioBitrate" in e.data){ - msg.targetAudioBitrate = e.data.targetAudioBitrate; - } - if (e.data.requestAs){ - msg.requestAs = e.data.requestAs; - } - if (e.data.remote){ - msg.remote = e.data.remote; - } - for (var i in session.rpcs) { - try { - if ("streamID" in session.rpcs[i]) { - if ("target" in e.data) { - if ((session.rpcs[i].streamID == e.data.target) || (e.data.target == "*")) { // specify a stream ID or let it apply to all videos - session.sendRequest(msg, i); - } - } else if (e.data.UUID && (e.data.UUID===i)) { - session.sendRequest(msg, i); - } else if (e.data.streamID) { - if (session.rpcs[i].streamID == e.data.streamID) { // specify a stream ID or let it apply to all videos - session.sendRequest(msg, i); - } - } else { - session.sendRequest(msg, i); // bitrate = 0 pauses the video - } - } - } catch (e) { - errorlog(e); - } - } - } - - if ("manualBitrate" in e.data){ - for (var i in session.rpcs) { - try { - if ("streamID" in session.rpcs[i]) { - if ("target" in e.data) { - if ((session.rpcs[i].streamID == e.data.target) || (e.data.target == "*")) { // specify a stream ID or let it apply to all videos - session.rpcs[i].manualBandwidth = e.data.manualBitrate; - session.requestRateLimit(false, i); - } - } else if (e.data.UUID && (e.data.UUID===i)) { - session.rpcs[i].manualBandwidth = e.data.manualBitrate; - session.requestRateLimit(false, i); - } else if (e.data.streamID) { - if (session.rpcs[i].streamID == e.data.streamID) { // specify a stream ID or let it apply to all videos - session.rpcs[i].manualBandwidth = e.data.manualBitrate - session.requestRateLimit(false, i); - } - } else { - session.rpcs[i].manualBandwidth = e.data.manualBitrate; - session.requestRateLimit(false, i); - } - } - } catch (e) { - errorlog(e); - } - } - } - - if ("bitrate" in e.data) { /// set a video bitrate for a video; scene or view link; kbps - var lock = true; - if ("lock" in e.data){ // since this is the iframe API, we're going to assume the default is manual over-ride. VDO.Ninja's automixer logic won't override a locked bitrate. - lock = e.data.lock; - } - for (var i in session.rpcs) { - try { - if ("streamID" in session.rpcs[i]) { // we only target publishers with this call - if ("target" in e.data) { - if ((session.rpcs[i].streamID == e.data.target) || (e.data.target == "*")) { // specify a stream ID or let it apply to all videos - session.requestRateLimit(e.data.bitrate, i, false, lock); - } - } else if (e.data.UUID && (e.data.UUID===i)) { - session.requestRateLimit(e.data.bitrate, i, false, lock); - } else if (e.data.streamID) { - if (session.rpcs[i].streamID == e.data.streamID) { // specify a stream ID or let it apply to all videos - session.requestRateLimit(e.data.bitrate, i, false, lock); - } - } else { - session.requestRateLimit(e.data.bitrate, i, false, lock); // bitrate = 0 pauses the video - } - } - } catch (e) { - errorlog(e); - } - } - } - - if ("audiobitrate" in e.data) { // changes the audio bitrate of a specific or all inbound media tracks. kbps - var lock = true; - if ("lock" in e.data){ // since this is the iframe API, we're going to assume the default is manual over-ride. VDO.Ninja's automixer logic won't override a locked bitrate. - lock = e.data.lock; - } - for (var i in session.rpcs) { - try { - if ("streamID" in session.rpcs[i]) { // we only target publishers with this call - if ("target" in e.data) { - if ((session.rpcs[i].streamID == e.data.target) || (e.data.target == "*")) { // specify a stream ID or let it apply to all videos - session.requestAudioRateLimit(parseInt(e.data.bitrate), i, lock); - } - } else if (e.data.UUID && (e.data.UUID===i)) { - session.requestAudioRateLimit(parseInt(e.data.bitrate), i, lock); - } else if (e.data.streamID) { - if (session.rpcs[i].streamID == e.data.streamID) { // specify a stream ID or let it apply to all videos - session.requestAudioRateLimit(parseInt(e.data.bitrate), i, lock); - } - } else { - session.requestAudioRateLimit(parseInt(e.data.bitrate), i, lock); // bitrate = 0 pauses the video - } - } - } catch (e) { - errorlog(e); - } - } - } - - if ("changeVideoDevice" in e.data) { - warnlog(e.data.changeVideoDevice); - changeVideoDevice(e.data.changeVideoDevice); - } - - if ("changeAudioDevice" in e.data) { - warnlog(e.data.changeAudioDevice); - changeAudioDevice(e.data.changeAudioDevice); - } - - if ("changeAudioOutputDevice" in e.data) { - warnlog(e.data.changeAudioOutputDevice); - changeAudioOutputDeviceById(e.data.changeAudioOutputDevice); - } - - if ("getDeviceList" in e.data) { // get a list of local camera / audio devices - warnlog(e.data.getDeviceList); - enumerateDevices().then(function(deviceInfos) { - parent.postMessage({ - "deviceList": JSON.parse(JSON.stringify(deviceInfos)) - }, session.iframetarget); - }); - } - - if ("sceneState" in e.data) { // TRUE OR FALSE - tells the connected peers if they are live or not via a tally light change. - var msg = {}; - msg.obsState = {}; - msg.obsState.visibility = e.data.sceneState || false; - msg.obsState.recording = e.data.sceneState || false; - session.sendRequest(msg); - } - - if ("layouts" in e.data) { - session.layouts = e.data.layouts; - if ("obsSceneTriggers" in e.data) { - session.obsSceneTriggers = e.data.obsSceneTriggers; - } else { - session.obsSceneTriggers = false; - } - for (var uid in session.pcs){ - if (session.pcs[uid].layout){ - session.sendMessage(e.data, uid); - } - } - // session.obsSceneSync(); // not sure I need to trigger this? - log(e.data); - } - - if ("sendMessage" in e.data) { // webrtc send to viewers - session.sendMessage(e.data); - } - - if ("sendRequest" in e.data) { // webrtc send to publishers - session.sendRequest(e.data); - } - - if ("sendRawMIDI" in e.data) { // webrtc send to publishers - //var msg = {}; - //msg.midi = {}; - //msg.midi.d = e.data.sendRawMIDI.data; aka [d1,d2,d3]; - //msg.midi.c = e.data.sendRawMIDI.channel; - //msg.midi.s = e.data.sendRawMIDI.timestamp; - // e.data.UUID or e.data.streamID or leave empty to send to all - if ("UUID" in e.data){ - sendRawMIDI(e.data.sendRawMIDI, e.data.UUID); // send to connection - } else if (e.data.streamID){ - sendRawMIDI(e.data.sendRawMIDI, false, e.data.streamID); // send to connection - } else { - sendRawMIDI(e.data.sendRawMIDI); // send to all - } - return; // make it send faster. - } - - if ("sendPeers" in e.data) { // webrtc send message to every connected peer; like send and request; a hammer vs a knife. - session.sendPeers(e.data); - } - - if ("reload" in e.data) { // reload the page - reloadRequested(); // location.reload();, but with no user prompt (force reload) - } - - if ("getFaces" in e.data){ - if (e.data.faceTrack){ - session.grabFaceData = true; - getFaces(); - } else { - session.grabFaceData = false; - } - } - - if (("getStats" in e.data)){ - var stats = {}; - try { - stats.inbound_stats = {}; - stats.total_outbound_connections = Object.keys(session.pcs).length; - stats.total_inbound_connections = Object.keys(session.rpcs).length; - for (var i in session.rpcs) { - stats.inbound_stats[session.rpcs[i].streamID] = session.rpcs[i].stats; - } - for (var uuid in session.pcs) { - setTimeout(function(UUID) { - session.pcs[UUID].getStats().then(function(stats) { - stats.forEach(stat => { - - if (stat.id && stat.id.startsWith("DEPRECATED_")){return;} - - if (stat.type == "outbound-rtp") { - if (stat.kind == "video") { - - if ("qualityLimitationReason" in stat) { - - session.pcs[UUID].stats.quality_limitation_reason = stat.qualityLimitationReason; - } - if ("framesPerSecond" in stat) { - session.pcs[UUID].stats.resolution = stat.frameWidth + " x " + stat.frameHeight + " @ " + stat.framesPerSecond; - } - if ("encoderImplementation" in stat) { - session.pcs[UUID].stats.encoder = stat.encoderImplementation; - } - } - } else if (stat.type == "remote-candidate") { - if ("relayProtocol" in stat) { - if ("ip" in stat) { - session.pcs[UUID].stats.remote_relay_IP = stat.ip; - } - session.pcs[UUID].stats.remote_relayProtocol = stat.relayProtocol; - } - if ("candidateType" in stat) { - session.pcs[UUID].stats.remote_candidateType = stat.candidateType; - } - } else if (stat.type == "local-candidate") { - if ("relayProtocol" in stat) { - if ("ip" in stat) { - session.pcs[UUID].stats.local_relayIP = stat.ip; - } - session.pcs[UUID].stats.local_relayProtocol = stat.relayProtocol; - } - if ("candidateType" in stat) { - session.pcs[UUID].stats.local_candidateType = stat.candidateType; - } - } else if ((stat.type == "candidate-pair" ) && (stat.nominated)) { - - if ("availableOutgoingBitrate" in stat){ - session.pcs[UUID].stats.available_outgoing_bitrate_kbps = parseInt(stat.availableOutgoingBitrate/1024); - } - if ("totalRoundTripTime" in stat){ - if ("responsesReceived" in stat){ - session.pcs[UUID].stats.average_roundTripTime_ms = parseInt((stat.totalRoundTripTime/stat.responsesReceived)*1000); - } - - } - } - return; - }); - return; - }); - }, 0, uuid); - } - } catch(e){ - // disconnected probably - } - setTimeout(function() { - stats.outbound_stats = {}; - try { - for (var i in session.pcs) { - stats.outbound_stats[i] = session.pcs[i].stats; - } - } catch(e){} - parent.postMessage({ - "stats": stats - }, session.iframetarget); - }, 1000); - } - - if ("getRemoteStats" in e.data) { - if (session.remote){ - session.sendRequest({"requestStats":true, "remote":session.remote}); - } else { - session.sendRequest({"requestStats":true}); - } - } - - if ("requestStatsContinuous" in e.data) { - if (session.remote){ - session.sendRequest({"requestStatsContinuous":e.data.requestStatsContinuous, "remote":session.remote}); - } else { - session.sendRequest({"requestStatsContinuous":e.data.requestStatsContinuous}); - } - } - - if ("getLoudness" in e.data) { - log("GOT LOUDNESS REQUEST"); - if (e.data.getLoudness == true) { - - if (!session.pushLoudness && (session.audioEffects!==true)){ - session.pushLoudness = true; - for (var i in session.rpcs) { - updateIncomingAudioElement(i); // this can be called when turning on/off inbound audio processing. - } - } else { - session.pushLoudness = true; - } - - var loudness = {}; - for (var i in session.rpcs) { - loudness[session.rpcs[i].streamID] = session.rpcs[i].stats.Audio_Loudness; - } - - parent.postMessage({ - "loudness": loudness - }, session.iframetarget); - - } else { - if (session.pushLoudness && !session.audioEffects){ // turn off audio processing - session.pushLoudness = false; - for (var i in session.rpcs) { - updateIncomingAudioElement(i) - } - } else { - session.pushLoudness = false; // can't turn off audio processing - } - } - } - - if ("getEffectsData" in e.data) { - log("GOT getEffects Data REQUESTed"); // face tracking info, etc. - if (e.data.getEffectsData !== false) { - session.pushEffectsData = e.data.getEffectsData; // which effect do you want the data from? it won't enable the effect necessarily; just the ML pipeline - - //parent.postMessage({ - // "effectsData": effectsData, - // "effectsID": session.pushEffectsData - //}, session.iframetarget); - - } else { - session.pushEffectsData = false; - } - } - - if ("getStreamIDs" in e.data) { // get a list of stream Ids, with a label if it is present. label = false if not there - if (e.data.getStreamIDs) { - var streamIDs = {}; - for (var i in session.rpcs) { - streamIDs[session.rpcs[i].streamID] = session.rpcs[i].label; - } - parent.postMessage({ - "streamIDs": streamIDs - }, session.iframetarget); - } - } - - if ("getStreamInfo" in e.data) { // get a list of stream Ids, with a label if it is present. label = false if not there - try { - var UUIDS = {}; - for (var i in session.rpcs){ - UUIDS[i] = {}; - UUIDS[i].label = session.rpcs[i].label || false; - UUIDS[i].streamID = session.rpcs[i].streamID || false; - if (session.rpcs[i].stats && session.rpcs[i].stats.info){ - UUIDS[i].info = session.rpcs[i].stats.info; - } else { - UUIDS[i].info = {}; - } - } - parent.postMessage({ - "streamInfo": UUIDS - }, session.iframetarget); - } catch(e){ - errorlog(e); - } - } - - if (("close" in e.data) || ("hangup" in e.data)) { // disconnect and hangup all inbound streams. - var tmp = e.data.close || e.data.hangup; - if (tmp == "estop"){ // try to stop the video recording even if not complete; if you can't wait even ms before a reload/exit. - console.log("ESTOP"); - session.hangup(false,true); - } else if (tmp == "reload"){ // stop and reload the page safely. - session.hangup(true); - } else { // just hangup, but can take up to 1-second to do so fully. - session.hangup(); - } - } - if ("hangup" in e.data) { // disconnect and hangup all inbound streams. - session.hangup(); - } - - if ("style" in e.data) { // insert a custom style sheet - try { - const style = document.createElement('style'); - style.textContent = e.data.style; - document.head.append(style); - log(style); - } catch (e) { - errorlog(e); - } - } - - if ("getDetailedState" in e.data) { - var detailedState = getDetailedState(); - parent.postMessage({ - "detailedState": detailedState - }, session.iframetarget); - } - - if ("automixer" in e.data) { // stop the auto mixer if you want to control the layout and bitrate yourself - if (e.data.automixer == true) { - session.manual = false; - try { - updateMixer(); - } catch (e) {} - } else if (e.data.automixer == false) { - session.manual = true; - } - } - - if ("advancedMode" in e.data){ - if (e.data.advancedMode){ - // Un-hiding advanced items - document.querySelectorAll(".advanced").forEach(element => { - element.classList.remove("hide"); - }) - } else { - // Hiding advanced items - document.querySelectorAll(".advanced").forEach(element => { - element.classList.add("hide"); - }) - } - } - - if ("requestStream" in e.data){ - if (e.data.requestStream){ // load a specific stream ID - log("requestStream iframe api"); - session.requestStream(e.data.requestStream); - } // don't use if the stream is in your room (as not needed) - } // you can load a stream ID from inside a room that exists outside any room - - - if ("previewMode" in e.data){ - if ("layout" in e.data){ - session.layout = e.data.layout; - pokeIframeAPI("layout-updated", session.layout); - } - switchModes(e.data.previewMode); - } else if ("layout" in e.data){ - warnlog("changing layout request via IFRAME API"); - session.layout = e.data.layout; - pokeIframeAPI("layout-updated", session.layout); - - if (e.data.obsCommand){ - issueLayoutOBS(e.data); - } else { - if (session.director){ - if ("scene" in e.data){ - if ("UUID" in e.data){ - issueLayout(e.data.layout, e.data.scene, e.data.UUID); - } else { - issueLayout(e.data.layout, e.data.scene); - } - } else if ("UUID" in e.data){ - issueLayout(e.data.layout, false, e.data.UUID); - } - } - } - updateMixer(); - } else if (e.data.obsCommand){ - errorlog("obsCommand via iframe API currently needs a layout.."); - } - - - if ("slotmode" in e.data){ - if (session.slotmode){ - session.slotmode = parseInt(e.data.slotmode); - } else { - session.slotmode = false; - } - } - - //////////// manual scale. Request a specific down-scaled resolution from a remote connection - var targetWidth = false; - var targetHeight = false; - if ("targetWidth" in e.data){ - targetWidth = e.data.targetWidth || 0; - } - if ("targetHeight" in e.data){ - targetHeight = e.data.targetHeight || 0; - } - // session.viewheight or session.viewwidth - if ((targetWidth || targetHeight) && e.data.UUID){ - var requestAs = false; - if (e.data.requestAs){ - requestAs = e.data.requestAs; - } - session.requestResolution(e.data.UUID, targetWidth || 4096 , targetHeight || 2160 , false, requestAs); // this is fine. - } - //////////////// - - if ("scale" in e.data) { - if (e.data.scale === false){ - session.dynamicScale = true; // disable manual scaling - updateMixer(); - var scale = false; - } else { - session.dynamicScale = false; - var scale = parseInt(e.data.scale) || 100; - } - - if (e.data.UUID){ - session.sendRequest({scale:scale}, UUID); - } else if (e.data.target){ - for (var i in session.rpcs) { - try { - if ("streamID" in session.rpcs[i]) { - if ("target" in e.data) { - if ((session.rpcs[i].streamID == e.data.target) || (e.data.target == "*")) { // specify a stream ID or let it apply to all videos - session.sendRequest({scale:scale}, i); - } - } else { - session.sendRequest({scale:scale}, i); - } - } - } catch (e) { - errorlog(e); - } - } - } else { - session.sendRequest({scale:scale}); - } - - } - - - if (("action" in e.data) && (e.data.action!="null")) { /////////////// reuse the Companion API - var resp = processMessage(e.data); // reuse the companion API - if (resp!==null){ - log(resp); - parent.postMessage(resp, session.iframetarget); - } - } else if ("target" in e.data) { - log(e.data); - for (var i in session.rpcs) { - try { - if ("streamID" in session.rpcs[i]) { - if ((session.rpcs[i].streamID == e.data.target) || (e.data.target == "*")) { - try { - - if ("settings" in e.data) { - try{ - for (const property in e.data.settings) { - try { - session.rpcs[i].videoElement[property] = e.data.settings[property]; - } catch(e){} - } - } catch(e){} - } - if ("add" in e.data) { - try{ - getById("gridlayout").appendChild(session.rpcs[i].videoElement); - } catch(e){warnlog(e);} - - } else if ("remove" in e.data) { - try { - session.rpcs[i].videoElement.parentNode.removeChild(session.rpcs[i].videoElement); - } catch (e) { - try { - session.rpcs[i].videoElement.parentNode.parentNode.removeChild(session.rpcs[i].videoElement.parentNode); - } catch (e) {} - } - } else if ("replace" in e.data) { // should allow for a cleaner cut between two video streams. - try { - getById("gridlayout").appendChild(session.rpcs[i].videoElement); - getById("gridlayout").childNodes.forEach(ele=>{ - if ((!ele.id) || (ele.id !== session.rpcs[i].videoElement.id)){ - getById("gridlayout").removeChild(ele); - } - }); - } catch(e){} - } - } catch (e) { - errorlog(e); - } - } - } - } catch (e) { - errorlog(e); - } - } - } - }; - - if (isIFrame) { // reduce CPU load if not needed. //iframe API - window.onmessage = session.remoteInterfaceAPI; - } - - if (session.midiHotkeys || session.midiOut!==false) { - - var script = document.createElement('script'); - script.onload = function() { - WebMidi.enable().then(() =>{ - - WebMidi.timeStart = Date.now(); // start time - - WebMidi.addListener("connected", function(e) { - log(e); - }); - - WebMidi.addListener("disconnected", function(e) { - log(e); - }); - - console.log(WebMidi.inputs); - - if (session.midiOut===true){ - for (var i = 0; i < WebMidi.inputs.length; i++) { - try { - var input = WebMidi.inputs[i]; - input.addListener("midimessage", function(e) { - e.timestamp += WebMidi.timeStart; - sendRawMIDI(e); - //var msg = {}; - //msg.midi = {}; - //msg.midi.d = e.data; aka [d1,d2,d3]; - //msg.midi.c = e.channel; - //msg.midi.s = e.timestamp; - }); - } catch(e){} - } - } else if (session.midiOut==parseInt(session.midiOut)){ - try{ - var input = WebMidi.inputs[parseInt(session.midiOut)-1]; - input.addListener("midimessage", function(e) { - e.timestamp += WebMidi.timeStart; - sendRawMIDI(e); - }); - } catch(e){errorlog(e);}; - } - - for (var i = 0; i < WebMidi.inputs.length; i++) { - - if (session.midiDevice && (session.midiDevice!==(i+1))){continue;} - - var input = WebMidi.inputs[i]; - if (session.midiChannel){ - input = input.channels[session.midiChannel]; - } - if (session.midiHotkeys==4){ - input.addListener('controlchange', function(e) { - log(e); - midiHotkeysCommand(e.controller.number, e.rawValue); - }); - } else if (session.midiHotkeys==5){ - if (session.midiOffset!==false){ - input.addListener('controlchange', function(e) { - midiHotkeysCommand_offset(e.controller.number, e.rawValue, session.midiOffset); - }); - } - } else { - input.addListener('noteon', function(e) { - log(e); - var note = e.note.name + e.note.octave; - var velocity = e.velocity || false; - midiHotkeysNote(note,velocity); - }); - } - } - }).catch(errorlog); - }; - script.src = "./thirdparty/webmidi3.js"; // dynamically load this only if its needed. Keeps loading time down. - document.head.appendChild(script); - } else if (session.midiIn){ - var script = document.createElement('script'); - script.src = "./thirdparty/webmidi3.js"; // dynamically load this only if its needed. Keeps loading time down. - script.onload = function() { - WebMidi.enable().then(() => console.log(WebMidi.outputs)).catch(errorlog); - } - document.head.appendChild(script); - } - - - var languages = getById('languagesList').querySelectorAll('li a'); - var timezones = []; - - languages.forEach(language => { - if (language.dataset.tz) { - var languageTimezones = language.dataset.tz.split(';'); // each link can have multiple timezones separated by ; - languageTimezones.forEach(element => { - timezones.push(element); - }); - } - }); - - var currentTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; - - if (timezones.includes(currentTimezone)) { - var el = getById('languagesList').querySelector("li a[data-tz*='" + currentTimezone +"']"); // select language li - el.parentElement.removeChild(el); // remove it - getById('languagesList').insertBefore(el, getById('languagesList').querySelector('li:nth-child(2)')); // insert it after English - } - - var visAudioTimeout = null - document.addEventListener("visibilitychange", function() { - //log("hidden : " +document.hidden); - //log("vis : "+document.visibilityState); - if ((iOS) || (iPad)) { // fixes a bug on iOS devices. Not need with other devices? - toggleAutoVideoMute(); - clearTimeout(visAudioTimeout); - if (document.visibilityState === 'visible') { - visAudioTimeout = setTimeout(function() { - resetupAudioOut(); - activatedPreview=false; - grabAudio("#audioSource3"); - }, 500); - } - } - }); - - // Warns user about network going down - window.addEventListener("offline", function (e) { - warnlog("connection lost"); - if ((session.view) && (session.permaid === false)) { - log("VDO.Ninja has no network connectivity and can't work properly." ); - } else if (session.scene !== false) { - log("VDO.Ninja has no network connectivity and can't work properly." ); - } else if (!session.cleanOutput) { - if (iOS || iPad){ - for (var UUID in session.pcs){ - session.pcs[UUID].close(); - delete(session.pcs[UUID]); - session.applySoloChat(); - applySceneState(); - } - } - if (location.hostname === "vdo.ninja"){ - warnUser(getTranslation("no-network-details")); - } else { - warnUser(getTranslation("no-network")); - } - - } else { - log("VDO.Ninja has no network connectivity and can't work properly."); - } - }); - - window.addEventListener("online", function (e) { - - log("Back ONLINE"); - closeModal(); - - if (!session.onceConnected){ // never connected to websockets before. Let's not trigger retryWatchInterval if we don't have to. - return; - } - - if (!session.retryWatchInterval()){ // ask for the streams again to watch - session.ping(); // if no streams requested, let's ping instead. - } - }); - - /* function updateConnectionStatus() { // no longer works in chrome. - - try{ - if (!session.stats){ - return; - } - - if (Connection.type){ - log("Connection type changed from " + session.stats.network_type + " to " + Connection.type); - - if (session.stats.network_type && (session.stats.network_type !== Connection.type)){ - var miniInfo = {}; - miniInfo.con = Connection.type; - session.sendMessage({"miniInfo":miniInfo}); - - if (!session.retryWatchInterval()){ // ask for the streams again to watch - session.ping(); // if no streams requested, let's ping instead. - } - - } else { // connection state changed, but doesn't seem like it actually changed... - session.ping(); // if no streams requested, let's ping instead. - } - - session.stats.network_type = Connection.type; - } - - } catch(e){warnlog(e);} - - } - - try { - var Connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection; - if (Connection){ - if (Connection.type){ - session.stats.network_type = Connection.type - } - Connection.addEventListener('change', updateConnectionStatus); - } - } catch (e) {log(e);} // effectiveType is not yet supported by Firefox or Safari; 2021 */ - - - setInterval(function() { - checkConnection(); - }, 5000); - - // Remove modal if network comes back up - window.addEventListener("online", function (e) { - if (!session.cleanOutput) { - // Remove last inserted modal; Could be improved by tagging the - // modal elements and only removing modals tagged 'offline' - let userWarnings = document.querySelectorAll('.alertModal'); - closeModal(userWarnings[userWarnings.length- 1]); - } else { - log( - "Network connectivity has been restored." - ); - } - }); - - document.addEventListener("DOMContentLoaded", function() { - var lazyVideos = [].slice.call(document.querySelectorAll("video.lazy")); - - if ("IntersectionObserver" in window) { - var lazyVideoObserver = new IntersectionObserver(function(entries, observer) { - entries.forEach(function(video) { - if (video.isIntersecting) { - for (var source in video.target.children) { - var videoSource = video.target.children[source]; - if (typeof videoSource.tagName === "string" && videoSource.tagName === "SOURCE") { - videoSource.src = videoSource.dataset.src; - } - } - - video.target.load(); - video.target.classList.remove("lazy"); - lazyVideoObserver.unobserve(video.target); - } - }); - }); - - lazyVideos.forEach(function(lazyVideo) { - lazyVideoObserver.observe(lazyVideo); - }); - } - }); - - document.addEventListener("dragstart", event => { - var url = event.target.href || event.target.value; - if (!url || !url.startsWith('https://')) return; - if (event.target.dataset.drag != "1") { - return; - } - //event.target.ondragend = function(){event.target.blur();} - - var streamId = url.split('view='); - var label = url.split('label='); - - if (session.label !== false) { - url += '&layer-name=' + session.label; - } else { - url += '&layer-name=VDO.Ninja'; - } - if (streamId.length > 1) url += ': ' + streamId[1].split('&')[0]; - if (label.length > 1) url += ' - ' + decodeURI(label[1].split('&')[0]); - - try { - if (document.getElementById("videosource")) { - var video = getById('videosource'); - if (typeof(video.videoWidth) == "undefined") { - url += '&layer-width=1920'; // this isn't always 100% correct, as the resolution can fluxuate, but it is probably good enough - url += '&layer-height=1080'; - } else if ((parseInt(video.videoWidth) < 360) || (video.videoHeight < 640)) { - url += '&layer-width=1920'; // this isn't always 100% correct, as the resolution can fluxuate, but it is probably good enough - url += '&layer-height=1080'; - } else { - url += '&layer-width=' + video.videoWidth; // this isn't always 100% correct, as the resolution can fluxuate, but it is probably good enough - url += '&layer-height=' + video.videoHeight; - } - } else { - url += '&layer-width=1920'; // this isn't always 100% correct, as the resolution can fluxuate, but it is probably good enough - url += '&layer-height=1080'; - } - } catch (error) { - url += '&layer-width=1920'; // this isn't always 100% correct, as the resolution can fluxuate, but it is probably good enough - url += '&layer-height=1080'; - } - - event.dataTransfer.setDragImage( getById('dragImage'), 24, 24); - event.dataTransfer.setData("text/uri-list", encodeURI(url)); - - }); - - if (navigator.getBattery){ - navigator.getBattery().then(function(battery) { - session.batteryState = {}; - if ("level" in battery){ - session.batteryState.level = battery.level; - } - if ("charging" in battery){ - session.batteryState.charging = battery.charging; - } - - if (session.batteryState == {}){ - session.batteryState = null; - } - battery.addEventListener('chargingchange', function() { - session.batteryState = {}; - var miniInfo = {}; - if ("level" in battery){ - session.batteryState.level = battery.level; - miniInfo.bat = battery.level; - } - if ("charging" in battery){ - session.batteryState.charging = battery.charging; - miniInfo.chrg = battery.charging; - } - if (session.batteryState == {}){ - session.batteryState = null; - } - session.sendMessage({"miniInfo":miniInfo}); - }); - - battery.addEventListener('levelchange', function(){ - session.batteryState = {}; - var miniInfo = {}; - if ("level" in battery){ - session.batteryState.level = battery.level; - miniInfo.bat = battery.level; - } - if ("charging" in battery){ - session.batteryState.charging = battery.charging; - miniInfo.chrg = battery.charging; - } - if (session.batteryState == {}){ - session.batteryState = null; - } - session.sendMessage({"miniInfo":miniInfo}); - }); - - }); - } - - - var lastTouchEnd = 0; - document.addEventListener('touchend', function(event) { - var now = (new Date()).getTime(); - if (now - lastTouchEnd <= 300) { - event.preventDefault(); - } - lastTouchEnd = now; - }, false); - - - document.addEventListener('click', function(event) { - if (session.firstPlayTriggered == false) { - playAllVideos(); - session.firstPlayTriggered = true; - - try { - if (session.audioCtx && session.audioCtx.state == "suspended"){ - session.audioCtx.resume(); - } - } catch(e){warnlog("session.audioCtx.resume(); failed 4");} - - history.pushState({}, ''); - } - }); - - document.addEventListener("keydown", event => { - keyDownEvent(event); - }); - - function keyDownEvent(event){ - - if ((event.ctrlKey) || (event.metaKey)) { // detect if CTRL is pressed - CtrlPressed = true; - } else { - CtrlPressed = false; - } - if (event.altKey) { - AltPressed = true; - } else { - AltPressed = false; - } - - if (event.key === "Escape") { - log("escape pressed; checking to see if modal box opened and will close"); - if (document.fullscreenElement) { - document.exitFullscreen(); - //updateMixer(); - } else { - - let userWarnings = document.querySelectorAll('.alertModal, .promptModal'); - if (userWarnings.length){ - closeModal(userWarnings[userWarnings.length- 1]); - } - } - return; - } - - if (session.disableHotKeys){return;} - - if (PPTHotkey){ - if (event.target && (event.target.tagName == "INPUT")){ - // skip, since an input field is selected - } else if ((PPTHotkey.ctrl === event.ctrlKey) && (PPTHotkey.alt === AltPressed) && (PPTHotkey.meta === event.metaKey) && ((PPTHotkey.key===false) || ((PPTHotkey.key!==false) && (PPTHotkey.key === event.key)))){ - if (session.muted && !PPTKeyPressed){ - session.muted = false; - PPTKeyPressed = true; - getById("mutebutton").classList.add("PPTActive"); - toggleMute(true); - } else if (!PPTKeyPressed){ - PPTKeyPressed = true; - getById("mutebutton").classList.add("PPTActive"); - } - event.preventDefault(); - event.stopPropagation(); - return; - } else if (PPTKeyPressed){ - PPTKeyPressed = false; - getById("mutebutton").classList.remove("PPTActive"); - if (!session.muted){ - session.muted = true; - toggleMute(true); - - } - event.preventDefault(); - event.stopPropagation(); - return; - } - } - - if (KeyPressedTimeout || PPTKeyPressed){ - event.preventDefault(); - event.stopPropagation(); - return; - } - - if (CtrlPressed && event.keyCode) { - if (event.keyCode == 77) { // M - if (event.metaKey) { - if (AltPressed) { - if (!KeyPressedTimeout){ - toggleMute(); // macOS - KeyPressedTimeout = Date.now(); - event.preventDefault(); - event.stopPropagation(); - return; - } - } - } else { - if (!KeyPressedTimeout){ - toggleMute(); // Windows - KeyPressedTimeout = Date.now(); - event.preventDefault(); - event.stopPropagation(); - return; - } - } - - } else if (event.keyCode == 66) { // B - toggleVideoMute(); - event.preventDefault(); - event.stopPropagation(); - return; - } - - if (AltPressed){ // CTRL + ALT - if (event.keyCode == 70) { // F - toggleFileshare()(); - event.preventDefault(); - event.stopPropagation(); - return; - } else if (event.keyCode == 67) { // C - cycleCameras(); - event.preventDefault(); - event.stopPropagation(); - return; - } else if (event.keyCode == 83) { - toggleScreenShare()(); - event.preventDefault(); - event.stopPropagation(); - return; - } else if (event.keyCode == 68) { - if (!drawOnScreenObject){ - drawOnScreen(); - } else { - drawOnScreenObject.stop(); - } - event.preventDefault(); - event.stopPropagation(); - return; - } else if (event.keyCode == 80) { // S - if (session.videoElement){ - togglePictureInPicture(session.videoElement); - event.preventDefault(); - event.stopPropagation(); - return; - } - } - } - } - } - - document.addEventListener("keyup", event => { - - if (PPTKeyPressed){ - PPTKeyPressed = false; - getById("mutebutton").classList.remove("PPTActive"); - if (!session.muted){ - session.muted = true; - toggleMute(true); - } - event.preventDefault(); - event.stopPropagation(); - return; - } - - if (!(event.ctrlKey || event.metaKey)) { - if (CtrlPressed) { - CtrlPressed = false; - for (var i in Callbacks) { - var cb = Callbacks[i]; - cb[0](...cb.slice(1)); // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#A_better_apply - } - Callbacks = []; - } - } - if (!(event.altKey)) { - AltPressed = false; - } - - if (event.altKey && event.shiftKey && event.keyCode === 67 /* C */) { - toggleControlBar(); - } - if (KeyPressedTimeout && ((event.keyCode == 77) || (!(event.ctrlKey || event.metaKey)))) { - if (Date.now() - KeyPressedTimeout>300){ - toggleMute(); - } - if (event.keyCode == 77){ - KeyPressedTimeout = 0; - } - } - }); - - setTimeout(function(){ // lets lazy load the following.. - window.addEventListener("beforeunload", confirmUnload); // This just keeps people from killing the live stream accidentally. Also give me a headsup that the stream is ending - window.addEventListener("unload", function(e) { - try { - if (session.ws){ - session.ws.close(); - } - if (session.videoElement.recording) { - session.videoElement.recorder.writer.close(); - session.videoElement.recording = false; - } - for (var i in session.rpcs) { - if (session.rpcs[i].videoElement) { - if (session.rpcs[i].videoElement.recording) { - session.rpcs[i].videoElement.recorder.writer.close(); - session.rpcs[i].videoElement.recording = false; - } - } - } - session.hangup(); - } catch (e) { - errorlog(e); - } - }); - - try { - navigator.serviceWorker.getRegistrations().then(registrations => { // getting rid of old service workers. - try { - log(registrations); - for(let registration of registrations) { - if (registration.scope != "https://"+window.location.hostname+window.location.pathname+"thirdparty/"){ - registration.unregister(); - } - } - } catch(e){} - }).catch(errorlog); - } catch(e){} - - var script = document.createElement('script'); - document.head.appendChild(script); - script.onload = function() { - var script = document.createElement('script'); - document.head.appendChild(script); - script.src = "./thirdparty/StreamSaver.js?v=19"; // dynamically load this only if its needed. Keeps loading time down. - }; - script.src = "./thirdparty/polyfill.min.js"; // dynamically load this only if its needed. Keeps loading time down. - },100); -} +/* +* Copyright (c) 2022 Steve Seguin. All Rights Reserved. +* +* Use of this source code is governed by the APGLv3 open-source license +* that can be found in the LICENSE file in the root of the source +* tree. Alternative licencing options can be made available on request. +* +*/ +/*jshint esversion: 6 */ +async function main(){ // main asyncronous thread; mostly initializes the user settings. + + var delayedStartupFuncs = []; + // translation stuff start //// + + var ConfigSettings = getById("main-js"); + var ln_template = false; + + try { + if (ConfigSettings) { + ln_template = ConfigSettings.getAttribute('data-translation'); // Translations + if (typeof ln_template === "undefined") { + ln_template = false; + } else if (ln_template === null) { + ln_template = false; + } + } + + if (urlParams.has('ln') || urlParams.has('language')) { + ln_template = urlParams.get('ln') || urlParams.get('language') || null; + } + } catch (e) { + errorlog(e); + } + if (ln_template===null) { + getById("mainmenu").style.opacity = 1; + } else if (ln_template!==false) { // checking if manual lanuage override enabled + try { + log("Lang Template: " + ln_template); + await changeLg(ln_template); + //getById("mainmenu").style.opacity = 1; + } catch (error) { + errorlog(error); + getById("mainmenu").style.opacity = 1; + } + } + if (location.hostname !== "vdo.ninja" && location.hostname !== "backup.vdo.ninja" && location.hostname !== "proxy.vdo.ninja" && location.hostname !== "obs.ninja") { + + errorReport = false; + + if (location.hostname === "rtc.ninja"){ + try { + if (session.label === false) { + document.title = "RTC.Ninja"; + } + getById("qos").innerHTML = ""; + getById("logoname").innerHTML = ""; + getById("helpbutton").style.display = "none"; + getById("helpbutton").style.opacity = 0; + getById("reportbutton").style.display = "none"; + getById("reportbutton").style.opacity = 0; + getById("dropButton").classList.add("hidden"); + getById("container-4").classList.add("hidden"); + if (!(urlParams.has('screenshare') || urlParams.has('ss'))){ + getById("container-2").classList.add("hidden"); + } + //getById("mainmenu").style.opacity = 1; + getById("mainmenu").style.margin = "30px 0"; + getById("translateButton").style.display = "none"; + getById("translateButton").style.opacity = 0; + getById("info").style.display = "none"; + getById("info").style.opacity = 0; + getById("chatBody").innerHTML = ""; + } catch (e) {} + } else if (session.label === false) { + document.title = location.hostname; + } + try { + if (ln_template===false){ + changeLg("blank"); + } + //getById("mainmenu").style.opacity = 1; + + getById("qos").innerHTML = '' + getById("logoname").innerHTML = getById("qos").outerHTML; + getById("helpbutton").style.display = "none"; + getById("reportbutton").style.display = "none"; + getById("chatBody").innerHTML = ""; + getById("qos").style.color = "#FFF0"; + getById("qos").style.fontSize = "70%"; + getById("logoname").style.display = "none"; + getById("logoname").style.margin = "0 0 0 5px"; + } catch (error) { + getById("mainmenu").style.opacity = 1; + errorlog(error); + } + } else { // check if automatic language translation is available + getById("mainmenu").style.opacity = 1; + } + + //// translation stuff ends //// + + if (urlParams.has('cleanoutput') || urlParams.has('clean') || urlParams.has('cleanish')) { + session.cleanOutput = true; + } + + if (urlParams.has('cleanviewer') || urlParams.has('cv')) { + session.cleanViewer = true; + } + + if (session.cleanOutput || session.cleanViewer){ + session.audioMeterGuest = false; + } + + if (urlParams.has('hidehome')){ + session.hidehome = true; + } + hideHomeCheck(); + + if (urlParams.has('previewmode')){ + session.switchMode = true; + } + + if (urlParams.has('director') || urlParams.has('dir')) { + session.director = urlParams.get('director') || urlParams.get('dir') || session.roomid || urlParams.get('roomid') || urlParams.get('r') || urlParams.get('room') || filename || true; + session.effect = null; // so the director can see the effects after a page refresh + getById("avatarDiv3").classList.remove("hidden"); // lets the director see the avatar option + } + + if (urlParams.has('controls') || urlParams.has('videocontrols')) { + session.showControls = true; // show the video control bar + + if (urlParams.get('controls') === "false"){ + session.showControls = false; + } else if (urlParams.get('controls') === "0"){ + session.showControls = false; + } else if (urlParams.get('controls') === "off"){ + session.showControls = false; + } + } + if (urlParams.has('nocontrols')) { + session.showControls = false; // show the video control bar + } + + if (!isIFrame && !window.obsstudio){ + if (ChromiumVersion===65){ + // pass, since probably manycam and that's bugged + } else if (getStorage("redirect") == "yes") { + setStorage("redirect", "", 0); + session.sticky = true; + } else if (getStorage("settings") != "") { + var URLGOTO = getStorage("settings"); + if (URLGOTO && URLGOTO.startsWith("https://")){ + if (URLGOTO === window.location.href) { + // continue, as its already matched + } else if (!(session.cleanOutput)){ + + window.focus(); + document.body.classList.remove("hidden"); + + session.sticky = await confirmAlt("Would you like to load your previous session?\n\nThis will redirect you to:\n\n"+URLGOTO, true); + if (!session.sticky) { + setStorage("settings", "", 0); + log("deleting cookie as user said no"); + } else { + var cookieSettings = decodeURI(URLGOTO); + setStorage("redirect", "yes", 1); + window.location.replace(cookieSettings); + } + } else { + var cookieSettings = decodeURI(URLGOTO); + setStorage("redirect", "yes", 1); + window.location.replace(cookieSettings); + } + } + } + + if (urlParams.has('sticky')){ // won't work with iframes. + + //if (getStorage("permission") == "") { + // session.sticky = confirm("Would you allow us to store a cookie to keep your session settings persistent?"); + //} else { + session.sticky = true; + + getById("saveRoom").style.display = "none"; + //} + //if (session.sticky) { + setStorage("permission", "yes", 999); + setStorage("settings", encodeURI(window.location.href), 90); + //} + } + } + + + if (urlParams.has('safemode')) { + session.safemode = true; // load defa + } else { + session.store = {}; + try { + loadSettings(); + } catch(e){ + errorlog(e); + } + } + + if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1) { + try { + //getById("electronDragZone").style.cursor="grab"; + if (!ipcRenderer){ + ipcRenderer = require('electron').ipcRenderer; + } + if (ipcRenderer){ + window.prompt = function(title, val){ + return ipcRenderer.sendSync('prompt', {title, val}); // call if needed in the future + }; + } + //ipcRenderer.sendSync('prompt', {title, val}); // call now -- but why? + } catch(e){} + } + + if (window.electronApi && window.electronApi.exposeDoSomethingInWebApp){ + window.electronApi.exposeDoSomethingInWebApp(function (fauxEventData) { + //alert("WORKS"); + //errorlog("WORKS!"); + session.remoteInterfaceAPI(fauxEventData); + }); + window.electronApi.updateVersion(session.version); + } + + if (urlParams.has('retrytimeout')) { + session.retryTimeout = parseInt(urlParams.get('retrytimeout')) || 5000; + if (session.retryTimeout<5000){ + session.retryTimeout = 5000; + } + } + + if (urlParams.has('ptz')){ + session.ptz=true; + } + + if (urlParams.has('notios')){ + iOS=false; + iPad=false; + } + + if (urlParams.has('optimize')) { + session.optimize = parseInt(urlParams.get('optimize')) || 0; + } + + if (urlParams.has('nosettings') || urlParams.has('ns')) { + session.screensharebutton = false; + session.showSettings = false; + } + + if (urlParams.has('nomicbutton') || urlParams.has('nmb')) { + getById("mutebutton").style.setProperty("display", "none", "important"); + } + + if (urlParams.has('novice') ) { + // Hiding advanced items + document.querySelectorAll(".advanced").forEach(element => { + element.classList.add("hide"); + }) + } + + if (urlParams.has('avatarimg') || urlParams.has('bgimage') || urlParams.has('bgimg')) { // URL or data:base64 image. Becomes local to this viewer only. This is like &avatar, but slightly different. Just CSS in this case + var avatarImg = urlParams.get('avatarimg') || urlParams.get('bgimage') || urlParams.get('bgimg') || "./media/avatar1.png"; + if (avatarImg){ + try { + avatarImg = decodeURIComponent(avatarImg); + } catch(e){} + try { + let fallbackImage = new Image(); + fallbackImage.src = avatarImg; + session.style = -1; + fallbackImage.onload = function(){ + document.documentElement.style.setProperty('--video-background-image', 'url("'+avatarImg+'")'); + if (session.meterStyle!==5){ + document.documentElement.style.setProperty('--video-background-image-size', "contain"); + } + } + } catch(e){} + } + } + if (urlParams.has('avatarimg2') || urlParams.has('bgimage2') || urlParams.has('bgimg2')) { // URL or data:base64 image. Becomes local to this viewer only. This is like &avatar, but slightly different. Just CSS in this case + var avatarImg2 = urlParams.get('avatarimg2') || urlParams.get('bgimage2') || urlParams.get('bgimg2') || "./media/avatar2.png"; + if (avatarImg2){ + try { + avatarImg2 = decodeURIComponent(avatarImg2); + } catch(e){} + try { + let fallbackImage2 = new Image(); + fallbackImage2.src = avatarImg2; + fallbackImage2.onload = function(){ + document.documentElement.style.setProperty('--video-background-image-talking', 'url("'+avatarImg2+'")'); + if (session.meterStyle!==5){ + document.documentElement.style.setProperty('--video-background-image-size', "contain"); + } + } + session.audioEffects = true; + session.meterStyle = 4; + session.style = -1; + if (session.showControls===null){ + session.showControls = false; + } + + } catch(e){} + } + } + + if (urlParams.has('avatarimg3') || urlParams.has('bgimage3') || urlParams.has('bgimg3')) { // URL or data:base64 image. Becomes local to this viewer only. This is like &avatar, but slightly different. Just CSS in this case + var avatarImg3 = urlParams.get('avatarimg3') || urlParams.get('bgimage3') || urlParams.get('bgimg3') || "./media/avatar3.png"; + if (avatarImg3){ + try { + avatarImg3 = decodeURIComponent(avatarImg3); + } catch(e){} + try { + + let fallbackImage3 = new Image(); + fallbackImage3.src = avatarImg3; + fallbackImage3.onload = function(){ + document.documentElement.style.setProperty('--video-background-image-screaming', 'url("'+avatarImg3+'")'); + if (session.meterStyle!==5){ + document.documentElement.style.setProperty('--video-background-image-size', "contain"); + } + } + session.audioEffects = true; + session.meterStyle = 4; + session.style = -1; + if (session.showControls===null){ + session.showControls = false; + } + } catch(e){} + } + } + + if (urlParams.has('background') || urlParams.has('appbg')) { // URL or data:base64 image. Use &chroma if you want to use a color instead of image. + var background = urlParams.get('background') || urlParams.get('appbg') || false; + if (background){ + try { + background = decodeURIComponent(background); + } catch(e){} + try { + background = 'url("'+background+'")'; + document.documentElement.style.setProperty('--background-main-image', background); + + } catch(e){} + } + } + + if (urlParams.has('poster')) { // URL or data:base64 image. Becomes local to this viewer only. This is like &avatar, but slightly different. Just CSS in this case + var posterImage = urlParams.get('poster') || "./media/avatar.webp"; + if (posterImage){ + try { + posterImage = decodeURIComponent(posterImage); + session.posterImage = posterImage; + } catch(e){} + } + } + + if (urlParams.has('hideplaybutton') || urlParams.has('hpb')) { // URL or data:base64 image. Becomes local to this viewer only. This is like &avatar, but slightly different. Just CSS in this case + try { + document.getElementById("bigPlayButton").classList.add("hidden"); + } catch(e){ + + } + } + + if (urlParams.has('whip') || urlParams.has('whipview')) { + session.whipView = urlParams.get('whip') || urlParams.get('whipview') || false; + if (session.whipView){ + setTimeout(function(){whipClient();},1000); + } + } + + if (urlParams.has('cftoken') || urlParams.has('cft')){ + session.whipOutput = urlParams.get('cftoken') || urlParams.get('cft') || false; + if (session.whipOutput){ + session.whipOutput = "https://cloudflare.vdo.ninja/"+session.whipOutput; + } + } + + if (urlParams.has('whippush') || urlParams.has('whipout') || urlParams.has('pushwhip')) { // URL or data:base64 image. Becomes local to this viewer only. This is like &avatar, but slightly different. Just CSS in this case + session.whipOutput = urlParams.get('whippush') || urlParams.get('whipout') || urlParams.get('pushwhip') || null; + if (session.whipOutput){ + try { + if (session.whipOutput == 'twitch'){ + session.whipOutput = "https://g.webrtc.live-video.net:4443/v2/offer"; + query("#publishOutToken input[type='password']").placeholder = "Twitch stream token here"; + } else { + session.whipOutput = decodeURIComponent(session.whipOutput); + } + } catch(e){ + errorlog(e); + } + } else { + getById("publishOutURL").classList.remove("hidden"); + } + + if (urlParams.has('whippushtoken') || urlParams.has('whipouttoken') || urlParams.has('pushwhiptoken')) {// URL or data:base64 image. Becomes local to this viewer only. This is like &avatar, but slightly different. Just CSS in this case + session.whipOutputToken = urlParams.get('whippushtoken') || urlParams.get('whipouttoken') || urlParams.get('pushwhiptoken') || false; + if (!session.whipOutputToken){ + getById("publishOutToken").classList.remove("hidden"); + } + } else if (session.whipOutput!==false){ + if (!session.whipOutputToken){ + getById("publishOutToken").classList.remove("hidden"); + } + } + } + + + + if (urlParams.has('whepplay')) { // URL or data:base64 image. Becomes local to this viewer only. This is like &avatar, but slightly different. Just CSS in this case + if (urlParams.get('whepplay')){ + try { + session.whepInput = decodeURIComponent(urlParams.get('whepplay')); + if (session.whepInput){ + setTimeout(function(){whepIn();},1000); + } + } catch(e){ + errorlog(e); + } + } + } + if (urlParams.has('whepplaytoken')) { // URL or data:base64 image. Becomes local to this viewer only. This is like &avatar, but slightly different. Just CSS in this case + if (urlParams.get('whepplaytoken')){ + try { + session.whepInputToken = urlParams.get('whepplaytoken') + } catch(e){ + errorlog(e); + } + } + } + + if (urlParams.has("hostwhep")){ + session.whepHost = urlParams.get("hostwhep") || session.streamID || false; + } + + if (urlParams.has('nomouseevents') || urlParams.has('nme')) { + session.disableMouseEvents = true; + } + if (urlParams.has('overlaycontrols')) { + session.overlayControls = true; + } + + if (urlParams.has('novideobutton') || urlParams.has('nvb')) { + getById("mutevideobutton").style.setProperty("display", "none", "important"); + } + + if (urlParams.has('nospeakerbutton') || urlParams.has('nsb')) { + getById("mutespeakerbutton").style.setProperty("display", "none", "important"); + } + + if (urlParams.has('noscale') || urlParams.has('noscaling')) { + session.noScaling = true; + } + + if (urlParams.has('pusheffectsdata') ) { + session.pushEffectsData=true; + } + + if (urlParams.has('pushloudness') || urlParams.has('getloudness')) { // this sets the loudness IFRAME API output, if available. + session.pushLoudness = true; + } + + if (urlParams.has('pushfaces') || urlParams.has('getfaces')) { + session.grabFaceData = true; + setTimeout(function(){ // give the app some time to load + getFaces(); + }, 2000); + } + + if (urlParams.has('notmobile')){ + session.mobile = false; + } else if (urlParams.has('mobile')){ + session.mobile = true; + session.audioEffects = false; // disable audio inbound effects also. + session.audioMeterGuest = false; + } else if (iOS || iPad) { + if (SafariVersion && SafariVersion<16){ + getById("oldiOSWarning").classList.remove('hidden'); + } + session.mobile = true; + session.audioEffects = false; // disable audio inbound effects also. + session.audioMeterGuest = false; + window.addEventListener('resize', function() { // Safari is the new IE. + + if (session.ws){ + var msg = {}; + msg.requestSceneUpdate = true; + session.sendMessage(msg); + } + if (screen && screen.orientation && screen.orientation.type){ + if (screen.orientation.type.includes("portrait")){ + document.getElementsByTagName("html")[0].style.height = "100vh"; + setTimeout(function(){ + document.getElementsByTagName("html")[0].style.height = "100%"; + }, 1000); + } else if (screen.orientation.type.includes("landscape")){ + document.getElementsByTagName("html")[0].style.height = "100vh"; + setTimeout(function(){ + document.getElementsByTagName("html")[0].style.height = "100%"; + }, 1000); + } + } else if ( window.matchMedia("(orientation: portrait)").matches ) { + document.getElementsByTagName("html")[0].style.height = "100vh"; + setTimeout(function(){ + document.getElementsByTagName("html")[0].style.height = "100%"; + }, 1000); + } else if ( window.matchMedia("(orientation: landscape)").matches ) { + document.getElementsByTagName("html")[0].style.height = "100vh"; + setTimeout(function(){ + document.getElementsByTagName("html")[0].style.height = "100%"; + }, 1000); + } + }); + + if (/CriOS/i.test(navigator.userAgent)) { // if runngin Chrome on iOS + if (!(session.cleanOutput)) { + try { + navigator.mediaDevices.getUserMedia; + } catch (e) { + warnUser("Chrome on this device does not support the required technology to use this site.\n\nPlease use Safari instead or update your iOS and browser version."); + } + } + } + } else if (/Android|Pixel|webOS|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { // not sure how accurate this is. + session.mobile = true; + session.audioEffects = false; // disable audio inbound effects also. + session.audioMeterGuest = false; + } else { + log("MAKE DRAGGABLE"); + delayedStartupFuncs.push([makeDraggableElement, document.getElementById("subControlButtons")]); + if (SafariVersion && !ChromiumVersion){ // if desktop Safari, so macOS, give a note saying it sucks + getById("SafariWarning").classList.remove("hidden"); + } + } + + + if (urlParams.has('broadcasttransfer') || urlParams.has('bct')) { + log("Broadcast transfer flag set"); + session.broadcastTransfer = urlParams.get('broadcasttransfer') || urlParams.get('bct') || null; + if (session.broadcastTransfer === "false") { + session.broadcastTransfer = false; + } else if (session.broadcastTransfer=== "0") { + session.broadcastTransfer = false; + } else if (session.broadcastTransfer === "no") { + session.broadcastTransfer = false; + } else if (session.broadcastTransfer === "off") { + session.broadcastTransfer = false; + } else { + session.broadcastTransfer = true; + } + if (transferSettings){ + transferSettings.broadcast = session.broadcastTransfer; + } + } + + if (urlParams.has('queuetransfer') || urlParams.has('qt')) { + log("Broadcast transfer flag set"); + session.queueTransfer = urlParams.get('queuetransfer') || urlParams.get('qt') || null; + if (session.queueTransfer === "false") { + session.queueTransfer = false; + } else if (session.queueTransfer=== "0") { + session.queueTransfer = false; + } else if (session.queueTransfer === "no") { + session.queueTransfer = false; + } else if (session.queueTransfer === "off") { + session.queueTransfer = false; + } else { + session.queueTransfer = true; + } + if (transferSettings){ + transferSettings.queue = session.queueTransfer; + } + } + + if (urlParams.has('broadcast') || urlParams.has('bc')) { + log("Broadcast flag set"); + session.broadcast = urlParams.get('broadcast') || urlParams.get('bc') || null; + + if (session.broadcast === "false") { + session.broadcast = false; + } else if (session.broadcast=== "0") { + session.broadcast = false; + } else if (session.broadcast === "no") { + session.broadcast = false; + } else if (session.broadcast === "off") { + session.broadcast = false; + } + + //if ((iOS) || (iPad)) { + // session.nopreview = false; + //} else { + // session.nopreview = true; + //} + session.minipreview = 2; // full screen if nothing else on screen. + session.style = 1; + //getById("header").style.display = "none"; + //getById("header").style.opacity = 0; + session.showList=true; + } + + if (urlParams.has('showlist')) { + session.showList = urlParams.get('showlist'); + if (session.showList === "false") { + session.showList = false; + } else if (session.showList=== "0") { + session.showList = false; + } else if (session.showList === "no") { + session.showList = false; + } else if (session.showList === "off") { + session.showList = false; + } else { + session.showList = true; + } + } + //if (session.showList===true){ + // getById("hideusers").classList.add("hidden"); + //} + + if (urlParams.has('meshcast')) { + session.meshcast = urlParams.get('meshcast') || "any"; + meshcast(true); + } + if (urlParams.has('meshcastcode') || urlParams.has('mccode')) { + session.meshcastCode = urlParams.get('meshcastcode') || urlParams.get('mccode') || false + } + + if (urlParams.has('nomeshcast')) { + session.noMeshcast = urlParams.get('nomeshcast') || true; + } + + + var filename = false; + try { + if (!session.decrypted){ + filename = window.location.pathname.substring(window.location.pathname.lastIndexOf('/') + 1); + filename = filename.replace("??", "?"); + filename2 = filename.split("?")[0]; + // split at ??? + if (filename.split(".").length == 1) { + if (filename2.length < 2) { // easy win + filename = false; + } else if (filename.startsWith("&")) { // easy win + var tmpHref = window.location.href.substring(0, window.location.href.lastIndexOf('/')) + "/?" + filename.split("&").slice(1).join("&"); + log("TMP " + tmpHref); + updateURL(filename.split("&")[1], true, tmpHref); + filename = false; + } else if (filename2.split("&")[0].includes("=")) { + log("asdf " + filename.split("&")[0]); + if (history.pushState) { + var tmpHref = window.location.href.substring(0, window.location.href.lastIndexOf('/')); + tmpHref = tmpHref + "/?" + filename; + filename = false; + //warnUser("Please ensure your URL is correctly formatted."); + window.history.pushState({path: tmpHref.toString()}, '', tmpHref.toString()); + } + } else { + filename = filename2.split("&")[0]; + if (filename2 != filename) { + warnUser("Warning: Please ensure your URL is correctly formatted."); + } + } + } else { + filename = false; + } + log(filename); + } + } catch (e) { + errorlog(e); + } + + + var directorLanding = false; + if (session.director) { + if (session.director===true){ // room not specified. + directorLanding = true; + } + session.meterStyle = 1; + session.signalMeter = true; + session.batteryMeter = true; + } else if (filename === "director") { + directorLanding = true; + filename = false; + session.meterStyle = 1; + session.signalMeter = true; + session.batteryMeter = true; + } + + session.slotmode = false; // temporary; remove in the future TODO: ## ----------------------- + if (urlParams.has('slotmode') || urlParams.has('slotsmode')){ + session.slotmode = parseInt(urlParams.get('slotmode')) || parseInt(urlParams.get('slotsmode')) || 1; + } + + + if (urlParams.has('signalmeter')) { + session.signalMeter = urlParams.get('signalmeter'); + if (session.signalMeter === "false") { + session.signalMeter = false; + } else if (session.signalMeter=== "0") { + session.signalMeter = false; + } else if (session.signalMeter === "no") { + session.signalMeter = false; + } else if (session.signalMeter === "off") { + session.signalMeter = false; + } else { + session.signalMeter = true; + } + } + + if (urlParams.has('batterymeter')) { + session.batteryMeter = urlParams.get('batterymeter'); + if (session.batteryMeter === "false") { + session.batteryMeter = false; + } else if (session.batteryMeter=== "0") { + session.batteryMeter = false; + } else if (session.batteryMeter === "no") { + session.batteryMeter = false; + } else if (session.batteryMeter === "off") { + session.batteryMeter = false; + } else { + session.batteryMeter = true; + } + } + + if (urlParams.has('rooms')) { + session.rooms = urlParams.get('rooms').split(",").map(function(e) { + return sanitizeRoomName(e); + }); + getById("rooms").classList.remove('hidden'); + } + + if (urlParams.has('leaveorientationflag')) { + session.removeOrientationFlag = false; // leave `a=extmap:3 urn:3gpp:video-orientation\r\n` alone + } + + if (urlParams.has('showdirector') || urlParams.has('sd')) { + session.showDirector = parseInt(urlParams.get('showdirector')) || parseInt(urlParams.get('sd')) || true; // if 2, video only allowed. True or 1 will be video + audio allowed. + // fyi, true is the same as 1 when == is used, so assert(1==true) is true. + } + + if (urlParams.has('bitratecutoff') || urlParams.has('bitcut')) { + session.lowBitrateCutoff = parseInt(urlParams.get('bitratecutoff')) || parseInt(urlParams.get('bitcut')) || 300; // low bitrate cut off. + } + + if (urlParams.has('motionswitch') || urlParams.has('motiondetection')) { // switch OBS to this scene when there is motion, and "solo view" this video in the VDO.Ninja auto-mixer, if used + session.motionSwitch = parseInt(urlParams.get('motionswitch')) || parseInt(urlParams.get('motiondetection')) || 15; // threshold of motion needed to trigger + } + + + if (urlParams.has('locked')) { + session.locked = urlParams.get('locked'); + + if ((session.locked == 'portrait') || (session.locked == 'vertical')){ + session.locked = 9.0/16.0; + } else if (session.locked == 'landscape'){ + session.locked = 16.0/9.0; + } else if (session.locked == 'square'){ + session.locked = 1.0; + } else { + session.locked = parseFloat(session.locked) || 16/9.0; + } + } + + + if (urlParams.has('lowbitratescene') || urlParams.has('cutscene')) { + session.lowBitrateSceneChange = urlParams.get('lowbitratescene') || urlParams.get('cutscene') || "cutscene"; // low bitrate cut off. + if (session.lowBitrateCutoff===false){ + session.lowBitrateCutoff=300; + } + } + + if (urlParams.has('rotate') ) { + session.rotate = urlParams.get('rotate') || 90; + session.rotate = parseInt(session.rotate); + } + + if (urlParams.has("rotatewindow") || urlParams.has("rotatepage")){ + let rotateThis = parseInt(urlParams.get("rotatewindow")) || parseInt(urlParams.get("rotatepage")) || 90; + updateForceRotatedCSS(rotateThis); + } + + if (urlParams.has('facing') ) { + session.facingMode = urlParams.get('facing') || false; + } + if (session.facingMode){ + session.facingMode = session.facingMode.toLowerCase(); + if (session.facingMode == "user"){ + // + } else if (session.facingMode == "environment"){ + // + } else if (session.facingMode == "rear"){ + session.facingMode = "environment"; + } else if (session.facingMode == "back"){ + session.facingMode = "environment"; + } else if (session.facingMode == "front"){ + session.facingMode = "user"; + } else { + session.facingMode = false; + } + } + + // session.facingMode }; // user or environment + + if (urlParams.has('forcelandscape') || urlParams.has('forcedlandscape') || urlParams.has('fl')){ + session.orientation = "landscape"; + if (Firefox){ + session.fullscreen = true; // windowed mode complicates things in this mode + } + } else if (urlParams.has('forceportrait') || urlParams.has('forcedportrait')|| urlParams.has('fp')){ + session.orientation = "portrait"; + if (Firefox){ + session.fullscreen = true; // windowed mode complicates things in this mode + } + } + + if (urlParams.has('forceviewerlandscape')){ + session.keepIncomingVideosInLandscape = parseInt(urlParams.get('forceviewerlandscape')) || 270; + } + + + document.addEventListener('fullscreenchange', event => { + log("full screen change event"); + log(event); + + if (document.getElementById("previewWebcam")){ + return; + } + + if (session.orientation && session.mobile){ + if (document.fullscreenElement) { + document.exitFullscreen(); + getById("fullscreenPageToggle").classList.add("la-expand-arrows-alt"); + getById("fullscreenPageToggle").classList.remove("la-compress-arrows-alt"); + } + return; + } + if (document.fullscreenElement) { + getById("fullscreenPageToggle").classList.remove("la-expand-arrows-alt"); + getById("fullscreenPageToggle").classList.add("la-compress-arrows-alt"); + } else { + getById("fullscreenPageToggle").classList.add("la-expand-arrows-alt"); + getById("fullscreenPageToggle").classList.remove("la-compress-arrows-alt"); + + } + updateMixer(); + }); + + if (urlParams.has('fullscreenbutton') || urlParams.has('fsb')){ // just an alternative; might be compoundable + if (!(iOS || iPad)){ + session.fullscreenButton = true; + document.documentElement.style.setProperty('--full-screen-button', 'none'); + getById("fullscreenPage").classList.remove("hidden"); + } + } + + if (urlParams.has('pip2') || urlParams.has('pipall')){ // just an alternative; might be compoundable + if (typeof documentPictureInPicture !== "undefined"){ + getById("PictureInPicturePage").classList.remove("hidden"); + } + } + + if (urlParams.has('midi') || urlParams.has('hotkeys')) { + session.midiHotkeys = urlParams.get('midi') || urlParams.get ('hotkeys') || 1; + session.midiHotkeys = parseInt(session.midiHotkeys); + } + + if (urlParams.has('disablehotkeys')){ + session.disableHotKeys = true; + } + + if (urlParams.has('nohangupbutton') || urlParams.has('nohub')){ + getById("hangupbutton").style.display = "none"; + session.hangupbutton = false; + } + + if (urlParams.has('hangupbutton') || urlParams.has('hub') || urlParams.has('humb64')){ + session.hangupbutton = true; + } + if (urlParams.has('hangupmessage') || urlParams.has('hum') || urlParams.has('humb64')){ + let htmlmessage = urlParams.get("hangupmessage") || urlParams.get("hum") || urlParams.get('humb64'); + + if (urlParams.get('humb64')){ + try { + htmlmessage = atob(htmlmessage); + } catch(e){} + } + + try { + htmlmessage = htmlmessage.replace(/(\r\n|\n|\r)/gm, ''); + htmlmessage = decodeURIComponent(htmlmessage); + } catch(e){console.error(e);} + getById("hangupContainer").innerHTML = htmlmessage; + + + } + + if (urlParams.has('socialstream')){ + session.socialstream = urlParams.get('socialstream') || false; + } + + if (urlParams.has('midioffset')){ + session.midiOffset = urlParams.get('midioffset') || 0; + session.midiOffset = parseInt(session.midiOffset); + } + + if (urlParams.has('midiremote') || urlParams.has('remotemidi')){ + if (session.director!==false){ + session.midiRemote = parseInt(urlParams.get('midiremote')) || parseInt(urlParams.get ('remotemidi')) || 4; + } else { + session.midiRemote = parseInt(urlParams.get('midiremote')) || parseInt(urlParams.get ('remotemidi')) || 1; + } + } + + if (urlParams.has('midipush') || urlParams.has('midiout') || urlParams.has('mo')){ + session.midiOut = parseInt(urlParams.get('midipush')) || parseInt(urlParams.get('midiout')) || parseInt(urlParams.get('mo')) || true; + } + + if (urlParams.has('midipull') || urlParams.has('midiin') || urlParams.has('midin') || urlParams.has('mi')){ + session.midiIn = parseInt(urlParams.get('midipull')) || parseInt(urlParams.get('midiin')) || parseInt(urlParams.get('midin')) || parseInt(urlParams.get('mi')) || true; + } + + if (urlParams.has('mididelay')){ // midi-in delay + session.midiDelay = parseInt(urlParams.get('mididelay')) || 1000; // 1 second playout delay? acts as a buffer as well I guess. + } + + if (urlParams.has('midichannel')){ + session.midiChannel = parseInt(urlParams.get('midichannel')) || false; + } + if (session.midiChannel){ + session.midiChannel = parseInt(session.midiChannel); + if (session.midiChannel>16){session.midiChannel=false;} + if (session.midiChannel<1){session.midiChannel=false;} + } + if (urlParams.has('mididevice')){ + session.midiDevice = parseInt(urlParams.get('mididevice')) || false; + } + if (session.midiDevice){ + session.midiDevice = parseInt(session.midiDevice); + } + + if (directorLanding) { + getById("container-1").classList.remove('hidden'); + getById("container-1").classList.add("skip-animation"); + getById("container-1").classList.remove('pointer'); + } else if (session.director){ + // if a director, webcam/screenshare/iframe auto-defaults shouldn't work + } else if (urlParams.has('webcam') || urlParams.has('wc') || urlParams.has('miconly')) { + session.webcamonly = true; + session.screensharebutton = false; + if (urlParams.has('miconly')){ + session.videoDevice=0; + session.miconly = true; + miniTranslate(getById("add_camera"), "share-your-mic", "Share your Microphone"); + getById("container-3").title = getById("add_camera").innerText; + + getById("videoMenu").style.display = "none"; + getById("container-3").classList.add("microphoneBackground"); + getById("flipcamerabutton").style.setProperty("display", "none", "important"); + getById("mutevideobutton").style.setProperty("display", "none", "important"); + getById("videoMenu3").style.setProperty("display", "none", "important"); + getById("previewWebcam").classList.add("miconly"); + //if (session.consent){ + // setTimeout(function(){ + // warnUser("⚠ Privacy warning: The director of this room can remotely switch your camera or microphone without permission.", 8000); + // }, 1500); + //} + } + } else if (urlParams.has('screenshare') || urlParams.has('ss')) { + session.screenshare = true; + if (urlParams.get('screenshare') || urlParams.get('ss')){ + session.screenshare = urlParams.get('screenshare') || urlParams.get('ss'); + } + } else if (urlParams.has('fileshare') || urlParams.has('fs')) { + getById("container-5").classList.remove('hidden'); + getById("container-5").classList.add("skip-animation"); + getById("container-5").classList.remove('pointer'); + + getById("sharefilebutton").style.display = "flex"; // this might be obsolete? + getById("mediafileshare").classList.remove("hidden"); + + + if (SafariVersion){ + getById("safari_warning_fileshare").classList.remove('hidden'); + } else if (!Firefox){ + getById("chrome_warning_fileshare").classList.remove('hidden'); + } + + } else if (!session.director && (urlParams.has('website') || urlParams.has('iframe'))){ + getById("container-6").classList.remove('hidden'); + getById("container-6").classList.add("skip-animation"); + getById("container-6").classList.remove('pointer'); + session.website = urlParams.get('website') || urlParams.get('iframe') || false; + if (session.website){ + session.website = decodeURI(session.website); + delayedStartupFuncs.push([session.publishIFrame, session.website]); + } + } else if (urlParams.has('webcam2') || urlParams.has('wc2')) { + session.webcamonly = true; + session.screensharebutton = false; + session.introButton = true; + } else if (urlParams.has('screenshare2') || urlParams.has('ss2')) { + session.screenshare = true; + session.introButton = true; + if (urlParams.get('screenshare2') || urlParams.get('ss2')){ + session.screenshare = urlParams.get('screenshare2') || urlParams.get('ss2'); + } + } + + if (session.director && (urlParams.has('website') || urlParams.has('iframe'))){ + getById("container-6").classList.remove('hidden'); + getById("container-6").classList.add("skip-animation"); + getById("container-6").classList.remove('pointer'); + session.website = urlParams.get('website') || urlParams.get('iframe') || false; + if (session.website){ + session.website = decodeURI(session.website); + delayedStartupFuncs.push([shareWebsite, session.website]); + } + } + + if (urlParams.has('sstype') || urlParams.has('screensharetype')) { // wha type of screen sharing is used; track replace, iframe, or secondary try + session.screenshareType = urlParams.get('sstype') || urlParams.get('screensharetype'); + session.screenshareType = parseInt(session.screenshareType) || false; + } + + if (urlParams.has('suppresslocalaudio')){ + session.suppressLocalAudioPlayback = true; + } + if (urlParams.has('prefercurrenttab')){ + session.preferCurrentTab = true; + } + if (urlParams.has('selfbrowsersurface')){ // exclude + session.selfBrowserSurface = urlParams.get('selfbrowsersurface') || "exclude"; + } + if (urlParams.has('surfaceswitching')){ + session.surfaceSwitching = urlParams.get('surfaceswitching') || "exclude"; + } + if (urlParams.has('systemaudio')){ // exclude or exclude + session.systemAudio = urlParams.get('systemaudio') || "exclude"; + } + if (urlParams.has('displaysurface')){ // browser, window, or monitor (which is default selected) + session.displaySurface = urlParams.get('displaysurface') || "monitor"; + } + + if (urlParams.has('locksize')){ // browser, window, or monitor (which is default selected) + session.lockWindowSize = urlParams.get('locksize') || true; + } + + if (urlParams.has('intro') || urlParams.has('ib')) { + session.introButton = true; + } + + if (urlParams.has('volumecontrol') || urlParams.has('volumecontrols') || urlParams.has('vc')) { + if (!(iOS || iPad)){ + session.volumeControl = true; + } + } + if (urlParams.has('controlbarspace')){ + session.dedicatedControlBarSpace = true; + } else if (urlParams.has('nocontrolbarspace')){ + session.dedicatedControlBarSpace = false; + } + + if (urlParams.has('hidesolo') || urlParams.has('hs')){ + session.hidesololinks=true; + } + + if (urlParams.has('mute') || urlParams.has('muted') || urlParams.has('m')) { + session.muted = true; + } + + if (urlParams.has('hideguest') || urlParams.has('hidden')) { + session.directorVideoMuted = true; + } + + if (urlParams.has('videomute') || urlParams.has('videomuted') || urlParams.has('vm')) { + session.videoMutedFlag = true; + } + + if (urlParams.has('layout')) { + session.accept_layouts = true; + try { + session.layout = JSON.parse(decodeURIComponent(urlParams.get('layout'))) || JSON.parse(urlParams.get('layout')) || {}; + } catch(e){ + try { + session.layout = JSON.parse(urlParams.get('layout')) || {}; + } catch(e){ + session.layout = {}; + } + } + console.warn("Warning: If using &layout with &broadcast, only the director's video will appear in the custom layout, which is likely not intended."); + } + + if (urlParams.has('layouts')) { // an ordered array of layouts, which can be used to switch between using the API layouts action. + // ie: ?layouts=[[{"x":0,"y":0,"w":100,"h":100,"slot":0}],[{"x":0,"y":0,"w":100,"h":100,"slot":1}],[{"x":0,"y":0,"w":100,"h":100,"slot":2}],[{"x":0,"y":0,"w":100,"h":100,"slot":3}],[{"x":0,"y":0,"w":50,"h":100,"c":false,"slot":0},{"x":50,"y":0,"w":50,"h":100,"c":false,"slot":1}],[{"x":0,"y":0,"w":100,"h":100,"z":0,"c":false,"slot":1},{"x":70,"y":70,"w":30,"h":30,"z":1,"c":true,"slot":0}],[{"x":0,"y":0,"w":50,"h":50,"c":true,"slot":0},{"x":50,"y":0,"w":50,"h":50,"c":true,"slot":1},{"x":0,"y":50,"w":50,"h":50,"c":true,"slot":2},{"x":50,"y":50,"w":50,"h":50,"c":true,"slot":3}],[{"x":0,"y":16.667,"w":66.667,"h":66.667,"c":true,"slot":0},{"x":66.667,"y":0,"w":33.333,"h":33.333,"c":true,"slot":1},{"x":66.667,"y":33.333,"w":33.333,"h":33.333,"c":true,"slot":2},{"x":66.667,"y":66.667,"w":33.333,"h":33.333,"c":true,"slot":3}]] + try { + session.layouts = JSON.parse(decodeURIComponent(urlParams.get('layouts'))) || JSON.parse(urlParams.get('layouts')) || {}; + } catch(e){ + try { + session.layouts = JSON.parse(urlParams.get('layouts')) || false; + } catch(e){ + session.layouts = false; + } + } + } + + /* if (session.layout && session.layouts && (typeof session.layout !== "object") && parseInt(session.layout) && (session.layout == parseInt(session.layout))){ + try { + session.layout = session.layouts[session.layout-1]; + } catch(e){ + session.layout= false; + } + } */ + + if (urlParams.has('deaf') || urlParams.has('deafen')) { + session.directorSpeakerMuted=true; // false == true in this case. + } + + if (urlParams.has('blind')) { + session.directorDisplayMuted=true; // false == true in this case. + } + + if (urlParams.has('blindall')) { + session.directorBlindButton=true; // false == true in this case. + } + if (session.directorBlindButton){ + getById("blindAllGuests").classList.remove("hidden"); + } + + if (urlParams.has('dpi') || urlParams.has('dpr') || urlParams.has('sharper') || urlParams.has('sharpen')) { + session.devicePixelRatio = urlParams.get('dpi') || urlParams.get('dpr') || 2.0; + session.devicePixelRatio = parseFloat(session.devicePixelRatio); + } //else if (window.devicePixelRatio && window.devicePixelRatio!==1){ + // session.devicePixelRatio = window.devicePixelRatio; // this annoys me to no end. + //} + + if (urlParams.has('speakermute') || urlParams.has('speakermuted') || urlParams.has('mutespeaker') || urlParams.has('sm') || urlParams.has('ms')) { + + var checkState = urlParams.get('speakermute') || urlParams.get('speakermuted') || urlParams.get('mutespeaker') || urlParams.get('sm') || urlParams.get('ms') || true; + + if ( checkState === "false") { + session.speakerMuted = false; + } else if (checkState === "0") { + session.speakerMuted = false; + } else if (checkState === "no") { + session.speakerMuted = false; + } else if (checkState === "off") { + session.speakerMuted = false; + } else { + session.speakerMuted = true; + } + + session.speakerMuted_default = session.speakerMuted; + + if (session.speakerMuted){ + getById("mutespeakertoggle").className = "las la-volume-mute toggleSize"; + //getById("mutespeakerbutton").className="hidden float2 red"; + getById("mutespeakerbutton").classList.add("red"); + getById("mutespeakerbutton").classList.add("float2"); + getById("mutespeakerbutton").classList.remove("float"); + + var sounds = document.getElementsByTagName("video"); + for (var i = 0; i < sounds.length; ++i) { + sounds[i].muted = session.speakerMuted; + } + } + } + + if (urlParams.has('chatbutton') || urlParams.has('chat') || urlParams.has('cb')) { + session.chatbutton = urlParams.get('chatbutton') || urlParams.get('chat') || urlParams.get('cb') || null; + if (session.chatbutton === "false") { + session.chatbutton = false; + } else if (session.chatbutton === "0") { + session.chatbutton = false; + } else if (session.chatbutton === "no") { + session.chatbutton = false; + } else if (session.chatbutton === "off") { + session.chatbutton = false; + } else { + session.chatbutton = true; + getById("chatbutton").classList.remove("hidden"); + } + } + + if (urlParams.has('app')){ // midi-in delay + session.screenshare = false; + getById("container-2").classList.add('hidden'); + getById("logoname").classList.add('hidden'); + getById("head1a").classList.remove('hidden'); + getById("main").classList.add('appmode'); + getById("jumptoroomButton").innerText = "Join Room"; + + if (getStorage("jumptoURL")){ + getById('joinbyURL').value = getStorage("jumptoURL"); + } + } + + if (session.screenshare !== false) { + if (session.introButton){ + getById("container-3").className = 'column columnfade hidden'; // Hide screen share + getById("head1").className = 'hidden'; + } else { + getById("container-3").className = 'column columnfade hidden'; // Hide webcam + getById("container-2").classList.add("skip-animation"); + getById("container-2").classList.remove('pointer'); + } + } + + if (urlParams.has('manual')) { + session.manual = true; + } + + if (urlParams.has('hands') || urlParams.has('hand')) { + session.raisehands = true; + } + + if (urlParams.has('portrait') || urlParams.has('916') || urlParams.has('vertical')) { // playback aspect ratio + session.aspectRatio = 1; // 9:16 (default of 0 is 16:9) + } else if (urlParams.has('square') || urlParams.has('11')) { + session.aspectRatio = 2; //1:1 ? + } else if (urlParams.has('43')) { + session.aspectRatio = 3; //1:1 ? + } + + if (urlParams.has('structure')) { + session.structure = true; + } + + + + if (urlParams.has('aspectratio') || urlParams.has('ar')) { // capture aspect ratio + session.forceAspectRatio = urlParams.get('aspectratio') || urlParams.get('ar') || false; + if (session.forceAspectRatio){ + if ((session.forceAspectRatio == 'portrait') || (session.forceAspectRatio == 'vertical')){ + session.forceAspectRatio = 9.0/16.0; + } else if (session.forceAspectRatio == 'landscape'){ + session.forceAspectRatio = 16.0/9.0; + } else if (session.forceAspectRatio == 'square'){ + session.forceAspectRatio = 1.0; + } else { + session.forceAspectRatio = parseFloat(session.forceAspectRatio) || false; + } + } + } + if (urlParams.has('screenshareaspectratio') || urlParams.has('ssar')) { // capture aspect ratio + session.forceScreenShareAspectRatio = urlParams.get('screenshareaspectratio') || urlParams.get('ssar') || 16.0/9.0; + if (session.forceScreenShareAspectRatio){ + if ((session.forceScreenShareAspectRatio == 'portrait') || (session.forceScreenShareAspectRatio == 'vertical')){ + session.forceScreenShareAspectRatio = 9.0/16.0; + } else if (session.forceScreenShareAspectRatio == 'landscape'){ + session.forceScreenShareAspectRatio = 16.0/9.0; + } else if (session.forceScreenShareAspectRatio == 'square'){ + session.forceScreenShareAspectRatio = 1.0; + } else { + session.forceScreenShareAspectRatio = parseFloat(session.forceScreenShareAspectRatio) || false; + } + } + } + + if (urlParams.has('crop')){ + var crop = parseFloat(urlParams.get('crop')) || 0; + if (crop>0){ + session.forceAspectRatio = 1.7777777778 * (crop/100); + } else if (crop<0){ + session.forceAspectRatio = 1.7777777778 / (crop/100); + } else { + session.forceAspectRatio = 1.3333333333; + } + } + + if (urlParams.has('cover')) { + session.cover = true; + document.documentElement.style.setProperty('--fit-style', 'cover'); + document.documentElement.style.setProperty('--myvideo-max-width', '100vw'); + document.documentElement.style.setProperty('--myvideo-width', '100vw'); + document.documentElement.style.setProperty('--myvideo-height', '100vh'); + } + + if (urlParams.has('record')) { + if (!(session.cleanOutput)) { + if (SafariVersion && !MediaRecorder) { + if (macOS){ + warnUser("Your browser may not support local media recording.\n\nTry Chrome instead if on macOS."); + } else { + warnUser("Your browser or device may not support local media recording.\n\nSafari sometimes allows the feature to be enabled via its experimental settings."); + } + } else if (SafariVersion){ + if (macOS){ + warnUser("It is recommended to use Chrome instead of Safari if doing local media recordings."); + } else { + warnUser("Local media recordings are an experimental feature on Apple devices.\n\nPlease at least test it out a few times first."); + } + } + } + session.recordLocal = urlParams.get('record'); + + if ((session.recordLocal==="false") || (session.recordLocal==="off")){ + session.record = false; + session.recordLocal = false; + } else if (session.recordLocal != parseInt(session.recordLocal)) { + session.recordLocal = 6000; + } else { + session.recordLocal = parseInt(session.recordLocal); + } + } + + if (session.record === false){ + getById("recordLocalbutton").classList.add("hidden"); + getById("recordLocalScreenbutton").classList.add("hidden"); + try{ + document.querySelectorAll('[data-action-type^="record"]').forEach(ele=>{ele.remove();delete ele;}); + document.querySelectorAll('[data-action="Record"]').forEach(ele=>{ele.parentNode.remove();delete ele.parentNode;}); + } catch(e){ + errorlog(e); + } + } + + if (urlParams.has('autorecord')) { + session.autorecord=true; + if (session.recordLocal===false){ + let bitautorec = urlParams.get('autorecord'); + if (bitautorec!==null){ + session.recordLocal = parseInt(bitautorec); + } else { + session.recordLocal = 6000; + } + } + } + if (urlParams.has('autorecordlocal')) { + session.autorecordlocal=true; + if (session.recordLocal===false){ + session.recordLocal = urlParams.get('autorecordlocal'); + if (session.recordLocal != parseInt(session.recordLocal)) { + session.recordLocal = 6000; + } else { + session.recordLocal = parseInt(session.recordLocal); + } + } + } + if (urlParams.has('autorecordremote')) { + session.autorecordremote=true; + if (session.recordLocal===false){ + session.recordLocal = urlParams.get('autorecordremote'); + if (session.recordLocal != parseInt(session.recordLocal)) { + session.recordLocal = 6000; + } else { + session.recordLocal = parseInt(session.recordLocal); + } + } + } + + if (urlParams.has('pcm')) { + session.pcm = true; + } + if (urlParams.has('recordcodec') || urlParams.has('rc')) { + session.recordingVideoCodec = urlParams.get('recordcodec') || urlParams.get('rc') || false; + } + + if (urlParams.has('bigbutton')) { + session.bigmutebutton = true; + getById("mutebutton").style.bottom = "100px"; + getById("mutebutton").style.padding = "100px"; + getById("mutebutton").style.position = "fixed"; + getById("mutetoggle").style.bottom = "20px"; + getById("mutetoggle").style.right = "0"; + getById("mutetoggle").style.top = "unset"; + + } + + if (urlParams.has('nosettings')){ + session.nosettings = true; + getById("settingsbutton").classList.add("hidden"); + } + + if (urlParams.has('publish')){ + session.publish = true; + getById("publishSettings").style.display = "block"; + } + + if (urlParams.has('nopush') || urlParams.has('noseed') || urlParams.has('viewonly') || urlParams.has('viewmode')) { // this is like a scene; Seeding is disabled. Can be used with &showall to show all videos on load + session.doNotSeed=true; + + if (session.scene===false){ + session.scene = null; // not a scene, but sorta. false vs null makes a difference here. + } + + session.dataMode = true; // thios will let us connect + // session.showall = true; // this can be used to SHOW the videos. (&showall) + } + + if (urlParams.has('scene') || urlParams.has('scn')) { + session.scene = urlParams.get('scene') || urlParams.get('scn') || 0; + if (typeof session.scene === "string"){ + session.scene = session.scene.replace(/[\W]+/g, "_"); + } else { + session.scene = (parseInt(session.scene) || 0) + ""; + } + } + + if (urlParams.has('solo')){ + if (session.scene===false){ + session.scene = "0"; + } + session.solo = true; + } + + if (session.scene!==false){ + session.disableWebAudio = true; + if (session.audioEffects===null){ + session.audioEffects = false; + } + session.audioMeterGuest = false; + } + + + if (urlParams.has('fakeuser')) { + log("ICE FILTER ENABLED"); + session.fakeUser = true; + session.dataMode = true; + session.autostart = true; + session.novideo = []; + session.noaudio = []; + session.noiframe = []; + session.cleanOutput=true; + } + + if (urlParams.has('datamode') || urlParams.has('dataonly')) { // this disables all media in/out. + session.dataMode = true; + } + + if (session.dataMode){ + + if (!(session.meshcast || (session.whipOutput!==false) || session.screenshare)){ + session.videoDevice = 0; + session.audioDevice = 0; + } + + getById("mainmenu").classList.add("hidden"); + //session.autohide = true; + //session.autostart = true; + //session.novideo = []; + //session.noaudio = []; + //session.noiframe = []; + //session.webcamonly = true; + } + + + + if (urlParams.has('autoadd')) { // the streams we want to view; if set, but let blank, we will request no streams to watch. + session.autoadd = urlParams.get('autoadd') || null; // this value can be comma seperated for multiple streams to pull + + if (session.autoadd == null) { + session.autoadd = false; + } + if (session.autoadd) { + session.autoadd = session.autoadd.split(","); + } + } + + //if (session.scene!=="1"){ // scene =0 and 1 should load instantly. + // session.hiddenSceneViewBitrate = 0; // By default this is ~ 400kbps, but if you have 10 scenes, i don't want to kill things. + //} + + if (urlParams.has('hiddenscenebitrate')) { + session.hiddenSceneViewBitrate = parseInt(urlParams.get('hiddenscenebitrate')) || 0; + } + + if (urlParams.has('preloadbitrate')) { + session.preloadbitrate = parseInt(urlParams.get('preloadbitrate')) || 0; // 1000 + } + + if (urlParams.has('rampuptime')) { + session.rampUpTime = parseInt(urlParams.get('rampuptime')) || 10000; + } + + if (urlParams.has('scenetype') || urlParams.has('type')) { + session.sceneType = parseInt(urlParams.get('scenetype')) || parseInt(urlParams.get('type')) || false; + } + + if (urlParams.has('mediasettings')) { + session.forceMediaSettings = true; + } + + if (urlParams.has('transcript') || urlParams.has('transcribe') || urlParams.has('trans')) { + session.transcript = urlParams.get('transcript') || urlParams.get('transcribe') || urlParams.get('trans') || "en-US"; + } + + + if (urlParams.has('cc') || urlParams.has('closedcaptions') || urlParams.has('captions')) { + session.closedCaptions = true; + } + + if (urlParams.has('css')){ + var cssURL = urlParams.get('css'); + cssURL = decodeURI(cssURL); + log(cssURL); + var cssStylesheet = document.createElement('link'); + cssStylesheet.rel = 'stylesheet'; + cssStylesheet.type = 'text/css'; + cssStylesheet.media = 'screen'; + cssStylesheet.href = cssURL; + document.getElementsByTagName('head')[0].appendChild(cssStylesheet); + + cssStylesheet.onload = function() { + getById("main").classList.remove('hidden'); + log("loaded remote style sheet"); + }; + + cssStylesheet.onerror = function() { + getById("main").classList.remove('hidden'); + errorlog("REMOTE STYLE SHEET HAD ERROR"); + }; + + } else { + getById("main").classList.remove('hidden'); + } + + if (urlParams.has('avatar')){ + var avatar = urlParams.get('avatar') || false; + if (avatar && (avatar=="default")){ + session.avatar = document.getElementById("defaultAvatar2"); + session.avatar.ready=false; + session.avatar.onload = () => { + session.avatar.ready = true; + getById("noAvatarSelected3").classList.remove("selected"); + getById("noAvatarSelected").classList.remove("selected"); + getById("defaultAvatar1").classList.add("selected"); + getById("defaultAvatar2").classList.add("selected"); + }; + if (session.avatar.complete){ + session.avatar.ready = true; + getById("noAvatarSelected3").classList.remove("selected"); + getById("noAvatarSelected").classList.remove("selected"); + getById("defaultAvatar1").classList.add("selected"); + getById("defaultAvatar2").classList.add("selected"); + } + } else if (avatar){ + try { + avatar = decodeURIComponent(avatar); + }catch(e){} + + session.avatar = getById("defaultAvatar2"); + session.avatar.ready = false; + session.avatar.onload = () => { + session.avatar.ready = true; + getById("noAvatarSelected3").classList.remove("selected"); + getById("noAvatarSelected").classList.remove("selected"); + getById("defaultAvatar1").classList.add("selected"); + getById("defaultAvatar2").classList.add("selected"); + }; + getById("defaultAvatar1").src = avatar; + getById("defaultAvatar2").src = avatar; + + } + getById("avatarDiv3").classList.remove("hidden"); + getById("avatarDiv").classList.remove("hidden"); + } + + if (urlParams.has('prompt') || urlParams.has('validate') || urlParams.has('approve')){ + session.promptAccess = true; + } + + if (urlParams.has('js')){ // ie: &js=https%3A%2F%2Fvdo.ninja%2Fexamples%2Ftestjs.js + console.warn("Third-party Javascript has been injected into the code. Security cannot be ensured."); + var jsURL = urlParams.get('js'); + jsURL = decodeURI(jsURL); + log(jsURL); + // type="text/javascript" crossorigin="anonymous" + var externalJavaascript = document.createElement('script'); + externalJavaascript.type = 'text/javascript'; + externalJavaascript.crossorigin = 'anonymous'; + externalJavaascript.src = jsURL; + externalJavaascript.onerror = function() { + warnlog("Third-party Javascript failed to load"); + }; + externalJavaascript.onload = function() { + log("Third-party Javascript loaded"); + }; + document.head.appendChild(externalJavaascript); + } + + if (urlParams.has("base64js") || urlParams.has("b64js") || urlParams.has("jsbase64") || urlParams.has("jsb64")) { + try { + var base64js = urlParams.get("base64js") || urlParams.get("b64js") || urlParams.get("jsbase64") || urlParams.get("jsb64"); + base64js = decodeURIComponent(atob(base64js)); // window.btoa(encodeURIComponent("alert('hi')")); // ?jsb64=YWxlcnQoJ2hpJyk7 + var externalJavaascript = document.createElement('script'); + externalJavaascript.type = 'text/javascript'; + externalJavaascript.crossorigin = 'anonymous'; + externalJavaascript.innerHTML = base64js; + externalJavaascript.onerror = function() { + errorlog("Third-party Javascript failed to load"); + }; + externalJavaascript.onload = function() { + log("Third-party Javascript loaded"); + }; + document.head.appendChild(externalJavaascript); + } catch(e){console.error(e);} + }; + + if (urlParams.has("base64css") || urlParams.has("b64css") || urlParams.has("cssbase64") || urlParams.has("cssb64")) { + try { + var base64Css = urlParams.get("base64css") || urlParams.get("b64css") || urlParams.get("cssbase64") || urlParams.get("cssb64"); + var css = decodeURIComponent(atob(base64Css)); // window.btoa(encodeURIComponent("#mainmenu{background-color: pink; ❤" )); + var cssStyleSheet = document.createElement("style"); + cssStyleSheet.innerText = css; + document.querySelector("head").appendChild(cssStyleSheet); + } catch(e){console.error(e);} + }; + + session.sitePassword = session.defaultPassword; + if (urlParams.has('password') || urlParams.has('pass') || urlParams.has('pw') || urlParams.has('p')) { + session.password = urlParams.get('password') || urlParams.get('pass') || urlParams.get('pw') || urlParams.get('p'); + + if (!session.password) { + window.focus(); + session.password = await promptAlt(getTranslation("enter-password"), true, true); + if (session.password){ + session.password = session.password.trim(); + } + } else if (session.password === "false") { + session.password = false; + session.defaultPassword = false; + } else if (session.password === "0") { + session.password = false; + session.defaultPassword = false; + } else if (session.password === "off") { + session.password = false; + session.defaultPassword = false; + } else { + try { + session.password = decodeURIComponent(session.password); // will be re-encoded in a moment. + } catch(e){errorlog(e);} + } + } else if (urlParams.has('nopassword') || urlParams.has('nopass') || urlParams.has('nopw') || urlParams.has('p0')) { + session.password = false; + session.defaultPassword = false; + } + + if (session.password) { + getById("passwordRoom").value = session.password; + session.password = sanitizePassword(session.password); + session.defaultPassword = false; + getById("addPasswordBasic").style.display = "none"; + } + + if (urlParams.has('salt') && urlParams.get('salt')){ + session.salt = urlParams.get('salt'); + } + + if (urlParams.has('showconnections')){ + session.showConnections = true; // shows remote guest connections as a stat + } + + if (urlParams.has('hash') || urlParams.has('crc') || urlParams.has('check')) { // could be brute forced in theory, so not as safe as just not using a hash check. + session.taintedSession = null; // waiting to see if valid or not. + var hash_input = urlParams.get('hash') || urlParams.get('crc') || urlParams.get('check'); + if (session.password === false) { + window.focus(); + session.password = await promptAlt(getTranslation("enter-password-2"), true, true); + session.password = sanitizePassword(session.password); + getById("passwordRoom").value = session.password; + session.defaultPassword = false; + } + + generateHash(session.password + session.salt, 6).then(function(hash) { // million to one error. + log("hash is " + hash); + if (hash.substring(0, 4) !== hash_input) { // hash crc checks are just first 4 characters. + generateHash(session.password + "obs.ninja", 6).then(function(hash2) { // million to one error. + log("hash2 is " + hash2); + if (hash2.substring(0, 4) !== hash_input) { // hash crc checks are just first 4 characters. + session.taintedSession = true; + if (!(session.cleanOutput)) { + miniTranslate(getById("request_info_prompt"),"password-incorrect"); + //getById("request_info_prompt").innerHTML = getTranslation("password-incorrect"); + getById("request_info_prompt").style.display = "block"; + getById("mainmenu").style.display = "none"; + getById("head1").style.display = "none"; + session.cleanOutput = true; + + } else { + getById("request_info_prompt").innerHTML = ""; + getById("request_info_prompt").style.display = "block"; + getById("mainmenu").style.display = "none"; + getById("head1").style.display = "none"; + } + } else { + session.taintedSession = false; + session.hash = hash; + } + }).catch(errorlog); + } else { + session.taintedSession = false; + session.hash = hash; + } + }).catch(errorlog); + } + + if (session.defaultPassword !== false) { + session.password = session.defaultPassword; // no user entered password; let's use the default password if its not disabled. + } + + if (urlParams.has('showlabels') || urlParams.has('showlabel') || urlParams.has('sl')) { + session.showlabels = urlParams.get('showlabels') || urlParams.get('showlabel') || urlParams.get('sl') || ""; + session.showlabels = sanitizeLabel(session.showlabels.replace(/[\W]+/g, "_").replace(/_+/g, '_')); + //session.style = 6; + + if (session.showlabels == "") { + session.labelstyle = false; + } else { + session.labelstyle = session.showlabels; + } + + session.showlabels = true; + } + + if (urlParams.has('sizelabel') || urlParams.has('labelsize') || urlParams.has('fontsize')) { + session.labelsize = urlParams.get('sizelabel') || urlParams.get('labelsize') || urlParams.get('fontsize') || 100; + session.labelsize = parseInt(session.labelsize); + } + + if (urlParams.has('label') || urlParams.has('l')) { + session.label = urlParams.get('label') || urlParams.get('l') || null; + var updateURLAsNeed = true; + if (session.label == null || session.label.length == 0) { + window.focus(); + session.label = await promptAlt(getTranslation("enter-display-name"), true); + } else { + var updateURLAsNeed = false; + try { + session.label = decodeURIComponent(session.label); + } catch(e){errorlog(e);} + session.label = session.label.replace(/_/g, " ") + } + if (session.label != null) { + session.label = sanitizeLabel(session.label); // alphanumeric was too strict. + document.title = session.label; // what the result is. + + if (updateURLAsNeed) { + var label = encodeURIComponent(session.label); + if (urlParams.has('l')) { + updateURL("l=" + label, true, false); + } else { + updateURL("label=" + label, true, false); + } + } + } + } else if (urlParams.has('defaultlabel') || urlParams.has('labelsuggestion') || urlParams.has('ls')) { + session.label = urlParams.get('defaultlabel') || urlParams.get('labelsuggestion') || urlParams.get('ls') || null; + var updateURLAsNeed = true; + window.focus(); + var label = await promptAlt(getTranslation("enter-display-name"), true); + if (label) { + session.label = sanitizeLabel(label); // alphanumeric was too strict. + } else { + session.label = sanitizeLabel(session.label); + updateURLAsNeed = false; + } + + document.title = session.label; // what the result is. + + if (updateURLAsNeed) { + var label = encodeURIComponent(session.label); + if (urlParams.has('l')) { + updateURL("l=" + label, true, false); + } else { + updateURL("label=" + label, true, false); + } + } + } + + + if (urlParams.has('transparent') || urlParams.has('transparency')) { // sets the window to be transparent - useful for IFRAMES? + session.transparent=true; + } + + if (session.transparent){ + getById("main").style.backgroundColor = "rgba(0,0,0,0)"; + document.documentElement.style.setProperty('--container-color', '#0000'); + document.documentElement.style.setProperty('--background-color', '#0000'); + document.documentElement.style.setProperty('--regular-margin', '0'); + document.documentElement.style.setProperty('--director-margin', '0 25px 0 0'); + document.documentElement.style.setProperty('--discord-grey-1a', '#0000'); + getById("directorLinksButton").style.color = "black"; + getById("main").style.overflow = "hidden"; + } + + if (urlParams.has('stereo') || urlParams.has('s') || urlParams.has('proaudio')) { // both peers need this enabled for HD stereo to be on. If just pub, you get no echo/noise cancellation. if just viewer, you get high bitrate mono + log("STEREO ENABLED"); + session.stereo = urlParams.get('stereo') || urlParams.get('s') || urlParams.get('proaudio'); + + if (session.stereo) { + session.stereo = session.stereo.toLowerCase(); + } + + //var supportedConstraints = navigator.mediaDevices.getSupportedConstraints(); + //supportedConstraints.channelCount; + + if (session.stereo === "false") { + session.stereo = 0; + session.audioInputChannels = 1; + } else if (session.stereo === "0") { + session.stereo = 0; + session.audioInputChannels = 1; + } else if (session.stereo === "no") { + session.stereo = 0; + session.audioInputChannels = 1; + } else if (session.stereo === "off") { + session.stereo = 0; + session.audioInputChannels = 1; + } else if (session.stereo === "1") { + session.stereo = 1; + } else if (session.stereo === "both") { + session.stereo = 1; + } else if (session.stereo === "3") { + session.stereo = 3; + } else if (session.stereo === "out") { + session.stereo = 3; + } else if (session.stereo === "mono") { + session.stereo = 3; + session.audiobitrate = 128; + } else if (session.stereo === "4") { + session.stereo = 4; + } else if (session.stereo === "multi") { + session.stereo = 4; + } else if (session.stereo === "2") { + session.stereo = 2; + } else if (session.stereo === "6") { + session.stereo = 6; + } else if (session.stereo === "in") { + session.stereo = 2; + } else { + session.stereo = 5; // guests; no stereo in, no high bitrate in, but otherwise like stereo=1 + } + + getById("whipoutstereo").classList.add("hidden"); + } + + if (urlParams.has('screensharestereo') || urlParams.has('sss') || urlParams.has('ssproaudio')) { // both peers need this enabled for HD stereo to be on. If just pub, you get no echo/noise cancellation. if just viewer, you get high bitrate mono + log("screenshare stereo ENABLED"); + session.screenshareStereo = urlParams.get('screensharestereo') || urlParams.get('sss') || urlParams.get('ssproaudio'); + + if (session.screenshareStereo) { + session.screenshareStereo = session.screenshareStereo.toLowerCase(); + } + + if (session.screenshareStereo === "false") { + session.screenshareStereo = 0; + } else if (session.screenshareStereo === "0") { + session.screenshareStereo = 0; + } else if (session.screenshareStereo === "no") { + session.screenshareStereo = 0; + } else if (session.screenshareStereo === "off") { + session.screenshareStereo = 0; + } else if (session.screenshareStereo === "1") { + session.screenshareStereo = 1; + } else if (session.screenshareStereo === "both") { + session.screenshareStereo = 1; + } else if (session.screenshareStereo === "3") { + session.screenshareStereo = 3; + } else if (session.screenshareStereo === "out") { + session.screenshareStereo = 3; + } else if (session.screenshareStereo === "mono") { + session.screenshareStereo = 3; + } else if (session.screenshareStereo === "4") { + session.screenshareStereo = 4; + } else if (session.screenshareStereo === "multi") { + session.screenshareStereo = 4; + } else if (session.screenshareStereo === "2") { + session.screenshareStereo = 2; + } else if (session.screenshareStereo === "in") { + session.screenshareStereo = 2; + } else { + session.screenshareStereo = 5; // guests; no stereo in, no high bitrate in, but otherwise like stereo=1 + } + } + + + // Deploy your own handshake server for free; see: https://github.com/steveseguin/websocket_server + if (urlParams.has('pie')){ // piesocket.com support is to be deprecated after dec/19/21, since piesocket is no longer a free service. + session.customWSS = urlParams.get('pie') || true; // If session.customWSS == true, then there is no need to set parameters via URL + session.wssSetViaUrl = true; + if (session.customWSS && (session.customWSS!==true)){ + session.wss = "wss://free3.piesocket.com/v3/1?api_key="+session.customWSS; // if URL param is set, it will use the API key. + } + } + + + + if (Firefox && !session.stereo || (session.stereo === 3)){ + session.mono = true; // this will set the SDP to mono if firefox + } + + if (urlParams.has('mono')) { + session.mono = true; + if ((session.stereo == 1) || (session.stereo == 4)) { + session.stereo = 3; + session.audiobitrate = 128; + } else if (session.stereo == 5) { + session.stereo = 3; // stereo out only + session.audiobitrate = 128; + } else if (session.stereo == 2) { + session.stereo = 0; + session.audiobitrate = 128; + } + } + + if ((session.stereo == 1) || (session.stereo == 3) || (session.stereo == 4) || (session.stereo == 5)) { + session.echoCancellation = false; + session.autoGainControl = false; + session.noiseSuppression = false; + } + + if (urlParams.has("channelcount") || urlParams.has("ac") || urlParams.has("inputchannels")) { // if updates to this, see also function toggleMonoStereoMic() + session.audioInputChannels = urlParams.get('channelcount') || urlParams.get('ac') || urlParams.get('inputchannels') || 0; + session.audioInputChannels = parseInt(session.audioInputChannels); + if (!session.audioInputChannels) { + session.audioInputChannels = false; + } + } else if (urlParams.has("monomic")){ + session.audioInputChannels = 1; + } + + if ((session.stereo === 5) && !session.audioInputChannels){ // allow the guest to set their mic to mono. + document.querySelectorAll(".gear_microphone").forEach(ele=>{ + ele.classList.remove("hidden"); + }); + } + + + if (urlParams.has("echocancellation") || urlParams.has("aec") || urlParams.has("ec")) { + + session.echoCancellation = urlParams.get("echocancellation") || urlParams.get('aec') || urlParams.get('ec'); + + if (session.echoCancellation) { + session.echoCancellation = session.echoCancellation.toLowerCase(); + } + if (session.echoCancellation == "false") { + session.echoCancellation = false; + } else if (session.echoCancellation == "0") { + session.echoCancellation = false; + } else if (session.echoCancellation == "no") { + session.echoCancellation = false; + } else if (session.echoCancellation == "off") { + session.echoCancellation = false; + } else { + session.echoCancellation = true; + } + } + + + if (urlParams.has("autogain") || urlParams.has("ag") || urlParams.has("agc")) { + + session.autoGainControl = urlParams.get('autogain') || urlParams.get('ag') || urlParams.get('agc'); + if (session.autoGainControl) { + session.autoGainControl = session.autoGainControl.toLowerCase(); + } + if (session.autoGainControl == "false") { + session.autoGainControl = false; + } else if (session.autoGainControl == "0") { + session.autoGainControl = false; + } else if (session.autoGainControl == "no") { + session.autoGainControl = false; + } else if (session.autoGainControl == "off") { + session.autoGainControl = false; + } else { + session.autoGainControl = true; + } + } + + if (urlParams.has("denoise") || urlParams.has("dn")) { + + session.noiseSuppression = urlParams.get('denoise') || urlParams.get('dn'); + + if (session.noiseSuppression) { + session.noiseSuppression = session.noiseSuppression.toLowerCase(); + } + if (session.noiseSuppression == "false") { + session.noiseSuppression = false; + } else if (session.noiseSuppression == "0") { + session.noiseSuppression = false; + } else if (session.noiseSuppression == "no") { + session.noiseSuppression = false; + } else if (session.noiseSuppression == "off") { + session.noiseSuppression = false; + } else { + session.noiseSuppression = true; + } + } + + if (session.noiseSuppression!==null){ + getById("whipoutdenoise").classList.add("hidden"); + } + if (session.autoGainControl!==null){ // should be the last + getById("whipoutautogain").classList.add("hidden"); + } + + if (urlParams.has("screenshareaec") || urlParams.has("ssec") || urlParams.has("ssaec")) { + + session.screenshareAEC = urlParams.get('screenshareaec') || urlParams.get('ssec') || urlParams.get("ssaec"); + + if (session.screenshareAEC) { + session.screenshareAEC = session.screenshareAEC.toLowerCase(); + } + if (session.screenshareAEC == "false") { + session.screenshareAEC = false; + } else if (session.screenshareAEC == "0") { + session.screenshareAEC = false; + } else if (session.screenshareAEC == "no") { + session.screenshareAEC = false; + } else if (session.screenshareAEC == "off") { + session.screenshareAEC = false; + } else { + session.screenshareAEC = true; + } + } + if (urlParams.has("screenshareautogain") || urlParams.has("ssag") || urlParams.has("ssagc")) { + + session.screenshareAutogain = urlParams.get('screenshareautogain') || urlParams.get('ssag') || urlParams.get('ssagc'); + if (session.screenshareAutogain) { + session.screenshareAutogain = session.screenshareAutogain.toLowerCase(); + } + if (session.screenshareAutogain == "false") { + session.screenshareAutogain = false; + } else if (session.screenshareAutogain == "0") { + session.screenshareAutogain = false; + } else if (session.screenshareAutogain == "no") { + session.screenshareAutogain = false; + } else if (session.screenshareAutogain == "off") { + session.screenshareAutogain = false; + } else { + session.screenshareAutogain = true; + } + } + if (urlParams.has("screensharedenoise") || urlParams.has("ssdn")) { + + session.screenshareDenoise = urlParams.get('screensharedenoise') || urlParams.get('ssdn'); + + if (session.screenshareDenoise) { + session.screenshareDenoise = session.screenshareDenoise.toLowerCase(); + } + if (session.screenshareDenoise == "false") { + session.screenshareDenoise = false; + } else if (session.screenshareDenoise == "0") { + session.screenshareDenoise = false; + } else if (session.screenshareDenoise == "no") { + session.screenshareDenoise = false; + } else if (session.screenshareDenoise == "off") { + session.screenshareDenoise = false; + } else { + session.screenshareDenoise = true; + } + } + + + if (urlParams.has('roombitrate') || urlParams.has('roomvideobitrate') || urlParams.has('rbr')) { + log("Room BITRATE SET"); + session.roombitrate = urlParams.get('roombitrate') || urlParams.get('rbr') || urlParams.get('roomvideobitrate'); + session.roombitrate = parseInt(session.roombitrate); + if (session.roombitrate < 1) { + session.roombitrate = 0; + } + } + + if ( urlParams.has('outboundaudiobitrate') || urlParams.has('oab')) { + session.outboundAudioBitrate = parseInt(urlParams.get('outboundaudiobitrate')) || parseInt(urlParams.get('oab')) || false; + } + if (urlParams.has('outboundvideobitrate') || urlParams.has('outboundbitrate') || urlParams.has('ovb')) { + session.outboundVideoBitrate = parseInt(urlParams.get('outboundvideobitrate')) || parseInt(urlParams.get('outboundbitrate')) || parseInt(urlParams.get('ovb')) || false; + } + + if (urlParams.has('webp') || urlParams.has('images')){ // deprecicating this. chunked mode will replace it. + session.webp = urlParams.get('webp') || urlParams.get('images') || "webp"; + } + + if (urlParams.has('webpquality') || urlParams.has('webpq') || urlParams.has('wq')){ + session.webPquality = parseInt(urlParams.get('webpquality')) || parseInt(urlParams.get('webpq')) || parseInt(urlParams.get('wq')) || 4; + } + + + if (urlParams.has('audiobitrate') || urlParams.has('ab')) { // both peers need this enabled for HD stereo to be on. If just pub, you get no echo/noise cancellation. if just viewer, you get high bitrate mono + log("AUDIO BITRATE SET"); + session.audiobitrate = urlParams.get('audiobitrate') || urlParams.get('ab'); + session.audiobitrate = parseInt(session.audiobitrate); + if (session.audiobitrate < 1) { + session.audiobitrate = false; + } else if (session.audiobitrate > 510) { + session.audiobitrate = 510; + } // this is to just prevent abuse + } + if ((iOS) || (iPad)) { + session.audiobitrate = false; // iOS devices seem to get distortion with custom audio bitrates. Disable for now. + } + + /* if (urlParams.has('whitebalance') || urlParams.has('temp')){ // Need to be applied after the camera is selected. bleh. not enforcible. remove for now. + var temperature = urlParams.get('whitebalance') || urlParams.get('temp'); + try{ + updateCameraConstraints('colorTemperature', parseFloat(temperature)); + } catch (e){errorlog(e);} + } */ + + + + if (urlParams.has('streamid') || urlParams.has('view') || urlParams.has('v') || urlParams.has('pull')) { // the streams we want to view; if set, but let blank, we will request no streams to watch. + session.view = urlParams.get('streamid') || urlParams.get('view') || urlParams.get('v') || urlParams.get('pull') || null; // this value can be comma seperated for multiple streams to pull + + getById("headphonesDiv2").style.display = "inline-block"; + getById("headphonesDiv").style.display = "inline-block"; + getById("addPasswordBasic").style.display = "none"; + + + if (session.view == null) { + session.view = ""; + } + + /* if (session.view){ + if (urlParams.has('include') && urlParams.get('include')){ + session.view += ","+urlParams.get('include'); + } + } */ + if ((session.scene !== false) && (session.style === false) && window.obsstudio){ + session.style = 1; + } + } + + if (urlParams.has('fakeguests') || urlParams.has('fakefeeds')) { + var total = parseInt(urlParams.get('fakeguests')) || parseInt(urlParams.get('fakefeeds')) || 4; + session.fakeFeeds = []; + log("Creating "+total+" fake feeds"); + for (var i=0;i1){ + session.allowScreen.push(split[0]); + session.view_set.splice(i, 1); + if (!(split[0] in session.view_set)){ + session.view_set.push(split[0]); + } + } else if (split[0]){ + session.allowVideos.push(split[0]); + } else { + session.view_set.splice(i, 1); + } + } + } + + if (urlParams.has('include') && urlParams.get('include')){ + urlParams.get('include').split(",").forEach(sid =>{ + var sidd = sid.split(":s")[0]; + if (sidd && !session.include.includes(sidd)){ + session.include.push(sidd); + } + }); + + } + + if (urlParams.has('directorview') || urlParams.has('dv')){ + session.directorView = true; + } + if (urlParams.has('graphs')){ + session.allowGraphs = true; + } + + if (urlParams.has('ruler') || urlParams.has('grid') || urlParams.has('thirds')) { + session.fullscreen = true; + if (!session.manual){ + session.manual = false; + } + session.ruleOfThirds = urlParams.get('ruler') || urlParams.get('grid') || urlParams.get('thirds') || "./media/thirds.svg"; + session.ruleOfThirds = decodeURIComponent(session.ruleOfThirds); + } + + if (urlParams.has('smallshare')){ + session.notifyScreenShare = false; + } + + if (urlParams.has('proxy')) { // routes the wss traffic via an alternative network path. Not + session.proxy=true; // only works if session.wss is set to false + } else if (location.hostname === "proxy.vdo.ninja"){ + session.proxy=true; + } + + + if (urlParams.has('nopreview') || urlParams.has('np')) { + log("preview OFF"); + session.nopreview = true; + if ((iOS) || (iPad)) { + session.nopreview = false; + session.minipreview = 3; // + } + } else if ((urlParams.has('preview')) || (urlParams.has('showpreview'))) { + log("preview ON"); + session.nopreview = false; + } else if ((urlParams.has('minipreview')) || (urlParams.has('mini'))) { + var mini = urlParams.has('minipreview') || urlParams.has('mini') || true; // 2 is a valid option. (3 is for iPhone with a hidden preview) + log("preview ON"); + session.nopreview = false; + session.minipreview = mini; + } + + if (urlParams.has('minipreviewoffset') || urlParams.has('mpo')){ // 40 would be centered + session.leftMiniPreview = urlParams.get('minipreviewoffset') || urlParams.get('mpo') || 0; + session.leftMiniPreview = parseInt(session.leftMiniPreview) || 0; + if (session.leftMiniPreview<-20){ + session.leftMiniPreview = -20; + } else if (session.leftMiniPreview>120){ + session.leftMiniPreview = 120; + } + } + + + if (urlParams.has('obsfix')) { + session.obsfix = urlParams.get('obsfix'); + if (session.obsfix) { + session.obsfix = session.obsfix.toLowerCase(); + } + if (session.obsfix == "false") { + session.obsfix = false; + } else if (session.obsfix == "0") { + session.obsfix = false; + } else if (session.obsfix == "no") { + session.obsfix = false; + } else if (session.obsfix == "off") { + session.obsfix = false; + } else if (parseInt(session.obsfix) > 0) { + session.obsfix = parseInt(session.obsfix); + } else { + session.obsfix = 1; // aggressive. + } + } + + if (urlParams.has('controlroombitrate') || urlParams.has('crb')) { + session.controlRoomBitrate = true; + } + + if (urlParams.has('minroombitrate') || urlParams.has('mrb')) { + session.minimumRoomBitrate = urlParams.get('minroombitrate') || urlParams.get('mrb') || false; + session.minimumRoomBitrate = parseInt(session.minimumRoomBitrate) || false; + } + + if (urlParams.has('remote') || urlParams.has('rem')) { + log("remote ENABLED"); + session.remote = urlParams.get('remote') || urlParams.get('rem') || true; + } + + if (urlParams.has("slideshow")){ // stream labs mobile fix ? + var ssinterval = parseInt(urlParams.get("slideshow")) || 25; + ssinterval = 1000/ssinterval; + session.manual = true; + session.dynamicScale = false; + setInterval(function(){ + try { + slideshowHack(); + } catch(e){errorlog(e);} + },ssinterval); + } + + if (urlParams.has('latency') || urlParams.has('al') || urlParams.has('audiolatency')) { + log("latency ENABLED"); + session.audioLatency = urlParams.get('latency') || urlParams.get('al') || urlParams.get('audiolatency'); + session.audioLatency = parseInt(session.audioLatency) || 0; + session.disableWebAudio = false; + } + + if (urlParams.has('micdelay') || urlParams.has('delay') || urlParams.has('md')) { + log("audio gain ENABLED"); + session.micDelay = urlParams.get('micdelay') || urlParams.get('delay') || urlParams.get('md') || 0; + session.micDelay = parseInt(session.micDelay) || 0; + session.disableWebAudio = false; + } + + if (urlParams.has('tips')){ + getById("guestTips").style.display="flex"; + } + + if (urlParams.has('audiogain') || urlParams.has('gain') || urlParams.has('g')) { + log("audio gain ENABLED"); + session.audioGain = urlParams.get('audiogain') || urlParams.get('gain') || urlParams.get('g'); + session.audioGain = parseInt(session.audioGain) || 0; + session.disableWebAudio = false; + } + if (urlParams.has('volume') || urlParams.has('vol') ) { // This sets the default volume for all new video playback elements; 0 to 100. + log("setting default volume for playback"); + session.volume = urlParams.get('volume') || urlParams.get('vol') || 100; + session.volume = parseInt(session.volume) || 0; + session.volume = session.volume/100; // 0 to 1.0 + } + if (urlParams.has('compressor') || urlParams.has('comp')) { + log("audio gain ENABLED"); + session.compressor = 1; + session.disableWebAudio = false; + } else if (urlParams.has('limiter')) { + log("audio gain ENABLED"); + session.compressor = 2; + session.disableWebAudio = false; + } + if ((urlParams.has('equalizer')) || (urlParams.has('eq'))) { + session.equalizer = true; + session.disableWebAudio = false; + } + if ((urlParams.has('lowcut')) || (urlParams.has('lc')) || (urlParams.has('higpass'))) { + session.lowcut = urlParams.get('lowcut') || urlParams.get('lc') || urlParams.get('higpass') || 100; + session.lowcut = parseInt(session.lowcut); + session.disableWebAudio = false; + } + + if (urlParams.has('pip')) { + session.pip = true; // togglePip + //session.manual=true; + //innerHTML = + } + if (urlParams.has('pip3') || urlParams.has('mypip') || urlParams.has('pipme')){ + session.pip3 = true; + } + + if (urlParams.has('keyframeinterval') || urlParams.has('keyframerate') || urlParams.has('keyframe') || urlParams.has('fki')) { + log("keyframeRate ENABLED"); + session.keyframeRate = parseInt(urlParams.get('keyframeinterval') || urlParams.get('keyframerate') || urlParams.get('keyframe') || urlParams.get('fki')) || 0; + } + + if (urlParams.has('obsoff') || urlParams.has('oo') || urlParams.has('disableobs')) { + log("OBS feedback disabled"); + session.disableOBS = true; + getById("obsState").style.setProperty("display", "none", "important"); + } + + if (urlParams.has('hidecodirectors') || urlParams.has('hidecodirector') || urlParams.has('hidedirector') || urlParams.has('hidedirectors') || urlParams.has('hd')){ + document.querySelector(':root').style.setProperty("--show-codirectors", "none", "important"); + session.hideDirector = true; + } + + if (urlParams.has('pptcontrols') || urlParams.has('slides') || urlParams.has('ppt') || urlParams.has('powerpoint')){ + session.pptControls = true; // shows powerpoint controls to remotely control a powerpoint slide. Requires additional remote setup. + } + + if (urlParams.has('obscontrols') || urlParams.has('remoteobs') || urlParams.has('obsremote') || urlParams.has('obs') || urlParams.has('controlobs')) { + session.obsControls = urlParams.get('obscontrols') || urlParams.get('remoteobs') || urlParams.get('obsremote') || urlParams.get('obs') || urlParams.get('controlobs'); + if (session.obsControls) { // whether to show the button or not; that's it. + session.obsControls = session.obsControls.toLowerCase(); + } + if (session.obsControls == "false") { + session.obsControls = false; + } else if (session.obsControls == "0") { + session.obsControls = false; + } else if (session.obsControls == "no") { + session.obsControls = false; + } else if (session.obsControls == "off") { + session.obsControls = false; + } else if (session.obsControls){ + session.obsControls = session.obsControls.toLowerCase(); + } else { + session.obsControls = true; + } + } + + if (urlParams.has('allowedscenes')){ + session.filterOBSscenes = urlParams.get('allowedscenes'); + if (session.filterOBSscenes){ + session.filterOBSscenes = session.filterOBSscenes.split(","); + } else { + session.filterOBSscenes = true; + } + } + + + if (urlParams.has('tallyoff') || urlParams.has('notally') || urlParams.has('disabletally') || urlParams.has('to')) { + log("Tally Light off"); + getById("obsState").style.setProperty("display", "none", "important"); + } else if (urlParams.has('tally')) { + session.tallyStyle = 1; + getById("obsState").classList.add("larger"); + } + + if (urlParams.has('automute') || urlParams.has('am')){ + session.automute = urlParams.get('automute') || true; + session.micIsolatedAutoMute = []; // default auto mutes + } + + if (window.obsstudio) { + session.disableWebAudio = true; // default true; might be useful to disable on slow or old computers? + session.audioMeterGuest = false; + if (session.audioEffects===null){ + session.audioEffects = false; + } + if (window.obsstudio.pluginVersion){ + if (macOS){ // if mac, no fix + //session.obsfix = false; + } else if (window.obsstudio.pluginVersion=="2.17.4"){ // if obs v27.2 beta, no fix + //session.obsfix = false; + } else { + var ver = window.obsstudio.pluginVersion.split("."); + if (ver.length >= 2){ + if (parseInt(ver[0])<=2){ + if (parseInt(ver[0])==2){ + if (parseInt(ver[1])<=16){ + session.obsfix = 15; + } + } else { + session.obsfix = 15; + } + } + } + } + } + try { + log("OBS VERSION:" + window.obsstudio.pluginVersion); + log("macOS: " + macOS); + log(window.obsstudio); + + if (!(urlParams.has('streamlabs'))) { + + var ver1 = window.obsstudio.pluginVersion.split("."); + + if (ver1.length == 3) { // Should be 3, but disabled3 + if ((ver1.length == 3) && (parseInt(ver1[0]) == 2) && (ChromiumVersion < 76) && (macOS)) { + updateURL("streamlabs"); + getById("main").innerHTML = "

    Update OBS Studio to v26.1.2 or newer; older versions and StreamLabs OBS are not supported on macOS.\ +
    download here: https://github.com/obsproject/obs-studio/releases\ +



    \ +

    Please use the Electron Capture app if there are further problems or if you wish to use StreamLabs on macOS still.

    \ +
    You can bypass this error message by refreshing, Clicking Here, or by adding &streamlabs to the URL, but it may still not actually work.\ + \ +
    Please report this problem to steve@seguin.email if you feel it is an error.\ +
    "; + } + } + } + + //if (navigator.userAgent.indexOf('Mac OS X') != -1) { + // session.codec = "h264"; // default the codec to h264 if OBS is on macOS (that's all it supports with hardware) // oct 2021, OBS now supports vp8 and actually breaks with h264 android devices. + //} + + if (session.disableOBS===false){ + if (typeof document.visibilityState !== "undefined"){ + session.obsState.visibility = document.visibilityState==="visible"; + } + + getOBSDetails(); + + window.addEventListener("obsSourceVisibleChanged", obsSourceVisibleChanged); + window.addEventListener("obsSourceActiveChanged", obsSourceActiveChanged); + window.addEventListener("obsSceneChanged", obsSceneChanged); + window.addEventListener("obsStreamingStarted", obsStreamingStarted); + window.addEventListener("obsStreamingStopped", obsStreamingStopped); + window.addEventListener("obsRecordingStarted", obsRecordingStarted); + window.addEventListener("obsRecordingStopped", obsRecordingStopped); + window.addEventListener("obsVirtualcamStarted", obsVirtualcamStarted); + window.addEventListener("obsVirtualcamStopped", obsVirtualcamStopped); + } + + } catch (e) { + errorlog(e); + } + } + + if (urlParams.has('chroma')) { + log("Chroma ENABLED"); + getById("main").style.backgroundColor = "#" + (urlParams.get('chroma') || "0F0"); + //try { + // document.querySelector('meta[name="theme-color"]')?.setAttribute('content', "#" + (urlParams.get('chroma') || "0F0")); .. meh + //} catch(e){} + //const ogColor = document.querySelector('meta[name="theme-color"]')?.getAttribute('content'); + } // else if (window.obsstudio || (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1)){ + // getById("main").style.backgroundColor = "rgba(0,0,0,0)"; + //} + + //if (urlParams.has('bgimg')){ + // + // getById("main").style.background = "#" + (urlParams.get('chroma') || "0F0"); + //} + + if (urlParams.has('margin')) { + try { + session.videoMargin = urlParams.get('margin') || 10; + session.videoMargin = parseInt(session.videoMargin); + //document.querySelector(':root').style.setProperty('--video-margin', session.videoMargin+"px"); + } catch(e){errorlog("variable css failed");} + } + + if (urlParams.has('rounded') || urlParams.has('round')) { + try { + session.borderRadius = urlParams.get('rounded') || urlParams.get('round') || 50; + session.borderRadius = parseInt(session.borderRadius); + document.querySelector(':root').style.setProperty('--video-rounded', session.borderRadius+"px"); + } catch(e){errorlog("variable css failed");} + + } + + if (urlParams.has('border')) { + try { + var videoBorder = urlParams.get('border') || 10; + videoBorder = parseInt(videoBorder); + session.border = videoBorder; + videoBorder+="px"; + document.querySelector(':root').style.setProperty('--video-border-color', "#000"); + document.querySelector(':root').style.setProperty('--video-border', videoBorder); + + } catch(e){errorlog("variable css failed");} + + } + + if (urlParams.has('bordercolor')) { + try { + session.borderColor = urlParams.get('bordercolor') || "#000"; + document.querySelector(':root').style.setProperty('--video-border-color', session.borderColor); + } catch(e){errorlog("variable css failed");} + } + + if (urlParams.has('color')) { + session.colorVideosBackground = urlParams.get('color') || session.borderColor || "#000"; + } + + if (urlParams.has('retry')) { + session.forceRetry = parseInt(urlParams.get('retry')) || 30; + } + + if (session.forceRetry){ + clearInterval(session.forceRetryTimeout); + session.forceRetryTimeout = setTimeout(function(){ + try { + session.retryWatchInterval(); + } catch(e){ + log(e); + clearTimeout(this); + } + }, session.forceRetry*1000); + } + + session.dbx = false; + if (urlParams.get('dropbox')){ + loadScript("https://cdnjs.cloudflare.com/ajax/libs/dropbox.js/10.34.0/Dropbox-sdk.min.js", ()=>{ + log("Loaded dropbox SDK"); + var accessToken = urlParams.get('dropbox'); + session.dbx = new Dropbox.Dropbox({ accessToken: accessToken }); + }); + } + + try { + if (urlParams.has("darkmode") || urlParams.has("nightmode")){ + session.darkmode = urlParams.get("darkmode") || urlParams.get("nightmode") || null; + if ((session.darkmode===null) || (session.darkmode === "")){ + session.darkmode=true; + } else if ((darkmode=="false") || (darkmode == "0") || (darkmode == 0) || (darkmode == "off")){ + session.darkmode=false; + } + } else if (urlParams.has("lightmode") || urlParams.has("lightmode")){ + session.darkmode = false; + } else if (window.obsstudio){ + session.darkmode = false; // prevent OBS from defaulting to dark mode, avoiding possible overlooked bugs. + } else if (session.darkmode===null){ + session.darkmode = getComputedStyle(document.querySelector(':root')).getPropertyValue('--color-mode').trim(); + if (session.darkmode == "dark"){ + session.darkmode = true; + } else { + session.darkmode = false; + } + } + + if (session.darkmode){ + document.body.classList.add("darktheme"); + //document.querySelector(':root').style.setProperty('--background-color',"#02050c" ); + } else { + document.body.classList.remove("darktheme"); + //document.querySelector(':root').style.setProperty('--background-color',"#141926" ); // already set as default. + } + } catch(e){errorlog(e);} + + if (urlParams.has("videodevice") || urlParams.has("vdevice") || urlParams.has('vd') || urlParams.has('device') || urlParams.has('d') || urlParams.has('vdo')) { + + session.videoDevice = urlParams.get("videodevice") || urlParams.get("vdevice") || urlParams.get("vd") || urlParams.get("device") || urlParams.get("d") || urlParams.get("vdo"); + + if (session.videoDevice === null) { + session.videoDevice = "1"; + } else if (session.videoDevice) { + session.videoDevice = session.videoDevice.toLowerCase().replace(/[\W]+/g, "_"); + } + + if (session.videoDevice == "false") { + session.videoDevice = 0; + } else if (session.videoDevice == "0") { + session.videoDevice = 0; + } else if (session.videoDevice == "no") { + session.videoDevice = 0; + } else if (session.videoDevice == "off") { + session.videoDevice = 0; + } else if (session.videoDevice == "snapcam") { + session.videoDevice = "snap_camera"; + } else if (session.videoDevice == "canon") { + session.videoDevice = "eos"; + } else if (session.videoDevice == "camlink") { + session.videoDevice = "cam_link"; + } else if (session.videoDevice == "ndi") { + session.videoDevice = "newtek_ndi_video"; + } else if (session.videoDevice == "") { + session.videoDevice = 1; + } else if (session.videoDevice == "1") { + session.videoDevice = 1; + } else if (session.videoDevice == "default") { + session.videoDevice = 1; + } + + if (!urlParams.has('vdo')){ + getById("videoMenu").style.display = "none"; + } + log("session.videoDevice:" + session.videoDevice); + } + + + // audioDevice + if (urlParams.has('audiodevice') || urlParams.has('adevice') || urlParams.has('ad') || urlParams.has('device') || urlParams.has('d')) { + + session.audioDevice = urlParams.get("audiodevice") || urlParams.get("adevice") || urlParams.get("ad") || urlParams.get("device") || urlParams.get("d"); + + if (session.audioDevice === null) { + session.audioDevice = "1"; + } else if (session.audioDevice) { + session.audioDevice = session.audioDevice.toLowerCase().replace(/[^-,'A-Za-z0-9]+/g,"_"); + } + + if (session.audioDevice == "false") { + session.audioDevice = 0; + } else if (session.audioDevice == "0") { + session.audioDevice = 0; + } else if (session.audioDevice == "no") { + session.audioDevice = 0; + } else if (session.audioDevice == "off") { + session.audioDevice = 0; + } else if (session.audioDevice == "") { + session.audioDevice = 1; + } else if (session.audioDevice == "1") { + session.audioDevice = 1; + } else if (session.audioDevice == "default") { + session.audioDevice = 1; + } else if (session.audioDevice == "ndi") { + session.audioDevice = ["line_newtek_ndi_audio"]; + } else { + session.audioDevice = session.audioDevice.split(","); + } + getById("headphonesDiv").style.display = "none"; + getById("headphonesDiv2").style.display = "none"; + + if (typeof session.audioDevice !== "object"){ + getById("audioMenu").style.display = "none"; + getById("audioScreenShare1").style.display = "none"; + } + + if (session.audioDevice){ // 0 or false, do not triger + log("requestAudioStream..()"); + try { + await requestAudioStream(); + } catch(e){errorlog(e);} + } + } + + if (session.videoDevice === 0) { + getById("previewWebcam").classList.add("miconly"); + if (session.audioDevice === 0) { + miniTranslate(getById("add_camera"), "click-start-to-join", "Click Start to Join"); + getById("container-2").className = 'column columnfade hidden'; + getById("container-3").classList.add("skip-animation"); + getById("container-3").classList.remove('pointer'); + delayedStartupFuncs.push([previewWebcam]); + session.webcamonly = true; + } else { + miniTranslate(getById("add_camera"), "share-your-mic", "Share your Microphone"); + getById("container-3").classList.add("microphoneBackground"); + } + getById("container-3").title = getById("add_camera").innerText; + } + + if (session.mobile){ + getById("shareScreenGear").style.display = "none"; + getById("dropButton").style.display = "none"; + //getById("container-2").className = 'column columnfade hidden'; // Hide screen share on mobile + session.screensharebutton = false; + screensharesupport = false; + + if (session.audioDevice!==0){ + getById("flipcamerabutton").classList.remove("hidden"); + } + } + + if (urlParams.has('androidfix')){ + session.AndroidFix = true; + } + + + + if (urlParams.has('consent')){ + session.consent = true; + getById("consentWarning").classList.remove("hidden"); + getById("consentWarning2").classList.remove("hidden"); + } + + if (urlParams.has('autojoin') || urlParams.has('autostart') || urlParams.has('aj') || urlParams.has('as')) { + session.autostart = true; + } + + if (session.dataMode){ + delayedStartupFuncs.push([joinDataMode]); + } else if (session.autostart){ + if (session.screenshare!==false) { + delayedStartupFuncs.push([publishScreen]); + } + if (session.consent){ + setTimeout(function(){ + warnUser("⚠ Privacy warning: The director of this room can remotely switch your camera or microphone without permission.", 8000); + }, 1500); + } + } + + if (urlParams.has('noiframe') || urlParams.has('noiframes') || urlParams.has('nif') || urlParams.has('nowebsite') ) { + + session.noiframe = urlParams.get('noiframe') || urlParams.get('noiframes') || urlParams.get('nif') || urlParams.get('nowebsite'); + + if (!(session.noiframe)) { + session.noiframe = []; + } else { + session.noiframe = session.noiframe.split(","); + } + log("disable iframe playback"); + log(session.noiframe); + } + + + if (urlParams.has('exclude') || urlParams.has('ex')) { + + session.exclude = urlParams.get('exclude') || urlParams.get('ex'); + + if (!(session.exclude)) { + session.exclude = false; + } else { + session.exclude = session.exclude.split(","); + } + log("exclude video playback"); + log(session.exclude); + } + + if (urlParams.has('novideo') || urlParams.has('nv') || urlParams.has('hidevideo') || urlParams.has('showonly')) { + + session.novideo = urlParams.get('novideo') || urlParams.get('nv') || urlParams.get('hidevideo') || urlParams.get('showonly'); + + if (!(session.novideo)) { + session.novideo = []; + } else { + session.novideo = session.novideo.split(","); + } + log("disable video playback"); + log(session.novideo); + } + + if (urlParams.has('noaudio') || urlParams.has('na') || urlParams.has('hideaudio')) { + + session.noaudio = urlParams.get('noaudio') || urlParams.get('na') || urlParams.get('hideaudio'); + + if (!session.noaudio) { + session.noaudio = []; + } else { + session.noaudio = session.noaudio.split(","); + } + log("disable audio playback"); + } + + if (urlParams.has('forceios')) { + log("allow iOS to work in video group chat; for this user at least"); + session.forceios = true; + } + + if (urlParams.has('nocursor')) { // on the screen, not in screen share + session.nocursor = true; + log("DISABLE CURSOR"); + var styletmp = document.createElement('style'); + styletmp.innerHTML = ` + video { + margin: 0; + padding: 0; + overflow: hidden; + cursor: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=), none; + user-select: none; + } + `; + document.head.appendChild(styletmp); + } + + if (urlParams.has('cursor') || urlParams.has('screensharecursor')) { + session.screensharecursor = true; + } + + if (urlParams.has('distort')) { + session.voicechanger = 1; + } + + if (urlParams.has('dtx') || urlParams.has('usedtx')) { + session.dtx = true; + session.cbr = 0; // no point dtx on if cbr is on, right? + } + + if (urlParams.has('youtube')) { // Set with a Youtube v3 clientID + "," + API key, then run YoutubeChatInterface(); + session.youtubeKey = urlParams.get('youtube') || ""; + //YoutubeChatInterface(); // queries Youtube for chat messages. Forwards them to the parent IFRAME only at the moment. + } + + if (urlParams.has('vbr')) { + session.cbr = 0; + getById("whipoutvbrcbr").classList.add("hidden"); + } else if (urlParams.has('cbr')) { + session.cbr = 1; + getById("whipoutvbrcbr").classList.add("hidden"); + } + + if (urlParams.has('order')) { + session.order = parseInt(urlParams.get('order')) || 1; + } + + if (urlParams.has('orderby')) { + session.orderby = urlParams.get('orderby') || "id"; // "label" also an option; the default is stream ID tho. + } + + if (urlParams.has('slot')) { + session.slot = parseInt(urlParams.get('slot')) || 0; + } + + if (urlParams.has('slots')) { + session.slots = parseInt(urlParams.get('slots')) || 4; + } + + if (urlParams.has('alpha')) { + session.alpha = true; + } + + if (urlParams.has('chunked')) { + session.chunked = parseInt(urlParams.get('chunked')) || 2500; // sender side; enables to allows. + // session.alpha = true; + } + if (urlParams.has('nochunk') || urlParams.has('nochunked')) { // viewer side + session.nochunk = true; + } + //if (urlParams.has('viewchunked') || urlParams.has('viewchunk') || urlParams.has('allowchunked') || urlParams.has('allowchunk')) { // viewer side + // session.forceChunked = true; + //} + + if (urlParams.has('token')) { + session.token = urlParams.get('token') || false; + // checkToken(); // this is sycnhonous + } + + if (urlParams.has('maindirectorpassword') || urlParams.has('maindirpass')) { + session.mainDirectorPassword = urlParams.get('maindirectorpassword') || urlParams.get('maindirpass') || false; + if (!session.mainDirectorPassword) { + window.focus(); + session.mainDirectorPassword = await promptAlt(getTranslation("director-password"), true, true); + if (session.mainDirectorPassword){ + session.mainDirectorPassword = session.mainDirectorPassword.trim(); + try { + session.mainDirectorPassword = decodeURIComponent(session.mainDirectorPassword); + } catch(e){errorlog(e);} + } + } + // registerToken(); + } + + + + if (urlParams.has('debug')){ + session.debug=true; + debugStart(); + } + + if (urlParams.has('group') || urlParams.has('groups')) { + session.group = urlParams.get('group') || urlParams.get('groups') || ""; + session.group = session.group.split(","); + } + + if (urlParams.has('groupview') || urlParams.has('viewgroup') || urlParams.has('gv')) { + session.groupView = urlParams.get('groupview') || urlParams.get('viewgroup') || urlParams.get('gv') || ""; + session.groupView = session.groupView.split(","); + } + + if (urlParams.has('groupaudio') || urlParams.has('ga')) { + session.groupAudio = true; + } + + if (urlParams.has('groupmode') || urlParams.has('gm')) { + session.allowNoGroup = true; + } + + if (urlParams.has('host')) { + session.roomhost = true; + } + + if (urlParams.has('sensors') || urlParams.has('sensor') || urlParams.has('gyro') || urlParams.has('gyros') || urlParams.has('accelerometer')) { + session.sensorData = urlParams.get('sensors') || urlParams.get('sensor') || urlParams.get('gyro') || urlParams.get('gyros') || urlParams.get('accelerometer') || 30; + session.sensorData = parseInt(session.sensorData); + } + if (urlParams.has('sensorfilter') || urlParams.has('sensorsfilter') || urlParams.has('filtersensor') || urlParams.has('filtersensors')) { + session.sensorDataFilter = urlParams.get('sensorfilter') || urlParams.get('sensorsfilter') || urlParams.get('filtersensor') || urlParams.get('filtersensors') || ""; + session.sensorDataFilter = session.sensorDataFilter.split(","); // ["pos","lin","ori","mag","gyro","acc"]; + } + + if (urlParams.has('ptime')) { + session.ptime = parseInt(urlParams.get('ptime')) || 20; + if (session.ptime<10){ + session.ptime = 10; + } + } + + if (urlParams.has('minptime')) { + session.minptime = parseInt(urlParams.get('minptime')) || 10; + if (session.minptime < 10) { + session.minptime = 10; + } + if (session.minptime > 300) { + session.minptime = 300; + } + } + + if (urlParams.has('maxptime')) { + session.maxptime = parseInt(urlParams.get('maxptime')) || 60; + if (session.maxptime < 10) { + session.maxptime = 10; + } + if (session.maxptime > 300) { + session.maxptime = 300; + } + } + + if (urlParams.has('contenthint') || urlParams.has('contenttype') || urlParams.has('content') || urlParams.has('hint')) { + session.contentHint = urlParams.get('contenthint') || urlParams.get('contenttype') || urlParams.get('content') || urlParams.get('hint') || "detail"; + } + + if (urlParams.has('audiocontenthint') || urlParams.has('audiocontenttype') || urlParams.has('audiocontent') || urlParams.has('audiohint')) { + session.audioContentHint = urlParams.get('audiocontenthint') || urlParams.get('audiocontenttype') || urlParams.get('audiocontent') || urlParams.get('audiohint') || "music"; + } + + if (urlParams.has('screensharecontenthint') || urlParams.has('sscontenthint') || urlParams.has('screensharecontenttype') || urlParams.has('sscontent') || urlParams.has('sshint')) { + session.screenshareContentHint = urlParams.get('screensharecontenthint') || urlParams.get('sscontenthint') || urlParams.get('screensharecontenttype') || urlParams.get('sscontent') || urlParams.get('sshint') || "detail"; + } + + if (urlParams.has('codec') || urlParams.has('codecs') || urlParams.has('videocodec')) { + log("codecs CHANGED"); + session.codecs = urlParams.get('codec') || urlParams.get('codecs') || urlParams.get('videocodec') || false; + if (session.codecs){ + session.codecs = session.codecs.toLowerCase(); + session.codecs = session.codecs.split(","); + if (session.codecs.length){ + session.codec = session.codecs.shift(); + if (!session.codec){ + session.codec = false; + session.codecs = false; + } + if (!session.codecs.length){ + session.codecs = false; + } + } else { + session.codecs = false; + } + } + } else if (OperaGx){ + session.codec = "vp8"; + warnlog("Defaulting to VP8 manually, as H264 with remote iOS devices is not supported"); + } + + + + if (urlParams.has('audiocodec')) { + log("CODEC CHANGED"); + session.audioCodec = urlParams.get('audiocodec') || false; + if (session.audioCodec){ + session.audioCodec = session.audioCodec.toLowerCase(); + } + } + + if (urlParams.has('scenelinkcodec')){ // this is mainly for a niche iframe API use + log("codecGroupFlag CHANGED"); + session.codecGroupFlag = urlParams.get('scenelinkcodec') || false; + if (session.codecGroupFlag){ + session.codecGroupFlag = "&codec="+session.codecGroupFlag.toLowerCase(); + } + } + if (urlParams.has('scenelinkbitrate')){ // this is mainly for a niche iframe API use + log("bitrateGroupFlag CHANGED"); + session.bitrateGroupFlag = urlParams.get('scenelinkbitrate') || false; + if (session.bitrateGroupFlag){ + session.bitrateGroupFlag = "&totalbitrate="+parseInt(session.bitrateGroupFlag); + } + } + + + if (urlParams.has('h264profile')) { + session.h264profile = urlParams.get('h264profile') || "42e01f"; // 42001f + session.h264profile = session.h264profile.substring(0, 6); + session.h264profile = session.h264profile.toLowerCase(); + if (session.h264profile=="0"){ + session.h264profile = false; + } else if (session.h264profile=="off"){ + session.h264profile = false; + } else if (session.h264profile=="disabl"){ + session.h264profile = false; + } else if (session.h264profile=="defaul"){ + session.h264profile = false; + } else if (session.h264profile=="false"){ + session.h264profile = false; + } + } else if ((session.codec==="hardware") && Android){ // same as &h264profile, but easier for me to remember. I'll try to automate this in the future. + session.codec = "h264"; + session.h264profile = "42e01f"; + } + + if (urlParams.has('nofec')){ // disables error control / throttling -- currently on audio + session.noFEC = true; + } + if (urlParams.has('nonacks') || urlParams.has('nonack')){ // disables error control / throttling. + session.noNacks = true; + } + if (urlParams.has('nopli')){ // disables error control / throttling. + session.noPLIs = true; + } + if (urlParams.has('noremb')){ // disables Receiver Estimated Maximum Bitrate (throttling) + session.noREMB = true; + } + + if (urlParams.has('scale')) { + if (urlParams.get('scale') == "false") {} else if (urlParams.get('scale') == "0") {} else if (urlParams.get('scale') == "no") {} else if (urlParams.get('scale') == "off") {} else { + log("Resolution scale requested"); + session.scale = parseFloat(urlParams.get('scale')) || 100; + } + session.dynamicScale = false; // default true + } else { + if (urlParams.has('viewwidth') || urlParams.has('vw')) { + session.viewwidth = urlParams.get('viewwidth') || urlParams.get('vw') || false; + if (session.viewwidth){ + session.viewwidth = parseInt(session.viewwidth); + } + session.dynamicScale = false; // default true + } + if (urlParams.has('viewheight') || urlParams.has('vh')) { + session.viewheight = urlParams.get('viewheight') || urlParams.get('vh') || false; + session.dynamicScale = false; // default true + if (session.viewheight){ + session.viewheight = parseInt(session.viewheight); + } + } + } + + if (urlParams.has('sharperscreen')) { // sets scale to 100 for inbound screenshares only + session.sharperScreen = true; + } + + if (urlParams.has('mcscale') || urlParams.has('meshcastscale') || urlParams.has('woscale') || urlParams.has('whipoutscale')) { + session.whipOutScale = parseFloat(urlParams.get('mcscale')) || parseFloat(urlParams.get('meshcastscale')) || parseFloat(urlParams.get('woscale')) || parseFloat(urlParams.get('whipoutscale')) || 100; + } + + + if (isIFrame) { + getById("helpbutton").style.display = "none"; + getById("helpbutton").style.opacity = 0; + getById("reportbutton").style.display = "none"; + getById("reportbutton").style.opacity = 0; + getById("calendarButton").style.display = "none"; + getById("calendarButton").style.opacity = 0; + getById("chatBody").innerHTML = ""; + } + + if (urlParams.has('beep') || urlParams.has('notify') || urlParams.has('tone')) { + session.beepToNotify = true; + var addtone = createAudioElement(); + addtone.id = "jointone"; + addtone.src = "./media/join.mp3"; + getById("testtone").parentNode.insertBefore(addtone, getById("testtone").nextSibling) + var addtone = createAudioElement(); + addtone.id = "leavetone"; + addtone.src = "./media/leave.mp3"; + getById("testtone").parentNode.insertBefore(addtone, getById("testtone").nextSibling) + + if (!Notification) { + warnlog('Desktop notifications are not available in your browser.'); + } else if (Notification.permission !== 'granted') { + Notification.requestPermission(); + } + + } + if (urlParams.has('r2d2')) { + /* var addtone = createAudioElement(); + addtone.id = "jointone"; + addtone.src = "./media/join.mp3"; + getById("testtone").parentNode.insertBefore(addtone, getById("testtone").nextSibling) + var addtone = createAudioElement(); + addtone.id = "leavetone"; + addtone.src = "./media/leave.mp3"; + getById("testtone").parentNode.insertBefore(addtone, getById("testtone").nextSibling) */ + getById("testtone").innerHTML = ""; + getById("testtone").src = "./media/robot.mp3"; + session.beepToNotify = true; + } + + if (urlParams.has('easyexit') || urlParams.has('ee')) { + session.noExitPrompt = true; + } + + if (urlParams.has('entrymsg') || urlParams.has('welcome') || urlParams.has('welcomeb64')) { + session.welcomeMessage = urlParams.get('entrymsg') || urlParams.get('welcome') || urlParams.get('welcomeb64'); + + if (urlParams.get('welcomeb64')){ + try { + session.welcomeMessage = atob(session.welcomeMessage); + } catch(e){} + } + try { + session.welcomeMessage = session.welcomeMessage.replace(/(\r\n|\n|\r)/gm, ' '); + session.welcomeMessage = decodeURIComponent(session.welcomeMessage); + } catch(e){} + } + + if (urlParams.has('welcomehtml')) { + session.welcomeHTML = urlParams.get('welcomehtml'); + + try { + session.welcomeHTML = atob(session.welcomeHTML); + } catch(e){} + try { + session.welcomeHTML = session.welcomeHTML.replace(/(\r\n|\n|\r)/gm, ' '); + session.welcomeHTML = decodeURIComponent(session.welcomeHTML); + + } catch(e){} + } + + if (urlParams.has('welcomeimage') || urlParams.has('welcomeimg')) { + session.welcomeImage = urlParams.get('welcomeimage') || urlParams.get('welcomeimg'); + try { + session.welcomeImage = decodeURIComponent(session.welcomeImage); + } catch(e){} + } + + if (urlParams.has('mixminus')){ + session.mixMinus = true; + } + + if (urlParams.has('clearstorage') || urlParams.has('clear')){ + clearStorage(); + } + + if (urlParams.has('videobitrate') || urlParams.has('bitrate') || urlParams.has('vb')) { + session.bitrate = urlParams.get('videobitrate') || urlParams.get('bitrate') || urlParams.get('vb'); + if (session.bitrate) { + if ((session.view_set) && (session.bitrate.split(",").length > 1)) { + session.bitrate_set = session.bitrate.split(","); + session.bitrate = parseInt(session.bitrate_set[0]); + } else { + session.bitrate = parseInt(session.bitrate); + } + if (session.bitrate < 1) { + session.bitrate = false; + } + log("BITRATE ENABLED"); + log(session.bitrate); + + } + } + + if (urlParams.has('maxvideobitrate') || urlParams.has('maxbitrate') || urlParams.has('mvb')) { + session.maxvideobitrate = urlParams.get('maxvideobitrate') || urlParams.get('maxbitrate') || urlParams.get('mvb'); + session.maxvideobitrate = parseInt(session.maxvideobitrate); + + if (session.maxvideobitrate < 1) { + session.maxvideobitrate = false; + } + log("maxvideobitrate ENABLED"); + log(session.maxvideobitrate); + } + + if (urlParams.has('totalroombitrate') || urlParams.has('totalroomvideobitrate') || urlParams.has('trb') || urlParams.has('totalbitrate') || urlParams.has('tb')) { + session.totalRoomBitrate = urlParams.get('totalroombitrate') || urlParams.get('totalroomvideobitrate') || urlParams.get('trb') || urlParams.get('totalbitrate') || urlParams.get('tb') || 0; + session.totalRoomBitrate = parseInt(session.totalRoomBitrate); + + if (session.totalRoomBitrate < 1) { + session.totalRoomBitrate = 0; + } + log("totalRoomBitrate ENABLED"); + log(session.totalRoomBitrate); + } + + if (session.totalRoomBitrate===false){ + session.totalRoomBitrate = session.bitrate || session.totalRoomBitrate_default; // sneaky sneaky + } else { + session.totalRoomBitrate_default = session.totalRoomBitrate; // trb_default doesn't change dynamically, but trb can (per director I guess) + } + + if (session.totalRoomBitrate_default>4000){ + getById("trbSettingInput").max = Math.ceil(session.totalRoomBitrate_default); + } + + if (urlParams.has('maxtotalscenebitrate') || urlParams.has('totalscenebitrate') || urlParams.has('mtsb') || urlParams.has('tsb') || urlParams.has('totalbitrate') || urlParams.has('tb')) { + session.totalSceneBitrate = urlParams.get('maxtotalscenebitrate') || urlParams.get('totalscenebitrate') || urlParams.get('mtsb') || urlParams.get('tsb') || urlParams.get('totalbitrate') || urlParams.get('tb') || false; + if (session.totalSceneBitrate){ + session.totalSceneBitrate = parseInt(session.totalSceneBitrate); + } + } + + if (urlParams.has('blur')){ + session.blurBackground = urlParams.get('blur') || 10; + session.blurBackground = parseInt(session.blurBackground) || 10; + if (session.blurBackground<0){session.blurBackground=false;} + session.structure=true; + } + + if (urlParams.has('limittotalbitrate') || urlParams.has('ltb')){ + session.limitTotalBitrate = urlParams.get('limittotalbitrate') || urlParams.get('ltb') || 2500; + session.limitTotalBitrate = parseInt(session.limitTotalBitrate); + } + + if (urlParams.has('mcscreensharebitrate') || urlParams.has('mcssbitrate') || urlParams.has('whipoutscreensharebitrate') || urlParams.has('wossbitrate')){ + session.whipOutScreenShareBitrate = urlParams.get('mcscreensharebitrate') || urlParams.get('mcssbitrate') || urlParams.get('whipoutscreensharebitrate') || urlParams.get('wossbitrate') || 2500; + session.whipOutScreenShareBitrate = parseInt(session.whipOutScreenShareBitrate); + } + + if (urlParams.has('mcscreensharecodec') || urlParams.has('mcsscodec') || urlParams.has('whipoutscreensharecodec') || urlParams.has('wosscodec')){ + session.whipOutScreenShareCodec = urlParams.get('mcscreensharecodec') || urlParams.get('mcsscodec') || urlParams.get('whipoutscreensharecodec') || urlParams.get('wosscodec') || false; + } + if (session.whipOutScreenShareCodec){ + session.whipOutScreenShareCodec = session.whipOutScreenShareCodec.toLowerCase(); + } + + if (urlParams.has('mccodec') || urlParams.has('meshcastcodec') || urlParams.has('whipoutcodec') || urlParams.has('woc')){ + session.whipOutCodec = urlParams.get('mccodec') || urlParams.get('meshcastcodec') || urlParams.get('whipoutcodec') || urlParams.get('woc') || false; + getById("whipoutcodecGroupFlag").classList.add("hidden"); + } + + if (session.whipOutCodec){ + session.whipOutCodec = session.whipOutCodec.toLowerCase(); + if (session.whipOutCodec == "h264"){ + if (Firefox){ + session.whipOutCodec = false; + } + } + if (session.whipOutCodec){ + session.whipOutCodec = session.whipOutCodec.split(','); + } + getById("whipoutcodecGroupFlag").classList.add("hidden"); + } + + if (urlParams.has('mcab') || urlParams.has('mcaudiobitrate') || urlParams.has('meshcastab') || urlParams.has('meshcastaudiobitrate ') || urlParams.has('whipoutaudiobitrate') || urlParams.has('woab')){ + session.whipOutAudioBitrate = urlParams.get('mcab') || urlParams.get('mcaudiobitrate') || urlParams.get('meshcastab') || urlParams.get('meshcastaudiobitrate ') || urlParams.get('whipoutaudiobitrate') || urlParams.get('woab') || false; + if (session.whipOutAudioBitrate ){ + session.whipOutAudioBitrate = parseInt(session.whipOutAudioBitrate ); + } + getById("whipoutaudiobitrate").classList.add("hidden"); + } + + if (urlParams.has('mcb') || urlParams.has('mcbitrate') || urlParams.has('meshcastbitrate') || urlParams.has('whipoutvideobitrate') || urlParams.has('wovb')){ + session.whipOutVideoBitrate = urlParams.get('mcb') || urlParams.get('mcbitrate') || urlParams.get('meshcastbitrate') || urlParams.get('whipoutvideobitrate') || urlParams.get('wovb') || false; + if (session.whipOutVideoBitrate){ + session.whipOutVideoBitrate = parseInt(session.whipOutVideoBitrate); + } + getById("whipoutbitrateGroupFlag").classList.add("hidden"); + getById("whipoutvbrcbr").classList.add("hidden"); + } + + if (urlParams.has('height') || urlParams.has('h')) { + session.height = urlParams.get('height') || urlParams.get('h'); + session.height = parseInt(session.height); + } + + if (urlParams.has('width') || urlParams.has('w')) { + session.width = urlParams.get('width') || urlParams.get('w'); + session.width = parseInt(session.width); + } + + if (urlParams.has('quality') || urlParams.has('q')) { + try { + session.quality = urlParams.get('quality') || urlParams.get('q') || 0; + session.quality = parseInt(session.quality); + getById("gear_screen").parentNode.removeChild(getById("gear_screen")); + getById("gear_webcam").parentNode.removeChild(getById("gear_webcam")); + } catch (e) { + errorlog(e); + } + } + + if (urlParams.has('sink')) { + session.sink = urlParams.get('sink'); + } else if (urlParams.has('outputdevice') || urlParams.has('od') || urlParams.has('audiooutput')) { + session.outputDevice = urlParams.get('outputdevice') || urlParams.get('od') || urlParams.get('audiooutput') || null; + + if (session.outputDevice) { + session.outputDevice = session.outputDevice.toLowerCase().replace(/[\W]+/g, "_"); + } else { + session.outputDevice = null; + getById("headphonesDiv3").style.display = "none"; // + } + + if (session.outputDevice) { + try { + enumerateDevices().then(function(deviceInfos) { + for (let i = 0; i !== deviceInfos.length; ++i) { + if (deviceInfos[i].kind === 'audiooutput') { + if (deviceInfos[i].label.replace(/[\W]+/g, "_").toLowerCase().includes(session.outputDevice)) { + session.sink = deviceInfos[i].deviceId; + log("AUDIO OUT DEVICE: " + deviceInfos[i].deviceId); + break; + } + } + } + }); + } catch (e) {} + } + + getById("headphonesDiv").style.display = "none"; + getById("headphonesDiv2").style.display = "none"; + } else if (session.sink){ + if (session.sink == "default"){session.sink = false;} + else { + enumerateDevices().then(function(deviceInfos) { + var matched = false; + for (let i = 0; i !== deviceInfos.length; ++i) { + if (deviceInfos[i].kind === 'audiooutput') { + if (deviceInfos[i].deviceId == session.sink) { + matched = true; + break; + } + } + } + if (!matched){ + session.sink = false; // make sure any saved output device exists. + } + }); + } + } + + if (window.obsstudio || (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1)){ + session.fullscreen = true; + } else if (urlParams.has('fullscreen')) { + session.fullscreen = true; + } + + if (urlParams.has('stats')) { + if (urlParams.get('stats') == "0") { + session.statsMenu = false; + } else if (urlParams.get('stats') == "false") { + session.statsMenu = false; + } else if (urlParams.get('stats') == "off") { + session.statsMenu = false; + } else { + session.statsMenu = true; + } + } else if (urlParams.has('nostats')) { + session.statsMenu = false; + } + + if (session.statsMenu === false){ // hide menu option + try { + document.queryselector('[data-action="ShowStats"]').parentNode.classList.add("hidden"); + } catch(e){} + } + + if (urlParams.has("statsinterval")){ + session.statsInterval = parseInt(urlParams.get("statsinterval")) || 3000; // milliseconds. interval of requesting stats of remote guests + } + + + if (urlParams.has('cleandirector') || urlParams.has('cdv')) { + session.cleanDirector = true; + } + + if (urlParams.has('hidetranslate')) { + getById("translateButton").style.display = "none"; + } + + if (session.cleanOutput){ + session.screensharebutton = false; + getById("translateButton").style.display = "none"; + getById("credits").style.display = "none"; + getById("header").style.display = "none"; + getById("controlButtons").classList.add("hidden"); + getById("helpbutton").style.display = "none"; + getById("helpbutton").style.opacity = 0; + getById("reportbutton").style.display = "none"; + getById("reportbutton").style.opacity = 0; + getById("calendarButton").style.display = "none"; + getById("calendarButton").style.opacity = 0; + document.documentElement.style.setProperty('--myvideo-background', '#0000'); + var styleTmp = document.createElement('style'); + styleTmp.innerHTML = ` + video { + background-image: none; + } + `; + document.head.appendChild(styleTmp); + } + getById("credits").innerHTML = "Version: " + session.version + " - " + getById("credits").innerHTML; + + if (urlParams.has('ssb') || urlParams.has('screensharebutton')) { + session.screensharebutton = true; + } + + if (urlParams.has('hideheader') || urlParams.has('noheader') || urlParams.has('hh')) { // needs to happen the room and permaid applications + getById("header").style.display = "none"; + getById("header").style.opacity = 0; + } else if (urlParams.has('showheader')) { // needs to happen the room and permaid applications + getById("header").style.display = "inherit"; + getById("header").style.opacity = 1; + } + + if (urlParams.has('minidirector')) { + try { + var cssStylesheet = document.createElement('link'); + cssStylesheet.rel = 'stylesheet'; + cssStylesheet.type = 'text/css'; + cssStylesheet.media = 'screen'; + cssStylesheet.href = 'minidirector.css'; + document.getElementsByTagName('head')[0].appendChild(cssStylesheet); + } catch (e) { + errorlog(e); + } + } + + + if (urlParams.has('postinterval')){ // interval to post snapimage images + session.postInterval = urlParams.get('postinterval') || session.postInterval; + session.postInterval = parseInt(session.postInterval) || 60; + if (session.postInterval<5){ + session.postInterval = 5; + } + } + if (urlParams.has('postimage')){ + var postURL = decodeURIComponent(urlParams.get('postimage')) || session.postURL; // default will post to https://temp.vdo.ninja/images/STREAMIDHERE.jpg + setInterval(function(postURL){ + try { + uploadImageSnapshot(postURL); + } catch(e){} + }, session.postInterval*1000 , postURL); + } + + if (urlParams.has('cleanish')) { + session.cleanish = true; + } + + + if (session.cleanish || !session.cleanOutput){ + if (session.obsControls){ + getById("obscontrolbutton").classList.remove("hidden"); + getById("controlButtons").classList.remove("hidden"); + } + } + + if (urlParams.has('channels')) { // must be loaded before channelOffset + session.audioChannels = parseInt(urlParams.get('channels')); // for audio output ; not input. see: &channelcount instead. + session.offsetChannel = 0; + log("max channels is 32; channels offset"); + session.audioEffects = true; + } + if (urlParams.has('channeloffset')) { + session.offsetChannel = parseInt(urlParams.get('channeloffset')); + log("max channels is 32; channels offset"); + session.audioEffects = true; + } + if (urlParams.get('playchannel')) { // must be loaded before channelOffset + session.playChannel = parseInt(urlParams.get('playchannel')); // for audio output ; not input. see: &channelcount instead. + session.audioEffects = true; + } + if (urlParams.has('enhance')) { + //if (parseInt(urlParams.get('enhance')>0){ + session.enhance = true; //parseInt(urlParams.get('enhance')); + //} + } + + if (urlParams.has('degrade')) { + session.degrade = urlParams.get('degrade') || true; // Firefox, and maybe Safari, supported I think. + // the possible values are maintain-framerate, maintain-resolution, or balanced. The default value is balanced + } + + if (urlParams.has('maxviewers') || urlParams.has('mv')) { + + session.maxviewers = urlParams.get('maxviewers') || urlParams.get('mv'); + if (session.maxviewers.length == 0) { + session.maxviewers = 1; + } else { + session.maxviewers = parseInt(session.maxviewers); + } + log("maxviewers set"); + } + + if (urlParams.has('maxpublishers') || urlParams.has('mp')) { + + session.maxpublishers = urlParams.get('maxpublishers') || urlParams.get('mp'); + if (session.maxpublishers.length == 0) { + session.maxpublishers = 1; + } else { + session.maxpublishers = parseInt(session.maxpublishers); + } + log("maxpublishers set"); + } + + if (urlParams.has('maxconnections') || urlParams.has('mc')) { + + session.maxconnections = urlParams.get('maxconnections') || urlParams.get('maxconnections'); + if (session.maxconnections.length == 0) { + session.maxconnections = 1; + } else { + session.maxconnections = parseInt(session.maxconnections); + } + + log("maxconnections set"); + } + + + if (urlParams.has('secure')) { + session.security = true; + if (!(session.cleanOutput)) { + delayedStartupFuncs.push([warnUser, "Enhanced Security Mode Enabled."]); + } + } + + if (urlParams.has('random') || urlParams.has('randomize')) { + session.randomize = true; + } + + if (urlParams.has('frameRate') || urlParams.has('fr') || urlParams.has('fps')) { + session.frameRate = urlParams.get('frameRate') || urlParams.get('fr') || urlParams.get('fps'); + session.frameRate = parseInt(session.frameRate); + log("frameRate Changed"); + log(session.frameRate); + } + + if (urlParams.has('tz')){ // being depreciated, but still works with meshcast (no longer turn) + session.tz = urlParams.get('tz'); + if ((session.tz === null) || (session.tz === "")){ + session.tz = false; + } else { + session.tz = parseInt(session.tz); + } + } + + if (urlParams.has('maxframerate') || urlParams.has('mfr') || urlParams.has('mfps')) { + session.maxframeRate = urlParams.get('maxframerate') || urlParams.get('mfr') || urlParams.get('mfps'); + session.maxframeRate = parseInt(session.maxframeRate); + log("max frameRate assigned"); + log(session.maxframeRate); + } + + if (urlParams.has('buffer') || urlParams.has('buffer2')) { // needs to be before sync + if ((ChromiumVersion > 50) && (ChromiumVersion< 78)){ + } else { + session.buffer = parseFloat(urlParams.get('buffer')) || parseFloat(urlParams.get('buffer2')) || 0; + log("buffer Changed: " + session.buffer); + } + if (urlParams.has('buffer2')){ + session.includeRTT = true; + } + } + + + if (urlParams.has('panning') || urlParams.has('pan')) { + session.panning = urlParams.get('panning') || urlParams.get('pan'); + if (session.panning===""){ + session.panning=true + } + session.audioEffects = true; + } + + if (urlParams.has('sync')) { + if ((ChromiumVersion > 50) && (ChromiumVersion< 78)){ + + } else { + session.sync = parseFloat(urlParams.get('sync')); + log("sync Changed; in milliseconds. If not set, defaults to auto."); + log(session.sync); + session.audioEffects = true; + if (session.buffer === false) { + session.buffer = 0; + } + } + } + + if (urlParams.has('nomirror')) { + session.nomirror = true; + } + + if (urlParams.has('mirror')) { + if (urlParams.get('mirror') == "3") { + getById("main").classList.add("mirror"); + } else if (urlParams.get('mirror') == "2") { + session.mirrored = 2; + } else if (urlParams.get('mirror') == "0") { + session.mirrored = 0; + } else if (urlParams.get('mirror') == "false") { + session.mirrored = 0; + } else if (urlParams.get('mirror') == "off") { + session.mirrored = 0; + } else { + session.mirrored = 1; + } + } + + if (urlParams.has('flip')) { + if (urlParams.get('flip') == "0") { + session.flipped = false; + } else if (urlParams.get('flip') == "false") { + session.flipped = false; + } else if (urlParams.get('flip') == "off") { + session.flipped = false; + } else { + session.flipped = true; + } + } + + if ((session.mirrored) && (session.flipped)) { + try { + log("Mirror all videos"); + var mirrorStyle = document.createElement('style'); + mirrorStyle.innerHTML = "video {transform: scaleX(-1) scaleY(-1); }"; + document.getElementsByTagName("head")[0].appendChild(mirrorStyle); + } catch (e) { + errorlog(e); + } + } else if (session.mirrored) { // mirror the video horizontally + try { + log("Mirror all videos"); + var mirrorStyle = document.createElement('style'); + mirrorStyle.innerHTML = "video {transform: scaleX(-1);}"; + document.getElementsByTagName("head")[0].appendChild(mirrorStyle); + } catch (e) { + errorlog(e); + } + } else if (session.flipped) { // mirror the video vertically + try { + log("Mirror all videos"); + var mirrorStyle = document.createElement('style'); + mirrorStyle.innerHTML = "video {transform: scaleY(-1);}"; + document.getElementsByTagName("head")[0].appendChild(mirrorStyle); + } catch (e) { + errorlog(e); + } + } + + + if (urlParams.has('icefilter')) { + log("ICE FILTER ENABLED"); + session.icefilter = urlParams.get('icefilter'); + } + + + + //if (!(ChromiumVersion>=57)){ + // getById("effectSelector").disabled=true; + // getById("effectSelector3").disabled=true; + // getById("effectSelector").title = "Effects are only support on Chromium-based browsers"; + // getById("effectSelector3").title = "Effects are only support on Chromium-based browsers"; + // var elementsTmp = document.querySelectorAll('[data-effectsNotice]'); + // for (let i = 0; i < elementsTmp.length; i++) { + // elementsTmp[i].style.display = "inline-block"; + // } + //} + + + if (urlParams.has('viewereffect') || urlParams.has('viewereffects') || urlParams.has('ve')) { + session.viewereffects = parseInt(urlParams.get('viewereffect')) || parseInt(urlParams.get('ve')) || false; + } + + if (urlParams.has('activespeaker') || urlParams.has('speakerview') || urlParams.has('sas')){ + session.activeSpeaker = urlParams.get('activespeaker') || urlParams.get('speakerview') || urlParams.get('sas') || 1; + session.activeSpeaker = parseInt(session.activeSpeaker); + session.style=6; + session.audioEffects = true; + //session.audioMeterGuest = true; + session.minipreview = 2; + if ((session.activeSpeaker==1) || (session.activeSpeaker==3)){ + session.animatedMoves = false; + } + session.fadein=true; + document.querySelector(':root').style.setProperty('--fadein-speed', 0.5); + setInterval(function(){activeSpeaker(false);},100); + + } else if (urlParams.has('noisegate') || urlParams.has('gating') || urlParams.has('gate') ||urlParams.has('ng')){ + session.quietOthers = urlParams.get('noisegate') || urlParams.get('gating') || urlParams.get('gate') || urlParams.get('ng') || 1; + session.quietOthers = parseInt(session.quietOthers); + + if (session.quietOthers == 1){ + session.quietOthers = false; + session.noisegate = true; + session.audioEffects = true; + //session.audioMeterGuest = true; + } else if (session.quietOthers == 4){ + session.quietOthers = 1; + session.audioEffects = true; + //session.audioMeterGuest = true; + setInterval(function(){activeSpeaker(false);},100); + } else if (!session.quietOthers){ + session.noisegate = false; + session.quietOthers = false; + } else { + session.audioEffects = true; + //session.audioMeterGuest = true; + setInterval(function(){activeSpeaker(false);},100); + } + } + + if (urlParams.has('noisegatesettings')){ + session.noisegateSettings = urlParams.get('noisegatesettings'); + session.noisegateSettings = session.noisegateSettings.split(","); + } + + if (urlParams.has('fadein')) { + session.fadein=true; + if (urlParams.get('fadein') || 0){ + try { + var fadeinspeed = parseInt(urlParams.get('fadein') || 0)/1000.0; + fadeinspeed+="s"; + document.querySelector(':root').style.setProperty('--fadein-speed', fadeinspeed); + } catch(e){errorlog("variable css failed");} + } else { + try { + var fadeinspeed = 0.5; + fadeinspeed+="s"; + document.querySelector(':root').style.setProperty('--fadein-speed', fadeinspeed); + } catch(e){errorlog("variable css failed");} + } + } + + + if (urlParams.has('widget')){ + session.widget = urlParams.get('widget') || false; + + if ((session.widget === "false") || (session.widget === "0") || (session.widget === "off")){ + session.noWidget=true; + session.widget = false; + } else if (session.widget){ + session.widget = decodeURI(session.widget) || false; + log(session.widget); + } + } + + if (urlParams.has('animated') || urlParams.has('animate')){ + session.animatedMoves = urlParams.get('animated') || urlParams.get('animate'); + if (session.animatedMoves === "false") { + session.animatedMoves = false; + } else if (session.animatedMoves === "0") { + session.animatedMoves = false; + } else if (session.animatedMoves === "no") { + session.animatedMoves = false; + } else if (session.animatedMoves === "off") { + session.animatedMoves = false; + } else { + session.animatedMoves = parseInt(session.animatedMoves) || 100; + } + if (session.animatedMoves>200){ + session.animatedMoves = 200; + } + } else if (session.mobile){ + session.animatedMoves=false; + } + + if (urlParams.has('meter') || urlParams.has('meterstyle')){ // same as also adding &style=3 + session.meterStyle = urlParams.get('meter') || urlParams.get('meterstyle') || 1; + session.meterStyle = parseInt(session.meterStyle); + if (session.meterStyle<4){ + session.style=3; // black canvas + } else { + session.style = -1; // no canvas + } + session.audioEffects = true; + } + + if (session.meterStyle==5){ + document.documentElement.style.setProperty('--video-background-image-size-talking', 'auto 35%'); + document.documentElement.style.setProperty('--video-background-image-size-screaming', 'auto 45%'); + } + + if (urlParams.has('directorchat') || urlParams.has('dc')){ + session.directorChat = true; + } + + + if (urlParams.has('style') || urlParams.has('st')) { + session.style = urlParams.get('style') || urlParams.get('st'); + if ((parseInt(session.style) === 0) || (session.style == "controls")) { // no audio only + session.style = 0; + } else if ((parseInt(session.style) == 1) || (session.style == "justvideo")) { // no audio only + session.style = 1; + } else if ((parseInt(session.style) == 2) || (session.style == "waveform")) { // audio waveform + session.style = 2; + session.audioEffects = true; ////!!!!!!! Do I want to enable the audioEffects myself? or do it here? + } else if ((parseInt(session.style) == 3) || (session.style == "volume")) { // audio meter ; see &meterstyle , where optios include default(false), 1, and 2. + session.style = 3; + session.audioEffects = true; + } else if (parseInt(session.style) == 4) { // black background + session.style = 4; + } else if (parseInt(session.style) == 5) { // random colored background + session.style = 5; + } else if (parseInt(session.style) == 7) { // shows video elements for all connections; even those without video/audio + session.style = parseInt(session.style); + session.showall = true; + } else if (parseInt(session.style)) { // 6 is the first letter of the name, surrounded with a colored circle + session.style = parseInt(session.style); + } else { + session.style = 1; + } + } + //if (session.style){ + // getById("toggleWaveformButton").classList.remove("hidden"); + //} + + if (urlParams.has('showall')){ // just an alternative; might be compoundable + session.showall = true; + } + + + + if (urlParams.has('samplerate') || urlParams.has('sr')) { + session.sampleRate = parseInt(urlParams.get('samplerate')) || parseInt(urlParams.get('samplerate')) || 48000; + if (session.audioCtx) { + session.audioCtx.close(); // close the default audio context. + } + session.audioCtx = new AudioContext({ // create a new audio context with a higher sample rate. + sampleRate: session.sampleRate + }); + session.audioEffects = true; + } + + + + // if (session.audioCodec === "lyra"){ // WIP. does not work + // try { + // var { default: Module } = await import('./thirdparty/lyra/webassembly_codec_wrapper.js'); + // await Module().then((module) => { + // console.log("Initialized codec's wasmModule."); + // session.lyraCodecModule = module; + // }).catch(e => { + // console.log(`Module() error: ${e.name} message: ${e.message}`); + // }); + // } catch(e){ + // errorlog(e); + // } + // if (session.lyraCodecModule){ + // console.log("Lyra module loaded"); + // session.micSampleRate = 16000; + // session.encodedInsertableStreams = true; + // } else { + // console.log("Lyra module failed to load"); + // } + // } + + if (urlParams.has("insertablestreams")){ + session.encodedInsertableStreams = true; + } + + if (urlParams.has('micsamplerate') || urlParams.has('msr')) { + session.micSampleRate = parseInt(urlParams.get('micsamplerate')) || parseInt(urlParams.get('msr')) || 48000; + } + + if (urlParams.has('micsamplesize')) { + session.micSampleSize = parseInt(urlParams.get('micsamplesize')) || 16; + } + + if (urlParams.has('noaudioprocessing') || urlParams.has('noap')) { + session.disableWebAudio = true; // default true; might be useful to disable on slow or old computers? + session.disableViewerWebAudioPipeline = true; // this has the potential to break things. + session.audioEffects = false; // disable audio inbound effects also. + session.audioMeterGuest = false; + if (session.noisegate===null){ + session.noisegate = false; + } + } + + // For info, see this: https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidatePairStats/availableOutgoingBitrate + if (urlParams.has('maxbandwidth')) { // limits the bitrate based on the outbound total available bandwidth; chromium-based + session.maxBandwidth = urlParams.get('maxbandwidth') || 80; // 0 to 100; will reduce bitrate as a percentage of available + session.maxBandwidth = parseInt(session.maxBandwidth); + if (session.maxBandwidth > 200){ // will over ride default 2500kbps if no bitrate is specified + session.maxBandwidth = 200; + } else if (session.maxBandwidth<0){ + session.maxBandwidth = 0; + } + } + + if (urlParams.has('iframetarget')) { + session.iframetarget = urlParams.get('iframetarget'); // speciifies the IFRAME Hostname target + if (session.iframetarget){ + session.iframetarget = decodeURIComponent(session.iframetarget); + } else { + session.iframetarget = window.location.hostname; + } + } + + + if (urlParams.has('sendframes')) { + session.sendframes = urlParams.get('sendframes'); + if (session.sendframes){ + try { + session.sendframes = decodeURIComponent(session.sendframes); + } catch(e){} + } else { + session.sendframes = session.iframetarget || "*"; + } + } + + if (urlParams.has('tcp')){ // forces the TURN servers to use TCP mode; still need to add &private to force TURN also tho + session.forceTcpMode = true; + } + + if (urlParams.has('stun')) { + var stunstring = urlParams.get('stun'); + stunstring = stunstring.split(";"); + if (stunstring[0] !== "false") { // false disables the TURN server. Useful for debuggin + var stun = {}; + if (stunstring.length==3){ + stun.username = stunstring[0]; // myusername + stun.credential = stunstring[1]; //mypassword + stun.urls = [stunstring[2]]; // ["turn:turn.obs.ninja:443"]; + } else if (stunstring.length==1){ + stun.urls = [stunstring[0]]; + } + session.stunServers = [stun]; + } else { + session.stunServers = []; + } + } + if (urlParams.has('addstun')) { + var stunstring = urlParams.get('addstun'); + stunstring = stunstring.split(";"); + var stun = {}; + if (stunstring.length==3){ + stun.username = stunstring[0]; // myusername + stun.credential = stunstring[1]; //mypassword + stun.urls = [stunstring[2]]; // ["turn:turn.obs.ninja:443"]; + } else if (stunstring.length==1){ + stun.urls = [stunstring[0]]; + } + session.stunServers = session.stunServers.concat(stun); + } + + if (urlParams.has('bundle')){ + session.bundlePolicy = urlParams.get('bundle') || "MaxBundle"; // default is browser default. + } + + if (urlParams.has('turn')) { + var turnstring = urlParams.get('turn'); + + if (turnstring == "twilio") { // a sample function on loading remote credentials for TURN servers. + try { + session.ws = false; // prevents connection + var twillioRequest = new XMLHttpRequest(); + twillioRequest.onload = function() { + if (this.status === 200) { + try{ + var res = JSON.parse(this.responseText); + } catch(e){ + console.error(e); + return; + } + session.configuration = { + iceServers: [{ + "username": res["1"], + "credential": res["2"], + "url": "turn:global.turn.twilio.com:3478?transport=tcp", + "urls": "turn:global.turn.twilio.com:3478?transport=tcp" + }, + { + "username": res["1"], + "credential": res["2"], + "url": "turn:global.turn.twilio.com:443?transport=tcp", + "urls": "turn:global.turn.twilio.com:443?transport=tcp" + } + ], + sdpSemantics: 'unified-plan' // future-proofing + }; + if (session.ws===false){ + session.ws=null; // allows connection (clears state) + session.connect(); // connect if not already connected. + } + } + // system does not connect if twilio API does not respond. + }; + twillioRequest.open('GET', 'https://turn.example.com:443/twilio', true); // `false` makes the request synchronous + twillioRequest.send(); + } catch (e) { + errorlog("Twilio Failed"); + } + + } else if (turnstring == "nostun") { // disable TURN servers + session.configuration = { + sdpSemantics: 'unified-plan' // future-proofing + }; + + } else if ((turnstring == "false") || (turnstring == "off") || (turnstring == "0")) { // disable TURN servers + session.configuration = { + iceServers: session.stunServers, + sdpSemantics: 'unified-plan' // future-proofing + }; + } else { + try { + //session.configuration = {iceServers: [], sdpSemantics: 'unified-plan'}; + turnstring = turnstring.split(";"); + if (turnstring !== "false") { // false disables the TURN server. Useful for debuggin + var turn = {}; + if (turnstring.length==3){ + turn.username = turnstring[0]; // myusername + turn.credential = turnstring[1]; //mypassword + turn.urls = [turnstring[2]]; // ["turn:turn.obs.ninja:443"]; + } else if (turnstring.length==1){ + turn.urls = [turnstring[0]]; + } + session.configuration = { + iceServers: session.stunServers, + sdpSemantics: 'unified-plan' // future-proofing + }; + + session.configuration.iceServers.push(turn); + } + } catch (e) { + if (!(session.cleanOutput)) { + warnUser("TURN server parameters were wrong."); + } + errorlog(e); + } + } + } + + if (urlParams.has('apiserver') && urlParams.get('apiserver')){ // must set this after any custom TURN / STUN settings, else it might over-ride them. + session.apiserver = urlParams.get('apiserver'); + } + + if (urlParams.has('speedtest')){ // must set this after any custom TURN / STUN settings, else it might over-ride them. + session.speedtest = true; + if (urlParams.get('speedtest')){ // forces essentially UDP mode, unless TCP is specified, and some other stuff + session.speedtest = urlParams.get('speedtest').toLowerCase(); // also limits bitrate + } + setupSpeedtest(); + } + + if (urlParams.has('privacy') || urlParams.has('private') || urlParams.has('relay')) { // please only use if you are also using your own TURN service. + session.privacy = urlParams.get('privacy') || urlParams.get('private') || urlParams.get('relay') || true; + + try { // I'll re-apply this in the setupSpeedtest() promise callback, just to be case. + if (session.configuration){ // this needs to set last, otherwise it might be overridden + session.configuration.iceTransportPolicy = "relay"; // https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate/address + } + } catch (e) { + if (!(session.cleanOutput)) { + warnUser("Privacy mode failed to configure."); + } + errorlog(e); + } + + if (session.speedtest){ + warnlog("Bitrate being throttled to max of 6000 kbps"); + if (session.maxvideobitrate !== false) { + if (session.maxvideobitrate > 6000) { + session.maxvideobitrate = 6000; // Please feel free to get rid of this if using your own TURN servers... + } + } else { + session.maxvideobitrate = 6000; // don't let people pull more than 6000 from you + } + if (session.bitrate !== false) { + if (session.bitrate > 6000) { + session.bitrate = 6000; // Please feel free to get rid of this if using your own TURN servers... + } + } + } else { + warnlog("Bitrate being throttled to max of 4000 kbps"); + if (session.maxvideobitrate !== false) { + if (session.maxvideobitrate > 4000) { + session.maxvideobitrate = 4000; // Please feel free to get rid of this if using your own TURN servers... + } + } else { + session.maxvideobitrate = 4000; // don't let people pull more than 4000 from you + } + if (session.bitrate !== false) { + if (session.bitrate > 4000) { + session.bitrate = 4000; // Please feel free to get rid of this if using your own TURN servers... + } + } + } + } + + if (urlParams.has('wss')) { + session.customWSS = true; + session.wssSetViaUrl = true; + if (urlParams.get('wss')) { + session.wss = urlParams.get('wss'); + if (!session.wss.startsWith("wss://")){ + session.wss = "wss://" + session.wss; + } + } + } else if (urlParams.has('wss2')) { + session.wssSetViaUrl = true; + if (urlParams.get('wss2')) { + session.wss = urlParams.get('wss2'); + if (!session.wss.startsWith("wss://")){ + session.wss = "wss://" + session.wss; + } + } + } + + if (urlParams.has("bypass")){ + session.bypass = true; + session.customWSS = true; + } + + if (urlParams.has('osc') || urlParams.has('api')) { + if (urlParams.get('osc') || urlParams.get('api')) { + session.api = urlParams.get('osc') || urlParams.get('api') || false; + if (session.api){ + setTimeout(function(){oscClient();},1000); + } + } + } + + if (urlParams.has('postapi') || urlParams.has('posturl')) { + session.postApi = urlParams.get('postapi') || urlParams.get('posturl') || false; // ie: &postapi=https%3A%2F%2Fwebhook.site%2Fb190f5bf-e4f8-454a-bd51-78b5807df9c1 + if (session.postApi){ + try { + session.postApi = decodeURI(session.postApi) || session.postApi ; // needs to be SSL enabled. + } catch(e){ + console.error(e); + } + } + } + + if (urlParams.has('queue')) { + session.queue = true; + if (urlParams.get('queue') === "false"){ + session.queue = false; + } else if (urlParams.get('queue') === "0"){ + session.queue = false; + } else if (urlParams.get('queue') === "off"){ + session.queue = false; + } + } + + + + if (urlParams.has('push') || urlParams.has('id') || urlParams.has('permaid') ) { + session.permaid = urlParams.get('push') || urlParams.get('id') || urlParams.get('permaid'); + + if (session.permaid) { + session.permaid = sanitizeStreamID(session.permaid) || null; + session.streamID = session.permaid || session.streamID; + } else if (urlParams.has('permaid') && getStorage("permaid")){ + session.streamID = sanitizeStreamID(getStorage("permaid")) || session.streamID; + session.permaid = null; + } else { + session.permaid = null; + } + + if (urlParams.has('permaid')){ + setStorage("permaid", session.streamID, 99999) + } + + if (urlParams.has('push')){ + updateURL("push="+session.streamID, true, false); + } else if (urlParams.has('id')){ + updateURL("id="+session.streamID, true, false); // not 'officially' supporting this yet; we'll see. + } else if (urlParams.has('permaid')){ + updateURL("permaid="+session.streamID, true, false); + } else { + updateURL("push="+session.streamID, true, false); + } + + if (session.director) { // if I do a short form of this, it will cause duplications in the code elsewhere. + //var director_room_input = urlParams.get('director'); + //director_room_input = sanitizeRoomName(director_room_input); + //createRoom(director_room_input); + session.permaid = false; // used to avoid a trigger later on. + } else { + getById("container-1").className = 'column columnfade hidden'; + getById("container-4").className = 'column columnfade hidden'; + getById("dropButton").className = 'column columnfade hidden'; + + getById("info").innerHTML = ""; + if (session.videoDevice === 0) { + miniTranslate(getById("add_camera"), "share-your-mic", "Share your Microphone"); + } else { + miniTranslate(getById("add_camera"), "share-your-camera", "Share your Camera"); + } + miniTranslate(getById("add_screen"), "share-your-screen", "Share your Screen"); + getById("container-2").title = getById("add_screen").innerText; + getById("container-3").title = getById("add_camera").innerText; + + getById("passwordRoom").value = ""; + getById("videoname1").value = ""; + getById("dirroomid").innerHTML = ""; + getById("roomid").innerHTML = ""; + + getById("mainmenu").style.alignSelf = "center"; + getById("mainmenu").classList.add("mainmenuclass"); + getById("header").style.alignSelf = "center"; + + //if ((iOS) || (iPad)) { + //getById("header").style.display = "none"; // just trying to free up space. + //} + + if (session.webcamonly == true) { // mobile or manual flag 'webcam' pflag set + getById("head1").innerHTML = '- Please accept any camera permissions'; + } else { + getById("head1").innerHTML = '
    - Please select which you wish to share'; + } + + if (!session.cleanOutput){ + try { + if (window.obsstudio){ + getById("unexpectedPushLink").classList.remove("hidden"); + } + } catch(e){} + } + + } + } + if (session.roomid || urlParams.has('roomid') || urlParams.has('r') || urlParams.has('room') || filename || (session.permaid !== false)) { + var roomid = ""; + if (urlParams.has('room')) { // needs to be first; takes priority + roomid = urlParams.get('room'); + } else if (urlParams.has('roomid')) { + roomid = urlParams.get('roomid'); + } else if (urlParams.has('r')) { + roomid = urlParams.get('r'); + } else if (session.roomid) { + roomid = session.roomid; + } else if (filename) { + roomid = filename; + } + session.roomid = sanitizeRoomName(roomid); + if (session.director){ + if (session.director !== session.roomid){ + if (!session.cleanOutput){ + warnUser("Conflicting director and room values were provided.\n\n Check your URL parameters; there should be only &director OR &room",5000); + } + } + session.roomid = false; + } + } + + if ((session.permaid===false) && (session.roomid===false) && (session.view===false) && (session.effect===false) && (session.director===false)){ + session.effect = null; + } + + if (urlParams.has('effects') || urlParams.has('effect')) { + session.effect = urlParams.get('effects') || urlParams.get('effect') || null; + } else if (urlParams.has('zoom')){ + session.effect = "7"; + } + + if (window.FaceDetector !== undefined){ + document.querySelectorAll(".facetracker").forEach(ele=>{ + ele.disabled = null; + ele.removeAttribute("disabled"); + ele.title = "Will slowly pan, tilt, and zoom in on the first face detected"; + }); + } + + + if (urlParams.has('imagelist')){ // "&imagelist="+encodeURIComponent(JSON.stringify(["./media/bg_sample.webp", "./media/bg_sample2.webp"])) + var imageList = urlParams.get('imagelist'); // + if (imageList){ + try { + imageList = JSON.parse(decodeURIComponent(imageList)); + } catch(e){ + console.error(e); + } + if (imageList.length){ + session.defaultBackgroundImages = imageList; // ["./media/bg_sample.webp", "./media/bg_sample2.webp"] + } else { + warnlog("empty image array; skipping"); + } + } + } + + if (session.effect!==false){ + if (session.effect === null){ + getById("effectsDiv").style.display = "inline-block"; + session.effect = "0"; + } else if (session.effect === "0" || session.effect === "false" || session.effect === "off" || session.effect === 0){ + session.effect = false; + getById("effectSelector3").style.display = "none"; + getById("effectsDiv3").style.display = "none"; + getById("effectSelector").style.display = "none"; + getById("effectsDiv").style.display = "none"; + } + + if (session.effect === "5"){ + + loadTFLITEImages(); + + getById("effectSelector").style.display = "none"; + getById("effectsDiv").style.display = "inline-block"; + + } + if (session.effect === "3a"){ // heavier blur + session.effectValue = 5; + session.effect = "3"; + } else if (session.effect === "3"){ + session.effectValue = 2; + } else if (session.effect === "7"){ + session.effectValue = 1; + } + // mirror == 2 + // face == 1 + // blur = 3 + // green = 4 + // image = 5 + } + + + if (urlParams.has('effectvalue') || urlParams.has('ev')) { + session.effectValue = parseInt(urlParams.get('effectvalue')) || parseInt(urlParams.get('ev')) || 0; + session.effectValue_default = session.effectValue; + } + + if (session.webcamonly == true) { + if (session.introButton){ + getById("container-2").className = 'column columnfade hidden'; // Hide screen share + getById("head3").classList.add('hidden'); + getById("head3a").classList.add('hidden'); + } else { + getById("container-2").className = 'column columnfade hidden'; // Hide screen share + getById("container-3").classList.add("skip-animation"); + getById("container-3").classList.remove('pointer'); + delayedStartupFuncs.push([previewWebcam]); + } + } + if (session.introOnClean && (session.permaid===false) && (session.roomid===false)){ + //getById("container-2").className = 'column columnfade hidden'; // Hide screen share + getById("head3").classList.add('hidden'); + getById("head3a").classList.add('hidden'); + } else if (session.introOnClean && (session.scene===false) && ((session.permaid!==false || session.roomid!==false))){ + getById("container-2").className = 'column columnfade hidden'; // Hide screen share + getById("container-3").classList.add("skip-animation"); + getById("container-3").classList.remove('pointer'); + delayedStartupFuncs.push([previewWebcam]); + } + + //if (!session.director && ((ChromiumVersion == 86) || (ChromiumVersion == 77) || (ChromiumVersion == 62) || (ChromiumVersion == 51)) && (((session.permaid===false) && session.view) || (session.scene!==false))){ + // session.studioSoftware = true; // vmix + if (window.obsstudio){ + session.studioSoftware = true; + getById("saveRoom").style.display = "none"; // don't let the user save the room if in OBS + } + if (session.cleanViewer){ + if (session.view && !session.director && session.permaid===false){ + session.cleanOutput = true; + } + } + if (urlParams.has('clock') || urlParams.has('clock24')){ + let urlClock = urlParams.get('clock') || urlParams.get('clock24'); + if (urlParams.has('clock24')){ + session.clock24 = true; + } + session.showTime = true; + if (urlClock === "false"){ + session.showTime = false; + } else if (urlClock === "0"){ + session.showTime = false; + } else if (urlClock === "1"){ + getById("overlayClockContainer2").classList.add("top"); + getById("overlayClockContainer2").classList.add("left"); + } else if (urlClock === "7"){ + getById("overlayClockContainer2").classList.add("bottom"); + getById("overlayClockContainer2").classList.add("left"); + } else if (urlClock === "4"){ + getById("overlayClockContainer2").classList.add("vmiddle"); + getById("overlayClockContainer2").classList.add("left"); + } else if (urlClock === "2"){ + getById("overlayClockContainer2").classList.add("top"); + getById("overlayClockContainer2").classList.add("hmiddle"); + } else if (urlClock === "8"){ + getById("overlayClockContainer2").classList.add("bottom"); + getById("overlayClockContainer2").classList.add("hmiddle"); + } else if (urlClock === "5"){ + getById("overlayClockContainer2").classList.add("vmiddle"); + getById("overlayClockContainer2").classList.add("hmiddle"); + } else if (urlClock === "3"){ + getById("overlayClockContainer2").classList.add("top"); + getById("overlayClockContainer2").classList.add("right"); + } else if (urlClock === "9"){ + getById("overlayClockContainer2").classList.add("bottom"); + getById("overlayClockContainer2").classList.add("right"); + } else if (urlClock === "6"){ + getById("overlayClockContainer2").classList.add("vmiddle"); + getById("overlayClockContainer2").classList.add("right"); + } + + } else if (session.cleanOutput){ + session.showTime = false; + } + + if (urlParams.has('timer')){ + if (urlParams.get('timer') === "1"){ + getById("overlayClockContainer").classList.add("top"); + getById("overlayClockContainer").classList.add("left"); + } else if (urlParams.get('timer') === "7"){ + getById("overlayClockContainer").classList.add("bottom"); + getById("overlayClockContainer").classList.add("left"); + } else if (urlParams.get('timer') === "4"){ + getById("overlayClockContainer").classList.add("vmiddle"); + getById("overlayClockContainer").classList.add("left"); + } else if (urlParams.get('timer') === "2"){ + getById("overlayClockContainer").classList.add("top"); + getById("overlayClockContainer").classList.add("hmiddle"); + } else if (urlParams.get('timer') === "8"){ + getById("overlayClockContainer").classList.add("bottom"); + getById("overlayClockContainer").classList.add("hmiddle"); + } else if (urlParams.get('timer') === "5"){ + getById("overlayClockContainer").classList.add("vmiddle"); + getById("overlayClockContainer").classList.add("hmiddle"); + } else if (urlParams.get('timer') === "3"){ + getById("overlayClockContainer").classList.add("top"); + getById("overlayClockContainer").classList.add("right"); + } else if (urlParams.get('timer') === "9"){ + getById("overlayClockContainer").classList.add("bottom"); + getById("overlayClockContainer").classList.add("right"); + } else if (urlParams.get('timer') === "6"){ + getById("overlayClockContainer").classList.add("vmiddle"); + getById("overlayClockContainer").classList.add("right"); + } else { + getById("overlayClockContainer").classList.add("top"); + getById("overlayClockContainer").classList.add("hmiddle"); + } + } + + if (urlParams.has('miconlyoption') || urlParams.has('moo')){ + session.optionalMicOnly = true; + } + + if (urlParams.has('hidescreenshare') || urlParams.has('hidess') || urlParams.has('sshide') || urlParams.has('screensharehide')) { // this way I don't need to remember what it's called. I can just guess. :D + session.screenShareElementHidden = true; + } + + if (urlParams.has('sspaused') || urlParams.has('sspause') || urlParams.has('ssp')) { // this way I don't need to remember what it's called. I can just guess. :D + session.screenShareStartPaused = true; + } + + if (urlParams.has('zoomedbitrate') || urlParams.has('zb')) { // this way I don't need to remember what it's called. I can just guess. :D + session.zoomedBitrate = urlParams.get('zoomedbitrate') || urlParams.get('zb') || 2500; + session.zoomedBitrate = parseInt(session.zoomedBitrate) ; + } + + if (urlParams.has('screenshareid') || urlParams.has('ssid')) { + if (urlParams.get('screenshareid') || urlParams.get('ssid')) { + session.screenshareid = urlParams.get('screenshareid') || urlParams.get('ssid'); + session.screenshareid = sanitizeStreamID(session.screenshareid); + } else { + session.screenshareid = session.streamID + "_ss"; + } + } + + if (urlParams.has('screensharevideoonly') || urlParams.has('ssvideoonly') || urlParams.has('ssvo')) { + session.screenshareVideoOnly = true; + getById("audioScreenShare1").classList.add("hidden"); + } + + if (urlParams.has('screensharefps') || urlParams.has('ssfps')) { + if (urlParams.get('screensharefps') || urlParams.get('ssfps')) { + session.screensharefps = urlParams.get('screensharefps') || urlParams.get('ssfps'); + session.screensharefps = parseInt(session.screensharefps) || 2; + } + } + + if (urlParams.has('screensharequality') || urlParams.has('ssq')) { + if (urlParams.get('screensharequality') || urlParams.get('ssq')) { + session.screensharequality = urlParams.get('screensharequality') || urlParams.get('ssq'); + session.screensharequality = parseInt(session.screensharequality) || 0; + try { + getById("gear_screen").parentNode.removeChild(getById("gear_screen")); + } catch(e){} + } + } + + if (urlParams.has('screensharebitrate') || urlParams.has('ssbitrate')) { + session.screenShareBitrate = urlParams.get('screensharebitrate') || urlParams.get('ssbitrate'); + session.screenShareBitrate = parseInt(session.screenShareBitrate) || 2500; + } + + if (urlParams.has('screensharelabel') || urlParams.has('sslabel')) { + session.screenShareLabel = urlParams.get('screensharelabel') || urlParams.get('sslabel'); + try { + session.screenShareLabel = decodeURIComponent(session.screenShareLabel); + } catch(e){} + session.screenShareLabel = session.screenShareLabel.replace(/_/g, " ") + } + + if (urlParams.has('whepshare') || urlParams.has('whepsrc')) { + try { + session.whepSrc = urlParams.get('whepshare') || urlParams.get('whepsrc') || null; + log("WHEP SRC: "+session.whepSrc); + if (session.whepSrc){ + try { + session.whepSrc = decodeURIComponent(session.whepSrc); + } catch(e){ + session.whepSrc = session.whepSrc; + } + } else { + session.whepSrc = await promptAlt("Enter the WHEP source as a URL"); + } + if (session.whepSrc){ + session.whipoutSettings = {type:"whep", "url": session.whepSrc}; + } + + } catch(e){ + errorlog(e); + } + } + + if (session.roomid!==false){ + if (!(session.cleanOutput)) { + if (session.roomid === "test") { + if (session.password === session.defaultPassword) { + window.focus(); + var testRoomResponse = confirm(getTranslation("room-test-not-good")); + if (testRoomResponse == false) { + hangup(); + throw new Error("User requested to not enter room 'room'."); + } + } + } + } + + if (session.audioDevice === false && session.outputDevice === false) { + getById("headphonesDiv2").style.display = "inline-block"; + getById("headphonesDiv").style.display = "inline-block"; + } + getById("addPasswordBasic").style.display = "none"; + + getById("info").innerHTML = ""; + getById("info").style.color = "#CCC"; + getById("videoname1").value = session.roomid; + getById("dirroomid").innerText = session.roomid; + getById("roomid").innerText = session.roomid; + getById("container-1").className = 'column columnfade hidden'; + getById("container-4").className = 'column columnfade hidden'; + // container 5 is share media file; 6 is share website + getById("container-7").style.display = 'none'; + getById("container-8").style.display = 'none'; + getById("container-9").style.display = 'none'; + getById("container-10").style.display = 'none'; + getById("container-11").style.display = 'none'; + getById("container-12").style.display = 'none'; + getById("container-13").style.display = 'none'; + getById("container-14").style.display = 'none'; + getById("container-15").style.display = 'none'; + getById("mainmenu").style.alignSelf = "center"; + getById("mainmenu").classList.add("mainmenuclass"); + getById("header").style.alignSelf = "center"; + + if (session.webcamonly == true) { // mobile or manual flag 'webcam' pflag set + getById("head1").innerHTML = ''; + } else { + getById("head1").innerHTML = 'Please select an option to join.'; + } + + if (session.roomid.length > 0) { + if (session.videoDevice === 0) { + if (session.audioDevice === 0) { + miniTranslate(getById("add_camera"), "join-room", "Join Room"); + } else { + miniTranslate(getById("add_camera"), "join-room-with-mic", "Join Room with Microphone"); + } + } else if (session.audioDevice === 0) { + miniTranslate(getById("add_camera"), "join-room-with-camera", "Join Room with Camera"); + } else if (session.optionalMicOnly){ + miniTranslate(getById("add_camera"), "join-room-with-video", "Join Room with Video"); + miniTranslate(getById("add_microphone"), "join-room-with-mic-only", "Join Room with just Microphone"); + getById("container-3a").classList.remove("hidden"); + + } else{ + miniTranslate(getById("add_camera"), "join-room-with-camera", "Join Room with Camera"); + } + miniTranslate(getById("add_screen"), "share-screen-with-room", "Screenshare with Room"); + } else { + if (session.videoDevice === 0) { + miniTranslate(getById("add_camera"), "share-your-mic", "Share your Microphone"); + } else { + miniTranslate(getById("add_camera"), "share-your-camera", "Share your Camera"); + } + miniTranslate(getById("add_screen"), "share-your-screen", "Share your Screen"); + } + getById("head3").classList.add('hidden'); + getById("head3a").classList.add('hidden'); + getById("container-2").title = getById("add_screen").innerText; + getById("container-3").title = getById("add_camera").innerText; + + if (session.scene !== false) { + getById("container-4").className = 'column columnfade'; + getById("container-3").className = 'column columnfade'; + getById("container-2").className = 'column columnfade'; + getById("container-1").className = 'column columnfade'; + getById("header").className = 'hidden'; + getById("info").className = 'hidden'; + getById("head1").className = 'hidden'; + getById("head2").className = 'hidden'; + getById("mainmenu").style.display = "none"; + getById("translateButton").style.display = "none"; + log("Update Mixer Event on REsize SET"); + window.onresize = updateMixer; + window.onorientationchange = function(){ + setTimeout(updateMixer, 200); + + }; + joinRoom(session.roomid); // this is a scene, so we want high resolutions + getById("main").style.overflow = "hidden"; + + if (session.chatbutton === true) { + getById("chatbutton").classList.remove("hidden"); + getById("controlButtons").classList.remove("hidden"); + } else if (session.chatbutton === false) { + getById("chatbutton").classList.add("hidden"); + } + } else if ((session.permaid === null) && (session.roomid == "")) { + if (!(session.cleanOutput)) { + getById("head3").classList.remove('hidden'); + getById("head3a").classList.remove('hidden'); + } + } else if ((window.obsstudio) && (session.permaid === false) && (session.director === false) && (session.view) &&(session.roomid.length>0)) { // we already know roomid !== false + updateURL("scene", true, false); // we also know it's not a scene, but we will assume it is in this specific case. + } + + + } else if (session.director) { // if I do a short form of this, it will cause duplications in the code elsewhere. + if (directorLanding == false){ // implies director is not true or false, but a string + try{ + var director_room_input = sanitizeRoomName(session.director); + log("director_room_input:" + director_room_input); + + if (urlParams.has('codirector') || urlParams.has('directorpassword') || urlParams.has('dirpass') || urlParams.has('dp')) { + session.directorPassword = urlParams.get('codirector') || urlParams.get('directorpassword') || urlParams.get('dirpass') || urlParams.get('dp'); + if (!session.directorPassword) { + window.focus(); + session.directorPassword = await promptAlt(getTranslation("enter-director-password"), true); + } else { + try { + session.directorPassword = decodeURIComponent(session.directorPassword); + } catch(e){} + } + if (session.directorPassword){ + session.directorPassword = sanitizePassword(session.directorPassword) + await generateHash(session.directorPassword + session.salt + "abc123", 12).then(function(hash) { // million to one error. + log("dir room hash is " + hash); + session.directorHash = hash; + return; + }).catch(errorlog); + } else { + session.directorPassword = false; + } + } + + setTimeout(function(director_room_input){createRoom(director_room_input);},20, director_room_input); + } catch(e){ + directorLanding = true; + session.director = true; + } + } + if (session.chatbutton === true) { + getById("chatbutton").classList.remove("hidden"); + getById("controlButtons").classList.remove("hidden"); + } else if (session.chatbutton === false) { + getById("chatbutton").classList.add("hidden"); + } + } else if (session.view && (session.permaid === false)) { + //if (!session.activeSpeaker){ + session.audioMeterGuest = false; + //} + if ((session.style===false) && window.obsstudio){ + session.style = 1; + } + if (session.audioEffects === null) { + session.audioEffects = false; + } + log("Update Mixer Event on REsize SET"); + getById("translateButton").style.display = "none"; + window.onresize = updateMixer; + window.onorientationchange = function(){setTimeout(function(){ + updateMixer(); + }, 200);}; + getById("main").style.overflow = "hidden"; + + if (session.chatbutton === true) { + getById("chatbutton").classList.remove("hidden"); + getById("controlButtons").classList.remove("hidden"); + } else if (session.chatbutton === false) { + getById("chatbutton").classList.add("hidden"); + } + } + + if (urlParams.has('nofileshare') || urlParams.has('nodownloads') || urlParams.has('nofiles')){ + session.hostedFiles = false; + session.nodownloads = true; + getById('sharefilebutton').style.display = "none"; + getById('sharefilebutton').classList.add("hidden"); + } else if (session.mobile){ + getById('sharefilebutton').style.display = "none"; + getById('sharefilebutton').classList.add("hidden"); + } else if (session.roomid==false){ + getById('sharefilebutton').style.display = "none"; + getById('sharefilebutton').classList.add("hidden"); + } else if (session.scene!==false){ + getById('sharefilebutton').style.display = "none"; + getById('sharefilebutton').classList.add("hidden"); + } else if (session.cleanOutput){ + getById('sharefilebutton').style.display = "none"; + getById('sharefilebutton').classList.add("hidden"); + } + + if (session.audioEffects === null) { + session.audioEffects = true; + } + + if (session.audioEffects) { + getById("channelGroup1").style.display = "block"; + getById("channelGroup2").style.display = "block"; + } + + if (urlParams.has('hidemenu') || urlParams.has('hm')) { // needs to happen the room and permaid applications + getById("mainmenu").style.display = "none"; + getById("header").style.display = "none"; + getById("mainmenu").style.opacity = 0; + getById("header").style.opacity = 0; + } + + if (session.view) { + getById("main").className = ""; + getById("credits").style.display = 'none'; + try { + if (session.label === false) { + if (document.title == "") { + document.title = "View=" + session.view.toString(); + } else { + document.title += ", View=" + session.view.toString(); + } + } + } catch (e) { + errorlog(e); + }; + } + + + if (urlParams.get('auth')) { + session.auth = urlParams.get('auth'); + } + + if (urlParams.has('waitimage')){ + session.waitImage = urlParams.get('waitimage') || false; + } + + if (((session.view) && (session.roomid === false)) || (session.waitImage && (session.scene!==false))) { + + getById("container-4").className = 'column columnfade'; + getById("container-3").className = 'column columnfade'; + getById("container-2").className = 'column columnfade'; + getById("container-1").className = 'column columnfade'; + //getById("header").className = 'hidden'; + getById("info").className = 'hidden'; + getById("header").className = 'hidden'; + getById("head1").className = 'hidden'; + getById("head2").className = 'hidden'; + getById("head3").classList.add('hidden'); + getById("head3a").classList.add('hidden'); + + + getById("mainmenu").style.backgroundRepeat = "no-repeat"; + getById("mainmenu").style.backgroundPosition = "bottom center"; + getById("mainmenu").style.minHeight = "100%"; + getById("mainmenu").style.minWidth = "100%"; + getById("mainmenu").style.backgroundSize = "100px 100px"; + getById("mainmenu").innerHTML = ''; + getById("mainmenu").classList.remove("row"); + getById("mainmenu").style.display = "block"; + + if (urlParams.has('waittimeout')){ + session.waitImageTimeout = parseInt(urlParams.get('waittimeout')) || 0; + } + if (!session.fakeFeeds){ + session.waitImageTimeoutObject = setTimeout(function() { + session.waitImageTimeoutObject = true; + try { + if ((session.view)) { + if (document.getElementById("mainmenu")) { + if (session.waitImage){ + getById("mainmenu").innerHTML += ''; + getById("retryimage").src = decodeURIComponent(session.waitImage); + getById("retryimage").onerror = function(){this.style.display='none';}; + + if (session.cover) { + getById("retryimage").style.objectFit = "cover"; + } + + } else if (!(session.cleanOutput)){ + getById("mainmenu").innerHTML += '
    '; + getById("retrySpinner").onclick = function(){ + updateURL("cleanoutput"); + location.reload(); + } + getById("retrySpinner").title = getTranslation("waiting-for-the-stream"); + } + if (urlParams.has('waitmessage')){ + getById("mainmenu").innerHTML += '
    '; + getById("retrymessage").innerText = urlParams.get('waitmessage'); + getById("retrySpinner").title = urlParams.get('waitmessage'); + } + } + } + } catch (e) { + errorlog(e); + } + }, session.waitImageTimeout); + } + + log("auto request videos"); + if ((iPad || iOS) && navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1 && SafariVersion > 13) { // Modern iOS doesn't need pop up + play(); + } else if (navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) { // Safari on Desktop does require pop up + if (!(session.cleanOutput)) { + warnUser("Safari requires us to ask for an audio permission to use peer-to-peer technology. You will need to accept it in a moment if asked to view this live video", 20000); + } + navigator.mediaDevices.getUserMedia({ + audio: true + }).then(function() { + closeModal(); + play(); + }).catch(function() { + play(); + }); + } else { // everything else is OK. + play(); + } + } else if (session.roomid) { + try { + if (session.label === false) { + if (document.title == "") { + document.title = "Room=" + session.roomid.toString(); + } else { + document.title += ": " + session.roomid.toString(); + } + } + } catch (e) { + errorlog(e); + }; + } + + hideHomeCheck(); + + setTimeout(function(){ + for (var i in delayedStartupFuncs) { + var cb = delayedStartupFuncs[i]; + log(cb.slice(1)); + cb[0](...cb.slice(1)); // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#A_better_apply + } + delayedStartupFuncs = []; + },50); + + if ((session.effect=="3") || (session.effect=="4") || (session.effect=="5")){ + attemptTFLiteJsFileLoad(); + } else if (session.effect=="6"){ + loadTensorflowJS(); + } else if (session.effect=="9"){ + session.effect="sample"; + //loadEffect(session.effect); + warnUser("Loading custom effects model...",1000); + } else if (session.effect=="10"){ + session.effect="dog"; + //loadEffect(session.effect); + warnUser("Loading custom effects model...",1000); + } else if (session.effect=="11"){ + session.effect="anon"; + //loadEffect(session.effect); + warnUser("Loading custom effects model...",1000); + } else if (session.effect=="12"){ + session.effect="sample"; + //loadEffect(session.effect); + warnUser("Loading custom effects model...",1000); + } else if (session.effect=="facetracking"){ + session.effect="1"; + } else if (session.effect=="zoom"){ + session.effect="7"; + } + + if (session.effect === "3"){ + getById("selectEffectAmount").style.display = "block"; + getById("selectEffectAmount3").style.display = "block"; + getById("selectEffectAmountInput").value = session.effectValue; + getById("selectEffectAmountInput3").value = session.effectValue; + } else if (session.effect === "7"){ + getById("selectEffectAmount").style.display = "block"; + getById("selectEffectAmount3").style.display = "block"; + session.effectValue = 1.0; + getById("selectEffectAmountInput").min = 1; + getById("selectEffectAmountInput").max = 1.99; + getById("selectEffectAmountInput").step = 0.01 + getById("selectEffectAmountInput3").min = 1; + getById("selectEffectAmountInput3").max = 1.99; + getById("selectEffectAmountInput3").step = 0.01 + + getById("selectEffectAmountInput").value = session.effectValue; + getById("selectEffectAmountInput3").value = session.effectValue; + } + + if (location.protocol !== 'https:') { + if (!(session.cleanOutput)) { + warnUser("SSL (https) is not enabled. This site will not work without it!

    Try accessing the site from here instead.", false, false); + } + } + + if (session.sensorData) { + setupSensorData(parseInt(session.sensorData)); + } + + try { + navigator.mediaDevices.ondevicechange = reconnectDevices; + } catch (e) { + errorlog(e); + } + + if (urlParams.has('autohide')) { + session.autohide=true; + } + if (session.autohide && (session.scene===false)){// && (session.roomid!==false)){ + getById("main").onmouseover = showControl; // this is correct. (it's not session.showControls) + document.ontouchstart = showControl; // this is correct. (it's not session.showControls) + getById("gridlayout").classList.add("nocontrolbar"); + } + + if (urlParams.has('experimental')) { + session.experimental = true; + } + + if (urlParams.has('flagship')) { + session.flagship = true; + } + //if (!session.flagship && session.mobile && (session.limitTotalBitrate===false)){ + // session.limitTotalBitrate = session.totalRoomBitrate_default; // 500, with the max per guest stream out at maxMobileBitrate (350kbps) or 35-kbps if more than X in the room. + //} + + if (urlParams.has('maxmobilebitrate')) { + session.maxMobileBitrate = parseInt(urlParams.has('maxmobilebitrate')) || 0; + } + if (urlParams.has('lowmobilebitrate')) { + session.lowMobileBitrate = parseInt(urlParams.has('lowmobilebitrate')) || 0; + } + + // Please contact steve on discord.vdo.ninja if you'd like this iFRAME tweaked, expanded, etc -- it's updated based on user request + + session.remoteInterfaceAPI = function(e) { // iFRAME api support + if (!e.data || (typeof e.data !== "object")){ + warnlog(e); + return; + } + log(e); + try { + if ("function" in e.data) { // these are calling in-app functions, with perhaps a callback -- TODO: add callbacks + var ret = null; + if (e.data.function === "previewWebcam") { + ret = previewWebcam(); + } else if (e.data.function === "changeHTML") { + ret = getById(e.data.target); + ret.innerHTML = e.data.value; + } else if (e.data.function === "publishScreen") { + ret = publishScreen(); + } else if (e.data.function === "routeMessage"){ + try { + session.ws.onmessage({data: e.data.value}); + } catch(e){warnlog("handshake not yet setup");} + } else if (e.data.function === "eval") { + eval(e.data.value); // eval == evil ; feedback welcomed + } + } + } catch (err) { + errorlog(err); + } + + if ("sendData" in e.data) { // send generic data via p2p. Send whatever you want I guess; there is a max chunk size of course. Use filetransfer for large files? + var UUID = false; + var streamID = false; + var type = false; + if (e.data.UUID){ + UUID = e.data.UUID; + } else if (e.data.streamID){ + streamID = e.data.streamID; + } + if (e.data.type){ + type = e.data.type; + } + var ret = session.sendGenericData(e.data.sendData, UUID, streamID, type); // comes out the other side as: ("dataReceived", data, UUID); + if (!ret){warnlog("Not connected yet or no peers available");} + return; + } + + if ("PPT" in e.data){ + log("PTT activated-webmain"); + if (e.data.PPT === true) { // unmute + session.muted = false; // set + getById("mutebutton").classList.add("PPTActive"); + log(session.muted); + toggleMute(true); // apply + + } else if (e.data.PPT === false) { // mute + session.muted = true; // set + getById("mutebutton").classList.remove("PPTActive"); + log(session.muted); + toggleMute(true); // apply + } else if (e.data.PPT === "toggle") { // toggle + toggleMute(); + } + return; // this is a high-load call, so lets skip the rest of the checks to save cpu. + } + + if ("sendChat" in e.data) { + sendChat(e.data.sendChat); // sends to all peers; more options down the road + return; + } + // Chat out gets called via getChatMessage function + // Related code: parent.postMessage({"chat": {"msg":-----,"type":----,"time":---} }, "*"); + + // session.requestResolution(vid.dataset.UUID, wrw*window.devicePixelRatio, hrh*window.devicePixelRatio); + + if ("mic" in e.data) { // this should work for the director's mic mute button as well. Needs to be manually enabled the first time still tho. + if (e.data.mic === true) { // unmute + session.muted = false; // set + log(session.muted); + toggleMute(true); // apply + } else if (e.data.mic === false) { // mute + session.muted = true; // set + log(session.muted); + toggleMute(true); // apply + } else if (e.data.mic === "toggle") { // toggle + toggleMute(); + } + } + + if ("toggleSettings" in e.data) { // this should work for the director's mic mute button as well. Needs to be manually enabled the first time still tho. + + if (e.data.toggleSettings && !toggleSettingsState){ + toggleSettings(); + } else if (e.data.toggleSettings=="toggle"){ + toggleSettings(); + } else if (toggleSettingsState){ + toggleSettings(); + } + } + + + if ("camera" in e.data) { // this should work for the director's mic mute button as well. Needs to be manually enabled the first time still tho. + if (e.data.camera === true) { // unmute + session.videoMuted = false; // set + log(session.videoMuted); + toggleVideoMute(true); // apply + } else if (e.data.camera === false) { // mute + session.videoMuted = true; // set + log(session.videoMuted); + toggleVideoMute(true); // apply + } else if (e.data.camera === "toggle") { // toggle + toggleVideoMute(); + } + } + + if ("keyframe" in e.data) { + session.sendKeyFrameScenes(); + } + + + if ("groups" in e.data) { + if (typeof e.data.groups == "object"){ + session.group = e.data.groups || []; + } else if (!e.data.group){ + session.group = []; + } else { + session.group = e.data.groups.split(","); + } + var eleGroup = getById("groups"); + eleGroup.querySelectorAll('[data-action-type="toggle-group"][data-group]').forEach(group=>{ + if (!(session.group && session.group.includes(group))){ + group.remove("green"); + } + }); + + if (session.group){ + session.group.forEach(group =>{ + var ele = eleGroup.querySelector('[data-action-type="toggle-group"][data-group="'+group+'"'); + if (!ele){ + ele = document.createElement("div"); + ele.dataset.actionType = "toggle-group"; + ele.dataset.group = group; + ele.classList.add('float'); + ele.style.display = "inline-block"; + ele.role = "button"; + ele.innerHTML = '
    '+group; + eleGroup.appendChild(ele); + ele.onclick = function(){ + changeGroupDirectorAPI(this.dataset.group); + } + } + ele.classList.add("green"); + }); + } + + updateMixer(); + + if (session.group.length || session.allowNoGroup){ + session.sendMessage({"group":session.group.join(",")}); + if (session.screenShareState && (session.screenshareType ===3)){ + session.sendMessage({"group":session.group.join(","), altUUID:true}); + } + } else { + session.sendMessage({"group":false}); + if (session.screenShareState && (session.screenshareType ===3)){ + session.sendMessage({"group":false, altUUID:true}); + } + } + + } + + if ("groupView" in e.data) { + if (typeof e.data.groupView == "object"){ + session.groupView = e.data.groupView || []; + } else if (!e.data.groupView){ + session.groupView = []; + } else { + session.groupView = e.data.groupView.split(","); + } + updateMixer(); + } + + + if ("mute" in e.data) { + if (e.data.mute === true) { // unmute + session.speakerMuted = true; // set + toggleSpeakerMute(true); // apply + } else if (e.data.mute === false) { // mute + session.speakerMuted = false; // set + toggleSpeakerMute(true); // apply + } else if (e.data.mute === "toggle") { // toggle + toggleSpeakerMute(); + } + } else if ("speaker" in e.data) { // same thing as mute. + if (e.data.speaker === true) { // unmute + session.speakerMuted = false; // set + toggleSpeakerMute(true); // apply + } else if (e.data.speaker === false) { // mute + session.speakerMuted = true; // set + toggleSpeakerMute(true); // apply + } else if (e.data.speaker === "toggle") { // toggle + toggleSpeakerMute(); + } + } + + if ("record" in e.data) { + if (e.data.record == false) { // mute + if ("recording" in session.videoElement) { + recordLocalVideo("stop"); + } + } else if (e.data.record === true){ + if ("recording" in session.videoElement) { + // already recording + } else { + recordLocalVideo("start"); + } + } else if (e.data.record){ + var video = document.getElementById(e.data.record); + if (video){ + var videoKbps = 4000; + if (session.recordLocal !== false) { + videoKbps = session.recordLocal; + } + recordLocalVideo(null, videoKbps, video); + } + } + } + + + if ("volume" in e.data) { // might not work with iframes or meshcast currently. + session.volume = parseFloat(e.data.volume) || 0; + if (session.volume > 1.0){ // this is a bit quasi improper. But the API is official 0 to 1.0; not 0 to 100, so this is mainly a catch for those not using the API right. + session.volume = session.volume/100.0; + } + if (!("target" in e.data) || (e.data.target == "*")){ + if (session.videoElement){ + session.videoElement.volume = session.volume; + } + } + for (var i in session.rpcs) { + try { + if (!session.rpcs[i].videoElement){continue;} + if ("streamID" in session.rpcs[i]) { + if ("target" in e.data) { + if ((session.rpcs[i].streamID == e.data.target) || (e.data.target == "*")) { // specify a stream ID or let it apply to all videos + session.rpcs[i].videoElement.volume = session.volume; + } + } else { + session.rpcs[i].videoElement.volume = session.volume; + } + } + } catch (e) { + errorlog(e); + } + } + } + + if ("enableYouTube" in e.data){ // panning adjusts the stereo pan , although current its UUID based. can add stream ID based if requested. + if (typeof e.data.enableYouTube == "string"){ + session.youtubeKey = e.data.enableYouTube; + } else if (!session.youtubeKey){ + errorlog("No Youtube Key provided"); + } + console.log(session.youtubeKey); + YoutubeChatInterface(true); + } + + if ("nextSlide" in e.data){ // panning adjusts the stereo pan , although current its UUID based. can add stream ID based if requested. + nextSlide(); + } + + if ("prevSlide" in e.data){ // panning adjusts the stereo pan , although current its UUID based. can add stream ID based if requested. + gobackSlide(); + } + + if ("panning" in e.data){ // panning adjusts the stereo pan , although current its UUID based. can add stream ID based if requested. + if ("UUID" in e.data){ + try { + adjustPan(UUID, e.data.panning); + } catch (e) { + errorlog(e); + } + } else { + for (var i in session.rpcs) { + try { + adjustPan(i, e.data.panning); + } catch (e) { + errorlog(e); + } + } + } + } + + if (("targetBitrate" in e.data) || ("targetAudioBitrate" in e.data)) { // this sets the fundemental bitrate target, but does not necessarily "lock" . + + var msg = {}; + if ("targetBitrate" in e.data){ + msg.targetBitrate = e.data.targetBitrate; + } + if ("targetAudioBitrate" in e.data){ + msg.targetAudioBitrate = e.data.targetAudioBitrate; + } + if (e.data.requestAs){ + msg.requestAs = e.data.requestAs; + } + if (e.data.remote){ + msg.remote = e.data.remote; + } + for (var i in session.rpcs) { + try { + if ("streamID" in session.rpcs[i]) { + if ("target" in e.data) { + if ((session.rpcs[i].streamID == e.data.target) || (e.data.target == "*")) { // specify a stream ID or let it apply to all videos + session.sendRequest(msg, i); + } + } else if (e.data.UUID && (e.data.UUID===i)) { + session.sendRequest(msg, i); + } else if (e.data.streamID) { + if (session.rpcs[i].streamID == e.data.streamID) { // specify a stream ID or let it apply to all videos + session.sendRequest(msg, i); + } + } else { + session.sendRequest(msg, i); // bitrate = 0 pauses the video + } + } + } catch (e) { + errorlog(e); + } + } + } + + if ("manualBitrate" in e.data){ + for (var i in session.rpcs) { + try { + if ("streamID" in session.rpcs[i]) { + if ("target" in e.data) { + if ((session.rpcs[i].streamID == e.data.target) || (e.data.target == "*")) { // specify a stream ID or let it apply to all videos + session.rpcs[i].manualBandwidth = e.data.manualBitrate; + session.requestRateLimit(false, i); + } + } else if (e.data.UUID && (e.data.UUID===i)) { + session.rpcs[i].manualBandwidth = e.data.manualBitrate; + session.requestRateLimit(false, i); + } else if (e.data.streamID) { + if (session.rpcs[i].streamID == e.data.streamID) { // specify a stream ID or let it apply to all videos + session.rpcs[i].manualBandwidth = e.data.manualBitrate + session.requestRateLimit(false, i); + } + } else { + session.rpcs[i].manualBandwidth = e.data.manualBitrate; + session.requestRateLimit(false, i); + } + } + } catch (e) { + errorlog(e); + } + } + } + + if ("bitrate" in e.data) { /// set a video bitrate for a video; scene or view link; kbps + var lock = true; + if ("lock" in e.data){ // since this is the iframe API, we're going to assume the default is manual over-ride. VDO.Ninja's automixer logic won't override a locked bitrate. + lock = e.data.lock; + } + for (var i in session.rpcs) { + try { + if ("streamID" in session.rpcs[i]) { // we only target publishers with this call + if ("target" in e.data) { + if ((session.rpcs[i].streamID == e.data.target) || (e.data.target == "*")) { // specify a stream ID or let it apply to all videos + session.requestRateLimit(e.data.bitrate, i, false, lock); + } + } else if (e.data.UUID && (e.data.UUID===i)) { + session.requestRateLimit(e.data.bitrate, i, false, lock); + } else if (e.data.streamID) { + if (session.rpcs[i].streamID == e.data.streamID) { // specify a stream ID or let it apply to all videos + session.requestRateLimit(e.data.bitrate, i, false, lock); + } + } else { + session.requestRateLimit(e.data.bitrate, i, false, lock); // bitrate = 0 pauses the video + } + } + } catch (e) { + errorlog(e); + } + } + } + + if ("audiobitrate" in e.data) { // changes the audio bitrate of a specific or all inbound media tracks. kbps + var lock = true; + if ("lock" in e.data){ // since this is the iframe API, we're going to assume the default is manual over-ride. VDO.Ninja's automixer logic won't override a locked bitrate. + lock = e.data.lock; + } + for (var i in session.rpcs) { + try { + if ("streamID" in session.rpcs[i]) { // we only target publishers with this call + if ("target" in e.data) { + if ((session.rpcs[i].streamID == e.data.target) || (e.data.target == "*")) { // specify a stream ID or let it apply to all videos + session.requestAudioRateLimit(parseInt(e.data.bitrate), i, lock); + } + } else if (e.data.UUID && (e.data.UUID===i)) { + session.requestAudioRateLimit(parseInt(e.data.bitrate), i, lock); + } else if (e.data.streamID) { + if (session.rpcs[i].streamID == e.data.streamID) { // specify a stream ID or let it apply to all videos + session.requestAudioRateLimit(parseInt(e.data.bitrate), i, lock); + } + } else { + session.requestAudioRateLimit(parseInt(e.data.bitrate), i, lock); // bitrate = 0 pauses the video + } + } + } catch (e) { + errorlog(e); + } + } + } + + if ("changeVideoDevice" in e.data) { + warnlog(e.data.changeVideoDevice); + changeVideoDevice(e.data.changeVideoDevice); + } + + if ("changeAudioDevice" in e.data) { + warnlog(e.data.changeAudioDevice); + changeAudioDevice(e.data.changeAudioDevice); + } + + if ("changeAudioOutputDevice" in e.data) { + warnlog(e.data.changeAudioOutputDevice); + changeAudioOutputDeviceById(e.data.changeAudioOutputDevice); + } + + if ("getDeviceList" in e.data) { // get a list of local camera / audio devices + warnlog(e.data.getDeviceList); + enumerateDevices().then(function(deviceInfos) { + parent.postMessage({ + "deviceList": JSON.parse(JSON.stringify(deviceInfos)) + }, session.iframetarget); + }); + } + + if ("sceneState" in e.data) { // TRUE OR FALSE - tells the connected peers if they are live or not via a tally light change. + var msg = {}; + msg.obsState = {}; + msg.obsState.visibility = e.data.sceneState || false; + msg.obsState.recording = e.data.sceneState || false; + session.sendRequest(msg); + } + + if ("layouts" in e.data) { + session.layouts = e.data.layouts; + if ("obsSceneTriggers" in e.data) { + session.obsSceneTriggers = e.data.obsSceneTriggers; + } else { + session.obsSceneTriggers = false; + } + for (var uid in session.pcs){ + if (session.pcs[uid].layout){ + session.sendMessage(e.data, uid); + } + } + // session.obsSceneSync(); // not sure I need to trigger this? + log(e.data); + } + + if ("sendMessage" in e.data) { // webrtc send to viewers + session.sendMessage(e.data); + } + + if ("sendRequest" in e.data) { // webrtc send to publishers + session.sendRequest(e.data); + } + + if ("sendRawMIDI" in e.data) { // webrtc send to publishers + //var msg = {}; + //msg.midi = {}; + //msg.midi.d = e.data.sendRawMIDI.data; aka [d1,d2,d3]; + //msg.midi.c = e.data.sendRawMIDI.channel; + //msg.midi.s = e.data.sendRawMIDI.timestamp; + // e.data.UUID or e.data.streamID or leave empty to send to all + if ("UUID" in e.data){ + sendRawMIDI(e.data.sendRawMIDI, e.data.UUID); // send to connection + } else if (e.data.streamID){ + sendRawMIDI(e.data.sendRawMIDI, false, e.data.streamID); // send to connection + } else { + sendRawMIDI(e.data.sendRawMIDI); // send to all + } + return; // make it send faster. + } + + if ("sendPeers" in e.data) { // webrtc send message to every connected peer; like send and request; a hammer vs a knife. + session.sendPeers(e.data); + } + + if ("reload" in e.data) { // reload the page + reloadRequested(); // location.reload();, but with no user prompt (force reload) + } + + if ("getFaces" in e.data){ + if (e.data.faceTrack){ + session.grabFaceData = true; + getFaces(); + } else { + session.grabFaceData = false; + } + } + + if (("getStats" in e.data)){ + var stats = {}; + try { + stats.inbound_stats = {}; + stats.total_outbound_connections = Object.keys(session.pcs).length; + stats.total_inbound_connections = Object.keys(session.rpcs).length; + for (var i in session.rpcs) { + stats.inbound_stats[session.rpcs[i].streamID] = session.rpcs[i].stats; + } + for (var uuid in session.pcs) { + setTimeout(function(UUID) { + session.pcs[UUID].getStats().then(function(stats) { + stats.forEach(stat => { + + if (stat.id && stat.id.startsWith("DEPRECATED_")){return;} + + if (stat.type == "outbound-rtp") { + if (stat.kind == "video") { + + if ("qualityLimitationReason" in stat) { + + session.pcs[UUID].stats.quality_limitation_reason = stat.qualityLimitationReason; + } + if ("framesPerSecond" in stat) { + session.pcs[UUID].stats.resolution = stat.frameWidth + " x " + stat.frameHeight + " @ " + stat.framesPerSecond; + } + if ("encoderImplementation" in stat) { + session.pcs[UUID].stats.encoder = stat.encoderImplementation; + } + } + } else if (stat.type == "remote-candidate") { + if ("relayProtocol" in stat) { + if ("ip" in stat) { + session.pcs[UUID].stats.remote_relay_IP = stat.ip; + } + session.pcs[UUID].stats.remote_relayProtocol = stat.relayProtocol; + } + if ("candidateType" in stat) { + session.pcs[UUID].stats.remote_candidateType = stat.candidateType; + } + } else if (stat.type == "local-candidate") { + if ("relayProtocol" in stat) { + if ("ip" in stat) { + session.pcs[UUID].stats.local_relayIP = stat.ip; + } + session.pcs[UUID].stats.local_relayProtocol = stat.relayProtocol; + } + if ("candidateType" in stat) { + session.pcs[UUID].stats.local_candidateType = stat.candidateType; + } + } else if ((stat.type == "candidate-pair" ) && (stat.nominated)) { + + if ("availableOutgoingBitrate" in stat){ + session.pcs[UUID].stats.available_outgoing_bitrate_kbps = parseInt(stat.availableOutgoingBitrate/1024); + } + if ("totalRoundTripTime" in stat){ + if ("responsesReceived" in stat){ + session.pcs[UUID].stats.average_roundTripTime_ms = parseInt((stat.totalRoundTripTime/stat.responsesReceived)*1000); + } + + } + } + return; + }); + return; + }); + }, 0, uuid); + } + } catch(e){ + // disconnected probably + } + setTimeout(function() { + stats.outbound_stats = {}; + try { + for (var i in session.pcs) { + stats.outbound_stats[i] = session.pcs[i].stats; + } + } catch(e){} + parent.postMessage({ + "stats": stats + }, session.iframetarget); + }, 1000); + } + + if ("getRemoteStats" in e.data) { + if (session.remote){ + session.sendRequest({"requestStats":true, "remote":session.remote}); + } else { + session.sendRequest({"requestStats":true}); + } + } + + if ("requestStatsContinuous" in e.data) { + if (session.remote){ + session.sendRequest({"requestStatsContinuous":e.data.requestStatsContinuous, "remote":session.remote}); + } else { + session.sendRequest({"requestStatsContinuous":e.data.requestStatsContinuous}); + } + } + + if ("getLoudness" in e.data) { + log("GOT LOUDNESS REQUEST"); + if (e.data.getLoudness == true) { + + if (!session.pushLoudness && (session.audioEffects!==true)){ + session.pushLoudness = true; + for (var i in session.rpcs) { + updateIncomingAudioElement(i); // this can be called when turning on/off inbound audio processing. + } + } else { + session.pushLoudness = true; + } + + var loudness = {}; + for (var i in session.rpcs) { + loudness[session.rpcs[i].streamID] = session.rpcs[i].stats.Audio_Loudness; + } + + parent.postMessage({ + "loudness": loudness + }, session.iframetarget); + + } else { + if (session.pushLoudness && !session.audioEffects){ // turn off audio processing + session.pushLoudness = false; + for (var i in session.rpcs) { + updateIncomingAudioElement(i) + } + } else { + session.pushLoudness = false; // can't turn off audio processing + } + } + } + + if ("getEffectsData" in e.data) { + log("GOT getEffects Data REQUESTed"); // face tracking info, etc. + if (e.data.getEffectsData !== false) { + session.pushEffectsData = e.data.getEffectsData; // which effect do you want the data from? it won't enable the effect necessarily; just the ML pipeline + + //parent.postMessage({ + // "effectsData": effectsData, + // "effectsID": session.pushEffectsData + //}, session.iframetarget); + + } else { + session.pushEffectsData = false; + } + } + + if ("getStreamIDs" in e.data) { // get a list of stream Ids, with a label if it is present. label = false if not there + if (e.data.getStreamIDs) { + var streamIDs = {}; + for (var i in session.rpcs) { + streamIDs[session.rpcs[i].streamID] = session.rpcs[i].label; + } + parent.postMessage({ + "streamIDs": streamIDs + }, session.iframetarget); + } + } + + if ("getStreamInfo" in e.data) { // get a list of stream Ids, with a label if it is present. label = false if not there + try { + var UUIDS = {}; + for (var i in session.rpcs){ + UUIDS[i] = {}; + UUIDS[i].label = session.rpcs[i].label || false; + UUIDS[i].streamID = session.rpcs[i].streamID || false; + if (session.rpcs[i].stats && session.rpcs[i].stats.info){ + UUIDS[i].info = session.rpcs[i].stats.info; + } else { + UUIDS[i].info = {}; + } + } + parent.postMessage({ + "streamInfo": UUIDS + }, session.iframetarget); + } catch(e){ + errorlog(e); + } + } + + if (("close" in e.data) || ("hangup" in e.data)) { // disconnect and hangup all inbound streams. + var tmp = e.data.close || e.data.hangup; + if (tmp == "estop"){ // try to stop the video recording even if not complete; if you can't wait even ms before a reload/exit. + console.log("ESTOP"); + session.hangup(false,true); + } else if (tmp == "reload"){ // stop and reload the page safely. + session.hangup(true); + } else { // just hangup, but can take up to 1-second to do so fully. + session.hangup(); + } + } + if ("hangup" in e.data) { // disconnect and hangup all inbound streams. + session.hangup(); + } + + if ("style" in e.data) { // insert a custom style sheet + try { + const style = document.createElement('style'); + style.textContent = e.data.style; + document.head.append(style); + log(style); + } catch (e) { + errorlog(e); + } + } + + if ("getDetailedState" in e.data) { + var detailedState = getDetailedState(); + parent.postMessage({ + "detailedState": detailedState + }, session.iframetarget); + } + + if ("automixer" in e.data) { // stop the auto mixer if you want to control the layout and bitrate yourself + if (e.data.automixer == true) { + session.manual = false; + try { + updateMixer(); + } catch (e) {} + } else if (e.data.automixer == false) { + session.manual = true; + } + } + + if ("advancedMode" in e.data){ + if (e.data.advancedMode){ + // Un-hiding advanced items + document.querySelectorAll(".advanced").forEach(element => { + element.classList.remove("hide"); + }) + } else { + // Hiding advanced items + document.querySelectorAll(".advanced").forEach(element => { + element.classList.add("hide"); + }) + } + } + + if ("requestStream" in e.data){ + if (e.data.requestStream){ // load a specific stream ID + log("requestStream iframe api"); + session.requestStream(e.data.requestStream); + } // don't use if the stream is in your room (as not needed) + } // you can load a stream ID from inside a room that exists outside any room + + + if ("previewMode" in e.data){ + if ("layout" in e.data){ + session.layout = e.data.layout; + pokeIframeAPI("layout-updated", session.layout); + } + switchModes(e.data.previewMode); + } else if ("layout" in e.data){ + warnlog("changing layout request via IFRAME API"); + session.layout = e.data.layout; + pokeIframeAPI("layout-updated", session.layout); + + if (e.data.obsCommand){ + issueLayoutOBS(e.data); + } else { + if (session.director){ + if ("scene" in e.data){ + if ("UUID" in e.data){ + issueLayout(e.data.layout, e.data.scene, e.data.UUID); + } else { + issueLayout(e.data.layout, e.data.scene); + } + } else if ("UUID" in e.data){ + issueLayout(e.data.layout, false, e.data.UUID); + } + } + } + updateMixer(); + } else if (e.data.obsCommand){ + errorlog("obsCommand via iframe API currently needs a layout.."); + } + + + if ("slotmode" in e.data){ + if (session.slotmode){ + session.slotmode = parseInt(e.data.slotmode); + } else { + session.slotmode = false; + } + } + + //////////// manual scale. Request a specific down-scaled resolution from a remote connection + var targetWidth = false; + var targetHeight = false; + if ("targetWidth" in e.data){ + targetWidth = e.data.targetWidth || 0; + } + if ("targetHeight" in e.data){ + targetHeight = e.data.targetHeight || 0; + } + // session.viewheight or session.viewwidth + if ((targetWidth || targetHeight) && e.data.UUID){ + var requestAs = false; + if (e.data.requestAs){ + requestAs = e.data.requestAs; + } + session.requestResolution(e.data.UUID, targetWidth || 4096 , targetHeight || 2160 , false, requestAs); // this is fine. + } + //////////////// + + if ("scale" in e.data) { + if (e.data.scale === false){ + session.dynamicScale = true; // disable manual scaling + updateMixer(); + var scale = false; + } else { + session.dynamicScale = false; + var scale = parseInt(e.data.scale) || 100; + } + + if (e.data.UUID){ + session.sendRequest({scale:scale}, UUID); + } else if (e.data.target){ + for (var i in session.rpcs) { + try { + if ("streamID" in session.rpcs[i]) { + if ("target" in e.data) { + if ((session.rpcs[i].streamID == e.data.target) || (e.data.target == "*")) { // specify a stream ID or let it apply to all videos + session.sendRequest({scale:scale}, i); + } + } else { + session.sendRequest({scale:scale}, i); + } + } + } catch (e) { + errorlog(e); + } + } + } else { + session.sendRequest({scale:scale}); + } + + } + + + if (("action" in e.data) && (e.data.action!="null")) { /////////////// reuse the Companion API + var resp = processMessage(e.data); // reuse the companion API + if (resp!==null){ + log(resp); + parent.postMessage(resp, session.iframetarget); + } + } else if ("target" in e.data) { + log(e.data); + for (var i in session.rpcs) { + try { + if ("streamID" in session.rpcs[i]) { + if ((session.rpcs[i].streamID == e.data.target) || (e.data.target == "*")) { + try { + + if ("settings" in e.data) { + try{ + for (const property in e.data.settings) { + try { + session.rpcs[i].videoElement[property] = e.data.settings[property]; + } catch(e){} + } + } catch(e){} + } + if ("add" in e.data) { + try{ + getById("gridlayout").appendChild(session.rpcs[i].videoElement); + } catch(e){warnlog(e);} + + } else if ("remove" in e.data) { + try { + session.rpcs[i].videoElement.parentNode.removeChild(session.rpcs[i].videoElement); + } catch (e) { + try { + session.rpcs[i].videoElement.parentNode.parentNode.removeChild(session.rpcs[i].videoElement.parentNode); + } catch (e) {} + } + } else if ("replace" in e.data) { // should allow for a cleaner cut between two video streams. + try { + getById("gridlayout").appendChild(session.rpcs[i].videoElement); + getById("gridlayout").childNodes.forEach(ele=>{ + if ((!ele.id) || (ele.id !== session.rpcs[i].videoElement.id)){ + getById("gridlayout").removeChild(ele); + } + }); + } catch(e){} + } + } catch (e) { + errorlog(e); + } + } + } + } catch (e) { + errorlog(e); + } + } + } + }; + + if (isIFrame) { // reduce CPU load if not needed. //iframe API + window.onmessage = session.remoteInterfaceAPI; + } + + if (session.midiHotkeys || session.midiOut!==false) { + + var script = document.createElement('script'); + script.onload = function() { + WebMidi.enable().then(() =>{ + + WebMidi.timeStart = Date.now(); // start time + + WebMidi.addListener("connected", function(e) { + log(e); + }); + + WebMidi.addListener("disconnected", function(e) { + log(e); + }); + + console.log(WebMidi.inputs); + + if (session.midiOut===true){ + for (var i = 0; i < WebMidi.inputs.length; i++) { + try { + var input = WebMidi.inputs[i]; + input.addListener("midimessage", function(e) { + e.timestamp += WebMidi.timeStart; + sendRawMIDI(e); + //var msg = {}; + //msg.midi = {}; + //msg.midi.d = e.data; aka [d1,d2,d3]; + //msg.midi.c = e.channel; + //msg.midi.s = e.timestamp; + }); + } catch(e){} + } + } else if (session.midiOut==parseInt(session.midiOut)){ + try{ + var input = WebMidi.inputs[parseInt(session.midiOut)-1]; + input.addListener("midimessage", function(e) { + e.timestamp += WebMidi.timeStart; + sendRawMIDI(e); + }); + } catch(e){errorlog(e);}; + } + + for (var i = 0; i < WebMidi.inputs.length; i++) { + + if (session.midiDevice && (session.midiDevice!==(i+1))){continue;} + + var input = WebMidi.inputs[i]; + if (session.midiChannel){ + input = input.channels[session.midiChannel]; + } + if (session.midiHotkeys==4){ + input.addListener('controlchange', function(e) { + log(e); + midiHotkeysCommand(e.controller.number, e.rawValue); + }); + } else if (session.midiHotkeys==5){ + if (session.midiOffset!==false){ + input.addListener('controlchange', function(e) { + midiHotkeysCommand_offset(e.controller.number, e.rawValue, session.midiOffset); + }); + } + } else { + input.addListener('noteon', function(e) { + log(e); + var note = e.note.name + e.note.octave; + var velocity = e.velocity || false; + midiHotkeysNote(note,velocity); + }); + } + } + }).catch(errorlog); + }; + script.src = "./thirdparty/webmidi3.js"; // dynamically load this only if its needed. Keeps loading time down. + document.head.appendChild(script); + } else if (session.midiIn){ + var script = document.createElement('script'); + script.src = "./thirdparty/webmidi3.js"; // dynamically load this only if its needed. Keeps loading time down. + script.onload = function() { + WebMidi.enable().then(() => console.log(WebMidi.outputs)).catch(errorlog); + } + document.head.appendChild(script); + } + + + var languages = getById('languagesList').querySelectorAll('li a'); + var timezones = []; + + languages.forEach(language => { + if (language.dataset.tz) { + var languageTimezones = language.dataset.tz.split(';'); // each link can have multiple timezones separated by ; + languageTimezones.forEach(element => { + timezones.push(element); + }); + } + }); + + var currentTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; + + if (timezones.includes(currentTimezone)) { + var el = getById('languagesList').querySelector("li a[data-tz*='" + currentTimezone +"']"); // select language li + el.parentElement.removeChild(el); // remove it + getById('languagesList').insertBefore(el, getById('languagesList').querySelector('li:nth-child(2)')); // insert it after English + } + + var visAudioTimeout = null + document.addEventListener("visibilitychange", function() { + //log("hidden : " +document.hidden); + //log("vis : "+document.visibilityState); + if ((iOS) || (iPad)) { // fixes a bug on iOS devices. Not need with other devices? + toggleAutoVideoMute(); + clearTimeout(visAudioTimeout); + if (document.visibilityState === 'visible') { + visAudioTimeout = setTimeout(function() { + resetupAudioOut(); + activatedPreview=false; + grabAudio("#audioSource3"); + }, 500); + } + } + }); + + // Warns user about network going down + window.addEventListener("offline", function (e) { + warnlog("connection lost"); + if ((session.view) && (session.permaid === false)) { + log("VDO.Ninja has no network connectivity and can't work properly." ); + } else if (session.scene !== false) { + log("VDO.Ninja has no network connectivity and can't work properly." ); + } else if (!session.cleanOutput) { + if (iOS || iPad){ + for (var UUID in session.pcs){ + session.pcs[UUID].close(); + delete(session.pcs[UUID]); + session.applySoloChat(); + applySceneState(); + } + } + if (location.hostname === "vdo.ninja"){ + warnUser(getTranslation("no-network-details")); + } else { + warnUser(getTranslation("no-network")); + } + + } else { + log("VDO.Ninja has no network connectivity and can't work properly."); + } + }); + + window.addEventListener("online", function (e) { + + log("Back ONLINE"); + closeModal(); + + if (!session.onceConnected){ // never connected to websockets before. Let's not trigger retryWatchInterval if we don't have to. + return; + } + + if (!session.retryWatchInterval()){ // ask for the streams again to watch + session.ping(); // if no streams requested, let's ping instead. + } + }); + + /* function updateConnectionStatus() { // no longer works in chrome. + + try{ + if (!session.stats){ + return; + } + + if (Connection.type){ + log("Connection type changed from " + session.stats.network_type + " to " + Connection.type); + + if (session.stats.network_type && (session.stats.network_type !== Connection.type)){ + var miniInfo = {}; + miniInfo.con = Connection.type; + session.sendMessage({"miniInfo":miniInfo}); + + if (!session.retryWatchInterval()){ // ask for the streams again to watch + session.ping(); // if no streams requested, let's ping instead. + } + + } else { // connection state changed, but doesn't seem like it actually changed... + session.ping(); // if no streams requested, let's ping instead. + } + + session.stats.network_type = Connection.type; + } + + } catch(e){warnlog(e);} + + } + + try { + var Connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection; + if (Connection){ + if (Connection.type){ + session.stats.network_type = Connection.type + } + Connection.addEventListener('change', updateConnectionStatus); + } + } catch (e) {log(e);} // effectiveType is not yet supported by Firefox or Safari; 2021 */ + + + setInterval(function() { + checkConnection(); + }, 5000); + + // Remove modal if network comes back up + window.addEventListener("online", function (e) { + if (!session.cleanOutput) { + // Remove last inserted modal; Could be improved by tagging the + // modal elements and only removing modals tagged 'offline' + let userWarnings = document.querySelectorAll('.alertModal'); + closeModal(userWarnings[userWarnings.length- 1]); + } else { + log( + "Network connectivity has been restored." + ); + } + }); + + document.addEventListener("DOMContentLoaded", function() { + var lazyVideos = [].slice.call(document.querySelectorAll("video.lazy")); + + if ("IntersectionObserver" in window) { + var lazyVideoObserver = new IntersectionObserver(function(entries, observer) { + entries.forEach(function(video) { + if (video.isIntersecting) { + for (var source in video.target.children) { + var videoSource = video.target.children[source]; + if (typeof videoSource.tagName === "string" && videoSource.tagName === "SOURCE") { + videoSource.src = videoSource.dataset.src; + } + } + + video.target.load(); + video.target.classList.remove("lazy"); + lazyVideoObserver.unobserve(video.target); + } + }); + }); + + lazyVideos.forEach(function(lazyVideo) { + lazyVideoObserver.observe(lazyVideo); + }); + } + }); + + document.addEventListener("dragstart", event => { + var url = event.target.href || event.target.value; + if (!url || !url.startsWith('https://')) return; + if (event.target.dataset.drag != "1") { + return; + } + //event.target.ondragend = function(){event.target.blur();} + + var streamId = url.split('view='); + var label = url.split('label='); + + if (session.label !== false) { + url += '&layer-name=' + session.label; + } else { + url += '&layer-name=VDO.Ninja'; + } + if (streamId.length > 1) url += ': ' + streamId[1].split('&')[0]; + if (label.length > 1) url += ' - ' + decodeURI(label[1].split('&')[0]); + + try { + if (document.getElementById("videosource")) { + var video = getById('videosource'); + if (typeof(video.videoWidth) == "undefined") { + url += '&layer-width=1920'; // this isn't always 100% correct, as the resolution can fluxuate, but it is probably good enough + url += '&layer-height=1080'; + } else if ((parseInt(video.videoWidth) < 360) || (video.videoHeight < 640)) { + url += '&layer-width=1920'; // this isn't always 100% correct, as the resolution can fluxuate, but it is probably good enough + url += '&layer-height=1080'; + } else { + url += '&layer-width=' + video.videoWidth; // this isn't always 100% correct, as the resolution can fluxuate, but it is probably good enough + url += '&layer-height=' + video.videoHeight; + } + } else { + url += '&layer-width=1920'; // this isn't always 100% correct, as the resolution can fluxuate, but it is probably good enough + url += '&layer-height=1080'; + } + } catch (error) { + url += '&layer-width=1920'; // this isn't always 100% correct, as the resolution can fluxuate, but it is probably good enough + url += '&layer-height=1080'; + } + + event.dataTransfer.setDragImage( getById('dragImage'), 24, 24); + event.dataTransfer.setData("text/uri-list", encodeURI(url)); + + }); + + if (navigator.getBattery){ + navigator.getBattery().then(function(battery) { + session.batteryState = {}; + if ("level" in battery){ + session.batteryState.level = battery.level; + } + if ("charging" in battery){ + session.batteryState.charging = battery.charging; + } + + if (session.batteryState == {}){ + session.batteryState = null; + } + battery.addEventListener('chargingchange', function() { + session.batteryState = {}; + var miniInfo = {}; + if ("level" in battery){ + session.batteryState.level = battery.level; + miniInfo.bat = battery.level; + } + if ("charging" in battery){ + session.batteryState.charging = battery.charging; + miniInfo.chrg = battery.charging; + } + if (session.batteryState == {}){ + session.batteryState = null; + } + session.sendMessage({"miniInfo":miniInfo}); + }); + + battery.addEventListener('levelchange', function(){ + session.batteryState = {}; + var miniInfo = {}; + if ("level" in battery){ + session.batteryState.level = battery.level; + miniInfo.bat = battery.level; + } + if ("charging" in battery){ + session.batteryState.charging = battery.charging; + miniInfo.chrg = battery.charging; + } + if (session.batteryState == {}){ + session.batteryState = null; + } + session.sendMessage({"miniInfo":miniInfo}); + }); + + }); + } + + + var lastTouchEnd = 0; + document.addEventListener('touchend', function(event) { + var now = (new Date()).getTime(); + if (now - lastTouchEnd <= 300) { + event.preventDefault(); + } + lastTouchEnd = now; + }, false); + + + document.addEventListener('click', function(event) { + if (session.firstPlayTriggered == false) { + playAllVideos(); + session.firstPlayTriggered = true; + + try { + if (session.audioCtx && session.audioCtx.state == "suspended"){ + session.audioCtx.resume(); + } + } catch(e){warnlog("session.audioCtx.resume(); failed 4");} + + history.pushState({}, ''); + } + }); + + document.addEventListener("keydown", event => { + keyDownEvent(event); + }); + + function keyDownEvent(event){ + + if ((event.ctrlKey) || (event.metaKey)) { // detect if CTRL is pressed + CtrlPressed = true; + } else { + CtrlPressed = false; + } + if (event.altKey) { + AltPressed = true; + } else { + AltPressed = false; + } + + if (event.key === "Escape") { + log("escape pressed; checking to see if modal box opened and will close"); + if (document.fullscreenElement) { + document.exitFullscreen(); + //updateMixer(); + } else { + + let userWarnings = document.querySelectorAll('.alertModal, .promptModal'); + if (userWarnings.length){ + closeModal(userWarnings[userWarnings.length- 1]); + } + } + return; + } + + if (session.disableHotKeys){return;} + + if (PPTHotkey){ + if (event.target && (event.target.tagName == "INPUT")){ + // skip, since an input field is selected + } else if ((PPTHotkey.ctrl === event.ctrlKey) && (PPTHotkey.alt === AltPressed) && (PPTHotkey.meta === event.metaKey) && ((PPTHotkey.key===false) || ((PPTHotkey.key!==false) && (PPTHotkey.key === event.key)))){ + if (session.muted && !PPTKeyPressed){ + session.muted = false; + PPTKeyPressed = true; + getById("mutebutton").classList.add("PPTActive"); + toggleMute(true); + } else if (!PPTKeyPressed){ + PPTKeyPressed = true; + getById("mutebutton").classList.add("PPTActive"); + } + event.preventDefault(); + event.stopPropagation(); + return; + } else if (PPTKeyPressed){ + PPTKeyPressed = false; + getById("mutebutton").classList.remove("PPTActive"); + if (!session.muted){ + session.muted = true; + toggleMute(true); + + } + event.preventDefault(); + event.stopPropagation(); + return; + } + } + + if (KeyPressedTimeout || PPTKeyPressed){ + event.preventDefault(); + event.stopPropagation(); + return; + } + + if (CtrlPressed && event.keyCode) { + if (event.keyCode == 77) { // M + if (event.metaKey) { + if (AltPressed) { + if (!KeyPressedTimeout){ + toggleMute(); // macOS + KeyPressedTimeout = Date.now(); + event.preventDefault(); + event.stopPropagation(); + return; + } + } + } else { + if (!KeyPressedTimeout){ + toggleMute(); // Windows + KeyPressedTimeout = Date.now(); + event.preventDefault(); + event.stopPropagation(); + return; + } + } + + } else if (event.keyCode == 66) { // B + toggleVideoMute(); + event.preventDefault(); + event.stopPropagation(); + return; + } + + if (AltPressed){ // CTRL + ALT + if (event.keyCode == 70) { // F + toggleFileshare()(); + event.preventDefault(); + event.stopPropagation(); + return; + } else if (event.keyCode == 67) { // C + cycleCameras(); + event.preventDefault(); + event.stopPropagation(); + return; + } else if (event.keyCode == 83) { + toggleScreenShare()(); + event.preventDefault(); + event.stopPropagation(); + return; + } else if (event.keyCode == 68) { + if (!drawOnScreenObject){ + drawOnScreen(); + } else { + drawOnScreenObject.stop(); + } + event.preventDefault(); + event.stopPropagation(); + return; + } else if (event.keyCode == 80) { // S + if (session.videoElement){ + togglePictureInPicture(session.videoElement); + event.preventDefault(); + event.stopPropagation(); + return; + } + } + } + } + } + + document.addEventListener("keyup", event => { + + if (PPTKeyPressed){ + PPTKeyPressed = false; + getById("mutebutton").classList.remove("PPTActive"); + if (!session.muted){ + session.muted = true; + toggleMute(true); + } + event.preventDefault(); + event.stopPropagation(); + return; + } + + if (!(event.ctrlKey || event.metaKey)) { + if (CtrlPressed) { + CtrlPressed = false; + for (var i in Callbacks) { + var cb = Callbacks[i]; + cb[0](...cb.slice(1)); // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#A_better_apply + } + Callbacks = []; + } + } + if (!(event.altKey)) { + AltPressed = false; + } + + if (event.altKey && event.shiftKey && event.keyCode === 67 /* C */) { + toggleControlBar(); + } + if (KeyPressedTimeout && ((event.keyCode == 77) || (!(event.ctrlKey || event.metaKey)))) { + if (Date.now() - KeyPressedTimeout>300){ + toggleMute(); + } + if (event.keyCode == 77){ + KeyPressedTimeout = 0; + } + } + }); + + setTimeout(function(){ // lets lazy load the following.. + window.addEventListener("beforeunload", confirmUnload); // This just keeps people from killing the live stream accidentally. Also give me a headsup that the stream is ending + window.addEventListener("unload", function(e) { + try { + if (session.ws){ + session.ws.close(); + } + if (session.videoElement.recording) { + session.videoElement.recorder.writer.close(); + session.videoElement.recording = false; + } + for (var i in session.rpcs) { + if (session.rpcs[i].videoElement) { + if (session.rpcs[i].videoElement.recording) { + session.rpcs[i].videoElement.recorder.writer.close(); + session.rpcs[i].videoElement.recording = false; + } + } + } + session.hangup(); + } catch (e) { + errorlog(e); + } + }); + + try { + navigator.serviceWorker.getRegistrations().then(registrations => { // getting rid of old service workers. + try { + log(registrations); + for(let registration of registrations) { + if (registration.scope != "https://"+window.location.hostname+window.location.pathname+"thirdparty/"){ + registration.unregister(); + } + } + } catch(e){} + }).catch(errorlog); + } catch(e){} + + var script = document.createElement('script'); + document.head.appendChild(script); + script.onload = function() { + var script = document.createElement('script'); + document.head.appendChild(script); + script.src = "./thirdparty/StreamSaver.js?v=19"; // dynamically load this only if its needed. Keeps loading time down. + }; + script.src = "./thirdparty/polyfill.min.js"; // dynamically load this only if its needed. Keeps loading time down. + },100); +} diff --git a/media/accept.png b/app/static/media/accept.png similarity index 100% rename from media/accept.png rename to app/static/media/accept.png diff --git a/media/avatar.webp b/app/static/media/avatar.webp similarity index 100% rename from media/avatar.webp rename to app/static/media/avatar.webp diff --git a/media/avatar1.png b/app/static/media/avatar1.png similarity index 100% rename from media/avatar1.png rename to app/static/media/avatar1.png diff --git a/media/avatar2.png b/app/static/media/avatar2.png similarity index 100% rename from media/avatar2.png rename to app/static/media/avatar2.png diff --git a/media/avatar3.png b/app/static/media/avatar3.png similarity index 100% rename from media/avatar3.png rename to app/static/media/avatar3.png diff --git a/media/bg_sample.webp b/app/static/media/bg_sample.webp similarity index 100% rename from media/bg_sample.webp rename to app/static/media/bg_sample.webp diff --git a/media/bg_sample2.webp b/app/static/media/bg_sample2.webp similarity index 100% rename from media/bg_sample2.webp rename to app/static/media/bg_sample2.webp diff --git a/media/camera_inkscape.svg b/app/static/media/camera_inkscape.svg similarity index 100% rename from media/camera_inkscape.svg rename to app/static/media/camera_inkscape.svg diff --git a/media/cap.webm b/app/static/media/cap.webm similarity index 100% rename from media/cap.webm rename to app/static/media/cap.webm diff --git a/media/fakesteve.webm b/app/static/media/fakesteve.webm similarity index 100% rename from media/fakesteve.webm rename to app/static/media/fakesteve.webm diff --git a/media/favicon-16x16.png b/app/static/media/favicon-16x16.png similarity index 100% rename from media/favicon-16x16.png rename to app/static/media/favicon-16x16.png diff --git a/media/favicon-32x32.png b/app/static/media/favicon-32x32.png similarity index 100% rename from media/favicon-32x32.png rename to app/static/media/favicon-32x32.png diff --git a/media/favicon.ico b/app/static/media/favicon.ico similarity index 100% rename from media/favicon.ico rename to app/static/media/favicon.ico diff --git a/media/grid_inkscape.svg b/app/static/media/grid_inkscape.svg similarity index 100% rename from media/grid_inkscape.svg rename to app/static/media/grid_inkscape.svg diff --git a/media/hd.svg b/app/static/media/hd.svg similarity index 100% rename from media/hd.svg rename to app/static/media/hd.svg diff --git a/media/icon.png b/app/static/media/icon.png similarity index 100% rename from media/icon.png rename to app/static/media/icon.png diff --git a/media/icon.svg b/app/static/media/icon.svg similarity index 100% rename from media/icon.svg rename to app/static/media/icon.svg diff --git a/media/join.mp3 b/app/static/media/join.mp3 similarity index 100% rename from media/join.mp3 rename to app/static/media/join.mp3 diff --git a/media/join.ogg b/app/static/media/join.ogg similarity index 100% rename from media/join.ogg rename to app/static/media/join.ogg diff --git a/media/join.wav b/app/static/media/join.wav similarity index 100% rename from media/join.wav rename to app/static/media/join.wav diff --git a/media/leave.mp3 b/app/static/media/leave.mp3 similarity index 100% rename from media/leave.mp3 rename to app/static/media/leave.mp3 diff --git a/media/leave.ogg b/app/static/media/leave.ogg similarity index 100% rename from media/leave.ogg rename to app/static/media/leave.ogg diff --git a/media/leave.wav b/app/static/media/leave.wav similarity index 100% rename from media/leave.wav rename to app/static/media/leave.wav diff --git a/media/logo_cropped.png b/app/static/media/logo_cropped.png similarity index 100% rename from media/logo_cropped.png rename to app/static/media/logo_cropped.png diff --git a/media/logo_cropped_512.png b/app/static/media/logo_cropped_512.png similarity index 100% rename from media/logo_cropped_512.png rename to app/static/media/logo_cropped_512.png diff --git a/media/micro.mp4 b/app/static/media/micro.mp4 similarity index 100% rename from media/micro.mp4 rename to app/static/media/micro.mp4 diff --git a/media/monitor_inkscape.svg b/app/static/media/monitor_inkscape.svg similarity index 100% rename from media/monitor_inkscape.svg rename to app/static/media/monitor_inkscape.svg diff --git a/media/old_icon.png b/app/static/media/old_icon.png similarity index 100% rename from media/old_icon.png rename to app/static/media/old_icon.png diff --git a/media/old_logo.png b/app/static/media/old_logo.png similarity index 100% rename from media/old_logo.png rename to app/static/media/old_logo.png diff --git a/media/permissions_chrome.jpg b/app/static/media/permissions_chrome.jpg similarity index 100% rename from media/permissions_chrome.jpg rename to app/static/media/permissions_chrome.jpg diff --git a/media/plane_inkscape.svg b/app/static/media/plane_inkscape.svg similarity index 100% rename from media/plane_inkscape.svg rename to app/static/media/plane_inkscape.svg diff --git a/media/profile.png b/app/static/media/profile.png similarity index 100% rename from media/profile.png rename to app/static/media/profile.png diff --git a/media/robot.mp3 b/app/static/media/robot.mp3 similarity index 100% rename from media/robot.mp3 rename to app/static/media/robot.mp3 diff --git a/media/screenshare.webm b/app/static/media/screenshare.webm similarity index 100% rename from media/screenshare.webm rename to app/static/media/screenshare.webm diff --git a/media/sd.svg b/app/static/media/sd.svg similarity index 100% rename from media/sd.svg rename to app/static/media/sd.svg diff --git a/media/share.jpg b/app/static/media/share.jpg similarity index 100% rename from media/share.jpg rename to app/static/media/share.jpg diff --git a/media/streamdeck.png b/app/static/media/streamdeck.png similarity index 100% rename from media/streamdeck.png rename to app/static/media/streamdeck.png diff --git a/media/svg.md b/app/static/media/svg.md similarity index 98% rename from media/svg.md rename to app/static/media/svg.md index de6d4d6..feb0663 100644 --- a/media/svg.md +++ b/app/static/media/svg.md @@ -1,2 +1,2 @@ -https://uxwing.com/collapse-icon/ +https://uxwing.com/collapse-icon/ https://uxwing.com/expand-icon/ \ No newline at end of file diff --git a/media/thirds.svg b/app/static/media/thirds.svg similarity index 99% rename from media/thirds.svg rename to app/static/media/thirds.svg index e88593b..e118c50 100644 --- a/media/thirds.svg +++ b/app/static/media/thirds.svg @@ -1,8 +1,8 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/media/thirdshead.svg b/app/static/media/thirdshead.svg similarity index 100% rename from media/thirdshead.svg rename to app/static/media/thirdshead.svg diff --git a/media/tone.mp3 b/app/static/media/tone.mp3 similarity index 100% rename from media/tone.mp3 rename to app/static/media/tone.mp3 diff --git a/media/tone.ogg b/app/static/media/tone.ogg similarity index 100% rename from media/tone.ogg rename to app/static/media/tone.ogg diff --git a/media/vdoNinja_logo_full.png b/app/static/media/vdoNinja_logo_full.png similarity index 100% rename from media/vdoNinja_logo_full.png rename to app/static/media/vdoNinja_logo_full.png diff --git a/media/vdoninja.svg b/app/static/media/vdoninja.svg similarity index 100% rename from media/vdoninja.svg rename to app/static/media/vdoninja.svg diff --git a/meet.html b/app/static/meet.html similarity index 96% rename from meet.html rename to app/static/meet.html index 9493658..12849a8 100644 --- a/meet.html +++ b/app/static/meet.html @@ -1,1976 +1,1976 @@ - - - Meet app - - - - - - - - - - - - - - - - -
    -
    -
    - - -
    -
    -

    Meet

    A simple free chat room

    - - - - Generate a random room name - -
    -
    -

    - -

    - -
    -
    -
    -
    -
    - - -
    - - - + + + Meet app + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    +
    +

    Meet

    A simple free chat room

    + + + + Generate a random room name + +
    +
    +

    + +

    + +
    +
    +
    +
    +
    + + +
    + + + \ No newline at end of file diff --git a/midi.html b/app/static/midi.html similarity index 96% rename from midi.html rename to app/static/midi.html index c0b6895..f35526d 100644 --- a/midi.html +++ b/app/static/midi.html @@ -1,575 +1,575 @@ - - - - - - VDO.Ninja MIDI Controller - - - - -
    -
    -

    VDO.Ninja MIDI test app

    - -
    -

    About

    -
    - You can check the console debug logs for added of input/output events and device details. -

    You can download a virtual MIDI I/O controller for Windows here:
    - http://www.tobias-erichsen.de/software/loopmidi.html -

    This code uses the WebMIDI.js library, referenced here:
    - https://github.com/djipco/webmidi -

    - Below you can test the MIDI hotkey commands for VDO.Ninja below:
    -

    - The idea of this app is to select a MIDI output loopback device. Pressing any of the below buttons will trigger the respective MIDI note/command, sending it to the output MIDI device. - Since the MIDI output device should be a loopback device, you can select the MIDI loopback device in VDO.Ninja as its MIDI input source. -

    Refer to the documentation for what a MIDI note/value does in VDO.Ninja, but it can be used to control the director's room, either locally or remotely. -
    -
    -
    -

    Select the MIDI Output device:

    -
    - - -
    -
    -
    -

    &midi=1

    -
    - -
    -
    -
    -

    &midi=3

    -
    - -
    -
    -
    -

    &midi=4 ; director

    -
    - -
    -
    -
    -

    &midi=4 ; guest 1

    -
    - -
    -
    -
    -

    &midi=4 ; guest 2

    -
    - -
    -
    -
    -

    Sample Remote Director Control links

    -
    - -
    -
    -
    -
    - - - -
    - -
    - - - + + + + + + VDO.Ninja MIDI Controller + + + + +
    +
    +

    VDO.Ninja MIDI test app

    + +
    +

    About

    +
    + You can check the console debug logs for added of input/output events and device details. +

    You can download a virtual MIDI I/O controller for Windows here:
    + http://www.tobias-erichsen.de/software/loopmidi.html +

    This code uses the WebMIDI.js library, referenced here:
    + https://github.com/djipco/webmidi +

    + Below you can test the MIDI hotkey commands for VDO.Ninja below:
    +

    + The idea of this app is to select a MIDI output loopback device. Pressing any of the below buttons will trigger the respective MIDI note/command, sending it to the output MIDI device. + Since the MIDI output device should be a loopback device, you can select the MIDI loopback device in VDO.Ninja as its MIDI input source. +

    Refer to the documentation for what a MIDI note/value does in VDO.Ninja, but it can be used to control the director's room, either locally or remotely. +
    +
    +
    +

    Select the MIDI Output device:

    +
    + + +
    +
    +
    +

    &midi=1

    +
    + +
    +
    +
    +

    &midi=3

    +
    + +
    +
    +
    +

    &midi=4 ; director

    +
    + +
    +
    +
    +

    &midi=4 ; guest 1

    +
    + +
    +
    +
    +

    &midi=4 ; guest 2

    +
    + +
    +
    +
    +

    Sample Remote Director Control links

    +
    + +
    +
    +
    +
    + + + +
    + +
    + + + diff --git a/minidirector.css b/app/static/minidirector.css similarity index 95% rename from minidirector.css rename to app/static/minidirector.css index 0730178..f0baf4e 100644 --- a/minidirector.css +++ b/app/static/minidirector.css @@ -1,146 +1,146 @@ -body{ - zoom: 75%; - background-color: #1F1E1F; -} -button[data-action-type='solo-chat'] { - display:none! important; -} - -button[data-action-type='recorder-local'] { - display:none! important; -} -span[data-action-type='ordering'] { - display:none! important; -} -button[data-action-type='open-file-share'] { - display:none! important; -} - -button[data-action-type='add-channel']{ - display:none! important; -} -button[data-action-type='toggle-remote-speaker']{ - display:none! important; -} -button[data-action-type='toggle-remote-display']{ - display:none! important; -} -button[data-action-type='hide-guest']{ - display:none! important; -} -button[data-action-type='create-timer']{ - display:none! important; -} -button[data-action-type='change-url']{ - display:none! important; -} -button[data-action-type='change-params']{ - display:none! important; -} -span[data-action-type='change-quality']{ - display:none! important; -} - -span[data-action-type='sceneCluster2']{ - display:none! important; -} -span[data-action-type='sceneCluster1']{ - display:none! important; -} -button[data-action-type='recorder-remote']{ - display:none! important; -} -button[data-action-type='advanced-camera-settings']{ - display:inline-block! important; -} -button[data-action-type='force-keyframe']{ - display:none! important; -} -body { - font-size: 0.9em! important; -} -button[data-action-type='mute-scene']{ - display:inline-block! important; - visibility: visible; - width: unset; - height: unset; - opacity: 1; -} -button[data-action-type='stats-remote']{ - display:inline-block! important; - visibility: visible; - width: unset; - height: unset; - opacity: 1; -} -button[data-action-type='advanced-audio-settings']{ - display:inline-block! important; - visibility: visible; - width: unset; - height: unset; - opacity: 1; -} -button[data-action-type='advanced-camera-settings']{ - display:inline-block! important; - visibility: visible; - width: unset; - height: unset; - opacity: 1; -} -button[data-action-type='advanced-camera-settings']{ - display:inline-block! important; - visibility: visible; - width: unset; - height: unset; - opacity: 1; -} -.groupcluster1 { - display:inline-block! important; - visibility: visible; - width: unset; - height: unset; - opacity: 1; -} -.hideDropMenu:empty{ - display:none; -} -.hideDropMenu { - display:none; -} -.orderspan{ - display:none! important; -} -#roomHeader{ - display:none! important; -} -.directorContainer { - display:none!important; -} -:root { - --advanced-mode: inline; -} - -#header{ - display:none!important; -} - -button[class="pull-right"]{ - display:none! important; -} - -div#guestFeeds { - padding: 1px!important; - margin: 1px!important; -} -div[class="vidcon directorMargins"] { - padding: 1px!important; - margin: 1px!important; - width: 260px; -} -button[data-action-type]{ - margin: 1px!important; -} - - - - +body{ + zoom: 75%; + background-color: #1F1E1F; +} +button[data-action-type='solo-chat'] { + display:none! important; +} + +button[data-action-type='recorder-local'] { + display:none! important; +} +span[data-action-type='ordering'] { + display:none! important; +} +button[data-action-type='open-file-share'] { + display:none! important; +} + +button[data-action-type='add-channel']{ + display:none! important; +} +button[data-action-type='toggle-remote-speaker']{ + display:none! important; +} +button[data-action-type='toggle-remote-display']{ + display:none! important; +} +button[data-action-type='hide-guest']{ + display:none! important; +} +button[data-action-type='create-timer']{ + display:none! important; +} +button[data-action-type='change-url']{ + display:none! important; +} +button[data-action-type='change-params']{ + display:none! important; +} +span[data-action-type='change-quality']{ + display:none! important; +} + +span[data-action-type='sceneCluster2']{ + display:none! important; +} +span[data-action-type='sceneCluster1']{ + display:none! important; +} +button[data-action-type='recorder-remote']{ + display:none! important; +} +button[data-action-type='advanced-camera-settings']{ + display:inline-block! important; +} +button[data-action-type='force-keyframe']{ + display:none! important; +} +body { + font-size: 0.9em! important; +} +button[data-action-type='mute-scene']{ + display:inline-block! important; + visibility: visible; + width: unset; + height: unset; + opacity: 1; +} +button[data-action-type='stats-remote']{ + display:inline-block! important; + visibility: visible; + width: unset; + height: unset; + opacity: 1; +} +button[data-action-type='advanced-audio-settings']{ + display:inline-block! important; + visibility: visible; + width: unset; + height: unset; + opacity: 1; +} +button[data-action-type='advanced-camera-settings']{ + display:inline-block! important; + visibility: visible; + width: unset; + height: unset; + opacity: 1; +} +button[data-action-type='advanced-camera-settings']{ + display:inline-block! important; + visibility: visible; + width: unset; + height: unset; + opacity: 1; +} +.groupcluster1 { + display:inline-block! important; + visibility: visible; + width: unset; + height: unset; + opacity: 1; +} +.hideDropMenu:empty{ + display:none; +} +.hideDropMenu { + display:none; +} +.orderspan{ + display:none! important; +} +#roomHeader{ + display:none! important; +} +.directorContainer { + display:none!important; +} +:root { + --advanced-mode: inline; +} + +#header{ + display:none!important; +} + +button[class="pull-right"]{ + display:none! important; +} + +div#guestFeeds { + padding: 1px!important; + margin: 1px!important; +} +div[class="vidcon directorMargins"] { + padding: 1px!important; + margin: 1px!important; + width: 260px; +} +button[data-action-type]{ + margin: 1px!important; +} + + + + diff --git a/mixer.html b/app/static/mixer.html similarity index 96% rename from mixer.html rename to app/static/mixer.html index 5b8ef7a..35abb3b 100644 --- a/mixer.html +++ b/app/static/mixer.html @@ -1,4192 +1,4192 @@ - - - Mixer app - - - - - - - - - - - - - - - - - - -
    -
    -
    Streams IDs appearing above are draggable
    -
    Remove
    -
    -
    - - -
    -
    - -
    - - -
    -
    -

    Mixer App

    Video chat with custom layouts

    - - - - Generate a random room name - -
    -
    -

    - -

    - -
    -
    -
    - -
    - - - - -
    - - -
    - - - + + + Mixer app + + + + + + + + + + + + + + + + + + +
    +
    +
    Streams IDs appearing above are draggable
    +
    Remove
    +
    +
    + + +
    +
    + +
    + + +
    +
    +

    Mixer App

    Video chat with custom layouts

    + + + + Generate a random room name + +
    +
    +

    + +

    + +
    +
    +
    + +
    + + + + +
    + + +
    + + + \ No newline at end of file diff --git a/monitor.html b/app/static/monitor.html similarity index 95% rename from monitor.html rename to app/static/monitor.html index 5071c0a..036c50b 100644 --- a/monitor.html +++ b/app/static/monitor.html @@ -1,590 +1,590 @@ - - - - - VDO.Ninja Monitoring - - - - -
    -

    - VDO.Ninja Remote Monitor -

    -
    - - - - - - - - - + + + + + VDO.Ninja Monitoring + + + + +
    +

    + VDO.Ninja Remote Monitor +

    +
    + + + + + + + + + diff --git a/popout.html b/app/static/popout.html similarity index 96% rename from popout.html rename to app/static/popout.html index 813aa5b..f51e57c 100644 --- a/popout.html +++ b/app/static/popout.html @@ -1,367 +1,367 @@ - - - - - - - -
    -
    -
    - Welcome to VDO.Ninja! You can send text messages directly to connected peers from here. -
    -
    -
    - - -
    -
    - - + + + + + + + +
    +
    +
    + Welcome to VDO.Ninja! You can send text messages directly to connected peers from here. +
    +
    +
    + + +
    +
    + + \ No newline at end of file diff --git a/publish.html b/app/static/publish.html similarity index 99% rename from publish.html rename to app/static/publish.html index 1fa3cce..f9d3257 100644 --- a/publish.html +++ b/app/static/publish.html @@ -2,7 +2,7 @@ - + -Regions for VDO.Ninja Tech Check - - -
    -

    Testing Regions for VDO.Ninja Tech Check -

    - - + + + + +Regions for VDO.Ninja Tech Check + + +
    +

    Testing Regions for VDO.Ninja Tech Check +

    + + \ No newline at end of file diff --git a/remotemidi.html b/app/static/remotemidi.html similarity index 96% rename from remotemidi.html rename to app/static/remotemidi.html index 73438b0..eeb2228 100644 --- a/remotemidi.html +++ b/app/static/remotemidi.html @@ -1,1724 +1,1724 @@ - - - Remote MIDI Controller - - - - - - - - - - - - - - - - - - -
    -
    - - - - - -
    -
    - - -
    -
    -

    MIDI Controller app

    Remote control a MIDI device or MIDI loopback device

    - - - - Generate a random room name - -
    -
    -

    - -

    - -
    -
    -
    -

    General Settings

    -

    Remove all Layouts

    -
    -

    Load Default Layouts

    -
    - -

    Export settings

    -
    -

    Import settings

    - Import buttons and settings from local file:


    - -
    - - - - - -
    - -
    - -
    - - -
    - - + + + Remote MIDI Controller + + + + + + + + + + + + + + + + + + +
    +
    + + + + + +
    +
    + + +
    +
    +

    MIDI Controller app

    Remote control a MIDI device or MIDI loopback device

    + + + + Generate a random room name + +
    +
    +

    + +

    + +
    +
    +
    +

    General Settings

    +

    Remove all Layouts

    +
    +

    Load Default Layouts

    +
    + +

    Export settings

    +
    +

    Import settings

    + Import buttons and settings from local file:


    + +
    + + + + + +
    + +
    + +
    + + +
    + + \ No newline at end of file diff --git a/results.html b/app/static/results.html similarity index 95% rename from results.html rename to app/static/results.html index 70eae0e..236c320 100644 --- a/results.html +++ b/app/static/results.html @@ -1,532 +1,532 @@ - - - - - - - VDON Speed Test - - - -
    -

    - Video and stream quality check results -

    -
    -
    -
    -
    -

    Bitrate (kbps)

    - 0 - -
    - -
    -

    Buffer delay (ms)

    - 0 - -
    - -
    -

    Packet Loss (%)

    - 0 - -
    -
    -
    -
    - -
    - - - - + + + + + + + VDON Speed Test + + + +
    +

    + Video and stream quality check results +

    +
    +
    +
    +
    +

    Bitrate (kbps)

    + 0 + +
    + +
    +

    Buffer delay (ms)

    + 0 + +
    + +
    +

    Packet Loss (%)

    + 0 + +
    +
    +
    +
    + +
    + + + + diff --git a/serviceWorker.js b/app/static/serviceWorker.js similarity index 100% rename from serviceWorker.js rename to app/static/serviceWorker.js diff --git a/speedtest.css b/app/static/speedtest.css similarity index 100% rename from speedtest.css rename to app/static/speedtest.css diff --git a/speedtest.html b/app/static/speedtest.html similarity index 95% rename from speedtest.html rename to app/static/speedtest.html index 4c0f053..970a4c2 100644 --- a/speedtest.html +++ b/app/static/speedtest.html @@ -1,593 +1,593 @@ - - - - - - - VDON Speed Test - - -

    - VDO.Ninja's video streaming quality test -

    -
    -
    -
    -
    -

    Bitrate (kbps)

    - 0 - -
    - -
    -

    Buffer delay (ms)

    - 0 - -
    - -
    -

    Packet Loss (%)

    - 0 - -
    -
    -
    -

    Log

    -
      -
      -
      -

      How to use

      -
        -
      1. Select your camera.
      2. -
      3. Hit start
      4. -
      5. - Wait for the video to load side-by-side. *If it does not auto-load within 20s, refresh and try again.* -
      6. -
      7. - Stats will be printed to screen, along with graphs visualizing the most important metrics. You can also hold CTRL (cmd) + Click on the video to access even more stats. -
      8. -
      9. - Bitrate, Buffer delay, and packet loss are important connection quality metrics. Monitor them for at least a minute. -
      10. -
      11. - Change the video bitrate by pressing the buttons below the video. It should approach 6000-kbps if the network allows. -
      12. -
      13. - Good packet loss will not exceed 0.1% and bad packet loss will exceed 1%. You can try testing against different global regions with the drop-down menu below. -
      14. -
      15. - Check out this Youtube video for more details and solutions to reduce packet loss if suffering from any. -
      16. -
      -

      Unsupervised guest check option

      - - You can also use this tool to have a guest perform a system and connection test, with those results being available to you via a result's page for up to a week.
      - You can either ask the remote guest to send you the link to their results page when they complete the test, or you can use check.html?id=xxx to pre-assign the test ID (ie: xxx), which will have the results then be available at results.html?id=xxx. -
      If you have questions, join the Discord here. -
      -
      -

      More testing options below

      - -
      -
      - Testing location: -


      -
      - - - - - + + + + + + + VDON Speed Test + + +

      + VDO.Ninja's video streaming quality test +

      +
      +
      +
      +
      +

      Bitrate (kbps)

      + 0 + +
      + +
      +

      Buffer delay (ms)

      + 0 + +
      + +
      +

      Packet Loss (%)

      + 0 + +
      +
      +
      +

      Log

      +
        +
        +
        +

        How to use

        +
          +
        1. Select your camera.
        2. +
        3. Hit start
        4. +
        5. + Wait for the video to load side-by-side. *If it does not auto-load within 20s, refresh and try again.* +
        6. +
        7. + Stats will be printed to screen, along with graphs visualizing the most important metrics. You can also hold CTRL (cmd) + Click on the video to access even more stats. +
        8. +
        9. + Bitrate, Buffer delay, and packet loss are important connection quality metrics. Monitor them for at least a minute. +
        10. +
        11. + Change the video bitrate by pressing the buttons below the video. It should approach 6000-kbps if the network allows. +
        12. +
        13. + Good packet loss will not exceed 0.1% and bad packet loss will exceed 1%. You can try testing against different global regions with the drop-down menu below. +
        14. +
        15. + Check out this Youtube video for more details and solutions to reduce packet loss if suffering from any. +
        16. +
        +

        Unsupervised guest check option

        + + You can also use this tool to have a guest perform a system and connection test, with those results being available to you via a result's page for up to a week.
        + You can either ask the remote guest to send you the link to their results page when they complete the test, or you can use check.html?id=xxx to pre-assign the test ID (ie: xxx), which will have the results then be available at results.html?id=xxx. +
        If you have questions, join the Discord here. +
        +
        +

        More testing options below

        + +
        +
        + Testing location: +


        +
        + + + + + diff --git a/stats.css b/app/static/stats.css similarity index 96% rename from stats.css rename to app/static/stats.css index 56107d8..72fe808 100644 --- a/stats.css +++ b/app/static/stats.css @@ -1,832 +1,832 @@ -:root{ - --aspect-ratio: 1.7777777777; - --chat-width: 450px; -} -#sources{ - display:none; -} -#col1{ - display:none; -} - -body{ - padding:0; - margin:0; - width:100%; - height:100%; - background-color: #c9c9c9; - font-family: 'Sora', sans-serif; - overflow: hidden; - position: absolute; - background: #2e445c; - box-shadow: 20px 20px 60px #273a4e, -20px -20px 60px #354e6a; - scrollbar-color:#666 #201c29; -} -header{ - color:white; - margin: 10px 0px 0 20px; - max-height: 300px; -} -span{ - display:inline-block; -} -iframe { - border: 0; - padding: 0; - display: none; - height: 0%; - width: 0%; - position: absolute; - left: -100px; - top: -100px; -} - -.popup-message { - display: none; - align-text: center; - position: absolute; - z-index: 35 !important; - padding: 5px !important; - border-radius: 3px; - min-width: 180px !important; - background-color: #fff !important; - border: solid 1px #dfdfdf !important; - box-shadow: 1px 1px 2px #cfcfcf !important; -} -.context-menu--active { - display: block !important; -} -.context-menu__items { - list-style: none !important; - margin: 0; - padding: 0; -} -.context-menu__item { - display: block; - margin-bottom: 4px !important; -} -.context-menu__item:last-child { - margin-bottom: 0 !important; -} -.context-menu__link { - display: block; - padding: 4px 12px; - color: #0066aa !important; - text-decoration: none; -} -button.menuButtons{ - background-color: #b4c5ca !important; - background: linear-gradient(135deg, #c2d2d7 60%,#c7d3d7 80%, #a3b5ba 100%) -} -.context-menu__link:hover { - color: #fff !important; - background-color: #0066aa !important; -} -.message{ - background: #3e3e3e00; - color: #FCFCFC; - vertical-align: top; - border: 1px solid #2e445c; - border-radius: 10px; - background: #2e445c; - box-shadow: 5px 5px 10px #121620, -5px -5px 10px #162a36; -} -.inMessage { - color: #000; - margin: 3px; - border-radius: 5px; - background: #FFF; - padding: 5px; - text-align: left; -} -.actionMessage { - color: #000; - margin: 3px; - border-radius: 5px; - background: #FFF; - padding: 5px; - text-align: left; -} -.outMessage { - color: #000; - margin: 3px; - border-radius: 5px; - background: #BCF; - padding: 5px; - text-align: right; -} -#chatBody { - background-color: #0004; - margin: 0 10px 10px 0; - border-radius: 5px; - overflow-y:scroll; - overflow-wrap: anywhere; - bottom: 45px; - position: absolute; -} - -#chatBody::-webkit-scrollbar { - width: 0px; - background: transparent; /* make scrollbar transparent */ -} - -#chatModule { - bottom: 0; - position: fixed; - margin: 10px; - align-self: center; - width: 400px; - max-width: 100%; - z-index:3; - height: calc(100vh - 40px); - overflow: hidden; - right:0; - - border: solid 2px #0005; - border-radius: 10px; - padding: 10px; - transition: all .2s ease-in-out; -} -#chatInput { - display: inline-block; - color: #000; - background-color: #FFFE; - width: 324px; - font-size: 105%; - margin-left: 7px; -} -#chatSendBar{ - display: inline-block; - bottom: 0px; - position:absolute; -} -#savedroompassword{ - width:50px; -} -button[data-state='true']{ - background-color:#CEF !important; -} -#inviteOptions{ - border: 0; - padding: 0; - display: block; - height: calc(100vh - 80px); - width: calc(100vw - 290px); - max-width:100%; - padding: 20px; - margin:0; - border:0; - display: inline-block; - background-color: #DDD; - border-radius: 18px; - background: #5b748f; - box-shadow: inset 20px 20px 40px #556c85, - inset -20px -20px 40px #617c99; -} -input[type="checkbox"] { - width: 20px; - height: 20px; - text-align: center; - vertical-align: middle; - transition: all .2s ease-in-out; -} -input[type="checkbox"]:checked { - transform: scale(1.1); -} - - -label { - margin: 0 0px 6px 0; - display: inline-block; - font-weight: 600; -} -.ui-widget-header{ - background: rgb(225,225,225); /* Old browsers */ - background: -moz-linear-gradient(-45deg, rgba(255,255,255,1) 0%, rgba(241,241,241,1) 50%, rgba(225,225,225,1) 51%, rgba(246,246,246,1) 100%); /* FF3.6-15 */ - background: -webkit-linear-gradient(-45deg, rgba(255,255,255,1) 0%,rgba(241,241,241,1) 50%,rgba(225,225,225,1) 51%,rgba(246,246,246,1) 100%); /* Chrome10-25,Safari5.1-6 */ - background: linear-gradient(135deg, rgba(255,255,255,1) 0%,rgba(241,241,241,1) 50%,rgba(225,225,225,1) 51%,rgba(246,246,246,1) 100%); -} - -#iframeContainer{ - height: 100%; - width: 100%; - max-width: calc(100vw - var(--chat-width)); - display:inline-block; - position:relative; -} -#graphs{ - height: 100%; - width: 100%; - max-width: 400px; - display:inline-block; - position:relative; -} -.graph{ - display:inline-block; - width:50px; - position:relative; -} -#viewlink { - width:400px; -} -#container { - display:block; - padding: 0; - margin: 0 auto; - width: 100%; -} - -.disconnected{ - border: 4px dotted red!important; - padding: 6px 0.25em 0.25em 0.25em!important; - outline: dashed 2px black!important; -} -.thing:hover{ - animation: horizontalShake 2s; - animation-iteration-count: 1; -} -#col1>.thing:hover{ - animation: enlargeAnimation 2s; - animation-iteration-count: 1; -} -#col1>.thing:active{ - transform: scale(1.05); - animation:none; -} -.thing:active{ - animation:none; - transform: translate(7px, 0px) rotate(0deg); -} -#delete:hover{ - animation: none!important; -} -#delete:active{ - transform: none!important; -} -@keyframes enlargeAnimation { - 0% { transform: scale(1.01); } - 20% { transform: scale(1.03); } - 80% { transform: scale(1.05); } - 100% { transform: scale(1.06); } -} -@keyframes horizontalShake { - 0% { transform: translate(3px, 0px) rotate(0deg); } - 20% { transform: translate(7px, 0px) rotate(0deg); } - 80% { transform: translate(8px, 0px) rotate(0deg); } - 100% { transform: translate(-1px, 0px) rotate(0deg); } -} -.shake { - animation: shake 0.5s; - animation-iteration-count: 1; -} -@keyframes shake { - 0% { transform: translate(1px, 1px) rotate(0deg); } - 10% { transform: translate(-1px, -2px) rotate(-1deg); } - 20% { transform: translate(-3px, 0px) rotate(1deg); } - 30% { transform: translate(3px, 2px) rotate(0deg); } - 40% { transform: translate(1px, -1px) rotate(1deg); } - 50% { transform: translate(-1px, 2px) rotate(-1deg); } - 60% { transform: translate(-3px, 1px) rotate(0deg); } - 70% { transform: translate(3px, 1px) rotate(-1deg); } - 80% { transform: translate(-1px, -1px) rotate(1deg); } - 90% { transform: translate(1px, 2px) rotate(0deg); } - 100% { transform: translate(1px, -2px) rotate(-1deg); } -} -input{ - padding:5px; - margin:5px; - border-radius: 3px; -} -.menuButton{ - width: 92%; -} - -.fadein, video { - animation: fadein 1s; - opacity: 1!important; -} -@keyframes fadeout { - 0% { - opacity: 1 - } - 100% { - opacity: 0 - } -} - -@keyframes fadein { - 0% { - opacity: 0 - } - 100% { - opacity: 1 - } -} -.partialFadeout{ - opacity: .1 !important; -} -.icon{ - width:16px; - height:16px; - display:inline-block; - vertical-align: text-bottom; -} -.greyout { - animation: greyout 3s; - opacity: 0.3!important; -} - -@keyframes greyout { - 0% { - opacity: 1 - } - 100% { - opacity: 0.3 - } -} -.container{ - border-radius: 15px; - background: #2e445c; - box-shadow: 14px 14px 28px #273a4e, - -14px -14px 28px #354e6a; - max-width: 730; - padding: 20px; - margin: 10px; - display:flex; -} -.graphSection{ - width:380px; - display:block; -} -.stats-container{ - display: flex; - color: white; - font-size: 12px; - position: relative; - top: 4px; - width: 390px; - right: 4px; -} -.stats-sub-container{ - border-radius: 3px; - background: #2e445c; - box-shadow: inset 5px 5px 8px #293d53, inset -5px -5px 8px #334b65; - margin: 4px; - padding: 5px 0; - width: 121.5px; - text-align: center; -} -.right-side{ - display:inline-block -} -.canvasStats{ - width: 380px; - height: 90px; - padding:0; - border-radius: 10px 10px 3px 10px; - box-shadow: inset 2px 2px 6px #273a4e, inset -2px -2px 6px #354e6a; - background-color: #0001; - padding: 0 2px 3px 2px; - position: relative; - top: 3px; -} -button{ - padding:5px; - margin:5px; - border-radius: 3px; - cursor:pointer; - background: linear-gradient(135deg, rgba(238,238,238,1) 60%,rgb(225, 225, 225, 1) 80%,rgb(210, 209, 209, 1) 100%); -} -video{ - max-width: 320px; - min-height: 180px; - margin: 0 18px 0 4px; -} -.video-meter { - padding:0.5vh; - display:block; - width:0.5vh; - height:0.5vh; - min-width:10px; - min-height:10px; - top: 2vh; - right: 2vh; - background-color:green; - position:absolute; - border-radius: 2vh; - pointer-events:none; - border: 1px black solid; -} -.battery { - border: 3px solid #4192c5; - width: 11px; - height: 19px; - border-radius: 4px; - position: absolute; - left: 27px; - top: 3px; - background-color: #fff2; - font-size: 1.5em; - z-index: 2; - cursor: help; - display: none; -} -.video-mute-state { - top: 2vh; - right: 2vh; - position: absolute; - color: #fff; - border-radius: 2vh; - background-color: #b11313; - padding: 2px 2px 2px 1px; -} - -.video-meter-director { - width: 10px; - height: 10px; - top: 8px; - right: 10px; -} -.video-meter2 { - display:block; - padding:0; - width: 1px; - height:0%; - bottom: 0; - right: 14px; - background-color:#0000; - position:absolute; - border-radius: 2vh 2vh 0 0; - pointer-events:none; - border: 1px black solid; - transition: height 0.1s ease, background-color 0.1s ease; - background-color: #38D !important; -} - - -.hasMedia > .video-meter2 { - display:block; -} -.hasMedia > .video-meter-2 { - display:block; -} -.hasMedia > .video-meter { - display:block; -} - - -#voiceMeterTemplate{ - display:none; -} -#voiceMeterTemplate2{ - display:none; -} -h2 { - text-shadow: 0 0 2px #a7a7a7; - color: #000b3a; -} -.inline{ - display:inline-block; -} -table { - font-size: 1em; -} - -::-webkit-scrollbar { - width: 15px; -} - -::-webkit-scrollbar-track { - -webkit-box-shadow: inset 0 0 13px rgb(0 0 0 / 90%); - border-radius: 4px; -} - -::-webkit-scrollbar-thumb { - border-radius: 4px; - -webkit-box-shadow: inset 0 0 16px rgb(150 150 150 / 100%); - border: solid 3px transparent; -} - -.ui-draggable, .ui-droppable { - background-position: top; -} - - -.draggable { width: 150px; height: 150px; padding: 0; margin:0; border:0; } -.resizable { width: 150px; height: 150px; padding: 0; margin:0; border:0; } -.resizable h3 { text-align: center; margin: 0; cursor: grab; border: 1px solid black;} -.ui-menu { width: 150px; } - -.ui-state-selected { - background-color: #64b1ff; -} -.ui-state-disabled { - background-color: grey; -} - -.widget { - background-color: #DDD; - position: absolute; -} -#canvas{ - background-color: #000; - width: 1280px; - height: 720px; - margin:0; - padding:0; - border:0; - display: inline-block; -} - -#sceneSettings{ - height: calc(100vh - 80px); - width: calc(100vw - 290px); - padding: 20px; - margin:0; - border:0; - display: inline-block; - background-color: #DDD; - border-radius: 18px; - background: #5b748f; - box-shadow: inset 20px 20px 40px #556c85, - inset -20px -20px 40px #617c99; -} - -#sceneSettings > h2{ - margin: 4px; -} - -.settings { - display: block; - background: #c0e3ff; - position: absolute; - top: 100px; - left: 100px; - z-Index:20; -} - -.hidden { - display:none!important; -} -.fadeout { - visibility: hidden; - transition: visibility 0s 0.5s, opacity 0.5s linear; - animation: fadeout 0.5s; - opacity: 0!important; -} -.fade2black{ - background-color: 000F; - transition: background-color 0.5s linear; -} -.hidden2{ - display:none!important; -} -.hidden3{ - display:none!important; -} - -.thing { - width: 100px; - padding: 10px 0.5em 0.5em 0.5em; - margin: 6px; - border: #565656 solid 1px; - background: rgba(0,0,0,0.8); - color: white; - font-family: sans-serif; - cursor: grab; - text-align: center; - border-radius: 6px; - word-wrap: break-word; - box-shadow: inset 6px 6px 6px #dadada1c, inset -6px -6px 6px #27272724; -} -.empty { - width: 100px; - padding: 10px 0.5em 0 0.5em; - margin: 0.5em 0.4em; - background: rgba(0,0,0,0.8); - color: white; - font-family: sans-serif; - user-select: none; - text-align: center; - border-radius: 6px; - height: 1.7em; - cursor: crosshair; - border: 1px solid black; -} -.col { - width: 130px; - height: calc(100vh - 20px); - padding: 5px; - border: 1px solid; - border-radius: 5px; - position: relative; - float: left; - user-select: none; -} -.pressed>canvas{ - border: solid 2px black; - background-color: #FFFA; -} -button.pressed { - background-color: #CEF; -} -.editButton{ - display:none; - position:absolute; - margin: 20px 14px; - z-index:2; -} -.setButton{ - display:none; - position:absolute; - margin: 20px 57px; - z-index:2; -} -.canvasContainer{ - display:inline-block; -} -.canvasContainer>canvas{ - transform: scale(calc( var(--aspect-ratio) / (16 / 9)), 1); -} -.pressed>button{ - display:inline-block; -} -b { - text-shadow: 0 0 1px #f4f4f4; -} -a { - display: inline-block; - margin: 5px; - background-color: #c9c9c9; - border: 2px solid black; - padding: 4px; - border-radius: 6px; - cursor:pointer; -} -#delete { - background-color: rgb(191 191 191); - text-align: center; - border: 1px solid black; - color: black; - cursor: crosshair; - margin: 5.5px; - border-radius: 0px; -} - -.modal { - display: none; - position: fixed; - padding-top: 50px; - left: 0; - top: 0; - width: 100%; - height: 100%; - background-color: rgb(0, 0, 0); - background-color: rgba(0, 0, 0, 0.5); - z-Index: 20; - -} -.modal-content { - position: relative; - padding: 20px; - margin: auto; - width: 75%; - -webkit-animation-name: animatetop; - -webkit-animation-duration: 0.4s; - animation-name: animatetop; - animation-duration: 0.4s; - border-radius: 4px; - background-color: #e2e2e2; - background-image: url("data:image/svg+xml,%3Csvg width='6' height='6' viewBox='0 0 6 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%239C92AC' fill-opacity='0.1' fill-rule='evenodd'%3E%3Cpath d='M5 0h1L0 6V5zM6 5v1H5z'/%3E%3C/g%3E%3C/svg%3E"); -} -.close-btn { - float: right; - color: lightgray; - font-size: 24px; - font-weight: bold; -} -.close-btn:hover { - color: darkgray; - cursor:pointer; -} -@-webkit-keyframes animatetop { - from {top:-300px; opacity:0} - to {top:0; opacity:1} -} -@keyframes animatetop { - from {top:-300px; opacity:0} - to {top:0; opacity:1} -} - -#welcomeWindow{ - display:none; - position:absolute; - top:0; - left:0; - width:100vw; - height:100vh; - z-index:5; - background: #2775dc; - box-shadow: 20px 20px 60px #51729d, - -20px -20px 60px #6d9ad5; - background: -moz-linear-gradient(-45deg, rgba(59,103,158,1) 2%, rgba(43,136,217,1) 50%, rgba(32,124,202,1) 79%, rgba(89, 165, 224,1) 100%); /* FF3.6-15 */ - background: -webkit-linear-gradient(-45deg, rgba(59,103,158,1) 2%,rgba(56, 134, 202,1) 50%,rgba(32,124,202,1) 79%,rgba(89, 165, 224,1) 100%); /* Chrome10-25,Safari5.1-6 */ - background: linear-gradient(135deg, rgba(59,103,158,1) 2%,rgba(56, 134, 202,1) 50%,rgba(32,124,202,1) 79%,rgba(89, 165, 224,1) 100%); - background: linear-gradient(135deg, rgba(59,103,158,1) 2%,rgba(56, 134, 202,1) 50%,rgba(32,124,202,0.8) 79%,rgba(89, 165, 224,1) 100%), url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='500' height='500'%3E%3Cfilter id='noise' x='0' y='0'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3CfeBlend mode='screen'/%3E%3C/filter%3E%3Crect width='500' height='500' filter='url(%23noise)' opacity='0.5'/%3E%3C/svg%3E"); -} -.center-content{ - align-content: center; - margin: 20px auto; - display: block; - width: 500px; - max-width: 100%; -} -.footer { - bottom: 0; - display: inline-block; - vertical-align: middle; - margin: 5px 20px; - height: 28px; - display: flex; - align-items: center; - position: fixed; - right: 0; -} -.footer>div{ - align-items: center; -} -.graphTitle{ - color:white; -} -.discord{ - background-image: url("data:image/svg+xml,%3Csvg width='71' height='55' viewBox='0 0 71 55' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0)'%3E%3Cpath d='M60.1045 4.8978C55.5792 2.8214 50.7265 1.2916 45.6527 0.41542C45.5603 0.39851 45.468 0.440769 45.4204 0.525289C44.7963 1.6353 44.105 3.0834 43.6209 4.2216C38.1637 3.4046 32.7345 3.4046 27.3892 4.2216C26.905 3.0581 26.1886 1.6353 25.5617 0.525289C25.5141 0.443589 25.4218 0.40133 25.3294 0.41542C20.2584 1.2888 15.4057 2.8186 10.8776 4.8978C10.8384 4.9147 10.8048 4.9429 10.7825 4.9795C1.57795 18.7309 -0.943561 32.1443 0.293408 45.3914C0.299005 45.4562 0.335386 45.5182 0.385761 45.5576C6.45866 50.0174 12.3413 52.7249 18.1147 54.5195C18.2071 54.5477 18.305 54.5139 18.3638 54.4378C19.7295 52.5728 20.9469 50.6063 21.9907 48.5383C22.0523 48.4172 21.9935 48.2735 21.8676 48.2256C19.9366 47.4931 18.0979 46.6 16.3292 45.5858C16.1893 45.5041 16.1781 45.304 16.3068 45.2082C16.679 44.9293 17.0513 44.6391 17.4067 44.3461C17.471 44.2926 17.5606 44.2813 17.6362 44.3151C29.2558 49.6202 41.8354 49.6202 53.3179 44.3151C53.3935 44.2785 53.4831 44.2898 53.5502 44.3433C53.9057 44.6363 54.2779 44.9293 54.6529 45.2082C54.7816 45.304 54.7732 45.5041 54.6333 45.5858C52.8646 46.6197 51.0259 47.4931 49.0921 48.2228C48.9662 48.2707 48.9102 48.4172 48.9718 48.5383C50.038 50.6034 51.2554 52.5699 52.5959 54.435C52.6519 54.5139 52.7526 54.5477 52.845 54.5195C58.6464 52.7249 64.529 50.0174 70.6019 45.5576C70.6551 45.5182 70.6887 45.459 70.6943 45.3942C72.1747 30.0791 68.2147 16.7757 60.1968 4.9823C60.1772 4.9429 60.1437 4.9147 60.1045 4.8978ZM23.7259 37.3253C20.2276 37.3253 17.3451 34.1136 17.3451 30.1693C17.3451 26.225 20.1717 23.0133 23.7259 23.0133C27.308 23.0133 30.1626 26.2532 30.1066 30.1693C30.1066 34.1136 27.28 37.3253 23.7259 37.3253ZM47.3178 37.3253C43.8196 37.3253 40.9371 34.1136 40.9371 30.1693C40.9371 26.225 43.7636 23.0133 47.3178 23.0133C50.9 23.0133 53.7545 26.2532 53.6986 30.1693C53.6986 34.1136 50.9 37.3253 47.3178 37.3253Z' fill='%23ffffff'/%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0'%3E%3Crect width='71' height='55' fill='white'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E"); - background-size: contain; - background-repeat: no-repeat; - width:14px; - height:10px; - border:0; - background-color:#0000; -} - -.github { - background-image: url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z'/%3E%3C/svg%3E"); - background-size: contain; - width:10px; - height:10px; - border:0; - background-color:#0000; -} -.tooltip { - z-index: 100; -} -.tooltip .tooltiptext { - visibility: hidden; - background-color: #333; - color: #fff; - text-align: center; - border-radius: 3px; - padding: 3px; - overflow: auto; - margin: 0; position:relative; - font-family: "Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", Times, Symbola, Aegyptus,Code2000, Code2001, Code2002, Musica, serif, LastResort; - -} -.tooltip:hover .tooltiptext { - visibility: visible; -} -.randomPassword{ - - width: 24px; - height: 24px; - background-size: contain; - background-repeat: no-repeat; - border-radius: 5px; - background: rgb(238,238,238); - background-image: url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 29 29'%3E%3Cpath d='M18 9v-3c-1 0-3.308-.188-4.506 2.216l-4.218 8.461c-1.015 2.036-3.094 3.323-5.37 3.323h-3.906v-2h3.906c1.517 0 2.903-.858 3.58-2.216l4.218-8.461c1.356-2.721 3.674-3.323 6.296-3.323v-3l6 4-6 4zm-9.463 1.324l1.117-2.242c-1.235-2.479-2.899-4.082-5.748-4.082h-3.906v2h3.906c2.872 0 3.644 2.343 4.631 4.324zm15.463 8.676l-6-4v3c-3.78 0-4.019-1.238-5.556-4.322l-1.118 2.241c1.021 2.049 2.1 4.081 6.674 4.081v3l6-4z'/%3E%3C/svg%3E"), linear-gradient(135deg, rgba(238,238,238,1) 0%,rgba(204,204,204,1) 100%); -} -#demoDrop{ - background-color: #30892c; - cursor: help; -} -.demoThing{ - width: 100px; - padding: 10px 0.5em 0.5em 0.5em; - margin: 6px; - border: #565656 solid 1px; - background: rgba(0,0,0,0.8); - color: white; - font-family: sans-serif; - cursor: grab; - text-align: center; - border-radius: 6px; - word-wrap: break-word; - box-shadow: inset 6px 6px 6px #dadada1c, inset -6px -6px 6px #27272724; -} -#roomtitle{ - display:block; - font-size:200%; - font-weight: 700; -} -#streamsConnected{ - display:inline-block; -} +:root{ + --aspect-ratio: 1.7777777777; + --chat-width: 450px; +} +#sources{ + display:none; +} +#col1{ + display:none; +} + +body{ + padding:0; + margin:0; + width:100%; + height:100%; + background-color: #c9c9c9; + font-family: 'Sora', sans-serif; + overflow: hidden; + position: absolute; + background: #2e445c; + box-shadow: 20px 20px 60px #273a4e, -20px -20px 60px #354e6a; + scrollbar-color:#666 #201c29; +} +header{ + color:white; + margin: 10px 0px 0 20px; + max-height: 300px; +} +span{ + display:inline-block; +} +iframe { + border: 0; + padding: 0; + display: none; + height: 0%; + width: 0%; + position: absolute; + left: -100px; + top: -100px; +} + +.popup-message { + display: none; + align-text: center; + position: absolute; + z-index: 35 !important; + padding: 5px !important; + border-radius: 3px; + min-width: 180px !important; + background-color: #fff !important; + border: solid 1px #dfdfdf !important; + box-shadow: 1px 1px 2px #cfcfcf !important; +} +.context-menu--active { + display: block !important; +} +.context-menu__items { + list-style: none !important; + margin: 0; + padding: 0; +} +.context-menu__item { + display: block; + margin-bottom: 4px !important; +} +.context-menu__item:last-child { + margin-bottom: 0 !important; +} +.context-menu__link { + display: block; + padding: 4px 12px; + color: #0066aa !important; + text-decoration: none; +} +button.menuButtons{ + background-color: #b4c5ca !important; + background: linear-gradient(135deg, #c2d2d7 60%,#c7d3d7 80%, #a3b5ba 100%) +} +.context-menu__link:hover { + color: #fff !important; + background-color: #0066aa !important; +} +.message{ + background: #3e3e3e00; + color: #FCFCFC; + vertical-align: top; + border: 1px solid #2e445c; + border-radius: 10px; + background: #2e445c; + box-shadow: 5px 5px 10px #121620, -5px -5px 10px #162a36; +} +.inMessage { + color: #000; + margin: 3px; + border-radius: 5px; + background: #FFF; + padding: 5px; + text-align: left; +} +.actionMessage { + color: #000; + margin: 3px; + border-radius: 5px; + background: #FFF; + padding: 5px; + text-align: left; +} +.outMessage { + color: #000; + margin: 3px; + border-radius: 5px; + background: #BCF; + padding: 5px; + text-align: right; +} +#chatBody { + background-color: #0004; + margin: 0 10px 10px 0; + border-radius: 5px; + overflow-y:scroll; + overflow-wrap: anywhere; + bottom: 45px; + position: absolute; +} + +#chatBody::-webkit-scrollbar { + width: 0px; + background: transparent; /* make scrollbar transparent */ +} + +#chatModule { + bottom: 0; + position: fixed; + margin: 10px; + align-self: center; + width: 400px; + max-width: 100%; + z-index:3; + height: calc(100vh - 40px); + overflow: hidden; + right:0; + + border: solid 2px #0005; + border-radius: 10px; + padding: 10px; + transition: all .2s ease-in-out; +} +#chatInput { + display: inline-block; + color: #000; + background-color: #FFFE; + width: 324px; + font-size: 105%; + margin-left: 7px; +} +#chatSendBar{ + display: inline-block; + bottom: 0px; + position:absolute; +} +#savedroompassword{ + width:50px; +} +button[data-state='true']{ + background-color:#CEF !important; +} +#inviteOptions{ + border: 0; + padding: 0; + display: block; + height: calc(100vh - 80px); + width: calc(100vw - 290px); + max-width:100%; + padding: 20px; + margin:0; + border:0; + display: inline-block; + background-color: #DDD; + border-radius: 18px; + background: #5b748f; + box-shadow: inset 20px 20px 40px #556c85, + inset -20px -20px 40px #617c99; +} +input[type="checkbox"] { + width: 20px; + height: 20px; + text-align: center; + vertical-align: middle; + transition: all .2s ease-in-out; +} +input[type="checkbox"]:checked { + transform: scale(1.1); +} + + +label { + margin: 0 0px 6px 0; + display: inline-block; + font-weight: 600; +} +.ui-widget-header{ + background: rgb(225,225,225); /* Old browsers */ + background: -moz-linear-gradient(-45deg, rgba(255,255,255,1) 0%, rgba(241,241,241,1) 50%, rgba(225,225,225,1) 51%, rgba(246,246,246,1) 100%); /* FF3.6-15 */ + background: -webkit-linear-gradient(-45deg, rgba(255,255,255,1) 0%,rgba(241,241,241,1) 50%,rgba(225,225,225,1) 51%,rgba(246,246,246,1) 100%); /* Chrome10-25,Safari5.1-6 */ + background: linear-gradient(135deg, rgba(255,255,255,1) 0%,rgba(241,241,241,1) 50%,rgba(225,225,225,1) 51%,rgba(246,246,246,1) 100%); +} + +#iframeContainer{ + height: 100%; + width: 100%; + max-width: calc(100vw - var(--chat-width)); + display:inline-block; + position:relative; +} +#graphs{ + height: 100%; + width: 100%; + max-width: 400px; + display:inline-block; + position:relative; +} +.graph{ + display:inline-block; + width:50px; + position:relative; +} +#viewlink { + width:400px; +} +#container { + display:block; + padding: 0; + margin: 0 auto; + width: 100%; +} + +.disconnected{ + border: 4px dotted red!important; + padding: 6px 0.25em 0.25em 0.25em!important; + outline: dashed 2px black!important; +} +.thing:hover{ + animation: horizontalShake 2s; + animation-iteration-count: 1; +} +#col1>.thing:hover{ + animation: enlargeAnimation 2s; + animation-iteration-count: 1; +} +#col1>.thing:active{ + transform: scale(1.05); + animation:none; +} +.thing:active{ + animation:none; + transform: translate(7px, 0px) rotate(0deg); +} +#delete:hover{ + animation: none!important; +} +#delete:active{ + transform: none!important; +} +@keyframes enlargeAnimation { + 0% { transform: scale(1.01); } + 20% { transform: scale(1.03); } + 80% { transform: scale(1.05); } + 100% { transform: scale(1.06); } +} +@keyframes horizontalShake { + 0% { transform: translate(3px, 0px) rotate(0deg); } + 20% { transform: translate(7px, 0px) rotate(0deg); } + 80% { transform: translate(8px, 0px) rotate(0deg); } + 100% { transform: translate(-1px, 0px) rotate(0deg); } +} +.shake { + animation: shake 0.5s; + animation-iteration-count: 1; +} +@keyframes shake { + 0% { transform: translate(1px, 1px) rotate(0deg); } + 10% { transform: translate(-1px, -2px) rotate(-1deg); } + 20% { transform: translate(-3px, 0px) rotate(1deg); } + 30% { transform: translate(3px, 2px) rotate(0deg); } + 40% { transform: translate(1px, -1px) rotate(1deg); } + 50% { transform: translate(-1px, 2px) rotate(-1deg); } + 60% { transform: translate(-3px, 1px) rotate(0deg); } + 70% { transform: translate(3px, 1px) rotate(-1deg); } + 80% { transform: translate(-1px, -1px) rotate(1deg); } + 90% { transform: translate(1px, 2px) rotate(0deg); } + 100% { transform: translate(1px, -2px) rotate(-1deg); } +} +input{ + padding:5px; + margin:5px; + border-radius: 3px; +} +.menuButton{ + width: 92%; +} + +.fadein, video { + animation: fadein 1s; + opacity: 1!important; +} +@keyframes fadeout { + 0% { + opacity: 1 + } + 100% { + opacity: 0 + } +} + +@keyframes fadein { + 0% { + opacity: 0 + } + 100% { + opacity: 1 + } +} +.partialFadeout{ + opacity: .1 !important; +} +.icon{ + width:16px; + height:16px; + display:inline-block; + vertical-align: text-bottom; +} +.greyout { + animation: greyout 3s; + opacity: 0.3!important; +} + +@keyframes greyout { + 0% { + opacity: 1 + } + 100% { + opacity: 0.3 + } +} +.container{ + border-radius: 15px; + background: #2e445c; + box-shadow: 14px 14px 28px #273a4e, + -14px -14px 28px #354e6a; + max-width: 730; + padding: 20px; + margin: 10px; + display:flex; +} +.graphSection{ + width:380px; + display:block; +} +.stats-container{ + display: flex; + color: white; + font-size: 12px; + position: relative; + top: 4px; + width: 390px; + right: 4px; +} +.stats-sub-container{ + border-radius: 3px; + background: #2e445c; + box-shadow: inset 5px 5px 8px #293d53, inset -5px -5px 8px #334b65; + margin: 4px; + padding: 5px 0; + width: 121.5px; + text-align: center; +} +.right-side{ + display:inline-block +} +.canvasStats{ + width: 380px; + height: 90px; + padding:0; + border-radius: 10px 10px 3px 10px; + box-shadow: inset 2px 2px 6px #273a4e, inset -2px -2px 6px #354e6a; + background-color: #0001; + padding: 0 2px 3px 2px; + position: relative; + top: 3px; +} +button{ + padding:5px; + margin:5px; + border-radius: 3px; + cursor:pointer; + background: linear-gradient(135deg, rgba(238,238,238,1) 60%,rgb(225, 225, 225, 1) 80%,rgb(210, 209, 209, 1) 100%); +} +video{ + max-width: 320px; + min-height: 180px; + margin: 0 18px 0 4px; +} +.video-meter { + padding:0.5vh; + display:block; + width:0.5vh; + height:0.5vh; + min-width:10px; + min-height:10px; + top: 2vh; + right: 2vh; + background-color:green; + position:absolute; + border-radius: 2vh; + pointer-events:none; + border: 1px black solid; +} +.battery { + border: 3px solid #4192c5; + width: 11px; + height: 19px; + border-radius: 4px; + position: absolute; + left: 27px; + top: 3px; + background-color: #fff2; + font-size: 1.5em; + z-index: 2; + cursor: help; + display: none; +} +.video-mute-state { + top: 2vh; + right: 2vh; + position: absolute; + color: #fff; + border-radius: 2vh; + background-color: #b11313; + padding: 2px 2px 2px 1px; +} + +.video-meter-director { + width: 10px; + height: 10px; + top: 8px; + right: 10px; +} +.video-meter2 { + display:block; + padding:0; + width: 1px; + height:0%; + bottom: 0; + right: 14px; + background-color:#0000; + position:absolute; + border-radius: 2vh 2vh 0 0; + pointer-events:none; + border: 1px black solid; + transition: height 0.1s ease, background-color 0.1s ease; + background-color: #38D !important; +} + + +.hasMedia > .video-meter2 { + display:block; +} +.hasMedia > .video-meter-2 { + display:block; +} +.hasMedia > .video-meter { + display:block; +} + + +#voiceMeterTemplate{ + display:none; +} +#voiceMeterTemplate2{ + display:none; +} +h2 { + text-shadow: 0 0 2px #a7a7a7; + color: #000b3a; +} +.inline{ + display:inline-block; +} +table { + font-size: 1em; +} + +::-webkit-scrollbar { + width: 15px; +} + +::-webkit-scrollbar-track { + -webkit-box-shadow: inset 0 0 13px rgb(0 0 0 / 90%); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb { + border-radius: 4px; + -webkit-box-shadow: inset 0 0 16px rgb(150 150 150 / 100%); + border: solid 3px transparent; +} + +.ui-draggable, .ui-droppable { + background-position: top; +} + + +.draggable { width: 150px; height: 150px; padding: 0; margin:0; border:0; } +.resizable { width: 150px; height: 150px; padding: 0; margin:0; border:0; } +.resizable h3 { text-align: center; margin: 0; cursor: grab; border: 1px solid black;} +.ui-menu { width: 150px; } + +.ui-state-selected { + background-color: #64b1ff; +} +.ui-state-disabled { + background-color: grey; +} + +.widget { + background-color: #DDD; + position: absolute; +} +#canvas{ + background-color: #000; + width: 1280px; + height: 720px; + margin:0; + padding:0; + border:0; + display: inline-block; +} + +#sceneSettings{ + height: calc(100vh - 80px); + width: calc(100vw - 290px); + padding: 20px; + margin:0; + border:0; + display: inline-block; + background-color: #DDD; + border-radius: 18px; + background: #5b748f; + box-shadow: inset 20px 20px 40px #556c85, + inset -20px -20px 40px #617c99; +} + +#sceneSettings > h2{ + margin: 4px; +} + +.settings { + display: block; + background: #c0e3ff; + position: absolute; + top: 100px; + left: 100px; + z-Index:20; +} + +.hidden { + display:none!important; +} +.fadeout { + visibility: hidden; + transition: visibility 0s 0.5s, opacity 0.5s linear; + animation: fadeout 0.5s; + opacity: 0!important; +} +.fade2black{ + background-color: 000F; + transition: background-color 0.5s linear; +} +.hidden2{ + display:none!important; +} +.hidden3{ + display:none!important; +} + +.thing { + width: 100px; + padding: 10px 0.5em 0.5em 0.5em; + margin: 6px; + border: #565656 solid 1px; + background: rgba(0,0,0,0.8); + color: white; + font-family: sans-serif; + cursor: grab; + text-align: center; + border-radius: 6px; + word-wrap: break-word; + box-shadow: inset 6px 6px 6px #dadada1c, inset -6px -6px 6px #27272724; +} +.empty { + width: 100px; + padding: 10px 0.5em 0 0.5em; + margin: 0.5em 0.4em; + background: rgba(0,0,0,0.8); + color: white; + font-family: sans-serif; + user-select: none; + text-align: center; + border-radius: 6px; + height: 1.7em; + cursor: crosshair; + border: 1px solid black; +} +.col { + width: 130px; + height: calc(100vh - 20px); + padding: 5px; + border: 1px solid; + border-radius: 5px; + position: relative; + float: left; + user-select: none; +} +.pressed>canvas{ + border: solid 2px black; + background-color: #FFFA; +} +button.pressed { + background-color: #CEF; +} +.editButton{ + display:none; + position:absolute; + margin: 20px 14px; + z-index:2; +} +.setButton{ + display:none; + position:absolute; + margin: 20px 57px; + z-index:2; +} +.canvasContainer{ + display:inline-block; +} +.canvasContainer>canvas{ + transform: scale(calc( var(--aspect-ratio) / (16 / 9)), 1); +} +.pressed>button{ + display:inline-block; +} +b { + text-shadow: 0 0 1px #f4f4f4; +} +a { + display: inline-block; + margin: 5px; + background-color: #c9c9c9; + border: 2px solid black; + padding: 4px; + border-radius: 6px; + cursor:pointer; +} +#delete { + background-color: rgb(191 191 191); + text-align: center; + border: 1px solid black; + color: black; + cursor: crosshair; + margin: 5.5px; + border-radius: 0px; +} + +.modal { + display: none; + position: fixed; + padding-top: 50px; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: rgb(0, 0, 0); + background-color: rgba(0, 0, 0, 0.5); + z-Index: 20; + +} +.modal-content { + position: relative; + padding: 20px; + margin: auto; + width: 75%; + -webkit-animation-name: animatetop; + -webkit-animation-duration: 0.4s; + animation-name: animatetop; + animation-duration: 0.4s; + border-radius: 4px; + background-color: #e2e2e2; + background-image: url("data:image/svg+xml,%3Csvg width='6' height='6' viewBox='0 0 6 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%239C92AC' fill-opacity='0.1' fill-rule='evenodd'%3E%3Cpath d='M5 0h1L0 6V5zM6 5v1H5z'/%3E%3C/g%3E%3C/svg%3E"); +} +.close-btn { + float: right; + color: lightgray; + font-size: 24px; + font-weight: bold; +} +.close-btn:hover { + color: darkgray; + cursor:pointer; +} +@-webkit-keyframes animatetop { + from {top:-300px; opacity:0} + to {top:0; opacity:1} +} +@keyframes animatetop { + from {top:-300px; opacity:0} + to {top:0; opacity:1} +} + +#welcomeWindow{ + display:none; + position:absolute; + top:0; + left:0; + width:100vw; + height:100vh; + z-index:5; + background: #2775dc; + box-shadow: 20px 20px 60px #51729d, + -20px -20px 60px #6d9ad5; + background: -moz-linear-gradient(-45deg, rgba(59,103,158,1) 2%, rgba(43,136,217,1) 50%, rgba(32,124,202,1) 79%, rgba(89, 165, 224,1) 100%); /* FF3.6-15 */ + background: -webkit-linear-gradient(-45deg, rgba(59,103,158,1) 2%,rgba(56, 134, 202,1) 50%,rgba(32,124,202,1) 79%,rgba(89, 165, 224,1) 100%); /* Chrome10-25,Safari5.1-6 */ + background: linear-gradient(135deg, rgba(59,103,158,1) 2%,rgba(56, 134, 202,1) 50%,rgba(32,124,202,1) 79%,rgba(89, 165, 224,1) 100%); + background: linear-gradient(135deg, rgba(59,103,158,1) 2%,rgba(56, 134, 202,1) 50%,rgba(32,124,202,0.8) 79%,rgba(89, 165, 224,1) 100%), url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='500' height='500'%3E%3Cfilter id='noise' x='0' y='0'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3CfeBlend mode='screen'/%3E%3C/filter%3E%3Crect width='500' height='500' filter='url(%23noise)' opacity='0.5'/%3E%3C/svg%3E"); +} +.center-content{ + align-content: center; + margin: 20px auto; + display: block; + width: 500px; + max-width: 100%; +} +.footer { + bottom: 0; + display: inline-block; + vertical-align: middle; + margin: 5px 20px; + height: 28px; + display: flex; + align-items: center; + position: fixed; + right: 0; +} +.footer>div{ + align-items: center; +} +.graphTitle{ + color:white; +} +.discord{ + background-image: url("data:image/svg+xml,%3Csvg width='71' height='55' viewBox='0 0 71 55' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0)'%3E%3Cpath d='M60.1045 4.8978C55.5792 2.8214 50.7265 1.2916 45.6527 0.41542C45.5603 0.39851 45.468 0.440769 45.4204 0.525289C44.7963 1.6353 44.105 3.0834 43.6209 4.2216C38.1637 3.4046 32.7345 3.4046 27.3892 4.2216C26.905 3.0581 26.1886 1.6353 25.5617 0.525289C25.5141 0.443589 25.4218 0.40133 25.3294 0.41542C20.2584 1.2888 15.4057 2.8186 10.8776 4.8978C10.8384 4.9147 10.8048 4.9429 10.7825 4.9795C1.57795 18.7309 -0.943561 32.1443 0.293408 45.3914C0.299005 45.4562 0.335386 45.5182 0.385761 45.5576C6.45866 50.0174 12.3413 52.7249 18.1147 54.5195C18.2071 54.5477 18.305 54.5139 18.3638 54.4378C19.7295 52.5728 20.9469 50.6063 21.9907 48.5383C22.0523 48.4172 21.9935 48.2735 21.8676 48.2256C19.9366 47.4931 18.0979 46.6 16.3292 45.5858C16.1893 45.5041 16.1781 45.304 16.3068 45.2082C16.679 44.9293 17.0513 44.6391 17.4067 44.3461C17.471 44.2926 17.5606 44.2813 17.6362 44.3151C29.2558 49.6202 41.8354 49.6202 53.3179 44.3151C53.3935 44.2785 53.4831 44.2898 53.5502 44.3433C53.9057 44.6363 54.2779 44.9293 54.6529 45.2082C54.7816 45.304 54.7732 45.5041 54.6333 45.5858C52.8646 46.6197 51.0259 47.4931 49.0921 48.2228C48.9662 48.2707 48.9102 48.4172 48.9718 48.5383C50.038 50.6034 51.2554 52.5699 52.5959 54.435C52.6519 54.5139 52.7526 54.5477 52.845 54.5195C58.6464 52.7249 64.529 50.0174 70.6019 45.5576C70.6551 45.5182 70.6887 45.459 70.6943 45.3942C72.1747 30.0791 68.2147 16.7757 60.1968 4.9823C60.1772 4.9429 60.1437 4.9147 60.1045 4.8978ZM23.7259 37.3253C20.2276 37.3253 17.3451 34.1136 17.3451 30.1693C17.3451 26.225 20.1717 23.0133 23.7259 23.0133C27.308 23.0133 30.1626 26.2532 30.1066 30.1693C30.1066 34.1136 27.28 37.3253 23.7259 37.3253ZM47.3178 37.3253C43.8196 37.3253 40.9371 34.1136 40.9371 30.1693C40.9371 26.225 43.7636 23.0133 47.3178 23.0133C50.9 23.0133 53.7545 26.2532 53.6986 30.1693C53.6986 34.1136 50.9 37.3253 47.3178 37.3253Z' fill='%23ffffff'/%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='clip0'%3E%3Crect width='71' height='55' fill='white'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E"); + background-size: contain; + background-repeat: no-repeat; + width:14px; + height:10px; + border:0; + background-color:#0000; +} + +.github { + background-image: url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z'/%3E%3C/svg%3E"); + background-size: contain; + width:10px; + height:10px; + border:0; + background-color:#0000; +} +.tooltip { + z-index: 100; +} +.tooltip .tooltiptext { + visibility: hidden; + background-color: #333; + color: #fff; + text-align: center; + border-radius: 3px; + padding: 3px; + overflow: auto; + margin: 0; position:relative; + font-family: "Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", Times, Symbola, Aegyptus,Code2000, Code2001, Code2002, Musica, serif, LastResort; + +} +.tooltip:hover .tooltiptext { + visibility: visible; +} +.randomPassword{ + + width: 24px; + height: 24px; + background-size: contain; + background-repeat: no-repeat; + border-radius: 5px; + background: rgb(238,238,238); + background-image: url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 29 29'%3E%3Cpath d='M18 9v-3c-1 0-3.308-.188-4.506 2.216l-4.218 8.461c-1.015 2.036-3.094 3.323-5.37 3.323h-3.906v-2h3.906c1.517 0 2.903-.858 3.58-2.216l4.218-8.461c1.356-2.721 3.674-3.323 6.296-3.323v-3l6 4-6 4zm-9.463 1.324l1.117-2.242c-1.235-2.479-2.899-4.082-5.748-4.082h-3.906v2h3.906c2.872 0 3.644 2.343 4.631 4.324zm15.463 8.676l-6-4v3c-3.78 0-4.019-1.238-5.556-4.322l-1.118 2.241c1.021 2.049 2.1 4.081 6.674 4.081v3l6-4z'/%3E%3C/svg%3E"), linear-gradient(135deg, rgba(238,238,238,1) 0%,rgba(204,204,204,1) 100%); +} +#demoDrop{ + background-color: #30892c; + cursor: help; +} +.demoThing{ + width: 100px; + padding: 10px 0.5em 0.5em 0.5em; + margin: 6px; + border: #565656 solid 1px; + background: rgba(0,0,0,0.8); + color: white; + font-family: sans-serif; + cursor: grab; + text-align: center; + border-radius: 6px; + word-wrap: break-word; + box-shadow: inset 6px 6px 6px #dadada1c, inset -6px -6px 6px #27272724; +} +#roomtitle{ + display:block; + font-size:200%; + font-weight: 700; +} +#streamsConnected{ + display:inline-block; +} diff --git a/stats.html b/app/static/stats.html similarity index 93% rename from stats.html rename to app/static/stats.html index 650ef9f..2ed1f8a 100644 --- a/stats.html +++ b/app/static/stats.html @@ -1,7 +1,7 @@ - - - - + + + + \ No newline at end of file diff --git a/supports.css b/app/static/supports.css similarity index 100% rename from supports.css rename to app/static/supports.css diff --git a/supports.html b/app/static/supports.html similarity index 94% rename from supports.html rename to app/static/supports.html index 657f93c..474b3d8 100644 --- a/supports.html +++ b/app/static/supports.html @@ -1,244 +1,244 @@ - - - - - - - - - -
        -

        💻 Browser supported options

        -

        - List of options your browser reports as supported. If an option lights up - green, your currently selected camera reports that it supports that option. -

        -
        -
        -
        -

        Device to check

        - -
        -
        -

        📹 Camera supported options

        -
        -
        -
        -

        📹 Camera settings

        -
        -
        - - - - + + + + + + + + + +
        +

        💻 Browser supported options

        +

        + List of options your browser reports as supported. If an option lights up + green, your currently selected camera reports that it supports that option. +

        +
        +
        +
        +

        Device to check

        + +
        +
        +

        📹 Camera supported options

        +
        +
        +
        +

        📹 Camera settings

        +
        +
        + + + + diff --git a/teleprompter.html b/app/static/teleprompter.html similarity index 96% rename from teleprompter.html rename to app/static/teleprompter.html index dbb83ed..b3fb174 100644 --- a/teleprompter.html +++ b/app/static/teleprompter.html @@ -1,770 +1,770 @@ - - - - - Teleprompter - VDO.Ninja - - - -
        -
        -
        - - - - - - - - - -
        -
        - -
        -
        - -
        -
        - -
        - - + + + + + Teleprompter - VDO.Ninja + + + +
        +
        +
        + + + + + + + + + +
        +
        + +
        +
        + +
        +
        + +
        + + \ No newline at end of file diff --git a/thirdparty/CodecsHandler.js b/app/static/thirdparty/CodecsHandler.js similarity index 100% rename from thirdparty/CodecsHandler.js rename to app/static/thirdparty/CodecsHandler.js diff --git a/thirdparty/StreamSaver.js b/app/static/thirdparty/StreamSaver.js similarity index 100% rename from thirdparty/StreamSaver.js rename to app/static/thirdparty/StreamSaver.js diff --git a/thirdparty/adapter.js b/app/static/thirdparty/adapter.js similarity index 100% rename from thirdparty/adapter.js rename to app/static/thirdparty/adapter.js diff --git a/thirdparty/aes.js b/app/static/thirdparty/aes.js similarity index 100% rename from thirdparty/aes.js rename to app/static/thirdparty/aes.js diff --git a/thirdparty/canvasFilters.js b/app/static/thirdparty/canvasFilters.js similarity index 100% rename from thirdparty/canvasFilters.js rename to app/static/thirdparty/canvasFilters.js diff --git a/thirdparty/ffmpeg.min.js b/app/static/thirdparty/ffmpeg.min.js similarity index 100% rename from thirdparty/ffmpeg.min.js rename to app/static/thirdparty/ffmpeg.min.js diff --git a/thirdparty/focus_worker.js b/app/static/thirdparty/focus_worker.js similarity index 100% rename from thirdparty/focus_worker.js rename to app/static/thirdparty/focus_worker.js diff --git a/thirdparty/jeeliz/JeelizResizer.js b/app/static/thirdparty/jeeliz/JeelizResizer.js similarity index 100% rename from thirdparty/jeeliz/JeelizResizer.js rename to app/static/thirdparty/jeeliz/JeelizResizer.js diff --git a/thirdparty/jeeliz/JeelizThreeHelper.js b/app/static/thirdparty/jeeliz/JeelizThreeHelper.js similarity index 100% rename from thirdparty/jeeliz/JeelizThreeHelper.js rename to app/static/thirdparty/jeeliz/JeelizThreeHelper.js diff --git a/thirdparty/jeeliz/Tween.min.js b/app/static/thirdparty/jeeliz/Tween.min.js similarity index 100% rename from thirdparty/jeeliz/Tween.min.js rename to app/static/thirdparty/jeeliz/Tween.min.js diff --git a/thirdparty/jeeliz/helpers/HeadControls.js b/app/static/thirdparty/jeeliz/helpers/HeadControls.js similarity index 100% rename from thirdparty/jeeliz/helpers/HeadControls.js rename to app/static/thirdparty/jeeliz/helpers/HeadControls.js diff --git a/thirdparty/jeeliz/helpers/JeelizCanvas2DHelper.js b/app/static/thirdparty/jeeliz/helpers/JeelizCanvas2DHelper.js similarity index 100% rename from thirdparty/jeeliz/helpers/JeelizCanvas2DHelper.js rename to app/static/thirdparty/jeeliz/helpers/JeelizCanvas2DHelper.js diff --git a/thirdparty/jeeliz/helpers/JeelizFaceCut.js b/app/static/thirdparty/jeeliz/helpers/JeelizFaceCut.js similarity index 100% rename from thirdparty/jeeliz/helpers/JeelizFaceCut.js rename to app/static/thirdparty/jeeliz/helpers/JeelizFaceCut.js diff --git a/thirdparty/jeeliz/helpers/JeelizResizer.js b/app/static/thirdparty/jeeliz/helpers/JeelizResizer.js similarity index 100% rename from thirdparty/jeeliz/helpers/JeelizResizer.js rename to app/static/thirdparty/jeeliz/helpers/JeelizResizer.js diff --git a/thirdparty/jeeliz/helpers/JeelizThreeHelper.js b/app/static/thirdparty/jeeliz/helpers/JeelizThreeHelper.js similarity index 100% rename from thirdparty/jeeliz/helpers/JeelizThreeHelper.js rename to app/static/thirdparty/jeeliz/helpers/JeelizThreeHelper.js diff --git a/thirdparty/jeeliz/helpers/README.md b/app/static/thirdparty/jeeliz/helpers/README.md similarity index 100% rename from thirdparty/jeeliz/helpers/README.md rename to app/static/thirdparty/jeeliz/helpers/README.md diff --git a/thirdparty/jeeliz/helpers/addDragEventListener.js b/app/static/thirdparty/jeeliz/helpers/addDragEventListener.js similarity index 100% rename from thirdparty/jeeliz/helpers/addDragEventListener.js rename to app/static/thirdparty/jeeliz/helpers/addDragEventListener.js diff --git a/thirdparty/jeeliz/jeelizFaceFilter.js b/app/static/thirdparty/jeeliz/jeelizFaceFilter.js similarity index 100% rename from thirdparty/jeeliz/jeelizFaceFilter.js rename to app/static/thirdparty/jeeliz/jeelizFaceFilter.js diff --git a/thirdparty/jeeliz/modules/jeelizFaceFilter.module.js b/app/static/thirdparty/jeeliz/modules/jeelizFaceFilter.module.js similarity index 100% rename from thirdparty/jeeliz/modules/jeelizFaceFilter.module.js rename to app/static/thirdparty/jeeliz/modules/jeelizFaceFilter.module.js diff --git a/thirdparty/jeeliz/modules/jeelizFaceFilter.moduleNoDOM.js b/app/static/thirdparty/jeeliz/modules/jeelizFaceFilter.moduleNoDOM.js similarity index 100% rename from thirdparty/jeeliz/modules/jeelizFaceFilter.moduleNoDOM.js rename to app/static/thirdparty/jeeliz/modules/jeelizFaceFilter.moduleNoDOM.js diff --git a/thirdparty/jeeliz/neuralNets/NN_4EXPR_0.json b/app/static/thirdparty/jeeliz/neuralNets/NN_4EXPR_0.json similarity index 100% rename from thirdparty/jeeliz/neuralNets/NN_4EXPR_0.json rename to app/static/thirdparty/jeeliz/neuralNets/NN_4EXPR_0.json diff --git a/thirdparty/jeeliz/neuralNets/NN_DEFAULT.json b/app/static/thirdparty/jeeliz/neuralNets/NN_DEFAULT.json similarity index 100% rename from thirdparty/jeeliz/neuralNets/NN_DEFAULT.json rename to app/static/thirdparty/jeeliz/neuralNets/NN_DEFAULT.json diff --git a/thirdparty/jeeliz/neuralNets/NN_INTEL1536.json b/app/static/thirdparty/jeeliz/neuralNets/NN_INTEL1536.json similarity index 100% rename from thirdparty/jeeliz/neuralNets/NN_INTEL1536.json rename to app/static/thirdparty/jeeliz/neuralNets/NN_INTEL1536.json diff --git a/thirdparty/jeeliz/neuralNets/NN_LIGHT_0.json b/app/static/thirdparty/jeeliz/neuralNets/NN_LIGHT_0.json similarity index 100% rename from thirdparty/jeeliz/neuralNets/NN_LIGHT_0.json rename to app/static/thirdparty/jeeliz/neuralNets/NN_LIGHT_0.json diff --git a/thirdparty/jeeliz/neuralNets/NN_VERYLIGHT_0.json b/app/static/thirdparty/jeeliz/neuralNets/NN_VERYLIGHT_0.json similarity index 100% rename from thirdparty/jeeliz/neuralNets/NN_VERYLIGHT_0.json rename to app/static/thirdparty/jeeliz/neuralNets/NN_VERYLIGHT_0.json diff --git a/thirdparty/jeeliz/neuralNets/NN_VIEWTOP_0.json b/app/static/thirdparty/jeeliz/neuralNets/NN_VIEWTOP_0.json similarity index 100% rename from thirdparty/jeeliz/neuralNets/NN_VIEWTOP_0.json rename to app/static/thirdparty/jeeliz/neuralNets/NN_VIEWTOP_0.json diff --git a/thirdparty/jeeliz/neuralNets/NN_WIDEANGLES_0.json b/app/static/thirdparty/jeeliz/neuralNets/NN_WIDEANGLES_0.json similarity index 100% rename from thirdparty/jeeliz/neuralNets/NN_WIDEANGLES_0.json rename to app/static/thirdparty/jeeliz/neuralNets/NN_WIDEANGLES_0.json diff --git a/thirdparty/jeeliz/readme.md b/app/static/thirdparty/jeeliz/readme.md similarity index 97% rename from thirdparty/jeeliz/readme.md rename to app/static/thirdparty/jeeliz/readme.md index 7b4e9b0..d6d55eb 100644 --- a/thirdparty/jeeliz/readme.md +++ b/app/static/thirdparty/jeeliz/readme.md @@ -1,6 +1,6 @@ - Many of the files found in this folder are obtained from Jeeliz - - jeelize/jeelizFaceFilter - Apache-2.0 License - + Many of the files found in this folder are obtained from Jeeliz + + jeelize/jeelizFaceFilter + Apache-2.0 License + https://github.com/jeeliz/jeelizFaceFilter/blob/master/LICENSE \ No newline at end of file diff --git a/thirdparty/jeeliz/three/ShaderParticleEngine/SPE.min.js b/app/static/thirdparty/jeeliz/three/ShaderParticleEngine/SPE.min.js similarity index 100% rename from thirdparty/jeeliz/three/ShaderParticleEngine/SPE.min.js rename to app/static/thirdparty/jeeliz/three/ShaderParticleEngine/SPE.min.js diff --git a/thirdparty/jeeliz/three/customMaterials/FlexMaterial/ThreeFlexMaterial.js b/app/static/thirdparty/jeeliz/three/customMaterials/FlexMaterial/ThreeFlexMaterial.js similarity index 100% rename from thirdparty/jeeliz/three/customMaterials/FlexMaterial/ThreeFlexMaterial.js rename to app/static/thirdparty/jeeliz/three/customMaterials/FlexMaterial/ThreeFlexMaterial.js diff --git a/thirdparty/jeeliz/three/customMaterials/GlowMaterial/threex.atmospherematerial.js b/app/static/thirdparty/jeeliz/three/customMaterials/GlowMaterial/threex.atmospherematerial.js similarity index 100% rename from thirdparty/jeeliz/three/customMaterials/GlowMaterial/threex.atmospherematerial.js rename to app/static/thirdparty/jeeliz/three/customMaterials/GlowMaterial/threex.atmospherematerial.js diff --git a/thirdparty/jeeliz/three/customMaterials/GlowMaterial/threex.atmospherematerialdatgui.js b/app/static/thirdparty/jeeliz/three/customMaterials/GlowMaterial/threex.atmospherematerialdatgui.js similarity index 100% rename from thirdparty/jeeliz/three/customMaterials/GlowMaterial/threex.atmospherematerialdatgui.js rename to app/static/thirdparty/jeeliz/three/customMaterials/GlowMaterial/threex.atmospherematerialdatgui.js diff --git a/thirdparty/jeeliz/three/customMaterials/GlowMaterial/threex.dilategeometry.js b/app/static/thirdparty/jeeliz/three/customMaterials/GlowMaterial/threex.dilategeometry.js similarity index 100% rename from thirdparty/jeeliz/three/customMaterials/GlowMaterial/threex.dilategeometry.js rename to app/static/thirdparty/jeeliz/three/customMaterials/GlowMaterial/threex.dilategeometry.js diff --git a/thirdparty/jeeliz/three/customMaterials/GlowMaterial/threex.geometricglow.js b/app/static/thirdparty/jeeliz/three/customMaterials/GlowMaterial/threex.geometricglow.js similarity index 100% rename from thirdparty/jeeliz/three/customMaterials/GlowMaterial/threex.geometricglow.js rename to app/static/thirdparty/jeeliz/three/customMaterials/GlowMaterial/threex.geometricglow.js diff --git a/thirdparty/jeeliz/three/matrix/THREEMatrix.js b/app/static/thirdparty/jeeliz/three/matrix/THREEMatrix.js similarity index 100% rename from thirdparty/jeeliz/three/matrix/THREEMatrix.js rename to app/static/thirdparty/jeeliz/three/matrix/THREEMatrix.js diff --git a/thirdparty/jeeliz/three/v112/GLTFLoader.js b/app/static/thirdparty/jeeliz/three/v112/GLTFLoader.js similarity index 100% rename from thirdparty/jeeliz/three/v112/GLTFLoader.js rename to app/static/thirdparty/jeeliz/three/v112/GLTFLoader.js diff --git a/thirdparty/jeeliz/three/v112/three.js b/app/static/thirdparty/jeeliz/three/v112/three.js similarity index 100% rename from thirdparty/jeeliz/three/v112/three.js rename to app/static/thirdparty/jeeliz/three/v112/three.js diff --git a/thirdparty/jeeliz/three/v112/three.min.js b/app/static/thirdparty/jeeliz/three/v112/three.min.js similarity index 100% rename from thirdparty/jeeliz/three/v112/three.min.js rename to app/static/thirdparty/jeeliz/three/v112/three.min.js diff --git a/thirdparty/jquery/jquery-3.6.0.js b/app/static/thirdparty/jquery/jquery-3.6.0.js similarity index 100% rename from thirdparty/jquery/jquery-3.6.0.js rename to app/static/thirdparty/jquery/jquery-3.6.0.js diff --git a/thirdparty/jquery/jquery-ui.css b/app/static/thirdparty/jquery/jquery-ui.css similarity index 100% rename from thirdparty/jquery/jquery-ui.css rename to app/static/thirdparty/jquery/jquery-ui.css diff --git a/thirdparty/jquery/jquery-ui.js b/app/static/thirdparty/jquery/jquery-ui.js similarity index 100% rename from thirdparty/jquery/jquery-ui.js rename to app/static/thirdparty/jquery/jquery-ui.js diff --git a/thirdparty/jquery/jquery-ui.min.js b/app/static/thirdparty/jquery/jquery-ui.min.js similarity index 100% rename from thirdparty/jquery/jquery-ui.min.js rename to app/static/thirdparty/jquery/jquery-ui.min.js diff --git a/thirdparty/lyra/README.md b/app/static/thirdparty/lyra/README.md similarity index 100% rename from thirdparty/lyra/README.md rename to app/static/thirdparty/lyra/README.md diff --git a/thirdparty/lyra/model_coeffs/README.md b/app/static/thirdparty/lyra/model_coeffs/README.md similarity index 97% rename from thirdparty/lyra/model_coeffs/README.md rename to app/static/thirdparty/lyra/model_coeffs/README.md index 2810aa8..5bb78dc 100644 --- a/thirdparty/lyra/model_coeffs/README.md +++ b/app/static/thirdparty/lyra/model_coeffs/README.md @@ -1,6 +1,6 @@ -TFLITE MODELS SOURCED FROM GOOGLE ON GITHUB - -google/lyra is licensed under the -Apache License 2.0 -https://github.com/google/lyra +TFLITE MODELS SOURCED FROM GOOGLE ON GITHUB + +google/lyra is licensed under the +Apache License 2.0 +https://github.com/google/lyra https://github.com/google/lyra/tree/f079e8c4dd1c61c87de1852178976ee3bdf15561/model_coeffs \ No newline at end of file diff --git a/thirdparty/lyra/model_coeffs/lyragan.tflite b/app/static/thirdparty/lyra/model_coeffs/lyragan.tflite similarity index 100% rename from thirdparty/lyra/model_coeffs/lyragan.tflite rename to app/static/thirdparty/lyra/model_coeffs/lyragan.tflite diff --git a/thirdparty/lyra/model_coeffs/quantizer.tflite b/app/static/thirdparty/lyra/model_coeffs/quantizer.tflite similarity index 100% rename from thirdparty/lyra/model_coeffs/quantizer.tflite rename to app/static/thirdparty/lyra/model_coeffs/quantizer.tflite diff --git a/thirdparty/lyra/model_coeffs/soundstream_encoder.tflite b/app/static/thirdparty/lyra/model_coeffs/soundstream_encoder.tflite similarity index 100% rename from thirdparty/lyra/model_coeffs/soundstream_encoder.tflite rename to app/static/thirdparty/lyra/model_coeffs/soundstream_encoder.tflite diff --git a/thirdparty/lyra/webassembly_codec_wrapper.js b/app/static/thirdparty/lyra/webassembly_codec_wrapper.js similarity index 100% rename from thirdparty/lyra/webassembly_codec_wrapper.js rename to app/static/thirdparty/lyra/webassembly_codec_wrapper.js diff --git a/thirdparty/lyra/webassembly_codec_wrapper.wasm b/app/static/thirdparty/lyra/webassembly_codec_wrapper.wasm similarity index 100% rename from thirdparty/lyra/webassembly_codec_wrapper.wasm rename to app/static/thirdparty/lyra/webassembly_codec_wrapper.wasm diff --git a/thirdparty/measureBlur.js b/app/static/thirdparty/measureBlur.js similarity index 100% rename from thirdparty/measureBlur.js rename to app/static/thirdparty/measureBlur.js diff --git a/thirdparty/mitm.html b/app/static/thirdparty/mitm.html similarity index 100% rename from thirdparty/mitm.html rename to app/static/thirdparty/mitm.html diff --git a/thirdparty/polyfill.min.js b/app/static/thirdparty/polyfill.min.js similarity index 100% rename from thirdparty/polyfill.min.js rename to app/static/thirdparty/polyfill.min.js diff --git a/thirdparty/polyfill.min.js.map b/app/static/thirdparty/polyfill.min.js.map similarity index 100% rename from thirdparty/polyfill.min.js.map rename to app/static/thirdparty/polyfill.min.js.map diff --git a/thirdparty/qrcode.min.js b/app/static/thirdparty/qrcode.min.js similarity index 100% rename from thirdparty/qrcode.min.js rename to app/static/thirdparty/qrcode.min.js diff --git a/thirdparty/readme.md b/app/static/thirdparty/readme.md similarity index 98% rename from thirdparty/readme.md rename to app/static/thirdparty/readme.md index 4dfca29..f9e4171 100644 --- a/thirdparty/readme.md +++ b/app/static/thirdparty/readme.md @@ -1,3 +1,3 @@ -This folder primarily contains third-party libraries; MIT/Apache2.0 licenced. - +This folder primarily contains third-party libraries; MIT/Apache2.0 licenced. + Most the files are superficially needed and not required for the core function of VDO.Ninja; most anyways. \ No newline at end of file diff --git a/thirdparty/sw.js b/app/static/thirdparty/sw.js similarity index 100% rename from thirdparty/sw.js rename to app/static/thirdparty/sw.js diff --git a/thirdparty/tfjs/face-landmarks-detection.js b/app/static/thirdparty/tfjs/face-landmarks-detection.js similarity index 99% rename from thirdparty/tfjs/face-landmarks-detection.js rename to app/static/thirdparty/tfjs/face-landmarks-detection.js index cb2f93d..2fc2f2f 100644 --- a/thirdparty/tfjs/face-landmarks-detection.js +++ b/app/static/thirdparty/tfjs/face-landmarks-detection.js @@ -20,68 +20,68 @@ (global = global || self, factory(global.faceLandmarksDetection = {}, global.tf, global.tf)); }(this, (function (exports, tf, tfconv) { 'use strict'; - /*! ***************************************************************************** - Copyright (c) Microsoft Corporation. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - PERFORMANCE OF THIS SOFTWARE. - ***************************************************************************** */ - - var __assign = function() { - __assign = Object.assign || function __assign(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; - } - return t; - }; - return __assign.apply(this, arguments); - }; - - function __awaiter(thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); - } - - function __generator(thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (_) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. + ***************************************************************************** */ + + var __assign = function() { + __assign = Object.assign || function __assign(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); + }; + + function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); + } + + function __generator(thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } } /** diff --git a/thirdparty/tfjs/tf-backend-webgl.js b/app/static/thirdparty/tfjs/tf-backend-webgl.js similarity index 99% rename from thirdparty/tfjs/tf-backend-webgl.js rename to app/static/thirdparty/tfjs/tf-backend-webgl.js index 7d7611e..803927c 100644 --- a/thirdparty/tfjs/tf-backend-webgl.js +++ b/app/static/thirdparty/tfjs/tf-backend-webgl.js @@ -20,71 +20,71 @@ (global = global || self, factory(global.tf = global.tf || {}, global.tf)); }(this, (function (exports, tf) { 'use strict'; - /*! ***************************************************************************** - Copyright (c) Microsoft Corporation. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - PERFORMANCE OF THIS SOFTWARE. - ***************************************************************************** */ - /* global Reflect, Promise */ - - var extendStatics = function(d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - - function __extends(d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - } - - function __awaiter(thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); - } - - function __generator(thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (_) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. + ***************************************************************************** */ + /* global Reflect, Promise */ + + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + + function __extends(d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + } + + function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); + } + + function __generator(thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } } /** diff --git a/thirdparty/tfjs/tf-backend-webgl.js.map b/app/static/thirdparty/tfjs/tf-backend-webgl.js.map similarity index 100% rename from thirdparty/tfjs/tf-backend-webgl.js.map rename to app/static/thirdparty/tfjs/tf-backend-webgl.js.map diff --git a/thirdparty/tfjs/tf-converter.js b/app/static/thirdparty/tfjs/tf-converter.js similarity index 99% rename from thirdparty/tfjs/tf-converter.js rename to app/static/thirdparty/tfjs/tf-converter.js index 795805a..f4fe055 100644 --- a/thirdparty/tfjs/tf-converter.js +++ b/app/static/thirdparty/tfjs/tf-converter.js @@ -20,91 +20,91 @@ (global = global || self, factory(global.tf = global.tf || {}, global.tf, global.tf)); }(this, (function (exports, tfc, tfOps) { 'use strict'; - /*! ***************************************************************************** - Copyright (c) Microsoft Corporation. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - PERFORMANCE OF THIS SOFTWARE. - ***************************************************************************** */ - - var __assign = function() { - __assign = Object.assign || function __assign(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; - } - return t; - }; - return __assign.apply(this, arguments); - }; - - function __awaiter(thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); - } - - function __generator(thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (_) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } - } - - function __read(o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; - } - - function __spread() { - for (var ar = [], i = 0; i < arguments.length; i++) - ar = ar.concat(__read(arguments[i])); - return ar; + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. + ***************************************************************************** */ + + var __assign = function() { + __assign = Object.assign || function __assign(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); + }; + + function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); + } + + function __generator(thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } + } + + function __read(o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; + } + + function __spread() { + for (var ar = [], i = 0; i < arguments.length; i++) + ar = ar.concat(__read(arguments[i])); + return ar; } /** diff --git a/thirdparty/tfjs/tf-converter.js.map b/app/static/thirdparty/tfjs/tf-converter.js.map similarity index 100% rename from thirdparty/tfjs/tf-converter.js.map rename to app/static/thirdparty/tfjs/tf-converter.js.map diff --git a/thirdparty/tfjs/tf-core.js b/app/static/thirdparty/tfjs/tf-core.js similarity index 99% rename from thirdparty/tfjs/tf-core.js rename to app/static/thirdparty/tfjs/tf-core.js index 5898f7b..2e65841 100644 --- a/thirdparty/tfjs/tf-core.js +++ b/app/static/thirdparty/tfjs/tf-core.js @@ -20,71 +20,71 @@ (global = global || self, factory(global.tf = global.tf || {})); }(this, (function (exports) { 'use strict'; - /*! ***************************************************************************** - Copyright (c) Microsoft Corporation. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - PERFORMANCE OF THIS SOFTWARE. - ***************************************************************************** */ - /* global Reflect, Promise */ - - var extendStatics = function(d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - - function __extends(d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - } - - function __awaiter(thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); - } - - function __generator(thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (_) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. + ***************************************************************************** */ + /* global Reflect, Promise */ + + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + + function __extends(d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + } + + function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); + } + + function __generator(thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } } /** @@ -1436,1328 +1436,1328 @@ return backendName + "_" + kernelName; } - var long_1 = Long; - - /** - * wasm optimizations, to do native i64 multiplication and divide - */ - var wasm = null; - - try { - wasm = new WebAssembly.Instance(new WebAssembly.Module(new Uint8Array([ - 0, 97, 115, 109, 1, 0, 0, 0, 1, 13, 2, 96, 0, 1, 127, 96, 4, 127, 127, 127, 127, 1, 127, 3, 7, 6, 0, 1, 1, 1, 1, 1, 6, 6, 1, 127, 1, 65, 0, 11, 7, 50, 6, 3, 109, 117, 108, 0, 1, 5, 100, 105, 118, 95, 115, 0, 2, 5, 100, 105, 118, 95, 117, 0, 3, 5, 114, 101, 109, 95, 115, 0, 4, 5, 114, 101, 109, 95, 117, 0, 5, 8, 103, 101, 116, 95, 104, 105, 103, 104, 0, 0, 10, 191, 1, 6, 4, 0, 35, 0, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 126, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 127, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 128, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 129, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 130, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11 - ])), {}).exports; - } catch (e) { - // no wasm support :( - } - - /** - * Constructs a 64 bit two's-complement integer, given its low and high 32 bit values as *signed* integers. - * See the from* functions below for more convenient ways of constructing Longs. - * @exports Long - * @class A Long class for representing a 64 bit two's-complement integer value. - * @param {number} low The low (signed) 32 bits of the long - * @param {number} high The high (signed) 32 bits of the long - * @param {boolean=} unsigned Whether unsigned or not, defaults to signed - * @constructor - */ - function Long(low, high, unsigned) { - - /** - * The low 32 bits as a signed value. - * @type {number} - */ - this.low = low | 0; - - /** - * The high 32 bits as a signed value. - * @type {number} - */ - this.high = high | 0; - - /** - * Whether unsigned or not. - * @type {boolean} - */ - this.unsigned = !!unsigned; - } - - // The internal representation of a long is the two given signed, 32-bit values. - // We use 32-bit pieces because these are the size of integers on which - // Javascript performs bit-operations. For operations like addition and - // multiplication, we split each number into 16 bit pieces, which can easily be - // multiplied within Javascript's floating-point representation without overflow - // or change in sign. - // - // In the algorithms below, we frequently reduce the negative case to the - // positive case by negating the input(s) and then post-processing the result. - // Note that we must ALWAYS check specially whether those values are MIN_VALUE - // (-2^63) because -MIN_VALUE == MIN_VALUE (since 2^63 cannot be represented as - // a positive number, it overflows back into a negative). Not handling this - // case would often result in infinite recursion. - // - // Common constant values ZERO, ONE, NEG_ONE, etc. are defined below the from* - // methods on which they depend. - - /** - * An indicator used to reliably determine if an object is a Long or not. - * @type {boolean} - * @const - * @private - */ - Long.prototype.__isLong__; - - Object.defineProperty(Long.prototype, "__isLong__", { value: true }); - - /** - * @function - * @param {*} obj Object - * @returns {boolean} - * @inner - */ - function isLong(obj) { - return (obj && obj["__isLong__"]) === true; - } - - /** - * Tests if the specified object is a Long. - * @function - * @param {*} obj Object - * @returns {boolean} - */ - Long.isLong = isLong; - - /** - * A cache of the Long representations of small integer values. - * @type {!Object} - * @inner - */ - var INT_CACHE = {}; - - /** - * A cache of the Long representations of small unsigned integer values. - * @type {!Object} - * @inner - */ - var UINT_CACHE = {}; - - /** - * @param {number} value - * @param {boolean=} unsigned - * @returns {!Long} - * @inner - */ - function fromInt(value, unsigned) { - var obj, cachedObj, cache; - if (unsigned) { - value >>>= 0; - if (cache = (0 <= value && value < 256)) { - cachedObj = UINT_CACHE[value]; - if (cachedObj) - return cachedObj; - } - obj = fromBits(value, (value | 0) < 0 ? -1 : 0, true); - if (cache) - UINT_CACHE[value] = obj; - return obj; - } else { - value |= 0; - if (cache = (-128 <= value && value < 128)) { - cachedObj = INT_CACHE[value]; - if (cachedObj) - return cachedObj; - } - obj = fromBits(value, value < 0 ? -1 : 0, false); - if (cache) - INT_CACHE[value] = obj; - return obj; - } - } - - /** - * Returns a Long representing the given 32 bit integer value. - * @function - * @param {number} value The 32 bit integer in question - * @param {boolean=} unsigned Whether unsigned or not, defaults to signed - * @returns {!Long} The corresponding Long value - */ - Long.fromInt = fromInt; - - /** - * @param {number} value - * @param {boolean=} unsigned - * @returns {!Long} - * @inner - */ - function fromNumber(value, unsigned) { - if (isNaN(value)) - return unsigned ? UZERO : ZERO; - if (unsigned) { - if (value < 0) - return UZERO; - if (value >= TWO_PWR_64_DBL) - return MAX_UNSIGNED_VALUE; - } else { - if (value <= -TWO_PWR_63_DBL) - return MIN_VALUE; - if (value + 1 >= TWO_PWR_63_DBL) - return MAX_VALUE; - } - if (value < 0) - return fromNumber(-value, unsigned).neg(); - return fromBits((value % TWO_PWR_32_DBL) | 0, (value / TWO_PWR_32_DBL) | 0, unsigned); - } - - /** - * Returns a Long representing the given value, provided that it is a finite number. Otherwise, zero is returned. - * @function - * @param {number} value The number in question - * @param {boolean=} unsigned Whether unsigned or not, defaults to signed - * @returns {!Long} The corresponding Long value - */ - Long.fromNumber = fromNumber; - - /** - * @param {number} lowBits - * @param {number} highBits - * @param {boolean=} unsigned - * @returns {!Long} - * @inner - */ - function fromBits(lowBits, highBits, unsigned) { - return new Long(lowBits, highBits, unsigned); - } - - /** - * Returns a Long representing the 64 bit integer that comes by concatenating the given low and high bits. Each is - * assumed to use 32 bits. - * @function - * @param {number} lowBits The low 32 bits - * @param {number} highBits The high 32 bits - * @param {boolean=} unsigned Whether unsigned or not, defaults to signed - * @returns {!Long} The corresponding Long value - */ - Long.fromBits = fromBits; - - /** - * @function - * @param {number} base - * @param {number} exponent - * @returns {number} - * @inner - */ - var pow_dbl = Math.pow; // Used 4 times (4*8 to 15+4) - - /** - * @param {string} str - * @param {(boolean|number)=} unsigned - * @param {number=} radix - * @returns {!Long} - * @inner - */ - function fromString(str, unsigned, radix) { - if (str.length === 0) - throw Error('empty string'); - if (str === "NaN" || str === "Infinity" || str === "+Infinity" || str === "-Infinity") - return ZERO; - if (typeof unsigned === 'number') { - // For goog.math.long compatibility - radix = unsigned, - unsigned = false; - } else { - unsigned = !! unsigned; - } - radix = radix || 10; - if (radix < 2 || 36 < radix) - throw RangeError('radix'); - - var p; - if ((p = str.indexOf('-')) > 0) - throw Error('interior hyphen'); - else if (p === 0) { - return fromString(str.substring(1), unsigned, radix).neg(); - } - - // Do several (8) digits each time through the loop, so as to - // minimize the calls to the very expensive emulated div. - var radixToPower = fromNumber(pow_dbl(radix, 8)); - - var result = ZERO; - for (var i = 0; i < str.length; i += 8) { - var size = Math.min(8, str.length - i), - value = parseInt(str.substring(i, i + size), radix); - if (size < 8) { - var power = fromNumber(pow_dbl(radix, size)); - result = result.mul(power).add(fromNumber(value)); - } else { - result = result.mul(radixToPower); - result = result.add(fromNumber(value)); - } - } - result.unsigned = unsigned; - return result; - } - - /** - * Returns a Long representation of the given string, written using the specified radix. - * @function - * @param {string} str The textual representation of the Long - * @param {(boolean|number)=} unsigned Whether unsigned or not, defaults to signed - * @param {number=} radix The radix in which the text is written (2-36), defaults to 10 - * @returns {!Long} The corresponding Long value - */ - Long.fromString = fromString; - - /** - * @function - * @param {!Long|number|string|!{low: number, high: number, unsigned: boolean}} val - * @param {boolean=} unsigned - * @returns {!Long} - * @inner - */ - function fromValue(val, unsigned) { - if (typeof val === 'number') - return fromNumber(val, unsigned); - if (typeof val === 'string') - return fromString(val, unsigned); - // Throws for non-objects, converts non-instanceof Long: - return fromBits(val.low, val.high, typeof unsigned === 'boolean' ? unsigned : val.unsigned); - } - - /** - * Converts the specified value to a Long using the appropriate from* function for its type. - * @function - * @param {!Long|number|string|!{low: number, high: number, unsigned: boolean}} val Value - * @param {boolean=} unsigned Whether unsigned or not, defaults to signed - * @returns {!Long} - */ - Long.fromValue = fromValue; - - // NOTE: the compiler should inline these constant values below and then remove these variables, so there should be - // no runtime penalty for these. - - /** - * @type {number} - * @const - * @inner - */ - var TWO_PWR_16_DBL = 1 << 16; - - /** - * @type {number} - * @const - * @inner - */ - var TWO_PWR_24_DBL = 1 << 24; - - /** - * @type {number} - * @const - * @inner - */ - var TWO_PWR_32_DBL = TWO_PWR_16_DBL * TWO_PWR_16_DBL; - - /** - * @type {number} - * @const - * @inner - */ - var TWO_PWR_64_DBL = TWO_PWR_32_DBL * TWO_PWR_32_DBL; - - /** - * @type {number} - * @const - * @inner - */ - var TWO_PWR_63_DBL = TWO_PWR_64_DBL / 2; - - /** - * @type {!Long} - * @const - * @inner - */ - var TWO_PWR_24 = fromInt(TWO_PWR_24_DBL); - - /** - * @type {!Long} - * @inner - */ - var ZERO = fromInt(0); - - /** - * Signed zero. - * @type {!Long} - */ - Long.ZERO = ZERO; - - /** - * @type {!Long} - * @inner - */ - var UZERO = fromInt(0, true); - - /** - * Unsigned zero. - * @type {!Long} - */ - Long.UZERO = UZERO; - - /** - * @type {!Long} - * @inner - */ - var ONE = fromInt(1); - - /** - * Signed one. - * @type {!Long} - */ - Long.ONE = ONE; - - /** - * @type {!Long} - * @inner - */ - var UONE = fromInt(1, true); - - /** - * Unsigned one. - * @type {!Long} - */ - Long.UONE = UONE; - - /** - * @type {!Long} - * @inner - */ - var NEG_ONE = fromInt(-1); - - /** - * Signed negative one. - * @type {!Long} - */ - Long.NEG_ONE = NEG_ONE; - - /** - * @type {!Long} - * @inner - */ - var MAX_VALUE = fromBits(0xFFFFFFFF|0, 0x7FFFFFFF|0, false); - - /** - * Maximum signed value. - * @type {!Long} - */ - Long.MAX_VALUE = MAX_VALUE; - - /** - * @type {!Long} - * @inner - */ - var MAX_UNSIGNED_VALUE = fromBits(0xFFFFFFFF|0, 0xFFFFFFFF|0, true); - - /** - * Maximum unsigned value. - * @type {!Long} - */ - Long.MAX_UNSIGNED_VALUE = MAX_UNSIGNED_VALUE; - - /** - * @type {!Long} - * @inner - */ - var MIN_VALUE = fromBits(0, 0x80000000|0, false); - - /** - * Minimum signed value. - * @type {!Long} - */ - Long.MIN_VALUE = MIN_VALUE; - - /** - * @alias Long.prototype - * @inner - */ - var LongPrototype = Long.prototype; - - /** - * Converts the Long to a 32 bit integer, assuming it is a 32 bit integer. - * @returns {number} - */ - LongPrototype.toInt = function toInt() { - return this.unsigned ? this.low >>> 0 : this.low; - }; - - /** - * Converts the Long to a the nearest floating-point representation of this value (double, 53 bit mantissa). - * @returns {number} - */ - LongPrototype.toNumber = function toNumber() { - if (this.unsigned) - return ((this.high >>> 0) * TWO_PWR_32_DBL) + (this.low >>> 0); - return this.high * TWO_PWR_32_DBL + (this.low >>> 0); - }; - - /** - * Converts the Long to a string written in the specified radix. - * @param {number=} radix Radix (2-36), defaults to 10 - * @returns {string} - * @override - * @throws {RangeError} If `radix` is out of range - */ - LongPrototype.toString = function toString(radix) { - radix = radix || 10; - if (radix < 2 || 36 < radix) - throw RangeError('radix'); - if (this.isZero()) - return '0'; - if (this.isNegative()) { // Unsigned Longs are never negative - if (this.eq(MIN_VALUE)) { - // We need to change the Long value before it can be negated, so we remove - // the bottom-most digit in this base and then recurse to do the rest. - var radixLong = fromNumber(radix), - div = this.div(radixLong), - rem1 = div.mul(radixLong).sub(this); - return div.toString(radix) + rem1.toInt().toString(radix); - } else - return '-' + this.neg().toString(radix); - } - - // Do several (6) digits each time through the loop, so as to - // minimize the calls to the very expensive emulated div. - var radixToPower = fromNumber(pow_dbl(radix, 6), this.unsigned), - rem = this; - var result = ''; - while (true) { - var remDiv = rem.div(radixToPower), - intval = rem.sub(remDiv.mul(radixToPower)).toInt() >>> 0, - digits = intval.toString(radix); - rem = remDiv; - if (rem.isZero()) - return digits + result; - else { - while (digits.length < 6) - digits = '0' + digits; - result = '' + digits + result; - } - } - }; - - /** - * Gets the high 32 bits as a signed integer. - * @returns {number} Signed high bits - */ - LongPrototype.getHighBits = function getHighBits() { - return this.high; - }; - - /** - * Gets the high 32 bits as an unsigned integer. - * @returns {number} Unsigned high bits - */ - LongPrototype.getHighBitsUnsigned = function getHighBitsUnsigned() { - return this.high >>> 0; - }; - - /** - * Gets the low 32 bits as a signed integer. - * @returns {number} Signed low bits - */ - LongPrototype.getLowBits = function getLowBits() { - return this.low; - }; - - /** - * Gets the low 32 bits as an unsigned integer. - * @returns {number} Unsigned low bits - */ - LongPrototype.getLowBitsUnsigned = function getLowBitsUnsigned() { - return this.low >>> 0; - }; - - /** - * Gets the number of bits needed to represent the absolute value of this Long. - * @returns {number} - */ - LongPrototype.getNumBitsAbs = function getNumBitsAbs() { - if (this.isNegative()) // Unsigned Longs are never negative - return this.eq(MIN_VALUE) ? 64 : this.neg().getNumBitsAbs(); - var val = this.high != 0 ? this.high : this.low; - for (var bit = 31; bit > 0; bit--) - if ((val & (1 << bit)) != 0) - break; - return this.high != 0 ? bit + 33 : bit + 1; - }; - - /** - * Tests if this Long's value equals zero. - * @returns {boolean} - */ - LongPrototype.isZero = function isZero() { - return this.high === 0 && this.low === 0; - }; - - /** - * Tests if this Long's value equals zero. This is an alias of {@link Long#isZero}. - * @returns {boolean} - */ - LongPrototype.eqz = LongPrototype.isZero; - - /** - * Tests if this Long's value is negative. - * @returns {boolean} - */ - LongPrototype.isNegative = function isNegative() { - return !this.unsigned && this.high < 0; - }; - - /** - * Tests if this Long's value is positive. - * @returns {boolean} - */ - LongPrototype.isPositive = function isPositive() { - return this.unsigned || this.high >= 0; - }; - - /** - * Tests if this Long's value is odd. - * @returns {boolean} - */ - LongPrototype.isOdd = function isOdd() { - return (this.low & 1) === 1; - }; - - /** - * Tests if this Long's value is even. - * @returns {boolean} - */ - LongPrototype.isEven = function isEven() { - return (this.low & 1) === 0; - }; - - /** - * Tests if this Long's value equals the specified's. - * @param {!Long|number|string} other Other value - * @returns {boolean} - */ - LongPrototype.equals = function equals(other) { - if (!isLong(other)) - other = fromValue(other); - if (this.unsigned !== other.unsigned && (this.high >>> 31) === 1 && (other.high >>> 31) === 1) - return false; - return this.high === other.high && this.low === other.low; - }; - - /** - * Tests if this Long's value equals the specified's. This is an alias of {@link Long#equals}. - * @function - * @param {!Long|number|string} other Other value - * @returns {boolean} - */ - LongPrototype.eq = LongPrototype.equals; - - /** - * Tests if this Long's value differs from the specified's. - * @param {!Long|number|string} other Other value - * @returns {boolean} - */ - LongPrototype.notEquals = function notEquals(other) { - return !this.eq(/* validates */ other); - }; - - /** - * Tests if this Long's value differs from the specified's. This is an alias of {@link Long#notEquals}. - * @function - * @param {!Long|number|string} other Other value - * @returns {boolean} - */ - LongPrototype.neq = LongPrototype.notEquals; - - /** - * Tests if this Long's value differs from the specified's. This is an alias of {@link Long#notEquals}. - * @function - * @param {!Long|number|string} other Other value - * @returns {boolean} - */ - LongPrototype.ne = LongPrototype.notEquals; - - /** - * Tests if this Long's value is less than the specified's. - * @param {!Long|number|string} other Other value - * @returns {boolean} - */ - LongPrototype.lessThan = function lessThan(other) { - return this.comp(/* validates */ other) < 0; - }; - - /** - * Tests if this Long's value is less than the specified's. This is an alias of {@link Long#lessThan}. - * @function - * @param {!Long|number|string} other Other value - * @returns {boolean} - */ - LongPrototype.lt = LongPrototype.lessThan; - - /** - * Tests if this Long's value is less than or equal the specified's. - * @param {!Long|number|string} other Other value - * @returns {boolean} - */ - LongPrototype.lessThanOrEqual = function lessThanOrEqual(other) { - return this.comp(/* validates */ other) <= 0; - }; - - /** - * Tests if this Long's value is less than or equal the specified's. This is an alias of {@link Long#lessThanOrEqual}. - * @function - * @param {!Long|number|string} other Other value - * @returns {boolean} - */ - LongPrototype.lte = LongPrototype.lessThanOrEqual; - - /** - * Tests if this Long's value is less than or equal the specified's. This is an alias of {@link Long#lessThanOrEqual}. - * @function - * @param {!Long|number|string} other Other value - * @returns {boolean} - */ - LongPrototype.le = LongPrototype.lessThanOrEqual; - - /** - * Tests if this Long's value is greater than the specified's. - * @param {!Long|number|string} other Other value - * @returns {boolean} - */ - LongPrototype.greaterThan = function greaterThan(other) { - return this.comp(/* validates */ other) > 0; - }; - - /** - * Tests if this Long's value is greater than the specified's. This is an alias of {@link Long#greaterThan}. - * @function - * @param {!Long|number|string} other Other value - * @returns {boolean} - */ - LongPrototype.gt = LongPrototype.greaterThan; - - /** - * Tests if this Long's value is greater than or equal the specified's. - * @param {!Long|number|string} other Other value - * @returns {boolean} - */ - LongPrototype.greaterThanOrEqual = function greaterThanOrEqual(other) { - return this.comp(/* validates */ other) >= 0; - }; - - /** - * Tests if this Long's value is greater than or equal the specified's. This is an alias of {@link Long#greaterThanOrEqual}. - * @function - * @param {!Long|number|string} other Other value - * @returns {boolean} - */ - LongPrototype.gte = LongPrototype.greaterThanOrEqual; - - /** - * Tests if this Long's value is greater than or equal the specified's. This is an alias of {@link Long#greaterThanOrEqual}. - * @function - * @param {!Long|number|string} other Other value - * @returns {boolean} - */ - LongPrototype.ge = LongPrototype.greaterThanOrEqual; - - /** - * Compares this Long's value with the specified's. - * @param {!Long|number|string} other Other value - * @returns {number} 0 if they are the same, 1 if the this is greater and -1 - * if the given one is greater - */ - LongPrototype.compare = function compare(other) { - if (!isLong(other)) - other = fromValue(other); - if (this.eq(other)) - return 0; - var thisNeg = this.isNegative(), - otherNeg = other.isNegative(); - if (thisNeg && !otherNeg) - return -1; - if (!thisNeg && otherNeg) - return 1; - // At this point the sign bits are the same - if (!this.unsigned) - return this.sub(other).isNegative() ? -1 : 1; - // Both are positive if at least one is unsigned - return (other.high >>> 0) > (this.high >>> 0) || (other.high === this.high && (other.low >>> 0) > (this.low >>> 0)) ? -1 : 1; - }; - - /** - * Compares this Long's value with the specified's. This is an alias of {@link Long#compare}. - * @function - * @param {!Long|number|string} other Other value - * @returns {number} 0 if they are the same, 1 if the this is greater and -1 - * if the given one is greater - */ - LongPrototype.comp = LongPrototype.compare; - - /** - * Negates this Long's value. - * @returns {!Long} Negated Long - */ - LongPrototype.negate = function negate() { - if (!this.unsigned && this.eq(MIN_VALUE)) - return MIN_VALUE; - return this.not().add(ONE); - }; - - /** - * Negates this Long's value. This is an alias of {@link Long#negate}. - * @function - * @returns {!Long} Negated Long - */ - LongPrototype.neg = LongPrototype.negate; - - /** - * Returns the sum of this and the specified Long. - * @param {!Long|number|string} addend Addend - * @returns {!Long} Sum - */ - LongPrototype.add = function add(addend) { - if (!isLong(addend)) - addend = fromValue(addend); - - // Divide each number into 4 chunks of 16 bits, and then sum the chunks. - - var a48 = this.high >>> 16; - var a32 = this.high & 0xFFFF; - var a16 = this.low >>> 16; - var a00 = this.low & 0xFFFF; - - var b48 = addend.high >>> 16; - var b32 = addend.high & 0xFFFF; - var b16 = addend.low >>> 16; - var b00 = addend.low & 0xFFFF; - - var c48 = 0, c32 = 0, c16 = 0, c00 = 0; - c00 += a00 + b00; - c16 += c00 >>> 16; - c00 &= 0xFFFF; - c16 += a16 + b16; - c32 += c16 >>> 16; - c16 &= 0xFFFF; - c32 += a32 + b32; - c48 += c32 >>> 16; - c32 &= 0xFFFF; - c48 += a48 + b48; - c48 &= 0xFFFF; - return fromBits((c16 << 16) | c00, (c48 << 16) | c32, this.unsigned); - }; - - /** - * Returns the difference of this and the specified Long. - * @param {!Long|number|string} subtrahend Subtrahend - * @returns {!Long} Difference - */ - LongPrototype.subtract = function subtract(subtrahend) { - if (!isLong(subtrahend)) - subtrahend = fromValue(subtrahend); - return this.add(subtrahend.neg()); - }; - - /** - * Returns the difference of this and the specified Long. This is an alias of {@link Long#subtract}. - * @function - * @param {!Long|number|string} subtrahend Subtrahend - * @returns {!Long} Difference - */ - LongPrototype.sub = LongPrototype.subtract; - - /** - * Returns the product of this and the specified Long. - * @param {!Long|number|string} multiplier Multiplier - * @returns {!Long} Product - */ - LongPrototype.multiply = function multiply(multiplier) { - if (this.isZero()) - return ZERO; - if (!isLong(multiplier)) - multiplier = fromValue(multiplier); - - // use wasm support if present - if (wasm) { - var low = wasm.mul(this.low, - this.high, - multiplier.low, - multiplier.high); - return fromBits(low, wasm.get_high(), this.unsigned); - } - - if (multiplier.isZero()) - return ZERO; - if (this.eq(MIN_VALUE)) - return multiplier.isOdd() ? MIN_VALUE : ZERO; - if (multiplier.eq(MIN_VALUE)) - return this.isOdd() ? MIN_VALUE : ZERO; - - if (this.isNegative()) { - if (multiplier.isNegative()) - return this.neg().mul(multiplier.neg()); - else - return this.neg().mul(multiplier).neg(); - } else if (multiplier.isNegative()) - return this.mul(multiplier.neg()).neg(); - - // If both longs are small, use float multiplication - if (this.lt(TWO_PWR_24) && multiplier.lt(TWO_PWR_24)) - return fromNumber(this.toNumber() * multiplier.toNumber(), this.unsigned); - - // Divide each long into 4 chunks of 16 bits, and then add up 4x4 products. - // We can skip products that would overflow. - - var a48 = this.high >>> 16; - var a32 = this.high & 0xFFFF; - var a16 = this.low >>> 16; - var a00 = this.low & 0xFFFF; - - var b48 = multiplier.high >>> 16; - var b32 = multiplier.high & 0xFFFF; - var b16 = multiplier.low >>> 16; - var b00 = multiplier.low & 0xFFFF; - - var c48 = 0, c32 = 0, c16 = 0, c00 = 0; - c00 += a00 * b00; - c16 += c00 >>> 16; - c00 &= 0xFFFF; - c16 += a16 * b00; - c32 += c16 >>> 16; - c16 &= 0xFFFF; - c16 += a00 * b16; - c32 += c16 >>> 16; - c16 &= 0xFFFF; - c32 += a32 * b00; - c48 += c32 >>> 16; - c32 &= 0xFFFF; - c32 += a16 * b16; - c48 += c32 >>> 16; - c32 &= 0xFFFF; - c32 += a00 * b32; - c48 += c32 >>> 16; - c32 &= 0xFFFF; - c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48; - c48 &= 0xFFFF; - return fromBits((c16 << 16) | c00, (c48 << 16) | c32, this.unsigned); - }; - - /** - * Returns the product of this and the specified Long. This is an alias of {@link Long#multiply}. - * @function - * @param {!Long|number|string} multiplier Multiplier - * @returns {!Long} Product - */ - LongPrototype.mul = LongPrototype.multiply; - - /** - * Returns this Long divided by the specified. The result is signed if this Long is signed or - * unsigned if this Long is unsigned. - * @param {!Long|number|string} divisor Divisor - * @returns {!Long} Quotient - */ - LongPrototype.divide = function divide(divisor) { - if (!isLong(divisor)) - divisor = fromValue(divisor); - if (divisor.isZero()) - throw Error('division by zero'); - - // use wasm support if present - if (wasm) { - // guard against signed division overflow: the largest - // negative number / -1 would be 1 larger than the largest - // positive number, due to two's complement. - if (!this.unsigned && - this.high === -0x80000000 && - divisor.low === -1 && divisor.high === -1) { - // be consistent with non-wasm code path - return this; - } - var low = (this.unsigned ? wasm.div_u : wasm.div_s)( - this.low, - this.high, - divisor.low, - divisor.high - ); - return fromBits(low, wasm.get_high(), this.unsigned); - } - - if (this.isZero()) - return this.unsigned ? UZERO : ZERO; - var approx, rem, res; - if (!this.unsigned) { - // This section is only relevant for signed longs and is derived from the - // closure library as a whole. - if (this.eq(MIN_VALUE)) { - if (divisor.eq(ONE) || divisor.eq(NEG_ONE)) - return MIN_VALUE; // recall that -MIN_VALUE == MIN_VALUE - else if (divisor.eq(MIN_VALUE)) - return ONE; - else { - // At this point, we have |other| >= 2, so |this/other| < |MIN_VALUE|. - var halfThis = this.shr(1); - approx = halfThis.div(divisor).shl(1); - if (approx.eq(ZERO)) { - return divisor.isNegative() ? ONE : NEG_ONE; - } else { - rem = this.sub(divisor.mul(approx)); - res = approx.add(rem.div(divisor)); - return res; - } - } - } else if (divisor.eq(MIN_VALUE)) - return this.unsigned ? UZERO : ZERO; - if (this.isNegative()) { - if (divisor.isNegative()) - return this.neg().div(divisor.neg()); - return this.neg().div(divisor).neg(); - } else if (divisor.isNegative()) - return this.div(divisor.neg()).neg(); - res = ZERO; - } else { - // The algorithm below has not been made for unsigned longs. It's therefore - // required to take special care of the MSB prior to running it. - if (!divisor.unsigned) - divisor = divisor.toUnsigned(); - if (divisor.gt(this)) - return UZERO; - if (divisor.gt(this.shru(1))) // 15 >>> 1 = 7 ; with divisor = 8 ; true - return UONE; - res = UZERO; - } - - // Repeat the following until the remainder is less than other: find a - // floating-point that approximates remainder / other *from below*, add this - // into the result, and subtract it from the remainder. It is critical that - // the approximate value is less than or equal to the real value so that the - // remainder never becomes negative. - rem = this; - while (rem.gte(divisor)) { - // Approximate the result of division. This may be a little greater or - // smaller than the actual value. - approx = Math.max(1, Math.floor(rem.toNumber() / divisor.toNumber())); - - // We will tweak the approximate result by changing it in the 48-th digit or - // the smallest non-fractional digit, whichever is larger. - var log2 = Math.ceil(Math.log(approx) / Math.LN2), - delta = (log2 <= 48) ? 1 : pow_dbl(2, log2 - 48), - - // Decrease the approximation until it is smaller than the remainder. Note - // that if it is too large, the product overflows and is negative. - approxRes = fromNumber(approx), - approxRem = approxRes.mul(divisor); - while (approxRem.isNegative() || approxRem.gt(rem)) { - approx -= delta; - approxRes = fromNumber(approx, this.unsigned); - approxRem = approxRes.mul(divisor); - } - - // We know the answer can't be zero... and actually, zero would cause - // infinite recursion since we would make no progress. - if (approxRes.isZero()) - approxRes = ONE; - - res = res.add(approxRes); - rem = rem.sub(approxRem); - } - return res; - }; - - /** - * Returns this Long divided by the specified. This is an alias of {@link Long#divide}. - * @function - * @param {!Long|number|string} divisor Divisor - * @returns {!Long} Quotient - */ - LongPrototype.div = LongPrototype.divide; - - /** - * Returns this Long modulo the specified. - * @param {!Long|number|string} divisor Divisor - * @returns {!Long} Remainder - */ - LongPrototype.modulo = function modulo(divisor) { - if (!isLong(divisor)) - divisor = fromValue(divisor); - - // use wasm support if present - if (wasm) { - var low = (this.unsigned ? wasm.rem_u : wasm.rem_s)( - this.low, - this.high, - divisor.low, - divisor.high - ); - return fromBits(low, wasm.get_high(), this.unsigned); - } - - return this.sub(this.div(divisor).mul(divisor)); - }; - - /** - * Returns this Long modulo the specified. This is an alias of {@link Long#modulo}. - * @function - * @param {!Long|number|string} divisor Divisor - * @returns {!Long} Remainder - */ - LongPrototype.mod = LongPrototype.modulo; - - /** - * Returns this Long modulo the specified. This is an alias of {@link Long#modulo}. - * @function - * @param {!Long|number|string} divisor Divisor - * @returns {!Long} Remainder - */ - LongPrototype.rem = LongPrototype.modulo; - - /** - * Returns the bitwise NOT of this Long. - * @returns {!Long} - */ - LongPrototype.not = function not() { - return fromBits(~this.low, ~this.high, this.unsigned); - }; - - /** - * Returns the bitwise AND of this Long and the specified. - * @param {!Long|number|string} other Other Long - * @returns {!Long} - */ - LongPrototype.and = function and(other) { - if (!isLong(other)) - other = fromValue(other); - return fromBits(this.low & other.low, this.high & other.high, this.unsigned); - }; - - /** - * Returns the bitwise OR of this Long and the specified. - * @param {!Long|number|string} other Other Long - * @returns {!Long} - */ - LongPrototype.or = function or(other) { - if (!isLong(other)) - other = fromValue(other); - return fromBits(this.low | other.low, this.high | other.high, this.unsigned); - }; - - /** - * Returns the bitwise XOR of this Long and the given one. - * @param {!Long|number|string} other Other Long - * @returns {!Long} - */ - LongPrototype.xor = function xor(other) { - if (!isLong(other)) - other = fromValue(other); - return fromBits(this.low ^ other.low, this.high ^ other.high, this.unsigned); - }; - - /** - * Returns this Long with bits shifted to the left by the given amount. - * @param {number|!Long} numBits Number of bits - * @returns {!Long} Shifted Long - */ - LongPrototype.shiftLeft = function shiftLeft(numBits) { - if (isLong(numBits)) - numBits = numBits.toInt(); - if ((numBits &= 63) === 0) - return this; - else if (numBits < 32) - return fromBits(this.low << numBits, (this.high << numBits) | (this.low >>> (32 - numBits)), this.unsigned); - else - return fromBits(0, this.low << (numBits - 32), this.unsigned); - }; - - /** - * Returns this Long with bits shifted to the left by the given amount. This is an alias of {@link Long#shiftLeft}. - * @function - * @param {number|!Long} numBits Number of bits - * @returns {!Long} Shifted Long - */ - LongPrototype.shl = LongPrototype.shiftLeft; - - /** - * Returns this Long with bits arithmetically shifted to the right by the given amount. - * @param {number|!Long} numBits Number of bits - * @returns {!Long} Shifted Long - */ - LongPrototype.shiftRight = function shiftRight(numBits) { - if (isLong(numBits)) - numBits = numBits.toInt(); - if ((numBits &= 63) === 0) - return this; - else if (numBits < 32) - return fromBits((this.low >>> numBits) | (this.high << (32 - numBits)), this.high >> numBits, this.unsigned); - else - return fromBits(this.high >> (numBits - 32), this.high >= 0 ? 0 : -1, this.unsigned); - }; - - /** - * Returns this Long with bits arithmetically shifted to the right by the given amount. This is an alias of {@link Long#shiftRight}. - * @function - * @param {number|!Long} numBits Number of bits - * @returns {!Long} Shifted Long - */ - LongPrototype.shr = LongPrototype.shiftRight; - - /** - * Returns this Long with bits logically shifted to the right by the given amount. - * @param {number|!Long} numBits Number of bits - * @returns {!Long} Shifted Long - */ - LongPrototype.shiftRightUnsigned = function shiftRightUnsigned(numBits) { - if (isLong(numBits)) - numBits = numBits.toInt(); - numBits &= 63; - if (numBits === 0) - return this; - else { - var high = this.high; - if (numBits < 32) { - var low = this.low; - return fromBits((low >>> numBits) | (high << (32 - numBits)), high >>> numBits, this.unsigned); - } else if (numBits === 32) - return fromBits(high, 0, this.unsigned); - else - return fromBits(high >>> (numBits - 32), 0, this.unsigned); - } - }; - - /** - * Returns this Long with bits logically shifted to the right by the given amount. This is an alias of {@link Long#shiftRightUnsigned}. - * @function - * @param {number|!Long} numBits Number of bits - * @returns {!Long} Shifted Long - */ - LongPrototype.shru = LongPrototype.shiftRightUnsigned; - - /** - * Returns this Long with bits logically shifted to the right by the given amount. This is an alias of {@link Long#shiftRightUnsigned}. - * @function - * @param {number|!Long} numBits Number of bits - * @returns {!Long} Shifted Long - */ - LongPrototype.shr_u = LongPrototype.shiftRightUnsigned; - - /** - * Converts this Long to signed. - * @returns {!Long} Signed long - */ - LongPrototype.toSigned = function toSigned() { - if (!this.unsigned) - return this; - return fromBits(this.low, this.high, false); - }; - - /** - * Converts this Long to unsigned. - * @returns {!Long} Unsigned long - */ - LongPrototype.toUnsigned = function toUnsigned() { - if (this.unsigned) - return this; - return fromBits(this.low, this.high, true); - }; - - /** - * Converts this Long to its byte representation. - * @param {boolean=} le Whether little or big endian, defaults to big endian - * @returns {!Array.} Byte representation - */ - LongPrototype.toBytes = function toBytes(le) { - return le ? this.toBytesLE() : this.toBytesBE(); - }; - - /** - * Converts this Long to its little endian byte representation. - * @returns {!Array.} Little endian byte representation - */ - LongPrototype.toBytesLE = function toBytesLE() { - var hi = this.high, - lo = this.low; - return [ - lo & 0xff, - lo >>> 8 & 0xff, - lo >>> 16 & 0xff, - lo >>> 24 , - hi & 0xff, - hi >>> 8 & 0xff, - hi >>> 16 & 0xff, - hi >>> 24 - ]; - }; - - /** - * Converts this Long to its big endian byte representation. - * @returns {!Array.} Big endian byte representation - */ - LongPrototype.toBytesBE = function toBytesBE() { - var hi = this.high, - lo = this.low; - return [ - hi >>> 24 , - hi >>> 16 & 0xff, - hi >>> 8 & 0xff, - hi & 0xff, - lo >>> 24 , - lo >>> 16 & 0xff, - lo >>> 8 & 0xff, - lo & 0xff - ]; - }; - - /** - * Creates a Long from its byte representation. - * @param {!Array.} bytes Byte representation - * @param {boolean=} unsigned Whether unsigned or not, defaults to signed - * @param {boolean=} le Whether little or big endian, defaults to big endian - * @returns {Long} The corresponding Long value - */ - Long.fromBytes = function fromBytes(bytes, unsigned, le) { - return le ? Long.fromBytesLE(bytes, unsigned) : Long.fromBytesBE(bytes, unsigned); - }; - - /** - * Creates a Long from its little endian byte representation. - * @param {!Array.} bytes Little endian byte representation - * @param {boolean=} unsigned Whether unsigned or not, defaults to signed - * @returns {Long} The corresponding Long value - */ - Long.fromBytesLE = function fromBytesLE(bytes, unsigned) { - return new Long( - bytes[0] | - bytes[1] << 8 | - bytes[2] << 16 | - bytes[3] << 24, - bytes[4] | - bytes[5] << 8 | - bytes[6] << 16 | - bytes[7] << 24, - unsigned - ); - }; - - /** - * Creates a Long from its big endian byte representation. - * @param {!Array.} bytes Big endian byte representation - * @param {boolean=} unsigned Whether unsigned or not, defaults to signed - * @returns {Long} The corresponding Long value - */ - Long.fromBytesBE = function fromBytesBE(bytes, unsigned) { - return new Long( - bytes[4] << 24 | - bytes[5] << 16 | - bytes[6] << 8 | - bytes[7], - bytes[0] << 24 | - bytes[1] << 16 | - bytes[2] << 8 | - bytes[3], - unsigned - ); + var long_1 = Long; + + /** + * wasm optimizations, to do native i64 multiplication and divide + */ + var wasm = null; + + try { + wasm = new WebAssembly.Instance(new WebAssembly.Module(new Uint8Array([ + 0, 97, 115, 109, 1, 0, 0, 0, 1, 13, 2, 96, 0, 1, 127, 96, 4, 127, 127, 127, 127, 1, 127, 3, 7, 6, 0, 1, 1, 1, 1, 1, 6, 6, 1, 127, 1, 65, 0, 11, 7, 50, 6, 3, 109, 117, 108, 0, 1, 5, 100, 105, 118, 95, 115, 0, 2, 5, 100, 105, 118, 95, 117, 0, 3, 5, 114, 101, 109, 95, 115, 0, 4, 5, 114, 101, 109, 95, 117, 0, 5, 8, 103, 101, 116, 95, 104, 105, 103, 104, 0, 0, 10, 191, 1, 6, 4, 0, 35, 0, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 126, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 127, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 128, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 129, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 130, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11 + ])), {}).exports; + } catch (e) { + // no wasm support :( + } + + /** + * Constructs a 64 bit two's-complement integer, given its low and high 32 bit values as *signed* integers. + * See the from* functions below for more convenient ways of constructing Longs. + * @exports Long + * @class A Long class for representing a 64 bit two's-complement integer value. + * @param {number} low The low (signed) 32 bits of the long + * @param {number} high The high (signed) 32 bits of the long + * @param {boolean=} unsigned Whether unsigned or not, defaults to signed + * @constructor + */ + function Long(low, high, unsigned) { + + /** + * The low 32 bits as a signed value. + * @type {number} + */ + this.low = low | 0; + + /** + * The high 32 bits as a signed value. + * @type {number} + */ + this.high = high | 0; + + /** + * Whether unsigned or not. + * @type {boolean} + */ + this.unsigned = !!unsigned; + } + + // The internal representation of a long is the two given signed, 32-bit values. + // We use 32-bit pieces because these are the size of integers on which + // Javascript performs bit-operations. For operations like addition and + // multiplication, we split each number into 16 bit pieces, which can easily be + // multiplied within Javascript's floating-point representation without overflow + // or change in sign. + // + // In the algorithms below, we frequently reduce the negative case to the + // positive case by negating the input(s) and then post-processing the result. + // Note that we must ALWAYS check specially whether those values are MIN_VALUE + // (-2^63) because -MIN_VALUE == MIN_VALUE (since 2^63 cannot be represented as + // a positive number, it overflows back into a negative). Not handling this + // case would often result in infinite recursion. + // + // Common constant values ZERO, ONE, NEG_ONE, etc. are defined below the from* + // methods on which they depend. + + /** + * An indicator used to reliably determine if an object is a Long or not. + * @type {boolean} + * @const + * @private + */ + Long.prototype.__isLong__; + + Object.defineProperty(Long.prototype, "__isLong__", { value: true }); + + /** + * @function + * @param {*} obj Object + * @returns {boolean} + * @inner + */ + function isLong(obj) { + return (obj && obj["__isLong__"]) === true; + } + + /** + * Tests if the specified object is a Long. + * @function + * @param {*} obj Object + * @returns {boolean} + */ + Long.isLong = isLong; + + /** + * A cache of the Long representations of small integer values. + * @type {!Object} + * @inner + */ + var INT_CACHE = {}; + + /** + * A cache of the Long representations of small unsigned integer values. + * @type {!Object} + * @inner + */ + var UINT_CACHE = {}; + + /** + * @param {number} value + * @param {boolean=} unsigned + * @returns {!Long} + * @inner + */ + function fromInt(value, unsigned) { + var obj, cachedObj, cache; + if (unsigned) { + value >>>= 0; + if (cache = (0 <= value && value < 256)) { + cachedObj = UINT_CACHE[value]; + if (cachedObj) + return cachedObj; + } + obj = fromBits(value, (value | 0) < 0 ? -1 : 0, true); + if (cache) + UINT_CACHE[value] = obj; + return obj; + } else { + value |= 0; + if (cache = (-128 <= value && value < 128)) { + cachedObj = INT_CACHE[value]; + if (cachedObj) + return cachedObj; + } + obj = fromBits(value, value < 0 ? -1 : 0, false); + if (cache) + INT_CACHE[value] = obj; + return obj; + } + } + + /** + * Returns a Long representing the given 32 bit integer value. + * @function + * @param {number} value The 32 bit integer in question + * @param {boolean=} unsigned Whether unsigned or not, defaults to signed + * @returns {!Long} The corresponding Long value + */ + Long.fromInt = fromInt; + + /** + * @param {number} value + * @param {boolean=} unsigned + * @returns {!Long} + * @inner + */ + function fromNumber(value, unsigned) { + if (isNaN(value)) + return unsigned ? UZERO : ZERO; + if (unsigned) { + if (value < 0) + return UZERO; + if (value >= TWO_PWR_64_DBL) + return MAX_UNSIGNED_VALUE; + } else { + if (value <= -TWO_PWR_63_DBL) + return MIN_VALUE; + if (value + 1 >= TWO_PWR_63_DBL) + return MAX_VALUE; + } + if (value < 0) + return fromNumber(-value, unsigned).neg(); + return fromBits((value % TWO_PWR_32_DBL) | 0, (value / TWO_PWR_32_DBL) | 0, unsigned); + } + + /** + * Returns a Long representing the given value, provided that it is a finite number. Otherwise, zero is returned. + * @function + * @param {number} value The number in question + * @param {boolean=} unsigned Whether unsigned or not, defaults to signed + * @returns {!Long} The corresponding Long value + */ + Long.fromNumber = fromNumber; + + /** + * @param {number} lowBits + * @param {number} highBits + * @param {boolean=} unsigned + * @returns {!Long} + * @inner + */ + function fromBits(lowBits, highBits, unsigned) { + return new Long(lowBits, highBits, unsigned); + } + + /** + * Returns a Long representing the 64 bit integer that comes by concatenating the given low and high bits. Each is + * assumed to use 32 bits. + * @function + * @param {number} lowBits The low 32 bits + * @param {number} highBits The high 32 bits + * @param {boolean=} unsigned Whether unsigned or not, defaults to signed + * @returns {!Long} The corresponding Long value + */ + Long.fromBits = fromBits; + + /** + * @function + * @param {number} base + * @param {number} exponent + * @returns {number} + * @inner + */ + var pow_dbl = Math.pow; // Used 4 times (4*8 to 15+4) + + /** + * @param {string} str + * @param {(boolean|number)=} unsigned + * @param {number=} radix + * @returns {!Long} + * @inner + */ + function fromString(str, unsigned, radix) { + if (str.length === 0) + throw Error('empty string'); + if (str === "NaN" || str === "Infinity" || str === "+Infinity" || str === "-Infinity") + return ZERO; + if (typeof unsigned === 'number') { + // For goog.math.long compatibility + radix = unsigned, + unsigned = false; + } else { + unsigned = !! unsigned; + } + radix = radix || 10; + if (radix < 2 || 36 < radix) + throw RangeError('radix'); + + var p; + if ((p = str.indexOf('-')) > 0) + throw Error('interior hyphen'); + else if (p === 0) { + return fromString(str.substring(1), unsigned, radix).neg(); + } + + // Do several (8) digits each time through the loop, so as to + // minimize the calls to the very expensive emulated div. + var radixToPower = fromNumber(pow_dbl(radix, 8)); + + var result = ZERO; + for (var i = 0; i < str.length; i += 8) { + var size = Math.min(8, str.length - i), + value = parseInt(str.substring(i, i + size), radix); + if (size < 8) { + var power = fromNumber(pow_dbl(radix, size)); + result = result.mul(power).add(fromNumber(value)); + } else { + result = result.mul(radixToPower); + result = result.add(fromNumber(value)); + } + } + result.unsigned = unsigned; + return result; + } + + /** + * Returns a Long representation of the given string, written using the specified radix. + * @function + * @param {string} str The textual representation of the Long + * @param {(boolean|number)=} unsigned Whether unsigned or not, defaults to signed + * @param {number=} radix The radix in which the text is written (2-36), defaults to 10 + * @returns {!Long} The corresponding Long value + */ + Long.fromString = fromString; + + /** + * @function + * @param {!Long|number|string|!{low: number, high: number, unsigned: boolean}} val + * @param {boolean=} unsigned + * @returns {!Long} + * @inner + */ + function fromValue(val, unsigned) { + if (typeof val === 'number') + return fromNumber(val, unsigned); + if (typeof val === 'string') + return fromString(val, unsigned); + // Throws for non-objects, converts non-instanceof Long: + return fromBits(val.low, val.high, typeof unsigned === 'boolean' ? unsigned : val.unsigned); + } + + /** + * Converts the specified value to a Long using the appropriate from* function for its type. + * @function + * @param {!Long|number|string|!{low: number, high: number, unsigned: boolean}} val Value + * @param {boolean=} unsigned Whether unsigned or not, defaults to signed + * @returns {!Long} + */ + Long.fromValue = fromValue; + + // NOTE: the compiler should inline these constant values below and then remove these variables, so there should be + // no runtime penalty for these. + + /** + * @type {number} + * @const + * @inner + */ + var TWO_PWR_16_DBL = 1 << 16; + + /** + * @type {number} + * @const + * @inner + */ + var TWO_PWR_24_DBL = 1 << 24; + + /** + * @type {number} + * @const + * @inner + */ + var TWO_PWR_32_DBL = TWO_PWR_16_DBL * TWO_PWR_16_DBL; + + /** + * @type {number} + * @const + * @inner + */ + var TWO_PWR_64_DBL = TWO_PWR_32_DBL * TWO_PWR_32_DBL; + + /** + * @type {number} + * @const + * @inner + */ + var TWO_PWR_63_DBL = TWO_PWR_64_DBL / 2; + + /** + * @type {!Long} + * @const + * @inner + */ + var TWO_PWR_24 = fromInt(TWO_PWR_24_DBL); + + /** + * @type {!Long} + * @inner + */ + var ZERO = fromInt(0); + + /** + * Signed zero. + * @type {!Long} + */ + Long.ZERO = ZERO; + + /** + * @type {!Long} + * @inner + */ + var UZERO = fromInt(0, true); + + /** + * Unsigned zero. + * @type {!Long} + */ + Long.UZERO = UZERO; + + /** + * @type {!Long} + * @inner + */ + var ONE = fromInt(1); + + /** + * Signed one. + * @type {!Long} + */ + Long.ONE = ONE; + + /** + * @type {!Long} + * @inner + */ + var UONE = fromInt(1, true); + + /** + * Unsigned one. + * @type {!Long} + */ + Long.UONE = UONE; + + /** + * @type {!Long} + * @inner + */ + var NEG_ONE = fromInt(-1); + + /** + * Signed negative one. + * @type {!Long} + */ + Long.NEG_ONE = NEG_ONE; + + /** + * @type {!Long} + * @inner + */ + var MAX_VALUE = fromBits(0xFFFFFFFF|0, 0x7FFFFFFF|0, false); + + /** + * Maximum signed value. + * @type {!Long} + */ + Long.MAX_VALUE = MAX_VALUE; + + /** + * @type {!Long} + * @inner + */ + var MAX_UNSIGNED_VALUE = fromBits(0xFFFFFFFF|0, 0xFFFFFFFF|0, true); + + /** + * Maximum unsigned value. + * @type {!Long} + */ + Long.MAX_UNSIGNED_VALUE = MAX_UNSIGNED_VALUE; + + /** + * @type {!Long} + * @inner + */ + var MIN_VALUE = fromBits(0, 0x80000000|0, false); + + /** + * Minimum signed value. + * @type {!Long} + */ + Long.MIN_VALUE = MIN_VALUE; + + /** + * @alias Long.prototype + * @inner + */ + var LongPrototype = Long.prototype; + + /** + * Converts the Long to a 32 bit integer, assuming it is a 32 bit integer. + * @returns {number} + */ + LongPrototype.toInt = function toInt() { + return this.unsigned ? this.low >>> 0 : this.low; + }; + + /** + * Converts the Long to a the nearest floating-point representation of this value (double, 53 bit mantissa). + * @returns {number} + */ + LongPrototype.toNumber = function toNumber() { + if (this.unsigned) + return ((this.high >>> 0) * TWO_PWR_32_DBL) + (this.low >>> 0); + return this.high * TWO_PWR_32_DBL + (this.low >>> 0); + }; + + /** + * Converts the Long to a string written in the specified radix. + * @param {number=} radix Radix (2-36), defaults to 10 + * @returns {string} + * @override + * @throws {RangeError} If `radix` is out of range + */ + LongPrototype.toString = function toString(radix) { + radix = radix || 10; + if (radix < 2 || 36 < radix) + throw RangeError('radix'); + if (this.isZero()) + return '0'; + if (this.isNegative()) { // Unsigned Longs are never negative + if (this.eq(MIN_VALUE)) { + // We need to change the Long value before it can be negated, so we remove + // the bottom-most digit in this base and then recurse to do the rest. + var radixLong = fromNumber(radix), + div = this.div(radixLong), + rem1 = div.mul(radixLong).sub(this); + return div.toString(radix) + rem1.toInt().toString(radix); + } else + return '-' + this.neg().toString(radix); + } + + // Do several (6) digits each time through the loop, so as to + // minimize the calls to the very expensive emulated div. + var radixToPower = fromNumber(pow_dbl(radix, 6), this.unsigned), + rem = this; + var result = ''; + while (true) { + var remDiv = rem.div(radixToPower), + intval = rem.sub(remDiv.mul(radixToPower)).toInt() >>> 0, + digits = intval.toString(radix); + rem = remDiv; + if (rem.isZero()) + return digits + result; + else { + while (digits.length < 6) + digits = '0' + digits; + result = '' + digits + result; + } + } + }; + + /** + * Gets the high 32 bits as a signed integer. + * @returns {number} Signed high bits + */ + LongPrototype.getHighBits = function getHighBits() { + return this.high; + }; + + /** + * Gets the high 32 bits as an unsigned integer. + * @returns {number} Unsigned high bits + */ + LongPrototype.getHighBitsUnsigned = function getHighBitsUnsigned() { + return this.high >>> 0; + }; + + /** + * Gets the low 32 bits as a signed integer. + * @returns {number} Signed low bits + */ + LongPrototype.getLowBits = function getLowBits() { + return this.low; + }; + + /** + * Gets the low 32 bits as an unsigned integer. + * @returns {number} Unsigned low bits + */ + LongPrototype.getLowBitsUnsigned = function getLowBitsUnsigned() { + return this.low >>> 0; + }; + + /** + * Gets the number of bits needed to represent the absolute value of this Long. + * @returns {number} + */ + LongPrototype.getNumBitsAbs = function getNumBitsAbs() { + if (this.isNegative()) // Unsigned Longs are never negative + return this.eq(MIN_VALUE) ? 64 : this.neg().getNumBitsAbs(); + var val = this.high != 0 ? this.high : this.low; + for (var bit = 31; bit > 0; bit--) + if ((val & (1 << bit)) != 0) + break; + return this.high != 0 ? bit + 33 : bit + 1; + }; + + /** + * Tests if this Long's value equals zero. + * @returns {boolean} + */ + LongPrototype.isZero = function isZero() { + return this.high === 0 && this.low === 0; + }; + + /** + * Tests if this Long's value equals zero. This is an alias of {@link Long#isZero}. + * @returns {boolean} + */ + LongPrototype.eqz = LongPrototype.isZero; + + /** + * Tests if this Long's value is negative. + * @returns {boolean} + */ + LongPrototype.isNegative = function isNegative() { + return !this.unsigned && this.high < 0; + }; + + /** + * Tests if this Long's value is positive. + * @returns {boolean} + */ + LongPrototype.isPositive = function isPositive() { + return this.unsigned || this.high >= 0; + }; + + /** + * Tests if this Long's value is odd. + * @returns {boolean} + */ + LongPrototype.isOdd = function isOdd() { + return (this.low & 1) === 1; + }; + + /** + * Tests if this Long's value is even. + * @returns {boolean} + */ + LongPrototype.isEven = function isEven() { + return (this.low & 1) === 0; + }; + + /** + * Tests if this Long's value equals the specified's. + * @param {!Long|number|string} other Other value + * @returns {boolean} + */ + LongPrototype.equals = function equals(other) { + if (!isLong(other)) + other = fromValue(other); + if (this.unsigned !== other.unsigned && (this.high >>> 31) === 1 && (other.high >>> 31) === 1) + return false; + return this.high === other.high && this.low === other.low; + }; + + /** + * Tests if this Long's value equals the specified's. This is an alias of {@link Long#equals}. + * @function + * @param {!Long|number|string} other Other value + * @returns {boolean} + */ + LongPrototype.eq = LongPrototype.equals; + + /** + * Tests if this Long's value differs from the specified's. + * @param {!Long|number|string} other Other value + * @returns {boolean} + */ + LongPrototype.notEquals = function notEquals(other) { + return !this.eq(/* validates */ other); + }; + + /** + * Tests if this Long's value differs from the specified's. This is an alias of {@link Long#notEquals}. + * @function + * @param {!Long|number|string} other Other value + * @returns {boolean} + */ + LongPrototype.neq = LongPrototype.notEquals; + + /** + * Tests if this Long's value differs from the specified's. This is an alias of {@link Long#notEquals}. + * @function + * @param {!Long|number|string} other Other value + * @returns {boolean} + */ + LongPrototype.ne = LongPrototype.notEquals; + + /** + * Tests if this Long's value is less than the specified's. + * @param {!Long|number|string} other Other value + * @returns {boolean} + */ + LongPrototype.lessThan = function lessThan(other) { + return this.comp(/* validates */ other) < 0; + }; + + /** + * Tests if this Long's value is less than the specified's. This is an alias of {@link Long#lessThan}. + * @function + * @param {!Long|number|string} other Other value + * @returns {boolean} + */ + LongPrototype.lt = LongPrototype.lessThan; + + /** + * Tests if this Long's value is less than or equal the specified's. + * @param {!Long|number|string} other Other value + * @returns {boolean} + */ + LongPrototype.lessThanOrEqual = function lessThanOrEqual(other) { + return this.comp(/* validates */ other) <= 0; + }; + + /** + * Tests if this Long's value is less than or equal the specified's. This is an alias of {@link Long#lessThanOrEqual}. + * @function + * @param {!Long|number|string} other Other value + * @returns {boolean} + */ + LongPrototype.lte = LongPrototype.lessThanOrEqual; + + /** + * Tests if this Long's value is less than or equal the specified's. This is an alias of {@link Long#lessThanOrEqual}. + * @function + * @param {!Long|number|string} other Other value + * @returns {boolean} + */ + LongPrototype.le = LongPrototype.lessThanOrEqual; + + /** + * Tests if this Long's value is greater than the specified's. + * @param {!Long|number|string} other Other value + * @returns {boolean} + */ + LongPrototype.greaterThan = function greaterThan(other) { + return this.comp(/* validates */ other) > 0; + }; + + /** + * Tests if this Long's value is greater than the specified's. This is an alias of {@link Long#greaterThan}. + * @function + * @param {!Long|number|string} other Other value + * @returns {boolean} + */ + LongPrototype.gt = LongPrototype.greaterThan; + + /** + * Tests if this Long's value is greater than or equal the specified's. + * @param {!Long|number|string} other Other value + * @returns {boolean} + */ + LongPrototype.greaterThanOrEqual = function greaterThanOrEqual(other) { + return this.comp(/* validates */ other) >= 0; + }; + + /** + * Tests if this Long's value is greater than or equal the specified's. This is an alias of {@link Long#greaterThanOrEqual}. + * @function + * @param {!Long|number|string} other Other value + * @returns {boolean} + */ + LongPrototype.gte = LongPrototype.greaterThanOrEqual; + + /** + * Tests if this Long's value is greater than or equal the specified's. This is an alias of {@link Long#greaterThanOrEqual}. + * @function + * @param {!Long|number|string} other Other value + * @returns {boolean} + */ + LongPrototype.ge = LongPrototype.greaterThanOrEqual; + + /** + * Compares this Long's value with the specified's. + * @param {!Long|number|string} other Other value + * @returns {number} 0 if they are the same, 1 if the this is greater and -1 + * if the given one is greater + */ + LongPrototype.compare = function compare(other) { + if (!isLong(other)) + other = fromValue(other); + if (this.eq(other)) + return 0; + var thisNeg = this.isNegative(), + otherNeg = other.isNegative(); + if (thisNeg && !otherNeg) + return -1; + if (!thisNeg && otherNeg) + return 1; + // At this point the sign bits are the same + if (!this.unsigned) + return this.sub(other).isNegative() ? -1 : 1; + // Both are positive if at least one is unsigned + return (other.high >>> 0) > (this.high >>> 0) || (other.high === this.high && (other.low >>> 0) > (this.low >>> 0)) ? -1 : 1; + }; + + /** + * Compares this Long's value with the specified's. This is an alias of {@link Long#compare}. + * @function + * @param {!Long|number|string} other Other value + * @returns {number} 0 if they are the same, 1 if the this is greater and -1 + * if the given one is greater + */ + LongPrototype.comp = LongPrototype.compare; + + /** + * Negates this Long's value. + * @returns {!Long} Negated Long + */ + LongPrototype.negate = function negate() { + if (!this.unsigned && this.eq(MIN_VALUE)) + return MIN_VALUE; + return this.not().add(ONE); + }; + + /** + * Negates this Long's value. This is an alias of {@link Long#negate}. + * @function + * @returns {!Long} Negated Long + */ + LongPrototype.neg = LongPrototype.negate; + + /** + * Returns the sum of this and the specified Long. + * @param {!Long|number|string} addend Addend + * @returns {!Long} Sum + */ + LongPrototype.add = function add(addend) { + if (!isLong(addend)) + addend = fromValue(addend); + + // Divide each number into 4 chunks of 16 bits, and then sum the chunks. + + var a48 = this.high >>> 16; + var a32 = this.high & 0xFFFF; + var a16 = this.low >>> 16; + var a00 = this.low & 0xFFFF; + + var b48 = addend.high >>> 16; + var b32 = addend.high & 0xFFFF; + var b16 = addend.low >>> 16; + var b00 = addend.low & 0xFFFF; + + var c48 = 0, c32 = 0, c16 = 0, c00 = 0; + c00 += a00 + b00; + c16 += c00 >>> 16; + c00 &= 0xFFFF; + c16 += a16 + b16; + c32 += c16 >>> 16; + c16 &= 0xFFFF; + c32 += a32 + b32; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c48 += a48 + b48; + c48 &= 0xFFFF; + return fromBits((c16 << 16) | c00, (c48 << 16) | c32, this.unsigned); + }; + + /** + * Returns the difference of this and the specified Long. + * @param {!Long|number|string} subtrahend Subtrahend + * @returns {!Long} Difference + */ + LongPrototype.subtract = function subtract(subtrahend) { + if (!isLong(subtrahend)) + subtrahend = fromValue(subtrahend); + return this.add(subtrahend.neg()); + }; + + /** + * Returns the difference of this and the specified Long. This is an alias of {@link Long#subtract}. + * @function + * @param {!Long|number|string} subtrahend Subtrahend + * @returns {!Long} Difference + */ + LongPrototype.sub = LongPrototype.subtract; + + /** + * Returns the product of this and the specified Long. + * @param {!Long|number|string} multiplier Multiplier + * @returns {!Long} Product + */ + LongPrototype.multiply = function multiply(multiplier) { + if (this.isZero()) + return ZERO; + if (!isLong(multiplier)) + multiplier = fromValue(multiplier); + + // use wasm support if present + if (wasm) { + var low = wasm.mul(this.low, + this.high, + multiplier.low, + multiplier.high); + return fromBits(low, wasm.get_high(), this.unsigned); + } + + if (multiplier.isZero()) + return ZERO; + if (this.eq(MIN_VALUE)) + return multiplier.isOdd() ? MIN_VALUE : ZERO; + if (multiplier.eq(MIN_VALUE)) + return this.isOdd() ? MIN_VALUE : ZERO; + + if (this.isNegative()) { + if (multiplier.isNegative()) + return this.neg().mul(multiplier.neg()); + else + return this.neg().mul(multiplier).neg(); + } else if (multiplier.isNegative()) + return this.mul(multiplier.neg()).neg(); + + // If both longs are small, use float multiplication + if (this.lt(TWO_PWR_24) && multiplier.lt(TWO_PWR_24)) + return fromNumber(this.toNumber() * multiplier.toNumber(), this.unsigned); + + // Divide each long into 4 chunks of 16 bits, and then add up 4x4 products. + // We can skip products that would overflow. + + var a48 = this.high >>> 16; + var a32 = this.high & 0xFFFF; + var a16 = this.low >>> 16; + var a00 = this.low & 0xFFFF; + + var b48 = multiplier.high >>> 16; + var b32 = multiplier.high & 0xFFFF; + var b16 = multiplier.low >>> 16; + var b00 = multiplier.low & 0xFFFF; + + var c48 = 0, c32 = 0, c16 = 0, c00 = 0; + c00 += a00 * b00; + c16 += c00 >>> 16; + c00 &= 0xFFFF; + c16 += a16 * b00; + c32 += c16 >>> 16; + c16 &= 0xFFFF; + c16 += a00 * b16; + c32 += c16 >>> 16; + c16 &= 0xFFFF; + c32 += a32 * b00; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c32 += a16 * b16; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c32 += a00 * b32; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48; + c48 &= 0xFFFF; + return fromBits((c16 << 16) | c00, (c48 << 16) | c32, this.unsigned); + }; + + /** + * Returns the product of this and the specified Long. This is an alias of {@link Long#multiply}. + * @function + * @param {!Long|number|string} multiplier Multiplier + * @returns {!Long} Product + */ + LongPrototype.mul = LongPrototype.multiply; + + /** + * Returns this Long divided by the specified. The result is signed if this Long is signed or + * unsigned if this Long is unsigned. + * @param {!Long|number|string} divisor Divisor + * @returns {!Long} Quotient + */ + LongPrototype.divide = function divide(divisor) { + if (!isLong(divisor)) + divisor = fromValue(divisor); + if (divisor.isZero()) + throw Error('division by zero'); + + // use wasm support if present + if (wasm) { + // guard against signed division overflow: the largest + // negative number / -1 would be 1 larger than the largest + // positive number, due to two's complement. + if (!this.unsigned && + this.high === -0x80000000 && + divisor.low === -1 && divisor.high === -1) { + // be consistent with non-wasm code path + return this; + } + var low = (this.unsigned ? wasm.div_u : wasm.div_s)( + this.low, + this.high, + divisor.low, + divisor.high + ); + return fromBits(low, wasm.get_high(), this.unsigned); + } + + if (this.isZero()) + return this.unsigned ? UZERO : ZERO; + var approx, rem, res; + if (!this.unsigned) { + // This section is only relevant for signed longs and is derived from the + // closure library as a whole. + if (this.eq(MIN_VALUE)) { + if (divisor.eq(ONE) || divisor.eq(NEG_ONE)) + return MIN_VALUE; // recall that -MIN_VALUE == MIN_VALUE + else if (divisor.eq(MIN_VALUE)) + return ONE; + else { + // At this point, we have |other| >= 2, so |this/other| < |MIN_VALUE|. + var halfThis = this.shr(1); + approx = halfThis.div(divisor).shl(1); + if (approx.eq(ZERO)) { + return divisor.isNegative() ? ONE : NEG_ONE; + } else { + rem = this.sub(divisor.mul(approx)); + res = approx.add(rem.div(divisor)); + return res; + } + } + } else if (divisor.eq(MIN_VALUE)) + return this.unsigned ? UZERO : ZERO; + if (this.isNegative()) { + if (divisor.isNegative()) + return this.neg().div(divisor.neg()); + return this.neg().div(divisor).neg(); + } else if (divisor.isNegative()) + return this.div(divisor.neg()).neg(); + res = ZERO; + } else { + // The algorithm below has not been made for unsigned longs. It's therefore + // required to take special care of the MSB prior to running it. + if (!divisor.unsigned) + divisor = divisor.toUnsigned(); + if (divisor.gt(this)) + return UZERO; + if (divisor.gt(this.shru(1))) // 15 >>> 1 = 7 ; with divisor = 8 ; true + return UONE; + res = UZERO; + } + + // Repeat the following until the remainder is less than other: find a + // floating-point that approximates remainder / other *from below*, add this + // into the result, and subtract it from the remainder. It is critical that + // the approximate value is less than or equal to the real value so that the + // remainder never becomes negative. + rem = this; + while (rem.gte(divisor)) { + // Approximate the result of division. This may be a little greater or + // smaller than the actual value. + approx = Math.max(1, Math.floor(rem.toNumber() / divisor.toNumber())); + + // We will tweak the approximate result by changing it in the 48-th digit or + // the smallest non-fractional digit, whichever is larger. + var log2 = Math.ceil(Math.log(approx) / Math.LN2), + delta = (log2 <= 48) ? 1 : pow_dbl(2, log2 - 48), + + // Decrease the approximation until it is smaller than the remainder. Note + // that if it is too large, the product overflows and is negative. + approxRes = fromNumber(approx), + approxRem = approxRes.mul(divisor); + while (approxRem.isNegative() || approxRem.gt(rem)) { + approx -= delta; + approxRes = fromNumber(approx, this.unsigned); + approxRem = approxRes.mul(divisor); + } + + // We know the answer can't be zero... and actually, zero would cause + // infinite recursion since we would make no progress. + if (approxRes.isZero()) + approxRes = ONE; + + res = res.add(approxRes); + rem = rem.sub(approxRem); + } + return res; + }; + + /** + * Returns this Long divided by the specified. This is an alias of {@link Long#divide}. + * @function + * @param {!Long|number|string} divisor Divisor + * @returns {!Long} Quotient + */ + LongPrototype.div = LongPrototype.divide; + + /** + * Returns this Long modulo the specified. + * @param {!Long|number|string} divisor Divisor + * @returns {!Long} Remainder + */ + LongPrototype.modulo = function modulo(divisor) { + if (!isLong(divisor)) + divisor = fromValue(divisor); + + // use wasm support if present + if (wasm) { + var low = (this.unsigned ? wasm.rem_u : wasm.rem_s)( + this.low, + this.high, + divisor.low, + divisor.high + ); + return fromBits(low, wasm.get_high(), this.unsigned); + } + + return this.sub(this.div(divisor).mul(divisor)); + }; + + /** + * Returns this Long modulo the specified. This is an alias of {@link Long#modulo}. + * @function + * @param {!Long|number|string} divisor Divisor + * @returns {!Long} Remainder + */ + LongPrototype.mod = LongPrototype.modulo; + + /** + * Returns this Long modulo the specified. This is an alias of {@link Long#modulo}. + * @function + * @param {!Long|number|string} divisor Divisor + * @returns {!Long} Remainder + */ + LongPrototype.rem = LongPrototype.modulo; + + /** + * Returns the bitwise NOT of this Long. + * @returns {!Long} + */ + LongPrototype.not = function not() { + return fromBits(~this.low, ~this.high, this.unsigned); + }; + + /** + * Returns the bitwise AND of this Long and the specified. + * @param {!Long|number|string} other Other Long + * @returns {!Long} + */ + LongPrototype.and = function and(other) { + if (!isLong(other)) + other = fromValue(other); + return fromBits(this.low & other.low, this.high & other.high, this.unsigned); + }; + + /** + * Returns the bitwise OR of this Long and the specified. + * @param {!Long|number|string} other Other Long + * @returns {!Long} + */ + LongPrototype.or = function or(other) { + if (!isLong(other)) + other = fromValue(other); + return fromBits(this.low | other.low, this.high | other.high, this.unsigned); + }; + + /** + * Returns the bitwise XOR of this Long and the given one. + * @param {!Long|number|string} other Other Long + * @returns {!Long} + */ + LongPrototype.xor = function xor(other) { + if (!isLong(other)) + other = fromValue(other); + return fromBits(this.low ^ other.low, this.high ^ other.high, this.unsigned); + }; + + /** + * Returns this Long with bits shifted to the left by the given amount. + * @param {number|!Long} numBits Number of bits + * @returns {!Long} Shifted Long + */ + LongPrototype.shiftLeft = function shiftLeft(numBits) { + if (isLong(numBits)) + numBits = numBits.toInt(); + if ((numBits &= 63) === 0) + return this; + else if (numBits < 32) + return fromBits(this.low << numBits, (this.high << numBits) | (this.low >>> (32 - numBits)), this.unsigned); + else + return fromBits(0, this.low << (numBits - 32), this.unsigned); + }; + + /** + * Returns this Long with bits shifted to the left by the given amount. This is an alias of {@link Long#shiftLeft}. + * @function + * @param {number|!Long} numBits Number of bits + * @returns {!Long} Shifted Long + */ + LongPrototype.shl = LongPrototype.shiftLeft; + + /** + * Returns this Long with bits arithmetically shifted to the right by the given amount. + * @param {number|!Long} numBits Number of bits + * @returns {!Long} Shifted Long + */ + LongPrototype.shiftRight = function shiftRight(numBits) { + if (isLong(numBits)) + numBits = numBits.toInt(); + if ((numBits &= 63) === 0) + return this; + else if (numBits < 32) + return fromBits((this.low >>> numBits) | (this.high << (32 - numBits)), this.high >> numBits, this.unsigned); + else + return fromBits(this.high >> (numBits - 32), this.high >= 0 ? 0 : -1, this.unsigned); + }; + + /** + * Returns this Long with bits arithmetically shifted to the right by the given amount. This is an alias of {@link Long#shiftRight}. + * @function + * @param {number|!Long} numBits Number of bits + * @returns {!Long} Shifted Long + */ + LongPrototype.shr = LongPrototype.shiftRight; + + /** + * Returns this Long with bits logically shifted to the right by the given amount. + * @param {number|!Long} numBits Number of bits + * @returns {!Long} Shifted Long + */ + LongPrototype.shiftRightUnsigned = function shiftRightUnsigned(numBits) { + if (isLong(numBits)) + numBits = numBits.toInt(); + numBits &= 63; + if (numBits === 0) + return this; + else { + var high = this.high; + if (numBits < 32) { + var low = this.low; + return fromBits((low >>> numBits) | (high << (32 - numBits)), high >>> numBits, this.unsigned); + } else if (numBits === 32) + return fromBits(high, 0, this.unsigned); + else + return fromBits(high >>> (numBits - 32), 0, this.unsigned); + } + }; + + /** + * Returns this Long with bits logically shifted to the right by the given amount. This is an alias of {@link Long#shiftRightUnsigned}. + * @function + * @param {number|!Long} numBits Number of bits + * @returns {!Long} Shifted Long + */ + LongPrototype.shru = LongPrototype.shiftRightUnsigned; + + /** + * Returns this Long with bits logically shifted to the right by the given amount. This is an alias of {@link Long#shiftRightUnsigned}. + * @function + * @param {number|!Long} numBits Number of bits + * @returns {!Long} Shifted Long + */ + LongPrototype.shr_u = LongPrototype.shiftRightUnsigned; + + /** + * Converts this Long to signed. + * @returns {!Long} Signed long + */ + LongPrototype.toSigned = function toSigned() { + if (!this.unsigned) + return this; + return fromBits(this.low, this.high, false); + }; + + /** + * Converts this Long to unsigned. + * @returns {!Long} Unsigned long + */ + LongPrototype.toUnsigned = function toUnsigned() { + if (this.unsigned) + return this; + return fromBits(this.low, this.high, true); + }; + + /** + * Converts this Long to its byte representation. + * @param {boolean=} le Whether little or big endian, defaults to big endian + * @returns {!Array.} Byte representation + */ + LongPrototype.toBytes = function toBytes(le) { + return le ? this.toBytesLE() : this.toBytesBE(); + }; + + /** + * Converts this Long to its little endian byte representation. + * @returns {!Array.} Little endian byte representation + */ + LongPrototype.toBytesLE = function toBytesLE() { + var hi = this.high, + lo = this.low; + return [ + lo & 0xff, + lo >>> 8 & 0xff, + lo >>> 16 & 0xff, + lo >>> 24 , + hi & 0xff, + hi >>> 8 & 0xff, + hi >>> 16 & 0xff, + hi >>> 24 + ]; + }; + + /** + * Converts this Long to its big endian byte representation. + * @returns {!Array.} Big endian byte representation + */ + LongPrototype.toBytesBE = function toBytesBE() { + var hi = this.high, + lo = this.low; + return [ + hi >>> 24 , + hi >>> 16 & 0xff, + hi >>> 8 & 0xff, + hi & 0xff, + lo >>> 24 , + lo >>> 16 & 0xff, + lo >>> 8 & 0xff, + lo & 0xff + ]; + }; + + /** + * Creates a Long from its byte representation. + * @param {!Array.} bytes Byte representation + * @param {boolean=} unsigned Whether unsigned or not, defaults to signed + * @param {boolean=} le Whether little or big endian, defaults to big endian + * @returns {Long} The corresponding Long value + */ + Long.fromBytes = function fromBytes(bytes, unsigned, le) { + return le ? Long.fromBytesLE(bytes, unsigned) : Long.fromBytesBE(bytes, unsigned); + }; + + /** + * Creates a Long from its little endian byte representation. + * @param {!Array.} bytes Little endian byte representation + * @param {boolean=} unsigned Whether unsigned or not, defaults to signed + * @returns {Long} The corresponding Long value + */ + Long.fromBytesLE = function fromBytesLE(bytes, unsigned) { + return new Long( + bytes[0] | + bytes[1] << 8 | + bytes[2] << 16 | + bytes[3] << 24, + bytes[4] | + bytes[5] << 8 | + bytes[6] << 16 | + bytes[7] << 24, + unsigned + ); + }; + + /** + * Creates a Long from its big endian byte representation. + * @param {!Array.} bytes Big endian byte representation + * @param {boolean=} unsigned Whether unsigned or not, defaults to signed + * @returns {Long} The corresponding Long value + */ + Long.fromBytesBE = function fromBytesBE(bytes, unsigned) { + return new Long( + bytes[4] << 24 | + bytes[5] << 16 | + bytes[6] << 8 | + bytes[7], + bytes[0] << 24 | + bytes[1] << 16 | + bytes[2] << 8 | + bytes[3], + unsigned + ); }; var LongExports = { diff --git a/thirdparty/tfjs/tf-core.js.map b/app/static/thirdparty/tfjs/tf-core.js.map similarity index 100% rename from thirdparty/tfjs/tf-core.js.map rename to app/static/thirdparty/tfjs/tf-core.js.map diff --git a/thirdparty/tflite/README.md b/app/static/thirdparty/tflite/README.md similarity index 100% rename from thirdparty/tflite/README.md rename to app/static/thirdparty/tflite/README.md diff --git a/thirdparty/tflite/segm_full_v679.tflite b/app/static/thirdparty/tflite/segm_full_v679.tflite similarity index 100% rename from thirdparty/tflite/segm_full_v679.tflite rename to app/static/thirdparty/tflite/segm_full_v679.tflite diff --git a/thirdparty/tflite/tflite-simd.js b/app/static/thirdparty/tflite/tflite-simd.js similarity index 97% rename from thirdparty/tflite/tflite-simd.js rename to app/static/thirdparty/tflite/tflite-simd.js index b458553..f116f7d 100644 --- a/thirdparty/tflite/tflite-simd.js +++ b/app/static/thirdparty/tflite/tflite-simd.js @@ -1,888 +1,888 @@ -var createTFLiteSIMDModule = (function() { - var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; - return ( - function(createTFLiteSIMDModule) { - createTFLiteSIMDModule = createTFLiteSIMDModule || {}; - - var Module = typeof createTFLiteSIMDModule !== "undefined" ? createTFLiteSIMDModule : {}; - var readyPromiseResolve, readyPromiseReject; - Module["ready"] = new Promise(function(resolve, reject) { - readyPromiseResolve = resolve; - readyPromiseReject = reject - }); - var moduleOverrides = {}; - var key; - for (key in Module) { - if (Module.hasOwnProperty(key)) { - moduleOverrides[key] = Module[key] - } - } - var arguments_ = []; - var thisProgram = "./this.program"; - var quit_ = function(status, toThrow) { - throw toThrow - }; - - var scriptDirectory = ""; - - function locateFile(path) { - if (Module["locateFile"]) { - return Module["locateFile"](path, scriptDirectory) - } - return scriptDirectory + path - } - var read_, readAsync, readBinary; - - if (typeof document !== "undefined" && document.currentScript) { - scriptDirectory = document.currentScript.src - } - - if (_scriptDir) { - scriptDirectory = _scriptDir - } - if (scriptDirectory.indexOf("blob:") !== 0) { - scriptDirectory = scriptDirectory.substr(0, scriptDirectory.lastIndexOf("/") + 1) - } else { - scriptDirectory = "" - } { - read_ = function(url) { - var xhr = new XMLHttpRequest; - xhr.open("GET", url, false); - xhr.send(null); - return xhr.responseText - }; - - readAsync = function(url, onload, onerror) { - var xhr = new XMLHttpRequest; - xhr.open("GET", url, true); - xhr.responseType = "arraybuffer"; - xhr.onload = function() { - if (xhr.status == 200 || xhr.status == 0 && xhr.response) { - onload(xhr.response); - return - } - onerror() - }; - xhr.onerror = onerror; - xhr.send(null) - } - } - - var out = Module["print"] || console.log.bind(console); - var err = Module["printErr"] || console.warn.bind(console); - for (key in moduleOverrides) { - if (moduleOverrides.hasOwnProperty(key)) { - Module[key] = moduleOverrides[key] - } - } - moduleOverrides = null; - if (Module["arguments"]) arguments_ = Module["arguments"]; - if (Module["thisProgram"]) thisProgram = Module["thisProgram"]; - if (Module["quit"]) quit_ = Module["quit"]; - var wasmBinary; - if (Module["wasmBinary"]) wasmBinary = Module["wasmBinary"]; - var noExitRuntime; - if (Module["noExitRuntime"]) noExitRuntime = Module["noExitRuntime"]; - if (typeof WebAssembly !== "object") { - abort("no native wasm support detected") - } - var wasmMemory; - var ABORT = false; - var EXITSTATUS; - - function assert(condition, text) { - if (!condition) { - abort("Assertion failed: " + text) - } - } - var UTF8Decoder = typeof TextDecoder !== "undefined" ? new TextDecoder("utf8") : undefined; - - function UTF8ArrayToString(heap, idx, maxBytesToRead) { - var endIdx = idx + maxBytesToRead; - var endPtr = idx; - while (heap[endPtr] && !(endPtr >= endIdx)) ++endPtr; - if (endPtr - idx > 16 && heap.subarray && UTF8Decoder) { - return UTF8Decoder.decode(heap.subarray(idx, endPtr)) - } else { - var str = ""; - while (idx < endPtr) { - var u0 = heap[idx++]; - if (!(u0 & 128)) { - str += String.fromCharCode(u0); - continue - } - var u1 = heap[idx++] & 63; - if ((u0 & 224) == 192) { - str += String.fromCharCode((u0 & 31) << 6 | u1); - continue - } - var u2 = heap[idx++] & 63; - if ((u0 & 240) == 224) { - u0 = (u0 & 15) << 12 | u1 << 6 | u2 - } else { - u0 = (u0 & 7) << 18 | u1 << 12 | u2 << 6 | heap[idx++] & 63 - } - if (u0 < 65536) { - str += String.fromCharCode(u0) - } else { - var ch = u0 - 65536; - str += String.fromCharCode(55296 | ch >> 10, 56320 | ch & 1023) - } - } - } - return str - } - - function UTF8ToString(ptr, maxBytesToRead) { - return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : "" - } - - function writeAsciiToMemory(str, buffer, dontAddNull) { - for (var i = 0; i < str.length; ++i) { - HEAP8[buffer++ >> 0] = str.charCodeAt(i) - } - if (!dontAddNull) HEAP8[buffer >> 0] = 0 - } - - function alignUp(x, multiple) { - if (x % multiple > 0) { - x += multiple - x % multiple - } - return x - } - var buffer, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64; - - function updateGlobalBufferAndViews(buf) { - buffer = buf; - Module["HEAP8"] = HEAP8 = new Int8Array(buf); - Module["HEAP16"] = HEAP16 = new Int16Array(buf); - Module["HEAP32"] = HEAP32 = new Int32Array(buf); - Module["HEAPU8"] = HEAPU8 = new Uint8Array(buf); - Module["HEAPU16"] = HEAPU16 = new Uint16Array(buf); - Module["HEAPU32"] = HEAPU32 = new Uint32Array(buf); - Module["HEAPF32"] = HEAPF32 = new Float32Array(buf); - Module["HEAPF64"] = HEAPF64 = new Float64Array(buf) - } - var INITIAL_MEMORY = Module["INITIAL_MEMORY"] || 16777216; - var wasmTable; - var __ATPRERUN__ = []; - var __ATINIT__ = []; - var __ATMAIN__ = []; - var __ATPOSTRUN__ = []; - var runtimeInitialized = false; - var runtimeExited = false; - __ATINIT__.push({ - func: function() { - ___wasm_call_ctors() - } - }); - - function preRun() { - if (Module["preRun"]) { - if (typeof Module["preRun"] == "function") Module["preRun"] = [Module["preRun"]]; - while (Module["preRun"].length) { - addOnPreRun(Module["preRun"].shift()) - } - } - callRuntimeCallbacks(__ATPRERUN__) - } - - function initRuntime() { - runtimeInitialized = true; - callRuntimeCallbacks(__ATINIT__) - } - - function preMain() { - callRuntimeCallbacks(__ATMAIN__) - } - - function exitRuntime() { - runtimeExited = true - } - - function postRun() { - if (Module["postRun"]) { - if (typeof Module["postRun"] == "function") Module["postRun"] = [Module["postRun"]]; - while (Module["postRun"].length) { - addOnPostRun(Module["postRun"].shift()) - } - } - callRuntimeCallbacks(__ATPOSTRUN__) - } - - function addOnPreRun(cb) { - __ATPRERUN__.unshift(cb) - } - - function addOnPostRun(cb) { - __ATPOSTRUN__.unshift(cb) - } - var runDependencies = 0; - var runDependencyWatcher = null; - var dependenciesFulfilled = null; - - function addRunDependency(id) { - runDependencies++; - if (Module["monitorRunDependencies"]) { - Module["monitorRunDependencies"](runDependencies) - } - } - - function removeRunDependency(id) { - runDependencies--; - if (Module["monitorRunDependencies"]) { - Module["monitorRunDependencies"](runDependencies) - } - if (runDependencies == 0) { - if (runDependencyWatcher !== null) { - clearInterval(runDependencyWatcher); - runDependencyWatcher = null - } - if (dependenciesFulfilled) { - var callback = dependenciesFulfilled; - dependenciesFulfilled = null; - callback() - } - } - } - Module["preloadedImages"] = {}; - Module["preloadedAudios"] = {}; - - function abort(what) { - if (Module["onAbort"]) { - Module["onAbort"](what) - } - what += ""; - err(what); - ABORT = true; - EXITSTATUS = 1; - what = "abort(" + what + "). Build with -s ASSERTIONS=1 for more info."; - var e = new WebAssembly.RuntimeError(what); - readyPromiseReject(e); - throw e - } - - function hasPrefix(str, prefix) { - return String.prototype.startsWith ? str.startsWith(prefix) : str.indexOf(prefix) === 0 - } - var dataURIPrefix = "data:application/octet-stream;base64,"; - - function isDataURI(filename) { - return hasPrefix(filename, dataURIPrefix) - } - var fileURIPrefix = "file://"; - - function isFileURI(filename) { - return hasPrefix(filename, fileURIPrefix) - } - var wasmBinaryFile = "tflite-simd.wasm"; - createTFLiteSIMDModule.simd = true; - - if (!isDataURI(wasmBinaryFile)) { - wasmBinaryFile = locateFile(wasmBinaryFile) - } - - function getBinary(file) { - try { - if (file == wasmBinaryFile && wasmBinary) { - return new Uint8Array(wasmBinary) - } - if (readBinary) { - return readBinary(file) - } else { - throw "both async and sync fetching of the wasm failed" - } - } catch (err) { - abort(err) - } - } - - function getBinaryPromise() { - if (!wasmBinary) { - if (typeof fetch === "function" && !isFileURI(wasmBinaryFile)) { - return fetch(wasmBinaryFile, { - credentials: "same-origin" - }).then(function(response) { - if (!response["ok"]) { - throw "failed to load wasm binary file at '" + wasmBinaryFile + "'" - } - return response["arrayBuffer"]() - }).catch(function() { - return getBinary(wasmBinaryFile) - }) - } else { - if (readAsync) { - return new Promise(function(resolve, reject) { - readAsync(wasmBinaryFile, function(response) { - resolve(new Uint8Array(response)) - }, reject) - }) - } - } - } - return Promise.resolve().then(function() { - return getBinary(wasmBinaryFile) - }) - } - - function createWasm() { - var info = { - "a": asmLibraryArg - }; - - function receiveInstance(instance, module) { - var exports = instance.exports; - Module["asm"] = exports; - wasmMemory = Module["asm"]["q"]; - updateGlobalBufferAndViews(wasmMemory.buffer); - wasmTable = Module["asm"]["D"]; - removeRunDependency("wasm-instantiate") - } - addRunDependency("wasm-instantiate"); - - function receiveInstantiatedSource(output) { - receiveInstance(output["instance"]) - } - - function instantiateArrayBuffer(receiver) { - return getBinaryPromise().then(function(binary) { - return WebAssembly.instantiate(binary, info) - }).then(receiver, function(reason) { - if (createTFLiteSIMDModule.simd){ - wasmBinaryFile = "tflite.wasm"; - createTFLiteSIMDModule.simd = false; - if (!isDataURI(wasmBinaryFile)) { - wasmBinaryFile = locateFile(wasmBinaryFile) - } - instantiateAsync(); - } else { - console.error("FAILED LOADING WASM TFLITE MODEL"); - } - }) - } - - function instantiateAsync() { - return instantiateArrayBuffer(receiveInstantiatedSource) - } - if (Module["instantiateWasm"]) { - try { - var exports = Module["instantiateWasm"](info, receiveInstance); - return exports - } catch (e) { - err("Module.instantiateWasm callback failed with error: " + e); - return false - } - } - instantiateAsync().catch(readyPromiseReject); - return {} - } - - function callRuntimeCallbacks(callbacks) { - while (callbacks.length > 0) { - var callback = callbacks.shift(); - if (typeof callback == "function") { - callback(Module); - continue - } - var func = callback.func; - if (typeof func === "number") { - if (callback.arg === undefined) { - wasmTable.get(func)() - } else { - wasmTable.get(func)(callback.arg) - } - } else { - func(callback.arg === undefined ? null : callback.arg) - } - } - } - - function _abort() { - abort() - } - var _emscripten_get_now; - if (typeof dateNow !== "undefined") { - _emscripten_get_now = dateNow - } else _emscripten_get_now = function() { - return performance.now() - }; - var _emscripten_get_now_is_monotonic = true; - - function setErrNo(value) { - HEAP32[___errno_location() >> 2] = value; - return value - } - - function _clock_gettime(clk_id, tp) { - var now; - if (clk_id === 0) { - now = Date.now() - } else if ((clk_id === 1 || clk_id === 4) && _emscripten_get_now_is_monotonic) { - now = _emscripten_get_now() - } else { - setErrNo(28); - return -1 - } - HEAP32[tp >> 2] = now / 1e3 | 0; - HEAP32[tp + 4 >> 2] = now % 1e3 * 1e3 * 1e3 | 0; - return 0 - } - - function _dlopen(filename, flag) { - abort("To use dlopen, you need to use Emscripten's linking support, see https://github.com/emscripten-core/emscripten/wiki/Linking") - } - - function _dlsym(handle, symbol) { - abort("To use dlopen, you need to use Emscripten's linking support, see https://github.com/emscripten-core/emscripten/wiki/Linking") - } - - function _emscripten_memcpy_big(dest, src, num) { - HEAPU8.copyWithin(dest, src, src + num) - } - - function _emscripten_get_heap_size() { - return HEAPU8.length - } - - function emscripten_realloc_buffer(size) { - try { - wasmMemory.grow(size - buffer.byteLength + 65535 >>> 16); - updateGlobalBufferAndViews(wasmMemory.buffer); - return 1 - } catch (e) {} - } - - function _emscripten_resize_heap(requestedSize) { - requestedSize = requestedSize >>> 0; - var oldSize = _emscripten_get_heap_size(); - var maxHeapSize = 2147483648; - if (requestedSize > maxHeapSize) { - return false - } - var minHeapSize = 16777216; - for (var cutDown = 1; cutDown <= 4; cutDown *= 2) { - var overGrownHeapSize = oldSize * (1 + .2 / cutDown); - overGrownHeapSize = Math.min(overGrownHeapSize, requestedSize + 100663296); - var newSize = Math.min(maxHeapSize, alignUp(Math.max(minHeapSize, requestedSize, overGrownHeapSize), 65536)); - var replacement = emscripten_realloc_buffer(newSize); - if (replacement) { - return true - } - } - return false - } - - function _emscripten_thread_sleep(msecs) { - var start = _emscripten_get_now(); - while (_emscripten_get_now() - start < msecs) {} - } - var ENV = {}; - - function getExecutableName() { - return thisProgram || "./this.program" - } - - function getEnvStrings() { - if (!getEnvStrings.strings) { - var lang = (typeof navigator === "object" && navigator.languages && navigator.languages[0] || "C").replace("-", "_") + ".UTF-8"; - var env = { - "USER": "web_user", - "LOGNAME": "web_user", - "PATH": "/", - "PWD": "/", - "HOME": "/home/web_user", - "LANG": lang, - "_": getExecutableName() - }; - for (var x in ENV) { - env[x] = ENV[x] - } - var strings = []; - for (var x in env) { - strings.push(x + "=" + env[x]) - } - getEnvStrings.strings = strings - } - return getEnvStrings.strings - } - var SYSCALLS = { - mappings: {}, - buffers: [null, [], - [] - ], - printChar: function(stream, curr) { - var buffer = SYSCALLS.buffers[stream]; - if (curr === 0 || curr === 10) { - (stream === 1 ? out : err)(UTF8ArrayToString(buffer, 0)); - buffer.length = 0 - } else { - buffer.push(curr) - } - }, - varargs: undefined, - get: function() { - SYSCALLS.varargs += 4; - var ret = HEAP32[SYSCALLS.varargs - 4 >> 2]; - return ret - }, - getStr: function(ptr) { - var ret = UTF8ToString(ptr); - return ret - }, - get64: function(low, high) { - return low - } - }; - - function _environ_get(__environ, environ_buf) { - var bufSize = 0; - getEnvStrings().forEach(function(string, i) { - var ptr = environ_buf + bufSize; - HEAP32[__environ + i * 4 >> 2] = ptr; - writeAsciiToMemory(string, ptr); - bufSize += string.length + 1 - }); - return 0 - } - - function _environ_sizes_get(penviron_count, penviron_buf_size) { - var strings = getEnvStrings(); - HEAP32[penviron_count >> 2] = strings.length; - var bufSize = 0; - strings.forEach(function(string) { - bufSize += string.length + 1 - }); - HEAP32[penviron_buf_size >> 2] = bufSize; - return 0 - } - - function _exit(status) { - exit(status) - } - - function _fd_close(fd) { - return 0 - } - - function _fd_seek(fd, offset_low, offset_high, whence, newOffset) {} - - function _fd_write(fd, iov, iovcnt, pnum) { - var num = 0; - for (var i = 0; i < iovcnt; i++) { - var ptr = HEAP32[iov + i * 8 >> 2]; - var len = HEAP32[iov + (i * 8 + 4) >> 2]; - for (var j = 0; j < len; j++) { - SYSCALLS.printChar(fd, HEAPU8[ptr + j]) - } - num += len - } - HEAP32[pnum >> 2] = num; - return 0 - } - - function _pthread_create() { - return 6 - } - - function _pthread_join() { - return 28 - } - - function _sysconf(name) { - switch (name) { - case 30: - return 16384; - case 85: - var maxHeapSize = 2147483648; - return maxHeapSize / 16384; - case 132: - case 133: - case 12: - case 137: - case 138: - case 15: - case 235: - case 16: - case 17: - case 18: - case 19: - case 20: - case 149: - case 13: - case 10: - case 236: - case 153: - case 9: - case 21: - case 22: - case 159: - case 154: - case 14: - case 77: - case 78: - case 139: - case 82: - case 68: - case 67: - case 164: - case 11: - case 29: - case 47: - case 48: - case 95: - case 52: - case 51: - case 46: - return 200809; - case 27: - case 246: - case 127: - case 128: - case 23: - case 24: - case 160: - case 161: - case 181: - case 182: - case 242: - case 183: - case 184: - case 243: - case 244: - case 245: - case 165: - case 178: - case 179: - case 49: - case 50: - case 168: - case 169: - case 175: - case 170: - case 171: - case 172: - case 97: - case 76: - case 32: - case 173: - case 35: - case 80: - case 81: - case 79: - return -1; - case 176: - case 177: - case 7: - case 155: - case 8: - case 157: - case 125: - case 126: - case 92: - case 93: - case 129: - case 130: - case 131: - case 94: - case 91: - return 1; - case 74: - case 60: - case 69: - case 70: - case 4: - return 1024; - case 31: - case 42: - case 72: - return 32; - case 87: - case 26: - case 33: - return 2147483647; - case 34: - case 1: - return 47839; - case 38: - case 36: - return 99; - case 43: - case 37: - return 2048; - case 0: - return 2097152; - case 3: - return 65536; - case 28: - return 32768; - case 44: - return 32767; - case 75: - return 16384; - case 39: - return 1e3; - case 89: - return 700; - case 71: - return 256; - case 40: - return 255; - case 2: - return 100; - case 180: - return 64; - case 25: - return 20; - case 5: - return 16; - case 6: - return 6; - case 73: - return 4; - case 84: { - if (typeof navigator === "object") return navigator["hardwareConcurrency"] || 1; - return 1 - } - } - setErrNo(28); - return -1 - } - var asmLibraryArg = { - "a": _abort, - "n": _clock_gettime, - "i": _dlopen, - "e": _dlsym, - "l": _emscripten_memcpy_big, - "m": _emscripten_resize_heap, - "o": _emscripten_thread_sleep, - "p": _environ_get, - "g": _environ_sizes_get, - "j": _exit, - "h": _fd_close, - "k": _fd_seek, - "c": _fd_write, - "d": _pthread_create, - "f": _pthread_join, - "b": _sysconf - }; - var asm = createWasm(); - var ___wasm_call_ctors = Module["___wasm_call_ctors"] = function() { - return (___wasm_call_ctors = Module["___wasm_call_ctors"] = Module["asm"]["r"]).apply(null, arguments) - }; - var _getModelBufferMemoryOffset = Module["_getModelBufferMemoryOffset"] = function() { - return (_getModelBufferMemoryOffset = Module["_getModelBufferMemoryOffset"] = Module["asm"]["s"]).apply(null, arguments) - }; - var _getInputMemoryOffset = Module["_getInputMemoryOffset"] = function() { - return (_getInputMemoryOffset = Module["_getInputMemoryOffset"] = Module["asm"]["t"]).apply(null, arguments) - }; - var _getInputHeight = Module["_getInputHeight"] = function() { - return (_getInputHeight = Module["_getInputHeight"] = Module["asm"]["u"]).apply(null, arguments) - }; - var _getInputWidth = Module["_getInputWidth"] = function() { - return (_getInputWidth = Module["_getInputWidth"] = Module["asm"]["v"]).apply(null, arguments) - }; - var _getInputChannelCount = Module["_getInputChannelCount"] = function() { - return (_getInputChannelCount = Module["_getInputChannelCount"] = Module["asm"]["w"]).apply(null, arguments) - }; - var _getOutputMemoryOffset = Module["_getOutputMemoryOffset"] = function() { - return (_getOutputMemoryOffset = Module["_getOutputMemoryOffset"] = Module["asm"]["x"]).apply(null, arguments) - }; - var _getOutputHeight = Module["_getOutputHeight"] = function() { - return (_getOutputHeight = Module["_getOutputHeight"] = Module["asm"]["y"]).apply(null, arguments) - }; - var _getOutputWidth = Module["_getOutputWidth"] = function() { - return (_getOutputWidth = Module["_getOutputWidth"] = Module["asm"]["z"]).apply(null, arguments) - }; - var _getOutputChannelCount = Module["_getOutputChannelCount"] = function() { - return (_getOutputChannelCount = Module["_getOutputChannelCount"] = Module["asm"]["A"]).apply(null, arguments) - }; - var _loadModel = Module["_loadModel"] = function() { - return (_loadModel = Module["_loadModel"] = Module["asm"]["B"]).apply(null, arguments) - }; - var _runInference = Module["_runInference"] = function() { - return (_runInference = Module["_runInference"] = Module["asm"]["C"]).apply(null, arguments) - }; - var ___errno_location = Module["___errno_location"] = function() { - return (___errno_location = Module["___errno_location"] = Module["asm"]["E"]).apply(null, arguments) - }; - var calledRun; - - function ExitStatus(status) { - this.name = "ExitStatus"; - this.message = "Program terminated with exit(" + status + ")"; - this.status = status - } - dependenciesFulfilled = function runCaller() { - if (!calledRun) run(); - if (!calledRun) dependenciesFulfilled = runCaller - }; - - function run(args) { - args = args || arguments_; - if (runDependencies > 0) { - return - } - preRun(); - if (runDependencies > 0) { - return - } - - function doRun() { - if (calledRun) return; - calledRun = true; - Module["calledRun"] = true; - if (ABORT) return; - initRuntime(); - preMain(); - readyPromiseResolve(Module); - if (Module["onRuntimeInitialized"]) Module["onRuntimeInitialized"](); - postRun() - } - if (Module["setStatus"]) { - Module["setStatus"]("Running..."); - setTimeout(function() { - setTimeout(function() { - Module["setStatus"]("") - }, 1); - doRun() - }, 1) - } else { - doRun() - } - } - Module["run"] = run; - - function exit(status, implicit) { - if (implicit && noExitRuntime && status === 0) { - return - } - if (noExitRuntime) {} else { - EXITSTATUS = status; - exitRuntime(); - if (Module["onExit"]) Module["onExit"](status); - ABORT = true - } - quit_(status, new ExitStatus(status)) - } - if (Module["preInit"]) { - if (typeof Module["preInit"] == "function") Module["preInit"] = [Module["preInit"]]; - while (Module["preInit"].length > 0) { - Module["preInit"].pop()() - } - } - noExitRuntime = true; - run(); - - - return createTFLiteSIMDModule.ready - } - ); -})(); -if (typeof exports === 'object' && typeof module === 'object') - module.exports = createTFLiteSIMDModule; -else if (typeof define === 'function' && define['amd']) - define([], function() { - return createTFLiteSIMDModule; - }); -else if (typeof exports === 'object') +var createTFLiteSIMDModule = (function() { + var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; + return ( + function(createTFLiteSIMDModule) { + createTFLiteSIMDModule = createTFLiteSIMDModule || {}; + + var Module = typeof createTFLiteSIMDModule !== "undefined" ? createTFLiteSIMDModule : {}; + var readyPromiseResolve, readyPromiseReject; + Module["ready"] = new Promise(function(resolve, reject) { + readyPromiseResolve = resolve; + readyPromiseReject = reject + }); + var moduleOverrides = {}; + var key; + for (key in Module) { + if (Module.hasOwnProperty(key)) { + moduleOverrides[key] = Module[key] + } + } + var arguments_ = []; + var thisProgram = "./this.program"; + var quit_ = function(status, toThrow) { + throw toThrow + }; + + var scriptDirectory = ""; + + function locateFile(path) { + if (Module["locateFile"]) { + return Module["locateFile"](path, scriptDirectory) + } + return scriptDirectory + path + } + var read_, readAsync, readBinary; + + if (typeof document !== "undefined" && document.currentScript) { + scriptDirectory = document.currentScript.src + } + + if (_scriptDir) { + scriptDirectory = _scriptDir + } + if (scriptDirectory.indexOf("blob:") !== 0) { + scriptDirectory = scriptDirectory.substr(0, scriptDirectory.lastIndexOf("/") + 1) + } else { + scriptDirectory = "" + } { + read_ = function(url) { + var xhr = new XMLHttpRequest; + xhr.open("GET", url, false); + xhr.send(null); + return xhr.responseText + }; + + readAsync = function(url, onload, onerror) { + var xhr = new XMLHttpRequest; + xhr.open("GET", url, true); + xhr.responseType = "arraybuffer"; + xhr.onload = function() { + if (xhr.status == 200 || xhr.status == 0 && xhr.response) { + onload(xhr.response); + return + } + onerror() + }; + xhr.onerror = onerror; + xhr.send(null) + } + } + + var out = Module["print"] || console.log.bind(console); + var err = Module["printErr"] || console.warn.bind(console); + for (key in moduleOverrides) { + if (moduleOverrides.hasOwnProperty(key)) { + Module[key] = moduleOverrides[key] + } + } + moduleOverrides = null; + if (Module["arguments"]) arguments_ = Module["arguments"]; + if (Module["thisProgram"]) thisProgram = Module["thisProgram"]; + if (Module["quit"]) quit_ = Module["quit"]; + var wasmBinary; + if (Module["wasmBinary"]) wasmBinary = Module["wasmBinary"]; + var noExitRuntime; + if (Module["noExitRuntime"]) noExitRuntime = Module["noExitRuntime"]; + if (typeof WebAssembly !== "object") { + abort("no native wasm support detected") + } + var wasmMemory; + var ABORT = false; + var EXITSTATUS; + + function assert(condition, text) { + if (!condition) { + abort("Assertion failed: " + text) + } + } + var UTF8Decoder = typeof TextDecoder !== "undefined" ? new TextDecoder("utf8") : undefined; + + function UTF8ArrayToString(heap, idx, maxBytesToRead) { + var endIdx = idx + maxBytesToRead; + var endPtr = idx; + while (heap[endPtr] && !(endPtr >= endIdx)) ++endPtr; + if (endPtr - idx > 16 && heap.subarray && UTF8Decoder) { + return UTF8Decoder.decode(heap.subarray(idx, endPtr)) + } else { + var str = ""; + while (idx < endPtr) { + var u0 = heap[idx++]; + if (!(u0 & 128)) { + str += String.fromCharCode(u0); + continue + } + var u1 = heap[idx++] & 63; + if ((u0 & 224) == 192) { + str += String.fromCharCode((u0 & 31) << 6 | u1); + continue + } + var u2 = heap[idx++] & 63; + if ((u0 & 240) == 224) { + u0 = (u0 & 15) << 12 | u1 << 6 | u2 + } else { + u0 = (u0 & 7) << 18 | u1 << 12 | u2 << 6 | heap[idx++] & 63 + } + if (u0 < 65536) { + str += String.fromCharCode(u0) + } else { + var ch = u0 - 65536; + str += String.fromCharCode(55296 | ch >> 10, 56320 | ch & 1023) + } + } + } + return str + } + + function UTF8ToString(ptr, maxBytesToRead) { + return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : "" + } + + function writeAsciiToMemory(str, buffer, dontAddNull) { + for (var i = 0; i < str.length; ++i) { + HEAP8[buffer++ >> 0] = str.charCodeAt(i) + } + if (!dontAddNull) HEAP8[buffer >> 0] = 0 + } + + function alignUp(x, multiple) { + if (x % multiple > 0) { + x += multiple - x % multiple + } + return x + } + var buffer, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64; + + function updateGlobalBufferAndViews(buf) { + buffer = buf; + Module["HEAP8"] = HEAP8 = new Int8Array(buf); + Module["HEAP16"] = HEAP16 = new Int16Array(buf); + Module["HEAP32"] = HEAP32 = new Int32Array(buf); + Module["HEAPU8"] = HEAPU8 = new Uint8Array(buf); + Module["HEAPU16"] = HEAPU16 = new Uint16Array(buf); + Module["HEAPU32"] = HEAPU32 = new Uint32Array(buf); + Module["HEAPF32"] = HEAPF32 = new Float32Array(buf); + Module["HEAPF64"] = HEAPF64 = new Float64Array(buf) + } + var INITIAL_MEMORY = Module["INITIAL_MEMORY"] || 16777216; + var wasmTable; + var __ATPRERUN__ = []; + var __ATINIT__ = []; + var __ATMAIN__ = []; + var __ATPOSTRUN__ = []; + var runtimeInitialized = false; + var runtimeExited = false; + __ATINIT__.push({ + func: function() { + ___wasm_call_ctors() + } + }); + + function preRun() { + if (Module["preRun"]) { + if (typeof Module["preRun"] == "function") Module["preRun"] = [Module["preRun"]]; + while (Module["preRun"].length) { + addOnPreRun(Module["preRun"].shift()) + } + } + callRuntimeCallbacks(__ATPRERUN__) + } + + function initRuntime() { + runtimeInitialized = true; + callRuntimeCallbacks(__ATINIT__) + } + + function preMain() { + callRuntimeCallbacks(__ATMAIN__) + } + + function exitRuntime() { + runtimeExited = true + } + + function postRun() { + if (Module["postRun"]) { + if (typeof Module["postRun"] == "function") Module["postRun"] = [Module["postRun"]]; + while (Module["postRun"].length) { + addOnPostRun(Module["postRun"].shift()) + } + } + callRuntimeCallbacks(__ATPOSTRUN__) + } + + function addOnPreRun(cb) { + __ATPRERUN__.unshift(cb) + } + + function addOnPostRun(cb) { + __ATPOSTRUN__.unshift(cb) + } + var runDependencies = 0; + var runDependencyWatcher = null; + var dependenciesFulfilled = null; + + function addRunDependency(id) { + runDependencies++; + if (Module["monitorRunDependencies"]) { + Module["monitorRunDependencies"](runDependencies) + } + } + + function removeRunDependency(id) { + runDependencies--; + if (Module["monitorRunDependencies"]) { + Module["monitorRunDependencies"](runDependencies) + } + if (runDependencies == 0) { + if (runDependencyWatcher !== null) { + clearInterval(runDependencyWatcher); + runDependencyWatcher = null + } + if (dependenciesFulfilled) { + var callback = dependenciesFulfilled; + dependenciesFulfilled = null; + callback() + } + } + } + Module["preloadedImages"] = {}; + Module["preloadedAudios"] = {}; + + function abort(what) { + if (Module["onAbort"]) { + Module["onAbort"](what) + } + what += ""; + err(what); + ABORT = true; + EXITSTATUS = 1; + what = "abort(" + what + "). Build with -s ASSERTIONS=1 for more info."; + var e = new WebAssembly.RuntimeError(what); + readyPromiseReject(e); + throw e + } + + function hasPrefix(str, prefix) { + return String.prototype.startsWith ? str.startsWith(prefix) : str.indexOf(prefix) === 0 + } + var dataURIPrefix = "data:application/octet-stream;base64,"; + + function isDataURI(filename) { + return hasPrefix(filename, dataURIPrefix) + } + var fileURIPrefix = "file://"; + + function isFileURI(filename) { + return hasPrefix(filename, fileURIPrefix) + } + var wasmBinaryFile = "tflite-simd.wasm"; + createTFLiteSIMDModule.simd = true; + + if (!isDataURI(wasmBinaryFile)) { + wasmBinaryFile = locateFile(wasmBinaryFile) + } + + function getBinary(file) { + try { + if (file == wasmBinaryFile && wasmBinary) { + return new Uint8Array(wasmBinary) + } + if (readBinary) { + return readBinary(file) + } else { + throw "both async and sync fetching of the wasm failed" + } + } catch (err) { + abort(err) + } + } + + function getBinaryPromise() { + if (!wasmBinary) { + if (typeof fetch === "function" && !isFileURI(wasmBinaryFile)) { + return fetch(wasmBinaryFile, { + credentials: "same-origin" + }).then(function(response) { + if (!response["ok"]) { + throw "failed to load wasm binary file at '" + wasmBinaryFile + "'" + } + return response["arrayBuffer"]() + }).catch(function() { + return getBinary(wasmBinaryFile) + }) + } else { + if (readAsync) { + return new Promise(function(resolve, reject) { + readAsync(wasmBinaryFile, function(response) { + resolve(new Uint8Array(response)) + }, reject) + }) + } + } + } + return Promise.resolve().then(function() { + return getBinary(wasmBinaryFile) + }) + } + + function createWasm() { + var info = { + "a": asmLibraryArg + }; + + function receiveInstance(instance, module) { + var exports = instance.exports; + Module["asm"] = exports; + wasmMemory = Module["asm"]["q"]; + updateGlobalBufferAndViews(wasmMemory.buffer); + wasmTable = Module["asm"]["D"]; + removeRunDependency("wasm-instantiate") + } + addRunDependency("wasm-instantiate"); + + function receiveInstantiatedSource(output) { + receiveInstance(output["instance"]) + } + + function instantiateArrayBuffer(receiver) { + return getBinaryPromise().then(function(binary) { + return WebAssembly.instantiate(binary, info) + }).then(receiver, function(reason) { + if (createTFLiteSIMDModule.simd){ + wasmBinaryFile = "tflite.wasm"; + createTFLiteSIMDModule.simd = false; + if (!isDataURI(wasmBinaryFile)) { + wasmBinaryFile = locateFile(wasmBinaryFile) + } + instantiateAsync(); + } else { + console.error("FAILED LOADING WASM TFLITE MODEL"); + } + }) + } + + function instantiateAsync() { + return instantiateArrayBuffer(receiveInstantiatedSource) + } + if (Module["instantiateWasm"]) { + try { + var exports = Module["instantiateWasm"](info, receiveInstance); + return exports + } catch (e) { + err("Module.instantiateWasm callback failed with error: " + e); + return false + } + } + instantiateAsync().catch(readyPromiseReject); + return {} + } + + function callRuntimeCallbacks(callbacks) { + while (callbacks.length > 0) { + var callback = callbacks.shift(); + if (typeof callback == "function") { + callback(Module); + continue + } + var func = callback.func; + if (typeof func === "number") { + if (callback.arg === undefined) { + wasmTable.get(func)() + } else { + wasmTable.get(func)(callback.arg) + } + } else { + func(callback.arg === undefined ? null : callback.arg) + } + } + } + + function _abort() { + abort() + } + var _emscripten_get_now; + if (typeof dateNow !== "undefined") { + _emscripten_get_now = dateNow + } else _emscripten_get_now = function() { + return performance.now() + }; + var _emscripten_get_now_is_monotonic = true; + + function setErrNo(value) { + HEAP32[___errno_location() >> 2] = value; + return value + } + + function _clock_gettime(clk_id, tp) { + var now; + if (clk_id === 0) { + now = Date.now() + } else if ((clk_id === 1 || clk_id === 4) && _emscripten_get_now_is_monotonic) { + now = _emscripten_get_now() + } else { + setErrNo(28); + return -1 + } + HEAP32[tp >> 2] = now / 1e3 | 0; + HEAP32[tp + 4 >> 2] = now % 1e3 * 1e3 * 1e3 | 0; + return 0 + } + + function _dlopen(filename, flag) { + abort("To use dlopen, you need to use Emscripten's linking support, see https://github.com/emscripten-core/emscripten/wiki/Linking") + } + + function _dlsym(handle, symbol) { + abort("To use dlopen, you need to use Emscripten's linking support, see https://github.com/emscripten-core/emscripten/wiki/Linking") + } + + function _emscripten_memcpy_big(dest, src, num) { + HEAPU8.copyWithin(dest, src, src + num) + } + + function _emscripten_get_heap_size() { + return HEAPU8.length + } + + function emscripten_realloc_buffer(size) { + try { + wasmMemory.grow(size - buffer.byteLength + 65535 >>> 16); + updateGlobalBufferAndViews(wasmMemory.buffer); + return 1 + } catch (e) {} + } + + function _emscripten_resize_heap(requestedSize) { + requestedSize = requestedSize >>> 0; + var oldSize = _emscripten_get_heap_size(); + var maxHeapSize = 2147483648; + if (requestedSize > maxHeapSize) { + return false + } + var minHeapSize = 16777216; + for (var cutDown = 1; cutDown <= 4; cutDown *= 2) { + var overGrownHeapSize = oldSize * (1 + .2 / cutDown); + overGrownHeapSize = Math.min(overGrownHeapSize, requestedSize + 100663296); + var newSize = Math.min(maxHeapSize, alignUp(Math.max(minHeapSize, requestedSize, overGrownHeapSize), 65536)); + var replacement = emscripten_realloc_buffer(newSize); + if (replacement) { + return true + } + } + return false + } + + function _emscripten_thread_sleep(msecs) { + var start = _emscripten_get_now(); + while (_emscripten_get_now() - start < msecs) {} + } + var ENV = {}; + + function getExecutableName() { + return thisProgram || "./this.program" + } + + function getEnvStrings() { + if (!getEnvStrings.strings) { + var lang = (typeof navigator === "object" && navigator.languages && navigator.languages[0] || "C").replace("-", "_") + ".UTF-8"; + var env = { + "USER": "web_user", + "LOGNAME": "web_user", + "PATH": "/", + "PWD": "/", + "HOME": "/home/web_user", + "LANG": lang, + "_": getExecutableName() + }; + for (var x in ENV) { + env[x] = ENV[x] + } + var strings = []; + for (var x in env) { + strings.push(x + "=" + env[x]) + } + getEnvStrings.strings = strings + } + return getEnvStrings.strings + } + var SYSCALLS = { + mappings: {}, + buffers: [null, [], + [] + ], + printChar: function(stream, curr) { + var buffer = SYSCALLS.buffers[stream]; + if (curr === 0 || curr === 10) { + (stream === 1 ? out : err)(UTF8ArrayToString(buffer, 0)); + buffer.length = 0 + } else { + buffer.push(curr) + } + }, + varargs: undefined, + get: function() { + SYSCALLS.varargs += 4; + var ret = HEAP32[SYSCALLS.varargs - 4 >> 2]; + return ret + }, + getStr: function(ptr) { + var ret = UTF8ToString(ptr); + return ret + }, + get64: function(low, high) { + return low + } + }; + + function _environ_get(__environ, environ_buf) { + var bufSize = 0; + getEnvStrings().forEach(function(string, i) { + var ptr = environ_buf + bufSize; + HEAP32[__environ + i * 4 >> 2] = ptr; + writeAsciiToMemory(string, ptr); + bufSize += string.length + 1 + }); + return 0 + } + + function _environ_sizes_get(penviron_count, penviron_buf_size) { + var strings = getEnvStrings(); + HEAP32[penviron_count >> 2] = strings.length; + var bufSize = 0; + strings.forEach(function(string) { + bufSize += string.length + 1 + }); + HEAP32[penviron_buf_size >> 2] = bufSize; + return 0 + } + + function _exit(status) { + exit(status) + } + + function _fd_close(fd) { + return 0 + } + + function _fd_seek(fd, offset_low, offset_high, whence, newOffset) {} + + function _fd_write(fd, iov, iovcnt, pnum) { + var num = 0; + for (var i = 0; i < iovcnt; i++) { + var ptr = HEAP32[iov + i * 8 >> 2]; + var len = HEAP32[iov + (i * 8 + 4) >> 2]; + for (var j = 0; j < len; j++) { + SYSCALLS.printChar(fd, HEAPU8[ptr + j]) + } + num += len + } + HEAP32[pnum >> 2] = num; + return 0 + } + + function _pthread_create() { + return 6 + } + + function _pthread_join() { + return 28 + } + + function _sysconf(name) { + switch (name) { + case 30: + return 16384; + case 85: + var maxHeapSize = 2147483648; + return maxHeapSize / 16384; + case 132: + case 133: + case 12: + case 137: + case 138: + case 15: + case 235: + case 16: + case 17: + case 18: + case 19: + case 20: + case 149: + case 13: + case 10: + case 236: + case 153: + case 9: + case 21: + case 22: + case 159: + case 154: + case 14: + case 77: + case 78: + case 139: + case 82: + case 68: + case 67: + case 164: + case 11: + case 29: + case 47: + case 48: + case 95: + case 52: + case 51: + case 46: + return 200809; + case 27: + case 246: + case 127: + case 128: + case 23: + case 24: + case 160: + case 161: + case 181: + case 182: + case 242: + case 183: + case 184: + case 243: + case 244: + case 245: + case 165: + case 178: + case 179: + case 49: + case 50: + case 168: + case 169: + case 175: + case 170: + case 171: + case 172: + case 97: + case 76: + case 32: + case 173: + case 35: + case 80: + case 81: + case 79: + return -1; + case 176: + case 177: + case 7: + case 155: + case 8: + case 157: + case 125: + case 126: + case 92: + case 93: + case 129: + case 130: + case 131: + case 94: + case 91: + return 1; + case 74: + case 60: + case 69: + case 70: + case 4: + return 1024; + case 31: + case 42: + case 72: + return 32; + case 87: + case 26: + case 33: + return 2147483647; + case 34: + case 1: + return 47839; + case 38: + case 36: + return 99; + case 43: + case 37: + return 2048; + case 0: + return 2097152; + case 3: + return 65536; + case 28: + return 32768; + case 44: + return 32767; + case 75: + return 16384; + case 39: + return 1e3; + case 89: + return 700; + case 71: + return 256; + case 40: + return 255; + case 2: + return 100; + case 180: + return 64; + case 25: + return 20; + case 5: + return 16; + case 6: + return 6; + case 73: + return 4; + case 84: { + if (typeof navigator === "object") return navigator["hardwareConcurrency"] || 1; + return 1 + } + } + setErrNo(28); + return -1 + } + var asmLibraryArg = { + "a": _abort, + "n": _clock_gettime, + "i": _dlopen, + "e": _dlsym, + "l": _emscripten_memcpy_big, + "m": _emscripten_resize_heap, + "o": _emscripten_thread_sleep, + "p": _environ_get, + "g": _environ_sizes_get, + "j": _exit, + "h": _fd_close, + "k": _fd_seek, + "c": _fd_write, + "d": _pthread_create, + "f": _pthread_join, + "b": _sysconf + }; + var asm = createWasm(); + var ___wasm_call_ctors = Module["___wasm_call_ctors"] = function() { + return (___wasm_call_ctors = Module["___wasm_call_ctors"] = Module["asm"]["r"]).apply(null, arguments) + }; + var _getModelBufferMemoryOffset = Module["_getModelBufferMemoryOffset"] = function() { + return (_getModelBufferMemoryOffset = Module["_getModelBufferMemoryOffset"] = Module["asm"]["s"]).apply(null, arguments) + }; + var _getInputMemoryOffset = Module["_getInputMemoryOffset"] = function() { + return (_getInputMemoryOffset = Module["_getInputMemoryOffset"] = Module["asm"]["t"]).apply(null, arguments) + }; + var _getInputHeight = Module["_getInputHeight"] = function() { + return (_getInputHeight = Module["_getInputHeight"] = Module["asm"]["u"]).apply(null, arguments) + }; + var _getInputWidth = Module["_getInputWidth"] = function() { + return (_getInputWidth = Module["_getInputWidth"] = Module["asm"]["v"]).apply(null, arguments) + }; + var _getInputChannelCount = Module["_getInputChannelCount"] = function() { + return (_getInputChannelCount = Module["_getInputChannelCount"] = Module["asm"]["w"]).apply(null, arguments) + }; + var _getOutputMemoryOffset = Module["_getOutputMemoryOffset"] = function() { + return (_getOutputMemoryOffset = Module["_getOutputMemoryOffset"] = Module["asm"]["x"]).apply(null, arguments) + }; + var _getOutputHeight = Module["_getOutputHeight"] = function() { + return (_getOutputHeight = Module["_getOutputHeight"] = Module["asm"]["y"]).apply(null, arguments) + }; + var _getOutputWidth = Module["_getOutputWidth"] = function() { + return (_getOutputWidth = Module["_getOutputWidth"] = Module["asm"]["z"]).apply(null, arguments) + }; + var _getOutputChannelCount = Module["_getOutputChannelCount"] = function() { + return (_getOutputChannelCount = Module["_getOutputChannelCount"] = Module["asm"]["A"]).apply(null, arguments) + }; + var _loadModel = Module["_loadModel"] = function() { + return (_loadModel = Module["_loadModel"] = Module["asm"]["B"]).apply(null, arguments) + }; + var _runInference = Module["_runInference"] = function() { + return (_runInference = Module["_runInference"] = Module["asm"]["C"]).apply(null, arguments) + }; + var ___errno_location = Module["___errno_location"] = function() { + return (___errno_location = Module["___errno_location"] = Module["asm"]["E"]).apply(null, arguments) + }; + var calledRun; + + function ExitStatus(status) { + this.name = "ExitStatus"; + this.message = "Program terminated with exit(" + status + ")"; + this.status = status + } + dependenciesFulfilled = function runCaller() { + if (!calledRun) run(); + if (!calledRun) dependenciesFulfilled = runCaller + }; + + function run(args) { + args = args || arguments_; + if (runDependencies > 0) { + return + } + preRun(); + if (runDependencies > 0) { + return + } + + function doRun() { + if (calledRun) return; + calledRun = true; + Module["calledRun"] = true; + if (ABORT) return; + initRuntime(); + preMain(); + readyPromiseResolve(Module); + if (Module["onRuntimeInitialized"]) Module["onRuntimeInitialized"](); + postRun() + } + if (Module["setStatus"]) { + Module["setStatus"]("Running..."); + setTimeout(function() { + setTimeout(function() { + Module["setStatus"]("") + }, 1); + doRun() + }, 1) + } else { + doRun() + } + } + Module["run"] = run; + + function exit(status, implicit) { + if (implicit && noExitRuntime && status === 0) { + return + } + if (noExitRuntime) {} else { + EXITSTATUS = status; + exitRuntime(); + if (Module["onExit"]) Module["onExit"](status); + ABORT = true + } + quit_(status, new ExitStatus(status)) + } + if (Module["preInit"]) { + if (typeof Module["preInit"] == "function") Module["preInit"] = [Module["preInit"]]; + while (Module["preInit"].length > 0) { + Module["preInit"].pop()() + } + } + noExitRuntime = true; + run(); + + + return createTFLiteSIMDModule.ready + } + ); +})(); +if (typeof exports === 'object' && typeof module === 'object') + module.exports = createTFLiteSIMDModule; +else if (typeof define === 'function' && define['amd']) + define([], function() { + return createTFLiteSIMDModule; + }); +else if (typeof exports === 'object') exports["createTFLiteSIMDModule"] = createTFLiteSIMDModule; \ No newline at end of file diff --git a/thirdparty/tflite/tflite-simd.wasm b/app/static/thirdparty/tflite/tflite-simd.wasm similarity index 100% rename from thirdparty/tflite/tflite-simd.wasm rename to app/static/thirdparty/tflite/tflite-simd.wasm diff --git a/thirdparty/tflite/tflite.wasm b/app/static/thirdparty/tflite/tflite.wasm similarity index 100% rename from thirdparty/tflite/tflite.wasm rename to app/static/thirdparty/tflite/tflite.wasm diff --git a/thirdparty/webmidi.js b/app/static/thirdparty/webmidi.js similarity index 100% rename from thirdparty/webmidi.js rename to app/static/thirdparty/webmidi.js diff --git a/thirdparty/webmidi3.js b/app/static/thirdparty/webmidi3.js similarity index 100% rename from thirdparty/webmidi3.js rename to app/static/thirdparty/webmidi3.js diff --git a/translations/blank.json b/app/static/translations/blank.json similarity index 100% rename from translations/blank.json rename to app/static/translations/blank.json diff --git a/translations/cn.json b/app/static/translations/cn.json similarity index 100% rename from translations/cn.json rename to app/static/translations/cn.json diff --git a/translations/cs.json b/app/static/translations/cs.json similarity index 100% rename from translations/cs.json rename to app/static/translations/cs.json diff --git a/translations/de.json b/app/static/translations/de.json similarity index 100% rename from translations/de.json rename to app/static/translations/de.json diff --git a/translations/en.json b/app/static/translations/en.json similarity index 100% rename from translations/en.json rename to app/static/translations/en.json diff --git a/translations/es.json b/app/static/translations/es.json similarity index 100% rename from translations/es.json rename to app/static/translations/es.json diff --git a/translations/eu.json b/app/static/translations/eu.json similarity index 100% rename from translations/eu.json rename to app/static/translations/eu.json diff --git a/translations/fr.json b/app/static/translations/fr.json similarity index 100% rename from translations/fr.json rename to app/static/translations/fr.json diff --git a/translations/it.json b/app/static/translations/it.json similarity index 100% rename from translations/it.json rename to app/static/translations/it.json diff --git a/translations/ja.json b/app/static/translations/ja.json similarity index 100% rename from translations/ja.json rename to app/static/translations/ja.json diff --git a/translations/makepig.js b/app/static/translations/makepig.js similarity index 97% rename from translations/makepig.js rename to app/static/translations/makepig.js index ea7dde0..5d492ef 100644 --- a/translations/makepig.js +++ b/app/static/translations/makepig.js @@ -1,74 +1,74 @@ -var trans = { - "titles": { - "join-by-room-name-here": "Enter a room name to quick join", - "join-room": "Join room", - "enable-the-chrome-experimental-features-flag-to-use-chrome-flags-enable-experimental-web-platform-features": "Enable the Chrome experimental features flag to use: chrome://flags/#enable-experimental-web-platform-features" - }, - "innerHTML": { - "mute-guest": "Mute Guest", - "logo-header": "\nVDO.Ninja \n", - "start": "START", - "update-your-device": "We've detected that you are using an old version of Apple iOS, which is known to have many issues.

        Please consider updating.", - "publish-via-whip": "Publish via WHIP", - "share-whepsrc": "Share via WHEP", - "enter-the-whep-URL-you-wish-to-share": "Enter the WHEP URL you wish to share." - }, - "placeholders": { - "join-by-room-name-here": "Join by Room Name here", - "enter-chat-message-to-send-here": "Enter chat message to send here", - "enter-your-message-here": "Enter your message here", - "-whip-url-to-publish-to-goes-here": "➡️ WHIP URL to publish to goes here", - "-authentication-bearer-token-optional-": "🗝️ Authentication Bearer Token (optional)" - }, - "miscellaneous": { - "start": "START", - "new-display-name": "Enter a new Display Name for this stream", - "this-is-you": "This is you, a co-director.
        You are also a performer.", - "preview-meshcast-disabled": "You can't adjust the preview bitrate for Meshcast-based streams" - } -}; - -function getAllContentNodes(element) { // takes an element. - if (!element.childNodes || !element.childNodes.length){ - element.textContent = pigLatin(element.textContent) || ""; - } - element.childNodes.forEach(node=>{ - if (node.childNodes.length){ - getAllContentNodes(node) - } else if ((node.nodeType === 3) && (node.textContent.trim().length > 0)){ - node.textContent = pigLatin(node.textContent) || ""; - } - }); -} - -function pigLatin(input) { // should be safe for line breaks, etc. - var vowels = ["a", "e", "i", "o", "u"]; - var translated = ""; - var cluster = ""; - if (vowels.includes(input[0])){ - translated = input + "way"; - } else { - for (var i = 0; i < input.length; i++) { - if (!vowels.includes(input[i])){ - cluster += input[i]; - } else { - translated = input.substring(i) + cluster + "ay"; - break; - } - translated = input + "ay"; - } - } - return translated; -} - -var xx = document.createElement("span"); - -Object.keys(trans).forEach(main=>{ - Object.keys(trans[main]).forEach(key=>{ - xx.innerHTML = trans[main][key]; - getAllContentNodes(xx); - trans[main][key] = xx.innerHTML; - }); -}); - +var trans = { + "titles": { + "join-by-room-name-here": "Enter a room name to quick join", + "join-room": "Join room", + "enable-the-chrome-experimental-features-flag-to-use-chrome-flags-enable-experimental-web-platform-features": "Enable the Chrome experimental features flag to use: chrome://flags/#enable-experimental-web-platform-features" + }, + "innerHTML": { + "mute-guest": "Mute Guest", + "logo-header": "\nVDO.Ninja \n", + "start": "START", + "update-your-device": "We've detected that you are using an old version of Apple iOS, which is known to have many issues.

        Please consider updating.", + "publish-via-whip": "Publish via WHIP", + "share-whepsrc": "Share via WHEP", + "enter-the-whep-URL-you-wish-to-share": "Enter the WHEP URL you wish to share." + }, + "placeholders": { + "join-by-room-name-here": "Join by Room Name here", + "enter-chat-message-to-send-here": "Enter chat message to send here", + "enter-your-message-here": "Enter your message here", + "-whip-url-to-publish-to-goes-here": "➡️ WHIP URL to publish to goes here", + "-authentication-bearer-token-optional-": "🗝️ Authentication Bearer Token (optional)" + }, + "miscellaneous": { + "start": "START", + "new-display-name": "Enter a new Display Name for this stream", + "this-is-you": "This is you, a co-director.
        You are also a performer.", + "preview-meshcast-disabled": "You can't adjust the preview bitrate for Meshcast-based streams" + } +}; + +function getAllContentNodes(element) { // takes an element. + if (!element.childNodes || !element.childNodes.length){ + element.textContent = pigLatin(element.textContent) || ""; + } + element.childNodes.forEach(node=>{ + if (node.childNodes.length){ + getAllContentNodes(node) + } else if ((node.nodeType === 3) && (node.textContent.trim().length > 0)){ + node.textContent = pigLatin(node.textContent) || ""; + } + }); +} + +function pigLatin(input) { // should be safe for line breaks, etc. + var vowels = ["a", "e", "i", "o", "u"]; + var translated = ""; + var cluster = ""; + if (vowels.includes(input[0])){ + translated = input + "way"; + } else { + for (var i = 0; i < input.length; i++) { + if (!vowels.includes(input[i])){ + cluster += input[i]; + } else { + translated = input.substring(i) + cluster + "ay"; + break; + } + translated = input + "ay"; + } + } + return translated; +} + +var xx = document.createElement("span"); + +Object.keys(trans).forEach(main=>{ + Object.keys(trans[main]).forEach(key=>{ + xx.innerHTML = trans[main][key]; + getAllContentNodes(xx); + trans[main][key] = xx.innerHTML; + }); +}); + console.log(JSON.stringify(trans)); // clean up and use. \ No newline at end of file diff --git a/translations/nl.json b/app/static/translations/nl.json similarity index 100% rename from translations/nl.json rename to app/static/translations/nl.json diff --git a/translations/pig.json b/app/static/translations/pig.json similarity index 100% rename from translations/pig.json rename to app/static/translations/pig.json diff --git a/translations/pt-br.json b/app/static/translations/pt-br.json similarity index 100% rename from translations/pt-br.json rename to app/static/translations/pt-br.json diff --git a/translations/pt.json b/app/static/translations/pt.json similarity index 100% rename from translations/pt.json rename to app/static/translations/pt.json diff --git a/translations/readme.md b/app/static/translations/readme.md similarity index 98% rename from translations/readme.md rename to app/static/translations/readme.md index 734f90e..e9d9f0e 100644 --- a/translations/readme.md +++ b/app/static/translations/readme.md @@ -1,18 +1,18 @@ -Welcome to the translation / language section of VDO.Ninja - -You can specify a translation using this code, if deploying the code yourself. blank can be replaced with ru, en, pt, etc.. -``` - -``` - -You can also add &ln=ru to the URL as a parameter to launch the translation that way. - -There is a file called translate.js, which if you copy/paste the content (and hit enter) into the Chrome browser's console, while on VDO.Ninja, it will download the translation files. -It will add any new translation entries and add them to the bottom of the files. Please feel to correct these new translations and upload them back to github as a pull request. - -In the future I will add a Github action so that this automatically occurs with new code posting. - -Translation files can contain more than just language; they can contain HTML and image links, etc. This offers a basic amount of customization. - -the "blank.json" file contains a minimal template. Little to no VDO.Ninja branding. - +Welcome to the translation / language section of VDO.Ninja + +You can specify a translation using this code, if deploying the code yourself. blank can be replaced with ru, en, pt, etc.. +``` + +``` + +You can also add &ln=ru to the URL as a parameter to launch the translation that way. + +There is a file called translate.js, which if you copy/paste the content (and hit enter) into the Chrome browser's console, while on VDO.Ninja, it will download the translation files. +It will add any new translation entries and add them to the bottom of the files. Please feel to correct these new translations and upload them back to github as a pull request. + +In the future I will add a Github action so that this automatically occurs with new code posting. + +Translation files can contain more than just language; they can contain HTML and image links, etc. This offers a basic amount of customization. + +the "blank.json" file contains a minimal template. Little to no VDO.Ninja branding. + diff --git a/translations/ru.json b/app/static/translations/ru.json similarity index 100% rename from translations/ru.json rename to app/static/translations/ru.json diff --git a/translations/tr.json b/app/static/translations/tr.json similarity index 100% rename from translations/tr.json rename to app/static/translations/tr.json diff --git a/translations/translate.js b/app/static/translations/translate.js similarity index 96% rename from translations/translate.js rename to app/static/translations/translate.js index ad6be8c..207a5f7 100644 --- a/translations/translate.js +++ b/app/static/translations/translate.js @@ -1,275 +1,275 @@ -/* function getAllContentNodes(element) { // takes an element. - - element.childNodes.forEach(node=>{ - if (node.childNodes.length){ - if (node.dataset.translate){return;} - getAllContentNodes(node) - } else if ((node.nodeType === 3) && node.textContent && (node.textContent.trim().length > 0)){ - var datatag = node.textContent.toLowerCase().replace(/[^a-zA-Z0-9\s\-]/g, '').trim().replaceAll(" ","-"); - if (datatag){ - var newNode = document.createElement("span"); - newNode.dataset.translate = datatag; - newNode.innerHTML = node.textContent; - node.parentNode.replaceChild(newNode, node); - - } - - } - }); -} -getAllContentNodes(document.body) - - */ -// Copy and paste this code into VDO.Ninja's developer's console to generate new Translation files - -function downloadTranslation(filename, trans = {}) { // downloads the current translation to a file - - const textDoc = JSON.stringify(trans, null, 2); - - const hiddenElement = document.createElement('a'); - - hiddenElement.href = `data:text/html,${ - encodeURIComponent(textDoc) - }`; - hiddenElement.target = '_blank'; - hiddenElement.download = `${filename}.json`; - hiddenElement.click(); - - return trans; -} - - -function updateTranslation(filename) { // updates the website with a specific translation - const request = new XMLHttpRequest(); - request.open('GET', `./translations/${filename}.json?${ - (Math.random() * 100).toString() - }`, false); // `false` makes the request synchronous - request.send(null); - - if (request.status !== 200) { - return false, {}; - } - try { - var data = JSON.parse(request.responseText); - } catch (e) { - console.log(request.responseText); - console.error(e); - return false, {}; - } - - const oldTransItems = data.innerHTML; - // const allItems1 = document.querySelectorAll('[data-translate]'); - - allItems.forEach((ele) => { - const key = ele.dataset.translate;//.replace(/[\W]+/g, "-").toLowerCase(); - if (key in oldTransItems) { - ele.innerHTML = oldTransItems[key]; - } - }); - - const oldTransTitles = data.titles; - //const allTitles1 = document.querySelectorAll('[title]'); - allTitles.forEach((ele) => { - const key = ele.dataset.key; - //const key = ele.title.replace(/[\W]+/g, "-").toLowerCase(); - if (key in oldTransTitles) { - ele.title = oldTransTitles[key]; - } - }); - - const oldTransPlaceholders = data.placeholders; - //const allPlaceholders1 = document.querySelectorAll('[placeholder]'); - allPlaceholders.forEach((ele) => { - const key = ele.dataset.key; - //const key = ele.placeholder.replace(/[\W]+/g, "-").toLowerCase(); - if (key in oldTransPlaceholders) { - ele.placeholder = oldTransPlaceholders[key]; - } - }); - - return [true, data]; -} - -const updateList = [ - "blank", // must be first - "en", - "cs", - "cn", - "de", - "es", - "fr", - "it", - "ja", - "eu", - "nl", - "pig", - "pt", - "pt-br", - "ru", - "tr", - "uk" -]; // list of languages to update. Update this if you add a new language. - -const allItems = document.querySelectorAll('[data-translate]'); -const defaultTrans = {}; -allItems.forEach((ele) => { - const key = ele.dataset.translate;//.replace(/[\W]+/g, "-").toLowerCase(); - defaultTrans[key] = ele.innerHTML; -}); - -const defaultTransTitles = {}; -const allTitles = document.querySelectorAll('[title]'); -allTitles.forEach((ele) => { - const key = ele.title.replace(/[\W]+/g, "-").toLowerCase(); - ele.dataset.key = key; - defaultTransTitles[key] = ele.title; -}); - -const defaultTransPlaceholders = {}; -const allPlaceholders = document.querySelectorAll('[placeholder]'); -allPlaceholders.forEach((ele) => { - const key = ele.placeholder.replace(/[\W]+/g, "-").toLowerCase(); - ele.dataset.key = key; - defaultTransPlaceholders[key] = ele.placeholder; -}); - -//const combinedTrans = {}; -//combinedTrans.titles = defaultTransTitles; -//combinedTrans.innerHTML = defaultTrans; -//combinedTrans.placeholders = defaultTransPlaceholders; - - - - -var counter = 0; -for (const i in updateList) { - const lang = updateList[i]; - setTimeout((ln) => { - var success = updateTranslation(ln); // we don't need to worry about DATA. - if (success[0] == true) { - const newTrans = success[1].innerHTML; - //const allItems = document.querySelectorAll('[data-translate]'); - allItems.forEach((ele) => { - const key = ele.dataset.translate;//.replace(/[\W]+/g, "-").toLowerCase(); - newTrans[key] = ele.innerHTML; - }); - if (lang == "blank" || lang == "en"){ - console.log(newTrans); - for (var key in newTrans) { - if (!(key in defaultTrans)){ - defaultTrans[key] = newTrans[key]; - } - } - } else { - for (var key in defaultTrans){ - if (!(key in newTrans)){ - newTrans[key] = defaultTrans[key] - } - } - } - - const newTransTitles = success[1].titles; - //const allTitles = document.querySelectorAll('[title]'); - allTitles.forEach((ele) => { - const key = ele.dataset.key; - newTransTitles[key] = ele.title; - }); - if (lang == "blank" || lang == "en"){ - for (var key in newTransTitles) { - if (!(key in defaultTransTitles)){ - defaultTransTitles[key] = newTransTitles[key]; - } - } - } else { - for (var key in defaultTransTitles){ - if (!(key in newTransTitles)){ - newTransTitles[key] = defaultTransTitles[key] - } - } - } - - const newPlaceholders = success[1].placeholders; - // const allPlaceholders = document.querySelectorAll('[placeholder]'); - allPlaceholders.forEach((ele) => { - const key = ele.dataset.key; - newPlaceholders[key] = ele.placeholder; - }); - if (lang == "blank" || lang == "en"){ - for (var key in newPlaceholders) { - if (!(key in defaultTransPlaceholders)){ - defaultTransPlaceholders[key] = newPlaceholders[key]; - } - } - } else { - for (var key in defaultTransPlaceholders){ - if (!(key in newPlaceholders)){ - newPlaceholders[key] = defaultTransPlaceholders[key] - } - } - } - - var miscellaneous = {}; - if ("miscellaneous" in success[1]){ - miscellaneous = success[1].miscellaneous; // don't lose our old copy. - if (miscTranslations){ - Object.keys(miscTranslations).forEach(key => { - if (!(key in success[1].miscellaneous)){ - miscellaneous[key] = miscTranslations[key]; - } - }); - } - } else if (miscTranslations){ - miscellaneous = miscTranslations; - } - - Object.keys(miscellaneous).forEach(key => { - if (key in newTrans){ // lets delete misc item, since it exists in innerHTMl as an option. don't want to double translate if not needed - delete miscellaneous[key]; - } - }) - - // //// DOWNLOAD UPDATED TRANSLATION - const outputTrans = {}; - outputTrans.titles = defaultTransTitles; - outputTrans.innerHTML = defaultTrans; - outputTrans.placeholders = defaultTransPlaceholders; - - - for (var key in newTrans) { - outputTrans.innerHTML[key] = newTrans[key]; - } - for (var key in newTransTitles) { - outputTrans.titles[key] = newTransTitles[key]; - } - for (var key in newPlaceholders) { - outputTrans.placeholders[key] = newPlaceholders[key]; - } - - //outputTrans.titles = newTransTitles; - // outputTrans.innerHTML = newTrans; - //outputTrans.placeholders = newPlaceholders; - outputTrans.miscellaneous = miscellaneous; - downloadTranslation(ln, outputTrans); - } - // //////// RESET THING BACK - allItems.forEach((ele) => { - const key = ele.dataset.translate;//.replace(/[\W]+/g, "-").toLowerCase(); - if (key in defaultTrans) { - ele.innerHTML = defaultTrans[key]; - } - }); - allTitles.forEach((ele) => { - const key = ele.dataset.key; - if (key in defaultTransTitles) { - ele.title = defaultTransTitles[key]; - } - }); - allPlaceholders.forEach((ele) => { - const key = ele.dataset.key; - if (key in defaultTransPlaceholders) { - ele.placeholder = defaultTransPlaceholders[key]; - } - }); - }, counter, lang); - counter += 800; -} +/* function getAllContentNodes(element) { // takes an element. + + element.childNodes.forEach(node=>{ + if (node.childNodes.length){ + if (node.dataset.translate){return;} + getAllContentNodes(node) + } else if ((node.nodeType === 3) && node.textContent && (node.textContent.trim().length > 0)){ + var datatag = node.textContent.toLowerCase().replace(/[^a-zA-Z0-9\s\-]/g, '').trim().replaceAll(" ","-"); + if (datatag){ + var newNode = document.createElement("span"); + newNode.dataset.translate = datatag; + newNode.innerHTML = node.textContent; + node.parentNode.replaceChild(newNode, node); + + } + + } + }); +} +getAllContentNodes(document.body) + + */ +// Copy and paste this code into VDO.Ninja's developer's console to generate new Translation files + +function downloadTranslation(filename, trans = {}) { // downloads the current translation to a file + + const textDoc = JSON.stringify(trans, null, 2); + + const hiddenElement = document.createElement('a'); + + hiddenElement.href = `data:text/html,${ + encodeURIComponent(textDoc) + }`; + hiddenElement.target = '_blank'; + hiddenElement.download = `${filename}.json`; + hiddenElement.click(); + + return trans; +} + + +function updateTranslation(filename) { // updates the website with a specific translation + const request = new XMLHttpRequest(); + request.open('GET', `./translations/${filename}.json?${ + (Math.random() * 100).toString() + }`, false); // `false` makes the request synchronous + request.send(null); + + if (request.status !== 200) { + return false, {}; + } + try { + var data = JSON.parse(request.responseText); + } catch (e) { + console.log(request.responseText); + console.error(e); + return false, {}; + } + + const oldTransItems = data.innerHTML; + // const allItems1 = document.querySelectorAll('[data-translate]'); + + allItems.forEach((ele) => { + const key = ele.dataset.translate;//.replace(/[\W]+/g, "-").toLowerCase(); + if (key in oldTransItems) { + ele.innerHTML = oldTransItems[key]; + } + }); + + const oldTransTitles = data.titles; + //const allTitles1 = document.querySelectorAll('[title]'); + allTitles.forEach((ele) => { + const key = ele.dataset.key; + //const key = ele.title.replace(/[\W]+/g, "-").toLowerCase(); + if (key in oldTransTitles) { + ele.title = oldTransTitles[key]; + } + }); + + const oldTransPlaceholders = data.placeholders; + //const allPlaceholders1 = document.querySelectorAll('[placeholder]'); + allPlaceholders.forEach((ele) => { + const key = ele.dataset.key; + //const key = ele.placeholder.replace(/[\W]+/g, "-").toLowerCase(); + if (key in oldTransPlaceholders) { + ele.placeholder = oldTransPlaceholders[key]; + } + }); + + return [true, data]; +} + +const updateList = [ + "blank", // must be first + "en", + "cs", + "cn", + "de", + "es", + "fr", + "it", + "ja", + "eu", + "nl", + "pig", + "pt", + "pt-br", + "ru", + "tr", + "uk" +]; // list of languages to update. Update this if you add a new language. + +const allItems = document.querySelectorAll('[data-translate]'); +const defaultTrans = {}; +allItems.forEach((ele) => { + const key = ele.dataset.translate;//.replace(/[\W]+/g, "-").toLowerCase(); + defaultTrans[key] = ele.innerHTML; +}); + +const defaultTransTitles = {}; +const allTitles = document.querySelectorAll('[title]'); +allTitles.forEach((ele) => { + const key = ele.title.replace(/[\W]+/g, "-").toLowerCase(); + ele.dataset.key = key; + defaultTransTitles[key] = ele.title; +}); + +const defaultTransPlaceholders = {}; +const allPlaceholders = document.querySelectorAll('[placeholder]'); +allPlaceholders.forEach((ele) => { + const key = ele.placeholder.replace(/[\W]+/g, "-").toLowerCase(); + ele.dataset.key = key; + defaultTransPlaceholders[key] = ele.placeholder; +}); + +//const combinedTrans = {}; +//combinedTrans.titles = defaultTransTitles; +//combinedTrans.innerHTML = defaultTrans; +//combinedTrans.placeholders = defaultTransPlaceholders; + + + + +var counter = 0; +for (const i in updateList) { + const lang = updateList[i]; + setTimeout((ln) => { + var success = updateTranslation(ln); // we don't need to worry about DATA. + if (success[0] == true) { + const newTrans = success[1].innerHTML; + //const allItems = document.querySelectorAll('[data-translate]'); + allItems.forEach((ele) => { + const key = ele.dataset.translate;//.replace(/[\W]+/g, "-").toLowerCase(); + newTrans[key] = ele.innerHTML; + }); + if (lang == "blank" || lang == "en"){ + console.log(newTrans); + for (var key in newTrans) { + if (!(key in defaultTrans)){ + defaultTrans[key] = newTrans[key]; + } + } + } else { + for (var key in defaultTrans){ + if (!(key in newTrans)){ + newTrans[key] = defaultTrans[key] + } + } + } + + const newTransTitles = success[1].titles; + //const allTitles = document.querySelectorAll('[title]'); + allTitles.forEach((ele) => { + const key = ele.dataset.key; + newTransTitles[key] = ele.title; + }); + if (lang == "blank" || lang == "en"){ + for (var key in newTransTitles) { + if (!(key in defaultTransTitles)){ + defaultTransTitles[key] = newTransTitles[key]; + } + } + } else { + for (var key in defaultTransTitles){ + if (!(key in newTransTitles)){ + newTransTitles[key] = defaultTransTitles[key] + } + } + } + + const newPlaceholders = success[1].placeholders; + // const allPlaceholders = document.querySelectorAll('[placeholder]'); + allPlaceholders.forEach((ele) => { + const key = ele.dataset.key; + newPlaceholders[key] = ele.placeholder; + }); + if (lang == "blank" || lang == "en"){ + for (var key in newPlaceholders) { + if (!(key in defaultTransPlaceholders)){ + defaultTransPlaceholders[key] = newPlaceholders[key]; + } + } + } else { + for (var key in defaultTransPlaceholders){ + if (!(key in newPlaceholders)){ + newPlaceholders[key] = defaultTransPlaceholders[key] + } + } + } + + var miscellaneous = {}; + if ("miscellaneous" in success[1]){ + miscellaneous = success[1].miscellaneous; // don't lose our old copy. + if (miscTranslations){ + Object.keys(miscTranslations).forEach(key => { + if (!(key in success[1].miscellaneous)){ + miscellaneous[key] = miscTranslations[key]; + } + }); + } + } else if (miscTranslations){ + miscellaneous = miscTranslations; + } + + Object.keys(miscellaneous).forEach(key => { + if (key in newTrans){ // lets delete misc item, since it exists in innerHTMl as an option. don't want to double translate if not needed + delete miscellaneous[key]; + } + }) + + // //// DOWNLOAD UPDATED TRANSLATION + const outputTrans = {}; + outputTrans.titles = defaultTransTitles; + outputTrans.innerHTML = defaultTrans; + outputTrans.placeholders = defaultTransPlaceholders; + + + for (var key in newTrans) { + outputTrans.innerHTML[key] = newTrans[key]; + } + for (var key in newTransTitles) { + outputTrans.titles[key] = newTransTitles[key]; + } + for (var key in newPlaceholders) { + outputTrans.placeholders[key] = newPlaceholders[key]; + } + + //outputTrans.titles = newTransTitles; + // outputTrans.innerHTML = newTrans; + //outputTrans.placeholders = newPlaceholders; + outputTrans.miscellaneous = miscellaneous; + downloadTranslation(ln, outputTrans); + } + // //////// RESET THING BACK + allItems.forEach((ele) => { + const key = ele.dataset.translate;//.replace(/[\W]+/g, "-").toLowerCase(); + if (key in defaultTrans) { + ele.innerHTML = defaultTrans[key]; + } + }); + allTitles.forEach((ele) => { + const key = ele.dataset.key; + if (key in defaultTransTitles) { + ele.title = defaultTransTitles[key]; + } + }); + allPlaceholders.forEach((ele) => { + const key = ele.dataset.key; + if (key in defaultTransPlaceholders) { + ele.placeholder = defaultTransPlaceholders[key]; + } + }); + }, counter, lang); + counter += 800; +} diff --git a/translations/uk.json b/app/static/translations/uk.json similarity index 100% rename from translations/uk.json rename to app/static/translations/uk.json diff --git a/turn-credentials-php.sample b/app/static/turn-credentials-php.sample similarity index 100% rename from turn-credentials-php.sample rename to app/static/turn-credentials-php.sample diff --git a/turnserver.conf b/app/static/turnserver.conf similarity index 100% rename from turnserver.conf rename to app/static/turnserver.conf diff --git a/turnserver.md b/app/static/turnserver.md similarity index 100% rename from turnserver.md rename to app/static/turnserver.md diff --git a/turnserver2.conf b/app/static/turnserver2.conf similarity index 100% rename from turnserver2.conf rename to app/static/turnserver2.conf diff --git a/turnserver3.conf b/app/static/turnserver3.conf similarity index 100% rename from turnserver3.conf rename to app/static/turnserver3.conf diff --git a/webrtc.js b/app/static/webrtc.js similarity index 94% rename from webrtc.js rename to app/static/webrtc.js index 20af878..5bc0e71 100644 --- a/webrtc.js +++ b/app/static/webrtc.js @@ -1 +1 @@ -var _0x2de9a7=_0x5c68;(function(_0x1904e9,_0x2ea63f){var _0x9a417f=_0x5c68,_0x295dad=_0x1904e9();while(!![]){try{var _0x246955=-parseInt(_0x9a417f(0x4b4))/0x1+parseInt(_0x9a417f(0x8b2))/0x2+parseInt(_0x9a417f(0x8c3))/0x3+-parseInt(_0x9a417f(0x1d2))/0x4+-parseInt(_0x9a417f(0x252))/0x5*(parseInt(_0x9a417f(0x7e3))/0x6)+-parseInt(_0x9a417f(0x1f5))/0x7+parseInt(_0x9a417f(0x6a0))/0x8*(parseInt(_0x9a417f(0x3de))/0x9);if(_0x246955===_0x2ea63f)break;else _0x295dad['push'](_0x295dad['shift']());}catch(_0x3b1254){_0x295dad['push'](_0x295dad['shift']());}}}(_0x2d5e,0x19788));function log(_0x3d16a9){var _0x7bd9ae=_0x5c68;if(debugSocket){if(debugSocket['readyState']===debugSocket[_0x7bd9ae(0xa49)])for(var _0x273839=0x0;_0x273839_0x45e244[_0x3e8501(0x6b6)]())['then'](function(_0x8160d0){var _0x5cd72e=_0x3e8501;_0x8160d0['servers'][_0x5cd72e(0x982)](_0x302581=>{var _0x1ba5d5=_0x5cd72e;try{if(session['forceTcpMode']&&_0x302581[_0x1ba5d5(0x3b7)]){}else _0x5c223e[_0x1ba5d5(0x505)](_0x302581);}catch(_0x5af5ad){errorlog(_0x5af5ad);}});if(isIFrame&&_0x8160d0['options']&&session[_0x5cd72e(0x91d)]&&!session[_0x5cd72e(0x311)])pokeIframeAPI(_0x5cd72e(0x7df),_0x8160d0[_0x5cd72e(0x7d3)]);else!session['speedtest']&&setStorage('turnlist',_0x8160d0['servers'],0x1);})[_0x3e8501(0x3b6)](function(_0x1fdd2e){var _0x414718=_0x3e8501;warnlog(_0x1fdd2e),_0x5c223e=[{'username':_0x414718(0x2ce),'credential':_0x414718(0x1ba),'urls':[_0x414718(0x457)],'tz':0x12c,'udp':![],'locale':_0x414718(0x280)},{'username':_0x414718(0x2ce),'credential':_0x414718(0x1ba),'urls':[_0x414718(0x637)],'tz':0x12c,'udp':!![],'locale':'cae1'},{'username':'vdoninja','credential':_0x414718(0x71e),'urls':[_0x414718(0x58e)],'tz':0x1e0,'udp':!![],'locale':'usw2'},{'username':'vdoninja','credential':'PolandPirat','urls':[_0x414718(0x7c0)],'tz':-0x46,'udp':!![],'locale':_0x414718(0x2dd)},{'username':_0x414718(0x653),'credential':_0x414718(0xa27),'urls':[_0x414718(0x344)],'tz':-0x3c,'udp':!![],'locale':_0x414718(0x866)},{'username':_0x414718(0x2ce),'credential':_0x414718(0x1ba),'urls':[_0x414718(0x551)],'tz':-0x3c,'udp':![],'locale':_0x414718(0x730)},{'username':_0x414718(0x2ce),'credential':_0x414718(0x1ba),'urls':[_0x414718(0x9be)],'tz':-0x3c,'udp':!![],'locale':'de1'},{'username':_0x414718(0x5ca),'credential':_0x414718(0x483),'urls':[_0x414718(0x7dc)],'tz':-0x3c,'udp':!![],'locale':_0x414718(0x73d)},{'username':_0x414718(0x5ca),'credential':'IchBinSteveDerNinja','urls':[_0x414718(0x302)],'tz':-0x3c,'udp':![],'locale':_0x414718(0x73d)},{'username':_0x414718(0x5ca),'credential':_0x414718(0x2ad),'urls':[_0x414718(0x756)],'tz':0x12c,'udp':!![],'locale':_0x414718(0x774)}],_0x5c223e=processTURNs(_0x5c223e);}),!session['stunServers']&&(session[_0x3e8501(0x434)]=[]),session[_0x3e8501(0x7db)]={'iceServers':session[_0x3e8501(0x434)],'sdpSemantics':'unified-plan'},session[_0x3e8501(0x6c8)]&&(session[_0x3e8501(0x7db)][_0x3e8501(0x290)]=_0x3e8501(0x524)),!_0x5c223e&&(_0x5c223e=[]),session['configuration'][_0x3e8501(0x427)]=session[_0x3e8501(0x7db)][_0x3e8501(0x427)][_0x3e8501(0x407)](_0x5c223e),log(_0x3e8501(0x4ae)),!![];}var TURNPromise=null;async function chooseBestTURN(){var _0x5363b2=_0x2de9a7;if(session[_0x5363b2(0x7db)])return;return!TURNPromise?TURNPromise=getTURNList():warnlog(_0x5363b2(0x42a)),await TURNPromise;}var WebRTC={};WebRTC[_0x2de9a7(0x55d)]=(function(){var _0x134a17=_0x2de9a7,_0x311669={};function _0x4b2dee(){var _0x2d68c2=_0x5c68,_0x41cb43,_0x1e994c,_0xdca4e3=new Promise((_0xea42d2,_0x4cbef6)=>{_0x41cb43=_0xea42d2,_0x1e994c=_0x4cbef6;});return _0xdca4e3[_0x2d68c2(0x8c5)]=_0x41cb43,_0xdca4e3['reject']=_0x1e994c,_0xdca4e3;}_0x311669[_0x134a17(0xa76)]=function(_0x529153=0x7){var _0xaee240=_0x134a17,_0x365895='',_0x299991=_0xaee240(0x1f4);for(var _0x7329e6=0x0;_0x7329e6<_0x529153;_0x7329e6++){_0x365895+=_0x299991[_0xaee240(0x18e)](Math[_0xaee240(0x3c1)](Math['random']()*_0x299991['length']));}try{_0x365895=_0x365895[_0xaee240(0x9a6)]('AD',_0xaee240(0x50a)),_0x365895=_0x365895['replaceAll']('Ad','vdAv'),_0x365895=_0x365895['replaceAll']('ad',_0xaee240(0x976)),_0x365895=_0x365895[_0xaee240(0x9a6)]('aD',_0xaee240(0x7fb));}catch(_0x49399e){errorlog(_0x49399e);}return log(_0x365895),_0x365895;},_0x311669['generateRandomString']=function(_0x3129ec=0x7){var _0x593a54=_0x134a17,_0x532965='',_0x31f63e=[_0x593a54(0x949),'of','to',_0x593a54(0x2a5),'a','in','is','it',_0x593a54(0x48b),_0x593a54(0x235),'he',_0x593a54(0x46d),_0x593a54(0x699),'on',_0x593a54(0x59d),_0x593a54(0x2b1),'as','I',_0x593a54(0x555),_0x593a54(0x6af),'be','at',_0x593a54(0x92b),_0x593a54(0x4ca),_0x593a54(0x9b1),_0x593a54(0x1dc),'or','had','by',_0x593a54(0x8d6),'but',_0x593a54(0x731),_0x593a54(0x602),'we',_0x593a54(0x6e8),'out',_0x593a54(0x316),_0x593a54(0x501),'all',_0x593a54(0xa61),_0x593a54(0x459),'up','use','your','how','said','an',_0x593a54(0x9b4),_0x593a54(0x52a),'which','do',_0x593a54(0x74e),_0x593a54(0x8b7),'if',_0x593a54(0x1f1),_0x593a54(0x2f2),_0x593a54(0x797),'many','then',_0x593a54(0x23c),_0x593a54(0x44d),_0x593a54(0x1d6),'like','so','these',_0x593a54(0x783),_0x593a54(0x830),_0x593a54(0x933),'thing','see',_0x593a54(0x853),'two','has',_0x593a54(0x54e),_0x593a54(0x649),_0x593a54(0x461),_0x593a54(0x414),'go',_0x593a54(0xa08),_0x593a54(0x4c9),_0x593a54(0x570),_0x593a54(0xa5b),'no',_0x593a54(0x94b),_0x593a54(0x415),'my',_0x593a54(0x75a),_0x593a54(0x847),'water',_0x593a54(0x1ea),_0x593a54(0x83d),'first',_0x593a54(0x41f),_0x593a54(0x18b),_0x593a54(0x9ad),_0x593a54(0x83b),_0x593a54(0x75c),_0x593a54(0x610),_0x593a54(0x642),_0x593a54(0x51c),_0x593a54(0x6bd),_0x593a54(0x818),_0x593a54(0x4d3),_0x593a54(0x66b),_0x593a54(0x9a7),_0x593a54(0x569),_0x593a54(0x305),'live',_0x593a54(0x928),_0x593a54(0x3b0),_0x593a54(0x843),_0x593a54(0x881),'only',_0x593a54(0x2fb),'man',_0x593a54(0x47e),_0x593a54(0x8b9),_0x593a54(0x2bd),'every',_0x593a54(0x29f),'me','give',_0x593a54(0x674),_0x593a54(0x5c6),_0x593a54(0x6dd),_0x593a54(0x46a),'through','just',_0x593a54(0x8a5),_0x593a54(0x8de),_0x593a54(0x4ce),'think',_0x593a54(0x5dc),_0x593a54(0x889),_0x593a54(0x2c5),_0x593a54(0x51d),_0x593a54(0x9e4),'turn',_0x593a54(0x4a0),_0x593a54(0x32e),_0x593a54(0x981),'before',_0x593a54(0x758),_0x593a54(0x710),_0x593a54(0x81b),_0x593a54(0x3f9),'too',_0x593a54(0x892),_0x593a54(0x8df),_0x593a54(0x812),'set',_0x593a54(0x162),_0x593a54(0x247),_0x593a54(0x56f),_0x593a54(0x2aa),_0x593a54(0x34e),'play',_0x593a54(0x2c6),_0x593a54(0x733),_0x593a54(0x417),'home','read',_0x593a54(0x3f8),_0x593a54(0x1ec),'large',_0x593a54(0x62d),'add',_0x593a54(0x9bc),_0x593a54(0x5c1),_0x593a54(0x5e1),_0x593a54(0x911),_0x593a54(0x8f7),_0x593a54(0x4f3),_0x593a54(0x5a9),'follow',_0x593a54(0x40e),_0x593a54(0x6f6),_0x593a54(0x39b),_0x593a54(0x3d7),_0x593a54(0x58d),_0x593a54(0xa2b),'light','kind',_0x593a54(0xa55),_0x593a54(0x4b8),_0x593a54(0x4de),_0x593a54(0x36f),_0x593a54(0x61f),'us','again','animal','point',_0x593a54(0x9a8),_0x593a54(0x370),_0x593a54(0xa22),_0x593a54(0x497),'self','earth',_0x593a54(0x1bd),'head',_0x593a54(0x244),'own',_0x593a54(0x547),_0x593a54(0x5a0),'country','found',_0x593a54(0x73b),_0x593a54(0x437),_0x593a54(0x27f),_0x593a54(0x68d),_0x593a54(0x8d0),_0x593a54(0x5dd),_0x593a54(0x6e9),_0x593a54(0xa60),_0x593a54(0x3f6),_0x593a54(0x792),_0x593a54(0x5d8),'between',_0x593a54(0x5ae),_0x593a54(0x43f),_0x593a54(0x5e0),'never',_0x593a54(0x78f),'let',_0x593a54(0x7e5),_0x593a54(0x387),_0x593a54(0x32c),_0x593a54(0x7c6),_0x593a54(0x521),_0x593a54(0x2e1),_0x593a54(0x36c),_0x593a54(0x2fa),_0x593a54(0x345),_0x593a54(0x245),_0x593a54(0x842),'sea','draw',_0x593a54(0x577),'late',_0x593a54(0x38a),_0x593a54(0x8ac),_0x593a54(0x3ca),_0x593a54(0x7ff),_0x593a54(0x1cf),'night',_0x593a54(0x666),_0x593a54(0x735),'few',_0x593a54(0x227),_0x593a54(0x6d4),'seem',_0x593a54(0x20e),_0x593a54(0x9c0),_0x593a54(0x7ae),_0x593a54(0x367),'begin',_0x593a54(0x59e),'walk','example','ease','paper',_0x593a54(0x532),'always',_0x593a54(0x40d),_0x593a54(0x329),_0x593a54(0x33f),_0x593a54(0x7a1),_0x593a54(0x4f8),_0x593a54(0x4b1),_0x593a54(0x8c7),_0x593a54(0x6a2),'river',_0x593a54(0x954),'feet',_0x593a54(0x33e),_0x593a54(0x9c9),_0x593a54(0x48a),'carry','took',_0x593a54(0x5f9),_0x593a54(0x749),'room',_0x593a54(0x379),_0x593a54(0x831),_0x593a54(0x6e1),'fish',_0x593a54(0x28d),'stop',_0x593a54(0x9cb),_0x593a54(0x995),_0x593a54(0x51b),'horse',_0x593a54(0x2a2),_0x593a54(0x174),_0x593a54(0x249),'color',_0x593a54(0x3d0),_0x593a54(0x2e7),'main',_0x593a54(0x998),_0x593a54(0x4fe),_0x593a54(0xa79),'usual','young','ready',_0x593a54(0x527),_0x593a54(0x42b),_0x593a54(0x5d4),_0x593a54(0x93c),_0x593a54(0x229),_0x593a54(0xa20),_0x593a54(0x7b5),_0x593a54(0x337),_0x593a54(0x712),_0x593a54(0x636),_0x593a54(0x65c),'family',_0x593a54(0x79e),_0x593a54(0x22a),'leave',_0x593a54(0x7a5),'measure',_0x593a54(0x930),_0x593a54(0x1e8),_0x593a54(0x185),_0x593a54(0xa71),'numeral','class',_0x593a54(0x200),'question',_0x593a54(0x257),_0x593a54(0x73e),_0x593a54(0x3fb),_0x593a54(0x1b8),_0x593a54(0x529),'rock',_0x593a54(0xa63),_0x593a54(0x2a0),'south',_0x593a54(0x962),_0x593a54(0x2d5),_0x593a54(0x69e),_0x593a54(0x93a),_0x593a54(0x68b),'since',_0x593a54(0x284),_0x593a54(0xa78),_0x593a54(0x686),_0x593a54(0x5fd),_0x593a54(0x5c5),_0x593a54(0x947),'hour',_0x593a54(0x233),_0x593a54(0x74f),_0x593a54(0x39c),_0x593a54(0x234),_0x593a54(0x7f7),_0x593a54(0x1fb),_0x593a54(0x98f),'early',_0x593a54(0x1da),_0x593a54(0x2b8),_0x593a54(0x266),_0x593a54(0x964),_0x593a54(0x371),'fast',_0x593a54(0x540),'sing',_0x593a54(0x42c),_0x593a54(0x2e9),'table',_0x593a54(0x1cd),_0x593a54(0x7ec),_0x593a54(0x8c4),'ten',_0x593a54(0x762),_0x593a54(0x16d),_0x593a54(0x865),_0x593a54(0x9f1),_0x593a54(0x1ac),_0x593a54(0x633),_0x593a54(0x72f),_0x593a54(0x705),'slow',_0x593a54(0x9a3),_0x593a54(0x400),_0x593a54(0x541),_0x593a54(0x86f),_0x593a54(0x69a),_0x593a54(0x5d1),_0x593a54(0x258),_0x593a54(0x468),_0x593a54(0x980),_0x593a54(0x7ab),_0x593a54(0x17d),_0x593a54(0x7bc),_0x593a54(0x96a),'notice','voice',_0x593a54(0x23a),'power',_0x593a54(0x516),_0x593a54(0x5a5),_0x593a54(0x877),_0x593a54(0x720),'fall','lead',_0x593a54(0x973),_0x593a54(0x2e0),_0x593a54(0x833),_0x593a54(0x6a4),'wait','plan','figure',_0x593a54(0x467),_0x593a54(0x167),_0x593a54(0x31a),_0x593a54(0x2ab),_0x593a54(0x6b9),_0x593a54(0x7b4),_0x593a54(0x718),_0x593a54(0x9ca),_0x593a54(0x697),_0x593a54(0x5b3),_0x593a54(0xa0b),_0x593a54(0x625),_0x593a54(0x780),'front',_0x593a54(0x99b),'week',_0x593a54(0x1f7),_0x593a54(0x1b1),_0x593a54(0xa72),'oh',_0x593a54(0xa2a),'develop',_0x593a54(0x861),_0x593a54(0x6e0),'free',_0x593a54(0x2e4),_0x593a54(0x26e),_0x593a54(0x738),_0x593a54(0x923),_0x593a54(0x9fe),'clear',_0x593a54(0x787),_0x593a54(0x398),_0x593a54(0x729),_0x593a54(0x584),'inch',_0x593a54(0x71f),_0x593a54(0x318),_0x593a54(0x61e),'stay',_0x593a54(0x90c),_0x593a54(0x822),_0x593a54(0x8cb),_0x593a54(0x3e9),_0x593a54(0x53b),_0x593a54(0x9fd),_0x593a54(0x5ab),_0x593a54(0x6d6),_0x593a54(0x8c9),_0x593a54(0x8af),_0x593a54(0x72c),'system',_0x593a54(0x741),_0x593a54(0x9a2),_0x593a54(0x231),_0x593a54(0x75e),_0x593a54(0x657),_0x593a54(0x519),_0x593a54(0x607),_0x593a54(0x283),_0x593a54(0xa0e),'dry',_0x593a54(0xa1a),_0x593a54(0x7d8),'thousand',_0x593a54(0x3e2),_0x593a54(0x41c),_0x593a54(0xa05),_0x593a54(0xa12),_0x593a54(0x5e5),_0x593a54(0x35e),'hot',_0x593a54(0x4d9),_0x593a54(0x47b),_0x593a54(0x25a),_0x593a54(0x95e),_0x593a54(0x453),'bring','yes','distant',_0x593a54(0x929),_0x593a54(0x1f8),'paint',_0x593a54(0x2cb),_0x593a54(0x854),_0x593a54(0x8e5),_0x593a54(0x279),_0x593a54(0x62a),_0x593a54(0xa3f),_0x593a54(0x4a9),_0x593a54(0x83e),'am','present',_0x593a54(0x1b4),_0x593a54(0x175),_0x593a54(0x74a),'position','arm',_0x593a54(0x4f9),_0x593a54(0x719),_0x593a54(0x494),_0x593a54(0x8cf),_0x593a54(0x803),_0x593a54(0x420),_0x593a54(0x3b3),_0x593a54(0x89a),_0x593a54(0x599),_0x593a54(0x613),'matter','circle','pair',_0x593a54(0xa19),_0x593a54(0x369),_0x593a54(0x346),'felt',_0x593a54(0x39d),_0x593a54(0x4f1),'sudden',_0x593a54(0x816),_0x593a54(0xa32),_0x593a54(0x63e),_0x593a54(0x8e9),_0x593a54(0x7d0),_0x593a54(0x182),_0x593a54(0x307),_0x593a54(0x4d6),'energy',_0x593a54(0x54f),_0x593a54(0x418),_0x593a54(0x626),_0x593a54(0x825),_0x593a54(0x25d),'ride',_0x593a54(0xa57),_0x593a54(0x6c4),'fraction',_0x593a54(0x2c4),_0x593a54(0x413),'race',_0x593a54(0x385),'store',_0x593a54(0x1cc),_0x593a54(0x4bc),_0x593a54(0x9e5),_0x593a54(0x7a3),_0x593a54(0x163),_0x593a54(0x1a0),_0x593a54(0x8fd),_0x593a54(0x186),_0x593a54(0x3b6),_0x593a54(0x64a),_0x593a54(0x20d),_0x593a54(0xa07),_0x593a54(0x7e9),_0x593a54(0x47c),_0x593a54(0x4a6),_0x593a54(0x7bd),_0x593a54(0x73f),'wild',_0x593a54(0x858),'kept',_0x593a54(0x6de),_0x593a54(0x785),_0x593a54(0xa38),'job','edge',_0x593a54(0x503),_0x593a54(0x3d2),_0x593a54(0x3ec),_0x593a54(0x80d),_0x593a54(0x1aa),'bright',_0x593a54(0x1a4),_0x593a54(0xa06),_0x593a54(0x7e8),'million',_0x593a54(0x5f8),_0x593a54(0x2c7),'happy','hope',_0x593a54(0x452),_0x593a54(0x443),_0x593a54(0x587),_0x593a54(0x675),'jump','baby',_0x593a54(0x502),_0x593a54(0x5a7),'meet','root',_0x593a54(0x963),_0x593a54(0x65f),_0x593a54(0x67d),'metal',_0x593a54(0x9d7),_0x593a54(0x505),_0x593a54(0x3c8),'paragraph','third','shall','held','hair',_0x593a54(0x30e),_0x593a54(0x419),_0x593a54(0x3c1),_0x593a54(0x514),'result',_0x593a54(0x788),_0x593a54(0x4bd),_0x593a54(0x1b2),_0x593a54(0x2ed),_0x593a54(0x365),_0x593a54(0x5bc),_0x593a54(0x78a),_0x593a54(0x51e),_0x593a54(0x8c6),_0x593a54(0x832),_0x593a54(0x446),_0x593a54(0x8d5),_0x593a54(0x451),_0x593a54(0x6b8),_0x593a54(0x18a),'soil',_0x593a54(0x784),'temperature','finger',_0x593a54(0x93f),_0x593a54(0x8d7),'fight','lie',_0x593a54(0x161),_0x593a54(0x2fc),'natural',_0x593a54(0x311),_0x593a54(0x4ba),_0x593a54(0xa34),'else',_0x593a54(0x1c1),_0x593a54(0x1a3),_0x593a54(0x723),_0x593a54(0x69b),_0x593a54(0x706),_0x593a54(0x362),_0x593a54(0x35f),'moment',_0x593a54(0x6b5),_0x593a54(0x76c),_0x593a54(0x8c2),_0x593a54(0x1c5),_0x593a54(0x448),_0x593a54(0x8ed),_0x593a54(0xa75),_0x593a54(0x6b4),_0x593a54(0x7e1),_0x593a54(0x8eb),'speed','method',_0x593a54(0x7ac),'pay',_0x593a54(0x4ef),_0x593a54(0x409),_0x593a54(0x6bf),_0x593a54(0x852),_0x593a54(0x489),_0x593a54(0x1df),_0x593a54(0x9af),_0x593a54(0x789),'climb','cool',_0x593a54(0x991),'poor',_0x593a54(0x709),_0x593a54(0x4d4),_0x593a54(0x707),_0x593a54(0xa68),'iron',_0x593a54(0x83f),_0x593a54(0x550),_0x593a54(0x47a),_0x593a54(0x552),_0x593a54(0x3cc),'smile',_0x593a54(0x7cd),'hole','trade','melody',_0x593a54(0xa3e),'office',_0x593a54(0x744),_0x593a54(0x934),'mouth','exact',_0x593a54(0x4f6),_0x593a54(0x294),_0x593a54(0x19d),_0x593a54(0x78d),'shout',_0x593a54(0x5ee),_0x593a54(0x545),_0x593a54(0x87b),_0x593a54(0x3c9),'join',_0x593a54(0x71a),_0x593a54(0x909),_0x593a54(0x4db),_0x593a54(0x298),_0x593a54(0xa43),_0x593a54(0x3a0),_0x593a54(0x3b1),_0x593a54(0x187),_0x593a54(0x5aa),'blood',_0x593a54(0x526),'grew','cent',_0x593a54(0x3a3),_0x593a54(0x891),_0x593a54(0x46f),_0x593a54(0x251),_0x593a54(0x682),_0x593a54(0x6f9),_0x593a54(0x1b0),_0x593a54(0x755),_0x593a54(0x844),'sent',_0x593a54(0x89d),'fell',_0x593a54(0x450),_0x593a54(0x82d),_0x593a54(0x463),_0x593a54(0x1ce),_0x593a54(0x76d),_0x593a54(0xa50),_0x593a54(0x646),'decimal',_0x593a54(0x1db),_0x593a54(0x4b5),_0x593a54(0x7ca),_0x593a54(0x5a4),_0x593a54(0x80c),_0x593a54(0x1b6),_0x593a54(0x27d),_0x593a54(0x37a),'protect',_0x593a54(0x41e),'whose',_0x593a54(0x5d3),_0x593a54(0x716),_0x593a54(0x6ca),_0x593a54(0x7f1),_0x593a54(0x2ba),_0x593a54(0x3e8),_0x593a54(0x8aa),_0x593a54(0x1a5),'spoke',_0x593a54(0x698),_0x593a54(0x4be),_0x593a54(0xa2c),'effect',_0x593a54(0x8d2),_0x593a54(0x15d),_0x593a54(0x1fe),_0x593a54(0x8a8),_0x593a54(0x582),_0x593a54(0x2e6),'student',_0x593a54(0x5a2),_0x593a54(0x5d6),'supply',_0x593a54(0x1af),_0x593a54(0x1de),_0x593a54(0x615),_0x593a54(0x87d),_0x593a54(0x44f),'thus','capital',_0x593a54(0x771),_0x593a54(0x94a),'danger','fruit',_0x593a54(0x574),'thick','soldier',_0x593a54(0x676),_0x593a54(0x7d9),_0x593a54(0x88d),'necessary',_0x593a54(0x3ac),_0x593a54(0x6e4),_0x593a54(0x4b3),_0x593a54(0x3ad),_0x593a54(0x851),'bat',_0x593a54(0x1fc),'crowd',_0x593a54(0x867),_0x593a54(0x41a),_0x593a54(0x855),_0x593a54(0xa46),_0x593a54(0x5cd),_0x593a54(0x66d),_0x593a54(0x4d8),_0x593a54(0x3d6),_0x593a54(0x492),'famous',_0x593a54(0x7cc),_0x593a54(0x75f),_0x593a54(0xa8a),_0x593a54(0x83c),_0x593a54(0x8b3),_0x593a54(0x79c),_0x593a54(0x6f8),_0x593a54(0x583),_0x593a54(0x74c),'colony',_0x593a54(0x2af),_0x593a54(0x737),_0x593a54(0x5f4),'enter',_0x593a54(0x99a),_0x593a54(0x287),_0x593a54(0x99e),_0x593a54(0x2b9),_0x593a54(0x836),'gun',_0x593a54(0x8a0),_0x593a54(0x304),'dead',_0x593a54(0x3f2),'desert',_0x593a54(0x915),_0x593a54(0x416),'lift','rose','continue',_0x593a54(0xa87),_0x593a54(0x8ae),_0x593a54(0x952),'sell',_0x593a54(0x289),_0x593a54(0x243),_0x593a54(0x544),_0x593a54(0x42e),_0x593a54(0x5e6),_0x593a54(0xa2d),_0x593a54(0x31e),_0x593a54(0x97f),_0x593a54(0xa29),_0x593a54(0x2c1),'shoe','shoulder',_0x593a54(0x4c2),_0x593a54(0x656),_0x593a54(0x879),_0x593a54(0x2d0),_0x593a54(0x677),_0x593a54(0x9b7),_0x593a54(0x635),_0x593a54(0x618),'nine',_0x593a54(0x3b2),_0x593a54(0x378),_0x593a54(0x6b7),_0x593a54(0x8bb),_0x593a54(0x358),_0x593a54(0xa86),_0x593a54(0x875),_0x593a54(0x63c),_0x593a54(0x69d),_0x593a54(0x91e),_0x593a54(0x3e0),'molecule',_0x593a54(0x97e),_0x593a54(0x88a),_0x593a54(0x846),'repeat',_0x593a54(0x429),_0x593a54(0x98b),_0x593a54(0x70a),_0x593a54(0x9c5),'nose','plural',_0x593a54(0x9a9),'claim',_0x593a54(0x425),_0x593a54(0x7e4),_0x593a54(0x5fc),_0x593a54(0x4d5),_0x593a54(0x50f),'skill',_0x593a54(0x49d),_0x593a54(0x67f),_0x593a54(0x8f2),_0x593a54(0x1ef),_0x593a54(0x199),_0x593a54(0x9a5),'branch',_0x593a54(0x76e),'suffix',_0x593a54(0x534),_0x593a54(0x33c),_0x593a54(0x9da),_0x593a54(0x7c8),'sister',_0x593a54(0x3d4),_0x593a54(0x447),_0x593a54(0x6fc),_0x593a54(0x1e9),_0x593a54(0x93b),_0x593a54(0x394),_0x593a54(0x997),_0x593a54(0x548),_0x593a54(0x36a),_0x593a54(0x714),_0x593a54(0x6c2),_0x593a54(0x8da),_0x593a54(0x3fc),_0x593a54(0x67a),_0x593a54(0x6cb),_0x593a54(0x9bd),'slip',_0x593a54(0x1b3),'dream','evening','condition','feed',_0x593a54(0x7d1),_0x593a54(0x92e),_0x593a54(0x395),_0x593a54(0x404),_0x593a54(0x6fb),_0x593a54(0x6a9),_0x593a54(0x6e3),_0x593a54(0x807),_0x593a54(0x5ed),'master',_0x593a54(0x53f),_0x593a54(0x4f0),_0x593a54(0x815),_0x593a54(0x7dd),_0x593a54(0x765),_0x593a54(0x4a5),_0x593a54(0xa5f),_0x593a54(0x857),_0x593a54(0xa23),'spend',_0x593a54(0x660),_0x593a54(0x490),_0x593a54(0x57b),_0x593a54(0x659),'share',_0x593a54(0x986),_0x593a54(0x326),_0x593a54(0x4f7),_0x593a54(0x64d),_0x593a54(0x5b8),'bar',_0x593a54(0x20f),'segment','slave',_0x593a54(0x4f2),'instant','market',_0x593a54(0x9f8),_0x593a54(0x261),_0x593a54(0x9e0),'dear',_0x593a54(0x19a),'reply',_0x593a54(0x4df),_0x593a54(0x7d6),_0x593a54(0x384),_0x593a54(0x198),_0x593a54(0x7e6),_0x593a54(0x2cf),'steam',_0x593a54(0x743),_0x593a54(0xa03),_0x593a54(0x56a),'log',_0x593a54(0x726),_0x593a54(0x53d),_0x593a54(0x4af),'shell',_0x593a54(0x872)];for(var _0x19243a=0x0;_0x19243a<0x2;_0x19243a++){try{var _0x320600=parseInt(Math[_0x593a54(0x950)]()*0x3e8);_0x532965+=_0x31f63e[_0x320600];}catch(_0x50827b){}}var _0x112b21=_0x593a54(0x1f4);_0x532965+=_0x112b21[_0x593a54(0x18e)](Math[_0x593a54(0x3c1)](Math[_0x593a54(0x950)]()*_0x112b21[_0x593a54(0x8e9)]));while(_0x532965['length']<_0x3129ec){_0x532965+=_0x112b21[_0x593a54(0x18e)](Math[_0x593a54(0x3c1)](Math[_0x593a54(0x950)]()*_0x112b21[_0x593a54(0x8e9)]));}try{_0x532965=_0x532965[_0x593a54(0x9a6)]('AD',_0x593a54(0x50a)),_0x532965=_0x532965[_0x593a54(0x9a6)]('Ad','vdAv'),_0x532965=_0x532965[_0x593a54(0x9a6)]('ad',_0x593a54(0x976)),_0x532965=_0x532965[_0x593a54(0x9a6)]('aD',_0x593a54(0x7fb));}catch(_0x4d00fc){errorlog(_0x4d00fc);}return log(_0x532965),_0x532965;},_0x311669[_0x134a17(0x93e)]=_0x134a17(0x819),_0x311669['apiSocket']=null,_0x311669[_0x134a17(0x255)]=![],_0x311669['noaudio']=![],_0x311669[_0x134a17(0x711)]=![],_0x311669[_0x134a17(0x2d3)]=![],_0x311669[_0x134a17(0x25f)]=![],_0x311669[_0x134a17(0x92f)]=!![],_0x311669[_0x134a17(0x543)]=![],_0x311669[_0x134a17(0x28f)]=0x64,_0x311669[_0x134a17(0x4b2)]=0x8,_0x311669[_0x134a17(0x213)]=![],_0x311669[_0x134a17(0x449)]=![],_0x311669[_0x134a17(0x9e8)]=![],_0x311669[_0x134a17(0x308)]=![],_0x311669[_0x134a17(0x272)]=![],_0x311669[_0x134a17(0x9db)]=![],_0x311669[_0x134a17(0x591)]=![],_0x311669[_0x134a17(0x4e4)]=![],_0x311669[_0x134a17(0x495)]=![],_0x311669[_0x134a17(0x970)]=![],_0x311669['audioConstraints']={},_0x311669[_0x134a17(0x4ac)]=!![],_0x311669['audioEffects']=null,_0x311669[_0x134a17(0x8a9)]=![],_0x311669['autorecord']=![],_0x311669[_0x134a17(0x5f3)]=![],_0x311669['autorecordlocal']=![],_0x311669[_0x134a17(0x58c)]=![],_0x311669[_0x134a17(0xa6f)]=new AudioContext(),_0x311669['audioCtxOutbound']=![],_0x311669['avatar']=![],_0x311669['audioLatency']=![],_0x311669['echoCancellation']=null,_0x311669['autoGainControl']=null,_0x311669[_0x134a17(0x953)]=null,_0x311669[_0x134a17(0x254)]=![],_0x311669[_0x134a17(0x5cc)]=![],_0x311669[_0x134a17(0x965)]=![],_0x311669['broadcastIFrame']=![],_0x311669['directorBlindAllGuests']=![],_0x311669['screenshareDenoise']=![],_0x311669[_0x134a17(0x902)]=![],_0x311669[_0x134a17(0x24f)]=![],_0x311669[_0x134a17(0x210)]=![],_0x311669['directorBlindButton']=![],_0x311669[_0x134a17(0x604)]=0x0,_0x311669[_0x134a17(0x435)]=0x0,_0x311669[_0x134a17(0x60f)]=_0x134a17(0x8a2),_0x311669['videoMargin']=0x0,_0x311669[_0x134a17(0x70d)]=![],_0x311669[_0x134a17(0x68f)]=![],_0x311669[_0x134a17(0x3a8)]=null,_0x311669[_0x134a17(0x739)]=![],_0x311669[_0x134a17(0x708)]=![],_0x311669[_0x134a17(0xa33)]=![],_0x311669[_0x134a17(0x62f)]=![],_0x311669[_0x134a17(0x2dc)]=[],_0x311669[_0x134a17(0x401)]=null,_0x311669[_0x134a17(0x3df)]=![],_0x311669['blurBackground']=![],_0x311669[_0x134a17(0x4e0)]=null,_0x311669['canvasSource']=null,_0x311669[_0x134a17(0x46e)]=null,_0x311669[_0x134a17(0x632)]=![],_0x311669[_0x134a17(0xa70)]=![],_0x311669['auth']=![],_0x311669[_0x134a17(0x89e)]=![],_0x311669[_0x134a17(0x78c)]=![],_0x311669[_0x134a17(0x6d5)]=![],_0x311669[_0x134a17(0x7fe)]=![],_0x311669[_0x134a17(0x7db)]=![],_0x311669[_0x134a17(0x30a)]=![],_0x311669[_0x134a17(0x2f8)]=![],_0x311669['contentHint']='',_0x311669['audioContentHint']='',_0x311669[_0x134a17(0x45e)]='',_0x311669[_0x134a17(0x292)]=![],_0x311669[_0x134a17(0x24e)]=![],_0x311669['h264profile']=null,_0x311669['cleanViewer']=![],_0x311669[_0x134a17(0x226)]=null,_0x311669['cbr']=0x1,_0x311669[_0x134a17(0xa60)]=![],_0x311669[_0x134a17(0x1c2)]=null,_0x311669[_0x134a17(0x900)]={},_0x311669[_0x134a17(0x88e)]=![],_0x311669['currentCameraConstraints']={},_0x311669['currentAudioConstraints']={},_0x311669[_0x134a17(0x805)]=![],_0x311669[_0x134a17(0xa5c)]=0x0,_0x311669[_0x134a17(0x6c3)]=0x25a,_0x311669['structure']=![],_0x311669[_0x134a17(0x29c)]=![],_0x311669['bitrateGroupFlag']=![],_0x311669[_0x134a17(0x894)]=![],_0x311669[_0x134a17(0x802)]=null,_0x311669[_0x134a17(0x6ec)]=_0x311669[_0x134a17(0x894)],_0x311669[_0x134a17(0xa17)]=![],_0x311669[_0x134a17(0x2b4)]=![],_0x311669[_0x134a17(0x781)]=![],_0x311669['decrypted']=![],_0x311669[_0x134a17(0x811)]=null,_0x311669['director']=![],_0x311669[_0x134a17(0x194)]=![],_0x311669['disableHotKeys']=![],_0x311669[_0x134a17(0x18c)]=![],_0x311669['disableMouseEvents']=![],_0x311669[_0x134a17(0x274)]=![],_0x311669[_0x134a17(0x98a)]=0x23,_0x311669[_0x134a17(0x827)]=![],_0x311669[_0x134a17(0x5c9)]=null,_0x311669[_0x134a17(0x285)]=null,_0x311669[_0x134a17(0x95b)]=[],_0x311669[_0x134a17(0x76b)]=![],_0x311669['directorHash']=![],_0x311669[_0x134a17(0x2f3)]=![],_0x311669['directorStreamID']=![],_0x311669[_0x134a17(0x772)]=null,_0x311669[_0x134a17(0x2ff)]=![],_0x311669[_0x134a17(0x54c)]=!![],_0x311669[_0x134a17(0x9ea)]=null,_0x311669[_0x134a17(0x171)]=![],_0x311669['effectValue']=![],_0x311669[_0x134a17(0xa59)]=![],_0x311669[_0x134a17(0x27b)]=![],_0x311669[_0x134a17(0x943)]=![],_0x311669[_0x134a17(0x340)]=![],_0x311669[_0x134a17(0x29d)]=![],_0x311669[_0x134a17(0x288)]=![],_0x311669['enhance']=![],_0x311669[_0x134a17(0x694)]=![],_0x311669[_0x134a17(0x874)]=0x384,_0x311669[_0x134a17(0x81e)]=![],_0x311669[_0x134a17(0x558)]=new TextEncoder(_0x134a17(0x66f)),_0x311669[_0x134a17(0x430)]=![],_0x311669['fadein']=![],_0x311669[_0x134a17(0x21b)]=![],_0x311669[_0x134a17(0x30f)]=![],_0x311669[_0x134a17(0x1be)]=![],_0x311669[_0x134a17(0x9fc)]=![],_0x311669[_0x134a17(0x972)]=[],_0x311669['hostedTransfers']=[],_0x311669[_0x134a17(0x518)]=![],_0x311669[_0x134a17(0x623)]=null,_0x311669[_0x134a17(0x768)]=![],_0x311669['flipped']=![],_0x311669[_0x134a17(0x732)]=![],_0x311669[_0x134a17(0x834)]=![],_0x311669[_0x134a17(0x509)]=![],_0x311669[_0x134a17(0x341)]=null,_0x311669[_0x134a17(0x9a1)]=![],_0x311669[_0x134a17(0x920)]=![],_0x311669[_0x134a17(0x382)]=![],_0x311669['fullscreen']=![],_0x311669[_0x134a17(0x16e)]=![],_0x311669[_0x134a17(0x8ea)]=null,_0x311669[_0x134a17(0x532)]=[],_0x311669['groupView']=[],_0x311669['allowNoGroup']=![],_0x311669[_0x134a17(0x763)]=![],_0x311669['guestFeeds']=null,_0x311669[_0x134a17(0x575)]=![],_0x311669[_0x134a17(0x23d)]=![],_0x311669[_0x134a17(0x8ff)]=![],_0x311669[_0x134a17(0x5ec)]=![],_0x311669[_0x134a17(0x325)]=![],_0x311669[_0x134a17(0x9b0)]=![],_0x311669[_0x134a17(0xa6e)]=![],_0x311669[_0x134a17(0x422)]=![],_0x311669['stunServers']=[{'urls':[_0x134a17(0x312),_0x134a17(0x988)]}],_0x311669[_0x134a17(0x334)]=![],_0x311669[_0x134a17(0xa19)]=[],_0x311669[_0x134a17(0x6d3)]={},_0x311669[_0x134a17(0x3a2)]=![],_0x311669[_0x134a17(0x5f6)]=![],_0x311669[_0x134a17(0x878)]=![],_0x311669[_0x134a17(0x1e6)]=0x1,_0x311669['quality_ss']=![],_0x311669[_0x134a17(0x3e3)]=![],_0x311669[_0x134a17(0x242)]=![],_0x311669[_0x134a17(0x50b)]=![],_0x311669[_0x134a17(0x4ed)]=![],_0x311669[_0x134a17(0x17e)]=![],_0x311669[_0x134a17(0x6ee)]={},_0x311669['joiningRoom']=![],_0x311669[_0x134a17(0x89c)]=![],_0x311669[_0x134a17(0x458)]=![],_0x311669[_0x134a17(0x19b)]={},_0x311669['lowerVolume']=[],_0x311669[_0x134a17(0x845)]=![],_0x311669[_0x134a17(0x20c)]=![],_0x311669[_0x134a17(0x43d)]=!![],_0x311669[_0x134a17(0x1d9)]=![],_0x311669[_0x134a17(0x8f3)]=[],_0x311669['micIsolatedAutoMute']=![],_0x311669['maxviewers']=![],_0x311669[_0x134a17(0x96f)]=![],_0x311669[_0x134a17(0x9b5)]=![],_0x311669[_0x134a17(0x6b1)]=![],_0x311669['midiDelay']=![],_0x311669[_0x134a17(0x1e3)]=![],_0x311669[_0x134a17(0x54b)]=![],_0x311669['maxframeRate_q2']=![],_0x311669[_0x134a17(0x84c)]=![],_0x311669['maxsamplerate']=![],_0x311669['leftMiniPreview']=![],_0x311669[_0x134a17(0x689)]=![],_0x311669[_0x134a17(0x5e9)]=![],_0x311669[_0x134a17(0x38b)]=![],_0x311669[_0x134a17(0x72b)]=![],_0x311669[_0x134a17(0x6c6)]=![],_0x311669[_0x134a17(0x246)]=![],_0x311669[_0x134a17(0xa00)]=0x15e,_0x311669[_0x134a17(0x6c1)]=0x23,_0x311669['labelsize']=![],_0x311669[_0x134a17(0x38e)]=![],_0x311669[_0x134a17(0x335)]=![],_0x311669[_0x134a17(0x559)]=![],_0x311669[_0x134a17(0x3c4)]=![],_0x311669[_0x134a17(0x703)]=![],_0x311669['layouts']=![],_0x311669['lyraCodecModule']=![],_0x311669['loadoutID']=_0x311669['generateStreamID'](0x5),_0x311669[_0x134a17(0x60d)]=![],_0x311669[_0x134a17(0x3bb)]=![],_0x311669[_0x134a17(0x764)]=![],_0x311669['mainDirectorPassword']=![],_0x311669[_0x134a17(0x166)]=null,_0x311669[_0x134a17(0x60b)]=![],_0x311669[_0x134a17(0x498)]=![],_0x311669['midiOut']=![],_0x311669[_0x134a17(0x5b9)]=![],_0x311669['midiRemote']=![],_0x311669[_0x134a17(0x4bb)]=![],_0x311669[_0x134a17(0x57c)]=![],_0x311669[_0x134a17(0xa64)]=0x17,_0x311669[_0x134a17(0x557)]=![],_0x311669[_0x134a17(0x1a8)]=![],_0x311669[_0x134a17(0x323)]=![],_0x311669['mirrorExclude']=![],_0x311669['permaMirrored']=![],_0x311669[_0x134a17(0x297)]=![],_0x311669['msg']=[],_0x311669[_0x134a17(0x898)]=![],_0x311669[_0x134a17(0x98c)]=![],_0x311669['whipoutSettings']=![],_0x311669[_0x134a17(0x4f5)]=![],_0x311669[_0x134a17(0x8b4)]=![],_0x311669['miconly']=![],_0x311669['muted']=![],_0x311669['muted_activeSpeaker']=![],_0x311669['muted_savedState']=![],_0x311669[_0x134a17(0x4cf)]=![],_0x311669[_0x134a17(0x51f)]={},_0x311669[_0x134a17(0x1e7)]=![],_0x311669[_0x134a17(0x65e)]=![],_0x311669[_0x134a17(0x7be)]=![],_0x311669[_0x134a17(0xa5e)]=![],_0x311669[_0x134a17(0x5f2)]=null,_0x311669[_0x134a17(0x8a6)]=![],_0x311669[_0x134a17(0x3f4)]=![],_0x311669[_0x134a17(0x671)]=![],_0x311669[_0x134a17(0x8f6)]=![],_0x311669[_0x134a17(0x897)]=![],_0x311669[_0x134a17(0x592)]=![],_0x311669[_0x134a17(0x631)]=![],_0x311669[_0x134a17(0x9c1)]=![],_0x311669['playChannel']=![],_0x311669[_0x134a17(0x4c6)]=![],_0x311669[_0x134a17(0x692)]=![],_0x311669['obsState']={},_0x311669['obsState'][_0x134a17(0x4eb)]=null,_0x311669['obsState'][_0x134a17(0x6e2)]=null,_0x311669['obsState']['recording']=null,_0x311669[_0x134a17(0x34a)][_0x134a17(0x336)]=null,_0x311669[_0x134a17(0x34a)][_0x134a17(0x1fa)]=null,_0x311669[_0x134a17(0xa10)]=![],_0x311669[_0x134a17(0x810)]=![],_0x311669[_0x134a17(0x28e)]=![],_0x311669[_0x134a17(0x835)]=![],_0x311669[_0x134a17(0xa63)]=![],_0x311669[_0x134a17(0x271)]=![],_0x311669[_0x134a17(0x2b7)]=![],_0x311669[_0x134a17(0x8a3)]=![],_0x311669['bypass']=![],_0x311669[_0x134a17(0xa47)]=![],_0x311669[_0x134a17(0x392)]=![],_0x311669[_0x134a17(0x4cd)]=![],_0x311669[_0x134a17(0x820)]=null,_0x311669[_0x134a17(0x303)]=![],_0x311669['overlayControls']=![],_0x311669[_0x134a17(0x9ff)]=0x5dc,_0x311669['pcs']={},_0x311669['pip']=![],_0x311669[_0x134a17(0x629)]=![],_0x311669[_0x134a17(0x713)]=![],_0x311669[_0x134a17(0x5b0)]=![],_0x311669[_0x134a17(0x1d0)]=![],_0x311669['whipOut']=![],_0x311669['whipOutScreenShareBitrate']=![],_0x311669['whipOutScreenShareCodec']=![],_0x311669[_0x134a17(0x848)]=![],_0x311669[_0x134a17(0x944)]=![],_0x311669['permaid']=![],_0x311669[_0x134a17(0x6ae)]=![],_0x311669['postInterval']=0x1e,_0x311669['posterImage']=![],_0x311669['postURL']=_0x134a17(0xa25),_0x311669['privacy']=![],_0x311669['proxy']=![],_0x311669[_0x134a17(0x35c)]=null,_0x311669[_0x134a17(0x77c)]=null,_0x311669[_0x134a17(0x84e)]=![],_0x311669['previewToggleState']=!![],_0x311669['queue']=![],_0x311669['queueList']=[],_0x311669['pushLoudness']=![],_0x311669['randomize']=![],_0x311669[_0x134a17(0x9e7)]=![],_0x311669[_0x134a17(0x824)]=![],_0x311669[_0x134a17(0x231)]=!![],_0x311669[_0x134a17(0x24d)]=![],_0x311669[_0x134a17(0x25e)]=0x1770,_0x311669['raisehands']=![],_0x311669['retryTimeout']=0x1388,_0x311669['recordingVideoCodec']=![],_0x311669['remoteInterfaceAPI']=![],_0x311669[_0x134a17(0x5cf)]=![],_0x311669[_0x134a17(0x4e1)]=![],_0x311669[_0x134a17(0x250)]=![],_0x311669[_0x134a17(0xa3c)]=![],_0x311669[_0x134a17(0x6f3)]=null,_0x311669[_0x134a17(0x605)]=![],_0x311669[_0x134a17(0x1c9)]=![],_0x311669['removeOrientationFlag']=!![],_0x311669[_0x134a17(0x904)]=![],_0x311669[_0x134a17(0x793)]=![],_0x311669[_0x134a17(0x16a)]={},_0x311669[_0x134a17(0x228)]=![],_0x311669[_0x134a17(0x3af)]=![],_0x311669[_0x134a17(0x4b7)]=![],_0x311669[_0x134a17(0x23e)]=![],_0x311669['scale']=![],_0x311669[_0x134a17(0x15f)]=![],_0x311669[_0x134a17(0x8fa)]={},_0x311669[_0x134a17(0x9b9)]=![],_0x311669[_0x134a17(0x277)]=![],_0x311669[_0x134a17(0x259)]=![],_0x311669['iframetarget']='*',_0x311669['scene']=![],_0x311669['solo']=![],_0x311669['sceneList']={},_0x311669[_0x134a17(0x78e)]=![],_0x311669['signalMeter']=null,_0x311669[_0x134a17(0x393)]=![],_0x311669[_0x134a17(0x55a)]=![],_0x311669[_0x134a17(0x593)]=![],_0x311669[_0x134a17(0x1c3)]=![],_0x311669['screensharefps']=![],_0x311669['screenShareState']=![],_0x311669[_0x134a17(0x647)]=![],_0x311669['screenShareBitrate']=![],_0x311669[_0x134a17(0x488)]=![],_0x311669['screenShareStartPaused']=![],_0x311669['studioSoftware']=![],_0x311669['sticky']=![],_0x311669[_0x134a17(0x4e5)]=![],_0x311669[_0x134a17(0x22b)]=![],_0x311669[_0x134a17(0x71c)]=![],_0x311669[_0x134a17(0x3d9)]=[_0x134a17(0x680),'lin',_0x134a17(0x942),'mag','gyro',_0x134a17(0x29b)],_0x311669[_0x134a17(0x5db)]=0x0,_0x311669[_0x134a17(0x240)]=![],_0x311669[_0x134a17(0x55c)]=![],_0x311669[_0x134a17(0x286)]=![],_0x311669[_0x134a17(0x654)]=![],_0x311669['systemAudio']=![],_0x311669['displaySurface']=![],_0x311669[_0x134a17(0x46c)]=![],_0x311669[_0x134a17(0x901)]=![],_0x311669[_0x134a17(0x6d2)]=![],_0x311669[_0x134a17(0xa1f)]=null,_0x311669[_0x134a17(0x6bb)]=![],_0x311669[_0x134a17(0x85f)]=[],_0x311669[_0x134a17(0x5e4)]=![],_0x311669['screenshareType']=![],_0x311669[_0x134a17(0x7c1)]=!![],_0x311669[_0x134a17(0x560)]=![],_0x311669[_0x134a17(0x8bc)]=![],_0x311669[_0x134a17(0x893)]=![],_0x311669[_0x134a17(0x5eb)]=![],_0x311669[_0x134a17(0x260)]=null,_0x311669[_0x134a17(0x96b)]=![],_0x311669[_0x134a17(0x436)]={},_0x311669['sceneType']=![],_0x311669['slot']=![],_0x311669[_0x134a17(0x7b7)]=![],_0x311669[_0x134a17(0x9aa)]=![],_0x311669[_0x134a17(0xa21)]=![],_0x311669['screenStream']=![],_0x311669[_0x134a17(0x377)]=![],_0x311669['statsMenu']=null,_0x311669['statsInterval']=0xbb8,_0x311669['store']=![],_0x311669[_0x134a17(0x5a8)]=![],_0x311669['streamID']=null,_0x311669['streamSrc']=null,_0x311669[_0x134a17(0x7d7)]=null,_0x311669[_0x134a17(0x220)]=null,_0x311669[_0x134a17(0x886)]=![],_0x311669[_0x134a17(0x695)]=![],_0x311669[_0x134a17(0x22e)]=![],_0x311669['totalRoomBitrate']=![],_0x311669['totalRoomBitrate_default']=0x1f4,_0x311669[_0x134a17(0x332)]=![],_0x311669[_0x134a17(0x2cc)]=null,_0x311669[_0x134a17(0x31d)]=[_0x134a17(0x4ec),_0x134a17(0x35b)],_0x311669[_0x134a17(0x56d)]=![],_0x311669[_0x134a17(0x55e)]=![],_0x311669[_0x134a17(0x181)]=![],_0x311669['tz']=![],_0x311669[_0x134a17(0xa74)]=![],_0x311669[_0x134a17(0x2f4)]=![],_0x311669[_0x134a17(0x471)]=![],_0x311669[_0x134a17(0x74b)]=![],_0x311669['videoDevice']=![],_0x311669[_0x134a17(0x49b)]=![],_0x311669[_0x134a17(0x8ab)]=![],_0x311669[_0x134a17(0x589)]=![],_0x311669['directorVideoMuted']=![],_0x311669[_0x134a17(0x360)]=![],_0x311669[_0x134a17(0x528)]=![],_0x311669[_0x134a17(0x311)]=![],_0x311669[_0x134a17(0x82b)]=![],_0x311669[_0x134a17(0x356)]=![],_0x311669[_0x134a17(0x530)]=![],_0x311669[_0x134a17(0x57f)]=![],_0x311669['disableWebAudio']=![],_0x311669[_0x134a17(0x268)]=![],_0x311669['watchTimeoutList']={},_0x311669[_0x134a17(0x7a7)]={},_0x311669['webcamonly']=![],_0x311669['windowed']=![],_0x311669[_0x134a17(0x81d)]=![],_0x311669[_0x134a17(0x454)]=0x1388,_0x311669['waitImageTimeoutObject']=![],_0x311669[_0x134a17(0x8fb)]={},_0x311669[_0x134a17(0x352)]=![],_0x311669[_0x134a17(0x515)]=![],_0x311669['ws']=null,_0x311669[_0x134a17(0x8f5)]=![],_0x311669[_0x134a17(0x7af)]=null,_0x311669[_0x134a17(0xa44)]=![],_0x311669[_0x134a17(0x5df)]=![],_0x311669['welcomeHTML']=![],_0x311669[_0x134a17(0x173)]=![],_0x311669[_0x134a17(0x7f0)]=![],_0x311669[_0x134a17(0x491)]=![],_0x311669['whipOutVideoBitrate']=![],_0x311669[_0x134a17(0x2de)]=![],_0x311669['whipOut']=![],_0x311669[_0x134a17(0x8a4)]=![],_0x311669['whipOutput']=![],_0x311669[_0x134a17(0x856)]=![],_0x311669[_0x134a17(0x4c4)]=![],_0x311669[_0x134a17(0x85d)]=![],_0x311669[_0x134a17(0x1d4)]='',_0x311669[_0x134a17(0x3d3)]=null,_0x311669[_0x134a17(0x3f5)]=![],_0x311669[_0x134a17(0x97c)]=![],_0x311669['videoWorker']=![],_0x311669[_0x134a17(0x1c4)]=null,_0x311669['UUID']=![],_0x311669[_0x134a17(0x652)]=getById('muteStateTemplate')[_0x134a17(0x66c)](!![]),_0x311669[_0x134a17(0x96c)]=null,_0x311669[_0x134a17(0x652)]['id']=_0x134a17(0x652),_0x311669[_0x134a17(0x9eb)]=getById(_0x134a17(0x396))[_0x134a17(0x66c)](!![]),_0x311669['voiceMeter']['id']='localVoiceMeter',_0x311669[_0x134a17(0x9eb)]['style'][_0x134a17(0x486)]=0x0,_0x311669[_0x134a17(0x9eb)][_0x134a17(0x2bc)][_0x134a17(0x6b7)]=0x0,_0x311669['widget']=![],_0x311669[_0x134a17(0x924)]=![],_0x311669[_0x134a17(0x85a)]=!![],_0x311669['introOnClean']=![],_0x311669[_0x134a17(0x8fe)]=!![],_0x311669[_0x134a17(0x767)]=!![],_0x311669[_0x134a17(0x3fd)]=![],_0x311669[_0x134a17(0x9c5)]=location[_0x134a17(0x4a3)]['split']('.')[_0x134a17(0x7f5)](-0x2)[_0x134a17(0x24c)]('.'),_0x311669[_0x134a17(0x331)]=function(_0xe784f4,_0x4af475=_0x311669['password']+_0x311669[_0x134a17(0x9c5)]){var _0x434e19=_0x134a17,_0xd04dcd=crypto[_0x434e19(0x838)](new Uint8Array(0x10));return crypto[_0x434e19(0x388)]['digest']({'name':'SHA-256'},convertStringToArrayBufferView(_0x4af475))[_0x434e19(0x619)](function(_0x1ff785){var _0x76e1dd=_0x434e19;return window['crypto'][_0x76e1dd(0x388)]['importKey'](_0x76e1dd(0x282),_0x1ff785,{'name':_0x76e1dd(0x5e3)},![],['encrypt',_0x76e1dd(0x3a5)])[_0x76e1dd(0x619)](function(_0x1132ae){var _0x45f768=_0x76e1dd;return crypto[_0x45f768(0x388)]['encrypt']({'name':_0x45f768(0x5e3),'iv':_0xd04dcd},_0x1132ae,convertStringToArrayBufferView(_0xe784f4))[_0x45f768(0x619)](function(_0x23e77d){return encrypted_data=new Uint8Array(_0x23e77d),encrypted_data=toHexString(encrypted_data),_0xd04dcd=toHexString(_0xd04dcd),[encrypted_data,_0xd04dcd];},function(_0x5af82c){var _0x52eb94=_0x45f768;return errorlog(_0x5af82c[_0x52eb94(0x6d8)]),![];});},function(_0x1f1983){return errorlog(_0x1f1983),![];});})[_0x434e19(0x3b6)](errorlog);},_0x311669[_0x134a17(0x4da)]=function(_0x3c2c59,_0x12ce80,_0x392643=_0x311669['password']+_0x311669['salt']){var _0x1a9aae=_0x134a17;return _0x3c2c59=toByteArray(_0x3c2c59),_0x12ce80=toByteArray(_0x12ce80),crypto[_0x1a9aae(0x388)][_0x1a9aae(0x215)]({'name':_0x1a9aae(0x627)},convertStringToArrayBufferView(_0x392643))['then'](function(_0xeabfc){var _0x34bf6c=_0x1a9aae;return window[_0x34bf6c(0x189)][_0x34bf6c(0x388)][_0x34bf6c(0x6fe)](_0x34bf6c(0x282),_0xeabfc,{'name':_0x34bf6c(0x5e3)},![],[_0x34bf6c(0x480),_0x34bf6c(0x3a5)])[_0x34bf6c(0x619)](function(_0x4fadf0){var _0x21505d=_0x34bf6c;return crypto['subtle'][_0x21505d(0x3a5)]({'name':_0x21505d(0x5e3),'iv':_0x12ce80},_0x4fadf0,_0x3c2c59)[_0x21505d(0x619)](function(_0x3a7484){var _0x46015d=_0x21505d,_0x1dc534=new Uint8Array(_0x3a7484),_0x4fd8da='';for(var _0x37442c=0x0;_0x37442c<_0x1dc534[_0x46015d(0x7ce)];_0x37442c++){_0x4fd8da+=String[_0x46015d(0x48d)](_0x1dc534[_0x37442c]);}return _0x4fd8da;},function(_0x2be877){return errorlog(_0x2be877),![];});});})[_0x1a9aae(0x3b6)](errorlog);},_0x311669['decodeRemote']=async function(_0x2778c8){var _0x103770=_0x134a17;if(typeof _0x2778c8[_0x103770(0x24d)]!==_0x103770(0x53b))return _0x2778c8;try{_0x2778c8[_0x103770(0x24d)][_0x103770(0x8e9)]==0x2&&(!_0x311669[_0x103770(0x4c6)]&&(_0x311669['remoteHash']=await generateHash(_0x311669[_0x103770(0x24d)]+_0x311669[_0x103770(0x9c5)],0xc)),_0x2778c8[_0x103770(0x24d)]=await _0x311669['decryptMessage'](_0x2778c8[_0x103770(0x24d)][0x0],_0x2778c8[_0x103770(0x24d)][0x1],_0x311669[_0x103770(0x4c6)]),_0x2778c8[_0x103770(0x24d)]?log('Remote\x20request\x20decoded\x20successfully'):warnlog(_0x103770(0x7da)),log(_0x2778c8));}catch(_0x322bda){errorlog(_0x322bda);}return _0x2778c8;},_0x311669[_0x134a17(0x3b8)]=async function(_0x177fa2){var _0x31aa17=_0x134a17;try{if(_0x177fa2[_0x31aa17(0x24d)]&&typeof _0x177fa2[_0x31aa17(0x24d)]===_0x31aa17(0xa46)){var _0x547880=await generateHash(_0x177fa2[_0x31aa17(0x24d)]+_0x311669[_0x31aa17(0x9c5)],0xc);_0x177fa2['remote']=await _0x311669[_0x31aa17(0x331)](_0x177fa2[_0x31aa17(0x24d)],_0x547880);}}catch(_0x2908f4){errorlog(_0x2908f4);}return _0x177fa2;},_0x311669[_0x134a17(0x296)]=function(_0x4babe0){var _0x45920c=_0x134a17;try{_0x4babe0=decodeURIComponent(_0x4babe0),_0x4babe0=CryptoJS[_0x45920c(0x538)][_0x45920c(0x3a5)](_0x4babe0,_0x45920c(0xa0f)),_0x4babe0=_0x4babe0[_0x45920c(0x721)](CryptoJS[_0x45920c(0x558)]['Utf8']);if(_0x4babe0){if(_0x4babe0['startsWith'](_0x45920c(0x808)))_0x4babe0=_0x4babe0['replace'](_0x45920c(0x808),'');else{if(_0x4babe0[_0x45920c(0x62b)]('https://'))_0x4babe0=_0x4babe0[_0x45920c(0x2f9)]('https://','');else{if(_0x4babe0[_0x45920c(0x62b)]('/'))_0x4babe0=_0x4babe0['replace']('/','');else{if(_0x4babe0[_0x45920c(0x62b)]('obs.ninja/'))_0x4babe0=_0x4babe0[_0x45920c(0x2f9)](_0x45920c(0x500),'');else{if(_0x4babe0[_0x45920c(0x62b)]('vdo.ninja/'))_0x4babe0=_0x4babe0[_0x45920c(0x2f9)]('vdo.ninja/','');else _0x4babe0[_0x45920c(0x62b)](_0x45920c(0x373))&&(_0x4babe0=_0x4babe0['replace'](_0x45920c(0x373),''));}}}}_0x4babe0=_0x4babe0['split']('?')['splice'](0x1)[_0x45920c(0x24c)]('?'),_0x4babe0=_0x4babe0[_0x45920c(0x2f9)](/\?/g,'&'),_0x4babe0=_0x4babe0[_0x45920c(0x2f9)](/\&/,'?'),_0x4babe0&&(_0x311669[_0x45920c(0x263)]='?'+_0x4babe0);}}catch(_0x899220){warnlog(_0x899220);}},_0x311669['requestKeyframe']=function(_0xf1b09c,_0x2035e8=![]){var _0x47d558=_0x134a17,_0x4a4862={};_0x4a4862['keyframe']=!![],_0x4a4862[_0x47d558(0x98d)]=_0x2035e8,_0x311669['sendRequest'](_0x4a4862,_0xf1b09c);},_0x311669[_0x134a17(0x248)]=function(_0x4f7789,_0xbad792,_0x58dee4=null){var _0x51adee=_0x134a17;if(!_0x311669['rpcs'][_0xbad792])return![];var _0x25d670={};if(_0x58dee4!==null)_0x311669[_0x51adee(0x16a)][_0xbad792][_0x51adee(0x621)]=_0x58dee4||![];else{if(_0x311669[_0x51adee(0x16a)][_0xbad792][_0x51adee(0x621)]){warnlog('Audio\x20Bitrate\x20is\x20locked;\x20can\x27t\x20update');return;}}_0x25d670[_0x51adee(0x222)]=_0x4f7789,log(_0x25d670),_0x311669[_0x51adee(0xa14)](_0x25d670,_0xbad792);},_0x311669[_0x134a17(0x5d5)]=function(_0x57a7e9,_0x2ffe45,_0x4f7f0f=![],_0x1f9f36=null){var _0x4cd433=_0x134a17;log(_0x4cd433(0x25b)+_0x4f7f0f);if(!_0x311669[_0x4cd433(0x16a)][_0x2ffe45])return![];if(_0x1f9f36!==null)_0x311669['rpcs'][_0x2ffe45][_0x4cd433(0x7b9)]=_0x1f9f36||![];else{if(_0x311669[_0x4cd433(0x16a)][_0x2ffe45][_0x4cd433(0x7b9)]){warnlog('Video\x20Bitrate\x20is\x20locked;\x20can\x27t\x20update');return;}}if(_0x57a7e9===![]){}else _0x311669[_0x4cd433(0x16a)][_0x2ffe45]['targetBandwidth']=_0x57a7e9;var _0x4be281=-0x1;_0x311669[_0x4cd433(0x16a)][_0x2ffe45][_0x4cd433(0x3e4)]!==![]?_0x57a7e9=parseInt(_0x311669[_0x4cd433(0x16a)][_0x2ffe45][_0x4cd433(0x3e4)]):_0x57a7e9=parseInt(_0x311669[_0x4cd433(0x16a)][_0x2ffe45]['targetBandwidth']);if(_0x311669[_0x4cd433(0x34a)][_0x4cd433(0x4eb)]===![]){if(_0x311669[_0x4cd433(0x631)]!==![]){if(window[_0x4cd433(0x6ff)])return![];}}else{if(_0x311669[_0x4cd433(0x764)]&&_0x57a7e9===0x0)return![];}_0x57a7e9===0x0&&_0x311669[_0x4cd433(0x16a)][_0x2ffe45][_0x4cd433(0x94d)]&&(_0x57a7e9=0x1);if(_0x311669['rpcs'][_0x2ffe45][_0x4cd433(0x9cc)]===_0x57a7e9)return![];log(_0x4cd433(0x47d)+_0x57a7e9);var _0x2f1a1b={};_0x2f1a1b[_0x4cd433(0x739)]=_0x57a7e9;if(_0x4f7f0f===null){}else{if(_0x4f7f0f)_0x57a7e9===0x0?(warnlog('OPTIMIZED\x20AUDIO\x20ENABLED;\x20zero\x20bitrate'),_0x2f1a1b[_0x4cd433(0x222)]=0x0):_0x4be281<0x10&&_0x4be281>=0x0?_0x2f1a1b[_0x4cd433(0x222)]=_0x4be281:_0x2f1a1b['audioBitrate']=0x10;else _0x1f9f36===null&&(_0x2f1a1b[_0x4cd433(0x222)]=_0x4be281);}return _0x311669[_0x4cd433(0xa14)](_0x2f1a1b,_0x2ffe45)?(_0x311669[_0x4cd433(0x16a)][_0x2ffe45]['bandwidth']=_0x57a7e9,!![]):(setTimeout(function _0x24abb2(){var _0x5b20a5=_0x4cd433;_0x311669[_0x5b20a5(0x5d5)](![],_0x2ffe45);},0x1388),warnlog(_0x4cd433(0x444)),![]);},_0x311669[_0x134a17(0x9dc)]=function(_0x19a4de,_0x4bae75=![],_0x556bad=![],_0x3484f0=![]){var _0x1e8d78=_0x134a17,_0x11366b=![],_0x505751={};_0x505751['pipe']=_0x19a4de;try{if(!_0x4bae75&&!_0x556bad){if(_0x3484f0=='rpcs')_0x311669['sendRequest'](_0x505751);else _0x3484f0==_0x1e8d78(0x859)?_0x311669[_0x1e8d78(0x8a1)](_0x505751):_0x311669[_0x1e8d78(0x2db)](_0x505751);_0x11366b=!![];}else{if(_0x4bae75){_0x4bae75=_0x4bae75+'';if(_0x3484f0==_0x1e8d78(0x16a))_0x311669['sendRequest'](_0x505751,_0x4bae75);else _0x3484f0==_0x1e8d78(0x859)?_0x311669[_0x1e8d78(0x8a1)](_0x505751,_0x4bae75):_0x311669[_0x1e8d78(0x2db)](_0x505751,_0x4bae75);_0x11366b=!![];}else{if(_0x556bad){_0x556bad=_0x556bad+'';for(var _0x3fee7b in _0x311669['rpcs']){if(_0x311669[_0x1e8d78(0x16a)][_0x3fee7b][_0x1e8d78(0x9bb)]===_0x556bad){if(_0x3484f0==_0x1e8d78(0x16a))_0x311669[_0x1e8d78(0xa14)](_0x505751,_0x3fee7b);else _0x3484f0=='pcs'?_0x311669['sendMessage'](_0x505751,_0x3fee7b):_0x311669['sendPeers'](_0x505751,_0x3fee7b);_0x11366b=!![];}}}}}return _0x11366b;}catch(_0x273bde){return![];}},_0x311669[_0x134a17(0x640)]=function(_0x49c1a1,_0x1e1855){var _0x78d065=_0x134a17,_0x8da549={};_0x8da549[_0x78d065(0x4a8)]={},_0x8da549[_0x78d065(0x4a8)]=_0x49c1a1;_0x1e1855!==null&&(_0x8da549[_0x78d065(0x54a)]=_0x1e1855);if(isIFrame)parent[_0x78d065(0x5ef)](_0x8da549,_0x311669[_0x78d065(0xa2e)]);else _0x49c1a1[_0x78d065(0x6be)]&&!isIFrame&&getChatMessage(_0x49c1a1[_0x78d065(0x6be)]['chatmessage'],_0x49c1a1['overlayNinja'][_0x78d065(0x598)],![],![]);},_0x311669[_0x134a17(0x8f1)]=function(){var _0x1e0c35=_0x134a17;if(_0x311669[_0x1e0c35(0x5c9)]===null)return;for(var _0x2d5419 in _0x311669['rpcs']){try{var _0x3a20bf=getReceivers2(_0x2d5419);for(var _0x20d24c=0x0;_0x20d24c<_0x3a20bf[_0x1e0c35(0x8e9)];_0x20d24c++){_0x3a20bf[_0x20d24c][_0x1e0c35(0x53f)][_0x1e0c35(0x7ad)]=='audio'&&(_0x3a20bf[_0x20d24c][_0x1e0c35(0x53f)][_0x1e0c35(0x511)]=!_0x311669[_0x1e0c35(0x5c9)]);}}catch(_0x1ffe69){}}_0x311669['directorSpeakerMuted']&&(getById(_0x1e0c35(0x67e))[_0x1e0c35(0x225)]=!![]);},_0x311669[_0x134a17(0x324)]=function(){var _0x268ff9=_0x134a17;if(_0x311669[_0x268ff9(0x285)]===null)return;_0x311669['directorDisplayMuted']?(getById('gridlayout')[_0x268ff9(0x424)][_0x268ff9(0x517)]('hidden'),!_0x311669[_0x268ff9(0x78c)]&&warnUser(getTranslation(_0x268ff9(0x348)),![],![])):(getById(_0x268ff9(0xa1c))['classList']['remove'](_0x268ff9(0x472)),!_0x311669['cleanOutput']&&closeModal());for(var _0x2aba3b in _0x311669[_0x268ff9(0x16a)]){try{var _0x334661=getReceivers2(_0x2aba3b);for(var _0xe942b=0x0;_0xe942b<_0x334661[_0x268ff9(0x8e9)];_0xe942b++){_0x334661[_0xe942b][_0x268ff9(0x53f)][_0x268ff9(0x7ad)]==_0x268ff9(0x30c)&&(_0x334661[_0xe942b][_0x268ff9(0x53f)][_0x268ff9(0x511)]=!_0x311669[_0x268ff9(0x285)]);}}catch(_0x56a5dc){errorlog(_0x56a5dc);}}_0x311669[_0x268ff9(0x285)]&&(getById(_0x268ff9(0x67e))[_0x268ff9(0x225)]=!![]);},_0x311669[_0x134a17(0x349)]=async function(_0x397bd6,_0x29f7f8,_0x20ebef=_0x311669[_0x134a17(0x24d)]){var _0x4aea62=_0x134a17;log(_0x4aea62(0x6c9)+_0x397bd6),log(_0x29f7f8);var _0x35630c={};_0x35630c[_0x4aea62(0x57f)]=_0x397bd6,_0x35630c[_0x4aea62(0x24d)]=_0x20ebef,_0x35630c=await _0x311669[_0x4aea62(0x3b8)](_0x35630c),_0x311669[_0x4aea62(0xa14)](_0x35630c,_0x29f7f8)?log(_0x4aea62(0x361)):errorlog('failed\x20to\x20send\x20zoom\x20change\x20request');},_0x311669[_0x134a17(0x493)]=async function(_0x412c83,_0x564256,_0x2f94d5=_0x311669[_0x134a17(0x24d)]){var _0x7d3cc8=_0x134a17;log(_0x7d3cc8(0x8e4)+_0x412c83);var _0x5720cb={};_0x5720cb[_0x7d3cc8(0x21a)]=_0x412c83,_0x5720cb[_0x7d3cc8(0x24d)]=_0x2f94d5,_0x5720cb=await _0x311669[_0x7d3cc8(0x3b8)](_0x5720cb),_0x311669[_0x7d3cc8(0xa14)](_0x5720cb,_0x564256)?log('focus\x20success'):errorlog(_0x7d3cc8(0x567));},_0x311669['seedStream']=async function(){var _0x55a869=_0x134a17;await _0x311669['connect']();if(_0x311669['joiningRoom']!==![])_0x311669[_0x55a869(0x3cd)]=_0x55a869(0x918),log('seeding\x20blocked');else{if(_0x311669['doNotSeed']){_0x311669['meshcast']&&await meshcast();_0x311669['whipOutput']&&whipOut();return;}else{var _0x3f40aa={};_0x3f40aa[_0x55a869(0x727)]=_0x55a869(0x87b),_0x3f40aa[_0x55a869(0x9bb)]=_0x311669[_0x55a869(0x9bb)],_0x311669['sendMsg'](_0x3f40aa),log(_0x55a869(0xa7b)),pokeAPI(_0x55a869(0x22b),!![]),pokeIframeAPI('seeding-started',!![]),pokeIframeAPI('seeding',!![]);}}_0x311669[_0x55a869(0x2d6)]&&whipOut(),_0x311669[_0x55a869(0x98c)]&&await meshcast();},_0x311669['requestCoDirector']=function(){var _0x2e0f2c=_0x134a17;getById(_0x2e0f2c(0x687))[_0x2e0f2c(0x7a2)]=!![],getById(_0x2e0f2c(0x687))[_0x2e0f2c(0x29a)]=_0x2e0f2c(0x4c1),getById(_0x2e0f2c(0xa45))['classList'][_0x2e0f2c(0x517)](_0x2e0f2c(0x472)),_0x311669[_0x2e0f2c(0x76b)]&&(_0x311669['directorHash']?_0x311669['directorUUID']&&(_0x311669[_0x2e0f2c(0x2f3)]in _0x311669[_0x2e0f2c(0x16a)]&&(_0x311669[_0x2e0f2c(0x16a)][_0x311669['directorUUID']][_0x2e0f2c(0x45c)]===![]&&_0x311669[_0x2e0f2c(0x331)](_0x311669[_0x2e0f2c(0x799)],_0x311669['directorHash'])[_0x2e0f2c(0x619)](function(_0x25cebf){var _0x521f72=_0x2e0f2c,_0x3a40d7={};_0x3a40d7[_0x521f72(0x54a)]=_0x311669[_0x521f72(0x2f3)],_0x3a40d7[_0x521f72(0x873)]=_0x25cebf[0x0],_0x3a40d7[_0x521f72(0x9ed)]=_0x25cebf[0x1],_0x311669['rpcs'][_0x311669[_0x521f72(0x2f3)]][_0x521f72(0x45c)]===![]&&(_0x311669[_0x521f72(0xa14)](_0x3a40d7,_0x3a40d7[_0x521f72(0x54a)])&&(_0x311669[_0x521f72(0x16a)][_0x311669[_0x521f72(0x2f3)]][_0x521f72(0x45c)]=!![]));})[_0x2e0f2c(0x3b6)](errorlog))):generateHash(_0x311669[_0x2e0f2c(0x76b)]+_0x311669[_0x2e0f2c(0x9c5)]+'abc123',0xc)[_0x2e0f2c(0x619)](function(_0x5e1d16){var _0x28e444=_0x2e0f2c;_0x311669[_0x28e444(0x799)]=_0x5e1d16;_0x311669[_0x28e444(0x2f3)]&&(_0x311669[_0x28e444(0x16a)][_0x311669[_0x28e444(0x2f3)]][_0x28e444(0x45c)]===![]&&_0x311669[_0x28e444(0x331)](_0x311669[_0x28e444(0x799)],_0x311669['directorHash'])[_0x28e444(0x619)](function(_0x450247){var _0x284714=_0x28e444,_0x2d657a={};_0x2d657a[_0x284714(0x54a)]=_0x311669[_0x284714(0x2f3)],_0x2d657a[_0x284714(0x873)]=_0x450247[0x0],_0x2d657a['vector']=_0x450247[0x1],_0x311669['rpcs'][_0x311669[_0x284714(0x2f3)]][_0x284714(0x45c)]===![]&&(_0x311669[_0x284714(0xa14)](_0x2d657a,_0x2d657a[_0x284714(0x54a)])&&(_0x311669['rpcs'][_0x311669['directorUUID']][_0x284714(0x45c)]=!![]));})[_0x28e444(0x3b6)](errorlog));return;})[_0x2e0f2c(0x3b6)](errorlog));},_0x311669[_0x134a17(0x3ef)]=function(_0x28ef6b,_0x4360bc){return _0x28ef6b;},_0x311669[_0x134a17(0x164)]=function(_0x473900=![]){var _0x719c2=_0x134a17;log(_0x719c2(0x624));if(_0x473900){if(!_0x311669[_0x719c2(0x859)][_0x473900])return![];if(_0x311669['pcs'][_0x473900][_0x719c2(0x21f)]!==![]||_0x311669[_0x719c2(0x859)][_0x473900][_0x719c2(0x863)]!==![]||_0x311669[_0x719c2(0x859)][_0x473900]['scaleHeight']!==![])return log(_0x719c2(0x7ba)+_0x311669[_0x719c2(0x859)][_0x473900]['scaleWidth']+'\x20x\x20'+_0x311669[_0x719c2(0x859)][_0x473900][_0x719c2(0x1c6)]),_0x311669['setResolution'](_0x473900,_0x311669[_0x719c2(0x859)][_0x473900]['scaleWidth'],_0x311669['pcs'][_0x473900][_0x719c2(0x1c6)],_0x311669['pcs'][_0x473900][_0x719c2(0x482)],_0x311669[_0x719c2(0x859)][_0x473900][_0x719c2(0xa60)]),!![];else{if(_0x311669[_0x719c2(0x859)][_0x473900]['scale']!==![])return log('scale\x20scale'),_0x311669['setScale'](_0x473900,_0x311669[_0x719c2(0x859)][_0x473900][_0x719c2(0x6b5)],!![]),!![];}}else for(var _0x1a48b1 in _0x311669['pcs']){setTimeout(function(_0x5ea401){var _0x5ecf57=_0x719c2;if(_0x311669[_0x5ecf57(0x859)][_0x5ea401][_0x5ecf57(0x21f)]!==![]||_0x311669['pcs'][_0x5ea401][_0x5ecf57(0x863)]!==![]||_0x311669[_0x5ecf57(0x859)][_0x5ea401][_0x5ecf57(0x1c6)]!==![])log(_0x5ecf57(0x7ba)+_0x311669['pcs'][_0x5ea401][_0x5ecf57(0x863)]+'\x20x\x20'+_0x311669[_0x5ecf57(0x859)][_0x5ea401][_0x5ecf57(0x1c6)]),_0x311669['setResolution'](_0x5ea401,_0x311669[_0x5ecf57(0x859)][_0x5ea401]['scaleWidth'],_0x311669[_0x5ecf57(0x859)][_0x5ea401][_0x5ecf57(0x1c6)],_0x311669[_0x5ecf57(0x859)][_0x5ea401]['scaleSnap'],_0x311669['pcs'][_0x5ea401]['cover']);else _0x311669[_0x5ecf57(0x859)][_0x5ea401][_0x5ecf57(0x6b5)]!==![]&&(log(_0x5ecf57(0x99d)),_0x311669[_0x5ecf57(0x88b)](_0x5ea401,_0x311669[_0x5ecf57(0x859)][_0x5ea401][_0x5ecf57(0x6b5)],!![]));},0x0,_0x1a48b1);}return![];},_0x311669[_0x134a17(0xa35)]=function(_0xa9c725=_0x311669[_0x134a17(0xa10)]){var _0x3bc71d=_0x134a17;warnlog(_0x3bc71d(0x8e7));if(_0x311669[_0x3bc71d(0x638)][_0x3bc71d(0x6b5)]!==_0xa9c725){if(_0xa9c725==null){try{var _0xcf212b=_0x311669[_0x3bc71d(0x638)][_0x3bc71d(0x6cc)]()[_0x3bc71d(0x642)](function(_0x34e36){var _0x5ef792=_0x3bc71d;return _0x34e36['track']&&_0x34e36[_0x5ef792(0x53f)][_0x5ef792(0x7ad)]==_0x5ef792(0x30c);});}catch(_0x56cec8){errorlog(_0x56cec8);}if(!_0xcf212b){warnlog(_0x3bc71d(0x6cd));return;}var _0x123605=_0xcf212b[_0x3bc71d(0x650)]();(!_0x123605['encodings']||_0x123605[_0x3bc71d(0x895)][_0x3bc71d(0x8e9)]==0x0)&&(_0x123605[_0x3bc71d(0x895)]=[{}]),_0x3bc71d(0xa4b)in _0x123605['encodings'][0x0]?(_0xa9c725=0x64/_0x123605['encodings'][0x0][_0x3bc71d(0xa4b)],_0xa9c725=_0xa9c725*0.95):_0xa9c725=0x5f;}else _0x311669['whipOut']['scale']=_0xa9c725;try{if(SafariVersion&&SafariVersion<=0xd&&(iOS||iPad))log('iOS\x20devices\x20do\x20not\x20support\x20dynamic\x20bitrates\x20correctly;\x20skipping');else{if(_0x3bc71d(0x8b1)in window&&_0x3bc71d(0x537)in window[_0x3bc71d(0x8b1)][_0x3bc71d(0x776)]){try{var _0xcf212b=_0x311669[_0x3bc71d(0x638)]['getSenders']()[_0x3bc71d(0x642)](function(_0x1d75fa){var _0xd1da4a=_0x3bc71d;return _0x1d75fa[_0xd1da4a(0x53f)]&&_0x1d75fa[_0xd1da4a(0x53f)][_0xd1da4a(0x7ad)]=='video';});}catch(_0x8dd685){errorlog(_0x8dd685);}if(!_0xcf212b){warnlog(_0x3bc71d(0x6cd));return;}var _0x563048={};if(_0xa9c725<=0x0||_0xa9c725==0x64){var _0x1e7fc9=getChromiumVersion();_0x1e7fc9>0x50?_0x563048[_0x3bc71d(0xa4b)]=null:_0x563048['scaleResolutionDownBy']=0x1;}else _0x563048[_0x3bc71d(0xa4b)]=0x64/_0xa9c725;setEncodings(_0xcf212b,_0x563048,function(_0x37ff10){var _0xbd5f77=_0x3bc71d;log(_0xbd5f77(0x3ce)),pokeIframeAPI(_0xbd5f77(0x907),_0x37ff10,_0xbd5f77(0x98c)),pokeIframeAPI('set-video-scale',_0x37ff10,_0xbd5f77(0x98c)),_0x311669['whipOut'][_0xbd5f77(0x436)][_0xbd5f77(0x7ee)]=parseInt(_0x37ff10)+'%';},_0xa9c725);return;}}}catch(_0x5248bb){errorlog(_0x5248bb);}}},_0x311669[_0x134a17(0x88b)]=function(_0x5be927,_0x35a667,_0x19c2be=![]){var _0x55561e=_0x134a17;warnlog(_0x55561e(0x16c)+_0x35a667);try{_0x311669['pcs'][_0x5be927]['stats']['scaleFactor']=_0x35a667;}catch(_0x213234){errorlog(_0x213234);}if(!_0x19c2be&&_0x311669[_0x55561e(0x859)][_0x5be927][_0x55561e(0x6b5)]===_0x35a667)return;if(_0x35a667==null){try{var _0x38b0ee=getSenders2(_0x5be927)[_0x55561e(0x642)](function(_0x36c939){var _0x281a52=_0x55561e;return _0x36c939[_0x281a52(0x53f)]&&_0x36c939[_0x281a52(0x53f)][_0x281a52(0x7ad)]=='video';});}catch(_0x1685cf){errorlog(_0x1685cf);}if(!_0x38b0ee){warnlog('can\x27t\x20change\x20bitrate;\x20no\x20video\x20senders\x20found');return;}var _0x5c573f=_0x38b0ee[_0x55561e(0x650)]();(!_0x5c573f[_0x55561e(0x895)]||_0x5c573f[_0x55561e(0x895)]['length']==0x0)&&(_0x5c573f['encodings']=[{}]),_0x55561e(0xa4b)in _0x5c573f[_0x55561e(0x895)][0x0]?(_0x35a667=0x64/_0x5c573f['encodings'][0x0][_0x55561e(0xa4b)],_0x35a667=_0x35a667*0.95):_0x35a667=0x5f;}else _0x35a667=Math['ceil'](_0x35a667),_0x311669['pcs'][_0x5be927][_0x55561e(0x6b5)]=_0x35a667;try{if(SafariVersion&&SafariVersion<=0xd&&(iOS||iPad))log(_0x55561e(0x68a));else{if(_0x55561e(0x8b1)in window&&'setParameters'in window['RTCRtpSender'][_0x55561e(0x776)]){try{var _0x38b0ee=getSenders2(_0x5be927)[_0x55561e(0x642)](function(_0x137702){var _0xc4dddf=_0x55561e;return _0x137702[_0xc4dddf(0x53f)]&&_0x137702[_0xc4dddf(0x53f)][_0xc4dddf(0x7ad)]=='video';});}catch(_0x1837da){errorlog(_0x1837da);}if(!_0x38b0ee){warnlog(_0x55561e(0x6cd));return;}_0x35a667=_0x311669[_0x55561e(0x9f0)](_0x5be927,![],_0x35a667);var _0x346af8={};if(_0x35a667<=0x0||_0x35a667==0x64){var _0x1d5c4a=getChromiumVersion();_0x1d5c4a>0x50?_0x346af8[_0x55561e(0xa4b)]=null:_0x346af8[_0x55561e(0xa4b)]=0x1;}else _0x346af8[_0x55561e(0xa4b)]=0x64/_0x35a667;setEncodings(_0x38b0ee,_0x346af8,function(_0x1b171c){var _0x433afe=_0x55561e;log(_0x433afe(0x6d7)+_0x1b171c[0x0]),pokeIframeAPI(_0x433afe(0x907),_0x1b171c[0x0],_0x1b171c[0x1]),pokeIframeAPI('set-video-scale',_0x1b171c[0x0],_0x1b171c[0x1]),_0x311669[_0x433afe(0x859)][_0x1b171c[0x1]]['stats'][_0x433afe(0x7ee)]=parseInt(_0x1b171c[0x0])+'%';},[_0x35a667,_0x5be927]);return;}}}catch(_0x226686){errorlog(_0x226686);}},_0x311669[_0x134a17(0x37b)]=function(_0x4802d2,_0x1f3bd6,_0x324a44,_0x36f19e=![],_0x50fa15=![],_0x38e29f=null){var _0xecd6a3=_0x134a17;if(!(_0x4802d2 in _0x311669[_0xecd6a3(0x16a)]))return;_0x38e29f===null&&(_0x38e29f=_0x311669[_0xecd6a3(0xa60)]||![]);var _0x12f519=![];!(_0x311669[_0xecd6a3(0x16a)][_0x4802d2]['scaleWidth']==Math[_0xecd6a3(0x3c1)](_0x1f3bd6)||_0x311669[_0xecd6a3(0x16a)][_0x4802d2][_0xecd6a3(0x863)]===Math[_0xecd6a3(0x2c3)](_0x1f3bd6))&&(_0x1f3bd6=Math[_0xecd6a3(0x2fb)](_0x1f3bd6),_0x311669[_0xecd6a3(0x16a)][_0x4802d2][_0xecd6a3(0x863)]=_0x1f3bd6,_0x12f519=!![]);!(_0x311669[_0xecd6a3(0x16a)][_0x4802d2][_0xecd6a3(0x1c6)]==Math['floor'](_0x324a44)||_0x311669[_0xecd6a3(0x16a)][_0x4802d2][_0xecd6a3(0x1c6)]===Math[_0xecd6a3(0x2c3)](_0x324a44))&&(_0x324a44=Math[_0xecd6a3(0x2fb)](_0x324a44),_0x311669[_0xecd6a3(0x16a)][_0x4802d2][_0xecd6a3(0x1c6)]=_0x324a44,_0x12f519=!![]);_0x311669[_0xecd6a3(0x16a)][_0x4802d2][_0xecd6a3(0x482)]!=_0x36f19e&&(_0x311669['rpcs'][_0x4802d2][_0xecd6a3(0x482)]=_0x36f19e,_0x12f519=!![]);_0x1f3bd6=Math[_0xecd6a3(0x2fb)](_0x1f3bd6),_0x324a44=Math[_0xecd6a3(0x2fb)](_0x324a44);if(_0x12f519){var _0x4a331b={};_0x4a331b[_0xecd6a3(0x54a)]=_0x4802d2,_0x4a331b['requestResolution']={'w':_0x1f3bd6,'h':_0x324a44,'s':_0x36f19e,'c':_0x38e29f},_0x50fa15&&(_0x4a331b['requestAs']=_0x50fa15),log(_0x1f3bd6+'\x20'+_0x324a44),_0x311669[_0xecd6a3(0xa14)](_0x4a331b,_0x4802d2);}_0x36f19e?_0x311669['rpcs'][_0x4802d2][_0xecd6a3(0x436)]['Requested_resolution']='~\x20'+parseInt(_0x1f3bd6)+'\x20x\x20'+parseInt(_0x324a44):_0x311669[_0xecd6a3(0x16a)][_0x4802d2]['stats'][_0xecd6a3(0x957)]=parseInt(_0x1f3bd6)+'\x20x\x20'+parseInt(_0x324a44);},_0x311669[_0x134a17(0x9f0)]=function(_0x231f6c,_0x3e0438=![],_0x38131b=![]){var _0xad161b=_0x134a17;if(_0x38131b){}else _0x311669[_0xad161b(0x859)][_0x231f6c][_0xad161b(0x6b5)]?_0x38131b=_0x311669[_0xad161b(0x859)][_0x231f6c]['scale']:_0x38131b=0x64;_0x311669['pcs'][_0x231f6c][_0xad161b(0x21f)]&&_0x38131b>_0x311669['pcs'][_0x231f6c][_0xad161b(0x21f)]&&(_0x38131b=_0x311669[_0xad161b(0x859)][_0x231f6c]['scaleResolution']);if(_0x3e0438)_0x38131b=_0x526e6b(_0x231f6c,_0x38131b,_0x3e0438);else _0x311669[_0xad161b(0x859)][_0x231f6c][_0xad161b(0x291)]&&_0x311669['pcs'][_0x231f6c]['scaleDueToBitrate']<_0x38131b&&(_0x38131b=_0x311669[_0xad161b(0x859)][_0x231f6c][_0xad161b(0x291)]);if(_0x311669[_0xad161b(0x701)]&&_0x311669['pcs'][_0x231f6c]['scaleSnap']){if(_0x38131b>0x55)_0x38131b=0x64;else _0x38131b>0x2a&&_0x38131b<0x32&&(_0x38131b=0x32);}return _0x38131b=_0x311669[_0xad161b(0x3ef)](_0x38131b,_0x231f6c),_0x38131b;},_0x311669[_0x134a17(0x83a)]=function(_0x21b864=![],_0x1a14bf=null,_0x52f080=null,_0x52a931=![],_0x3a6b60=![]){var _0x56ad40=_0x134a17;warnlog(_0x56ad40(0x525)+_0x1a14bf+'x'+_0x52f080);if(_0x21b864&&!(_0x21b864 in _0x311669[_0x56ad40(0x859)]))return;else{if(!_0x21b864){for(var _0x21bf98 in _0x311669['pcs']){_0x311669['setResolution'](_0x21bf98,_0x311669[_0x56ad40(0x859)][_0x21bf98]['scaleWidth'],_0x311669[_0x56ad40(0x859)][_0x21bf98][_0x56ad40(0x1c6)],_0x311669[_0x56ad40(0x859)][_0x21bf98]['scaleSnap'],_0x311669['pcs'][_0x21bf98][_0x56ad40(0xa60)]);}return;}}_0x3a6b60=_0x3a6b60||![],snape=_0x52a931||![];if(_0x1a14bf===null&&_0x52f080===null){if(!_0x311669['pcs'][_0x21b864][_0x56ad40(0x863)]&&!_0x311669[_0x56ad40(0x859)][_0x21b864][_0x56ad40(0x1c6)])return;else _0x1a14bf=_0x311669[_0x56ad40(0x859)][_0x21b864]['scaleWidth']||0x64,_0x52f080=_0x311669['pcs'][_0x21b864][_0x56ad40(0x1c6)]||0x64;}else _0x311669[_0x56ad40(0x859)][_0x21b864][_0x56ad40(0x863)]=_0x1a14bf,_0x311669[_0x56ad40(0x859)][_0x21b864]['scaleHeight']=_0x52f080,_0x311669['pcs'][_0x21b864][_0x56ad40(0x482)]=_0x52a931,_0x311669['pcs'][_0x21b864][_0x56ad40(0xa60)]=_0x3a6b60;if(SafariVersion&&SafariVersion<=0xd&&(iOS||iPad))return;if(_0x56ad40(0x8b1)in window&&_0x56ad40(0x537)in window['RTCRtpSender']['prototype']){var _0x203ebf=getSenders2(_0x21b864)[_0x56ad40(0x642)](function(_0x410f53){var _0x561922=_0x56ad40;return _0x410f53['track']&&_0x410f53['track'][_0x561922(0x7ad)]==_0x561922(0x30c);});if(!_0x203ebf){warnlog(_0x56ad40(0x6d9));return;}var _0xa8b4a9={};if(_0x56ad40(0x65b)in _0x311669[_0x56ad40(0x859)][_0x21b864]){var _0x3bbf2b=_0x311669[_0x56ad40(0x7a0)][_0x56ad40(0x87a)]();if(_0x3bbf2b[_0x56ad40(0x8e9)])var _0x38683f=_0x3bbf2b[0x0]['getSettings'](),_0x375b7d=_0x38683f[_0x56ad40(0x5ec)],_0x139648=_0x38683f[_0x56ad40(0x530)];else return;}else{if(_0x311669['videoElement']&&_0x311669[_0x56ad40(0x49b)][_0x56ad40(0x5c4)]){var _0x3bbf2b=_0x311669[_0x56ad40(0x49b)][_0x56ad40(0x5c4)][_0x56ad40(0x87a)]();if(_0x3bbf2b[_0x56ad40(0x8e9)])var _0x38683f=_0x3bbf2b[0x0][_0x56ad40(0x2e2)](),_0x375b7d=_0x38683f[_0x56ad40(0x5ec)],_0x139648=_0x38683f[_0x56ad40(0x530)];else return;}else return;}var _0x505955=0x64*_0x1a14bf/_0x139648,_0x19f7b5=0x64*_0x52f080/_0x375b7d;warnlog(_0x505955+_0x56ad40(0x399)+_0x19f7b5);var _0x251e00=0x64;if(_0x1a14bf===null)_0x251e00=_0x19f7b5;else{if(_0x52f080===null)_0x251e00=_0x505955;else _0x3a6b60?_0x505955>_0x19f7b5?_0x251e00=_0x505955:_0x251e00=_0x19f7b5:_0x505955<_0x19f7b5?_0x251e00=_0x505955:_0x251e00=_0x19f7b5;}_0x251e00>0x64&&(_0x251e00=0x64);log(_0x56ad40(0x7ba)+_0x251e00),_0x311669['pcs'][_0x21b864][_0x56ad40(0x21f)]=_0x251e00;var _0x20dac2=_0x311669[_0x56ad40(0x9f0)](_0x21b864);if(_0x20dac2<=0x0||_0x20dac2==0x64){var _0x426e41=getChromiumVersion();_0x426e41>0x50?_0xa8b4a9['scaleResolutionDownBy']=null:_0xa8b4a9[_0x56ad40(0xa4b)]=0x1;}else _0xa8b4a9[_0x56ad40(0xa4b)]=0x64/_0x20dac2;setEncodings(_0x203ebf,_0xa8b4a9,function(_0x411e9e){var _0x37baf7=_0x56ad40;log('scale\x20set!'),pokeIframeAPI(_0x37baf7(0x907),_0x411e9e[0x0],_0x411e9e[0x1]),pokeIframeAPI('set-video-scale',_0x411e9e[0x0],_0x411e9e[0x1]),_0x311669[_0x37baf7(0x859)][_0x411e9e[0x1]]['stats'][_0x37baf7(0x7ee)]=parseInt(_0x411e9e[0x0])+'%';},[_0x20dac2,_0x21b864]);return;}},_0x311669[_0x134a17(0x2e3)]=function(_0x497e1a=null,_0x22ff6b=null){var _0x60b1aa=_0x134a17;_0x22ff6b&&_0x22ff6b[_0x60b1aa(0x890)]();_0x1e48c9&&(_0x1e48c9[_0x60b1aa(0x7fa)]=!![],log(_0x60b1aa(0x363)+_0x497e1a));if(iOS||iPad)return log(_0x60b1aa(0x68a)),![];else{if(_0x60b1aa(0x8b1)in window&&_0x60b1aa(0x537)in window[_0x60b1aa(0x8b1)][_0x60b1aa(0x776)]){log('FORCING\x20A\x20KEY\x20FRAME:\x20'+_0x497e1a);if(_0x497e1a==null){for(_0x497e1a in _0x311669[_0x60b1aa(0x859)]){_0x311669['forcePLI'](_0x497e1a);}return![];}if(!(_0x497e1a in _0x311669['pcs']))return![];_0x311669[_0x60b1aa(0x859)][_0x497e1a][_0x60b1aa(0x458)]&&(_0x311669[_0x60b1aa(0x859)][_0x497e1a][_0x60b1aa(0x849)]&&(clearTimeout(_0x311669[_0x60b1aa(0x859)][_0x497e1a][_0x60b1aa(0x849)]),_0x311669[_0x60b1aa(0x859)][_0x497e1a]['keyframeTimeout']=null),_0x311669[_0x60b1aa(0x859)][_0x497e1a][_0x60b1aa(0x849)]=setTimeout(function(_0x10e657){var _0x1721af=_0x60b1aa;!_0x311669[_0x1721af(0x859)][_0x10e657]?clearInterval(this):_0x311669['forcePLI'](_0x10e657);},parseInt(_0x311669['pcs'][_0x497e1a][_0x60b1aa(0x458)]),_0x497e1a));try{var _0x43c000=getSenders2(_0x497e1a)[_0x60b1aa(0x642)](function(_0xcb1420){var _0x50005c=_0x60b1aa;return _0xcb1420['track']&&_0xcb1420[_0x50005c(0x53f)]['kind']==_0x50005c(0x30c);});if(!_0x43c000)return warnlog(_0x60b1aa(0x6d9)),![];var _0x409b52={};return _0x409b52[_0x60b1aa(0xa4b)]=0xa,setEncodings(_0x43c000,_0x409b52,function(_0x12817e){var _0x7a930b=_0x60b1aa;log(_0x7a930b(0x3c0)+_0x12817e[0x0]);var _0x2840b3=_0x311669['calculateScale'](_0x12817e[0x0]),_0x2bbb1a={};if(_0x2840b3<=0x0||_0x2840b3==0x64){var _0xb13fdd=getChromiumVersion();_0xb13fdd>0x50?_0x2bbb1a[_0x7a930b(0xa4b)]=null:_0x2bbb1a[_0x7a930b(0xa4b)]=0x1;}else _0x2bbb1a[_0x7a930b(0xa4b)]=0x64/_0x2840b3;setEncodings(_0x12817e[0x1],_0x2bbb1a,function(){var _0x1b99a4=_0x7a930b;log(_0x1b99a4(0x9dd));});},[_0x497e1a,_0x43c000]),!![];}catch(_0x531c17){errorlog(_0x531c17);}}}return![];},_0x311669[_0x134a17(0x71b)]=function(_0xf68aaa){var _0x484973=_0x134a17;log('enhacing\x20audio\x20encoder');var _0x5180d1=getSenders2(_0xf68aaa)[_0x484973(0x642)](function(_0x3474aa){var _0x1010d1=_0x484973;return _0x3474aa[_0x1010d1(0x53f)]&&_0x3474aa['track'][_0x1010d1(0x7ad)]==_0x1010d1(0x433);});if(!_0x5180d1)return log(_0x484973(0x476)),![];var _0x550579={};try{_0x550579['networkPriority']=_0x484973(0x4f3),_0x550579[_0x484973(0x931)]=_0x484973(0x4f3),_0x550579[_0x484973(0x841)]=!![],setEncodings(_0x5180d1,_0x550579,function(_0x450474){var _0x2c2f03=_0x484973;log('done\x20clearing\x20audio'),pokeIframeAPI(_0x2c2f03(0x4cb),!![],_0x450474);},_0xf68aaa);}catch(_0x595865){errorlog(_0x595865);}},_0x311669[_0x134a17(0x5c7)]=function(_0x16f2dc,_0xf1d11=_0x134a17(0x757)){var _0x555855=_0x134a17,_0x7f3a1=getSenders2(_0x16f2dc)[_0x555855(0x642)](function(_0x1f393c){var _0x7bdf4d=_0x555855;return _0x1f393c[_0x7bdf4d(0x53f)]&&_0x1f393c[_0x7bdf4d(0x53f)][_0x7bdf4d(0x7ad)]==_0x7bdf4d(0x30c);});if(!_0x7f3a1)return log(_0x555855(0x80b)),![];var _0x5a5980={};try{_0xf1d11===!![]?(_0x5a5980[_0x555855(0x5c7)]='maintain-framerate',log('done\x20setting\x20degrad\x20to\x20maintain-framerate')):(_0x5a5980['degradationPreference']=_0xf1d11,log(_0x555855(0x219)+_0xf1d11)),setEncodings(_0x7f3a1,_0x5a5980,(function(){var _0x3d643c=_0x555855;log(_0x3d643c(0x956));}()));}catch(_0x1e8d54){errorlog(_0x1e8d54);}},_0x311669[_0x134a17(0x295)]=function(_0x2a7b00,_0x35ae60,_0x45f649=![]){var _0x482e5c=_0x134a17;log(_0x482e5c(0x76a)+_0x2a7b00+_0x482e5c(0x880)+_0x45f649);if(_0x311669[_0x482e5c(0x9b5)]===![])return;_0x35ae60['maxBandwidth']=parseInt(_0x311669['maxBandwidth']/0x64*_0x2a7b00),_0x45f649?_0x311669[_0x482e5c(0x217)](null):_0x311669[_0x482e5c(0x3da)](_0x35ae60['UUID'],null);},_0x311669[_0x134a17(0x33d)]=function(_0x4d6ce2,_0x439d26=0x7d00,_0x10528e=0x3e8){var _0x5ba1fc=_0x134a17;log(_0x5ba1fc(0x8b6));var _0x5df5b0=getSenders2(_0x4d6ce2)[_0x5ba1fc(0x642)](function(_0x16df0e){var _0x4f7dba=_0x5ba1fc;return _0x16df0e['track']&&_0x16df0e[_0x4f7dba(0x53f)][_0x4f7dba(0x7ad)]==_0x4f7dba(0x433);});if(!_0x5df5b0)return log(_0x5ba1fc(0x476)),![];var _0x594a3f={};_0x594a3f['maxBitrate']=_0x439d26,setEncodings(_0x5df5b0,_0x594a3f,function(_0x3da119){var _0x50a3ef=_0x5ba1fc;pokeIframeAPI(_0x50a3ef(0x9c8),_0x3da119[0x0],_0x3da119[0x1]),pokeIframeAPI(_0x50a3ef(0x899),_0x3da119[0x0],_0x3da119[0x1]),_0x3da119[0x2]>0x0&&setTimeout(function(){var _0x1f9e60=_0x50a3ef;try{if(_0x3da119[0x1]in _0x311669[_0x1f9e60(0x859)])var _0x514989=getSenders2(_0x3da119[0x1])[_0x1f9e60(0x642)](function(_0x347260){var _0x908d95=_0x1f9e60;return _0x347260[_0x908d95(0x53f)]&&_0x347260[_0x908d95(0x53f)][_0x908d95(0x7ad)]=='audio';});else return![];if(!_0x514989)return log(_0x1f9e60(0x476)),![];var _0x4b9d56={};_0x4b9d56['maxBitrate']=null,setEncodings(_0x514989,_0x4b9d56,function(){var _0x5e832a=_0x1f9e60;log(_0x5e832a(0x4c7));});}catch(_0x256dad){errorlog(_0x256dad);}},_0x3da119[0x2],_0x3da119[0x1]);},[_0x439d26,_0x4d6ce2,_0x10528e]);},_0x311669[_0x134a17(0x975)]=function(_0x3b22ed,_0x499783,_0x4c8492){var _0x4160cd=_0x134a17;pokeIframeAPI(_0x4160cd(0x8dd),_0x3b22ed,_0x4c8492);if(_0x311669[_0x4160cd(0x8a3)])return generateHash(_0x3b22ed+_0x311669['password']+_0x311669[_0x4160cd(0x9c5)],0x10)['then'](function(_0x1ac96a){var _0x2a2c37=_0x4160cd,_0x54a255={};_0x499783['updateurl']&&(_0x499783['roomenc']=_0x1ac96a);if(_0x311669[_0x2a2c37(0x75b)]&&_0x311669[_0x2a2c37(0x2f3)])_0x54a255[_0x2a2c37(0x3fe)]=_0x4c8492,_0x54a255['roomid']=_0x1ac96a,_0x54a255[_0x2a2c37(0x321)]=_0x499783,_0x311669['sendRequest'](_0x54a255,_0x311669[_0x2a2c37(0x2f3)]),log(_0x54a255);else{if(_0x499783['updateurl'])_0x54a255[_0x2a2c37(0x727)]=_0x2a2c37(0x3fe),_0x54a255['transferSettings']=_0x499783,log(_0x54a255),_0x311669[_0x2a2c37(0xa14)](_0x54a255,_0x4c8492,function(){var _0x48c1d2=_0x2a2c37,_0x5d9082={};_0x5d9082[_0x48c1d2(0x727)]=_0x48c1d2(0x3fe),_0x5d9082['roomid']=_0x1ac96a,_0x5d9082[_0x48c1d2(0x81f)]=_0x4c8492,_0x311669[_0x48c1d2(0x663)](_0x5d9082);}),log(_0x54a255);else{if(_0x2a2c37(0x254)in _0x499783)_0x54a255[_0x2a2c37(0x727)]=_0x2a2c37(0x3fe),_0x54a255['transferSettings']=_0x499783,delete _0x54a255[_0x2a2c37(0x321)][_0x2a2c37(0x4e1)],delete _0x54a255[_0x2a2c37(0x321)][_0x2a2c37(0x5cf)],log(_0x54a255),_0x311669['sendRequest'](_0x54a255,_0x4c8492,function(){var _0x3a7ad7=_0x2a2c37,_0x5cb453={};_0x5cb453[_0x3a7ad7(0x727)]=_0x3a7ad7(0x3fe),_0x5cb453[_0x3a7ad7(0x4e1)]=_0x1ac96a,_0x5cb453['target']=_0x4c8492,_0x311669[_0x3a7ad7(0x663)](_0x5cb453);}),log(_0x54a255);else Object[_0x2a2c37(0x19b)](_0x499783)[_0x2a2c37(0x8e9)]?(_0x54a255[_0x2a2c37(0x727)]=_0x2a2c37(0x3fe),_0x54a255[_0x2a2c37(0x321)]=_0x499783,delete _0x54a255['transferSettings'][_0x2a2c37(0x4e1)],delete _0x54a255[_0x2a2c37(0x321)][_0x2a2c37(0x5cf)],log(_0x54a255),_0x311669[_0x2a2c37(0xa14)](_0x54a255,_0x4c8492,function(){var _0x46f835=_0x2a2c37,_0x4cfa19={};_0x4cfa19[_0x46f835(0x727)]=_0x46f835(0x3fe),_0x4cfa19['roomid']=_0x1ac96a,_0x4cfa19['target']=_0x4c8492,_0x311669[_0x46f835(0x663)](_0x4cfa19);}),log(_0x54a255)):(_0x54a255[_0x2a2c37(0x727)]=_0x2a2c37(0x3fe),_0x54a255[_0x2a2c37(0x4e1)]=_0x1ac96a,_0x54a255[_0x2a2c37(0x81f)]=_0x4c8492,_0x311669[_0x2a2c37(0x663)](_0x54a255));}}})['catch'](errorlog);else{_0x499783[_0x4160cd(0x339)]&&(_0x499783[_0x4160cd(0x5cf)]=_0x3b22ed);var _0x3abf7f={};if(_0x311669[_0x4160cd(0x75b)]&&_0x311669[_0x4160cd(0x2f3)])_0x3abf7f[_0x4160cd(0x3fe)]=_0x4c8492,_0x3abf7f[_0x4160cd(0x4e1)]=_0x3b22ed,_0x3abf7f['transferSettings']=_0x499783,_0x311669['sendRequest'](_0x3abf7f,_0x311669[_0x4160cd(0x2f3)]),log(_0x3abf7f);else{if(_0x499783[_0x4160cd(0x339)])_0x3abf7f[_0x4160cd(0x727)]=_0x4160cd(0x3fe),_0x3abf7f[_0x4160cd(0x321)]=_0x499783,_0x311669[_0x4160cd(0xa14)](_0x3abf7f,_0x4c8492,function(){var _0x7f927d=_0x4160cd,_0x3eccef={};_0x3eccef['request']=_0x7f927d(0x3fe),_0x3eccef[_0x7f927d(0x4e1)]=_0x3b22ed,_0x3eccef[_0x7f927d(0x81f)]=_0x4c8492,_0x311669[_0x7f927d(0x663)](_0x3eccef);});else{if(_0x4160cd(0x254)in _0x499783)_0x3abf7f[_0x4160cd(0x727)]=_0x4160cd(0x3fe),_0x3abf7f['transferSettings']=_0x499783,delete _0x3abf7f[_0x4160cd(0x321)][_0x4160cd(0x4e1)],delete _0x3abf7f['transferSettings'][_0x4160cd(0x5cf)],_0x311669['sendRequest'](_0x3abf7f,_0x4c8492,function(){var _0x4091f8=_0x4160cd,_0xf532f9={};_0xf532f9[_0x4091f8(0x727)]=_0x4091f8(0x3fe),_0xf532f9[_0x4091f8(0x4e1)]=_0x3b22ed,_0xf532f9[_0x4091f8(0x81f)]=_0x4c8492,_0x311669[_0x4091f8(0x663)](_0xf532f9);});else Object[_0x4160cd(0x19b)](_0x499783)['length']?(_0x3abf7f['request']=_0x4160cd(0x3fe),_0x3abf7f[_0x4160cd(0x321)]=_0x499783,delete _0x3abf7f[_0x4160cd(0x321)]['roomid'],delete _0x3abf7f[_0x4160cd(0x321)][_0x4160cd(0x5cf)],log(_0x3abf7f),_0x311669[_0x4160cd(0xa14)](_0x3abf7f,_0x4c8492,function(){var _0x1d12fc=_0x4160cd,_0x469443={};_0x469443[_0x1d12fc(0x727)]=_0x1d12fc(0x3fe),_0x469443[_0x1d12fc(0x4e1)]=_0x3b22ed,_0x469443['target']=_0x4c8492,_0x311669[_0x1d12fc(0x663)](_0x469443);}),log(_0x3abf7f)):(_0x3abf7f['request']=_0x4160cd(0x3fe),_0x3abf7f[_0x4160cd(0x4e1)]=_0x3b22ed,_0x3abf7f[_0x4160cd(0x81f)]=_0x4c8492,_0x311669[_0x4160cd(0x663)](_0x3abf7f));}}}},_0x311669[_0x134a17(0x184)]=async function(_0x331462,_0xbf65a3){var _0x11cc10=_0x134a17;_0xbf65a3=parseInt(_0xbf65a3);try{var _0x15f901=getSenders2(_0x331462)[_0x11cc10(0x642)](function(_0x47c0a1){var _0x30baf0=_0x11cc10;return _0x47c0a1[_0x30baf0(0x53f)]&&_0x47c0a1[_0x30baf0(0x53f)][_0x30baf0(0x7ad)]==_0x30baf0(0x433);});if(!_0x15f901){warnlog('can\x27t\x20change\x20audio\x20bitrate;\x20no\x20audio\x20sender\x20found');return;}var _0x13bf00={};if(_0xbf65a3<0x0){_0x13bf00[_0x11cc10(0x9fb)]=!![];if(SafariVersion&&SafariVersion<=0xd&&(iOS||iPad)){_0xbf65a3=0x20;if(_0x311669[_0x11cc10(0x859)][_0x331462][_0x11cc10(0x9c8)]!==![])_0xbf65a3=_0x311669[_0x11cc10(0x859)][_0x331462][_0x11cc10(0x9c8)];else _0x311669[_0x11cc10(0x543)]&&(_0xbf65a3=_0x311669['audiobitrate']);_0x13bf00[_0x11cc10(0x9d1)]=_0xbf65a3*0x400;}else _0x311669[_0x11cc10(0x859)][_0x331462][_0x11cc10(0x9c8)]!==![]?(_0xbf65a3=_0x311669[_0x11cc10(0x859)][_0x331462][_0x11cc10(0x9c8)],_0x13bf00[_0x11cc10(0x9d1)]=_0xbf65a3*0x400):_0x13bf00['maxBitrate']=null;}else _0xbf65a3===0x0?_0x13bf00[_0x11cc10(0x9fb)]=![]:(_0x13bf00[_0x11cc10(0x9fb)]=!![],_0x13bf00[_0x11cc10(0x9d1)]=_0xbf65a3*0x400);_0x311669['pcs'][_0x331462][_0x11cc10(0x989)]&&(_0x13bf00['active']=![]),setEncodings(_0x15f901,_0x13bf00,function(_0x306789){var _0x170ebb=_0x11cc10;pokeIframeAPI(_0x170ebb(0x9c8),_0x306789[0x0],_0x306789[0x1]),pokeIframeAPI(_0x170ebb(0x899),_0x306789[0x0],_0x306789[0x1]),log(_0x170ebb(0x887));},[_0xbf65a3,_0x331462]);}catch(_0x34c4ce){errorlog(_0x34c4ce),log(_0x331462),log(_0x311669[_0x11cc10(0x859)][_0x331462]);}},_0x311669[_0x134a17(0xa11)]=function(_0x5e19b4){var _0xbec236=_0x134a17;if(_0x311669[_0xbec236(0x325)]&&_0x311669[_0xbec236(0x859)][_0x5e19b4][_0xbec236(0x299)]===!![])_0x311669[_0xbec236(0x3da)](_0x5e19b4,0x0),_0x311669['pcs'][_0x5e19b4]['optimizedBitrate']===0x0&&(_0x311669[_0xbec236(0x859)][_0x5e19b4][_0xbec236(0x34a)][_0xbec236(0x4eb)]===![]?_0x311669[_0xbec236(0x184)](_0x5e19b4,0x0):_0x311669[_0xbec236(0x184)](_0x5e19b4,-0x1));else{if(_0x311669[_0xbec236(0x859)][_0x5e19b4]&&_0x311669['pcs'][_0x5e19b4][_0xbec236(0x273)]!==![]){if(_0x311669['pcs'][_0x5e19b4][_0xbec236(0x34a)]['visibility']===![]){var _0x54c154=_0x311669[_0xbec236(0x859)][_0x5e19b4]['optimizedBitrate'];_0x311669[_0xbec236(0x859)][_0x5e19b4][_0xbec236(0x969)]&&_0x311669[_0xbec236(0x859)][_0x5e19b4][_0xbec236(0x969)]>0x0&&(_0x311669[_0xbec236(0x859)][_0x5e19b4][_0xbec236(0x969)]<_0x311669[_0xbec236(0x859)][_0x5e19b4][_0xbec236(0x273)]&&(_0x54c154=_0x311669['pcs'][_0x5e19b4][_0xbec236(0x969)])),_0x311669[_0xbec236(0x3da)](_0x5e19b4,_0x54c154),_0x311669[_0xbec236(0x859)][_0x5e19b4]['optimizedBitrate']===0x0&&_0x311669[_0xbec236(0x184)](_0x5e19b4,0x0);}else _0x311669[_0xbec236(0x859)][_0x5e19b4][_0xbec236(0x273)]===0x0&&(_0x311669[_0xbec236(0x184)](_0x5e19b4,-0x1),_0x311669[_0xbec236(0x484)](),_0x311669[_0xbec236(0x84c)]&&_0x311669[_0xbec236(0x3da)](_0x5e19b4,null));}else _0x311669['limitTotalBitrateGuests'](),_0x311669[_0xbec236(0x84c)]&&_0x311669['limitBitrate'](_0x5e19b4,null);}},_0x311669[_0x134a17(0x484)]=function(_0x23a70f=0x0,_0xa4487d=![]){var _0xcbe244=_0x134a17;if(!_0x311669['limitTotalBitrate'])return _0x23a70f;if(!_0x311669[_0xcbe244(0x4e1)]||_0x311669[_0xcbe244(0x98d)]!==![])return log('Switching\x20to\x20limitTotalBitrateAll'),_0x311669[_0xcbe244(0x32a)](_0x23a70f,_0xa4487d),_0x23a70f;if((iOS||iPad)&&SafariVersion&&SafariVersion<=0xd)return _0x23a70f;var _0x1c7953=_0x23a70f;if(_0xa4487d===![])_0x1c7953=0x0;else _0x1c7953<0x0&&(_0x1c7953=_0x311669[_0xcbe244(0x859)][_0xa4487d][_0xcbe244(0x9f3)]||_0x311669[_0xcbe244(0x810)]||_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x9b5)]||0x9c4);var _0x4a6e52=0x0;for(var _0x225915 in _0x311669['pcs']){if(_0xa4487d===_0x225915)continue;if(!_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x798)])continue;try{var _0x33814b=getSenders2(_0x225915)['find'](function(_0x2e872d){var _0x1d2dd3=_0xcbe244;return _0x2e872d[_0x1d2dd3(0x53f)]&&_0x2e872d[_0x1d2dd3(0x53f)]['kind']==_0x1d2dd3(0x30c);});if(!_0x33814b)continue;var _0x2d4598=_0x33814b[_0xcbe244(0x650)]();if(!_0x2d4598['encodings']||_0x2d4598[_0xcbe244(0x895)]['length']==0x0){_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x9f3)]<0x0?_0x1c7953+=_0x311669[_0xcbe244(0x810)]||_0x311669[_0xcbe244(0x859)][_0x225915]['maxBandwidth']||0x9c4:_0x1c7953+=_0x311669[_0xcbe244(0x859)][_0x225915]['setBitrate']||_0x311669[_0xcbe244(0x810)]||_0x311669[_0xcbe244(0x859)][_0x225915]['maxBandwidth']||0x9c4;warnlog(_0x1c7953),_0x4a6e52+=0x1;continue;}if(_0x2d4598[_0xcbe244(0x895)][0x0][_0xcbe244(0x9fb)]==![])continue;if(_0x2d4598[_0xcbe244(0x895)][0x0][_0xcbe244(0x9d1)])_0xcbe244(0x5a6)in _0x311669[_0xcbe244(0x859)][_0x225915]?_0x1c7953+=parseInt(_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x5a6)]):_0x1c7953+=parseInt(_0x2d4598[_0xcbe244(0x895)][0x0][_0xcbe244(0x9d1)])/0x400;else _0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x9f3)]<0x0?_0x1c7953+=_0x311669[_0xcbe244(0x810)]||_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x9b5)]||0x9c4:(_0x1c7953+=_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x9f3)]||_0x311669['outboundVideoBitrate']||_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x9b5)]||0x9c4,warnlog(_0x1c7953));_0x4a6e52+=0x1;}catch(_0x316d68){errorlog(_0x316d68);}}if(!_0x1c7953)return _0x1c7953;warnlog(_0xcbe244(0x553)+_0x1c7953);var _0x1d7f6f=parseFloat(_0x1c7953/_0x311669['limitTotalBitrate']);_0x1d7f6f<0x1&&(_0x1d7f6f=0x1);for(var _0x225915 in _0x311669[_0xcbe244(0x859)]){if(_0xa4487d===_0x225915)continue;if(!_0x311669['pcs'][_0x225915]['guest'])continue;try{var _0x33814b=getSenders2(_0x225915)['find'](function(_0x121174){var _0x51776b=_0xcbe244;return _0x121174['track']&&_0x121174[_0x51776b(0x53f)][_0x51776b(0x7ad)]==_0x51776b(0x30c);});if(!_0x33814b)continue;var _0x2d4598=_0x33814b[_0xcbe244(0x650)]();if(!_0x2d4598[_0xcbe244(0x895)]||_0x2d4598[_0xcbe244(0x895)][_0xcbe244(0x8e9)]==0x0){if(_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x9f3)]<0x0)var _0x38dcae=_0x311669[_0xcbe244(0x810)]||_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x9b5)]||0x9c4;else var _0x38dcae=_0x311669['pcs'][_0x225915][_0xcbe244(0x9f3)]||_0x311669['outboundVideoBitrate']||_0x311669[_0xcbe244(0x859)][_0x225915]['maxBandwidth']||0x9c4;var _0x107a12=parseInt(_0x38dcae/_0x1d7f6f);_0x311669[_0xcbe244(0x3da)](_0x225915,_0x107a12,!![]);continue;}if(_0x2d4598[_0xcbe244(0x895)][0x0][_0xcbe244(0x9fb)]==![])continue;if(_0x2d4598['encodings'][0x0][_0xcbe244(0x9d1)]){if(_0xcbe244(0x5a6)in _0x311669[_0xcbe244(0x859)][_0x225915])var _0x38dcae=parseInt(_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x5a6)]);else var _0x38dcae=parseInt(parseInt(_0x2d4598[_0xcbe244(0x895)][0x0][_0xcbe244(0x9d1)])/0x400);var _0x107a12=parseInt(_0x38dcae/_0x1d7f6f);_0x311669[_0xcbe244(0x3da)](_0x225915,_0x107a12,!![]);}else{if(_0x311669[_0xcbe244(0x859)][_0x225915]['setBitrate']<0x0)var _0x38dcae=_0x311669[_0xcbe244(0x810)]||_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x9b5)]||0x9c4;else var _0x38dcae=_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x9f3)]||_0x311669[_0xcbe244(0x810)]||_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x9b5)]||0x9c4;var _0x107a12=parseInt(_0x38dcae/_0x1d7f6f);_0x311669[_0xcbe244(0x3da)](_0x225915,_0x107a12,!![]);}}catch(_0x3f29d3){errorlog(_0x3f29d3);}}return parseInt(_0x23a70f/_0x1d7f6f);},_0x311669[_0x134a17(0x32a)]=function(_0x3fe6ec=0x0,_0x1f41e1=![]){var _0x13604f=_0x134a17;if(!_0x311669['limitTotalBitrate'])return _0x3fe6ec;if((iOS||iPad)&&SafariVersion&&SafariVersion<=0xd)return _0x3fe6ec;var _0x2a7b8e=_0x3fe6ec;if(_0x1f41e1===![])_0x2a7b8e=0x0;else _0x2a7b8e<0x0&&(_0x2a7b8e=_0x311669['pcs'][_0x1f41e1][_0x13604f(0x9f3)]||_0x311669['outboundVideoBitrate']||_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9b5)]||0x9c4);var _0x3e57c8=0x0;for(var _0x4efbb6 in _0x311669[_0x13604f(0x859)]){if(_0x1f41e1===_0x4efbb6)continue;try{var _0x280d78=getSenders2(_0x4efbb6)['find'](function(_0x589e36){var _0xca8aaa=_0x13604f;return _0x589e36[_0xca8aaa(0x53f)]&&_0x589e36[_0xca8aaa(0x53f)]['kind']==_0xca8aaa(0x30c);});if(!_0x280d78)continue;var _0x387658=_0x280d78[_0x13604f(0x650)]();if(!_0x387658[_0x13604f(0x895)]||_0x387658['encodings'][_0x13604f(0x8e9)]==0x0){_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9f3)]<0x0?_0x2a7b8e+=_0x311669[_0x13604f(0x810)]||_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9b5)]||0x9c4:_0x2a7b8e+=_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9f3)]||_0x311669['outboundVideoBitrate']||_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9b5)]||0x9c4;warnlog(_0x2a7b8e),_0x3e57c8+=0x1;continue;}if(_0x387658[_0x13604f(0x895)][0x0][_0x13604f(0x9fb)]==![])continue;if(_0x387658['encodings'][0x0][_0x13604f(0x9d1)])'preLimitedBitrate'in _0x311669['pcs'][_0x4efbb6]?_0x2a7b8e+=parseInt(_0x311669['pcs'][_0x4efbb6][_0x13604f(0x5a6)]):_0x2a7b8e+=parseInt(_0x387658[_0x13604f(0x895)][0x0]['maxBitrate'])/0x400;else _0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9f3)]<0x0?_0x2a7b8e+=_0x311669[_0x13604f(0x810)]||_0x311669['pcs'][_0x4efbb6][_0x13604f(0x9b5)]||0x9c4:(_0x2a7b8e+=_0x311669['pcs'][_0x4efbb6][_0x13604f(0x9f3)]||_0x311669[_0x13604f(0x810)]||_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9b5)]||0x9c4,warnlog(_0x2a7b8e));_0x3e57c8+=0x1;}catch(_0x3882d6){errorlog(_0x3882d6);}}if(!_0x2a7b8e)return _0x2a7b8e;warnlog(_0x13604f(0x553)+_0x2a7b8e);var _0x7ef431=parseFloat(_0x2a7b8e/_0x311669[_0x13604f(0x335)]);_0x7ef431<0x1&&(_0x7ef431=0x1);for(var _0x4efbb6 in _0x311669['pcs']){if(_0x1f41e1===_0x4efbb6)continue;try{var _0x280d78=getSenders2(_0x4efbb6)[_0x13604f(0x642)](function(_0x7cb49d){var _0x9c7fd7=_0x13604f;return _0x7cb49d[_0x9c7fd7(0x53f)]&&_0x7cb49d[_0x9c7fd7(0x53f)][_0x9c7fd7(0x7ad)]==_0x9c7fd7(0x30c);});if(!_0x280d78)continue;var _0x387658=_0x280d78[_0x13604f(0x650)]();if(!_0x387658['encodings']||_0x387658['encodings'][_0x13604f(0x8e9)]==0x0){if(_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9f3)]<0x0)var _0x54f415=_0x311669[_0x13604f(0x810)]||_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9b5)]||0x9c4;else var _0x54f415=_0x311669[_0x13604f(0x859)][_0x4efbb6]['setBitrate']||_0x311669[_0x13604f(0x810)]||_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9b5)]||0x9c4;var _0x5129c5=parseInt(_0x54f415/_0x7ef431);_0x311669['limitBitrate'](_0x4efbb6,_0x5129c5,!![]);continue;}if(_0x387658['encodings'][0x0][_0x13604f(0x9fb)]==![])continue;if(_0x387658['encodings'][0x0][_0x13604f(0x9d1)]){if(_0x13604f(0x5a6)in _0x311669[_0x13604f(0x859)][_0x4efbb6])var _0x54f415=parseInt(_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x5a6)]);else var _0x54f415=parseInt(parseInt(_0x387658[_0x13604f(0x895)][0x0][_0x13604f(0x9d1)])/0x400);var _0x5129c5=parseInt(_0x54f415/_0x7ef431);_0x311669[_0x13604f(0x3da)](_0x4efbb6,_0x5129c5,!![]);}else{if(_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9f3)]<0x0)var _0x54f415=_0x311669[_0x13604f(0x810)]||_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9b5)]||0x9c4;else var _0x54f415=_0x311669[_0x13604f(0x859)][_0x4efbb6]['setBitrate']||_0x311669[_0x13604f(0x810)]||_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9b5)]||0x9c4;var _0x5129c5=parseInt(_0x54f415/_0x7ef431);_0x311669[_0x13604f(0x3da)](_0x4efbb6,_0x5129c5,!![]);}}catch(_0xad4e36){errorlog(_0xad4e36);}}return parseInt(_0x3fe6ec/_0x7ef431);},_0x311669[_0x134a17(0x60e)]=function(_0x42d908,_0x4e35f4=![]){var _0x51ded1=_0x134a17,_0x4c45c1={};_0x4c45c1[_0x51ded1(0x8ce)]={},_0x4c45c1['directorSettings'][_0x51ded1(0x1d7)]=[_0x42d908],_0x311669['sendPeers'](_0x4c45c1,_0x4e35f4);},_0x311669[_0x134a17(0x217)]=function(_0x3cf104=null){var _0x4f7544=_0x134a17;if(!_0x311669['whipOut'])return;_0x311669[_0x4f7544(0x638)][_0x4f7544(0x62e)]&&(clearInterval(_0x311669[_0x4f7544(0x638)][_0x4f7544(0x62e)]),_0x311669[_0x4f7544(0x638)][_0x4f7544(0x62e)]=null);if(_0x3cf104===null){if(_0x311669[_0x4f7544(0x638)][_0x4f7544(0x969)]===![])return;_0x3cf104=_0x311669['whipOut'][_0x4f7544(0x969)];}_0x3cf104=parseInt(_0x3cf104);if(_0x311669[_0x4f7544(0x638)]['setBitrate']&&_0x3cf104>_0x311669['whipOut'][_0x4f7544(0x9f3)])_0x3cf104=_0x311669[_0x4f7544(0x638)][_0x4f7544(0x9f3)];else _0x311669[_0x4f7544(0x638)][_0x4f7544(0x9f3)]===![]&&(_0x3cf104<0x0&&(_0x311669[_0x4f7544(0x810)]?_0x3cf104=_0x311669[_0x4f7544(0x810)]:_0x3cf104=0x9c4));_0x311669[_0x4f7544(0x84c)]&&(_0x3cf104>_0x311669[_0x4f7544(0x84c)]&&(_0x3cf104=_0x311669[_0x4f7544(0x84c)]));_0x311669[_0x4f7544(0x638)][_0x4f7544(0x969)]=_0x3cf104;_0x311669['whipOut']['optimizedBitrate']!==![]&&(_0x311669[_0x4f7544(0x638)][_0x4f7544(0x34a)][_0x4f7544(0x4eb)]===![]&&(_0x3cf104>_0x311669[_0x4f7544(0x638)][_0x4f7544(0x273)]&&(_0x311669[_0x4f7544(0x638)]['savedBitrate']=_0x3cf104,_0x3cf104=parseInt(_0x311669['whipOut'][_0x4f7544(0x273)])||0x0)));if(_0x311669[_0x4f7544(0x638)]['maxBandwidth']!==null){if(_0x311669[_0x4f7544(0x638)][_0x4f7544(0x9b5)]<_0x3cf104)_0x3cf104=_0x311669[_0x4f7544(0x638)][_0x4f7544(0x9b5)],_0x311669[_0x4f7544(0x638)][_0x4f7544(0x436)][_0x4f7544(0xa2f)]=_0x3cf104,warnlog(_0x4f7544(0x3ab)+_0x3cf104+_0x4f7544(0x78b));else _0x311669[_0x4f7544(0x638)]['stats']&&(_0x311669[_0x4f7544(0x638)][_0x4f7544(0x436)][_0x4f7544(0xa2f)]=![]);}else _0x4f7544(0xa2f)in _0x311669[_0x4f7544(0x638)][_0x4f7544(0x436)]&&(_0x311669[_0x4f7544(0x638)]['stats'][_0x4f7544(0xa2f)]=![]);if(_0x3cf104===0x0){var _0xcdc56b=Date[_0x4f7544(0x610)]()-_0x311669['whipOut']['startTime'];_0xcdc56b<_0x311669[_0x4f7544(0x25e)]&&(_0x3cf104=_0x311669[_0x4f7544(0x9ff)],log(_0x4f7544(0x7c4)+(Date[_0x4f7544(0x610)]()-_0x311669['whipOut'][_0x4f7544(0x586)])),_0x311669['whipOut'][_0x4f7544(0x62e)]=setTimeout(function(){var _0x38a010=_0x4f7544;try{warnlog(_0x38a010(0x908)+(Date[_0x38a010(0x610)]()-_0x311669[_0x38a010(0x638)][_0x38a010(0x586)])),_0x311669[_0x38a010(0x217)](null);}catch(_0x12b5f1){};},_0x311669[_0x4f7544(0x25e)]-_0xcdc56b+0x5));}try{if((iOS||iPad)&&SafariVersion&&SafariVersion<=0xd){log(_0x4f7544(0x68a));var _0x4f6830=_0x311669['whipOut'][_0x4f7544(0x6cc)]()[_0x4f7544(0x642)](function(_0x2896b4){var _0x2dd292=_0x4f7544;return _0x2896b4[_0x2dd292(0x53f)]&&_0x2896b4['track']['kind']==_0x2dd292(0x30c);});if(!_0x4f6830){warnlog(_0x4f7544(0x6d9));return;}var _0x54adc4={};if(_0x3cf104<0x0)_0x54adc4[_0x4f7544(0x9fb)]=!![],_0x3cf104=0x9c4,_0x311669[_0x4f7544(0x739)]&&(_0x3cf104=_0x311669[_0x4f7544(0x739)]),_0x311669['maxvideobitrate']&&(_0x3cf104>_0x311669['maxvideobitrate']&&(_0x3cf104=_0x311669[_0x4f7544(0x84c)])),_0x54adc4[_0x4f7544(0x9d1)]=_0x3cf104*0x400;else _0x3cf104===0x0?_0x54adc4[_0x4f7544(0x9fb)]=![]:(_0x54adc4[_0x4f7544(0x9fb)]=!![],_0x54adc4[_0x4f7544(0x9d1)]=_0x3cf104*0x400);setEncodings(_0x4f6830,_0x54adc4,function(_0x4fa90b){var _0x1848aa=_0x4f7544;pokeIframeAPI(_0x1848aa(0x683),_0x4fa90b),log(_0x1848aa(0x984)+_0x4fa90b);},_0x3cf104);return;}else{if(_0x4f7544(0x8b1)in window&&_0x4f7544(0x537)in window[_0x4f7544(0x8b1)]['prototype']){var _0x4f6830=_0x311669[_0x4f7544(0x638)][_0x4f7544(0x6cc)]()[_0x4f7544(0x642)](function(_0x30f372){var _0x1c1c6a=_0x4f7544;return _0x30f372['track']&&_0x30f372[_0x1c1c6a(0x53f)]['kind']==_0x1c1c6a(0x30c);});if(!_0x4f6830){warnlog(_0x4f7544(0x6d9));return;}var _0x54adc4={};if(_0x3cf104<0x0)_0x54adc4[_0x4f7544(0x9fb)]==![]&&(_0x54adc4[_0x4f7544(0x9fb)]=!![]),_0x54adc4['maxBitrate']=null;else _0x3cf104===0x0?(_0x54adc4['active']=![],Firefox&&(_0x54adc4['maxBitrate']=0x1)):(_0x54adc4[_0x4f7544(0x9fb)]=!![],_0x54adc4['maxBitrate']=_0x3cf104*0x400);iPad||iOS||Firefox?_0x311669[_0x4f7544(0x638)][_0x4f7544(0x4d0)]?(clearInterval(_0x311669[_0x4f7544(0x638)][_0x4f7544(0x4d0)]),_0x311669[_0x4f7544(0x638)]['bitrateTimeoutFirefox']=setTimeout(function(){var _0x256913=_0x4f7544;log(_0x256913(0x4e8)+_0x3cf104),_0x311669[_0x256913(0x638)][_0x256913(0x4d0)]=![],_0x311669[_0x256913(0x217)](null);},0x1f4)):(_0x311669['whipOut'][_0x4f7544(0x4d0)]=setTimeout(function(){var _0x32a5b0=_0x4f7544;_0x311669['whipOut'][_0x32a5b0(0x4d0)]=![];},0x1f4),setEncodings(_0x4f6830,_0x54adc4,function(_0x4f82de){var _0x207671=_0x4f7544;log(_0x207671(0x888)+_0x4f82de),pokeIframeAPI(_0x207671(0x683),_0x4f82de);},_0x3cf104)):setEncodings(_0x4f6830,_0x54adc4,function(_0x24a53a){var _0x5134a8=_0x4f7544;log(_0x5134a8(0x3f1)+_0x24a53a),pokeIframeAPI('set-meshcast-video-bitrate',_0x24a53a);},_0x3cf104);return;}else warnlog(_0x4f7544(0x9ac));}}catch(_0x165ff6){errorlog(_0x165ff6);}},_0x311669['targetBitrate']=function(_0x197f7c,_0x11d0eb){var _0x1dd508=_0x134a17;_0x11d0eb===![]?(_0x311669['pcs'][_0x197f7c][_0x1dd508(0x9f3)]=![],_0x311669['limitBitrate'](_0x197f7c,-0x1)):(_0x11d0eb=parseInt(_0x11d0eb)||-0x1,_0x11d0eb>=0x0&&(_0x311669['pcs'][_0x197f7c][_0x1dd508(0x9f3)]=_0x11d0eb,_0x311669[_0x1dd508(0x3da)](_0x197f7c,_0x11d0eb)));},_0x311669[_0x134a17(0x347)]=function(_0x419ab3,_0x11bbd8){var _0x208c85=_0x134a17;_0x11bbd8===![]?(_0x311669[_0x208c85(0x859)][_0x419ab3][_0x208c85(0x9c8)]=![],_0x311669[_0x208c85(0x184)](_0x419ab3,-0x1)):(_0x11bbd8=parseInt(_0x11bbd8)||-0x1,_0x11bbd8>=0x0&&(_0x311669[_0x208c85(0x859)][_0x419ab3][_0x208c85(0x9c8)]=_0x11bbd8,_0x311669['limitAudioBitrate'](_0x419ab3,_0x11bbd8)));},_0x311669[_0x134a17(0x3da)]=function(_0x1eca7f,_0x2cf438=null,_0x62acd6=![]){var _0x586ce9=_0x134a17;warnlog(_0x586ce9(0x207)+_0x2cf438);if(!(_0x1eca7f in _0x311669[_0x586ce9(0x859)]))return;_0x311669[_0x586ce9(0x859)][_0x1eca7f]['bitrateTimeout']&&(clearInterval(_0x311669[_0x586ce9(0x859)][_0x1eca7f]['bitrateTimeout']),_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x62e)]=null);var _0x26f8b3=!![];if(_0x2cf438===null){if(_0x311669[_0x586ce9(0x859)][_0x1eca7f]['savedBitrate']===![]){if(_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x9b5)]===null)return;else _0x2cf438=_0x311669['pcs'][_0x1eca7f][_0x586ce9(0x9b5)],_0x26f8b3=![];}else _0x2cf438=_0x311669['pcs'][_0x1eca7f]['savedBitrate'];}_0x2cf438=parseInt(_0x2cf438);if(_0x311669['pcs'][_0x1eca7f][_0x586ce9(0x9f3)]&&_0x2cf438>_0x311669['pcs'][_0x1eca7f][_0x586ce9(0x9f3)])_0x2cf438=_0x311669[_0x586ce9(0x859)][_0x1eca7f]['setBitrate'];else _0x2cf438<0x0&&(_0x2cf438=_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x9f3)]||_0x311669['outboundVideoBitrate']||0x9c4);_0x311669[_0x586ce9(0x84c)]&&(_0x2cf438>_0x311669['maxvideobitrate']&&(_0x2cf438=_0x311669[_0x586ce9(0x84c)]));_0x26f8b3&&!_0x62acd6&&(log(_0x586ce9(0x5be)+_0x2cf438),_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x969)]=_0x2cf438);_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x273)]!==![]&&(_0x311669[_0x586ce9(0x859)][_0x1eca7f]['obsState'][_0x586ce9(0x4eb)]===![]&&(_0x2cf438>_0x311669['pcs'][_0x1eca7f][_0x586ce9(0x273)]&&(_0x26f8b3&&(_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x969)]=_0x2cf438),_0x2cf438=parseInt(_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x273)])||0x0)));if(_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x9b5)]!==null){if(_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x9b5)]<_0x2cf438)_0x2cf438=_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x9b5)],_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x436)][_0x586ce9(0xa2f)]=_0x2cf438,warnlog(_0x586ce9(0x3ab)+_0x2cf438+'-kbps');else _0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x9b5)]===_0x2cf438&&!_0x26f8b3?(_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x436)]['max_bandwidth_capped_kbps']=_0x2cf438,warnlog(_0x586ce9(0x372)+_0x2cf438+_0x586ce9(0x78b))):(warnlog(_0x586ce9(0x736)+_0x2cf438+_0x586ce9(0x78b)),_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x436)]['max_bandwidth_capped_kbps']=![]);}else _0x586ce9(0xa2f)in _0x311669[_0x586ce9(0x859)][_0x1eca7f]['stats']&&(_0x311669['pcs'][_0x1eca7f][_0x586ce9(0x436)]['max_bandwidth_capped_kbps']=![]);_0x62acd6===![]&&(_0x311669[_0x586ce9(0x335)]&&(_0x311669['pcs'][_0x1eca7f][_0x586ce9(0x5a6)]=_0x2cf438,_0x2cf438=_0x311669['limitTotalBitrateGuests'](_0x2cf438,_0x1eca7f)));if(_0x2cf438===0x0){var _0xe90eaa=Date['now']()-_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x586)];_0xe90eaa<_0x311669[_0x586ce9(0x25e)]&&(_0x2cf438=_0x311669[_0x586ce9(0x9ff)],log('starting\x20some\x20preload\x20bitrate\x20'+(Date['now']()-_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x586)])),_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x62e)]=setTimeout(function(_0x419588){var _0x5832ad=_0x586ce9;try{warnlog('stopping\x20some\x20preload\x20bitrate\x20'+(Date['now']()-_0x311669[_0x5832ad(0x859)][_0x419588][_0x5832ad(0x586)])),_0x311669[_0x5832ad(0x3da)](_0x419588,null);}catch(_0x3d20a0){};},_0x311669[_0x586ce9(0x25e)]-_0xe90eaa+0x5,_0x1eca7f));}try{if((iOS||iPad)&&SafariVersion&&SafariVersion<=0xd){log(_0x586ce9(0x68a));if(_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x798)]==!![]&&_0x311669[_0x586ce9(0x859)][_0x1eca7f]['forceios']==![])return;var _0x1fafc3=getSenders2(_0x1eca7f)[_0x586ce9(0x642)](function(_0x5b0c90){var _0x1e4b8d=_0x586ce9;return _0x5b0c90['track']&&_0x5b0c90[_0x1e4b8d(0x53f)][_0x1e4b8d(0x7ad)]=='video';});if(!_0x1fafc3){warnlog(_0x586ce9(0x6d9));return;}var _0x5c945b={};_0x2cf438===0x0?_0x5c945b[_0x586ce9(0x9fb)]=![]:(_0x5c945b[_0x586ce9(0x9fb)]=!![],_0x5c945b[_0x586ce9(0x9d1)]=_0x2cf438*0x400);setEncodings(_0x1fafc3,_0x5c945b,function(_0x3f882d){var _0x11b0fd=_0x586ce9;pokeIframeAPI('setVideoBitrate',_0x3f882d[0x0],_0x3f882d[0x1]),pokeIframeAPI(_0x11b0fd(0x581),_0x3f882d[0x0],_0x3f882d[0x1]),log('bandwidth\x20set\x20a!\x20'+_0x3f882d[0x0]);},[_0x2cf438,_0x1eca7f]);return;}else{if(_0x586ce9(0x8b1)in window&&_0x586ce9(0x537)in window[_0x586ce9(0x8b1)][_0x586ce9(0x776)]){var _0x1fafc3=getSenders2(_0x1eca7f)['find'](function(_0x372fc8){var _0x27be4d=_0x586ce9;return _0x372fc8[_0x27be4d(0x53f)]&&_0x372fc8[_0x27be4d(0x53f)][_0x27be4d(0x7ad)]==_0x27be4d(0x30c);});if(!_0x1fafc3){warnlog('can\x27t\x20change\x20bitrate;\x20no\x20video\x20sender\x20found');return;}var _0x5c945b={};_0x2cf438===0x0?(_0x5c945b[_0x586ce9(0x9fb)]=![],Firefox&&(_0x5c945b[_0x586ce9(0x9d1)]=0x1,_0x5c945b[_0x586ce9(0xa4b)]=0x3e8)):(_0x5c945b[_0x586ce9(0x9fb)]=!![],_0x5c945b['maxBitrate']=_0x2cf438*0x400);if(_0x2cf438!==0x0){var _0x5a0d4e=_0x311669['calculateScale'](_0x1eca7f,_0x2cf438);if(_0x5a0d4e<=0x0||_0x5a0d4e==0x64){var _0x24564b=getChromiumVersion();_0x24564b>0x50?_0x5c945b[_0x586ce9(0xa4b)]=null:_0x5c945b[_0x586ce9(0xa4b)]=0x1;}else _0x5c945b[_0x586ce9(0xa4b)]=0x64/_0x5a0d4e;iPad||iOS||Firefox?_0x311669[_0x586ce9(0x859)][_0x1eca7f]['bitrateTimeoutFirefox']?(clearInterval(_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x4d0)]),_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x4d0)]=setTimeout(function(_0x46b16e,_0x251d1e){var _0x3de3ad=_0x586ce9;log(_0x3de3ad(0x4e8)+_0x2cf438),_0x311669[_0x3de3ad(0x859)][_0x46b16e][_0x3de3ad(0x4d0)]=![],_0x311669[_0x3de3ad(0x3da)](_0x46b16e,null,_0x251d1e);},0x1f4,_0x1eca7f,_0x62acd6)):(_0x311669['pcs'][_0x1eca7f]['bitrateTimeoutFirefox']=setTimeout(function(_0x18dd75){var _0x2e0ddc=_0x586ce9;_0x311669['pcs'][_0x18dd75][_0x2e0ddc(0x4d0)]=![];},0x1f4,_0x1eca7f),setEncodings(_0x1fafc3,_0x5c945b,function(_0xc465d4){var _0x2b287a=_0x586ce9;log('bandwidth\x20set\x20b!\x20'+_0xc465d4[0x0]),_0x311669[_0x2b287a(0x859)][_0xc465d4[0x1]][_0x2b287a(0x436)][_0x2b287a(0x7ee)]=parseInt(_0xc465d4[0x2])+'%',pokeIframeAPI(_0x2b287a(0x37e),_0xc465d4[0x0],_0xc465d4[0x1]),pokeIframeAPI(_0x2b287a(0x907),_0xc465d4[0x2],_0xc465d4[0x1]),pokeIframeAPI(_0x2b287a(0x581),_0xc465d4[0x0],_0xc465d4[0x1]),pokeIframeAPI(_0x2b287a(0x8e0),_0xc465d4[0x2],_0xc465d4[0x1]);},[_0x2cf438,_0x1eca7f,_0x5a0d4e])):(warnlog(_0x5c945b),setEncodings(_0x1fafc3,_0x5c945b,function(_0x5ef75f){var _0x576b46=_0x586ce9;log('bandwidth\x20set\x20c!\x20'+_0x5ef75f[0x0]),_0x311669[_0x576b46(0x859)][_0x5ef75f[0x1]][_0x576b46(0x436)]['scaleFactor']=parseInt(_0x5ef75f[0x2])+'%',pokeIframeAPI(_0x576b46(0x37e),_0x5ef75f[0x0],_0x5ef75f[0x1]),pokeIframeAPI('setVideoScale',_0x5ef75f[0x2],_0x5ef75f[0x1]),pokeIframeAPI(_0x576b46(0x581),_0x5ef75f[0x0],_0x5ef75f[0x1]),pokeIframeAPI(_0x576b46(0x8e0),_0x5ef75f[0x2],_0x5ef75f[0x1]);},[_0x2cf438,_0x1eca7f,_0x5a0d4e]));}else iPad||iOS||Firefox?_0x311669['pcs'][_0x1eca7f][_0x586ce9(0x4d0)]?(clearInterval(_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x4d0)]),_0x311669['pcs'][_0x1eca7f][_0x586ce9(0x4d0)]=setTimeout(function(_0x2ec560,_0x2bd845){var _0x58acd6=_0x586ce9;log('bitrate\x20timeout;\x20ios/firefox\x20specific:\x20'+_0x2cf438),_0x311669[_0x58acd6(0x859)][_0x2ec560][_0x58acd6(0x4d0)]=![],_0x311669[_0x58acd6(0x3da)](_0x2ec560,null,_0x2bd845);},0x1f4,_0x1eca7f,_0x62acd6)):(_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x4d0)]=setTimeout(function(_0x5c2e66){var _0xc7f44d=_0x586ce9;_0x311669[_0xc7f44d(0x859)][_0x5c2e66][_0xc7f44d(0x4d0)]=![];},0x1f4,_0x1eca7f),setEncodings(_0x1fafc3,_0x5c945b,function(_0x27858d){var _0x148541=_0x586ce9;log('bandwidth\x20set\x20d!\x20'+_0x27858d[0x0]),pokeIframeAPI(_0x148541(0x37e),_0x27858d[0x0],_0x27858d[0x1]),pokeIframeAPI(_0x148541(0x581),_0x27858d[0x0],_0x27858d[0x1]);},[_0x2cf438,_0x1eca7f])):setEncodings(_0x1fafc3,_0x5c945b,function(_0x192359){var _0x467070=_0x586ce9;log('bandwidth\x20set\x20e!\x20'+_0x192359[0x0]),pokeIframeAPI(_0x467070(0x37e),_0x192359[0x0],_0x192359[0x1]),pokeIframeAPI(_0x467070(0x581),_0x192359[0x0],_0x192359[0x1]);},[_0x2cf438,_0x1eca7f]);}else warnlog(_0x586ce9(0x9ac));}}catch(_0x3905f5){errorlog(_0x3905f5);}};function _0x526e6b(_0x510321,_0x21490f,_0x45e24b){var _0x1ce6be=_0x134a17;if(_0x311669[_0x1ce6be(0x9b9)])return _0x21490f;warnlog('getOptimizedScale:\x20'+_0x21490f+'\x20:\x20'+_0x45e24b);if(_0x45e24b<0x0)_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64;else{if(_0x45e24b>=0x259)_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64;else{if(_0x1ce6be(0x65b)in _0x311669[_0x1ce6be(0x859)][_0x510321])_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64;else{if(_0x311669[_0x1ce6be(0x701)])_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64;else{var _0x1a8762=getNativeOutputResolution();if(_0x1a8762)try{_0x1a8762=_0x1a8762[_0x1ce6be(0x530)]*_0x1a8762['height'],_0x1a8762=Math[_0x1ce6be(0x507)](_0x1a8762,0.5);}catch(_0x253473){_0x1a8762=![];}warnlog('dimension:\x20'+_0x1a8762);if(_0x45e24b>=0x15e){if(_0x1a8762&&_0x1a8762<=0x1e0)_0x311669['pcs'][_0x510321][_0x1ce6be(0x291)]=0x64;else{if(_0x311669[_0x1ce6be(0x1e3)]){if(_0x1a8762&&_0x1a8762>=0x5a0)_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64/0x3;else _0x311669['flagship']?_0x1a8762&&_0x1a8762>=0x3c0?_0x311669['pcs'][_0x510321][_0x1ce6be(0x291)]=0x64/0x2:_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64:_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64/0x2;}else{if(_0x1a8762&&_0x1a8762>=0x5a0)_0x311669['pcs'][_0x510321][_0x1ce6be(0x291)]=0x64/2.5;else _0x1a8762&&_0x1a8762>=0x3c0?_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64/0x2:_0x311669[_0x1ce6be(0x859)][_0x510321]['scaleDueToBitrate']=0x64;}}}else{if(_0x45e24b>=0xc9){if(_0x1a8762&&_0x1a8762<0x1e0)_0x311669['pcs'][_0x510321][_0x1ce6be(0x291)]=0x64;else{if(_0x311669[_0x1ce6be(0x1e3)]){if(_0x1a8762&&_0x1a8762>=0x5a0)_0x311669[_0x1ce6be(0x859)][_0x510321]['scaleDueToBitrate']=0x64/0x4;else _0x311669[_0x1ce6be(0x5f6)]?_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64/0x2:_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64/2.5;}else _0x1a8762&&_0x1a8762>=0x5a0?_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64/0x3:_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64/0x2;}}else{if(_0x1a8762&&_0x1a8762<=0xf0)_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64;else{if(_0x45e24b>=0x51){if(_0x311669[_0x1ce6be(0x1e3)]){if(_0x1a8762&&_0x1a8762>=0x5a0)_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64/0x6;else _0x311669[_0x1ce6be(0x5f6)]?_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64/0x3:_0x311669['pcs'][_0x510321][_0x1ce6be(0x291)]=0x64/0x4;}else _0x1a8762&&_0x1a8762>=0x5a0?_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64/0x4:_0x311669[_0x1ce6be(0x859)][_0x510321]['scaleDueToBitrate']=0x64/0x3;}else{if(_0x311669[_0x1ce6be(0x1e3)]){if(_0x1a8762&&_0x1a8762>=0x3c0)_0x311669['pcs'][_0x510321][_0x1ce6be(0x291)]=0x64/0x6;else _0x311669[_0x1ce6be(0x5f6)]?_0x311669['pcs'][_0x510321][_0x1ce6be(0x291)]=0x64/0x4:_0x311669['pcs'][_0x510321][_0x1ce6be(0x291)]=0x64/0x5;}else _0x1a8762&&_0x1a8762>=0x5a0?_0x311669['pcs'][_0x510321][_0x1ce6be(0x291)]=0x64/0x5:_0x311669['pcs'][_0x510321]['scaleDueToBitrate']=0x64/0x4;}}}}}}}}return _0x311669['pcs'][_0x510321][_0x1ce6be(0x291)]<_0x21490f&&(_0x21490f=_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]),_0x21490f;}function _0x300658(_0x55cdec,_0x49859f=0x2710){var _0x3fd1c5=_0x134a17;_0x49859f=parseInt(_0x49859f);if(_0x311669[_0x3fd1c5(0x543)])_0x49859f+=_0x311669['audiobitrate'];else{if(_0x311669[_0x3fd1c5(0x75b)]&&_0x311669[_0x3fd1c5(0x5a8)]==0x5)_0x49859f+=0x20;else _0x311669[_0x3fd1c5(0x5a8)]&&_0x311669[_0x3fd1c5(0x5a8)]!=0x3?_0x49859f+=0x100:_0x49859f+=0x20;}return log(_0x3fd1c5(0x4b6)+_0x49859f),_0x49859f<0x1&&(_0x49859f=0x1),_0x55cdec=CodecsHandler[_0x3fd1c5(0x5bb)](_0x55cdec,{'min':parseInt(_0x49859f/0xa)||0x1,'max':_0x49859f||0x1},_0x311669[_0x3fd1c5(0x24e)]),_0x55cdec;}_0x311669['signData']=function(_0x476619,_0x52e635){var _0x4c36fb=_0x134a17;log(_0x476619),_0x311669['mykey']==={}&&log(_0x4c36fb(0x839)),window[_0x4c36fb(0x189)][_0x4c36fb(0x388)][_0x4c36fb(0x503)]({'name':'RSASSA-PKCS1-v1_5'},_0x311669[_0x4c36fb(0x51f)][_0x4c36fb(0x445)],_0x311669['enc']['encode'](_0x476619))['then'](function(_0x34f07c){var _0x42462a=_0x4c36fb;_0x34f07c=new Uint8Array(_0x34f07c),_0x34f07c=_0x34f07c[_0x42462a(0x21d)]((_0x253553,_0x359234)=>_0x253553+_0x359234[_0x42462a(0x721)](0x10)[_0x42462a(0x595)](0x2,'0'),''),_0x52e635(_0x476619,_0x34f07c),log(JSON[_0x42462a(0x479)](_0x34f07c));})[_0x4c36fb(0x3b6)](errorlog);},_0x311669['verifyData']=function(_0x555aac,_0x488bf4){var _0x16e2c2=_0x134a17;_0x555aac[_0x16e2c2(0x925)]=new Uint8Array(_0x555aac[_0x16e2c2(0x925)][_0x16e2c2(0x76e)](/.{1,2}/g)[_0x16e2c2(0x468)](_0x26612a=>parseInt(_0x26612a,0x10)));if(_0x311669[_0x16e2c2(0x19b)][_0x488bf4][_0x16e2c2(0xa5a)])return window[_0x16e2c2(0x189)]['subtle'][_0x16e2c2(0x2f0)]({'name':_0x16e2c2(0x597)},_0x311669[_0x16e2c2(0x19b)][_0x488bf4][_0x16e2c2(0xa5a)],_0x555aac[_0x16e2c2(0x925)],_0x311669[_0x16e2c2(0x558)][_0x16e2c2(0x9d3)](_0x555aac[_0x16e2c2(0x3bc)]))[_0x16e2c2(0x619)](function(_0x3fee0b){return _0x3fee0b;})[_0x16e2c2(0x3b6)](function(_0x4849a6){return errorlog(_0x4849a6),![];});},_0x311669[_0x134a17(0x4bf)]=function(_0xa5a7a4){var _0x44bffe=_0x134a17;if(_0x311669[_0x44bffe(0x8a3)])return _0x311669[_0x44bffe(0x8ff)]!==![]?(_0xa5a7a4=_0xa5a7a4[_0x44bffe(0x7f5)](0x0,-0x1*_0x311669[_0x44bffe(0x8ff)][_0x44bffe(0x8e9)]),_0xa5a7a4):generateHash(_0x311669[_0x44bffe(0x8a3)]+_0x311669[_0x44bffe(0x9c5)],0x6)[_0x44bffe(0x619)](function(_0x4f8296){var _0x4ff385=_0x44bffe;return _0x311669[_0x4ff385(0x8ff)]=_0x4f8296,_0xa5a7a4=_0xa5a7a4[_0x4ff385(0x7f5)](0x0,-0x1*_0x311669[_0x4ff385(0x8ff)][_0x4ff385(0x8e9)]),_0xa5a7a4;})[_0x44bffe(0x3b6)](errorlog);return _0xa5a7a4;},_0x311669['ping']=function(){var _0x1d5e49=_0x134a17;if(_0x311669[_0x1d5e49(0x1d0)])return;clearTimeout(_0x311669[_0x1d5e49(0x35c)]);if(!_0x311669['ws']||_0x311669['ws']['readyState']!==0x1)return;_0x311669['pingTimeout']=setTimeout(function(){var _0x10124d=_0x1d5e49;log(_0x10124d(0x2a1));var _0x9e3457={};_0x9e3457['request']=_0x10124d(0x66a),_0x311669[_0x10124d(0x663)](_0x9e3457);},0xbb8);},_0x311669['watchStream']=async function(_0x4c6d81){var _0x1b5d91=_0x134a17;await _0x311669[_0x1b5d91(0x857)]();if(_0x4c6d81[_0x1b5d91(0x8e9)]>0x0){if(_0x4c6d81===_0x311669['streamID']){warnlog(_0x1b5d91(0x28c));return;}var _0x4987f6={};_0x4987f6[_0x1b5d91(0x727)]=_0x1b5d91(0x6a8),_0x4987f6[_0x1b5d91(0x9bb)]=_0x4c6d81,_0x311669[_0x1b5d91(0x663)](_0x4987f6),_0x311669[_0x1b5d91(0x8fb)][_0x4c6d81]=!![],pokeIframeAPI(_0x1b5d91(0x6fa),_0x4c6d81);}else log(_0x1b5d91(0x9ee));},_0x311669[_0x134a17(0x28a)]=async function _0x4fbc41(_0x3251df){var _0x2a89c4=_0x134a17;_0x311669[_0x2a89c4(0x3cd)]===![]&&(_0x311669[_0x2a89c4(0x3cd)]=!![]);await _0x311669[_0x2a89c4(0x857)]();var _0x452c34={};_0x452c34[_0x2a89c4(0x727)]='joinroom';_0x311669[_0x2a89c4(0x75b)]&&!_0x311669['directorView']&&(_0x452c34[_0x2a89c4(0x178)]=!![]);_0x311669[_0x2a89c4(0x1d0)]&&(_0x452c34[_0x2a89c4(0x9bb)]=_0x311669[_0x2a89c4(0x9bb)]);var _0x55873a='';return _0x311669[_0x2a89c4(0x9f7)]&&(_0x55873a=_0x311669[_0x2a89c4(0x9f7)]),_0x311669['password']?_0x311669['hash']?generateHash(_0x3251df+_0x311669[_0x2a89c4(0x8a3)]+_0x311669[_0x2a89c4(0x9c5)]+_0x55873a,0x10)[_0x2a89c4(0x619)](function(_0x32305e){var _0x2eecaa=_0x2a89c4;return _0x311669[_0x2eecaa(0x1d0)]&&(_0x311669[_0x2eecaa(0x5cf)]=_0x32305e),_0x452c34[_0x2eecaa(0x4e1)]=_0x32305e,_0x311669['sendMsg'](_0x452c34),_0x311669[_0x2eecaa(0x778)]=_0x4b2dee(),log(_0x2eecaa(0x958)),pokeIframeAPI(_0x2eecaa(0x860),_0x3251df),_0x311669[_0x2eecaa(0x778)];})[_0x2a89c4(0x3b6)](errorlog):generateHash(_0x311669['password']+_0x311669['salt'],0x6)[_0x2a89c4(0x619)](function(_0x587027){var _0x4d96e0=_0x2a89c4;return _0x311669[_0x4d96e0(0x8ff)]=_0x587027,log(_0x4d96e0(0x172)+_0x587027),log('rejoining\x20room'),_0x311669['joinRoom'](_0x3251df);})[_0x2a89c4(0x3b6)](errorlog):(_0x311669[_0x2a89c4(0x1d0)]&&(_0x311669[_0x2a89c4(0x5cf)]=_0x3251df),_0x452c34[_0x2a89c4(0x4e1)]=_0x3251df,_0x311669[_0x2a89c4(0x663)](_0x452c34),_0x311669[_0x2a89c4(0x778)]=_0x4b2dee(),log(_0x2a89c4(0x5e7)),pokeIframeAPI(_0x2a89c4(0x860),_0x3251df),_0x311669['listPromise']);},_0x311669['sendMsg']=function(_0x261f33,_0x36efcb=![]){var _0x2d9eca=_0x134a17;_0x36efcb&&(_0x261f33[_0x2d9eca(0x54a)]=_0x36efcb);if(_0x311669[_0x2d9eca(0x1d0)]){_0x311669[_0x2d9eca(0x54a)]?_0x261f33[_0x2d9eca(0x1dc)]=_0x311669['UUID']:(_0x311669[_0x2d9eca(0x54a)]=_0x311669[_0x2d9eca(0xa76)](0x14),_0x261f33[_0x2d9eca(0x1dc)]=_0x311669[_0x2d9eca(0x54a)]);if(_0x261f33[_0x2d9eca(0x54a)]&&_0x261f33['from']===_0x261f33[_0x2d9eca(0x54a)])return;_0x311669[_0x2d9eca(0x75b)]&&(_0x261f33[_0x2d9eca(0x75b)]=!![]),!(_0x2d9eca(0x4e1)in _0x261f33)&&(_0x311669[_0x2d9eca(0x5cf)]&&(_0x261f33['roomid']=_0x311669['roomenc']));}clearTimeout(_0x311669[_0x2d9eca(0x35c)]);try{if(_0x311669[_0x2d9eca(0x8a3)]){if(_0x261f33[_0x2d9eca(0x9bb)]){if(_0x311669[_0x2d9eca(0x8ff)]!==![]){if(!_0x311669['ws']||(typeof _0x311669['ws']!==_0x2d9eca(0x53b)||_0x311669['ws'][_0x2d9eca(0x8ee)]!==0x1))log(_0x261f33,_0x2d9eca(0x5c2)),_0x311669['msg'][_0x2d9eca(0x505)](_0x261f33);else{_0x261f33[_0x2d9eca(0x9bb)]=_0x261f33['streamID'][_0x2d9eca(0x30d)](0x0,0x2c)+_0x311669['hash'][_0x2d9eca(0x30d)](0x0,0x6);var _0x4e3319=JSON[_0x2d9eca(0x479)](_0x261f33);if(_0x4e3319[_0x2d9eca(0x8e9)]>0x3a98){errorlog(_0x2d9eca(0x568)),errorlog(_0x261f33),errorlog(_0x4e3319['length']);return;}_0x311669['ws'][_0x2d9eca(0x2b9)](_0x4e3319);}}else return generateHash(_0x311669[_0x2d9eca(0x8a3)]+_0x311669['salt'],0x6)['then'](function(_0x34f6ca){var _0x3f8812=_0x2d9eca;_0x311669['hash']=_0x34f6ca;if(typeof _0x311669['ws']!==_0x3f8812(0x53b)||_0x311669['ws'][_0x3f8812(0x8ee)]!==0x1)log(_0x261f33,'could\x20not\x20be\x20sent;\x20queuing\x20it'),_0x311669[_0x3f8812(0x6e7)]['push'](_0x261f33);else{_0x261f33[_0x3f8812(0x9bb)]=_0x261f33['streamID'][_0x3f8812(0x30d)](0x0,0x2c)+_0x311669[_0x3f8812(0x8ff)][_0x3f8812(0x30d)](0x0,0x6);var _0x209ce2=JSON['stringify'](_0x261f33);if(_0x209ce2[_0x3f8812(0x8e9)]>0x3a98){errorlog('msg\x20size\x20error');return;}_0x311669['ws']['send'](_0x209ce2);}})[_0x2d9eca(0x3b6)](errorlog);}else{if(!_0x311669['ws']||(typeof _0x311669['ws']!==_0x2d9eca(0x53b)||_0x311669['ws'][_0x2d9eca(0x8ee)]!==0x1))log(_0x261f33,_0x2d9eca(0x5c2)),_0x311669['msg']['push'](_0x261f33);else{var _0x4e3319=JSON[_0x2d9eca(0x479)](_0x261f33);if(_0x4e3319['length']>0x3a98){errorlog(_0x2d9eca(0x568));return;}_0x311669['ws']['send'](_0x4e3319);}}}else{if(typeof _0x311669['ws']!==_0x2d9eca(0x53b)||_0x311669['ws']['readyState']!==0x1)warnlog(_0x2d9eca(0x3e6)),_0x311669[_0x2d9eca(0x6e7)][_0x2d9eca(0x505)](_0x261f33);else{var _0x4e3319=JSON[_0x2d9eca(0x479)](_0x261f33);if(_0x4e3319[_0x2d9eca(0x8e9)]>0x3a98){errorlog(_0x2d9eca(0x568));return;}_0x311669['ws'][_0x2d9eca(0x2b9)](_0x4e3319);}}}catch(_0x2461b3){errorlog(_0x2461b3);}},_0x311669[_0x134a17(0x2db)]=function(_0x2f479e,_0x223463=![],_0x217456=![]){var _0x5b11fa=_0x134a17,_0x560e63=[],_0x3de05a=JSON['stringify'](_0x2f479e);for(var _0x2f31e1 in _0x311669[_0x5b11fa(0x859)]){if(_0x217456&&_0x217456===_0x2f31e1)continue;if(_0x223463&&_0x223463!==_0x2f31e1)continue;try{_0x311669['pcs'][_0x2f31e1]['sendChannel']['send'](_0x3de05a),_0x560e63[_0x5b11fa(0x505)](_0x2f31e1);}catch(_0x347c4f){warnlog(_0x5b11fa(0x3e5));}if(_0x223463&&_0x223463===_0x2f31e1)return _0x560e63[_0x5b11fa(0x8e9)];}for(var _0x2f31e1 in _0x311669['rpcs']){if(_0x217456&&_0x217456===_0x2f31e1)continue;if(_0x223463&&_0x223463!==_0x2f31e1)continue;if(_0x560e63[_0x5b11fa(0x2c2)](_0x2f31e1))continue;if(_0x311669[_0x5b11fa(0x16a)][_0x2f31e1][_0x5b11fa(0x751)]){warnlog(_0x3de05a);continue;}try{if(_0x5b11fa(0x65b)in _0x311669[_0x5b11fa(0x16a)][_0x2f31e1]){var _0x132705=JSON[_0x5b11fa(0x52d)](_0x2f479e);_0x132705[_0x5b11fa(0x67b)]=!![],_0x132705=JSON[_0x5b11fa(0x479)](_0x132705),_0x311669[_0x5b11fa(0x16a)][_0x311669[_0x5b11fa(0x16a)][_0x2f31e1]['realUUID']]['receiveChannel'][_0x5b11fa(0x2b9)](_0x132705);}else _0x311669['rpcs'][_0x2f31e1][_0x5b11fa(0x4a7)][_0x5b11fa(0x2b9)](_0x3de05a);_0x560e63[_0x5b11fa(0x505)](_0x2f31e1);}catch(_0x4cf219){warnlog('RTC\x20Connection\x20seems\x20to\x20be\x20dead\x20or\x20not\x20yet\x20open?\x202');}}return _0x560e63['length'];},_0x311669[_0x134a17(0x966)]=function(_0x4feb04,_0x5a4161=![]){var _0x187aed=_0x134a17,_0x43f86d=![];if(_0x187aed(0x54a)in _0x4feb04)_0x43f86d=_0x311669['sendMessage'](_0x4feb04,_0x4feb04[_0x187aed(0x54a)]),_0x43f86d?(log(_0x4feb04),log(_0x187aed(0x69c))):(log('sending\x20message\x20via\x20WSS\x20as\x20WebRTC\x20failed\x20to\x20send\x20message'),_0x311669[_0x187aed(0x663)](_0x4feb04));else _0x5a4161?(_0x43f86d=_0x311669['sendMessage'](_0x4feb04),_0x43f86d?(log(_0x4feb04),log('successfully\x20sent\x20message\x20vis\x20WebRTC\x20instead\x20of\x20WSS\x20to\x20all\x20RTC\x20Peers')):(log(_0x187aed(0x914)),_0x311669[_0x187aed(0x663)](_0x4feb04))):(_0x311669[_0x187aed(0x663)](_0x4feb04),warnlog('sending\x20message\x20via\x20server'),warnlog(_0x4feb04));},_0x311669['anyrequest']=function(_0x3486ce,_0x286fc4=![]){var _0x215ba2=_0x134a17,_0x2a6909=![];if('UUID'in _0x3486ce)_0x2a6909=_0x311669[_0x215ba2(0xa14)](_0x3486ce,_0x3486ce[_0x215ba2(0x54a)]),_0x2a6909?log(_0x215ba2(0x69c)):(log(_0x215ba2(0xa58)),_0x311669[_0x215ba2(0x663)](_0x3486ce));else _0x286fc4?(_0x2a6909=_0x311669[_0x215ba2(0xa14)](_0x3486ce),_0x2a6909?log(_0x215ba2(0x32b)):(log(_0x215ba2(0x914)),_0x311669[_0x215ba2(0x663)](_0x3486ce))):(_0x311669[_0x215ba2(0x663)](_0x3486ce),warnlog(_0x215ba2(0x8f9)),warnlog(_0x3486ce));},_0x311669[_0x134a17(0xa37)]=function(_0x5986c5){var _0x5313a1=_0x134a17;log(_0x5986c5);if('action'in _0x5986c5){if('target'in _0x5986c5){if(_0x5313a1(0x98d)in _0x5986c5){if(_0x311669[_0x5313a1(0x98d)]!==![]){var _0x2ddedf=![],_0x4cb66b=0x0;for(var _0x2991ad in _0x311669['rpcs']){_0x4cb66b+=0x1;if(_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x9bb)]===_0x5986c5[_0x5313a1(0x81f)]){if(_0x5313a1(0x8d7)in _0x5986c5){if(_0x5986c5['action']=='mute')_0x5986c5[_0x5313a1(0x8d7)]==0x1?(log('Mute\x20video\x203306'),_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x4fc)]=!![],applyMuteState(_0x2991ad)):(log(_0x5313a1(0xa7e)),_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x4fc)]=![],applyMuteState(_0x2991ad)),_0x311669[_0x5313a1(0x919)](_0x2991ad);else{if(_0x5986c5[_0x5313a1(0x95f)]=='display'){if(_0x311669[_0x5313a1(0x311)])return;;if(_0x311669['scene']===_0x5986c5[_0x5313a1(0x98d)]){if(_0x311669['sceneType']==0x2){if(_0x5986c5[_0x5313a1(0x8d7)]==0x0){_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x3e1)]=!![],applyMuteState(_0x2991ad);_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]['style'][_0x5313a1(0x355)]&&_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x886)]['display']!=='none'&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]=_0x5313a1(0x42d),_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x935)]=![],_0x2ddedf=!![]));_0x311669[_0x5313a1(0x16a)][_0x2991ad]['iframeEle']&&_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x9b0)]['style']['display']&&_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x9b0)]['style'][_0x5313a1(0x355)]!==_0x5313a1(0x42d)&&(_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)]['display']='none',_0x311669[_0x5313a1(0x16a)][_0x2991ad]['iframeEle']['sceneType2']=![],_0x2ddedf=!![]);var _0x10266b=0x0,_0x179252=![];for(var _0x158cb3 in _0x311669[_0x5313a1(0x16a)]){_0x158cb3!==_0x2991ad&&(_0x311669['rpcs'][_0x158cb3][_0x5313a1(0x49b)]&&_0x311669['rpcs'][_0x158cb3][_0x5313a1(0x49b)]['sceneType2']&&(_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x49b)]['sceneType2']>_0x10266b&&(_0x10266b=_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x49b)]['sceneType2'],_0x179252=_0x158cb3)),_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x9b0)]&&_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x9b0)]['sceneType2']&&(_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x9b0)]['sceneType2']>_0x10266b&&(_0x10266b=_0x311669['rpcs'][_0x158cb3][_0x5313a1(0x9b0)][_0x5313a1(0x935)],_0x179252=_0x158cb3)));}_0x179252&&(_0x311669[_0x5313a1(0x16a)][_0x179252][_0x5313a1(0x3e1)]=![],applyMuteState(_0x179252),_0x311669[_0x5313a1(0x16a)][_0x179252][_0x5313a1(0x49b)]&&(_0x311669[_0x5313a1(0x16a)][_0x179252]['videoElement'][_0x5313a1(0x3b5)]&&clearInterval(_0x311669[_0x5313a1(0x16a)][_0x179252][_0x5313a1(0x49b)][_0x5313a1(0x3b5)]),_0x311669[_0x5313a1(0x16a)][_0x179252][_0x5313a1(0x49b)][_0x5313a1(0x9f4)]=![],_0x311669[_0x5313a1(0x802)]&&(_0x311669[_0x5313a1(0x16a)][_0x179252]['videoElement'][_0x5313a1(0x3b5)]=setTimeout(showControlBar[_0x5313a1(0x5a3)](null,_0x311669[_0x5313a1(0x16a)][_0x179252][_0x5313a1(0x49b)]),0x3e8)),_0x311669[_0x5313a1(0x16a)][_0x179252][_0x5313a1(0x49b)]['style']['display']&&_0x311669[_0x5313a1(0x16a)][_0x179252][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]!==_0x5313a1(0xa87)&&(_0x311669[_0x5313a1(0x16a)][_0x179252]['videoElement'][_0x5313a1(0x886)][_0x5313a1(0x355)]=_0x5313a1(0xa87),_0x311669['rpcs'][_0x179252][_0x5313a1(0x49b)][_0x5313a1(0x935)]=Date[_0x5313a1(0x610)](),_0x2ddedf=!![])),_0x311669[_0x5313a1(0x16a)][_0x179252]['iframeEle']&&_0x311669[_0x5313a1(0x16a)][_0x179252][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]&&_0x311669[_0x5313a1(0x16a)][_0x179252]['iframeEle'][_0x5313a1(0x886)][_0x5313a1(0x355)]!==_0x5313a1(0xa87)&&(_0x311669['rpcs'][_0x179252]['iframeEle'][_0x5313a1(0x886)]['display']=_0x5313a1(0xa87),_0x311669['rpcs'][_0x179252]['iframeEle']['sceneType2']=Date[_0x5313a1(0x610)](),_0x2ddedf=!![]));}else{for(var _0x158cb3 in _0x311669['rpcs']){_0x158cb3!==_0x2991ad&&(_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x3e1)]=!![],applyMuteState(_0x158cb3),_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x49b)]&&(_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x49b)]['style'][_0x5313a1(0x355)]&&_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x49b)]['style']['display']!==_0x5313a1(0x42d)&&(_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]=_0x5313a1(0x42d),_0x2ddedf=!![])),_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x9b0)]&&_0x311669['rpcs'][_0x158cb3][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]&&_0x311669['rpcs'][_0x158cb3][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]!==_0x5313a1(0x42d)&&(_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x9b0)][_0x5313a1(0x886)]['display']=_0x5313a1(0x42d),_0x2ddedf=!![]));}_0x311669[_0x5313a1(0x16a)][_0x2991ad]['mutedStateScene']=![],applyMuteState(_0x2991ad),_0x311669[_0x5313a1(0x16a)][_0x2991ad]['videoElement']&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x3b5)]&&clearInterval(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x3b5)]),_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x9f4)]=![],_0x311669[_0x5313a1(0x802)]&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x3b5)]=setTimeout(showControlBar['bind'](null,_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]),0x3e8)),_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]&&_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]['style'][_0x5313a1(0x355)]!==_0x5313a1(0xa87)&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]='block',_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]['sceneType2']=Date[_0x5313a1(0x610)](),_0x2ddedf=!![])),_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x9b0)]&&_0x311669[_0x5313a1(0x16a)][_0x2991ad]['iframeEle']['style'][_0x5313a1(0x355)]&&_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]!=='block'&&(_0x311669['rpcs'][_0x2991ad]['iframeEle']['style'][_0x5313a1(0x355)]=_0x5313a1(0xa87),_0x311669[_0x5313a1(0x16a)][_0x2991ad]['iframeEle']['sceneType2']=Date['now'](),_0x2ddedf=!![]);}}else{if(_0x311669[_0x5313a1(0x301)]==0x1){if(_0x5986c5[_0x5313a1(0x8d7)]==0x0)_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]['style'][_0x5313a1(0x355)]&&_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]['style']['display']!==_0x5313a1(0x42d)&&(_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]=_0x5313a1(0x42d),_0x2ddedf=!![])),_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x9b0)]&&_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]&&_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]!==_0x5313a1(0x42d)&&(_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)]['display']=_0x5313a1(0x42d),_0x2ddedf=!![]);else{for(var _0x158cb3 in _0x311669['rpcs']){_0x158cb3!==_0x2991ad&&(_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x49b)]&&(_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]&&_0x311669['rpcs'][_0x158cb3][_0x5313a1(0x49b)]['style'][_0x5313a1(0x355)]!==_0x5313a1(0x42d)&&(_0x311669['rpcs'][_0x158cb3][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]=_0x5313a1(0x42d),_0x2ddedf=!![])),_0x311669['rpcs'][_0x158cb3][_0x5313a1(0x9b0)]&&_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x9b0)]['style'][_0x5313a1(0x355)]&&_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]!==_0x5313a1(0x42d)&&(_0x311669[_0x5313a1(0x16a)][_0x158cb3]['iframeEle'][_0x5313a1(0x886)][_0x5313a1(0x355)]='none',_0x2ddedf=!![]));}_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x49b)]&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x3b5)]&&clearInterval(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]['controlTimer']),_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x9f4)]=![],_0x311669[_0x5313a1(0x802)]&&(_0x311669['rpcs'][_0x2991ad]['videoElement'][_0x5313a1(0x3b5)]=setTimeout(showControlBar[_0x5313a1(0x5a3)](null,_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]),0x3e8)),_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]&&_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]!==_0x5313a1(0xa87)&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad]['videoElement']['style'][_0x5313a1(0x355)]=_0x5313a1(0xa87),_0x2ddedf=!![])),_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x9b0)]&&_0x311669['rpcs'][_0x2991ad]['iframeEle'][_0x5313a1(0x886)]['display']&&_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]!==_0x5313a1(0xa87)&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]=_0x5313a1(0xa87),_0x2ddedf=!![]);}}else _0x5986c5[_0x5313a1(0x8d7)]==0x0?(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x3e1)]=!![],applyMuteState(_0x2991ad),_0x311669[_0x5313a1(0x16a)][_0x2991ad]['videoElement']&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x886)]['display']&&_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]!==_0x5313a1(0x42d)&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]['style']['display']=_0x5313a1(0x42d),_0x2ddedf=!![])),_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x9b0)]&&_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]&&_0x311669[_0x5313a1(0x16a)][_0x2991ad]['iframeEle'][_0x5313a1(0x886)][_0x5313a1(0x355)]!==_0x5313a1(0x42d)&&(_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]='none',_0x2ddedf=!![])):(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x3e1)]=![],applyMuteState(_0x2991ad),_0x311669[_0x5313a1(0x16a)][_0x2991ad]['videoElement']&&(_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x49b)]['controlTimer']&&clearInterval(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x3b5)]),_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x9f4)]=![],_0x311669[_0x5313a1(0x802)]&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x3b5)]=setTimeout(showControlBar[_0x5313a1(0x5a3)](null,_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]),0x3e8)),_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]&&_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]['style']['display']!=='block'&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]='block',_0x2ddedf=!![])),_0x311669[_0x5313a1(0x16a)][_0x2991ad]['iframeEle']&&_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)]['display']&&_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]!==_0x5313a1(0xa87)&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]=_0x5313a1(0xa87),_0x2ddedf=!![]));}}_0x311669['sceneSync'](_0x2991ad);}else _0x5986c5[_0x5313a1(0x95f)]==_0x5313a1(0x356)&&(log(parseInt(_0x5986c5[_0x5313a1(0x8d7)])/0x64),_0x311669['rpcs'][_0x2991ad]['videoElement']&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x356)]=parseInt(_0x5986c5[_0x5313a1(0x8d7)])/0x64,log(_0x5313a1(0x267))));}}}}_0x2ddedf&&updateMixer();}}else{if(_0x5986c5['action']=='migrate'){}else{if(_0x5986c5[_0x5313a1(0x95f)]=='hangup'){}}}}else _0x5986c5[_0x5313a1(0x95f)]===_0x5313a1(0x559)&&(warnlog(_0x5313a1(0x70e)),log(_0x5986c5),_0x311669[_0x5313a1(0x559)]=_0x5986c5[_0x5313a1(0x8d7)],pokeIframeAPI(_0x5313a1(0x276),_0x311669[_0x5313a1(0x559)]),updateMixer());}},_0x311669['newMainDirectorSetup']=function(){var _0x11687b=_0x134a17;log(_0x11687b(0x262)),_0x311669['directorUUID']in _0x311669[_0x11687b(0x859)]&&(_0x311669[_0x11687b(0x859)][_0x311669[_0x11687b(0x2f3)]][_0x11687b(0x436)]&&_0x311669[_0x11687b(0x859)][_0x311669['directorUUID']][_0x11687b(0x436)][_0x11687b(0x6ee)]&&(_0x311669[_0x11687b(0x859)][_0x311669[_0x11687b(0x2f3)]][_0x11687b(0x436)][_0x11687b(0x6ee)][_0x11687b(0x75b)]=!![])),_0x311669['directorUUID']in _0x311669[_0x11687b(0x16a)]&&(_0x311669[_0x11687b(0x16a)][_0x311669['directorUUID']]['stats']&&_0x311669['rpcs'][_0x311669[_0x11687b(0x2f3)]][_0x11687b(0x436)][_0x11687b(0x6ee)]&&(_0x311669[_0x11687b(0x16a)][_0x311669[_0x11687b(0x2f3)]][_0x11687b(0x436)]['info'][_0x11687b(0x75b)]=!![]),_0x311669[_0x11687b(0x75b)]&&(getById(_0x11687b(0x2f7)+_0x311669[_0x11687b(0x2f3)])[_0x11687b(0x424)]['add'](_0x11687b(0x770)),_0x311669['rpcs'][_0x311669[_0x11687b(0x2f3)]][_0x11687b(0x89c)]===![]&&miniTranslate(getById(_0x11687b(0x1c8)+_0x311669['directorUUID']),_0x11687b(0x330)))),_0x311669['requestCoDirector'](),updateUserList();},_0x311669[_0x134a17(0x857)]=async function _0x398e5b(_0x591448=![]){var _0x4b4643=_0x134a17;if(_0x311669[_0x4b4643(0x2f4)]===!![]){log(_0x4b4643(0x1cb));return;}if(_0x311669['ws']!==null){log('already\x20connected');return;}_0x311669[_0x4b4643(0x8f5)]==![]&&(_0x311669[_0x4b4643(0x39a)]!==![]?_0x311669[_0x4b4643(0x8f5)]=_0x4b4643(0x310):_0x311669[_0x4b4643(0x8f5)]=_0x4b4643(0x3fa));if(!RTCPeerConnection){console[_0x4b4643(0x4a4)](getTranslation(_0x4b4643(0x43a)));!_0x311669[_0x4b4643(0x78c)]&&warnUser(getTranslation(_0x4b4643(0x43a)),![],![]);return;}_0x311669['ws']===null&&(_0x311669['ws']=![],await chooseBestTURN());if(_0x311669[_0x4b4643(0x1d0)]===![]){_0x311669[_0x4b4643(0x7af)]=_0x311669[_0x4b4643(0xa76)](0xc);for(var _0x59c6b3 in _0x311669[_0x4b4643(0x16a)]){warnlog(_0x4b4643(0x554)),_0x311669['rpcs'][_0x59c6b3]['connectionState']===_0x4b4643(0x190)&&(warnlog(_0x4b4643(0x405)),_0x311669['closeRPC'](_0x59c6b3));}}_0x311669[_0x4b4643(0x197)]?(_0x311669['ws']={},_0x311669['ws']['readyState']=0x1,_0x311669['ws'][_0x4b4643(0x2b9)]=function(_0x2703f9){var _0x4aeb3f=_0x4b4643;parent[_0x4aeb3f(0x5ef)]({'bypass':_0x2703f9},_0x311669[_0x4aeb3f(0xa2e)]);},setTimeout(function(){var _0x395c3c=_0x4b4643;_0x311669['ws'][_0x395c3c(0x777)]();},0xa)):_0x311669['ws']=new WebSocket(_0x311669[_0x4b4643(0x8f5)]),_0x591448==![]&&(_0x311669[_0x4b4643(0x6f3)]===!![]&&(_0x311669[_0x4b4643(0x6f3)]=null,toggleClock()),_0x311669['timeout']=setTimeout(function(){var _0x16efe3=_0x4b4643;pokeIframeAPI(_0x16efe3(0x49c),_0x16efe3(0x94c)),pokeIframeAPI(_0x16efe3(0x77d),_0x16efe3(0x94c)),errorlog(_0x16efe3(0x390)),!_0x311669[_0x16efe3(0x78c)]&&(!_0x311669[_0x16efe3(0x7c5)]&&setTimeout(function(){var _0x38bf56=_0x16efe3;warnUser(getTranslation(_0x38bf56(0x922)),![],![]);},0x1));},0x7530)),_0x311669['ws'][_0x4b4643(0x777)]=function _0xc4b4f5(){var _0x3b9409=_0x4b4643;if(_0x311669['auth'])try{_0x311669[_0x3b9409(0x663)]({'auth':_0x311669[_0x3b9409(0x662)]});}catch(_0x834491){errorlog(_0x834491);}_0x311669[_0x3b9409(0x271)]=!![],clearTimeout(_0x311669[_0x3b9409(0x35c)]),clearTimeout(_0x311669['timeout']),log(_0x3b9409(0x6fd)),checkConnection();if(_0x311669[_0x3b9409(0x74b)]){errorlog(_0x3b9409(0x39e));for(_0x16cc22 in _0x311669[_0x3b9409(0x16a)]){try{_0x311669[_0x3b9409(0x16a)][_0x16cc22][_0x3b9409(0x9bb)]?!_0x311669['include'][_0x3b9409(0x2c2)](_0x311669[_0x3b9409(0x16a)][_0x16cc22][_0x3b9409(0x9bb)])&&_0x311669[_0x3b9409(0x7b8)](_0x16cc22):_0x311669[_0x3b9409(0x7b8)](_0x16cc22);}catch(_0x5a95aa){}}for(_0x16cc22 in _0x311669['pcs']){try{_0x311669[_0x3b9409(0x9d2)](_0x16cc22);}catch(_0x17fb30){}}_0x311669[_0x3b9409(0x74b)]=![],_0x311669[_0x3b9409(0x45b)]=![];}if(_0x311669[_0x3b9409(0x6e7)]!==[])try{var _0x3bb182=_0x311669[_0x3b9409(0x6e7)][_0x3b9409(0x7f5)](-0x1e);_0x311669[_0x3b9409(0x6e7)]=[];for(var _0x2f0b5e in _0x3bb182){log('resending\x20message'),_0x311669[_0x3b9409(0x663)](_0x3bb182[_0x2f0b5e]);}}catch(_0x2d036a){errorlog(_0x2d036a);}if(_0x591448==!![]){pokeIframeAPI(_0x3b9409(0x49c),_0x3b9409(0x24b)),pokeIframeAPI(_0x3b9409(0x77d),'reconnected');_0x311669[_0x3b9409(0x22b)]&&_0x311669[_0x3b9409(0x6df)]();if(_0x311669[_0x3b9409(0x4e1)]){log('ROOMID\x20EANBLED'),log('Update\x20Mixer\x20Event\x20on\x20REsize\x20SET'),joinRoom(_0x311669[_0x3b9409(0x4e1)]);if(_0x311669[_0x3b9409(0xa19)]['length']){var _0x43cf56=Object['keys'](_0x311669['waitingWatchList']);for(var _0x16cc22 in _0x43cf56){_0x311669[_0x3b9409(0xa19)][_0x3b9409(0x2c2)](_0x43cf56[_0x16cc22])&&(log(_0x3b9409(0x72a)+_0x43cf56[_0x16cc22]),_0x311669['watchStream'](_0x43cf56[_0x16cc22]));}}}else{var _0x43cf56=Object[_0x3b9409(0x19b)](_0x311669['waitingWatchList']);for(var _0x16cc22 in _0x43cf56){log(_0x3b9409(0x72a)+_0x43cf56[_0x16cc22]),_0x311669[_0x3b9409(0x5ad)](_0x43cf56[_0x16cc22]);}}}else pokeIframeAPI(_0x3b9409(0x49c),_0x3b9409(0x7ea)),pokeIframeAPI(_0x3b9409(0x77d),'connected');},_0x311669[_0x4b4643(0x79a)]=function(_0x423b84){var _0x3e96b9=_0x4b4643;for(var _0x493d8d in _0x311669[_0x3e96b9(0x16a)]){if(_0x311669[_0x3e96b9(0x16a)][_0x493d8d][_0x3e96b9(0x9bb)]===_0x423b84)return log(_0x3e96b9(0x91b)),![];}if(_0x311669[_0x3e96b9(0x8fb)][_0x423b84])return log('already\x20waiting\x20for\x20stream'),![];return _0x311669[_0x3e96b9(0x5ad)](_0x423b84),log('requesting\x20stream'),!![];},_0x311669['ws'][_0x4b4643(0x17a)]=async function(_0x35679c){var _0x1c9f0a=_0x4b4643;clearTimeout(_0x311669[_0x1c9f0a(0x35c)]);try{var _0x1eea7b=JSON[_0x1c9f0a(0x52d)](_0x35679c[_0x1c9f0a(0x3bc)]);}catch(_0x1ef619){try{var _0x1eea7b=JSON['parse'](_0x35679c[_0x1c9f0a(0x3bc)][_0x1c9f0a(0x721)]());}catch(_0x38357d){errorlog(_0x38357d);return;}}'streamID'in _0x1eea7b&&(_0x1eea7b[_0x1c9f0a(0x9bb)]=_0x311669['desaltStreamID'](_0x1eea7b[_0x1c9f0a(0x9bb)]));if(_0x1c9f0a(0x24d)in _0x1eea7b){_0x1eea7b=await _0x311669[_0x1c9f0a(0x643)](_0x1eea7b);if(!_0x1eea7b)return;}if(_0x311669[_0x1c9f0a(0x1d0)]){if(_0x1c9f0a(0x1dc)in _0x1eea7b&&_0x311669[_0x1c9f0a(0x54a)]&&_0x1eea7b[_0x1c9f0a(0x1dc)]===_0x311669['UUID'])return;else log(_0x1eea7b);if(_0x1c9f0a(0x54a)in _0x1eea7b){if(_0x311669['UUID']){if(_0x1eea7b[_0x1c9f0a(0x54a)]!==_0x311669['UUID'])return;}else return;delete _0x1eea7b[_0x1c9f0a(0x54a)];}if('roomid'in _0x1eea7b){if(!_0x311669[_0x1c9f0a(0x5cf)])return;if(_0x1c9f0a(0x727)in _0x1eea7b){if(_0x1eea7b[_0x1c9f0a(0x727)]===_0x1c9f0a(0x3fe)){if('roomid'in _0x1eea7b){if(_0x1c9f0a(0x81f)in _0x1eea7b){if(_0x1eea7b[_0x1c9f0a(0x81f)]==_0x311669['UUID']){_0x1eea7b[_0x1c9f0a(0x727)]=_0x1c9f0a(0x74b),_0x311669[_0x1c9f0a(0x5cf)]=_0x1eea7b[_0x1c9f0a(0x4e1)];var _0x4dabec={};_0x4dabec[_0x1c9f0a(0x727)]=_0x1c9f0a(0xa7a),_0x4dabec[_0x1c9f0a(0x4e1)]=_0x311669['roomenc'],_0x4dabec[_0x1c9f0a(0x9bb)]=_0x311669[_0x1c9f0a(0x9bb)],_0x311669[_0x1c9f0a(0x663)](_0x4dabec);}else return;}else return;}else return;}else{if(_0x1eea7b['roomid']!==_0x311669[_0x1c9f0a(0x5cf)])return;}}else{if(_0x1eea7b[_0x1c9f0a(0x4e1)]!==_0x311669[_0x1c9f0a(0x5cf)])return;}delete _0x1eea7b[_0x1c9f0a(0x4e1)];}if('director'in _0x1eea7b){if(_0x311669[_0x1c9f0a(0x9f7)]||_0x311669[_0x1c9f0a(0xa84)])await checkToken();else _0x1eea7b[_0x1c9f0a(0x1dc)]&&(_0x311669[_0x1c9f0a(0x2f3)]=_0x1eea7b[_0x1c9f0a(0x1dc)],_0x311669[_0x1c9f0a(0xa66)]=![],_0x311669[_0x1c9f0a(0x95b)]=[],_0x311669[_0x1c9f0a(0x95b)][_0x1c9f0a(0x505)](_0x311669[_0x1c9f0a(0x2f3)]),_0x311669[_0x1c9f0a(0x6ab)]());delete _0x1eea7b['director'];}'from'in _0x1eea7b&&(_0x1eea7b['UUID']=_0x1eea7b[_0x1c9f0a(0x1dc)],delete _0x1eea7b[_0x1c9f0a(0x1dc)]);if('request'in _0x1eea7b){if(_0x1eea7b['request']===_0x1c9f0a(0x6a8)){if(_0x1c9f0a(0x9bb)in _0x1eea7b){if(_0x1eea7b[_0x1c9f0a(0x9bb)]===_0x311669['streamID'])_0x1eea7b['request']='offerSDP';else return;}}else{if(_0x1eea7b[_0x1c9f0a(0x727)]===_0x1c9f0a(0x87b)){if(_0x311669[_0x1c9f0a(0x82b)]){if(_0x311669[_0x1c9f0a(0x82b)][_0x1c9f0a(0x2c2)](_0x1eea7b[_0x1c9f0a(0x9bb)])){play(_0x1eea7b['streamID']);return;}else return;}}else{if(_0x1eea7b[_0x1c9f0a(0x727)]===_0x1c9f0a(0xa7a)){if('streamID'in _0x1eea7b){if(_0x311669[_0x1c9f0a(0x82b)]){if(_0x311669[_0x1c9f0a(0x82b)][_0x1c9f0a(0x2c2)](_0x1eea7b[_0x1c9f0a(0x9bb)]))play(_0x1eea7b[_0x1c9f0a(0x9bb)]);else{}}else play(_0x1eea7b[_0x1c9f0a(0x9bb)]);}_0x1eea7b['request']='offerSDP';}}}}else{if(_0x1c9f0a(0x9bb)in _0x1eea7b){if(_0x311669[_0x1c9f0a(0x82b)]){if(_0x311669[_0x1c9f0a(0x82b)][_0x1c9f0a(0x2c2)](_0x1eea7b[_0x1c9f0a(0x9bb)])){}else return;}else{if(_0x311669[_0x1c9f0a(0x311)]){if(_0x311669['view']!==_0x1eea7b[_0x1c9f0a(0x9bb)])return;else{}}}}}}if(_0x1eea7b[_0x1c9f0a(0x727)]){if(_0x1eea7b['request']==_0x1c9f0a(0x49a)){if(_0x311669['queue']){if(_0x311669['directorList'][_0x1c9f0a(0x49e)](_0x1eea7b['UUID'])>=0x0)_0x311669[_0x1c9f0a(0x49a)](_0x1eea7b[_0x1c9f0a(0x54a)]);else _0x311669[_0x1c9f0a(0x75b)]&&(_0x1eea7b[_0x1c9f0a(0x54a)]in _0x311669[_0x1c9f0a(0x16a)]&&_0x311669[_0x1c9f0a(0x49a)](_0x1eea7b[_0x1c9f0a(0x54a)]));}else _0x311669[_0x1c9f0a(0x49a)](_0x1eea7b[_0x1c9f0a(0x54a)]);}else{if(_0x1eea7b[_0x1c9f0a(0x727)]==_0x1c9f0a(0xa53)){log(_0x1eea7b);if(_0x311669[_0x1c9f0a(0x9f7)]||_0x311669[_0x1c9f0a(0xa84)])await checkToken();else _0x1c9f0a(0x75b)in _0x1eea7b?(_0x311669[_0x1c9f0a(0x2f3)]=_0x1eea7b[_0x1c9f0a(0x75b)],_0x311669[_0x1c9f0a(0xa66)]=![],_0x311669['directorList']=[],_0x311669[_0x1c9f0a(0x95b)][_0x1c9f0a(0x505)](_0x311669[_0x1c9f0a(0x2f3)]),_0x311669[_0x1c9f0a(0x6ab)]()):(_0x311669['directorUUID']=![],_0x311669[_0x1c9f0a(0xa66)]=![],_0x311669[_0x1c9f0a(0x95b)]=[]);if(_0x311669[_0x1c9f0a(0xa84)]){}else{if(_0x1c9f0a(0x178)in _0x1eea7b){if(_0x311669[_0x1c9f0a(0x9f7)]||_0x1eea7b[_0x1c9f0a(0x178)]==![]){if(!_0x311669[_0x1c9f0a(0x78c)]){miniTranslate(getById('head4'),_0x1c9f0a(0x939));if(_0x311669[_0x1c9f0a(0x76b)])_0x311669['directorState']===null&&warnUser(getTranslation('room-is-claimed-codirector'),![],![]);else _0x311669[_0x1c9f0a(0x9f7)]?setTimeout(function(){warnUser(getTranslation('token-room-is-claimed'),![],![]);},0x1):setTimeout(function(){var _0x1f3262=_0x1c9f0a;warnUser(getTranslation(_0x1f3262(0x8ba)),![],![]);},0x1);}_0x311669['directorState']=![],pokeAPI(_0x1c9f0a(0x75b),![]),pokeIframeAPI(_0x1c9f0a(0x75b),![]);}else _0x311669[_0x1c9f0a(0x772)]=!![],pokeAPI(_0x1c9f0a(0x75b),!![]),pokeIframeAPI(_0x1c9f0a(0x75b),!![]);}}_0x311669['alreadyJoinedMembers']=_0x1eea7b['list'],_0x311669['listPromise'][_0x1c9f0a(0x8c5)](_0x1eea7b[_0x1c9f0a(0x93c)]);}else{if(_0x1eea7b['request']==_0x1c9f0a(0x74b)){_0x311669[_0x1c9f0a(0x983)]=[],_0x311669['transferred']=!![],_0x311669[_0x1c9f0a(0x45b)]=![],log(_0x1c9f0a(0x98e)),pokeIframeAPI(_0x1c9f0a(0x74b));let _0xf2718d=![];if(!_0x311669[_0x1c9f0a(0x75b)]){if(_0x311669[_0x1c9f0a(0x64f)]==0x2)_0x311669[_0x1c9f0a(0x64f)]=!![],_0x311669[_0x1c9f0a(0x74b)]=!![];else _0x311669['queue']==0x3?(_0x311669['queue']=![],_0xf2718d=!![]):(_0x311669[_0x1c9f0a(0x64f)]=![],_0x311669[_0x1c9f0a(0x74b)]=!![]);}else _0x311669[_0x1c9f0a(0x74b)]=!![];if(!_0xf2718d){for(_0x29af5b in _0x311669[_0x1c9f0a(0x16a)]){try{!_0x311669[_0x1c9f0a(0xa19)]['includes'](_0x311669['rpcs'][_0x29af5b][_0x1c9f0a(0x9bb)])&&(warnlog(_0x1c9f0a(0x3e7)),_0x311669[_0x1c9f0a(0x7b8)](_0x29af5b));}catch(_0x2359f4){}}for(_0x29af5b in _0x311669[_0x1c9f0a(0x859)]){try{log(_0x1c9f0a(0x193)),_0x311669[_0x1c9f0a(0x9d2)](_0x29af5b);}catch(_0x2e5477){}}}if(!_0xf2718d){if(_0x311669[_0x1c9f0a(0x9f7)]||_0x311669['mainDirectorPassword'])await checkToken();else'director'in _0x1eea7b?(_0x311669[_0x1c9f0a(0x2f3)]=_0x1eea7b[_0x1c9f0a(0x75b)],_0x311669[_0x1c9f0a(0xa66)]=![],_0x311669['directorList']=[],_0x311669[_0x1c9f0a(0x95b)][_0x1c9f0a(0x505)](_0x311669['directorUUID']),_0x311669[_0x1c9f0a(0x6ab)]()):(_0x311669[_0x1c9f0a(0x2f3)]=![],_0x311669[_0x1c9f0a(0xa66)]=![],_0x311669[_0x1c9f0a(0x95b)]=[]);youveBeenTransferred(),_0x311669['totalRoomBitrate']=_0x311669[_0x1c9f0a(0x6bc)],updateMixer();}else youveBeenActivated();log('Members\x20in\x20Room'),log(_0x1eea7b[_0x1c9f0a(0x93c)]);for(var _0x29af5b in _0x1eea7b[_0x1c9f0a(0x93c)]){if(_0x1c9f0a(0x54a)in _0x1eea7b['list'][_0x29af5b]){if(_0x1c9f0a(0x9bb)in _0x1eea7b[_0x1c9f0a(0x93c)][_0x29af5b]){if(_0x1eea7b['list'][_0x29af5b][_0x1c9f0a(0x54a)]in _0x311669[_0x1c9f0a(0x16a)])log('RTC\x20already\x20connected');else{var _0x1d98a0=_0x311669['desaltStreamID'](_0x1eea7b[_0x1c9f0a(0x93c)][_0x29af5b]['streamID']);log(_0x1c9f0a(0x669)+_0x1d98a0);if(_0x311669[_0x1c9f0a(0x64f)]){if(_0x311669['directorList'][_0x1c9f0a(0x49e)](_0x1eea7b['list'][_0x29af5b][_0x1c9f0a(0x54a)])>=0x0)play(_0x1d98a0,_0x1eea7b[_0x1c9f0a(0x93c)][_0x29af5b]['UUID']);else{if(_0x311669['view_set']&&_0x311669[_0x1c9f0a(0x82b)][_0x1c9f0a(0x2c2)](_0x1d98a0))play(_0x1d98a0,_0x1eea7b[_0x1c9f0a(0x93c)][_0x29af5b][_0x1c9f0a(0x54a)]);else _0x311669[_0x1c9f0a(0x983)][_0x1c9f0a(0x8e9)]<0x1388&&(!(_0x1d98a0 in _0x311669[_0x1c9f0a(0x241)])&&!_0x311669[_0x1c9f0a(0x983)][_0x1c9f0a(0x2c2)](_0x1d98a0)&&_0x311669['queueList']['push'](_0x1d98a0));}}else play(_0x1d98a0,_0x1eea7b[_0x1c9f0a(0x93c)][_0x29af5b][_0x1c9f0a(0x54a)]);}}}}updateQueue();}else{if(_0x1eea7b['request']==_0x1c9f0a(0x386)){log(_0x1eea7b);if(_0x311669[_0x1c9f0a(0x9f7)]||_0x311669[_0x1c9f0a(0xa84)])await checkToken();else'director'in _0x1eea7b?(_0x311669[_0x1c9f0a(0x2f3)]=_0x1eea7b[_0x1c9f0a(0x75b)],_0x311669[_0x1c9f0a(0xa66)]=![],_0x311669[_0x1c9f0a(0x95b)]=[],_0x311669['directorList'][_0x1c9f0a(0x505)](_0x311669['directorUUID']),_0x311669[_0x1c9f0a(0x6ab)]()):(_0x311669[_0x1c9f0a(0x2f3)]=![],_0x311669[_0x1c9f0a(0x95b)]=[],errorlog(_0x1c9f0a(0x573)));updateUserList();}else{if(_0x1eea7b['request']==_0x1c9f0a(0x7f2)){log(_0x1c9f0a(0x71d)),log(_0x1eea7b);try{if(_0x311669[_0x1c9f0a(0x9f7)]||_0x311669[_0x1c9f0a(0xa4f)]){}else'director'in _0x1eea7b&&(_0x1eea7b['director']==!![]&&_0x311669[_0x1c9f0a(0xa37)](_0x1eea7b));}catch(_0x510cdb){errorlog(_0x510cdb);}}else{if(_0x1eea7b['request']=='someonejoined'){if(_0x311669[_0x1c9f0a(0x9f7)]||_0x311669[_0x1c9f0a(0xa84)])await checkToken();else _0x1eea7b[_0x1c9f0a(0x75b)]&&(_0x311669[_0x1c9f0a(0x2f3)]=_0x1eea7b[_0x1c9f0a(0x54a)],_0x311669[_0x1c9f0a(0xa66)]=![],_0x311669['directorList']=[],_0x311669[_0x1c9f0a(0x95b)][_0x1c9f0a(0x505)](_0x311669['directorUUID']),_0x311669['newMainDirectorSetup']());if(_0x1c9f0a(0x9bb)in _0x1eea7b){log('Someone\x20Joined\x20the\x20Room\x20with\x20a\x20video');if(_0x311669['queue']){if(_0x311669['directorList']['indexOf'](_0x1eea7b[_0x1c9f0a(0x54a)])>=0x0)play(_0x1d98a0,_0x1eea7b[_0x1c9f0a(0x54a)]);else{if(_0x311669['view_set']&&_0x311669['view_set'][_0x1c9f0a(0x2c2)](_0x1d98a0))play(_0x1d98a0,_0x1eea7b['UUID']);else _0x311669[_0x1c9f0a(0x983)][_0x1c9f0a(0x8e9)]<0x1388&&(!(_0x1eea7b[_0x1c9f0a(0x9bb)]in _0x311669['watchTimeoutList'])&&!_0x311669['queueList'][_0x1c9f0a(0x2c2)](_0x1eea7b[_0x1c9f0a(0x9bb)])&&(_0x311669[_0x1c9f0a(0x983)][_0x1c9f0a(0x505)](_0x1eea7b[_0x1c9f0a(0x9bb)]),updateQueue(!![])));}}else play(_0x1eea7b['streamID']);}else log(_0x1c9f0a(0x994));}else{if(_0x1eea7b[_0x1c9f0a(0x727)]=='videoaddedtoroom'){log(_0x1c9f0a(0x7b6)),log(_0x1eea7b);if(_0x311669[_0x1c9f0a(0x64f)]){if(_0x311669[_0x1c9f0a(0x95b)][_0x1c9f0a(0x49e)](_0x1eea7b[_0x1c9f0a(0x54a)])>=0x0)play(_0x1d98a0,_0x1eea7b[_0x1c9f0a(0x54a)]);else{if(_0x311669[_0x1c9f0a(0x82b)]&&_0x311669[_0x1c9f0a(0x82b)][_0x1c9f0a(0x2c2)](_0x1d98a0))play(_0x1d98a0,_0x1eea7b[_0x1c9f0a(0x54a)]);else _0x311669[_0x1c9f0a(0x983)][_0x1c9f0a(0x8e9)]<0x1388&&(!(_0x1eea7b[_0x1c9f0a(0x9bb)]in _0x311669[_0x1c9f0a(0x241)])&&!_0x311669['queueList']['includes'](_0x1eea7b[_0x1c9f0a(0x9bb)])&&(_0x311669['queueList'][_0x1c9f0a(0x505)](_0x1eea7b[_0x1c9f0a(0x9bb)]),updateQueue(!![])));}}else play(_0x1eea7b[_0x1c9f0a(0x9bb)]);}else{if(_0x1eea7b[_0x1c9f0a(0x727)]=='alert'){errorlog(_0x1eea7b),pokeIframeAPI('alert',_0x1eea7b[_0x1c9f0a(0x6d8)]);if(_0x311669[_0x1c9f0a(0x98d)]===![]){if('message'in _0x1eea7b){if(_0x1eea7b[_0x1c9f0a(0x6d8)]===_0x1c9f0a(0x717))_0x311669[_0x1c9f0a(0x5db)]<0x2?(_0x311669[_0x1c9f0a(0x5db)]=parseInt(_0x311669[_0x1c9f0a(0x5db)])+0x1,setTimeout(function(){_0x311669['seedStream']();},0x1388)):(hangup(),!_0x311669[_0x1c9f0a(0x78c)]&&setTimeout(function(){var _0x5dbbf3=_0x1c9f0a;warnUser(getTranslation(_0x5dbbf3(0x3c7)),![],![]);},0x1));else{if(_0x311669[_0x1c9f0a(0x9f7)]||_0x311669[_0x1c9f0a(0xa4f)]){}else _0x1eea7b[_0x1c9f0a(0x6d8)]==='Room\x20is\x20already\x20claimed\x20by\x20someone\x20else.'?(!_0x311669['cleanOutput']&&(miniTranslate(getById(_0x1c9f0a(0x9d4)),_0x1c9f0a(0x939)),_0x311669[_0x1c9f0a(0x76b)]?_0x311669['directorState']===null&&warnUser(getTranslation('room-is-claimed-codirector'),![],![]):setTimeout(function(){warnUser(getTranslation('room-is-claimed'),![],![]);},0x1)),_0x311669[_0x1c9f0a(0x772)]=![],pokeAPI(_0x1c9f0a(0x75b),![]),pokeIframeAPI('director',![])):!_0x311669[_0x1c9f0a(0x78c)]&&setTimeout(function(){warnUser(_0x1eea7b['message']);},0x1);}}}}else _0x1eea7b[_0x1c9f0a(0x727)]==_0x1c9f0a(0x1ad)?_0x1c9f0a(0x6d8)in _0x1eea7b&&warnlog(_0x1eea7b[_0x1c9f0a(0x6d8)]):log(_0x1eea7b);}}}}}}}}else{if(_0x1eea7b['description'])_0x1c9f0a(0x9bb)in _0x1eea7b&&(_0x1eea7b[_0x1c9f0a(0x9bb)]in _0x311669[_0x1c9f0a(0x241)]&&(clearTimeout(_0x311669['watchTimeoutList'][_0x1eea7b[_0x1c9f0a(0x9bb)]]),delete _0x311669[_0x1c9f0a(0x241)][_0x1eea7b['streamID']])),_0x311669[_0x1c9f0a(0x6d1)](_0x1eea7b);else{if(_0x1eea7b['candidate'])log(_0x1c9f0a(0x8e2)),_0x311669[_0x1c9f0a(0x1d3)](_0x1eea7b);else{if(_0x1eea7b[_0x1c9f0a(0x2a8)])log(_0x1c9f0a(0x61b)),_0x311669[_0x1c9f0a(0x411)](_0x1eea7b);else _0x1eea7b[_0x1c9f0a(0xa83)]||_0x1eea7b[_0x1c9f0a(0x727)]==_0x1c9f0a(0x546)?(warnlog('Clean\x20up'),_0x1eea7b[_0x1c9f0a(0x54a)]in _0x311669[_0x1c9f0a(0x859)]&&(warnlog('problem'),log(_0x1c9f0a(0x193)),_0x311669[_0x1c9f0a(0x9d2)](_0x1eea7b['UUID'])),_0x1eea7b['UUID']in _0x311669[_0x1c9f0a(0x16a)]&&(warnlog('problem'),_0x311669['closeRPC'](_0x1eea7b[_0x1c9f0a(0x54a)]))):log(_0x1c9f0a(0x1e5));}}}},_0x311669['ws'][_0x4b4643(0x88c)]=async function(_0x5dfda9){warnlog(_0x5dfda9);},_0x311669['ws'][_0x4b4643(0x661)]=async function(_0xbcf61f){var _0x4b5bf1=_0x4b4643;clearTimeout(_0x311669[_0x4b5bf1(0x35c)]),pokeIframeAPI('hssConnection',_0x4b5bf1(0x208)),pokeIframeAPI(_0x4b5bf1(0x77d),_0x4b5bf1(0x208));try{_0x4b5bf1(0x7d2)in _0xbcf61f&&(_0xbcf61f[_0x4b5bf1(0x7d2)]==0x1f7&&(_0x591448==![]&&(clearTimeout(_0x311669['timeout']),!_0x311669['cleanOutput']&&warnUser(_0x4b5bf1(0xa67),0x7530,![]))));}catch(_0x4f597e){errorlog(_0x4f597e);}warnlog(_0x4b5bf1(0x8d9));if(_0x311669[_0x4b5bf1(0x4e5)]==![])try{_0x311669['ws'][_0x4b5bf1(0x8ee)]===WebSocket[_0x4b5bf1(0x2a9)]&&(_0x311669['ws']=null,setTimeout(()=>{var _0x17c3c8=_0x4b5bf1;try{_0x311669[_0x17c3c8(0x857)](!![]);}catch(_0x28b9a7){};},0x7d0));}catch(_0x14e4bb){errorlog(_0x14e4bb);}};},_0x311669[_0x134a17(0x8a1)]=function(_0x4affdc,_0x325a10=null){var _0x58fc32=_0x134a17;log(_0x58fc32(0x4e9)),warnlog(_0x4affdc),_0x4affdc=JSON[_0x58fc32(0x479)](_0x4affdc);if(_0x325a10==null){for(var _0x2e03c9 in _0x311669[_0x58fc32(0x859)]){try{_0x311669[_0x58fc32(0x859)][_0x2e03c9][_0x58fc32(0x73a)]['send'](_0x4affdc);}catch(_0x13ec4b){warnlog('RTC\x20Connection\x20seems\x20to\x20be\x20dead\x20or\x20not\x20yet\x20open?\x204');}}return!![];}else try{return _0x311669['pcs'][_0x325a10][_0x58fc32(0x73a)]['send'](_0x4affdc),!![];}catch(_0x1ac55d){return warnlog('RTC\x20Connection\x20seems\x20to\x20be\x20dead\x20or\x20not\x20yet\x20open?\x203'),warnlog(_0x4affdc),![];}return![];};var _0x34fdf7={};function _0x506aab(_0x34a77a){var _0x4dafd9=_0x134a17;try{var _0x381e58=_0x34fdf7[_0x34a77a]||![];_0x381e58&&(clearTimeout(_0x381e58[0x1]),delete _0x34fdf7[_0x34a77a],warnlog(_0x4dafd9(0x8cc)+_0x34a77a),_0x381e58[0x0]());}catch(_0x33efe4){errorlog(_0x33efe4);}}_0x311669[_0x134a17(0xa14)]=function(_0x38ad89,_0x1f7407=null,_0x589a81=![]){var _0x153dc5=_0x134a17;if(_0x1f7407==null){var _0x34aa75=[],_0x3ec696=JSON[_0x153dc5(0x479)](_0x38ad89);for(var _0x791d46 in _0x311669['rpcs']){if(_0x311669[_0x153dc5(0x16a)][_0x791d46][_0x153dc5(0x751)]){warnlog(_0x38ad89);continue;}try{if('realUUID'in _0x311669[_0x153dc5(0x16a)][_0x791d46]){var _0x2aa5aa=_0x38ad89;_0x2aa5aa[_0x153dc5(0x67b)]=!![],_0x2aa5aa=JSON[_0x153dc5(0x479)](_0x2aa5aa),_0x311669['rpcs'][_0x311669[_0x153dc5(0x16a)][_0x791d46][_0x153dc5(0x65b)]][_0x153dc5(0x4a7)]['send'](_0x2aa5aa);}else _0x311669[_0x153dc5(0x16a)][_0x791d46][_0x153dc5(0x4a7)][_0x153dc5(0x2b9)](_0x3ec696);_0x34aa75['push'](_0x791d46);}catch(_0x3bdf96){log(_0x38ad89),warnlog(_0x153dc5(0x180)),warnlog(_0x3bdf96);}}return _0x34aa75[_0x153dc5(0x8e9)];}else{if(_0x311669[_0x153dc5(0x16a)][_0x1f7407][_0x153dc5(0x751)]){warnlog(_0x38ad89);return;}try{if(_0x589a81)try{var _0xa2c72a=parseInt(Math['random']()*0x174876e7ff);_0x38ad89[_0x153dc5(0x4fb)]=_0xa2c72a;var _0x3bd689=setTimeout(function(_0x3b7285){var _0x502405=_0x34fdf7[_0x3b7285]||![];_0x502405&&(delete _0x34fdf7[_0x3b7285],_0x502405[0x0]());},0x1388,_0xa2c72a);_0x34fdf7[_0xa2c72a]=[_0x589a81,_0x3bd689],warnlog(_0x153dc5(0xa80)+_0xa2c72a);}catch(_0x183c4b){errorlog(_0x183c4b);}if(_0x153dc5(0x65b)in _0x311669[_0x153dc5(0x16a)][_0x1f7407]){var _0x2aa5aa=_0x38ad89;_0x2aa5aa['altUUID']=!![],_0x311669['rpcs'][_0x311669[_0x153dc5(0x16a)][_0x1f7407][_0x153dc5(0x65b)]]['receiveChannel'][_0x153dc5(0x2b9)](JSON[_0x153dc5(0x479)](_0x2aa5aa));}else _0x311669[_0x153dc5(0x16a)][_0x1f7407]['receiveChannel'][_0x153dc5(0x2b9)](JSON[_0x153dc5(0x479)](_0x38ad89));return!![];}catch(_0x47b0a8){return warnlog(_0x47b0a8),log('PUBLISHER\x27s\x20RTC\x20Connection\x20seems\x20to\x20be\x20dead?\x202'),![];}}},_0x311669[_0x134a17(0x813)]=function(_0x4c6b6a=![],_0x32c597=![]){var _0x56267a=_0x134a17;try{window['removeEventListener'](_0x56267a(0x99f),confirmUnload);}catch(_0x31f50d){}_0x32c597&&recordLocalVideo(_0x56267a(0x7f8));_0x311669['taintedSession']=!![],warnlog(_0x56267a(0x92c));try{recordLocalVideo(_0x56267a(0x86e));}catch(_0x1e2f59){}try{var _0x54242f={};_0x54242f[_0x56267a(0x8ab)]=!![],_0x54242f['bye']=!![],_0x311669[_0x56267a(0x8a1)](_0x54242f);}catch(_0x3357e3){}try{_0x311669['ws'][_0x56267a(0x1cf)]();}catch(_0x30ad21){}try{transferList[_0x56267a(0x982)](_0x2a84f0=>{var _0x4380d7=_0x56267a;_0x2a84f0[_0x4380d7(0x88f)]&&_0x2a84f0[_0x4380d7(0x88f)][_0x4380d7(0x1cf)](),_0x2a84f0[_0x4380d7(0x203)]&&_0x2a84f0[_0x4380d7(0x6da)];});}catch(_0x7d0450){errorlog(_0x7d0450);}try{_0x311669['canvasSource']&&_0x311669[_0x56267a(0x17b)][_0x56267a(0x5c4)]&&_0x311669[_0x56267a(0x17b)]['srcObject']['getTracks']()[_0x56267a(0x982)](function(_0x53766c){var _0x1fecc9=_0x56267a;_0x311669[_0x1fecc9(0x17b)]['srcObject']['removeTrack'](_0x53766c),_0x53766c[_0x1fecc9(0x86e)](),log(_0x1fecc9(0x93d));}),_0x311669['videoElement']&&_0x311669[_0x56267a(0x49b)][_0x56267a(0x5c4)]&&_0x311669['videoElement'][_0x56267a(0x5c4)][_0x56267a(0x951)]()['forEach'](function(_0x384e52){var _0x112be2=_0x56267a;_0x311669[_0x112be2(0x49b)][_0x112be2(0x5c4)]['removeTrack'](_0x384e52),_0x384e52['stop'](),log('stopping\x20old\x20track');}),_0x311669[_0x56267a(0x585)]&&_0x311669[_0x56267a(0x585)][_0x56267a(0x951)]()['forEach'](function(_0x4df8bf){var _0x975cde=_0x56267a;_0x311669[_0x975cde(0x585)]['removeTrack'](_0x4df8bf),_0x4df8bf['stop'](),log('stopping\x20old\x20track');}),_0x311669[_0x56267a(0x7d7)]&&_0x311669[_0x56267a(0x7d7)]['getTracks']()[_0x56267a(0x982)](function(_0x2fa901){var _0x22ed11=_0x56267a;_0x311669[_0x22ed11(0x7d7)][_0x22ed11(0x59b)](_0x2fa901),_0x2fa901[_0x22ed11(0x86e)](),log('stopping\x20old\x20track');}),_0x311669['screenStream']&&_0x311669[_0x56267a(0x7a0)][_0x56267a(0x951)]()[_0x56267a(0x982)](function(_0x5c8c56){var _0xa3b817=_0x56267a;_0x311669[_0xa3b817(0x7a0)][_0xa3b817(0x59b)](_0x5c8c56),_0x5c8c56['stop'](),log(_0xa3b817(0x93d));});}catch(_0xd318da){errorlog(_0xd318da);}try{for(i in _0x311669[_0x56267a(0x16a)]){try{_0x311669[_0x56267a(0x16a)][i][_0x56267a(0x49b)]&&(_0x311669[_0x56267a(0x16a)][i]['videoElement'][_0x56267a(0x2df)]&&recordLocalVideo(_0x56267a(0x86e),null,_0x311669[_0x56267a(0x16a)][i][_0x56267a(0x49b)]));}catch(_0x40b971){}log(_0x56267a(0x790)),_0x311669[_0x56267a(0x7b8)](i,!![]);}for(i in _0x311669[_0x56267a(0x859)]){log(_0x56267a(0x523)),_0x311669[_0x56267a(0x9d2)](i);}}catch(_0x4a55b2){errorlog(_0x4a55b2);}for(var _0x562510 in _0x311669[_0x56267a(0x241)]){clearTimeout(_0x311669['watchTimeoutList'][_0x562510]);}if(_0x4c6b6a){reloadRequested(),warnlog('Reloading?\x20uh\x20oh.\x20Why\x20didn\x27t\x20it?');return;}else setTimeout(function(){for(i in _0x311669){try{delete _0x311669[i];}catch(_0x27badb){}}delete _0x311669;},0x4b0),hangupComplete(),log(_0x56267a(0x4a1));},_0x311669['hangupDirector']=function(){var _0x17baa4=_0x134a17;_0x311669['taintedSession']=!![],_0x311669[_0x17baa4(0x701)]=![],pokeIframeAPI(_0x17baa4(0x728),_0x311669[_0x17baa4(0x701)],null,_0x311669[_0x17baa4(0x9bb)]),notifyOfScreenShare(),warnlog('hanging\x20up'),pokeIframeAPI(_0x17baa4(0x59a),![],![],_0x311669[_0x17baa4(0x9bb)]),pokeIframeAPI(_0x17baa4(0x22b),![],![],_0x311669['streamID']),pokeAPI(_0x17baa4(0x22b),![]);try{_0x311669['videoElement']&&_0x311669[_0x17baa4(0x49b)][_0x17baa4(0x5c4)]&&_0x311669['videoElement'][_0x17baa4(0x5c4)][_0x17baa4(0x951)]()[_0x17baa4(0x982)](function(_0x1f9ed3){var _0xc8270b=_0x17baa4;_0x311669['videoElement']['srcObject'][_0xc8270b(0x59b)](_0x1f9ed3),_0x1f9ed3[_0xc8270b(0x86e)](),log('stopping\x20old\x20track');});_0x311669[_0x17baa4(0x585)]&&(_0x311669[_0x17baa4(0x585)][_0x17baa4(0x87a)]()[_0x17baa4(0x982)](function(_0x59c420){var _0x1a663f=_0x17baa4;_0x311669['videoDevice']=_0x59c420[_0x1a663f(0x89c)][_0x1a663f(0x62c)]()[_0x1a663f(0x2f9)](/[\W]+/g,'_'),_0x311669[_0x1a663f(0x585)]['removeTrack'](_0x59c420),_0x59c420[_0x1a663f(0x86e)](),log('stopping\x20old\x20track');}),_0x311669[_0x17baa4(0x213)]=[],_0x311669[_0x17baa4(0x585)][_0x17baa4(0xa6c)]()[_0x17baa4(0x982)](function(_0x2dbf18){var _0x443a24=_0x17baa4;_0x311669['audioDevice'][_0x443a24(0x505)](_0x2dbf18[_0x443a24(0x89c)][_0x443a24(0x62c)]()[_0x443a24(0x2f9)](/[\W]+/g,'_')),_0x311669['streamSrc']['removeTrack'](_0x2dbf18),_0x2dbf18['stop'](),log('stopping\x20old\x20track');}),!_0x311669[_0x17baa4(0x213)]['length']&&(_0x311669[_0x17baa4(0x213)]=![]));_0x311669[_0x17baa4(0x7d7)]&&_0x311669[_0x17baa4(0x7d7)][_0x17baa4(0x951)]()[_0x17baa4(0x982)](function(_0x36d5de){var _0x8f51ff=_0x17baa4;_0x311669['streamSrcClone'][_0x8f51ff(0x59b)](_0x36d5de),_0x36d5de[_0x8f51ff(0x86e)]();});for(UUID in _0x311669[_0x17baa4(0x859)]){var _0x530f70=getSenders2(UUID);_0x530f70[_0x17baa4(0x982)](_0x2a0edc=>{var _0x50469b=_0x17baa4;_0x2a0edc[_0x50469b(0x53f)]&&(_0x2a0edc[_0x50469b(0x53f)]['enabled']=![]);});}try{document[_0x17baa4(0x5c8)](_0x17baa4(0x2f1))&&(!_0x311669[_0x17baa4(0x78e)]&&(_0x311669[_0x17baa4(0x78e)]={}),_0x311669[_0x17baa4(0x9bb)]&&(_0x311669[_0x17baa4(0x78e)][_0x311669[_0x17baa4(0x9bb)]]=getDetailedState(_0x311669['streamID'])),getById(_0x17baa4(0x2f1))[_0x17baa4(0x826)]['removeChild'](getById('container_director')),updateLockedElements());}catch(_0x21b70c){warnlog(_0x21b70c);}var _0x4b4bc8={};_0x4b4bc8['videoMuted']=!![],_0x4b4bc8[_0x17baa4(0xa39)]=!![],_0x311669[_0x17baa4(0x8a1)](_0x4b4bc8),getById(_0x17baa4(0x67e))[_0x17baa4(0x761)]();}catch(_0x54061d){errorlog(_0x17baa4(0x5ce));}log(_0x17baa4(0x7bb));},_0x311669[_0x134a17(0x903)]=function(_0x3ae371,_0x54bd25=![]){var _0x5da327=_0x134a17;_0x311669[_0x5da327(0x859)][_0x3ae371][_0x5da327(0x903)]({'iceRestart':_0x54bd25})[_0x5da327(0x619)](_0x55d817=>{var _0x5b426e=_0x5da327;log('create\x20offer\x20worked');if(SafariVersion&&SafariVersion<=0xd&&(iOS||iPad)){}else{if(_0x311669[_0x5b426e(0x5a8)]==0x3||_0x311669[_0x5b426e(0x5a8)]==0x5||_0x311669[_0x5b426e(0x5a8)]==0x1)_0x55d817[_0x5b426e(0x648)]=CodecsHandler[_0x5b426e(0x608)](_0x55d817[_0x5b426e(0x648)],{'stereo':0x1}),log(_0x5b426e(0x206));else{if(iOS||iPad){}else _0x311669['stereo']==0x4&&(_0x55d817[_0x5b426e(0x648)]=CodecsHandler[_0x5b426e(0x608)](_0x55d817[_0x5b426e(0x648)],{'stereo':0x2}),log(_0x5b426e(0x206)));}}(iOS||iPad)&&(_0x311669[_0x5b426e(0x971)]&&_0x55d817['sdp']['includes'](_0x5b426e(0x168))&&(_0x55d817[_0x5b426e(0x648)]=_0x55d817[_0x5b426e(0x648)]['replace'](_0x5b426e(0x168),'')));if(_0x311669[_0x5b426e(0x859)][_0x3ae371][_0x5b426e(0x3aa)])try{_0x55d817[_0x5b426e(0x648)]=CodecsHandler[_0x5b426e(0x37c)](_0x55d817['sdp'],_0x311669['pcs'][_0x3ae371][_0x5b426e(0x3aa)]),log(_0x5b426e(0x5fe)+_0x311669['pcs'][_0x3ae371]['preferVideoCodec']+_0x5b426e(0x508));}catch(_0x4240ec){errorlog(_0x4240ec),warnlog('couldn\x27t\x20set\x20preferred\x20video\x20codec');}if(_0x311669[_0x5b426e(0x859)][_0x3ae371][_0x5b426e(0x5ea)])try{if(_0x311669[_0x5b426e(0x859)][_0x3ae371][_0x5b426e(0x5ea)]===_0x5b426e(0x61d))_0x55d817[_0x5b426e(0x648)]=CodecsHandler[_0x5b426e(0x794)](_0x55d817[_0x5b426e(0x648)]);else{if(_0x311669['pcs'][_0x3ae371]['preferAudioCodec']===_0x5b426e(0x944)){if(_0x311669[_0x5b426e(0x8a9)]&&_0x311669[_0x5b426e(0x8a9)]==0x1)_0x55d817[_0x5b426e(0x648)]=CodecsHandler[_0x5b426e(0x230)](_0x55d817[_0x5b426e(0x648)],_0x311669[_0x5b426e(0x4b7)]||0xbb80,![]);else _0x311669['stereo']?_0x55d817[_0x5b426e(0x648)]=CodecsHandler[_0x5b426e(0x230)](_0x55d817[_0x5b426e(0x648)],_0x311669['micSampleRate']||0xbb80,!![]):_0x55d817[_0x5b426e(0x648)]=CodecsHandler[_0x5b426e(0x230)](_0x55d817[_0x5b426e(0x648)],_0x311669['micSampleRate']||0xbb80,![]);}else _0x55d817[_0x5b426e(0x648)]=CodecsHandler['preferAudioCodec'](_0x55d817[_0x5b426e(0x648)],_0x311669[_0x5b426e(0x859)][_0x3ae371][_0x5b426e(0x5ea)]);}log(_0x5b426e(0x5fe)+_0x311669['pcs'][_0x3ae371][_0x5b426e(0x5ea)]+_0x5b426e(0x2ee));}catch(_0x382a5f){errorlog(_0x382a5f),warnlog(_0x5b426e(0x57a));}Android&&_0x311669[_0x5b426e(0x6d0)]!==![]&&_0x311669[_0x5b426e(0x25f)]&&(_0x55d817[_0x5b426e(0x648)]=_0x55d817[_0x5b426e(0x648)][_0x5b426e(0x2f9)](/42e01f/gi,_0x5b426e(0x49f))),_0x311669['pcs'][_0x3ae371][_0x5b426e(0xa1b)](_0x55d817)['then'](function(){var _0x19a6fc=_0x5b426e;log(_0x19a6fc(0x9ae)+_0x3ae371),_0x311669['applyIsolatedChat'](_0x3ae371);var _0x173e20={};_0x173e20['UUID']=_0x3ae371,_0x173e20['streamID']=_0x311669[_0x19a6fc(0x9bb)],_0x173e20[_0x19a6fc(0x9f5)]=_0x311669[_0x19a6fc(0x859)][_0x3ae371][_0x19a6fc(0x1f3)],_0x173e20[_0x19a6fc(0x1fd)]=_0x311669['pcs'][_0x3ae371][_0x19a6fc(0x1fd)];_0x311669[_0x19a6fc(0x1d0)]&&(_0x173e20['isScene']=_0x311669[_0x19a6fc(0x98d)]);_0x311669[_0x19a6fc(0x97a)]!==![]&&(_0x173e20['slot']=_0x311669[_0x19a6fc(0x97a)]);if(_0x311669[_0x19a6fc(0x7a0)]!==![]){var _0x59d627=_0x311669['screenStream'][_0x19a6fc(0x951)](),_0x3d0d03=_0x311669[_0x19a6fc(0x859)][_0x3ae371][_0x19a6fc(0x6cc)](),_0x7eff8c=[];for(var _0x501442=0x0;_0x501442<_0x3d0d03['length'];_0x501442++){for(var _0x244e32=0x0;_0x244e32<_0x59d627[_0x19a6fc(0x8e9)];_0x244e32++){_0x3d0d03[_0x501442]['track']&&_0x3d0d03[_0x501442][_0x19a6fc(0x53f)]['id']==_0x59d627[_0x244e32]['id']&&_0x3d0d03[_0x501442]['track'][_0x19a6fc(0x7ad)]==_0x59d627[_0x244e32][_0x19a6fc(0x7ad)]&&_0x7eff8c['push'](_0x501442);}}_0x7eff8c[_0x19a6fc(0x8e9)]&&(_0x173e20[_0x19a6fc(0x43e)]=_0x7eff8c);}_0x311669['password']?_0x311669[_0x19a6fc(0x331)](JSON[_0x19a6fc(0x479)](_0x173e20[_0x19a6fc(0x9f5)]))[_0x19a6fc(0x619)](function(_0x35b91a){var _0x15bc8e=_0x19a6fc;_0x173e20[_0x15bc8e(0x9f5)]=_0x35b91a[0x0],_0x173e20[_0x15bc8e(0x9ed)]=_0x35b91a[0x1],_0x311669[_0x15bc8e(0x966)](_0x173e20);})[_0x19a6fc(0x3b6)](errorlog):_0x311669[_0x19a6fc(0x966)](_0x173e20);})[_0x5b426e(0x3b6)](errorlog);})[_0x5da327(0x3b6)](errorlog);},_0x311669['sendKeyFrameScenes']=function(){var _0x29b9f7=_0x134a17;for(var _0x7550d1 in _0x311669[_0x29b9f7(0x859)]){_0x311669[_0x29b9f7(0x859)][_0x7550d1][_0x29b9f7(0x98d)]!==![]?(_0x311669[_0x29b9f7(0x2e3)](_0x7550d1),log('FORCE\x20KEYFRAME\x20FOR\x20SCENE')):log(_0x29b9f7(0x862));}},_0x311669[_0x134a17(0x9d2)]=function(_0x1aa03c,_0x5e5cb4=!![]){var _0x4399ce=_0x134a17;log(_0x4399ce(0x9d2));if(!(_0x1aa03c in _0x311669[_0x4399ce(0x859)]))return;clearTimeout(_0x311669['pcs'][_0x1aa03c][_0x4399ce(0x44b)]),clearTimeout(_0x311669['pcs'][_0x1aa03c][_0x4399ce(0x9a4)]),clearInterval(_0x311669['pcs'][_0x1aa03c][_0x4399ce(0x927)]),pokeIframeAPI(_0x4399ce(0x397),![],_0x1aa03c);if(_0x4399ce(0x65b)in _0x311669[_0x4399ce(0x859)][_0x1aa03c]){delete _0x311669[_0x4399ce(0x859)][_0x1aa03c],applySceneState();return;}_0x1aa03c+_0x4399ce(0x33b)in _0x311669[_0x4399ce(0x859)]&&_0x311669[_0x4399ce(0x859)][_0x1aa03c+'_screen']['realUUID']&&_0x311669[_0x4399ce(0x859)][_0x1aa03c+_0x4399ce(0x33b)][_0x4399ce(0x65b)]===_0x1aa03c&&(clearTimeout(_0x311669[_0x4399ce(0x859)][_0x1aa03c+'_screen'][_0x4399ce(0x44b)]),clearTimeout(_0x311669[_0x4399ce(0x859)][_0x1aa03c+_0x4399ce(0x33b)][_0x4399ce(0x9a4)]),clearInterval(_0x311669[_0x4399ce(0x859)][_0x1aa03c+_0x4399ce(0x33b)]['requestedStatsInterval']),_0x311669[_0x4399ce(0x859)][_0x1aa03c+_0x4399ce(0x33b)]=null,delete _0x311669[_0x4399ce(0x859)][_0x1aa03c+_0x4399ce(0x33b)]);try{_0x311669[_0x4399ce(0x8a1)]({'bye':!![]},_0x1aa03c);}catch(_0x4b410b){}try{_0x311669[_0x4399ce(0x859)][_0x1aa03c][_0x4399ce(0x1cf)]();}catch(_0x5ef688){}_0x311669['pcs'][_0x1aa03c][_0x4399ce(0x798)]&&(_0x311669[_0x4399ce(0x3df)]&&(_0x5e5cb4&&(warnlog(_0x4399ce(0x556)),playtone(![],_0x4399ce(0x9c2))))),_0x311669[_0x4399ce(0x859)][_0x1aa03c]=null,_0x311669[_0x4399ce(0x4e5)]&&(!_0x311669[_0x4399ce(0x78c)]&&setTimeout(function _0x137844(){warnUser('Remote\x20peer\x20disconnected.\x20Due\x20to\x20enhanced\x20security,\x20please\x20refresh\x20to\x20create\x20a\x20new\x20connection.');},0x1)),delete _0x311669[_0x4399ce(0x859)][_0x1aa03c],_0x311669[_0x4399ce(0x1f6)](),applySceneState();},_0x311669[_0x134a17(0x7b8)]=function(_0xb141ee,_0x400b0c=![]){var _0x345cf4=_0x134a17;if(!(_0xb141ee in _0x311669['rpcs'])){log(_0x345cf4(0x8c0));return;}warnlog(_0x345cf4(0x7b8)),clearInterval(_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x9a4)]);try{_0x311669[_0x345cf4(0xa14)]({'bye':!![]},_0xb141ee),warnlog(_0x345cf4(0x3d1));}catch(_0x14a4f8){}try{var _0x2d8da6=_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x9bb)];}catch(_0x3e101c){}try{_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x1cf)]();}catch(_0x2cea8c){warnlog(_0x345cf4(0x8a7));}_0x311669[_0x345cf4(0x16a)][_0xb141ee]['motionDetectionInterval']&&clearInterval(_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x665)]);try{_0x311669['rpcs'][_0xb141ee]['streamSrc']&&_0x311669[_0x345cf4(0x16a)][_0xb141ee]['streamSrc'][_0x345cf4(0x951)]()[_0x345cf4(0x982)](function(_0x59a98f){var _0x41b355=_0x345cf4;_0x59a98f[_0x41b355(0x86e)](),log(_0x41b355(0xa89));});}catch(_0x228ca5){}if(_0x311669[_0x345cf4(0x75b)])try{_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x49b)]&&_0x345cf4(0x90b)in _0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x49b)]&&_0x311669[_0x345cf4(0x16a)][_0xb141ee]['videoElement'][_0x345cf4(0x90b)]['stop']();}catch(_0x187611){warnlog(_0x187611);}else!_0x311669['roomid']&&(_0x311669[_0x345cf4(0x3df)]&&playtone(![],'leavetone'));try{document[_0x345cf4(0x5c8)](_0x345cf4(0x2f7)+_0xb141ee)&&(!_0x311669[_0x345cf4(0x78e)]&&(_0x311669[_0x345cf4(0x78e)]={}),_0x2d8da6&&(_0x311669[_0x345cf4(0x78e)][_0x2d8da6]=getDetailedState(_0x2d8da6)),getById(_0x345cf4(0x2f7)+_0xb141ee)[_0x345cf4(0x826)][_0x345cf4(0x9d0)](getById(_0x345cf4(0x2f7)+_0xb141ee)),updateLockedElements());}catch(_0x105a23){warnlog(_0x105a23);}try{_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x49b)]&&_0x311669['rpcs'][_0xb141ee][_0x345cf4(0x49b)][_0x345cf4(0x761)]();}catch(_0x4f3230){}try{if(_0x311669[_0x345cf4(0x254)]!==![]){if(_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x9b0)]){try{_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x9b0)]['remove']();}catch(_0x1223da){errorlog(_0x1223da);}_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x9b0)][_0x345cf4(0x761)]();}}}catch(_0x3293ae){}try{_0x311669[_0x345cf4(0x16a)][_0xb141ee]['canvas']&&_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x4e0)][_0x345cf4(0x761)]();}catch(_0x5844b9){}try{_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x678)]&&_0x311669['rpcs'][_0xb141ee][_0x345cf4(0x678)][_0x345cf4(0x761)]();}catch(_0x391b8d){}_0x345cf4(0xa4a)in _0x311669[_0x345cf4(0x16a)][_0xb141ee]&&clearInterval(_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0xa4a)]);pokeIframeAPI(_0x345cf4(0x759),![],_0xb141ee),pokeAPI(_0x345cf4(0x775),_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x9bb)]);_0x311669['rpcs'][_0xb141ee]['whip']&&(_0x2d8da6=![]);try{_0x311669['rpcs'][_0xb141ee]=null,delete _0x311669[_0x345cf4(0x16a)][_0xb141ee];}catch(_0x3ebcc8){}try{_0x311669[_0x345cf4(0x7b8)](_0xb141ee+_0x345cf4(0x33b));}catch(_0x336c15){}(!_0x311669[_0x345cf4(0x75b)]||_0x311669['switchMode'])&&setTimeout(function(){updateMixer();},0x1);if(typeof _0x2d8da6==_0x345cf4(0x31b))return;try{warnlog('Should\x20we\x20ask\x20to\x20play\x20the\x20stream\x20Again?'),_0x2d8da6&&(_0x2d8da6 in _0x311669['watchTimeoutList']&&(log('watchTimeoutList:'+_0x2d8da6),clearTimeout(_0x311669[_0x345cf4(0x241)][_0x2d8da6]),delete _0x311669['watchTimeoutList'][_0x2d8da6]),_0x311669[_0x345cf4(0x241)][_0x2d8da6]=setTimeout(function(_0x439499){var _0x50871b=_0x345cf4;try{delete _0x311669[_0x50871b(0x241)][_0x439499];}catch(_0x21b420){warnlog(_0x50871b(0x9d5));return;}log(_0x50871b(0x786)+_0x439499);try{for(var _0x5b1f1f in _0x311669[_0x50871b(0x16a)]){if(_0x311669[_0x50871b(0x16a)][_0x5b1f1f][_0x50871b(0x9bb)]===_0x439499){if(_0x311669[_0x50871b(0x16a)][_0x5b1f1f]['connectionState']===_0x50871b(0x7ea)){warnlog('\x20---\x20we\x20will\x20not\x20ask\x20again;\x20we\x27re\x20already\x20connected');return;}}}}catch(_0x4ae4ad){errorlog(_0x4ae4ad);}warnlog(_0x50871b(0xa24)),_0x311669['watchStream'](_0x439499);},_0x311669[_0x345cf4(0x667)],_0x2d8da6));}catch(_0x2160bf){errorlog(_0x2160bf);}pokeIframeAPI('new-view-connection',![],_0xb141ee),_0x2d8da6!==null?pokeIframeAPI('end-view-connection',_0x2d8da6,_0xb141ee):pokeIframeAPI(_0x345cf4(0x594),!![],_0xb141ee),updateUserList();},_0x311669[_0x134a17(0x913)]=null,_0x311669[_0x134a17(0x77f)]=function(){var _0x38ee6b=_0x134a17,_0x497be4=![];if(_0x311669[_0x38ee6b(0x311)]){_0x311669[_0x38ee6b(0x874)]&&clearTimeout(_0x311669[_0x38ee6b(0x913)]);if(_0x311669['ws']===null||(typeof _0x311669['ws']!==_0x38ee6b(0x53b)||_0x311669['ws'][_0x38ee6b(0x8ee)]!==0x1)){}else{var _0x3f22a1=_0x311669[_0x38ee6b(0x311)][_0x38ee6b(0x256)](',');for(var _0x206cac in _0x3f22a1){if(_0x3f22a1[_0x206cac]){var _0x68ae5d=![];for(var _0x4a7f7e in _0x311669[_0x38ee6b(0x16a)]){if(_0x311669[_0x38ee6b(0x16a)][_0x4a7f7e][_0x38ee6b(0x9bb)]&&_0x311669[_0x38ee6b(0x16a)][_0x4a7f7e][_0x38ee6b(0x9bb)]===_0x3f22a1[_0x206cac]){_0x68ae5d=!![];break;}}_0x3f22a1[_0x206cac]in _0x311669[_0x38ee6b(0x241)]&&(_0x68ae5d=!![]);if(_0x68ae5d)continue;_0x311669[_0x38ee6b(0x5ad)](_0x3f22a1[_0x206cac]),_0x497be4=!![];}}}_0x311669[_0x38ee6b(0x874)]&&_0x311669[_0x38ee6b(0x874)]<0xa&&(_0x311669[_0x38ee6b(0x874)]=0xa),_0x311669[_0x38ee6b(0x874)]&&(_0x311669['forceRetryTimeout']=setTimeout(function(){var _0x5e7346=_0x38ee6b;log(_0x5e7346(0x565)),_0x311669[_0x5e7346(0x77f)]();},_0x311669[_0x38ee6b(0x874)]*0x3e8));}return _0x497be4;},_0x311669['offerSDP']=async function(_0x2551e1){var _0x42f4f2=_0x134a17;if(_0x2551e1 in _0x311669[_0x42f4f2(0x859)]){if(_0x311669[_0x42f4f2(0x859)][_0x2551e1]['connectionState']==='failed'||_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x3ee)]===_0x42f4f2(0x208))log(_0x42f4f2(0x86d)),_0x311669['closePC'](_0x2551e1),warnlog(_0x42f4f2(0x405));else{if(iPad||iOS)log(_0x42f4f2(0x804)),_0x311669[_0x42f4f2(0x9d2)](_0x2551e1),warnlog('cleaning\x20up\x20lost\x20connection\x20--\x20disconnected\x20-\x20iOS\x20specific');else{if(_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x3ee)]!==_0x42f4f2(0x7ea)){await sleep(0xbb8);if(_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x3ee)]!==_0x42f4f2(0x7ea))log(_0x42f4f2(0x86d)),_0x311669[_0x42f4f2(0x9d2)](_0x2551e1),warnlog(_0x42f4f2(0x405));else{warnlog('The\x20other\x20end\x20is\x20just\x20being\x20a\x20keener.\x20Ignore\x20it:\x20'+_0x311669[_0x42f4f2(0x859)][_0x2551e1]['connectionState']);return;}}else{warnlog('The\x20other\x20end\x20is\x20just\x20being\x20a\x20keener.\x20Ignore\x20it:\x20'+_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x3ee)]);return;}}}}else log(_0x42f4f2(0x2d4));if(_0x311669[_0x42f4f2(0x209)]!==![]){if(Object[_0x42f4f2(0x19b)](_0x311669[_0x42f4f2(0x859)])['length']>_0x311669[_0x42f4f2(0x209)]){log(_0x42f4f2(0x192)),log('closing\x208'),_0x311669['closePC'](_0x2551e1);return;}}else{if(_0x311669[_0x42f4f2(0x6b1)]!==![]){if(Object[_0x42f4f2(0x19b)](_0x311669['rpcs'])[_0x42f4f2(0x8e9)]+Object['keys'](_0x311669[_0x42f4f2(0x859)])[_0x42f4f2(0x8e9)]>_0x311669['maxconnections']){log(_0x42f4f2(0xa7c)),log(_0x42f4f2(0x8dc)),_0x311669[_0x42f4f2(0x9d2)](_0x2551e1);return;}}}!_0x311669[_0x42f4f2(0x7db)]&&await chooseBestTURN();_0x311669[_0x42f4f2(0xa6e)]&&(_0x311669['configuration'][_0x42f4f2(0xa6e)]=!![]);_0x311669[_0x42f4f2(0x70d)]&&(_0x311669[_0x42f4f2(0x7db)]['BundlePolicy']=_0x311669[_0x42f4f2(0x70d)]);try{_0x311669['pcs'][_0x2551e1]=new RTCPeerConnection(_0x311669[_0x42f4f2(0x7db)]);}catch(_0x1a8f98){!_0x311669['cleanOutput']&&warnUser('An\x20RTC\x20error\x20occured');console['error'](_0x1a8f98);return;}if(_0x311669[_0x42f4f2(0x4e5)]){if(Object['keys'](_0x311669['pcs'])['length']>0x1){log('closing\x203'),log(_0x42f4f2(0x1bf)),_0x311669['closePC'](_0x2551e1);return;}}_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x436)]={},_0x311669[_0x42f4f2(0x859)][_0x2551e1]['session']=_0x311669[_0x42f4f2(0x56b)]+_0x311669[_0x42f4f2(0xa76)](0x5),_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x42f)]=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x506)]=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x34a)]={},_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x34a)][_0x42f4f2(0x4eb)]=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x34a)]['sourceActive']=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x34a)]['streaming']=null,_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x34a)][_0x42f4f2(0x2df)]=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x34a)][_0x42f4f2(0x336)]=null,_0x311669['pcs'][_0x2551e1]['optimizedBitrate']=![],_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x969)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1]['solo']=null,_0x311669['pcs'][_0x2551e1]['layout']=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x62e)]=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1]['maxBandwidth']=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1]['audioMutedOverride']=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x4d0)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x5b5)]=![],_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x9f3)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x9c8)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x798)]=![],_0x311669['pcs'][_0x2551e1]['limitAudio']=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1]['enhanceAudio']=![],_0x311669['pcs'][_0x2551e1]['degradationPreference']=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1]['encoder']=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x920)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x670)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x760)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x299)]=![],_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x368)]=![],_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x97b)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x7c3)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1]['allowDownloads']=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1]['allowMIDI']=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1]['allowBroadcast']=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1]['allowScreenVideo']=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x6b0)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x45a)]=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x54a)]=_0x2551e1,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x6b5)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x596)]=![],_0x311669['pcs'][_0x2551e1]['scaleDueToBitrate']=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x863)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x1c6)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x482)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0xa60)]=![],_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x21f)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1]['showDirector']=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x98d)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1]['keyframeRate']=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x849)]=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x89c)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1]['order']=![],_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x3aa)]=![],_0x311669['pcs'][_0x2551e1]['preferAudioCodec']=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x9a4)]=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x7af)]=_0x311669['wssid'],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x24d)]=![],_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x586)]=Date[_0x42f4f2(0x610)]();function _0x31a3e5(_0x4f819c=![]){var _0x4fb5bf=_0x42f4f2;if(_0x4f819c)return;_0x311669['pcs'][_0x2551e1][_0x4fb5bf(0x73a)]=_0x311669[_0x4fb5bf(0x859)][_0x2551e1][_0x4fb5bf(0x1e1)]('sendChannel'),_0x311669[_0x4fb5bf(0x859)][_0x2551e1][_0x4fb5bf(0x73a)][_0x4fb5bf(0x54a)]=_0x2551e1,_0x311669[_0x4fb5bf(0x859)][_0x2551e1]['sendChannel'][_0x4fb5bf(0x88c)]=_0xfadecb=>{var _0x25abc6=_0x4fb5bf;warnlog(_0xfadecb),log(_0x25abc6(0x36e)+_0x2551e1);},_0x311669[_0x4fb5bf(0x859)][_0x2551e1][_0x4fb5bf(0x73a)][_0x4fb5bf(0x777)]=()=>{var _0x258fb2=_0x4fb5bf;if(_0x4f819c)return;log('send\x20channel\x20open\x20pcs'),msg={},msg[_0x258fb2(0x6ee)]={},msg[_0x258fb2(0x6ee)][_0x258fb2(0x89c)]=_0x311669[_0x258fb2(0x89c)],msg[_0x258fb2(0x6ee)][_0x258fb2(0xa63)]=_0x311669[_0x258fb2(0xa63)],msg[_0x258fb2(0x6ee)][_0x258fb2(0x225)]=_0x311669[_0x258fb2(0x225)],msg[_0x258fb2(0x6ee)]['queued']=_0x311669[_0x258fb2(0x64f)];try{(_0x311669[_0x258fb2(0x532)]['length']||_0x311669[_0x258fb2(0xa7d)])&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0x868)]=_0x311669[_0x258fb2(0x532)][_0x258fb2(0x24c)](','));}catch(_0x55304b){}msg[_0x258fb2(0x6ee)][_0x258fb2(0x5c9)]=_0x311669[_0x258fb2(0x5c9)],msg[_0x258fb2(0x6ee)]['directorDisplayMuted']=_0x311669[_0x258fb2(0x285)],msg['info'][_0x258fb2(0x3be)]=_0x311669['directorVideoMuted'],msg[_0x258fb2(0x6ee)][_0x258fb2(0x317)]=_0x311669[_0x258fb2(0xa7f)],msg[_0x258fb2(0x6ee)][_0x258fb2(0x3ed)]=_0x311669[_0x258fb2(0x8ab)];_0x311669[_0x258fb2(0x4e1)]?msg[_0x258fb2(0x6ee)][_0x258fb2(0x864)]=!![]:msg[_0x258fb2(0x6ee)][_0x258fb2(0x864)]=![];if(_0x311669[_0x258fb2(0x75b)]){if(!_0x311669[_0x258fb2(0xa84)]&&_0x311669[_0x258fb2(0x2f3)]&&_0x311669['directorUUID']===_0x2551e1)_0x311669[_0x258fb2(0x6ab)]();else{msg[_0x258fb2(0x8ce)]={};_0x311669['mainDirectorPassword']&&(msg['directorSettings'][_0x258fb2(0xa73)]=!![]);msg['directorSettings'][_0x258fb2(0x32f)]=_0x311669[_0x258fb2(0x32f)];_0x311669[_0x258fb2(0x85f)]['length']&&!_0x311669[_0x258fb2(0x85f)][_0x258fb2(0x2c2)](_0x2551e1)&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0x225)]=!![]);var _0x4c16d7=[];for(var _0x4d46f3 in _0x311669[_0x258fb2(0x859)]){_0x311669[_0x258fb2(0x859)][_0x4d46f3][_0x258fb2(0x5b5)]===!![]&&_0x4c16d7[_0x258fb2(0x505)](_0x4d46f3);}_0x311669[_0x258fb2(0x750)]&&(msg[_0x258fb2(0x8ce)][_0x258fb2(0x9ba)]=!![]),_0x4c16d7[_0x258fb2(0x8e9)]&&(msg[_0x258fb2(0x8ce)][_0x258fb2(0x1d7)]=_0x4c16d7);}_0x311669[_0x258fb2(0x495)]&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0x16f)]=_0x311669[_0x258fb2(0x495)]);}_0x311669[_0x258fb2(0x254)]!==![]?msg[_0x258fb2(0x6ee)]['broadcast_mode']=!![]:msg[_0x258fb2(0x6ee)][_0x258fb2(0x5ba)]=![];_0x311669['remote']?msg['info'][_0x258fb2(0x24d)]=!![]:msg[_0x258fb2(0x6ee)]['remote']=![];if(_0x311669[_0x258fb2(0x820)])msg[_0x258fb2(0x6ee)][_0x258fb2(0x201)]=_0x311669[_0x258fb2(0x820)];else{if(_0x311669[_0x258fb2(0x820)]===![])msg['info'][_0x258fb2(0x201)]=![];else _0x311669[_0x258fb2(0x4e1)]&&!_0x311669['director']?msg['info'][_0x258fb2(0x201)]=![]:msg[_0x258fb2(0x6ee)]['obs_control']=null;}_0x311669[_0x258fb2(0x5b0)]&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0x5b0)]=!![]);msg[_0x258fb2(0x6ee)][_0x258fb2(0x612)]=_0x311669[_0x258fb2(0x393)];_0x311669[_0x258fb2(0x43d)]&&!_0x311669[_0x258fb2(0x7a0)]?msg[_0x258fb2(0x6ee)]['screenShareState']=_0x311669['screenShareState']:msg[_0x258fb2(0x6ee)][_0x258fb2(0x701)]=![];msg[_0x258fb2(0x6ee)][_0x258fb2(0x3b4)]=_0x311669['width'],msg['info'][_0x258fb2(0x3b9)]=_0x311669[_0x258fb2(0x5ec)];try{if(_0x311669['streamSrc']){let _0x1e829e=_0x311669[_0x258fb2(0x585)][_0x258fb2(0x87a)]();if(_0x1e829e['length']){let _0x2c574a=_0x1e829e[0x0][_0x258fb2(0x2e2)]();msg['info'][_0x258fb2(0x4ff)]=_0x2c574a[_0x258fb2(0x530)]||![],msg['info'][_0x258fb2(0x748)]=_0x2c574a[_0x258fb2(0x5ec)]||![],msg[_0x258fb2(0x6ee)][_0x258fb2(0x539)]=parseInt(_0x2c574a[_0x258fb2(0x732)])||![];}}if(_0x311669[_0x258fb2(0x7a0)]&&_0x311669[_0x258fb2(0x7a0)]['srcObject']){let _0x16d605=_0x311669[_0x258fb2(0x7a0)][_0x258fb2(0x5c4)][_0x258fb2(0x87a)]();if(_0x16d605['length']){let _0xba2e2c=_0x16d605[0x0][_0x258fb2(0x2e2)]();msg['info'][_0x258fb2(0x6e6)]=_0xba2e2c[_0x258fb2(0x530)]||![],msg[_0x258fb2(0x6ee)][_0x258fb2(0x1b7)]=_0xba2e2c['height']||![],msg[_0x258fb2(0x6ee)]['video_2_init_frameRate']=parseInt(_0xba2e2c[_0x258fb2(0x732)])||![];}}}catch(_0x4a5bac){errorlog(_0x4a5bac);}msg['info']['quality_url']=_0x311669[_0x258fb2(0x878)],msg[_0x258fb2(0x6ee)][_0x258fb2(0x7a4)]=_0x311669[_0x258fb2(0x84c)],msg[_0x258fb2(0x6ee)][_0x258fb2(0x6f7)]=_0x311669['maxviewers'],msg[_0x258fb2(0x6ee)][_0x258fb2(0x9d9)]=_0x311669[_0x258fb2(0x5a8)],msg[_0x258fb2(0x6ee)][_0x258fb2(0xa18)]=_0x311669['echoCancellation'],msg['info'][_0x258fb2(0x374)]=_0x311669['autoGainControl'],msg[_0x258fb2(0x6ee)]['denoise_url']=_0x311669[_0x258fb2(0x953)],msg[_0x258fb2(0x6ee)]['version']=_0x311669[_0x258fb2(0x3d3)],msg[_0x258fb2(0x6ee)][_0x258fb2(0x3a7)]=_0x311669[_0x258fb2(0x591)],msg[_0x258fb2(0x6ee)]['recording_audio_compressor_type']=_0x311669['compressor'],msg[_0x258fb2(0x6ee)][_0x258fb2(0x2d1)]=_0x311669[_0x258fb2(0x1d9)],msg[_0x258fb2(0x6ee)]['recording_audio_ctx_latency']=_0x311669[_0x258fb2(0x2ea)],msg[_0x258fb2(0x6ee)][_0x258fb2(0x996)]=!_0x311669['disableWebAudio'],msg[_0x258fb2(0x6ee)][_0x258fb2(0x917)]=_0x311669[_0x258fb2(0x579)],msg['info'][_0x258fb2(0x3ba)]=_0x311669[_0x258fb2(0x228)],msg[_0x258fb2(0x6ee)][_0x258fb2(0x421)]=_0x311669[_0x258fb2(0x4ac)];_0x311669[_0x258fb2(0x436)]['network_type']&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0x850)]=_0x311669[_0x258fb2(0x436)][_0x258fb2(0x1b9)]);_0x311669[_0x258fb2(0xa47)]!==![]?_0x311669[_0x258fb2(0x1c9)]?msg['info'][_0x258fb2(0x5b1)]=_0x311669[_0x258fb2(0xa47)]+parseInt(_0x311669[_0x258fb2(0x1c9)]):msg[_0x258fb2(0x6ee)]['rotate_video']=_0x311669[_0x258fb2(0xa47)]:msg[_0x258fb2(0x6ee)]['rotate_video']=_0x311669[_0x258fb2(0x1c9)];msg[_0x258fb2(0x6ee)]['rotate_video']&&msg['info']['rotate_video']>=0x168&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0x5b1)]-=0x168);try{navigator&&navigator['userAgent']&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0xa77)]=navigator[_0x258fb2(0x64e)]);navigator&&navigator[_0x258fb2(0x9f2)]&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0x9f2)]=navigator['platform']);gpgpuSupport&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0x6f5)]=gpgpuSupport);cpuSupport&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0x354)]=cpuSupport);iOS&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0x5af)]=iPhone12Up);if(SafariVersion)msg['info'][_0x258fb2(0x884)]=_0x258fb2(0x3cf)+SafariVersion;else{if(getChromiumVersion()>0x3c)msg[_0x258fb2(0x6ee)]['Browser']=_0x258fb2(0x470)+getChromiumVersion();else{if(Firefox)msg[_0x258fb2(0x6ee)][_0x258fb2(0x884)]=_0x258fb2(0x571);else navigator[_0x258fb2(0x64e)][_0x258fb2(0x49e)](_0x258fb2(0xa65))>=0x0?msg[_0x258fb2(0x6ee)][_0x258fb2(0x884)]='Chrome\x20for\x20iOS':msg[_0x258fb2(0x6ee)][_0x258fb2(0x884)]=_0x258fb2(0x4c0);}}}catch(_0xa44e4f){};_0x311669[_0x258fb2(0x401)]&&('level'in _0x311669[_0x258fb2(0x401)]&&(typeof _0x311669[_0x258fb2(0x401)][_0x258fb2(0x6b7)]==_0x258fb2(0x570)?msg[_0x258fb2(0x6ee)]['power_level']=parseInt(_0x311669[_0x258fb2(0x401)]['level']*0x64):msg[_0x258fb2(0x6ee)][_0x258fb2(0xa3d)]=_0x311669[_0x258fb2(0x401)][_0x258fb2(0x6b7)]),_0x258fb2(0x269)in _0x311669[_0x258fb2(0x401)]&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0x2eb)]=_0x311669['batteryState'][_0x258fb2(0x269)]));_0x311669[_0x258fb2(0x632)]&&(msg[_0x258fb2(0x6ee)]['cpuLimited']=_0x311669[_0x258fb2(0x632)]);try{_0x311669[_0x258fb2(0x6ee)][_0x258fb2(0x26c)]&&(msg[_0x258fb2(0x48f)]={},msg[_0x258fb2(0x48f)]['out']={},msg[_0x258fb2(0x48f)][_0x258fb2(0x26c)]['c']=_0x311669['info']['out']['c']);}catch(_0x58f4c5){}_0x311669[_0x258fb2(0x8a1)](msg,_0x2551e1),pokeIframeAPI('new-push-connection',!![],_0x2551e1),pokeIframeAPI(_0x258fb2(0x397),!![],_0x2551e1),updateUserList();},_0x311669[_0x4fb5bf(0x859)][_0x2551e1]['sendChannel'][_0x4fb5bf(0x661)]=()=>{var _0x2cd010=_0x4fb5bf;pokeIframeAPI(_0x2cd010(0x960),![],_0x2551e1),_0x311669[_0x2cd010(0x66a)](),warnlog(_0x2cd010(0x9c3));return;},_0x311669[_0x4fb5bf(0x859)][_0x2551e1][_0x4fb5bf(0x73a)][_0x4fb5bf(0x17a)]=async function(_0x5e7858){var _0x1fd7ed=_0x4fb5bf;log(_0x1fd7ed(0x9c7));try{var _0x19a68e=JSON['parse'](_0x5e7858[_0x1fd7ed(0x3bc)]);}catch(_0x5d972d){warnlog('Couldn\x27t\x20parse\x20JSON;\x20will\x20attempt\x20as\x20ArrayBuffer\x20UINT8ARRAY'),log(_0x5e7858['data']);try{var _0x3c3642=new TextDecoder()[_0x1fd7ed(0x5f5)](_0x5e7858[_0x1fd7ed(0x3bc)]),_0x19a68e=JSON[_0x1fd7ed(0x52d)](_0x3c3642);}catch(_0x4d84bd){try{var _0x19a68e=await new Response(_0x5e7858[_0x1fd7ed(0x3bc)])['text']();_0x19a68e=JSON[_0x1fd7ed(0x52d)](_0x19a68e);}catch(_0x2564a2){return;}}}warnlog(_0x19a68e);if('remote'in _0x19a68e)try{_0x19a68e=await _0x311669[_0x1fd7ed(0x643)](_0x19a68e);if(!_0x19a68e)return;}catch(_0x460498){errorlor(_0x460498);}_0x1fd7ed(0x67b)in _0x19a68e?await _0x311669[_0x1fd7ed(0x840)](_0x19a68e,_0x2551e1+'_screen',_0x2551e1):await _0x311669['processPCSOnMessage'](_0x19a68e,_0x2551e1);};}!_0x311669['legacywebrtc']&&_0x31a3e5(![]);_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x2ca)]=function(_0x2b1fb5){var _0x695044=_0x42f4f2;warnlog(_0x695044(0xa30)),warnlog(_0x2b1fb5);if(_0x2b1fb5[_0x695044(0x27a)]['label']&&_0x2b1fb5[_0x695044(0x27a)][_0x695044(0x89c)]!==_0x695044(0x73a)){_0x311669['recieveFile'](_0x311669[_0x695044(0x16a)],_0x2551e1,_0x2b1fb5[_0x695044(0x27a)]);return;}},_0x311669[_0x42f4f2(0x859)][_0x2551e1]['onnegotiationneeded']=function(_0xdd2cd3){var _0x956c96=_0x42f4f2;log(_0x956c96(0x59c)),_0x311669[_0x956c96(0x903)](_0x2551e1);},_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x196)]=_0x3ea9fc=>{var _0x12c348=_0x42f4f2;errorlog(_0x12c348(0x693));},_0x311669[_0x42f4f2(0x859)][_0x2551e1]['iceTimer']=null,_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x2f5)]=[],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x2a3)]=function(_0x5109f3){var _0x23068a=_0x42f4f2;if(_0x5109f3[_0x23068a(0x905)]==null){log(_0x23068a(0x9d6));return;}log(_0x5109f3);try{if(_0x311669[_0x23068a(0x242)]){if(_0x5109f3['candidate'][_0x23068a(0x905)][_0x23068a(0x49e)](_0x311669[_0x23068a(0x242)])===-0x1){log(_0x23068a(0x6f4));return;}else log(_0x5109f3[_0x23068a(0x905)]);}}catch(_0x337581){errorlog(_0x337581);}if(_0x311669[_0x23068a(0x859)][_0x2551e1]['iceTimer']!==null){_0x311669['pcs'][_0x2551e1][_0x23068a(0x2f5)][_0x23068a(0x505)](_0x5109f3['candidate']);return;}_0x311669[_0x23068a(0x859)][_0x2551e1][_0x23068a(0x2f5)][_0x23068a(0x505)](_0x5109f3[_0x23068a(0x905)]),_0x311669['pcs'][_0x2551e1][_0x23068a(0x44b)]=setTimeout(function(_0x2e6ce4){var _0x3a954e=_0x23068a;try{_0x311669[_0x3a954e(0x859)][_0x2e6ce4]['iceTimer']=null;}catch(_0x1469dd){warnlog(_0x3a954e(0x795));return;}var _0x2e0350={};_0x2e0350['UUID']=_0x2e6ce4,_0x2e0350[_0x3a954e(0x78a)]=_0x3a954e(0x628),_0x2e0350[_0x3a954e(0x2a8)]=_0x311669[_0x3a954e(0x859)][_0x2e6ce4][_0x3a954e(0x2f5)],_0x2e0350['session']=_0x311669[_0x3a954e(0x859)][_0x2e6ce4][_0x3a954e(0x1fd)],_0x311669['pcs'][_0x2e6ce4][_0x3a954e(0x2f5)]=[],_0x311669[_0x3a954e(0x8a3)]?_0x311669[_0x3a954e(0x331)](JSON['stringify'](_0x2e0350[_0x3a954e(0x2a8)]))[_0x3a954e(0x619)](function(_0x12c942){var _0x1150f0=_0x3a954e;_0x2e0350[_0x1150f0(0x2a8)]=_0x12c942[0x0],_0x2e0350['vector']=_0x12c942[0x1],_0x311669[_0x1150f0(0x966)](_0x2e0350);})[_0x3a954e(0x3b6)](errorlog):_0x311669[_0x3a954e(0x966)](_0x2e0350);},0xc8,_0x2551e1);},_0x311669['processPCSOnMessage']=async function(_0x27465c,_0x1f2fd5,_0x1dae43=![]){var _0x71e5c3=_0x42f4f2;_0x27465c[_0x71e5c3(0x54a)]=_0x1f2fd5;if(_0x27465c['description']){_0x311669[_0x71e5c3(0x6d1)](_0x27465c);return;}else{if(_0x27465c['candidate']){log(_0x71e5c3(0x8e2)),_0x311669[_0x71e5c3(0x1d3)](_0x27465c);return;}else{if(_0x27465c[_0x71e5c3(0x2a8)]){log(_0x71e5c3(0x2fd)),_0x311669[_0x71e5c3(0x411)](_0x27465c);return;}else{if(_0x71e5c3(0x66a)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x92d)]=_0x27465c[_0x71e5c3(0x66a)],_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5),warnlog('PINGED');return;}else{if(_0x71e5c3(0x92d)in _0x27465c){warnlog(_0x71e5c3(0x333));return;}else{if(_0x71e5c3(0xa83)in _0x27465c){warnlog(_0x71e5c3(0x6ea)),log(_0x71e5c3(0x313)),_0x311669[_0x71e5c3(0x9d2)](_0x1f2fd5);return;}}}}}}if(_0x311669[_0x71e5c3(0x75b)]){if(_0x71e5c3(0x873)in _0x27465c&&_0x71e5c3(0x9ed)in _0x27465c){if(_0x311669[_0x71e5c3(0x76b)])_0x311669['directorHash']?_0x311669['decryptMessage'](_0x27465c[_0x71e5c3(0x873)],_0x27465c['vector'],_0x311669[_0x71e5c3(0x799)])[_0x71e5c3(0x619)](function(_0x108d47){var _0x1ac7c4=_0x71e5c3;if(_0x108d47===_0x311669['directorHash']){_0x311669[_0x1ac7c4(0x859)][_0x1f2fd5][_0x1ac7c4(0x5b5)]=!![],_0x311669['directorList'][_0x1ac7c4(0x505)](_0x1f2fd5),getById('container_'+_0x1f2fd5)[_0x1ac7c4(0x424)]['add'](_0x1ac7c4(0x278)),_0x311669[_0x1ac7c4(0x60e)](_0x1f2fd5);var _0x3bcacb={};_0x3bcacb[_0x1ac7c4(0x634)]=_0x1ac7c4(0x873),_0x311669[_0x1ac7c4(0x8a1)](_0x3bcacb,_0x1f2fd5);}else{warnlog(_0x1ac7c4(0x322));var _0x3bcacb={};_0x3bcacb[_0x1ac7c4(0x485)]='requestCoDirector',_0x311669[_0x1ac7c4(0x8a1)](_0x3bcacb,_0x1f2fd5);}})['catch'](function(){var _0xf439f1=_0x71e5c3;warnlog('Failed\x20attempt\x20to\x20connect\x20as\x20co-director');var _0x25a34b={};_0x25a34b['rejected']='requestCoDirector',_0x311669[_0xf439f1(0x8a1)](_0x25a34b,_0x1f2fd5);}):generateHash(_0x311669[_0x71e5c3(0x76b)]+_0x311669[_0x71e5c3(0x9c5)]+_0x71e5c3(0x47f),0xc)['then'](function(_0x3e8d49){var _0x406fe9=_0x71e5c3;_0x311669[_0x406fe9(0x799)]=_0x3e8d49,_0x311669[_0x406fe9(0x4da)](_0x27465c[_0x406fe9(0x873)],_0x27465c[_0x406fe9(0x9ed)],_0x311669[_0x406fe9(0x799)])['then'](function(_0x572e12){var _0x41cbf1=_0x406fe9;if(_0x572e12===_0x311669[_0x41cbf1(0x799)]){_0x311669[_0x41cbf1(0x859)][_0x1f2fd5]['coDirector']=!![],_0x311669[_0x41cbf1(0x95b)][_0x41cbf1(0x505)](_0x1f2fd5),getById('container_'+_0x1f2fd5)[_0x41cbf1(0x424)][_0x41cbf1(0x517)](_0x41cbf1(0x278)),_0x311669[_0x41cbf1(0x60e)](_0x1f2fd5);var _0x4a71d0={};_0x4a71d0[_0x41cbf1(0x634)]=_0x41cbf1(0x873),_0x311669[_0x41cbf1(0xa14)](_0x4a71d0,_0x1f2fd5);}else{warnlog(_0x41cbf1(0x322));var _0x4a71d0={};_0x4a71d0[_0x41cbf1(0x485)]=_0x41cbf1(0x873),_0x311669[_0x41cbf1(0xa14)](_0x4a71d0,_0x1f2fd5);}})[_0x406fe9(0x3b6)](function(){var _0x51f4e=_0x406fe9;warnlog('Failed\x20attempt\x20to\x20connect\x20as\x20co-director');var _0x433c75={};_0x433c75['rejected']=_0x51f4e(0x873),_0x311669[_0x51f4e(0xa14)](_0x433c75,_0x1f2fd5);});return;})[_0x71e5c3(0x3b6)](errorlog);else{warnlog('reject\x20co');var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]='requestCoDirector',_0x311669[_0x71e5c3(0xa14)](_0x3d5b95,_0x1f2fd5);}}if(_0x71e5c3(0x3fe)in _0x27465c&&'roomid'in _0x27465c){log('Someone\x20is\x20trying\x20to\x20transfer\x20a\x20guest');if(_0x311669[_0x71e5c3(0x8fe)]){if(_0x1f2fd5 in _0x311669['pcs']&&_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x5b5)]===!![]){log(_0x71e5c3(0x566));var _0x3d5b95={};if(_0x27465c[_0x71e5c3(0x321)]&&_0x27465c[_0x71e5c3(0x321)][_0x71e5c3(0x339)])_0x3d5b95['request']=_0x71e5c3(0x3fe),_0x3d5b95[_0x71e5c3(0x321)]=_0x27465c['transferSettings'],log(_0x3d5b95),_0x311669[_0x71e5c3(0xa14)](_0x3d5b95,_0x27465c['migrate'][_0x71e5c3(0x721)](),function(){var _0x8fa27f=_0x71e5c3,_0x18ab3e={};_0x18ab3e[_0x8fa27f(0x727)]=_0x8fa27f(0x3fe),_0x18ab3e[_0x8fa27f(0x4e1)]=_0x27465c[_0x8fa27f(0x4e1)],_0x18ab3e[_0x8fa27f(0x81f)]=_0x27465c[_0x8fa27f(0x3fe)]['toString'](),_0x311669['sendMsg'](_0x18ab3e);}),log(_0x3d5b95);else{if(_0x27465c['transferSettings']&&_0x71e5c3(0x254)in _0x27465c[_0x71e5c3(0x321)])_0x3d5b95['request']=_0x71e5c3(0x3fe),_0x3d5b95[_0x71e5c3(0x321)]=_0x27465c[_0x71e5c3(0x321)],delete _0x3d5b95[_0x71e5c3(0x321)][_0x71e5c3(0x4e1)],delete _0x3d5b95[_0x71e5c3(0x321)][_0x71e5c3(0x5cf)],log(_0x3d5b95),_0x311669[_0x71e5c3(0xa14)](_0x3d5b95,_0x27465c[_0x71e5c3(0x3fe)]['toString'](),function(){var _0x52735e=_0x71e5c3,_0x2b649b={};_0x2b649b[_0x52735e(0x727)]=_0x52735e(0x3fe),_0x2b649b[_0x52735e(0x4e1)]=_0x27465c['roomid'],_0x2b649b[_0x52735e(0x81f)]=_0x27465c[_0x52735e(0x3fe)]['toString'](),_0x311669['sendMsg'](_0x2b649b);}),log(_0x3d5b95);else Object[_0x71e5c3(0x19b)](_0x27465c[_0x71e5c3(0x321)])[_0x71e5c3(0x8e9)]?(_0x3d5b95['request']=_0x71e5c3(0x3fe),_0x3d5b95['transferSettings']=_0x27465c[_0x71e5c3(0x321)],delete _0x3d5b95['transferSettings'][_0x71e5c3(0x4e1)],delete _0x3d5b95['transferSettings'][_0x71e5c3(0x5cf)],log(_0x3d5b95),_0x311669['sendRequest'](_0x3d5b95,_0x27465c[_0x71e5c3(0x3fe)][_0x71e5c3(0x721)](),function(){var _0x4af745=_0x71e5c3,_0x15bb5b={};_0x15bb5b[_0x4af745(0x727)]='migrate',_0x15bb5b[_0x4af745(0x4e1)]=_0x27465c[_0x4af745(0x4e1)],_0x15bb5b[_0x4af745(0x81f)]=_0x27465c[_0x4af745(0x3fe)]['toString'](),_0x311669[_0x4af745(0x663)](_0x15bb5b);}),log(_0x3d5b95)):(_0x3d5b95[_0x71e5c3(0x727)]=_0x71e5c3(0x3fe),_0x3d5b95[_0x71e5c3(0x4e1)]=_0x27465c[_0x71e5c3(0x4e1)],_0x3d5b95[_0x71e5c3(0x81f)]=_0x27465c[_0x71e5c3(0x3fe)][_0x71e5c3(0x721)](),_0x311669[_0x71e5c3(0x663)](_0x3d5b95));}pokeIframeAPI(_0x71e5c3(0x8dd),_0x27465c[_0x71e5c3(0x4e1)],_0x27465c[_0x71e5c3(0x3fe)][_0x71e5c3(0x721)]());}}else{var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x1a6),_0x311669[_0x71e5c3(0xa14)](_0x3d5b95,_0x1f2fd5);}}}if('requestAs'in _0x27465c){if(!_0x27465c[_0x71e5c3(0x54a)]){log(_0x71e5c3(0x8db));return;}var _0x2198ed=_0x27465c[_0x71e5c3(0x5c3)];if(!_0x311669[_0x71e5c3(0x859)][_0x2198ed]){log(_0x71e5c3(0x275));return;}if(_0x311669[_0x71e5c3(0x95b)][_0x71e5c3(0x49e)](_0x2198ed)>=0x0){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]='requestAs',_0x311669['sendMessage'](_0x3d5b95,_0x27465c['UUID']),warnlog('Remote\x20user\x20is\x20a\x20director');return;}if(_0x311669['remote']){if(_0x71e5c3(0x24d)in _0x27465c&&_0x27465c[_0x71e5c3(0x24d)]===_0x311669['remote']&&_0x311669['remote']){}else{if(_0x311669['remote']===!![]){}}}else{if(_0x311669[_0x71e5c3(0x95b)][_0x71e5c3(0x49e)](_0x27465c[_0x71e5c3(0x54a)])>=0x0){}else return;}_0x71e5c3(0x202)in _0x27465c&&_0x311669[_0x71e5c3(0x202)](_0x2198ed,_0x27465c[_0x71e5c3(0x202)]);'targetAudioBitrate'in _0x27465c&&_0x311669[_0x71e5c3(0x347)](_0x2198ed,_0x27465c[_0x71e5c3(0x347)]);if('requestResolution'in _0x27465c)try{_0x311669['setResolution'](_0x2198ed,_0x27465c[_0x71e5c3(0x37b)]['w'],_0x27465c[_0x71e5c3(0x37b)]['h'],_0x27465c[_0x71e5c3(0x37b)]['s'],_0x27465c[_0x71e5c3(0x37b)]['c']);}catch(_0x45a743){errorlog(_0x45a743);}return;}manageSceneState(_0x27465c,_0x1f2fd5);try{if(_0x71e5c3(0x6ee)in _0x27465c){_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x436)]['info']=_0x27465c[_0x71e5c3(0x6ee)];_0x71e5c3(0x89c)in _0x27465c[_0x71e5c3(0x6ee)]&&(typeof _0x27465c[_0x71e5c3(0x6ee)][_0x71e5c3(0x89c)]=='string'?_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x89c)]=sanitizeLabel(_0x27465c[_0x71e5c3(0x6ee)][_0x71e5c3(0x89c)]):_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['label']=![]);if(_0x1dae43){if(_0x1dae43===_0x311669[_0x71e5c3(0x2f3)])try{_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x436)][_0x71e5c3(0x6ee)][_0x71e5c3(0x75b)]=!![];}catch(_0x165e74){}else{if(_0x311669[_0x71e5c3(0x95b)][_0x71e5c3(0x49e)](_0x1dae43)>=0x0)try{_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['stats']['info'][_0x71e5c3(0x5b5)]=!![];}catch(_0x3d99b6){}}}else{if(_0x1f2fd5===_0x311669[_0x71e5c3(0x2f3)])try{_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x436)][_0x71e5c3(0x6ee)][_0x71e5c3(0x75b)]=!![];}catch(_0x1ad745){}else{if(_0x311669[_0x71e5c3(0x95b)]['indexOf'](_0x1f2fd5)>=0x0)try{_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x436)][_0x71e5c3(0x6ee)][_0x71e5c3(0x5b5)]=!![];}catch(_0x456f67){}}}if(_0x311669[_0x71e5c3(0x993)]&&_0x311669[_0x71e5c3(0x75b)]&&_0x71e5c3(0x34c)in _0x27465c['info']&&_0x27465c[_0x71e5c3(0x6ee)]['obs']){var _0xf16ed3=createSlotUpdate(_0x1f2fd5);_0x311669[_0x71e5c3(0x692)]?_0x311669[_0x71e5c3(0x8a1)]({'slotsUpdate':_0xf16ed3,'obsSceneTriggers':_0x311669[_0x71e5c3(0x692)],'layouts':_0x311669['layouts']},_0x1f2fd5):_0x311669['sendMessage']({'slotsUpdate':_0xf16ed3,'layouts':_0x311669[_0x71e5c3(0x993)]},_0x1f2fd5);}if(Firefox)try{_0x71e5c3(0x2c0)in _0x27465c[_0x71e5c3(0x6ee)]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x969)]===![]&&(_0x27465c['info'][_0x71e5c3(0x2c0)]&&parseInt(_0x27465c['info'][_0x71e5c3(0x2c0)])>0x0&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['savedBitrate']=parseInt(_0x27465c[_0x71e5c3(0x6ee)][_0x71e5c3(0x2c0)]),_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['bitrateTimeout']&&clearTimeout(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x62e)]),_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x62e)]=setTimeout(function(_0x5983a3){var _0x174349=_0x71e5c3;_0x311669[_0x174349(0x3da)](_0x5983a3,null);},0x3e8,_0x1f2fd5))));}catch(_0x222d35){errorlog(_0x222d35);}pokeIframeAPI(_0x71e5c3(0xa28),_0x27465c[_0x71e5c3(0x6ee)],_0x1f2fd5);}if('ifs'in _0x27465c){if(_0x311669['iframeSrc'])try{_0x311669['iframeSrc'][_0x71e5c3(0x62b)](_0x71e5c3(0x26a))&&processIframeSyncFeedback(_0x27465c[_0x71e5c3(0x90a)],_0x1f2fd5);}catch(_0x4ebb84){errorlog(_0x4ebb84);}}_0x71e5c3(0x7e0)in _0x27465c&&_0x311669[_0x71e5c3(0x640)](_0x27465c[_0x71e5c3(0x7e0)],_0x1f2fd5);_0x71e5c3(0x16f)in _0x27465c&&(_0x311669[_0x71e5c3(0x495)]=_0x27465c[_0x71e5c3(0x16f)],_0x311669['autoSyncCallback'](_0x1f2fd5));_0x71e5c3(0x273)in _0x27465c&&(_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x273)]=parseInt(_0x27465c[_0x71e5c3(0x273)]));'audioBitrate'in _0x27465c&&_0x311669[_0x71e5c3(0x184)](_0x1f2fd5,_0x27465c[_0x71e5c3(0x222)]);_0x71e5c3(0x739)in _0x27465c&&_0x311669[_0x71e5c3(0x3da)](_0x1f2fd5,_0x27465c[_0x71e5c3(0x739)]);_0x71e5c3(0x202)in _0x27465c&&_0x311669[_0x71e5c3(0x202)](_0x1f2fd5,_0x27465c[_0x71e5c3(0x202)]);_0x71e5c3(0x347)in _0x27465c&&_0x311669[_0x71e5c3(0x347)](_0x1f2fd5,_0x27465c['targetAudioBitrate']);if(_0x71e5c3(0x813)in _0x27465c){if(_0x71e5c3(0x24d)in _0x27465c){if(_0x27465c['remote']===_0x311669[_0x71e5c3(0x24d)]&&_0x311669['remote']||_0x311669[_0x71e5c3(0x24d)]===!![]){_0x311669['hangup']();return;}}}if(_0x71e5c3(0x94e)in _0x27465c){if(_0x71e5c3(0x24d)in _0x27465c){if(_0x27465c['remote']===_0x311669['remote']&&_0x311669['remote']||_0x311669[_0x71e5c3(0x24d)]===!![]){_0x311669['hangup'](!![]);return;}}}if(_0x71e5c3(0x50d)in _0x27465c){if(_0x311669[_0x71e5c3(0x95b)]['indexOf'](_0x1dae43||_0x1f2fd5)>=0x0){var _0x50c150={};if(_0x311669['whipOut'][_0x71e5c3(0x436)])_0x50c150[_0x71e5c3(0x638)]=_0x311669[_0x71e5c3(0x638)][_0x71e5c3(0x436)];else for(var _0x5f1d22 in _0x311669[_0x71e5c3(0x859)]){if(_0x5f1d22===_0x1f2fd5)continue;_0x50c150[_0x5f1d22]=_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)];}var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x6db)]=_0x50c150,_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if('remote'in _0x27465c){if(_0x27465c[_0x71e5c3(0x24d)]===_0x311669[_0x71e5c3(0x24d)]&&_0x311669[_0x71e5c3(0x24d)]||_0x311669[_0x71e5c3(0x24d)]===!![]){var _0x50c150={};if(_0x311669[_0x71e5c3(0x638)][_0x71e5c3(0x436)])_0x50c150[_0x71e5c3(0x638)]=_0x311669[_0x71e5c3(0x638)][_0x71e5c3(0x436)];else for(var _0x5f1d22 in _0x311669['pcs']){if(_0x5f1d22===_0x1f2fd5)continue;_0x50c150[_0x5f1d22]=_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x436)];}var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x6db)]=_0x50c150,_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}}else{var _0x50c150={};if(_0x311669['whipOut']['stats'])_0x50c150[_0x71e5c3(0x638)]=_0x311669[_0x71e5c3(0x638)]['stats'];else for(var _0x5f1d22 in _0x311669[_0x71e5c3(0x859)]){if(_0x5f1d22===_0x1f2fd5)continue;if(!_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)])continue;if(_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x798)])continue;if(_0x311669[_0x71e5c3(0x4e1)]){if(_0x71e5c3(0x98d)in _0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)]){if(_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x98d)]===![])continue;}else continue;}_0x50c150[_0x5f1d22]={},_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)]['video_bitrate_kbps']&&(_0x50c150[_0x5f1d22][_0x71e5c3(0x7fc)]=_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x7fc)]),_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x5b6)]&&(_0x50c150[_0x5f1d22]['nacks_per_second']=_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x5b6)]),_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)]['available_outgoing_bitrate_kbps']&&(_0x50c150[_0x5f1d22][_0x71e5c3(0x978)]=_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x978)]),_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x436)]['scene']&&(_0x50c150[_0x5f1d22]['scene']=_0x311669[_0x71e5c3(0x859)][_0x5f1d22]['stats'][_0x71e5c3(0x98d)]),_0x311669[_0x71e5c3(0x859)][_0x5f1d22]['label']&&(_0x50c150[_0x5f1d22][_0x71e5c3(0x89c)]=_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x89c)]),_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x990)]&&(_0x50c150[_0x5f1d22]['resolution']=_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x990)]),_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x436)]['video_encoder']&&(_0x50c150[_0x5f1d22][_0x71e5c3(0x68e)]=_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x68e)]);}var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x6db)]=_0x50c150,_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}}}if(_0x71e5c3(0x160)in _0x27465c){clearInterval(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x927)]);if(_0x311669[_0x71e5c3(0x95b)][_0x71e5c3(0x49e)](_0x1dae43||_0x1f2fd5)>=0x0){if(_0x27465c['requestStatsContinuous']){_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x927)]=setInterval(function(_0x4eb0fa){var _0x145bf7=_0x71e5c3,_0x116a38={};if(_0x311669[_0x145bf7(0x638)][_0x145bf7(0x436)])_0x116a38['whipOut']=_0x311669['whipOut'][_0x145bf7(0x436)];else for(var _0xb0620 in _0x311669[_0x145bf7(0x859)]){if(_0xb0620===_0x4eb0fa)continue;if(!_0x311669[_0x145bf7(0x859)][_0xb0620][_0x145bf7(0x436)])continue;if(_0x311669['pcs'][_0xb0620][_0x145bf7(0x798)])continue;_0x116a38[_0xb0620]=_0x311669[_0x145bf7(0x859)][_0xb0620][_0x145bf7(0x436)];}var _0x35d7f6={};_0x35d7f6[_0x145bf7(0x6db)]=_0x116a38,_0x311669[_0x145bf7(0x8a1)](_0x35d7f6,_0x4eb0fa);},0xbb8,_0x1f2fd5);var _0x50c150={};if(_0x311669[_0x71e5c3(0x638)]['stats'])_0x50c150[_0x71e5c3(0x638)]=_0x311669['whipOut'][_0x71e5c3(0x436)];else for(var _0x5f1d22 in _0x311669['pcs']){if(_0x5f1d22===_0x1f2fd5)continue;if(!_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)])continue;if(_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x798)])continue;_0x50c150[_0x5f1d22]=_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x436)];}var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x6db)]=_0x50c150,_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}}else{if('remote'in _0x27465c){if(_0x27465c['remote']===_0x311669[_0x71e5c3(0x24d)]&&_0x311669['remote']||_0x311669['remote']===!![]){if(_0x27465c[_0x71e5c3(0x160)]){_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x927)]=setInterval(function(_0x2eca71){var _0x5b7be9=_0x71e5c3,_0x1297db={};if(_0x311669[_0x5b7be9(0x638)][_0x5b7be9(0x436)])_0x1297db[_0x5b7be9(0x638)]=_0x311669[_0x5b7be9(0x638)][_0x5b7be9(0x436)];else for(var _0xe914f in _0x311669[_0x5b7be9(0x859)]){if(_0xe914f===_0x2eca71)continue;if(!_0x311669[_0x5b7be9(0x859)][_0xe914f][_0x5b7be9(0x436)])continue;if(_0x311669[_0x5b7be9(0x859)][_0xe914f][_0x5b7be9(0x798)])continue;_0x1297db[_0xe914f]=_0x311669[_0x5b7be9(0x859)][_0xe914f][_0x5b7be9(0x436)];}var _0x552562={};_0x552562['remoteStats']=_0x1297db,_0x311669[_0x5b7be9(0x8a1)](_0x552562,_0x2eca71);},0xbb8,_0x1f2fd5);var _0x50c150={};if(_0x311669[_0x71e5c3(0x638)][_0x71e5c3(0x436)])_0x50c150[_0x71e5c3(0x638)]=_0x311669[_0x71e5c3(0x638)][_0x71e5c3(0x436)];else for(var _0x5f1d22 in _0x311669[_0x71e5c3(0x859)]){if(_0x5f1d22===_0x1f2fd5)continue;if(!_0x311669[_0x71e5c3(0x859)][_0x5f1d22]['stats'])continue;if(_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x798)])continue;_0x50c150[_0x5f1d22]=_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)];}var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x6db)]=_0x50c150,_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}}}else{if(_0x27465c[_0x71e5c3(0x160)]){_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x927)]=setInterval(function(_0x3ed1e3){var _0x21578e=_0x71e5c3,_0x3b0a40={};if(_0x311669[_0x21578e(0x638)]['stats'])_0x3b0a40[_0x21578e(0x638)]=_0x311669['whipOut'][_0x21578e(0x436)];else for(var _0xc3d97f in _0x311669[_0x21578e(0x859)]){if(_0xc3d97f===_0x3ed1e3)continue;if(!_0x311669[_0x21578e(0x859)][_0xc3d97f][_0x21578e(0x436)])continue;if(_0x311669['pcs'][_0xc3d97f][_0x21578e(0x798)])continue;if(_0x311669[_0x21578e(0x4e1)]){if('scene'in _0x311669[_0x21578e(0x859)][_0xc3d97f][_0x21578e(0x436)]){if(_0x311669[_0x21578e(0x859)][_0xc3d97f][_0x21578e(0x436)]['scene']===![])continue;}else continue;}_0x3b0a40[_0xc3d97f]={},_0x311669[_0x21578e(0x859)][_0xc3d97f]['stats'][_0x21578e(0x7fc)]&&(_0x3b0a40[_0xc3d97f][_0x21578e(0x7fc)]=_0x311669[_0x21578e(0x859)][_0xc3d97f]['stats'][_0x21578e(0x7fc)]),_0x311669[_0x21578e(0x859)][_0xc3d97f]['stats'][_0x21578e(0x5b6)]&&(_0x3b0a40[_0xc3d97f][_0x21578e(0x5b6)]=_0x311669[_0x21578e(0x859)][_0xc3d97f][_0x21578e(0x436)][_0x21578e(0x5b6)]),_0x311669[_0x21578e(0x859)][_0xc3d97f][_0x21578e(0x436)][_0x21578e(0x978)]&&(_0x3b0a40[_0xc3d97f]['available_outgoing_bitrate_kbps']=_0x311669[_0x21578e(0x859)][_0xc3d97f][_0x21578e(0x436)][_0x21578e(0x978)]),_0x311669[_0x21578e(0x859)][_0xc3d97f][_0x21578e(0x436)][_0x21578e(0x98d)]&&(_0x3b0a40[_0xc3d97f][_0x21578e(0x98d)]=_0x311669[_0x21578e(0x859)][_0xc3d97f][_0x21578e(0x436)][_0x21578e(0x98d)]),_0x311669[_0x21578e(0x859)][_0xc3d97f]['label']&&(_0x3b0a40[_0xc3d97f]['label']=_0x311669['pcs'][_0xc3d97f][_0x21578e(0x89c)]),_0x311669[_0x21578e(0x859)][_0xc3d97f][_0x21578e(0x436)][_0x21578e(0x990)]&&(_0x3b0a40[_0xc3d97f][_0x21578e(0x990)]=_0x311669[_0x21578e(0x859)][_0xc3d97f][_0x21578e(0x436)][_0x21578e(0x990)]),_0x311669[_0x21578e(0x859)][_0xc3d97f][_0x21578e(0x436)][_0x21578e(0x68e)]&&(_0x3b0a40[_0xc3d97f]['video_encoder']=_0x311669['pcs'][_0xc3d97f][_0x21578e(0x436)][_0x21578e(0x68e)]);}var _0x1a3a81={};_0x1a3a81['remoteStats']=_0x3b0a40,_0x311669[_0x21578e(0x8a1)](_0x1a3a81,_0x3ed1e3);},0xbb8,_0x1f2fd5);var _0x50c150={};if(_0x311669[_0x71e5c3(0x638)][_0x71e5c3(0x436)])_0x50c150[_0x71e5c3(0x638)]=_0x311669[_0x71e5c3(0x638)][_0x71e5c3(0x436)];else for(var _0x5f1d22 in _0x311669[_0x71e5c3(0x859)]){if(_0x5f1d22===_0x1f2fd5)continue;if(!_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)])continue;if(_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x798)])continue;if(_0x311669[_0x71e5c3(0x4e1)]){if(_0x71e5c3(0x98d)in _0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)]){if(_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x98d)]===![])continue;}else continue;}_0x50c150[_0x5f1d22]={},_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)]['video_bitrate_kbps']&&(_0x50c150[_0x5f1d22][_0x71e5c3(0x7fc)]=_0x311669[_0x71e5c3(0x859)][_0x5f1d22]['stats'][_0x71e5c3(0x7fc)]),_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x5b6)]&&(_0x50c150[_0x5f1d22]['nacks_per_second']=_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x5b6)]),_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x978)]&&(_0x50c150[_0x5f1d22]['available_outgoing_bitrate_kbps']=_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x978)]),_0x311669[_0x71e5c3(0x859)][_0x5f1d22]['stats'][_0x71e5c3(0x98d)]&&(_0x50c150[_0x5f1d22][_0x71e5c3(0x98d)]=_0x311669[_0x71e5c3(0x859)][_0x5f1d22]['stats'][_0x71e5c3(0x98d)]),_0x311669[_0x71e5c3(0x859)][_0x5f1d22]['label']&&(_0x50c150[_0x5f1d22]['label']=_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x89c)]),_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x990)]&&(_0x50c150[_0x5f1d22][_0x71e5c3(0x990)]=_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x436)]['resolution']),_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)]['video_encoder']&&(_0x50c150[_0x5f1d22][_0x71e5c3(0x68e)]=_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x68e)]);}var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x6db)]=_0x50c150,_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}}}}if(_0x71e5c3(0x37b)in _0x27465c)try{_0x311669[_0x71e5c3(0x83a)](_0x1f2fd5,_0x27465c[_0x71e5c3(0x37b)]['w'],_0x27465c['requestResolution']['h'],_0x27465c['requestResolution']['s'],_0x27465c[_0x71e5c3(0x37b)]['c']);}catch(_0x1828fc){errorlog(_0x1828fc);}_0x71e5c3(0x236)in _0x27465c&&(_0x27465c['scene']?_0x311669[_0x71e5c3(0x95b)][_0x71e5c3(0x49e)](_0x1dae43||_0x1f2fd5)>=0x0?_0x311669['sendKeyFrameScenes']():errorlog(_0x71e5c3(0x979)):_0x311669[_0x71e5c3(0x2e3)](_0x1f2fd5));if('chat'in _0x27465c){var _0x4eb553=![],_0x5eef=![];_0x311669[_0x71e5c3(0x95b)][_0x71e5c3(0x49e)](_0x1dae43||_0x1f2fd5)>=0x0&&(_0x4eb553=!![],'overlay'in _0x27465c&&(_0x27465c[_0x71e5c3(0x563)]==!![]&&(_0x5eef=!![]))),log('isDirector\x20'+_0x4eb553),getChatMessage(_0x27465c[_0x71e5c3(0x2f8)],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x89c)],_0x4eb553,_0x5eef);}if(_0x71e5c3(0xa63)in _0x27465c){_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['order']=parseInt(_0x27465c[_0x71e5c3(0xa63)])||0x0;_0x1f2fd5 in _0x311669[_0x71e5c3(0x16a)]&&(_0x311669[_0x71e5c3(0x16a)][_0x1f2fd5]['order']=_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['order']);if(_0x311669[_0x71e5c3(0x75b)]){var _0x337561=document[_0x71e5c3(0x319)](_0x71e5c3(0x1a7)+_0x1f2fd5+'\x22]');log(_0x337561),_0x337561[0x0]&&(_0x337561[0x0][_0x71e5c3(0x882)]=parseInt(_0x27465c['order'])||0x0);}updateMixer();}'scale'in _0x27465c&&_0x311669[_0x71e5c3(0x88b)](_0x1f2fd5,_0x27465c['scale']);if(_0x311669['director']&&_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x5b5)]&&_0x71e5c3(0x772)in _0x27465c){log(_0x27465c),_0x311669[_0x71e5c3(0x78e)]=_0x27465c[_0x71e5c3(0x772)];for(var _0x311070 in _0x311669[_0x71e5c3(0x78e)]){syncSceneState(_0x311070),syncOtherState(_0x311070);}}if(_0x311669['directorList']['indexOf'](_0x1dae43||_0x1f2fd5)==-0x1){if(_0x71e5c3(0x4c5)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x4c5),_0x311669['sendMessage'](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x89f)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]='requestVideoRecord',_0x311669['sendMessage'](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x5f7)in _0x27465c){var _0x3d5b95={};_0x3d5b95['rejected']=_0x71e5c3(0x5f7),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x8f8)in _0x27465c){var _0x3d5b95={};_0x3d5b95['rejected']=_0x71e5c3(0x8f8),_0x311669['sendMessage'](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x7c9)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x7c9),_0x311669['sendMessage'](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x2a7)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x2a7),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x3ff)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x3ff),_0x311669['sendMessage'](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x478)in _0x27465c){var _0x3d5b95={};_0x3d5b95['rejected']=_0x71e5c3(0x478),_0x311669['sendMessage'](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x614)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x614),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if('remoteVideoMuted'in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]='remoteVideoMuted',_0x311669['sendMessage'](_0x3d5b95,_0x1f2fd5);}else{if('requestChangeMicDelay'in _0x27465c){var _0x3d5b95={};_0x3d5b95['rejected']=_0x71e5c3(0x52c),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if('lowerhand'in _0x27465c){var _0x3d5b95={};_0x3d5b95['rejected']=_0x71e5c3(0x7f6),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if('hangup'in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x813),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x4fd)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x4fd),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x7c2)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x7c2),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x356)in _0x27465c){var _0x3d5b95={};_0x3d5b95['rejected']=_0x71e5c3(0x356),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if('micIsolated'in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x8f3),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x941)in _0x27465c){var _0x3d5b95={};_0x3d5b95['rejected']=_0x71e5c3(0x941),_0x311669['sendMessage'](_0x3d5b95,_0x1f2fd5);}else{if('stopClock'in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x65a),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if('resumeClock'in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x2c8),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x7b3)in _0x27465c){var _0x3d5b95={};_0x3d5b95['rejected']=_0x71e5c3(0x7b3),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x165)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x165),_0x311669['sendMessage'](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x5e8)in _0x27465c){var _0x3d5b95={};_0x3d5b95['rejected']=_0x71e5c3(0x5e8),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x876)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x876),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if('pauseClock'in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x4dc),_0x311669['sendMessage'](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x6f3)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x6f3),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if('group'in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x532),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}}}}}}}}}}}}}}}}}}}}}}}}}}}}else{if(_0x71e5c3(0x4c5)in _0x27465c){var _0x3d77cd=_0x311669[_0x71e5c3(0x585)]['getAudioTracks']();_0x3d77cd[_0x71e5c3(0x8e9)]&&(_0x71e5c3(0x800)in _0x27465c?applyAudioHack(_0x27465c[_0x71e5c3(0x766)],_0x27465c['value'],_0x27465c['deviceId']):applyAudioHack(_0x27465c[_0x71e5c3(0x766)],_0x27465c[_0x71e5c3(0x8d7)]));}if(_0x71e5c3(0x89f)in _0x27465c){if(_0x27465c['requestVideoRecord']){if(_0x311669[_0x71e5c3(0x49b)]){var _0x185464=0x1770;_0x27465c[_0x71e5c3(0x8d7)]&&(_0x185464=parseInt(_0x27465c['value'])),recordLocalVideo(_0x71e5c3(0x36c),_0x185464);}}else _0x311669['videoElement']&&recordLocalVideo(_0x71e5c3(0x86e));}if(_0x71e5c3(0x5f7)in _0x27465c){_0x311669[_0x71e5c3(0xa63)]==![]&&(_0x311669[_0x71e5c3(0xa63)]=0x0);_0x311669[_0x71e5c3(0xa63)]+=parseInt(_0x27465c[_0x71e5c3(0x5f7)])||0x0;var _0x3d5b95={};_0x3d5b95={},_0x3d5b95['order']=_0x311669['order'],_0x311669[_0x71e5c3(0x2db)](_0x3d5b95),updateMixer();}_0x71e5c3(0x8f8)in _0x27465c&&changeURL(_0x27465c[_0x71e5c3(0x8f8)]);'stopClock'in _0x27465c&&stopClock();_0x71e5c3(0x2c8)in _0x27465c&&resumeClock();'setClock'in _0x27465c&&setClock(_0x27465c[_0x71e5c3(0x7b3)]);_0x71e5c3(0x165)in _0x27465c&&hideClock();'showClock'in _0x27465c&&showClock();_0x71e5c3(0x876)in _0x27465c&&startClock();_0x71e5c3(0x4dc)in _0x27465c&&pauseClock();if(_0x71e5c3(0x6f3)in _0x27465c){if(_0x311669['showTime']!==![]){if(_0x27465c[_0x71e5c3(0x6f3)]&&!_0x311669[_0x71e5c3(0x6f3)])toggleClock(_0x27465c['clock24']||![]);else!_0x27465c[_0x71e5c3(0x6f3)]&&_0x311669[_0x71e5c3(0x6f3)]&&toggleClock(_0x27465c[_0x71e5c3(0x226)]||![]);}}_0x71e5c3(0x941)in _0x27465c&&toggleFileshare(_0x1f2fd5);if(_0x71e5c3(0x532)in _0x27465c)try{_0x1dae43?(_0x27465c['group']?_0x311669[_0x71e5c3(0x531)]=_0x27465c[_0x71e5c3(0x532)][_0x71e5c3(0x256)](','):_0x311669['group_alt']=[],_0x311669[_0x71e5c3(0x8a1)]({'group':_0x27465c[_0x71e5c3(0x532)],'altUUID':!![]})):(_0x27465c[_0x71e5c3(0x532)]?_0x311669[_0x71e5c3(0x532)]=_0x27465c[_0x71e5c3(0x532)]['split'](','):_0x311669['group']=[],_0x311669['sendMessage']({'group':_0x27465c['group']})),updateMixer(),pokeIframeAPI(_0x71e5c3(0x23f),_0x311669[_0x71e5c3(0x532)]);}catch(_0x52fc8c){}if(_0x71e5c3(0x7c9)in _0x27465c){if(_0x71e5c3(0x8d7)in _0x27465c){if(typeof _0x27465c[_0x71e5c3(0x8d7)]==_0x71e5c3(0xa46)){_0x311669['label']=sanitizeLabel(_0x27465c[_0x71e5c3(0x8d7)]),log('New\x20Label:\x20'+_0x311669['label']);if(_0x311669['director']){var _0x337561=getById(_0x71e5c3(0x1c8)+_0x1f2fd5);if(_0x311669[_0x71e5c3(0x89c)])_0x337561[_0x71e5c3(0x882)]=_0x311669[_0x71e5c3(0x89c)],_0x337561[_0x71e5c3(0x424)][_0x71e5c3(0x761)](_0x71e5c3(0x55f));else _0x311669[_0x71e5c3(0x2f3)]===(_0x1dae43||_0x1f2fd5)?(miniTranslate(_0x337561[_0x71e5c3(0x82f)],_0x71e5c3(0x330)),_0x337561['classList']['remove'](_0x71e5c3(0x55f))):(miniTranslate(_0x337561['innerHTML'],'add-a-label'),_0x337561[_0x71e5c3(0x424)][_0x71e5c3(0x517)]('addALabel'));}else _0x311669[_0x71e5c3(0x901)]&&updateMixer();!_0x311669[_0x71e5c3(0x75b)]&&(_0x311669[_0x71e5c3(0x89c)]?document[_0x71e5c3(0x29a)]=_0x311669[_0x71e5c3(0x89c)]:document[_0x71e5c3(0x29a)]=location[_0x71e5c3(0x4a3)]);var _0x5e81da=encodeURIComponent(_0x311669[_0x71e5c3(0x89c)]);urlParams[_0x71e5c3(0x4d7)]('l')?updateURL('l='+_0x5e81da,!![],![]):updateURL('label='+_0x5e81da,!![],![]);var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x7c9)]=!![],_0x3d5b95['value']=_0x311669[_0x71e5c3(0x89c)],_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95);}else{_0x311669['label']=![];var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x7c9)]=!![],_0x3d5b95[_0x71e5c3(0x8d7)]=_0x311669[_0x71e5c3(0x89c)],_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95);if(_0x311669['director']){var _0x337561=getById(_0x71e5c3(0x1c8)+_0x1f2fd5);_0x311669[_0x71e5c3(0x2f3)]===(_0x1dae43||_0x1f2fd5)?(miniTranslate(_0x337561[_0x71e5c3(0x82f)],_0x71e5c3(0x330)),_0x337561[_0x71e5c3(0x424)][_0x71e5c3(0x761)](_0x71e5c3(0x55f))):(miniTranslate(_0x337561[_0x71e5c3(0x82f)],_0x71e5c3(0x43c)),_0x337561['classList'][_0x71e5c3(0x517)](_0x71e5c3(0x55f)));}else _0x311669[_0x71e5c3(0x901)]?(document['title']=location[_0x71e5c3(0x4a3)],updateMixer()):document[_0x71e5c3(0x29a)]=location[_0x71e5c3(0x4a3)];}}}if(_0x71e5c3(0x2a7)in _0x27465c){if(_0x27465c[_0x71e5c3(0x766)]=='low')changeLowEQ(parseFloat(_0x27465c[_0x71e5c3(0x8d7)]),_0x27465c[_0x71e5c3(0x53f)]);else{if(_0x27465c[_0x71e5c3(0x766)]==_0x71e5c3(0x64c))changeMidEQ(parseFloat(_0x27465c['value']),_0x27465c[_0x71e5c3(0x53f)]);else _0x27465c[_0x71e5c3(0x766)]==_0x71e5c3(0x4f3)&&changeHighEQ(parseFloat(_0x27465c[_0x71e5c3(0x8d7)]),_0x27465c[_0x71e5c3(0x53f)]);}}if(_0x71e5c3(0x3ff)in _0x27465c){var _0xfc15d0=_0x311669[_0x71e5c3(0x8ea)];if(_0x27465c['value']===_0x71e5c3(0x309))_0x311669[_0x71e5c3(0x8ea)]=![],log('noise\x20gate\x20off');else _0x27465c[_0x71e5c3(0x8d7)]===_0x71e5c3(0x8fc)?(_0x311669['noisegate']=!![],log(_0x71e5c3(0x27e))):_0x311669[_0x71e5c3(0x8ea)]=_0x27465c[_0x71e5c3(0x8d7)];_0x311669[_0x71e5c3(0x8ea)]!==_0xfc15d0&&senderAudioUpdate();}if(_0x71e5c3(0x478)in _0x27465c){var _0xfc15d0=_0x311669[_0x71e5c3(0x30a)];if(_0x27465c[_0x71e5c3(0x8d7)]===_0x71e5c3(0x309))_0x311669[_0x71e5c3(0x30a)]=![],log(_0x71e5c3(0x32d));else{if(_0x27465c[_0x71e5c3(0x8d7)]==='1')_0x311669[_0x71e5c3(0x30a)]=0x1,log(_0x71e5c3(0x27e));else _0x27465c[_0x71e5c3(0x8d7)]==='2'?(_0x311669[_0x71e5c3(0x30a)]=0x2,log(_0x71e5c3(0x27e))):_0x311669[_0x71e5c3(0x30a)]=parseInt(_0x27465c['value'])||![];}_0x311669[_0x71e5c3(0x30a)]!==_0xfc15d0&&senderAudioUpdate();}_0x71e5c3(0x52c)in _0x27465c&&(_0x311669['micDelay']===![]?(_0x311669['micDelay']=parseInt(_0x27465c[_0x71e5c3(0x8d7)])||0x0,senderAudioUpdate()):(_0x311669[_0x71e5c3(0x1d9)]=parseInt(_0x27465c[_0x71e5c3(0x8d7)])||0x0,changeMicDelay(_0x311669[_0x71e5c3(0x1d9)],_0x27465c[_0x71e5c3(0x800)])));'requestChangeSubGain'in _0x27465c&&changeSubGain(parseFloat(_0x27465c[_0x71e5c3(0x8d7)]),_0x27465c[_0x71e5c3(0x800)]);_0x71e5c3(0x7f6)in _0x27465c&&(_0x311669[_0x71e5c3(0x7a8)]&&lowerhand());if(_0x71e5c3(0x2d8)in _0x27465c&&'mirrorGuestTarget'in _0x27465c){if(_0x27465c[_0x71e5c3(0x402)]&&_0x27465c[_0x71e5c3(0x402)]===!![])_0x311669['permaMirrored']=_0x27465c[_0x71e5c3(0x2d8)],applyMirror(_0x311669[_0x71e5c3(0x89b)]);else _0x27465c[_0x71e5c3(0x402)]&&_0x27465c[_0x71e5c3(0x402)]in _0x311669['rpcs']&&(_0x311669[_0x71e5c3(0x16a)][_0x27465c['mirrorGuestTarget']][_0x71e5c3(0x350)]=_0x27465c['mirrorGuestState'],_0x311669[_0x71e5c3(0x16a)][_0x27465c[_0x71e5c3(0x402)]][_0x71e5c3(0x49b)]&&applyMirrorGuest(_0x27465c[_0x71e5c3(0x2d8)],_0x311669[_0x71e5c3(0x16a)][_0x27465c['mirrorGuestTarget']][_0x71e5c3(0x49b)]));}if(_0x71e5c3(0x6a6)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x54a)]=_0x1f2fd5,_0x3d5b95['audioOptions']=listAudioSettingsPrep(),sendMediaDevices(_0x3d5b95[_0x71e5c3(0x54a)]),_0x311669['sendMessage'](_0x3d5b95,_0x3d5b95[_0x71e5c3(0x54a)]);}if(_0x71e5c3(0x389)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x54a)]=_0x1f2fd5,_0x3d5b95[_0x71e5c3(0x177)]=listVideoSettingsPrep(),sendMediaDevices(_0x3d5b95[_0x71e5c3(0x54a)]),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x3d5b95[_0x71e5c3(0x54a)]);}_0x71e5c3(0x715)in _0x27465c&&changeAudioOutputDeviceById(_0x27465c[_0x71e5c3(0x715)],_0x1f2fd5);_0x71e5c3(0x3eb)in _0x27465c&&changeAudioDeviceById(_0x27465c[_0x71e5c3(0x3eb)],_0x1f2fd5);_0x71e5c3(0x791)in _0x27465c&&changeVideoDeviceById(_0x27465c[_0x71e5c3(0x791)],_0x1f2fd5);'requestVideoHack'in _0x27465c&&(_0x71e5c3(0x8be)in _0x27465c&&_0x27465c[_0x71e5c3(0x8be)]?updateCameraConstraints(_0x27465c[_0x71e5c3(0x766)],_0x27465c[_0x71e5c3(0x8d7)],!![],_0x1f2fd5):updateCameraConstraints(_0x27465c['keyname'],_0x27465c[_0x71e5c3(0x8d7)],![],![]));_0x71e5c3(0x940)in _0x27465c&&changeLowCut(parseFloat(_0x27465c[_0x71e5c3(0x8d7)]),_0x27465c['track']);'requestChangeLowcut'in _0x27465c&&changeLowCut(parseFloat(_0x27465c[_0x71e5c3(0x8d7)]),_0x27465c['track']);_0x71e5c3(0x813)in _0x27465c&&(_0x311669[_0x71e5c3(0x2f3)]&&_0x311669['hangup']());if('mute'in _0x27465c){}if('volume'in _0x27465c){var _0x2bef61=parseInt(_0x27465c[_0x71e5c3(0x356)])/0x64||0x0;_0x311669[_0x71e5c3(0x591)]=parseInt(_0x27465c[_0x71e5c3(0x356)])||0x0;try{for(var _0x393e8c in _0x311669[_0x71e5c3(0x7a7)]){log(_0x71e5c3(0x52f)),_0x311669[_0x71e5c3(0x7a7)][_0x393e8c][_0x71e5c3(0x987)][_0x71e5c3(0x823)][_0x71e5c3(0x44a)](_0x2bef61,_0x311669[_0x71e5c3(0x7a7)][_0x393e8c][_0x71e5c3(0x837)]['currentTime']);}}catch(_0x451ac6){}updateVolume(!![]);}if('micIsolate'in _0x27465c){if(_0x27465c[_0x71e5c3(0x52e)])_0x311669[_0x71e5c3(0x95b)]['indexOf'](_0x1dae43||_0x1f2fd5)>=0x0&&(_0x311669[_0x71e5c3(0x8f3)][_0x71e5c3(0x505)](_0x1f2fd5),_0x311669[_0x71e5c3(0x8ef)]());else{var _0x493e27=_0x311669[_0x71e5c3(0x8f3)][_0x71e5c3(0x49e)](_0x1f2fd5);_0x493e27>-0x1&&(_0x311669[_0x71e5c3(0x8f3)][_0x71e5c3(0x48c)](_0x493e27,0x1),_0x311669['applyIsolatedChat']());}}if(_0x71e5c3(0x4d1)in _0x27465c){if(_0x27465c[_0x71e5c3(0x4d1)])_0x311669[_0x71e5c3(0x95b)][_0x71e5c3(0x49e)](_0x1dae43||_0x1f2fd5)>=0x0&&(_0x311669[_0x71e5c3(0x4d1)][_0x71e5c3(0x505)](_0x1f2fd5),_0x311669[_0x71e5c3(0x645)]());else{var _0x493e27=_0x311669[_0x71e5c3(0x4d1)]['indexOf'](_0x1f2fd5);_0x493e27>-0x1&&(_0x311669[_0x71e5c3(0x4d1)]['splice'](_0x493e27,0x1),_0x311669[_0x71e5c3(0x645)]());}}_0x71e5c3(0x7c2)in _0x27465c&&(_0x27465c[_0x71e5c3(0x7c2)]?(_0x311669[_0x71e5c3(0x5c9)]=!![],_0x311669[_0x71e5c3(0x8f1)]()):(_0x311669[_0x71e5c3(0x5c9)]=![],_0x311669[_0x71e5c3(0x8f1)]()));_0x71e5c3(0x4fd)in _0x27465c&&(_0x27465c['displayMute']?(_0x311669[_0x71e5c3(0x285)]=!![],_0x311669['directorDisplayMute']()):(_0x311669['directorDisplayMuted']=![],_0x311669[_0x71e5c3(0x324)]()));if('remoteVideoMuted'in _0x27465c){_0x311669[_0x71e5c3(0x360)]=_0x27465c[_0x71e5c3(0x360)],toggleVideoMute(!![]);if(!_0x311669[_0x71e5c3(0x8ab)]){var _0x3d5b95={};_0x3d5b95['videoMuted']=_0x311669[_0x71e5c3(0x360)],_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95);}}_0x71e5c3(0x20b)in _0x27465c&&applyNewParams(_0x27465c[_0x71e5c3(0x20b)]);}if(_0x311669['directorUUID']===(_0x1dae43||_0x1f2fd5)){_0x27465c[_0x71e5c3(0x727)]==='migrate'&&(warnlog(_0x71e5c3(0x19e)),_0x71e5c3(0x321)in _0x27465c&&(_0x71e5c3(0x5cf)in _0x27465c[_0x71e5c3(0x321)]&&(_0x311669[_0x71e5c3(0x5cf)]=_0x27465c[_0x71e5c3(0x5cf)]),_0x71e5c3(0x254)in _0x27465c[_0x71e5c3(0x321)]&&(_0x27465c[_0x71e5c3(0x321)][_0x71e5c3(0x254)]===!![]||_0x27465c['transferSettings'][_0x71e5c3(0x254)]===null?(_0x311669[_0x71e5c3(0x254)]=null,_0x311669[_0x71e5c3(0x557)]===![]&&(_0x311669[_0x71e5c3(0x557)]=0x2),_0x311669[_0x71e5c3(0x886)]===![]&&(_0x311669[_0x71e5c3(0x886)]=0x1),_0x311669[_0x71e5c3(0xa1f)]===null&&(_0x311669['showList']=!![])):_0x311669[_0x71e5c3(0x254)]=_0x27465c[_0x71e5c3(0x321)][_0x71e5c3(0x254)],_0x27465c[_0x71e5c3(0x321)][_0x71e5c3(0x339)]&&(_0x311669[_0x71e5c3(0x254)]!==![]?_0x311669['broadcast']===null?updateURL(_0x71e5c3(0x254),!![]):updateURL('broadcast='+_0x311669[_0x71e5c3(0x254)],!![]):updateURL(_0x71e5c3(0x1eb),!![]))),_0x71e5c3(0x4e1)in _0x27465c[_0x71e5c3(0x321)]&&(_0x311669[_0x71e5c3(0x4e1)]=_0x27465c[_0x71e5c3(0x321)]['roomid'],_0x27465c['transferSettings'][_0x71e5c3(0x339)]&&updateURL(_0x71e5c3(0x70b)+_0x311669[_0x71e5c3(0x4e1)],!![])),_0x71e5c3(0x64f)in _0x27465c['transferSettings']&&(_0x311669[_0x71e5c3(0x64f)]=_0x27465c[_0x71e5c3(0x321)][_0x71e5c3(0x64f)],_0x311669[_0x71e5c3(0x64f)]&&(_0x311669[_0x71e5c3(0x64f)]=0x2),_0x27465c[_0x71e5c3(0x321)][_0x71e5c3(0x339)]&&(_0x311669['queue']?updateURL(_0x71e5c3(0x64f),!![]):updateURL('queue=false',!![]))),_0x71e5c3(0x817)in _0x27465c[_0x71e5c3(0x321)]&&(_0x311669['queue']&&(_0x311669[_0x71e5c3(0x64f)]=0x3,_0x27465c['transferSettings']['updateurl']&&updateURL(_0x71e5c3(0x985),!![])))));try{if(_0x71e5c3(0x8ce)in _0x27465c&&'addCoDirector'in _0x27465c[_0x71e5c3(0x8ce)])for(var _0x5972cd=0x0;_0x5972cd<_0x27465c['directorSettings'][_0x71e5c3(0x1d7)][_0x71e5c3(0x8e9)];_0x5972cd++){if(!_0x311669['directorList'][_0x71e5c3(0x2c2)](_0x27465c[_0x71e5c3(0x8ce)][_0x71e5c3(0x1d7)][_0x5972cd][_0x71e5c3(0x721)])){_0x311669[_0x71e5c3(0x95b)]['push'](_0x27465c[_0x71e5c3(0x8ce)]['addCoDirector'][_0x5972cd][_0x71e5c3(0x721)]());var _0x3e6659=getById('container_'+_0x27465c['directorSettings'][_0x71e5c3(0x1d7)][_0x5972cd]['toString']());_0x3e6659&&_0x3e6659[_0x71e5c3(0x424)]['add'](_0x71e5c3(0x278));}}}catch(_0x55e304){errorlog(_0x55e304);}if(_0x71e5c3(0x4fb)in _0x27465c)try{_0x311669[_0x71e5c3(0x8a1)]({'cbid':_0x27465c[_0x71e5c3(0x4fb)]},_0x1f2fd5);}catch(_0x1eee2a){errorlog(_0x1eee2a);}}if(_0x71e5c3(0x57f)in _0x27465c){if(_0x311669['remote']){if(_0x71e5c3(0x24d)in _0x27465c&&_0x27465c[_0x71e5c3(0x24d)]===_0x311669[_0x71e5c3(0x24d)]&&_0x311669[_0x71e5c3(0x24d)])_0x311669['remoteZoom'](parseFloat(_0x27465c[_0x71e5c3(0x57f)]));else _0x311669[_0x71e5c3(0x24d)]===!![]&&_0x311669[_0x71e5c3(0x403)](parseFloat(_0x27465c[_0x71e5c3(0x57f)]));}else{if(_0x311669['directorList'][_0x71e5c3(0x49e)](_0x1dae43||_0x1f2fd5)>=0x0)_0x311669[_0x71e5c3(0x403)](parseFloat(_0x27465c[_0x71e5c3(0x57f)]));else return;}}if(_0x71e5c3(0x21a)in _0x27465c){if(_0x311669[_0x71e5c3(0x24d)]){if(_0x71e5c3(0x24d)in _0x27465c&&_0x27465c[_0x71e5c3(0x24d)]===_0x311669[_0x71e5c3(0x24d)]&&_0x311669[_0x71e5c3(0x24d)])_0x311669[_0x71e5c3(0x2a6)](parseFloat(_0x27465c['focus']));else _0x311669[_0x71e5c3(0x24d)]===!![]&&_0x311669[_0x71e5c3(0x2a6)](parseFloat(_0x27465c[_0x71e5c3(0x21a)]));}else{if(_0x311669[_0x71e5c3(0x95b)][_0x71e5c3(0x49e)](_0x1dae43||_0x1f2fd5)>=0x0)_0x311669['remoteFocus'](parseFloat(_0x27465c[_0x71e5c3(0x21a)]));else return;}}if(_0x71e5c3(0x91a)in _0x27465c){log('requestFile');try{_0x311669[_0x71e5c3(0x8ca)](_0x1f2fd5,_0x27465c[_0x71e5c3(0x91a)]);}catch(_0x426a58){errorlog(_0x426a58);}}_0x71e5c3(0x1e4)in _0x27465c&&playbackMIDI(_0x27465c['midi'],!![]);}catch(_0xeda637){errorlog(_0xeda637);}if('rejected'in _0x27465c){if(_0x27465c[_0x71e5c3(0x485)]=='obsCommand'){if(_0x311669['remote'])warnUser(getTranslation(_0x71e5c3(0x8e3)),0xbb8);else document[_0x71e5c3(0xa5d)](_0x71e5c3(0x85e))&&document['querySelector'](_0x71e5c3(0x85e))[_0x71e5c3(0x8d7)]?warnUser(getTranslation(_0x71e5c3(0xa41)),0x1b58):warnUser(getTranslation('request-rejected-obs'),0x2710);getById(_0x71e5c3(0x684))[_0x71e5c3(0x424)][_0x71e5c3(0x761)](_0x71e5c3(0x472));}else{if(_0x311669[_0x71e5c3(0x75b)])!_0x311669[_0x71e5c3(0x78c)]&&warnUser(_0x71e5c3(0x55b)+_0x27465c[_0x71e5c3(0x485)]+_0x71e5c3(0x2c9),0x1388);else!_0x311669[_0x71e5c3(0x78c)]&&(_0x311669[_0x71e5c3(0x24d)]?warnUser(_0x71e5c3(0x1e2),0x1388):warnUser(_0x71e5c3(0x9f9),0x1388));}errorlog('ACTION\x20REJECTED:\x20'+_0x27465c[_0x71e5c3(0x485)]+_0x71e5c3(0x1ed)+_0x311669[_0x71e5c3(0x75b)]),pokeIframeAPI(_0x71e5c3(0x485),_0x27465c[_0x71e5c3(0x485)],_0x1f2fd5);return;}else{if(_0x71e5c3(0x634)in _0x27465c){log(_0x71e5c3(0x7ef)+_0x27465c[_0x71e5c3(0x634)]),pokeIframeAPI(_0x71e5c3(0x634),_0x27465c[_0x71e5c3(0x634)],_0x1f2fd5);return;}}if(_0x71e5c3(0x433)in _0x27465c||'video'in _0x27465c){log(_0x71e5c3(0xa56));_0x27465c[_0x71e5c3(0x433)]&&(_0x311669['pcs'][_0x1f2fd5]['allowAudio']=!![]);if(_0x311669[_0x71e5c3(0x352)]&&_0x71e5c3(0x81a)in _0x27465c&&_0x27465c['allowwebp']!==![])_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x7c3)]=_0x27465c[_0x71e5c3(0x81a)],_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x670)]=![],setTimeout(function(){makeImages(!![]);},0x3e8);else _0x27465c[_0x71e5c3(0x30c)]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['allowVideo']=!![]);_0x71e5c3(0x254)in _0x27465c&&_0x27465c[_0x71e5c3(0x254)]!==![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x211)]=_0x27465c[_0x71e5c3(0x254)]);_0x71e5c3(0x9b8)in _0x27465c&&_0x27465c[_0x71e5c3(0x9b8)]!==![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x97b)]=_0x27465c[_0x71e5c3(0x9b8)]);_0x71e5c3(0x54d)in _0x27465c&&_0x27465c['iframe']!==![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x299)]=_0x27465c['iframe']);'widget'in _0x27465c&&_0x27465c[_0x71e5c3(0x8b0)]!==![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x368)]=_0x27465c[_0x71e5c3(0x8b0)]);_0x71e5c3(0x622)in _0x27465c&&_0x27465c[_0x71e5c3(0x622)]!==![]&&(_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x87f)]=_0x27465c['allowmidi']);_0x71e5c3(0x5bf)in _0x27465c&&_0x27465c['downloads']!==![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x681)]=_0x27465c[_0x71e5c3(0x5bf)]);_0x71e5c3(0x25c)in _0x27465c&&_0x27465c[_0x71e5c3(0x25c)]!==![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x6b0)]=!![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0xa6b)]=!![]);_0x71e5c3(0x4ea)in _0x27465c&&_0x27465c['allowscreenvideo']!==![]&&(_0x311669['pcs'][_0x1f2fd5]['allowScreenVideo']=!![]);_0x71e5c3(0x343)in _0x27465c&&_0x27465c[_0x71e5c3(0x343)]!==![]&&(_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x6b0)]=!![]);_0x71e5c3(0x3aa)in _0x27465c&&_0x27465c[_0x71e5c3(0x3aa)]!==![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['preferVideoCodec']=_0x27465c[_0x71e5c3(0x3aa)][_0x71e5c3(0x62c)]());'preferAudioCodec'in _0x27465c&&_0x27465c[_0x71e5c3(0x5ea)]!==![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x5ea)]=_0x27465c[_0x71e5c3(0x5ea)][_0x71e5c3(0x62c)]());if(_0x71e5c3(0x1c0)in _0x27465c&&_0x27465c['allowmeshcast']===![])_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['whipout']=![];else{if(_0x71e5c3(0x7c7)in _0x27465c&&_0x27465c[_0x71e5c3(0x7c7)]===![])_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x45a)]=![];else{if(_0x311669[_0x71e5c3(0x98c)]){if(_0x311669['meshcast']=='video')_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x670)]=![];else{if(_0x311669['meshcast']=='audio')_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x760)]=![];else _0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x670)]==![]?_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['whipout']=![]:(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x760)]=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x670)]=![]);}}else _0x311669[_0x71e5c3(0x2d6)]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['allowAudio']=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x670)]=![]);}}if(_0x311669[_0x71e5c3(0x84e)]){playtone(),window[_0x71e5c3(0x21a)]();var _0x1bdd64=![];_0x1f2fd5 in _0x311669[_0x71e5c3(0x16a)]&&_0x311669[_0x71e5c3(0x16a)][_0x1f2fd5]['label']&&(_0x1bdd64=_0x311669[_0x71e5c3(0x16a)][_0x1f2fd5]['label']||_0x311669[_0x71e5c3(0x16a)][_0x1f2fd5]['streamID']||![]);_0x1bdd64=_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x89c)]||_0x1bdd64||_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x9bb)]||_0x1f2fd5||_0x71e5c3(0x99c);var _0x158b0f=await confirmAlt(_0x1bdd64+getTranslation(_0x71e5c3(0x753)),!![]);if(!_0x158b0f){try{log(_0x71e5c3(0xa62)),_0x311669[_0x71e5c3(0x9d2)](_0x1f2fd5);}catch(_0x52865e){}return;}}_0x71e5c3(0x798)in _0x27465c&&(_0x27465c[_0x71e5c3(0x798)]==!![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x798)]=!![],_0x311669[_0x71e5c3(0x3df)]&&(playtone(![],_0x71e5c3(0x7a9)),showNotification(_0x71e5c3(0x300),'')),pokeIframeAPI(_0x71e5c3(0x439),_0x27465c[_0x71e5c3(0x75b)],_0x1f2fd5)));_0x71e5c3(0x920)in _0x27465c&&(_0x27465c[_0x71e5c3(0x920)]===!![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['forceios']=!![]));_0x71e5c3(0x24d)in _0x27465c&&(_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x24d)]=_0x27465c[_0x71e5c3(0x24d)]);_0x71e5c3(0x6f0)in _0x27465c&&(_0x27465c['limitaudio']==!![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0xa0d)]=!![]));_0x71e5c3(0x391)in _0x27465c&&(_0x27465c[_0x71e5c3(0x391)]==!![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['enhanceAudio']=!![]));_0x27465c[_0x71e5c3(0x288)]&&(_0x311669['pcs'][_0x1f2fd5]['degradationPreference']=_0x27465c[_0x71e5c3(0x288)]);if(_0x71e5c3(0x458)in _0x27465c)try{_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x458)]=_0x27465c[_0x71e5c3(0x458)],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x458)]&&setTimeout(function(_0x509b92){var _0x105a86=_0x71e5c3;_0x311669[_0x105a86(0x2e3)](_0x509b92);},0x1388,_0x1f2fd5);}catch(_0x52171f){warnlog(_0x52171f);}_0x71e5c3(0x438)in _0x27465c&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x438)]=_0x27465c[_0x71e5c3(0x438)]);_0x71e5c3(0x559)in _0x27465c&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x559)]=_0x27465c['layout'],!_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x559)]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x559)]=_0x27465c['layout'],_0x311669[_0x71e5c3(0x15f)]&&_0x311669[_0x71e5c3(0x75b)]&&_0x311669['pcs'][_0x1f2fd5]&&_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x559)]&&createSlotUpdate(_0x1f2fd5)));if(_0x71e5c3(0x98d)in _0x27465c){if(_0x27465c[_0x71e5c3(0x98d)]!==![]){try{typeof _0x27465c['scene']==='string'?_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x98d)]=_0x27465c[_0x71e5c3(0x98d)]['replace'](/[\W]+/g,'_'):_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x98d)]=(parseInt(_0x27465c['scene'])||0x0)+'',_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x436)][_0x71e5c3(0x98d)]=_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x98d)],updateSceneList(_0x311669['pcs'][_0x1f2fd5]['scene']);}catch(_0x52c932){errorlog(_0x52c932);}_0x71e5c3(0x560)in _0x27465c?_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x560)]=_0x27465c[_0x71e5c3(0x560)]:_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x560)]=_0x311669[_0x71e5c3(0x560)];if(_0x311669[_0x71e5c3(0x75b)]){if(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['showDirector']==![])_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x760)]=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x670)]=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x299)]=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['allowWidget']=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x45a)]=![],_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x7c3)]=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['allowScreenAudio']=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0xa6b)]=![];else{if(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x560)]==0x1)_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x299)]=![],_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x368)]=![];else{if(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x560)]==0x2)_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x760)]=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['allowScreenAudio']=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x299)]=![],_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x368)]=![];else{if(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['showDirector']==0x3)_0x311669['pcs'][_0x1f2fd5]['allowAudio']=![],_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x670)]=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x299)]=![],_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x368)]=![],_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x45a)]=![],_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x7c3)]=![];else{if(_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x560)]==0x4){}}}}}}_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['solo']?pokeIframeAPI(_0x71e5c3(0x885),_0x27465c[_0x71e5c3(0x98d)],_0x1f2fd5):pokeIframeAPI('scene-connected',_0x27465c[_0x71e5c3(0x98d)],_0x1f2fd5);}_0x311669[_0x71e5c3(0x464)](_0x1f2fd5);}else _0x27465c[_0x71e5c3(0x75b)]&&((iOS||iPad)&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x920)]==!![]&&(_0x311669['pcs'][_0x1f2fd5]['guest']=!![])),_0x311669[_0x71e5c3(0x3df)]&&(playtone(![],_0x71e5c3(0x7a9)),showNotification(_0x71e5c3(0x1ab),_0x71e5c3(0x6ac))),_0x311669[_0x71e5c3(0x464)](_0x1f2fd5),pokeIframeAPI(_0x71e5c3(0x56e),_0x27465c[_0x71e5c3(0x75b)],_0x1f2fd5));_0x311669[_0x71e5c3(0x75b)]&&(_0x71e5c3(0x3cb)in _0x27465c&&(_0x27465c[_0x71e5c3(0x3cb)]==!![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x760)]=![],_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x670)]=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x299)]=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['allowWidget']=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x45a)]=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['allowWebp']=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['allowScreenAudio']=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['allowScreenVideo']=![]))),_0x311669[_0x71e5c3(0xa1d)](_0x1f2fd5);}},_0x311669[_0x42f4f2(0x464)]=function(_0x3fa4bb){var _0x214690=_0x42f4f2;if(!(_0x311669[_0x214690(0x772)]||_0x311669[_0x214690(0x98d)]))return;try{var _0x155aba={};_0x311669[_0x214690(0x859)][_0x3fa4bb]&&(_0x155aba[_0x214690(0x8ce)]=getDirectorSettings(_0x311669[_0x214690(0x859)][_0x3fa4bb][_0x214690(0x98d)]));log(_0x214690(0x8c1)+_0x3fa4bb);var _0x479814=![];_0x311669['alreadyJoinedMembers']&&_0x311669[_0x214690(0x9e8)]['forEach'](_0x3faa69=>{var _0x2ed52e=_0x214690;_0x3faa69[_0x2ed52e(0x54a)]===_0x3fa4bb&&(_0x479814=!![]);}),!_0x479814?_0x155aba[_0x214690(0x772)]=getDetailedState():warnlog(_0x214690(0x408)),Object[_0x214690(0x19b)](_0x155aba)[_0x214690(0x8e9)]&&_0x311669[_0x214690(0x2db)](_0x155aba,_0x3fa4bb);}catch(_0x3f596d){}},_0x311669[_0x42f4f2(0xa1d)]=function(_0xf0ced3){var _0x3827fd=_0x42f4f2;log(_0x3827fd(0x442)+_0xf0ced3);if(_0xf0ced3 in _0x311669[_0x3827fd(0x859)]){}else{errorlog(_0x3827fd(0x31c));return;}getSenders2(_0xf0ced3)['length']&&errorlog(_0x3827fd(0x90e)+getSenders2(_0xf0ced3)['length']);if(_0x311669[_0x3827fd(0x859)][_0xf0ced3][_0x3827fd(0x299)]===!![]){if(_0x311669[_0x3827fd(0x325)]){var _0x3de62e={};_0x3de62e[_0x3827fd(0x325)]=_0x311669[_0x3827fd(0x325)],_0x311669[_0x3827fd(0x9b0)]&&_0x311669[_0x3827fd(0x9b0)]['sendOnNewConnect']&&(_0x311669[_0x3827fd(0x325)][_0x3827fd(0x62b)](_0x3827fd(0x26a))&&(_0x3de62e['iframeSrc']+=_0x3827fd(0x31f)+parseInt(Math[_0x3827fd(0x2c3)](_0x311669[_0x3827fd(0x9b0)][_0x3827fd(0x6ba)][_0x3827fd(0x90a)]['t']))+'')),_0x311669[_0x3827fd(0x8a1)](_0x3de62e,_0xf0ced3);}}if(_0x311669[_0x3827fd(0x859)][_0xf0ced3][_0x3827fd(0x368)]===!![]){if(_0x311669[_0x3827fd(0x8b0)]&&_0x311669['director']){var _0x3de62e={};_0x3de62e[_0x3827fd(0x2f6)]=_0x311669['widget'],_0x311669['sendMessage'](_0x3de62e,_0xf0ced3);}}_0x311669['pcs'][_0xf0ced3][_0x3827fd(0x681)]===!![]&&_0x311669[_0x3827fd(0x869)](_0xf0ced3);if(_0x311669['chunked']&&_0x311669['pcs'][_0xf0ced3][_0x3827fd(0x97b)]){_0x311669[_0x3827fd(0x504)](_0xf0ced3);return;}var _0x109764=_0x311669[_0x3827fd(0x700)]();log(_0x3827fd(0x4e2)),log(_0x109764[_0x3827fd(0x951)]());if(_0x311669[_0x3827fd(0x948)]&&_0x311669['pcs'][_0xf0ced3][_0x3827fd(0x45a)]===null){_0x311669[_0x3827fd(0x859)][_0xf0ced3][_0x3827fd(0x45a)]=!![];var _0x3de62e={};_0x3de62e['whepSettings']=_0x311669['whipoutSettings'],_0x311669['sendMessage'](_0x3de62e,_0xf0ced3),warnlog(_0x3de62e);}(_0x311669[_0x3827fd(0x859)][_0xf0ced3][_0x3827fd(0xa6b)]||_0x311669[_0x3827fd(0x859)][_0xf0ced3][_0x3827fd(0x6b0)])&&createSecondStream2(_0xf0ced3);var _0x221555=![];_0x109764[_0x3827fd(0x87a)]()[_0x3827fd(0x982)](_0x411454=>{var _0x5ec9f4=_0x3827fd;try{_0x311669[_0x5ec9f4(0x859)][_0xf0ced3][_0x5ec9f4(0x670)]===!![]&&(_0x411454[_0x5ec9f4(0x7ad)]==_0x5ec9f4(0x30c)&&(_0x311669[_0x5ec9f4(0x859)][_0xf0ced3][_0x5ec9f4(0x798)]===!![]&&_0x311669[_0x5ec9f4(0x250)]===0x0?log(_0x5ec9f4(0x9ec)):(_0x311669['pcs'][_0xf0ced3][_0x5ec9f4(0x58b)](_0x411454,_0x109764),warnlog(_0x5ec9f4(0x4c3)),_0x221555=!![],setTimeout(function(_0x44da1e){try{_0x311669['optimizeBitrate'](_0x44da1e);}catch(_0x9aa45e){warnlog(_0x9aa45e);}},_0x311669[_0x5ec9f4(0x25e)],_0xf0ced3))));}catch(_0x506266){errorlog(_0x506266);}});_0x311669[_0x3827fd(0x655)]&&(_0x109764=mixMinusAudio(_0xf0ced3));_0x311669[_0x3827fd(0x859)][_0xf0ced3][_0x3827fd(0x760)]&&(_0x109764['getAudioTracks']()[_0x3827fd(0x982)](_0x504fcc=>{var _0x2ec919=_0x3827fd;try{_0x504fcc[_0x2ec919(0x7ad)]==_0x2ec919(0x433)&&(_0x311669[_0x2ec919(0x859)][_0xf0ced3][_0x2ec919(0x58b)](_0x504fcc,_0x109764),warnlog('added\x20audio\x20track'));}catch(_0x13cc96){errorlog(_0x13cc96);}}),log('does\x20any\x20audio\x20exist?'),_0x109764[_0x3827fd(0xa6c)]()['length']&&(_0x311669[_0x3827fd(0x75b)]!==![]&&_0x311669[_0x3827fd(0x1f6)](),log(_0x3827fd(0x883)),_0x311669[_0x3827fd(0x859)][_0xf0ced3][_0x3827fd(0x5ea)]==_0x3827fd(0x61d)&&lyraEncode(_0xf0ced3),_0x311669[_0x3827fd(0x859)][_0xf0ced3][_0x3827fd(0xa0d)]===!![]&&(warnlog(_0x3827fd(0x4fa)),setTimeout(_0x311669[_0x3827fd(0x33d)],0x3e8,_0xf0ced3,0x7d00,0x0)),_0x311669[_0x3827fd(0x859)][_0xf0ced3][_0x3827fd(0x410)]===!![]&&setTimeout(_0x311669['enhanceAudioEncoder'],0x3e8,_0xf0ced3)));if(_0x311669[_0x3827fd(0x859)][_0xf0ced3][_0x3827fd(0x5c7)])setTimeout(_0x311669[_0x3827fd(0x5c7)],0x3e8,_0xf0ced3,_0x311669[_0x3827fd(0x859)][_0xf0ced3][_0x3827fd(0x5c7)]);else{if(_0x311669[_0x3827fd(0x34f)]&&SafariVersion){if(_0x311669[_0x3827fd(0x34f)]=='detail')setTimeout(_0x311669[_0x3827fd(0x5c7)],0x3e8,_0xf0ced3,'maintain-resolution');else _0x311669[_0x3827fd(0x34f)]==_0x3827fd(0x743)&&setTimeout(_0x311669[_0x3827fd(0x5c7)],0x3e8,_0xf0ced3,'maintain-framerate');}}if(iOS||iPad){if(SafariVersion&&SafariVersion<=0xd){}else _0x221555&&(setTimeout(function(_0x50012b){_0x311669['setScale'](_0x50012b,null,!![]);},0x7d0,_0xf0ced3),setTimeout(function(_0x25098a){var _0x1664d8=_0x3827fd,_0x5598b2=_0x311669[_0x1664d8(0x164)](_0x25098a);!_0x5598b2&&_0x311669[_0x1664d8(0x88b)](_0x25098a,0x64,!![]);},0x1388,_0xf0ced3));}else setTimeout(function(_0x41fed3){var _0x1b02ad=_0x3827fd;_0x311669[_0x1b02ad(0x164)](_0x41fed3);},0x3e8,_0xf0ced3);};function _0x5af428(_0x3e5866,_0x3e3fd0,_0xe300f0){var _0x419597=_0x42f4f2,_0x55a2eb=new Blob([_0x3e5866],{'type':'text/plain'}),_0x575da7=new FileReader();_0x575da7['onload']=function(_0x63efb1){var _0x8355b5=_0x5c68;_0xe300f0(_0x63efb1[_0x8355b5(0x81f)]['result']);},_0x575da7[_0x419597(0x95c)](_0x55a2eb,_0x3e3fd0);}_0x311669[_0x42f4f2(0x869)]=function(_0x4190d5){var _0x496ce4=_0x42f4f2;log(_0x496ce4(0x967));if(!_0x311669[_0x496ce4(0x972)]||!_0x311669[_0x496ce4(0x972)]['length'])return;var _0x2907ce={},_0x479067=[];for(var _0x176d9c=0x0;_0x176d9c<_0x311669[_0x496ce4(0x972)][_0x496ce4(0x8e9)];_0x176d9c++){(_0x311669['hostedFiles'][_0x176d9c][_0x496ce4(0x474)]===![]||_0x311669[_0x496ce4(0x972)][_0x176d9c]['restricted']===_0x4190d5)&&_0x479067[_0x496ce4(0x505)]({'id':_0x311669[_0x496ce4(0x972)][_0x176d9c]['id'],'name':_0x311669[_0x496ce4(0x972)][_0x176d9c][_0x496ce4(0x6dd)],'size':_0x311669[_0x496ce4(0x972)][_0x176d9c][_0x496ce4(0x8cf)]});}_0x2907ce[_0x496ce4(0x2e8)]=_0x479067;if(_0x4190d5 in _0x311669[_0x496ce4(0x859)])_0x311669[_0x496ce4(0x8a1)](_0x2907ce,_0x4190d5);else _0x4190d5 in _0x311669[_0x496ce4(0x16a)]&&_0x311669[_0x496ce4(0xa14)](_0x2907ce,_0x4190d5);log(_0x2907ce);},_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x183)]=function(_0x233209){var _0x23bc9d=_0x42f4f2;if(!(_0x2551e1 in _0x311669[_0x23bc9d(0x859)]))return;try{if(this['iceConnectionState']===_0x23bc9d(0x208))log(_0x23bc9d(0x40c));else{if(this[_0x23bc9d(0x870)]===_0x23bc9d(0x381))log('PCS:\x20ICE\x20Disconnected;\x20wait\x20for\x20retry?\x20pcs');else{if(this[_0x23bc9d(0x870)]==='failed')log('ICE\x20FAILed.\x20bad?'),_0x311669[_0x23bc9d(0x859)][_0x2551e1][_0x23bc9d(0x9b6)]?_0x311669['pcs'][_0x2551e1][_0x23bc9d(0x9b6)]():_0x311669[_0x23bc9d(0x903)](_0x2551e1,!![]);else this[_0x23bc9d(0x870)]===_0x23bc9d(0x7ea)?log(_0x23bc9d(0x73c)):log(this[_0x23bc9d(0x870)]);}}}catch(_0x3820cc){errorlog(_0x3820cc);}},_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x6e5)]=function(_0x1ced25){var _0x1c0ed5=_0x42f4f2;switch(_0x311669['pcs'][_0x2551e1][_0x1c0ed5(0x3ee)]){case'connected':log(_0x1c0ed5(0x9a0)),clearTimeout(_0x311669[_0x1c0ed5(0x859)][_0x2551e1][_0x1c0ed5(0x9a4)]);if(_0x311669[_0x1c0ed5(0x4e5)]){if(_0x311669['ws'][_0x1c0ed5(0x8ee)]!==0x1){_0x311669['ws']['close']();break;}_0x311669['ws']['close'](),setTimeout(function(){var _0x210e8e=_0x1c0ed5;_0x311669[_0x210e8e(0x78c)]!=!![]&&warnUser(getTranslation(_0x210e8e(0x320)));},0x1);}break;case _0x1c0ed5(0x381):log(_0x1c0ed5(0x773)),clearTimeout(_0x311669[_0x1c0ed5(0x859)][_0x2551e1]['closeTimeout']),_0x311669[_0x1c0ed5(0x859)][_0x2551e1][_0x1c0ed5(0x9a4)]=setTimeout(function(_0x46ec55){var _0x5235e4=_0x1c0ed5;_0x46ec55 in _0x311669['pcs']?(warnlog(_0x5235e4(0x2cd)),log(_0x5235e4(0x475)),_0x311669['closePC'](_0x46ec55)):errorlog(_0x5235e4(0x7b2));},0x2710,_0x2551e1);break;case _0x1c0ed5(0x190):warnlog(_0x1c0ed5(0x4d2)),clearTimeout(_0x311669[_0x1c0ed5(0x859)][_0x2551e1]['closeTimeout']),warnlog(_0x1c0ed5(0x376)+_0x311669[_0x1c0ed5(0x859)][_0x2551e1][_0x1c0ed5(0x3ee)]),_0x311669[_0x1c0ed5(0x859)][_0x2551e1]['closeTimeout']=setTimeout(function(_0x235d77){var _0xa5f4f2=_0x1c0ed5;_0x235d77 in _0x311669[_0xa5f4f2(0x859)]?(warnlog(_0xa5f4f2(0x2cd)),log(_0xa5f4f2(0x2bf)),_0x311669[_0xa5f4f2(0x9d2)](_0x235d77)):errorlog('\x20---\x20PC\x20TIMED\x20OUT\x20and\x20already\x20deleted.\x20shouldn\x27t\x20happen');},0xbb8,_0x2551e1);break;case _0x1c0ed5(0x208):warnlog(_0x1c0ed5(0x3dc)),log(_0x1c0ed5(0x9c4)),_0x311669[_0x1c0ed5(0x9d2)](_0x2551e1);break;default:log('rtc\x20state:\x20'+_0x311669['pcs'][_0x2551e1][_0x1c0ed5(0x3ee)]),clearTimeout(_0x311669[_0x1c0ed5(0x859)][_0x2551e1][_0x1c0ed5(0x9a4)]);break;}},_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x661)]=function(_0x48b2d4){var _0x216f90=_0x42f4f2;warnlog(_0x216f90(0x26b)),log(_0x216f90(0x588)),_0x311669[_0x216f90(0x9d2)](_0x2551e1);},_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x777)]=function _0xd893cd(){var _0x1a0c8e=_0x42f4f2;log(_0x1a0c8e(0x809));};},_0x311669[_0x134a17(0x170)]=function(_0x3b1292){var _0x5914c0=_0x134a17,_0x505cd1=_0x3b1292[_0x5914c0(0x54a)];if(_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x78a)]==_0x5914c0(0x20f))_0x311669[_0x5914c0(0x679)](_0x3b1292),_0x311669[_0x5914c0(0x2bb)](_0x3b1292);else try{if(!(_0x3b1292[_0x5914c0(0x54a)]in _0x311669[_0x5914c0(0x859)]))return;var _0x2c82b7=_0x311669[_0x5914c0(0x84c)];if(_0x311669[_0x5914c0(0x1e3)]&&_0x311669[_0x5914c0(0x859)][_0x3b1292['UUID']]['guest']==!![]&&_0x311669[_0x5914c0(0x859)][_0x3b1292['UUID']][_0x5914c0(0x920)]==![]){if(_0x2c82b7===![]||_0x2c82b7>_0x311669[_0x5914c0(0xa00)]){var _0x260592=Object['keys'](_0x311669[_0x5914c0(0x859)])['length'];if(_0x311669[_0x5914c0(0x5f6)])_0x2c82b7=_0x311669[_0x5914c0(0xa00)];else{if(_0x260592>0x4)_0x2c82b7=_0x311669['lowMobileBitrate'];else(iOS||iPad)&&SafariVersion&&SafariVersion<=0xd?_0x2c82b7=_0x311669[_0x5914c0(0x6c1)]:_0x2c82b7=_0x311669[_0x5914c0(0xa00)];}}if(iOS||iPad){if(_0x2c82b7!==![]){if(_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x969)]===![])_0x311669[_0x5914c0(0x859)][_0x3b1292['UUID']][_0x5914c0(0x9f3)]=_0x2c82b7,_0x3b1292['description'][_0x5914c0(0x648)]=CodecsHandler[_0x5914c0(0x37c)](_0x3b1292['description'][_0x5914c0(0x648)],'vp8'),_0x3b1292[_0x5914c0(0x9f5)]['sdp']=CodecsHandler[_0x5914c0(0x5bb)](_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)],{'min':parseInt(_0x2c82b7/0xa)||0x1,'max':_0x2c82b7});else _0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]]['savedBitrate']>_0x2c82b7&&(_0x311669['pcs'][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x9f3)]=_0x2c82b7,_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)]=CodecsHandler[_0x5914c0(0x37c)](_0x3b1292['description'][_0x5914c0(0x648)],'vp8'),_0x3b1292[_0x5914c0(0x9f5)]['sdp']=CodecsHandler[_0x5914c0(0x5bb)](_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)],{'min':parseInt(_0x2c82b7/0xa)||0x1,'max':_0x2c82b7}));_0x2c82b7=![];}}}else{if(_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x798)]==!![])_0x2c82b7!==![]?_0x311669[_0x5914c0(0x250)]!==![]&&(_0x311669[_0x5914c0(0x250)]<_0x2c82b7&&(_0x2c82b7=_0x311669['roombitrate'])):_0x2c82b7=_0x311669[_0x5914c0(0x250)],(iOS||iPad)&&_0x311669['pcs'][_0x3b1292['UUID']][_0x5914c0(0x920)]&&(_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]]['encoder']=!![]);else{if(iOS||iPad){var _0x24631b=0x0;for(var _0x33ac31 in _0x311669['pcs']){_0x3b1292[_0x5914c0(0x54a)]!==_0x33ac31&&(_0x311669[_0x5914c0(0x859)][_0x33ac31][_0x5914c0(0x27c)]===!![]&&(_0x24631b+=0x1));}if(_0x24631b>=0x3){if(_0x311669[_0x5914c0(0x859)][_0x3b1292['UUID']][_0x5914c0(0x920)])_0x311669['pcs'][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x27c)]=!![],_0x311669[_0x5914c0(0x859)][_0x3b1292['UUID']][_0x5914c0(0x3aa)]&&_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]===_0x5914c0(0x57e)&&(_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)]=CodecsHandler['preferCodec'](_0x3b1292['description'][_0x5914c0(0x648)],_0x5914c0(0x57e)),log(_0x5914c0(0x5fe)+_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]+_0x5914c0(0x685)));else _0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]]['preferVideoCodec']&&_0x311669['pcs'][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]===_0x5914c0(0x609)?(_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)]=CodecsHandler[_0x5914c0(0x37c)](_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)],_0x5914c0(0x609)),log('Trying\x20to\x20set\x20'+_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]+_0x5914c0(0x685)),_0x311669['pcs'][_0x3b1292['UUID']][_0x5914c0(0x27c)]=![]):(_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)]=CodecsHandler[_0x5914c0(0x37c)](_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)],_0x5914c0(0x5de)),log('Setting\x20Codec\x20to\x20vp8'),_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]]['encoder']=![]);}else _0x311669['pcs'][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]&&_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]!==_0x5914c0(0x57e)?_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]]['preferVideoCodec']===_0x5914c0(0x609)||_0x311669['pcs'][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]===_0x5914c0(0x5de)?(_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)]=CodecsHandler['preferCodec'](_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)],_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]]['preferVideoCodec']),log(_0x5914c0(0x5fe)+_0x311669['pcs'][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]+'\x20as\x20preferred\x20codec\x20by\x20viewer\x20via\x20API'),_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]]['encoder']=![]):_0x311669['pcs'][_0x3b1292['UUID']][_0x5914c0(0x27c)]=!![]:(_0x311669['pcs'][_0x3b1292['UUID']]['encoder']=!![],_0x311669['pcs'][_0x3b1292['UUID']][_0x5914c0(0x3aa)]&&_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]==='h264'&&(_0x3b1292['description'][_0x5914c0(0x648)]=CodecsHandler[_0x5914c0(0x37c)](_0x3b1292['description'][_0x5914c0(0x648)],'h264'),log(_0x5914c0(0x5fe)+_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]+_0x5914c0(0x685))));}else _0x311669['pcs'][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]&&(_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)]=CodecsHandler['preferCodec'](_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)],_0x311669['pcs'][_0x3b1292['UUID']]['preferVideoCodec']),log(_0x5914c0(0x5fe)+_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]+_0x5914c0(0x685)));}}if(_0x2c82b7){var _0x256552=CodecsHandler['getVideoBitrates'](_0x3b1292['description'][_0x5914c0(0x648)]);log('BITRATE\x201:\x20'+_0x256552);_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x969)]!==![]&&(_0x311669[_0x5914c0(0x859)][_0x3b1292['UUID']][_0x5914c0(0x969)]<_0x2c82b7&&(_0x2c82b7=![]));if(_0x2c82b7===![])_0x311669[_0x5914c0(0x859)][_0x3b1292['UUID']]['setBitrate']=_0x256552;else{if(_0x256552!==![]&&_0x256552>_0x2c82b7){var _0x60f23a=CodecsHandler[_0x5914c0(0x580)](_0x3b1292['description'][_0x5914c0(0x648)])||0x0;_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)]=CodecsHandler[_0x5914c0(0x5bb)](_0x3b1292[_0x5914c0(0x9f5)]['sdp'],{'min':parseInt(_0x2c82b7/0xa)||0x1,'max':parseInt(_0x2c82b7+_0x60f23a/0x400)}),_0x311669[_0x5914c0(0x859)][_0x3b1292['UUID']]['setBitrate']=_0x2c82b7;}else{if(_0x256552===![]){var _0x60f23a=CodecsHandler[_0x5914c0(0x580)](_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)])||0x0;_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)]=CodecsHandler[_0x5914c0(0x5bb)](_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)],{'min':parseInt(_0x2c82b7/0xa)||0x1,'max':parseInt(_0x2c82b7+_0x60f23a/0x400)});if(_0x311669['outboundVideoBitrate']&&_0x311669['outboundVideoBitrate']>_0x2c82b7)_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]]['setBitrate']=_0x2c82b7;else _0x311669[_0x5914c0(0x810)]?_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]]['setBitrate']=_0x311669[_0x5914c0(0x810)]:_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x969)]=0x9c4;}else _0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x9f3)]=_0x256552;}}}else{if(_0x311669[_0x5914c0(0x810)]!==![]){var _0x256552=CodecsHandler[_0x5914c0(0x5f1)](_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)]);log(_0x5914c0(0x314)+_0x256552);if(_0x256552===![]){var _0x60f23a=CodecsHandler[_0x5914c0(0x580)](_0x3b1292['description'][_0x5914c0(0x648)])||0x0;_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)]=CodecsHandler['setVideoBitrates'](_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)],{'min':parseInt(_0x311669[_0x5914c0(0x810)]/0xa)||0x1,'max':parseInt(_0x311669[_0x5914c0(0x810)]+_0x60f23a/0x400)});}else _0x311669['pcs'][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x9f3)]===![]&&(_0x311669[_0x5914c0(0x859)][_0x3b1292['UUID']][_0x5914c0(0x9f3)]=_0x256552);}else _0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x9f3)]===![]&&(_0x311669['pcs'][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x9f3)]=CodecsHandler[_0x5914c0(0x5f1)](_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)]),log(_0x5914c0(0x9ce)+_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]]['setBitrate']));}_0x311669[_0x5914c0(0x28e)]&&(_0x3b1292['description']['sdp']=CodecsHandler[_0x5914c0(0x608)](_0x3b1292['description'][_0x5914c0(0x648)],{'maxaveragebitrate':_0x311669[_0x5914c0(0x28e)]*0x400,'cbr':_0x311669['cbr']}));if(_0x5914c0(0x1fd)in _0x3b1292&&_0x3b1292[_0x5914c0(0x1fd)]!=_0x311669['pcs'][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x1fd)]){errorlog(_0x5914c0(0x6eb));return;}_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]]['setRemoteDescription'](_0x3b1292[_0x5914c0(0x9f5)])[_0x5914c0(0x619)]()['catch'](errorlog);}catch(_0x4b4ede){errorlog(_0x4b4ede);}},_0x311669['processDescription']=function(_0x8a319e){var _0x3369d7=_0x134a17;_0x311669[_0x3369d7(0x8a3)]&&_0x3369d7(0x9ed)in _0x8a319e?_0x311669[_0x3369d7(0x4da)](_0x8a319e[_0x3369d7(0x9f5)],_0x8a319e[_0x3369d7(0x9ed)])[_0x3369d7(0x619)](function(_0x271811){var _0x240d90=_0x3369d7;_0x8a319e[_0x240d90(0x9f5)]=JSON[_0x240d90(0x52d)](_0x271811),_0x311669[_0x240d90(0x170)](_0x8a319e);}):_0x311669[_0x3369d7(0x170)](_0x8a319e);},_0x311669[_0x134a17(0x1d3)]=function(_0x49d286){var _0x2f175f=_0x134a17;_0x311669['password']&&_0x2f175f(0x9ed)in _0x49d286?_0x311669[_0x2f175f(0x4da)](_0x49d286[_0x2f175f(0x905)],_0x49d286[_0x2f175f(0x9ed)])[_0x2f175f(0x619)](function(_0x352207){var _0x52647c=_0x2f175f;_0x49d286['candidate']=JSON[_0x52647c(0x52d)](_0x352207),_0x311669[_0x52647c(0xa09)](_0x49d286);}):_0x311669[_0x2f175f(0xa09)](_0x49d286);},_0x311669[_0x134a17(0xa09)]=function(_0x28e3fa){var _0x1318e4=_0x134a17;try{if(_0x311669[_0x1318e4(0x242)]){if(_0x28e3fa[_0x1318e4(0x905)][_0x1318e4(0x905)][_0x1318e4(0x49e)](_0x311669[_0x1318e4(0x242)])===-0x1){log(_0x1318e4(0x6f4)),log(_0x28e3fa[_0x1318e4(0x905)]);return;}else log('PASSED'),log(_0x28e3fa[_0x1318e4(0x905)]);}}catch(_0xc02c6f){errorlog(_0xc02c6f);}if(_0x28e3fa[_0x1318e4(0x905)]&&_0x1318e4(0x905)in _0x28e3fa['candidate']&&_0x28e3fa[_0x1318e4(0x905)]['candidate']=='')return;if(_0x28e3fa[_0x1318e4(0x54a)]in _0x311669['pcs']&&_0x28e3fa[_0x1318e4(0x78a)]==_0x1318e4(0x24d)){log(_0x1318e4(0x6a3));if(_0x1318e4(0x1fd)in _0x28e3fa&&_0x311669[_0x1318e4(0x859)][_0x28e3fa['UUID']]['session']!=_0x28e3fa[_0x1318e4(0x1fd)]){errorlog('Incoming\x20Ice\x20Offer\x20does\x20not\x20match\x20Session');return;}_0x311669[_0x1318e4(0x859)][_0x28e3fa[_0x1318e4(0x54a)]]['addIceCandidate'](_0x28e3fa[_0x1318e4(0x905)])[_0x1318e4(0x619)]()[_0x1318e4(0x3b6)](function(_0x5c5a46){});}else{if(_0x28e3fa[_0x1318e4(0x54a)]in _0x311669[_0x1318e4(0x16a)]&&_0x28e3fa['type']==_0x1318e4(0x628)){log(_0x1318e4(0x383));if(_0x1318e4(0x1fd)in _0x28e3fa&&_0x311669[_0x1318e4(0x16a)][_0x28e3fa[_0x1318e4(0x54a)]][_0x1318e4(0x1fd)]!=_0x28e3fa[_0x1318e4(0x1fd)]){errorlog('Incoming\x20Ice\x20Offer\x20does\x20not\x20match\x20Session');return;}if(_0x311669[_0x1318e4(0x16a)][_0x28e3fa[_0x1318e4(0x54a)]]===null)return;_0x311669[_0x1318e4(0x16a)][_0x28e3fa['UUID']][_0x1318e4(0x8f4)](_0x28e3fa[_0x1318e4(0x905)])[_0x1318e4(0x619)]()['catch'](function(_0x19f1b9){});}else warnlog(_0x28e3fa),errorlog('ICE\x20DID\x20NOT\x20FIND\x20A\x20PC\x20OPTION?\x20peer\x20might\x20have\x20left\x20before\x20ICE\x20complete?');}},_0x311669[_0x134a17(0x411)]=function(_0x5dcd6d){var _0x397e37=_0x134a17;if(_0x311669[_0x397e37(0x8a3)]&&_0x397e37(0x9ed)in _0x5dcd6d)_0x311669['decryptMessage'](_0x5dcd6d[_0x397e37(0x2a8)],_0x5dcd6d['vector'])[_0x397e37(0x619)](function(_0x4fcd6d){var _0x210b9c=_0x397e37;_0x5dcd6d[_0x210b9c(0x2a8)]=JSON[_0x210b9c(0x52d)](_0x4fcd6d);var _0x483174={};_0x483174[_0x210b9c(0x54a)]=_0x5dcd6d['UUID'],_0x483174[_0x210b9c(0x78a)]=_0x5dcd6d[_0x210b9c(0x78a)];for(var _0x1923a2=0x0;_0x1923a2<_0x5dcd6d[_0x210b9c(0x2a8)][_0x210b9c(0x8e9)];_0x1923a2++){_0x483174[_0x210b9c(0x905)]=_0x5dcd6d[_0x210b9c(0x2a8)][_0x1923a2],_0x311669['processIce2'](_0x483174);}});else{var _0x11986f={};_0x11986f[_0x397e37(0x54a)]=_0x5dcd6d['UUID'],_0x11986f[_0x397e37(0x78a)]=_0x5dcd6d[_0x397e37(0x78a)];for(var _0x17808e=0x0;_0x17808e<_0x5dcd6d[_0x397e37(0x2a8)][_0x397e37(0x8e9)];_0x17808e++){_0x11986f['candidate']=_0x5dcd6d[_0x397e37(0x2a8)][_0x17808e],_0x311669['processIce2'](_0x11986f);}}},_0x311669[_0x134a17(0x2bb)]=async function(_0x3da073){var _0x2e344a=_0x134a17;_0x2e344a(0x43e)in _0x3da073&&(_0x311669[_0x2e344a(0x16a)][_0x3da073[_0x2e344a(0x54a)]][_0x2e344a(0x2ef)]=_0x3da073[_0x2e344a(0x43e)],log(_0x2e344a(0x722)),log(_0x3da073[_0x2e344a(0x43e)])),warnlog(_0x3da073),_0x311669[_0x2e344a(0x971)]&&_0x3da073[_0x2e344a(0x9f5)]&&_0x3da073[_0x2e344a(0x9f5)]['sdp']&&_0x3da073[_0x2e344a(0x9f5)][_0x2e344a(0x648)]['includes']('a=extmap:3\x20urn:3gpp:video-orientation\x0d\x0a')&&(_0x3da073[_0x2e344a(0x9f5)][_0x2e344a(0x648)]=_0x3da073[_0x2e344a(0x9f5)][_0x2e344a(0x648)]['replace']('a=extmap:3\x20urn:3gpp:video-orientation\x0d\x0a',''),warnlog(_0x2e344a(0x7e7))),_0x311669[_0x2e344a(0xa5e)]&&(_0x3da073[_0x2e344a(0x9f5)]['sdp']=CodecsHandler[_0x2e344a(0x9d8)](_0x3da073[_0x2e344a(0x9f5)]['sdp'])),_0x311669['noREMB']&&(_0x3da073[_0x2e344a(0x9f5)]['sdp']=CodecsHandler[_0x2e344a(0x5d0)](_0x3da073['description']['sdp'])),_0x311669[_0x2e344a(0x7be)]&&(log(_0x3da073[_0x2e344a(0x9f5)][_0x2e344a(0x648)]),_0x3da073[_0x2e344a(0x9f5)][_0x2e344a(0x648)]=CodecsHandler['disableNACK'](_0x3da073[_0x2e344a(0x9f5)][_0x2e344a(0x648)])),_0x311669[_0x2e344a(0x16a)][_0x3da073[_0x2e344a(0x54a)]][_0x2e344a(0x5fb)](_0x3da073['description'])['then'](async function(){var _0x1a87bb=_0x2e344a;if(_0x311669[_0x1a87bb(0x16a)][_0x3da073[_0x1a87bb(0x54a)]][_0x1a87bb(0x5b4)][_0x1a87bb(0x78a)]==='offer')_0x311669[_0x1a87bb(0x16a)][_0x3da073[_0x1a87bb(0x54a)]]['createAnswer']()[_0x1a87bb(0x619)](function(_0x5d8761){var _0xbe8017=_0x1a87bb;log(_0xbe8017(0x1ae));if(_0x311669['rpcs'][_0x3da073[_0xbe8017(0x54a)]][_0xbe8017(0x751)]){if(_0x311669['stereo']&&_0x311669[_0xbe8017(0x5a8)]==0x4)_0x5d8761['sdp']=CodecsHandler[_0xbe8017(0x608)](_0x5d8761[_0xbe8017(0x648)],{'stereo':0x2},!![]);else _0x311669[_0xbe8017(0x5a8)]&&!_0x311669[_0xbe8017(0x4cf)]&&_0x311669[_0xbe8017(0x5a8)]!=0x3&&(_0x5d8761[_0xbe8017(0x648)]=CodecsHandler[_0xbe8017(0x608)](_0x5d8761[_0xbe8017(0x648)],{'stereo':0x1},!![]));return _0x311669[_0xbe8017(0x16a)][_0x3da073[_0xbe8017(0x54a)]][_0xbe8017(0xa1b)](_0x5d8761);}var _0x36b338=![];if(!_0x311669[_0xbe8017(0x75b)]&&_0x311669[_0xbe8017(0x5a8)]==0x5)_0x36b338={'stereo':0x1,'maxaveragebitrate':(_0x311669[_0xbe8017(0x543)]||0x100)*0x400,'cbr':_0x311669[_0xbe8017(0x441)],'useinbandfec':_0x311669[_0xbe8017(0x5f2)]?0x0:0x1,'maxptime':_0x311669[_0xbe8017(0x5e9)],'minptime':_0x311669['minptime'],'ptime':_0x311669[_0xbe8017(0x72b)],'dtx':_0x311669[_0xbe8017(0x6c6)]},log(_0xbe8017(0x7eb));else{if(_0x311669[_0xbe8017(0x4cf)]&&Firefox)_0x311669[_0xbe8017(0x543)]?_0x36b338={'stereo':0x0,'maxaveragebitrate':_0x311669['audiobitrate']*0x400,'cbr':_0x311669['cbr'],'useinbandfec':_0x311669['noFEC']?0x0:0x1,'maxptime':_0x311669[_0xbe8017(0x5e9)],'minptime':_0x311669[_0xbe8017(0x38b)],'ptime':_0x311669['ptime'],'dtx':_0x311669[_0xbe8017(0x6c6)]}:_0x36b338={'stereo':0x0,'useinbandfec':_0x311669[_0xbe8017(0x5f2)]?0x0:0x1,'maxptime':_0x311669['maxptime'],'minptime':_0x311669[_0xbe8017(0x38b)],'ptime':_0x311669[_0xbe8017(0x72b)],'dtx':_0x311669[_0xbe8017(0x6c6)]};else{if(_0x311669['stereo']==0x1||_0x311669[_0xbe8017(0x5a8)]==0x2||_0x311669['stereo']==0x5)_0x36b338={'stereo':0x1,'maxaveragebitrate':(_0x311669[_0xbe8017(0x543)]||0x100)*0x400,'cbr':_0x311669[_0xbe8017(0x441)],'useinbandfec':_0x311669[_0xbe8017(0x5f2)]?0x0:0x1,'maxptime':_0x311669['maxptime'],'minptime':_0x311669[_0xbe8017(0x38b)],'ptime':_0x311669[_0xbe8017(0x72b)],'dtx':_0x311669[_0xbe8017(0x6c6)]},log(_0xbe8017(0x7eb));else{if(_0x311669[_0xbe8017(0x5a8)]==0x4)_0x36b338={'stereo':0x2,'maxaveragebitrate':(_0x311669[_0xbe8017(0x543)]||0x100)*0x400,'cbr':_0x311669[_0xbe8017(0x441)],'useinbandfec':_0x311669[_0xbe8017(0x5f2)]?0x0:0x1,'maxptime':_0x311669[_0xbe8017(0x5e9)],'minptime':_0x311669[_0xbe8017(0x38b)],'ptime':_0x311669[_0xbe8017(0x72b)],'dtx':_0x311669[_0xbe8017(0x6c6)]};else{if(_0x311669[_0xbe8017(0x543)])_0x36b338={'maxaveragebitrate':_0x311669[_0xbe8017(0x543)]*0x400,'cbr':_0x311669[_0xbe8017(0x441)],'useinbandfec':_0x311669['noFEC']?0x0:0x1,'maxptime':_0x311669['maxptime'],'minptime':_0x311669[_0xbe8017(0x38b)],'ptime':_0x311669[_0xbe8017(0x72b)],'dtx':_0x311669[_0xbe8017(0x6c6)]};else{if(_0x311669[_0xbe8017(0x5f2)])_0x36b338={'useinbandfec':0x0,'maxptime':_0x311669[_0xbe8017(0x5e9)],'minptime':_0x311669[_0xbe8017(0x38b)],'ptime':_0x311669[_0xbe8017(0x72b)],'dtx':_0x311669['dtx']};else _0x311669['dtx']&&(_0x36b338={'maxptime':_0x311669[_0xbe8017(0x5e9)],'minptime':_0x311669[_0xbe8017(0x38b)],'ptime':_0x311669[_0xbe8017(0x72b)],'dtx':_0x311669[_0xbe8017(0x6c6)]});}}}}}_0x311669[_0xbe8017(0x5a8)]===0x6&&(!_0x36b338?_0x36b338={'stereo':0x1}:_0x36b338['stereo']=0x1);_0x36b338&&(_0x5d8761[_0xbe8017(0x648)]=CodecsHandler[_0xbe8017(0x608)](_0x5d8761['sdp'],_0x36b338));if(_0x311669['audioCodec'])try{if(_0x311669[_0xbe8017(0x292)]===_0xbe8017(0x61d))_0x5d8761['sdp']=CodecsHandler['modifyDescLyra'](_0x5d8761['sdp']);else{if(_0x311669[_0xbe8017(0x292)]===_0xbe8017(0x944)){if(_0x311669[_0xbe8017(0x4cf)])_0x5d8761[_0xbe8017(0x648)]=CodecsHandler[_0xbe8017(0x230)](_0x5d8761[_0xbe8017(0x648)],_0x311669[_0xbe8017(0x228)]||0xbb80,![],_0x311669[_0xbe8017(0x72b)]);else _0x311669[_0xbe8017(0x5a8)]?_0x5d8761[_0xbe8017(0x648)]=CodecsHandler[_0xbe8017(0x230)](_0x5d8761['sdp'],_0x311669[_0xbe8017(0x228)]||0x7d00,!![],_0x311669[_0xbe8017(0x72b)]):_0x5d8761[_0xbe8017(0x648)]=CodecsHandler[_0xbe8017(0x230)](_0x5d8761[_0xbe8017(0x648)],_0x311669['sampleRate']||0xbb80,![],_0x311669['ptime']);}else _0x5d8761[_0xbe8017(0x648)]=CodecsHandler[_0xbe8017(0x5ea)](_0x5d8761[_0xbe8017(0x648)],_0x311669[_0xbe8017(0x292)]);}}catch(_0x3cca35){errorlog(_0x3cca35),warnlog(_0xbe8017(0x57a));}if(_0x311669[_0xbe8017(0x4e7)]&&_0x311669[_0xbe8017(0x4e7)][_0xbe8017(0x8e9)])for(var _0x25ee0d=_0x311669[_0xbe8017(0x4e7)][_0xbe8017(0x8e9)]-0x1;_0x25ee0d>=0x0;_0x25ee0d--){try{_0x5d8761['sdp']=CodecsHandler[_0xbe8017(0x37c)](_0x5d8761[_0xbe8017(0x648)],_0x311669[_0xbe8017(0x4e7)][_0x25ee0d]);}catch(_0x21a1c1){errorlog(_0x21a1c1);break;}}_0x311669[_0xbe8017(0x24e)]&&(_0x5d8761['sdp']=CodecsHandler['preferCodec'](_0x5d8761[_0xbe8017(0x648)],_0x311669[_0xbe8017(0x24e)]));_0x311669['h264profile']&&(log(_0xbe8017(0x821)),_0x5d8761[_0xbe8017(0x648)]=_0x5d8761[_0xbe8017(0x648)][_0xbe8017(0x2f9)](/42e01f/gi,_0x311669[_0xbe8017(0x6d0)]),_0x5d8761[_0xbe8017(0x648)]=_0x5d8761[_0xbe8017(0x648)][_0xbe8017(0x2f9)](/42001f/gi,_0x311669[_0xbe8017(0x6d0)]),_0x5d8761[_0xbe8017(0x648)]=_0x5d8761[_0xbe8017(0x648)][_0xbe8017(0x2f9)](/420029/gi,_0x311669['h264profile']),_0x5d8761[_0xbe8017(0x648)]=_0x5d8761[_0xbe8017(0x648)]['replace'](/42a01e/gi,_0x311669[_0xbe8017(0x6d0)]),_0x5d8761[_0xbe8017(0x648)]=_0x5d8761[_0xbe8017(0x648)][_0xbe8017(0x2f9)](/42a014/gi,_0x311669[_0xbe8017(0x6d0)]),_0x5d8761[_0xbe8017(0x648)]=_0x5d8761[_0xbe8017(0x648)]['replace'](/42a00b/gi,_0x311669[_0xbe8017(0x6d0)]),_0x5d8761[_0xbe8017(0x648)]=_0x5d8761['sdp']['replace'](/640c1f/gi,_0x311669[_0xbe8017(0x6d0)]));_0x311669['noPLIs']&&(_0x5d8761[_0xbe8017(0x648)]=CodecsHandler[_0xbe8017(0x9d8)](_0x5d8761[_0xbe8017(0x648)]));_0x311669[_0xbe8017(0x65e)]&&(_0x5d8761[_0xbe8017(0x648)]=CodecsHandler['disableREMB'](_0x5d8761[_0xbe8017(0x648)]));_0x311669[_0xbe8017(0x7be)]&&(log(_0x5d8761[_0xbe8017(0x648)]),_0x5d8761[_0xbe8017(0x648)]=CodecsHandler[_0xbe8017(0x9e1)](_0x5d8761[_0xbe8017(0x648)]));if(_0x311669[_0xbe8017(0x16a)][_0x3da073[_0xbe8017(0x54a)]][_0xbe8017(0x3e4)])log(_0xbe8017(0x1f9)),_0x5d8761[_0xbe8017(0x648)]=_0x300658(_0x5d8761['sdp'],_0x311669[_0xbe8017(0x16a)][_0x3da073['UUID']][_0xbe8017(0x3e4)]);else _0x311669[_0xbe8017(0x739)]&&(log(_0xbe8017(0x1f9)),_0x5d8761['sdp']=_0x300658(_0x5d8761[_0xbe8017(0x648)],_0x311669[_0xbe8017(0x739)]));return log(_0x5d8761),_0x311669[_0xbe8017(0x16a)][_0x3da073['UUID']][_0xbe8017(0xa1b)](_0x5d8761);})[_0x1a87bb(0x619)](function _0x1875b1(){var _0xb51792=_0x1a87bb;log(_0xb51792(0x2b5));if(_0x311669['rpcs'][_0x3da073[_0xb51792(0x54a)]][_0xb51792(0x751)]){_0x311669['rpcs'][_0x3da073[_0xb51792(0x54a)]]['whipCallback']&&_0x311669['rpcs'][_0x3da073[_0xb51792(0x54a)]][_0xb51792(0x87c)]();return;}var _0x3eba1f={};_0x3eba1f[_0xb51792(0x54a)]=_0x3da073[_0xb51792(0x54a)],_0x3eba1f[_0xb51792(0x9f5)]=_0x311669[_0xb51792(0x16a)][_0x3da073[_0xb51792(0x54a)]]['localDescription'],_0x3eba1f['session']=_0x311669['rpcs'][_0x3da073[_0xb51792(0x54a)]]['session'],_0x311669[_0xb51792(0x8a3)]?_0x311669[_0xb51792(0x331)](JSON[_0xb51792(0x479)](_0x3eba1f['description']))['then'](function(_0x2e41c0){var _0x49e324=_0xb51792;_0x3eba1f[_0x49e324(0x9f5)]=_0x2e41c0[0x0],_0x3eba1f[_0x49e324(0x9ed)]=_0x2e41c0[0x1],_0x311669[_0x49e324(0x668)](_0x3eba1f);})[_0xb51792(0x3b6)](errorlog):_0x311669[_0xb51792(0x668)](_0x3eba1f);})[_0x1a87bb(0x3b6)](errorlog);else _0x311669[_0x1a87bb(0x16a)][_0x3da073[_0x1a87bb(0x54a)]]['remoteDescription'][_0x1a87bb(0x78a)]===_0x1a87bb(0x73b)&&errorlog(_0x1a87bb(0x672));})[_0x2e344a(0x3b6)](errorlog);},_0x311669[_0x134a17(0x700)]=function(){var _0x3a98d8=_0x134a17;if(_0x311669[_0x3a98d8(0x49b)]&&_0x311669[_0x3a98d8(0x49b)]['srcObject'])return _0x311669[_0x3a98d8(0x49b)][_0x3a98d8(0x5c4)];else return _0x311669[_0x3a98d8(0x49b)]&&_0x311669[_0x3a98d8(0x49b)]['src']&&_0x311669[_0x3a98d8(0x585)]?_0x311669[_0x3a98d8(0x585)]:(log(_0x3a98d8(0x562)),checkBasicStreamsExist(),_0x311669[_0x3a98d8(0x49b)]['srcObject']);};var _0xe532b8={},_0x1e48c9=![],_0x48cd13=[];_0x311669[_0x134a17(0x8ca)]=function(_0x2161ee,_0x28518f){var _0x32c845=_0x134a17;log(_0x32c845(0xa6a)+_0x28518f+'\x20'+_0x2161ee);var _0x3ff547=new FileReader(),_0x3bc7fb=![];for(var _0x2c3bb6=0x0;_0x2c3bb6<_0x311669[_0x32c845(0x972)][_0x32c845(0x8e9)];_0x2c3bb6++){if(_0x311669[_0x32c845(0x972)][_0x2c3bb6]['id']===_0x28518f){_0x3bc7fb=_0x2c3bb6;break;}}if(_0x3bc7fb===![]){warnlog(_0x32c845(0x651));return;}else{if(_0x311669[_0x32c845(0x972)][_0x3bc7fb][_0x32c845(0x5ae)]==0x0){warnlog(_0x32c845(0xa85));return;}else{if(!(_0x311669['hostedFiles'][_0x3bc7fb][_0x32c845(0x474)]===![]||_0x311669[_0x32c845(0x972)][_0x3bc7fb][_0x32c845(0x474)]===_0x2161ee)){warnlog(_0x32c845(0x205));return;}}}var _0x3d9cc8=0x4000,_0x335c5f=0x0,_0x43664d=_0x3bc7fb;_0x43664d===_0x32c845(0x73a)&&(_0x43664d=_0x32c845(0x22f)+_0x311669[_0x32c845(0xa76)](0x5));if(_0x2161ee in _0x311669['pcs'])var _0x4e9580=_0x311669[_0x32c845(0x859)][_0x2161ee][_0x32c845(0x1e1)](_0x43664d);else{if(_0x2161ee in _0x311669[_0x32c845(0x16a)])var _0x4e9580=_0x311669['rpcs'][_0x2161ee]['createDataChannel'](_0x43664d);else{warnlog('UUID\x20does\x20not\x20exist');return;}}_0x4e9580['binaryType']='arraybuffer';var _0x400903=_0x311669[_0x32c845(0x972)][_0x3bc7fb]['slice'](0x0,_0x3d9cc8);_0x4e9580[_0x32c845(0x777)]=()=>{var _0x40488f=_0x32c845;_0x4e9580['send'](JSON[_0x40488f(0x479)]({'type':_0x40488f(0x80e),'size':_0x311669[_0x40488f(0x972)][_0x3bc7fb][_0x40488f(0x8cf)],'filename':_0x311669[_0x40488f(0x972)][_0x3bc7fb][_0x40488f(0x6dd)],'id':_0x311669[_0x40488f(0x972)][_0x3bc7fb]['id']})),_0x3ff547[_0x40488f(0x745)](_0x400903);},_0x4e9580['onclose']=()=>{var _0x410195=_0x32c845;try{var _0x4b25cb=_0x311669['hostedTransfers']['indexOf'](_0x4e9580);_0x4b25cb>-0x1&&_0x311669[_0x410195(0x8b5)][_0x410195(0x48c)](_0x4b25cb,0x1);}catch(_0x21ff48){errorlog(_0x21ff48);}log('Transfer\x20ended'),_0x4e9580=null;},_0x4e9580[_0x32c845(0x17a)]=_0x479241=>{},_0x311669['hostedTransfers'][_0x32c845(0x505)](_0x4e9580),_0x3ff547[_0x32c845(0x19f)]=function(){var _0x541529=_0x32c845;if(_0x311669[_0x541529(0x972)][_0x3bc7fb][_0x541529(0x5ae)]==0x0)return;var _0x50c794=_0x3ff547[_0x541529(0x8e8)];log(_0x50c794);try{_0x4e9580['send'](_0x50c794);}catch(_0x1121e2){try{_0x4e9580['close']();}catch(_0x145ecd){}warnlog(_0x1121e2);return;}_0x335c5f+=0x1;if(_0x335c5f*_0x3d9cc8<_0x311669['hostedFiles'][_0x3bc7fb]['size'])try{log(_0x541529(0x4ee)+_0x335c5f),_0x400903=_0x311669['hostedFiles'][_0x3bc7fb][_0x541529(0x7f5)](_0x335c5f*_0x3d9cc8,(_0x335c5f+0x1)*_0x3d9cc8),_0x3ff547[_0x541529(0x745)](_0x400903);}catch(_0x4d202e){errorlog(_0x4d202e);}else _0x4e9580[_0x541529(0x2b9)]('EOF1'),_0x4e9580[_0x541529(0x1cf)]();};},_0x311669[_0x134a17(0x29e)]=null,_0x311669[_0x134a17(0x564)]=null,_0x311669[_0x134a17(0x630)]=async function(_0x385f51=null){var _0x2a6737=_0x134a17;if(_0x311669['chunkedVideoEnabled']!==null)return;else _0x311669[_0x2a6737(0x29e)]=![];!_0x385f51&&_0x311669[_0x2a6737(0x436)]['Chunked_video']&&(_0x385f51=_0x311669[_0x2a6737(0x436)][_0x2a6737(0x6cf)]);let _0x2a6630=0x0;var _0x536df0=_0x311669[_0x2a6737(0x585)][_0x2a6737(0x87a)]()[0x0];if(!_0x536df0){_0x311669[_0x2a6737(0x29e)]=null;return;}var _0x4222bf=new MediaStreamTrackProcessor(_0x536df0),_0x449a5a=_0x4222bf[_0x2a6737(0x4aa)];const _0x7034b4=_0x449a5a[_0x2a6737(0xa36)]();var _0x31022e=![],_0x2e1081=-0x1,_0x1ef9ee=-0x1;const _0x422e0b={'output':_0xebe3ba=>{var _0x153a4e=_0x2a6737;if(_0xebe3ba[_0x153a4e(0x611)][_0x153a4e(0x6dd)]==_0x153a4e(0x17c)){let _0x5b3e12=new Uint8Array(_0xebe3ba[_0x153a4e(0x7ce)]);_0xebe3ba[_0x153a4e(0x423)](_0x5b3e12),_0x48cd13[_0x153a4e(0x505)]([_0xebe3ba['timestamp']-_0x1ef9ee,_0xebe3ba['type']]),_0x48cd13[_0x153a4e(0x505)](_0x5b3e12),_0x1e48c9[_0x153a4e(0x64b)]('video');}},'error':_0x414d73=>{errorlog(_0x414d73);}};let _0x4ff59f=new VideoEncoder(_0x422e0b);_0x4ff59f[_0x2a6737(0x50e)](_0x385f51),_0x311669[_0x2a6737(0x436)][_0x2a6737(0x6cf)]=_0x385f51,_0x1e48c9[_0x2a6737(0x639)]=_0x4ff59f;var _0x51d30a,_0x2c7a4a=new Promise((_0x4de9a2,_0x1b23d7)=>{_0x51d30a=_0x4de9a2;});_0x2c7a4a['resolve']=_0x51d30a,_0x7034b4[_0x2a6737(0x829)]()[_0x2a6737(0x619)](function _0x1d4a09({done:_0x30d6a7,value:_0x3028aa}){var _0x36e17e=_0x2a6737;if(_0x30d6a7||_0x31022e){_0x4ff59f[_0x36e17e(0x1cf)]();_0x3028aa&&_0x3028aa[_0x36e17e(0x1cf)]();_0x311669[_0x36e17e(0x29e)]=null;return;}_0x1ef9ee==-0x1&&(_0x1ef9ee=_0x3028aa[_0x36e17e(0x82c)],_0x311669[_0x36e17e(0x436)]['Chunked_video'][_0x36e17e(0x3c5)]=Date[_0x36e17e(0x610)](),_0x2c7a4a[_0x36e17e(0x8c5)]());_0x2e1081==_0x3028aa['timestamp']&&(_0x3028aa[_0x36e17e(0x82c)]+=0x1,warnlog('Timestamp\x20duplicated'));if(!_0x31022e){_0x2e1081=_0x3028aa[_0x36e17e(0x82c)],_0x2a6630++;if(_0x1e48c9[_0x36e17e(0x7fa)]){const _0x535c4a=_0x2a6630>=0x3c;_0x535c4a&&(_0x2a6630=0x0,_0x1e48c9['needKeyFrame']=![],warnlog(_0x36e17e(0x590))),_0x4ff59f['encode'](_0x3028aa,{'keyFrame':_0x535c4a});}else _0x4ff59f[_0x36e17e(0x9d3)](_0x3028aa,{'keyFrame':![]});}_0x3028aa[_0x36e17e(0x1cf)](),_0x7034b4[_0x36e17e(0x829)]()[_0x36e17e(0x619)](_0x1d4a09);}),_0x311669['chunkedVideoEnabled']=!![],await _0x2c7a4a;},_0x311669[_0x134a17(0x9df)]=async function(_0x4a0d71){var _0x290766=_0x134a17;if(_0x311669['chunkedAudioEnabled']!==null)return;else _0x311669['chunkedAudioEnabled']=![];!_0x4a0d71&&_0x311669['stats'][_0x290766(0x520)]&&(_0x4a0d71=_0x311669[_0x290766(0x436)][_0x290766(0x520)]);var _0x64370b=_0x311669['videoElement'][_0x290766(0x5c4)][_0x290766(0xa6c)]()[0x0];if(!_0x64370b){_0x311669[_0x290766(0x564)]=null;return;}var _0x3069c8=_0x64370b[_0x290766(0x2e2)]();_0x4a0d71[_0x290766(0x512)]>_0x3069c8[_0x290766(0x2b2)]&&(_0x4a0d71[_0x290766(0x512)]=_0x3069c8[_0x290766(0x2b2)],_0x4a0d71[_0x290766(0x306)]=_0x3069c8['channelCount']);_0x4a0d71[_0x290766(0x228)]>_0x3069c8[_0x290766(0x228)]&&(_0x4a0d71[_0x290766(0x228)]=_0x3069c8[_0x290766(0x228)]);var _0x5f2164=new MediaStreamTrackProcessor(_0x64370b),_0x8d455b=_0x5f2164[_0x290766(0x4aa)];const _0x52b552=_0x8d455b[_0x290766(0xa36)]();var _0x2bba8b=![],_0x20b4ef=-0x1,_0x56fdcb=-0x1;const _0x1fad7f={'output':_0xad76a1=>{var _0xa29337=_0x290766;if(_0xad76a1[_0xa29337(0x611)][_0xa29337(0x6dd)]==_0xa29337(0x69f)){let _0x43300d=new Uint8Array(_0xad76a1[_0xa29337(0x7ce)]);_0xad76a1['copyTo'](_0x43300d),_0x48cd13[_0xa29337(0x505)]([_0xad76a1[_0xa29337(0x82c)]-_0x56fdcb,_0xa29337(0x433)]),_0x48cd13[_0xa29337(0x505)](_0x43300d),_0x1e48c9[_0xa29337(0x64b)](_0xa29337(0x433));}},'error':_0x39eae7=>{errorlog(_0x39eae7);}};let _0x420f58=new AudioEncoder(_0x1fad7f);_0x420f58[_0x290766(0x50e)](_0x4a0d71),_0x311669[_0x290766(0x436)][_0x290766(0x520)]={},_0x311669[_0x290766(0x436)][_0x290766(0x520)][_0x290766(0x24e)]=_0x4a0d71['codec'],_0x311669[_0x290766(0x436)][_0x290766(0x520)][_0x290766(0x512)]=_0x4a0d71[_0x290766(0x512)],_0x311669['stats'][_0x290766(0x520)]['sampleRate']=_0x4a0d71[_0x290766(0x228)],_0x311669[_0x290766(0x436)]['Chunked_audio']['bitrate']=_0x4a0d71[_0x290766(0x63d)][_0x290766(0x739)];var _0x471f5a,_0x594601=new Promise((_0x198be8,_0x294783)=>{_0x471f5a=_0x198be8;});_0x594601[_0x290766(0x8c5)]=_0x471f5a,_0x52b552['read']()[_0x290766(0x619)](function _0x1e4d48({done:_0x213df5,value:_0xde0bf8}){var _0x269313=_0x290766;if(_0x213df5||_0x2bba8b){_0x420f58[_0x269313(0x1cf)]();_0xde0bf8&&_0xde0bf8[_0x269313(0x1cf)]();_0x311669[_0x269313(0x564)]=null;return;}_0x56fdcb==-0x1&&(_0x56fdcb=_0xde0bf8['timestamp'],_0x311669[_0x269313(0x436)][_0x269313(0x520)][_0x269313(0x3c5)]=Date[_0x269313(0x610)](),_0x594601['resolve']()),_0x20b4ef==_0xde0bf8[_0x269313(0x82c)]&&(_0xde0bf8[_0x269313(0x82c)]+=0x1),!_0x2bba8b&&(_0x20b4ef=_0xde0bf8[_0x269313(0x82c)],_0x420f58[_0x269313(0x9d3)](_0xde0bf8)),_0xde0bf8['close'](),_0x52b552[_0x269313(0x829)]()[_0x269313(0x619)](_0x1e4d48);}),_0x311669['chunkedAudioEnabled']=!![],await _0x594601;},_0x311669['getPCM']=function(_0x4f797e){var _0x3460bb=_0x134a17;warnlog(_0x3460bb(0x9bf));const _0x550c9e=window[_0x3460bb(0x45f)]||window[_0x3460bb(0x8e1)],_0x19630c=new _0x550c9e(),_0x544d75=_0x19630c[_0x3460bb(0x9ab)](_0x4f797e),_0x4850cd=0x800,_0x2a8c9a=(_0x19630c[_0x3460bb(0x195)]||_0x19630c[_0x3460bb(0x3f3)])[_0x3460bb(0x83d)](_0x19630c,_0x4850cd,0x1,0x1);return _0x2a8c9a[_0x3460bb(0x455)]=function(_0x23d171){var _0xb95a56=_0x3460bb,_0x19abd0=new Uint8Array(_0x23d171['inputBuffer'][_0xb95a56(0x43b)](0x0)['buffer']);_0x48cd13[_0xb95a56(0x505)]([0x0,_0xb95a56(0x944)]),_0x48cd13[_0xb95a56(0x505)](_0x19abd0),_0x1e48c9[_0xb95a56(0x64b)](_0xb95a56(0x944));},_0x544d75[_0x3460bb(0x857)](_0x2a8c9a),_0x2a8c9a['connect'](_0x19630c[_0x3460bb(0x9e2)]),_0x311669[_0x3460bb(0x436)][_0x3460bb(0x520)]={},_0x311669[_0x3460bb(0x564)]=!![],_0x2a8c9a;},_0x311669[_0x134a17(0x504)]=async function(_0xe4571f){var _0x14ff4f=_0x134a17;log(_0x14ff4f(0x696)+_0xe4571f);!_0x311669['chunkedVideoEnabled']&&_0x311669['stats'][_0x14ff4f(0x6cf)]&&(config=_0x311669['stats'][_0x14ff4f(0x6cf)],await _0x311669[_0x14ff4f(0x630)](config));!_0x311669[_0x14ff4f(0x564)]&&_0x311669[_0x14ff4f(0x436)]['Chunked_audio']&&(config=_0x311669[_0x14ff4f(0x436)][_0x14ff4f(0x520)],await _0x311669[_0x14ff4f(0x9df)](config));if(_0xe4571f in _0xe532b8)return;if(!_0x1e48c9){var _0x36bef4=_0x311669[_0x14ff4f(0x700)](),_0x38ae3b=_0x311669[_0x14ff4f(0x88e)],_0x12df42=null;_0x311669[_0x14ff4f(0x84c)]&&_0x311669['maxvideobitrate']<_0x38ae3b&&(_0x38ae3b=_0x311669['maxvideobitrate']);var _0x1e7140={'codec':_0x14ff4f(0x38f),'width':0x780,'height':0x438,'bitrate':parseInt(_0x38ae3b*0x3e8),'frameRate':0x1e,'latencyMode':_0x14ff4f(0x264)};_0x311669['alpha']&&(_0x1e7140[_0x14ff4f(0x970)]=_0x14ff4f(0x43f));var _0x3130d3=_0x36bef4['getVideoTracks']();if(_0x3130d3[_0x14ff4f(0x8e9)]){var _0x23e3ef=_0x3130d3[0x0][_0x14ff4f(0x2e2)]();_0x23e3ef[_0x14ff4f(0x530)]&&(_0x1e7140[_0x14ff4f(0x530)]=_0x23e3ef[_0x14ff4f(0x530)]),_0x23e3ef[_0x14ff4f(0x5ec)]&&(_0x1e7140['height']=_0x23e3ef[_0x14ff4f(0x5ec)]),_0x23e3ef[_0x14ff4f(0x732)]&&(_0x1e7140[_0x14ff4f(0x732)]=_0x23e3ef[_0x14ff4f(0x732)]);}else _0x1e7140=![];if(_0x38ae3b<0x259){var _0xfdbc03=_0x1e7140[_0x14ff4f(0x530)]*_0x1e7140['height']/(0x280*0x168);if(_0xfdbc03>=0x2)_0x1e7140['width']=parseInt(_0x1e7140[_0x14ff4f(0x530)]/0x2),_0x1e7140[_0x14ff4f(0x5ec)]=parseInt(_0x1e7140[_0x14ff4f(0x5ec)]/0x2);else _0xfdbc03>=1.5&&(_0x1e7140['width']=parseInt(_0x1e7140['width']/1.5),_0x1e7140[_0x14ff4f(0x5ec)]=parseInt(_0x1e7140['height']/1.5));}var _0x32e504={'codec':_0x14ff4f(0x2b0),'numberOfChannels':0x2,'channels':0x2,'sampleRate':0xbb80,'bitrate':0xfa00,'tuning':{'bitrate':0xfa00}};if(_0x38ae3b>0xbb8)var _0x32e504={'codec':_0x14ff4f(0x2b0),'numberOfChannels':0x2,'channels':0x2,'sampleRate':0xbb80,'tuning':{'bitrate':0x1f400}};else{if(_0x38ae3b<0x259)var _0x32e504={'codec':_0x14ff4f(0x2b0),'numberOfChannels':0x2,'channels':0x2,'sampleRate':0xbb80,'tuning':{'bitrate':0x7d00}};}_0x311669[_0x14ff4f(0x944)]&&(_0x32e504={'codec':_0x14ff4f(0x944),'numberOfChannels':0x2,'channels':0x2,'sampleRate':0xbb80});!_0x36bef4[_0x14ff4f(0xa6c)]()[_0x14ff4f(0x8e9)]&&(_0x32e504=![]);if(!_0x32e504&&!_0x1e7140)return;_0x1e48c9={},_0x1e48c9['needKeyFrame']=!![],_0x1e48c9['configVideo']=_0x1e7140||![],_0x1e48c9[_0x14ff4f(0x7b1)]=_0x32e504||![],_0x1e48c9[_0x14ff4f(0x691)]&&await _0x311669['webCodec'](_0x1e48c9['configVideo']),_0x1e48c9['configAudio']&&(_0x1e48c9[_0x14ff4f(0x7b1)][_0x14ff4f(0x24e)]=='pcm'?_0x311669['getPCM'](_0x36bef4):await _0x311669['webCodecAudio'](_0x1e48c9[_0x14ff4f(0x7b1)])),_0x1e48c9['sendChunks']=function(_0xd7605c=_0x14ff4f(0x38c)){var _0x425c75=_0x14ff4f;if(_0x12df42)return;_0x12df42=!![];var _0x1ec12f=_0xd7605c;while(_0x48cd13['length']){if(!Object[_0x425c75(0x19b)](_0xe532b8)[_0x425c75(0x8e9)]){_0x48cd13=[],_0x12df42=null,_0x311669[_0x425c75(0x436)][_0x425c75(0x724)]=0x0;return;}_0x311669[_0x425c75(0x436)][_0x425c75(0x724)]=_0x48cd13[_0x425c75(0x8e9)];var _0x2c3673=0x0,_0x52299c=_0x48cd13['shift']();if(_0x52299c['length']===0x2){_0x1ec12f=_0x52299c[0x1],_0x52299c[_0x425c75(0x505)](_0x48cd13[_0x425c75(0x8e9)]);var _0x10fb71=JSON[_0x425c75(0x479)](_0x52299c);for(var _0x19e53c in _0xe532b8){if((_0x1ec12f=='key'||_0x1ec12f==_0x425c75(0x44c)||_0x1ec12f==_0x425c75(0x30c))&&!_0x311669['pcs'][_0x19e53c][_0x425c75(0x670)])continue;if((_0x1ec12f=='audio'||_0x1ec12f=='pcm')&&!_0x311669[_0x425c75(0x859)][_0x19e53c]['allowAudio'])continue;try{_0xe532b8[_0x19e53c][_0x425c75(0x8ee)]===_0x425c75(0x6d4)&&_0xe532b8[_0x19e53c][_0x425c75(0x2b9)](_0x10fb71),_0x311669['pcs'][_0x19e53c]['stats'][_0x425c75(0x4ab)]=_0xe532b8[_0x19e53c][_0x425c75(0x4ab)],_0x2c3673<_0x311669[_0x425c75(0x859)][_0x19e53c]['stats'][_0x425c75(0x4ab)]&&(_0x2c3673=_0x311669[_0x425c75(0x859)][_0x19e53c][_0x425c75(0x436)][_0x425c75(0x4ab)]);}catch(_0x2292e9){}}}else{if(_0x52299c[_0x425c75(0x7ce)]>0x40000){for(var _0x19e53c in _0xe532b8){if((_0x1ec12f=='key'||_0x1ec12f=='delta'||_0x1ec12f=='video')&&!_0x311669[_0x425c75(0x859)][_0x19e53c][_0x425c75(0x670)])continue;if((_0x1ec12f==_0x425c75(0x433)||_0x1ec12f==_0x425c75(0x944))&&!_0x311669[_0x425c75(0x859)][_0x19e53c]['allowAudio'])continue;try{_0xe532b8[_0x19e53c]['readyState']===_0x425c75(0x6d4)&&_0xe532b8[_0x19e53c][_0x425c75(0x2b9)](_0x52299c[_0x425c75(0x7f5)](0x0,0x40000)),_0x311669[_0x425c75(0x859)][_0x19e53c][_0x425c75(0x436)][_0x425c75(0x4ab)]=_0xe532b8[_0x19e53c][_0x425c75(0x4ab)],_0x2c3673<_0x311669[_0x425c75(0x859)][_0x19e53c][_0x425c75(0x436)]['bufferedAmount']&&(_0x2c3673=_0x311669[_0x425c75(0x859)][_0x19e53c][_0x425c75(0x436)][_0x425c75(0x4ab)]);}catch(_0x3ef4c3){}}_0x48cd13[_0x425c75(0x91c)](_0x52299c[_0x425c75(0x7f5)](0x40000));}else for(var _0x19e53c in _0xe532b8){if((_0x1ec12f==_0x425c75(0xa68)||_0x1ec12f==_0x425c75(0x44c)||_0x1ec12f==_0x425c75(0x30c))&&!_0x311669['pcs'][_0x19e53c][_0x425c75(0x670)])continue;if((_0x1ec12f==_0x425c75(0x433)||_0x1ec12f==_0x425c75(0x944))&&!_0x311669[_0x425c75(0x859)][_0x19e53c][_0x425c75(0x760)])continue;try{_0xe532b8[_0x19e53c][_0x425c75(0x8ee)]===_0x425c75(0x6d4)&&_0xe532b8[_0x19e53c]['send'](_0x52299c),_0x311669[_0x425c75(0x859)][_0x19e53c]['stats'][_0x425c75(0x4ab)]=_0xe532b8[_0x19e53c][_0x425c75(0x4ab)],_0x2c3673<_0x311669['pcs'][_0x19e53c][_0x425c75(0x436)]['bufferedAmount']&&(_0x2c3673=_0x311669[_0x425c75(0x859)][_0x19e53c][_0x425c75(0x436)]['bufferedAmount']);}catch(_0x576d6d){}}}_0x311669['stats'][_0x425c75(0xa02)]=_0x2c3673;if(!_0x1e48c9['throttle']&&_0x2c3673>0x1f4)_0x1e48c9[_0x425c75(0xa31)]=!![],_0x311669[_0x425c75(0x436)]['throttling']=_0x1e48c9['throttle'],_0x311669[_0x425c75(0x436)][_0x425c75(0x6cf)]['bitrate']=parseInt(_0x311669['chunked']*0x3e8/0xa),_0x1e48c9[_0x425c75(0x639)][_0x425c75(0x50e)](_0x311669[_0x425c75(0x436)][_0x425c75(0x6cf)]);else _0x1e48c9[_0x425c75(0xa31)]&&_0x2c3673<0x12c&&(_0x1e48c9[_0x425c75(0xa31)]=![],_0x311669[_0x425c75(0x436)][_0x425c75(0x412)]=_0x1e48c9[_0x425c75(0xa31)],_0x311669[_0x425c75(0x436)][_0x425c75(0x6cf)]['bitrate']=parseInt(_0x311669[_0x425c75(0x88e)]*0x3e8),_0x1e48c9[_0x425c75(0x639)][_0x425c75(0x50e)](_0x311669['stats'][_0x425c75(0x6cf)]));}_0x12df42=null,_0x311669[_0x425c75(0x436)]['chunkedInQueue']=0x0;},_0x36bef4[_0x14ff4f(0x293)]=function(_0x5c1c96){};}var _0x4ac1f0='chunked';if(_0xe4571f in _0x311669['pcs'])_0xe532b8[_0xe4571f]=_0x311669[_0x14ff4f(0x859)][_0xe4571f][_0x14ff4f(0x1e1)](_0x4ac1f0,{'ordered':!![]});else{warnlog('UUID\x20does\x20not\x20exist');return;}_0xe532b8[_0xe4571f][_0x14ff4f(0x4f4)]=_0x14ff4f(0xa54),_0xe532b8[_0xe4571f]['binaryType']=_0x14ff4f(0x328),_0xe532b8[_0xe4571f][_0x14ff4f(0x6a7)]=![],_0xe532b8[_0xe4571f][_0x14ff4f(0x777)]=()=>{var _0x1baad6=_0x14ff4f;log('chunkedtransfer\x20OPEN');if(_0x311669[_0x1baad6(0x564)]&&_0x311669[_0x1baad6(0x29e)]&&_0x311669[_0x1baad6(0x859)][_0xe4571f][_0x1baad6(0x760)]&&_0x311669[_0x1baad6(0x859)][_0xe4571f]['allowVideo'])_0xe532b8[_0xe4571f]['send'](JSON[_0x1baad6(0x479)]({'timestamp':Date[_0x1baad6(0x610)](),'type':_0x1baad6(0x1ff),'realTimeVideo':_0x311669[_0x1baad6(0x436)][_0x1baad6(0x6cf)][_0x1baad6(0x3c5)],'realTimeAudio':_0x311669[_0x1baad6(0x436)][_0x1baad6(0x520)]['realTime'],'size':0x5af3107a3fff,'configVideo':_0x1e48c9[_0x1baad6(0x691)],'configAudio':_0x1e48c9['configAudio'],'recordType':_0x311669[_0x1baad6(0x88e)],'filename':_0x4ac1f0+_0x1baad6(0x9e9),'id':_0x4ac1f0}));else{if(_0x311669[_0x1baad6(0x564)]&&_0x311669[_0x1baad6(0x859)][_0xe4571f][_0x1baad6(0x760)])_0xe532b8[_0xe4571f]['send'](JSON['stringify']({'timestamp':Date[_0x1baad6(0x610)](),'type':'chunkedtransfer','realTimeAudio':_0x311669[_0x1baad6(0x436)][_0x1baad6(0x520)][_0x1baad6(0x3c5)],'size':0x5af3107a3fff,'configAudio':_0x1e48c9[_0x1baad6(0x7b1)],'recordType':_0x311669['chunked'],'filename':_0x4ac1f0+_0x1baad6(0x9e9),'id':_0x4ac1f0}));else{if(_0x311669['chunkedVideoEnabled']&&_0x311669[_0x1baad6(0x859)][_0xe4571f][_0x1baad6(0x670)])_0xe532b8[_0xe4571f][_0x1baad6(0x2b9)](JSON[_0x1baad6(0x479)]({'timestamp':Date[_0x1baad6(0x610)](),'type':_0x1baad6(0x1ff),'realTimeVideo':_0x311669['stats'][_0x1baad6(0x6cf)][_0x1baad6(0x3c5)],'size':0x5af3107a3fff,'configVideo':_0x1e48c9[_0x1baad6(0x691)],'recordType':_0x311669[_0x1baad6(0x88e)],'filename':_0x4ac1f0+_0x1baad6(0x9e9),'id':_0x4ac1f0}));else{}}}},_0xe532b8[_0xe4571f][_0x14ff4f(0x661)]=()=>{var _0x5ed178=_0x14ff4f;try{var _0x35aa36=_0x311669['hostedTransfers'][_0x5ed178(0x49e)](_0xe532b8[_0xe4571f]);_0x35aa36>-0x1&&_0x311669[_0x5ed178(0x8b5)][_0x5ed178(0x48c)](_0x35aa36,0x1);}catch(_0x309307){errorlog(_0x309307);}log(_0x5ed178(0x4a2)),_0xe532b8[_0xe4571f]=null,delete _0xe532b8[_0xe4571f];var _0x1c0da3=![];for(var _0x4aed08=0x0;_0x4aed08<_0x311669['hostedTransfers'][_0x5ed178(0x8e9)];_0x4aed08++){if(_0x5ed178(0x4f4)in _0x311669['hostedTransfers'][_0x4aed08]&&_0x311669['hostedTransfers'][_0x4aed08][_0x5ed178(0x4f4)]==_0x5ed178(0xa54)){_0x1c0da3=!![];break;}}if(_0x1c0da3)try{_0x1e48c9[_0x5ed178(0x86e)]();}catch(_0x8cc4e3){}},_0xe532b8[_0xe4571f][_0x14ff4f(0x17a)]=_0x22c341=>{var _0x2411ff=_0x14ff4f;if(_0x22c341[_0x2411ff(0x3bc)])try{var _0x222401=JSON[_0x2411ff(0x52d)](_0x22c341[_0x2411ff(0x3bc)]);_0x222401['kf']&&(log(_0x2411ff(0x3c3)),_0x1e48c9[_0x2411ff(0x7fa)]=!![]);}catch(_0x5d5703){}},_0x311669[_0x14ff4f(0x8b5)][_0x14ff4f(0x505)](_0xe532b8[_0xe4571f]);},_0x311669[_0x134a17(0x1b5)]=async function(_0x44cc48,_0x5e3dbb,_0x1d0eea){var _0x569353=_0x134a17;log('Created\x20transfer\x20channel');var _0x3c18c2=_0x1d0eea;_0x3c18c2[_0x569353(0x992)]=_0x569353(0x328);var _0x4a1e8c='',_0x15946c=0x0,_0xa542c5=![],_0x324395=![],_0x33cab6=0x0,_0x463481={};_0x3c18c2['onopen']=_0x39b7f2=>{var _0x323ebc=_0x569353;log(_0x323ebc(0x366));},_0x3c18c2['onmessage']=_0x5e7efa=>{var _0x5210d0=_0x569353;if(!_0xa542c5)try{_0xa542c5=JSON[_0x5210d0(0x52d)](_0x5e7efa[_0x5210d0(0x3bc)]);if(_0xa542c5[_0x5210d0(0x78a)]=='filetransfer'){var {readable:_0x3ce10e,writable:_0x4fc3b3}=new TransformStream({'transform':(_0x2df772,_0x300e09)=>_0x2df772[_0x5210d0(0x440)]()[_0x5210d0(0x619)](_0x350fac=>_0x300e09[_0x5210d0(0x21e)](new Uint8Array(_0x350fac)))});_0x463481[_0x5210d0(0x88f)]=_0x4fc3b3[_0x5210d0(0x673)]();;_0x3ce10e['pipeTo'](streamSaver[_0x5210d0(0x239)](_0xa542c5['filename']));for(var _0x397ab7=0x0;_0x397ab7{var _0x570161=_0x569353;_0x33cab6<=0x0&&(_0x463481[_0x570161(0x88f)]&&setTimeout(function(_0x358712,_0x31a75a){var _0x29d7cd=_0x570161;_0x31a75a<=0x0?(_0x358712[_0x29d7cd(0x1cf)](),_0x358712=null):setTimeout(function(_0x53da83,_0x317c56){var _0x3576ca=_0x29d7cd;_0x53da83[_0x3576ca(0x1cf)](),_0x53da83=null;},0x1388,_0x358712);},0x3e8,_0x463481[_0x570161(0x88f)],_0x33cab6));_0x3c18c2=null;return;};return;};async function _0x4c3c9f(_0x2b2002,_0x1307e9=![]){var _0x15bc5a=_0x134a17;_0x2b2002['decoder'][_0x15bc5a(0x5f5)](_0x2b2002[_0x15bc5a(0x64f)]['shift']());if(_0x2b2002[_0x15bc5a(0x265)]===null&&!_0x1307e9)return;_0x2b2002[_0x15bc5a(0x265)]=setTimeout(function(_0x4ce3ea){_0x4c3c9f(_0x4ce3ea);},0x21,_0x2b2002);}return _0x311669[_0x134a17(0xa51)]=async function(_0x583108,_0x3522b1,_0x3bbdcb){var _0x58c10=_0x134a17;log(_0x58c10(0x79b));var _0x55fd2e=_0x3bbdcb;_0x55fd2e[_0x58c10(0x992)]='arraybuffer';var _0x558469='',_0x47de28=0x0,_0x2ecb3b=![],_0x2dab49=![],_0x2b645c={};_0x55fd2e[_0x58c10(0x777)]=_0x4243bb=>{var _0x43943c=_0x58c10;log(_0x43943c(0x366));},_0x55fd2e[_0x58c10(0x661)]=async function(_0x3f4af1){var _0x50eee3=_0x58c10;if(_0x2b645c['videoWriter']){if(_0x2b645c[_0x50eee3(0x49b)][_0x50eee3(0x6da)]){await delay(0x3e8);try{await _0x2b645c['videoElement'][_0x50eee3(0x6da)]();}catch(_0x84d3e1){}}}_0x55fd2e=null;_0x311669[_0x50eee3(0x16a)][_0x3522b1]&&(delete _0x311669['rpcs'][_0x3522b1][_0x50eee3(0x436)][_0x50eee3(0x702)],delete _0x311669[_0x50eee3(0x16a)][_0x3522b1]['stats']['chunked_mode_audio']);return;};async function _0x1a70fc(){var _0x44024c=_0x58c10,_0x2d6799=await window[_0x44024c(0x806)]({'startIn':'videos','suggestedName':'myVideo.webm','types':[{'description':_0x44024c(0x704),'accept':{'video/webm':[_0x44024c(0x9e9)]}}]}),_0x5ab509=await _0x2d6799[_0x44024c(0x84a)]();return _0x2b645c[_0x44024c(0x44e)]['fileWriter']=_0x5ab509,_0x2b645c[_0x44024c(0x203)]=new WebMWriter(_0x2b645c[_0x44024c(0x44e)]),_0x2b645c[_0x44024c(0x49b)]['stopWriter']=async function(){var _0x4700e7=_0x44024c;_0x2b645c['videoElement'][_0x4700e7(0x6da)]=![],clearInterval(_0x2b645c[_0x4700e7(0x57d)]),_0x2b645c[_0x4700e7(0x57d)]=null,await _0x2b645c['videoWriter'][_0x4700e7(0x73e)](),_0x2b645c['writer_config'][_0x4700e7(0x1bb)][_0x4700e7(0x1cf)]();},_0x2b645c[_0x44024c(0x203)];}_0x55fd2e[_0x58c10(0x17a)]=async function(_0x4fe1ed){var _0x5afb41=_0x58c10;if(!_0x2ecb3b)try{_0x2ecb3b=JSON[_0x5afb41(0x52d)](_0x4fe1ed['data']);if(_0x2ecb3b[_0x5afb41(0x78a)]==_0x5afb41(0x1ff)){log(_0x5afb41(0x4ad)),log(_0x2ecb3b),_0x2b645c[_0x5afb41(0x54a)]=_0x3522b1,_0x2b645c[_0x5afb41(0x63a)]=0x0,_0x2b645c[_0x5afb41(0xa13)]=0x2,_0x2b645c[_0x5afb41(0x8b7)]=Date[_0x5afb41(0x610)](),_0x2b645c[_0x5afb41(0x8cd)]=_0x2ecb3b[_0x5afb41(0x82c)],_0x2b645c[_0x5afb41(0x2fe)]=_0x2b645c[_0x5afb41(0x8b7)]-_0x2ecb3b['timestamp'],_0x2b645c['dc']=_0x55fd2e,_0x2b645c['id']=_0x2ecb3b['id'],_0x2b645c[_0x5afb41(0x57d)]=null,_0x2b645c[_0x5afb41(0xa33)]=![],_0x2b645c[_0x5afb41(0x49b)]=createVideoElement(),_0x2b645c[_0x5afb41(0x49b)][_0x5afb41(0x740)]=!![],_0x2b645c[_0x5afb41(0x49b)][_0x5afb41(0x225)]=![],_0x2b645c[_0x5afb41(0x49b)]['setAttribute']('playsinline',''),_0x2b645c[_0x5afb41(0x49b)][_0x5afb41(0x2bc)]['UUID']=_0x3522b1,_0x2b645c['videoElement'][_0x5afb41(0x1ff)]=!![],_0x2b645c[_0x5afb41(0x49b)]['srcObject']=new MediaStream(),_0x311669['rpcs'][_0x3522b1][_0x5afb41(0x585)]=_0x2b645c['videoElement'][_0x5afb41(0x5c4)],_0x311669[_0x5afb41(0x16a)][_0x3522b1][_0x5afb41(0x49b)]=_0x2b645c[_0x5afb41(0x49b)];_0x311669[_0x5afb41(0x16a)][_0x3522b1]['mirrorState']&&applyMirrorGuest(_0x311669[_0x5afb41(0x16a)][_0x3522b1]['mirrorState'],_0x311669['rpcs'][_0x3522b1][_0x5afb41(0x49b)]);_0x311669[_0x5afb41(0x16a)][_0x3522b1][_0x5afb41(0x1c9)]!==![]&&(_0x311669[_0x5afb41(0x16a)][_0x3522b1][_0x5afb41(0x49b)][_0x5afb41(0x3bf)]=_0x311669[_0x5afb41(0x16a)][_0x3522b1][_0x5afb41(0x1c9)]);_0x2b645c[_0x5afb41(0x49b)][_0x5afb41(0x8bf)](_0x5afb41(0x462),_0x271ebd=>{var _0x12cef4=_0x5afb41;try{var _0x1ab590=document[_0x12cef4(0x5c8)](_0x12cef4(0x8d8));_0x1ab590&&_0x1ab590[_0x12cef4(0x826)][_0x12cef4(0x9d0)](_0x1ab590);}catch(_0x2e916c){}_0x2b645c['playing']=!![];if(_0x2b645c['audioContext'])_0x2b645c['audioContext'][_0x12cef4(0x327)]();else _0x311669[_0x12cef4(0xa6f)]&&_0x311669[_0x12cef4(0xa6f)][_0x12cef4(0x327)]();try{_0x311669[_0x12cef4(0x36b)]&&(v['readyState']>=0x3&&(!v[_0x12cef4(0x36b)]&&(v['pip']=!![],toggleSystemPip(v,!![]))));}catch(_0x59afd9){}},{'once':!![]}),_0x2b645c[_0x5afb41(0x49b)][_0x5afb41(0x8bf)]('error',function(_0x21e789){errorlog(_0x21e789);}),_0x2b645c[_0x5afb41(0x49b)][_0x5afb41(0x1d5)]=_0x1a70fc,_0x2b645c[_0x5afb41(0x49b)]['oncanplay']=function(){updateMixer();},_0x2b645c[_0x5afb41(0x203)]=![],_0x2b645c['frameMeta']=![],_0x2b645c[_0x5afb41(0x44e)]={},_0x2b645c[_0x5afb41(0x44e)][_0x5afb41(0x30c)]=![],_0x2b645c[_0x5afb41(0x44e)]['audio']=![],_0x2b645c[_0x5afb41(0x77a)]=![],_0x2b645c[_0x5afb41(0x79f)]=![],_0x2b645c[_0x5afb41(0x3db)]=![],_0x2b645c[_0x5afb41(0x82e)]=![],_0x2b645c['video']=![],_0x2b645c[_0x5afb41(0x433)]=![],_0x2b645c[_0x5afb41(0x75d)]=![],_0x2b645c[_0x5afb41(0x462)]=![];_0x2ecb3b[_0x5afb41(0x691)]&&(_0x311669[_0x5afb41(0x16a)][_0x3522b1][_0x5afb41(0x436)][_0x5afb41(0x702)]=_0x2ecb3b['configVideo'],_0x2b645c[_0x5afb41(0x77a)]={},_0x2b645c[_0x5afb41(0x77a)][_0x5afb41(0x530)]=_0x2ecb3b[_0x5afb41(0x691)][_0x5afb41(0x530)]+''||_0x5afb41(0x5ac),_0x2b645c[_0x5afb41(0x77a)][_0x5afb41(0x5ec)]=_0x2ecb3b[_0x5afb41(0x691)][_0x5afb41(0x5ec)]+''||_0x5afb41(0x5b7),_0x2b645c[_0x5afb41(0x77a)][_0x5afb41(0x24e)]=_0x2ecb3b[_0x5afb41(0x691)]['codec']||_0x5afb41(0x38f),_0x2b645c[_0x5afb41(0x44e)][_0x5afb41(0x30c)]=!![],_0x2b645c['writer_config'][_0x5afb41(0x530)]=parseInt(_0x2b645c[_0x5afb41(0x77a)]['width']),_0x2b645c[_0x5afb41(0x44e)]['height']=parseInt(_0x2b645c[_0x5afb41(0x77a)][_0x5afb41(0x5ec)]),_0x2ecb3b[_0x5afb41(0x691)][_0x5afb41(0x24e)]==_0x5afb41(0x38f)?_0x2b645c['writer_config'][_0x5afb41(0x24e)]='VP9':_0x2b645c[_0x5afb41(0x44e)][_0x5afb41(0x24e)]=_0x5afb41(0x3bd),_0x2b645c[_0x5afb41(0x3db)]={'output':_0x3809c7=>{var _0x2391f9=_0x5afb41;_0x2b645c[_0x2391f9(0x30c)][_0x2391f9(0x8c8)][_0x2391f9(0x44d)](_0x3809c7);},'error':_0x55b13f=>{var _0x237cf7=_0x5afb41;_0x2b645c[_0x237cf7(0x30c)][_0x237cf7(0x28b)][_0x237cf7(0x5ae)]==_0x237cf7(0x208)?warnlog(_0x237cf7(0x2a9)):errorlog(_0x55b13f[_0x237cf7(0x6d8)]);}},_0x2b645c[_0x5afb41(0x30c)]={},_0x2b645c['video'][_0x5afb41(0xa1e)]=new MediaStreamTrackGenerator({'kind':_0x5afb41(0x30c)}),_0x2b645c[_0x5afb41(0x30c)]['stream']=new MediaStream([_0x2b645c[_0x5afb41(0x30c)][_0x5afb41(0xa1e)]]),_0x2b645c[_0x5afb41(0x30c)][_0x5afb41(0x8c8)]=_0x2b645c[_0x5afb41(0x30c)][_0x5afb41(0xa1e)]['writable'][_0x5afb41(0x673)](),_0x2b645c[_0x5afb41(0x30c)][_0x5afb41(0x28b)]=new VideoDecoder(_0x2b645c[_0x5afb41(0x3db)]),_0x2b645c[_0x5afb41(0x30c)]['decoder'][_0x5afb41(0x50e)](_0x2b645c[_0x5afb41(0x77a)]),_0x2b645c['video'][_0x5afb41(0x64f)]=[],_0x2b645c[_0x5afb41(0x30c)]['nextQueue']=null,_0x2b645c[_0x5afb41(0x30c)]['playbackheader']=![],_0x2b645c['video'][_0x5afb41(0x6a7)]=![],_0x5afb41(0x8f0)in _0x2ecb3b&&(_0x2b645c[_0x5afb41(0x30c)]['realTime']=_0x2ecb3b[_0x5afb41(0x8f0)]),_0x2b645c[_0x5afb41(0x49b)]['srcObject'][_0x5afb41(0x58b)](_0x2b645c['video'][_0x5afb41(0x75f)][_0x5afb41(0x87a)]()[0x0]));_0x2ecb3b[_0x5afb41(0x7b1)]&&(_0x311669[_0x5afb41(0x16a)][_0x3522b1]['stats']['chunked_mode_audio']=_0x2ecb3b[_0x5afb41(0x7b1)],_0x2b645c[_0x5afb41(0x79f)]=_0x2ecb3b['configAudio'],_0x2b645c[_0x5afb41(0x44e)][_0x5afb41(0x433)]=!![],_0x2b645c[_0x5afb41(0x44e)][_0x5afb41(0x690)]=_0x2ecb3b['configAudio']['sampleRate']||0xbb80,_0x2b645c[_0x5afb41(0x44e)]['channels']=_0x2ecb3b[_0x5afb41(0x7b1)][_0x5afb41(0x512)]||0x1,_0x2b645c[_0x5afb41(0x79f)]['codec']&&_0x2b645c[_0x5afb41(0x79f)]['codec']==_0x5afb41(0x944)?(!_0x2b645c[_0x5afb41(0x9e2)]?_0x2b645c[_0x5afb41(0x9e2)]=_0x311669[_0x5afb41(0xa6f)][_0x5afb41(0x6c7)]():_0x2b645c['videoElement'][_0x5afb41(0x5c4)][_0x5afb41(0xa6c)]()['forEach'](_0x3187ff=>{var _0x48d939=_0x5afb41;_0x2b645c['videoElement'][_0x48d939(0x5c4)][_0x48d939(0x59b)](_0x3187ff);}),_0x2b645c[_0x5afb41(0x9e2)][_0x5afb41(0x75f)][_0x5afb41(0xa6c)]()[_0x5afb41(0x982)](_0x1b5ae8=>{var _0x4db9c6=_0x5afb41;_0x2b645c[_0x4db9c6(0x49b)]['srcObject'][_0x4db9c6(0x58b)](_0x1b5ae8);}),_0x2b645c[_0x5afb41(0x5a1)]=!![]):(_0x2b645c['audio']={},_0x2b645c[_0x5afb41(0x433)][_0x5afb41(0x64f)]=[],_0x2b645c[_0x5afb41(0x433)][_0x5afb41(0x265)]=null,'realTimeAudio'in _0x2ecb3b&&(_0x2b645c[_0x5afb41(0x433)][_0x5afb41(0x3c5)]=_0x2ecb3b['realTimeAudio']),_0x2b645c[_0x5afb41(0x82e)]={'output':_0x171afd=>{var _0xcd9409=_0x5afb41;_0x2b645c['audio']['frameWriter'][_0xcd9409(0x44d)](_0x171afd);if(_0x2b645c[_0xcd9409(0x5e2)])return;var _0x1b6670=_0x171afd[_0xcd9409(0x82c)]/0x3e8-(Date[_0xcd9409(0x610)]()-_0x2b645c[_0xcd9409(0x2fe)]-_0x2b645c[_0xcd9409(0x433)][_0xcd9409(0x3c5)]);_0x1b6670=_0x1b6670-(_0x311669['audioCtx']['baseLatency']||0x0)*0x3e8-(_0x311669[_0xcd9409(0xa6f)][_0xcd9409(0x3d8)]||0x0)*0x3e8;var _0x180e19=0x3e7;if(!_0x311669[_0xcd9409(0x16a)][_0x2b645c[_0xcd9409(0x54a)]])return;else{if(_0x311669[_0xcd9409(0x16a)][_0x2b645c[_0xcd9409(0x54a)]][_0xcd9409(0xa33)]!==![])_0x180e19=_0x311669['rpcs'][_0x2b645c[_0xcd9409(0x54a)]][_0xcd9409(0xa33)];else _0x311669[_0xcd9409(0xa33)]!==![]?_0x180e19=_0x311669[_0xcd9409(0xa33)]:_0x311669[_0xcd9409(0x16a)][_0x2b645c[_0xcd9409(0x54a)]][_0xcd9409(0xa33)]=_0x180e19;}_0x1b6670+=_0x180e19-0x78,_0x1b6670<=0x0&&(_0x1b6670=0x0),_0x2b645c[_0xcd9409(0x432)]['delayTime'][_0xcd9409(0x44a)](parseFloat(_0x1b6670/0x3e8),_0x311669['audioCtx']['currentTime']),_0x2b645c[_0xcd9409(0x5e2)]=setTimeout(function(){var _0x4cdea0=_0xcd9409;_0x2b645c[_0x4cdea0(0x5e2)]=null;},_0x1b6670);},'error':_0x26593b=>{var _0x138b0e=_0x5afb41;_0x2b645c[_0x138b0e(0x433)][_0x138b0e(0x28b)]['state']==_0x138b0e(0x208)?warnlog(_0x138b0e(0x2a9)):errorlog(_0x26593b[_0x138b0e(0x6d8)]);}},_0x2b645c[_0x5afb41(0x433)][_0x5afb41(0x28b)]=new AudioDecoder(_0x2b645c['init_audio']),_0x2b645c[_0x5afb41(0x433)][_0x5afb41(0x28b)]['configure'](_0x2b645c[_0x5afb41(0x79f)]),_0x2b645c[_0x5afb41(0x433)][_0x5afb41(0xa1e)]=new MediaStreamTrackGenerator({'kind':_0x5afb41(0x433)}),_0x2b645c[_0x5afb41(0x433)]['frameWriter']=_0x2b645c[_0x5afb41(0x433)]['generator'][_0x5afb41(0x70f)][_0x5afb41(0x673)](),_0x2b645c[_0x5afb41(0x433)][_0x5afb41(0x75f)]=new MediaStream([_0x2b645c[_0x5afb41(0x433)][_0x5afb41(0xa1e)]]),_0x2b645c[_0x5afb41(0x433)]['audioNode']=_0x311669['audioCtx'][_0x5afb41(0x9ab)](_0x2b645c[_0x5afb41(0x433)][_0x5afb41(0x75f)]),_0x2b645c[_0x5afb41(0x432)]=_0x311669[_0x5afb41(0xa6f)][_0x5afb41(0x9b2)](0x1e),_0x2b645c[_0x5afb41(0x432)][_0x5afb41(0xa88)][_0x5afb41(0x8d7)]=0x0,_0x2b645c[_0x5afb41(0x433)]['audioNode'][_0x5afb41(0x857)](_0x2b645c[_0x5afb41(0x432)]),_0x2b645c[_0x5afb41(0x9e2)]=_0x311669[_0x5afb41(0xa6f)][_0x5afb41(0x6c7)](),_0x2b645c[_0x5afb41(0x432)]['connect'](_0x2b645c[_0x5afb41(0x9e2)]),_0x2b645c['destination'][_0x5afb41(0x75f)][_0x5afb41(0xa6c)]()['forEach'](_0x5d395f=>{var _0x512100=_0x5afb41;_0x2b645c[_0x512100(0x49b)][_0x512100(0x5c4)][_0x512100(0x58b)](_0x5d395f);})));warnlog(_0x2ecb3b),setupIncomingVideoTracking(_0x311669[_0x5afb41(0x16a)][_0x3522b1][_0x5afb41(0x49b)],_0x3522b1);if(_0x2b645c[_0x5afb41(0x433)]&&_0x2b645c[_0x5afb41(0x30c)])updateIncomingVideoElement(_0x3522b1);else{if(_0x2b645c['video'])updateIncomingVideoElement(_0x3522b1,!![],![]);else _0x2b645c[_0x5afb41(0x433)]&&updateIncomingVideoElement(_0x3522b1,![],!![]);}transferList[_0x5afb41(0x505)](_0x2b645c),_0x2dab49=transferList[_0x5afb41(0x8e9)]-0x1,updateDownloadLink(_0x2dab49),_0x2b645c[_0x5afb41(0x4e3)]=async function(_0x2d457b){var _0x14c64e=_0x5afb41;if(_0x2d457b['type']=='audio')_0x311669['rpcs'][_0x3522b1]['stats'][_0x14c64e(0x688)]['time_seconds']=parseInt(_0x2d457b[_0x14c64e(0x82c)]/0x2710)/0x64,_0x2b645c['processFrameAudio'](_0x2d457b);else{if(_0x2d457b[_0x14c64e(0x78a)]==_0x14c64e(0x944)){var _0x9b4720=_0x311669[_0x14c64e(0xa6f)][_0x14c64e(0x2ac)]();_0x9b4720[_0x14c64e(0x857)](_0x2b645c[_0x14c64e(0x9e2)]),_0x9b4720[_0x14c64e(0x9ef)]=function(){this['disconnect']();};var _0x1c7635=_0x311669['audioCtx'][_0x14c64e(0x6ce)](0x2,_0x2d457b[_0x14c64e(0x3bc)][_0x14c64e(0x8e9)],_0x311669[_0x14c64e(0xa6f)][_0x14c64e(0x228)]/0x2);_0x9b4720['buffer']=_0x1c7635;var _0x42a798=_0x1c7635['getChannelData'](0x0)['set'](_0x2d457b[_0x14c64e(0x3bc)]);_0x9b4720[_0x14c64e(0x36c)](0x0);}else _0x311669[_0x14c64e(0x16a)][_0x3522b1][_0x14c64e(0x436)]['chunked_mode_video'][_0x14c64e(0x94f)]=parseInt(_0x2d457b['timestamp']/0x2710)/0x64,_0x2b645c[_0x14c64e(0x53a)](_0x2d457b);}},_0x2b645c['processFrameVideo']=async function(_0x11db69){var _0x1ea5f=_0x5afb41;try{_0x11db69=new EncodedVideoChunk(_0x11db69);}catch(_0x3d2aec){errorlog(_0x3d2aec),errorlog(_0x11db69);return;}if(_0x2b645c['videoWriter']&&_0x2b645c[_0x1ea5f(0x49b)]['stopWriter']){if(!_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x6a7)]&&_0x11db69['type']!==_0x1ea5f(0xa68))log(_0x1ea5f(0x52b)),log(_0x11db69),!_0x2b645c[_0x1ea5f(0x5ff)]&&(_0x55fd2e['send'](JSON[_0x1ea5f(0x479)]({'kf':!![]})),_0x2b645c[_0x1ea5f(0x5ff)]=setTimeout(function(){var _0x17488f=_0x1ea5f;clearTimeout(_0x2b645c['requestKeyframe']),_0x2b645c[_0x17488f(0x5ff)]=null;},0x3e8));else!_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x6a7)]?(_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x6a7)]=Date[_0x1ea5f(0x610)](),_0x2b645c[_0x1ea5f(0x203)]['addFrame'](_0x11db69),log(_0x1ea5f(0x8bd)),_0x311669[_0x1ea5f(0x75b)]&&!_0x2b645c[_0x1ea5f(0x57d)]&&(_0x2b645c['updateTime']=setInterval(function(_0x102fca){var _0x3399bf=_0x1ea5f,_0x1aacb7=(Date[_0x3399bf(0x610)]()-_0x2b645c['video']['header'])/0x3e8,_0x21b212=Math[_0x3399bf(0x3c1)](_0x1aacb7/0x3c),_0x2aabb5=Math[_0x3399bf(0x3c1)](_0x1aacb7-_0x21b212*0x3c);try{document[_0x3399bf(0xa5d)](_0x3399bf(0x955)+_0x102fca+'\x27]')[_0x3399bf(0x82f)]=_0x3399bf(0x6ed)+_0x21b212+_0x3399bf(0x85c)+zpadTime(_0x2aabb5)+'s';}catch(_0x28778b){log(_0x3399bf(0x86c));}},0x3e8,_0x3522b1))):_0x2b645c['videoWriter'][_0x1ea5f(0x641)](_0x11db69);}_0x2b645c['video'][_0x1ea5f(0x9fa)]&&_0x2b645c[_0x1ea5f(0x30c)]&&_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x28b)]['state']===_0x1ea5f(0x208)&&(warnlog(_0x1ea5f(0x572)),_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x9fa)]=![],_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x28b)]=new VideoDecoder(_0x2b645c['init_video']),await _0x2b645c[_0x1ea5f(0x30c)]['decoder'][_0x1ea5f(0x50e)](_0x2b645c[_0x1ea5f(0x77a)]),_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x9fa)]=![]);if(_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x9fa)]||_0x11db69[_0x1ea5f(0x78a)]===_0x1ea5f(0xa68)){_0x2b645c[_0x1ea5f(0x30c)]['playbackheader']=!![];try{if(_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x265)])_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x64f)][_0x1ea5f(0x505)](_0x11db69);else{if(_0x2b645c[_0x1ea5f(0x30c)]['queue'][_0x1ea5f(0x8e9)])_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x64f)][_0x1ea5f(0x505)](_0x11db69);else{if(_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x3c5)]){_0x2b645c['video'][_0x1ea5f(0x265)]=!![];function _0x4c8794(_0x3a6478,_0xebabf9){var _0xb675c2=_0x1ea5f,_0xeef4ec=_0x3a6478[_0xb675c2(0x82c)]/0x3e8-(Date['now']()-_0xebabf9[_0xb675c2(0x2fe)]-_0xebabf9[_0xb675c2(0x30c)]['realTime']),_0x345b43=0x3e7;if(!_0x311669[_0xb675c2(0x16a)][_0xebabf9[_0xb675c2(0x54a)]]){clearTimeout(_0xebabf9[_0xb675c2(0x30c)][_0xb675c2(0x265)]),_0xebabf9[_0xb675c2(0x30c)][_0xb675c2(0x265)]=null,_0xebabf9[_0xb675c2(0x30c)][_0xb675c2(0x64f)]=[];return;}else{if(_0x311669['rpcs'][_0xebabf9[_0xb675c2(0x54a)]][_0xb675c2(0xa33)]!==![])_0x345b43=_0x311669[_0xb675c2(0x16a)][_0xebabf9[_0xb675c2(0x54a)]]['buffer'];else _0x311669[_0xb675c2(0xa33)]!==![]?_0x345b43=_0x311669[_0xb675c2(0xa33)]:_0x311669[_0xb675c2(0x16a)][_0xebabf9[_0xb675c2(0x54a)]][_0xb675c2(0xa33)]=_0x345b43;}_0xeef4ec+=_0x345b43,_0xeef4ec<0x0&&(_0xeef4ec=0x0),_0xebabf9[_0xb675c2(0x30c)][_0xb675c2(0x265)]=setTimeout(function(_0x47e91b,_0x1d07c2){var _0x20ba88=_0xb675c2;_0x47e91b[_0x20ba88(0x30c)][_0x20ba88(0x28b)][_0x20ba88(0x5f5)](_0x1d07c2),_0x47e91b[_0x20ba88(0x30c)]['queue'][_0x20ba88(0x8e9)]?_0x4c8794(_0x47e91b[_0x20ba88(0x30c)][_0x20ba88(0x64f)][_0x20ba88(0x74d)](),_0x47e91b):_0x47e91b[_0x20ba88(0x30c)][_0x20ba88(0x265)]=null;},_0xeef4ec,_0xebabf9,_0x3a6478);}try{_0x4c8794(_0x11db69,_0x2b645c);}catch(_0x19ef8c){errorlog(_0x19ef8c),_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x265)]=null,!_0x2b645c[_0x1ea5f(0x5ff)]&&(_0x55fd2e[_0x1ea5f(0x2b9)](JSON['stringify']({'kf':!![]})),_0x2b645c[_0x1ea5f(0x5ff)]=setTimeout(function(){var _0x10fe72=_0x1ea5f;clearTimeout(_0x2b645c[_0x10fe72(0x5ff)]),_0x2b645c[_0x10fe72(0x5ff)]=null;},0x3e8));}}else _0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x28b)][_0x1ea5f(0x5f5)](_0x11db69);}}}catch(_0x1a1504){errorlog(_0x1a1504),_0x2b645c['video'][_0x1ea5f(0x9fa)]=![];}}!_0x2b645c['video'][_0x1ea5f(0x9fa)]&&(!_0x2b645c['requestKeyframe']&&(_0x55fd2e[_0x1ea5f(0x2b9)](JSON[_0x1ea5f(0x479)]({'kf':!![]})),_0x2b645c['requestKeyframe']=setTimeout(function(){var _0x24916a=_0x1ea5f;clearTimeout(_0x2b645c[_0x24916a(0x5ff)]),_0x2b645c[_0x24916a(0x5ff)]=null;},0x3e8)));},_0x2b645c[_0x5afb41(0x7ed)]=async function(_0x3a6843){var _0x218965=_0x5afb41;if(!_0x2b645c[_0x218965(0x433)]){errorlog(_0x218965(0x946));return;}try{_0x3a6843['type']='key',_0x3a6843=new EncodedAudioChunk(_0x3a6843);}catch(_0xf4181e){return;}_0x2b645c[_0x218965(0x203)]&&_0x2b645c[_0x218965(0x30c)][_0x218965(0x6a7)]&&_0x2b645c[_0x218965(0x49b)][_0x218965(0x6da)]&&_0x2b645c[_0x218965(0x203)][_0x218965(0x641)](_0x3a6843),_0x2b645c['audio'][_0x218965(0x28b)][_0x218965(0x5ae)]==='closed'&&(_0x2b645c[_0x218965(0x433)][_0x218965(0x28b)]=new AudioDecoder(_0x2b645c[_0x218965(0x82e)]),_0x2b645c[_0x218965(0x433)][_0x218965(0x28b)][_0x218965(0x50e)](_0x2b645c['stream_configAudio'])),_0x2b645c[_0x218965(0x433)][_0x218965(0x28b)][_0x218965(0x5f5)](_0x3a6843);};}else{if(_0x2b645c['audio']&&_0x2ecb3b[_0x5afb41(0x747)])_0x2b645c['audio'][_0x5afb41(0x3c5)]=_0x2ecb3b['realTimeAudio'];else _0x2b645c['video']&&_0x2ecb3b[_0x5afb41(0x8f0)]?_0x2b645c['video']['realTime']=_0x2ecb3b[_0x5afb41(0x8f0)]:errorlog(_0x2ecb3b);}return;}catch(_0x3ed2e4){errorlog(_0x3ed2e4);}try{var _0x18043a=_0x4fe1ed[_0x5afb41(0x3bc)];if(typeof _0x18043a==_0x5afb41(0xa46)){if(_0x2b645c[_0x5afb41(0xa33)]){var _0x527795=new Int8Array(_0x18043a['buffer']);_0x2b645c['buffer']=![],await _0x2b645c[_0x5afb41(0x4e3)]({'data':_0x527795,'timestamp':_0x2b645c[_0x5afb41(0x481)][0x0],'type':_0x2b645c[_0x5afb41(0x481)][0x1]});}_0x2b645c[_0x5afb41(0x481)]=JSON[_0x5afb41(0x52d)](_0x18043a);}else{try{if(_0x18043a[_0x5afb41(0x7ce)]>=0x40000){if(_0x2b645c[_0x5afb41(0xa33)]){_0x18043a=new Int8Array(_0x18043a);var _0x527795=new Int8Array(_0x2b645c[_0x5afb41(0xa33)][_0x5afb41(0x8e9)]+_0x18043a[_0x5afb41(0x8e9)]);_0x527795[_0x5afb41(0x5d7)](_0x2b645c[_0x5afb41(0xa33)]),_0x527795[_0x5afb41(0x5d7)](_0x18043a,_0x2b645c['buffer'][_0x5afb41(0x8e9)]),_0x2b645c['buffer']=_0x527795;}else _0x2b645c[_0x5afb41(0xa33)]=new Int8Array(_0x18043a);return;}else{if(_0x2b645c['buffer']){_0x18043a=new Int8Array(_0x18043a);var _0x527795=new Int8Array(_0x2b645c[_0x5afb41(0xa33)][_0x5afb41(0x8e9)]+_0x18043a['length']);_0x527795[_0x5afb41(0x5d7)](_0x2b645c[_0x5afb41(0xa33)]),_0x527795[_0x5afb41(0x5d7)](_0x18043a,_0x2b645c[_0x5afb41(0xa33)][_0x5afb41(0x8e9)]),_0x2b645c[_0x5afb41(0xa33)]=![],await _0x2b645c[_0x5afb41(0x4e3)]({'data':_0x527795,'timestamp':_0x2b645c[_0x5afb41(0x481)][0x0],'type':_0x2b645c['frameMeta'][0x1]});}else await _0x2b645c['processFrame']({'data':new Uint8Array(_0x18043a),'timestamp':_0x2b645c[_0x5afb41(0x481)][0x0],'type':_0x2b645c[_0x5afb41(0x481)][0x1]}),_0x2b645c[_0x5afb41(0x906)]&&_0x2b645c[_0x5afb41(0x906)]();}}catch(_0x158406){errorlog(_0x158406);}return;}}catch(_0x7bfb8d){errorlog(_0x7bfb8d);}};return;},_0x311669['setupIncoming']=async function(_0x311f09){var _0x3106c2=_0x134a17;log(_0x3106c2(0x53e));var _0x5be36a=_0x311f09[_0x3106c2(0x54a)];if(_0x5be36a in _0x311669[_0x3106c2(0x16a)]){if(_0x3106c2(0x1fd)in _0x311f09&&_0x311f09['session']){if(_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x1fd)]==_0x311f09[_0x3106c2(0x1fd)]){log(_0x3106c2(0x7fd));return;}warnlog(_0x3106c2(0x59f)),_0x311669[_0x3106c2(0x7b8)](_0x5be36a);}}else log(_0x3106c2(0x3ea));try{for(var _0x4b22a6 in _0x311669[_0x3106c2(0x16a)]){_0x311669[_0x3106c2(0x16a)][_0x4b22a6]['streamID']==_0x311f09[_0x3106c2(0x9bb)]&&(_0x311669[_0x3106c2(0x16a)][_0x4b22a6][_0x3106c2(0x751)]&&errorlog(_0x3106c2(0x216)),_0x311669[_0x3106c2(0x16a)][_0x4b22a6]['videoElement']&&(_0x311669[_0x3106c2(0x16a)][_0x4b22a6][_0x3106c2(0x49b)]['style'][_0x3106c2(0x355)]=_0x3106c2(0x42d)),warnlog(_0x3106c2(0x37d)),_0x311669['closeRPC'](_0x4b22a6),_0x4b22a6!==_0x5be36a&&(_0x4b22a6 in _0x311669[_0x3106c2(0x859)]&&(_0x311f09[_0x3106c2(0x1fd)]&&_0x311f09[_0x3106c2(0x1fd)][_0x3106c2(0x30d)](0x0,0x6)!==_0x311669[_0x3106c2(0x56b)]?(warnlog('CLOSING\x20SECONDARY\x20CONNECTION;\x20matched\x20stream\x20ID\x20has\x20re-connected'),log(_0x3106c2(0x3a9)),_0x311669[_0x3106c2(0x9d2)](_0x4b22a6,![])):warnlog(_0x3106c2(0x473)))));}document[_0x3106c2(0x5c8)](_0x3106c2(0x212))&&(document[_0x3106c2(0x5c8)](_0x3106c2(0x212))[_0x3106c2(0x826)][_0x3106c2(0x9d0)](document[_0x3106c2(0x5c8)](_0x3106c2(0x212))),document[_0x3106c2(0x319)](_0x3106c2(0xa3b))[_0x3106c2(0x982)](_0x5e1e9a=>{var _0x309cde=_0x3106c2;_0x5e1e9a[_0x309cde(0x424)][_0x309cde(0x761)]('hidden2');}));}catch(_0x56a4fc){errorlog(_0x56a4fc);}if(_0x311669[_0x3106c2(0x96f)]!==![]){if(Object['keys'](_0x311669[_0x3106c2(0x16a)])[_0x3106c2(0x8e9)]>=_0x311669[_0x3106c2(0x96f)]){warnlog(_0x3106c2(0x380));return;}}else{if(_0x311669[_0x3106c2(0x6b1)]!==![]){if(Object[_0x3106c2(0x19b)](_0x311669[_0x3106c2(0x16a)])[_0x3106c2(0x8e9)]+Object[_0x3106c2(0x19b)](_0x311669[_0x3106c2(0x859)])[_0x3106c2(0x8e9)]>=_0x311669['maxconnections']){warnlog('Publisher\x20will\x20be\x20ignored\x20due\x20to\x20max\x20connections\x20already\x20hit');return;}}}if(_0x311669[_0x3106c2(0x64f)]){if(_0x311669[_0x3106c2(0x75b)])!(_0x5be36a in _0x311669[_0x3106c2(0x859)])&&_0x311669[_0x3106c2(0x49a)](_0x5be36a);else{if(_0x311669[_0x3106c2(0x95b)][_0x3106c2(0x49e)](_0x5be36a)==-0x1)return;}}!_0x311669['configuration']&&await chooseBestTURN();_0x311669['encodedInsertableStreams']&&(_0x311669['configuration'][_0x3106c2(0xa6e)]=!![]);_0x311669[_0x3106c2(0x70d)]&&(_0x311669[_0x3106c2(0x7db)][_0x3106c2(0x9de)]=_0x311669[_0x3106c2(0x70d)]);try{_0x311669[_0x3106c2(0x16a)][_0x5be36a]=new RTCPeerConnection(_0x311669[_0x3106c2(0x7db)]);}catch(_0x532b76){!_0x311669[_0x3106c2(0x78c)]&&warnUser(_0x3106c2(0x342));errorlog(_0x532b76);return;}if(_0x311669[_0x3106c2(0x4e5)]){if(Object[_0x3106c2(0x19b)](_0x311669[_0x3106c2(0x16a)])['length']>0x1){warnlog(_0x3106c2(0xa0a)),log(_0x311669[_0x3106c2(0x16a)]),delete _0x311669['rpcs'][_0x5be36a],updateUserList();return;}else warnlog(_0x3106c2(0x8d3));}_0x311f09[_0x3106c2(0x9bb)]in _0x311669[_0x3106c2(0x8fb)]&&delete _0x311669[_0x3106c2(0x8fb)][_0x311f09[_0x3106c2(0x9bb)]];try{_0x311669['rpcs'][_0x5be36a][_0x3106c2(0x9bb)]=_0x311f09[_0x3106c2(0x9bb)],await checkDirectorStreamID();}catch(_0x580a57){errorlog(_0x580a57);return;}_0x311f09['session']?_0x311669[_0x3106c2(0x16a)][_0x5be36a]['session']=_0x311f09[_0x3106c2(0x1fd)]:_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x1fd)]=null;_0x311669[_0x3106c2(0x16a)][_0x5be36a]['activelySpeaking']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x84b)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['allowMIDI']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['allowGraphs']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x436)]={},_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x97a)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['stats']['Audio_Loudness']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['showDirector']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x45c)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['canvasIntervalAction']=null,_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x9cc)]=-0x1,_0x311669['rpcs'][_0x5be36a]['bandwidthMuted']=![],_0x311669['rpcs'][_0x5be36a][_0x3106c2(0xa33)]=![],_0x311669['rpcs'][_0x5be36a][_0x3106c2(0x921)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['channelWidth']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x53c)]=-0x1,_0x311669['rpcs'][_0x5be36a]['manualBandwidth']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x49b)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x678)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x9eb)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x532)]=[],_0x311669['rpcs'][_0x5be36a][_0x3106c2(0x8ab)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['iframeVideo']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x7b9)]=![],_0x311669['rpcs'][_0x5be36a][_0x3106c2(0x621)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['virtualHangup']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x94d)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['remoteMuteElement']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x9a4)]=null,_0x311669['rpcs'][_0x5be36a][_0x3106c2(0x600)]=![],_0x311669['rpcs'][_0x5be36a][_0x3106c2(0x4fc)]=null,_0x311669[_0x3106c2(0x16a)][_0x5be36a]['mutedStateMixer']=null,_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x3e1)]=null,_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x350)]=null,_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x665)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['rotate']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['savedVolume']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['scaleHeight']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x863)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x482)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x46b)]=![],_0x311669['rpcs'][_0x5be36a]['volumeControl']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x585)]=null,_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x2ef)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x701)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['director']=null,_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x3be)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x6f2)]=0x64,_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x426)]=0x0,_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x1a1)]=0x0,_0x311669[_0x3106c2(0x16a)][_0x5be36a]['settings']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['opacityDisconnect']='1',_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x644)]='1',_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x176)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x353)]=0x0,_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x89c)]=![],_0x311669['rpcs'][_0x5be36a][_0x3106c2(0xa63)]=![],_0x311669['rpcs'][_0x5be36a][_0x3106c2(0x237)]=null,_0x311669[_0x3106c2(0x16a)][_0x5be36a]['canvas']=null,_0x311669[_0x3106c2(0x16a)][_0x5be36a]['inboundAudioPipeline']={},_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x325)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x9b0)]=![],_0x311669['rpcs'][_0x5be36a][_0x3106c2(0x586)]=Date[_0x3106c2(0x610)](),_0x311669['rpcs'][_0x5be36a][_0x3106c2(0x87c)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x7af)]=_0x311669[_0x3106c2(0x7af)];(_0x311669[_0x3106c2(0x2d3)]==0x2||_0x311669[_0x3106c2(0x2d3)]==0x4)&&(_0x311669[_0x3106c2(0x16a)][_0x5be36a]['loudest']=!![]);if(_0x311669[_0x3106c2(0x277)]){var _0x5eee14=createRichVideoElement(_0x5be36a);_0x5eee14[_0x3106c2(0x886)][_0x3106c2(0x355)]=_0x3106c2(0xa87);}if(_0x311669['director']){if(_0x311669['customWSS']&&_0x3106c2(0x782)in _0x311f09&&_0x311f09['isScene']!==![]){}else{var _0x367427=soloLinkGenerator(_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x9bb)]);_0x3106c2(0x97a)in _0x311f09?createControlBox(_0x5be36a,_0x367427,_0x311669['rpcs'][_0x5be36a]['streamID'],_0x311f09['slot']):createControlBox(_0x5be36a,_0x367427,_0x311669['rpcs'][_0x5be36a]['streamID']);}}_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x54a)]=_0x5be36a;try{if(_0x311669[_0x3106c2(0x82b)]){if(_0x311669['view_set'][_0x3106c2(0x2c2)](_0x311669[_0x3106c2(0x16a)][_0x5be36a]['streamID'])){if(_0x311669[_0x3106c2(0x708)]!==![]){let _0x13b8a3=_0x311669[_0x3106c2(0x82b)][_0x3106c2(0x49e)](_0x311669['rpcs'][_0x5be36a][_0x3106c2(0x9bb)]);_0x311669['bitrate_set']['length']>_0x13b8a3&&(_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x3e4)]=parseInt(_0x311669['bitrate_set'][_0x13b8a3]),_0x311669[_0x3106c2(0x16a)][_0x5be36a]['manualBandwidth']<=0x0&&(_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x3e4)]=![]));}}}}catch(_0x5389e5){errorlog(_0x5389e5);}_0x311669[_0x3106c2(0x16a)][_0x5be36a]['onclose']=function(_0x4ff9d9){var _0x4b8997=_0x3106c2;log(_0x4b8997(0x169)),_0x311669[_0x4b8997(0x7b8)](_0x5be36a);},_0x311669['rpcs'][_0x5be36a]['iceTimer']=null,_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x2f5)]=[],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x2a3)]=function(_0x2957e2){var _0x583488=_0x3106c2;if(_0x2957e2['candidate']==null){log(_0x583488(0x499));_0x311669[_0x583488(0x16a)][_0x5be36a]&&_0x311669[_0x583488(0x16a)][_0x5be36a][_0x583488(0x204)]&&(_0x311669['rpcs'][_0x5be36a][_0x583488(0x204)]([..._0x311669[_0x583488(0x16a)][_0x5be36a]['iceBundle']]),clearTimeout(_0x311669[_0x583488(0x16a)][_0x5be36a][_0x583488(0x44b)]),_0x311669[_0x583488(0x16a)][_0x5be36a]['iceTimer']=null,_0x311669['rpcs'][_0x5be36a]['iceBundle']=[],_0x311669[_0x583488(0x16a)][_0x5be36a][_0x583488(0x204)]=null);return;}try{if(_0x311669[_0x583488(0x242)]){if(_0x2957e2['candidate'][_0x583488(0x905)]['indexOf'](_0x311669[_0x583488(0x242)])===-0x1){log(_0x583488(0x6f4));return;}else log(_0x2957e2[_0x583488(0x905)]);}}catch(_0x555678){errorlog(_0x555678);}if(_0x311669[_0x583488(0x16a)][_0x5be36a]&&(_0x311669[_0x583488(0x16a)][_0x5be36a][_0x583488(0x204)]||_0x311669[_0x583488(0x16a)][_0x5be36a][_0x583488(0x44b)]!==null)){_0x311669[_0x583488(0x16a)][_0x5be36a][_0x583488(0x2f5)]['push'](_0x2957e2[_0x583488(0x905)]);return;}_0x311669['rpcs'][_0x5be36a][_0x583488(0x2f5)]['push'](_0x2957e2[_0x583488(0x905)]),_0x311669['rpcs'][_0x5be36a]['iceTimer']=setTimeout(function(_0x30bb85){var _0x139c4d=_0x583488;if(!(_0x30bb85 in _0x311669['rpcs']))return;if(_0x311669[_0x139c4d(0x16a)][_0x30bb85]['whipCallback2'])return;_0x311669[_0x139c4d(0x16a)][_0x30bb85]['iceTimer']=null;if(_0x311669[_0x139c4d(0x16a)][_0x30bb85][_0x139c4d(0x2f5)]==[])return;var _0x22b9dc={};_0x22b9dc[_0x139c4d(0x54a)]=_0x30bb85,_0x22b9dc[_0x139c4d(0x78a)]=_0x139c4d(0x24d),_0x22b9dc['candidates']=_0x311669['rpcs'][_0x30bb85][_0x139c4d(0x2f5)],_0x22b9dc[_0x139c4d(0x1fd)]=_0x311669[_0x139c4d(0x16a)][_0x30bb85]['session'],_0x311669['rpcs'][_0x30bb85]['iceBundle']=[],_0x311669['password']?_0x311669[_0x139c4d(0x331)](JSON[_0x139c4d(0x479)](_0x22b9dc[_0x139c4d(0x2a8)]))[_0x139c4d(0x619)](function(_0x1aac6a){var _0x2046f3=_0x139c4d;_0x22b9dc[_0x2046f3(0x2a8)]=_0x1aac6a[0x0],_0x22b9dc[_0x2046f3(0x9ed)]=_0x1aac6a[0x1],_0x311669['anyrequest'](_0x22b9dc);})[_0x139c4d(0x3b6)](errorlog):_0x311669['anyrequest'](_0x22b9dc);},0x190,_0x5be36a);},_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x6e5)]=function(_0x5975ac){var _0x26fdf6=_0x3106c2;switch(this[_0x26fdf6(0x3ee)]){case _0x26fdf6(0x6bd):log('new'),log(_0x26fdf6(0x81c)),clearInterval(_0x311669['rpcs'][this[_0x26fdf6(0x54a)]]['closeTimeout']);case _0x26fdf6(0x56c):log('checking'),log(_0x26fdf6(0x2be)),clearInterval(_0x311669['rpcs'][this[_0x26fdf6(0x54a)]][_0x26fdf6(0x9a4)]);case _0x26fdf6(0x7ea):log('**\x20connected'),log(_0x26fdf6(0x5da)),clearInterval(_0x311669[_0x26fdf6(0x16a)][this[_0x26fdf6(0x54a)]][_0x26fdf6(0x9a4)]);if(_0x311669[_0x26fdf6(0x4e5)]){if(_0x311669['ws'][_0x26fdf6(0x8ee)]!==0x1){_0x311669['ws'][_0x26fdf6(0x1cf)]();break;}_0x311669['ws'][_0x26fdf6(0x1cf)](),setTimeout(function(){var _0x421ac3=_0x26fdf6;_0x311669[_0x421ac3(0x78c)]!=!![]&&warnUser(getTranslation(_0x421ac3(0x320)));},0x1);}break;case _0x26fdf6(0x381):log(_0x26fdf6(0x5d9)),warnlog('rpcs\x20onconnectionstatechange\x20Disconnected;\x20retry\x20in\x205s'),clearInterval(_0x311669[_0x26fdf6(0x16a)][this[_0x26fdf6(0x54a)]]['closeTimeout']);if(_0x311669[_0x26fdf6(0x16a)][this[_0x26fdf6(0x54a)]][_0x26fdf6(0x87c)])return;this[_0x26fdf6(0x54a)]in _0x311669[_0x26fdf6(0x16a)]?_0x311669[_0x26fdf6(0x16a)][this[_0x26fdf6(0x54a)]][_0x26fdf6(0x9a4)]=setTimeout(function(_0x47e983){var _0x31719c=_0x26fdf6;log('no\x20reconnect\x20even\x20after\x205s;\x20closing'),_0x311669[_0x31719c(0x7b8)](_0x47e983);},0x1388,this['UUID']):log(_0x26fdf6(0x26f));break;case'failed':warnlog(_0x26fdf6(0x5b2)),log(_0x26fdf6(0xa3a)),clearInterval(_0x311669[_0x26fdf6(0x16a)][this[_0x26fdf6(0x54a)]][_0x26fdf6(0x9a4)]);this[_0x26fdf6(0x54a)]in _0x311669['rpcs']?_0x311669['rpcs'][this['UUID']][_0x26fdf6(0x9a4)]=setTimeout(function(_0x545ebc){var _0x4f4738=_0x26fdf6;log('No\x20reconnect\x20even\x20after\x205s;\x20closing'),_0x311669[_0x4f4738(0x7b8)](_0x545ebc);},0xbb8,this[_0x26fdf6(0x54a)]):log(_0x26fdf6(0x26f));break;case _0x26fdf6(0x208):warnlog('RTC\x20closed'),_0x311669[_0x26fdf6(0x7b8)](this[_0x26fdf6(0x54a)]);break;default:log(_0x26fdf6(0x253)),log('this.connectionState:\x20'+this[_0x26fdf6(0x3ee)]),clearInterval(_0x311669[_0x26fdf6(0x16a)][this[_0x26fdf6(0x54a)]][_0x26fdf6(0x9a4)]);break;}},_0x311669['rpcs'][_0x5be36a]['onicegatheringstatechange']=function(_0x774723){var _0x3bccac=_0x3106c2;let _0x109569=_0x774723[_0x3bccac(0x81f)];switch(_0x109569[_0x3bccac(0x8b8)]){case _0x3bccac(0x87e):log(_0x3bccac(0xa52));break;case _0x3bccac(0x73e):log(_0x3bccac(0x60c));_0x311669[_0x3bccac(0x16a)][_0x5be36a][_0x3bccac(0x204)]&&(_0x311669[_0x3bccac(0x16a)][_0x5be36a]['whipCallback2']([..._0x311669[_0x3bccac(0x16a)][_0x5be36a][_0x3bccac(0x2f5)]]),clearTimeout(_0x311669[_0x3bccac(0x16a)][_0x5be36a][_0x3bccac(0x44b)]),_0x311669['rpcs'][_0x5be36a]['iceTimer']=null,_0x311669[_0x3bccac(0x16a)][_0x5be36a][_0x3bccac(0x2f5)]=[],_0x311669[_0x3bccac(0x16a)][_0x5be36a][_0x3bccac(0x204)]=null);break;}},_0x311669[_0x3106c2(0x16a)][_0x5be36a]['oniceconnectionstatechange']=function(){var _0x24eba5=_0x3106c2;try{if(this[_0x24eba5(0x870)]==_0x24eba5(0x208))errorlog(_0x24eba5(0x2a9));else{if(this[_0x24eba5(0x870)]==_0x24eba5(0x381)){if(_0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x87c)])return;warnlog(_0x24eba5(0xa4e)),_0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x15c)]='0',_0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x49b)]['style']['opacity']='0',_0x311669['rpcs'][_0x5be36a][_0x24eba5(0x221)]=setTimeout(function(_0x35be26){updateMixer();},0x1f4,_0x5be36a);}else this[_0x24eba5(0x870)]==_0x24eba5(0x190)?errorlog('ICE\x20FAILED'):(log(_0x24eba5(0x3f0)+this[_0x24eba5(0x870)]),_0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x221)]&&clearTimeout(_0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x221)]),_0x311669['rpcs'][_0x5be36a][_0x24eba5(0x49b)]&&_0x24eba5(0x486)in _0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x49b)][_0x24eba5(0x886)]?_0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x15c)]=='0'&&_0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x644)]=='1'?(_0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x49b)][_0x24eba5(0x886)][_0x24eba5(0x486)]='1',_0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x15c)]='1',updateMixer()):_0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x15c)]='1':_0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x15c)]='1');}}catch(_0x2831a7){}},_0x311669[_0x3106c2(0x16a)][_0x5be36a]['ondatachannel']=function(_0x49b3f7){var _0x471263=_0x3106c2;log(_0x49b3f7);if(_0x49b3f7[_0x471263(0x27a)][_0x471263(0x89c)]&&_0x49b3f7[_0x471263(0x27a)][_0x471263(0x89c)]!==_0x471263(0x73a)){if(_0x311669[_0x471263(0x2dc)][_0x471263(0x2c2)](_0x311669[_0x471263(0x16a)][_0x5be36a][_0x471263(0x9bb)]))return;_0x49b3f7['channel'][_0x471263(0x89c)]===_0x471263(0x88e)?_0x311669[_0x471263(0xa51)](_0x311669[_0x471263(0x16a)],_0x5be36a,_0x49b3f7[_0x471263(0x27a)]):_0x311669[_0x471263(0x1b5)](_0x311669[_0x471263(0x16a)],_0x5be36a,_0x49b3f7[_0x471263(0x27a)]);return;}_0x311669[_0x471263(0x16a)][_0x5be36a][_0x471263(0x4a7)]=_0x49b3f7['channel'],_0x311669[_0x471263(0x16a)][_0x5be36a][_0x471263(0x4a7)][_0x471263(0x54a)]=_0x5be36a,_0x311669[_0x471263(0x16a)][_0x5be36a]['receiveChannel']['onerror']=_0x55179f=>{var _0x50f892=_0x471263;warnlog(_0x55179f),log(_0x50f892(0x77b)+_0x5be36a);},_0x311669[_0x471263(0x16a)][_0x5be36a][_0x471263(0x4a7)][_0x471263(0x777)]=_0x2f1780=>{var _0x3be940=_0x471263,_0x58ad64={};_0x58ad64[_0x3be940(0x5bf)]=![],_0x58ad64['allowmidi']=![],_0x58ad64[_0x3be940(0x54d)]=![],_0x58ad64[_0x3be940(0x8b0)]=![],_0x58ad64[_0x3be940(0x433)]=![],_0x58ad64[_0x3be940(0x30c)]=![],_0x58ad64[_0x3be940(0x254)]=![],_0x58ad64['allowwebp']=![],_0x58ad64[_0x3be940(0x343)]=![],_0x58ad64[_0x3be940(0x4ea)]=![],_0x58ad64['allowchunked']=![];_0x311669['audioCodec']&&(_0x311669['audioCodec']==='red'||_0x311669['audioCodec']===_0x3be940(0x61d))&&(_0x58ad64['preferAudioCodec']=_0x311669['audioCodec']);try{if(_0x311669[_0x3be940(0x308)]!==![]){if(_0x311669[_0x3be940(0x308)]===!![])_0x58ad64[_0x3be940(0x343)]=!![],_0x58ad64['allowscreenvideo']=!![];else _0x311669[_0x3be940(0x308)][_0x3be940(0x2c2)](_0x311669['rpcs'][_0x5be36a][_0x3be940(0x9bb)])?(_0x58ad64[_0x3be940(0x343)]=!![],_0x58ad64[_0x3be940(0x4ea)]=!![]):(_0x58ad64['allowscreenaudio']=![],_0x58ad64['allowscreenvideo']=![]);}else _0x58ad64[_0x3be940(0x343)]=!![],_0x58ad64['allowscreenvideo']=!![];if(_0x58ad64['allowscreenvideo']){if(_0x311669[_0x3be940(0x711)]!==![])!_0x311669[_0x3be940(0x711)][_0x3be940(0x2c2)](_0x311669[_0x3be940(0x16a)][_0x5be36a]['streamID']+':s')&&(_0x58ad64[_0x3be940(0x4ea)]=![]);else{if(_0x311669[_0x3be940(0x254)]!==![]){if(_0x311669[_0x3be940(0x254)]!==null)_0x311669[_0x3be940(0x16a)][_0x5be36a][_0x3be940(0x9bb)]+':s'===_0x311669['broadcast']?_0x58ad64['broadcast']=!![]:_0x58ad64[_0x3be940(0x4ea)]=![];else _0x311669[_0x3be940(0x2f3)]&&(_0x5be36a==_0x311669['directorUUID']?_0x58ad64[_0x3be940(0x254)]=!![]:_0x58ad64[_0x3be940(0x4ea)]=![]);}else _0x311669[_0x3be940(0x430)]!==![]&&(_0x311669[_0x3be940(0x430)][_0x3be940(0x2c2)](_0x311669['rpcs'][_0x5be36a]['streamID']+':s')&&(_0x58ad64['video']=![]));}}_0x58ad64['allowscreenaudio']&&(_0x311669[_0x3be940(0x542)]!==![]&&(!_0x311669[_0x3be940(0x542)][_0x3be940(0x2c2)](_0x311669[_0x3be940(0x16a)][_0x5be36a]['streamID']+':s')&&(_0x58ad64[_0x3be940(0x343)]=![])));}catch(_0xc3970b){errorlog(_0xc3970b);}try{if(_0x311669[_0x3be940(0x711)]!==![])_0x311669[_0x3be940(0x711)][_0x3be940(0x2c2)](_0x311669[_0x3be940(0x16a)][_0x5be36a][_0x3be940(0x9bb)])?_0x58ad64[_0x3be940(0x30c)]=!![]:_0x58ad64[_0x3be940(0x30c)]=![];else{if(_0x311669['broadcast']!==![]){if(_0x311669[_0x3be940(0x254)]!==null)_0x311669[_0x3be940(0x16a)][_0x5be36a][_0x3be940(0x9bb)]===_0x311669[_0x3be940(0x254)]?(_0x58ad64[_0x3be940(0x254)]=!![],_0x58ad64['video']=!![]):_0x58ad64['video']=![];else _0x311669[_0x3be940(0x2f3)]&&(_0x5be36a==_0x311669['directorUUID']?(_0x58ad64[_0x3be940(0x254)]=!![],_0x58ad64['video']=!![]):_0x58ad64[_0x3be940(0x30c)]=![]);}else _0x311669['exclude']!==![]?_0x311669[_0x3be940(0x430)][_0x3be940(0x2c2)](_0x311669[_0x3be940(0x16a)][_0x5be36a][_0x3be940(0x9bb)])?_0x58ad64[_0x3be940(0x30c)]=![]:_0x58ad64[_0x3be940(0x30c)]=!![]:_0x58ad64[_0x3be940(0x30c)]=!![];}_0x311669['noaudio']!==![]?_0x311669['noaudio'][_0x3be940(0x2c2)](_0x311669[_0x3be940(0x16a)][_0x5be36a][_0x3be940(0x9bb)])?_0x58ad64[_0x3be940(0x433)]=!![]:_0x58ad64[_0x3be940(0x433)]=![]:_0x58ad64[_0x3be940(0x433)]=!![];_0x311669[_0x3be940(0x3a2)]!==![]?_0x311669['noiframe']['includes'](_0x311669[_0x3be940(0x16a)][_0x5be36a][_0x3be940(0x9bb)])?_0x58ad64[_0x3be940(0x54d)]=!![]:_0x58ad64[_0x3be940(0x54d)]=![]:_0x58ad64['iframe']=!![];if(_0x311669[_0x3be940(0x924)]!==![])_0x311669[_0x3be940(0x924)]['includes'](_0x311669[_0x3be940(0x16a)][_0x5be36a][_0x3be940(0x9bb)])?_0x58ad64['widget']=!![]:_0x58ad64[_0x3be940(0x8b0)]=![];else{if(_0x311669[_0x3be940(0x98d)]!==![])_0x58ad64[_0x3be940(0x8b0)]=![];else _0x311669['view']&&!_0x311669[_0x3be940(0x75b)]&&_0x311669[_0x3be940(0x7cb)]===![]?_0x58ad64['widget']=![]:_0x58ad64[_0x3be940(0x8b0)]=!![];}_0x311669[_0x3be940(0x8b4)]&&(_0x58ad64[_0x3be940(0x1c0)]=![]);_0x311669['hideDirector']&&(_0x58ad64[_0x3be940(0x3cb)]=_0x311669[_0x3be940(0x9fc)]);_0x311669[_0x3be940(0x272)]!==![]&&(!_0x311669['allowVideos'][_0x3be940(0x2c2)](_0x311669['rpcs'][_0x5be36a][_0x3be940(0x9bb)])&&(_0x58ad64[_0x3be940(0x30c)]=![],_0x58ad64[_0x3be940(0x433)]=![]));(_0x311669[_0x3be940(0x5b9)]||_0x311669['midiRemote'])&&(_0x58ad64['allowmidi']=_0x311669['midiIn']||_0x311669[_0x3be940(0x734)]);_0x58ad64[_0x3be940(0x5bf)]=!![];_0x311669['nodownloads']&&(_0x58ad64[_0x3be940(0x5bf)]=![]);_0x311669[_0x3be940(0x1e7)]?_0x58ad64[_0x3be940(0x9b8)]=![]:_0x58ad64[_0x3be940(0x9b8)]=!![];_0x311669[_0x3be940(0x24e)]&&(_0x311669[_0x3be940(0x24e)]==_0x3be940(0x352)||_0x311669[_0x3be940(0x24e)]==_0x3be940(0x359)||_0x311669[_0x3be940(0x24e)]=='jpeg')&&(_0x58ad64['allowwebp']=!![]);_0x311669['accept_layouts']&&(_0x58ad64['layout']=!![]);if(_0x311669['badStreamList'][_0x3be940(0x2c2)](_0x311669[_0x3be940(0x16a)][_0x5be36a][_0x3be940(0x9bb)])){warnlog('new\x20connection\x20is\x20contained\x20in\x20badStreamList!\x20This\x20might\x20be\x20the\x20director\x27s\x20video/audio\x20->\x20this\x20a\x20scene?'),_0x58ad64[_0x3be940(0x5bf)]=![],_0x58ad64['allowmidi']=![],_0x58ad64['iframe']=![],_0x58ad64[_0x3be940(0x8b0)]=![],_0x58ad64[_0x3be940(0x433)]=![],_0x58ad64['video']=![],_0x58ad64[_0x3be940(0x254)]=![],_0x58ad64[_0x3be940(0x81a)]=![];;}}catch(_0x40e611){errorlog(_0x40e611);}try{_0x58ad64[_0x3be940(0x6ee)]={},_0x58ad64['info']['label']=_0x311669['label'],_0x58ad64['info']['order']=_0x311669[_0x3be940(0xa63)],_0x58ad64['info'][_0x3be940(0x9d9)]=_0x311669['stereo'],_0x58ad64[_0x3be940(0x6ee)][_0x3be940(0x2c0)]=_0x311669[_0x3be940(0x739)],_0x58ad64[_0x3be940(0x6ee)][_0x3be940(0x16b)]=_0x311669[_0x3be940(0x543)],_0x58ad64['info']['codec_url']=_0x311669[_0x3be940(0x24e)];_0x311669[_0x3be940(0x292)]&&(_0x58ad64[_0x3be940(0x6ee)]['audio_codec_url']=_0x311669[_0x3be940(0x292)]);_0x58ad64[_0x3be940(0x6ee)][_0x3be940(0x3d3)]=_0x311669[_0x3be940(0x3d3)],_0x58ad64[_0x3be940(0x6ee)][_0x3be940(0x920)]=_0x311669[_0x3be940(0x920)],_0x58ad64[_0x3be940(0x6ee)]['enhance_audio']=_0x311669[_0x3be940(0x1a9)],_0x58ad64[_0x3be940(0x6ee)][_0x3be940(0x72b)]=_0x311669[_0x3be940(0x72b)],_0x58ad64['info'][_0x3be940(0x38b)]=_0x311669[_0x3be940(0x38b)],_0x58ad64[_0x3be940(0x6ee)][_0x3be940(0x5e9)]=_0x311669[_0x3be940(0x5e9)];navigator&&navigator[_0x3be940(0x64e)]&&(_0x58ad64[_0x3be940(0x6ee)][_0x3be940(0xa77)]=navigator['userAgent']);navigator&&navigator[_0x3be940(0x9f2)]&&(_0x58ad64['info'][_0x3be940(0x9f2)]=navigator[_0x3be940(0x9f2)]);gpgpuSupport&&(_0x58ad64[_0x3be940(0x6ee)][_0x3be940(0x6f5)]=gpgpuSupport);cpuSupport&&(_0x58ad64[_0x3be940(0x6ee)]['CPU']=cpuSupport);if(_0x311669['disableOBS']===![]){if(window['obsstudio']){_0x58ad64[_0x3be940(0x6ee)]['obs']=window[_0x3be940(0x6ff)][_0x3be940(0x51a)];try{_0x58ad64=_0x311669[_0x3be940(0xa4c)](_0x58ad64,_0x5be36a);}catch(_0x41924c){errorlog(_0x41924c),warnUser(_0x41924c[_0x3be940(0x6d8)]);}}else _0x58ad64[_0x3be940(0x6ee)]['obs']=![];}else _0x58ad64[_0x3be940(0x6ee)][_0x3be940(0x34c)]=![];}catch(_0x270115){};_0x58ad64[_0x3be940(0x798)]=![],_0x58ad64[_0x3be940(0x98d)]=![],_0x58ad64[_0x3be940(0x75b)]=![],_0x58ad64[_0x3be940(0x6f0)]=![],_0x58ad64[_0x3be940(0x920)]=![];_0x311669[_0x3be940(0x24d)]&&(_0x58ad64['remote']=!![]);_0x311669[_0x3be940(0x1a9)]&&(_0x58ad64[_0x3be940(0x391)]=!![]);_0x311669[_0x3be940(0x288)]&&(_0x58ad64['degrade']=_0x311669['degrade']);_0x311669['solo']&&(_0x58ad64[_0x3be940(0x438)]=_0x311669['solo']);_0x311669[_0x3be940(0x458)]!==![]&&(_0x58ad64['keyframeRate']=_0x311669['keyframeRate']);if(_0x311669['director']){_0x58ad64['director']=!![],_0x58ad64[_0x3be940(0x920)]=_0x311669[_0x3be940(0x920)];if(_0x311669['directorUUID']&&_0x311669[_0x3be940(0x2f3)]===_0x5be36a)_0x311669[_0x3be940(0x6ab)]();else{var _0x1d15b7={};_0x1d15b7[_0x3be940(0x1d7)]=[];for(var _0x45d1d8 in _0x311669['pcs']){_0x311669[_0x3be940(0x859)][_0x45d1d8][_0x3be940(0x5b5)]===!![]&&_0x1d15b7[_0x3be940(0x1d7)][_0x3be940(0x505)](_0x45d1d8);}_0x1d15b7[_0x3be940(0x1d7)][_0x3be940(0x8e9)]&&(_0x58ad64[_0x3be940(0x8ce)]=_0x1d15b7);}if(_0x311669[_0x3be940(0xa3c)]&&_0x311669['roomTimer']>0x0)_0x58ad64['setClock']=_0x311669[_0x3be940(0xa3c)]-Date['now']()/0x3e8,_0x58ad64[_0x3be940(0x5e8)]=!![],_0x58ad64[_0x3be940(0x876)]=!![];else _0x311669[_0x3be940(0xa3c)]&&_0x311669[_0x3be940(0xa3c)]<0x0&&(_0x58ad64[_0x3be940(0x7b3)]=_0x311669[_0x3be940(0xa3c)]*-0x1,_0x58ad64[_0x3be940(0x5e8)]=!![],_0x58ad64['startClock']=!![],_0x58ad64[_0x3be940(0x4dc)]=!![]);_0x311669['showRoomTime']&&(_0x58ad64[_0x3be940(0x6f3)]=!![]);}else{if(_0x311669[_0x3be940(0x98d)]!==![])_0x58ad64[_0x3be940(0x98d)]=_0x311669[_0x3be940(0x98d)],(_0x311669[_0x3be940(0x560)]||_0x311669[_0x3be940(0x438)])&&(_0x58ad64['showDirector']=_0x311669[_0x3be940(0x560)]||_0x311669[_0x3be940(0x438)]);else _0x311669[_0x3be940(0x4e1)]!==![]&&_0x311669[_0x3be940(0x4e1)]!==''&&(_0x58ad64['forceios']=_0x311669[_0x3be940(0x920)],_0x58ad64['guest']=!![]);}if(_0x311669[_0x3be940(0x6b5)])_0x58ad64[_0x3be940(0x6b5)]=parseFloat(_0x311669[_0x3be940(0x6b5)]);else(_0x311669[_0x3be940(0x3f5)]||_0x311669[_0x3be940(0x97c)])&&(_0x58ad64[_0x3be940(0x37b)]={},_0x58ad64['requestResolution']['h']=null,_0x58ad64[_0x3be940(0x37b)]['w']=null,_0x311669[_0x3be940(0x3f5)]&&(_0x58ad64[_0x3be940(0x37b)]['h']=_0x311669[_0x3be940(0x3f5)],_0x311669[_0x3be940(0x16a)][_0x5be36a][_0x3be940(0x1c6)]=_0x311669[_0x3be940(0x3f5)]),_0x311669[_0x3be940(0x97c)]&&(_0x58ad64[_0x3be940(0x37b)]['w']=_0x311669[_0x3be940(0x97c)],_0x311669[_0x3be940(0x16a)][_0x5be36a][_0x3be940(0x863)]=_0x311669['viewwidth']));!_0x311669[_0x3be940(0x4e1)]&&(_0x311669[_0x3be940(0x3df)]&&(playtone(![],'jointone'),showNotification('There\x27s\x20a\x20new\x20incoming\x20connection.'))),_0x311669['rpcs'][_0x5be36a][_0x3be940(0x8e6)]=_0x58ad64,_0x311669[_0x3be940(0xa14)](_0x58ad64,_0x5be36a)?log(_0x3be940(0x945)):errorlog(_0x3be940(0x926)),pokeIframeAPI(_0x3be940(0x22d),!![],_0x5be36a),pokeIframeAPI(_0x3be940(0x759),!![],_0x5be36a),pokeAPI(_0x3be940(0x60a),_0x311669[_0x3be940(0x16a)][_0x5be36a]['streamID']);},_0x311669['rpcs'][_0x5be36a][_0x471263(0x4a7)][_0x471263(0x17a)]=async _0x2efe54=>{var _0x13d2e3=_0x471263;if(typeof _0x2efe54[_0x13d2e3(0x3bc)]=='object'){if(!_0x311669[_0x13d2e3(0x16a)][_0x5be36a][_0x13d2e3(0x678)]){_0x311669[_0x13d2e3(0x16a)][_0x5be36a]['imageElement']=document[_0x13d2e3(0xa4d)](_0x13d2e3(0x95d)),_0x311669[_0x13d2e3(0x16a)][_0x5be36a][_0x13d2e3(0x678)]['width']=0x10,_0x311669[_0x13d2e3(0x16a)][_0x5be36a][_0x13d2e3(0x678)][_0x13d2e3(0x5ec)]=0x9,_0x311669[_0x13d2e3(0x16a)][_0x5be36a][_0x13d2e3(0x678)]['style']['objectFit']='contain',_0x311669[_0x13d2e3(0x16a)][_0x5be36a]['imageElement'][_0x13d2e3(0x2bc)]['UUID']=_0x5be36a;try{_0x311669[_0x13d2e3(0x16a)][_0x5be36a]['imageElement'][_0x13d2e3(0x2bc)]['sid']=_0x311669[_0x13d2e3(0x16a)][_0x5be36a][_0x13d2e3(0x9bb)];}catch(_0x187310){}_0x311669[_0x13d2e3(0x16a)][_0x5be36a][_0x13d2e3(0x678)][_0x13d2e3(0x472)]=![],_0x311669[_0x13d2e3(0x16a)][_0x5be36a][_0x13d2e3(0x678)][_0x13d2e3(0x8bf)](_0x13d2e3(0x4dd),function(_0x2bbf05){var _0xce7059=_0x13d2e3;log(_0xce7059(0x779));try{if(_0x2bbf05[_0xce7059(0x65d)]||_0x2bbf05['metaKey']){_0x2bbf05['preventDefault']();if(_0x311669[_0xce7059(0x26d)]!==![]){var _0x2c282d=_0x2bbf05[_0xce7059(0x6c5)]['dataset'][_0xce7059(0x54a)];if('stats'in _0x311669['rpcs'][_0x2c282d]){var [_0x3a4073,_0x43d111]=statsMenuCreator();printViewStats(_0x43d111,_0x2c282d),_0x3a4073[_0xce7059(0x754)]=setInterval(printViewStats,_0x311669[_0xce7059(0x2ae)],_0x43d111,_0x2c282d);}}return _0x2bbf05[_0xce7059(0x890)](),![];}}catch(_0x37665e){errorlog(_0x37665e);}}),updateMixer();}else _0x311669['rpcs'][_0x5be36a][_0x13d2e3(0x678)]['hidden']&&(_0x311669[_0x13d2e3(0x16a)][_0x5be36a][_0x13d2e3(0x678)][_0x13d2e3(0x472)]=![],_0x311669[_0x13d2e3(0x16a)][_0x5be36a][_0x13d2e3(0x678)][_0x13d2e3(0x886)]['visibility']=_0x13d2e3(0x6ef));_0x311669[_0x13d2e3(0x16a)][_0x5be36a]['imageElement'][_0x13d2e3(0x95a)]=window[_0x13d2e3(0x188)][_0x13d2e3(0x2b3)](new Blob([new Uint8Array(_0x2efe54[_0x13d2e3(0x3bc)])],{'type':_0x13d2e3(0x658)}));return;}try{var _0xfdd301=JSON[_0x13d2e3(0x52d)](_0x2efe54[_0x13d2e3(0x3bc)]);}catch(_0x2fd216){_0xfdd301=_0x2fd216[_0x13d2e3(0x3bc)];}_0xfdd301[_0x13d2e3(0x54a)]=_0x5be36a,_0x13d2e3(0x67b)in _0xfdd301?await _0x311669[_0x13d2e3(0x84d)](_0xfdd301,_0x5be36a+'_screen'):await _0x311669['processRPCSOnMessage'](_0xfdd301,_0x5be36a);},_0x311669[_0x471263(0x84d)]=async function(_0x4576bd,_0x17d4b3){var _0x33269f=_0x471263;if(_0x33269f(0xa83)in _0x4576bd){warnlog(_0x33269f(0x752)),_0x311669['closeRPC'](_0x17d4b3,!![]);return;}else{if(_0x33269f(0x66a)in _0x4576bd){var _0x34f872={};_0x34f872['pong']=_0x4576bd[_0x33269f(0x66a)],_0x311669[_0x33269f(0xa14)](_0x34f872,_0x17d4b3),warnlog(_0x33269f(0x974));return;}else{if(_0x33269f(0x92d)in _0x4576bd){warnlog('PONGED');return;}}}log('incoming\x20message\x20from\x20publisher'),log(_0x4576bd);var _0x12282c=![],_0x155255=![];if(_0x33269f(0x9f5)in _0x4576bd)_0x311669[_0x33269f(0x6d1)](_0x4576bd);else{if(_0x33269f(0x905)in _0x4576bd)_0x4576bd[_0x33269f(0x54a)]=_0x17d4b3,log(_0x33269f(0x8e2)),_0x311669['processIce'](_0x4576bd);else _0x33269f(0x2a8)in _0x4576bd&&(_0x4576bd[_0x33269f(0x54a)]=_0x17d4b3,log(_0x33269f(0x61b)),_0x311669[_0x33269f(0x411)](_0x4576bd));}_0x33269f(0x4fb)in _0x4576bd&&_0x506aab(_0x4576bd[_0x33269f(0x4fb)]);if(_0x33269f(0x485)in _0x4576bd){if(_0x4576bd[_0x33269f(0x485)]===_0x33269f(0x873))_0x311669[_0x33269f(0x772)]=![],!_0x311669['cleanOutput']&&(warnUser(getTranslation('director-denied'),0xbb8),miniTranslate(getById(_0x33269f(0x9d4)),_0x33269f(0x939)));else{if(_0x4576bd[_0x33269f(0x485)]===_0x33269f(0x1a6))!_0x311669[_0x33269f(0x78c)]&&warnUser(getTranslation('only-main-director'),0xbb8);else{if(!_0x311669['cleanOutput']){if(_0x311669['directorUUID']===_0x17d4b3)warnUser(getTranslation('request-failed'),0x1388);else _0x311669[_0x33269f(0x24d)]&&!_0x311669[_0x33269f(0x75b)]?warnUser(getTranslation(_0x33269f(0x3dd)),0x1388):warnUser(getTranslation('token-not-director'),0x1388);}else{if(_0x311669[_0x33269f(0x75b)])!_0x311669[_0x33269f(0x78c)]&&warnUser(_0x33269f(0x55b)+_0x4576bd[_0x33269f(0x485)]+')\x20failed\x20due\x20to\x20permissions\x20or\x20it\x20was\x20rejected\x20by\x20the\x20user',0x1388);else{if(!_0x311669[_0x33269f(0x78c)])_0x311669[_0x33269f(0x24d)]?warnUser(getTranslation(_0x33269f(0x1e2)),0x1388):warnUser(getTranslation(_0x33269f(0x9f9)),0x1388);else{}}}}}errorlog(_0x33269f(0x578)+_0x4576bd[_0x33269f(0x485)]+_0x33269f(0x1ed)+_0x311669[_0x33269f(0x75b)]),pokeIframeAPI('rejected',_0x4576bd[_0x33269f(0x485)],_0x17d4b3);return;}else{if(_0x33269f(0x634)in _0x4576bd){if(_0x4576bd[_0x33269f(0x634)]==='requestCoDirector'){if(_0x311669[_0x33269f(0x75b)]){try{_0x311669[_0x33269f(0x89c)]===![]&&(document['title']=getTranslation(_0x33269f(0x977)));}catch(_0x1d7b58){errorlog(_0x1d7b58);}!_0x311669[_0x33269f(0x78c)]&&!_0x311669[_0x33269f(0x772)]&&(warnUser(getTranslation(_0x33269f(0x58f)),0xbb8),miniTranslate(getById(_0x33269f(0x9d4)),_0x33269f(0x72d)),miniTranslate(getById('yourDirectorStatus'),'this-is-you')),!_0x311669[_0x33269f(0x772)]&&(_0x311669[_0x33269f(0x772)]=!![],pokeAPI(_0x33269f(0x1e0),!![]),_0x311669[_0x33269f(0x464)](_0x17d4b3));}}log('approved:\x20'+_0x4576bd['approved']),pokeIframeAPI(_0x33269f(0x634),_0x4576bd[_0x33269f(0x634)],_0x17d4b3);return;}}if(_0x33269f(0x325)in _0x4576bd)try{_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x325)]=_0x4576bd[_0x33269f(0x325)]||![];if(_0x311669[_0x33269f(0x75b)]){if(_0x311669['rpcs'][_0x17d4b3]['iframeSrc']){var _0x188995=document[_0x33269f(0xa4d)](_0x33269f(0x45d));_0x188995['innerText']=_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x325)],_0x188995['innerText']=_0x188995[_0x33269f(0x82f)],_0x188995=_0x188995[_0x33269f(0x232)]||_0x188995['innerText']||'',getById(_0x33269f(0x496)+_0x17d4b3)['innerHTML']='Shared\x20website:\x20=0x0&&(_0x311669[_0x33269f(0x7a8)]&&lowerhand()));_0x33269f(0x559)in _0x4576bd&&(_0x311669[_0x33269f(0x95b)][_0x33269f(0x49e)](_0x17d4b3)>=0x0&&(_0x311669[_0x33269f(0x559)]=_0x4576bd[_0x33269f(0x559)],pokeIframeAPI(_0x33269f(0x276),_0x311669[_0x33269f(0x559)]),_0x12282c=!![]));if(_0x33269f(0x50b)in _0x4576bd){_0x311669[_0x33269f(0x50b)]=![],_0x311669[_0x33269f(0x4ed)]=![];if(_0x311669[_0x33269f(0x254)]===![]){log(_0x4576bd);if(_0x311669[_0x33269f(0x95b)][_0x33269f(0x49e)](_0x17d4b3)>=0x0){if(_0x4576bd[_0x33269f(0x50b)]!==![]){if(_0x4576bd[_0x33269f(0x50b)]===_0x311669[_0x33269f(0x9bb)])_0x311669[_0x33269f(0x50b)]=!![];else{if(_0x311669[_0x33269f(0x82b)][_0x33269f(0x8e9)]&&!(_0x4576bd[_0x33269f(0x50b)]in _0x311669[_0x33269f(0x82b)]))warnlog(_0x33269f(0x70c)),_0x311669['infocus']=![];else{if(_0x311669['view']&&_0x311669['view']!==_0x4576bd['infocus'])warnlog('NOT\x20VIEW\x20TARGET'),_0x311669[_0x33269f(0x50b)]=![];else{if(_0x311669[_0x33269f(0x98d)]!==![]&&_0x311669[_0x33269f(0x2f3)]&&_0x311669[_0x33269f(0x2f3)]in _0x311669[_0x33269f(0x16a)]&&!_0x311669[_0x33269f(0x16a)][_0x311669[_0x33269f(0x2f3)]][_0x33269f(0x560)]&&_0x4576bd[_0x33269f(0x50b)]===_0x311669[_0x33269f(0x16a)][_0x311669[_0x33269f(0x2f3)]][_0x33269f(0x9bb)])warnlog(_0x33269f(0x68c)),_0x311669[_0x33269f(0x50b)]=![];else{for(var _0x4b58dc in _0x311669[_0x33269f(0x16a)]){if(_0x311669[_0x33269f(0x16a)][_0x4b58dc][_0x33269f(0x9bb)]===_0x4576bd[_0x33269f(0x50b)]){_0x311669['infocus']=_0x4b58dc;break;}}warnlog(_0x33269f(0x96e));}}}}}else _0x311669[_0x33269f(0x50b)]=![];_0x12282c=!![],_0x155255=!![],_0x311669['infocus']?_0x311669[_0x33269f(0x17e)]=!![]:_0x311669[_0x33269f(0x17e)]=![];}}}else{if('infocus2'in _0x4576bd){_0x311669['infocus']=![],_0x311669[_0x33269f(0x4ed)]=![];if(_0x311669[_0x33269f(0x254)]===![]){log(_0x4576bd);if(_0x311669[_0x33269f(0x95b)][_0x33269f(0x49e)](_0x17d4b3)>=0x0){if(_0x4576bd[_0x33269f(0x4ed)]!==![]){if(_0x4576bd[_0x33269f(0x4ed)]===_0x311669[_0x33269f(0x9bb)])_0x311669[_0x33269f(0x4ed)]=!![];else{if(_0x311669[_0x33269f(0x82b)]['length']&&!(_0x4576bd[_0x33269f(0x4ed)]in _0x311669['view_set']))warnlog('NOT\x20IN\x20VIEW\x20SET'),_0x311669[_0x33269f(0x4ed)]=![];else{if(_0x311669[_0x33269f(0x311)]&&_0x311669[_0x33269f(0x311)]!==_0x4576bd[_0x33269f(0x4ed)])warnlog(_0x33269f(0x58a)),_0x311669['infocus2']=![];else{if(_0x311669[_0x33269f(0x98d)]!==![]&&_0x311669['directorUUID']&&_0x311669[_0x33269f(0x2f3)]in _0x311669[_0x33269f(0x16a)]&&!_0x311669['rpcs'][_0x311669[_0x33269f(0x2f3)]][_0x33269f(0x560)]&&_0x4576bd[_0x33269f(0x4ed)]===_0x311669['rpcs'][_0x311669[_0x33269f(0x2f3)]][_0x33269f(0x9bb)])warnlog(_0x33269f(0x68c)),_0x311669[_0x33269f(0x4ed)]=![];else{for(var _0x4b58dc in _0x311669['rpcs']){if(_0x311669[_0x33269f(0x16a)][_0x4b58dc][_0x33269f(0x9bb)]===_0x4576bd[_0x33269f(0x4ed)]){_0x311669[_0x33269f(0x4ed)]=_0x4b58dc;break;}}warnlog(_0x33269f(0x96e));}}}}}else _0x311669['infocus2']=![];_0x311669['infocus2']?_0x311669[_0x33269f(0x17e)]=!![]:_0x311669[_0x33269f(0x17e)]=![],_0x12282c=!![],_0x155255=!![];}}}}_0x33269f(0x893)in _0x4576bd&&(log(_0x4576bd),_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x436)][_0x33269f(0x893)]=_0x4576bd[_0x33269f(0x893)],isIFrame&&parent['postMessage']({'sensors':_0x4576bd['sensors']},_0x311669[_0x33269f(0xa2e)]));_0x33269f(0x1e4)in _0x4576bd&&playbackMIDI(_0x4576bd[_0x33269f(0x1e4)]);_0x33269f(0x2e8)in _0x4576bd&&_0x4576bd['fileList']&&addDownloadLink(_0x4576bd[_0x33269f(0x2e8)],_0x17d4b3,_0x311669[_0x33269f(0x16a)]);_0x33269f(0x5b1)in _0x4576bd&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x1c9)]!==_0x4576bd['rotate_video']&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x1c9)]=_0x4576bd[_0x33269f(0x5b1)],_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x49b)]&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3]['videoElement'][_0x33269f(0x3bf)]=_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x1c9)]),_0x12282c=!![]));if(_0x33269f(0x6ee)in _0x4576bd){warnlog(_0x4576bd),_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x436)]['info']=_0x4576bd[_0x33269f(0x6ee)];_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x16f)]&&(!_0x311669[_0x33269f(0x495)]&&(_0x311669[_0x33269f(0x495)]=_0x4576bd['info']['autoSync'],_0x311669[_0x33269f(0x469)](_0x17d4b3)));if(_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x46b)]){if(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x436)][_0x33269f(0x6ee)][_0x33269f(0x632)])_0x311669['rpcs'][_0x17d4b3]['signalMeter'][_0x33269f(0x2bc)][_0x33269f(0x801)]='1';else _0x33269f(0x632)in _0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x436)]['info']&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x46b)][_0x33269f(0x2bc)][_0x33269f(0x801)]='0');}_0x33269f(0x201)in _0x4576bd[_0x33269f(0x6ee)]&&(_0x4576bd['info'][_0x33269f(0x201)]!==![]?(_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x176)]=_0x4576bd['info']['obs_control'],_0x311669[_0x33269f(0x601)](_0x33269f(0x910),_0x17d4b3)):_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x176)]=![]);if(_0x33269f(0x89c)in _0x4576bd[_0x33269f(0x6ee)])try{typeof _0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x89c)]==_0x33269f(0xa46)?_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x89c)]=sanitizeLabel(_0x4576bd['info']['label']):_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x89c)]=![],applyStyleEffect(_0x17d4b3),_0x311669[_0x33269f(0x75b)]&&setupGuestLabelControl(_0x17d4b3);}catch(_0x4e35b6){errorlog(_0x4e35b6);}if(_0x33269f(0xa63)in _0x4576bd[_0x33269f(0x6ee)])try{_0x311669[_0x33269f(0x16a)][_0x17d4b3]['order']=parseInt(_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0xa63)])||0x0;if(_0x311669['director']){var _0x2f58a5=document[_0x33269f(0x319)](_0x33269f(0x1a7)+_0x17d4b3+'\x22]');_0x2f58a5[0x0]&&(_0x2f58a5[0x0][_0x33269f(0x882)]=_0x311669['rpcs'][_0x17d4b3][_0x33269f(0xa63)]);}}catch(_0x599b9e){errorlog(_0x599b9e);}else _0x311669[_0x33269f(0x16a)][_0x17d4b3]['order']=0x0;if(_0x4576bd['info'][_0x33269f(0x3ae)])try{if(_0x311669['director']&&!_0x311669[_0x33269f(0x64f)]){var _0x2f58a5=document[_0x33269f(0x319)](_0x33269f(0x90d)+_0x17d4b3+'\x22]');_0x2f58a5[0x0]&&_0x2f58a5[0x0][_0x33269f(0x424)][_0x33269f(0x761)](_0x33269f(0x472));}}catch(_0x6cdb11){errorlog(_0x6cdb11);}if(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x7f3)])try{if(_0x33269f(0xa3d)in _0x4576bd[_0x33269f(0x6ee)]){if(_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0xa3d)]!==null){var _0x3e29e8=_0x311669[_0x33269f(0x16a)][_0x17d4b3]['batteryMeter'][_0x33269f(0xa5d)]('.battery-level');if(_0x3e29e8){var _0x31de5c=parseInt(_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x436)][_0x33269f(0x6ee)][_0x33269f(0xa3d)])||0x0;_0x31de5c>0x64&&(_0x31de5c=0x64);_0x31de5c<0x0&&(_0x31de5c=0x0);_0x3e29e8[_0x33269f(0x886)]['height']=parseInt(_0x31de5c)+'%';if(_0x31de5c<0xa)_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x7f3)][_0x33269f(0x424)][_0x33269f(0x761)](_0x33269f(0x1ad)),_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x7f3)][_0x33269f(0x424)][_0x33269f(0x517)](_0x33269f(0x38d));else _0x31de5c<0x19?(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x7f3)][_0x33269f(0x424)]['remove'](_0x33269f(0x38d)),_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x7f3)][_0x33269f(0x424)][_0x33269f(0x517)](_0x33269f(0x1ad))):(_0x311669[_0x33269f(0x16a)][_0x17d4b3]['batteryMeter']['classList'][_0x33269f(0x761)]('alert'),_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x7f3)][_0x33269f(0x424)][_0x33269f(0x761)](_0x33269f(0x1ad)));_0x31de5c<0x64&&_0x311669[_0x33269f(0x16a)][_0x17d4b3]['batteryMeter'][_0x33269f(0x424)]['remove'](_0x33269f(0x472)),_0x311669[_0x33269f(0x16a)][_0x17d4b3]['batteryMeter'][_0x33269f(0x29a)]=_0x31de5c+_0x33269f(0x85b);}}}_0x33269f(0x2eb)in _0x4576bd[_0x33269f(0x6ee)]&&(_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x2eb)]===![]?(_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x7f3)][_0x33269f(0x2bc)]['plugged']='0',_0x311669[_0x33269f(0x16a)][_0x17d4b3]['batteryMeter'][_0x33269f(0x424)]['remove']('hidden')):_0x311669[_0x33269f(0x16a)][_0x17d4b3]['batteryMeter']['dataset'][_0x33269f(0x487)]='1');}catch(_0x51cac6){errorlog(_0x51cac6);}if(_0x33269f(0x868)in _0x4576bd[_0x33269f(0x6ee)])try{_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x868)]?_0x311669['rpcs'][_0x17d4b3]['group']=_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x868)][_0x33269f(0x256)](','):_0x311669['rpcs'][_0x17d4b3]['group']=[],_0x311669[_0x33269f(0x75b)]?(initGroupButtons(_0x17d4b3),_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x532)][_0x33269f(0x8e9)]&&syncGroup(_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x532)],_0x17d4b3)):_0x12282c=!![];}catch(_0x3b043d){errorlog(_0x3b043d);}if(_0x33269f(0x225)in _0x4576bd[_0x33269f(0x6ee)])try{_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x94d)]=_0x4576bd['info'][_0x33269f(0x225)],_0x311669[_0x33269f(0x98d)]===![]&&(_0x311669['roomid']&&((!_0x311669[_0x33269f(0x78c)]||_0x311669[_0x33269f(0x75b)])&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x40b)]?_0x311669['rpcs'][_0x17d4b3]['remoteMuteState']?_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x40b)][_0x33269f(0x424)][_0x33269f(0x761)]('hidden'):_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x40b)][_0x33269f(0x424)][_0x33269f(0x517)](_0x33269f(0x472)):(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x40b)]=getById(_0x33269f(0x6a1))[_0x33269f(0x66c)](!![]),_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x40b)]['id']=_0x33269f(0x466)+_0x17d4b3,_0x311669[_0x33269f(0x16a)][_0x17d4b3]['remoteMuteState']?_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x40b)]['classList'][_0x33269f(0x761)](_0x33269f(0x472)):_0x311669[_0x33269f(0x16a)][_0x17d4b3]['remoteMuteElement'][_0x33269f(0x424)][_0x33269f(0x517)]('hidden'),_0x12282c=!![])))),pokeIframeAPI(_0x33269f(0xa15),_0x311669[_0x33269f(0x16a)][_0x17d4b3]['remoteMuteState'],_0x17d4b3);}catch(_0x52f187){errorlog(_0x52f187);}if(_0x311669[_0x33269f(0x75b)]){try{_0x33269f(0x996)in _0x4576bd[_0x33269f(0x6ee)]&&(_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x996)]==![]&&initRecordingImpossible(_0x17d4b3));}catch(_0x56fa72){errorlog(_0x56fa72);}try{if('recording_audio_gain'in _0x4576bd[_0x33269f(0x6ee)]){if(_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x3a7)]!==![]){let _0x328b8e=parseInt(_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x3a7)])||0x0;initAudioButtons(_0x328b8e,_0x17d4b3);}}}catch(_0xe5d4b7){errorlog(_0xe5d4b7);}try{'directorSpeakerMuted'in _0x4576bd[_0x33269f(0x6ee)]&&(_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x5c9)]&&updateRemoteSpeakerMute(_0x17d4b3));}catch(_0x25c388){errorlog(_0x25c388);}try{_0x33269f(0x285)in _0x4576bd[_0x33269f(0x6ee)]&&(_0x4576bd[_0x33269f(0x6ee)]['directorDisplayMuted']&&updateRemoteDisplayMute(_0x17d4b3));}catch(_0x472405){errorlog(_0x472405);}}if(_0x33269f(0x3be)in _0x4576bd[_0x33269f(0x6ee)])try{_0x311669[_0x33269f(0x75b)]?_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x3be)]&&updateDirectorVideoMute(_0x17d4b3):(_0x311669['rpcs'][_0x17d4b3]['directorVideoMuted']=_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x3be)],_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x3be)]&&(_0x17d4b3 in _0x311669[_0x33269f(0x16a)]&&_0x311669['requestRateLimit'](0x0,_0x17d4b3)));}catch(_0x4119ce){errorlog(_0x4119ce);}if('directorMirror'in _0x4576bd['info'])try{_0x311669[_0x33269f(0x75b)]&&(_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x317)]&&(getById('container_'+_0x17d4b3)['querySelector'](_0x33269f(0x214))&&(getById(_0x33269f(0x2f7)+_0x17d4b3)[_0x33269f(0xa5d)]('[data-action-type=\x22mirror-guest\x22]')[_0x33269f(0x424)][_0x33269f(0x517)](_0x33269f(0x620)),getById('container_'+_0x17d4b3)[_0x33269f(0xa5d)](_0x33269f(0x214))[_0x33269f(0x238)]=_0x33269f(0x8fc)))),_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x350)]=_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x317)],_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x49b)]&&applyMirrorGuest(_0x311669[_0x33269f(0x16a)][_0x17d4b3]['mirrorState'],_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x49b)]);}catch(_0x4e006a){errorlog(_0x4e006a);}if(_0x33269f(0x3ed)in _0x4576bd['info'])try{_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x8ab)]=_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x3ed)],_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x8ab)]&&(_0x311669['director']&&_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x82a)][_0x33269f(0x424)]['remove']('hidden')),pokeIframeAPI('remote-video-mute-state',_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x8ab)],_0x17d4b3);}catch(_0x313480){errorlog(_0x313480);}_0x33269f(0x5b1)in _0x4576bd[_0x33269f(0x6ee)]&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x1c9)]!==_0x4576bd[_0x33269f(0x6ee)]['rotate_video']&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3]['rotate']=_0x4576bd['info'][_0x33269f(0x5b1)],_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x49b)]&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x49b)][_0x33269f(0x3bf)]=_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x1c9)]),_0x12282c=!![])),_0x33269f(0x864)in _0x4576bd[_0x33269f(0x6ee)]&&(_0x4576bd[_0x33269f(0x6ee)]['room_init']===![]&&soloLinkGeneratorInit(_0x17d4b3)),directorCoDirectorColoring(_0x17d4b3),_0x155255=!![],pokeAPI(_0x33269f(0x910),getDetailedState(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x9bb)])),pokeIframeAPI(_0x33269f(0x7cf),_0x4576bd['info'],_0x17d4b3);}_0x33269f(0x48f)in _0x4576bd&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x436)]&&_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x436)][_0x33269f(0x6ee)]&&processMiniInfoUpdate(_0x4576bd[_0x33269f(0x48f)],_0x17d4b3));if(_0x4576bd['directorSettings']){_0x311669['rpcs'][_0x17d4b3]['director']=!![];_0x4576bd['directorSettings'][_0x33269f(0xa73)]&&await checkToken();if(_0x311669['directorUUID']===_0x17d4b3){_0x33269f(0x32f)in _0x4576bd['directorSettings']&&(_0x311669['totalRoomBitrate']=parseInt(_0x4576bd[_0x33269f(0x8ce)][_0x33269f(0x32f)])||0x0,_0x12282c=!![]);if(_0x4576bd['directorSettings'][_0x33269f(0x936)]){if(_0x311669[_0x33269f(0x254)]===![]){if(_0x4576bd['directorSettings'][_0x33269f(0x936)]===_0x311669[_0x33269f(0x9bb)])_0x311669[_0x33269f(0x50b)]=!![];else for(var _0x4b58dc in _0x311669[_0x33269f(0x16a)]){if(_0x311669['rpcs'][_0x4b58dc]['streamID']===_0x4576bd[_0x33269f(0x8ce)][_0x33269f(0x936)]){if((_0x311669['directorList']['includes'](_0x4b58dc)||_0x311669[_0x33269f(0x16a)][_0x4b58dc][_0x33269f(0x75b)])&&!_0x311669[_0x33269f(0x560)])break;_0x311669[_0x33269f(0x50b)]=_0x4b58dc;break;}}_0x12282c=!![],_0x155255=!![];}}if('showDirector'in _0x4576bd[_0x33269f(0x8ce)]){if(_0x311669[_0x33269f(0x98d)]!==![]){if(_0x311669[_0x33269f(0x560)])_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x560)]=_0x311669[_0x33269f(0x560)];else _0x4576bd[_0x33269f(0x8ce)][_0x33269f(0x560)]&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3]['showDirector']=_0x4576bd[_0x33269f(0x8ce)][_0x33269f(0x560)]);}}if(_0x311669['scene']!==![]){if(_0x4576bd['directorSettings'][_0x33269f(0x98d)])for(var _0x4b58dc in _0x4576bd[_0x33269f(0x8ce)]['scene']){setTimeout(function(_0x45d740){var _0xec9b72=_0x33269f;_0x311669[_0xec9b72(0xa37)](_0x45d740);},0x3e8,_0x4576bd[_0x33269f(0x8ce)][_0x33269f(0x98d)][_0x4b58dc]);}if(_0x4576bd[_0x33269f(0x8ce)][_0x33269f(0xa26)])for(var _0x4b58dc in _0x4576bd[_0x33269f(0x8ce)][_0x33269f(0xa26)]){setTimeout(function(_0x5083c8){var _0x457923=_0x33269f;_0x311669[_0x457923(0xa37)](_0x5083c8);},0x3e8,_0x4576bd[_0x33269f(0x8ce)]['mute'][_0x4b58dc]);}}if(_0x33269f(0x1d7)in _0x4576bd[_0x33269f(0x8ce)])for(var _0x133441=0x0;_0x133441<_0x4576bd[_0x33269f(0x8ce)][_0x33269f(0x1d7)][_0x33269f(0x8e9)];_0x133441++){!_0x311669[_0x33269f(0x95b)][_0x33269f(0x2c2)](_0x4576bd[_0x33269f(0x8ce)][_0x33269f(0x1d7)][_0x133441]['toString'])&&(_0x311669[_0x33269f(0x95b)][_0x33269f(0x505)](_0x4576bd[_0x33269f(0x8ce)][_0x33269f(0x1d7)][_0x133441][_0x33269f(0x721)]()),addDirectorBlue(_0x4576bd[_0x33269f(0x8ce)][_0x33269f(0x1d7)][_0x133441][_0x33269f(0x721)]()));}}}if(_0x311669[_0x33269f(0x95b)][_0x33269f(0x49e)](_0x17d4b3)>=0x0){_0x311669[_0x33269f(0x98d)]!==![]&&(_0x33269f(0x95f)in _0x4576bd&&_0x311669[_0x33269f(0xa37)](_0x4576bd));_0x33269f(0x8ce)in _0x4576bd&&_0x4576bd[_0x33269f(0x8ce)][_0x33269f(0x9ba)]&&(!_0x311669[_0x33269f(0x75b)]&&(_0x311669[_0x33269f(0x98d)]===![]&&(_0x311669[_0x33269f(0x285)]=!![],_0x311669[_0x33269f(0x324)]())));if(_0x33269f(0x2d8)in _0x4576bd&&_0x33269f(0x402)in _0x4576bd){if(_0x4576bd[_0x33269f(0x402)]&&_0x4576bd[_0x33269f(0x402)]===!![]){_0x311669[_0x33269f(0xa7f)]=_0x4576bd['mirrorGuestState'],applyMirror(_0x311669['mirrorExclude']);if(_0x311669[_0x33269f(0x75b)]){if(_0x4576bd[_0x33269f(0x6ee)]['directorMirror']){if(getById(_0x33269f(0x2f1))[_0x33269f(0xa5d)]('[data-action-type=\x22mirror-guest\x22]'))getById(_0x33269f(0x2f1))[_0x33269f(0xa5d)](_0x33269f(0x214))['classList'][_0x33269f(0x517)](_0x33269f(0x620)),getById(_0x33269f(0x2f1))[_0x33269f(0xa5d)](_0x33269f(0x214))[_0x33269f(0x238)]='true';else getById(_0x33269f(0x2f1))[_0x33269f(0xa5d)](_0x33269f(0x214))&&(getById(_0x33269f(0x2f1))[_0x33269f(0xa5d)](_0x33269f(0x214))['classList'][_0x33269f(0x761)](_0x33269f(0x620)),getById(_0x33269f(0x2f1))[_0x33269f(0xa5d)](_0x33269f(0x214))['ariaPressed']=_0x33269f(0x309));}}}else{if(_0x4576bd[_0x33269f(0x402)]&&_0x4576bd[_0x33269f(0x402)]in _0x311669[_0x33269f(0x16a)]){_0x311669[_0x33269f(0x16a)][_0x4576bd[_0x33269f(0x402)]][_0x33269f(0x350)]=_0x4576bd[_0x33269f(0x2d8)];_0x311669['rpcs'][_0x4576bd[_0x33269f(0x402)]][_0x33269f(0x49b)]&&applyMirrorGuest(_0x4576bd['mirrorGuestState'],_0x311669[_0x33269f(0x16a)][_0x4576bd[_0x33269f(0x402)]]['videoElement']);if(_0x311669[_0x33269f(0x75b)]){if(_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x317)])getById(_0x33269f(0x2f7)+_0x17d4b3)[_0x33269f(0xa5d)](_0x33269f(0x214))&&(getById('container_'+_0x17d4b3)[_0x33269f(0xa5d)](_0x33269f(0x214))[_0x33269f(0x424)][_0x33269f(0x517)]('pressed'),getById(_0x33269f(0x2f7)+_0x17d4b3)['querySelector'](_0x33269f(0x214))[_0x33269f(0x238)]='true');else getById(_0x33269f(0x2f7)+_0x17d4b3)[_0x33269f(0xa5d)](_0x33269f(0x214))&&(getById(_0x33269f(0x2f7)+_0x17d4b3)[_0x33269f(0xa5d)](_0x33269f(0x214))[_0x33269f(0x424)][_0x33269f(0x761)]('pressed'),getById('container_'+_0x17d4b3)[_0x33269f(0xa5d)](_0x33269f(0x214))[_0x33269f(0x238)]=_0x33269f(0x309));}}}}if(_0x33269f(0x772)in _0x4576bd){_0x311669[_0x33269f(0x78e)]=_0x4576bd['directorState'],log(_0x4576bd);for(var _0xe307a5 in _0x311669[_0x33269f(0x78e)]){syncSceneState(_0xe307a5),syncOtherState(_0xe307a5);}}if(_0x33269f(0x2f6)in _0x4576bd){_0x311669['widget']=_0x4576bd[_0x33269f(0x2f6)]||![];let _0x43d2be=document['getElementById'](_0x33269f(0x8b0));try{_0x43d2be?!_0x311669[_0x33269f(0x8b0)]?(document['getElementById'](_0x33269f(0x8b0))[_0x33269f(0x761)](),_0x12282c=!![]):_0x43d2be[_0x33269f(0x95a)]=parseURL4Iframe(_0x311669[_0x33269f(0x8b0)]):_0x12282c=!![],_0x311669[_0x33269f(0x75b)]&&(getById(_0x33269f(0x15e))[_0x33269f(0x8d7)]=_0x311669[_0x33269f(0x8b0)]||'');}catch(_0x434f23){errorlog(_0x434f23);}pokeIframeAPI('widget-src',_0x311669['widget'],_0x17d4b3);}if(_0x33269f(0x364)in _0x4576bd){_0x311669[_0x33269f(0x9aa)]=_0x4576bd['slotsUpdate'];if(!_0x311669[_0x33269f(0x6b3)]()){if(_0x311669['layout']){_0x311669[_0x33269f(0x559)]=combinedLayoutSimple(_0x311669[_0x33269f(0x559)]);;updateMixer();}}warnlog(_0x4576bd);}'layouts'in _0x4576bd&&(_0x311669[_0x33269f(0x993)]=_0x4576bd[_0x33269f(0x993)],_0x33269f(0x692)in _0x4576bd?(_0x311669[_0x33269f(0x692)]=_0x4576bd['obsSceneTriggers'],_0x311669[_0x33269f(0x6b3)]()):_0x311669['obsSceneTriggers']=![]);}if(_0x33269f(0xa63)in _0x4576bd){_0x311669[_0x33269f(0x16a)][_0x17d4b3]['order']=parseInt(_0x4576bd['order'])||0x0;_0x17d4b3 in _0x311669[_0x33269f(0x859)]&&(_0x311669[_0x33269f(0x859)][_0x17d4b3]['order']=parseInt(_0x4576bd[_0x33269f(0xa63)])||0x0);if(_0x311669['director']){var _0x2f58a5=document['querySelectorAll'](_0x33269f(0x1a7)+_0x17d4b3+'\x22]');_0x2f58a5[0x0]&&(_0x2f58a5[0x0][_0x33269f(0x882)]=parseInt(_0x4576bd['order'])||0x0);}_0x12282c=!![];}if(_0x33269f(0x7c9)in _0x4576bd){log(_0x33269f(0x21c));if('value'in _0x4576bd){log(_0x33269f(0x7e2));if(typeof _0x4576bd[_0x33269f(0x8d7)]==_0x33269f(0xa46)){_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x89c)]=sanitizeLabel(_0x4576bd[_0x33269f(0x8d7)]);_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x89c)]['length']==0x0&&(_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x89c)]=![]);applyStyleEffect(_0x17d4b3);if(_0x311669[_0x33269f(0x75b)])updateLabelDirectors(_0x17d4b3);else _0x311669[_0x33269f(0x901)]&&(_0x12282c=!![]);}else{_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x89c)]=![],applyStyleEffect(_0x17d4b3);if(_0x311669[_0x33269f(0x75b)])updateLabelDirectors2(_0x17d4b3);else _0x311669[_0x33269f(0x901)]&&(_0x12282c=!![]);}_0x155255=!![],pokeIframeAPI('remote-label-changed',_0x311669['rpcs'][_0x17d4b3]['label'],_0x17d4b3);}}_0x33269f(0x3c2)in _0x4576bd&&(log(_0x4576bd),_0x311669[_0x33269f(0x16a)][_0x17d4b3]['remoteMuteState']=_0x4576bd['muteState'],_0x311669[_0x33269f(0x5d5)](![],_0x17d4b3),_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x436)][_0x33269f(0x6ee)]&&(_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x436)][_0x33269f(0x6ee)]['muted']=_0x311669[_0x33269f(0x16a)][_0x17d4b3]['remoteMuteState']),_0x311669[_0x33269f(0x98d)]===![]&&(_0x311669[_0x33269f(0x4e1)]&&((!_0x311669[_0x33269f(0x78c)]||_0x311669[_0x33269f(0x75b)])&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3]['remoteMuteElement']?_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x94d)]?_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x40b)]['classList'][_0x33269f(0x761)](_0x33269f(0x472)):_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x40b)][_0x33269f(0x424)]['add'](_0x33269f(0x472)):(_0x311669[_0x33269f(0x16a)][_0x17d4b3]['remoteMuteElement']=getById(_0x33269f(0x6a1))[_0x33269f(0x66c)](!![]),_0x311669[_0x33269f(0x16a)][_0x17d4b3]['remoteMuteElement']['id']=_0x33269f(0x466)+_0x17d4b3,_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x94d)]?_0x311669[_0x33269f(0x16a)][_0x17d4b3]['remoteMuteElement'][_0x33269f(0x424)]['remove'](_0x33269f(0x472)):_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x40b)]['classList'][_0x33269f(0x517)](_0x33269f(0x472)),_0x12282c=!![]),_0x155255=!![]))),pokeAPI(_0x33269f(0x725),_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x94d)],_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x9bb)]),pokeIframeAPI(_0x33269f(0xa15),_0x4576bd['muteState'],_0x17d4b3));if(_0x33269f(0x63f)in _0x4576bd){var _0x56bb50=getChromiumVersion();_0x56bb50&&(_0x56bb50<0x50&&(_0x12282c=!![]));}if(_0x33269f(0x8ab)in _0x4576bd){log(_0x33269f(0x2d9)+_0x4576bd[_0x33269f(0x8ab)]),_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x8ab)]=_0x4576bd[_0x33269f(0x8ab)];_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x8ab)]?(!_0x311669[_0x33269f(0x166)]&&_0x311669[_0x33269f(0x5d5)](0x0,_0x17d4b3),_0x311669[_0x33269f(0x16a)][_0x17d4b3]['imageElement']&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3]['imageElement'][_0x33269f(0x472)]=!![],_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x678)]['style'][_0x33269f(0x4eb)]=_0x33269f(0x472))):updateIncomingVideoElement(_0x17d4b3,!![],![]);_0x12282c=!![];_0x311669[_0x33269f(0x75b)]&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x8ab)]?_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x82a)][_0x33269f(0x424)][_0x33269f(0x761)]('hidden'):_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x82a)][_0x33269f(0x424)][_0x33269f(0x517)](_0x33269f(0x472)));if(_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x9cf)]&&_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x8ab)])setTimeout(function(){activeSpeaker();},0x0);else!_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x8ab)]&&setTimeout(function(){activeSpeaker();},0x0);_0x155255=!![],pokeAPI('remoteVideoMuted',_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x8ab)],_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x9bb)]),pokeIframeAPI(_0x33269f(0x77e),_0x4576bd['videoMuted'],_0x17d4b3);}if('screenStopped'in _0x4576bd){if(_0x17d4b3+_0x33269f(0x33b)in _0x311669[_0x33269f(0x16a)]){_0x311669[_0x33269f(0x16a)][_0x17d4b3+_0x33269f(0x33b)]['virtualHangup']=_0x4576bd[_0x33269f(0x19c)];try{_0x311669['rpcs'][_0x17d4b3+_0x33269f(0x33b)]['virtualHangup']&&(!(SafariVersion&&SafariVersion>0x10)&&(iPad||iOS)&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3+_0x33269f(0x33b)][_0x33269f(0x49b)][_0x33269f(0x932)]=!![]));}catch(_0x5b913b){}_0x311669['director']&&(_0x4576bd[_0x33269f(0x19c)]?getById('container_'+_0x17d4b3+_0x33269f(0x33b))[_0x33269f(0x424)][_0x33269f(0x517)](_0x33269f(0x9f6)):getById(_0x33269f(0x2f7)+_0x17d4b3+_0x33269f(0x33b))[_0x33269f(0x424)][_0x33269f(0x761)](_0x33269f(0x9f6))),_0x12282c=!![],_0x155255=!![];}}_0x33269f(0x701)in _0x4576bd&&(_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x701)]=_0x4576bd['screenShareState'],_0x12282c=!![],pokeIframeAPI(_0x33269f(0x7d5),_0x4576bd[_0x33269f(0x701)],_0x17d4b3));if(_0x33269f(0x912)in _0x4576bd){if(!_0x311669['director']){if(_0x33269f(0x81f)in _0x4576bd){if(_0x311669[_0x33269f(0x95b)][_0x33269f(0x49e)](_0x17d4b3)>=0x0){var _0x35a28d=_0x4576bd[_0x33269f(0x81f)];if(_0x35a28d===!![])_0x311669[_0x33269f(0x3be)]=_0x4576bd[_0x33269f(0x912)];else _0x35a28d in _0x311669[_0x33269f(0x16a)]&&(_0x311669['rpcs'][_0x35a28d]['directorVideoMuted']=_0x4576bd[_0x33269f(0x912)],_0x311669[_0x33269f(0x16a)][_0x35a28d][_0x33269f(0x3be)]&&_0x311669[_0x33269f(0x5d5)](0x0,_0x35a28d),_0x12282c=!![]);}}}_0x155255=!![];}_0x33269f(0xa39)in _0x4576bd&&(!_0x311669[_0x33269f(0x75b)]&&(_0x311669[_0x33269f(0x95b)][_0x33269f(0x49e)](_0x17d4b3)>=0x0&&(_0x17d4b3 in _0x311669[_0x33269f(0x16a)]&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0xa39)]=_0x4576bd[_0x33269f(0xa39)],_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0xa39)]&&(_0x17d4b3 in _0x311669[_0x33269f(0x16a)]&&_0x311669['requestRateLimit'](0x0,_0x17d4b3)),_0x12282c=!![]))),_0x155255=!![]);if('requestFile'in _0x4576bd){log(_0x33269f(0xa6d));try{_0x311669[_0x33269f(0x8ca)](_0x17d4b3,_0x4576bd[_0x33269f(0x91a)]);}catch(_0x2beb44){errorlog(_0x2beb44);}}_0x33269f(0x6db)in _0x4576bd&&remoteStats(_0x4576bd,_0x17d4b3);if(_0x12282c)setTimeout(function(){updateMixer(),updateUserList();},0x1);else _0x155255&&updateUserList();},_0x311669[_0x471263(0x16a)][_0x5be36a][_0x471263(0x4a7)][_0x471263(0x661)]=()=>{var _0x495631=_0x471263;warnlog(_0x495631(0x4b9));};},_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x196)]=_0x17b813=>{var _0xce7d81=_0x3106c2;warnlog(_0xce7d81(0x1d8)),_0x311669[_0xce7d81(0x938)](_0x17b813,_0x5be36a);},log(_0x3106c2(0x41d));},_0x311669[_0x134a17(0x86b)]=function(_0x1250fc,_0x18d3f1){var _0x40d2a0=_0x134a17;log('session.setupScreenShareAddon'),!_0x311669[_0x40d2a0(0x16a)][_0x18d3f1][_0x40d2a0(0x549)]?(_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen']={},_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x65b)]=_0x18d3f1,_0x311669[_0x40d2a0(0x16a)][_0x18d3f1][_0x40d2a0(0x549)]=createVideoElement(),_0x311669[_0x40d2a0(0x16a)][_0x18d3f1][_0x40d2a0(0x549)]['needsLoading']=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1][_0x40d2a0(0x549)][_0x40d2a0(0x8bf)](_0x40d2a0(0x664),_0x437be8=>{var _0x1805b0=_0x40d2a0;log(_0x1805b0(0x18f)),_0x437be8[_0x1805b0(0x81f)][_0x1805b0(0x932)]=![];}),_0x311669[_0x40d2a0(0x16a)][_0x18d3f1][_0x40d2a0(0x549)][_0x40d2a0(0x5c4)]=createMediaStream(),_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x49b)]=_0x311669['rpcs'][_0x18d3f1][_0x40d2a0(0x549)],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)]['streamSrc']=createMediaStream(),_0x311669['rpcs'][_0x18d3f1][_0x40d2a0(0x9bb)]&&(_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)]['streamID']=_0x311669['rpcs'][_0x18d3f1][_0x40d2a0(0x9bb)]+':s'),_0x311669['rpcs'][_0x18d3f1+'_screen'][_0x40d2a0(0x436)]={},_0x311669[_0x40d2a0(0x16a)][_0x18d3f1][_0x40d2a0(0x436)][_0x40d2a0(0x375)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen']['getStats']=function(){return new Promise((_0x204b06,_0x4a183a)=>{_0x204b06([]);});},_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x9db)]=![],_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x87f)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x665)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)]['activelySpeaking']=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x84b)]=![],_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x79d)]=null,_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen'][_0x40d2a0(0x45c)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen'][_0x40d2a0(0xa33)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen'][_0x40d2a0(0x9cc)]=-0x1,_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x24a)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen'][_0x40d2a0(0x560)]=![],_0x311669['rpcs'][_0x18d3f1+'_screen']['channelOffset']=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x592)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x53c)]=-0x1,_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x3e4)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x678)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x9eb)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x532)]=_0x311669[_0x40d2a0(0x16a)][_0x18d3f1][_0x40d2a0(0x532)]||[],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x8ab)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x223)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x3be)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0xa39)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen']['remoteMuteState']=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen'][_0x40d2a0(0x40b)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x7b9)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen'][_0x40d2a0(0x621)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x9a4)]=null,_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen']['mutedState']=null,_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x896)]=null,_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x3e1)]=null,_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x350)]=null,_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)]['scaleHeight']=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)]['scaleWidth']=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x482)]=![],_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x97a)]=![],_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x46b)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)]['volumeControl']=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x2ef)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x701)]=!![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen'][_0x40d2a0(0x6f2)]=0x64,_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x426)]=0x0,_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)]['nackCount']=0x0,_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen'][_0x40d2a0(0x15c)]='1',_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x644)]='1',_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen']['obsControl']=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x353)]=0x0,_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x89c)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0xa63)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x237)]=null,_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x4e0)]=null,_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x6dc)]={},_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x325)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x9b0)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x586)]=Date[_0x40d2a0(0x610)](),_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)]['settings']=![],_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)]['savedVolume']=![],(_0x311669[_0x40d2a0(0x2d3)]==0x2||_0x311669['activeSpeaker']==0x4)&&(_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x84b)]=!![]),_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)]['videoElement'][_0x40d2a0(0x2bc)]['UUID']=_0x18d3f1+'_screen',_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen'][_0x40d2a0(0x49b)]['id']=_0x40d2a0(0x9c6)+_0x18d3f1+_0x40d2a0(0x33b),_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x9bb)]&&(_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x49b)]['dataset'][_0x40d2a0(0x34b)]=_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x9bb)]),_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x49b)]['screenshare']=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)]['voiceMeter']=![],setupIncomingScreenTracking(_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x49b)],_0x18d3f1+'_screen'),_0x1250fc[_0x40d2a0(0x982)](function(_0x3ff137){var _0x913462=_0x40d2a0;_0x311669['rpcs'][_0x18d3f1][_0x913462(0x549)][_0x913462(0x5c4)][_0x913462(0x58b)](_0x3ff137),_0x311669['rpcs'][_0x18d3f1+_0x913462(0x33b)][_0x913462(0x585)][_0x913462(0x58b)](_0x3ff137);}),_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x49b)][_0x40d2a0(0x740)]=!![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen'][_0x40d2a0(0x49b)][_0x40d2a0(0x4b0)](_0x40d2a0(0x35a),''),mediaSourceUpdated(_0x18d3f1+_0x40d2a0(0x33b),_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x9bb)])):_0x1250fc[_0x40d2a0(0x982)](function(_0x2e25ff){var _0x1ea153=_0x40d2a0,_0x1ce77c=![];_0x311669[_0x1ea153(0x16a)][_0x18d3f1][_0x1ea153(0x549)][_0x1ea153(0x5c4)]['getTracks']()[_0x1ea153(0x982)](function(_0x332d8b){var _0x3f7dae=_0x1ea153;_0x332d8b['id']==_0x2e25ff['id']&&_0x332d8b[_0x3f7dae(0x7ad)]==_0x2e25ff[_0x3f7dae(0x7ad)]&&(_0x1ce77c=!![]);});!_0x1ce77c&&_0x311669[_0x1ea153(0x16a)][_0x18d3f1][_0x1ea153(0x549)][_0x1ea153(0x5c4)][_0x1ea153(0x58b)](_0x2e25ff);var _0x1ce77c=![];_0x311669['rpcs'][_0x18d3f1+_0x1ea153(0x33b)][_0x1ea153(0x585)][_0x1ea153(0x951)]()[_0x1ea153(0x982)](function(_0x47756b){var _0x40d456=_0x1ea153;_0x47756b['id']==_0x2e25ff['id']&&_0x47756b[_0x40d456(0x7ad)]==_0x2e25ff[_0x40d456(0x7ad)]&&(_0x1ce77c=!![]);}),!_0x1ce77c&&_0x311669[_0x1ea153(0x16a)][_0x18d3f1+_0x1ea153(0x33b)][_0x1ea153(0x585)]['addTrack'](_0x2e25ff);});},_0x311669;}());function getMeshcastCanvasTrack(_0x49759a=session[_0x2de9a7(0x638)]){var _0x171370=_0x2de9a7;!_0x49759a&&errorlog(_0x171370(0xa40));!_0x49759a['canvas']&&(_0x49759a[_0x171370(0x4e0)]=document[_0x171370(0xa4d)](_0x171370(0x4e0)),_0x49759a[_0x171370(0x4e0)][_0x171370(0x530)]=0x140,_0x49759a[_0x171370(0x4e0)][_0x171370(0x5ec)]=0xb4);!_0x49759a[_0x171370(0xa01)]&&(_0x49759a['ctx']=_0x49759a[_0x171370(0x4e0)][_0x171370(0x179)]('2d',{'alpha':![]}),_0x49759a['ctx'][_0x171370(0x6a5)]=_0x171370(0x8a2),_0x49759a['ctx'][_0x171370(0xa0c)](0x0,0x0,_0x49759a[_0x171370(0x4e0)][_0x171370(0x530)],_0x49759a['canvas'][_0x171370(0x5ec)]));!_0x49759a[_0x171370(0x6ad)]&&(function _0x163d53(){var _0x2dec03=_0x171370;_0x49759a[_0x2dec03(0xa01)][_0x2dec03(0xa0c)](0x0,0x0,_0x49759a[_0x2dec03(0x4e0)][_0x2dec03(0x530)],_0x49759a['canvas'][_0x2dec03(0x5ec)]),setTimeout(_0x163d53,0xfa);}(),_0x49759a[_0x171370(0x6ad)]=_0x49759a[_0x171370(0x4e0)][_0x171370(0x961)](0x4));var _0x15bb03=_0x49759a['canvasStream'][_0x171370(0x87a)]();if(_0x15bb03['length'])return _0x15bb03[0x0];return errorlog(_0x171370(0xa81)),![];}var meshcastServer=![];function _0x5c68(_0x4305d2,_0x59ee0d){var _0x2d5e64=_0x2d5e();return _0x5c68=function(_0x5c68d8,_0xa4daae){_0x5c68d8=_0x5c68d8-0x15c;var _0x23a63d=_0x2d5e64[_0x5c68d8];return _0x23a63d;},_0x5c68(_0x4305d2,_0x59ee0d);}function selectMeshcast(_0xcdfe2d){var _0x4b0f65=_0x2de9a7;meshcastServer={};var _0x3dbd90=_0xcdfe2d[_0x4b0f65(0x37f)],_0x27ee76=_0xcdfe2d[_0x4b0f65(0x7d3)];meshcastServer[_0x4b0f65(0x4e6)]=_0x27ee76[_0x3dbd90][_0x4b0f65(0x4e6)],meshcastServer[_0x4b0f65(0x7d2)]=_0x27ee76[_0x3dbd90]['code'];}async function meshcast(_0x5c9d1e=![]){var _0x1f8631=_0x2de9a7;async function _0x25b98b(_0x2c9705,_0x547584){var _0x3dbecb=_0x5c68;const _0x27e277=new XMLHttpRequest();_0x27e277[_0x3dbecb(0x19f)]=function(){var _0x42ebbd=_0x3dbecb;if(parseFloat(this[_0x42ebbd(0x80f)])>=0x0){if(parseFloat(this[_0x42ebbd(0x80f)])>0x32)_0x2c9705[_0x42ebbd(0x82f)]+='\x20(full)';else{if(parseFloat(this[_0x42ebbd(0x80f)])>0x19)_0x2c9705[_0x42ebbd(0x82f)]+='\x20(fair)';else{if(parseFloat(this[_0x42ebbd(0x80f)])>0xa)_0x2c9705[_0x42ebbd(0x82f)]+='\x20(ok)';else{if(parseFloat(this[_0x42ebbd(0x80f)])>0x0)_0x2c9705['innerHTML']+='\x20(good)';else{var _0x2c5083=![];_0x2c9705[_0x42ebbd(0xa16)]&&(_0x2c5083=!![]),_0x2c9705[_0x42ebbd(0x7a2)]=!![],_0x2c9705[_0x42ebbd(0x82f)]+=_0x42ebbd(0x61c),document[_0x42ebbd(0x5c8)](_0x42ebbd(0x3a1))[_0x42ebbd(0x40a)](_0x2c9705),_0x2c5083&&(document[_0x42ebbd(0x5c8)](_0x42ebbd(0x3a1))[_0x42ebbd(0x7d3)][0x0][_0x42ebbd(0xa16)]=!![]);}}}}}else{var _0x2c5083=![];_0x2c9705[_0x42ebbd(0xa16)]&&(_0x2c5083=!![]),document[_0x42ebbd(0x5c8)](_0x42ebbd(0x3a1))[_0x42ebbd(0x40a)](_0x2c9705),_0x2c9705[_0x42ebbd(0x82f)]+=_0x42ebbd(0x61c),_0x2c9705[_0x42ebbd(0x7a2)]=!![],_0x2c5083&&(document[_0x42ebbd(0x5c8)](_0x42ebbd(0x3a1))['options'][0x0][_0x42ebbd(0xa16)]=!![]);}session[_0x42ebbd(0x75b)]&&!session[_0x42ebbd(0x78c)]&&!session[_0x42ebbd(0x89e)]&&document[_0x42ebbd(0x5c8)]('meshcastMenu')[_0x42ebbd(0x424)][_0x42ebbd(0x761)](_0x42ebbd(0x472));},_0x27e277['onerror']=function(){var _0x4a5589=_0x3dbecb,_0x3700c7=![];_0x2c9705['selected']&&(_0x3700c7=!![]),document[_0x4a5589(0x5c8)](_0x4a5589(0x3a1))[_0x4a5589(0x40a)](_0x2c9705),_0x2c9705[_0x4a5589(0x82f)]+='\x20(fail)',_0x2c9705[_0x4a5589(0x7a2)]=!![],_0x3700c7&&(document[_0x4a5589(0x5c8)](_0x4a5589(0x3a1))['options'][0x0][_0x4a5589(0xa16)]=!![]);},_0x27e277[_0x3dbecb(0x6d4)]('GET',_0x547584,!![]),_0x27e277[_0x3dbecb(0x94c)]=0x3e8,_0x27e277[_0x3dbecb(0x48e)]=function(_0x49f646){var _0x543a52=_0x3dbecb,_0x4c8303=![];_0x2c9705['selected']&&(_0x4c8303=!![]),document[_0x543a52(0x5c8)]('edgelist')[_0x543a52(0x40a)](_0x2c9705),_0x2c9705[_0x543a52(0x82f)]+='\x20(timeout)',_0x4c8303&&(document[_0x543a52(0x5c8)]('edgelist')[_0x543a52(0x7d3)][0x0][_0x543a52(0xa16)]=!![]);},_0x27e277[_0x3dbecb(0x2b9)]();}async function _0x3e64f2(_0x3b3458=![]){var _0x36a6be=_0x5c68,_0x154b40=new Date(),_0xe948d4=_0x154b40[_0x36a6be(0x3c6)]();urlParams['has']('tz')&&(_0xe948d4=parseInt(urlParams[_0x36a6be(0x9a7)]('tz'))||_0xe948d4),fetch('https://meshcast.io/servers.json?ts='+Date[_0x36a6be(0x610)]())[_0x36a6be(0x619)](_0x151541=>_0x151541[_0x36a6be(0x6b6)]())[_0x36a6be(0x619)](async _0x2146ff=>{var _0x3beb7f=_0x36a6be;for(var _0x37628f=0x0;_0x37628f<_0x2146ff[_0x3beb7f(0x8e9)];_0x37628f++){var _0x353d94=Math['abs'](_0x2146ff[_0x37628f]['tz']-_0xe948d4);Math[_0x3beb7f(0x86a)](_0x353d94-0x3c*0x18)<_0x353d94&&(_0x353d94=Math[_0x3beb7f(0x86a)](_0x353d94-0x3c*0x18));_0x2146ff[_0x37628f][_0x3beb7f(0x44c)]=_0x353d94;if(session['meshcastCode']&&session['meshcastCode']!==_0x2146ff[_0x37628f][_0x3beb7f(0x7d2)])_0x2146ff[_0x37628f][_0x3beb7f(0x44c)]+=0x3e8;else!session[_0x3beb7f(0x4f5)]&&session[_0x3beb7f(0x98c)]!==_0x2146ff[_0x37628f][_0x3beb7f(0x7d2)]&&(_0x2146ff[_0x37628f][_0x3beb7f(0x44c)]+=0x3e8);}_0x2146ff[_0x3beb7f(0xa42)](compare_deltas),console[_0x3beb7f(0x828)](_0x2146ff);for(var _0x37628f=0x0;_0x37628f<_0x2146ff[_0x3beb7f(0x8e9)];_0x37628f++){var _0x5ccd6a=document['createElement'](_0x3beb7f(0x522));_0x5ccd6a[_0x3beb7f(0x7d2)]=_0x2146ff[_0x37628f][_0x3beb7f(0x7d2)],_0x5ccd6a[_0x3beb7f(0x4e6)]=_0x2146ff[_0x37628f][_0x3beb7f(0x4e6)],_0x5ccd6a[_0x3beb7f(0x82f)]=_0x2146ff[_0x37628f][_0x3beb7f(0x89c)],_0x25b98b(_0x5ccd6a,_0x2146ff[_0x37628f][_0x3beb7f(0x4e6)]+_0x3beb7f(0x428)),document[_0x3beb7f(0x5c8)]('edgelist')[_0x3beb7f(0x40a)](_0x5ccd6a);}meshcastServer=_0x2146ff[0x0],_0x3b3458&&_0x3b3458();});}if(!session['meshcast'])return;if(_0x5c9d1e){_0x3e64f2();return;}if(session['whipoutSettings']!==![])return;else{if(session[_0x1f8631(0x58c)]){}else{if(!session[_0x1f8631(0x49b)][_0x1f8631(0x5c4)]||!session[_0x1f8631(0x49b)][_0x1f8631(0x5c4)][_0x1f8631(0x951)]()['length'])return;}}session[_0x1f8631(0x948)]=null,warnlog(_0x1f8631(0x465));function _0x34a3a6(_0x37488f){var _0x420777=_0x1f8631;warnlog(_0x420777(0x9cd)),warnlog(_0x37488f);try{session[_0x420777(0x638)][_0x420777(0x903)]()['then'](function(_0x27bfb4){var _0x1dcd9f=_0x420777;try{_0x27bfb4=configureWhipOutSDP(_0x27bfb4);}catch(_0x2068ed){errorlog(_0x2068ed);}return session[_0x1dcd9f(0x638)][_0x1dcd9f(0xa1b)](_0x27bfb4);})[_0x420777(0x619)](function(){var _0xc8cb13=_0x420777;log(session[_0xc8cb13(0x638)][_0xc8cb13(0x1f3)]);var _0x25e4da=session['whipOut'][_0xc8cb13(0x1f3)][_0xc8cb13(0x648)];(iOS||iPad)&&(session[_0xc8cb13(0x971)]&&_0x25e4da[_0xc8cb13(0x2c2)](_0xc8cb13(0x168))&&(_0x25e4da=_0x25e4da[_0xc8cb13(0x2f9)](_0xc8cb13(0x168),''))),_0xa06b85(_0x25e4da,'sdp');})[_0x420777(0x3b6)](function(_0x2b5837){});}catch(_0x24adfa){errorlog(_0x24adfa);}}try{var _0x32d6bd=[],_0x426fa1=session[_0x1f8631(0xa76)](0xe);async function _0x2524e4(){var _0x402d9f=_0x1f8631;document[_0x402d9f(0x5c8)](_0x402d9f(0x3a1))[_0x402d9f(0x7a2)]=!![],document[_0x402d9f(0x5c8)](_0x402d9f(0x3a1))[_0x402d9f(0x29a)]=_0x402d9f(0x2d7);!session[_0x402d9f(0x7db)]&&await chooseBestTURN();try{session[_0x402d9f(0x638)]=new RTCPeerConnection(session['configuration']),session[_0x402d9f(0x638)][_0x402d9f(0x436)]={},session['whipOut'][_0x402d9f(0x9b5)]=null,session['whipOut'][_0x402d9f(0x6b5)]=![],session[_0x402d9f(0x638)][_0x402d9f(0x6f1)]=![],session[_0x402d9f(0x638)]['offerToReceiveVideo']=![];}catch(_0x16db3c){!session['cleanOutput']&&warnUser(_0x402d9f(0xa48));}try{if(session[_0x402d9f(0x98c)]!==_0x402d9f(0x30c)){var _0x23531a=![];session[_0x402d9f(0x49b)]&&session[_0x402d9f(0x49b)][_0x402d9f(0x5c4)]&&(_0x23531a=session[_0x402d9f(0x49b)][_0x402d9f(0x5c4)][_0x402d9f(0xa6c)]());if(!_0x23531a||!_0x23531a['length']){var _0x561554=new AudioContext(),_0x37f371=_0x561554[_0x402d9f(0x6c7)]();_0x37f371[_0x402d9f(0x75f)][_0x402d9f(0xa6c)]()[_0x402d9f(0x982)](_0x2f7fe8=>{_0x23531a=_0x2f7fe8;});}else _0x23531a=_0x23531a[0x0];if(session[_0x402d9f(0xa82)]&&_0x23531a[_0x402d9f(0x7ad)]==='audio')try{_0x23531a[_0x402d9f(0x34f)]=session[_0x402d9f(0xa82)];}catch(_0x16de11){errorlog(_0x16de11);}if(_0x23531a)try{session[_0x402d9f(0x638)][_0x402d9f(0x5d2)](_0x23531a,{'streams':[session[_0x402d9f(0x49b)][_0x402d9f(0x5c4)]],'direction':_0x402d9f(0x3a6)});}catch(_0x15cf5f){errorlog(_0x15cf5f),session['whipOut']['addTrack'](_0x23531a);}}if(session[_0x402d9f(0x98c)]!==_0x402d9f(0x433)){var _0x23531a=![];session[_0x402d9f(0x49b)]&&session[_0x402d9f(0x49b)][_0x402d9f(0x5c4)]&&(_0x23531a=session[_0x402d9f(0x49b)][_0x402d9f(0x5c4)][_0x402d9f(0x87a)]());!_0x23531a||!_0x23531a[_0x402d9f(0x8e9)]?_0x23531a=getMeshcastCanvasTrack(session[_0x402d9f(0x638)]):_0x23531a=_0x23531a[0x0];if(session['screenShareState']&&session[_0x402d9f(0x45e)]&&_0x23531a[_0x402d9f(0x7ad)]===_0x402d9f(0x30c))try{_0x23531a['contentHint']=session[_0x402d9f(0x45e)];}catch(_0x355f5b){errorlog(_0x355f5b);}else{if(session[_0x402d9f(0x34f)]&&_0x23531a['kind']==='video')try{_0x23531a[_0x402d9f(0x34f)]=session[_0x402d9f(0x34f)];}catch(_0x512cbe){errorlog(_0x512cbe);}}if(_0x23531a)try{session[_0x402d9f(0x638)][_0x402d9f(0x5d2)](_0x23531a,{'streams':[session[_0x402d9f(0x49b)]['srcObject']],'direction':_0x402d9f(0x3a6)});}catch(_0x2cb1ad){errorlog(_0x2cb1ad),session[_0x402d9f(0x638)][_0x402d9f(0x58b)](_0x23531a);}}session[_0x402d9f(0x638)][_0x402d9f(0x23b)]=_0x34a3a6,session[_0x402d9f(0x638)]['onicecandidate']=function(_0x27d61c){var _0x4e3cd8=_0x402d9f;if(_0x27d61c[_0x4e3cd8(0x905)]==null)return;log(_0x27d61c[_0x4e3cd8(0x905)]),_0x32d6bd[_0x4e3cd8(0x505)](_0x27d61c[_0x4e3cd8(0x905)]);};}catch(_0x45497a){errorlog(_0x45497a);}}!meshcastServer?_0x3e64f2(_0x2524e4):_0x2524e4();}catch(_0x5c8ff0){errorlog(_0x5c8ff0);}function _0xa06b85(_0x52c030,_0x267462,_0xdb811e=![]){var _0x51bb31=_0x1f8631;try{var _0x24e43a=new XMLHttpRequest();_0x24e43a[_0x51bb31(0x7a6)]=function(){var _0x3d5485=_0x51bb31;if(this[_0x3d5485(0x8ee)]==0x4&&(this[_0x3d5485(0xa13)]==0xc8||this['status']==0xc9)){var _0x379ee3=this[_0x3d5485(0x5f0)](_0x3d5485(0x937));if(_0x379ee3==_0x3d5485(0x916)){var _0x5c604b={};_0x5c604b[_0x3d5485(0x648)]=this['responseText'],_0x5c604b['type']=_0x3d5485(0x73b);try{_0x5c604b=configureWhipOutSDP(_0x5c604b);}catch(_0x5129e0){errorlog(_0x5129e0);}session[_0x3d5485(0x638)][_0x3d5485(0x5fb)](_0x5c604b)[_0x3d5485(0x619)](function(){var _0x1f45be=_0x3d5485;if(_0x32d6bd[_0x1f45be(0x8e9)]){var _0x2d7ab7=JSON[_0x1f45be(0x479)](_0x32d6bd[_0x1f45be(0x513)]());_0xa06b85(_0x2d7ab7,'ice',function(){var _0x301186=_0x1f45be;session[_0x301186(0xa35)](),_0x4e1c34();});}})[_0x3d5485(0x3b6)](function(_0x15329c){log(_0x15329c);});}else{if(_0x379ee3==_0x3d5485(0x224))this[_0x3d5485(0x80f)]==0x1b0?warnUser(_0x3d5485(0x76f)):warnUser('Unknown\x20Meshcast\x20error');else _0xdb811e&&_0xdb811e();}}};var _0x351c05=0x9c4;session[_0x51bb31(0x2a4)]!==![]&&(_0x351c05=session['whipOutVideoBitrate']);session['screenShareState']&&session['whipOutScreenShareBitrate']!==![]&&(_0x351c05=session[_0x51bb31(0x477)]);var _0x5a1ad8=parseInt(0x61a8/_0x351c05)||0xa,_0x2f2763='';if(session[_0x51bb31(0x701)]&&session['whipOutScreenShareCodec'])_0x2f2763=session['whipOutScreenShareCodec'];else{if(session[_0x51bb31(0x491)])_0x2f2763=session[_0x51bb31(0x491)];else(iOS||iPad)&&(_0x2f2763=_0x51bb31(0x7f9));}_0x24e43a[_0x51bb31(0x6d4)](_0x51bb31(0x742),meshcastServer[_0x51bb31(0x4e6)]+'/'+_0x5a1ad8+'/'+_0x2f2763,!![]),_0x24e43a[_0x51bb31(0x746)](_0x51bb31(0x40f),_0x51bb31(0x9b3)+_0x267462+_0x51bb31(0x34d)),_0x24e43a[_0x51bb31(0x746)](_0x51bb31(0x80a),_0x51bb31(0x1f0)+_0x426fa1),_0x24e43a[_0x51bb31(0x88c)]=function(_0x4a11f6){var _0x267626=_0x51bb31;errorlog(_0x4a11f6),warnUser(_0x267626(0x18d)),window['location'][_0x267626(0x456)]!=='vdo.ninja'?console[_0x267626(0x1ad)](_0x267626(0x7bf)):console[_0x267626(0x1ad)](_0x267626(0x576));},_0x24e43a['send'](_0x52c030);}catch(_0x44816a){errorlog(_0x44816a);}}async function _0x4e1c34(){var _0x48c08e=_0x1f8631;if(meshcastServer[_0x48c08e(0x7d2)])var _0x514487=_0x48c08e(0xa69)+meshcastServer[_0x48c08e(0x7d2)]+'&id='+_0x426fa1;else var _0x514487='https://meshcast.io/view.html?id='+_0x426fa1;console[_0x48c08e(0x828)](_0x48c08e(0x1ee)+_0x514487);!session['whipOut'][_0x48c08e(0x436)]&&(session[_0x48c08e(0x638)][_0x48c08e(0x436)]={});session[_0x48c08e(0x638)][_0x48c08e(0x436)][_0x48c08e(0x281)]=meshcastServer[_0x48c08e(0x7d2)],session[_0x48c08e(0x638)][_0x48c08e(0x436)][_0x48c08e(0x3d5)]=_0x514487,session[_0x48c08e(0x638)]['stats'][_0x48c08e(0x84f)]=_0x48c08e(0x1bc),session[_0x48c08e(0x638)]['stats'][_0x48c08e(0x50c)]=![],await sleep(0x1f4),session[_0x48c08e(0x948)]={'type':'meshcast','token':_0x426fa1,'url':meshcastServer[_0x48c08e(0x4e6)]};for(var _0xebde50 in session['pcs']){if(session['pcs'][_0xebde50][_0x48c08e(0x45a)]===null){var _0x226a1e={};_0x226a1e[_0x48c08e(0x2d2)]=session[_0x48c08e(0x948)],_0x226a1e[_0x48c08e(0x98c)]=session[_0x48c08e(0x948)],session[_0x48c08e(0x8a1)](_0x226a1e,_0xebde50)&&(session[_0x48c08e(0x859)][_0xebde50][_0x48c08e(0x45a)]=!![]);}}}}async function whepWatch(_0xef4573,_0xf7b6c){var _0x5e7218=_0x2de9a7;if(session[_0x5e7218(0x8b4)])return;log(_0xf7b6c);if(_0xf7b6c[_0x5e7218(0x78a)]=='meshcast')meshcastWatch(_0xef4573,_0xf7b6c[_0x5e7218(0x98c)]);else _0xf7b6c[_0x5e7218(0x78a)]=='whep'&&(_0xf7b6c&&_0xf7b6c[_0x5e7218(0x4e6)]&&whepIn(_0xf7b6c[_0x5e7218(0x4e6)],![],_0xef4573));}function _0x2d5e(){var _0x3d0c02=['showConnections','volumeControl','We\x20will\x20not\x20request\x20the\x20meshcast\x20as\x20no\x20audio\x20or\x20video\x20is\x20requested','ON\x20FOCUS\x20NOT\x20FOUND','maxpublishers','alpha','removeOrientationFlag','hostedFiles','cry','PINGED','directMigrateIssue','vdav','control-room-co-director','available_outgoing_bitrate_kbps','Not\x20director','slot','allowChunked','viewwidth','measureUnsignedInt','select','term','rain','mean','forEach','queueList','bandwidth\x20set\x20g!\x20','queue=false','station','gainNode','stun:stun.cloudflare.com:3478','audioMutedOverride','directorViewBitrate','broad','meshcast','scene','You\x27ve\x20been\x20transferred','step','resolution','design','binaryType','layouts','Someone\x20Joined\x20the\x20Room','base','recording_audio_pipeline','score','enough','isDirector\x20','major','teach','Someone','scale\x20scale','search','beforeunload','CONNECTEED!','aspectRatio','test','center','closeTimeout','thank','replaceAll','get','mother','anger','currentSlots','createMediaStreamSource','BROWER\x20DID\x20NOT\x20SUPPORT\x20LIMIT\x20BITRATE','down','publishing\x20SDP\x20Offer:\x20','stone','iframeEle','this','createDelay','application/','each','maxBandwidth','restartIce','born','allowchunked','noScaling','blindAllGuests','streamID','even','rope','turn:turn-eu1.vdo.ninja:3478','PCM\x20STARTED','next','autohide','leavetone','send\x20channel\x20closed','closing\x2018','salt','videosource_','received\x20data\x20from\x20viewer','setAudioBitrate','second','pound','once','bandwidth','ON\x20NEGO\x20NEEDED','BITRATE\x203:\x20','defaultSpeaker','removeChild','maxBitrate','closePC','encode','head4','session.watchTimeoutList\x20no\x20longer\x20exists;\x20won\x27t\x20retry.','empty\x20ice..','whether','disablePLI','stereo_url','afraid','allowGraphs','sendGenericData','scaleResolutionDownBy\x20set\x202b!','BundlePolicy','webCodecAudio','chick','disableNACK','destination','Offset\x20may\x20not\x20be\x20negative','differ','sleep','Bad\x20EBML\x20datatype\x20','recordedBlobs','alreadyJoinedMembers','.webm','darkmode','voiceMeter','room\x20rate\x20restriction\x20detected.\x20No\x20videos\x20will\x20be\x20published\x20to\x20other\x20guests','vector','stream\x20ID\x20is\x200\x20length','onended','calculateScale','toward','platform','setBitrate','controls','description','screenshareNotActive','token','degree','remote-control-failed','playbackheader','active','hideDirector','decide','behind','preloadbitrate','maxMobileBitrate','ctx','maxBufferSize','path','EOF2','check','weather','sky','come','processIce2','TOO\x20MANY\x20PUBLISHING\x20PEERS','drive','fillRect','limitAudio','stead','OBSNINJAFORLIFE','whipOutScale','optimizeBitrate','game','status','sendRequest','remote-mute-state','selected','dataMode','aec_url','include','wonder','setLocalDescription','gridlayout','initialPublish','generator','showList','feel','sharperScreen','near','post','\x20---\x20we\x20will\x20ask\x20again','https://temp.vdo.ninja/','mute','tabernac','push-connection-info','opposite','quick','went','history','deal','iframetarget','max_bandwidth_capped_kbps','data\x20channel\x20being\x20used\x20in\x20reverse;\x20this\x20shouldn\x27t\x20really\x20happen,\x20except\x20if\x20maybe\x20doing\x20a\x20file\x20transfer','throttle','square','buffer','ear','whipOutSetScale','getReader','directorActions','cow','virtualHangup','closeTimeout\x20cancelled;\x206\x27\x20retry\x20in\x203s?','.hidden2','roomTimer','power_level','trip','wave','Meshcast\x20(or\x20whip|?)\x20not\x20connected;\x20cant\x27\x20create\x20canvas\x20for\x20it','invalid-remote-code-obs','sort','yard','website','codirectorSettings','string','forceRotate','An\x20RTC\x20error\x20occured','OPEN','eventPlayActive','scaleResolutionDownBy','getOBSOptimization','createElement','ICE\x20DISCONNECTED','mainDirectorPasswor','save','recieveChunkedStream','ICE\x20GATHER\x20START','listing','chunks','off','ASKING\x20FOR\x20AUDIO\x20AND\x20VIDEO?','cell','sending\x20message\x20via\x20WSS\x20as\x20WebRTC\x20failed\x20to\x20send\x20message','effectValue_default','publicKey','sound','hiddenSceneViewBitrate','querySelector','noPLIs','favor','cover','there','closing\x2013','order','midiOffset','CriOS','directorStreamID','Failed\x20to\x20connect\x20to\x20service:\x20Error\x20503Possibly\x20too\x20many\x20connections\x20from\x20the\x20same\x20address\x20tried\x20to\x20connect.Visit\x20https://discord.vdo.ninja\x20for\x20support.','key','https://meshcast.io/view.html?api=','SENDING\x20FILE:\x20','allowScreenVideo','getAudioTracks','requestFile\x20in\x20reverse','encodedInsertableStreams','audioCtx','controlRoomBitrate','short','green','tokenDirector','transparent','consonant','generateStreamID','useragent','whole','girl','joinroom','seeding\x20!!','closing\x202','allowNoGroup','Unmute\x20video','permaMirrored','ISSUING\x20CALLBACK:\x20','Meschast\x20canvas\x20not\x20working','audioContentHint','bye','mainDirectorPassword','requested\x20file\x20has\x20been\x20removed.','shop','block','delayTime','Track\x20stopped','fear','opacityDisconnect','expect','widgetURL','slotmode','requestStatsContinuous','beat','three','lone','refreshScale','hideClock','manual','box','a=extmap:3\x20urn:3gpp:video-orientation\x0d\x0a','webrtc\x20connectioned\x20closed-event','rpcs','ab_url','SET\x20SCALING\x20IS\x20FIRING,\x20which\x20is\x20GOOD\x20!!!!!!\x20','several','keepIncomingVideosInLandscape','autoSync','processDescription2','effect','hash\x20is\x20','welcomeImage','sure','dance','obsControl','videoOptions','claim','getContext','onmessage','canvasSource','EncodedVideoChunk','govern','infocusForceMode','Seeking\x20beyond\x20the\x20end\x20of\x20file\x20is\x20not\x20allowed','PUBLISHER\x27s\x20RTC\x20Connection\x20seems\x20to\x20be\x20dead?\x20','tfliteModule','art','oniceconnectionstatechange','limitAudioBitrate','black','wall','blow','URL','crypto','sand','may','defaultMedia','Meshcast\x20not\x20available.','charAt','incoming\x20screen\x20share\x20started\x20loading','failed','Offset\x20may\x20not\x20be\x20NaN','closing\x201','closing\x204','directorView','createScriptProcessor','ontrack','bypass','speech','silver','enemy','keys','screenStopped','least','TRANSFERRING?','onload','leg','nackCount','Not\x20supported;\x20expected\x20\x27filetransfer\x27','broke','gas','radio','requestCoMigrate','[data-action-type=\x22order-value\x22][data--u-u-i-d=\x22','mirrored','enhance','fun','A\x20director\x20joined\x20the\x20room','war','warn','creating\x20answer','bone','wear','gave','safe','win','heavy','recieveFile','difficult','video_2_init_height','area','network_type','setupYourOwnPlease','fileWriter','Meshcast','father','hidesololinks','closing\x2010','allowmeshcast','quite','chatbutton','screensharequality','updateLocalStatsInterval','observe','scaleHeight','writeByte','label_','rotate','TrackNumber\x20must\x20be\x20>\x200\x20and\x20<\x20127','tainted','summer','travel','bank','close','customWSS','span','322564eFyzOF','processIce','defaultIframeSrc','startWriter','would','addCoDirector','New\x20ON\x20TRACK\x20event','micDelay','hold','gentle','from','timecode','rail','quiet','codirector','createDataChannel','remote-token-rejected','mobile','midi','what\x20is\x20this?','quality_wb','nochunk','product','similar','than','broadcast=false','port',',\x20isDirector:\x20','MESHCAST\x20LINK:\x20','magnet','Bearer\x20','will','application/json;\x20charset=utf-8','localDescription','ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789','197463wVromh','applySoloChat','final','east','bit\x20rate\x20being\x20munged','sourceActive','remember','rather','session','crop','chunkedtransfer','wind','obs_control','targetBitrate','videoWriter','whipCallback2','user\x20didn\x27t\x20have\x20access\x20for\x20this\x20file.','stereo\x20enabled','Bitrate\x20request:\x20','closed','maxviewers','isArray','changeParams','noisegateSettings','wish','together','offer','screenshareStereo','allowBroadcast','mainmenu','audioDevice','[data-action-type=\x22mirror-guest\x22]','digest','This\x20stream\x20token\x20is\x20already\x20connected.\x20Are\x20you\x20having\x20a\x20CORS\x20issue?\x20Also,\x20ensure\x20SSL\x20if\x20enforced\x20on\x20your\x20host\x20everywhere.','limitMeshcastBitrate','writeDoubleBE','done\x20setting\x20degrad\x20to\x20','focus','focusStyle','Change\x20Label','reduce','enqueue','scaleResolution','screenSrc','disconnectedTimeout','audioBitrate','iframeVideo','application/error','muted','clock24','north','sampleRate','though','pose','seeding','wss://debug.vdo.ninja:443','new-view-connection','forceTcpMode','sendChannel_','modifyDescPCM','record','textContent','better','hundred','that','keyframe','canvasCtx','ariaPressed','createWriteStream','unit','onnegotiationneeded','them','switchMode','safemode','group-set-updated','suppressLocalAudioPlayback','watchTimeoutList','icefilter','company','stand','saw','publish','want','requestAudioRateLimit','watch','bandwidthMuted','reconnected','join','remote','codec','screenshareAEC','roombitrate','cost','104685BmJJrm','closeTimeout\x20cancelled;\x207','broadcast','api','split','happen','road','sendframes','heat','requestRateLimit\x20RUN:\x20','allowscreen','egg','rampUpTime','AndroidFix','speakerMuted_default','populate','session.newMainDirectorSetup','decrypted','realtime','nextQueue','ground','UN-MUTED','disableViewerWebAudioPipeline','charging','https://www.youtube.com/','WebRTC\x20Connection\x20Closed.\x20Clean\x20up.\x20657','out','statsMenu','strong','UUID\x20not\x20found;\x20can\x27t\x20close.','EOF1','onceConnected','allowVideos','optimizedBitrate','directorChat','no\x20pcs[UUID]','layout-updated','showall','directorBlue','ball','channel','experimental','encoder','doctor','noise\x20gate\x20on','grow','cae1','publishing_region','raw','plane','top','directorDisplayMuted','preferCurrentTab','fresh','degrade','success','joinRoom','decoder','Can\x27t\x20play\x20your\x20own\x20stream\x20ID','mountain','outboundAudioBitrate','animatedMoves','iceTransportPolicy','scaleDueToBitrate','audioCodec','ended','die','limitMaxBandwidth','decodeInvite','minimumRoomBitrate','lady','allowIframe','title','acc','codecGroupFlag','fullscreenButton','chunkedVideoEnabled','good','fire','Pinging','cut','onicecandidate','whipOutVideoBitrate','and','remoteFocus','requestChangeEQ','candidates','CLOSED','well','field','createBufferSource','EastSideRepresentZ','statsInterval','clock','opus','with','channelCount','createObjectURL','doNotSeed','providing\x20answer','FileSystemWritableFileStream','panning','west','send','caught','connectPeer','dataset','show','closeTimeout\x20cancelled;\x203','closing\x2016','vb_url','wife','includes','ceil','forest','low','small','finish','resumeClock',')\x20failed\x20due\x20to\x20permissions\x20or\x20it\x20was\x20rejected\x20by\x20the\x20user','ondatachannel','language','TFJSModel','\x20---\x20PC\x20TIMED\x20OUT,\x20but\x20still\x20alive.\x20Killing\x20it.','steve','range','invent','recording_audio_mic_delay','whepSettings','activeSpeaker','Create\x20a\x20new\x20RTC\x20connection;\x20offering\x20SDP\x20on\x20request','piece','whipOutput','Can\x27t\x20change\x20the\x20location\x20once\x20started\x20streaming','mirrorGuestState','videoMuted:\x20','document','sendPeers','badStreamList','pol1','whipOutAudioBitrate','recording','dark','hard','getSettings','forcePLI','minute','und','hit','wood','fileList','six','audioLatency','plugged_in','ArrayBufferDataStream','cat','\x20as\x20preferred\x20audio\x20codec\x20by\x20viewer\x20via\x20API\x20(offer)','screenIndexes','verify','container_director','way','directorUUID','taintedSession','iceBundle','widgetSrc','container_','chat','replace','might','round','excite','GOT\x20ICEs!!','timedelta','disableOBS','A\x20Guest\x20joined\x20the\x20room','sceneType','turns:www.turn.vdo.ninja:443','filterOBSscenes','print','made','channels','subject','allowScreen','false','compressor','mediaDevices','video','substring','describe','roomhost','wss://proxywss.rtc.ninja:443','view','stun:stun.l.google.com:19302','closing\x2012','BITRATE\x202:\x20','video/webm','other','directorMirror','nothing','querySelectorAll','noun','undefined','UUID\x20not\x20found\x20in\x20pcs','defaultBackgroundImages','swim','&start=','remote-peer-connected','transferSettings','codirector\x20request\x20hash\x20failed','nomirror','directorDisplayMute','iframeSrc','dad','resume','arraybuffer','those','limitTotalBitrateAll','successfully\x20sent\x20message\x20vis\x20WebRTC\x20instead\x20of\x20WSS\x20to\x20all\x20RTC\x20Peers','tree','noise\x20gate\x20off','much','totalRoomBitrate','main-director','encryptMessage','totalSceneBitrate','PONGED','introButton','limitTotalBitrate','virtualcam','bird','unified-plan','updateurl','Transfer\x20was\x20completed\x20successfully','_screen','fig','limitAudioEncoder','care','both','fakeUser','forceScreenShareAspectRatio','An\x20RTC\x20error\x20occured.','allowscreenaudio','turn:turn-eu2.obs.ninja:3478','story','syllable','targetAudioBitrate','vision-disabled','requestZoomChange','obsState','sid','obs',';\x20charset=utf-8','also','contentHint','mirrorState','https://turnservers.vdo.ninja/','webp','pliCount','CPU','display','volume','BlobBuffer','gather','images','playsinline','./media/bg_sample2.webp','pingTimeout','writeString','equate','lake','remoteVideoMuted','zoom\x20success','son','FORCING\x20A\x20CHUNKED\x20KEY\x20FRAME:\x20','slotsUpdate','century','Opened\x20transfer\x20channel','children','allowWidget','divide','bought','pip','start','vdo.socialstream.ninja','rtc\x20data\x20channel\x20error:\x20','picture','world','reach','Max\x20bandwidth\x20controlling\x20bitrate:\x20','backup.vdo.ninja/','agc_url','Audio_Loudness','connectionState:\x20','socialstream','noise','friend','please','requestResolution','preferCodec','already\x20connected\x202.\x20disconnecting..','setVideoBitrate','selectedIndex','Publisher\x20will\x20be\x20ignored\x20due\x20to\x20max\x20connections\x20already\x20hit','disconnected','forceMediaSettings','RPCS\x20WINS\x20ICE','support','window','roomclaimed','city','subtle','getVideoSettings','run','minptime','null','alert','lowBitrateCutoff','vp09.00.10.08','Websockets\x20timed\x20out;\x2030\x20seconds','enhanceaudio','orientation','screenshare','experience','basic','voiceMeterTemplate','push-connection','produce','\x20x\x20','proxy','ask','during','perhaps','RECONNECTING\x20to\x20HSS;\x20DISCONNECTING\x20FROM\x20TRANSFERRED\x20ROOM','&code=','rise','edgelist','noiframe','mix','measureEBMLVarInt','decrypt','sendonly','recording_audio_gain','broadcastTransfer','closing\x2020','preferVideoCodec','Max\x20bandwidth\x20being\x20capped:\x20','sharp','neighbor','queued','micSampleSize','after','bad','truck','speak','width_url','controlTimer','catch','udp','encodeRemote','height_url','playback_audio_samplerate','meshcastAudioBitrate','data','VP9','directorVideoMuted','rotated','scaleResolutionDownBy\x20set\x202a!\x20','floor','muteState','KEY\x20FRAME\x20REQUESTED','accept_layouts','realTime','getTimezoneOffset','streamid-already-published','seven','tone','while','hidedirector','skin','joiningRoom','scale\x20set!','Safari\x20','face','SEND\x20BYE','visit','version','steel','watch_URL','rub','men','outputLatency','sensorDataFilter','limitBitrate','init_video','pcs\x20RTC\x20CLOSED','tokens-did-not-match','13968nOuRbw','beepToNotify','column','mutedStateScene','ago','quietOthers','manualBandwidth','RTC\x20Connection\x20seems\x20to\x20be\x20dead\x20or\x20not\x20yet\x20open?\x201','message\x20could\x20not\x20be\x20sent;\x20queuing\x20it','transferred\x20and\x20closing','period','blue','MAKING\x20A\x20NEW\x20RPCS\x20RTC\x20CONNECTION','changeMicrophone','past','video_muted_init','connectionState','pixelFix','ICE:\x20','bandwidth\x20set\x20i!\x20','spot','createJavaScriptNode','nodownloads','viewheight','food','trackNumber','hand','old','wss://wss.vdo.ninja:443','ship','mass','youtubeKey','migrate','requestChangeGating','love','batteryState','mirrorGuestTarget','remoteZoom','smell','cleaning\x20up\x20lost\x20connection','dataOffset','concat','this\x20unverified\x20director\x20was\x20already\x20connected;\x20not\x20going\x20to\x20send\x20my\x20director\x20state\x20to\x20them','section','appendChild','remoteMuteElement','ICE\x20closed?','music','act','Content-Type','enhanceAudio','processIceBundle','throttling','sit','could','people','current','put','probable','cook','compare','Bad\x20EBML\x20VINT\x20size\x20','ran','setup\x20peer\x20complete','noon','who','settle','playback_audio_volume_meter','invite','copyTo','classList','continent','directorMutedState','iceServers','/status','require','Second\x20Thread\x20Waiting\x20for\x20TURN\x20LIST\x20to\x20load','ever','listen','none','event','sceneDisplay','exclude','remoteRaisedHandElement','delayNode','audio','stunServers','borderRadius','stats','school','solo','guest-connected','webrtc-is-blocked','getChannelData','add-a-label','notifyScreenShare','screen','keep','arrayBuffer','cbr','INITIAL\x20PUBLISH\x20START:\x20','clothe','couldn\x27t\x20set\x20rate\x20limit','privateKey','copy','discuss','child','outputDevice','setValueAtTime','iceTimer','delta','write','writer_config','agree','fit','silent','flower','tire','waitImageTimeout','onaudioprocess','host','turns:www.turn.obs.ninja:443','keyframeRate','when','whipout','broadcastIFrame','codirectorRequested','div','screenshareContentHint','AudioContext','WebMWriter','day','playing','fair','initialDirectorSync','MESHCAST();','remoteMuteState_','star','map','autoSyncCallback','very','signalMeter','devicePixelRatio','was','canvasWebGL','wire','Chromium-based\x20v','transcript','hidden','Websocket\x20connection\x20failed\x20or\x20something;\x20this\x20is\x20a\x20split\x20connection.\x20not\x20ideal,\x20as\x20it\x20could\x20be\x20unstable.','restricted','closing\x2014','no\x20audio\x20track\x20to\x20poke','whipOutScreenShareBitrate','requestChangeCompressor','stringify','flat','brought','joy','request\x20rate\x20limit:\x20','year','abc123','encrypt','frameMeta','scaleSnap','IchBinSteveDerNinja','limitTotalBitrateGuests','rejected','opacity','plugged','screenShareLabel','surprise','book','you','splice','fromCharCode','ontimeout','miniInfo','fat','whipOutCodec','tube','requestFocusChange','material','autoSyncObject','iframeDetails_','build','midiHotkeys','null\x20ice\x20rpcs','offerSDP','videoElement','hssConnection','women','indexOf','42001f','cause','HANG\x20UP\x20COMPLETE','Transfer\x20ended','hostname','error','substance','winter','receiveChannel','dataReceived','drop','readable','bufferedAmount','audioMeterGuest','CHUNKED\x20DETAILS','Remote\x20TURN\x20LIST\x20Loaded\x20**\x20','teeth','setAttribute','letter','audioChannels','create','200055ZLKMrE','woman','actual\x20bitrate:','micSampleRate','need','rpc\x20datachannel\x20closed','sense','midiChannel','train','hill','human','desaltStreamID','Unknown','Only\x20the\x20main\x20director\x20can\x20use\x20this\x20setting','spread','added\x20video\x20track','whepInputToken','requestAudioHack','remoteHash','done\x20clearing\x20audio','writeEBMLVarIntWidth','did','have','prioritize-audio','seek','optionalMicOnly','great','mono','bitrateTimeoutFirefox','lowerVolume','Setting\x20pc\x20connection\x20timeout\x20in\x205\x20seconds\x20??','part','experiment','death','region','has','meat','miss','decryptMessage','break','pauseClock','click','house','drink','canvas','roomid','Does\x20Local\x20Stream\x20Source\x20EXIST?','processFrame','autoadd','security','url','codecs','bitrate\x20timeout;\x20ios/firefox\x20specific:\x20','Messaging\x20sent','allowscreenvideo','visibility','./media/bg_sample.webp','infocus2','cid:','age','parent','pick','duck','high','contentType','meshcastCode','symbol','bread','often','wide','limiting\x20AudioEncoder','cbid','mutedState','displayMute','plain','video_init_width','obs.ninja/','were','eight','sign','chunkedStream','push','sceneMute','pow','\x20as\x20preferred\x20video\x20codec\x20by\x20viewer\x20via\x20API\x20(offer)','forceAspectRatio','vDAv','infocus','whep_URL','requestStats','configure','pretty','obsCommand','enabled','numberOfChannels','pop','either','webPquality','town','add','automute','gold','pluginVersion','hear','any','line','law','mykey','Chunked_audio','farm','option','closing\x205','relay','setResolution\x20triggered;\x20','touch','above','videoMutedFlag','half','she','waiting\x20for\x20keyframe','requestChangeMicDelay','parse','micIsolate','Adjusting\x20Gain;\x20only\x20track\x200\x20in\x20all\x20likely\x20hood,\x20unless\x20more\x20than\x20track\x200\x20support\x20is\x20added.','width','group_alt','group','VDO-Ninja','especially','subarray','turnlist','setParameters','AES','video_init_frameRate','processFrameVideo','object','targetBandwidth','quotient','SETUP\x20INCOMING','track','verb','person','noaudio','audiobitrate','subtract','wrote','cleanup','page','apple','screenElement','UUID','maxframeRate','dynamicScale','iframe','look','hunt','stick','turns:turn.obs.ninja:443','twenty','totalBitrate:\x20','Checking\x20to\x20see\x20if\x20reconnectino\x20to\x20ws\x20lost\x20any\x20peers','his','WHY\x20ARE\x20YOU\x20GOD\x20DAMN\x20BEEPING','minipreview','enc','layout','screenShareElement','The\x20request\x20(','surfaceSwitching','Media','tallyStyle','addALabel','showDirector','intime','checkBasicStreamsExist','overlay','chunkedAudioEnabled','retrying\x20at\x20an\x20interval','Valid\x20co\x20director\x20trying\x20to\x20transfer\x20a\x20guest','failed\x20to\x20send\x20focus\x20change\x20request','msg\x20size\x20error','place','liquid','loadoutID','checking','selectImageTFLITE_contents','director-connected','air','number','Firefox','Restarting\x20since\x20closed','This\x20shouldn\x27t\x20happen','rich','grabFaceData','Please\x20contact\x20steve@seguin.email\x20or\x20join\x20https://discord.vdo.ninja\x20if\x20Meshcast\x20is\x20not\x20working.','left','ACTION\x20REJECTED:\x20','audioEffects','couldn\x27t\x20set\x20preferred\x20audio\x20codec','glad','midiDevice','updateTime','h264','zoom','getOpusBitrate','set-video-bitrate','element','hurry','street','streamSrc','startTime','strange','closing\x2019','viewDirectorOnly','NOT\x20VIEW\x20TARGET','addTrack','autostart','change','turn:turn-usw2.vdo.ninja:3478','approved-as-director','Keyframe\x20inserted','audioGain','channelWidth','screenshareid','end-view-connection','padStart','rotation','RSASSA-PKCS1-v1_5','chatname','general','director-share','removeTrack','onnegotiationneeded\x20triggered;\x20creating\x20offer','are','got','already\x20connected\x201','should','PCMSource','corner','bind','practice','fine','preLimitedBitrate','village','stereo','such','oil','surface','1280','watchStream','state','iPhone12Up','consent','rotate_video','FAIL\x20rpcs\x20onconnectionstatechange','beauty','remoteDescription','coDirector','nacks_per_second','720','proper','midiIn','broadcast_mode','setVideoBitrates','consider','frame','save\x20bandwidth:\x20','downloads','setUint32','land','could\x20not\x20be\x20sent;\x20queuing\x20it','requestAs','srcObject','heard','under','degradationPreference','getElementById','directorSpeakerMuted','vdoninja','locale','broadcastChannel','bell','failed\x20to\x20disconnect','roomenc','disableREMB','appear','addTransceiver','locate','red','requestRateLimit','party','set','four','closeTimeout\x20cancelled;\x205','closeTimeout\x20cancelled;\x204','seedAttempts','say','learn','vp8','welcomeMessage','eye','here','audioTime','AES-CBC','screenShareElementHidden','shape','particular','deferring\x20with\x20a\x20promise','showClock','maxptime','preferAudioCodec','speakerMuted','height','arrive','except','postMessage','getResponseHeader','getVideoBitrates','noFEC','autorecordremote','tie','decode','flagship','changeOrder','bear','science','writeFloatBE','setRemoteDescription','sugar','space','Trying\x20to\x20set\x20','requestKeyframe','whep','obsStateSync','some','Unhandeled\x20Error\x20occured','border','showRoomTime','getWrittenSize','possible','setOpusAttributes','vp9','newViewConnection','manualSink','ICE\x20GATHER\x20COMPLETED','meterStyle','announceCoDirector','borderColor','now','constructor','screenshare_url','ice','requestChangeSubGain','imagine','','audioOptions','quart','then','\x20is\x20not\x20defined;\x20skipping.','GOT\x20ICES!!','\x20(fail)','lyra','course','try','pressed','lockedAudioBitrate','allowmidi','hangupbutton','Refreshing\x20scale','stood','bed','SHA-256','local','pip3','yet','startsWith','toLowerCase','spell','bitrateTimeout','includeRTT','webCodec','optimize','cpuLimited','lay','approved','determine','body','turn:turn-cae1.vdo.ninja:3478','whipOut','videoEncoder','completed','err','throw','tuning','reason','requestSceneUpdate','gotGenericData','addFrame','find','decodeRemote','opacityMuted','applyIsolatedVolume','control','screensharecursor','sdp','more','mount','sendChunks','mid','charge','userAgent','queue','getParameters','requested\x20file\x20was\x20not\x20found','localMuteElement','obsninja','selfBrowserSurface','mixMinus','arrange','common','image/webp','original','stopClock','realUUID','dog','ctrlKey','noREMB','raise','chord','onclose','auth','sendMsg','loadstart','motionDetectionInterval','real','retryTimeout','anyrequest','STREAM\x20ID\x20desalted\x202:','ping','take','cloneNode','depend','\x20query\x20is\x20not\x20defined;\x20skipping.','utf-8','allowVideo','noExitPrompt','Someone\x20sent\x20us\x20an\x20ANSWER\x20sdp??','getWriter','our','gone','process','cotton','imageElement','setupIncoming','card','altUUID','writeBytes','solve','videosource','season','pos','allowDownloads','lost','set-meshcast-video-bitrate','obsRemotePassword','\x20as\x20preferred\x20codec\x20by\x20viewer\x20via\x20API','king','coDirectorEnable','chunked_mode_audio','nosettings','iOS\x20devices\x20do\x20not\x20support\x20dynamic\x20bitrates\x20correctly;\x20skipping','pass','not\x20allowed\x20to\x20show\x20the\x20director','study','video_encoder','bigmutebutton','samplingFrequency','configVideo','obsSceneTriggers','Publisher\x20is\x20being\x20sent\x20a\x20video\x20stream???\x20NOT\x20EXPECTED!','pushEffectsData','sync','SENDING\x20CHUNKS\x20TO:\x20','done','atom','for','serve','middle','successfully\x20sent\x20message\x20vis\x20WebRTC\x20instead\x20of\x20WSS','shine','told','EncodedAudioChunk','1240dkFXfV','muteStateTemplate','mile','PCS\x20WINS\x20ICE','note','fillStyle','getAudioSettings','header','play','nor','EBML\x20VINT\x20size\x20not\x20supported\x20','newMainDirectorSetup','Trying\x20to\x20join\x20at\x20least','canvasStream','pptControls','they','allowScreenAudio','maxconnections','isInteger','obsSceneSync','nation','scale','json','level','tall','rest','sendOnNewConnect','labelstyle','totalRoomBitrate_default','new','overlayNinja','dress','loadend','lowMobileBitrate','pitch','zoomedBitrate','believe','currentTarget','dtx','createMediaStreamDestination','privacy','request\x20zoom\x20change:\x20','character','band','getSenders','can\x27t\x20change\x20bitrate;\x20no\x20video\x20senders\x20found','createBuffer','Chunked_video','h264profile','processDescription','screenshareVideoOnly','iframeSrcs','open','cleanish','deep','scale\x20set!\x20','message','can\x27t\x20change\x20bitrate;\x20no\x20video\x20sender\x20found','stopWriter','remoteStats','inboundAudioPipeline','name','glass','seedStream','warm','idea','streaming','double','wing','onconnectionstatechange','video_2_init_width','msg','can','plant','BYE','Answer\x20SDP\x20does\x20not\x20have\x20a\x20matching\x20session\x20ID','sitePassword','\x20','info','visible','limitaudio','offerToReceiveAudio','directorVolumeState','showTime','dropped\x20candidate\x20due\x20to\x20filter','gpGPU','why','maxviewers_url','planet','brown','requested-stream','valley','forward','connected\x20to\x20video\x20server','importKey','obsstudio','getLocalStream','screenShareState','chunked_mode_video','lowcut','Video\x20File','pattern','kill','bottom','bitrate_set','lot','prepare','room=','NOT\x20IN\x20VIEW\x20SET','bundlePolicy','custom\x20layout\x20being\x20applied','writable','right','novideo','soon','pipWindow','led','changeSpeaker','ring','Stream\x20ID\x20is\x20already\x20in\x20use.','able','sail','suggest','enhanceAudioEncoder','sensorData','Inbound\x20User-based\x20Message\x20from\x20Room','theyBeSharksHere','multiply','fly','toString','SCREENS','case','chunkedInQueue','remoteMuted','meant','request','screen-share-state','fact','LOADING\x20UP\x20WAITING\x20WATCH\x20STREAM:\x20','ptime','foot','you-are-a-codirector','writeEBMLVarInt','against','de1','what','frameRate','end','midiRemote','life','Max\x20bandwidth\x20NOT\x20being\x20capped:\x20','mine','special','bitrate','sendChannel','answer','iceConnectionState\x20==\x20connected','de2','complete','written','autoplay','busy','POST','motion','receive','readAsArrayBuffer','setRequestHeader','realTimeAudio','video_init_height','eat','engine','transferred','chief','shift','their','true\x20.','directorBlindAllGuests','whip','BYE\x20RPCS','prompt-access-request','interval','garden','turn:turn-use1.vdo.ninja:3478','maintain-framerate','move','view-connection','over','director','been','promise_audio','boat','stream','allowAudio','remove','simple','groupAudio','motionSwitch','sheet','keyname','codirector_changeURL','firstPlayTriggered','cmd','session.limitMaxBandwidth\x20running:\x20','directorPassword','loud','collect','match','Meshcast\x20error:\x20432','directorBox','wont','directorState','onconnectionstatechange\x20pcs\x20ice\x20--\x20disconnected,\x20but\x20not\x20yet\x20closed?\x20','use1','endViewConnection','prototype','onopen','listPromise','clicked','stream_configVideo','rtc\x20data\x20channel\x20error\x202:\x20','nopreview','hss-connection','remote-video-mute-state','retryWatchInterval','contain','debug','isScene','her','roll','grass','watchTimeoutList2:','tail','burn','tiny','type','-kbps','cleanOutput','trouble','syncState','last','closing\x20rpc\x20due\x20to\x20hangup\x20event','changeCamera','sun','ptz','modifyDescLyra','ice\x20timer\x20no\x20longer\x20exists','writeUnsignedIntBE','about','guest','directorHash','requestStream','Created\x20transfer\x20channel','triangle','canvasIntervalAction','direct','stream_configAudio','screenStream','mark','disabled','prove','maxvb_url','song','onreadystatechange','webAudios','raisehands','jointone','A_OPUS','rule','organ','kind','white','wssid','createAnswer','configAudio','\x20---\x20PC\x20TIMED\x20OUT\x20and\x20already\x20deleted.\x20shouldn\x27t\x20happen','setClock','correct','talk','Someone\x20published\x20a\x20video\x20to\x20the\x20Room','slots','closeRPC','lockedVideoBitrate','resolution\x20scale:\x20','HANG\x20UP\x202\x20COMPLETE','pull','sat','noNacks','If\x20self-hosting\x20VDO.Ninja,\x20please\x20contact\x20steve@seguin.email\x20to\x20request\x20having\x20access\x20to\x20Meshcast.','turn:turn-eu4.vdo.ninja:3478','showSettings','speakerMute','allowWebp','starting\x20some\x20preload\x20bitrate\x20','studioSoftware','cross','allowwhipout','huge','changeLabel','captain','permaid','dollar','crease','byteLength','view-connection-info','represent','tool','code','options','\x27\x20target=\x27_blank\x27>','remote-screenshare-state','occur','streamSrcClone','laugh','operate','Remote\x20request\x20failed\x20to\x20decode;\x20continuing\x20still.','configuration','turn:www.turn.vdo.ninja:3478','division','writeU8','available-speedtest-servers','pipe','dictionary','value\x20there','6REUDVY','oxygen','thought','nature','removed\x20from\x20SDP:\x20\x27a=extmap:3\x20urn:3gpp:video-orientation\x0d\x0a\x27','month','board','connected','stereo\x20inbound\x20enabled','less','processFrameAudio','scaleFactor','approved:\x20','wssSetViaUrl','insect','sendroom','batteryMeter','effectsData','slice','lowerhand','five','estop','42e01f','needKeyFrame','vDav','video_bitrate_kbps','SDP\x20Sessions\x20Match.\x20I\x20assume\x20ADDING\x20TRACKS.\x20RPCS','closedCaptions','press','deviceId','cpu','showControls','vary','closing\x207','colorVideosBackground','showSaveFilePicker','seat','http://','WEBRTC\x20CONNECTION\x20OPEN','Authorization','no\x20video\x20track\x20to\x20control','separate','soft','filetransfer','responseText','outboundVideoBitrate','dedicatedControlBarSpace','does','hangup','timer','shore','count','justResetting','work','wss://api.vdo.ninja:443','allowwebp','boy','closeTimeout\x20cancelled;\x202','waitImage','equalizer','target','obsControls','h264profile\x20being\x20modified','full','gain','recordLocal','brother','parentNode','directorEnabledPPT','log','read','remoteVideoMuteElement','view_set','timestamp','flow','init_audio','innerHTML','long','began','coast','machine','focusDistance','orderby','yellow','audioContext','getRandomValues','Generate\x20Some\x20Crypto\x20keys\x20first','setResolution','side','sight','call','heart','single','processPCSOnMessage','adaptivePtime','far','back','equal','lockWindowSize','gray','know','locked','keyframeTimeout','createWritable','loudest','maxvideobitrate','processRPCSOnMessage','promptAccess','whip_Host','conn_type','wash','cloud','him','among','poem','whepInput','connect','instrument','pcs','screensharebutton','%\x20battery\x20remaining','m\x20:\x20','whipView','#obsRemotePassword>input','soloChatUUID','joining-room','ocean','Not\x20a\x20scene','scaleWidth','room_init','vowel','fr1','corn','initial_group','provideFileList','abs','setupScreenShareAddon','not\x20record\x20button\x20detected;\x20can\x27t\x20update\x20time\x20since\x20started\x20recording','closing\x206','stop','money','iceConnectionState','hands_','neck','requestCoDirector','forceRetry','stretch','startClock','certain','quality','camp','getVideoTracks','seed','whipCallback','provide','gathering','allowMIDI',',\x20mc?:\x20','little','innerText','starting\x20kicker','Browser','solo-scene-connected','style','audio\x20bandwidth\x20set\x20f!','bandwidth\x20set\x20h!\x20','help','wrong','setScale','onerror','guess','chunked','writer','stopPropagation','team','same','sensors','defaultPassword','encodings','mutedStateMixer','offsetChannel','hidehome','set-audio-bitrate','weight','mirrorExclude','label','choose','cleanDirector','requestVideoRecord','allow','sendMessage','#000','password','whipOutputToken','form','nocursor','already\x20closed\x20PCS','modern','audioInputChannels','indicate','videoMuted','dont','RPCS\x20for\x20MESHCAST\x20ISNT\x20MADE\x20YET??','chart','island','widget','RTCRtpSender','147934mdboeE','thin','noMeshcast','hostedTransfers','encodering\x20being\x20kicked','time','iceGatheringState','came','room-is-claimed','chance','sink','start\x20writing\x20frames','ctrl','addEventListener','UUID\x20not\x20found;\x20cant\x27\x20close','TRYING\x20TO\x20SYNC\x20WITH\x20SENDING:\x20','spring','358929WGKhVq','morning','resolve','bit','until','frameWriter','moon','sendFile','force','RUNNING\x20CALLBACK:\x20','theirtime','directorSettings','size','still','charCodeAt','electric','CONNECTED\x20TO\x20FIRST\x20PEER','offset','phrase','word','value','bigPlayButton','Connection\x20to\x20Control\x20Server\x20lost.\x0a\x0aWill\x20try\x20to\x20reconnect\x20in\x202\x20seconds.','coat','no\x20UUID\x20in\x20msg','closing\x209','transfer','sentence','tell','set-video-scale','webkitAudioContext','GOT\x20ICE!!','invalid-remote-code','request\x20focus\x20change:\x20','grand','settings','Meshcast\x20SET\x20SCALING\x20IS\x20FIRING,\x20which\x20is\x20GOOD\x20!!!!!!','result','length','noisegate','milk','Bad\x20UINT\x20size\x20','straight','readyState','applyIsolatedChat','realTimeVideo','directorSpeakerMute','solution','micIsolated','addIceCandidate','wss','obsfix','big','changeURL','sending\x20request\x20via\x20server','pastSlots','waitingWatchList','true','exercise','codirector_transfer','hash','cameraConstraints','showlabels','screenshareAutogain','createOffer','ruleOfThirds','candidate','fillDataBuffer','setVideoScale','stopping\x20some\x20preload\x20bitrate\x20','clean','ifs','recorder','wheel','[data-action-type=\x22remove-queue\x22][data--u-u-i-d=\x22','PROBLEM,\x20Senders\x20is\x20more\x20than\x200:\x20','remote-group-change','details','must','directVideoMuted','forceRetryTimeout','sending\x20message\x20via\x20WSS\x20as\x20WebRTC\x20failed\x20to\x20send\x20message;\x20RTC\x20peers\x20only','suit','application/sdp','playback_audio_pipeline','seedPlz','sceneSync','requestFile','already\x20watching\x20stream','unshift','speedtest','property','?ts=','forceios','channelOffset','site-not-responsive','mind','noWidget','signature','Failed\x20to\x20request\x20video\x20and\x20audio;\x20iOS\x20device\x20asking?','requestedStatsInterval','where','fill','hasOwnProperty','one','hanging\x20up','pong','total','activelySpeaking','door','priority','needsLoading','make','row','sceneType2','soloVideo','content-type','onTrack','not-the-director','knew','guide','list','stopping\x20old\x20track','apiserver','industry','requestChangeLowcut','requestUpload','ori','fakeFeeds','pcm','successfully\x20requested\x20audio\x20and\x20video?\x20maybe?','Audio\x20isn\x27t\x20setup\x20yet.','best','whipoutSettings','the','chair','most','timeout','remoteMuteState','reload','time_seconds','random','getTracks','hat','noiseSuppression','car','[data-action-type=\x27recorder-local\x27][data--u-u-i-d=\x27','done\x20setting\x20degrad','Requested_resolution','deferring\x20with\x20a\x20promise;\x20hashed\x20room','getAsDataArray','src','directorList','readAsText','img','snow','action','new-push-connection','captureStream','problem','buy','interest','broadcastChannelID','anysend','session.provideFileList','Overwrite\x20crosses\x20blob\x20boundaries','savedBitrate','cold'];_0x2d5e=function(){return _0x3d0c02;};return _0x2d5e();}async function meshcastWatch(_0x2a80ac,_0x1ea9d5){var _0xf745aa=_0x2de9a7;!(_0x2a80ac in session['rpcs'])&&(session[_0xf745aa(0x16a)][_0x2a80ac]={},session[_0xf745aa(0x16a)][_0x2a80ac][_0xf745aa(0x436)]={},session[_0xf745aa(0x16a)][_0x2a80ac][_0xf745aa(0x9db)]=![],session[_0xf745aa(0x16a)][_0x2a80ac][_0xf745aa(0x6dc)]={},session['rpcs'][_0x2a80ac][_0xf745aa(0x921)]=![],session[_0xf745aa(0x16a)][_0x2a80ac][_0xf745aa(0x592)]=![],session['rpcs'][_0x2a80ac][_0xf745aa(0x8e6)]=![],session['rpcs'][_0x2a80ac][_0xf745aa(0x350)]=null,session['rpcs'][_0x2a80ac][_0xf745aa(0x665)]=![],session['rpcs'][_0x2a80ac][_0xf745aa(0x7b9)]=![],session[_0xf745aa(0x16a)][_0x2a80ac][_0xf745aa(0x621)]=![],session[_0xf745aa(0x16a)][_0x2a80ac][_0xf745aa(0x3e4)]=![],errorlog(_0xf745aa(0x8ad)));var _0x157d02=!![],_0x5bdb16=!![];if(session[_0xf745aa(0x711)]!==![]&&!session['novideo'][_0xf745aa(0x2c2)](session[_0xf745aa(0x16a)][_0x2a80ac][_0xf745aa(0x9bb)]))_0x157d02=![];else session[_0xf745aa(0x16a)][_0x2a80ac][_0xf745aa(0x8e6)]&&!session[_0xf745aa(0x16a)][_0x2a80ac][_0xf745aa(0x8e6)][_0xf745aa(0x30c)]&&(_0x157d02=![]);if(session[_0xf745aa(0x542)]!==![]&&!session['noaudio'][_0xf745aa(0x2c2)](session['rpcs'][_0x2a80ac]['streamID']))_0x5bdb16=![];else session['rpcs'][_0x2a80ac]['settings']&&!session['rpcs'][_0x2a80ac][_0xf745aa(0x8e6)][_0xf745aa(0x433)]&&(_0x5bdb16=![]);if(!_0x5bdb16&&!_0x157d02){errorlog(_0xf745aa(0x96d));return;}disableQualityDirector(_0x2a80ac);!session[_0xf745aa(0x7db)]&&await chooseBestTURN();try{session[_0xf745aa(0x16a)][_0x2a80ac][_0xf745aa(0x600)]=new RTCPeerConnection(session[_0xf745aa(0x7db)]);}catch(_0x5f488a){!session['cleanOutput']&&warnUser(_0xf745aa(0xa48));}session[_0xf745aa(0x16a)][_0x2a80ac]['whep'][_0xf745aa(0x196)]=function(_0x34dae9){var _0x4c050a=_0xf745aa;session[_0x4c050a(0x938)](_0x34dae9,_0x2a80ac);};var _0x27c92c=session[_0xf745aa(0xa76)](0xe),_0x31ea3a={};_0x31ea3a[_0xf745aa(0x9bb)]=_0x1ea9d5['token'],_0x31ea3a['UUID']=_0x27c92c;function _0x282161(_0x5ddbd9){var _0xa4f57d=_0xf745aa,_0xceae3=new XMLHttpRequest();_0xceae3[_0xa4f57d(0x7a6)]=function(){var _0x2f56d4=_0xa4f57d;if(this[_0x2f56d4(0x8ee)]==0x4&&(this[_0x2f56d4(0xa13)]==0xc8||this[_0x2f56d4(0xa13)]==0xc9)){var _0xfd1744=this['getResponseHeader']('content-type');if(_0xfd1744==_0x2f56d4(0x916)){var _0xc1f7be={};_0xc1f7be[_0x2f56d4(0x648)]=this[_0x2f56d4(0x80f)],_0xc1f7be['type']=_0x2f56d4(0x20f),session[_0x2f56d4(0x16a)][_0x2a80ac][_0x2f56d4(0x600)]['setRemoteDescription'](_0xc1f7be)['then'](function(){_0x5181c6();})[_0x2f56d4(0x3b6)](function(_0x5dc6c4){log(_0x5dc6c4);});}}else log(this);},_0xceae3[_0xa4f57d(0x6d4)](_0xa4f57d(0x742),_0x1ea9d5['url'],!![]),_0xceae3[_0xa4f57d(0x746)](_0xa4f57d(0x40f),_0xa4f57d(0x1f2)),_0xceae3['setRequestHeader'](_0xa4f57d(0x80a),_0xa4f57d(0x1f0)+_0x1ea9d5[_0xa4f57d(0x9f7)]),_0xceae3[_0xa4f57d(0x2b9)](JSON[_0xa4f57d(0x479)](_0x5ddbd9));}function _0x5181c6(){var _0x9f8565=_0xf745aa;session[_0x9f8565(0x16a)][_0x2a80ac][_0x9f8565(0x600)][_0x9f8565(0x7b0)]()['then'](function(_0x184342){var _0x545fd6=_0x9f8565;return _0x184342['sdp']=CodecsHandler[_0x545fd6(0x608)](_0x184342[_0x545fd6(0x648)],{'stereo':0x1}),session[_0x545fd6(0x16a)][_0x2a80ac]['whep']['setLocalDescription'](_0x184342);})[_0x9f8565(0x619)](function(){var _0x4ee111=_0x9f8565,_0x36427d={};_0x36427d[_0x4ee111(0x54a)]=_0x27c92c,_0x36427d[_0x4ee111(0x73b)]=session[_0x4ee111(0x16a)][_0x2a80ac]['whep'][_0x4ee111(0x1f3)][_0x4ee111(0x648)],_0x282161(_0x36427d);})[_0x9f8565(0x3b6)](function(_0x21c658){});}_0x282161(_0x31ea3a);}(function(){'use strict';var _0x1048df=_0x2de9a7;let _0xb02573=function(_0x4042eb){this['data']=new Uint8Array(_0x4042eb),this['pos']=0x0;};_0xb02573[_0x1048df(0x776)]['seek']=function(_0x18d44f){var _0x34442a=_0x1048df;this[_0x34442a(0x680)]=_0x18d44f;},_0xb02573[_0x1048df(0x776)][_0x1048df(0x67c)]=function(_0x27cced){var _0x574bbc=_0x1048df;for(let _0x69b7a0=0x0;_0x69b7a0<_0x27cced[_0x574bbc(0x8e9)];_0x69b7a0++){this[_0x574bbc(0x3bc)][this[_0x574bbc(0x680)]++]=_0x27cced[_0x69b7a0];}},_0xb02573[_0x1048df(0x776)][_0x1048df(0x1c7)]=function(_0xa27620){var _0x38f29a=_0x1048df;this[_0x38f29a(0x3bc)][this[_0x38f29a(0x680)]++]=_0xa27620;},_0xb02573[_0x1048df(0x776)]['writeU8']=_0xb02573['prototype'][_0x1048df(0x1c7)],_0xb02573[_0x1048df(0x776)]['writeU16BE']=function(_0x2cf3b9){var _0x537c23=_0x1048df;this[_0x537c23(0x3bc)][this[_0x537c23(0x680)]++]=_0x2cf3b9>>0x8,this[_0x537c23(0x3bc)][this[_0x537c23(0x680)]++]=_0x2cf3b9;},_0xb02573[_0x1048df(0x776)][_0x1048df(0x218)]=function(_0x51b961){var _0xd0b85b=_0x1048df;let _0x40d111=new Uint8Array(new Float64Array([_0x51b961])[_0xd0b85b(0xa33)]);for(let _0x58e10f=_0x40d111[_0xd0b85b(0x8e9)]-0x1;_0x58e10f>=0x0;_0x58e10f--){this[_0xd0b85b(0x1c7)](_0x40d111[_0x58e10f]);}},_0xb02573[_0x1048df(0x776)][_0x1048df(0x5fa)]=function(_0x3f3b96){var _0x40ffd3=_0x1048df;let _0x5d5c78=new Uint8Array(new Float32Array([_0x3f3b96])[_0x40ffd3(0xa33)]);for(let _0xa4803b=_0x5d5c78['length']-0x1;_0xa4803b>=0x0;_0xa4803b--){this[_0x40ffd3(0x1c7)](_0x5d5c78[_0xa4803b]);}},_0xb02573[_0x1048df(0x776)][_0x1048df(0x35d)]=function(_0xe8069c){var _0x1efdbe=_0x1048df;for(let _0x348311=0x0;_0x348311<_0xe8069c[_0x1efdbe(0x8e9)];_0x348311++){this[_0x1efdbe(0x3bc)][this[_0x1efdbe(0x680)]++]=_0xe8069c[_0x1efdbe(0x8d1)](_0x348311);}},_0xb02573[_0x1048df(0x776)][_0x1048df(0x4c8)]=function(_0x2e4ea3,_0x5a7531){var _0x4f5207=_0x1048df;switch(_0x5a7531){case 0x1:this[_0x4f5207(0x7de)](0x1<<0x7|_0x2e4ea3);break;case 0x2:this[_0x4f5207(0x7de)](0x1<<0x6|_0x2e4ea3>>0x8),this[_0x4f5207(0x7de)](_0x2e4ea3);break;case 0x3:this[_0x4f5207(0x7de)](0x1<<0x5|_0x2e4ea3>>0x10),this[_0x4f5207(0x7de)](_0x2e4ea3>>0x8),this[_0x4f5207(0x7de)](_0x2e4ea3);break;case 0x4:this[_0x4f5207(0x7de)](0x1<<0x4|_0x2e4ea3>>0x18),this[_0x4f5207(0x7de)](_0x2e4ea3>>0x10),this[_0x4f5207(0x7de)](_0x2e4ea3>>0x8),this[_0x4f5207(0x7de)](_0x2e4ea3);break;case 0x5:this[_0x4f5207(0x7de)](0x1<<0x3|_0x2e4ea3/0x100000000&0x7),this[_0x4f5207(0x7de)](_0x2e4ea3>>0x18),this['writeU8'](_0x2e4ea3>>0x10),this[_0x4f5207(0x7de)](_0x2e4ea3>>0x8),this[_0x4f5207(0x7de)](_0x2e4ea3);break;default:throw new Error(_0x4f5207(0x41b)+_0x5a7531);}},_0xb02573[_0x1048df(0x776)][_0x1048df(0x3a4)]=function(_0xf50d9e){var _0x355dae=_0x1048df;if(_0xf50d9e<(0x1<<0x7)-0x1)return 0x1;else{if(_0xf50d9e<(0x1<<0xe)-0x1)return 0x2;else{if(_0xf50d9e<(0x1<<0x15)-0x1)return 0x3;else{if(_0xf50d9e<(0x1<<0x1c)-0x1)return 0x4;else{if(_0xf50d9e<0x7ffffffff)return 0x5;else throw new Error(_0x355dae(0x6aa)+_0xf50d9e);}}}}},_0xb02573[_0x1048df(0x776)][_0x1048df(0x72e)]=function(_0x11d6c7){var _0x4192b2=_0x1048df;this[_0x4192b2(0x4c8)](_0x11d6c7,this[_0x4192b2(0x3a4)](_0x11d6c7));},_0xb02573[_0x1048df(0x776)]['writeUnsignedIntBE']=function(_0x283350,_0x3b448c){var _0x5c3823=_0x1048df;_0x3b448c===undefined&&(_0x3b448c=this[_0x5c3823(0x97d)](_0x283350));switch(_0x3b448c){case 0x5:this[_0x5c3823(0x7de)](Math[_0x5c3823(0x3c1)](_0x283350/0x100000000));case 0x4:this[_0x5c3823(0x7de)](_0x283350>>0x18);case 0x3:this[_0x5c3823(0x7de)](_0x283350>>0x10);case 0x2:this['writeU8'](_0x283350>>0x8);case 0x1:this['writeU8'](_0x283350);break;default:throw new Error(_0x5c3823(0x8ec)+_0x3b448c);}},_0xb02573[_0x1048df(0x776)][_0x1048df(0x97d)]=function(_0x207f2d){if(_0x207f2d<0x1<<0x8)return 0x1;else{if(_0x207f2d<0x1<<0x10)return 0x2;else{if(_0x207f2d<0x1<<0x18)return 0x3;else return _0x207f2d<0x100000000?0x4:0x5;}}},_0xb02573['prototype']['getAsDataArray']=function(){var _0x513424=_0x1048df;if(this[_0x513424(0x680)]this['length'])throw new Error(_0x8c6f3a(0x17f));this[_0x8c6f3a(0x680)]=_0x139464;},this[_0x2f6764(0x44d)]=function(_0x833ac9){var _0x3e98c6=_0x2f6764;let _0x48bdc4={'offset':this[_0x3e98c6(0x680)],'data':_0x833ac9,'length':_0xf18960(_0x833ac9)},_0x26e421=_0x48bdc4['offset']>=this[_0x3e98c6(0x8e9)];this[_0x3e98c6(0x680)]+=_0x48bdc4[_0x3e98c6(0x8e9)],this[_0x3e98c6(0x8e9)]=Math['max'](this[_0x3e98c6(0x8e9)],this[_0x3e98c6(0x680)]),_0x33b208=_0x33b208[_0x3e98c6(0x619)](async function(){var _0x5ca49c=_0x3e98c6;if(_0x19fc93)return new Promise(function(_0x33166a,_0x1e6a05){var _0x15001b=_0x5c68;_0x20dc80(_0x48bdc4[_0x15001b(0x3bc)])['then'](function(_0x2ff33a){var _0x26adca=_0x15001b;let _0x307884=0x0,_0x4f75bc=Buffer['from'](_0x2ff33a['buffer']),_0x6766e4=function(_0x5daf95,_0x57db1f,_0x424619){var _0x37e2f0=_0x5c68;_0x307884+=_0x57db1f,_0x307884>=_0x424619[_0x37e2f0(0x8e9)]?_0x33166a():_0x3122fc[_0x37e2f0(0x44d)](_0x19fc93,_0x424619,_0x307884,_0x424619['length']-_0x307884,_0x48bdc4[_0x37e2f0(0x8d4)]+_0x307884,_0x6766e4);};_0x3122fc[_0x26adca(0x44d)](_0x19fc93,_0x4f75bc,0x0,_0x4f75bc[_0x26adca(0x8e9)],_0x48bdc4[_0x26adca(0x8d4)],_0x6766e4);});});else{if(_0xa6357c)return new Promise(function(_0x5463d7,_0x1cb93c){var _0x2b7146=_0x5c68;_0xa6357c[_0x2b7146(0x4cc)](_0x48bdc4['offset'])[_0x2b7146(0x619)](()=>{var _0x57fd8d=_0x2b7146;_0xa6357c[_0x57fd8d(0x44d)](new Blob([_0x48bdc4['data']]));})[_0x2b7146(0x619)](()=>{_0x5463d7();});});else{if(!_0x26e421)for(let _0x568e76=0x0;_0x568e76<_0x5a8774['length'];_0x568e76++){let _0x58a222=_0x5a8774[_0x568e76];if(!(_0x48bdc4['offset']+_0x48bdc4[_0x5ca49c(0x8e9)]<=_0x58a222[_0x5ca49c(0x8d4)]||_0x48bdc4['offset']>=_0x58a222[_0x5ca49c(0x8d4)]+_0x58a222[_0x5ca49c(0x8e9)])){if(_0x48bdc4[_0x5ca49c(0x8d4)]<_0x58a222[_0x5ca49c(0x8d4)]||_0x48bdc4[_0x5ca49c(0x8d4)]+_0x48bdc4[_0x5ca49c(0x8e9)]>_0x58a222['offset']+_0x58a222['length'])throw new Error(_0x5ca49c(0x968));if(_0x48bdc4[_0x5ca49c(0x8d4)]==_0x58a222['offset']&&_0x48bdc4[_0x5ca49c(0x8e9)]==_0x58a222[_0x5ca49c(0x8e9)]){_0x58a222['data']=_0x48bdc4['data'];return;}else return _0x20dc80(_0x58a222[_0x5ca49c(0x3bc)])['then'](function(_0x1fd8c5){var _0x1fe974=_0x5ca49c;return _0x58a222['data']=_0x1fd8c5,_0x20dc80(_0x48bdc4[_0x1fe974(0x3bc)]);})[_0x5ca49c(0x619)](function(_0x10df0a){var _0x3b68b8=_0x5ca49c;_0x48bdc4[_0x3b68b8(0x3bc)]=_0x10df0a,_0x58a222[_0x3b68b8(0x3bc)]['set'](_0x48bdc4[_0x3b68b8(0x3bc)],_0x48bdc4['offset']-_0x58a222[_0x3b68b8(0x8d4)]);});}}}}_0x5a8774[_0x5ca49c(0x505)](_0x48bdc4);});},this[_0x2f6764(0x73e)]=function(_0x333559){var _0x1c69dd=_0x2f6764;return _0x19fc93||_0xa6357c?_0x33b208=_0x33b208[_0x1c69dd(0x619)](function(){return null;}):_0x33b208=_0x33b208[_0x1c69dd(0x619)](function(){var _0x49eb98=_0x1c69dd;let _0x3d688f=[];for(let _0x587cce=0x0;_0x587cce<_0x5a8774[_0x49eb98(0x8e9)];_0x587cce++){_0x3d688f['push'](_0x5a8774[_0x587cce][_0x49eb98(0x3bc)]);}return new Blob(_0x3d688f,{'type':_0x333559});}),_0x33b208;};};};window[_0x2d2b95(0x357)]=_0x38128b(null);}()),(function(){'use strict';var _0x4f505d=_0x2de9a7;function _0xf2aa72(_0x16b264){var _0x39719b=_0x5c68;this[_0x39719b(0x8d7)]=_0x16b264;}function _0x18bc03(_0x1770c7,_0xec7cdc){var _0x8eb305=_0x5c68;let _0x1d8bfe={};return[_0x1770c7,_0xec7cdc][_0x8eb305(0x982)](function(_0x5b7114){var _0x5179cb=_0x8eb305;for(let _0x1ac084 in _0x5b7114){Object['prototype'][_0x5179cb(0x92a)]['call'](_0x5b7114,_0x1ac084)&&(_0x1d8bfe[_0x1ac084]=_0x5b7114[_0x1ac084]);}}),_0x1d8bfe;}function _0x3a6d8a(_0x4c5dde,_0x2743d7,_0x38ca1a){var _0x28b834=_0x5c68;if(Array[_0x28b834(0x20a)](_0x38ca1a))for(let _0x14e401=0x0;_0x14e401<_0x38ca1a['length'];_0x14e401++){_0x3a6d8a(_0x4c5dde,_0x2743d7,_0x38ca1a[_0x14e401]);}else{if(typeof _0x38ca1a==='string')_0x4c5dde[_0x28b834(0x35d)](_0x38ca1a);else{if(_0x38ca1a instanceof Uint8Array)_0x4c5dde[_0x28b834(0x67c)](_0x38ca1a);else{if(_0x38ca1a['id']){_0x38ca1a[_0x28b834(0x8d4)]=_0x4c5dde['pos']+_0x2743d7,_0x4c5dde[_0x28b834(0x796)](_0x38ca1a['id']);if(Array[_0x28b834(0x20a)](_0x38ca1a[_0x28b834(0x3bc)])){let _0x5cfdbb,_0x4711b0,_0x1f9fcb;_0x38ca1a[_0x28b834(0x8cf)]===-0x1?_0x4c5dde[_0x28b834(0x1c7)](0xff):(_0x5cfdbb=_0x4c5dde[_0x28b834(0x680)],_0x4c5dde['writeBytes']([0x0,0x0,0x0,0x0])),_0x4711b0=_0x4c5dde[_0x28b834(0x680)],_0x38ca1a[_0x28b834(0x406)]=_0x4711b0+_0x2743d7,_0x3a6d8a(_0x4c5dde,_0x2743d7,_0x38ca1a[_0x28b834(0x3bc)]),_0x38ca1a[_0x28b834(0x8cf)]!==-0x1&&(_0x1f9fcb=_0x4c5dde['pos'],_0x38ca1a[_0x28b834(0x8cf)]=_0x1f9fcb-_0x4711b0,_0x4c5dde[_0x28b834(0x4cc)](_0x5cfdbb),_0x4c5dde[_0x28b834(0x4c8)](_0x38ca1a[_0x28b834(0x8cf)],0x4),_0x4c5dde[_0x28b834(0x4cc)](_0x1f9fcb));}else{if(typeof _0x38ca1a[_0x28b834(0x3bc)]===_0x28b834(0xa46))_0x4c5dde[_0x28b834(0x72e)](_0x38ca1a[_0x28b834(0x3bc)][_0x28b834(0x8e9)]),_0x38ca1a[_0x28b834(0x406)]=_0x4c5dde[_0x28b834(0x680)]+_0x2743d7,_0x4c5dde[_0x28b834(0x35d)](_0x38ca1a[_0x28b834(0x3bc)]);else{if(typeof _0x38ca1a[_0x28b834(0x3bc)]===_0x28b834(0x570))!_0x38ca1a['size']&&(_0x38ca1a[_0x28b834(0x8cf)]=_0x4c5dde[_0x28b834(0x97d)](_0x38ca1a[_0x28b834(0x3bc)])),_0x4c5dde[_0x28b834(0x72e)](_0x38ca1a[_0x28b834(0x8cf)]),_0x38ca1a[_0x28b834(0x406)]=_0x4c5dde['pos']+_0x2743d7,_0x4c5dde[_0x28b834(0x796)](_0x38ca1a[_0x28b834(0x3bc)],_0x38ca1a['size']);else{if(_0x38ca1a[_0x28b834(0x3bc)]instanceof _0xf2aa72)_0x4c5dde['writeEBMLVarInt'](0x8),_0x38ca1a[_0x28b834(0x406)]=_0x4c5dde['pos']+_0x2743d7,_0x4c5dde[_0x28b834(0x218)](_0x38ca1a[_0x28b834(0x3bc)][_0x28b834(0x8d7)]);else{if(_0x38ca1a[_0x28b834(0x3bc)]instanceof _0xf2aa72)_0x4c5dde[_0x28b834(0x72e)](0x4),_0x38ca1a[_0x28b834(0x406)]=_0x4c5dde[_0x28b834(0x680)]+_0x2743d7,_0x4c5dde[_0x28b834(0x5fa)](_0x38ca1a[_0x28b834(0x3bc)][_0x28b834(0x8d7)]);else{if(_0x38ca1a[_0x28b834(0x3bc)]instanceof Uint8Array)_0x4c5dde['writeEBMLVarInt'](_0x38ca1a[_0x28b834(0x3bc)][_0x28b834(0x7ce)]),_0x38ca1a[_0x28b834(0x406)]=_0x4c5dde[_0x28b834(0x680)]+_0x2743d7,_0x4c5dde[_0x28b834(0x67c)](_0x38ca1a[_0x28b834(0x3bc)]);else throw new Error(_0x28b834(0x9e6)+typeof _0x38ca1a[_0x28b834(0x3bc)]);}}}}}}else throw new Error(_0x28b834(0x9e6)+typeof _0x38ca1a[_0x28b834(0x3bc)]);}}}}let _0x2e4729=function(_0x1ee306,_0x17b493){return function(_0x1d5998){var _0x52dcb5=_0x5c68;let _0x2fc561=0x1388,_0x525486=![],_0x57575c=0x0,_0x88da70=0x0,_0x3cb890=!![],_0x58d674=0x0,_0x110b63=0xbb80,_0x1ac8c3=0x1,_0x108e76=[],_0x1e33ba=0x0,_0x42297d=0x0,_0x13134f=0x0,_0x5f4351={'fileWriter':null,'codec':_0x52dcb5(0x3bd)},_0x31c2da,_0x470d4b={'id':0x4489,'data':new _0xf2aa72(0x0)},_0x418cb6=new _0x17b493(_0x1d5998[_0x52dcb5(0x1bb)]);function _0x11a929(_0x28c46d,_0x359e4c){var _0x3359fd=_0x52dcb5;return _0x359e4c=new Uint8Array(_0x359e4c),_0x3ae9c0(_0x5ad0ff(_0x28c46d),_0x590e8f(_0x359e4c[_0x3359fd(0x7ce)]),_0x359e4c);}function _0x3ae9c0(){var _0x1c8ee3=_0x52dcb5,_0x397621,_0xca6cda=0x0,_0xb48277;for(_0x397621=0x0;_0x397621>>0x18&0xff,_0x218160>>>0x10&0xff,_0x218160>>>0x8&0xff,_0x218160&0xff]);if((_0x218160&0xff0000)!=0x0)return new Uint8Array([_0x218160>>>0x10&0xff,_0x218160>>>0x8&0xff,_0x218160&0xff]);if((_0x218160&0xff00)!=0x0)return new Uint8Array([_0x218160>>>0x8&0xff,_0x218160&0xff]);if((_0x218160&0xff)!=0x0)return new Uint8Array([_0x218160&0xff]);throw'InvalidOperationException';}function _0x590e8f(_0x19fe3e){if(_0x19fe3e<=0x7f)return new Uint8Array([0x80|_0x19fe3e&0x7f]);if(_0x19fe3e<=0x3fff)return new Uint8Array([0x40|_0x19fe3e>>0x8&0x3f,_0x19fe3e&0xff]);return new Uint8Array([0x8,_0x19fe3e>>>0x18&0xff,_0x19fe3e>>>0x10&0xff,_0x19fe3e>>>0x8&0xff,_0x19fe3e&0xff]);}function _0x158722(_0x449c8a,_0x56e421){var _0x2c316c=new DataView(new ArrayBuffer(0x4));return _0x2c316c['setFloat32'](0x0,_0x56e421,![]),_0x11a929(_0x449c8a,new Uint8Array(_0x2c316c['buffer']));}function _0x1ae1da(_0x15f2e4){var _0x3a7b37=_0x52dcb5;if(_0x15f2e4<=0xff)return new Uint8Array([_0x15f2e4&0xff]);if(_0x15f2e4<=0xffff)return new Uint8Array([_0x15f2e4>>>0x8&0xff,_0x15f2e4&0xff]);if(_0x15f2e4<=0xffffff)return new Uint8Array([_0x15f2e4>>0x10&0xff,_0x15f2e4>>0x8&0xff,_0x15f2e4&0xff]);return new Uint8Array([_0x15f2e4>>>0x18&0xff,_0x15f2e4>>>0x10&0xff,_0x15f2e4>>>0x8&0xff,_0x15f2e4&0xff]);var _0x348ed0=new DataView(new ArrayBuffer(0x4));return _0x348ed0[_0x3a7b37(0x5c0)](0x0,_0x15f2e4,![]),_0x348ed0;}function _0x912883(_0x2ff424,_0x2e9b16){return _0x11a929(_0x2ff424,_0x1ae1da(_0x2e9b16));}function _0x534564(_0x1fdf09,_0x16523b){var _0x5132de=_0x52dcb5;return _0x11a929(_0x1fdf09,new TextEncoder()[_0x5132de(0x9d3)](_0x16523b));}function _0x7556e5(){var _0x4ad562=_0x52dcb5;let _0x7c4ecc={'id':0x1a45dfa3,'data':[_0x912883(0x4286,0x1),_0x912883(0x42f7,0x1),_0x912883(0x42f2,0x4),_0x912883(0x42f3,0x8),_0x534564(0x4282,'webm'),_0x912883(0x4287,0x4),_0x912883(0x4285,0x2)]},_0x2a9066={'id':0x1549a966,'data':[_0x912883(0x2ad7b1,0xf4240),_0x534564(0x4d80,'VDO-Ninja'),_0x534564(0x5741,_0x4ad562(0x533)),_0x470d4b]},_0x2e95a7=[{'id':0xb0,'data':_0x57575c},{'id':0xba,'data':_0x88da70}],_0x5d4d61={'id':0x1654ae6b,'data':[{'id':0xae,'data':[_0x912883(0xd7,0x1),_0x912883(0x73c5,0x1),_0x912883(0x9c,0x0),_0x534564(0x22b59c,_0x4ad562(0x2e5)),_0x534564(0x86,'V_'+_0x1d5998[_0x4ad562(0x24e)]),_0x912883(0x83,0x1),{'id':0xe0,'data':[_0x912883(0xb0,_0x57575c),_0x912883(0xba,_0x88da70)]}]},{'id':0xae,'data':[_0x912883(0xd7,0x2),_0x912883(0x73c5,0x2),_0x912883(0x9c,0x0),_0x534564(0x22b59c,_0x4ad562(0x2e5)),_0x534564(0x86,_0x4ad562(0x7aa)),_0x912883(0x83,0x2),{'id':0xe1,'data':[_0x158722(0xb5,_0x110b63),_0x912883(0x9f,_0x1ac8c3)]},_0x11a929(0x63a2,new Uint8Array(['O'[_0x4ad562(0x8d1)](0x0),'p'['charCodeAt'](0x0),'u'[_0x4ad562(0x8d1)](0x0),'s'['charCodeAt'](0x0),'H'[_0x4ad562(0x8d1)](0x0),'e'[_0x4ad562(0x8d1)](0x0),'a'[_0x4ad562(0x8d1)](0x0),'d'['charCodeAt'](0x0),0x1,_0x1ac8c3&0xff,0x38,0x1,_0x110b63>>>0x0&0xff,_0x110b63>>>0x8&0xff,_0x110b63>>>0x10&0xff,_0x110b63>>>0x18&0xff,0x0,0x0,0x0]))]}]};_0x31c2da={'id':0x18538067,'size':-0x1,'data':[_0x2a9066,_0x5d4d61]};let _0x4f45f9=new _0x1ee306(0x200);_0x3a6d8a(_0x4f45f9,_0x418cb6[_0x4ad562(0x680)],[_0x7c4ecc,_0x31c2da]),_0x418cb6[_0x4ad562(0x44d)](_0x4f45f9[_0x4ad562(0x959)]()),_0x525486=!![];}function _0x3e12c2(_0x5f541d){var _0x383df7=_0x52dcb5;let _0x25e53f=new _0x1ee306(0x1+0x2+0x1);if(!(_0x5f541d['trackNumber']>0x0&&_0x5f541d[_0x383df7(0x3f7)]<0x7f))throw new Error(_0x383df7(0x1ca));return _0x25e53f[_0x383df7(0x72e)](_0x5f541d[_0x383df7(0x3f7)]),_0x25e53f['writeU16BE'](_0x5f541d[_0x383df7(0x1dd)]),_0x25e53f['writeByte']((_0x5f541d['type']==_0x383df7(0xa68)?0x1:0x0)<<0x7),{'id':0xa3,'data':[_0x25e53f[_0x383df7(0x959)](),_0x5f541d['frame']]};}function _0x596b02(_0x299752){var _0x2eb2bd=_0x52dcb5;return{'id':0x1f43b675,'data':[{'id':0xe7,'data':Math[_0x2eb2bd(0x2fb)](_0x299752[_0x2eb2bd(0x1dd)])}]};}function _0x5bca44(){var _0x57d17b=_0x52dcb5;if(_0x108e76['length']===0x0)return;let _0x3ff042=0x0;for(let _0x8f51c3=0x0;_0x8f51c3<_0x108e76[_0x57d17b(0x8e9)];_0x8f51c3++){_0x3ff042+=_0x108e76[_0x8f51c3][_0x57d17b(0x5bd)][_0x57d17b(0x7ce)];}let _0x20a89c=new _0x1ee306(_0x3ff042+_0x108e76[_0x57d17b(0x8e9)]*0x40),_0x97a5b2=_0x596b02({'timecode':Math[_0x57d17b(0x2fb)](_0x1e33ba)});for(let _0x2ceef1=0x0;_0x2ceef1<_0x108e76[_0x57d17b(0x8e9)];_0x2ceef1++){_0x97a5b2[_0x57d17b(0x3bc)][_0x57d17b(0x505)](_0x3e12c2(_0x108e76[_0x2ceef1]));}_0x3a6d8a(_0x20a89c,_0x418cb6['pos'],_0x97a5b2),_0x418cb6['write'](_0x20a89c[_0x57d17b(0x959)]()),_0x108e76=[],_0x42297d=0x0;}function _0xee3eab(_0x494fab,_0x484c9f){var _0x47a16e=_0x52dcb5;_0x494fab['trackNumber']=_0x484c9f;var _0x466d0f=_0x494fab[_0x47a16e(0x561)]/0x3e8;_0x3cb890?(_0x58d674=_0x466d0f,_0x466d0f=0x0,_0x3cb890=![]):_0x466d0f=_0x466d0f-_0x58d674;_0x13134f=_0x466d0f;if(_0x42297d==0x0)_0x1e33ba=_0x466d0f;_0x494fab[_0x47a16e(0x1dd)]=Math[_0x47a16e(0x2fb)](_0x466d0f-_0x1e33ba),_0x108e76[_0x47a16e(0x505)](_0x494fab),_0x42297d=_0x494fab['timecode']+0x1,_0x42297d>=_0x2fc561&&_0x5bca44();}function _0x1e47e4(){var _0x4e31f3=_0x52dcb5;let _0x3b1a94=new _0x1ee306(seekHead[_0x4e31f3(0x8cf)]),_0x4aa5e1=_0x418cb6[_0x4e31f3(0x680)];_0x3a6d8a(_0x3b1a94,seekHead[_0x4e31f3(0x406)],seekHead[_0x4e31f3(0x3bc)]),_0x418cb6[_0x4e31f3(0x4cc)](seekHead[_0x4e31f3(0x406)]),_0x418cb6[_0x4e31f3(0x44d)](_0x3b1a94[_0x4e31f3(0x959)]()),_0x418cb6[_0x4e31f3(0x4cc)](_0x4aa5e1);}function _0x212052(){var _0xeba8f3=_0x52dcb5;let _0x15a01e=new _0x1ee306(0x8),_0x110e16=_0x418cb6[_0xeba8f3(0x680)];_0x15a01e[_0xeba8f3(0x218)](_0x13134f),_0x418cb6[_0xeba8f3(0x4cc)](_0x470d4b['dataOffset']),_0x418cb6[_0xeba8f3(0x44d)](_0x15a01e['getAsDataArray']()),_0x418cb6[_0xeba8f3(0x4cc)](_0x110e16);}this[_0x52dcb5(0x641)]=function(_0x115a3b){var _0x2654ae=_0x52dcb5;!_0x525486&&(_0x57575c=_0x1d5998[_0x2654ae(0x530)],_0x88da70=_0x1d5998[_0x2654ae(0x5ec)],_0x110b63=_0x1d5998[_0x2654ae(0x690)],_0x1ac8c3=_0x1d5998[_0x2654ae(0x306)],_0x7556e5());if(_0x115a3b['constructor'][_0x2654ae(0x6dd)]==_0x2654ae(0x17c)){let _0x584695=new Uint8Array(_0x115a3b[_0x2654ae(0x7ce)]);_0x115a3b['copyTo'](_0x584695),_0xee3eab({'frame':_0x584695,'intime':_0x115a3b[_0x2654ae(0x82c)],'type':_0x115a3b[_0x2654ae(0x78a)]},0x1);return;}else{if(_0x115a3b[_0x2654ae(0x611)][_0x2654ae(0x6dd)]=='EncodedAudioChunk'){let _0x2a8539=new Uint8Array(_0x115a3b[_0x2654ae(0x7ce)]);_0x115a3b[_0x2654ae(0x423)](_0x2a8539),_0xee3eab({'frame':_0x2a8539,'intime':_0x115a3b['timestamp'],'type':_0x115a3b['type']},0x2);return;}}},this[_0x52dcb5(0x73e)]=function(){var _0x3242cf=_0x52dcb5;return!_0x525486&&_0x7556e5(),_0x3cb890=!![],_0x5bca44(),_0x212052(),_0x418cb6[_0x3242cf(0x73e)](_0x3242cf(0x315));},this[_0x52dcb5(0x606)]=function(){var _0x10fe16=_0x52dcb5;return _0x418cb6[_0x10fe16(0x8e9)];},_0x1d5998=_0x18bc03(_0x5f4351,_0x1d5998||{});};};window[_0x4f505d(0x460)]=_0x2e4729(window[_0x4f505d(0x2ec)],window[_0x4f505d(0x357)]);}()); \ No newline at end of file +var _0x2de9a7=_0x5c68;(function(_0x1904e9,_0x2ea63f){var _0x9a417f=_0x5c68,_0x295dad=_0x1904e9();while(!![]){try{var _0x246955=-parseInt(_0x9a417f(0x4b4))/0x1+parseInt(_0x9a417f(0x8b2))/0x2+parseInt(_0x9a417f(0x8c3))/0x3+-parseInt(_0x9a417f(0x1d2))/0x4+-parseInt(_0x9a417f(0x252))/0x5*(parseInt(_0x9a417f(0x7e3))/0x6)+-parseInt(_0x9a417f(0x1f5))/0x7+parseInt(_0x9a417f(0x6a0))/0x8*(parseInt(_0x9a417f(0x3de))/0x9);if(_0x246955===_0x2ea63f)break;else _0x295dad['push'](_0x295dad['shift']());}catch(_0x3b1254){_0x295dad['push'](_0x295dad['shift']());}}}(_0x2d5e,0x19788));function log(_0x3d16a9){var _0x7bd9ae=_0x5c68;if(debugSocket){if(debugSocket['readyState']===debugSocket[_0x7bd9ae(0xa49)])for(var _0x273839=0x0;_0x273839_0x45e244[_0x3e8501(0x6b6)]())['then'](function(_0x8160d0){var _0x5cd72e=_0x3e8501;_0x8160d0['servers'][_0x5cd72e(0x982)](_0x302581=>{var _0x1ba5d5=_0x5cd72e;try{if(session['forceTcpMode']&&_0x302581[_0x1ba5d5(0x3b7)]){}else _0x5c223e[_0x1ba5d5(0x505)](_0x302581);}catch(_0x5af5ad){errorlog(_0x5af5ad);}});if(isIFrame&&_0x8160d0['options']&&session[_0x5cd72e(0x91d)]&&!session[_0x5cd72e(0x311)])pokeIframeAPI(_0x5cd72e(0x7df),_0x8160d0[_0x5cd72e(0x7d3)]);else!session['speedtest']&&setStorage('turnlist',_0x8160d0['servers'],0x1);})[_0x3e8501(0x3b6)](function(_0x1fdd2e){var _0x414718=_0x3e8501;warnlog(_0x1fdd2e),_0x5c223e=[{'username':_0x414718(0x2ce),'credential':_0x414718(0x1ba),'urls':[_0x414718(0x457)],'tz':0x12c,'udp':![],'locale':_0x414718(0x280)},{'username':_0x414718(0x2ce),'credential':_0x414718(0x1ba),'urls':[_0x414718(0x637)],'tz':0x12c,'udp':!![],'locale':'cae1'},{'username':'vdoninja','credential':_0x414718(0x71e),'urls':[_0x414718(0x58e)],'tz':0x1e0,'udp':!![],'locale':'usw2'},{'username':'vdoninja','credential':'PolandPirat','urls':[_0x414718(0x7c0)],'tz':-0x46,'udp':!![],'locale':_0x414718(0x2dd)},{'username':_0x414718(0x653),'credential':_0x414718(0xa27),'urls':[_0x414718(0x344)],'tz':-0x3c,'udp':!![],'locale':_0x414718(0x866)},{'username':_0x414718(0x2ce),'credential':_0x414718(0x1ba),'urls':[_0x414718(0x551)],'tz':-0x3c,'udp':![],'locale':_0x414718(0x730)},{'username':_0x414718(0x2ce),'credential':_0x414718(0x1ba),'urls':[_0x414718(0x9be)],'tz':-0x3c,'udp':!![],'locale':'de1'},{'username':_0x414718(0x5ca),'credential':_0x414718(0x483),'urls':[_0x414718(0x7dc)],'tz':-0x3c,'udp':!![],'locale':_0x414718(0x73d)},{'username':_0x414718(0x5ca),'credential':'IchBinSteveDerNinja','urls':[_0x414718(0x302)],'tz':-0x3c,'udp':![],'locale':_0x414718(0x73d)},{'username':_0x414718(0x5ca),'credential':_0x414718(0x2ad),'urls':[_0x414718(0x756)],'tz':0x12c,'udp':!![],'locale':_0x414718(0x774)}],_0x5c223e=processTURNs(_0x5c223e);}),!session['stunServers']&&(session[_0x3e8501(0x434)]=[]),session[_0x3e8501(0x7db)]={'iceServers':session[_0x3e8501(0x434)],'sdpSemantics':'unified-plan'},session[_0x3e8501(0x6c8)]&&(session[_0x3e8501(0x7db)][_0x3e8501(0x290)]=_0x3e8501(0x524)),!_0x5c223e&&(_0x5c223e=[]),session['configuration'][_0x3e8501(0x427)]=session[_0x3e8501(0x7db)][_0x3e8501(0x427)][_0x3e8501(0x407)](_0x5c223e),log(_0x3e8501(0x4ae)),!![];}var TURNPromise=null;async function chooseBestTURN(){var _0x5363b2=_0x2de9a7;if(session[_0x5363b2(0x7db)])return;return!TURNPromise?TURNPromise=getTURNList():warnlog(_0x5363b2(0x42a)),await TURNPromise;}var WebRTC={};WebRTC[_0x2de9a7(0x55d)]=(function(){var _0x134a17=_0x2de9a7,_0x311669={};function _0x4b2dee(){var _0x2d68c2=_0x5c68,_0x41cb43,_0x1e994c,_0xdca4e3=new Promise((_0xea42d2,_0x4cbef6)=>{_0x41cb43=_0xea42d2,_0x1e994c=_0x4cbef6;});return _0xdca4e3[_0x2d68c2(0x8c5)]=_0x41cb43,_0xdca4e3['reject']=_0x1e994c,_0xdca4e3;}_0x311669[_0x134a17(0xa76)]=function(_0x529153=0x7){var _0xaee240=_0x134a17,_0x365895='',_0x299991=_0xaee240(0x1f4);for(var _0x7329e6=0x0;_0x7329e6<_0x529153;_0x7329e6++){_0x365895+=_0x299991[_0xaee240(0x18e)](Math[_0xaee240(0x3c1)](Math['random']()*_0x299991['length']));}try{_0x365895=_0x365895[_0xaee240(0x9a6)]('AD',_0xaee240(0x50a)),_0x365895=_0x365895['replaceAll']('Ad','vdAv'),_0x365895=_0x365895['replaceAll']('ad',_0xaee240(0x976)),_0x365895=_0x365895[_0xaee240(0x9a6)]('aD',_0xaee240(0x7fb));}catch(_0x49399e){errorlog(_0x49399e);}return log(_0x365895),_0x365895;},_0x311669['generateRandomString']=function(_0x3129ec=0x7){var _0x593a54=_0x134a17,_0x532965='',_0x31f63e=[_0x593a54(0x949),'of','to',_0x593a54(0x2a5),'a','in','is','it',_0x593a54(0x48b),_0x593a54(0x235),'he',_0x593a54(0x46d),_0x593a54(0x699),'on',_0x593a54(0x59d),_0x593a54(0x2b1),'as','I',_0x593a54(0x555),_0x593a54(0x6af),'be','at',_0x593a54(0x92b),_0x593a54(0x4ca),_0x593a54(0x9b1),_0x593a54(0x1dc),'or','had','by',_0x593a54(0x8d6),'but',_0x593a54(0x731),_0x593a54(0x602),'we',_0x593a54(0x6e8),'out',_0x593a54(0x316),_0x593a54(0x501),'all',_0x593a54(0xa61),_0x593a54(0x459),'up','use','your','how','said','an',_0x593a54(0x9b4),_0x593a54(0x52a),'which','do',_0x593a54(0x74e),_0x593a54(0x8b7),'if',_0x593a54(0x1f1),_0x593a54(0x2f2),_0x593a54(0x797),'many','then',_0x593a54(0x23c),_0x593a54(0x44d),_0x593a54(0x1d6),'like','so','these',_0x593a54(0x783),_0x593a54(0x830),_0x593a54(0x933),'thing','see',_0x593a54(0x853),'two','has',_0x593a54(0x54e),_0x593a54(0x649),_0x593a54(0x461),_0x593a54(0x414),'go',_0x593a54(0xa08),_0x593a54(0x4c9),_0x593a54(0x570),_0x593a54(0xa5b),'no',_0x593a54(0x94b),_0x593a54(0x415),'my',_0x593a54(0x75a),_0x593a54(0x847),'water',_0x593a54(0x1ea),_0x593a54(0x83d),'first',_0x593a54(0x41f),_0x593a54(0x18b),_0x593a54(0x9ad),_0x593a54(0x83b),_0x593a54(0x75c),_0x593a54(0x610),_0x593a54(0x642),_0x593a54(0x51c),_0x593a54(0x6bd),_0x593a54(0x818),_0x593a54(0x4d3),_0x593a54(0x66b),_0x593a54(0x9a7),_0x593a54(0x569),_0x593a54(0x305),'live',_0x593a54(0x928),_0x593a54(0x3b0),_0x593a54(0x843),_0x593a54(0x881),'only',_0x593a54(0x2fb),'man',_0x593a54(0x47e),_0x593a54(0x8b9),_0x593a54(0x2bd),'every',_0x593a54(0x29f),'me','give',_0x593a54(0x674),_0x593a54(0x5c6),_0x593a54(0x6dd),_0x593a54(0x46a),'through','just',_0x593a54(0x8a5),_0x593a54(0x8de),_0x593a54(0x4ce),'think',_0x593a54(0x5dc),_0x593a54(0x889),_0x593a54(0x2c5),_0x593a54(0x51d),_0x593a54(0x9e4),'turn',_0x593a54(0x4a0),_0x593a54(0x32e),_0x593a54(0x981),'before',_0x593a54(0x758),_0x593a54(0x710),_0x593a54(0x81b),_0x593a54(0x3f9),'too',_0x593a54(0x892),_0x593a54(0x8df),_0x593a54(0x812),'set',_0x593a54(0x162),_0x593a54(0x247),_0x593a54(0x56f),_0x593a54(0x2aa),_0x593a54(0x34e),'play',_0x593a54(0x2c6),_0x593a54(0x733),_0x593a54(0x417),'home','read',_0x593a54(0x3f8),_0x593a54(0x1ec),'large',_0x593a54(0x62d),'add',_0x593a54(0x9bc),_0x593a54(0x5c1),_0x593a54(0x5e1),_0x593a54(0x911),_0x593a54(0x8f7),_0x593a54(0x4f3),_0x593a54(0x5a9),'follow',_0x593a54(0x40e),_0x593a54(0x6f6),_0x593a54(0x39b),_0x593a54(0x3d7),_0x593a54(0x58d),_0x593a54(0xa2b),'light','kind',_0x593a54(0xa55),_0x593a54(0x4b8),_0x593a54(0x4de),_0x593a54(0x36f),_0x593a54(0x61f),'us','again','animal','point',_0x593a54(0x9a8),_0x593a54(0x370),_0x593a54(0xa22),_0x593a54(0x497),'self','earth',_0x593a54(0x1bd),'head',_0x593a54(0x244),'own',_0x593a54(0x547),_0x593a54(0x5a0),'country','found',_0x593a54(0x73b),_0x593a54(0x437),_0x593a54(0x27f),_0x593a54(0x68d),_0x593a54(0x8d0),_0x593a54(0x5dd),_0x593a54(0x6e9),_0x593a54(0xa60),_0x593a54(0x3f6),_0x593a54(0x792),_0x593a54(0x5d8),'between',_0x593a54(0x5ae),_0x593a54(0x43f),_0x593a54(0x5e0),'never',_0x593a54(0x78f),'let',_0x593a54(0x7e5),_0x593a54(0x387),_0x593a54(0x32c),_0x593a54(0x7c6),_0x593a54(0x521),_0x593a54(0x2e1),_0x593a54(0x36c),_0x593a54(0x2fa),_0x593a54(0x345),_0x593a54(0x245),_0x593a54(0x842),'sea','draw',_0x593a54(0x577),'late',_0x593a54(0x38a),_0x593a54(0x8ac),_0x593a54(0x3ca),_0x593a54(0x7ff),_0x593a54(0x1cf),'night',_0x593a54(0x666),_0x593a54(0x735),'few',_0x593a54(0x227),_0x593a54(0x6d4),'seem',_0x593a54(0x20e),_0x593a54(0x9c0),_0x593a54(0x7ae),_0x593a54(0x367),'begin',_0x593a54(0x59e),'walk','example','ease','paper',_0x593a54(0x532),'always',_0x593a54(0x40d),_0x593a54(0x329),_0x593a54(0x33f),_0x593a54(0x7a1),_0x593a54(0x4f8),_0x593a54(0x4b1),_0x593a54(0x8c7),_0x593a54(0x6a2),'river',_0x593a54(0x954),'feet',_0x593a54(0x33e),_0x593a54(0x9c9),_0x593a54(0x48a),'carry','took',_0x593a54(0x5f9),_0x593a54(0x749),'room',_0x593a54(0x379),_0x593a54(0x831),_0x593a54(0x6e1),'fish',_0x593a54(0x28d),'stop',_0x593a54(0x9cb),_0x593a54(0x995),_0x593a54(0x51b),'horse',_0x593a54(0x2a2),_0x593a54(0x174),_0x593a54(0x249),'color',_0x593a54(0x3d0),_0x593a54(0x2e7),'main',_0x593a54(0x998),_0x593a54(0x4fe),_0x593a54(0xa79),'usual','young','ready',_0x593a54(0x527),_0x593a54(0x42b),_0x593a54(0x5d4),_0x593a54(0x93c),_0x593a54(0x229),_0x593a54(0xa20),_0x593a54(0x7b5),_0x593a54(0x337),_0x593a54(0x712),_0x593a54(0x636),_0x593a54(0x65c),'family',_0x593a54(0x79e),_0x593a54(0x22a),'leave',_0x593a54(0x7a5),'measure',_0x593a54(0x930),_0x593a54(0x1e8),_0x593a54(0x185),_0x593a54(0xa71),'numeral','class',_0x593a54(0x200),'question',_0x593a54(0x257),_0x593a54(0x73e),_0x593a54(0x3fb),_0x593a54(0x1b8),_0x593a54(0x529),'rock',_0x593a54(0xa63),_0x593a54(0x2a0),'south',_0x593a54(0x962),_0x593a54(0x2d5),_0x593a54(0x69e),_0x593a54(0x93a),_0x593a54(0x68b),'since',_0x593a54(0x284),_0x593a54(0xa78),_0x593a54(0x686),_0x593a54(0x5fd),_0x593a54(0x5c5),_0x593a54(0x947),'hour',_0x593a54(0x233),_0x593a54(0x74f),_0x593a54(0x39c),_0x593a54(0x234),_0x593a54(0x7f7),_0x593a54(0x1fb),_0x593a54(0x98f),'early',_0x593a54(0x1da),_0x593a54(0x2b8),_0x593a54(0x266),_0x593a54(0x964),_0x593a54(0x371),'fast',_0x593a54(0x540),'sing',_0x593a54(0x42c),_0x593a54(0x2e9),'table',_0x593a54(0x1cd),_0x593a54(0x7ec),_0x593a54(0x8c4),'ten',_0x593a54(0x762),_0x593a54(0x16d),_0x593a54(0x865),_0x593a54(0x9f1),_0x593a54(0x1ac),_0x593a54(0x633),_0x593a54(0x72f),_0x593a54(0x705),'slow',_0x593a54(0x9a3),_0x593a54(0x400),_0x593a54(0x541),_0x593a54(0x86f),_0x593a54(0x69a),_0x593a54(0x5d1),_0x593a54(0x258),_0x593a54(0x468),_0x593a54(0x980),_0x593a54(0x7ab),_0x593a54(0x17d),_0x593a54(0x7bc),_0x593a54(0x96a),'notice','voice',_0x593a54(0x23a),'power',_0x593a54(0x516),_0x593a54(0x5a5),_0x593a54(0x877),_0x593a54(0x720),'fall','lead',_0x593a54(0x973),_0x593a54(0x2e0),_0x593a54(0x833),_0x593a54(0x6a4),'wait','plan','figure',_0x593a54(0x467),_0x593a54(0x167),_0x593a54(0x31a),_0x593a54(0x2ab),_0x593a54(0x6b9),_0x593a54(0x7b4),_0x593a54(0x718),_0x593a54(0x9ca),_0x593a54(0x697),_0x593a54(0x5b3),_0x593a54(0xa0b),_0x593a54(0x625),_0x593a54(0x780),'front',_0x593a54(0x99b),'week',_0x593a54(0x1f7),_0x593a54(0x1b1),_0x593a54(0xa72),'oh',_0x593a54(0xa2a),'develop',_0x593a54(0x861),_0x593a54(0x6e0),'free',_0x593a54(0x2e4),_0x593a54(0x26e),_0x593a54(0x738),_0x593a54(0x923),_0x593a54(0x9fe),'clear',_0x593a54(0x787),_0x593a54(0x398),_0x593a54(0x729),_0x593a54(0x584),'inch',_0x593a54(0x71f),_0x593a54(0x318),_0x593a54(0x61e),'stay',_0x593a54(0x90c),_0x593a54(0x822),_0x593a54(0x8cb),_0x593a54(0x3e9),_0x593a54(0x53b),_0x593a54(0x9fd),_0x593a54(0x5ab),_0x593a54(0x6d6),_0x593a54(0x8c9),_0x593a54(0x8af),_0x593a54(0x72c),'system',_0x593a54(0x741),_0x593a54(0x9a2),_0x593a54(0x231),_0x593a54(0x75e),_0x593a54(0x657),_0x593a54(0x519),_0x593a54(0x607),_0x593a54(0x283),_0x593a54(0xa0e),'dry',_0x593a54(0xa1a),_0x593a54(0x7d8),'thousand',_0x593a54(0x3e2),_0x593a54(0x41c),_0x593a54(0xa05),_0x593a54(0xa12),_0x593a54(0x5e5),_0x593a54(0x35e),'hot',_0x593a54(0x4d9),_0x593a54(0x47b),_0x593a54(0x25a),_0x593a54(0x95e),_0x593a54(0x453),'bring','yes','distant',_0x593a54(0x929),_0x593a54(0x1f8),'paint',_0x593a54(0x2cb),_0x593a54(0x854),_0x593a54(0x8e5),_0x593a54(0x279),_0x593a54(0x62a),_0x593a54(0xa3f),_0x593a54(0x4a9),_0x593a54(0x83e),'am','present',_0x593a54(0x1b4),_0x593a54(0x175),_0x593a54(0x74a),'position','arm',_0x593a54(0x4f9),_0x593a54(0x719),_0x593a54(0x494),_0x593a54(0x8cf),_0x593a54(0x803),_0x593a54(0x420),_0x593a54(0x3b3),_0x593a54(0x89a),_0x593a54(0x599),_0x593a54(0x613),'matter','circle','pair',_0x593a54(0xa19),_0x593a54(0x369),_0x593a54(0x346),'felt',_0x593a54(0x39d),_0x593a54(0x4f1),'sudden',_0x593a54(0x816),_0x593a54(0xa32),_0x593a54(0x63e),_0x593a54(0x8e9),_0x593a54(0x7d0),_0x593a54(0x182),_0x593a54(0x307),_0x593a54(0x4d6),'energy',_0x593a54(0x54f),_0x593a54(0x418),_0x593a54(0x626),_0x593a54(0x825),_0x593a54(0x25d),'ride',_0x593a54(0xa57),_0x593a54(0x6c4),'fraction',_0x593a54(0x2c4),_0x593a54(0x413),'race',_0x593a54(0x385),'store',_0x593a54(0x1cc),_0x593a54(0x4bc),_0x593a54(0x9e5),_0x593a54(0x7a3),_0x593a54(0x163),_0x593a54(0x1a0),_0x593a54(0x8fd),_0x593a54(0x186),_0x593a54(0x3b6),_0x593a54(0x64a),_0x593a54(0x20d),_0x593a54(0xa07),_0x593a54(0x7e9),_0x593a54(0x47c),_0x593a54(0x4a6),_0x593a54(0x7bd),_0x593a54(0x73f),'wild',_0x593a54(0x858),'kept',_0x593a54(0x6de),_0x593a54(0x785),_0x593a54(0xa38),'job','edge',_0x593a54(0x503),_0x593a54(0x3d2),_0x593a54(0x3ec),_0x593a54(0x80d),_0x593a54(0x1aa),'bright',_0x593a54(0x1a4),_0x593a54(0xa06),_0x593a54(0x7e8),'million',_0x593a54(0x5f8),_0x593a54(0x2c7),'happy','hope',_0x593a54(0x452),_0x593a54(0x443),_0x593a54(0x587),_0x593a54(0x675),'jump','baby',_0x593a54(0x502),_0x593a54(0x5a7),'meet','root',_0x593a54(0x963),_0x593a54(0x65f),_0x593a54(0x67d),'metal',_0x593a54(0x9d7),_0x593a54(0x505),_0x593a54(0x3c8),'paragraph','third','shall','held','hair',_0x593a54(0x30e),_0x593a54(0x419),_0x593a54(0x3c1),_0x593a54(0x514),'result',_0x593a54(0x788),_0x593a54(0x4bd),_0x593a54(0x1b2),_0x593a54(0x2ed),_0x593a54(0x365),_0x593a54(0x5bc),_0x593a54(0x78a),_0x593a54(0x51e),_0x593a54(0x8c6),_0x593a54(0x832),_0x593a54(0x446),_0x593a54(0x8d5),_0x593a54(0x451),_0x593a54(0x6b8),_0x593a54(0x18a),'soil',_0x593a54(0x784),'temperature','finger',_0x593a54(0x93f),_0x593a54(0x8d7),'fight','lie',_0x593a54(0x161),_0x593a54(0x2fc),'natural',_0x593a54(0x311),_0x593a54(0x4ba),_0x593a54(0xa34),'else',_0x593a54(0x1c1),_0x593a54(0x1a3),_0x593a54(0x723),_0x593a54(0x69b),_0x593a54(0x706),_0x593a54(0x362),_0x593a54(0x35f),'moment',_0x593a54(0x6b5),_0x593a54(0x76c),_0x593a54(0x8c2),_0x593a54(0x1c5),_0x593a54(0x448),_0x593a54(0x8ed),_0x593a54(0xa75),_0x593a54(0x6b4),_0x593a54(0x7e1),_0x593a54(0x8eb),'speed','method',_0x593a54(0x7ac),'pay',_0x593a54(0x4ef),_0x593a54(0x409),_0x593a54(0x6bf),_0x593a54(0x852),_0x593a54(0x489),_0x593a54(0x1df),_0x593a54(0x9af),_0x593a54(0x789),'climb','cool',_0x593a54(0x991),'poor',_0x593a54(0x709),_0x593a54(0x4d4),_0x593a54(0x707),_0x593a54(0xa68),'iron',_0x593a54(0x83f),_0x593a54(0x550),_0x593a54(0x47a),_0x593a54(0x552),_0x593a54(0x3cc),'smile',_0x593a54(0x7cd),'hole','trade','melody',_0x593a54(0xa3e),'office',_0x593a54(0x744),_0x593a54(0x934),'mouth','exact',_0x593a54(0x4f6),_0x593a54(0x294),_0x593a54(0x19d),_0x593a54(0x78d),'shout',_0x593a54(0x5ee),_0x593a54(0x545),_0x593a54(0x87b),_0x593a54(0x3c9),'join',_0x593a54(0x71a),_0x593a54(0x909),_0x593a54(0x4db),_0x593a54(0x298),_0x593a54(0xa43),_0x593a54(0x3a0),_0x593a54(0x3b1),_0x593a54(0x187),_0x593a54(0x5aa),'blood',_0x593a54(0x526),'grew','cent',_0x593a54(0x3a3),_0x593a54(0x891),_0x593a54(0x46f),_0x593a54(0x251),_0x593a54(0x682),_0x593a54(0x6f9),_0x593a54(0x1b0),_0x593a54(0x755),_0x593a54(0x844),'sent',_0x593a54(0x89d),'fell',_0x593a54(0x450),_0x593a54(0x82d),_0x593a54(0x463),_0x593a54(0x1ce),_0x593a54(0x76d),_0x593a54(0xa50),_0x593a54(0x646),'decimal',_0x593a54(0x1db),_0x593a54(0x4b5),_0x593a54(0x7ca),_0x593a54(0x5a4),_0x593a54(0x80c),_0x593a54(0x1b6),_0x593a54(0x27d),_0x593a54(0x37a),'protect',_0x593a54(0x41e),'whose',_0x593a54(0x5d3),_0x593a54(0x716),_0x593a54(0x6ca),_0x593a54(0x7f1),_0x593a54(0x2ba),_0x593a54(0x3e8),_0x593a54(0x8aa),_0x593a54(0x1a5),'spoke',_0x593a54(0x698),_0x593a54(0x4be),_0x593a54(0xa2c),'effect',_0x593a54(0x8d2),_0x593a54(0x15d),_0x593a54(0x1fe),_0x593a54(0x8a8),_0x593a54(0x582),_0x593a54(0x2e6),'student',_0x593a54(0x5a2),_0x593a54(0x5d6),'supply',_0x593a54(0x1af),_0x593a54(0x1de),_0x593a54(0x615),_0x593a54(0x87d),_0x593a54(0x44f),'thus','capital',_0x593a54(0x771),_0x593a54(0x94a),'danger','fruit',_0x593a54(0x574),'thick','soldier',_0x593a54(0x676),_0x593a54(0x7d9),_0x593a54(0x88d),'necessary',_0x593a54(0x3ac),_0x593a54(0x6e4),_0x593a54(0x4b3),_0x593a54(0x3ad),_0x593a54(0x851),'bat',_0x593a54(0x1fc),'crowd',_0x593a54(0x867),_0x593a54(0x41a),_0x593a54(0x855),_0x593a54(0xa46),_0x593a54(0x5cd),_0x593a54(0x66d),_0x593a54(0x4d8),_0x593a54(0x3d6),_0x593a54(0x492),'famous',_0x593a54(0x7cc),_0x593a54(0x75f),_0x593a54(0xa8a),_0x593a54(0x83c),_0x593a54(0x8b3),_0x593a54(0x79c),_0x593a54(0x6f8),_0x593a54(0x583),_0x593a54(0x74c),'colony',_0x593a54(0x2af),_0x593a54(0x737),_0x593a54(0x5f4),'enter',_0x593a54(0x99a),_0x593a54(0x287),_0x593a54(0x99e),_0x593a54(0x2b9),_0x593a54(0x836),'gun',_0x593a54(0x8a0),_0x593a54(0x304),'dead',_0x593a54(0x3f2),'desert',_0x593a54(0x915),_0x593a54(0x416),'lift','rose','continue',_0x593a54(0xa87),_0x593a54(0x8ae),_0x593a54(0x952),'sell',_0x593a54(0x289),_0x593a54(0x243),_0x593a54(0x544),_0x593a54(0x42e),_0x593a54(0x5e6),_0x593a54(0xa2d),_0x593a54(0x31e),_0x593a54(0x97f),_0x593a54(0xa29),_0x593a54(0x2c1),'shoe','shoulder',_0x593a54(0x4c2),_0x593a54(0x656),_0x593a54(0x879),_0x593a54(0x2d0),_0x593a54(0x677),_0x593a54(0x9b7),_0x593a54(0x635),_0x593a54(0x618),'nine',_0x593a54(0x3b2),_0x593a54(0x378),_0x593a54(0x6b7),_0x593a54(0x8bb),_0x593a54(0x358),_0x593a54(0xa86),_0x593a54(0x875),_0x593a54(0x63c),_0x593a54(0x69d),_0x593a54(0x91e),_0x593a54(0x3e0),'molecule',_0x593a54(0x97e),_0x593a54(0x88a),_0x593a54(0x846),'repeat',_0x593a54(0x429),_0x593a54(0x98b),_0x593a54(0x70a),_0x593a54(0x9c5),'nose','plural',_0x593a54(0x9a9),'claim',_0x593a54(0x425),_0x593a54(0x7e4),_0x593a54(0x5fc),_0x593a54(0x4d5),_0x593a54(0x50f),'skill',_0x593a54(0x49d),_0x593a54(0x67f),_0x593a54(0x8f2),_0x593a54(0x1ef),_0x593a54(0x199),_0x593a54(0x9a5),'branch',_0x593a54(0x76e),'suffix',_0x593a54(0x534),_0x593a54(0x33c),_0x593a54(0x9da),_0x593a54(0x7c8),'sister',_0x593a54(0x3d4),_0x593a54(0x447),_0x593a54(0x6fc),_0x593a54(0x1e9),_0x593a54(0x93b),_0x593a54(0x394),_0x593a54(0x997),_0x593a54(0x548),_0x593a54(0x36a),_0x593a54(0x714),_0x593a54(0x6c2),_0x593a54(0x8da),_0x593a54(0x3fc),_0x593a54(0x67a),_0x593a54(0x6cb),_0x593a54(0x9bd),'slip',_0x593a54(0x1b3),'dream','evening','condition','feed',_0x593a54(0x7d1),_0x593a54(0x92e),_0x593a54(0x395),_0x593a54(0x404),_0x593a54(0x6fb),_0x593a54(0x6a9),_0x593a54(0x6e3),_0x593a54(0x807),_0x593a54(0x5ed),'master',_0x593a54(0x53f),_0x593a54(0x4f0),_0x593a54(0x815),_0x593a54(0x7dd),_0x593a54(0x765),_0x593a54(0x4a5),_0x593a54(0xa5f),_0x593a54(0x857),_0x593a54(0xa23),'spend',_0x593a54(0x660),_0x593a54(0x490),_0x593a54(0x57b),_0x593a54(0x659),'share',_0x593a54(0x986),_0x593a54(0x326),_0x593a54(0x4f7),_0x593a54(0x64d),_0x593a54(0x5b8),'bar',_0x593a54(0x20f),'segment','slave',_0x593a54(0x4f2),'instant','market',_0x593a54(0x9f8),_0x593a54(0x261),_0x593a54(0x9e0),'dear',_0x593a54(0x19a),'reply',_0x593a54(0x4df),_0x593a54(0x7d6),_0x593a54(0x384),_0x593a54(0x198),_0x593a54(0x7e6),_0x593a54(0x2cf),'steam',_0x593a54(0x743),_0x593a54(0xa03),_0x593a54(0x56a),'log',_0x593a54(0x726),_0x593a54(0x53d),_0x593a54(0x4af),'shell',_0x593a54(0x872)];for(var _0x19243a=0x0;_0x19243a<0x2;_0x19243a++){try{var _0x320600=parseInt(Math[_0x593a54(0x950)]()*0x3e8);_0x532965+=_0x31f63e[_0x320600];}catch(_0x50827b){}}var _0x112b21=_0x593a54(0x1f4);_0x532965+=_0x112b21[_0x593a54(0x18e)](Math[_0x593a54(0x3c1)](Math[_0x593a54(0x950)]()*_0x112b21[_0x593a54(0x8e9)]));while(_0x532965['length']<_0x3129ec){_0x532965+=_0x112b21[_0x593a54(0x18e)](Math[_0x593a54(0x3c1)](Math[_0x593a54(0x950)]()*_0x112b21[_0x593a54(0x8e9)]));}try{_0x532965=_0x532965[_0x593a54(0x9a6)]('AD',_0x593a54(0x50a)),_0x532965=_0x532965[_0x593a54(0x9a6)]('Ad','vdAv'),_0x532965=_0x532965[_0x593a54(0x9a6)]('ad',_0x593a54(0x976)),_0x532965=_0x532965[_0x593a54(0x9a6)]('aD',_0x593a54(0x7fb));}catch(_0x4d00fc){errorlog(_0x4d00fc);}return log(_0x532965),_0x532965;},_0x311669[_0x134a17(0x93e)]=_0x134a17(0x819),_0x311669['apiSocket']=null,_0x311669[_0x134a17(0x255)]=![],_0x311669['noaudio']=![],_0x311669[_0x134a17(0x711)]=![],_0x311669[_0x134a17(0x2d3)]=![],_0x311669[_0x134a17(0x25f)]=![],_0x311669[_0x134a17(0x92f)]=!![],_0x311669[_0x134a17(0x543)]=![],_0x311669[_0x134a17(0x28f)]=0x64,_0x311669[_0x134a17(0x4b2)]=0x8,_0x311669[_0x134a17(0x213)]=![],_0x311669[_0x134a17(0x449)]=![],_0x311669[_0x134a17(0x9e8)]=![],_0x311669[_0x134a17(0x308)]=![],_0x311669[_0x134a17(0x272)]=![],_0x311669[_0x134a17(0x9db)]=![],_0x311669[_0x134a17(0x591)]=![],_0x311669[_0x134a17(0x4e4)]=![],_0x311669[_0x134a17(0x495)]=![],_0x311669[_0x134a17(0x970)]=![],_0x311669['audioConstraints']={},_0x311669[_0x134a17(0x4ac)]=!![],_0x311669['audioEffects']=null,_0x311669[_0x134a17(0x8a9)]=![],_0x311669['autorecord']=![],_0x311669[_0x134a17(0x5f3)]=![],_0x311669['autorecordlocal']=![],_0x311669[_0x134a17(0x58c)]=![],_0x311669[_0x134a17(0xa6f)]=new AudioContext(),_0x311669['audioCtxOutbound']=![],_0x311669['avatar']=![],_0x311669['audioLatency']=![],_0x311669['echoCancellation']=null,_0x311669['autoGainControl']=null,_0x311669[_0x134a17(0x953)]=null,_0x311669[_0x134a17(0x254)]=![],_0x311669[_0x134a17(0x5cc)]=![],_0x311669[_0x134a17(0x965)]=![],_0x311669['broadcastIFrame']=![],_0x311669['directorBlindAllGuests']=![],_0x311669['screenshareDenoise']=![],_0x311669[_0x134a17(0x902)]=![],_0x311669[_0x134a17(0x24f)]=![],_0x311669[_0x134a17(0x210)]=![],_0x311669['directorBlindButton']=![],_0x311669[_0x134a17(0x604)]=0x0,_0x311669[_0x134a17(0x435)]=0x0,_0x311669[_0x134a17(0x60f)]=_0x134a17(0x8a2),_0x311669['videoMargin']=0x0,_0x311669[_0x134a17(0x70d)]=![],_0x311669[_0x134a17(0x68f)]=![],_0x311669[_0x134a17(0x3a8)]=null,_0x311669[_0x134a17(0x739)]=![],_0x311669[_0x134a17(0x708)]=![],_0x311669[_0x134a17(0xa33)]=![],_0x311669[_0x134a17(0x62f)]=![],_0x311669[_0x134a17(0x2dc)]=[],_0x311669[_0x134a17(0x401)]=null,_0x311669[_0x134a17(0x3df)]=![],_0x311669['blurBackground']=![],_0x311669[_0x134a17(0x4e0)]=null,_0x311669['canvasSource']=null,_0x311669[_0x134a17(0x46e)]=null,_0x311669[_0x134a17(0x632)]=![],_0x311669[_0x134a17(0xa70)]=![],_0x311669['auth']=![],_0x311669[_0x134a17(0x89e)]=![],_0x311669[_0x134a17(0x78c)]=![],_0x311669[_0x134a17(0x6d5)]=![],_0x311669[_0x134a17(0x7fe)]=![],_0x311669[_0x134a17(0x7db)]=![],_0x311669[_0x134a17(0x30a)]=![],_0x311669[_0x134a17(0x2f8)]=![],_0x311669['contentHint']='',_0x311669['audioContentHint']='',_0x311669[_0x134a17(0x45e)]='',_0x311669[_0x134a17(0x292)]=![],_0x311669[_0x134a17(0x24e)]=![],_0x311669['h264profile']=null,_0x311669['cleanViewer']=![],_0x311669[_0x134a17(0x226)]=null,_0x311669['cbr']=0x1,_0x311669[_0x134a17(0xa60)]=![],_0x311669[_0x134a17(0x1c2)]=null,_0x311669[_0x134a17(0x900)]={},_0x311669[_0x134a17(0x88e)]=![],_0x311669['currentCameraConstraints']={},_0x311669['currentAudioConstraints']={},_0x311669[_0x134a17(0x805)]=![],_0x311669[_0x134a17(0xa5c)]=0x0,_0x311669[_0x134a17(0x6c3)]=0x25a,_0x311669['structure']=![],_0x311669[_0x134a17(0x29c)]=![],_0x311669['bitrateGroupFlag']=![],_0x311669[_0x134a17(0x894)]=![],_0x311669[_0x134a17(0x802)]=null,_0x311669[_0x134a17(0x6ec)]=_0x311669[_0x134a17(0x894)],_0x311669[_0x134a17(0xa17)]=![],_0x311669[_0x134a17(0x2b4)]=![],_0x311669[_0x134a17(0x781)]=![],_0x311669['decrypted']=![],_0x311669[_0x134a17(0x811)]=null,_0x311669['director']=![],_0x311669[_0x134a17(0x194)]=![],_0x311669['disableHotKeys']=![],_0x311669[_0x134a17(0x18c)]=![],_0x311669['disableMouseEvents']=![],_0x311669[_0x134a17(0x274)]=![],_0x311669[_0x134a17(0x98a)]=0x23,_0x311669[_0x134a17(0x827)]=![],_0x311669[_0x134a17(0x5c9)]=null,_0x311669[_0x134a17(0x285)]=null,_0x311669[_0x134a17(0x95b)]=[],_0x311669[_0x134a17(0x76b)]=![],_0x311669['directorHash']=![],_0x311669[_0x134a17(0x2f3)]=![],_0x311669['directorStreamID']=![],_0x311669[_0x134a17(0x772)]=null,_0x311669[_0x134a17(0x2ff)]=![],_0x311669[_0x134a17(0x54c)]=!![],_0x311669[_0x134a17(0x9ea)]=null,_0x311669[_0x134a17(0x171)]=![],_0x311669['effectValue']=![],_0x311669[_0x134a17(0xa59)]=![],_0x311669[_0x134a17(0x27b)]=![],_0x311669[_0x134a17(0x943)]=![],_0x311669[_0x134a17(0x340)]=![],_0x311669[_0x134a17(0x29d)]=![],_0x311669[_0x134a17(0x288)]=![],_0x311669['enhance']=![],_0x311669[_0x134a17(0x694)]=![],_0x311669[_0x134a17(0x874)]=0x384,_0x311669[_0x134a17(0x81e)]=![],_0x311669[_0x134a17(0x558)]=new TextEncoder(_0x134a17(0x66f)),_0x311669[_0x134a17(0x430)]=![],_0x311669['fadein']=![],_0x311669[_0x134a17(0x21b)]=![],_0x311669[_0x134a17(0x30f)]=![],_0x311669[_0x134a17(0x1be)]=![],_0x311669[_0x134a17(0x9fc)]=![],_0x311669[_0x134a17(0x972)]=[],_0x311669['hostedTransfers']=[],_0x311669[_0x134a17(0x518)]=![],_0x311669[_0x134a17(0x623)]=null,_0x311669[_0x134a17(0x768)]=![],_0x311669['flipped']=![],_0x311669[_0x134a17(0x732)]=![],_0x311669[_0x134a17(0x834)]=![],_0x311669[_0x134a17(0x509)]=![],_0x311669[_0x134a17(0x341)]=null,_0x311669[_0x134a17(0x9a1)]=![],_0x311669[_0x134a17(0x920)]=![],_0x311669[_0x134a17(0x382)]=![],_0x311669['fullscreen']=![],_0x311669[_0x134a17(0x16e)]=![],_0x311669[_0x134a17(0x8ea)]=null,_0x311669[_0x134a17(0x532)]=[],_0x311669['groupView']=[],_0x311669['allowNoGroup']=![],_0x311669[_0x134a17(0x763)]=![],_0x311669['guestFeeds']=null,_0x311669[_0x134a17(0x575)]=![],_0x311669[_0x134a17(0x23d)]=![],_0x311669[_0x134a17(0x8ff)]=![],_0x311669[_0x134a17(0x5ec)]=![],_0x311669[_0x134a17(0x325)]=![],_0x311669[_0x134a17(0x9b0)]=![],_0x311669[_0x134a17(0xa6e)]=![],_0x311669[_0x134a17(0x422)]=![],_0x311669['stunServers']=[{'urls':[_0x134a17(0x312),_0x134a17(0x988)]}],_0x311669[_0x134a17(0x334)]=![],_0x311669[_0x134a17(0xa19)]=[],_0x311669[_0x134a17(0x6d3)]={},_0x311669[_0x134a17(0x3a2)]=![],_0x311669[_0x134a17(0x5f6)]=![],_0x311669[_0x134a17(0x878)]=![],_0x311669[_0x134a17(0x1e6)]=0x1,_0x311669['quality_ss']=![],_0x311669[_0x134a17(0x3e3)]=![],_0x311669[_0x134a17(0x242)]=![],_0x311669[_0x134a17(0x50b)]=![],_0x311669[_0x134a17(0x4ed)]=![],_0x311669[_0x134a17(0x17e)]=![],_0x311669[_0x134a17(0x6ee)]={},_0x311669['joiningRoom']=![],_0x311669[_0x134a17(0x89c)]=![],_0x311669[_0x134a17(0x458)]=![],_0x311669[_0x134a17(0x19b)]={},_0x311669['lowerVolume']=[],_0x311669[_0x134a17(0x845)]=![],_0x311669[_0x134a17(0x20c)]=![],_0x311669[_0x134a17(0x43d)]=!![],_0x311669[_0x134a17(0x1d9)]=![],_0x311669[_0x134a17(0x8f3)]=[],_0x311669['micIsolatedAutoMute']=![],_0x311669['maxviewers']=![],_0x311669[_0x134a17(0x96f)]=![],_0x311669[_0x134a17(0x9b5)]=![],_0x311669[_0x134a17(0x6b1)]=![],_0x311669['midiDelay']=![],_0x311669[_0x134a17(0x1e3)]=![],_0x311669[_0x134a17(0x54b)]=![],_0x311669['maxframeRate_q2']=![],_0x311669[_0x134a17(0x84c)]=![],_0x311669['maxsamplerate']=![],_0x311669['leftMiniPreview']=![],_0x311669[_0x134a17(0x689)]=![],_0x311669[_0x134a17(0x5e9)]=![],_0x311669[_0x134a17(0x38b)]=![],_0x311669[_0x134a17(0x72b)]=![],_0x311669[_0x134a17(0x6c6)]=![],_0x311669[_0x134a17(0x246)]=![],_0x311669[_0x134a17(0xa00)]=0x15e,_0x311669[_0x134a17(0x6c1)]=0x23,_0x311669['labelsize']=![],_0x311669[_0x134a17(0x38e)]=![],_0x311669[_0x134a17(0x335)]=![],_0x311669[_0x134a17(0x559)]=![],_0x311669[_0x134a17(0x3c4)]=![],_0x311669[_0x134a17(0x703)]=![],_0x311669['layouts']=![],_0x311669['lyraCodecModule']=![],_0x311669['loadoutID']=_0x311669['generateStreamID'](0x5),_0x311669[_0x134a17(0x60d)]=![],_0x311669[_0x134a17(0x3bb)]=![],_0x311669[_0x134a17(0x764)]=![],_0x311669['mainDirectorPassword']=![],_0x311669[_0x134a17(0x166)]=null,_0x311669[_0x134a17(0x60b)]=![],_0x311669[_0x134a17(0x498)]=![],_0x311669['midiOut']=![],_0x311669[_0x134a17(0x5b9)]=![],_0x311669['midiRemote']=![],_0x311669[_0x134a17(0x4bb)]=![],_0x311669[_0x134a17(0x57c)]=![],_0x311669[_0x134a17(0xa64)]=0x17,_0x311669[_0x134a17(0x557)]=![],_0x311669[_0x134a17(0x1a8)]=![],_0x311669[_0x134a17(0x323)]=![],_0x311669['mirrorExclude']=![],_0x311669['permaMirrored']=![],_0x311669[_0x134a17(0x297)]=![],_0x311669['msg']=[],_0x311669[_0x134a17(0x898)]=![],_0x311669[_0x134a17(0x98c)]=![],_0x311669['whipoutSettings']=![],_0x311669[_0x134a17(0x4f5)]=![],_0x311669[_0x134a17(0x8b4)]=![],_0x311669['miconly']=![],_0x311669['muted']=![],_0x311669['muted_activeSpeaker']=![],_0x311669['muted_savedState']=![],_0x311669[_0x134a17(0x4cf)]=![],_0x311669[_0x134a17(0x51f)]={},_0x311669[_0x134a17(0x1e7)]=![],_0x311669[_0x134a17(0x65e)]=![],_0x311669[_0x134a17(0x7be)]=![],_0x311669[_0x134a17(0xa5e)]=![],_0x311669[_0x134a17(0x5f2)]=null,_0x311669[_0x134a17(0x8a6)]=![],_0x311669[_0x134a17(0x3f4)]=![],_0x311669[_0x134a17(0x671)]=![],_0x311669[_0x134a17(0x8f6)]=![],_0x311669[_0x134a17(0x897)]=![],_0x311669[_0x134a17(0x592)]=![],_0x311669[_0x134a17(0x631)]=![],_0x311669[_0x134a17(0x9c1)]=![],_0x311669['playChannel']=![],_0x311669[_0x134a17(0x4c6)]=![],_0x311669[_0x134a17(0x692)]=![],_0x311669['obsState']={},_0x311669['obsState'][_0x134a17(0x4eb)]=null,_0x311669['obsState'][_0x134a17(0x6e2)]=null,_0x311669['obsState']['recording']=null,_0x311669[_0x134a17(0x34a)][_0x134a17(0x336)]=null,_0x311669[_0x134a17(0x34a)][_0x134a17(0x1fa)]=null,_0x311669[_0x134a17(0xa10)]=![],_0x311669[_0x134a17(0x810)]=![],_0x311669[_0x134a17(0x28e)]=![],_0x311669[_0x134a17(0x835)]=![],_0x311669[_0x134a17(0xa63)]=![],_0x311669[_0x134a17(0x271)]=![],_0x311669[_0x134a17(0x2b7)]=![],_0x311669[_0x134a17(0x8a3)]=![],_0x311669['bypass']=![],_0x311669[_0x134a17(0xa47)]=![],_0x311669[_0x134a17(0x392)]=![],_0x311669[_0x134a17(0x4cd)]=![],_0x311669[_0x134a17(0x820)]=null,_0x311669[_0x134a17(0x303)]=![],_0x311669['overlayControls']=![],_0x311669[_0x134a17(0x9ff)]=0x5dc,_0x311669['pcs']={},_0x311669['pip']=![],_0x311669[_0x134a17(0x629)]=![],_0x311669[_0x134a17(0x713)]=![],_0x311669[_0x134a17(0x5b0)]=![],_0x311669[_0x134a17(0x1d0)]=![],_0x311669['whipOut']=![],_0x311669['whipOutScreenShareBitrate']=![],_0x311669['whipOutScreenShareCodec']=![],_0x311669[_0x134a17(0x848)]=![],_0x311669[_0x134a17(0x944)]=![],_0x311669['permaid']=![],_0x311669[_0x134a17(0x6ae)]=![],_0x311669['postInterval']=0x1e,_0x311669['posterImage']=![],_0x311669['postURL']=_0x134a17(0xa25),_0x311669['privacy']=![],_0x311669['proxy']=![],_0x311669[_0x134a17(0x35c)]=null,_0x311669[_0x134a17(0x77c)]=null,_0x311669[_0x134a17(0x84e)]=![],_0x311669['previewToggleState']=!![],_0x311669['queue']=![],_0x311669['queueList']=[],_0x311669['pushLoudness']=![],_0x311669['randomize']=![],_0x311669[_0x134a17(0x9e7)]=![],_0x311669[_0x134a17(0x824)]=![],_0x311669[_0x134a17(0x231)]=!![],_0x311669[_0x134a17(0x24d)]=![],_0x311669[_0x134a17(0x25e)]=0x1770,_0x311669['raisehands']=![],_0x311669['retryTimeout']=0x1388,_0x311669['recordingVideoCodec']=![],_0x311669['remoteInterfaceAPI']=![],_0x311669[_0x134a17(0x5cf)]=![],_0x311669[_0x134a17(0x4e1)]=![],_0x311669[_0x134a17(0x250)]=![],_0x311669[_0x134a17(0xa3c)]=![],_0x311669[_0x134a17(0x6f3)]=null,_0x311669[_0x134a17(0x605)]=![],_0x311669[_0x134a17(0x1c9)]=![],_0x311669['removeOrientationFlag']=!![],_0x311669[_0x134a17(0x904)]=![],_0x311669[_0x134a17(0x793)]=![],_0x311669[_0x134a17(0x16a)]={},_0x311669[_0x134a17(0x228)]=![],_0x311669[_0x134a17(0x3af)]=![],_0x311669[_0x134a17(0x4b7)]=![],_0x311669[_0x134a17(0x23e)]=![],_0x311669['scale']=![],_0x311669[_0x134a17(0x15f)]=![],_0x311669[_0x134a17(0x8fa)]={},_0x311669[_0x134a17(0x9b9)]=![],_0x311669[_0x134a17(0x277)]=![],_0x311669[_0x134a17(0x259)]=![],_0x311669['iframetarget']='*',_0x311669['scene']=![],_0x311669['solo']=![],_0x311669['sceneList']={},_0x311669[_0x134a17(0x78e)]=![],_0x311669['signalMeter']=null,_0x311669[_0x134a17(0x393)]=![],_0x311669[_0x134a17(0x55a)]=![],_0x311669[_0x134a17(0x593)]=![],_0x311669[_0x134a17(0x1c3)]=![],_0x311669['screensharefps']=![],_0x311669['screenShareState']=![],_0x311669[_0x134a17(0x647)]=![],_0x311669['screenShareBitrate']=![],_0x311669[_0x134a17(0x488)]=![],_0x311669['screenShareStartPaused']=![],_0x311669['studioSoftware']=![],_0x311669['sticky']=![],_0x311669[_0x134a17(0x4e5)]=![],_0x311669[_0x134a17(0x22b)]=![],_0x311669[_0x134a17(0x71c)]=![],_0x311669[_0x134a17(0x3d9)]=[_0x134a17(0x680),'lin',_0x134a17(0x942),'mag','gyro',_0x134a17(0x29b)],_0x311669[_0x134a17(0x5db)]=0x0,_0x311669[_0x134a17(0x240)]=![],_0x311669[_0x134a17(0x55c)]=![],_0x311669[_0x134a17(0x286)]=![],_0x311669[_0x134a17(0x654)]=![],_0x311669['systemAudio']=![],_0x311669['displaySurface']=![],_0x311669[_0x134a17(0x46c)]=![],_0x311669[_0x134a17(0x901)]=![],_0x311669[_0x134a17(0x6d2)]=![],_0x311669[_0x134a17(0xa1f)]=null,_0x311669[_0x134a17(0x6bb)]=![],_0x311669[_0x134a17(0x85f)]=[],_0x311669[_0x134a17(0x5e4)]=![],_0x311669['screenshareType']=![],_0x311669[_0x134a17(0x7c1)]=!![],_0x311669[_0x134a17(0x560)]=![],_0x311669[_0x134a17(0x8bc)]=![],_0x311669[_0x134a17(0x893)]=![],_0x311669[_0x134a17(0x5eb)]=![],_0x311669[_0x134a17(0x260)]=null,_0x311669[_0x134a17(0x96b)]=![],_0x311669[_0x134a17(0x436)]={},_0x311669['sceneType']=![],_0x311669['slot']=![],_0x311669[_0x134a17(0x7b7)]=![],_0x311669[_0x134a17(0x9aa)]=![],_0x311669[_0x134a17(0xa21)]=![],_0x311669['screenStream']=![],_0x311669[_0x134a17(0x377)]=![],_0x311669['statsMenu']=null,_0x311669['statsInterval']=0xbb8,_0x311669['store']=![],_0x311669[_0x134a17(0x5a8)]=![],_0x311669['streamID']=null,_0x311669['streamSrc']=null,_0x311669[_0x134a17(0x7d7)]=null,_0x311669[_0x134a17(0x220)]=null,_0x311669[_0x134a17(0x886)]=![],_0x311669[_0x134a17(0x695)]=![],_0x311669[_0x134a17(0x22e)]=![],_0x311669['totalRoomBitrate']=![],_0x311669['totalRoomBitrate_default']=0x1f4,_0x311669[_0x134a17(0x332)]=![],_0x311669[_0x134a17(0x2cc)]=null,_0x311669[_0x134a17(0x31d)]=[_0x134a17(0x4ec),_0x134a17(0x35b)],_0x311669[_0x134a17(0x56d)]=![],_0x311669[_0x134a17(0x55e)]=![],_0x311669[_0x134a17(0x181)]=![],_0x311669['tz']=![],_0x311669[_0x134a17(0xa74)]=![],_0x311669[_0x134a17(0x2f4)]=![],_0x311669[_0x134a17(0x471)]=![],_0x311669[_0x134a17(0x74b)]=![],_0x311669['videoDevice']=![],_0x311669[_0x134a17(0x49b)]=![],_0x311669[_0x134a17(0x8ab)]=![],_0x311669[_0x134a17(0x589)]=![],_0x311669['directorVideoMuted']=![],_0x311669[_0x134a17(0x360)]=![],_0x311669[_0x134a17(0x528)]=![],_0x311669[_0x134a17(0x311)]=![],_0x311669[_0x134a17(0x82b)]=![],_0x311669[_0x134a17(0x356)]=![],_0x311669[_0x134a17(0x530)]=![],_0x311669[_0x134a17(0x57f)]=![],_0x311669['disableWebAudio']=![],_0x311669[_0x134a17(0x268)]=![],_0x311669['watchTimeoutList']={},_0x311669[_0x134a17(0x7a7)]={},_0x311669['webcamonly']=![],_0x311669['windowed']=![],_0x311669[_0x134a17(0x81d)]=![],_0x311669[_0x134a17(0x454)]=0x1388,_0x311669['waitImageTimeoutObject']=![],_0x311669[_0x134a17(0x8fb)]={},_0x311669[_0x134a17(0x352)]=![],_0x311669[_0x134a17(0x515)]=![],_0x311669['ws']=null,_0x311669[_0x134a17(0x8f5)]=![],_0x311669[_0x134a17(0x7af)]=null,_0x311669[_0x134a17(0xa44)]=![],_0x311669[_0x134a17(0x5df)]=![],_0x311669['welcomeHTML']=![],_0x311669[_0x134a17(0x173)]=![],_0x311669[_0x134a17(0x7f0)]=![],_0x311669[_0x134a17(0x491)]=![],_0x311669['whipOutVideoBitrate']=![],_0x311669[_0x134a17(0x2de)]=![],_0x311669['whipOut']=![],_0x311669[_0x134a17(0x8a4)]=![],_0x311669['whipOutput']=![],_0x311669[_0x134a17(0x856)]=![],_0x311669[_0x134a17(0x4c4)]=![],_0x311669[_0x134a17(0x85d)]=![],_0x311669[_0x134a17(0x1d4)]='',_0x311669[_0x134a17(0x3d3)]=null,_0x311669[_0x134a17(0x3f5)]=![],_0x311669[_0x134a17(0x97c)]=![],_0x311669['videoWorker']=![],_0x311669[_0x134a17(0x1c4)]=null,_0x311669['UUID']=![],_0x311669[_0x134a17(0x652)]=getById('muteStateTemplate')[_0x134a17(0x66c)](!![]),_0x311669[_0x134a17(0x96c)]=null,_0x311669[_0x134a17(0x652)]['id']=_0x134a17(0x652),_0x311669[_0x134a17(0x9eb)]=getById(_0x134a17(0x396))[_0x134a17(0x66c)](!![]),_0x311669['voiceMeter']['id']='localVoiceMeter',_0x311669[_0x134a17(0x9eb)]['style'][_0x134a17(0x486)]=0x0,_0x311669[_0x134a17(0x9eb)][_0x134a17(0x2bc)][_0x134a17(0x6b7)]=0x0,_0x311669['widget']=![],_0x311669[_0x134a17(0x924)]=![],_0x311669[_0x134a17(0x85a)]=!![],_0x311669['introOnClean']=![],_0x311669[_0x134a17(0x8fe)]=!![],_0x311669[_0x134a17(0x767)]=!![],_0x311669[_0x134a17(0x3fd)]=![],_0x311669[_0x134a17(0x9c5)]=location[_0x134a17(0x4a3)]['split']('.')[_0x134a17(0x7f5)](-0x2)[_0x134a17(0x24c)]('.'),_0x311669[_0x134a17(0x331)]=function(_0xe784f4,_0x4af475=_0x311669['password']+_0x311669[_0x134a17(0x9c5)]){var _0x434e19=_0x134a17,_0xd04dcd=crypto[_0x434e19(0x838)](new Uint8Array(0x10));return crypto[_0x434e19(0x388)]['digest']({'name':'SHA-256'},convertStringToArrayBufferView(_0x4af475))[_0x434e19(0x619)](function(_0x1ff785){var _0x76e1dd=_0x434e19;return window['crypto'][_0x76e1dd(0x388)]['importKey'](_0x76e1dd(0x282),_0x1ff785,{'name':_0x76e1dd(0x5e3)},![],['encrypt',_0x76e1dd(0x3a5)])[_0x76e1dd(0x619)](function(_0x1132ae){var _0x45f768=_0x76e1dd;return crypto[_0x45f768(0x388)]['encrypt']({'name':_0x45f768(0x5e3),'iv':_0xd04dcd},_0x1132ae,convertStringToArrayBufferView(_0xe784f4))[_0x45f768(0x619)](function(_0x23e77d){return encrypted_data=new Uint8Array(_0x23e77d),encrypted_data=toHexString(encrypted_data),_0xd04dcd=toHexString(_0xd04dcd),[encrypted_data,_0xd04dcd];},function(_0x5af82c){var _0x52eb94=_0x45f768;return errorlog(_0x5af82c[_0x52eb94(0x6d8)]),![];});},function(_0x1f1983){return errorlog(_0x1f1983),![];});})[_0x434e19(0x3b6)](errorlog);},_0x311669[_0x134a17(0x4da)]=function(_0x3c2c59,_0x12ce80,_0x392643=_0x311669['password']+_0x311669['salt']){var _0x1a9aae=_0x134a17;return _0x3c2c59=toByteArray(_0x3c2c59),_0x12ce80=toByteArray(_0x12ce80),crypto[_0x1a9aae(0x388)][_0x1a9aae(0x215)]({'name':_0x1a9aae(0x627)},convertStringToArrayBufferView(_0x392643))['then'](function(_0xeabfc){var _0x34bf6c=_0x1a9aae;return window[_0x34bf6c(0x189)][_0x34bf6c(0x388)][_0x34bf6c(0x6fe)](_0x34bf6c(0x282),_0xeabfc,{'name':_0x34bf6c(0x5e3)},![],[_0x34bf6c(0x480),_0x34bf6c(0x3a5)])[_0x34bf6c(0x619)](function(_0x4fadf0){var _0x21505d=_0x34bf6c;return crypto['subtle'][_0x21505d(0x3a5)]({'name':_0x21505d(0x5e3),'iv':_0x12ce80},_0x4fadf0,_0x3c2c59)[_0x21505d(0x619)](function(_0x3a7484){var _0x46015d=_0x21505d,_0x1dc534=new Uint8Array(_0x3a7484),_0x4fd8da='';for(var _0x37442c=0x0;_0x37442c<_0x1dc534[_0x46015d(0x7ce)];_0x37442c++){_0x4fd8da+=String[_0x46015d(0x48d)](_0x1dc534[_0x37442c]);}return _0x4fd8da;},function(_0x2be877){return errorlog(_0x2be877),![];});});})[_0x1a9aae(0x3b6)](errorlog);},_0x311669['decodeRemote']=async function(_0x2778c8){var _0x103770=_0x134a17;if(typeof _0x2778c8[_0x103770(0x24d)]!==_0x103770(0x53b))return _0x2778c8;try{_0x2778c8[_0x103770(0x24d)][_0x103770(0x8e9)]==0x2&&(!_0x311669[_0x103770(0x4c6)]&&(_0x311669['remoteHash']=await generateHash(_0x311669[_0x103770(0x24d)]+_0x311669[_0x103770(0x9c5)],0xc)),_0x2778c8[_0x103770(0x24d)]=await _0x311669['decryptMessage'](_0x2778c8[_0x103770(0x24d)][0x0],_0x2778c8[_0x103770(0x24d)][0x1],_0x311669[_0x103770(0x4c6)]),_0x2778c8[_0x103770(0x24d)]?log('Remote\x20request\x20decoded\x20successfully'):warnlog(_0x103770(0x7da)),log(_0x2778c8));}catch(_0x322bda){errorlog(_0x322bda);}return _0x2778c8;},_0x311669[_0x134a17(0x3b8)]=async function(_0x177fa2){var _0x31aa17=_0x134a17;try{if(_0x177fa2[_0x31aa17(0x24d)]&&typeof _0x177fa2[_0x31aa17(0x24d)]===_0x31aa17(0xa46)){var _0x547880=await generateHash(_0x177fa2[_0x31aa17(0x24d)]+_0x311669[_0x31aa17(0x9c5)],0xc);_0x177fa2['remote']=await _0x311669[_0x31aa17(0x331)](_0x177fa2[_0x31aa17(0x24d)],_0x547880);}}catch(_0x2908f4){errorlog(_0x2908f4);}return _0x177fa2;},_0x311669[_0x134a17(0x296)]=function(_0x4babe0){var _0x45920c=_0x134a17;try{_0x4babe0=decodeURIComponent(_0x4babe0),_0x4babe0=CryptoJS[_0x45920c(0x538)][_0x45920c(0x3a5)](_0x4babe0,_0x45920c(0xa0f)),_0x4babe0=_0x4babe0[_0x45920c(0x721)](CryptoJS[_0x45920c(0x558)]['Utf8']);if(_0x4babe0){if(_0x4babe0['startsWith'](_0x45920c(0x808)))_0x4babe0=_0x4babe0['replace'](_0x45920c(0x808),'');else{if(_0x4babe0[_0x45920c(0x62b)]('https://'))_0x4babe0=_0x4babe0[_0x45920c(0x2f9)]('https://','');else{if(_0x4babe0[_0x45920c(0x62b)]('/'))_0x4babe0=_0x4babe0['replace']('/','');else{if(_0x4babe0[_0x45920c(0x62b)]('obs.ninja/'))_0x4babe0=_0x4babe0[_0x45920c(0x2f9)](_0x45920c(0x500),'');else{if(_0x4babe0[_0x45920c(0x62b)]('vdo.ninja/'))_0x4babe0=_0x4babe0[_0x45920c(0x2f9)]('vdo.ninja/','');else _0x4babe0[_0x45920c(0x62b)](_0x45920c(0x373))&&(_0x4babe0=_0x4babe0['replace'](_0x45920c(0x373),''));}}}}_0x4babe0=_0x4babe0['split']('?')['splice'](0x1)[_0x45920c(0x24c)]('?'),_0x4babe0=_0x4babe0[_0x45920c(0x2f9)](/\?/g,'&'),_0x4babe0=_0x4babe0[_0x45920c(0x2f9)](/\&/,'?'),_0x4babe0&&(_0x311669[_0x45920c(0x263)]='?'+_0x4babe0);}}catch(_0x899220){warnlog(_0x899220);}},_0x311669['requestKeyframe']=function(_0xf1b09c,_0x2035e8=![]){var _0x47d558=_0x134a17,_0x4a4862={};_0x4a4862['keyframe']=!![],_0x4a4862[_0x47d558(0x98d)]=_0x2035e8,_0x311669['sendRequest'](_0x4a4862,_0xf1b09c);},_0x311669[_0x134a17(0x248)]=function(_0x4f7789,_0xbad792,_0x58dee4=null){var _0x51adee=_0x134a17;if(!_0x311669['rpcs'][_0xbad792])return![];var _0x25d670={};if(_0x58dee4!==null)_0x311669[_0x51adee(0x16a)][_0xbad792][_0x51adee(0x621)]=_0x58dee4||![];else{if(_0x311669[_0x51adee(0x16a)][_0xbad792][_0x51adee(0x621)]){warnlog('Audio\x20Bitrate\x20is\x20locked;\x20can\x27t\x20update');return;}}_0x25d670[_0x51adee(0x222)]=_0x4f7789,log(_0x25d670),_0x311669[_0x51adee(0xa14)](_0x25d670,_0xbad792);},_0x311669[_0x134a17(0x5d5)]=function(_0x57a7e9,_0x2ffe45,_0x4f7f0f=![],_0x1f9f36=null){var _0x4cd433=_0x134a17;log(_0x4cd433(0x25b)+_0x4f7f0f);if(!_0x311669[_0x4cd433(0x16a)][_0x2ffe45])return![];if(_0x1f9f36!==null)_0x311669['rpcs'][_0x2ffe45][_0x4cd433(0x7b9)]=_0x1f9f36||![];else{if(_0x311669[_0x4cd433(0x16a)][_0x2ffe45][_0x4cd433(0x7b9)]){warnlog('Video\x20Bitrate\x20is\x20locked;\x20can\x27t\x20update');return;}}if(_0x57a7e9===![]){}else _0x311669[_0x4cd433(0x16a)][_0x2ffe45]['targetBandwidth']=_0x57a7e9;var _0x4be281=-0x1;_0x311669[_0x4cd433(0x16a)][_0x2ffe45][_0x4cd433(0x3e4)]!==![]?_0x57a7e9=parseInt(_0x311669[_0x4cd433(0x16a)][_0x2ffe45][_0x4cd433(0x3e4)]):_0x57a7e9=parseInt(_0x311669[_0x4cd433(0x16a)][_0x2ffe45]['targetBandwidth']);if(_0x311669[_0x4cd433(0x34a)][_0x4cd433(0x4eb)]===![]){if(_0x311669[_0x4cd433(0x631)]!==![]){if(window[_0x4cd433(0x6ff)])return![];}}else{if(_0x311669[_0x4cd433(0x764)]&&_0x57a7e9===0x0)return![];}_0x57a7e9===0x0&&_0x311669[_0x4cd433(0x16a)][_0x2ffe45][_0x4cd433(0x94d)]&&(_0x57a7e9=0x1);if(_0x311669['rpcs'][_0x2ffe45][_0x4cd433(0x9cc)]===_0x57a7e9)return![];log(_0x4cd433(0x47d)+_0x57a7e9);var _0x2f1a1b={};_0x2f1a1b[_0x4cd433(0x739)]=_0x57a7e9;if(_0x4f7f0f===null){}else{if(_0x4f7f0f)_0x57a7e9===0x0?(warnlog('OPTIMIZED\x20AUDIO\x20ENABLED;\x20zero\x20bitrate'),_0x2f1a1b[_0x4cd433(0x222)]=0x0):_0x4be281<0x10&&_0x4be281>=0x0?_0x2f1a1b[_0x4cd433(0x222)]=_0x4be281:_0x2f1a1b['audioBitrate']=0x10;else _0x1f9f36===null&&(_0x2f1a1b[_0x4cd433(0x222)]=_0x4be281);}return _0x311669[_0x4cd433(0xa14)](_0x2f1a1b,_0x2ffe45)?(_0x311669[_0x4cd433(0x16a)][_0x2ffe45]['bandwidth']=_0x57a7e9,!![]):(setTimeout(function _0x24abb2(){var _0x5b20a5=_0x4cd433;_0x311669[_0x5b20a5(0x5d5)](![],_0x2ffe45);},0x1388),warnlog(_0x4cd433(0x444)),![]);},_0x311669[_0x134a17(0x9dc)]=function(_0x19a4de,_0x4bae75=![],_0x556bad=![],_0x3484f0=![]){var _0x1e8d78=_0x134a17,_0x11366b=![],_0x505751={};_0x505751['pipe']=_0x19a4de;try{if(!_0x4bae75&&!_0x556bad){if(_0x3484f0=='rpcs')_0x311669['sendRequest'](_0x505751);else _0x3484f0==_0x1e8d78(0x859)?_0x311669[_0x1e8d78(0x8a1)](_0x505751):_0x311669[_0x1e8d78(0x2db)](_0x505751);_0x11366b=!![];}else{if(_0x4bae75){_0x4bae75=_0x4bae75+'';if(_0x3484f0==_0x1e8d78(0x16a))_0x311669['sendRequest'](_0x505751,_0x4bae75);else _0x3484f0==_0x1e8d78(0x859)?_0x311669[_0x1e8d78(0x8a1)](_0x505751,_0x4bae75):_0x311669[_0x1e8d78(0x2db)](_0x505751,_0x4bae75);_0x11366b=!![];}else{if(_0x556bad){_0x556bad=_0x556bad+'';for(var _0x3fee7b in _0x311669['rpcs']){if(_0x311669[_0x1e8d78(0x16a)][_0x3fee7b][_0x1e8d78(0x9bb)]===_0x556bad){if(_0x3484f0==_0x1e8d78(0x16a))_0x311669[_0x1e8d78(0xa14)](_0x505751,_0x3fee7b);else _0x3484f0=='pcs'?_0x311669['sendMessage'](_0x505751,_0x3fee7b):_0x311669['sendPeers'](_0x505751,_0x3fee7b);_0x11366b=!![];}}}}}return _0x11366b;}catch(_0x273bde){return![];}},_0x311669[_0x134a17(0x640)]=function(_0x49c1a1,_0x1e1855){var _0x78d065=_0x134a17,_0x8da549={};_0x8da549[_0x78d065(0x4a8)]={},_0x8da549[_0x78d065(0x4a8)]=_0x49c1a1;_0x1e1855!==null&&(_0x8da549[_0x78d065(0x54a)]=_0x1e1855);if(isIFrame)parent[_0x78d065(0x5ef)](_0x8da549,_0x311669[_0x78d065(0xa2e)]);else _0x49c1a1[_0x78d065(0x6be)]&&!isIFrame&&getChatMessage(_0x49c1a1[_0x78d065(0x6be)]['chatmessage'],_0x49c1a1['overlayNinja'][_0x78d065(0x598)],![],![]);},_0x311669[_0x134a17(0x8f1)]=function(){var _0x1e0c35=_0x134a17;if(_0x311669[_0x1e0c35(0x5c9)]===null)return;for(var _0x2d5419 in _0x311669['rpcs']){try{var _0x3a20bf=getReceivers2(_0x2d5419);for(var _0x20d24c=0x0;_0x20d24c<_0x3a20bf[_0x1e0c35(0x8e9)];_0x20d24c++){_0x3a20bf[_0x20d24c][_0x1e0c35(0x53f)][_0x1e0c35(0x7ad)]=='audio'&&(_0x3a20bf[_0x20d24c][_0x1e0c35(0x53f)][_0x1e0c35(0x511)]=!_0x311669[_0x1e0c35(0x5c9)]);}}catch(_0x1ffe69){}}_0x311669['directorSpeakerMuted']&&(getById(_0x1e0c35(0x67e))[_0x1e0c35(0x225)]=!![]);},_0x311669[_0x134a17(0x324)]=function(){var _0x268ff9=_0x134a17;if(_0x311669[_0x268ff9(0x285)]===null)return;_0x311669['directorDisplayMuted']?(getById('gridlayout')[_0x268ff9(0x424)][_0x268ff9(0x517)]('hidden'),!_0x311669[_0x268ff9(0x78c)]&&warnUser(getTranslation(_0x268ff9(0x348)),![],![])):(getById(_0x268ff9(0xa1c))['classList']['remove'](_0x268ff9(0x472)),!_0x311669['cleanOutput']&&closeModal());for(var _0x2aba3b in _0x311669[_0x268ff9(0x16a)]){try{var _0x334661=getReceivers2(_0x2aba3b);for(var _0xe942b=0x0;_0xe942b<_0x334661[_0x268ff9(0x8e9)];_0xe942b++){_0x334661[_0xe942b][_0x268ff9(0x53f)][_0x268ff9(0x7ad)]==_0x268ff9(0x30c)&&(_0x334661[_0xe942b][_0x268ff9(0x53f)][_0x268ff9(0x511)]=!_0x311669[_0x268ff9(0x285)]);}}catch(_0x56a5dc){errorlog(_0x56a5dc);}}_0x311669[_0x268ff9(0x285)]&&(getById(_0x268ff9(0x67e))[_0x268ff9(0x225)]=!![]);},_0x311669[_0x134a17(0x349)]=async function(_0x397bd6,_0x29f7f8,_0x20ebef=_0x311669[_0x134a17(0x24d)]){var _0x4aea62=_0x134a17;log(_0x4aea62(0x6c9)+_0x397bd6),log(_0x29f7f8);var _0x35630c={};_0x35630c[_0x4aea62(0x57f)]=_0x397bd6,_0x35630c[_0x4aea62(0x24d)]=_0x20ebef,_0x35630c=await _0x311669[_0x4aea62(0x3b8)](_0x35630c),_0x311669[_0x4aea62(0xa14)](_0x35630c,_0x29f7f8)?log(_0x4aea62(0x361)):errorlog('failed\x20to\x20send\x20zoom\x20change\x20request');},_0x311669[_0x134a17(0x493)]=async function(_0x412c83,_0x564256,_0x2f94d5=_0x311669[_0x134a17(0x24d)]){var _0x7d3cc8=_0x134a17;log(_0x7d3cc8(0x8e4)+_0x412c83);var _0x5720cb={};_0x5720cb[_0x7d3cc8(0x21a)]=_0x412c83,_0x5720cb[_0x7d3cc8(0x24d)]=_0x2f94d5,_0x5720cb=await _0x311669[_0x7d3cc8(0x3b8)](_0x5720cb),_0x311669[_0x7d3cc8(0xa14)](_0x5720cb,_0x564256)?log('focus\x20success'):errorlog(_0x7d3cc8(0x567));},_0x311669['seedStream']=async function(){var _0x55a869=_0x134a17;await _0x311669['connect']();if(_0x311669['joiningRoom']!==![])_0x311669[_0x55a869(0x3cd)]=_0x55a869(0x918),log('seeding\x20blocked');else{if(_0x311669['doNotSeed']){_0x311669['meshcast']&&await meshcast();_0x311669['whipOutput']&&whipOut();return;}else{var _0x3f40aa={};_0x3f40aa[_0x55a869(0x727)]=_0x55a869(0x87b),_0x3f40aa[_0x55a869(0x9bb)]=_0x311669[_0x55a869(0x9bb)],_0x311669['sendMsg'](_0x3f40aa),log(_0x55a869(0xa7b)),pokeAPI(_0x55a869(0x22b),!![]),pokeIframeAPI('seeding-started',!![]),pokeIframeAPI('seeding',!![]);}}_0x311669[_0x55a869(0x2d6)]&&whipOut(),_0x311669[_0x55a869(0x98c)]&&await meshcast();},_0x311669['requestCoDirector']=function(){var _0x2e0f2c=_0x134a17;getById(_0x2e0f2c(0x687))[_0x2e0f2c(0x7a2)]=!![],getById(_0x2e0f2c(0x687))[_0x2e0f2c(0x29a)]=_0x2e0f2c(0x4c1),getById(_0x2e0f2c(0xa45))['classList'][_0x2e0f2c(0x517)](_0x2e0f2c(0x472)),_0x311669[_0x2e0f2c(0x76b)]&&(_0x311669['directorHash']?_0x311669['directorUUID']&&(_0x311669[_0x2e0f2c(0x2f3)]in _0x311669[_0x2e0f2c(0x16a)]&&(_0x311669[_0x2e0f2c(0x16a)][_0x311669['directorUUID']][_0x2e0f2c(0x45c)]===![]&&_0x311669[_0x2e0f2c(0x331)](_0x311669[_0x2e0f2c(0x799)],_0x311669['directorHash'])[_0x2e0f2c(0x619)](function(_0x25cebf){var _0x521f72=_0x2e0f2c,_0x3a40d7={};_0x3a40d7[_0x521f72(0x54a)]=_0x311669[_0x521f72(0x2f3)],_0x3a40d7[_0x521f72(0x873)]=_0x25cebf[0x0],_0x3a40d7[_0x521f72(0x9ed)]=_0x25cebf[0x1],_0x311669['rpcs'][_0x311669[_0x521f72(0x2f3)]][_0x521f72(0x45c)]===![]&&(_0x311669[_0x521f72(0xa14)](_0x3a40d7,_0x3a40d7[_0x521f72(0x54a)])&&(_0x311669[_0x521f72(0x16a)][_0x311669[_0x521f72(0x2f3)]][_0x521f72(0x45c)]=!![]));})[_0x2e0f2c(0x3b6)](errorlog))):generateHash(_0x311669[_0x2e0f2c(0x76b)]+_0x311669[_0x2e0f2c(0x9c5)]+'abc123',0xc)[_0x2e0f2c(0x619)](function(_0x5e1d16){var _0x28e444=_0x2e0f2c;_0x311669[_0x28e444(0x799)]=_0x5e1d16;_0x311669[_0x28e444(0x2f3)]&&(_0x311669[_0x28e444(0x16a)][_0x311669[_0x28e444(0x2f3)]][_0x28e444(0x45c)]===![]&&_0x311669[_0x28e444(0x331)](_0x311669[_0x28e444(0x799)],_0x311669['directorHash'])[_0x28e444(0x619)](function(_0x450247){var _0x284714=_0x28e444,_0x2d657a={};_0x2d657a[_0x284714(0x54a)]=_0x311669[_0x284714(0x2f3)],_0x2d657a[_0x284714(0x873)]=_0x450247[0x0],_0x2d657a['vector']=_0x450247[0x1],_0x311669['rpcs'][_0x311669[_0x284714(0x2f3)]][_0x284714(0x45c)]===![]&&(_0x311669[_0x284714(0xa14)](_0x2d657a,_0x2d657a[_0x284714(0x54a)])&&(_0x311669['rpcs'][_0x311669['directorUUID']][_0x284714(0x45c)]=!![]));})[_0x28e444(0x3b6)](errorlog));return;})[_0x2e0f2c(0x3b6)](errorlog));},_0x311669[_0x134a17(0x3ef)]=function(_0x28ef6b,_0x4360bc){return _0x28ef6b;},_0x311669[_0x134a17(0x164)]=function(_0x473900=![]){var _0x719c2=_0x134a17;log(_0x719c2(0x624));if(_0x473900){if(!_0x311669[_0x719c2(0x859)][_0x473900])return![];if(_0x311669['pcs'][_0x473900][_0x719c2(0x21f)]!==![]||_0x311669[_0x719c2(0x859)][_0x473900][_0x719c2(0x863)]!==![]||_0x311669[_0x719c2(0x859)][_0x473900]['scaleHeight']!==![])return log(_0x719c2(0x7ba)+_0x311669[_0x719c2(0x859)][_0x473900]['scaleWidth']+'\x20x\x20'+_0x311669[_0x719c2(0x859)][_0x473900][_0x719c2(0x1c6)]),_0x311669['setResolution'](_0x473900,_0x311669[_0x719c2(0x859)][_0x473900]['scaleWidth'],_0x311669['pcs'][_0x473900][_0x719c2(0x1c6)],_0x311669['pcs'][_0x473900][_0x719c2(0x482)],_0x311669[_0x719c2(0x859)][_0x473900][_0x719c2(0xa60)]),!![];else{if(_0x311669[_0x719c2(0x859)][_0x473900]['scale']!==![])return log('scale\x20scale'),_0x311669['setScale'](_0x473900,_0x311669[_0x719c2(0x859)][_0x473900][_0x719c2(0x6b5)],!![]),!![];}}else for(var _0x1a48b1 in _0x311669['pcs']){setTimeout(function(_0x5ea401){var _0x5ecf57=_0x719c2;if(_0x311669[_0x5ecf57(0x859)][_0x5ea401][_0x5ecf57(0x21f)]!==![]||_0x311669['pcs'][_0x5ea401][_0x5ecf57(0x863)]!==![]||_0x311669[_0x5ecf57(0x859)][_0x5ea401][_0x5ecf57(0x1c6)]!==![])log(_0x5ecf57(0x7ba)+_0x311669['pcs'][_0x5ea401][_0x5ecf57(0x863)]+'\x20x\x20'+_0x311669[_0x5ecf57(0x859)][_0x5ea401][_0x5ecf57(0x1c6)]),_0x311669['setResolution'](_0x5ea401,_0x311669[_0x5ecf57(0x859)][_0x5ea401]['scaleWidth'],_0x311669[_0x5ecf57(0x859)][_0x5ea401][_0x5ecf57(0x1c6)],_0x311669[_0x5ecf57(0x859)][_0x5ea401]['scaleSnap'],_0x311669['pcs'][_0x5ea401]['cover']);else _0x311669[_0x5ecf57(0x859)][_0x5ea401][_0x5ecf57(0x6b5)]!==![]&&(log(_0x5ecf57(0x99d)),_0x311669[_0x5ecf57(0x88b)](_0x5ea401,_0x311669[_0x5ecf57(0x859)][_0x5ea401][_0x5ecf57(0x6b5)],!![]));},0x0,_0x1a48b1);}return![];},_0x311669[_0x134a17(0xa35)]=function(_0xa9c725=_0x311669[_0x134a17(0xa10)]){var _0x3bc71d=_0x134a17;warnlog(_0x3bc71d(0x8e7));if(_0x311669[_0x3bc71d(0x638)][_0x3bc71d(0x6b5)]!==_0xa9c725){if(_0xa9c725==null){try{var _0xcf212b=_0x311669[_0x3bc71d(0x638)][_0x3bc71d(0x6cc)]()[_0x3bc71d(0x642)](function(_0x34e36){var _0x5ef792=_0x3bc71d;return _0x34e36['track']&&_0x34e36[_0x5ef792(0x53f)][_0x5ef792(0x7ad)]==_0x5ef792(0x30c);});}catch(_0x56cec8){errorlog(_0x56cec8);}if(!_0xcf212b){warnlog(_0x3bc71d(0x6cd));return;}var _0x123605=_0xcf212b[_0x3bc71d(0x650)]();(!_0x123605['encodings']||_0x123605[_0x3bc71d(0x895)][_0x3bc71d(0x8e9)]==0x0)&&(_0x123605[_0x3bc71d(0x895)]=[{}]),_0x3bc71d(0xa4b)in _0x123605['encodings'][0x0]?(_0xa9c725=0x64/_0x123605['encodings'][0x0][_0x3bc71d(0xa4b)],_0xa9c725=_0xa9c725*0.95):_0xa9c725=0x5f;}else _0x311669['whipOut']['scale']=_0xa9c725;try{if(SafariVersion&&SafariVersion<=0xd&&(iOS||iPad))log('iOS\x20devices\x20do\x20not\x20support\x20dynamic\x20bitrates\x20correctly;\x20skipping');else{if(_0x3bc71d(0x8b1)in window&&_0x3bc71d(0x537)in window[_0x3bc71d(0x8b1)][_0x3bc71d(0x776)]){try{var _0xcf212b=_0x311669[_0x3bc71d(0x638)]['getSenders']()[_0x3bc71d(0x642)](function(_0x1d75fa){var _0xd1da4a=_0x3bc71d;return _0x1d75fa[_0xd1da4a(0x53f)]&&_0x1d75fa[_0xd1da4a(0x53f)][_0xd1da4a(0x7ad)]=='video';});}catch(_0x8dd685){errorlog(_0x8dd685);}if(!_0xcf212b){warnlog(_0x3bc71d(0x6cd));return;}var _0x563048={};if(_0xa9c725<=0x0||_0xa9c725==0x64){var _0x1e7fc9=getChromiumVersion();_0x1e7fc9>0x50?_0x563048[_0x3bc71d(0xa4b)]=null:_0x563048['scaleResolutionDownBy']=0x1;}else _0x563048[_0x3bc71d(0xa4b)]=0x64/_0xa9c725;setEncodings(_0xcf212b,_0x563048,function(_0x37ff10){var _0xbd5f77=_0x3bc71d;log(_0xbd5f77(0x3ce)),pokeIframeAPI(_0xbd5f77(0x907),_0x37ff10,_0xbd5f77(0x98c)),pokeIframeAPI('set-video-scale',_0x37ff10,_0xbd5f77(0x98c)),_0x311669['whipOut'][_0xbd5f77(0x436)][_0xbd5f77(0x7ee)]=parseInt(_0x37ff10)+'%';},_0xa9c725);return;}}}catch(_0x5248bb){errorlog(_0x5248bb);}}},_0x311669[_0x134a17(0x88b)]=function(_0x5be927,_0x35a667,_0x19c2be=![]){var _0x55561e=_0x134a17;warnlog(_0x55561e(0x16c)+_0x35a667);try{_0x311669['pcs'][_0x5be927]['stats']['scaleFactor']=_0x35a667;}catch(_0x213234){errorlog(_0x213234);}if(!_0x19c2be&&_0x311669[_0x55561e(0x859)][_0x5be927][_0x55561e(0x6b5)]===_0x35a667)return;if(_0x35a667==null){try{var _0x38b0ee=getSenders2(_0x5be927)[_0x55561e(0x642)](function(_0x36c939){var _0x281a52=_0x55561e;return _0x36c939[_0x281a52(0x53f)]&&_0x36c939[_0x281a52(0x53f)][_0x281a52(0x7ad)]=='video';});}catch(_0x1685cf){errorlog(_0x1685cf);}if(!_0x38b0ee){warnlog('can\x27t\x20change\x20bitrate;\x20no\x20video\x20senders\x20found');return;}var _0x5c573f=_0x38b0ee[_0x55561e(0x650)]();(!_0x5c573f[_0x55561e(0x895)]||_0x5c573f[_0x55561e(0x895)]['length']==0x0)&&(_0x5c573f['encodings']=[{}]),_0x55561e(0xa4b)in _0x5c573f[_0x55561e(0x895)][0x0]?(_0x35a667=0x64/_0x5c573f['encodings'][0x0][_0x55561e(0xa4b)],_0x35a667=_0x35a667*0.95):_0x35a667=0x5f;}else _0x35a667=Math['ceil'](_0x35a667),_0x311669['pcs'][_0x5be927][_0x55561e(0x6b5)]=_0x35a667;try{if(SafariVersion&&SafariVersion<=0xd&&(iOS||iPad))log(_0x55561e(0x68a));else{if(_0x55561e(0x8b1)in window&&'setParameters'in window['RTCRtpSender'][_0x55561e(0x776)]){try{var _0x38b0ee=getSenders2(_0x5be927)[_0x55561e(0x642)](function(_0x137702){var _0xc4dddf=_0x55561e;return _0x137702[_0xc4dddf(0x53f)]&&_0x137702[_0xc4dddf(0x53f)][_0xc4dddf(0x7ad)]=='video';});}catch(_0x1837da){errorlog(_0x1837da);}if(!_0x38b0ee){warnlog(_0x55561e(0x6cd));return;}_0x35a667=_0x311669[_0x55561e(0x9f0)](_0x5be927,![],_0x35a667);var _0x346af8={};if(_0x35a667<=0x0||_0x35a667==0x64){var _0x1d5c4a=getChromiumVersion();_0x1d5c4a>0x50?_0x346af8[_0x55561e(0xa4b)]=null:_0x346af8[_0x55561e(0xa4b)]=0x1;}else _0x346af8[_0x55561e(0xa4b)]=0x64/_0x35a667;setEncodings(_0x38b0ee,_0x346af8,function(_0x1b171c){var _0x433afe=_0x55561e;log(_0x433afe(0x6d7)+_0x1b171c[0x0]),pokeIframeAPI(_0x433afe(0x907),_0x1b171c[0x0],_0x1b171c[0x1]),pokeIframeAPI('set-video-scale',_0x1b171c[0x0],_0x1b171c[0x1]),_0x311669[_0x433afe(0x859)][_0x1b171c[0x1]]['stats'][_0x433afe(0x7ee)]=parseInt(_0x1b171c[0x0])+'%';},[_0x35a667,_0x5be927]);return;}}}catch(_0x226686){errorlog(_0x226686);}},_0x311669[_0x134a17(0x37b)]=function(_0x4802d2,_0x1f3bd6,_0x324a44,_0x36f19e=![],_0x50fa15=![],_0x38e29f=null){var _0xecd6a3=_0x134a17;if(!(_0x4802d2 in _0x311669[_0xecd6a3(0x16a)]))return;_0x38e29f===null&&(_0x38e29f=_0x311669[_0xecd6a3(0xa60)]||![]);var _0x12f519=![];!(_0x311669[_0xecd6a3(0x16a)][_0x4802d2]['scaleWidth']==Math[_0xecd6a3(0x3c1)](_0x1f3bd6)||_0x311669[_0xecd6a3(0x16a)][_0x4802d2][_0xecd6a3(0x863)]===Math[_0xecd6a3(0x2c3)](_0x1f3bd6))&&(_0x1f3bd6=Math[_0xecd6a3(0x2fb)](_0x1f3bd6),_0x311669[_0xecd6a3(0x16a)][_0x4802d2][_0xecd6a3(0x863)]=_0x1f3bd6,_0x12f519=!![]);!(_0x311669[_0xecd6a3(0x16a)][_0x4802d2][_0xecd6a3(0x1c6)]==Math['floor'](_0x324a44)||_0x311669[_0xecd6a3(0x16a)][_0x4802d2][_0xecd6a3(0x1c6)]===Math[_0xecd6a3(0x2c3)](_0x324a44))&&(_0x324a44=Math[_0xecd6a3(0x2fb)](_0x324a44),_0x311669[_0xecd6a3(0x16a)][_0x4802d2][_0xecd6a3(0x1c6)]=_0x324a44,_0x12f519=!![]);_0x311669[_0xecd6a3(0x16a)][_0x4802d2][_0xecd6a3(0x482)]!=_0x36f19e&&(_0x311669['rpcs'][_0x4802d2][_0xecd6a3(0x482)]=_0x36f19e,_0x12f519=!![]);_0x1f3bd6=Math[_0xecd6a3(0x2fb)](_0x1f3bd6),_0x324a44=Math[_0xecd6a3(0x2fb)](_0x324a44);if(_0x12f519){var _0x4a331b={};_0x4a331b[_0xecd6a3(0x54a)]=_0x4802d2,_0x4a331b['requestResolution']={'w':_0x1f3bd6,'h':_0x324a44,'s':_0x36f19e,'c':_0x38e29f},_0x50fa15&&(_0x4a331b['requestAs']=_0x50fa15),log(_0x1f3bd6+'\x20'+_0x324a44),_0x311669[_0xecd6a3(0xa14)](_0x4a331b,_0x4802d2);}_0x36f19e?_0x311669['rpcs'][_0x4802d2][_0xecd6a3(0x436)]['Requested_resolution']='~\x20'+parseInt(_0x1f3bd6)+'\x20x\x20'+parseInt(_0x324a44):_0x311669[_0xecd6a3(0x16a)][_0x4802d2]['stats'][_0xecd6a3(0x957)]=parseInt(_0x1f3bd6)+'\x20x\x20'+parseInt(_0x324a44);},_0x311669[_0x134a17(0x9f0)]=function(_0x231f6c,_0x3e0438=![],_0x38131b=![]){var _0xad161b=_0x134a17;if(_0x38131b){}else _0x311669[_0xad161b(0x859)][_0x231f6c][_0xad161b(0x6b5)]?_0x38131b=_0x311669[_0xad161b(0x859)][_0x231f6c]['scale']:_0x38131b=0x64;_0x311669['pcs'][_0x231f6c][_0xad161b(0x21f)]&&_0x38131b>_0x311669['pcs'][_0x231f6c][_0xad161b(0x21f)]&&(_0x38131b=_0x311669[_0xad161b(0x859)][_0x231f6c]['scaleResolution']);if(_0x3e0438)_0x38131b=_0x526e6b(_0x231f6c,_0x38131b,_0x3e0438);else _0x311669[_0xad161b(0x859)][_0x231f6c][_0xad161b(0x291)]&&_0x311669['pcs'][_0x231f6c]['scaleDueToBitrate']<_0x38131b&&(_0x38131b=_0x311669[_0xad161b(0x859)][_0x231f6c][_0xad161b(0x291)]);if(_0x311669[_0xad161b(0x701)]&&_0x311669['pcs'][_0x231f6c]['scaleSnap']){if(_0x38131b>0x55)_0x38131b=0x64;else _0x38131b>0x2a&&_0x38131b<0x32&&(_0x38131b=0x32);}return _0x38131b=_0x311669[_0xad161b(0x3ef)](_0x38131b,_0x231f6c),_0x38131b;},_0x311669[_0x134a17(0x83a)]=function(_0x21b864=![],_0x1a14bf=null,_0x52f080=null,_0x52a931=![],_0x3a6b60=![]){var _0x56ad40=_0x134a17;warnlog(_0x56ad40(0x525)+_0x1a14bf+'x'+_0x52f080);if(_0x21b864&&!(_0x21b864 in _0x311669[_0x56ad40(0x859)]))return;else{if(!_0x21b864){for(var _0x21bf98 in _0x311669['pcs']){_0x311669['setResolution'](_0x21bf98,_0x311669[_0x56ad40(0x859)][_0x21bf98]['scaleWidth'],_0x311669[_0x56ad40(0x859)][_0x21bf98][_0x56ad40(0x1c6)],_0x311669[_0x56ad40(0x859)][_0x21bf98]['scaleSnap'],_0x311669['pcs'][_0x21bf98][_0x56ad40(0xa60)]);}return;}}_0x3a6b60=_0x3a6b60||![],snape=_0x52a931||![];if(_0x1a14bf===null&&_0x52f080===null){if(!_0x311669['pcs'][_0x21b864][_0x56ad40(0x863)]&&!_0x311669[_0x56ad40(0x859)][_0x21b864][_0x56ad40(0x1c6)])return;else _0x1a14bf=_0x311669[_0x56ad40(0x859)][_0x21b864]['scaleWidth']||0x64,_0x52f080=_0x311669['pcs'][_0x21b864][_0x56ad40(0x1c6)]||0x64;}else _0x311669[_0x56ad40(0x859)][_0x21b864][_0x56ad40(0x863)]=_0x1a14bf,_0x311669[_0x56ad40(0x859)][_0x21b864]['scaleHeight']=_0x52f080,_0x311669['pcs'][_0x21b864][_0x56ad40(0x482)]=_0x52a931,_0x311669['pcs'][_0x21b864][_0x56ad40(0xa60)]=_0x3a6b60;if(SafariVersion&&SafariVersion<=0xd&&(iOS||iPad))return;if(_0x56ad40(0x8b1)in window&&_0x56ad40(0x537)in window['RTCRtpSender']['prototype']){var _0x203ebf=getSenders2(_0x21b864)[_0x56ad40(0x642)](function(_0x410f53){var _0x561922=_0x56ad40;return _0x410f53['track']&&_0x410f53['track'][_0x561922(0x7ad)]==_0x561922(0x30c);});if(!_0x203ebf){warnlog(_0x56ad40(0x6d9));return;}var _0xa8b4a9={};if(_0x56ad40(0x65b)in _0x311669[_0x56ad40(0x859)][_0x21b864]){var _0x3bbf2b=_0x311669[_0x56ad40(0x7a0)][_0x56ad40(0x87a)]();if(_0x3bbf2b[_0x56ad40(0x8e9)])var _0x38683f=_0x3bbf2b[0x0]['getSettings'](),_0x375b7d=_0x38683f[_0x56ad40(0x5ec)],_0x139648=_0x38683f[_0x56ad40(0x530)];else return;}else{if(_0x311669['videoElement']&&_0x311669[_0x56ad40(0x49b)][_0x56ad40(0x5c4)]){var _0x3bbf2b=_0x311669[_0x56ad40(0x49b)][_0x56ad40(0x5c4)][_0x56ad40(0x87a)]();if(_0x3bbf2b[_0x56ad40(0x8e9)])var _0x38683f=_0x3bbf2b[0x0][_0x56ad40(0x2e2)](),_0x375b7d=_0x38683f[_0x56ad40(0x5ec)],_0x139648=_0x38683f[_0x56ad40(0x530)];else return;}else return;}var _0x505955=0x64*_0x1a14bf/_0x139648,_0x19f7b5=0x64*_0x52f080/_0x375b7d;warnlog(_0x505955+_0x56ad40(0x399)+_0x19f7b5);var _0x251e00=0x64;if(_0x1a14bf===null)_0x251e00=_0x19f7b5;else{if(_0x52f080===null)_0x251e00=_0x505955;else _0x3a6b60?_0x505955>_0x19f7b5?_0x251e00=_0x505955:_0x251e00=_0x19f7b5:_0x505955<_0x19f7b5?_0x251e00=_0x505955:_0x251e00=_0x19f7b5;}_0x251e00>0x64&&(_0x251e00=0x64);log(_0x56ad40(0x7ba)+_0x251e00),_0x311669['pcs'][_0x21b864][_0x56ad40(0x21f)]=_0x251e00;var _0x20dac2=_0x311669[_0x56ad40(0x9f0)](_0x21b864);if(_0x20dac2<=0x0||_0x20dac2==0x64){var _0x426e41=getChromiumVersion();_0x426e41>0x50?_0xa8b4a9['scaleResolutionDownBy']=null:_0xa8b4a9[_0x56ad40(0xa4b)]=0x1;}else _0xa8b4a9[_0x56ad40(0xa4b)]=0x64/_0x20dac2;setEncodings(_0x203ebf,_0xa8b4a9,function(_0x411e9e){var _0x37baf7=_0x56ad40;log('scale\x20set!'),pokeIframeAPI(_0x37baf7(0x907),_0x411e9e[0x0],_0x411e9e[0x1]),pokeIframeAPI('set-video-scale',_0x411e9e[0x0],_0x411e9e[0x1]),_0x311669[_0x37baf7(0x859)][_0x411e9e[0x1]]['stats'][_0x37baf7(0x7ee)]=parseInt(_0x411e9e[0x0])+'%';},[_0x20dac2,_0x21b864]);return;}},_0x311669[_0x134a17(0x2e3)]=function(_0x497e1a=null,_0x22ff6b=null){var _0x60b1aa=_0x134a17;_0x22ff6b&&_0x22ff6b[_0x60b1aa(0x890)]();_0x1e48c9&&(_0x1e48c9[_0x60b1aa(0x7fa)]=!![],log(_0x60b1aa(0x363)+_0x497e1a));if(iOS||iPad)return log(_0x60b1aa(0x68a)),![];else{if(_0x60b1aa(0x8b1)in window&&_0x60b1aa(0x537)in window[_0x60b1aa(0x8b1)][_0x60b1aa(0x776)]){log('FORCING\x20A\x20KEY\x20FRAME:\x20'+_0x497e1a);if(_0x497e1a==null){for(_0x497e1a in _0x311669[_0x60b1aa(0x859)]){_0x311669['forcePLI'](_0x497e1a);}return![];}if(!(_0x497e1a in _0x311669['pcs']))return![];_0x311669[_0x60b1aa(0x859)][_0x497e1a][_0x60b1aa(0x458)]&&(_0x311669[_0x60b1aa(0x859)][_0x497e1a][_0x60b1aa(0x849)]&&(clearTimeout(_0x311669[_0x60b1aa(0x859)][_0x497e1a][_0x60b1aa(0x849)]),_0x311669[_0x60b1aa(0x859)][_0x497e1a]['keyframeTimeout']=null),_0x311669[_0x60b1aa(0x859)][_0x497e1a][_0x60b1aa(0x849)]=setTimeout(function(_0x10e657){var _0x1721af=_0x60b1aa;!_0x311669[_0x1721af(0x859)][_0x10e657]?clearInterval(this):_0x311669['forcePLI'](_0x10e657);},parseInt(_0x311669['pcs'][_0x497e1a][_0x60b1aa(0x458)]),_0x497e1a));try{var _0x43c000=getSenders2(_0x497e1a)[_0x60b1aa(0x642)](function(_0xcb1420){var _0x50005c=_0x60b1aa;return _0xcb1420['track']&&_0xcb1420[_0x50005c(0x53f)]['kind']==_0x50005c(0x30c);});if(!_0x43c000)return warnlog(_0x60b1aa(0x6d9)),![];var _0x409b52={};return _0x409b52[_0x60b1aa(0xa4b)]=0xa,setEncodings(_0x43c000,_0x409b52,function(_0x12817e){var _0x7a930b=_0x60b1aa;log(_0x7a930b(0x3c0)+_0x12817e[0x0]);var _0x2840b3=_0x311669['calculateScale'](_0x12817e[0x0]),_0x2bbb1a={};if(_0x2840b3<=0x0||_0x2840b3==0x64){var _0xb13fdd=getChromiumVersion();_0xb13fdd>0x50?_0x2bbb1a[_0x7a930b(0xa4b)]=null:_0x2bbb1a[_0x7a930b(0xa4b)]=0x1;}else _0x2bbb1a[_0x7a930b(0xa4b)]=0x64/_0x2840b3;setEncodings(_0x12817e[0x1],_0x2bbb1a,function(){var _0x1b99a4=_0x7a930b;log(_0x1b99a4(0x9dd));});},[_0x497e1a,_0x43c000]),!![];}catch(_0x531c17){errorlog(_0x531c17);}}}return![];},_0x311669[_0x134a17(0x71b)]=function(_0xf68aaa){var _0x484973=_0x134a17;log('enhacing\x20audio\x20encoder');var _0x5180d1=getSenders2(_0xf68aaa)[_0x484973(0x642)](function(_0x3474aa){var _0x1010d1=_0x484973;return _0x3474aa[_0x1010d1(0x53f)]&&_0x3474aa['track'][_0x1010d1(0x7ad)]==_0x1010d1(0x433);});if(!_0x5180d1)return log(_0x484973(0x476)),![];var _0x550579={};try{_0x550579['networkPriority']=_0x484973(0x4f3),_0x550579[_0x484973(0x931)]=_0x484973(0x4f3),_0x550579[_0x484973(0x841)]=!![],setEncodings(_0x5180d1,_0x550579,function(_0x450474){var _0x2c2f03=_0x484973;log('done\x20clearing\x20audio'),pokeIframeAPI(_0x2c2f03(0x4cb),!![],_0x450474);},_0xf68aaa);}catch(_0x595865){errorlog(_0x595865);}},_0x311669[_0x134a17(0x5c7)]=function(_0x16f2dc,_0xf1d11=_0x134a17(0x757)){var _0x555855=_0x134a17,_0x7f3a1=getSenders2(_0x16f2dc)[_0x555855(0x642)](function(_0x1f393c){var _0x7bdf4d=_0x555855;return _0x1f393c[_0x7bdf4d(0x53f)]&&_0x1f393c[_0x7bdf4d(0x53f)][_0x7bdf4d(0x7ad)]==_0x7bdf4d(0x30c);});if(!_0x7f3a1)return log(_0x555855(0x80b)),![];var _0x5a5980={};try{_0xf1d11===!![]?(_0x5a5980[_0x555855(0x5c7)]='maintain-framerate',log('done\x20setting\x20degrad\x20to\x20maintain-framerate')):(_0x5a5980['degradationPreference']=_0xf1d11,log(_0x555855(0x219)+_0xf1d11)),setEncodings(_0x7f3a1,_0x5a5980,(function(){var _0x3d643c=_0x555855;log(_0x3d643c(0x956));}()));}catch(_0x1e8d54){errorlog(_0x1e8d54);}},_0x311669[_0x134a17(0x295)]=function(_0x2a7b00,_0x35ae60,_0x45f649=![]){var _0x482e5c=_0x134a17;log(_0x482e5c(0x76a)+_0x2a7b00+_0x482e5c(0x880)+_0x45f649);if(_0x311669[_0x482e5c(0x9b5)]===![])return;_0x35ae60['maxBandwidth']=parseInt(_0x311669['maxBandwidth']/0x64*_0x2a7b00),_0x45f649?_0x311669[_0x482e5c(0x217)](null):_0x311669[_0x482e5c(0x3da)](_0x35ae60['UUID'],null);},_0x311669[_0x134a17(0x33d)]=function(_0x4d6ce2,_0x439d26=0x7d00,_0x10528e=0x3e8){var _0x5ba1fc=_0x134a17;log(_0x5ba1fc(0x8b6));var _0x5df5b0=getSenders2(_0x4d6ce2)[_0x5ba1fc(0x642)](function(_0x16df0e){var _0x4f7dba=_0x5ba1fc;return _0x16df0e['track']&&_0x16df0e[_0x4f7dba(0x53f)][_0x4f7dba(0x7ad)]==_0x4f7dba(0x433);});if(!_0x5df5b0)return log(_0x5ba1fc(0x476)),![];var _0x594a3f={};_0x594a3f['maxBitrate']=_0x439d26,setEncodings(_0x5df5b0,_0x594a3f,function(_0x3da119){var _0x50a3ef=_0x5ba1fc;pokeIframeAPI(_0x50a3ef(0x9c8),_0x3da119[0x0],_0x3da119[0x1]),pokeIframeAPI(_0x50a3ef(0x899),_0x3da119[0x0],_0x3da119[0x1]),_0x3da119[0x2]>0x0&&setTimeout(function(){var _0x1f9e60=_0x50a3ef;try{if(_0x3da119[0x1]in _0x311669[_0x1f9e60(0x859)])var _0x514989=getSenders2(_0x3da119[0x1])[_0x1f9e60(0x642)](function(_0x347260){var _0x908d95=_0x1f9e60;return _0x347260[_0x908d95(0x53f)]&&_0x347260[_0x908d95(0x53f)][_0x908d95(0x7ad)]=='audio';});else return![];if(!_0x514989)return log(_0x1f9e60(0x476)),![];var _0x4b9d56={};_0x4b9d56['maxBitrate']=null,setEncodings(_0x514989,_0x4b9d56,function(){var _0x5e832a=_0x1f9e60;log(_0x5e832a(0x4c7));});}catch(_0x256dad){errorlog(_0x256dad);}},_0x3da119[0x2],_0x3da119[0x1]);},[_0x439d26,_0x4d6ce2,_0x10528e]);},_0x311669[_0x134a17(0x975)]=function(_0x3b22ed,_0x499783,_0x4c8492){var _0x4160cd=_0x134a17;pokeIframeAPI(_0x4160cd(0x8dd),_0x3b22ed,_0x4c8492);if(_0x311669[_0x4160cd(0x8a3)])return generateHash(_0x3b22ed+_0x311669['password']+_0x311669[_0x4160cd(0x9c5)],0x10)['then'](function(_0x1ac96a){var _0x2a2c37=_0x4160cd,_0x54a255={};_0x499783['updateurl']&&(_0x499783['roomenc']=_0x1ac96a);if(_0x311669[_0x2a2c37(0x75b)]&&_0x311669[_0x2a2c37(0x2f3)])_0x54a255[_0x2a2c37(0x3fe)]=_0x4c8492,_0x54a255['roomid']=_0x1ac96a,_0x54a255[_0x2a2c37(0x321)]=_0x499783,_0x311669['sendRequest'](_0x54a255,_0x311669[_0x2a2c37(0x2f3)]),log(_0x54a255);else{if(_0x499783['updateurl'])_0x54a255[_0x2a2c37(0x727)]=_0x2a2c37(0x3fe),_0x54a255['transferSettings']=_0x499783,log(_0x54a255),_0x311669[_0x2a2c37(0xa14)](_0x54a255,_0x4c8492,function(){var _0x48c1d2=_0x2a2c37,_0x5d9082={};_0x5d9082[_0x48c1d2(0x727)]=_0x48c1d2(0x3fe),_0x5d9082['roomid']=_0x1ac96a,_0x5d9082[_0x48c1d2(0x81f)]=_0x4c8492,_0x311669[_0x48c1d2(0x663)](_0x5d9082);}),log(_0x54a255);else{if(_0x2a2c37(0x254)in _0x499783)_0x54a255[_0x2a2c37(0x727)]=_0x2a2c37(0x3fe),_0x54a255['transferSettings']=_0x499783,delete _0x54a255[_0x2a2c37(0x321)][_0x2a2c37(0x4e1)],delete _0x54a255[_0x2a2c37(0x321)][_0x2a2c37(0x5cf)],log(_0x54a255),_0x311669['sendRequest'](_0x54a255,_0x4c8492,function(){var _0x3a7ad7=_0x2a2c37,_0x5cb453={};_0x5cb453[_0x3a7ad7(0x727)]=_0x3a7ad7(0x3fe),_0x5cb453[_0x3a7ad7(0x4e1)]=_0x1ac96a,_0x5cb453['target']=_0x4c8492,_0x311669[_0x3a7ad7(0x663)](_0x5cb453);}),log(_0x54a255);else Object[_0x2a2c37(0x19b)](_0x499783)[_0x2a2c37(0x8e9)]?(_0x54a255[_0x2a2c37(0x727)]=_0x2a2c37(0x3fe),_0x54a255[_0x2a2c37(0x321)]=_0x499783,delete _0x54a255['transferSettings'][_0x2a2c37(0x4e1)],delete _0x54a255[_0x2a2c37(0x321)][_0x2a2c37(0x5cf)],log(_0x54a255),_0x311669[_0x2a2c37(0xa14)](_0x54a255,_0x4c8492,function(){var _0x46f835=_0x2a2c37,_0x4cfa19={};_0x4cfa19[_0x46f835(0x727)]=_0x46f835(0x3fe),_0x4cfa19['roomid']=_0x1ac96a,_0x4cfa19['target']=_0x4c8492,_0x311669[_0x46f835(0x663)](_0x4cfa19);}),log(_0x54a255)):(_0x54a255[_0x2a2c37(0x727)]=_0x2a2c37(0x3fe),_0x54a255[_0x2a2c37(0x4e1)]=_0x1ac96a,_0x54a255[_0x2a2c37(0x81f)]=_0x4c8492,_0x311669[_0x2a2c37(0x663)](_0x54a255));}}})['catch'](errorlog);else{_0x499783[_0x4160cd(0x339)]&&(_0x499783[_0x4160cd(0x5cf)]=_0x3b22ed);var _0x3abf7f={};if(_0x311669[_0x4160cd(0x75b)]&&_0x311669[_0x4160cd(0x2f3)])_0x3abf7f[_0x4160cd(0x3fe)]=_0x4c8492,_0x3abf7f[_0x4160cd(0x4e1)]=_0x3b22ed,_0x3abf7f['transferSettings']=_0x499783,_0x311669['sendRequest'](_0x3abf7f,_0x311669[_0x4160cd(0x2f3)]),log(_0x3abf7f);else{if(_0x499783[_0x4160cd(0x339)])_0x3abf7f[_0x4160cd(0x727)]=_0x4160cd(0x3fe),_0x3abf7f[_0x4160cd(0x321)]=_0x499783,_0x311669[_0x4160cd(0xa14)](_0x3abf7f,_0x4c8492,function(){var _0x7f927d=_0x4160cd,_0x3eccef={};_0x3eccef['request']=_0x7f927d(0x3fe),_0x3eccef[_0x7f927d(0x4e1)]=_0x3b22ed,_0x3eccef[_0x7f927d(0x81f)]=_0x4c8492,_0x311669[_0x7f927d(0x663)](_0x3eccef);});else{if(_0x4160cd(0x254)in _0x499783)_0x3abf7f[_0x4160cd(0x727)]=_0x4160cd(0x3fe),_0x3abf7f['transferSettings']=_0x499783,delete _0x3abf7f[_0x4160cd(0x321)][_0x4160cd(0x4e1)],delete _0x3abf7f['transferSettings'][_0x4160cd(0x5cf)],_0x311669['sendRequest'](_0x3abf7f,_0x4c8492,function(){var _0x4091f8=_0x4160cd,_0xf532f9={};_0xf532f9[_0x4091f8(0x727)]=_0x4091f8(0x3fe),_0xf532f9[_0x4091f8(0x4e1)]=_0x3b22ed,_0xf532f9[_0x4091f8(0x81f)]=_0x4c8492,_0x311669[_0x4091f8(0x663)](_0xf532f9);});else Object[_0x4160cd(0x19b)](_0x499783)['length']?(_0x3abf7f['request']=_0x4160cd(0x3fe),_0x3abf7f[_0x4160cd(0x321)]=_0x499783,delete _0x3abf7f[_0x4160cd(0x321)]['roomid'],delete _0x3abf7f[_0x4160cd(0x321)][_0x4160cd(0x5cf)],log(_0x3abf7f),_0x311669[_0x4160cd(0xa14)](_0x3abf7f,_0x4c8492,function(){var _0x1d12fc=_0x4160cd,_0x469443={};_0x469443[_0x1d12fc(0x727)]=_0x1d12fc(0x3fe),_0x469443[_0x1d12fc(0x4e1)]=_0x3b22ed,_0x469443['target']=_0x4c8492,_0x311669[_0x1d12fc(0x663)](_0x469443);}),log(_0x3abf7f)):(_0x3abf7f['request']=_0x4160cd(0x3fe),_0x3abf7f[_0x4160cd(0x4e1)]=_0x3b22ed,_0x3abf7f[_0x4160cd(0x81f)]=_0x4c8492,_0x311669[_0x4160cd(0x663)](_0x3abf7f));}}}},_0x311669[_0x134a17(0x184)]=async function(_0x331462,_0xbf65a3){var _0x11cc10=_0x134a17;_0xbf65a3=parseInt(_0xbf65a3);try{var _0x15f901=getSenders2(_0x331462)[_0x11cc10(0x642)](function(_0x47c0a1){var _0x30baf0=_0x11cc10;return _0x47c0a1[_0x30baf0(0x53f)]&&_0x47c0a1[_0x30baf0(0x53f)][_0x30baf0(0x7ad)]==_0x30baf0(0x433);});if(!_0x15f901){warnlog('can\x27t\x20change\x20audio\x20bitrate;\x20no\x20audio\x20sender\x20found');return;}var _0x13bf00={};if(_0xbf65a3<0x0){_0x13bf00[_0x11cc10(0x9fb)]=!![];if(SafariVersion&&SafariVersion<=0xd&&(iOS||iPad)){_0xbf65a3=0x20;if(_0x311669[_0x11cc10(0x859)][_0x331462][_0x11cc10(0x9c8)]!==![])_0xbf65a3=_0x311669[_0x11cc10(0x859)][_0x331462][_0x11cc10(0x9c8)];else _0x311669[_0x11cc10(0x543)]&&(_0xbf65a3=_0x311669['audiobitrate']);_0x13bf00[_0x11cc10(0x9d1)]=_0xbf65a3*0x400;}else _0x311669[_0x11cc10(0x859)][_0x331462][_0x11cc10(0x9c8)]!==![]?(_0xbf65a3=_0x311669[_0x11cc10(0x859)][_0x331462][_0x11cc10(0x9c8)],_0x13bf00[_0x11cc10(0x9d1)]=_0xbf65a3*0x400):_0x13bf00['maxBitrate']=null;}else _0xbf65a3===0x0?_0x13bf00[_0x11cc10(0x9fb)]=![]:(_0x13bf00[_0x11cc10(0x9fb)]=!![],_0x13bf00[_0x11cc10(0x9d1)]=_0xbf65a3*0x400);_0x311669['pcs'][_0x331462][_0x11cc10(0x989)]&&(_0x13bf00['active']=![]),setEncodings(_0x15f901,_0x13bf00,function(_0x306789){var _0x170ebb=_0x11cc10;pokeIframeAPI(_0x170ebb(0x9c8),_0x306789[0x0],_0x306789[0x1]),pokeIframeAPI(_0x170ebb(0x899),_0x306789[0x0],_0x306789[0x1]),log(_0x170ebb(0x887));},[_0xbf65a3,_0x331462]);}catch(_0x34c4ce){errorlog(_0x34c4ce),log(_0x331462),log(_0x311669[_0x11cc10(0x859)][_0x331462]);}},_0x311669[_0x134a17(0xa11)]=function(_0x5e19b4){var _0xbec236=_0x134a17;if(_0x311669[_0xbec236(0x325)]&&_0x311669[_0xbec236(0x859)][_0x5e19b4][_0xbec236(0x299)]===!![])_0x311669[_0xbec236(0x3da)](_0x5e19b4,0x0),_0x311669['pcs'][_0x5e19b4]['optimizedBitrate']===0x0&&(_0x311669[_0xbec236(0x859)][_0x5e19b4][_0xbec236(0x34a)][_0xbec236(0x4eb)]===![]?_0x311669[_0xbec236(0x184)](_0x5e19b4,0x0):_0x311669[_0xbec236(0x184)](_0x5e19b4,-0x1));else{if(_0x311669[_0xbec236(0x859)][_0x5e19b4]&&_0x311669['pcs'][_0x5e19b4][_0xbec236(0x273)]!==![]){if(_0x311669['pcs'][_0x5e19b4][_0xbec236(0x34a)]['visibility']===![]){var _0x54c154=_0x311669[_0xbec236(0x859)][_0x5e19b4]['optimizedBitrate'];_0x311669[_0xbec236(0x859)][_0x5e19b4][_0xbec236(0x969)]&&_0x311669[_0xbec236(0x859)][_0x5e19b4][_0xbec236(0x969)]>0x0&&(_0x311669[_0xbec236(0x859)][_0x5e19b4][_0xbec236(0x969)]<_0x311669[_0xbec236(0x859)][_0x5e19b4][_0xbec236(0x273)]&&(_0x54c154=_0x311669['pcs'][_0x5e19b4][_0xbec236(0x969)])),_0x311669[_0xbec236(0x3da)](_0x5e19b4,_0x54c154),_0x311669[_0xbec236(0x859)][_0x5e19b4]['optimizedBitrate']===0x0&&_0x311669[_0xbec236(0x184)](_0x5e19b4,0x0);}else _0x311669[_0xbec236(0x859)][_0x5e19b4][_0xbec236(0x273)]===0x0&&(_0x311669[_0xbec236(0x184)](_0x5e19b4,-0x1),_0x311669[_0xbec236(0x484)](),_0x311669[_0xbec236(0x84c)]&&_0x311669[_0xbec236(0x3da)](_0x5e19b4,null));}else _0x311669['limitTotalBitrateGuests'](),_0x311669[_0xbec236(0x84c)]&&_0x311669['limitBitrate'](_0x5e19b4,null);}},_0x311669[_0x134a17(0x484)]=function(_0x23a70f=0x0,_0xa4487d=![]){var _0xcbe244=_0x134a17;if(!_0x311669['limitTotalBitrate'])return _0x23a70f;if(!_0x311669[_0xcbe244(0x4e1)]||_0x311669[_0xcbe244(0x98d)]!==![])return log('Switching\x20to\x20limitTotalBitrateAll'),_0x311669[_0xcbe244(0x32a)](_0x23a70f,_0xa4487d),_0x23a70f;if((iOS||iPad)&&SafariVersion&&SafariVersion<=0xd)return _0x23a70f;var _0x1c7953=_0x23a70f;if(_0xa4487d===![])_0x1c7953=0x0;else _0x1c7953<0x0&&(_0x1c7953=_0x311669[_0xcbe244(0x859)][_0xa4487d][_0xcbe244(0x9f3)]||_0x311669[_0xcbe244(0x810)]||_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x9b5)]||0x9c4);var _0x4a6e52=0x0;for(var _0x225915 in _0x311669['pcs']){if(_0xa4487d===_0x225915)continue;if(!_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x798)])continue;try{var _0x33814b=getSenders2(_0x225915)['find'](function(_0x2e872d){var _0x1d2dd3=_0xcbe244;return _0x2e872d[_0x1d2dd3(0x53f)]&&_0x2e872d[_0x1d2dd3(0x53f)]['kind']==_0x1d2dd3(0x30c);});if(!_0x33814b)continue;var _0x2d4598=_0x33814b[_0xcbe244(0x650)]();if(!_0x2d4598['encodings']||_0x2d4598[_0xcbe244(0x895)]['length']==0x0){_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x9f3)]<0x0?_0x1c7953+=_0x311669[_0xcbe244(0x810)]||_0x311669[_0xcbe244(0x859)][_0x225915]['maxBandwidth']||0x9c4:_0x1c7953+=_0x311669[_0xcbe244(0x859)][_0x225915]['setBitrate']||_0x311669[_0xcbe244(0x810)]||_0x311669[_0xcbe244(0x859)][_0x225915]['maxBandwidth']||0x9c4;warnlog(_0x1c7953),_0x4a6e52+=0x1;continue;}if(_0x2d4598[_0xcbe244(0x895)][0x0][_0xcbe244(0x9fb)]==![])continue;if(_0x2d4598[_0xcbe244(0x895)][0x0][_0xcbe244(0x9d1)])_0xcbe244(0x5a6)in _0x311669[_0xcbe244(0x859)][_0x225915]?_0x1c7953+=parseInt(_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x5a6)]):_0x1c7953+=parseInt(_0x2d4598[_0xcbe244(0x895)][0x0][_0xcbe244(0x9d1)])/0x400;else _0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x9f3)]<0x0?_0x1c7953+=_0x311669[_0xcbe244(0x810)]||_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x9b5)]||0x9c4:(_0x1c7953+=_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x9f3)]||_0x311669['outboundVideoBitrate']||_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x9b5)]||0x9c4,warnlog(_0x1c7953));_0x4a6e52+=0x1;}catch(_0x316d68){errorlog(_0x316d68);}}if(!_0x1c7953)return _0x1c7953;warnlog(_0xcbe244(0x553)+_0x1c7953);var _0x1d7f6f=parseFloat(_0x1c7953/_0x311669['limitTotalBitrate']);_0x1d7f6f<0x1&&(_0x1d7f6f=0x1);for(var _0x225915 in _0x311669[_0xcbe244(0x859)]){if(_0xa4487d===_0x225915)continue;if(!_0x311669['pcs'][_0x225915]['guest'])continue;try{var _0x33814b=getSenders2(_0x225915)['find'](function(_0x121174){var _0x51776b=_0xcbe244;return _0x121174['track']&&_0x121174[_0x51776b(0x53f)][_0x51776b(0x7ad)]==_0x51776b(0x30c);});if(!_0x33814b)continue;var _0x2d4598=_0x33814b[_0xcbe244(0x650)]();if(!_0x2d4598[_0xcbe244(0x895)]||_0x2d4598[_0xcbe244(0x895)][_0xcbe244(0x8e9)]==0x0){if(_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x9f3)]<0x0)var _0x38dcae=_0x311669[_0xcbe244(0x810)]||_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x9b5)]||0x9c4;else var _0x38dcae=_0x311669['pcs'][_0x225915][_0xcbe244(0x9f3)]||_0x311669['outboundVideoBitrate']||_0x311669[_0xcbe244(0x859)][_0x225915]['maxBandwidth']||0x9c4;var _0x107a12=parseInt(_0x38dcae/_0x1d7f6f);_0x311669[_0xcbe244(0x3da)](_0x225915,_0x107a12,!![]);continue;}if(_0x2d4598[_0xcbe244(0x895)][0x0][_0xcbe244(0x9fb)]==![])continue;if(_0x2d4598['encodings'][0x0][_0xcbe244(0x9d1)]){if(_0xcbe244(0x5a6)in _0x311669[_0xcbe244(0x859)][_0x225915])var _0x38dcae=parseInt(_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x5a6)]);else var _0x38dcae=parseInt(parseInt(_0x2d4598[_0xcbe244(0x895)][0x0][_0xcbe244(0x9d1)])/0x400);var _0x107a12=parseInt(_0x38dcae/_0x1d7f6f);_0x311669[_0xcbe244(0x3da)](_0x225915,_0x107a12,!![]);}else{if(_0x311669[_0xcbe244(0x859)][_0x225915]['setBitrate']<0x0)var _0x38dcae=_0x311669[_0xcbe244(0x810)]||_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x9b5)]||0x9c4;else var _0x38dcae=_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x9f3)]||_0x311669[_0xcbe244(0x810)]||_0x311669[_0xcbe244(0x859)][_0x225915][_0xcbe244(0x9b5)]||0x9c4;var _0x107a12=parseInt(_0x38dcae/_0x1d7f6f);_0x311669[_0xcbe244(0x3da)](_0x225915,_0x107a12,!![]);}}catch(_0x3f29d3){errorlog(_0x3f29d3);}}return parseInt(_0x23a70f/_0x1d7f6f);},_0x311669[_0x134a17(0x32a)]=function(_0x3fe6ec=0x0,_0x1f41e1=![]){var _0x13604f=_0x134a17;if(!_0x311669['limitTotalBitrate'])return _0x3fe6ec;if((iOS||iPad)&&SafariVersion&&SafariVersion<=0xd)return _0x3fe6ec;var _0x2a7b8e=_0x3fe6ec;if(_0x1f41e1===![])_0x2a7b8e=0x0;else _0x2a7b8e<0x0&&(_0x2a7b8e=_0x311669['pcs'][_0x1f41e1][_0x13604f(0x9f3)]||_0x311669['outboundVideoBitrate']||_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9b5)]||0x9c4);var _0x3e57c8=0x0;for(var _0x4efbb6 in _0x311669[_0x13604f(0x859)]){if(_0x1f41e1===_0x4efbb6)continue;try{var _0x280d78=getSenders2(_0x4efbb6)['find'](function(_0x589e36){var _0xca8aaa=_0x13604f;return _0x589e36[_0xca8aaa(0x53f)]&&_0x589e36[_0xca8aaa(0x53f)]['kind']==_0xca8aaa(0x30c);});if(!_0x280d78)continue;var _0x387658=_0x280d78[_0x13604f(0x650)]();if(!_0x387658[_0x13604f(0x895)]||_0x387658['encodings'][_0x13604f(0x8e9)]==0x0){_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9f3)]<0x0?_0x2a7b8e+=_0x311669[_0x13604f(0x810)]||_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9b5)]||0x9c4:_0x2a7b8e+=_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9f3)]||_0x311669['outboundVideoBitrate']||_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9b5)]||0x9c4;warnlog(_0x2a7b8e),_0x3e57c8+=0x1;continue;}if(_0x387658[_0x13604f(0x895)][0x0][_0x13604f(0x9fb)]==![])continue;if(_0x387658['encodings'][0x0][_0x13604f(0x9d1)])'preLimitedBitrate'in _0x311669['pcs'][_0x4efbb6]?_0x2a7b8e+=parseInt(_0x311669['pcs'][_0x4efbb6][_0x13604f(0x5a6)]):_0x2a7b8e+=parseInt(_0x387658[_0x13604f(0x895)][0x0]['maxBitrate'])/0x400;else _0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9f3)]<0x0?_0x2a7b8e+=_0x311669[_0x13604f(0x810)]||_0x311669['pcs'][_0x4efbb6][_0x13604f(0x9b5)]||0x9c4:(_0x2a7b8e+=_0x311669['pcs'][_0x4efbb6][_0x13604f(0x9f3)]||_0x311669[_0x13604f(0x810)]||_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9b5)]||0x9c4,warnlog(_0x2a7b8e));_0x3e57c8+=0x1;}catch(_0x3882d6){errorlog(_0x3882d6);}}if(!_0x2a7b8e)return _0x2a7b8e;warnlog(_0x13604f(0x553)+_0x2a7b8e);var _0x7ef431=parseFloat(_0x2a7b8e/_0x311669[_0x13604f(0x335)]);_0x7ef431<0x1&&(_0x7ef431=0x1);for(var _0x4efbb6 in _0x311669['pcs']){if(_0x1f41e1===_0x4efbb6)continue;try{var _0x280d78=getSenders2(_0x4efbb6)[_0x13604f(0x642)](function(_0x7cb49d){var _0x9c7fd7=_0x13604f;return _0x7cb49d[_0x9c7fd7(0x53f)]&&_0x7cb49d[_0x9c7fd7(0x53f)][_0x9c7fd7(0x7ad)]==_0x9c7fd7(0x30c);});if(!_0x280d78)continue;var _0x387658=_0x280d78[_0x13604f(0x650)]();if(!_0x387658['encodings']||_0x387658['encodings'][_0x13604f(0x8e9)]==0x0){if(_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9f3)]<0x0)var _0x54f415=_0x311669[_0x13604f(0x810)]||_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9b5)]||0x9c4;else var _0x54f415=_0x311669[_0x13604f(0x859)][_0x4efbb6]['setBitrate']||_0x311669[_0x13604f(0x810)]||_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9b5)]||0x9c4;var _0x5129c5=parseInt(_0x54f415/_0x7ef431);_0x311669['limitBitrate'](_0x4efbb6,_0x5129c5,!![]);continue;}if(_0x387658['encodings'][0x0][_0x13604f(0x9fb)]==![])continue;if(_0x387658['encodings'][0x0][_0x13604f(0x9d1)]){if(_0x13604f(0x5a6)in _0x311669[_0x13604f(0x859)][_0x4efbb6])var _0x54f415=parseInt(_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x5a6)]);else var _0x54f415=parseInt(parseInt(_0x387658[_0x13604f(0x895)][0x0][_0x13604f(0x9d1)])/0x400);var _0x5129c5=parseInt(_0x54f415/_0x7ef431);_0x311669[_0x13604f(0x3da)](_0x4efbb6,_0x5129c5,!![]);}else{if(_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9f3)]<0x0)var _0x54f415=_0x311669[_0x13604f(0x810)]||_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9b5)]||0x9c4;else var _0x54f415=_0x311669[_0x13604f(0x859)][_0x4efbb6]['setBitrate']||_0x311669[_0x13604f(0x810)]||_0x311669[_0x13604f(0x859)][_0x4efbb6][_0x13604f(0x9b5)]||0x9c4;var _0x5129c5=parseInt(_0x54f415/_0x7ef431);_0x311669[_0x13604f(0x3da)](_0x4efbb6,_0x5129c5,!![]);}}catch(_0xad4e36){errorlog(_0xad4e36);}}return parseInt(_0x3fe6ec/_0x7ef431);},_0x311669[_0x134a17(0x60e)]=function(_0x42d908,_0x4e35f4=![]){var _0x51ded1=_0x134a17,_0x4c45c1={};_0x4c45c1[_0x51ded1(0x8ce)]={},_0x4c45c1['directorSettings'][_0x51ded1(0x1d7)]=[_0x42d908],_0x311669['sendPeers'](_0x4c45c1,_0x4e35f4);},_0x311669[_0x134a17(0x217)]=function(_0x3cf104=null){var _0x4f7544=_0x134a17;if(!_0x311669['whipOut'])return;_0x311669[_0x4f7544(0x638)][_0x4f7544(0x62e)]&&(clearInterval(_0x311669[_0x4f7544(0x638)][_0x4f7544(0x62e)]),_0x311669[_0x4f7544(0x638)][_0x4f7544(0x62e)]=null);if(_0x3cf104===null){if(_0x311669[_0x4f7544(0x638)][_0x4f7544(0x969)]===![])return;_0x3cf104=_0x311669['whipOut'][_0x4f7544(0x969)];}_0x3cf104=parseInt(_0x3cf104);if(_0x311669[_0x4f7544(0x638)]['setBitrate']&&_0x3cf104>_0x311669['whipOut'][_0x4f7544(0x9f3)])_0x3cf104=_0x311669[_0x4f7544(0x638)][_0x4f7544(0x9f3)];else _0x311669[_0x4f7544(0x638)][_0x4f7544(0x9f3)]===![]&&(_0x3cf104<0x0&&(_0x311669[_0x4f7544(0x810)]?_0x3cf104=_0x311669[_0x4f7544(0x810)]:_0x3cf104=0x9c4));_0x311669[_0x4f7544(0x84c)]&&(_0x3cf104>_0x311669[_0x4f7544(0x84c)]&&(_0x3cf104=_0x311669[_0x4f7544(0x84c)]));_0x311669[_0x4f7544(0x638)][_0x4f7544(0x969)]=_0x3cf104;_0x311669['whipOut']['optimizedBitrate']!==![]&&(_0x311669[_0x4f7544(0x638)][_0x4f7544(0x34a)][_0x4f7544(0x4eb)]===![]&&(_0x3cf104>_0x311669[_0x4f7544(0x638)][_0x4f7544(0x273)]&&(_0x311669[_0x4f7544(0x638)]['savedBitrate']=_0x3cf104,_0x3cf104=parseInt(_0x311669['whipOut'][_0x4f7544(0x273)])||0x0)));if(_0x311669[_0x4f7544(0x638)]['maxBandwidth']!==null){if(_0x311669[_0x4f7544(0x638)][_0x4f7544(0x9b5)]<_0x3cf104)_0x3cf104=_0x311669[_0x4f7544(0x638)][_0x4f7544(0x9b5)],_0x311669[_0x4f7544(0x638)][_0x4f7544(0x436)][_0x4f7544(0xa2f)]=_0x3cf104,warnlog(_0x4f7544(0x3ab)+_0x3cf104+_0x4f7544(0x78b));else _0x311669[_0x4f7544(0x638)]['stats']&&(_0x311669[_0x4f7544(0x638)][_0x4f7544(0x436)][_0x4f7544(0xa2f)]=![]);}else _0x4f7544(0xa2f)in _0x311669[_0x4f7544(0x638)][_0x4f7544(0x436)]&&(_0x311669[_0x4f7544(0x638)]['stats'][_0x4f7544(0xa2f)]=![]);if(_0x3cf104===0x0){var _0xcdc56b=Date[_0x4f7544(0x610)]()-_0x311669['whipOut']['startTime'];_0xcdc56b<_0x311669[_0x4f7544(0x25e)]&&(_0x3cf104=_0x311669[_0x4f7544(0x9ff)],log(_0x4f7544(0x7c4)+(Date[_0x4f7544(0x610)]()-_0x311669['whipOut'][_0x4f7544(0x586)])),_0x311669['whipOut'][_0x4f7544(0x62e)]=setTimeout(function(){var _0x38a010=_0x4f7544;try{warnlog(_0x38a010(0x908)+(Date[_0x38a010(0x610)]()-_0x311669[_0x38a010(0x638)][_0x38a010(0x586)])),_0x311669[_0x38a010(0x217)](null);}catch(_0x12b5f1){};},_0x311669[_0x4f7544(0x25e)]-_0xcdc56b+0x5));}try{if((iOS||iPad)&&SafariVersion&&SafariVersion<=0xd){log(_0x4f7544(0x68a));var _0x4f6830=_0x311669['whipOut'][_0x4f7544(0x6cc)]()[_0x4f7544(0x642)](function(_0x2896b4){var _0x2dd292=_0x4f7544;return _0x2896b4[_0x2dd292(0x53f)]&&_0x2896b4['track']['kind']==_0x2dd292(0x30c);});if(!_0x4f6830){warnlog(_0x4f7544(0x6d9));return;}var _0x54adc4={};if(_0x3cf104<0x0)_0x54adc4[_0x4f7544(0x9fb)]=!![],_0x3cf104=0x9c4,_0x311669[_0x4f7544(0x739)]&&(_0x3cf104=_0x311669[_0x4f7544(0x739)]),_0x311669['maxvideobitrate']&&(_0x3cf104>_0x311669['maxvideobitrate']&&(_0x3cf104=_0x311669[_0x4f7544(0x84c)])),_0x54adc4[_0x4f7544(0x9d1)]=_0x3cf104*0x400;else _0x3cf104===0x0?_0x54adc4[_0x4f7544(0x9fb)]=![]:(_0x54adc4[_0x4f7544(0x9fb)]=!![],_0x54adc4[_0x4f7544(0x9d1)]=_0x3cf104*0x400);setEncodings(_0x4f6830,_0x54adc4,function(_0x4fa90b){var _0x1848aa=_0x4f7544;pokeIframeAPI(_0x1848aa(0x683),_0x4fa90b),log(_0x1848aa(0x984)+_0x4fa90b);},_0x3cf104);return;}else{if(_0x4f7544(0x8b1)in window&&_0x4f7544(0x537)in window[_0x4f7544(0x8b1)]['prototype']){var _0x4f6830=_0x311669[_0x4f7544(0x638)][_0x4f7544(0x6cc)]()[_0x4f7544(0x642)](function(_0x30f372){var _0x1c1c6a=_0x4f7544;return _0x30f372['track']&&_0x30f372[_0x1c1c6a(0x53f)]['kind']==_0x1c1c6a(0x30c);});if(!_0x4f6830){warnlog(_0x4f7544(0x6d9));return;}var _0x54adc4={};if(_0x3cf104<0x0)_0x54adc4[_0x4f7544(0x9fb)]==![]&&(_0x54adc4[_0x4f7544(0x9fb)]=!![]),_0x54adc4['maxBitrate']=null;else _0x3cf104===0x0?(_0x54adc4['active']=![],Firefox&&(_0x54adc4['maxBitrate']=0x1)):(_0x54adc4[_0x4f7544(0x9fb)]=!![],_0x54adc4['maxBitrate']=_0x3cf104*0x400);iPad||iOS||Firefox?_0x311669[_0x4f7544(0x638)][_0x4f7544(0x4d0)]?(clearInterval(_0x311669[_0x4f7544(0x638)][_0x4f7544(0x4d0)]),_0x311669[_0x4f7544(0x638)]['bitrateTimeoutFirefox']=setTimeout(function(){var _0x256913=_0x4f7544;log(_0x256913(0x4e8)+_0x3cf104),_0x311669[_0x256913(0x638)][_0x256913(0x4d0)]=![],_0x311669[_0x256913(0x217)](null);},0x1f4)):(_0x311669['whipOut'][_0x4f7544(0x4d0)]=setTimeout(function(){var _0x32a5b0=_0x4f7544;_0x311669['whipOut'][_0x32a5b0(0x4d0)]=![];},0x1f4),setEncodings(_0x4f6830,_0x54adc4,function(_0x4f82de){var _0x207671=_0x4f7544;log(_0x207671(0x888)+_0x4f82de),pokeIframeAPI(_0x207671(0x683),_0x4f82de);},_0x3cf104)):setEncodings(_0x4f6830,_0x54adc4,function(_0x24a53a){var _0x5134a8=_0x4f7544;log(_0x5134a8(0x3f1)+_0x24a53a),pokeIframeAPI('set-meshcast-video-bitrate',_0x24a53a);},_0x3cf104);return;}else warnlog(_0x4f7544(0x9ac));}}catch(_0x165ff6){errorlog(_0x165ff6);}},_0x311669['targetBitrate']=function(_0x197f7c,_0x11d0eb){var _0x1dd508=_0x134a17;_0x11d0eb===![]?(_0x311669['pcs'][_0x197f7c][_0x1dd508(0x9f3)]=![],_0x311669['limitBitrate'](_0x197f7c,-0x1)):(_0x11d0eb=parseInt(_0x11d0eb)||-0x1,_0x11d0eb>=0x0&&(_0x311669['pcs'][_0x197f7c][_0x1dd508(0x9f3)]=_0x11d0eb,_0x311669[_0x1dd508(0x3da)](_0x197f7c,_0x11d0eb)));},_0x311669[_0x134a17(0x347)]=function(_0x419ab3,_0x11bbd8){var _0x208c85=_0x134a17;_0x11bbd8===![]?(_0x311669[_0x208c85(0x859)][_0x419ab3][_0x208c85(0x9c8)]=![],_0x311669[_0x208c85(0x184)](_0x419ab3,-0x1)):(_0x11bbd8=parseInt(_0x11bbd8)||-0x1,_0x11bbd8>=0x0&&(_0x311669[_0x208c85(0x859)][_0x419ab3][_0x208c85(0x9c8)]=_0x11bbd8,_0x311669['limitAudioBitrate'](_0x419ab3,_0x11bbd8)));},_0x311669[_0x134a17(0x3da)]=function(_0x1eca7f,_0x2cf438=null,_0x62acd6=![]){var _0x586ce9=_0x134a17;warnlog(_0x586ce9(0x207)+_0x2cf438);if(!(_0x1eca7f in _0x311669[_0x586ce9(0x859)]))return;_0x311669[_0x586ce9(0x859)][_0x1eca7f]['bitrateTimeout']&&(clearInterval(_0x311669[_0x586ce9(0x859)][_0x1eca7f]['bitrateTimeout']),_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x62e)]=null);var _0x26f8b3=!![];if(_0x2cf438===null){if(_0x311669[_0x586ce9(0x859)][_0x1eca7f]['savedBitrate']===![]){if(_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x9b5)]===null)return;else _0x2cf438=_0x311669['pcs'][_0x1eca7f][_0x586ce9(0x9b5)],_0x26f8b3=![];}else _0x2cf438=_0x311669['pcs'][_0x1eca7f]['savedBitrate'];}_0x2cf438=parseInt(_0x2cf438);if(_0x311669['pcs'][_0x1eca7f][_0x586ce9(0x9f3)]&&_0x2cf438>_0x311669['pcs'][_0x1eca7f][_0x586ce9(0x9f3)])_0x2cf438=_0x311669[_0x586ce9(0x859)][_0x1eca7f]['setBitrate'];else _0x2cf438<0x0&&(_0x2cf438=_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x9f3)]||_0x311669['outboundVideoBitrate']||0x9c4);_0x311669[_0x586ce9(0x84c)]&&(_0x2cf438>_0x311669['maxvideobitrate']&&(_0x2cf438=_0x311669[_0x586ce9(0x84c)]));_0x26f8b3&&!_0x62acd6&&(log(_0x586ce9(0x5be)+_0x2cf438),_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x969)]=_0x2cf438);_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x273)]!==![]&&(_0x311669[_0x586ce9(0x859)][_0x1eca7f]['obsState'][_0x586ce9(0x4eb)]===![]&&(_0x2cf438>_0x311669['pcs'][_0x1eca7f][_0x586ce9(0x273)]&&(_0x26f8b3&&(_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x969)]=_0x2cf438),_0x2cf438=parseInt(_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x273)])||0x0)));if(_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x9b5)]!==null){if(_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x9b5)]<_0x2cf438)_0x2cf438=_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x9b5)],_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x436)][_0x586ce9(0xa2f)]=_0x2cf438,warnlog(_0x586ce9(0x3ab)+_0x2cf438+'-kbps');else _0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x9b5)]===_0x2cf438&&!_0x26f8b3?(_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x436)]['max_bandwidth_capped_kbps']=_0x2cf438,warnlog(_0x586ce9(0x372)+_0x2cf438+_0x586ce9(0x78b))):(warnlog(_0x586ce9(0x736)+_0x2cf438+_0x586ce9(0x78b)),_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x436)]['max_bandwidth_capped_kbps']=![]);}else _0x586ce9(0xa2f)in _0x311669[_0x586ce9(0x859)][_0x1eca7f]['stats']&&(_0x311669['pcs'][_0x1eca7f][_0x586ce9(0x436)]['max_bandwidth_capped_kbps']=![]);_0x62acd6===![]&&(_0x311669[_0x586ce9(0x335)]&&(_0x311669['pcs'][_0x1eca7f][_0x586ce9(0x5a6)]=_0x2cf438,_0x2cf438=_0x311669['limitTotalBitrateGuests'](_0x2cf438,_0x1eca7f)));if(_0x2cf438===0x0){var _0xe90eaa=Date['now']()-_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x586)];_0xe90eaa<_0x311669[_0x586ce9(0x25e)]&&(_0x2cf438=_0x311669[_0x586ce9(0x9ff)],log('starting\x20some\x20preload\x20bitrate\x20'+(Date['now']()-_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x586)])),_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x62e)]=setTimeout(function(_0x419588){var _0x5832ad=_0x586ce9;try{warnlog('stopping\x20some\x20preload\x20bitrate\x20'+(Date['now']()-_0x311669[_0x5832ad(0x859)][_0x419588][_0x5832ad(0x586)])),_0x311669[_0x5832ad(0x3da)](_0x419588,null);}catch(_0x3d20a0){};},_0x311669[_0x586ce9(0x25e)]-_0xe90eaa+0x5,_0x1eca7f));}try{if((iOS||iPad)&&SafariVersion&&SafariVersion<=0xd){log(_0x586ce9(0x68a));if(_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x798)]==!![]&&_0x311669[_0x586ce9(0x859)][_0x1eca7f]['forceios']==![])return;var _0x1fafc3=getSenders2(_0x1eca7f)[_0x586ce9(0x642)](function(_0x5b0c90){var _0x1e4b8d=_0x586ce9;return _0x5b0c90['track']&&_0x5b0c90[_0x1e4b8d(0x53f)][_0x1e4b8d(0x7ad)]=='video';});if(!_0x1fafc3){warnlog(_0x586ce9(0x6d9));return;}var _0x5c945b={};_0x2cf438===0x0?_0x5c945b[_0x586ce9(0x9fb)]=![]:(_0x5c945b[_0x586ce9(0x9fb)]=!![],_0x5c945b[_0x586ce9(0x9d1)]=_0x2cf438*0x400);setEncodings(_0x1fafc3,_0x5c945b,function(_0x3f882d){var _0x11b0fd=_0x586ce9;pokeIframeAPI('setVideoBitrate',_0x3f882d[0x0],_0x3f882d[0x1]),pokeIframeAPI(_0x11b0fd(0x581),_0x3f882d[0x0],_0x3f882d[0x1]),log('bandwidth\x20set\x20a!\x20'+_0x3f882d[0x0]);},[_0x2cf438,_0x1eca7f]);return;}else{if(_0x586ce9(0x8b1)in window&&_0x586ce9(0x537)in window[_0x586ce9(0x8b1)][_0x586ce9(0x776)]){var _0x1fafc3=getSenders2(_0x1eca7f)['find'](function(_0x372fc8){var _0x27be4d=_0x586ce9;return _0x372fc8[_0x27be4d(0x53f)]&&_0x372fc8[_0x27be4d(0x53f)][_0x27be4d(0x7ad)]==_0x27be4d(0x30c);});if(!_0x1fafc3){warnlog('can\x27t\x20change\x20bitrate;\x20no\x20video\x20sender\x20found');return;}var _0x5c945b={};_0x2cf438===0x0?(_0x5c945b[_0x586ce9(0x9fb)]=![],Firefox&&(_0x5c945b[_0x586ce9(0x9d1)]=0x1,_0x5c945b[_0x586ce9(0xa4b)]=0x3e8)):(_0x5c945b[_0x586ce9(0x9fb)]=!![],_0x5c945b['maxBitrate']=_0x2cf438*0x400);if(_0x2cf438!==0x0){var _0x5a0d4e=_0x311669['calculateScale'](_0x1eca7f,_0x2cf438);if(_0x5a0d4e<=0x0||_0x5a0d4e==0x64){var _0x24564b=getChromiumVersion();_0x24564b>0x50?_0x5c945b[_0x586ce9(0xa4b)]=null:_0x5c945b[_0x586ce9(0xa4b)]=0x1;}else _0x5c945b[_0x586ce9(0xa4b)]=0x64/_0x5a0d4e;iPad||iOS||Firefox?_0x311669[_0x586ce9(0x859)][_0x1eca7f]['bitrateTimeoutFirefox']?(clearInterval(_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x4d0)]),_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x4d0)]=setTimeout(function(_0x46b16e,_0x251d1e){var _0x3de3ad=_0x586ce9;log(_0x3de3ad(0x4e8)+_0x2cf438),_0x311669[_0x3de3ad(0x859)][_0x46b16e][_0x3de3ad(0x4d0)]=![],_0x311669[_0x3de3ad(0x3da)](_0x46b16e,null,_0x251d1e);},0x1f4,_0x1eca7f,_0x62acd6)):(_0x311669['pcs'][_0x1eca7f]['bitrateTimeoutFirefox']=setTimeout(function(_0x18dd75){var _0x2e0ddc=_0x586ce9;_0x311669['pcs'][_0x18dd75][_0x2e0ddc(0x4d0)]=![];},0x1f4,_0x1eca7f),setEncodings(_0x1fafc3,_0x5c945b,function(_0xc465d4){var _0x2b287a=_0x586ce9;log('bandwidth\x20set\x20b!\x20'+_0xc465d4[0x0]),_0x311669[_0x2b287a(0x859)][_0xc465d4[0x1]][_0x2b287a(0x436)][_0x2b287a(0x7ee)]=parseInt(_0xc465d4[0x2])+'%',pokeIframeAPI(_0x2b287a(0x37e),_0xc465d4[0x0],_0xc465d4[0x1]),pokeIframeAPI(_0x2b287a(0x907),_0xc465d4[0x2],_0xc465d4[0x1]),pokeIframeAPI(_0x2b287a(0x581),_0xc465d4[0x0],_0xc465d4[0x1]),pokeIframeAPI(_0x2b287a(0x8e0),_0xc465d4[0x2],_0xc465d4[0x1]);},[_0x2cf438,_0x1eca7f,_0x5a0d4e])):(warnlog(_0x5c945b),setEncodings(_0x1fafc3,_0x5c945b,function(_0x5ef75f){var _0x576b46=_0x586ce9;log('bandwidth\x20set\x20c!\x20'+_0x5ef75f[0x0]),_0x311669[_0x576b46(0x859)][_0x5ef75f[0x1]][_0x576b46(0x436)]['scaleFactor']=parseInt(_0x5ef75f[0x2])+'%',pokeIframeAPI(_0x576b46(0x37e),_0x5ef75f[0x0],_0x5ef75f[0x1]),pokeIframeAPI('setVideoScale',_0x5ef75f[0x2],_0x5ef75f[0x1]),pokeIframeAPI(_0x576b46(0x581),_0x5ef75f[0x0],_0x5ef75f[0x1]),pokeIframeAPI(_0x576b46(0x8e0),_0x5ef75f[0x2],_0x5ef75f[0x1]);},[_0x2cf438,_0x1eca7f,_0x5a0d4e]));}else iPad||iOS||Firefox?_0x311669['pcs'][_0x1eca7f][_0x586ce9(0x4d0)]?(clearInterval(_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x4d0)]),_0x311669['pcs'][_0x1eca7f][_0x586ce9(0x4d0)]=setTimeout(function(_0x2ec560,_0x2bd845){var _0x58acd6=_0x586ce9;log('bitrate\x20timeout;\x20ios/firefox\x20specific:\x20'+_0x2cf438),_0x311669[_0x58acd6(0x859)][_0x2ec560][_0x58acd6(0x4d0)]=![],_0x311669[_0x58acd6(0x3da)](_0x2ec560,null,_0x2bd845);},0x1f4,_0x1eca7f,_0x62acd6)):(_0x311669[_0x586ce9(0x859)][_0x1eca7f][_0x586ce9(0x4d0)]=setTimeout(function(_0x5c2e66){var _0xc7f44d=_0x586ce9;_0x311669[_0xc7f44d(0x859)][_0x5c2e66][_0xc7f44d(0x4d0)]=![];},0x1f4,_0x1eca7f),setEncodings(_0x1fafc3,_0x5c945b,function(_0x27858d){var _0x148541=_0x586ce9;log('bandwidth\x20set\x20d!\x20'+_0x27858d[0x0]),pokeIframeAPI(_0x148541(0x37e),_0x27858d[0x0],_0x27858d[0x1]),pokeIframeAPI(_0x148541(0x581),_0x27858d[0x0],_0x27858d[0x1]);},[_0x2cf438,_0x1eca7f])):setEncodings(_0x1fafc3,_0x5c945b,function(_0x192359){var _0x467070=_0x586ce9;log('bandwidth\x20set\x20e!\x20'+_0x192359[0x0]),pokeIframeAPI(_0x467070(0x37e),_0x192359[0x0],_0x192359[0x1]),pokeIframeAPI(_0x467070(0x581),_0x192359[0x0],_0x192359[0x1]);},[_0x2cf438,_0x1eca7f]);}else warnlog(_0x586ce9(0x9ac));}}catch(_0x3905f5){errorlog(_0x3905f5);}};function _0x526e6b(_0x510321,_0x21490f,_0x45e24b){var _0x1ce6be=_0x134a17;if(_0x311669[_0x1ce6be(0x9b9)])return _0x21490f;warnlog('getOptimizedScale:\x20'+_0x21490f+'\x20:\x20'+_0x45e24b);if(_0x45e24b<0x0)_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64;else{if(_0x45e24b>=0x259)_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64;else{if(_0x1ce6be(0x65b)in _0x311669[_0x1ce6be(0x859)][_0x510321])_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64;else{if(_0x311669[_0x1ce6be(0x701)])_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64;else{var _0x1a8762=getNativeOutputResolution();if(_0x1a8762)try{_0x1a8762=_0x1a8762[_0x1ce6be(0x530)]*_0x1a8762['height'],_0x1a8762=Math[_0x1ce6be(0x507)](_0x1a8762,0.5);}catch(_0x253473){_0x1a8762=![];}warnlog('dimension:\x20'+_0x1a8762);if(_0x45e24b>=0x15e){if(_0x1a8762&&_0x1a8762<=0x1e0)_0x311669['pcs'][_0x510321][_0x1ce6be(0x291)]=0x64;else{if(_0x311669[_0x1ce6be(0x1e3)]){if(_0x1a8762&&_0x1a8762>=0x5a0)_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64/0x3;else _0x311669['flagship']?_0x1a8762&&_0x1a8762>=0x3c0?_0x311669['pcs'][_0x510321][_0x1ce6be(0x291)]=0x64/0x2:_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64:_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64/0x2;}else{if(_0x1a8762&&_0x1a8762>=0x5a0)_0x311669['pcs'][_0x510321][_0x1ce6be(0x291)]=0x64/2.5;else _0x1a8762&&_0x1a8762>=0x3c0?_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64/0x2:_0x311669[_0x1ce6be(0x859)][_0x510321]['scaleDueToBitrate']=0x64;}}}else{if(_0x45e24b>=0xc9){if(_0x1a8762&&_0x1a8762<0x1e0)_0x311669['pcs'][_0x510321][_0x1ce6be(0x291)]=0x64;else{if(_0x311669[_0x1ce6be(0x1e3)]){if(_0x1a8762&&_0x1a8762>=0x5a0)_0x311669[_0x1ce6be(0x859)][_0x510321]['scaleDueToBitrate']=0x64/0x4;else _0x311669[_0x1ce6be(0x5f6)]?_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64/0x2:_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64/2.5;}else _0x1a8762&&_0x1a8762>=0x5a0?_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64/0x3:_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64/0x2;}}else{if(_0x1a8762&&_0x1a8762<=0xf0)_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64;else{if(_0x45e24b>=0x51){if(_0x311669[_0x1ce6be(0x1e3)]){if(_0x1a8762&&_0x1a8762>=0x5a0)_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64/0x6;else _0x311669[_0x1ce6be(0x5f6)]?_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64/0x3:_0x311669['pcs'][_0x510321][_0x1ce6be(0x291)]=0x64/0x4;}else _0x1a8762&&_0x1a8762>=0x5a0?_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]=0x64/0x4:_0x311669[_0x1ce6be(0x859)][_0x510321]['scaleDueToBitrate']=0x64/0x3;}else{if(_0x311669[_0x1ce6be(0x1e3)]){if(_0x1a8762&&_0x1a8762>=0x3c0)_0x311669['pcs'][_0x510321][_0x1ce6be(0x291)]=0x64/0x6;else _0x311669[_0x1ce6be(0x5f6)]?_0x311669['pcs'][_0x510321][_0x1ce6be(0x291)]=0x64/0x4:_0x311669['pcs'][_0x510321][_0x1ce6be(0x291)]=0x64/0x5;}else _0x1a8762&&_0x1a8762>=0x5a0?_0x311669['pcs'][_0x510321][_0x1ce6be(0x291)]=0x64/0x5:_0x311669['pcs'][_0x510321]['scaleDueToBitrate']=0x64/0x4;}}}}}}}}return _0x311669['pcs'][_0x510321][_0x1ce6be(0x291)]<_0x21490f&&(_0x21490f=_0x311669[_0x1ce6be(0x859)][_0x510321][_0x1ce6be(0x291)]),_0x21490f;}function _0x300658(_0x55cdec,_0x49859f=0x2710){var _0x3fd1c5=_0x134a17;_0x49859f=parseInt(_0x49859f);if(_0x311669[_0x3fd1c5(0x543)])_0x49859f+=_0x311669['audiobitrate'];else{if(_0x311669[_0x3fd1c5(0x75b)]&&_0x311669[_0x3fd1c5(0x5a8)]==0x5)_0x49859f+=0x20;else _0x311669[_0x3fd1c5(0x5a8)]&&_0x311669[_0x3fd1c5(0x5a8)]!=0x3?_0x49859f+=0x100:_0x49859f+=0x20;}return log(_0x3fd1c5(0x4b6)+_0x49859f),_0x49859f<0x1&&(_0x49859f=0x1),_0x55cdec=CodecsHandler[_0x3fd1c5(0x5bb)](_0x55cdec,{'min':parseInt(_0x49859f/0xa)||0x1,'max':_0x49859f||0x1},_0x311669[_0x3fd1c5(0x24e)]),_0x55cdec;}_0x311669['signData']=function(_0x476619,_0x52e635){var _0x4c36fb=_0x134a17;log(_0x476619),_0x311669['mykey']==={}&&log(_0x4c36fb(0x839)),window[_0x4c36fb(0x189)][_0x4c36fb(0x388)][_0x4c36fb(0x503)]({'name':'RSASSA-PKCS1-v1_5'},_0x311669[_0x4c36fb(0x51f)][_0x4c36fb(0x445)],_0x311669['enc']['encode'](_0x476619))['then'](function(_0x34f07c){var _0x42462a=_0x4c36fb;_0x34f07c=new Uint8Array(_0x34f07c),_0x34f07c=_0x34f07c[_0x42462a(0x21d)]((_0x253553,_0x359234)=>_0x253553+_0x359234[_0x42462a(0x721)](0x10)[_0x42462a(0x595)](0x2,'0'),''),_0x52e635(_0x476619,_0x34f07c),log(JSON[_0x42462a(0x479)](_0x34f07c));})[_0x4c36fb(0x3b6)](errorlog);},_0x311669['verifyData']=function(_0x555aac,_0x488bf4){var _0x16e2c2=_0x134a17;_0x555aac[_0x16e2c2(0x925)]=new Uint8Array(_0x555aac[_0x16e2c2(0x925)][_0x16e2c2(0x76e)](/.{1,2}/g)[_0x16e2c2(0x468)](_0x26612a=>parseInt(_0x26612a,0x10)));if(_0x311669[_0x16e2c2(0x19b)][_0x488bf4][_0x16e2c2(0xa5a)])return window[_0x16e2c2(0x189)]['subtle'][_0x16e2c2(0x2f0)]({'name':_0x16e2c2(0x597)},_0x311669[_0x16e2c2(0x19b)][_0x488bf4][_0x16e2c2(0xa5a)],_0x555aac[_0x16e2c2(0x925)],_0x311669[_0x16e2c2(0x558)][_0x16e2c2(0x9d3)](_0x555aac[_0x16e2c2(0x3bc)]))[_0x16e2c2(0x619)](function(_0x3fee0b){return _0x3fee0b;})[_0x16e2c2(0x3b6)](function(_0x4849a6){return errorlog(_0x4849a6),![];});},_0x311669[_0x134a17(0x4bf)]=function(_0xa5a7a4){var _0x44bffe=_0x134a17;if(_0x311669[_0x44bffe(0x8a3)])return _0x311669[_0x44bffe(0x8ff)]!==![]?(_0xa5a7a4=_0xa5a7a4[_0x44bffe(0x7f5)](0x0,-0x1*_0x311669[_0x44bffe(0x8ff)][_0x44bffe(0x8e9)]),_0xa5a7a4):generateHash(_0x311669[_0x44bffe(0x8a3)]+_0x311669[_0x44bffe(0x9c5)],0x6)[_0x44bffe(0x619)](function(_0x4f8296){var _0x4ff385=_0x44bffe;return _0x311669[_0x4ff385(0x8ff)]=_0x4f8296,_0xa5a7a4=_0xa5a7a4[_0x4ff385(0x7f5)](0x0,-0x1*_0x311669[_0x4ff385(0x8ff)][_0x4ff385(0x8e9)]),_0xa5a7a4;})[_0x44bffe(0x3b6)](errorlog);return _0xa5a7a4;},_0x311669['ping']=function(){var _0x1d5e49=_0x134a17;if(_0x311669[_0x1d5e49(0x1d0)])return;clearTimeout(_0x311669[_0x1d5e49(0x35c)]);if(!_0x311669['ws']||_0x311669['ws']['readyState']!==0x1)return;_0x311669['pingTimeout']=setTimeout(function(){var _0x10124d=_0x1d5e49;log(_0x10124d(0x2a1));var _0x9e3457={};_0x9e3457['request']=_0x10124d(0x66a),_0x311669[_0x10124d(0x663)](_0x9e3457);},0xbb8);},_0x311669['watchStream']=async function(_0x4c6d81){var _0x1b5d91=_0x134a17;await _0x311669[_0x1b5d91(0x857)]();if(_0x4c6d81[_0x1b5d91(0x8e9)]>0x0){if(_0x4c6d81===_0x311669['streamID']){warnlog(_0x1b5d91(0x28c));return;}var _0x4987f6={};_0x4987f6[_0x1b5d91(0x727)]=_0x1b5d91(0x6a8),_0x4987f6[_0x1b5d91(0x9bb)]=_0x4c6d81,_0x311669[_0x1b5d91(0x663)](_0x4987f6),_0x311669[_0x1b5d91(0x8fb)][_0x4c6d81]=!![],pokeIframeAPI(_0x1b5d91(0x6fa),_0x4c6d81);}else log(_0x1b5d91(0x9ee));},_0x311669[_0x134a17(0x28a)]=async function _0x4fbc41(_0x3251df){var _0x2a89c4=_0x134a17;_0x311669[_0x2a89c4(0x3cd)]===![]&&(_0x311669[_0x2a89c4(0x3cd)]=!![]);await _0x311669[_0x2a89c4(0x857)]();var _0x452c34={};_0x452c34[_0x2a89c4(0x727)]='joinroom';_0x311669[_0x2a89c4(0x75b)]&&!_0x311669['directorView']&&(_0x452c34[_0x2a89c4(0x178)]=!![]);_0x311669[_0x2a89c4(0x1d0)]&&(_0x452c34[_0x2a89c4(0x9bb)]=_0x311669[_0x2a89c4(0x9bb)]);var _0x55873a='';return _0x311669[_0x2a89c4(0x9f7)]&&(_0x55873a=_0x311669[_0x2a89c4(0x9f7)]),_0x311669['password']?_0x311669['hash']?generateHash(_0x3251df+_0x311669[_0x2a89c4(0x8a3)]+_0x311669[_0x2a89c4(0x9c5)]+_0x55873a,0x10)[_0x2a89c4(0x619)](function(_0x32305e){var _0x2eecaa=_0x2a89c4;return _0x311669[_0x2eecaa(0x1d0)]&&(_0x311669[_0x2eecaa(0x5cf)]=_0x32305e),_0x452c34[_0x2eecaa(0x4e1)]=_0x32305e,_0x311669['sendMsg'](_0x452c34),_0x311669[_0x2eecaa(0x778)]=_0x4b2dee(),log(_0x2eecaa(0x958)),pokeIframeAPI(_0x2eecaa(0x860),_0x3251df),_0x311669[_0x2eecaa(0x778)];})[_0x2a89c4(0x3b6)](errorlog):generateHash(_0x311669['password']+_0x311669['salt'],0x6)[_0x2a89c4(0x619)](function(_0x587027){var _0x4d96e0=_0x2a89c4;return _0x311669[_0x4d96e0(0x8ff)]=_0x587027,log(_0x4d96e0(0x172)+_0x587027),log('rejoining\x20room'),_0x311669['joinRoom'](_0x3251df);})[_0x2a89c4(0x3b6)](errorlog):(_0x311669[_0x2a89c4(0x1d0)]&&(_0x311669[_0x2a89c4(0x5cf)]=_0x3251df),_0x452c34[_0x2a89c4(0x4e1)]=_0x3251df,_0x311669[_0x2a89c4(0x663)](_0x452c34),_0x311669[_0x2a89c4(0x778)]=_0x4b2dee(),log(_0x2a89c4(0x5e7)),pokeIframeAPI(_0x2a89c4(0x860),_0x3251df),_0x311669['listPromise']);},_0x311669['sendMsg']=function(_0x261f33,_0x36efcb=![]){var _0x2d9eca=_0x134a17;_0x36efcb&&(_0x261f33[_0x2d9eca(0x54a)]=_0x36efcb);if(_0x311669[_0x2d9eca(0x1d0)]){_0x311669[_0x2d9eca(0x54a)]?_0x261f33[_0x2d9eca(0x1dc)]=_0x311669['UUID']:(_0x311669[_0x2d9eca(0x54a)]=_0x311669[_0x2d9eca(0xa76)](0x14),_0x261f33[_0x2d9eca(0x1dc)]=_0x311669[_0x2d9eca(0x54a)]);if(_0x261f33[_0x2d9eca(0x54a)]&&_0x261f33['from']===_0x261f33[_0x2d9eca(0x54a)])return;_0x311669[_0x2d9eca(0x75b)]&&(_0x261f33[_0x2d9eca(0x75b)]=!![]),!(_0x2d9eca(0x4e1)in _0x261f33)&&(_0x311669[_0x2d9eca(0x5cf)]&&(_0x261f33['roomid']=_0x311669['roomenc']));}clearTimeout(_0x311669[_0x2d9eca(0x35c)]);try{if(_0x311669[_0x2d9eca(0x8a3)]){if(_0x261f33[_0x2d9eca(0x9bb)]){if(_0x311669[_0x2d9eca(0x8ff)]!==![]){if(!_0x311669['ws']||(typeof _0x311669['ws']!==_0x2d9eca(0x53b)||_0x311669['ws'][_0x2d9eca(0x8ee)]!==0x1))log(_0x261f33,_0x2d9eca(0x5c2)),_0x311669['msg'][_0x2d9eca(0x505)](_0x261f33);else{_0x261f33[_0x2d9eca(0x9bb)]=_0x261f33['streamID'][_0x2d9eca(0x30d)](0x0,0x2c)+_0x311669['hash'][_0x2d9eca(0x30d)](0x0,0x6);var _0x4e3319=JSON[_0x2d9eca(0x479)](_0x261f33);if(_0x4e3319[_0x2d9eca(0x8e9)]>0x3a98){errorlog(_0x2d9eca(0x568)),errorlog(_0x261f33),errorlog(_0x4e3319['length']);return;}_0x311669['ws'][_0x2d9eca(0x2b9)](_0x4e3319);}}else return generateHash(_0x311669[_0x2d9eca(0x8a3)]+_0x311669['salt'],0x6)['then'](function(_0x34f6ca){var _0x3f8812=_0x2d9eca;_0x311669['hash']=_0x34f6ca;if(typeof _0x311669['ws']!==_0x3f8812(0x53b)||_0x311669['ws'][_0x3f8812(0x8ee)]!==0x1)log(_0x261f33,'could\x20not\x20be\x20sent;\x20queuing\x20it'),_0x311669[_0x3f8812(0x6e7)]['push'](_0x261f33);else{_0x261f33[_0x3f8812(0x9bb)]=_0x261f33['streamID'][_0x3f8812(0x30d)](0x0,0x2c)+_0x311669[_0x3f8812(0x8ff)][_0x3f8812(0x30d)](0x0,0x6);var _0x209ce2=JSON['stringify'](_0x261f33);if(_0x209ce2[_0x3f8812(0x8e9)]>0x3a98){errorlog('msg\x20size\x20error');return;}_0x311669['ws']['send'](_0x209ce2);}})[_0x2d9eca(0x3b6)](errorlog);}else{if(!_0x311669['ws']||(typeof _0x311669['ws']!==_0x2d9eca(0x53b)||_0x311669['ws'][_0x2d9eca(0x8ee)]!==0x1))log(_0x261f33,_0x2d9eca(0x5c2)),_0x311669['msg']['push'](_0x261f33);else{var _0x4e3319=JSON[_0x2d9eca(0x479)](_0x261f33);if(_0x4e3319['length']>0x3a98){errorlog(_0x2d9eca(0x568));return;}_0x311669['ws']['send'](_0x4e3319);}}}else{if(typeof _0x311669['ws']!==_0x2d9eca(0x53b)||_0x311669['ws']['readyState']!==0x1)warnlog(_0x2d9eca(0x3e6)),_0x311669[_0x2d9eca(0x6e7)][_0x2d9eca(0x505)](_0x261f33);else{var _0x4e3319=JSON[_0x2d9eca(0x479)](_0x261f33);if(_0x4e3319[_0x2d9eca(0x8e9)]>0x3a98){errorlog(_0x2d9eca(0x568));return;}_0x311669['ws'][_0x2d9eca(0x2b9)](_0x4e3319);}}}catch(_0x2461b3){errorlog(_0x2461b3);}},_0x311669[_0x134a17(0x2db)]=function(_0x2f479e,_0x223463=![],_0x217456=![]){var _0x5b11fa=_0x134a17,_0x560e63=[],_0x3de05a=JSON['stringify'](_0x2f479e);for(var _0x2f31e1 in _0x311669[_0x5b11fa(0x859)]){if(_0x217456&&_0x217456===_0x2f31e1)continue;if(_0x223463&&_0x223463!==_0x2f31e1)continue;try{_0x311669['pcs'][_0x2f31e1]['sendChannel']['send'](_0x3de05a),_0x560e63[_0x5b11fa(0x505)](_0x2f31e1);}catch(_0x347c4f){warnlog(_0x5b11fa(0x3e5));}if(_0x223463&&_0x223463===_0x2f31e1)return _0x560e63[_0x5b11fa(0x8e9)];}for(var _0x2f31e1 in _0x311669['rpcs']){if(_0x217456&&_0x217456===_0x2f31e1)continue;if(_0x223463&&_0x223463!==_0x2f31e1)continue;if(_0x560e63[_0x5b11fa(0x2c2)](_0x2f31e1))continue;if(_0x311669[_0x5b11fa(0x16a)][_0x2f31e1][_0x5b11fa(0x751)]){warnlog(_0x3de05a);continue;}try{if(_0x5b11fa(0x65b)in _0x311669[_0x5b11fa(0x16a)][_0x2f31e1]){var _0x132705=JSON[_0x5b11fa(0x52d)](_0x2f479e);_0x132705[_0x5b11fa(0x67b)]=!![],_0x132705=JSON[_0x5b11fa(0x479)](_0x132705),_0x311669[_0x5b11fa(0x16a)][_0x311669[_0x5b11fa(0x16a)][_0x2f31e1]['realUUID']]['receiveChannel'][_0x5b11fa(0x2b9)](_0x132705);}else _0x311669['rpcs'][_0x2f31e1][_0x5b11fa(0x4a7)][_0x5b11fa(0x2b9)](_0x3de05a);_0x560e63[_0x5b11fa(0x505)](_0x2f31e1);}catch(_0x4cf219){warnlog('RTC\x20Connection\x20seems\x20to\x20be\x20dead\x20or\x20not\x20yet\x20open?\x202');}}return _0x560e63['length'];},_0x311669[_0x134a17(0x966)]=function(_0x4feb04,_0x5a4161=![]){var _0x187aed=_0x134a17,_0x43f86d=![];if(_0x187aed(0x54a)in _0x4feb04)_0x43f86d=_0x311669['sendMessage'](_0x4feb04,_0x4feb04[_0x187aed(0x54a)]),_0x43f86d?(log(_0x4feb04),log(_0x187aed(0x69c))):(log('sending\x20message\x20via\x20WSS\x20as\x20WebRTC\x20failed\x20to\x20send\x20message'),_0x311669[_0x187aed(0x663)](_0x4feb04));else _0x5a4161?(_0x43f86d=_0x311669['sendMessage'](_0x4feb04),_0x43f86d?(log(_0x4feb04),log('successfully\x20sent\x20message\x20vis\x20WebRTC\x20instead\x20of\x20WSS\x20to\x20all\x20RTC\x20Peers')):(log(_0x187aed(0x914)),_0x311669[_0x187aed(0x663)](_0x4feb04))):(_0x311669[_0x187aed(0x663)](_0x4feb04),warnlog('sending\x20message\x20via\x20server'),warnlog(_0x4feb04));},_0x311669['anyrequest']=function(_0x3486ce,_0x286fc4=![]){var _0x215ba2=_0x134a17,_0x2a6909=![];if('UUID'in _0x3486ce)_0x2a6909=_0x311669[_0x215ba2(0xa14)](_0x3486ce,_0x3486ce[_0x215ba2(0x54a)]),_0x2a6909?log(_0x215ba2(0x69c)):(log(_0x215ba2(0xa58)),_0x311669[_0x215ba2(0x663)](_0x3486ce));else _0x286fc4?(_0x2a6909=_0x311669[_0x215ba2(0xa14)](_0x3486ce),_0x2a6909?log(_0x215ba2(0x32b)):(log(_0x215ba2(0x914)),_0x311669[_0x215ba2(0x663)](_0x3486ce))):(_0x311669[_0x215ba2(0x663)](_0x3486ce),warnlog(_0x215ba2(0x8f9)),warnlog(_0x3486ce));},_0x311669[_0x134a17(0xa37)]=function(_0x5986c5){var _0x5313a1=_0x134a17;log(_0x5986c5);if('action'in _0x5986c5){if('target'in _0x5986c5){if(_0x5313a1(0x98d)in _0x5986c5){if(_0x311669[_0x5313a1(0x98d)]!==![]){var _0x2ddedf=![],_0x4cb66b=0x0;for(var _0x2991ad in _0x311669['rpcs']){_0x4cb66b+=0x1;if(_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x9bb)]===_0x5986c5[_0x5313a1(0x81f)]){if(_0x5313a1(0x8d7)in _0x5986c5){if(_0x5986c5['action']=='mute')_0x5986c5[_0x5313a1(0x8d7)]==0x1?(log('Mute\x20video\x203306'),_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x4fc)]=!![],applyMuteState(_0x2991ad)):(log(_0x5313a1(0xa7e)),_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x4fc)]=![],applyMuteState(_0x2991ad)),_0x311669[_0x5313a1(0x919)](_0x2991ad);else{if(_0x5986c5[_0x5313a1(0x95f)]=='display'){if(_0x311669[_0x5313a1(0x311)])return;;if(_0x311669['scene']===_0x5986c5[_0x5313a1(0x98d)]){if(_0x311669['sceneType']==0x2){if(_0x5986c5[_0x5313a1(0x8d7)]==0x0){_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x3e1)]=!![],applyMuteState(_0x2991ad);_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]['style'][_0x5313a1(0x355)]&&_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x886)]['display']!=='none'&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]=_0x5313a1(0x42d),_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x935)]=![],_0x2ddedf=!![]));_0x311669[_0x5313a1(0x16a)][_0x2991ad]['iframeEle']&&_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x9b0)]['style']['display']&&_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x9b0)]['style'][_0x5313a1(0x355)]!==_0x5313a1(0x42d)&&(_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)]['display']='none',_0x311669[_0x5313a1(0x16a)][_0x2991ad]['iframeEle']['sceneType2']=![],_0x2ddedf=!![]);var _0x10266b=0x0,_0x179252=![];for(var _0x158cb3 in _0x311669[_0x5313a1(0x16a)]){_0x158cb3!==_0x2991ad&&(_0x311669['rpcs'][_0x158cb3][_0x5313a1(0x49b)]&&_0x311669['rpcs'][_0x158cb3][_0x5313a1(0x49b)]['sceneType2']&&(_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x49b)]['sceneType2']>_0x10266b&&(_0x10266b=_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x49b)]['sceneType2'],_0x179252=_0x158cb3)),_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x9b0)]&&_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x9b0)]['sceneType2']&&(_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x9b0)]['sceneType2']>_0x10266b&&(_0x10266b=_0x311669['rpcs'][_0x158cb3][_0x5313a1(0x9b0)][_0x5313a1(0x935)],_0x179252=_0x158cb3)));}_0x179252&&(_0x311669[_0x5313a1(0x16a)][_0x179252][_0x5313a1(0x3e1)]=![],applyMuteState(_0x179252),_0x311669[_0x5313a1(0x16a)][_0x179252][_0x5313a1(0x49b)]&&(_0x311669[_0x5313a1(0x16a)][_0x179252]['videoElement'][_0x5313a1(0x3b5)]&&clearInterval(_0x311669[_0x5313a1(0x16a)][_0x179252][_0x5313a1(0x49b)][_0x5313a1(0x3b5)]),_0x311669[_0x5313a1(0x16a)][_0x179252][_0x5313a1(0x49b)][_0x5313a1(0x9f4)]=![],_0x311669[_0x5313a1(0x802)]&&(_0x311669[_0x5313a1(0x16a)][_0x179252]['videoElement'][_0x5313a1(0x3b5)]=setTimeout(showControlBar[_0x5313a1(0x5a3)](null,_0x311669[_0x5313a1(0x16a)][_0x179252][_0x5313a1(0x49b)]),0x3e8)),_0x311669[_0x5313a1(0x16a)][_0x179252][_0x5313a1(0x49b)]['style']['display']&&_0x311669[_0x5313a1(0x16a)][_0x179252][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]!==_0x5313a1(0xa87)&&(_0x311669[_0x5313a1(0x16a)][_0x179252]['videoElement'][_0x5313a1(0x886)][_0x5313a1(0x355)]=_0x5313a1(0xa87),_0x311669['rpcs'][_0x179252][_0x5313a1(0x49b)][_0x5313a1(0x935)]=Date[_0x5313a1(0x610)](),_0x2ddedf=!![])),_0x311669[_0x5313a1(0x16a)][_0x179252]['iframeEle']&&_0x311669[_0x5313a1(0x16a)][_0x179252][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]&&_0x311669[_0x5313a1(0x16a)][_0x179252]['iframeEle'][_0x5313a1(0x886)][_0x5313a1(0x355)]!==_0x5313a1(0xa87)&&(_0x311669['rpcs'][_0x179252]['iframeEle'][_0x5313a1(0x886)]['display']=_0x5313a1(0xa87),_0x311669['rpcs'][_0x179252]['iframeEle']['sceneType2']=Date[_0x5313a1(0x610)](),_0x2ddedf=!![]));}else{for(var _0x158cb3 in _0x311669['rpcs']){_0x158cb3!==_0x2991ad&&(_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x3e1)]=!![],applyMuteState(_0x158cb3),_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x49b)]&&(_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x49b)]['style'][_0x5313a1(0x355)]&&_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x49b)]['style']['display']!==_0x5313a1(0x42d)&&(_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]=_0x5313a1(0x42d),_0x2ddedf=!![])),_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x9b0)]&&_0x311669['rpcs'][_0x158cb3][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]&&_0x311669['rpcs'][_0x158cb3][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]!==_0x5313a1(0x42d)&&(_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x9b0)][_0x5313a1(0x886)]['display']=_0x5313a1(0x42d),_0x2ddedf=!![]));}_0x311669[_0x5313a1(0x16a)][_0x2991ad]['mutedStateScene']=![],applyMuteState(_0x2991ad),_0x311669[_0x5313a1(0x16a)][_0x2991ad]['videoElement']&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x3b5)]&&clearInterval(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x3b5)]),_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x9f4)]=![],_0x311669[_0x5313a1(0x802)]&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x3b5)]=setTimeout(showControlBar['bind'](null,_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]),0x3e8)),_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]&&_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]['style'][_0x5313a1(0x355)]!==_0x5313a1(0xa87)&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]='block',_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]['sceneType2']=Date[_0x5313a1(0x610)](),_0x2ddedf=!![])),_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x9b0)]&&_0x311669[_0x5313a1(0x16a)][_0x2991ad]['iframeEle']['style'][_0x5313a1(0x355)]&&_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]!=='block'&&(_0x311669['rpcs'][_0x2991ad]['iframeEle']['style'][_0x5313a1(0x355)]=_0x5313a1(0xa87),_0x311669[_0x5313a1(0x16a)][_0x2991ad]['iframeEle']['sceneType2']=Date['now'](),_0x2ddedf=!![]);}}else{if(_0x311669[_0x5313a1(0x301)]==0x1){if(_0x5986c5[_0x5313a1(0x8d7)]==0x0)_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]['style'][_0x5313a1(0x355)]&&_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]['style']['display']!==_0x5313a1(0x42d)&&(_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]=_0x5313a1(0x42d),_0x2ddedf=!![])),_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x9b0)]&&_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]&&_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]!==_0x5313a1(0x42d)&&(_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)]['display']=_0x5313a1(0x42d),_0x2ddedf=!![]);else{for(var _0x158cb3 in _0x311669['rpcs']){_0x158cb3!==_0x2991ad&&(_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x49b)]&&(_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]&&_0x311669['rpcs'][_0x158cb3][_0x5313a1(0x49b)]['style'][_0x5313a1(0x355)]!==_0x5313a1(0x42d)&&(_0x311669['rpcs'][_0x158cb3][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]=_0x5313a1(0x42d),_0x2ddedf=!![])),_0x311669['rpcs'][_0x158cb3][_0x5313a1(0x9b0)]&&_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x9b0)]['style'][_0x5313a1(0x355)]&&_0x311669[_0x5313a1(0x16a)][_0x158cb3][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]!==_0x5313a1(0x42d)&&(_0x311669[_0x5313a1(0x16a)][_0x158cb3]['iframeEle'][_0x5313a1(0x886)][_0x5313a1(0x355)]='none',_0x2ddedf=!![]));}_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x49b)]&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x3b5)]&&clearInterval(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]['controlTimer']),_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x9f4)]=![],_0x311669[_0x5313a1(0x802)]&&(_0x311669['rpcs'][_0x2991ad]['videoElement'][_0x5313a1(0x3b5)]=setTimeout(showControlBar[_0x5313a1(0x5a3)](null,_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]),0x3e8)),_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]&&_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]!==_0x5313a1(0xa87)&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad]['videoElement']['style'][_0x5313a1(0x355)]=_0x5313a1(0xa87),_0x2ddedf=!![])),_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x9b0)]&&_0x311669['rpcs'][_0x2991ad]['iframeEle'][_0x5313a1(0x886)]['display']&&_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]!==_0x5313a1(0xa87)&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]=_0x5313a1(0xa87),_0x2ddedf=!![]);}}else _0x5986c5[_0x5313a1(0x8d7)]==0x0?(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x3e1)]=!![],applyMuteState(_0x2991ad),_0x311669[_0x5313a1(0x16a)][_0x2991ad]['videoElement']&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x886)]['display']&&_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]!==_0x5313a1(0x42d)&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]['style']['display']=_0x5313a1(0x42d),_0x2ddedf=!![])),_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x9b0)]&&_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]&&_0x311669[_0x5313a1(0x16a)][_0x2991ad]['iframeEle'][_0x5313a1(0x886)][_0x5313a1(0x355)]!==_0x5313a1(0x42d)&&(_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]='none',_0x2ddedf=!![])):(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x3e1)]=![],applyMuteState(_0x2991ad),_0x311669[_0x5313a1(0x16a)][_0x2991ad]['videoElement']&&(_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x49b)]['controlTimer']&&clearInterval(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x3b5)]),_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x9f4)]=![],_0x311669[_0x5313a1(0x802)]&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x3b5)]=setTimeout(showControlBar[_0x5313a1(0x5a3)](null,_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]),0x3e8)),_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]&&_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)]['style']['display']!=='block'&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x886)][_0x5313a1(0x355)]='block',_0x2ddedf=!![])),_0x311669[_0x5313a1(0x16a)][_0x2991ad]['iframeEle']&&_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)]['display']&&_0x311669['rpcs'][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]!==_0x5313a1(0xa87)&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x9b0)][_0x5313a1(0x886)][_0x5313a1(0x355)]=_0x5313a1(0xa87),_0x2ddedf=!![]));}}_0x311669['sceneSync'](_0x2991ad);}else _0x5986c5[_0x5313a1(0x95f)]==_0x5313a1(0x356)&&(log(parseInt(_0x5986c5[_0x5313a1(0x8d7)])/0x64),_0x311669['rpcs'][_0x2991ad]['videoElement']&&(_0x311669[_0x5313a1(0x16a)][_0x2991ad][_0x5313a1(0x49b)][_0x5313a1(0x356)]=parseInt(_0x5986c5[_0x5313a1(0x8d7)])/0x64,log(_0x5313a1(0x267))));}}}}_0x2ddedf&&updateMixer();}}else{if(_0x5986c5['action']=='migrate'){}else{if(_0x5986c5[_0x5313a1(0x95f)]=='hangup'){}}}}else _0x5986c5[_0x5313a1(0x95f)]===_0x5313a1(0x559)&&(warnlog(_0x5313a1(0x70e)),log(_0x5986c5),_0x311669[_0x5313a1(0x559)]=_0x5986c5[_0x5313a1(0x8d7)],pokeIframeAPI(_0x5313a1(0x276),_0x311669[_0x5313a1(0x559)]),updateMixer());}},_0x311669['newMainDirectorSetup']=function(){var _0x11687b=_0x134a17;log(_0x11687b(0x262)),_0x311669['directorUUID']in _0x311669[_0x11687b(0x859)]&&(_0x311669[_0x11687b(0x859)][_0x311669[_0x11687b(0x2f3)]][_0x11687b(0x436)]&&_0x311669[_0x11687b(0x859)][_0x311669['directorUUID']][_0x11687b(0x436)][_0x11687b(0x6ee)]&&(_0x311669[_0x11687b(0x859)][_0x311669[_0x11687b(0x2f3)]][_0x11687b(0x436)][_0x11687b(0x6ee)][_0x11687b(0x75b)]=!![])),_0x311669['directorUUID']in _0x311669[_0x11687b(0x16a)]&&(_0x311669[_0x11687b(0x16a)][_0x311669['directorUUID']]['stats']&&_0x311669['rpcs'][_0x311669[_0x11687b(0x2f3)]][_0x11687b(0x436)][_0x11687b(0x6ee)]&&(_0x311669[_0x11687b(0x16a)][_0x311669[_0x11687b(0x2f3)]][_0x11687b(0x436)]['info'][_0x11687b(0x75b)]=!![]),_0x311669[_0x11687b(0x75b)]&&(getById(_0x11687b(0x2f7)+_0x311669[_0x11687b(0x2f3)])[_0x11687b(0x424)]['add'](_0x11687b(0x770)),_0x311669['rpcs'][_0x311669[_0x11687b(0x2f3)]][_0x11687b(0x89c)]===![]&&miniTranslate(getById(_0x11687b(0x1c8)+_0x311669['directorUUID']),_0x11687b(0x330)))),_0x311669['requestCoDirector'](),updateUserList();},_0x311669[_0x134a17(0x857)]=async function _0x398e5b(_0x591448=![]){var _0x4b4643=_0x134a17;if(_0x311669[_0x4b4643(0x2f4)]===!![]){log(_0x4b4643(0x1cb));return;}if(_0x311669['ws']!==null){log('already\x20connected');return;}_0x311669[_0x4b4643(0x8f5)]==![]&&(_0x311669[_0x4b4643(0x39a)]!==![]?_0x311669[_0x4b4643(0x8f5)]=_0x4b4643(0x310):_0x311669[_0x4b4643(0x8f5)]=_0x4b4643(0x3fa));if(!RTCPeerConnection){console[_0x4b4643(0x4a4)](getTranslation(_0x4b4643(0x43a)));!_0x311669[_0x4b4643(0x78c)]&&warnUser(getTranslation(_0x4b4643(0x43a)),![],![]);return;}_0x311669['ws']===null&&(_0x311669['ws']=![],await chooseBestTURN());if(_0x311669[_0x4b4643(0x1d0)]===![]){_0x311669[_0x4b4643(0x7af)]=_0x311669[_0x4b4643(0xa76)](0xc);for(var _0x59c6b3 in _0x311669[_0x4b4643(0x16a)]){warnlog(_0x4b4643(0x554)),_0x311669['rpcs'][_0x59c6b3]['connectionState']===_0x4b4643(0x190)&&(warnlog(_0x4b4643(0x405)),_0x311669['closeRPC'](_0x59c6b3));}}_0x311669[_0x4b4643(0x197)]?(_0x311669['ws']={},_0x311669['ws']['readyState']=0x1,_0x311669['ws'][_0x4b4643(0x2b9)]=function(_0x2703f9){var _0x4aeb3f=_0x4b4643;parent[_0x4aeb3f(0x5ef)]({'bypass':_0x2703f9},_0x311669[_0x4aeb3f(0xa2e)]);},setTimeout(function(){var _0x395c3c=_0x4b4643;_0x311669['ws'][_0x395c3c(0x777)]();},0xa)):_0x311669['ws']=new WebSocket(_0x311669[_0x4b4643(0x8f5)]),_0x591448==![]&&(_0x311669[_0x4b4643(0x6f3)]===!![]&&(_0x311669[_0x4b4643(0x6f3)]=null,toggleClock()),_0x311669['timeout']=setTimeout(function(){var _0x16efe3=_0x4b4643;pokeIframeAPI(_0x16efe3(0x49c),_0x16efe3(0x94c)),pokeIframeAPI(_0x16efe3(0x77d),_0x16efe3(0x94c)),errorlog(_0x16efe3(0x390)),!_0x311669[_0x16efe3(0x78c)]&&(!_0x311669[_0x16efe3(0x7c5)]&&setTimeout(function(){var _0x38bf56=_0x16efe3;warnUser(getTranslation(_0x38bf56(0x922)),![],![]);},0x1));},0x7530)),_0x311669['ws'][_0x4b4643(0x777)]=function _0xc4b4f5(){var _0x3b9409=_0x4b4643;if(_0x311669['auth'])try{_0x311669[_0x3b9409(0x663)]({'auth':_0x311669[_0x3b9409(0x662)]});}catch(_0x834491){errorlog(_0x834491);}_0x311669[_0x3b9409(0x271)]=!![],clearTimeout(_0x311669[_0x3b9409(0x35c)]),clearTimeout(_0x311669['timeout']),log(_0x3b9409(0x6fd)),checkConnection();if(_0x311669[_0x3b9409(0x74b)]){errorlog(_0x3b9409(0x39e));for(_0x16cc22 in _0x311669[_0x3b9409(0x16a)]){try{_0x311669[_0x3b9409(0x16a)][_0x16cc22][_0x3b9409(0x9bb)]?!_0x311669['include'][_0x3b9409(0x2c2)](_0x311669[_0x3b9409(0x16a)][_0x16cc22][_0x3b9409(0x9bb)])&&_0x311669[_0x3b9409(0x7b8)](_0x16cc22):_0x311669[_0x3b9409(0x7b8)](_0x16cc22);}catch(_0x5a95aa){}}for(_0x16cc22 in _0x311669['pcs']){try{_0x311669[_0x3b9409(0x9d2)](_0x16cc22);}catch(_0x17fb30){}}_0x311669[_0x3b9409(0x74b)]=![],_0x311669[_0x3b9409(0x45b)]=![];}if(_0x311669[_0x3b9409(0x6e7)]!==[])try{var _0x3bb182=_0x311669[_0x3b9409(0x6e7)][_0x3b9409(0x7f5)](-0x1e);_0x311669[_0x3b9409(0x6e7)]=[];for(var _0x2f0b5e in _0x3bb182){log('resending\x20message'),_0x311669[_0x3b9409(0x663)](_0x3bb182[_0x2f0b5e]);}}catch(_0x2d036a){errorlog(_0x2d036a);}if(_0x591448==!![]){pokeIframeAPI(_0x3b9409(0x49c),_0x3b9409(0x24b)),pokeIframeAPI(_0x3b9409(0x77d),'reconnected');_0x311669[_0x3b9409(0x22b)]&&_0x311669[_0x3b9409(0x6df)]();if(_0x311669[_0x3b9409(0x4e1)]){log('ROOMID\x20EANBLED'),log('Update\x20Mixer\x20Event\x20on\x20REsize\x20SET'),joinRoom(_0x311669[_0x3b9409(0x4e1)]);if(_0x311669[_0x3b9409(0xa19)]['length']){var _0x43cf56=Object['keys'](_0x311669['waitingWatchList']);for(var _0x16cc22 in _0x43cf56){_0x311669[_0x3b9409(0xa19)][_0x3b9409(0x2c2)](_0x43cf56[_0x16cc22])&&(log(_0x3b9409(0x72a)+_0x43cf56[_0x16cc22]),_0x311669['watchStream'](_0x43cf56[_0x16cc22]));}}}else{var _0x43cf56=Object[_0x3b9409(0x19b)](_0x311669['waitingWatchList']);for(var _0x16cc22 in _0x43cf56){log(_0x3b9409(0x72a)+_0x43cf56[_0x16cc22]),_0x311669[_0x3b9409(0x5ad)](_0x43cf56[_0x16cc22]);}}}else pokeIframeAPI(_0x3b9409(0x49c),_0x3b9409(0x7ea)),pokeIframeAPI(_0x3b9409(0x77d),'connected');},_0x311669[_0x4b4643(0x79a)]=function(_0x423b84){var _0x3e96b9=_0x4b4643;for(var _0x493d8d in _0x311669[_0x3e96b9(0x16a)]){if(_0x311669[_0x3e96b9(0x16a)][_0x493d8d][_0x3e96b9(0x9bb)]===_0x423b84)return log(_0x3e96b9(0x91b)),![];}if(_0x311669[_0x3e96b9(0x8fb)][_0x423b84])return log('already\x20waiting\x20for\x20stream'),![];return _0x311669[_0x3e96b9(0x5ad)](_0x423b84),log('requesting\x20stream'),!![];},_0x311669['ws'][_0x4b4643(0x17a)]=async function(_0x35679c){var _0x1c9f0a=_0x4b4643;clearTimeout(_0x311669[_0x1c9f0a(0x35c)]);try{var _0x1eea7b=JSON[_0x1c9f0a(0x52d)](_0x35679c[_0x1c9f0a(0x3bc)]);}catch(_0x1ef619){try{var _0x1eea7b=JSON['parse'](_0x35679c[_0x1c9f0a(0x3bc)][_0x1c9f0a(0x721)]());}catch(_0x38357d){errorlog(_0x38357d);return;}}'streamID'in _0x1eea7b&&(_0x1eea7b[_0x1c9f0a(0x9bb)]=_0x311669['desaltStreamID'](_0x1eea7b[_0x1c9f0a(0x9bb)]));if(_0x1c9f0a(0x24d)in _0x1eea7b){_0x1eea7b=await _0x311669[_0x1c9f0a(0x643)](_0x1eea7b);if(!_0x1eea7b)return;}if(_0x311669[_0x1c9f0a(0x1d0)]){if(_0x1c9f0a(0x1dc)in _0x1eea7b&&_0x311669[_0x1c9f0a(0x54a)]&&_0x1eea7b[_0x1c9f0a(0x1dc)]===_0x311669['UUID'])return;else log(_0x1eea7b);if(_0x1c9f0a(0x54a)in _0x1eea7b){if(_0x311669['UUID']){if(_0x1eea7b[_0x1c9f0a(0x54a)]!==_0x311669['UUID'])return;}else return;delete _0x1eea7b[_0x1c9f0a(0x54a)];}if('roomid'in _0x1eea7b){if(!_0x311669[_0x1c9f0a(0x5cf)])return;if(_0x1c9f0a(0x727)in _0x1eea7b){if(_0x1eea7b[_0x1c9f0a(0x727)]===_0x1c9f0a(0x3fe)){if('roomid'in _0x1eea7b){if(_0x1c9f0a(0x81f)in _0x1eea7b){if(_0x1eea7b[_0x1c9f0a(0x81f)]==_0x311669['UUID']){_0x1eea7b[_0x1c9f0a(0x727)]=_0x1c9f0a(0x74b),_0x311669[_0x1c9f0a(0x5cf)]=_0x1eea7b[_0x1c9f0a(0x4e1)];var _0x4dabec={};_0x4dabec[_0x1c9f0a(0x727)]=_0x1c9f0a(0xa7a),_0x4dabec[_0x1c9f0a(0x4e1)]=_0x311669['roomenc'],_0x4dabec[_0x1c9f0a(0x9bb)]=_0x311669[_0x1c9f0a(0x9bb)],_0x311669[_0x1c9f0a(0x663)](_0x4dabec);}else return;}else return;}else return;}else{if(_0x1eea7b['roomid']!==_0x311669[_0x1c9f0a(0x5cf)])return;}}else{if(_0x1eea7b[_0x1c9f0a(0x4e1)]!==_0x311669[_0x1c9f0a(0x5cf)])return;}delete _0x1eea7b[_0x1c9f0a(0x4e1)];}if('director'in _0x1eea7b){if(_0x311669[_0x1c9f0a(0x9f7)]||_0x311669[_0x1c9f0a(0xa84)])await checkToken();else _0x1eea7b[_0x1c9f0a(0x1dc)]&&(_0x311669[_0x1c9f0a(0x2f3)]=_0x1eea7b[_0x1c9f0a(0x1dc)],_0x311669[_0x1c9f0a(0xa66)]=![],_0x311669[_0x1c9f0a(0x95b)]=[],_0x311669[_0x1c9f0a(0x95b)][_0x1c9f0a(0x505)](_0x311669[_0x1c9f0a(0x2f3)]),_0x311669[_0x1c9f0a(0x6ab)]());delete _0x1eea7b['director'];}'from'in _0x1eea7b&&(_0x1eea7b['UUID']=_0x1eea7b[_0x1c9f0a(0x1dc)],delete _0x1eea7b[_0x1c9f0a(0x1dc)]);if('request'in _0x1eea7b){if(_0x1eea7b['request']===_0x1c9f0a(0x6a8)){if(_0x1c9f0a(0x9bb)in _0x1eea7b){if(_0x1eea7b[_0x1c9f0a(0x9bb)]===_0x311669['streamID'])_0x1eea7b['request']='offerSDP';else return;}}else{if(_0x1eea7b[_0x1c9f0a(0x727)]===_0x1c9f0a(0x87b)){if(_0x311669[_0x1c9f0a(0x82b)]){if(_0x311669[_0x1c9f0a(0x82b)][_0x1c9f0a(0x2c2)](_0x1eea7b[_0x1c9f0a(0x9bb)])){play(_0x1eea7b['streamID']);return;}else return;}}else{if(_0x1eea7b[_0x1c9f0a(0x727)]===_0x1c9f0a(0xa7a)){if('streamID'in _0x1eea7b){if(_0x311669[_0x1c9f0a(0x82b)]){if(_0x311669[_0x1c9f0a(0x82b)][_0x1c9f0a(0x2c2)](_0x1eea7b[_0x1c9f0a(0x9bb)]))play(_0x1eea7b[_0x1c9f0a(0x9bb)]);else{}}else play(_0x1eea7b[_0x1c9f0a(0x9bb)]);}_0x1eea7b['request']='offerSDP';}}}}else{if(_0x1c9f0a(0x9bb)in _0x1eea7b){if(_0x311669[_0x1c9f0a(0x82b)]){if(_0x311669[_0x1c9f0a(0x82b)][_0x1c9f0a(0x2c2)](_0x1eea7b[_0x1c9f0a(0x9bb)])){}else return;}else{if(_0x311669[_0x1c9f0a(0x311)]){if(_0x311669['view']!==_0x1eea7b[_0x1c9f0a(0x9bb)])return;else{}}}}}}if(_0x1eea7b[_0x1c9f0a(0x727)]){if(_0x1eea7b['request']==_0x1c9f0a(0x49a)){if(_0x311669['queue']){if(_0x311669['directorList'][_0x1c9f0a(0x49e)](_0x1eea7b['UUID'])>=0x0)_0x311669[_0x1c9f0a(0x49a)](_0x1eea7b[_0x1c9f0a(0x54a)]);else _0x311669[_0x1c9f0a(0x75b)]&&(_0x1eea7b[_0x1c9f0a(0x54a)]in _0x311669[_0x1c9f0a(0x16a)]&&_0x311669[_0x1c9f0a(0x49a)](_0x1eea7b[_0x1c9f0a(0x54a)]));}else _0x311669[_0x1c9f0a(0x49a)](_0x1eea7b[_0x1c9f0a(0x54a)]);}else{if(_0x1eea7b[_0x1c9f0a(0x727)]==_0x1c9f0a(0xa53)){log(_0x1eea7b);if(_0x311669[_0x1c9f0a(0x9f7)]||_0x311669[_0x1c9f0a(0xa84)])await checkToken();else _0x1c9f0a(0x75b)in _0x1eea7b?(_0x311669[_0x1c9f0a(0x2f3)]=_0x1eea7b[_0x1c9f0a(0x75b)],_0x311669[_0x1c9f0a(0xa66)]=![],_0x311669['directorList']=[],_0x311669[_0x1c9f0a(0x95b)][_0x1c9f0a(0x505)](_0x311669[_0x1c9f0a(0x2f3)]),_0x311669[_0x1c9f0a(0x6ab)]()):(_0x311669['directorUUID']=![],_0x311669[_0x1c9f0a(0xa66)]=![],_0x311669[_0x1c9f0a(0x95b)]=[]);if(_0x311669[_0x1c9f0a(0xa84)]){}else{if(_0x1c9f0a(0x178)in _0x1eea7b){if(_0x311669[_0x1c9f0a(0x9f7)]||_0x1eea7b[_0x1c9f0a(0x178)]==![]){if(!_0x311669[_0x1c9f0a(0x78c)]){miniTranslate(getById('head4'),_0x1c9f0a(0x939));if(_0x311669[_0x1c9f0a(0x76b)])_0x311669['directorState']===null&&warnUser(getTranslation('room-is-claimed-codirector'),![],![]);else _0x311669[_0x1c9f0a(0x9f7)]?setTimeout(function(){warnUser(getTranslation('token-room-is-claimed'),![],![]);},0x1):setTimeout(function(){var _0x1f3262=_0x1c9f0a;warnUser(getTranslation(_0x1f3262(0x8ba)),![],![]);},0x1);}_0x311669['directorState']=![],pokeAPI(_0x1c9f0a(0x75b),![]),pokeIframeAPI(_0x1c9f0a(0x75b),![]);}else _0x311669[_0x1c9f0a(0x772)]=!![],pokeAPI(_0x1c9f0a(0x75b),!![]),pokeIframeAPI(_0x1c9f0a(0x75b),!![]);}}_0x311669['alreadyJoinedMembers']=_0x1eea7b['list'],_0x311669['listPromise'][_0x1c9f0a(0x8c5)](_0x1eea7b[_0x1c9f0a(0x93c)]);}else{if(_0x1eea7b['request']==_0x1c9f0a(0x74b)){_0x311669[_0x1c9f0a(0x983)]=[],_0x311669['transferred']=!![],_0x311669[_0x1c9f0a(0x45b)]=![],log(_0x1c9f0a(0x98e)),pokeIframeAPI(_0x1c9f0a(0x74b));let _0xf2718d=![];if(!_0x311669[_0x1c9f0a(0x75b)]){if(_0x311669[_0x1c9f0a(0x64f)]==0x2)_0x311669[_0x1c9f0a(0x64f)]=!![],_0x311669[_0x1c9f0a(0x74b)]=!![];else _0x311669['queue']==0x3?(_0x311669['queue']=![],_0xf2718d=!![]):(_0x311669[_0x1c9f0a(0x64f)]=![],_0x311669[_0x1c9f0a(0x74b)]=!![]);}else _0x311669[_0x1c9f0a(0x74b)]=!![];if(!_0xf2718d){for(_0x29af5b in _0x311669[_0x1c9f0a(0x16a)]){try{!_0x311669[_0x1c9f0a(0xa19)]['includes'](_0x311669['rpcs'][_0x29af5b][_0x1c9f0a(0x9bb)])&&(warnlog(_0x1c9f0a(0x3e7)),_0x311669[_0x1c9f0a(0x7b8)](_0x29af5b));}catch(_0x2359f4){}}for(_0x29af5b in _0x311669[_0x1c9f0a(0x859)]){try{log(_0x1c9f0a(0x193)),_0x311669[_0x1c9f0a(0x9d2)](_0x29af5b);}catch(_0x2e5477){}}}if(!_0xf2718d){if(_0x311669[_0x1c9f0a(0x9f7)]||_0x311669['mainDirectorPassword'])await checkToken();else'director'in _0x1eea7b?(_0x311669[_0x1c9f0a(0x2f3)]=_0x1eea7b[_0x1c9f0a(0x75b)],_0x311669[_0x1c9f0a(0xa66)]=![],_0x311669['directorList']=[],_0x311669[_0x1c9f0a(0x95b)][_0x1c9f0a(0x505)](_0x311669['directorUUID']),_0x311669[_0x1c9f0a(0x6ab)]()):(_0x311669[_0x1c9f0a(0x2f3)]=![],_0x311669[_0x1c9f0a(0xa66)]=![],_0x311669[_0x1c9f0a(0x95b)]=[]);youveBeenTransferred(),_0x311669['totalRoomBitrate']=_0x311669[_0x1c9f0a(0x6bc)],updateMixer();}else youveBeenActivated();log('Members\x20in\x20Room'),log(_0x1eea7b[_0x1c9f0a(0x93c)]);for(var _0x29af5b in _0x1eea7b[_0x1c9f0a(0x93c)]){if(_0x1c9f0a(0x54a)in _0x1eea7b['list'][_0x29af5b]){if(_0x1c9f0a(0x9bb)in _0x1eea7b[_0x1c9f0a(0x93c)][_0x29af5b]){if(_0x1eea7b['list'][_0x29af5b][_0x1c9f0a(0x54a)]in _0x311669[_0x1c9f0a(0x16a)])log('RTC\x20already\x20connected');else{var _0x1d98a0=_0x311669['desaltStreamID'](_0x1eea7b[_0x1c9f0a(0x93c)][_0x29af5b]['streamID']);log(_0x1c9f0a(0x669)+_0x1d98a0);if(_0x311669[_0x1c9f0a(0x64f)]){if(_0x311669['directorList'][_0x1c9f0a(0x49e)](_0x1eea7b['list'][_0x29af5b][_0x1c9f0a(0x54a)])>=0x0)play(_0x1d98a0,_0x1eea7b[_0x1c9f0a(0x93c)][_0x29af5b]['UUID']);else{if(_0x311669['view_set']&&_0x311669[_0x1c9f0a(0x82b)][_0x1c9f0a(0x2c2)](_0x1d98a0))play(_0x1d98a0,_0x1eea7b[_0x1c9f0a(0x93c)][_0x29af5b][_0x1c9f0a(0x54a)]);else _0x311669[_0x1c9f0a(0x983)][_0x1c9f0a(0x8e9)]<0x1388&&(!(_0x1d98a0 in _0x311669[_0x1c9f0a(0x241)])&&!_0x311669[_0x1c9f0a(0x983)][_0x1c9f0a(0x2c2)](_0x1d98a0)&&_0x311669['queueList']['push'](_0x1d98a0));}}else play(_0x1d98a0,_0x1eea7b[_0x1c9f0a(0x93c)][_0x29af5b][_0x1c9f0a(0x54a)]);}}}}updateQueue();}else{if(_0x1eea7b['request']==_0x1c9f0a(0x386)){log(_0x1eea7b);if(_0x311669[_0x1c9f0a(0x9f7)]||_0x311669[_0x1c9f0a(0xa84)])await checkToken();else'director'in _0x1eea7b?(_0x311669[_0x1c9f0a(0x2f3)]=_0x1eea7b[_0x1c9f0a(0x75b)],_0x311669[_0x1c9f0a(0xa66)]=![],_0x311669[_0x1c9f0a(0x95b)]=[],_0x311669['directorList'][_0x1c9f0a(0x505)](_0x311669['directorUUID']),_0x311669[_0x1c9f0a(0x6ab)]()):(_0x311669[_0x1c9f0a(0x2f3)]=![],_0x311669[_0x1c9f0a(0x95b)]=[],errorlog(_0x1c9f0a(0x573)));updateUserList();}else{if(_0x1eea7b['request']==_0x1c9f0a(0x7f2)){log(_0x1c9f0a(0x71d)),log(_0x1eea7b);try{if(_0x311669[_0x1c9f0a(0x9f7)]||_0x311669[_0x1c9f0a(0xa4f)]){}else'director'in _0x1eea7b&&(_0x1eea7b['director']==!![]&&_0x311669[_0x1c9f0a(0xa37)](_0x1eea7b));}catch(_0x510cdb){errorlog(_0x510cdb);}}else{if(_0x1eea7b['request']=='someonejoined'){if(_0x311669[_0x1c9f0a(0x9f7)]||_0x311669[_0x1c9f0a(0xa84)])await checkToken();else _0x1eea7b[_0x1c9f0a(0x75b)]&&(_0x311669[_0x1c9f0a(0x2f3)]=_0x1eea7b[_0x1c9f0a(0x54a)],_0x311669[_0x1c9f0a(0xa66)]=![],_0x311669['directorList']=[],_0x311669[_0x1c9f0a(0x95b)][_0x1c9f0a(0x505)](_0x311669['directorUUID']),_0x311669['newMainDirectorSetup']());if(_0x1c9f0a(0x9bb)in _0x1eea7b){log('Someone\x20Joined\x20the\x20Room\x20with\x20a\x20video');if(_0x311669['queue']){if(_0x311669['directorList']['indexOf'](_0x1eea7b[_0x1c9f0a(0x54a)])>=0x0)play(_0x1d98a0,_0x1eea7b[_0x1c9f0a(0x54a)]);else{if(_0x311669['view_set']&&_0x311669['view_set'][_0x1c9f0a(0x2c2)](_0x1d98a0))play(_0x1d98a0,_0x1eea7b['UUID']);else _0x311669[_0x1c9f0a(0x983)][_0x1c9f0a(0x8e9)]<0x1388&&(!(_0x1eea7b[_0x1c9f0a(0x9bb)]in _0x311669['watchTimeoutList'])&&!_0x311669['queueList'][_0x1c9f0a(0x2c2)](_0x1eea7b[_0x1c9f0a(0x9bb)])&&(_0x311669[_0x1c9f0a(0x983)][_0x1c9f0a(0x505)](_0x1eea7b[_0x1c9f0a(0x9bb)]),updateQueue(!![])));}}else play(_0x1eea7b['streamID']);}else log(_0x1c9f0a(0x994));}else{if(_0x1eea7b[_0x1c9f0a(0x727)]=='videoaddedtoroom'){log(_0x1c9f0a(0x7b6)),log(_0x1eea7b);if(_0x311669[_0x1c9f0a(0x64f)]){if(_0x311669[_0x1c9f0a(0x95b)][_0x1c9f0a(0x49e)](_0x1eea7b[_0x1c9f0a(0x54a)])>=0x0)play(_0x1d98a0,_0x1eea7b[_0x1c9f0a(0x54a)]);else{if(_0x311669[_0x1c9f0a(0x82b)]&&_0x311669[_0x1c9f0a(0x82b)][_0x1c9f0a(0x2c2)](_0x1d98a0))play(_0x1d98a0,_0x1eea7b[_0x1c9f0a(0x54a)]);else _0x311669[_0x1c9f0a(0x983)][_0x1c9f0a(0x8e9)]<0x1388&&(!(_0x1eea7b[_0x1c9f0a(0x9bb)]in _0x311669[_0x1c9f0a(0x241)])&&!_0x311669['queueList']['includes'](_0x1eea7b[_0x1c9f0a(0x9bb)])&&(_0x311669['queueList'][_0x1c9f0a(0x505)](_0x1eea7b[_0x1c9f0a(0x9bb)]),updateQueue(!![])));}}else play(_0x1eea7b[_0x1c9f0a(0x9bb)]);}else{if(_0x1eea7b[_0x1c9f0a(0x727)]=='alert'){errorlog(_0x1eea7b),pokeIframeAPI('alert',_0x1eea7b[_0x1c9f0a(0x6d8)]);if(_0x311669[_0x1c9f0a(0x98d)]===![]){if('message'in _0x1eea7b){if(_0x1eea7b[_0x1c9f0a(0x6d8)]===_0x1c9f0a(0x717))_0x311669[_0x1c9f0a(0x5db)]<0x2?(_0x311669[_0x1c9f0a(0x5db)]=parseInt(_0x311669[_0x1c9f0a(0x5db)])+0x1,setTimeout(function(){_0x311669['seedStream']();},0x1388)):(hangup(),!_0x311669[_0x1c9f0a(0x78c)]&&setTimeout(function(){var _0x5dbbf3=_0x1c9f0a;warnUser(getTranslation(_0x5dbbf3(0x3c7)),![],![]);},0x1));else{if(_0x311669[_0x1c9f0a(0x9f7)]||_0x311669[_0x1c9f0a(0xa4f)]){}else _0x1eea7b[_0x1c9f0a(0x6d8)]==='Room\x20is\x20already\x20claimed\x20by\x20someone\x20else.'?(!_0x311669['cleanOutput']&&(miniTranslate(getById(_0x1c9f0a(0x9d4)),_0x1c9f0a(0x939)),_0x311669[_0x1c9f0a(0x76b)]?_0x311669['directorState']===null&&warnUser(getTranslation('room-is-claimed-codirector'),![],![]):setTimeout(function(){warnUser(getTranslation('room-is-claimed'),![],![]);},0x1)),_0x311669[_0x1c9f0a(0x772)]=![],pokeAPI(_0x1c9f0a(0x75b),![]),pokeIframeAPI('director',![])):!_0x311669[_0x1c9f0a(0x78c)]&&setTimeout(function(){warnUser(_0x1eea7b['message']);},0x1);}}}}else _0x1eea7b[_0x1c9f0a(0x727)]==_0x1c9f0a(0x1ad)?_0x1c9f0a(0x6d8)in _0x1eea7b&&warnlog(_0x1eea7b[_0x1c9f0a(0x6d8)]):log(_0x1eea7b);}}}}}}}}else{if(_0x1eea7b['description'])_0x1c9f0a(0x9bb)in _0x1eea7b&&(_0x1eea7b[_0x1c9f0a(0x9bb)]in _0x311669[_0x1c9f0a(0x241)]&&(clearTimeout(_0x311669['watchTimeoutList'][_0x1eea7b[_0x1c9f0a(0x9bb)]]),delete _0x311669[_0x1c9f0a(0x241)][_0x1eea7b['streamID']])),_0x311669[_0x1c9f0a(0x6d1)](_0x1eea7b);else{if(_0x1eea7b['candidate'])log(_0x1c9f0a(0x8e2)),_0x311669[_0x1c9f0a(0x1d3)](_0x1eea7b);else{if(_0x1eea7b[_0x1c9f0a(0x2a8)])log(_0x1c9f0a(0x61b)),_0x311669[_0x1c9f0a(0x411)](_0x1eea7b);else _0x1eea7b[_0x1c9f0a(0xa83)]||_0x1eea7b[_0x1c9f0a(0x727)]==_0x1c9f0a(0x546)?(warnlog('Clean\x20up'),_0x1eea7b[_0x1c9f0a(0x54a)]in _0x311669[_0x1c9f0a(0x859)]&&(warnlog('problem'),log(_0x1c9f0a(0x193)),_0x311669[_0x1c9f0a(0x9d2)](_0x1eea7b['UUID'])),_0x1eea7b['UUID']in _0x311669[_0x1c9f0a(0x16a)]&&(warnlog('problem'),_0x311669['closeRPC'](_0x1eea7b[_0x1c9f0a(0x54a)]))):log(_0x1c9f0a(0x1e5));}}}},_0x311669['ws'][_0x4b4643(0x88c)]=async function(_0x5dfda9){warnlog(_0x5dfda9);},_0x311669['ws'][_0x4b4643(0x661)]=async function(_0xbcf61f){var _0x4b5bf1=_0x4b4643;clearTimeout(_0x311669[_0x4b5bf1(0x35c)]),pokeIframeAPI('hssConnection',_0x4b5bf1(0x208)),pokeIframeAPI(_0x4b5bf1(0x77d),_0x4b5bf1(0x208));try{_0x4b5bf1(0x7d2)in _0xbcf61f&&(_0xbcf61f[_0x4b5bf1(0x7d2)]==0x1f7&&(_0x591448==![]&&(clearTimeout(_0x311669['timeout']),!_0x311669['cleanOutput']&&warnUser(_0x4b5bf1(0xa67),0x7530,![]))));}catch(_0x4f597e){errorlog(_0x4f597e);}warnlog(_0x4b5bf1(0x8d9));if(_0x311669[_0x4b5bf1(0x4e5)]==![])try{_0x311669['ws'][_0x4b5bf1(0x8ee)]===WebSocket[_0x4b5bf1(0x2a9)]&&(_0x311669['ws']=null,setTimeout(()=>{var _0x17c3c8=_0x4b5bf1;try{_0x311669[_0x17c3c8(0x857)](!![]);}catch(_0x28b9a7){};},0x7d0));}catch(_0x14e4bb){errorlog(_0x14e4bb);}};},_0x311669[_0x134a17(0x8a1)]=function(_0x4affdc,_0x325a10=null){var _0x58fc32=_0x134a17;log(_0x58fc32(0x4e9)),warnlog(_0x4affdc),_0x4affdc=JSON[_0x58fc32(0x479)](_0x4affdc);if(_0x325a10==null){for(var _0x2e03c9 in _0x311669[_0x58fc32(0x859)]){try{_0x311669[_0x58fc32(0x859)][_0x2e03c9][_0x58fc32(0x73a)]['send'](_0x4affdc);}catch(_0x13ec4b){warnlog('RTC\x20Connection\x20seems\x20to\x20be\x20dead\x20or\x20not\x20yet\x20open?\x204');}}return!![];}else try{return _0x311669['pcs'][_0x325a10][_0x58fc32(0x73a)]['send'](_0x4affdc),!![];}catch(_0x1ac55d){return warnlog('RTC\x20Connection\x20seems\x20to\x20be\x20dead\x20or\x20not\x20yet\x20open?\x203'),warnlog(_0x4affdc),![];}return![];};var _0x34fdf7={};function _0x506aab(_0x34a77a){var _0x4dafd9=_0x134a17;try{var _0x381e58=_0x34fdf7[_0x34a77a]||![];_0x381e58&&(clearTimeout(_0x381e58[0x1]),delete _0x34fdf7[_0x34a77a],warnlog(_0x4dafd9(0x8cc)+_0x34a77a),_0x381e58[0x0]());}catch(_0x33efe4){errorlog(_0x33efe4);}}_0x311669[_0x134a17(0xa14)]=function(_0x38ad89,_0x1f7407=null,_0x589a81=![]){var _0x153dc5=_0x134a17;if(_0x1f7407==null){var _0x34aa75=[],_0x3ec696=JSON[_0x153dc5(0x479)](_0x38ad89);for(var _0x791d46 in _0x311669['rpcs']){if(_0x311669[_0x153dc5(0x16a)][_0x791d46][_0x153dc5(0x751)]){warnlog(_0x38ad89);continue;}try{if('realUUID'in _0x311669[_0x153dc5(0x16a)][_0x791d46]){var _0x2aa5aa=_0x38ad89;_0x2aa5aa[_0x153dc5(0x67b)]=!![],_0x2aa5aa=JSON[_0x153dc5(0x479)](_0x2aa5aa),_0x311669['rpcs'][_0x311669[_0x153dc5(0x16a)][_0x791d46][_0x153dc5(0x65b)]][_0x153dc5(0x4a7)]['send'](_0x2aa5aa);}else _0x311669[_0x153dc5(0x16a)][_0x791d46][_0x153dc5(0x4a7)][_0x153dc5(0x2b9)](_0x3ec696);_0x34aa75['push'](_0x791d46);}catch(_0x3bdf96){log(_0x38ad89),warnlog(_0x153dc5(0x180)),warnlog(_0x3bdf96);}}return _0x34aa75[_0x153dc5(0x8e9)];}else{if(_0x311669[_0x153dc5(0x16a)][_0x1f7407][_0x153dc5(0x751)]){warnlog(_0x38ad89);return;}try{if(_0x589a81)try{var _0xa2c72a=parseInt(Math['random']()*0x174876e7ff);_0x38ad89[_0x153dc5(0x4fb)]=_0xa2c72a;var _0x3bd689=setTimeout(function(_0x3b7285){var _0x502405=_0x34fdf7[_0x3b7285]||![];_0x502405&&(delete _0x34fdf7[_0x3b7285],_0x502405[0x0]());},0x1388,_0xa2c72a);_0x34fdf7[_0xa2c72a]=[_0x589a81,_0x3bd689],warnlog(_0x153dc5(0xa80)+_0xa2c72a);}catch(_0x183c4b){errorlog(_0x183c4b);}if(_0x153dc5(0x65b)in _0x311669[_0x153dc5(0x16a)][_0x1f7407]){var _0x2aa5aa=_0x38ad89;_0x2aa5aa['altUUID']=!![],_0x311669['rpcs'][_0x311669[_0x153dc5(0x16a)][_0x1f7407][_0x153dc5(0x65b)]]['receiveChannel'][_0x153dc5(0x2b9)](JSON[_0x153dc5(0x479)](_0x2aa5aa));}else _0x311669[_0x153dc5(0x16a)][_0x1f7407]['receiveChannel'][_0x153dc5(0x2b9)](JSON[_0x153dc5(0x479)](_0x38ad89));return!![];}catch(_0x47b0a8){return warnlog(_0x47b0a8),log('PUBLISHER\x27s\x20RTC\x20Connection\x20seems\x20to\x20be\x20dead?\x202'),![];}}},_0x311669[_0x134a17(0x813)]=function(_0x4c6b6a=![],_0x32c597=![]){var _0x56267a=_0x134a17;try{window['removeEventListener'](_0x56267a(0x99f),confirmUnload);}catch(_0x31f50d){}_0x32c597&&recordLocalVideo(_0x56267a(0x7f8));_0x311669['taintedSession']=!![],warnlog(_0x56267a(0x92c));try{recordLocalVideo(_0x56267a(0x86e));}catch(_0x1e2f59){}try{var _0x54242f={};_0x54242f[_0x56267a(0x8ab)]=!![],_0x54242f['bye']=!![],_0x311669[_0x56267a(0x8a1)](_0x54242f);}catch(_0x3357e3){}try{_0x311669['ws'][_0x56267a(0x1cf)]();}catch(_0x30ad21){}try{transferList[_0x56267a(0x982)](_0x2a84f0=>{var _0x4380d7=_0x56267a;_0x2a84f0[_0x4380d7(0x88f)]&&_0x2a84f0[_0x4380d7(0x88f)][_0x4380d7(0x1cf)](),_0x2a84f0[_0x4380d7(0x203)]&&_0x2a84f0[_0x4380d7(0x6da)];});}catch(_0x7d0450){errorlog(_0x7d0450);}try{_0x311669['canvasSource']&&_0x311669[_0x56267a(0x17b)][_0x56267a(0x5c4)]&&_0x311669[_0x56267a(0x17b)]['srcObject']['getTracks']()[_0x56267a(0x982)](function(_0x53766c){var _0x1fecc9=_0x56267a;_0x311669[_0x1fecc9(0x17b)]['srcObject']['removeTrack'](_0x53766c),_0x53766c[_0x1fecc9(0x86e)](),log(_0x1fecc9(0x93d));}),_0x311669['videoElement']&&_0x311669[_0x56267a(0x49b)][_0x56267a(0x5c4)]&&_0x311669['videoElement'][_0x56267a(0x5c4)][_0x56267a(0x951)]()['forEach'](function(_0x384e52){var _0x112be2=_0x56267a;_0x311669[_0x112be2(0x49b)][_0x112be2(0x5c4)]['removeTrack'](_0x384e52),_0x384e52['stop'](),log('stopping\x20old\x20track');}),_0x311669[_0x56267a(0x585)]&&_0x311669[_0x56267a(0x585)][_0x56267a(0x951)]()['forEach'](function(_0x4df8bf){var _0x975cde=_0x56267a;_0x311669[_0x975cde(0x585)]['removeTrack'](_0x4df8bf),_0x4df8bf['stop'](),log('stopping\x20old\x20track');}),_0x311669[_0x56267a(0x7d7)]&&_0x311669[_0x56267a(0x7d7)]['getTracks']()[_0x56267a(0x982)](function(_0x2fa901){var _0x22ed11=_0x56267a;_0x311669[_0x22ed11(0x7d7)][_0x22ed11(0x59b)](_0x2fa901),_0x2fa901[_0x22ed11(0x86e)](),log('stopping\x20old\x20track');}),_0x311669['screenStream']&&_0x311669[_0x56267a(0x7a0)][_0x56267a(0x951)]()[_0x56267a(0x982)](function(_0x5c8c56){var _0xa3b817=_0x56267a;_0x311669[_0xa3b817(0x7a0)][_0xa3b817(0x59b)](_0x5c8c56),_0x5c8c56['stop'](),log(_0xa3b817(0x93d));});}catch(_0xd318da){errorlog(_0xd318da);}try{for(i in _0x311669[_0x56267a(0x16a)]){try{_0x311669[_0x56267a(0x16a)][i][_0x56267a(0x49b)]&&(_0x311669[_0x56267a(0x16a)][i]['videoElement'][_0x56267a(0x2df)]&&recordLocalVideo(_0x56267a(0x86e),null,_0x311669[_0x56267a(0x16a)][i][_0x56267a(0x49b)]));}catch(_0x40b971){}log(_0x56267a(0x790)),_0x311669[_0x56267a(0x7b8)](i,!![]);}for(i in _0x311669[_0x56267a(0x859)]){log(_0x56267a(0x523)),_0x311669[_0x56267a(0x9d2)](i);}}catch(_0x4a55b2){errorlog(_0x4a55b2);}for(var _0x562510 in _0x311669[_0x56267a(0x241)]){clearTimeout(_0x311669['watchTimeoutList'][_0x562510]);}if(_0x4c6b6a){reloadRequested(),warnlog('Reloading?\x20uh\x20oh.\x20Why\x20didn\x27t\x20it?');return;}else setTimeout(function(){for(i in _0x311669){try{delete _0x311669[i];}catch(_0x27badb){}}delete _0x311669;},0x4b0),hangupComplete(),log(_0x56267a(0x4a1));},_0x311669['hangupDirector']=function(){var _0x17baa4=_0x134a17;_0x311669['taintedSession']=!![],_0x311669[_0x17baa4(0x701)]=![],pokeIframeAPI(_0x17baa4(0x728),_0x311669[_0x17baa4(0x701)],null,_0x311669[_0x17baa4(0x9bb)]),notifyOfScreenShare(),warnlog('hanging\x20up'),pokeIframeAPI(_0x17baa4(0x59a),![],![],_0x311669[_0x17baa4(0x9bb)]),pokeIframeAPI(_0x17baa4(0x22b),![],![],_0x311669['streamID']),pokeAPI(_0x17baa4(0x22b),![]);try{_0x311669['videoElement']&&_0x311669[_0x17baa4(0x49b)][_0x17baa4(0x5c4)]&&_0x311669['videoElement'][_0x17baa4(0x5c4)][_0x17baa4(0x951)]()[_0x17baa4(0x982)](function(_0x1f9ed3){var _0xc8270b=_0x17baa4;_0x311669['videoElement']['srcObject'][_0xc8270b(0x59b)](_0x1f9ed3),_0x1f9ed3[_0xc8270b(0x86e)](),log('stopping\x20old\x20track');});_0x311669[_0x17baa4(0x585)]&&(_0x311669[_0x17baa4(0x585)][_0x17baa4(0x87a)]()[_0x17baa4(0x982)](function(_0x59c420){var _0x1a663f=_0x17baa4;_0x311669['videoDevice']=_0x59c420[_0x1a663f(0x89c)][_0x1a663f(0x62c)]()[_0x1a663f(0x2f9)](/[\W]+/g,'_'),_0x311669[_0x1a663f(0x585)]['removeTrack'](_0x59c420),_0x59c420[_0x1a663f(0x86e)](),log('stopping\x20old\x20track');}),_0x311669[_0x17baa4(0x213)]=[],_0x311669[_0x17baa4(0x585)][_0x17baa4(0xa6c)]()[_0x17baa4(0x982)](function(_0x2dbf18){var _0x443a24=_0x17baa4;_0x311669['audioDevice'][_0x443a24(0x505)](_0x2dbf18[_0x443a24(0x89c)][_0x443a24(0x62c)]()[_0x443a24(0x2f9)](/[\W]+/g,'_')),_0x311669['streamSrc']['removeTrack'](_0x2dbf18),_0x2dbf18['stop'](),log('stopping\x20old\x20track');}),!_0x311669[_0x17baa4(0x213)]['length']&&(_0x311669[_0x17baa4(0x213)]=![]));_0x311669[_0x17baa4(0x7d7)]&&_0x311669[_0x17baa4(0x7d7)][_0x17baa4(0x951)]()[_0x17baa4(0x982)](function(_0x36d5de){var _0x8f51ff=_0x17baa4;_0x311669['streamSrcClone'][_0x8f51ff(0x59b)](_0x36d5de),_0x36d5de[_0x8f51ff(0x86e)]();});for(UUID in _0x311669[_0x17baa4(0x859)]){var _0x530f70=getSenders2(UUID);_0x530f70[_0x17baa4(0x982)](_0x2a0edc=>{var _0x50469b=_0x17baa4;_0x2a0edc[_0x50469b(0x53f)]&&(_0x2a0edc[_0x50469b(0x53f)]['enabled']=![]);});}try{document[_0x17baa4(0x5c8)](_0x17baa4(0x2f1))&&(!_0x311669[_0x17baa4(0x78e)]&&(_0x311669[_0x17baa4(0x78e)]={}),_0x311669[_0x17baa4(0x9bb)]&&(_0x311669[_0x17baa4(0x78e)][_0x311669[_0x17baa4(0x9bb)]]=getDetailedState(_0x311669['streamID'])),getById(_0x17baa4(0x2f1))[_0x17baa4(0x826)]['removeChild'](getById('container_director')),updateLockedElements());}catch(_0x21b70c){warnlog(_0x21b70c);}var _0x4b4bc8={};_0x4b4bc8['videoMuted']=!![],_0x4b4bc8[_0x17baa4(0xa39)]=!![],_0x311669[_0x17baa4(0x8a1)](_0x4b4bc8),getById(_0x17baa4(0x67e))[_0x17baa4(0x761)]();}catch(_0x54061d){errorlog(_0x17baa4(0x5ce));}log(_0x17baa4(0x7bb));},_0x311669[_0x134a17(0x903)]=function(_0x3ae371,_0x54bd25=![]){var _0x5da327=_0x134a17;_0x311669[_0x5da327(0x859)][_0x3ae371][_0x5da327(0x903)]({'iceRestart':_0x54bd25})[_0x5da327(0x619)](_0x55d817=>{var _0x5b426e=_0x5da327;log('create\x20offer\x20worked');if(SafariVersion&&SafariVersion<=0xd&&(iOS||iPad)){}else{if(_0x311669[_0x5b426e(0x5a8)]==0x3||_0x311669[_0x5b426e(0x5a8)]==0x5||_0x311669[_0x5b426e(0x5a8)]==0x1)_0x55d817[_0x5b426e(0x648)]=CodecsHandler[_0x5b426e(0x608)](_0x55d817[_0x5b426e(0x648)],{'stereo':0x1}),log(_0x5b426e(0x206));else{if(iOS||iPad){}else _0x311669['stereo']==0x4&&(_0x55d817[_0x5b426e(0x648)]=CodecsHandler[_0x5b426e(0x608)](_0x55d817[_0x5b426e(0x648)],{'stereo':0x2}),log(_0x5b426e(0x206)));}}(iOS||iPad)&&(_0x311669[_0x5b426e(0x971)]&&_0x55d817['sdp']['includes'](_0x5b426e(0x168))&&(_0x55d817[_0x5b426e(0x648)]=_0x55d817[_0x5b426e(0x648)]['replace'](_0x5b426e(0x168),'')));if(_0x311669[_0x5b426e(0x859)][_0x3ae371][_0x5b426e(0x3aa)])try{_0x55d817[_0x5b426e(0x648)]=CodecsHandler[_0x5b426e(0x37c)](_0x55d817['sdp'],_0x311669['pcs'][_0x3ae371][_0x5b426e(0x3aa)]),log(_0x5b426e(0x5fe)+_0x311669['pcs'][_0x3ae371]['preferVideoCodec']+_0x5b426e(0x508));}catch(_0x4240ec){errorlog(_0x4240ec),warnlog('couldn\x27t\x20set\x20preferred\x20video\x20codec');}if(_0x311669[_0x5b426e(0x859)][_0x3ae371][_0x5b426e(0x5ea)])try{if(_0x311669[_0x5b426e(0x859)][_0x3ae371][_0x5b426e(0x5ea)]===_0x5b426e(0x61d))_0x55d817[_0x5b426e(0x648)]=CodecsHandler[_0x5b426e(0x794)](_0x55d817[_0x5b426e(0x648)]);else{if(_0x311669['pcs'][_0x3ae371]['preferAudioCodec']===_0x5b426e(0x944)){if(_0x311669[_0x5b426e(0x8a9)]&&_0x311669[_0x5b426e(0x8a9)]==0x1)_0x55d817[_0x5b426e(0x648)]=CodecsHandler[_0x5b426e(0x230)](_0x55d817[_0x5b426e(0x648)],_0x311669[_0x5b426e(0x4b7)]||0xbb80,![]);else _0x311669['stereo']?_0x55d817[_0x5b426e(0x648)]=CodecsHandler[_0x5b426e(0x230)](_0x55d817[_0x5b426e(0x648)],_0x311669['micSampleRate']||0xbb80,!![]):_0x55d817[_0x5b426e(0x648)]=CodecsHandler[_0x5b426e(0x230)](_0x55d817[_0x5b426e(0x648)],_0x311669['micSampleRate']||0xbb80,![]);}else _0x55d817[_0x5b426e(0x648)]=CodecsHandler['preferAudioCodec'](_0x55d817[_0x5b426e(0x648)],_0x311669[_0x5b426e(0x859)][_0x3ae371][_0x5b426e(0x5ea)]);}log(_0x5b426e(0x5fe)+_0x311669['pcs'][_0x3ae371][_0x5b426e(0x5ea)]+_0x5b426e(0x2ee));}catch(_0x382a5f){errorlog(_0x382a5f),warnlog(_0x5b426e(0x57a));}Android&&_0x311669[_0x5b426e(0x6d0)]!==![]&&_0x311669[_0x5b426e(0x25f)]&&(_0x55d817[_0x5b426e(0x648)]=_0x55d817[_0x5b426e(0x648)][_0x5b426e(0x2f9)](/42e01f/gi,_0x5b426e(0x49f))),_0x311669['pcs'][_0x3ae371][_0x5b426e(0xa1b)](_0x55d817)['then'](function(){var _0x19a6fc=_0x5b426e;log(_0x19a6fc(0x9ae)+_0x3ae371),_0x311669['applyIsolatedChat'](_0x3ae371);var _0x173e20={};_0x173e20['UUID']=_0x3ae371,_0x173e20['streamID']=_0x311669[_0x19a6fc(0x9bb)],_0x173e20[_0x19a6fc(0x9f5)]=_0x311669[_0x19a6fc(0x859)][_0x3ae371][_0x19a6fc(0x1f3)],_0x173e20[_0x19a6fc(0x1fd)]=_0x311669['pcs'][_0x3ae371][_0x19a6fc(0x1fd)];_0x311669[_0x19a6fc(0x1d0)]&&(_0x173e20['isScene']=_0x311669[_0x19a6fc(0x98d)]);_0x311669[_0x19a6fc(0x97a)]!==![]&&(_0x173e20['slot']=_0x311669[_0x19a6fc(0x97a)]);if(_0x311669[_0x19a6fc(0x7a0)]!==![]){var _0x59d627=_0x311669['screenStream'][_0x19a6fc(0x951)](),_0x3d0d03=_0x311669[_0x19a6fc(0x859)][_0x3ae371][_0x19a6fc(0x6cc)](),_0x7eff8c=[];for(var _0x501442=0x0;_0x501442<_0x3d0d03['length'];_0x501442++){for(var _0x244e32=0x0;_0x244e32<_0x59d627[_0x19a6fc(0x8e9)];_0x244e32++){_0x3d0d03[_0x501442]['track']&&_0x3d0d03[_0x501442][_0x19a6fc(0x53f)]['id']==_0x59d627[_0x244e32]['id']&&_0x3d0d03[_0x501442]['track'][_0x19a6fc(0x7ad)]==_0x59d627[_0x244e32][_0x19a6fc(0x7ad)]&&_0x7eff8c['push'](_0x501442);}}_0x7eff8c[_0x19a6fc(0x8e9)]&&(_0x173e20[_0x19a6fc(0x43e)]=_0x7eff8c);}_0x311669['password']?_0x311669[_0x19a6fc(0x331)](JSON[_0x19a6fc(0x479)](_0x173e20[_0x19a6fc(0x9f5)]))[_0x19a6fc(0x619)](function(_0x35b91a){var _0x15bc8e=_0x19a6fc;_0x173e20[_0x15bc8e(0x9f5)]=_0x35b91a[0x0],_0x173e20[_0x15bc8e(0x9ed)]=_0x35b91a[0x1],_0x311669[_0x15bc8e(0x966)](_0x173e20);})[_0x19a6fc(0x3b6)](errorlog):_0x311669[_0x19a6fc(0x966)](_0x173e20);})[_0x5b426e(0x3b6)](errorlog);})[_0x5da327(0x3b6)](errorlog);},_0x311669['sendKeyFrameScenes']=function(){var _0x29b9f7=_0x134a17;for(var _0x7550d1 in _0x311669[_0x29b9f7(0x859)]){_0x311669[_0x29b9f7(0x859)][_0x7550d1][_0x29b9f7(0x98d)]!==![]?(_0x311669[_0x29b9f7(0x2e3)](_0x7550d1),log('FORCE\x20KEYFRAME\x20FOR\x20SCENE')):log(_0x29b9f7(0x862));}},_0x311669[_0x134a17(0x9d2)]=function(_0x1aa03c,_0x5e5cb4=!![]){var _0x4399ce=_0x134a17;log(_0x4399ce(0x9d2));if(!(_0x1aa03c in _0x311669[_0x4399ce(0x859)]))return;clearTimeout(_0x311669['pcs'][_0x1aa03c][_0x4399ce(0x44b)]),clearTimeout(_0x311669['pcs'][_0x1aa03c][_0x4399ce(0x9a4)]),clearInterval(_0x311669['pcs'][_0x1aa03c][_0x4399ce(0x927)]),pokeIframeAPI(_0x4399ce(0x397),![],_0x1aa03c);if(_0x4399ce(0x65b)in _0x311669[_0x4399ce(0x859)][_0x1aa03c]){delete _0x311669[_0x4399ce(0x859)][_0x1aa03c],applySceneState();return;}_0x1aa03c+_0x4399ce(0x33b)in _0x311669[_0x4399ce(0x859)]&&_0x311669[_0x4399ce(0x859)][_0x1aa03c+'_screen']['realUUID']&&_0x311669[_0x4399ce(0x859)][_0x1aa03c+_0x4399ce(0x33b)][_0x4399ce(0x65b)]===_0x1aa03c&&(clearTimeout(_0x311669[_0x4399ce(0x859)][_0x1aa03c+'_screen'][_0x4399ce(0x44b)]),clearTimeout(_0x311669[_0x4399ce(0x859)][_0x1aa03c+_0x4399ce(0x33b)][_0x4399ce(0x9a4)]),clearInterval(_0x311669[_0x4399ce(0x859)][_0x1aa03c+_0x4399ce(0x33b)]['requestedStatsInterval']),_0x311669[_0x4399ce(0x859)][_0x1aa03c+_0x4399ce(0x33b)]=null,delete _0x311669[_0x4399ce(0x859)][_0x1aa03c+_0x4399ce(0x33b)]);try{_0x311669[_0x4399ce(0x8a1)]({'bye':!![]},_0x1aa03c);}catch(_0x4b410b){}try{_0x311669[_0x4399ce(0x859)][_0x1aa03c][_0x4399ce(0x1cf)]();}catch(_0x5ef688){}_0x311669['pcs'][_0x1aa03c][_0x4399ce(0x798)]&&(_0x311669[_0x4399ce(0x3df)]&&(_0x5e5cb4&&(warnlog(_0x4399ce(0x556)),playtone(![],_0x4399ce(0x9c2))))),_0x311669[_0x4399ce(0x859)][_0x1aa03c]=null,_0x311669[_0x4399ce(0x4e5)]&&(!_0x311669[_0x4399ce(0x78c)]&&setTimeout(function _0x137844(){warnUser('Remote\x20peer\x20disconnected.\x20Due\x20to\x20enhanced\x20security,\x20please\x20refresh\x20to\x20create\x20a\x20new\x20connection.');},0x1)),delete _0x311669[_0x4399ce(0x859)][_0x1aa03c],_0x311669[_0x4399ce(0x1f6)](),applySceneState();},_0x311669[_0x134a17(0x7b8)]=function(_0xb141ee,_0x400b0c=![]){var _0x345cf4=_0x134a17;if(!(_0xb141ee in _0x311669['rpcs'])){log(_0x345cf4(0x8c0));return;}warnlog(_0x345cf4(0x7b8)),clearInterval(_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x9a4)]);try{_0x311669[_0x345cf4(0xa14)]({'bye':!![]},_0xb141ee),warnlog(_0x345cf4(0x3d1));}catch(_0x14a4f8){}try{var _0x2d8da6=_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x9bb)];}catch(_0x3e101c){}try{_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x1cf)]();}catch(_0x2cea8c){warnlog(_0x345cf4(0x8a7));}_0x311669[_0x345cf4(0x16a)][_0xb141ee]['motionDetectionInterval']&&clearInterval(_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x665)]);try{_0x311669['rpcs'][_0xb141ee]['streamSrc']&&_0x311669[_0x345cf4(0x16a)][_0xb141ee]['streamSrc'][_0x345cf4(0x951)]()[_0x345cf4(0x982)](function(_0x59a98f){var _0x41b355=_0x345cf4;_0x59a98f[_0x41b355(0x86e)](),log(_0x41b355(0xa89));});}catch(_0x228ca5){}if(_0x311669[_0x345cf4(0x75b)])try{_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x49b)]&&_0x345cf4(0x90b)in _0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x49b)]&&_0x311669[_0x345cf4(0x16a)][_0xb141ee]['videoElement'][_0x345cf4(0x90b)]['stop']();}catch(_0x187611){warnlog(_0x187611);}else!_0x311669['roomid']&&(_0x311669[_0x345cf4(0x3df)]&&playtone(![],'leavetone'));try{document[_0x345cf4(0x5c8)](_0x345cf4(0x2f7)+_0xb141ee)&&(!_0x311669[_0x345cf4(0x78e)]&&(_0x311669[_0x345cf4(0x78e)]={}),_0x2d8da6&&(_0x311669[_0x345cf4(0x78e)][_0x2d8da6]=getDetailedState(_0x2d8da6)),getById(_0x345cf4(0x2f7)+_0xb141ee)[_0x345cf4(0x826)][_0x345cf4(0x9d0)](getById(_0x345cf4(0x2f7)+_0xb141ee)),updateLockedElements());}catch(_0x105a23){warnlog(_0x105a23);}try{_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x49b)]&&_0x311669['rpcs'][_0xb141ee][_0x345cf4(0x49b)][_0x345cf4(0x761)]();}catch(_0x4f3230){}try{if(_0x311669[_0x345cf4(0x254)]!==![]){if(_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x9b0)]){try{_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x9b0)]['remove']();}catch(_0x1223da){errorlog(_0x1223da);}_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x9b0)][_0x345cf4(0x761)]();}}}catch(_0x3293ae){}try{_0x311669[_0x345cf4(0x16a)][_0xb141ee]['canvas']&&_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x4e0)][_0x345cf4(0x761)]();}catch(_0x5844b9){}try{_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x678)]&&_0x311669['rpcs'][_0xb141ee][_0x345cf4(0x678)][_0x345cf4(0x761)]();}catch(_0x391b8d){}_0x345cf4(0xa4a)in _0x311669[_0x345cf4(0x16a)][_0xb141ee]&&clearInterval(_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0xa4a)]);pokeIframeAPI(_0x345cf4(0x759),![],_0xb141ee),pokeAPI(_0x345cf4(0x775),_0x311669[_0x345cf4(0x16a)][_0xb141ee][_0x345cf4(0x9bb)]);_0x311669['rpcs'][_0xb141ee]['whip']&&(_0x2d8da6=![]);try{_0x311669['rpcs'][_0xb141ee]=null,delete _0x311669[_0x345cf4(0x16a)][_0xb141ee];}catch(_0x3ebcc8){}try{_0x311669[_0x345cf4(0x7b8)](_0xb141ee+_0x345cf4(0x33b));}catch(_0x336c15){}(!_0x311669[_0x345cf4(0x75b)]||_0x311669['switchMode'])&&setTimeout(function(){updateMixer();},0x1);if(typeof _0x2d8da6==_0x345cf4(0x31b))return;try{warnlog('Should\x20we\x20ask\x20to\x20play\x20the\x20stream\x20Again?'),_0x2d8da6&&(_0x2d8da6 in _0x311669['watchTimeoutList']&&(log('watchTimeoutList:'+_0x2d8da6),clearTimeout(_0x311669[_0x345cf4(0x241)][_0x2d8da6]),delete _0x311669['watchTimeoutList'][_0x2d8da6]),_0x311669[_0x345cf4(0x241)][_0x2d8da6]=setTimeout(function(_0x439499){var _0x50871b=_0x345cf4;try{delete _0x311669[_0x50871b(0x241)][_0x439499];}catch(_0x21b420){warnlog(_0x50871b(0x9d5));return;}log(_0x50871b(0x786)+_0x439499);try{for(var _0x5b1f1f in _0x311669[_0x50871b(0x16a)]){if(_0x311669[_0x50871b(0x16a)][_0x5b1f1f][_0x50871b(0x9bb)]===_0x439499){if(_0x311669[_0x50871b(0x16a)][_0x5b1f1f]['connectionState']===_0x50871b(0x7ea)){warnlog('\x20---\x20we\x20will\x20not\x20ask\x20again;\x20we\x27re\x20already\x20connected');return;}}}}catch(_0x4ae4ad){errorlog(_0x4ae4ad);}warnlog(_0x50871b(0xa24)),_0x311669['watchStream'](_0x439499);},_0x311669[_0x345cf4(0x667)],_0x2d8da6));}catch(_0x2160bf){errorlog(_0x2160bf);}pokeIframeAPI('new-view-connection',![],_0xb141ee),_0x2d8da6!==null?pokeIframeAPI('end-view-connection',_0x2d8da6,_0xb141ee):pokeIframeAPI(_0x345cf4(0x594),!![],_0xb141ee),updateUserList();},_0x311669[_0x134a17(0x913)]=null,_0x311669[_0x134a17(0x77f)]=function(){var _0x38ee6b=_0x134a17,_0x497be4=![];if(_0x311669[_0x38ee6b(0x311)]){_0x311669[_0x38ee6b(0x874)]&&clearTimeout(_0x311669[_0x38ee6b(0x913)]);if(_0x311669['ws']===null||(typeof _0x311669['ws']!==_0x38ee6b(0x53b)||_0x311669['ws'][_0x38ee6b(0x8ee)]!==0x1)){}else{var _0x3f22a1=_0x311669[_0x38ee6b(0x311)][_0x38ee6b(0x256)](',');for(var _0x206cac in _0x3f22a1){if(_0x3f22a1[_0x206cac]){var _0x68ae5d=![];for(var _0x4a7f7e in _0x311669[_0x38ee6b(0x16a)]){if(_0x311669[_0x38ee6b(0x16a)][_0x4a7f7e][_0x38ee6b(0x9bb)]&&_0x311669[_0x38ee6b(0x16a)][_0x4a7f7e][_0x38ee6b(0x9bb)]===_0x3f22a1[_0x206cac]){_0x68ae5d=!![];break;}}_0x3f22a1[_0x206cac]in _0x311669[_0x38ee6b(0x241)]&&(_0x68ae5d=!![]);if(_0x68ae5d)continue;_0x311669[_0x38ee6b(0x5ad)](_0x3f22a1[_0x206cac]),_0x497be4=!![];}}}_0x311669[_0x38ee6b(0x874)]&&_0x311669[_0x38ee6b(0x874)]<0xa&&(_0x311669[_0x38ee6b(0x874)]=0xa),_0x311669[_0x38ee6b(0x874)]&&(_0x311669['forceRetryTimeout']=setTimeout(function(){var _0x5e7346=_0x38ee6b;log(_0x5e7346(0x565)),_0x311669[_0x5e7346(0x77f)]();},_0x311669[_0x38ee6b(0x874)]*0x3e8));}return _0x497be4;},_0x311669['offerSDP']=async function(_0x2551e1){var _0x42f4f2=_0x134a17;if(_0x2551e1 in _0x311669[_0x42f4f2(0x859)]){if(_0x311669[_0x42f4f2(0x859)][_0x2551e1]['connectionState']==='failed'||_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x3ee)]===_0x42f4f2(0x208))log(_0x42f4f2(0x86d)),_0x311669['closePC'](_0x2551e1),warnlog(_0x42f4f2(0x405));else{if(iPad||iOS)log(_0x42f4f2(0x804)),_0x311669[_0x42f4f2(0x9d2)](_0x2551e1),warnlog('cleaning\x20up\x20lost\x20connection\x20--\x20disconnected\x20-\x20iOS\x20specific');else{if(_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x3ee)]!==_0x42f4f2(0x7ea)){await sleep(0xbb8);if(_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x3ee)]!==_0x42f4f2(0x7ea))log(_0x42f4f2(0x86d)),_0x311669[_0x42f4f2(0x9d2)](_0x2551e1),warnlog(_0x42f4f2(0x405));else{warnlog('The\x20other\x20end\x20is\x20just\x20being\x20a\x20keener.\x20Ignore\x20it:\x20'+_0x311669[_0x42f4f2(0x859)][_0x2551e1]['connectionState']);return;}}else{warnlog('The\x20other\x20end\x20is\x20just\x20being\x20a\x20keener.\x20Ignore\x20it:\x20'+_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x3ee)]);return;}}}}else log(_0x42f4f2(0x2d4));if(_0x311669[_0x42f4f2(0x209)]!==![]){if(Object[_0x42f4f2(0x19b)](_0x311669[_0x42f4f2(0x859)])['length']>_0x311669[_0x42f4f2(0x209)]){log(_0x42f4f2(0x192)),log('closing\x208'),_0x311669['closePC'](_0x2551e1);return;}}else{if(_0x311669[_0x42f4f2(0x6b1)]!==![]){if(Object[_0x42f4f2(0x19b)](_0x311669['rpcs'])[_0x42f4f2(0x8e9)]+Object['keys'](_0x311669[_0x42f4f2(0x859)])[_0x42f4f2(0x8e9)]>_0x311669['maxconnections']){log(_0x42f4f2(0xa7c)),log(_0x42f4f2(0x8dc)),_0x311669[_0x42f4f2(0x9d2)](_0x2551e1);return;}}}!_0x311669[_0x42f4f2(0x7db)]&&await chooseBestTURN();_0x311669[_0x42f4f2(0xa6e)]&&(_0x311669['configuration'][_0x42f4f2(0xa6e)]=!![]);_0x311669[_0x42f4f2(0x70d)]&&(_0x311669[_0x42f4f2(0x7db)]['BundlePolicy']=_0x311669[_0x42f4f2(0x70d)]);try{_0x311669['pcs'][_0x2551e1]=new RTCPeerConnection(_0x311669[_0x42f4f2(0x7db)]);}catch(_0x1a8f98){!_0x311669['cleanOutput']&&warnUser('An\x20RTC\x20error\x20occured');console['error'](_0x1a8f98);return;}if(_0x311669[_0x42f4f2(0x4e5)]){if(Object['keys'](_0x311669['pcs'])['length']>0x1){log('closing\x203'),log(_0x42f4f2(0x1bf)),_0x311669['closePC'](_0x2551e1);return;}}_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x436)]={},_0x311669[_0x42f4f2(0x859)][_0x2551e1]['session']=_0x311669[_0x42f4f2(0x56b)]+_0x311669[_0x42f4f2(0xa76)](0x5),_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x42f)]=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x506)]=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x34a)]={},_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x34a)][_0x42f4f2(0x4eb)]=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x34a)]['sourceActive']=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x34a)]['streaming']=null,_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x34a)][_0x42f4f2(0x2df)]=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x34a)][_0x42f4f2(0x336)]=null,_0x311669['pcs'][_0x2551e1]['optimizedBitrate']=![],_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x969)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1]['solo']=null,_0x311669['pcs'][_0x2551e1]['layout']=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x62e)]=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1]['maxBandwidth']=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1]['audioMutedOverride']=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x4d0)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x5b5)]=![],_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x9f3)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x9c8)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x798)]=![],_0x311669['pcs'][_0x2551e1]['limitAudio']=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1]['enhanceAudio']=![],_0x311669['pcs'][_0x2551e1]['degradationPreference']=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1]['encoder']=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x920)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x670)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x760)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x299)]=![],_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x368)]=![],_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x97b)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x7c3)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1]['allowDownloads']=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1]['allowMIDI']=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1]['allowBroadcast']=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1]['allowScreenVideo']=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x6b0)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x45a)]=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x54a)]=_0x2551e1,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x6b5)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x596)]=![],_0x311669['pcs'][_0x2551e1]['scaleDueToBitrate']=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x863)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x1c6)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x482)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0xa60)]=![],_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x21f)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1]['showDirector']=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x98d)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1]['keyframeRate']=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x849)]=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x89c)]=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1]['order']=![],_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x3aa)]=![],_0x311669['pcs'][_0x2551e1]['preferAudioCodec']=![],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x9a4)]=null,_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x7af)]=_0x311669['wssid'],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x24d)]=![],_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x586)]=Date[_0x42f4f2(0x610)]();function _0x31a3e5(_0x4f819c=![]){var _0x4fb5bf=_0x42f4f2;if(_0x4f819c)return;_0x311669['pcs'][_0x2551e1][_0x4fb5bf(0x73a)]=_0x311669[_0x4fb5bf(0x859)][_0x2551e1][_0x4fb5bf(0x1e1)]('sendChannel'),_0x311669[_0x4fb5bf(0x859)][_0x2551e1][_0x4fb5bf(0x73a)][_0x4fb5bf(0x54a)]=_0x2551e1,_0x311669[_0x4fb5bf(0x859)][_0x2551e1]['sendChannel'][_0x4fb5bf(0x88c)]=_0xfadecb=>{var _0x25abc6=_0x4fb5bf;warnlog(_0xfadecb),log(_0x25abc6(0x36e)+_0x2551e1);},_0x311669[_0x4fb5bf(0x859)][_0x2551e1][_0x4fb5bf(0x73a)][_0x4fb5bf(0x777)]=()=>{var _0x258fb2=_0x4fb5bf;if(_0x4f819c)return;log('send\x20channel\x20open\x20pcs'),msg={},msg[_0x258fb2(0x6ee)]={},msg[_0x258fb2(0x6ee)][_0x258fb2(0x89c)]=_0x311669[_0x258fb2(0x89c)],msg[_0x258fb2(0x6ee)][_0x258fb2(0xa63)]=_0x311669[_0x258fb2(0xa63)],msg[_0x258fb2(0x6ee)][_0x258fb2(0x225)]=_0x311669[_0x258fb2(0x225)],msg[_0x258fb2(0x6ee)]['queued']=_0x311669[_0x258fb2(0x64f)];try{(_0x311669[_0x258fb2(0x532)]['length']||_0x311669[_0x258fb2(0xa7d)])&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0x868)]=_0x311669[_0x258fb2(0x532)][_0x258fb2(0x24c)](','));}catch(_0x55304b){}msg[_0x258fb2(0x6ee)][_0x258fb2(0x5c9)]=_0x311669[_0x258fb2(0x5c9)],msg[_0x258fb2(0x6ee)]['directorDisplayMuted']=_0x311669[_0x258fb2(0x285)],msg['info'][_0x258fb2(0x3be)]=_0x311669['directorVideoMuted'],msg[_0x258fb2(0x6ee)][_0x258fb2(0x317)]=_0x311669[_0x258fb2(0xa7f)],msg[_0x258fb2(0x6ee)][_0x258fb2(0x3ed)]=_0x311669[_0x258fb2(0x8ab)];_0x311669[_0x258fb2(0x4e1)]?msg[_0x258fb2(0x6ee)][_0x258fb2(0x864)]=!![]:msg[_0x258fb2(0x6ee)][_0x258fb2(0x864)]=![];if(_0x311669[_0x258fb2(0x75b)]){if(!_0x311669[_0x258fb2(0xa84)]&&_0x311669[_0x258fb2(0x2f3)]&&_0x311669['directorUUID']===_0x2551e1)_0x311669[_0x258fb2(0x6ab)]();else{msg[_0x258fb2(0x8ce)]={};_0x311669['mainDirectorPassword']&&(msg['directorSettings'][_0x258fb2(0xa73)]=!![]);msg['directorSettings'][_0x258fb2(0x32f)]=_0x311669[_0x258fb2(0x32f)];_0x311669[_0x258fb2(0x85f)]['length']&&!_0x311669[_0x258fb2(0x85f)][_0x258fb2(0x2c2)](_0x2551e1)&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0x225)]=!![]);var _0x4c16d7=[];for(var _0x4d46f3 in _0x311669[_0x258fb2(0x859)]){_0x311669[_0x258fb2(0x859)][_0x4d46f3][_0x258fb2(0x5b5)]===!![]&&_0x4c16d7[_0x258fb2(0x505)](_0x4d46f3);}_0x311669[_0x258fb2(0x750)]&&(msg[_0x258fb2(0x8ce)][_0x258fb2(0x9ba)]=!![]),_0x4c16d7[_0x258fb2(0x8e9)]&&(msg[_0x258fb2(0x8ce)][_0x258fb2(0x1d7)]=_0x4c16d7);}_0x311669[_0x258fb2(0x495)]&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0x16f)]=_0x311669[_0x258fb2(0x495)]);}_0x311669[_0x258fb2(0x254)]!==![]?msg[_0x258fb2(0x6ee)]['broadcast_mode']=!![]:msg[_0x258fb2(0x6ee)][_0x258fb2(0x5ba)]=![];_0x311669['remote']?msg['info'][_0x258fb2(0x24d)]=!![]:msg[_0x258fb2(0x6ee)]['remote']=![];if(_0x311669[_0x258fb2(0x820)])msg[_0x258fb2(0x6ee)][_0x258fb2(0x201)]=_0x311669[_0x258fb2(0x820)];else{if(_0x311669[_0x258fb2(0x820)]===![])msg['info'][_0x258fb2(0x201)]=![];else _0x311669[_0x258fb2(0x4e1)]&&!_0x311669['director']?msg['info'][_0x258fb2(0x201)]=![]:msg[_0x258fb2(0x6ee)]['obs_control']=null;}_0x311669[_0x258fb2(0x5b0)]&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0x5b0)]=!![]);msg[_0x258fb2(0x6ee)][_0x258fb2(0x612)]=_0x311669[_0x258fb2(0x393)];_0x311669[_0x258fb2(0x43d)]&&!_0x311669[_0x258fb2(0x7a0)]?msg[_0x258fb2(0x6ee)]['screenShareState']=_0x311669['screenShareState']:msg[_0x258fb2(0x6ee)][_0x258fb2(0x701)]=![];msg[_0x258fb2(0x6ee)][_0x258fb2(0x3b4)]=_0x311669['width'],msg['info'][_0x258fb2(0x3b9)]=_0x311669[_0x258fb2(0x5ec)];try{if(_0x311669['streamSrc']){let _0x1e829e=_0x311669[_0x258fb2(0x585)][_0x258fb2(0x87a)]();if(_0x1e829e['length']){let _0x2c574a=_0x1e829e[0x0][_0x258fb2(0x2e2)]();msg['info'][_0x258fb2(0x4ff)]=_0x2c574a[_0x258fb2(0x530)]||![],msg['info'][_0x258fb2(0x748)]=_0x2c574a[_0x258fb2(0x5ec)]||![],msg[_0x258fb2(0x6ee)][_0x258fb2(0x539)]=parseInt(_0x2c574a[_0x258fb2(0x732)])||![];}}if(_0x311669[_0x258fb2(0x7a0)]&&_0x311669[_0x258fb2(0x7a0)]['srcObject']){let _0x16d605=_0x311669[_0x258fb2(0x7a0)][_0x258fb2(0x5c4)][_0x258fb2(0x87a)]();if(_0x16d605['length']){let _0xba2e2c=_0x16d605[0x0][_0x258fb2(0x2e2)]();msg['info'][_0x258fb2(0x6e6)]=_0xba2e2c[_0x258fb2(0x530)]||![],msg[_0x258fb2(0x6ee)][_0x258fb2(0x1b7)]=_0xba2e2c['height']||![],msg[_0x258fb2(0x6ee)]['video_2_init_frameRate']=parseInt(_0xba2e2c[_0x258fb2(0x732)])||![];}}}catch(_0x4a5bac){errorlog(_0x4a5bac);}msg['info']['quality_url']=_0x311669[_0x258fb2(0x878)],msg[_0x258fb2(0x6ee)][_0x258fb2(0x7a4)]=_0x311669[_0x258fb2(0x84c)],msg[_0x258fb2(0x6ee)][_0x258fb2(0x6f7)]=_0x311669['maxviewers'],msg[_0x258fb2(0x6ee)][_0x258fb2(0x9d9)]=_0x311669[_0x258fb2(0x5a8)],msg[_0x258fb2(0x6ee)][_0x258fb2(0xa18)]=_0x311669['echoCancellation'],msg['info'][_0x258fb2(0x374)]=_0x311669['autoGainControl'],msg[_0x258fb2(0x6ee)]['denoise_url']=_0x311669[_0x258fb2(0x953)],msg[_0x258fb2(0x6ee)]['version']=_0x311669[_0x258fb2(0x3d3)],msg[_0x258fb2(0x6ee)][_0x258fb2(0x3a7)]=_0x311669[_0x258fb2(0x591)],msg[_0x258fb2(0x6ee)]['recording_audio_compressor_type']=_0x311669['compressor'],msg[_0x258fb2(0x6ee)][_0x258fb2(0x2d1)]=_0x311669[_0x258fb2(0x1d9)],msg[_0x258fb2(0x6ee)]['recording_audio_ctx_latency']=_0x311669[_0x258fb2(0x2ea)],msg[_0x258fb2(0x6ee)][_0x258fb2(0x996)]=!_0x311669['disableWebAudio'],msg[_0x258fb2(0x6ee)][_0x258fb2(0x917)]=_0x311669[_0x258fb2(0x579)],msg['info'][_0x258fb2(0x3ba)]=_0x311669[_0x258fb2(0x228)],msg[_0x258fb2(0x6ee)][_0x258fb2(0x421)]=_0x311669[_0x258fb2(0x4ac)];_0x311669[_0x258fb2(0x436)]['network_type']&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0x850)]=_0x311669[_0x258fb2(0x436)][_0x258fb2(0x1b9)]);_0x311669[_0x258fb2(0xa47)]!==![]?_0x311669[_0x258fb2(0x1c9)]?msg['info'][_0x258fb2(0x5b1)]=_0x311669[_0x258fb2(0xa47)]+parseInt(_0x311669[_0x258fb2(0x1c9)]):msg[_0x258fb2(0x6ee)]['rotate_video']=_0x311669[_0x258fb2(0xa47)]:msg[_0x258fb2(0x6ee)]['rotate_video']=_0x311669[_0x258fb2(0x1c9)];msg[_0x258fb2(0x6ee)]['rotate_video']&&msg['info']['rotate_video']>=0x168&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0x5b1)]-=0x168);try{navigator&&navigator['userAgent']&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0xa77)]=navigator[_0x258fb2(0x64e)]);navigator&&navigator[_0x258fb2(0x9f2)]&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0x9f2)]=navigator['platform']);gpgpuSupport&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0x6f5)]=gpgpuSupport);cpuSupport&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0x354)]=cpuSupport);iOS&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0x5af)]=iPhone12Up);if(SafariVersion)msg['info'][_0x258fb2(0x884)]=_0x258fb2(0x3cf)+SafariVersion;else{if(getChromiumVersion()>0x3c)msg[_0x258fb2(0x6ee)]['Browser']=_0x258fb2(0x470)+getChromiumVersion();else{if(Firefox)msg[_0x258fb2(0x6ee)][_0x258fb2(0x884)]=_0x258fb2(0x571);else navigator[_0x258fb2(0x64e)][_0x258fb2(0x49e)](_0x258fb2(0xa65))>=0x0?msg[_0x258fb2(0x6ee)][_0x258fb2(0x884)]='Chrome\x20for\x20iOS':msg[_0x258fb2(0x6ee)][_0x258fb2(0x884)]=_0x258fb2(0x4c0);}}}catch(_0xa44e4f){};_0x311669[_0x258fb2(0x401)]&&('level'in _0x311669[_0x258fb2(0x401)]&&(typeof _0x311669[_0x258fb2(0x401)][_0x258fb2(0x6b7)]==_0x258fb2(0x570)?msg[_0x258fb2(0x6ee)]['power_level']=parseInt(_0x311669[_0x258fb2(0x401)]['level']*0x64):msg[_0x258fb2(0x6ee)][_0x258fb2(0xa3d)]=_0x311669[_0x258fb2(0x401)][_0x258fb2(0x6b7)]),_0x258fb2(0x269)in _0x311669[_0x258fb2(0x401)]&&(msg[_0x258fb2(0x6ee)][_0x258fb2(0x2eb)]=_0x311669['batteryState'][_0x258fb2(0x269)]));_0x311669[_0x258fb2(0x632)]&&(msg[_0x258fb2(0x6ee)]['cpuLimited']=_0x311669[_0x258fb2(0x632)]);try{_0x311669[_0x258fb2(0x6ee)][_0x258fb2(0x26c)]&&(msg[_0x258fb2(0x48f)]={},msg[_0x258fb2(0x48f)]['out']={},msg[_0x258fb2(0x48f)][_0x258fb2(0x26c)]['c']=_0x311669['info']['out']['c']);}catch(_0x58f4c5){}_0x311669[_0x258fb2(0x8a1)](msg,_0x2551e1),pokeIframeAPI('new-push-connection',!![],_0x2551e1),pokeIframeAPI(_0x258fb2(0x397),!![],_0x2551e1),updateUserList();},_0x311669[_0x4fb5bf(0x859)][_0x2551e1]['sendChannel'][_0x4fb5bf(0x661)]=()=>{var _0x2cd010=_0x4fb5bf;pokeIframeAPI(_0x2cd010(0x960),![],_0x2551e1),_0x311669[_0x2cd010(0x66a)](),warnlog(_0x2cd010(0x9c3));return;},_0x311669[_0x4fb5bf(0x859)][_0x2551e1][_0x4fb5bf(0x73a)][_0x4fb5bf(0x17a)]=async function(_0x5e7858){var _0x1fd7ed=_0x4fb5bf;log(_0x1fd7ed(0x9c7));try{var _0x19a68e=JSON['parse'](_0x5e7858[_0x1fd7ed(0x3bc)]);}catch(_0x5d972d){warnlog('Couldn\x27t\x20parse\x20JSON;\x20will\x20attempt\x20as\x20ArrayBuffer\x20UINT8ARRAY'),log(_0x5e7858['data']);try{var _0x3c3642=new TextDecoder()[_0x1fd7ed(0x5f5)](_0x5e7858[_0x1fd7ed(0x3bc)]),_0x19a68e=JSON[_0x1fd7ed(0x52d)](_0x3c3642);}catch(_0x4d84bd){try{var _0x19a68e=await new Response(_0x5e7858[_0x1fd7ed(0x3bc)])['text']();_0x19a68e=JSON[_0x1fd7ed(0x52d)](_0x19a68e);}catch(_0x2564a2){return;}}}warnlog(_0x19a68e);if('remote'in _0x19a68e)try{_0x19a68e=await _0x311669[_0x1fd7ed(0x643)](_0x19a68e);if(!_0x19a68e)return;}catch(_0x460498){errorlor(_0x460498);}_0x1fd7ed(0x67b)in _0x19a68e?await _0x311669[_0x1fd7ed(0x840)](_0x19a68e,_0x2551e1+'_screen',_0x2551e1):await _0x311669['processPCSOnMessage'](_0x19a68e,_0x2551e1);};}!_0x311669['legacywebrtc']&&_0x31a3e5(![]);_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x2ca)]=function(_0x2b1fb5){var _0x695044=_0x42f4f2;warnlog(_0x695044(0xa30)),warnlog(_0x2b1fb5);if(_0x2b1fb5[_0x695044(0x27a)]['label']&&_0x2b1fb5[_0x695044(0x27a)][_0x695044(0x89c)]!==_0x695044(0x73a)){_0x311669['recieveFile'](_0x311669[_0x695044(0x16a)],_0x2551e1,_0x2b1fb5[_0x695044(0x27a)]);return;}},_0x311669[_0x42f4f2(0x859)][_0x2551e1]['onnegotiationneeded']=function(_0xdd2cd3){var _0x956c96=_0x42f4f2;log(_0x956c96(0x59c)),_0x311669[_0x956c96(0x903)](_0x2551e1);},_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x196)]=_0x3ea9fc=>{var _0x12c348=_0x42f4f2;errorlog(_0x12c348(0x693));},_0x311669[_0x42f4f2(0x859)][_0x2551e1]['iceTimer']=null,_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x2f5)]=[],_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x2a3)]=function(_0x5109f3){var _0x23068a=_0x42f4f2;if(_0x5109f3[_0x23068a(0x905)]==null){log(_0x23068a(0x9d6));return;}log(_0x5109f3);try{if(_0x311669[_0x23068a(0x242)]){if(_0x5109f3['candidate'][_0x23068a(0x905)][_0x23068a(0x49e)](_0x311669[_0x23068a(0x242)])===-0x1){log(_0x23068a(0x6f4));return;}else log(_0x5109f3[_0x23068a(0x905)]);}}catch(_0x337581){errorlog(_0x337581);}if(_0x311669[_0x23068a(0x859)][_0x2551e1]['iceTimer']!==null){_0x311669['pcs'][_0x2551e1][_0x23068a(0x2f5)][_0x23068a(0x505)](_0x5109f3['candidate']);return;}_0x311669[_0x23068a(0x859)][_0x2551e1][_0x23068a(0x2f5)][_0x23068a(0x505)](_0x5109f3[_0x23068a(0x905)]),_0x311669['pcs'][_0x2551e1][_0x23068a(0x44b)]=setTimeout(function(_0x2e6ce4){var _0x3a954e=_0x23068a;try{_0x311669[_0x3a954e(0x859)][_0x2e6ce4]['iceTimer']=null;}catch(_0x1469dd){warnlog(_0x3a954e(0x795));return;}var _0x2e0350={};_0x2e0350['UUID']=_0x2e6ce4,_0x2e0350[_0x3a954e(0x78a)]=_0x3a954e(0x628),_0x2e0350[_0x3a954e(0x2a8)]=_0x311669[_0x3a954e(0x859)][_0x2e6ce4][_0x3a954e(0x2f5)],_0x2e0350['session']=_0x311669[_0x3a954e(0x859)][_0x2e6ce4][_0x3a954e(0x1fd)],_0x311669['pcs'][_0x2e6ce4][_0x3a954e(0x2f5)]=[],_0x311669[_0x3a954e(0x8a3)]?_0x311669[_0x3a954e(0x331)](JSON['stringify'](_0x2e0350[_0x3a954e(0x2a8)]))[_0x3a954e(0x619)](function(_0x12c942){var _0x1150f0=_0x3a954e;_0x2e0350[_0x1150f0(0x2a8)]=_0x12c942[0x0],_0x2e0350['vector']=_0x12c942[0x1],_0x311669[_0x1150f0(0x966)](_0x2e0350);})[_0x3a954e(0x3b6)](errorlog):_0x311669[_0x3a954e(0x966)](_0x2e0350);},0xc8,_0x2551e1);},_0x311669['processPCSOnMessage']=async function(_0x27465c,_0x1f2fd5,_0x1dae43=![]){var _0x71e5c3=_0x42f4f2;_0x27465c[_0x71e5c3(0x54a)]=_0x1f2fd5;if(_0x27465c['description']){_0x311669[_0x71e5c3(0x6d1)](_0x27465c);return;}else{if(_0x27465c['candidate']){log(_0x71e5c3(0x8e2)),_0x311669[_0x71e5c3(0x1d3)](_0x27465c);return;}else{if(_0x27465c[_0x71e5c3(0x2a8)]){log(_0x71e5c3(0x2fd)),_0x311669[_0x71e5c3(0x411)](_0x27465c);return;}else{if(_0x71e5c3(0x66a)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x92d)]=_0x27465c[_0x71e5c3(0x66a)],_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5),warnlog('PINGED');return;}else{if(_0x71e5c3(0x92d)in _0x27465c){warnlog(_0x71e5c3(0x333));return;}else{if(_0x71e5c3(0xa83)in _0x27465c){warnlog(_0x71e5c3(0x6ea)),log(_0x71e5c3(0x313)),_0x311669[_0x71e5c3(0x9d2)](_0x1f2fd5);return;}}}}}}if(_0x311669[_0x71e5c3(0x75b)]){if(_0x71e5c3(0x873)in _0x27465c&&_0x71e5c3(0x9ed)in _0x27465c){if(_0x311669[_0x71e5c3(0x76b)])_0x311669['directorHash']?_0x311669['decryptMessage'](_0x27465c[_0x71e5c3(0x873)],_0x27465c['vector'],_0x311669[_0x71e5c3(0x799)])[_0x71e5c3(0x619)](function(_0x108d47){var _0x1ac7c4=_0x71e5c3;if(_0x108d47===_0x311669['directorHash']){_0x311669[_0x1ac7c4(0x859)][_0x1f2fd5][_0x1ac7c4(0x5b5)]=!![],_0x311669['directorList'][_0x1ac7c4(0x505)](_0x1f2fd5),getById('container_'+_0x1f2fd5)[_0x1ac7c4(0x424)]['add'](_0x1ac7c4(0x278)),_0x311669[_0x1ac7c4(0x60e)](_0x1f2fd5);var _0x3bcacb={};_0x3bcacb[_0x1ac7c4(0x634)]=_0x1ac7c4(0x873),_0x311669[_0x1ac7c4(0x8a1)](_0x3bcacb,_0x1f2fd5);}else{warnlog(_0x1ac7c4(0x322));var _0x3bcacb={};_0x3bcacb[_0x1ac7c4(0x485)]='requestCoDirector',_0x311669[_0x1ac7c4(0x8a1)](_0x3bcacb,_0x1f2fd5);}})['catch'](function(){var _0xf439f1=_0x71e5c3;warnlog('Failed\x20attempt\x20to\x20connect\x20as\x20co-director');var _0x25a34b={};_0x25a34b['rejected']='requestCoDirector',_0x311669[_0xf439f1(0x8a1)](_0x25a34b,_0x1f2fd5);}):generateHash(_0x311669[_0x71e5c3(0x76b)]+_0x311669[_0x71e5c3(0x9c5)]+_0x71e5c3(0x47f),0xc)['then'](function(_0x3e8d49){var _0x406fe9=_0x71e5c3;_0x311669[_0x406fe9(0x799)]=_0x3e8d49,_0x311669[_0x406fe9(0x4da)](_0x27465c[_0x406fe9(0x873)],_0x27465c[_0x406fe9(0x9ed)],_0x311669[_0x406fe9(0x799)])['then'](function(_0x572e12){var _0x41cbf1=_0x406fe9;if(_0x572e12===_0x311669[_0x41cbf1(0x799)]){_0x311669[_0x41cbf1(0x859)][_0x1f2fd5]['coDirector']=!![],_0x311669[_0x41cbf1(0x95b)][_0x41cbf1(0x505)](_0x1f2fd5),getById('container_'+_0x1f2fd5)[_0x41cbf1(0x424)][_0x41cbf1(0x517)](_0x41cbf1(0x278)),_0x311669[_0x41cbf1(0x60e)](_0x1f2fd5);var _0x4a71d0={};_0x4a71d0[_0x41cbf1(0x634)]=_0x41cbf1(0x873),_0x311669[_0x41cbf1(0xa14)](_0x4a71d0,_0x1f2fd5);}else{warnlog(_0x41cbf1(0x322));var _0x4a71d0={};_0x4a71d0[_0x41cbf1(0x485)]=_0x41cbf1(0x873),_0x311669[_0x41cbf1(0xa14)](_0x4a71d0,_0x1f2fd5);}})[_0x406fe9(0x3b6)](function(){var _0x51f4e=_0x406fe9;warnlog('Failed\x20attempt\x20to\x20connect\x20as\x20co-director');var _0x433c75={};_0x433c75['rejected']=_0x51f4e(0x873),_0x311669[_0x51f4e(0xa14)](_0x433c75,_0x1f2fd5);});return;})[_0x71e5c3(0x3b6)](errorlog);else{warnlog('reject\x20co');var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]='requestCoDirector',_0x311669[_0x71e5c3(0xa14)](_0x3d5b95,_0x1f2fd5);}}if(_0x71e5c3(0x3fe)in _0x27465c&&'roomid'in _0x27465c){log('Someone\x20is\x20trying\x20to\x20transfer\x20a\x20guest');if(_0x311669[_0x71e5c3(0x8fe)]){if(_0x1f2fd5 in _0x311669['pcs']&&_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x5b5)]===!![]){log(_0x71e5c3(0x566));var _0x3d5b95={};if(_0x27465c[_0x71e5c3(0x321)]&&_0x27465c[_0x71e5c3(0x321)][_0x71e5c3(0x339)])_0x3d5b95['request']=_0x71e5c3(0x3fe),_0x3d5b95[_0x71e5c3(0x321)]=_0x27465c['transferSettings'],log(_0x3d5b95),_0x311669[_0x71e5c3(0xa14)](_0x3d5b95,_0x27465c['migrate'][_0x71e5c3(0x721)](),function(){var _0x8fa27f=_0x71e5c3,_0x18ab3e={};_0x18ab3e[_0x8fa27f(0x727)]=_0x8fa27f(0x3fe),_0x18ab3e[_0x8fa27f(0x4e1)]=_0x27465c[_0x8fa27f(0x4e1)],_0x18ab3e[_0x8fa27f(0x81f)]=_0x27465c[_0x8fa27f(0x3fe)]['toString'](),_0x311669['sendMsg'](_0x18ab3e);}),log(_0x3d5b95);else{if(_0x27465c['transferSettings']&&_0x71e5c3(0x254)in _0x27465c[_0x71e5c3(0x321)])_0x3d5b95['request']=_0x71e5c3(0x3fe),_0x3d5b95[_0x71e5c3(0x321)]=_0x27465c[_0x71e5c3(0x321)],delete _0x3d5b95[_0x71e5c3(0x321)][_0x71e5c3(0x4e1)],delete _0x3d5b95[_0x71e5c3(0x321)][_0x71e5c3(0x5cf)],log(_0x3d5b95),_0x311669[_0x71e5c3(0xa14)](_0x3d5b95,_0x27465c[_0x71e5c3(0x3fe)]['toString'](),function(){var _0x52735e=_0x71e5c3,_0x2b649b={};_0x2b649b[_0x52735e(0x727)]=_0x52735e(0x3fe),_0x2b649b[_0x52735e(0x4e1)]=_0x27465c['roomid'],_0x2b649b[_0x52735e(0x81f)]=_0x27465c[_0x52735e(0x3fe)]['toString'](),_0x311669['sendMsg'](_0x2b649b);}),log(_0x3d5b95);else Object[_0x71e5c3(0x19b)](_0x27465c[_0x71e5c3(0x321)])[_0x71e5c3(0x8e9)]?(_0x3d5b95['request']=_0x71e5c3(0x3fe),_0x3d5b95['transferSettings']=_0x27465c[_0x71e5c3(0x321)],delete _0x3d5b95['transferSettings'][_0x71e5c3(0x4e1)],delete _0x3d5b95['transferSettings'][_0x71e5c3(0x5cf)],log(_0x3d5b95),_0x311669['sendRequest'](_0x3d5b95,_0x27465c[_0x71e5c3(0x3fe)][_0x71e5c3(0x721)](),function(){var _0x4af745=_0x71e5c3,_0x15bb5b={};_0x15bb5b[_0x4af745(0x727)]='migrate',_0x15bb5b[_0x4af745(0x4e1)]=_0x27465c[_0x4af745(0x4e1)],_0x15bb5b[_0x4af745(0x81f)]=_0x27465c[_0x4af745(0x3fe)]['toString'](),_0x311669[_0x4af745(0x663)](_0x15bb5b);}),log(_0x3d5b95)):(_0x3d5b95[_0x71e5c3(0x727)]=_0x71e5c3(0x3fe),_0x3d5b95[_0x71e5c3(0x4e1)]=_0x27465c[_0x71e5c3(0x4e1)],_0x3d5b95[_0x71e5c3(0x81f)]=_0x27465c[_0x71e5c3(0x3fe)][_0x71e5c3(0x721)](),_0x311669[_0x71e5c3(0x663)](_0x3d5b95));}pokeIframeAPI(_0x71e5c3(0x8dd),_0x27465c[_0x71e5c3(0x4e1)],_0x27465c[_0x71e5c3(0x3fe)][_0x71e5c3(0x721)]());}}else{var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x1a6),_0x311669[_0x71e5c3(0xa14)](_0x3d5b95,_0x1f2fd5);}}}if('requestAs'in _0x27465c){if(!_0x27465c[_0x71e5c3(0x54a)]){log(_0x71e5c3(0x8db));return;}var _0x2198ed=_0x27465c[_0x71e5c3(0x5c3)];if(!_0x311669[_0x71e5c3(0x859)][_0x2198ed]){log(_0x71e5c3(0x275));return;}if(_0x311669[_0x71e5c3(0x95b)][_0x71e5c3(0x49e)](_0x2198ed)>=0x0){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]='requestAs',_0x311669['sendMessage'](_0x3d5b95,_0x27465c['UUID']),warnlog('Remote\x20user\x20is\x20a\x20director');return;}if(_0x311669['remote']){if(_0x71e5c3(0x24d)in _0x27465c&&_0x27465c[_0x71e5c3(0x24d)]===_0x311669['remote']&&_0x311669['remote']){}else{if(_0x311669['remote']===!![]){}}}else{if(_0x311669[_0x71e5c3(0x95b)][_0x71e5c3(0x49e)](_0x27465c[_0x71e5c3(0x54a)])>=0x0){}else return;}_0x71e5c3(0x202)in _0x27465c&&_0x311669[_0x71e5c3(0x202)](_0x2198ed,_0x27465c[_0x71e5c3(0x202)]);'targetAudioBitrate'in _0x27465c&&_0x311669[_0x71e5c3(0x347)](_0x2198ed,_0x27465c[_0x71e5c3(0x347)]);if('requestResolution'in _0x27465c)try{_0x311669['setResolution'](_0x2198ed,_0x27465c[_0x71e5c3(0x37b)]['w'],_0x27465c[_0x71e5c3(0x37b)]['h'],_0x27465c[_0x71e5c3(0x37b)]['s'],_0x27465c[_0x71e5c3(0x37b)]['c']);}catch(_0x45a743){errorlog(_0x45a743);}return;}manageSceneState(_0x27465c,_0x1f2fd5);try{if(_0x71e5c3(0x6ee)in _0x27465c){_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x436)]['info']=_0x27465c[_0x71e5c3(0x6ee)];_0x71e5c3(0x89c)in _0x27465c[_0x71e5c3(0x6ee)]&&(typeof _0x27465c[_0x71e5c3(0x6ee)][_0x71e5c3(0x89c)]=='string'?_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x89c)]=sanitizeLabel(_0x27465c[_0x71e5c3(0x6ee)][_0x71e5c3(0x89c)]):_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['label']=![]);if(_0x1dae43){if(_0x1dae43===_0x311669[_0x71e5c3(0x2f3)])try{_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x436)][_0x71e5c3(0x6ee)][_0x71e5c3(0x75b)]=!![];}catch(_0x165e74){}else{if(_0x311669[_0x71e5c3(0x95b)][_0x71e5c3(0x49e)](_0x1dae43)>=0x0)try{_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['stats']['info'][_0x71e5c3(0x5b5)]=!![];}catch(_0x3d99b6){}}}else{if(_0x1f2fd5===_0x311669[_0x71e5c3(0x2f3)])try{_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x436)][_0x71e5c3(0x6ee)][_0x71e5c3(0x75b)]=!![];}catch(_0x1ad745){}else{if(_0x311669[_0x71e5c3(0x95b)]['indexOf'](_0x1f2fd5)>=0x0)try{_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x436)][_0x71e5c3(0x6ee)][_0x71e5c3(0x5b5)]=!![];}catch(_0x456f67){}}}if(_0x311669[_0x71e5c3(0x993)]&&_0x311669[_0x71e5c3(0x75b)]&&_0x71e5c3(0x34c)in _0x27465c['info']&&_0x27465c[_0x71e5c3(0x6ee)]['obs']){var _0xf16ed3=createSlotUpdate(_0x1f2fd5);_0x311669[_0x71e5c3(0x692)]?_0x311669[_0x71e5c3(0x8a1)]({'slotsUpdate':_0xf16ed3,'obsSceneTriggers':_0x311669[_0x71e5c3(0x692)],'layouts':_0x311669['layouts']},_0x1f2fd5):_0x311669['sendMessage']({'slotsUpdate':_0xf16ed3,'layouts':_0x311669[_0x71e5c3(0x993)]},_0x1f2fd5);}if(Firefox)try{_0x71e5c3(0x2c0)in _0x27465c[_0x71e5c3(0x6ee)]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x969)]===![]&&(_0x27465c['info'][_0x71e5c3(0x2c0)]&&parseInt(_0x27465c['info'][_0x71e5c3(0x2c0)])>0x0&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['savedBitrate']=parseInt(_0x27465c[_0x71e5c3(0x6ee)][_0x71e5c3(0x2c0)]),_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['bitrateTimeout']&&clearTimeout(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x62e)]),_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x62e)]=setTimeout(function(_0x5983a3){var _0x174349=_0x71e5c3;_0x311669[_0x174349(0x3da)](_0x5983a3,null);},0x3e8,_0x1f2fd5))));}catch(_0x222d35){errorlog(_0x222d35);}pokeIframeAPI(_0x71e5c3(0xa28),_0x27465c[_0x71e5c3(0x6ee)],_0x1f2fd5);}if('ifs'in _0x27465c){if(_0x311669['iframeSrc'])try{_0x311669['iframeSrc'][_0x71e5c3(0x62b)](_0x71e5c3(0x26a))&&processIframeSyncFeedback(_0x27465c[_0x71e5c3(0x90a)],_0x1f2fd5);}catch(_0x4ebb84){errorlog(_0x4ebb84);}}_0x71e5c3(0x7e0)in _0x27465c&&_0x311669[_0x71e5c3(0x640)](_0x27465c[_0x71e5c3(0x7e0)],_0x1f2fd5);_0x71e5c3(0x16f)in _0x27465c&&(_0x311669[_0x71e5c3(0x495)]=_0x27465c[_0x71e5c3(0x16f)],_0x311669['autoSyncCallback'](_0x1f2fd5));_0x71e5c3(0x273)in _0x27465c&&(_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x273)]=parseInt(_0x27465c[_0x71e5c3(0x273)]));'audioBitrate'in _0x27465c&&_0x311669[_0x71e5c3(0x184)](_0x1f2fd5,_0x27465c[_0x71e5c3(0x222)]);_0x71e5c3(0x739)in _0x27465c&&_0x311669[_0x71e5c3(0x3da)](_0x1f2fd5,_0x27465c[_0x71e5c3(0x739)]);_0x71e5c3(0x202)in _0x27465c&&_0x311669[_0x71e5c3(0x202)](_0x1f2fd5,_0x27465c[_0x71e5c3(0x202)]);_0x71e5c3(0x347)in _0x27465c&&_0x311669[_0x71e5c3(0x347)](_0x1f2fd5,_0x27465c['targetAudioBitrate']);if(_0x71e5c3(0x813)in _0x27465c){if(_0x71e5c3(0x24d)in _0x27465c){if(_0x27465c['remote']===_0x311669[_0x71e5c3(0x24d)]&&_0x311669['remote']||_0x311669[_0x71e5c3(0x24d)]===!![]){_0x311669['hangup']();return;}}}if(_0x71e5c3(0x94e)in _0x27465c){if(_0x71e5c3(0x24d)in _0x27465c){if(_0x27465c['remote']===_0x311669['remote']&&_0x311669['remote']||_0x311669[_0x71e5c3(0x24d)]===!![]){_0x311669['hangup'](!![]);return;}}}if(_0x71e5c3(0x50d)in _0x27465c){if(_0x311669[_0x71e5c3(0x95b)]['indexOf'](_0x1dae43||_0x1f2fd5)>=0x0){var _0x50c150={};if(_0x311669['whipOut'][_0x71e5c3(0x436)])_0x50c150[_0x71e5c3(0x638)]=_0x311669[_0x71e5c3(0x638)][_0x71e5c3(0x436)];else for(var _0x5f1d22 in _0x311669[_0x71e5c3(0x859)]){if(_0x5f1d22===_0x1f2fd5)continue;_0x50c150[_0x5f1d22]=_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)];}var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x6db)]=_0x50c150,_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if('remote'in _0x27465c){if(_0x27465c[_0x71e5c3(0x24d)]===_0x311669[_0x71e5c3(0x24d)]&&_0x311669[_0x71e5c3(0x24d)]||_0x311669[_0x71e5c3(0x24d)]===!![]){var _0x50c150={};if(_0x311669[_0x71e5c3(0x638)][_0x71e5c3(0x436)])_0x50c150[_0x71e5c3(0x638)]=_0x311669[_0x71e5c3(0x638)][_0x71e5c3(0x436)];else for(var _0x5f1d22 in _0x311669['pcs']){if(_0x5f1d22===_0x1f2fd5)continue;_0x50c150[_0x5f1d22]=_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x436)];}var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x6db)]=_0x50c150,_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}}else{var _0x50c150={};if(_0x311669['whipOut']['stats'])_0x50c150[_0x71e5c3(0x638)]=_0x311669[_0x71e5c3(0x638)]['stats'];else for(var _0x5f1d22 in _0x311669[_0x71e5c3(0x859)]){if(_0x5f1d22===_0x1f2fd5)continue;if(!_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)])continue;if(_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x798)])continue;if(_0x311669[_0x71e5c3(0x4e1)]){if(_0x71e5c3(0x98d)in _0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)]){if(_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x98d)]===![])continue;}else continue;}_0x50c150[_0x5f1d22]={},_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)]['video_bitrate_kbps']&&(_0x50c150[_0x5f1d22][_0x71e5c3(0x7fc)]=_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x7fc)]),_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x5b6)]&&(_0x50c150[_0x5f1d22]['nacks_per_second']=_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x5b6)]),_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)]['available_outgoing_bitrate_kbps']&&(_0x50c150[_0x5f1d22][_0x71e5c3(0x978)]=_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x978)]),_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x436)]['scene']&&(_0x50c150[_0x5f1d22]['scene']=_0x311669[_0x71e5c3(0x859)][_0x5f1d22]['stats'][_0x71e5c3(0x98d)]),_0x311669[_0x71e5c3(0x859)][_0x5f1d22]['label']&&(_0x50c150[_0x5f1d22][_0x71e5c3(0x89c)]=_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x89c)]),_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x990)]&&(_0x50c150[_0x5f1d22]['resolution']=_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x990)]),_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x436)]['video_encoder']&&(_0x50c150[_0x5f1d22][_0x71e5c3(0x68e)]=_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x68e)]);}var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x6db)]=_0x50c150,_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}}}if(_0x71e5c3(0x160)in _0x27465c){clearInterval(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x927)]);if(_0x311669[_0x71e5c3(0x95b)][_0x71e5c3(0x49e)](_0x1dae43||_0x1f2fd5)>=0x0){if(_0x27465c['requestStatsContinuous']){_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x927)]=setInterval(function(_0x4eb0fa){var _0x145bf7=_0x71e5c3,_0x116a38={};if(_0x311669[_0x145bf7(0x638)][_0x145bf7(0x436)])_0x116a38['whipOut']=_0x311669['whipOut'][_0x145bf7(0x436)];else for(var _0xb0620 in _0x311669[_0x145bf7(0x859)]){if(_0xb0620===_0x4eb0fa)continue;if(!_0x311669[_0x145bf7(0x859)][_0xb0620][_0x145bf7(0x436)])continue;if(_0x311669['pcs'][_0xb0620][_0x145bf7(0x798)])continue;_0x116a38[_0xb0620]=_0x311669[_0x145bf7(0x859)][_0xb0620][_0x145bf7(0x436)];}var _0x35d7f6={};_0x35d7f6[_0x145bf7(0x6db)]=_0x116a38,_0x311669[_0x145bf7(0x8a1)](_0x35d7f6,_0x4eb0fa);},0xbb8,_0x1f2fd5);var _0x50c150={};if(_0x311669[_0x71e5c3(0x638)]['stats'])_0x50c150[_0x71e5c3(0x638)]=_0x311669['whipOut'][_0x71e5c3(0x436)];else for(var _0x5f1d22 in _0x311669['pcs']){if(_0x5f1d22===_0x1f2fd5)continue;if(!_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)])continue;if(_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x798)])continue;_0x50c150[_0x5f1d22]=_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x436)];}var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x6db)]=_0x50c150,_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}}else{if('remote'in _0x27465c){if(_0x27465c['remote']===_0x311669[_0x71e5c3(0x24d)]&&_0x311669['remote']||_0x311669['remote']===!![]){if(_0x27465c[_0x71e5c3(0x160)]){_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x927)]=setInterval(function(_0x2eca71){var _0x5b7be9=_0x71e5c3,_0x1297db={};if(_0x311669[_0x5b7be9(0x638)][_0x5b7be9(0x436)])_0x1297db[_0x5b7be9(0x638)]=_0x311669[_0x5b7be9(0x638)][_0x5b7be9(0x436)];else for(var _0xe914f in _0x311669[_0x5b7be9(0x859)]){if(_0xe914f===_0x2eca71)continue;if(!_0x311669[_0x5b7be9(0x859)][_0xe914f][_0x5b7be9(0x436)])continue;if(_0x311669[_0x5b7be9(0x859)][_0xe914f][_0x5b7be9(0x798)])continue;_0x1297db[_0xe914f]=_0x311669[_0x5b7be9(0x859)][_0xe914f][_0x5b7be9(0x436)];}var _0x552562={};_0x552562['remoteStats']=_0x1297db,_0x311669[_0x5b7be9(0x8a1)](_0x552562,_0x2eca71);},0xbb8,_0x1f2fd5);var _0x50c150={};if(_0x311669[_0x71e5c3(0x638)][_0x71e5c3(0x436)])_0x50c150[_0x71e5c3(0x638)]=_0x311669[_0x71e5c3(0x638)][_0x71e5c3(0x436)];else for(var _0x5f1d22 in _0x311669[_0x71e5c3(0x859)]){if(_0x5f1d22===_0x1f2fd5)continue;if(!_0x311669[_0x71e5c3(0x859)][_0x5f1d22]['stats'])continue;if(_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x798)])continue;_0x50c150[_0x5f1d22]=_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)];}var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x6db)]=_0x50c150,_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}}}else{if(_0x27465c[_0x71e5c3(0x160)]){_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x927)]=setInterval(function(_0x3ed1e3){var _0x21578e=_0x71e5c3,_0x3b0a40={};if(_0x311669[_0x21578e(0x638)]['stats'])_0x3b0a40[_0x21578e(0x638)]=_0x311669['whipOut'][_0x21578e(0x436)];else for(var _0xc3d97f in _0x311669[_0x21578e(0x859)]){if(_0xc3d97f===_0x3ed1e3)continue;if(!_0x311669[_0x21578e(0x859)][_0xc3d97f][_0x21578e(0x436)])continue;if(_0x311669['pcs'][_0xc3d97f][_0x21578e(0x798)])continue;if(_0x311669[_0x21578e(0x4e1)]){if('scene'in _0x311669[_0x21578e(0x859)][_0xc3d97f][_0x21578e(0x436)]){if(_0x311669[_0x21578e(0x859)][_0xc3d97f][_0x21578e(0x436)]['scene']===![])continue;}else continue;}_0x3b0a40[_0xc3d97f]={},_0x311669[_0x21578e(0x859)][_0xc3d97f]['stats'][_0x21578e(0x7fc)]&&(_0x3b0a40[_0xc3d97f][_0x21578e(0x7fc)]=_0x311669[_0x21578e(0x859)][_0xc3d97f]['stats'][_0x21578e(0x7fc)]),_0x311669[_0x21578e(0x859)][_0xc3d97f]['stats'][_0x21578e(0x5b6)]&&(_0x3b0a40[_0xc3d97f][_0x21578e(0x5b6)]=_0x311669[_0x21578e(0x859)][_0xc3d97f][_0x21578e(0x436)][_0x21578e(0x5b6)]),_0x311669[_0x21578e(0x859)][_0xc3d97f][_0x21578e(0x436)][_0x21578e(0x978)]&&(_0x3b0a40[_0xc3d97f]['available_outgoing_bitrate_kbps']=_0x311669[_0x21578e(0x859)][_0xc3d97f][_0x21578e(0x436)][_0x21578e(0x978)]),_0x311669[_0x21578e(0x859)][_0xc3d97f][_0x21578e(0x436)][_0x21578e(0x98d)]&&(_0x3b0a40[_0xc3d97f][_0x21578e(0x98d)]=_0x311669[_0x21578e(0x859)][_0xc3d97f][_0x21578e(0x436)][_0x21578e(0x98d)]),_0x311669[_0x21578e(0x859)][_0xc3d97f]['label']&&(_0x3b0a40[_0xc3d97f]['label']=_0x311669['pcs'][_0xc3d97f][_0x21578e(0x89c)]),_0x311669[_0x21578e(0x859)][_0xc3d97f][_0x21578e(0x436)][_0x21578e(0x990)]&&(_0x3b0a40[_0xc3d97f][_0x21578e(0x990)]=_0x311669[_0x21578e(0x859)][_0xc3d97f][_0x21578e(0x436)][_0x21578e(0x990)]),_0x311669[_0x21578e(0x859)][_0xc3d97f][_0x21578e(0x436)][_0x21578e(0x68e)]&&(_0x3b0a40[_0xc3d97f]['video_encoder']=_0x311669['pcs'][_0xc3d97f][_0x21578e(0x436)][_0x21578e(0x68e)]);}var _0x1a3a81={};_0x1a3a81['remoteStats']=_0x3b0a40,_0x311669[_0x21578e(0x8a1)](_0x1a3a81,_0x3ed1e3);},0xbb8,_0x1f2fd5);var _0x50c150={};if(_0x311669[_0x71e5c3(0x638)][_0x71e5c3(0x436)])_0x50c150[_0x71e5c3(0x638)]=_0x311669[_0x71e5c3(0x638)][_0x71e5c3(0x436)];else for(var _0x5f1d22 in _0x311669[_0x71e5c3(0x859)]){if(_0x5f1d22===_0x1f2fd5)continue;if(!_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)])continue;if(_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x798)])continue;if(_0x311669[_0x71e5c3(0x4e1)]){if(_0x71e5c3(0x98d)in _0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)]){if(_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x98d)]===![])continue;}else continue;}_0x50c150[_0x5f1d22]={},_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)]['video_bitrate_kbps']&&(_0x50c150[_0x5f1d22][_0x71e5c3(0x7fc)]=_0x311669[_0x71e5c3(0x859)][_0x5f1d22]['stats'][_0x71e5c3(0x7fc)]),_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x5b6)]&&(_0x50c150[_0x5f1d22]['nacks_per_second']=_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x5b6)]),_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x978)]&&(_0x50c150[_0x5f1d22]['available_outgoing_bitrate_kbps']=_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x978)]),_0x311669[_0x71e5c3(0x859)][_0x5f1d22]['stats'][_0x71e5c3(0x98d)]&&(_0x50c150[_0x5f1d22][_0x71e5c3(0x98d)]=_0x311669[_0x71e5c3(0x859)][_0x5f1d22]['stats'][_0x71e5c3(0x98d)]),_0x311669[_0x71e5c3(0x859)][_0x5f1d22]['label']&&(_0x50c150[_0x5f1d22]['label']=_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x89c)]),_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x990)]&&(_0x50c150[_0x5f1d22][_0x71e5c3(0x990)]=_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x436)]['resolution']),_0x311669[_0x71e5c3(0x859)][_0x5f1d22][_0x71e5c3(0x436)]['video_encoder']&&(_0x50c150[_0x5f1d22][_0x71e5c3(0x68e)]=_0x311669['pcs'][_0x5f1d22][_0x71e5c3(0x436)][_0x71e5c3(0x68e)]);}var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x6db)]=_0x50c150,_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}}}}if(_0x71e5c3(0x37b)in _0x27465c)try{_0x311669[_0x71e5c3(0x83a)](_0x1f2fd5,_0x27465c[_0x71e5c3(0x37b)]['w'],_0x27465c['requestResolution']['h'],_0x27465c['requestResolution']['s'],_0x27465c[_0x71e5c3(0x37b)]['c']);}catch(_0x1828fc){errorlog(_0x1828fc);}_0x71e5c3(0x236)in _0x27465c&&(_0x27465c['scene']?_0x311669[_0x71e5c3(0x95b)][_0x71e5c3(0x49e)](_0x1dae43||_0x1f2fd5)>=0x0?_0x311669['sendKeyFrameScenes']():errorlog(_0x71e5c3(0x979)):_0x311669[_0x71e5c3(0x2e3)](_0x1f2fd5));if('chat'in _0x27465c){var _0x4eb553=![],_0x5eef=![];_0x311669[_0x71e5c3(0x95b)][_0x71e5c3(0x49e)](_0x1dae43||_0x1f2fd5)>=0x0&&(_0x4eb553=!![],'overlay'in _0x27465c&&(_0x27465c[_0x71e5c3(0x563)]==!![]&&(_0x5eef=!![]))),log('isDirector\x20'+_0x4eb553),getChatMessage(_0x27465c[_0x71e5c3(0x2f8)],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x89c)],_0x4eb553,_0x5eef);}if(_0x71e5c3(0xa63)in _0x27465c){_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['order']=parseInt(_0x27465c[_0x71e5c3(0xa63)])||0x0;_0x1f2fd5 in _0x311669[_0x71e5c3(0x16a)]&&(_0x311669[_0x71e5c3(0x16a)][_0x1f2fd5]['order']=_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['order']);if(_0x311669[_0x71e5c3(0x75b)]){var _0x337561=document[_0x71e5c3(0x319)](_0x71e5c3(0x1a7)+_0x1f2fd5+'\x22]');log(_0x337561),_0x337561[0x0]&&(_0x337561[0x0][_0x71e5c3(0x882)]=parseInt(_0x27465c['order'])||0x0);}updateMixer();}'scale'in _0x27465c&&_0x311669[_0x71e5c3(0x88b)](_0x1f2fd5,_0x27465c['scale']);if(_0x311669['director']&&_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x5b5)]&&_0x71e5c3(0x772)in _0x27465c){log(_0x27465c),_0x311669[_0x71e5c3(0x78e)]=_0x27465c[_0x71e5c3(0x772)];for(var _0x311070 in _0x311669[_0x71e5c3(0x78e)]){syncSceneState(_0x311070),syncOtherState(_0x311070);}}if(_0x311669['directorList']['indexOf'](_0x1dae43||_0x1f2fd5)==-0x1){if(_0x71e5c3(0x4c5)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x4c5),_0x311669['sendMessage'](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x89f)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]='requestVideoRecord',_0x311669['sendMessage'](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x5f7)in _0x27465c){var _0x3d5b95={};_0x3d5b95['rejected']=_0x71e5c3(0x5f7),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x8f8)in _0x27465c){var _0x3d5b95={};_0x3d5b95['rejected']=_0x71e5c3(0x8f8),_0x311669['sendMessage'](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x7c9)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x7c9),_0x311669['sendMessage'](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x2a7)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x2a7),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x3ff)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x3ff),_0x311669['sendMessage'](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x478)in _0x27465c){var _0x3d5b95={};_0x3d5b95['rejected']=_0x71e5c3(0x478),_0x311669['sendMessage'](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x614)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x614),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if('remoteVideoMuted'in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]='remoteVideoMuted',_0x311669['sendMessage'](_0x3d5b95,_0x1f2fd5);}else{if('requestChangeMicDelay'in _0x27465c){var _0x3d5b95={};_0x3d5b95['rejected']=_0x71e5c3(0x52c),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if('lowerhand'in _0x27465c){var _0x3d5b95={};_0x3d5b95['rejected']=_0x71e5c3(0x7f6),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if('hangup'in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x813),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x4fd)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x4fd),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x7c2)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x7c2),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x356)in _0x27465c){var _0x3d5b95={};_0x3d5b95['rejected']=_0x71e5c3(0x356),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if('micIsolated'in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x8f3),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x941)in _0x27465c){var _0x3d5b95={};_0x3d5b95['rejected']=_0x71e5c3(0x941),_0x311669['sendMessage'](_0x3d5b95,_0x1f2fd5);}else{if('stopClock'in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x65a),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if('resumeClock'in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x2c8),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x7b3)in _0x27465c){var _0x3d5b95={};_0x3d5b95['rejected']=_0x71e5c3(0x7b3),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x165)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x165),_0x311669['sendMessage'](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x5e8)in _0x27465c){var _0x3d5b95={};_0x3d5b95['rejected']=_0x71e5c3(0x5e8),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x876)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x876),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if('pauseClock'in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x4dc),_0x311669['sendMessage'](_0x3d5b95,_0x1f2fd5);}else{if(_0x71e5c3(0x6f3)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x6f3),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}else{if('group'in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x485)]=_0x71e5c3(0x532),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x1f2fd5);}}}}}}}}}}}}}}}}}}}}}}}}}}}}else{if(_0x71e5c3(0x4c5)in _0x27465c){var _0x3d77cd=_0x311669[_0x71e5c3(0x585)]['getAudioTracks']();_0x3d77cd[_0x71e5c3(0x8e9)]&&(_0x71e5c3(0x800)in _0x27465c?applyAudioHack(_0x27465c[_0x71e5c3(0x766)],_0x27465c['value'],_0x27465c['deviceId']):applyAudioHack(_0x27465c[_0x71e5c3(0x766)],_0x27465c[_0x71e5c3(0x8d7)]));}if(_0x71e5c3(0x89f)in _0x27465c){if(_0x27465c['requestVideoRecord']){if(_0x311669[_0x71e5c3(0x49b)]){var _0x185464=0x1770;_0x27465c[_0x71e5c3(0x8d7)]&&(_0x185464=parseInt(_0x27465c['value'])),recordLocalVideo(_0x71e5c3(0x36c),_0x185464);}}else _0x311669['videoElement']&&recordLocalVideo(_0x71e5c3(0x86e));}if(_0x71e5c3(0x5f7)in _0x27465c){_0x311669[_0x71e5c3(0xa63)]==![]&&(_0x311669[_0x71e5c3(0xa63)]=0x0);_0x311669[_0x71e5c3(0xa63)]+=parseInt(_0x27465c[_0x71e5c3(0x5f7)])||0x0;var _0x3d5b95={};_0x3d5b95={},_0x3d5b95['order']=_0x311669['order'],_0x311669[_0x71e5c3(0x2db)](_0x3d5b95),updateMixer();}_0x71e5c3(0x8f8)in _0x27465c&&changeURL(_0x27465c[_0x71e5c3(0x8f8)]);'stopClock'in _0x27465c&&stopClock();_0x71e5c3(0x2c8)in _0x27465c&&resumeClock();'setClock'in _0x27465c&&setClock(_0x27465c[_0x71e5c3(0x7b3)]);_0x71e5c3(0x165)in _0x27465c&&hideClock();'showClock'in _0x27465c&&showClock();_0x71e5c3(0x876)in _0x27465c&&startClock();_0x71e5c3(0x4dc)in _0x27465c&&pauseClock();if(_0x71e5c3(0x6f3)in _0x27465c){if(_0x311669['showTime']!==![]){if(_0x27465c[_0x71e5c3(0x6f3)]&&!_0x311669[_0x71e5c3(0x6f3)])toggleClock(_0x27465c['clock24']||![]);else!_0x27465c[_0x71e5c3(0x6f3)]&&_0x311669[_0x71e5c3(0x6f3)]&&toggleClock(_0x27465c[_0x71e5c3(0x226)]||![]);}}_0x71e5c3(0x941)in _0x27465c&&toggleFileshare(_0x1f2fd5);if(_0x71e5c3(0x532)in _0x27465c)try{_0x1dae43?(_0x27465c['group']?_0x311669[_0x71e5c3(0x531)]=_0x27465c[_0x71e5c3(0x532)][_0x71e5c3(0x256)](','):_0x311669['group_alt']=[],_0x311669[_0x71e5c3(0x8a1)]({'group':_0x27465c[_0x71e5c3(0x532)],'altUUID':!![]})):(_0x27465c[_0x71e5c3(0x532)]?_0x311669[_0x71e5c3(0x532)]=_0x27465c[_0x71e5c3(0x532)]['split'](','):_0x311669['group']=[],_0x311669['sendMessage']({'group':_0x27465c['group']})),updateMixer(),pokeIframeAPI(_0x71e5c3(0x23f),_0x311669[_0x71e5c3(0x532)]);}catch(_0x52fc8c){}if(_0x71e5c3(0x7c9)in _0x27465c){if(_0x71e5c3(0x8d7)in _0x27465c){if(typeof _0x27465c[_0x71e5c3(0x8d7)]==_0x71e5c3(0xa46)){_0x311669['label']=sanitizeLabel(_0x27465c[_0x71e5c3(0x8d7)]),log('New\x20Label:\x20'+_0x311669['label']);if(_0x311669['director']){var _0x337561=getById(_0x71e5c3(0x1c8)+_0x1f2fd5);if(_0x311669[_0x71e5c3(0x89c)])_0x337561[_0x71e5c3(0x882)]=_0x311669[_0x71e5c3(0x89c)],_0x337561[_0x71e5c3(0x424)][_0x71e5c3(0x761)](_0x71e5c3(0x55f));else _0x311669[_0x71e5c3(0x2f3)]===(_0x1dae43||_0x1f2fd5)?(miniTranslate(_0x337561[_0x71e5c3(0x82f)],_0x71e5c3(0x330)),_0x337561['classList']['remove'](_0x71e5c3(0x55f))):(miniTranslate(_0x337561['innerHTML'],'add-a-label'),_0x337561[_0x71e5c3(0x424)][_0x71e5c3(0x517)]('addALabel'));}else _0x311669[_0x71e5c3(0x901)]&&updateMixer();!_0x311669[_0x71e5c3(0x75b)]&&(_0x311669[_0x71e5c3(0x89c)]?document[_0x71e5c3(0x29a)]=_0x311669[_0x71e5c3(0x89c)]:document[_0x71e5c3(0x29a)]=location[_0x71e5c3(0x4a3)]);var _0x5e81da=encodeURIComponent(_0x311669[_0x71e5c3(0x89c)]);urlParams[_0x71e5c3(0x4d7)]('l')?updateURL('l='+_0x5e81da,!![],![]):updateURL('label='+_0x5e81da,!![],![]);var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x7c9)]=!![],_0x3d5b95['value']=_0x311669[_0x71e5c3(0x89c)],_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95);}else{_0x311669['label']=![];var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x7c9)]=!![],_0x3d5b95[_0x71e5c3(0x8d7)]=_0x311669[_0x71e5c3(0x89c)],_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95);if(_0x311669['director']){var _0x337561=getById(_0x71e5c3(0x1c8)+_0x1f2fd5);_0x311669[_0x71e5c3(0x2f3)]===(_0x1dae43||_0x1f2fd5)?(miniTranslate(_0x337561[_0x71e5c3(0x82f)],_0x71e5c3(0x330)),_0x337561[_0x71e5c3(0x424)][_0x71e5c3(0x761)](_0x71e5c3(0x55f))):(miniTranslate(_0x337561[_0x71e5c3(0x82f)],_0x71e5c3(0x43c)),_0x337561['classList'][_0x71e5c3(0x517)](_0x71e5c3(0x55f)));}else _0x311669[_0x71e5c3(0x901)]?(document['title']=location[_0x71e5c3(0x4a3)],updateMixer()):document[_0x71e5c3(0x29a)]=location[_0x71e5c3(0x4a3)];}}}if(_0x71e5c3(0x2a7)in _0x27465c){if(_0x27465c[_0x71e5c3(0x766)]=='low')changeLowEQ(parseFloat(_0x27465c[_0x71e5c3(0x8d7)]),_0x27465c[_0x71e5c3(0x53f)]);else{if(_0x27465c[_0x71e5c3(0x766)]==_0x71e5c3(0x64c))changeMidEQ(parseFloat(_0x27465c['value']),_0x27465c[_0x71e5c3(0x53f)]);else _0x27465c[_0x71e5c3(0x766)]==_0x71e5c3(0x4f3)&&changeHighEQ(parseFloat(_0x27465c[_0x71e5c3(0x8d7)]),_0x27465c[_0x71e5c3(0x53f)]);}}if(_0x71e5c3(0x3ff)in _0x27465c){var _0xfc15d0=_0x311669[_0x71e5c3(0x8ea)];if(_0x27465c['value']===_0x71e5c3(0x309))_0x311669[_0x71e5c3(0x8ea)]=![],log('noise\x20gate\x20off');else _0x27465c[_0x71e5c3(0x8d7)]===_0x71e5c3(0x8fc)?(_0x311669['noisegate']=!![],log(_0x71e5c3(0x27e))):_0x311669[_0x71e5c3(0x8ea)]=_0x27465c[_0x71e5c3(0x8d7)];_0x311669[_0x71e5c3(0x8ea)]!==_0xfc15d0&&senderAudioUpdate();}if(_0x71e5c3(0x478)in _0x27465c){var _0xfc15d0=_0x311669[_0x71e5c3(0x30a)];if(_0x27465c[_0x71e5c3(0x8d7)]===_0x71e5c3(0x309))_0x311669[_0x71e5c3(0x30a)]=![],log(_0x71e5c3(0x32d));else{if(_0x27465c[_0x71e5c3(0x8d7)]==='1')_0x311669[_0x71e5c3(0x30a)]=0x1,log(_0x71e5c3(0x27e));else _0x27465c[_0x71e5c3(0x8d7)]==='2'?(_0x311669[_0x71e5c3(0x30a)]=0x2,log(_0x71e5c3(0x27e))):_0x311669[_0x71e5c3(0x30a)]=parseInt(_0x27465c['value'])||![];}_0x311669[_0x71e5c3(0x30a)]!==_0xfc15d0&&senderAudioUpdate();}_0x71e5c3(0x52c)in _0x27465c&&(_0x311669['micDelay']===![]?(_0x311669['micDelay']=parseInt(_0x27465c[_0x71e5c3(0x8d7)])||0x0,senderAudioUpdate()):(_0x311669[_0x71e5c3(0x1d9)]=parseInt(_0x27465c[_0x71e5c3(0x8d7)])||0x0,changeMicDelay(_0x311669[_0x71e5c3(0x1d9)],_0x27465c[_0x71e5c3(0x800)])));'requestChangeSubGain'in _0x27465c&&changeSubGain(parseFloat(_0x27465c[_0x71e5c3(0x8d7)]),_0x27465c[_0x71e5c3(0x800)]);_0x71e5c3(0x7f6)in _0x27465c&&(_0x311669[_0x71e5c3(0x7a8)]&&lowerhand());if(_0x71e5c3(0x2d8)in _0x27465c&&'mirrorGuestTarget'in _0x27465c){if(_0x27465c[_0x71e5c3(0x402)]&&_0x27465c[_0x71e5c3(0x402)]===!![])_0x311669['permaMirrored']=_0x27465c[_0x71e5c3(0x2d8)],applyMirror(_0x311669[_0x71e5c3(0x89b)]);else _0x27465c[_0x71e5c3(0x402)]&&_0x27465c[_0x71e5c3(0x402)]in _0x311669['rpcs']&&(_0x311669[_0x71e5c3(0x16a)][_0x27465c['mirrorGuestTarget']][_0x71e5c3(0x350)]=_0x27465c['mirrorGuestState'],_0x311669[_0x71e5c3(0x16a)][_0x27465c[_0x71e5c3(0x402)]][_0x71e5c3(0x49b)]&&applyMirrorGuest(_0x27465c[_0x71e5c3(0x2d8)],_0x311669[_0x71e5c3(0x16a)][_0x27465c['mirrorGuestTarget']][_0x71e5c3(0x49b)]));}if(_0x71e5c3(0x6a6)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x54a)]=_0x1f2fd5,_0x3d5b95['audioOptions']=listAudioSettingsPrep(),sendMediaDevices(_0x3d5b95[_0x71e5c3(0x54a)]),_0x311669['sendMessage'](_0x3d5b95,_0x3d5b95[_0x71e5c3(0x54a)]);}if(_0x71e5c3(0x389)in _0x27465c){var _0x3d5b95={};_0x3d5b95[_0x71e5c3(0x54a)]=_0x1f2fd5,_0x3d5b95[_0x71e5c3(0x177)]=listVideoSettingsPrep(),sendMediaDevices(_0x3d5b95[_0x71e5c3(0x54a)]),_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95,_0x3d5b95[_0x71e5c3(0x54a)]);}_0x71e5c3(0x715)in _0x27465c&&changeAudioOutputDeviceById(_0x27465c[_0x71e5c3(0x715)],_0x1f2fd5);_0x71e5c3(0x3eb)in _0x27465c&&changeAudioDeviceById(_0x27465c[_0x71e5c3(0x3eb)],_0x1f2fd5);_0x71e5c3(0x791)in _0x27465c&&changeVideoDeviceById(_0x27465c[_0x71e5c3(0x791)],_0x1f2fd5);'requestVideoHack'in _0x27465c&&(_0x71e5c3(0x8be)in _0x27465c&&_0x27465c[_0x71e5c3(0x8be)]?updateCameraConstraints(_0x27465c[_0x71e5c3(0x766)],_0x27465c[_0x71e5c3(0x8d7)],!![],_0x1f2fd5):updateCameraConstraints(_0x27465c['keyname'],_0x27465c[_0x71e5c3(0x8d7)],![],![]));_0x71e5c3(0x940)in _0x27465c&&changeLowCut(parseFloat(_0x27465c[_0x71e5c3(0x8d7)]),_0x27465c['track']);'requestChangeLowcut'in _0x27465c&&changeLowCut(parseFloat(_0x27465c[_0x71e5c3(0x8d7)]),_0x27465c['track']);_0x71e5c3(0x813)in _0x27465c&&(_0x311669[_0x71e5c3(0x2f3)]&&_0x311669['hangup']());if('mute'in _0x27465c){}if('volume'in _0x27465c){var _0x2bef61=parseInt(_0x27465c[_0x71e5c3(0x356)])/0x64||0x0;_0x311669[_0x71e5c3(0x591)]=parseInt(_0x27465c[_0x71e5c3(0x356)])||0x0;try{for(var _0x393e8c in _0x311669[_0x71e5c3(0x7a7)]){log(_0x71e5c3(0x52f)),_0x311669[_0x71e5c3(0x7a7)][_0x393e8c][_0x71e5c3(0x987)][_0x71e5c3(0x823)][_0x71e5c3(0x44a)](_0x2bef61,_0x311669[_0x71e5c3(0x7a7)][_0x393e8c][_0x71e5c3(0x837)]['currentTime']);}}catch(_0x451ac6){}updateVolume(!![]);}if('micIsolate'in _0x27465c){if(_0x27465c[_0x71e5c3(0x52e)])_0x311669[_0x71e5c3(0x95b)]['indexOf'](_0x1dae43||_0x1f2fd5)>=0x0&&(_0x311669[_0x71e5c3(0x8f3)][_0x71e5c3(0x505)](_0x1f2fd5),_0x311669[_0x71e5c3(0x8ef)]());else{var _0x493e27=_0x311669[_0x71e5c3(0x8f3)][_0x71e5c3(0x49e)](_0x1f2fd5);_0x493e27>-0x1&&(_0x311669[_0x71e5c3(0x8f3)][_0x71e5c3(0x48c)](_0x493e27,0x1),_0x311669['applyIsolatedChat']());}}if(_0x71e5c3(0x4d1)in _0x27465c){if(_0x27465c[_0x71e5c3(0x4d1)])_0x311669[_0x71e5c3(0x95b)][_0x71e5c3(0x49e)](_0x1dae43||_0x1f2fd5)>=0x0&&(_0x311669[_0x71e5c3(0x4d1)][_0x71e5c3(0x505)](_0x1f2fd5),_0x311669[_0x71e5c3(0x645)]());else{var _0x493e27=_0x311669[_0x71e5c3(0x4d1)]['indexOf'](_0x1f2fd5);_0x493e27>-0x1&&(_0x311669[_0x71e5c3(0x4d1)]['splice'](_0x493e27,0x1),_0x311669[_0x71e5c3(0x645)]());}}_0x71e5c3(0x7c2)in _0x27465c&&(_0x27465c[_0x71e5c3(0x7c2)]?(_0x311669[_0x71e5c3(0x5c9)]=!![],_0x311669[_0x71e5c3(0x8f1)]()):(_0x311669[_0x71e5c3(0x5c9)]=![],_0x311669[_0x71e5c3(0x8f1)]()));_0x71e5c3(0x4fd)in _0x27465c&&(_0x27465c['displayMute']?(_0x311669[_0x71e5c3(0x285)]=!![],_0x311669['directorDisplayMute']()):(_0x311669['directorDisplayMuted']=![],_0x311669[_0x71e5c3(0x324)]()));if('remoteVideoMuted'in _0x27465c){_0x311669[_0x71e5c3(0x360)]=_0x27465c[_0x71e5c3(0x360)],toggleVideoMute(!![]);if(!_0x311669[_0x71e5c3(0x8ab)]){var _0x3d5b95={};_0x3d5b95['videoMuted']=_0x311669[_0x71e5c3(0x360)],_0x311669[_0x71e5c3(0x8a1)](_0x3d5b95);}}_0x71e5c3(0x20b)in _0x27465c&&applyNewParams(_0x27465c[_0x71e5c3(0x20b)]);}if(_0x311669['directorUUID']===(_0x1dae43||_0x1f2fd5)){_0x27465c[_0x71e5c3(0x727)]==='migrate'&&(warnlog(_0x71e5c3(0x19e)),_0x71e5c3(0x321)in _0x27465c&&(_0x71e5c3(0x5cf)in _0x27465c[_0x71e5c3(0x321)]&&(_0x311669[_0x71e5c3(0x5cf)]=_0x27465c[_0x71e5c3(0x5cf)]),_0x71e5c3(0x254)in _0x27465c[_0x71e5c3(0x321)]&&(_0x27465c[_0x71e5c3(0x321)][_0x71e5c3(0x254)]===!![]||_0x27465c['transferSettings'][_0x71e5c3(0x254)]===null?(_0x311669[_0x71e5c3(0x254)]=null,_0x311669[_0x71e5c3(0x557)]===![]&&(_0x311669[_0x71e5c3(0x557)]=0x2),_0x311669[_0x71e5c3(0x886)]===![]&&(_0x311669[_0x71e5c3(0x886)]=0x1),_0x311669[_0x71e5c3(0xa1f)]===null&&(_0x311669['showList']=!![])):_0x311669[_0x71e5c3(0x254)]=_0x27465c[_0x71e5c3(0x321)][_0x71e5c3(0x254)],_0x27465c[_0x71e5c3(0x321)][_0x71e5c3(0x339)]&&(_0x311669[_0x71e5c3(0x254)]!==![]?_0x311669['broadcast']===null?updateURL(_0x71e5c3(0x254),!![]):updateURL('broadcast='+_0x311669[_0x71e5c3(0x254)],!![]):updateURL(_0x71e5c3(0x1eb),!![]))),_0x71e5c3(0x4e1)in _0x27465c[_0x71e5c3(0x321)]&&(_0x311669[_0x71e5c3(0x4e1)]=_0x27465c[_0x71e5c3(0x321)]['roomid'],_0x27465c['transferSettings'][_0x71e5c3(0x339)]&&updateURL(_0x71e5c3(0x70b)+_0x311669[_0x71e5c3(0x4e1)],!![])),_0x71e5c3(0x64f)in _0x27465c['transferSettings']&&(_0x311669[_0x71e5c3(0x64f)]=_0x27465c[_0x71e5c3(0x321)][_0x71e5c3(0x64f)],_0x311669[_0x71e5c3(0x64f)]&&(_0x311669[_0x71e5c3(0x64f)]=0x2),_0x27465c[_0x71e5c3(0x321)][_0x71e5c3(0x339)]&&(_0x311669['queue']?updateURL(_0x71e5c3(0x64f),!![]):updateURL('queue=false',!![]))),_0x71e5c3(0x817)in _0x27465c[_0x71e5c3(0x321)]&&(_0x311669['queue']&&(_0x311669[_0x71e5c3(0x64f)]=0x3,_0x27465c['transferSettings']['updateurl']&&updateURL(_0x71e5c3(0x985),!![])))));try{if(_0x71e5c3(0x8ce)in _0x27465c&&'addCoDirector'in _0x27465c[_0x71e5c3(0x8ce)])for(var _0x5972cd=0x0;_0x5972cd<_0x27465c['directorSettings'][_0x71e5c3(0x1d7)][_0x71e5c3(0x8e9)];_0x5972cd++){if(!_0x311669['directorList'][_0x71e5c3(0x2c2)](_0x27465c[_0x71e5c3(0x8ce)][_0x71e5c3(0x1d7)][_0x5972cd][_0x71e5c3(0x721)])){_0x311669[_0x71e5c3(0x95b)]['push'](_0x27465c[_0x71e5c3(0x8ce)]['addCoDirector'][_0x5972cd][_0x71e5c3(0x721)]());var _0x3e6659=getById('container_'+_0x27465c['directorSettings'][_0x71e5c3(0x1d7)][_0x5972cd]['toString']());_0x3e6659&&_0x3e6659[_0x71e5c3(0x424)]['add'](_0x71e5c3(0x278));}}}catch(_0x55e304){errorlog(_0x55e304);}if(_0x71e5c3(0x4fb)in _0x27465c)try{_0x311669[_0x71e5c3(0x8a1)]({'cbid':_0x27465c[_0x71e5c3(0x4fb)]},_0x1f2fd5);}catch(_0x1eee2a){errorlog(_0x1eee2a);}}if(_0x71e5c3(0x57f)in _0x27465c){if(_0x311669['remote']){if(_0x71e5c3(0x24d)in _0x27465c&&_0x27465c[_0x71e5c3(0x24d)]===_0x311669[_0x71e5c3(0x24d)]&&_0x311669[_0x71e5c3(0x24d)])_0x311669['remoteZoom'](parseFloat(_0x27465c[_0x71e5c3(0x57f)]));else _0x311669[_0x71e5c3(0x24d)]===!![]&&_0x311669[_0x71e5c3(0x403)](parseFloat(_0x27465c[_0x71e5c3(0x57f)]));}else{if(_0x311669['directorList'][_0x71e5c3(0x49e)](_0x1dae43||_0x1f2fd5)>=0x0)_0x311669[_0x71e5c3(0x403)](parseFloat(_0x27465c[_0x71e5c3(0x57f)]));else return;}}if(_0x71e5c3(0x21a)in _0x27465c){if(_0x311669[_0x71e5c3(0x24d)]){if(_0x71e5c3(0x24d)in _0x27465c&&_0x27465c[_0x71e5c3(0x24d)]===_0x311669[_0x71e5c3(0x24d)]&&_0x311669[_0x71e5c3(0x24d)])_0x311669[_0x71e5c3(0x2a6)](parseFloat(_0x27465c['focus']));else _0x311669[_0x71e5c3(0x24d)]===!![]&&_0x311669[_0x71e5c3(0x2a6)](parseFloat(_0x27465c[_0x71e5c3(0x21a)]));}else{if(_0x311669[_0x71e5c3(0x95b)][_0x71e5c3(0x49e)](_0x1dae43||_0x1f2fd5)>=0x0)_0x311669['remoteFocus'](parseFloat(_0x27465c[_0x71e5c3(0x21a)]));else return;}}if(_0x71e5c3(0x91a)in _0x27465c){log('requestFile');try{_0x311669[_0x71e5c3(0x8ca)](_0x1f2fd5,_0x27465c[_0x71e5c3(0x91a)]);}catch(_0x426a58){errorlog(_0x426a58);}}_0x71e5c3(0x1e4)in _0x27465c&&playbackMIDI(_0x27465c['midi'],!![]);}catch(_0xeda637){errorlog(_0xeda637);}if('rejected'in _0x27465c){if(_0x27465c[_0x71e5c3(0x485)]=='obsCommand'){if(_0x311669['remote'])warnUser(getTranslation(_0x71e5c3(0x8e3)),0xbb8);else document[_0x71e5c3(0xa5d)](_0x71e5c3(0x85e))&&document['querySelector'](_0x71e5c3(0x85e))[_0x71e5c3(0x8d7)]?warnUser(getTranslation(_0x71e5c3(0xa41)),0x1b58):warnUser(getTranslation('request-rejected-obs'),0x2710);getById(_0x71e5c3(0x684))[_0x71e5c3(0x424)][_0x71e5c3(0x761)](_0x71e5c3(0x472));}else{if(_0x311669[_0x71e5c3(0x75b)])!_0x311669[_0x71e5c3(0x78c)]&&warnUser(_0x71e5c3(0x55b)+_0x27465c[_0x71e5c3(0x485)]+_0x71e5c3(0x2c9),0x1388);else!_0x311669[_0x71e5c3(0x78c)]&&(_0x311669[_0x71e5c3(0x24d)]?warnUser(_0x71e5c3(0x1e2),0x1388):warnUser(_0x71e5c3(0x9f9),0x1388));}errorlog('ACTION\x20REJECTED:\x20'+_0x27465c[_0x71e5c3(0x485)]+_0x71e5c3(0x1ed)+_0x311669[_0x71e5c3(0x75b)]),pokeIframeAPI(_0x71e5c3(0x485),_0x27465c[_0x71e5c3(0x485)],_0x1f2fd5);return;}else{if(_0x71e5c3(0x634)in _0x27465c){log(_0x71e5c3(0x7ef)+_0x27465c[_0x71e5c3(0x634)]),pokeIframeAPI(_0x71e5c3(0x634),_0x27465c[_0x71e5c3(0x634)],_0x1f2fd5);return;}}if(_0x71e5c3(0x433)in _0x27465c||'video'in _0x27465c){log(_0x71e5c3(0xa56));_0x27465c[_0x71e5c3(0x433)]&&(_0x311669['pcs'][_0x1f2fd5]['allowAudio']=!![]);if(_0x311669[_0x71e5c3(0x352)]&&_0x71e5c3(0x81a)in _0x27465c&&_0x27465c['allowwebp']!==![])_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x7c3)]=_0x27465c[_0x71e5c3(0x81a)],_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x670)]=![],setTimeout(function(){makeImages(!![]);},0x3e8);else _0x27465c[_0x71e5c3(0x30c)]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['allowVideo']=!![]);_0x71e5c3(0x254)in _0x27465c&&_0x27465c[_0x71e5c3(0x254)]!==![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x211)]=_0x27465c[_0x71e5c3(0x254)]);_0x71e5c3(0x9b8)in _0x27465c&&_0x27465c[_0x71e5c3(0x9b8)]!==![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x97b)]=_0x27465c[_0x71e5c3(0x9b8)]);_0x71e5c3(0x54d)in _0x27465c&&_0x27465c['iframe']!==![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x299)]=_0x27465c['iframe']);'widget'in _0x27465c&&_0x27465c[_0x71e5c3(0x8b0)]!==![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x368)]=_0x27465c[_0x71e5c3(0x8b0)]);_0x71e5c3(0x622)in _0x27465c&&_0x27465c[_0x71e5c3(0x622)]!==![]&&(_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x87f)]=_0x27465c['allowmidi']);_0x71e5c3(0x5bf)in _0x27465c&&_0x27465c['downloads']!==![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x681)]=_0x27465c[_0x71e5c3(0x5bf)]);_0x71e5c3(0x25c)in _0x27465c&&_0x27465c[_0x71e5c3(0x25c)]!==![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x6b0)]=!![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0xa6b)]=!![]);_0x71e5c3(0x4ea)in _0x27465c&&_0x27465c['allowscreenvideo']!==![]&&(_0x311669['pcs'][_0x1f2fd5]['allowScreenVideo']=!![]);_0x71e5c3(0x343)in _0x27465c&&_0x27465c[_0x71e5c3(0x343)]!==![]&&(_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x6b0)]=!![]);_0x71e5c3(0x3aa)in _0x27465c&&_0x27465c[_0x71e5c3(0x3aa)]!==![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['preferVideoCodec']=_0x27465c[_0x71e5c3(0x3aa)][_0x71e5c3(0x62c)]());'preferAudioCodec'in _0x27465c&&_0x27465c[_0x71e5c3(0x5ea)]!==![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x5ea)]=_0x27465c[_0x71e5c3(0x5ea)][_0x71e5c3(0x62c)]());if(_0x71e5c3(0x1c0)in _0x27465c&&_0x27465c['allowmeshcast']===![])_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['whipout']=![];else{if(_0x71e5c3(0x7c7)in _0x27465c&&_0x27465c[_0x71e5c3(0x7c7)]===![])_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x45a)]=![];else{if(_0x311669[_0x71e5c3(0x98c)]){if(_0x311669['meshcast']=='video')_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x670)]=![];else{if(_0x311669['meshcast']=='audio')_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x760)]=![];else _0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x670)]==![]?_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['whipout']=![]:(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x760)]=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x670)]=![]);}}else _0x311669[_0x71e5c3(0x2d6)]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['allowAudio']=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x670)]=![]);}}if(_0x311669[_0x71e5c3(0x84e)]){playtone(),window[_0x71e5c3(0x21a)]();var _0x1bdd64=![];_0x1f2fd5 in _0x311669[_0x71e5c3(0x16a)]&&_0x311669[_0x71e5c3(0x16a)][_0x1f2fd5]['label']&&(_0x1bdd64=_0x311669[_0x71e5c3(0x16a)][_0x1f2fd5]['label']||_0x311669[_0x71e5c3(0x16a)][_0x1f2fd5]['streamID']||![]);_0x1bdd64=_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x89c)]||_0x1bdd64||_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x9bb)]||_0x1f2fd5||_0x71e5c3(0x99c);var _0x158b0f=await confirmAlt(_0x1bdd64+getTranslation(_0x71e5c3(0x753)),!![]);if(!_0x158b0f){try{log(_0x71e5c3(0xa62)),_0x311669[_0x71e5c3(0x9d2)](_0x1f2fd5);}catch(_0x52865e){}return;}}_0x71e5c3(0x798)in _0x27465c&&(_0x27465c[_0x71e5c3(0x798)]==!![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x798)]=!![],_0x311669[_0x71e5c3(0x3df)]&&(playtone(![],_0x71e5c3(0x7a9)),showNotification(_0x71e5c3(0x300),'')),pokeIframeAPI(_0x71e5c3(0x439),_0x27465c[_0x71e5c3(0x75b)],_0x1f2fd5)));_0x71e5c3(0x920)in _0x27465c&&(_0x27465c[_0x71e5c3(0x920)]===!![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['forceios']=!![]));_0x71e5c3(0x24d)in _0x27465c&&(_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x24d)]=_0x27465c[_0x71e5c3(0x24d)]);_0x71e5c3(0x6f0)in _0x27465c&&(_0x27465c['limitaudio']==!![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0xa0d)]=!![]));_0x71e5c3(0x391)in _0x27465c&&(_0x27465c[_0x71e5c3(0x391)]==!![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['enhanceAudio']=!![]));_0x27465c[_0x71e5c3(0x288)]&&(_0x311669['pcs'][_0x1f2fd5]['degradationPreference']=_0x27465c[_0x71e5c3(0x288)]);if(_0x71e5c3(0x458)in _0x27465c)try{_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x458)]=_0x27465c[_0x71e5c3(0x458)],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x458)]&&setTimeout(function(_0x509b92){var _0x105a86=_0x71e5c3;_0x311669[_0x105a86(0x2e3)](_0x509b92);},0x1388,_0x1f2fd5);}catch(_0x52171f){warnlog(_0x52171f);}_0x71e5c3(0x438)in _0x27465c&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x438)]=_0x27465c[_0x71e5c3(0x438)]);_0x71e5c3(0x559)in _0x27465c&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x559)]=_0x27465c['layout'],!_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x559)]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x559)]=_0x27465c['layout'],_0x311669[_0x71e5c3(0x15f)]&&_0x311669[_0x71e5c3(0x75b)]&&_0x311669['pcs'][_0x1f2fd5]&&_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x559)]&&createSlotUpdate(_0x1f2fd5)));if(_0x71e5c3(0x98d)in _0x27465c){if(_0x27465c[_0x71e5c3(0x98d)]!==![]){try{typeof _0x27465c['scene']==='string'?_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x98d)]=_0x27465c[_0x71e5c3(0x98d)]['replace'](/[\W]+/g,'_'):_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x98d)]=(parseInt(_0x27465c['scene'])||0x0)+'',_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x436)][_0x71e5c3(0x98d)]=_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x98d)],updateSceneList(_0x311669['pcs'][_0x1f2fd5]['scene']);}catch(_0x52c932){errorlog(_0x52c932);}_0x71e5c3(0x560)in _0x27465c?_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x560)]=_0x27465c[_0x71e5c3(0x560)]:_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x560)]=_0x311669[_0x71e5c3(0x560)];if(_0x311669[_0x71e5c3(0x75b)]){if(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['showDirector']==![])_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x760)]=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x670)]=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x299)]=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['allowWidget']=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x45a)]=![],_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x7c3)]=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['allowScreenAudio']=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0xa6b)]=![];else{if(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x560)]==0x1)_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x299)]=![],_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x368)]=![];else{if(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x560)]==0x2)_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x760)]=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['allowScreenAudio']=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x299)]=![],_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x368)]=![];else{if(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['showDirector']==0x3)_0x311669['pcs'][_0x1f2fd5]['allowAudio']=![],_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x670)]=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x299)]=![],_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x368)]=![],_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x45a)]=![],_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x7c3)]=![];else{if(_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x560)]==0x4){}}}}}}_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['solo']?pokeIframeAPI(_0x71e5c3(0x885),_0x27465c[_0x71e5c3(0x98d)],_0x1f2fd5):pokeIframeAPI('scene-connected',_0x27465c[_0x71e5c3(0x98d)],_0x1f2fd5);}_0x311669[_0x71e5c3(0x464)](_0x1f2fd5);}else _0x27465c[_0x71e5c3(0x75b)]&&((iOS||iPad)&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x920)]==!![]&&(_0x311669['pcs'][_0x1f2fd5]['guest']=!![])),_0x311669[_0x71e5c3(0x3df)]&&(playtone(![],_0x71e5c3(0x7a9)),showNotification(_0x71e5c3(0x1ab),_0x71e5c3(0x6ac))),_0x311669[_0x71e5c3(0x464)](_0x1f2fd5),pokeIframeAPI(_0x71e5c3(0x56e),_0x27465c[_0x71e5c3(0x75b)],_0x1f2fd5));_0x311669[_0x71e5c3(0x75b)]&&(_0x71e5c3(0x3cb)in _0x27465c&&(_0x27465c[_0x71e5c3(0x3cb)]==!![]&&(_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x760)]=![],_0x311669['pcs'][_0x1f2fd5][_0x71e5c3(0x670)]=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x299)]=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['allowWidget']=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5][_0x71e5c3(0x45a)]=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['allowWebp']=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['allowScreenAudio']=![],_0x311669[_0x71e5c3(0x859)][_0x1f2fd5]['allowScreenVideo']=![]))),_0x311669[_0x71e5c3(0xa1d)](_0x1f2fd5);}},_0x311669[_0x42f4f2(0x464)]=function(_0x3fa4bb){var _0x214690=_0x42f4f2;if(!(_0x311669[_0x214690(0x772)]||_0x311669[_0x214690(0x98d)]))return;try{var _0x155aba={};_0x311669[_0x214690(0x859)][_0x3fa4bb]&&(_0x155aba[_0x214690(0x8ce)]=getDirectorSettings(_0x311669[_0x214690(0x859)][_0x3fa4bb][_0x214690(0x98d)]));log(_0x214690(0x8c1)+_0x3fa4bb);var _0x479814=![];_0x311669['alreadyJoinedMembers']&&_0x311669[_0x214690(0x9e8)]['forEach'](_0x3faa69=>{var _0x2ed52e=_0x214690;_0x3faa69[_0x2ed52e(0x54a)]===_0x3fa4bb&&(_0x479814=!![]);}),!_0x479814?_0x155aba[_0x214690(0x772)]=getDetailedState():warnlog(_0x214690(0x408)),Object[_0x214690(0x19b)](_0x155aba)[_0x214690(0x8e9)]&&_0x311669[_0x214690(0x2db)](_0x155aba,_0x3fa4bb);}catch(_0x3f596d){}},_0x311669[_0x42f4f2(0xa1d)]=function(_0xf0ced3){var _0x3827fd=_0x42f4f2;log(_0x3827fd(0x442)+_0xf0ced3);if(_0xf0ced3 in _0x311669[_0x3827fd(0x859)]){}else{errorlog(_0x3827fd(0x31c));return;}getSenders2(_0xf0ced3)['length']&&errorlog(_0x3827fd(0x90e)+getSenders2(_0xf0ced3)['length']);if(_0x311669[_0x3827fd(0x859)][_0xf0ced3][_0x3827fd(0x299)]===!![]){if(_0x311669[_0x3827fd(0x325)]){var _0x3de62e={};_0x3de62e[_0x3827fd(0x325)]=_0x311669[_0x3827fd(0x325)],_0x311669[_0x3827fd(0x9b0)]&&_0x311669[_0x3827fd(0x9b0)]['sendOnNewConnect']&&(_0x311669[_0x3827fd(0x325)][_0x3827fd(0x62b)](_0x3827fd(0x26a))&&(_0x3de62e['iframeSrc']+=_0x3827fd(0x31f)+parseInt(Math[_0x3827fd(0x2c3)](_0x311669[_0x3827fd(0x9b0)][_0x3827fd(0x6ba)][_0x3827fd(0x90a)]['t']))+'')),_0x311669[_0x3827fd(0x8a1)](_0x3de62e,_0xf0ced3);}}if(_0x311669[_0x3827fd(0x859)][_0xf0ced3][_0x3827fd(0x368)]===!![]){if(_0x311669[_0x3827fd(0x8b0)]&&_0x311669['director']){var _0x3de62e={};_0x3de62e[_0x3827fd(0x2f6)]=_0x311669['widget'],_0x311669['sendMessage'](_0x3de62e,_0xf0ced3);}}_0x311669['pcs'][_0xf0ced3][_0x3827fd(0x681)]===!![]&&_0x311669[_0x3827fd(0x869)](_0xf0ced3);if(_0x311669['chunked']&&_0x311669['pcs'][_0xf0ced3][_0x3827fd(0x97b)]){_0x311669[_0x3827fd(0x504)](_0xf0ced3);return;}var _0x109764=_0x311669[_0x3827fd(0x700)]();log(_0x3827fd(0x4e2)),log(_0x109764[_0x3827fd(0x951)]());if(_0x311669[_0x3827fd(0x948)]&&_0x311669['pcs'][_0xf0ced3][_0x3827fd(0x45a)]===null){_0x311669[_0x3827fd(0x859)][_0xf0ced3][_0x3827fd(0x45a)]=!![];var _0x3de62e={};_0x3de62e['whepSettings']=_0x311669['whipoutSettings'],_0x311669['sendMessage'](_0x3de62e,_0xf0ced3),warnlog(_0x3de62e);}(_0x311669[_0x3827fd(0x859)][_0xf0ced3][_0x3827fd(0xa6b)]||_0x311669[_0x3827fd(0x859)][_0xf0ced3][_0x3827fd(0x6b0)])&&createSecondStream2(_0xf0ced3);var _0x221555=![];_0x109764[_0x3827fd(0x87a)]()[_0x3827fd(0x982)](_0x411454=>{var _0x5ec9f4=_0x3827fd;try{_0x311669[_0x5ec9f4(0x859)][_0xf0ced3][_0x5ec9f4(0x670)]===!![]&&(_0x411454[_0x5ec9f4(0x7ad)]==_0x5ec9f4(0x30c)&&(_0x311669[_0x5ec9f4(0x859)][_0xf0ced3][_0x5ec9f4(0x798)]===!![]&&_0x311669[_0x5ec9f4(0x250)]===0x0?log(_0x5ec9f4(0x9ec)):(_0x311669['pcs'][_0xf0ced3][_0x5ec9f4(0x58b)](_0x411454,_0x109764),warnlog(_0x5ec9f4(0x4c3)),_0x221555=!![],setTimeout(function(_0x44da1e){try{_0x311669['optimizeBitrate'](_0x44da1e);}catch(_0x9aa45e){warnlog(_0x9aa45e);}},_0x311669[_0x5ec9f4(0x25e)],_0xf0ced3))));}catch(_0x506266){errorlog(_0x506266);}});_0x311669[_0x3827fd(0x655)]&&(_0x109764=mixMinusAudio(_0xf0ced3));_0x311669[_0x3827fd(0x859)][_0xf0ced3][_0x3827fd(0x760)]&&(_0x109764['getAudioTracks']()[_0x3827fd(0x982)](_0x504fcc=>{var _0x2ec919=_0x3827fd;try{_0x504fcc[_0x2ec919(0x7ad)]==_0x2ec919(0x433)&&(_0x311669[_0x2ec919(0x859)][_0xf0ced3][_0x2ec919(0x58b)](_0x504fcc,_0x109764),warnlog('added\x20audio\x20track'));}catch(_0x13cc96){errorlog(_0x13cc96);}}),log('does\x20any\x20audio\x20exist?'),_0x109764[_0x3827fd(0xa6c)]()['length']&&(_0x311669[_0x3827fd(0x75b)]!==![]&&_0x311669[_0x3827fd(0x1f6)](),log(_0x3827fd(0x883)),_0x311669[_0x3827fd(0x859)][_0xf0ced3][_0x3827fd(0x5ea)]==_0x3827fd(0x61d)&&lyraEncode(_0xf0ced3),_0x311669[_0x3827fd(0x859)][_0xf0ced3][_0x3827fd(0xa0d)]===!![]&&(warnlog(_0x3827fd(0x4fa)),setTimeout(_0x311669[_0x3827fd(0x33d)],0x3e8,_0xf0ced3,0x7d00,0x0)),_0x311669[_0x3827fd(0x859)][_0xf0ced3][_0x3827fd(0x410)]===!![]&&setTimeout(_0x311669['enhanceAudioEncoder'],0x3e8,_0xf0ced3)));if(_0x311669[_0x3827fd(0x859)][_0xf0ced3][_0x3827fd(0x5c7)])setTimeout(_0x311669[_0x3827fd(0x5c7)],0x3e8,_0xf0ced3,_0x311669[_0x3827fd(0x859)][_0xf0ced3][_0x3827fd(0x5c7)]);else{if(_0x311669[_0x3827fd(0x34f)]&&SafariVersion){if(_0x311669[_0x3827fd(0x34f)]=='detail')setTimeout(_0x311669[_0x3827fd(0x5c7)],0x3e8,_0xf0ced3,'maintain-resolution');else _0x311669[_0x3827fd(0x34f)]==_0x3827fd(0x743)&&setTimeout(_0x311669[_0x3827fd(0x5c7)],0x3e8,_0xf0ced3,'maintain-framerate');}}if(iOS||iPad){if(SafariVersion&&SafariVersion<=0xd){}else _0x221555&&(setTimeout(function(_0x50012b){_0x311669['setScale'](_0x50012b,null,!![]);},0x7d0,_0xf0ced3),setTimeout(function(_0x25098a){var _0x1664d8=_0x3827fd,_0x5598b2=_0x311669[_0x1664d8(0x164)](_0x25098a);!_0x5598b2&&_0x311669[_0x1664d8(0x88b)](_0x25098a,0x64,!![]);},0x1388,_0xf0ced3));}else setTimeout(function(_0x41fed3){var _0x1b02ad=_0x3827fd;_0x311669[_0x1b02ad(0x164)](_0x41fed3);},0x3e8,_0xf0ced3);};function _0x5af428(_0x3e5866,_0x3e3fd0,_0xe300f0){var _0x419597=_0x42f4f2,_0x55a2eb=new Blob([_0x3e5866],{'type':'text/plain'}),_0x575da7=new FileReader();_0x575da7['onload']=function(_0x63efb1){var _0x8355b5=_0x5c68;_0xe300f0(_0x63efb1[_0x8355b5(0x81f)]['result']);},_0x575da7[_0x419597(0x95c)](_0x55a2eb,_0x3e3fd0);}_0x311669[_0x42f4f2(0x869)]=function(_0x4190d5){var _0x496ce4=_0x42f4f2;log(_0x496ce4(0x967));if(!_0x311669[_0x496ce4(0x972)]||!_0x311669[_0x496ce4(0x972)]['length'])return;var _0x2907ce={},_0x479067=[];for(var _0x176d9c=0x0;_0x176d9c<_0x311669[_0x496ce4(0x972)][_0x496ce4(0x8e9)];_0x176d9c++){(_0x311669['hostedFiles'][_0x176d9c][_0x496ce4(0x474)]===![]||_0x311669[_0x496ce4(0x972)][_0x176d9c]['restricted']===_0x4190d5)&&_0x479067[_0x496ce4(0x505)]({'id':_0x311669[_0x496ce4(0x972)][_0x176d9c]['id'],'name':_0x311669[_0x496ce4(0x972)][_0x176d9c][_0x496ce4(0x6dd)],'size':_0x311669[_0x496ce4(0x972)][_0x176d9c][_0x496ce4(0x8cf)]});}_0x2907ce[_0x496ce4(0x2e8)]=_0x479067;if(_0x4190d5 in _0x311669[_0x496ce4(0x859)])_0x311669[_0x496ce4(0x8a1)](_0x2907ce,_0x4190d5);else _0x4190d5 in _0x311669[_0x496ce4(0x16a)]&&_0x311669[_0x496ce4(0xa14)](_0x2907ce,_0x4190d5);log(_0x2907ce);},_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x183)]=function(_0x233209){var _0x23bc9d=_0x42f4f2;if(!(_0x2551e1 in _0x311669[_0x23bc9d(0x859)]))return;try{if(this['iceConnectionState']===_0x23bc9d(0x208))log(_0x23bc9d(0x40c));else{if(this[_0x23bc9d(0x870)]===_0x23bc9d(0x381))log('PCS:\x20ICE\x20Disconnected;\x20wait\x20for\x20retry?\x20pcs');else{if(this[_0x23bc9d(0x870)]==='failed')log('ICE\x20FAILed.\x20bad?'),_0x311669[_0x23bc9d(0x859)][_0x2551e1][_0x23bc9d(0x9b6)]?_0x311669['pcs'][_0x2551e1][_0x23bc9d(0x9b6)]():_0x311669[_0x23bc9d(0x903)](_0x2551e1,!![]);else this[_0x23bc9d(0x870)]===_0x23bc9d(0x7ea)?log(_0x23bc9d(0x73c)):log(this[_0x23bc9d(0x870)]);}}}catch(_0x3820cc){errorlog(_0x3820cc);}},_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x6e5)]=function(_0x1ced25){var _0x1c0ed5=_0x42f4f2;switch(_0x311669['pcs'][_0x2551e1][_0x1c0ed5(0x3ee)]){case'connected':log(_0x1c0ed5(0x9a0)),clearTimeout(_0x311669[_0x1c0ed5(0x859)][_0x2551e1][_0x1c0ed5(0x9a4)]);if(_0x311669[_0x1c0ed5(0x4e5)]){if(_0x311669['ws'][_0x1c0ed5(0x8ee)]!==0x1){_0x311669['ws']['close']();break;}_0x311669['ws']['close'](),setTimeout(function(){var _0x210e8e=_0x1c0ed5;_0x311669[_0x210e8e(0x78c)]!=!![]&&warnUser(getTranslation(_0x210e8e(0x320)));},0x1);}break;case _0x1c0ed5(0x381):log(_0x1c0ed5(0x773)),clearTimeout(_0x311669[_0x1c0ed5(0x859)][_0x2551e1]['closeTimeout']),_0x311669[_0x1c0ed5(0x859)][_0x2551e1][_0x1c0ed5(0x9a4)]=setTimeout(function(_0x46ec55){var _0x5235e4=_0x1c0ed5;_0x46ec55 in _0x311669['pcs']?(warnlog(_0x5235e4(0x2cd)),log(_0x5235e4(0x475)),_0x311669['closePC'](_0x46ec55)):errorlog(_0x5235e4(0x7b2));},0x2710,_0x2551e1);break;case _0x1c0ed5(0x190):warnlog(_0x1c0ed5(0x4d2)),clearTimeout(_0x311669[_0x1c0ed5(0x859)][_0x2551e1]['closeTimeout']),warnlog(_0x1c0ed5(0x376)+_0x311669[_0x1c0ed5(0x859)][_0x2551e1][_0x1c0ed5(0x3ee)]),_0x311669[_0x1c0ed5(0x859)][_0x2551e1]['closeTimeout']=setTimeout(function(_0x235d77){var _0xa5f4f2=_0x1c0ed5;_0x235d77 in _0x311669[_0xa5f4f2(0x859)]?(warnlog(_0xa5f4f2(0x2cd)),log(_0xa5f4f2(0x2bf)),_0x311669[_0xa5f4f2(0x9d2)](_0x235d77)):errorlog('\x20---\x20PC\x20TIMED\x20OUT\x20and\x20already\x20deleted.\x20shouldn\x27t\x20happen');},0xbb8,_0x2551e1);break;case _0x1c0ed5(0x208):warnlog(_0x1c0ed5(0x3dc)),log(_0x1c0ed5(0x9c4)),_0x311669[_0x1c0ed5(0x9d2)](_0x2551e1);break;default:log('rtc\x20state:\x20'+_0x311669['pcs'][_0x2551e1][_0x1c0ed5(0x3ee)]),clearTimeout(_0x311669[_0x1c0ed5(0x859)][_0x2551e1][_0x1c0ed5(0x9a4)]);break;}},_0x311669['pcs'][_0x2551e1][_0x42f4f2(0x661)]=function(_0x48b2d4){var _0x216f90=_0x42f4f2;warnlog(_0x216f90(0x26b)),log(_0x216f90(0x588)),_0x311669[_0x216f90(0x9d2)](_0x2551e1);},_0x311669[_0x42f4f2(0x859)][_0x2551e1][_0x42f4f2(0x777)]=function _0xd893cd(){var _0x1a0c8e=_0x42f4f2;log(_0x1a0c8e(0x809));};},_0x311669[_0x134a17(0x170)]=function(_0x3b1292){var _0x5914c0=_0x134a17,_0x505cd1=_0x3b1292[_0x5914c0(0x54a)];if(_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x78a)]==_0x5914c0(0x20f))_0x311669[_0x5914c0(0x679)](_0x3b1292),_0x311669[_0x5914c0(0x2bb)](_0x3b1292);else try{if(!(_0x3b1292[_0x5914c0(0x54a)]in _0x311669[_0x5914c0(0x859)]))return;var _0x2c82b7=_0x311669[_0x5914c0(0x84c)];if(_0x311669[_0x5914c0(0x1e3)]&&_0x311669[_0x5914c0(0x859)][_0x3b1292['UUID']]['guest']==!![]&&_0x311669[_0x5914c0(0x859)][_0x3b1292['UUID']][_0x5914c0(0x920)]==![]){if(_0x2c82b7===![]||_0x2c82b7>_0x311669[_0x5914c0(0xa00)]){var _0x260592=Object['keys'](_0x311669[_0x5914c0(0x859)])['length'];if(_0x311669[_0x5914c0(0x5f6)])_0x2c82b7=_0x311669[_0x5914c0(0xa00)];else{if(_0x260592>0x4)_0x2c82b7=_0x311669['lowMobileBitrate'];else(iOS||iPad)&&SafariVersion&&SafariVersion<=0xd?_0x2c82b7=_0x311669[_0x5914c0(0x6c1)]:_0x2c82b7=_0x311669[_0x5914c0(0xa00)];}}if(iOS||iPad){if(_0x2c82b7!==![]){if(_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x969)]===![])_0x311669[_0x5914c0(0x859)][_0x3b1292['UUID']][_0x5914c0(0x9f3)]=_0x2c82b7,_0x3b1292['description'][_0x5914c0(0x648)]=CodecsHandler[_0x5914c0(0x37c)](_0x3b1292['description'][_0x5914c0(0x648)],'vp8'),_0x3b1292[_0x5914c0(0x9f5)]['sdp']=CodecsHandler[_0x5914c0(0x5bb)](_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)],{'min':parseInt(_0x2c82b7/0xa)||0x1,'max':_0x2c82b7});else _0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]]['savedBitrate']>_0x2c82b7&&(_0x311669['pcs'][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x9f3)]=_0x2c82b7,_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)]=CodecsHandler[_0x5914c0(0x37c)](_0x3b1292['description'][_0x5914c0(0x648)],'vp8'),_0x3b1292[_0x5914c0(0x9f5)]['sdp']=CodecsHandler[_0x5914c0(0x5bb)](_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)],{'min':parseInt(_0x2c82b7/0xa)||0x1,'max':_0x2c82b7}));_0x2c82b7=![];}}}else{if(_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x798)]==!![])_0x2c82b7!==![]?_0x311669[_0x5914c0(0x250)]!==![]&&(_0x311669[_0x5914c0(0x250)]<_0x2c82b7&&(_0x2c82b7=_0x311669['roombitrate'])):_0x2c82b7=_0x311669[_0x5914c0(0x250)],(iOS||iPad)&&_0x311669['pcs'][_0x3b1292['UUID']][_0x5914c0(0x920)]&&(_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]]['encoder']=!![]);else{if(iOS||iPad){var _0x24631b=0x0;for(var _0x33ac31 in _0x311669['pcs']){_0x3b1292[_0x5914c0(0x54a)]!==_0x33ac31&&(_0x311669[_0x5914c0(0x859)][_0x33ac31][_0x5914c0(0x27c)]===!![]&&(_0x24631b+=0x1));}if(_0x24631b>=0x3){if(_0x311669[_0x5914c0(0x859)][_0x3b1292['UUID']][_0x5914c0(0x920)])_0x311669['pcs'][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x27c)]=!![],_0x311669[_0x5914c0(0x859)][_0x3b1292['UUID']][_0x5914c0(0x3aa)]&&_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]===_0x5914c0(0x57e)&&(_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)]=CodecsHandler['preferCodec'](_0x3b1292['description'][_0x5914c0(0x648)],_0x5914c0(0x57e)),log(_0x5914c0(0x5fe)+_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]+_0x5914c0(0x685)));else _0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]]['preferVideoCodec']&&_0x311669['pcs'][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]===_0x5914c0(0x609)?(_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)]=CodecsHandler[_0x5914c0(0x37c)](_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)],_0x5914c0(0x609)),log('Trying\x20to\x20set\x20'+_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]+_0x5914c0(0x685)),_0x311669['pcs'][_0x3b1292['UUID']][_0x5914c0(0x27c)]=![]):(_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)]=CodecsHandler[_0x5914c0(0x37c)](_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)],_0x5914c0(0x5de)),log('Setting\x20Codec\x20to\x20vp8'),_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]]['encoder']=![]);}else _0x311669['pcs'][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]&&_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]!==_0x5914c0(0x57e)?_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]]['preferVideoCodec']===_0x5914c0(0x609)||_0x311669['pcs'][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]===_0x5914c0(0x5de)?(_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)]=CodecsHandler['preferCodec'](_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)],_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]]['preferVideoCodec']),log(_0x5914c0(0x5fe)+_0x311669['pcs'][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]+'\x20as\x20preferred\x20codec\x20by\x20viewer\x20via\x20API'),_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]]['encoder']=![]):_0x311669['pcs'][_0x3b1292['UUID']][_0x5914c0(0x27c)]=!![]:(_0x311669['pcs'][_0x3b1292['UUID']]['encoder']=!![],_0x311669['pcs'][_0x3b1292['UUID']][_0x5914c0(0x3aa)]&&_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]==='h264'&&(_0x3b1292['description'][_0x5914c0(0x648)]=CodecsHandler[_0x5914c0(0x37c)](_0x3b1292['description'][_0x5914c0(0x648)],'h264'),log(_0x5914c0(0x5fe)+_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]+_0x5914c0(0x685))));}else _0x311669['pcs'][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]&&(_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)]=CodecsHandler['preferCodec'](_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)],_0x311669['pcs'][_0x3b1292['UUID']]['preferVideoCodec']),log(_0x5914c0(0x5fe)+_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x3aa)]+_0x5914c0(0x685)));}}if(_0x2c82b7){var _0x256552=CodecsHandler['getVideoBitrates'](_0x3b1292['description'][_0x5914c0(0x648)]);log('BITRATE\x201:\x20'+_0x256552);_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x969)]!==![]&&(_0x311669[_0x5914c0(0x859)][_0x3b1292['UUID']][_0x5914c0(0x969)]<_0x2c82b7&&(_0x2c82b7=![]));if(_0x2c82b7===![])_0x311669[_0x5914c0(0x859)][_0x3b1292['UUID']]['setBitrate']=_0x256552;else{if(_0x256552!==![]&&_0x256552>_0x2c82b7){var _0x60f23a=CodecsHandler[_0x5914c0(0x580)](_0x3b1292['description'][_0x5914c0(0x648)])||0x0;_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)]=CodecsHandler[_0x5914c0(0x5bb)](_0x3b1292[_0x5914c0(0x9f5)]['sdp'],{'min':parseInt(_0x2c82b7/0xa)||0x1,'max':parseInt(_0x2c82b7+_0x60f23a/0x400)}),_0x311669[_0x5914c0(0x859)][_0x3b1292['UUID']]['setBitrate']=_0x2c82b7;}else{if(_0x256552===![]){var _0x60f23a=CodecsHandler[_0x5914c0(0x580)](_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)])||0x0;_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)]=CodecsHandler[_0x5914c0(0x5bb)](_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)],{'min':parseInt(_0x2c82b7/0xa)||0x1,'max':parseInt(_0x2c82b7+_0x60f23a/0x400)});if(_0x311669['outboundVideoBitrate']&&_0x311669['outboundVideoBitrate']>_0x2c82b7)_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]]['setBitrate']=_0x2c82b7;else _0x311669[_0x5914c0(0x810)]?_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]]['setBitrate']=_0x311669[_0x5914c0(0x810)]:_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x969)]=0x9c4;}else _0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x9f3)]=_0x256552;}}}else{if(_0x311669[_0x5914c0(0x810)]!==![]){var _0x256552=CodecsHandler[_0x5914c0(0x5f1)](_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)]);log(_0x5914c0(0x314)+_0x256552);if(_0x256552===![]){var _0x60f23a=CodecsHandler[_0x5914c0(0x580)](_0x3b1292['description'][_0x5914c0(0x648)])||0x0;_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)]=CodecsHandler['setVideoBitrates'](_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)],{'min':parseInt(_0x311669[_0x5914c0(0x810)]/0xa)||0x1,'max':parseInt(_0x311669[_0x5914c0(0x810)]+_0x60f23a/0x400)});}else _0x311669['pcs'][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x9f3)]===![]&&(_0x311669[_0x5914c0(0x859)][_0x3b1292['UUID']][_0x5914c0(0x9f3)]=_0x256552);}else _0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x9f3)]===![]&&(_0x311669['pcs'][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x9f3)]=CodecsHandler[_0x5914c0(0x5f1)](_0x3b1292[_0x5914c0(0x9f5)][_0x5914c0(0x648)]),log(_0x5914c0(0x9ce)+_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]]['setBitrate']));}_0x311669[_0x5914c0(0x28e)]&&(_0x3b1292['description']['sdp']=CodecsHandler[_0x5914c0(0x608)](_0x3b1292['description'][_0x5914c0(0x648)],{'maxaveragebitrate':_0x311669[_0x5914c0(0x28e)]*0x400,'cbr':_0x311669['cbr']}));if(_0x5914c0(0x1fd)in _0x3b1292&&_0x3b1292[_0x5914c0(0x1fd)]!=_0x311669['pcs'][_0x3b1292[_0x5914c0(0x54a)]][_0x5914c0(0x1fd)]){errorlog(_0x5914c0(0x6eb));return;}_0x311669[_0x5914c0(0x859)][_0x3b1292[_0x5914c0(0x54a)]]['setRemoteDescription'](_0x3b1292[_0x5914c0(0x9f5)])[_0x5914c0(0x619)]()['catch'](errorlog);}catch(_0x4b4ede){errorlog(_0x4b4ede);}},_0x311669['processDescription']=function(_0x8a319e){var _0x3369d7=_0x134a17;_0x311669[_0x3369d7(0x8a3)]&&_0x3369d7(0x9ed)in _0x8a319e?_0x311669[_0x3369d7(0x4da)](_0x8a319e[_0x3369d7(0x9f5)],_0x8a319e[_0x3369d7(0x9ed)])[_0x3369d7(0x619)](function(_0x271811){var _0x240d90=_0x3369d7;_0x8a319e[_0x240d90(0x9f5)]=JSON[_0x240d90(0x52d)](_0x271811),_0x311669[_0x240d90(0x170)](_0x8a319e);}):_0x311669[_0x3369d7(0x170)](_0x8a319e);},_0x311669[_0x134a17(0x1d3)]=function(_0x49d286){var _0x2f175f=_0x134a17;_0x311669['password']&&_0x2f175f(0x9ed)in _0x49d286?_0x311669[_0x2f175f(0x4da)](_0x49d286[_0x2f175f(0x905)],_0x49d286[_0x2f175f(0x9ed)])[_0x2f175f(0x619)](function(_0x352207){var _0x52647c=_0x2f175f;_0x49d286['candidate']=JSON[_0x52647c(0x52d)](_0x352207),_0x311669[_0x52647c(0xa09)](_0x49d286);}):_0x311669[_0x2f175f(0xa09)](_0x49d286);},_0x311669[_0x134a17(0xa09)]=function(_0x28e3fa){var _0x1318e4=_0x134a17;try{if(_0x311669[_0x1318e4(0x242)]){if(_0x28e3fa[_0x1318e4(0x905)][_0x1318e4(0x905)][_0x1318e4(0x49e)](_0x311669[_0x1318e4(0x242)])===-0x1){log(_0x1318e4(0x6f4)),log(_0x28e3fa[_0x1318e4(0x905)]);return;}else log('PASSED'),log(_0x28e3fa[_0x1318e4(0x905)]);}}catch(_0xc02c6f){errorlog(_0xc02c6f);}if(_0x28e3fa[_0x1318e4(0x905)]&&_0x1318e4(0x905)in _0x28e3fa['candidate']&&_0x28e3fa[_0x1318e4(0x905)]['candidate']=='')return;if(_0x28e3fa[_0x1318e4(0x54a)]in _0x311669['pcs']&&_0x28e3fa[_0x1318e4(0x78a)]==_0x1318e4(0x24d)){log(_0x1318e4(0x6a3));if(_0x1318e4(0x1fd)in _0x28e3fa&&_0x311669[_0x1318e4(0x859)][_0x28e3fa['UUID']]['session']!=_0x28e3fa[_0x1318e4(0x1fd)]){errorlog('Incoming\x20Ice\x20Offer\x20does\x20not\x20match\x20Session');return;}_0x311669[_0x1318e4(0x859)][_0x28e3fa[_0x1318e4(0x54a)]]['addIceCandidate'](_0x28e3fa[_0x1318e4(0x905)])[_0x1318e4(0x619)]()[_0x1318e4(0x3b6)](function(_0x5c5a46){});}else{if(_0x28e3fa[_0x1318e4(0x54a)]in _0x311669[_0x1318e4(0x16a)]&&_0x28e3fa['type']==_0x1318e4(0x628)){log(_0x1318e4(0x383));if(_0x1318e4(0x1fd)in _0x28e3fa&&_0x311669[_0x1318e4(0x16a)][_0x28e3fa[_0x1318e4(0x54a)]][_0x1318e4(0x1fd)]!=_0x28e3fa[_0x1318e4(0x1fd)]){errorlog('Incoming\x20Ice\x20Offer\x20does\x20not\x20match\x20Session');return;}if(_0x311669[_0x1318e4(0x16a)][_0x28e3fa[_0x1318e4(0x54a)]]===null)return;_0x311669[_0x1318e4(0x16a)][_0x28e3fa['UUID']][_0x1318e4(0x8f4)](_0x28e3fa[_0x1318e4(0x905)])[_0x1318e4(0x619)]()['catch'](function(_0x19f1b9){});}else warnlog(_0x28e3fa),errorlog('ICE\x20DID\x20NOT\x20FIND\x20A\x20PC\x20OPTION?\x20peer\x20might\x20have\x20left\x20before\x20ICE\x20complete?');}},_0x311669[_0x134a17(0x411)]=function(_0x5dcd6d){var _0x397e37=_0x134a17;if(_0x311669[_0x397e37(0x8a3)]&&_0x397e37(0x9ed)in _0x5dcd6d)_0x311669['decryptMessage'](_0x5dcd6d[_0x397e37(0x2a8)],_0x5dcd6d['vector'])[_0x397e37(0x619)](function(_0x4fcd6d){var _0x210b9c=_0x397e37;_0x5dcd6d[_0x210b9c(0x2a8)]=JSON[_0x210b9c(0x52d)](_0x4fcd6d);var _0x483174={};_0x483174[_0x210b9c(0x54a)]=_0x5dcd6d['UUID'],_0x483174[_0x210b9c(0x78a)]=_0x5dcd6d[_0x210b9c(0x78a)];for(var _0x1923a2=0x0;_0x1923a2<_0x5dcd6d[_0x210b9c(0x2a8)][_0x210b9c(0x8e9)];_0x1923a2++){_0x483174[_0x210b9c(0x905)]=_0x5dcd6d[_0x210b9c(0x2a8)][_0x1923a2],_0x311669['processIce2'](_0x483174);}});else{var _0x11986f={};_0x11986f[_0x397e37(0x54a)]=_0x5dcd6d['UUID'],_0x11986f[_0x397e37(0x78a)]=_0x5dcd6d[_0x397e37(0x78a)];for(var _0x17808e=0x0;_0x17808e<_0x5dcd6d[_0x397e37(0x2a8)][_0x397e37(0x8e9)];_0x17808e++){_0x11986f['candidate']=_0x5dcd6d[_0x397e37(0x2a8)][_0x17808e],_0x311669['processIce2'](_0x11986f);}}},_0x311669[_0x134a17(0x2bb)]=async function(_0x3da073){var _0x2e344a=_0x134a17;_0x2e344a(0x43e)in _0x3da073&&(_0x311669[_0x2e344a(0x16a)][_0x3da073[_0x2e344a(0x54a)]][_0x2e344a(0x2ef)]=_0x3da073[_0x2e344a(0x43e)],log(_0x2e344a(0x722)),log(_0x3da073[_0x2e344a(0x43e)])),warnlog(_0x3da073),_0x311669[_0x2e344a(0x971)]&&_0x3da073[_0x2e344a(0x9f5)]&&_0x3da073[_0x2e344a(0x9f5)]['sdp']&&_0x3da073[_0x2e344a(0x9f5)][_0x2e344a(0x648)]['includes']('a=extmap:3\x20urn:3gpp:video-orientation\x0d\x0a')&&(_0x3da073[_0x2e344a(0x9f5)][_0x2e344a(0x648)]=_0x3da073[_0x2e344a(0x9f5)][_0x2e344a(0x648)]['replace']('a=extmap:3\x20urn:3gpp:video-orientation\x0d\x0a',''),warnlog(_0x2e344a(0x7e7))),_0x311669[_0x2e344a(0xa5e)]&&(_0x3da073[_0x2e344a(0x9f5)]['sdp']=CodecsHandler[_0x2e344a(0x9d8)](_0x3da073[_0x2e344a(0x9f5)]['sdp'])),_0x311669['noREMB']&&(_0x3da073[_0x2e344a(0x9f5)]['sdp']=CodecsHandler[_0x2e344a(0x5d0)](_0x3da073['description']['sdp'])),_0x311669[_0x2e344a(0x7be)]&&(log(_0x3da073[_0x2e344a(0x9f5)][_0x2e344a(0x648)]),_0x3da073[_0x2e344a(0x9f5)][_0x2e344a(0x648)]=CodecsHandler['disableNACK'](_0x3da073[_0x2e344a(0x9f5)][_0x2e344a(0x648)])),_0x311669[_0x2e344a(0x16a)][_0x3da073[_0x2e344a(0x54a)]][_0x2e344a(0x5fb)](_0x3da073['description'])['then'](async function(){var _0x1a87bb=_0x2e344a;if(_0x311669[_0x1a87bb(0x16a)][_0x3da073[_0x1a87bb(0x54a)]][_0x1a87bb(0x5b4)][_0x1a87bb(0x78a)]==='offer')_0x311669[_0x1a87bb(0x16a)][_0x3da073[_0x1a87bb(0x54a)]]['createAnswer']()[_0x1a87bb(0x619)](function(_0x5d8761){var _0xbe8017=_0x1a87bb;log(_0xbe8017(0x1ae));if(_0x311669['rpcs'][_0x3da073[_0xbe8017(0x54a)]][_0xbe8017(0x751)]){if(_0x311669['stereo']&&_0x311669[_0xbe8017(0x5a8)]==0x4)_0x5d8761['sdp']=CodecsHandler[_0xbe8017(0x608)](_0x5d8761[_0xbe8017(0x648)],{'stereo':0x2},!![]);else _0x311669[_0xbe8017(0x5a8)]&&!_0x311669[_0xbe8017(0x4cf)]&&_0x311669[_0xbe8017(0x5a8)]!=0x3&&(_0x5d8761[_0xbe8017(0x648)]=CodecsHandler[_0xbe8017(0x608)](_0x5d8761[_0xbe8017(0x648)],{'stereo':0x1},!![]));return _0x311669[_0xbe8017(0x16a)][_0x3da073[_0xbe8017(0x54a)]][_0xbe8017(0xa1b)](_0x5d8761);}var _0x36b338=![];if(!_0x311669[_0xbe8017(0x75b)]&&_0x311669[_0xbe8017(0x5a8)]==0x5)_0x36b338={'stereo':0x1,'maxaveragebitrate':(_0x311669[_0xbe8017(0x543)]||0x100)*0x400,'cbr':_0x311669[_0xbe8017(0x441)],'useinbandfec':_0x311669[_0xbe8017(0x5f2)]?0x0:0x1,'maxptime':_0x311669[_0xbe8017(0x5e9)],'minptime':_0x311669['minptime'],'ptime':_0x311669[_0xbe8017(0x72b)],'dtx':_0x311669[_0xbe8017(0x6c6)]},log(_0xbe8017(0x7eb));else{if(_0x311669[_0xbe8017(0x4cf)]&&Firefox)_0x311669[_0xbe8017(0x543)]?_0x36b338={'stereo':0x0,'maxaveragebitrate':_0x311669['audiobitrate']*0x400,'cbr':_0x311669['cbr'],'useinbandfec':_0x311669['noFEC']?0x0:0x1,'maxptime':_0x311669[_0xbe8017(0x5e9)],'minptime':_0x311669[_0xbe8017(0x38b)],'ptime':_0x311669['ptime'],'dtx':_0x311669[_0xbe8017(0x6c6)]}:_0x36b338={'stereo':0x0,'useinbandfec':_0x311669[_0xbe8017(0x5f2)]?0x0:0x1,'maxptime':_0x311669['maxptime'],'minptime':_0x311669[_0xbe8017(0x38b)],'ptime':_0x311669[_0xbe8017(0x72b)],'dtx':_0x311669[_0xbe8017(0x6c6)]};else{if(_0x311669['stereo']==0x1||_0x311669[_0xbe8017(0x5a8)]==0x2||_0x311669['stereo']==0x5)_0x36b338={'stereo':0x1,'maxaveragebitrate':(_0x311669[_0xbe8017(0x543)]||0x100)*0x400,'cbr':_0x311669[_0xbe8017(0x441)],'useinbandfec':_0x311669[_0xbe8017(0x5f2)]?0x0:0x1,'maxptime':_0x311669['maxptime'],'minptime':_0x311669[_0xbe8017(0x38b)],'ptime':_0x311669[_0xbe8017(0x72b)],'dtx':_0x311669[_0xbe8017(0x6c6)]},log(_0xbe8017(0x7eb));else{if(_0x311669[_0xbe8017(0x5a8)]==0x4)_0x36b338={'stereo':0x2,'maxaveragebitrate':(_0x311669[_0xbe8017(0x543)]||0x100)*0x400,'cbr':_0x311669[_0xbe8017(0x441)],'useinbandfec':_0x311669[_0xbe8017(0x5f2)]?0x0:0x1,'maxptime':_0x311669[_0xbe8017(0x5e9)],'minptime':_0x311669[_0xbe8017(0x38b)],'ptime':_0x311669[_0xbe8017(0x72b)],'dtx':_0x311669[_0xbe8017(0x6c6)]};else{if(_0x311669[_0xbe8017(0x543)])_0x36b338={'maxaveragebitrate':_0x311669[_0xbe8017(0x543)]*0x400,'cbr':_0x311669[_0xbe8017(0x441)],'useinbandfec':_0x311669['noFEC']?0x0:0x1,'maxptime':_0x311669['maxptime'],'minptime':_0x311669[_0xbe8017(0x38b)],'ptime':_0x311669[_0xbe8017(0x72b)],'dtx':_0x311669[_0xbe8017(0x6c6)]};else{if(_0x311669[_0xbe8017(0x5f2)])_0x36b338={'useinbandfec':0x0,'maxptime':_0x311669[_0xbe8017(0x5e9)],'minptime':_0x311669[_0xbe8017(0x38b)],'ptime':_0x311669[_0xbe8017(0x72b)],'dtx':_0x311669['dtx']};else _0x311669['dtx']&&(_0x36b338={'maxptime':_0x311669[_0xbe8017(0x5e9)],'minptime':_0x311669[_0xbe8017(0x38b)],'ptime':_0x311669[_0xbe8017(0x72b)],'dtx':_0x311669[_0xbe8017(0x6c6)]});}}}}}_0x311669[_0xbe8017(0x5a8)]===0x6&&(!_0x36b338?_0x36b338={'stereo':0x1}:_0x36b338['stereo']=0x1);_0x36b338&&(_0x5d8761[_0xbe8017(0x648)]=CodecsHandler[_0xbe8017(0x608)](_0x5d8761['sdp'],_0x36b338));if(_0x311669['audioCodec'])try{if(_0x311669[_0xbe8017(0x292)]===_0xbe8017(0x61d))_0x5d8761['sdp']=CodecsHandler['modifyDescLyra'](_0x5d8761['sdp']);else{if(_0x311669[_0xbe8017(0x292)]===_0xbe8017(0x944)){if(_0x311669[_0xbe8017(0x4cf)])_0x5d8761[_0xbe8017(0x648)]=CodecsHandler[_0xbe8017(0x230)](_0x5d8761[_0xbe8017(0x648)],_0x311669[_0xbe8017(0x228)]||0xbb80,![],_0x311669[_0xbe8017(0x72b)]);else _0x311669[_0xbe8017(0x5a8)]?_0x5d8761[_0xbe8017(0x648)]=CodecsHandler[_0xbe8017(0x230)](_0x5d8761['sdp'],_0x311669[_0xbe8017(0x228)]||0x7d00,!![],_0x311669[_0xbe8017(0x72b)]):_0x5d8761[_0xbe8017(0x648)]=CodecsHandler[_0xbe8017(0x230)](_0x5d8761[_0xbe8017(0x648)],_0x311669['sampleRate']||0xbb80,![],_0x311669['ptime']);}else _0x5d8761[_0xbe8017(0x648)]=CodecsHandler[_0xbe8017(0x5ea)](_0x5d8761[_0xbe8017(0x648)],_0x311669[_0xbe8017(0x292)]);}}catch(_0x3cca35){errorlog(_0x3cca35),warnlog(_0xbe8017(0x57a));}if(_0x311669[_0xbe8017(0x4e7)]&&_0x311669[_0xbe8017(0x4e7)][_0xbe8017(0x8e9)])for(var _0x25ee0d=_0x311669[_0xbe8017(0x4e7)][_0xbe8017(0x8e9)]-0x1;_0x25ee0d>=0x0;_0x25ee0d--){try{_0x5d8761['sdp']=CodecsHandler[_0xbe8017(0x37c)](_0x5d8761[_0xbe8017(0x648)],_0x311669[_0xbe8017(0x4e7)][_0x25ee0d]);}catch(_0x21a1c1){errorlog(_0x21a1c1);break;}}_0x311669[_0xbe8017(0x24e)]&&(_0x5d8761['sdp']=CodecsHandler['preferCodec'](_0x5d8761[_0xbe8017(0x648)],_0x311669[_0xbe8017(0x24e)]));_0x311669['h264profile']&&(log(_0xbe8017(0x821)),_0x5d8761[_0xbe8017(0x648)]=_0x5d8761[_0xbe8017(0x648)][_0xbe8017(0x2f9)](/42e01f/gi,_0x311669[_0xbe8017(0x6d0)]),_0x5d8761[_0xbe8017(0x648)]=_0x5d8761[_0xbe8017(0x648)][_0xbe8017(0x2f9)](/42001f/gi,_0x311669[_0xbe8017(0x6d0)]),_0x5d8761[_0xbe8017(0x648)]=_0x5d8761[_0xbe8017(0x648)][_0xbe8017(0x2f9)](/420029/gi,_0x311669['h264profile']),_0x5d8761[_0xbe8017(0x648)]=_0x5d8761[_0xbe8017(0x648)]['replace'](/42a01e/gi,_0x311669[_0xbe8017(0x6d0)]),_0x5d8761[_0xbe8017(0x648)]=_0x5d8761[_0xbe8017(0x648)][_0xbe8017(0x2f9)](/42a014/gi,_0x311669[_0xbe8017(0x6d0)]),_0x5d8761[_0xbe8017(0x648)]=_0x5d8761[_0xbe8017(0x648)]['replace'](/42a00b/gi,_0x311669[_0xbe8017(0x6d0)]),_0x5d8761[_0xbe8017(0x648)]=_0x5d8761['sdp']['replace'](/640c1f/gi,_0x311669[_0xbe8017(0x6d0)]));_0x311669['noPLIs']&&(_0x5d8761[_0xbe8017(0x648)]=CodecsHandler[_0xbe8017(0x9d8)](_0x5d8761[_0xbe8017(0x648)]));_0x311669[_0xbe8017(0x65e)]&&(_0x5d8761[_0xbe8017(0x648)]=CodecsHandler['disableREMB'](_0x5d8761[_0xbe8017(0x648)]));_0x311669[_0xbe8017(0x7be)]&&(log(_0x5d8761[_0xbe8017(0x648)]),_0x5d8761[_0xbe8017(0x648)]=CodecsHandler[_0xbe8017(0x9e1)](_0x5d8761[_0xbe8017(0x648)]));if(_0x311669[_0xbe8017(0x16a)][_0x3da073[_0xbe8017(0x54a)]][_0xbe8017(0x3e4)])log(_0xbe8017(0x1f9)),_0x5d8761[_0xbe8017(0x648)]=_0x300658(_0x5d8761['sdp'],_0x311669[_0xbe8017(0x16a)][_0x3da073['UUID']][_0xbe8017(0x3e4)]);else _0x311669[_0xbe8017(0x739)]&&(log(_0xbe8017(0x1f9)),_0x5d8761['sdp']=_0x300658(_0x5d8761[_0xbe8017(0x648)],_0x311669[_0xbe8017(0x739)]));return log(_0x5d8761),_0x311669[_0xbe8017(0x16a)][_0x3da073['UUID']][_0xbe8017(0xa1b)](_0x5d8761);})[_0x1a87bb(0x619)](function _0x1875b1(){var _0xb51792=_0x1a87bb;log(_0xb51792(0x2b5));if(_0x311669['rpcs'][_0x3da073[_0xb51792(0x54a)]][_0xb51792(0x751)]){_0x311669['rpcs'][_0x3da073[_0xb51792(0x54a)]]['whipCallback']&&_0x311669['rpcs'][_0x3da073[_0xb51792(0x54a)]][_0xb51792(0x87c)]();return;}var _0x3eba1f={};_0x3eba1f[_0xb51792(0x54a)]=_0x3da073[_0xb51792(0x54a)],_0x3eba1f[_0xb51792(0x9f5)]=_0x311669[_0xb51792(0x16a)][_0x3da073[_0xb51792(0x54a)]]['localDescription'],_0x3eba1f['session']=_0x311669['rpcs'][_0x3da073[_0xb51792(0x54a)]]['session'],_0x311669[_0xb51792(0x8a3)]?_0x311669[_0xb51792(0x331)](JSON[_0xb51792(0x479)](_0x3eba1f['description']))['then'](function(_0x2e41c0){var _0x49e324=_0xb51792;_0x3eba1f[_0x49e324(0x9f5)]=_0x2e41c0[0x0],_0x3eba1f[_0x49e324(0x9ed)]=_0x2e41c0[0x1],_0x311669[_0x49e324(0x668)](_0x3eba1f);})[_0xb51792(0x3b6)](errorlog):_0x311669[_0xb51792(0x668)](_0x3eba1f);})[_0x1a87bb(0x3b6)](errorlog);else _0x311669[_0x1a87bb(0x16a)][_0x3da073[_0x1a87bb(0x54a)]]['remoteDescription'][_0x1a87bb(0x78a)]===_0x1a87bb(0x73b)&&errorlog(_0x1a87bb(0x672));})[_0x2e344a(0x3b6)](errorlog);},_0x311669[_0x134a17(0x700)]=function(){var _0x3a98d8=_0x134a17;if(_0x311669[_0x3a98d8(0x49b)]&&_0x311669[_0x3a98d8(0x49b)]['srcObject'])return _0x311669[_0x3a98d8(0x49b)][_0x3a98d8(0x5c4)];else return _0x311669[_0x3a98d8(0x49b)]&&_0x311669[_0x3a98d8(0x49b)]['src']&&_0x311669[_0x3a98d8(0x585)]?_0x311669[_0x3a98d8(0x585)]:(log(_0x3a98d8(0x562)),checkBasicStreamsExist(),_0x311669[_0x3a98d8(0x49b)]['srcObject']);};var _0xe532b8={},_0x1e48c9=![],_0x48cd13=[];_0x311669[_0x134a17(0x8ca)]=function(_0x2161ee,_0x28518f){var _0x32c845=_0x134a17;log(_0x32c845(0xa6a)+_0x28518f+'\x20'+_0x2161ee);var _0x3ff547=new FileReader(),_0x3bc7fb=![];for(var _0x2c3bb6=0x0;_0x2c3bb6<_0x311669[_0x32c845(0x972)][_0x32c845(0x8e9)];_0x2c3bb6++){if(_0x311669[_0x32c845(0x972)][_0x2c3bb6]['id']===_0x28518f){_0x3bc7fb=_0x2c3bb6;break;}}if(_0x3bc7fb===![]){warnlog(_0x32c845(0x651));return;}else{if(_0x311669[_0x32c845(0x972)][_0x3bc7fb][_0x32c845(0x5ae)]==0x0){warnlog(_0x32c845(0xa85));return;}else{if(!(_0x311669['hostedFiles'][_0x3bc7fb][_0x32c845(0x474)]===![]||_0x311669[_0x32c845(0x972)][_0x3bc7fb][_0x32c845(0x474)]===_0x2161ee)){warnlog(_0x32c845(0x205));return;}}}var _0x3d9cc8=0x4000,_0x335c5f=0x0,_0x43664d=_0x3bc7fb;_0x43664d===_0x32c845(0x73a)&&(_0x43664d=_0x32c845(0x22f)+_0x311669[_0x32c845(0xa76)](0x5));if(_0x2161ee in _0x311669['pcs'])var _0x4e9580=_0x311669[_0x32c845(0x859)][_0x2161ee][_0x32c845(0x1e1)](_0x43664d);else{if(_0x2161ee in _0x311669[_0x32c845(0x16a)])var _0x4e9580=_0x311669['rpcs'][_0x2161ee]['createDataChannel'](_0x43664d);else{warnlog('UUID\x20does\x20not\x20exist');return;}}_0x4e9580['binaryType']='arraybuffer';var _0x400903=_0x311669[_0x32c845(0x972)][_0x3bc7fb]['slice'](0x0,_0x3d9cc8);_0x4e9580[_0x32c845(0x777)]=()=>{var _0x40488f=_0x32c845;_0x4e9580['send'](JSON[_0x40488f(0x479)]({'type':_0x40488f(0x80e),'size':_0x311669[_0x40488f(0x972)][_0x3bc7fb][_0x40488f(0x8cf)],'filename':_0x311669[_0x40488f(0x972)][_0x3bc7fb][_0x40488f(0x6dd)],'id':_0x311669[_0x40488f(0x972)][_0x3bc7fb]['id']})),_0x3ff547[_0x40488f(0x745)](_0x400903);},_0x4e9580['onclose']=()=>{var _0x410195=_0x32c845;try{var _0x4b25cb=_0x311669['hostedTransfers']['indexOf'](_0x4e9580);_0x4b25cb>-0x1&&_0x311669[_0x410195(0x8b5)][_0x410195(0x48c)](_0x4b25cb,0x1);}catch(_0x21ff48){errorlog(_0x21ff48);}log('Transfer\x20ended'),_0x4e9580=null;},_0x4e9580[_0x32c845(0x17a)]=_0x479241=>{},_0x311669['hostedTransfers'][_0x32c845(0x505)](_0x4e9580),_0x3ff547[_0x32c845(0x19f)]=function(){var _0x541529=_0x32c845;if(_0x311669[_0x541529(0x972)][_0x3bc7fb][_0x541529(0x5ae)]==0x0)return;var _0x50c794=_0x3ff547[_0x541529(0x8e8)];log(_0x50c794);try{_0x4e9580['send'](_0x50c794);}catch(_0x1121e2){try{_0x4e9580['close']();}catch(_0x145ecd){}warnlog(_0x1121e2);return;}_0x335c5f+=0x1;if(_0x335c5f*_0x3d9cc8<_0x311669['hostedFiles'][_0x3bc7fb]['size'])try{log(_0x541529(0x4ee)+_0x335c5f),_0x400903=_0x311669['hostedFiles'][_0x3bc7fb][_0x541529(0x7f5)](_0x335c5f*_0x3d9cc8,(_0x335c5f+0x1)*_0x3d9cc8),_0x3ff547[_0x541529(0x745)](_0x400903);}catch(_0x4d202e){errorlog(_0x4d202e);}else _0x4e9580[_0x541529(0x2b9)]('EOF1'),_0x4e9580[_0x541529(0x1cf)]();};},_0x311669[_0x134a17(0x29e)]=null,_0x311669[_0x134a17(0x564)]=null,_0x311669[_0x134a17(0x630)]=async function(_0x385f51=null){var _0x2a6737=_0x134a17;if(_0x311669['chunkedVideoEnabled']!==null)return;else _0x311669[_0x2a6737(0x29e)]=![];!_0x385f51&&_0x311669[_0x2a6737(0x436)]['Chunked_video']&&(_0x385f51=_0x311669[_0x2a6737(0x436)][_0x2a6737(0x6cf)]);let _0x2a6630=0x0;var _0x536df0=_0x311669[_0x2a6737(0x585)][_0x2a6737(0x87a)]()[0x0];if(!_0x536df0){_0x311669[_0x2a6737(0x29e)]=null;return;}var _0x4222bf=new MediaStreamTrackProcessor(_0x536df0),_0x449a5a=_0x4222bf[_0x2a6737(0x4aa)];const _0x7034b4=_0x449a5a[_0x2a6737(0xa36)]();var _0x31022e=![],_0x2e1081=-0x1,_0x1ef9ee=-0x1;const _0x422e0b={'output':_0xebe3ba=>{var _0x153a4e=_0x2a6737;if(_0xebe3ba[_0x153a4e(0x611)][_0x153a4e(0x6dd)]==_0x153a4e(0x17c)){let _0x5b3e12=new Uint8Array(_0xebe3ba[_0x153a4e(0x7ce)]);_0xebe3ba[_0x153a4e(0x423)](_0x5b3e12),_0x48cd13[_0x153a4e(0x505)]([_0xebe3ba['timestamp']-_0x1ef9ee,_0xebe3ba['type']]),_0x48cd13[_0x153a4e(0x505)](_0x5b3e12),_0x1e48c9[_0x153a4e(0x64b)]('video');}},'error':_0x414d73=>{errorlog(_0x414d73);}};let _0x4ff59f=new VideoEncoder(_0x422e0b);_0x4ff59f[_0x2a6737(0x50e)](_0x385f51),_0x311669[_0x2a6737(0x436)][_0x2a6737(0x6cf)]=_0x385f51,_0x1e48c9[_0x2a6737(0x639)]=_0x4ff59f;var _0x51d30a,_0x2c7a4a=new Promise((_0x4de9a2,_0x1b23d7)=>{_0x51d30a=_0x4de9a2;});_0x2c7a4a['resolve']=_0x51d30a,_0x7034b4[_0x2a6737(0x829)]()[_0x2a6737(0x619)](function _0x1d4a09({done:_0x30d6a7,value:_0x3028aa}){var _0x36e17e=_0x2a6737;if(_0x30d6a7||_0x31022e){_0x4ff59f[_0x36e17e(0x1cf)]();_0x3028aa&&_0x3028aa[_0x36e17e(0x1cf)]();_0x311669[_0x36e17e(0x29e)]=null;return;}_0x1ef9ee==-0x1&&(_0x1ef9ee=_0x3028aa[_0x36e17e(0x82c)],_0x311669[_0x36e17e(0x436)]['Chunked_video'][_0x36e17e(0x3c5)]=Date[_0x36e17e(0x610)](),_0x2c7a4a[_0x36e17e(0x8c5)]());_0x2e1081==_0x3028aa['timestamp']&&(_0x3028aa[_0x36e17e(0x82c)]+=0x1,warnlog('Timestamp\x20duplicated'));if(!_0x31022e){_0x2e1081=_0x3028aa[_0x36e17e(0x82c)],_0x2a6630++;if(_0x1e48c9[_0x36e17e(0x7fa)]){const _0x535c4a=_0x2a6630>=0x3c;_0x535c4a&&(_0x2a6630=0x0,_0x1e48c9['needKeyFrame']=![],warnlog(_0x36e17e(0x590))),_0x4ff59f['encode'](_0x3028aa,{'keyFrame':_0x535c4a});}else _0x4ff59f[_0x36e17e(0x9d3)](_0x3028aa,{'keyFrame':![]});}_0x3028aa[_0x36e17e(0x1cf)](),_0x7034b4[_0x36e17e(0x829)]()[_0x36e17e(0x619)](_0x1d4a09);}),_0x311669['chunkedVideoEnabled']=!![],await _0x2c7a4a;},_0x311669[_0x134a17(0x9df)]=async function(_0x4a0d71){var _0x290766=_0x134a17;if(_0x311669['chunkedAudioEnabled']!==null)return;else _0x311669['chunkedAudioEnabled']=![];!_0x4a0d71&&_0x311669['stats'][_0x290766(0x520)]&&(_0x4a0d71=_0x311669[_0x290766(0x436)][_0x290766(0x520)]);var _0x64370b=_0x311669['videoElement'][_0x290766(0x5c4)][_0x290766(0xa6c)]()[0x0];if(!_0x64370b){_0x311669[_0x290766(0x564)]=null;return;}var _0x3069c8=_0x64370b[_0x290766(0x2e2)]();_0x4a0d71[_0x290766(0x512)]>_0x3069c8[_0x290766(0x2b2)]&&(_0x4a0d71[_0x290766(0x512)]=_0x3069c8[_0x290766(0x2b2)],_0x4a0d71[_0x290766(0x306)]=_0x3069c8['channelCount']);_0x4a0d71[_0x290766(0x228)]>_0x3069c8[_0x290766(0x228)]&&(_0x4a0d71[_0x290766(0x228)]=_0x3069c8[_0x290766(0x228)]);var _0x5f2164=new MediaStreamTrackProcessor(_0x64370b),_0x8d455b=_0x5f2164[_0x290766(0x4aa)];const _0x52b552=_0x8d455b[_0x290766(0xa36)]();var _0x2bba8b=![],_0x20b4ef=-0x1,_0x56fdcb=-0x1;const _0x1fad7f={'output':_0xad76a1=>{var _0xa29337=_0x290766;if(_0xad76a1[_0xa29337(0x611)][_0xa29337(0x6dd)]==_0xa29337(0x69f)){let _0x43300d=new Uint8Array(_0xad76a1[_0xa29337(0x7ce)]);_0xad76a1['copyTo'](_0x43300d),_0x48cd13[_0xa29337(0x505)]([_0xad76a1[_0xa29337(0x82c)]-_0x56fdcb,_0xa29337(0x433)]),_0x48cd13[_0xa29337(0x505)](_0x43300d),_0x1e48c9[_0xa29337(0x64b)](_0xa29337(0x433));}},'error':_0x39eae7=>{errorlog(_0x39eae7);}};let _0x420f58=new AudioEncoder(_0x1fad7f);_0x420f58[_0x290766(0x50e)](_0x4a0d71),_0x311669[_0x290766(0x436)][_0x290766(0x520)]={},_0x311669[_0x290766(0x436)][_0x290766(0x520)][_0x290766(0x24e)]=_0x4a0d71['codec'],_0x311669[_0x290766(0x436)][_0x290766(0x520)][_0x290766(0x512)]=_0x4a0d71[_0x290766(0x512)],_0x311669['stats'][_0x290766(0x520)]['sampleRate']=_0x4a0d71[_0x290766(0x228)],_0x311669[_0x290766(0x436)]['Chunked_audio']['bitrate']=_0x4a0d71[_0x290766(0x63d)][_0x290766(0x739)];var _0x471f5a,_0x594601=new Promise((_0x198be8,_0x294783)=>{_0x471f5a=_0x198be8;});_0x594601[_0x290766(0x8c5)]=_0x471f5a,_0x52b552['read']()[_0x290766(0x619)](function _0x1e4d48({done:_0x213df5,value:_0xde0bf8}){var _0x269313=_0x290766;if(_0x213df5||_0x2bba8b){_0x420f58[_0x269313(0x1cf)]();_0xde0bf8&&_0xde0bf8[_0x269313(0x1cf)]();_0x311669[_0x269313(0x564)]=null;return;}_0x56fdcb==-0x1&&(_0x56fdcb=_0xde0bf8['timestamp'],_0x311669[_0x269313(0x436)][_0x269313(0x520)][_0x269313(0x3c5)]=Date[_0x269313(0x610)](),_0x594601['resolve']()),_0x20b4ef==_0xde0bf8[_0x269313(0x82c)]&&(_0xde0bf8[_0x269313(0x82c)]+=0x1),!_0x2bba8b&&(_0x20b4ef=_0xde0bf8[_0x269313(0x82c)],_0x420f58[_0x269313(0x9d3)](_0xde0bf8)),_0xde0bf8['close'](),_0x52b552[_0x269313(0x829)]()[_0x269313(0x619)](_0x1e4d48);}),_0x311669['chunkedAudioEnabled']=!![],await _0x594601;},_0x311669['getPCM']=function(_0x4f797e){var _0x3460bb=_0x134a17;warnlog(_0x3460bb(0x9bf));const _0x550c9e=window[_0x3460bb(0x45f)]||window[_0x3460bb(0x8e1)],_0x19630c=new _0x550c9e(),_0x544d75=_0x19630c[_0x3460bb(0x9ab)](_0x4f797e),_0x4850cd=0x800,_0x2a8c9a=(_0x19630c[_0x3460bb(0x195)]||_0x19630c[_0x3460bb(0x3f3)])[_0x3460bb(0x83d)](_0x19630c,_0x4850cd,0x1,0x1);return _0x2a8c9a[_0x3460bb(0x455)]=function(_0x23d171){var _0xb95a56=_0x3460bb,_0x19abd0=new Uint8Array(_0x23d171['inputBuffer'][_0xb95a56(0x43b)](0x0)['buffer']);_0x48cd13[_0xb95a56(0x505)]([0x0,_0xb95a56(0x944)]),_0x48cd13[_0xb95a56(0x505)](_0x19abd0),_0x1e48c9[_0xb95a56(0x64b)](_0xb95a56(0x944));},_0x544d75[_0x3460bb(0x857)](_0x2a8c9a),_0x2a8c9a['connect'](_0x19630c[_0x3460bb(0x9e2)]),_0x311669[_0x3460bb(0x436)][_0x3460bb(0x520)]={},_0x311669[_0x3460bb(0x564)]=!![],_0x2a8c9a;},_0x311669[_0x134a17(0x504)]=async function(_0xe4571f){var _0x14ff4f=_0x134a17;log(_0x14ff4f(0x696)+_0xe4571f);!_0x311669['chunkedVideoEnabled']&&_0x311669['stats'][_0x14ff4f(0x6cf)]&&(config=_0x311669['stats'][_0x14ff4f(0x6cf)],await _0x311669[_0x14ff4f(0x630)](config));!_0x311669[_0x14ff4f(0x564)]&&_0x311669[_0x14ff4f(0x436)]['Chunked_audio']&&(config=_0x311669[_0x14ff4f(0x436)][_0x14ff4f(0x520)],await _0x311669[_0x14ff4f(0x9df)](config));if(_0xe4571f in _0xe532b8)return;if(!_0x1e48c9){var _0x36bef4=_0x311669[_0x14ff4f(0x700)](),_0x38ae3b=_0x311669[_0x14ff4f(0x88e)],_0x12df42=null;_0x311669[_0x14ff4f(0x84c)]&&_0x311669['maxvideobitrate']<_0x38ae3b&&(_0x38ae3b=_0x311669['maxvideobitrate']);var _0x1e7140={'codec':_0x14ff4f(0x38f),'width':0x780,'height':0x438,'bitrate':parseInt(_0x38ae3b*0x3e8),'frameRate':0x1e,'latencyMode':_0x14ff4f(0x264)};_0x311669['alpha']&&(_0x1e7140[_0x14ff4f(0x970)]=_0x14ff4f(0x43f));var _0x3130d3=_0x36bef4['getVideoTracks']();if(_0x3130d3[_0x14ff4f(0x8e9)]){var _0x23e3ef=_0x3130d3[0x0][_0x14ff4f(0x2e2)]();_0x23e3ef[_0x14ff4f(0x530)]&&(_0x1e7140[_0x14ff4f(0x530)]=_0x23e3ef[_0x14ff4f(0x530)]),_0x23e3ef[_0x14ff4f(0x5ec)]&&(_0x1e7140['height']=_0x23e3ef[_0x14ff4f(0x5ec)]),_0x23e3ef[_0x14ff4f(0x732)]&&(_0x1e7140[_0x14ff4f(0x732)]=_0x23e3ef[_0x14ff4f(0x732)]);}else _0x1e7140=![];if(_0x38ae3b<0x259){var _0xfdbc03=_0x1e7140[_0x14ff4f(0x530)]*_0x1e7140['height']/(0x280*0x168);if(_0xfdbc03>=0x2)_0x1e7140['width']=parseInt(_0x1e7140[_0x14ff4f(0x530)]/0x2),_0x1e7140[_0x14ff4f(0x5ec)]=parseInt(_0x1e7140[_0x14ff4f(0x5ec)]/0x2);else _0xfdbc03>=1.5&&(_0x1e7140['width']=parseInt(_0x1e7140['width']/1.5),_0x1e7140[_0x14ff4f(0x5ec)]=parseInt(_0x1e7140['height']/1.5));}var _0x32e504={'codec':_0x14ff4f(0x2b0),'numberOfChannels':0x2,'channels':0x2,'sampleRate':0xbb80,'bitrate':0xfa00,'tuning':{'bitrate':0xfa00}};if(_0x38ae3b>0xbb8)var _0x32e504={'codec':_0x14ff4f(0x2b0),'numberOfChannels':0x2,'channels':0x2,'sampleRate':0xbb80,'tuning':{'bitrate':0x1f400}};else{if(_0x38ae3b<0x259)var _0x32e504={'codec':_0x14ff4f(0x2b0),'numberOfChannels':0x2,'channels':0x2,'sampleRate':0xbb80,'tuning':{'bitrate':0x7d00}};}_0x311669[_0x14ff4f(0x944)]&&(_0x32e504={'codec':_0x14ff4f(0x944),'numberOfChannels':0x2,'channels':0x2,'sampleRate':0xbb80});!_0x36bef4[_0x14ff4f(0xa6c)]()[_0x14ff4f(0x8e9)]&&(_0x32e504=![]);if(!_0x32e504&&!_0x1e7140)return;_0x1e48c9={},_0x1e48c9['needKeyFrame']=!![],_0x1e48c9['configVideo']=_0x1e7140||![],_0x1e48c9[_0x14ff4f(0x7b1)]=_0x32e504||![],_0x1e48c9[_0x14ff4f(0x691)]&&await _0x311669['webCodec'](_0x1e48c9['configVideo']),_0x1e48c9['configAudio']&&(_0x1e48c9[_0x14ff4f(0x7b1)][_0x14ff4f(0x24e)]=='pcm'?_0x311669['getPCM'](_0x36bef4):await _0x311669['webCodecAudio'](_0x1e48c9[_0x14ff4f(0x7b1)])),_0x1e48c9['sendChunks']=function(_0xd7605c=_0x14ff4f(0x38c)){var _0x425c75=_0x14ff4f;if(_0x12df42)return;_0x12df42=!![];var _0x1ec12f=_0xd7605c;while(_0x48cd13['length']){if(!Object[_0x425c75(0x19b)](_0xe532b8)[_0x425c75(0x8e9)]){_0x48cd13=[],_0x12df42=null,_0x311669[_0x425c75(0x436)][_0x425c75(0x724)]=0x0;return;}_0x311669[_0x425c75(0x436)][_0x425c75(0x724)]=_0x48cd13[_0x425c75(0x8e9)];var _0x2c3673=0x0,_0x52299c=_0x48cd13['shift']();if(_0x52299c['length']===0x2){_0x1ec12f=_0x52299c[0x1],_0x52299c[_0x425c75(0x505)](_0x48cd13[_0x425c75(0x8e9)]);var _0x10fb71=JSON[_0x425c75(0x479)](_0x52299c);for(var _0x19e53c in _0xe532b8){if((_0x1ec12f=='key'||_0x1ec12f==_0x425c75(0x44c)||_0x1ec12f==_0x425c75(0x30c))&&!_0x311669['pcs'][_0x19e53c][_0x425c75(0x670)])continue;if((_0x1ec12f=='audio'||_0x1ec12f=='pcm')&&!_0x311669[_0x425c75(0x859)][_0x19e53c]['allowAudio'])continue;try{_0xe532b8[_0x19e53c][_0x425c75(0x8ee)]===_0x425c75(0x6d4)&&_0xe532b8[_0x19e53c][_0x425c75(0x2b9)](_0x10fb71),_0x311669['pcs'][_0x19e53c]['stats'][_0x425c75(0x4ab)]=_0xe532b8[_0x19e53c][_0x425c75(0x4ab)],_0x2c3673<_0x311669[_0x425c75(0x859)][_0x19e53c]['stats'][_0x425c75(0x4ab)]&&(_0x2c3673=_0x311669[_0x425c75(0x859)][_0x19e53c][_0x425c75(0x436)][_0x425c75(0x4ab)]);}catch(_0x2292e9){}}}else{if(_0x52299c[_0x425c75(0x7ce)]>0x40000){for(var _0x19e53c in _0xe532b8){if((_0x1ec12f=='key'||_0x1ec12f=='delta'||_0x1ec12f=='video')&&!_0x311669[_0x425c75(0x859)][_0x19e53c][_0x425c75(0x670)])continue;if((_0x1ec12f==_0x425c75(0x433)||_0x1ec12f==_0x425c75(0x944))&&!_0x311669[_0x425c75(0x859)][_0x19e53c]['allowAudio'])continue;try{_0xe532b8[_0x19e53c]['readyState']===_0x425c75(0x6d4)&&_0xe532b8[_0x19e53c][_0x425c75(0x2b9)](_0x52299c[_0x425c75(0x7f5)](0x0,0x40000)),_0x311669[_0x425c75(0x859)][_0x19e53c][_0x425c75(0x436)][_0x425c75(0x4ab)]=_0xe532b8[_0x19e53c][_0x425c75(0x4ab)],_0x2c3673<_0x311669[_0x425c75(0x859)][_0x19e53c][_0x425c75(0x436)]['bufferedAmount']&&(_0x2c3673=_0x311669[_0x425c75(0x859)][_0x19e53c][_0x425c75(0x436)][_0x425c75(0x4ab)]);}catch(_0x3ef4c3){}}_0x48cd13[_0x425c75(0x91c)](_0x52299c[_0x425c75(0x7f5)](0x40000));}else for(var _0x19e53c in _0xe532b8){if((_0x1ec12f==_0x425c75(0xa68)||_0x1ec12f==_0x425c75(0x44c)||_0x1ec12f==_0x425c75(0x30c))&&!_0x311669['pcs'][_0x19e53c][_0x425c75(0x670)])continue;if((_0x1ec12f==_0x425c75(0x433)||_0x1ec12f==_0x425c75(0x944))&&!_0x311669[_0x425c75(0x859)][_0x19e53c][_0x425c75(0x760)])continue;try{_0xe532b8[_0x19e53c][_0x425c75(0x8ee)]===_0x425c75(0x6d4)&&_0xe532b8[_0x19e53c]['send'](_0x52299c),_0x311669[_0x425c75(0x859)][_0x19e53c]['stats'][_0x425c75(0x4ab)]=_0xe532b8[_0x19e53c][_0x425c75(0x4ab)],_0x2c3673<_0x311669['pcs'][_0x19e53c][_0x425c75(0x436)]['bufferedAmount']&&(_0x2c3673=_0x311669[_0x425c75(0x859)][_0x19e53c][_0x425c75(0x436)]['bufferedAmount']);}catch(_0x576d6d){}}}_0x311669['stats'][_0x425c75(0xa02)]=_0x2c3673;if(!_0x1e48c9['throttle']&&_0x2c3673>0x1f4)_0x1e48c9[_0x425c75(0xa31)]=!![],_0x311669[_0x425c75(0x436)]['throttling']=_0x1e48c9['throttle'],_0x311669[_0x425c75(0x436)][_0x425c75(0x6cf)]['bitrate']=parseInt(_0x311669['chunked']*0x3e8/0xa),_0x1e48c9[_0x425c75(0x639)][_0x425c75(0x50e)](_0x311669[_0x425c75(0x436)][_0x425c75(0x6cf)]);else _0x1e48c9[_0x425c75(0xa31)]&&_0x2c3673<0x12c&&(_0x1e48c9[_0x425c75(0xa31)]=![],_0x311669[_0x425c75(0x436)][_0x425c75(0x412)]=_0x1e48c9[_0x425c75(0xa31)],_0x311669[_0x425c75(0x436)][_0x425c75(0x6cf)]['bitrate']=parseInt(_0x311669[_0x425c75(0x88e)]*0x3e8),_0x1e48c9[_0x425c75(0x639)][_0x425c75(0x50e)](_0x311669['stats'][_0x425c75(0x6cf)]));}_0x12df42=null,_0x311669[_0x425c75(0x436)]['chunkedInQueue']=0x0;},_0x36bef4[_0x14ff4f(0x293)]=function(_0x5c1c96){};}var _0x4ac1f0='chunked';if(_0xe4571f in _0x311669['pcs'])_0xe532b8[_0xe4571f]=_0x311669[_0x14ff4f(0x859)][_0xe4571f][_0x14ff4f(0x1e1)](_0x4ac1f0,{'ordered':!![]});else{warnlog('UUID\x20does\x20not\x20exist');return;}_0xe532b8[_0xe4571f][_0x14ff4f(0x4f4)]=_0x14ff4f(0xa54),_0xe532b8[_0xe4571f]['binaryType']=_0x14ff4f(0x328),_0xe532b8[_0xe4571f][_0x14ff4f(0x6a7)]=![],_0xe532b8[_0xe4571f][_0x14ff4f(0x777)]=()=>{var _0x1baad6=_0x14ff4f;log('chunkedtransfer\x20OPEN');if(_0x311669[_0x1baad6(0x564)]&&_0x311669[_0x1baad6(0x29e)]&&_0x311669[_0x1baad6(0x859)][_0xe4571f][_0x1baad6(0x760)]&&_0x311669[_0x1baad6(0x859)][_0xe4571f]['allowVideo'])_0xe532b8[_0xe4571f]['send'](JSON[_0x1baad6(0x479)]({'timestamp':Date[_0x1baad6(0x610)](),'type':_0x1baad6(0x1ff),'realTimeVideo':_0x311669[_0x1baad6(0x436)][_0x1baad6(0x6cf)][_0x1baad6(0x3c5)],'realTimeAudio':_0x311669[_0x1baad6(0x436)][_0x1baad6(0x520)]['realTime'],'size':0x5af3107a3fff,'configVideo':_0x1e48c9[_0x1baad6(0x691)],'configAudio':_0x1e48c9['configAudio'],'recordType':_0x311669[_0x1baad6(0x88e)],'filename':_0x4ac1f0+_0x1baad6(0x9e9),'id':_0x4ac1f0}));else{if(_0x311669[_0x1baad6(0x564)]&&_0x311669[_0x1baad6(0x859)][_0xe4571f][_0x1baad6(0x760)])_0xe532b8[_0xe4571f]['send'](JSON['stringify']({'timestamp':Date[_0x1baad6(0x610)](),'type':'chunkedtransfer','realTimeAudio':_0x311669[_0x1baad6(0x436)][_0x1baad6(0x520)][_0x1baad6(0x3c5)],'size':0x5af3107a3fff,'configAudio':_0x1e48c9[_0x1baad6(0x7b1)],'recordType':_0x311669['chunked'],'filename':_0x4ac1f0+_0x1baad6(0x9e9),'id':_0x4ac1f0}));else{if(_0x311669['chunkedVideoEnabled']&&_0x311669[_0x1baad6(0x859)][_0xe4571f][_0x1baad6(0x670)])_0xe532b8[_0xe4571f][_0x1baad6(0x2b9)](JSON[_0x1baad6(0x479)]({'timestamp':Date[_0x1baad6(0x610)](),'type':_0x1baad6(0x1ff),'realTimeVideo':_0x311669['stats'][_0x1baad6(0x6cf)][_0x1baad6(0x3c5)],'size':0x5af3107a3fff,'configVideo':_0x1e48c9[_0x1baad6(0x691)],'recordType':_0x311669[_0x1baad6(0x88e)],'filename':_0x4ac1f0+_0x1baad6(0x9e9),'id':_0x4ac1f0}));else{}}}},_0xe532b8[_0xe4571f][_0x14ff4f(0x661)]=()=>{var _0x5ed178=_0x14ff4f;try{var _0x35aa36=_0x311669['hostedTransfers'][_0x5ed178(0x49e)](_0xe532b8[_0xe4571f]);_0x35aa36>-0x1&&_0x311669[_0x5ed178(0x8b5)][_0x5ed178(0x48c)](_0x35aa36,0x1);}catch(_0x309307){errorlog(_0x309307);}log(_0x5ed178(0x4a2)),_0xe532b8[_0xe4571f]=null,delete _0xe532b8[_0xe4571f];var _0x1c0da3=![];for(var _0x4aed08=0x0;_0x4aed08<_0x311669['hostedTransfers'][_0x5ed178(0x8e9)];_0x4aed08++){if(_0x5ed178(0x4f4)in _0x311669['hostedTransfers'][_0x4aed08]&&_0x311669['hostedTransfers'][_0x4aed08][_0x5ed178(0x4f4)]==_0x5ed178(0xa54)){_0x1c0da3=!![];break;}}if(_0x1c0da3)try{_0x1e48c9[_0x5ed178(0x86e)]();}catch(_0x8cc4e3){}},_0xe532b8[_0xe4571f][_0x14ff4f(0x17a)]=_0x22c341=>{var _0x2411ff=_0x14ff4f;if(_0x22c341[_0x2411ff(0x3bc)])try{var _0x222401=JSON[_0x2411ff(0x52d)](_0x22c341[_0x2411ff(0x3bc)]);_0x222401['kf']&&(log(_0x2411ff(0x3c3)),_0x1e48c9[_0x2411ff(0x7fa)]=!![]);}catch(_0x5d5703){}},_0x311669[_0x14ff4f(0x8b5)][_0x14ff4f(0x505)](_0xe532b8[_0xe4571f]);},_0x311669[_0x134a17(0x1b5)]=async function(_0x44cc48,_0x5e3dbb,_0x1d0eea){var _0x569353=_0x134a17;log('Created\x20transfer\x20channel');var _0x3c18c2=_0x1d0eea;_0x3c18c2[_0x569353(0x992)]=_0x569353(0x328);var _0x4a1e8c='',_0x15946c=0x0,_0xa542c5=![],_0x324395=![],_0x33cab6=0x0,_0x463481={};_0x3c18c2['onopen']=_0x39b7f2=>{var _0x323ebc=_0x569353;log(_0x323ebc(0x366));},_0x3c18c2['onmessage']=_0x5e7efa=>{var _0x5210d0=_0x569353;if(!_0xa542c5)try{_0xa542c5=JSON[_0x5210d0(0x52d)](_0x5e7efa[_0x5210d0(0x3bc)]);if(_0xa542c5[_0x5210d0(0x78a)]=='filetransfer'){var {readable:_0x3ce10e,writable:_0x4fc3b3}=new TransformStream({'transform':(_0x2df772,_0x300e09)=>_0x2df772[_0x5210d0(0x440)]()[_0x5210d0(0x619)](_0x350fac=>_0x300e09[_0x5210d0(0x21e)](new Uint8Array(_0x350fac)))});_0x463481[_0x5210d0(0x88f)]=_0x4fc3b3[_0x5210d0(0x673)]();;_0x3ce10e['pipeTo'](streamSaver[_0x5210d0(0x239)](_0xa542c5['filename']));for(var _0x397ab7=0x0;_0x397ab7{var _0x570161=_0x569353;_0x33cab6<=0x0&&(_0x463481[_0x570161(0x88f)]&&setTimeout(function(_0x358712,_0x31a75a){var _0x29d7cd=_0x570161;_0x31a75a<=0x0?(_0x358712[_0x29d7cd(0x1cf)](),_0x358712=null):setTimeout(function(_0x53da83,_0x317c56){var _0x3576ca=_0x29d7cd;_0x53da83[_0x3576ca(0x1cf)](),_0x53da83=null;},0x1388,_0x358712);},0x3e8,_0x463481[_0x570161(0x88f)],_0x33cab6));_0x3c18c2=null;return;};return;};async function _0x4c3c9f(_0x2b2002,_0x1307e9=![]){var _0x15bc5a=_0x134a17;_0x2b2002['decoder'][_0x15bc5a(0x5f5)](_0x2b2002[_0x15bc5a(0x64f)]['shift']());if(_0x2b2002[_0x15bc5a(0x265)]===null&&!_0x1307e9)return;_0x2b2002[_0x15bc5a(0x265)]=setTimeout(function(_0x4ce3ea){_0x4c3c9f(_0x4ce3ea);},0x21,_0x2b2002);}return _0x311669[_0x134a17(0xa51)]=async function(_0x583108,_0x3522b1,_0x3bbdcb){var _0x58c10=_0x134a17;log(_0x58c10(0x79b));var _0x55fd2e=_0x3bbdcb;_0x55fd2e[_0x58c10(0x992)]='arraybuffer';var _0x558469='',_0x47de28=0x0,_0x2ecb3b=![],_0x2dab49=![],_0x2b645c={};_0x55fd2e[_0x58c10(0x777)]=_0x4243bb=>{var _0x43943c=_0x58c10;log(_0x43943c(0x366));},_0x55fd2e[_0x58c10(0x661)]=async function(_0x3f4af1){var _0x50eee3=_0x58c10;if(_0x2b645c['videoWriter']){if(_0x2b645c[_0x50eee3(0x49b)][_0x50eee3(0x6da)]){await delay(0x3e8);try{await _0x2b645c['videoElement'][_0x50eee3(0x6da)]();}catch(_0x84d3e1){}}}_0x55fd2e=null;_0x311669[_0x50eee3(0x16a)][_0x3522b1]&&(delete _0x311669['rpcs'][_0x3522b1][_0x50eee3(0x436)][_0x50eee3(0x702)],delete _0x311669[_0x50eee3(0x16a)][_0x3522b1]['stats']['chunked_mode_audio']);return;};async function _0x1a70fc(){var _0x44024c=_0x58c10,_0x2d6799=await window[_0x44024c(0x806)]({'startIn':'videos','suggestedName':'myVideo.webm','types':[{'description':_0x44024c(0x704),'accept':{'video/webm':[_0x44024c(0x9e9)]}}]}),_0x5ab509=await _0x2d6799[_0x44024c(0x84a)]();return _0x2b645c[_0x44024c(0x44e)]['fileWriter']=_0x5ab509,_0x2b645c[_0x44024c(0x203)]=new WebMWriter(_0x2b645c[_0x44024c(0x44e)]),_0x2b645c[_0x44024c(0x49b)]['stopWriter']=async function(){var _0x4700e7=_0x44024c;_0x2b645c['videoElement'][_0x4700e7(0x6da)]=![],clearInterval(_0x2b645c[_0x4700e7(0x57d)]),_0x2b645c[_0x4700e7(0x57d)]=null,await _0x2b645c['videoWriter'][_0x4700e7(0x73e)](),_0x2b645c['writer_config'][_0x4700e7(0x1bb)][_0x4700e7(0x1cf)]();},_0x2b645c[_0x44024c(0x203)];}_0x55fd2e[_0x58c10(0x17a)]=async function(_0x4fe1ed){var _0x5afb41=_0x58c10;if(!_0x2ecb3b)try{_0x2ecb3b=JSON[_0x5afb41(0x52d)](_0x4fe1ed['data']);if(_0x2ecb3b[_0x5afb41(0x78a)]==_0x5afb41(0x1ff)){log(_0x5afb41(0x4ad)),log(_0x2ecb3b),_0x2b645c[_0x5afb41(0x54a)]=_0x3522b1,_0x2b645c[_0x5afb41(0x63a)]=0x0,_0x2b645c[_0x5afb41(0xa13)]=0x2,_0x2b645c[_0x5afb41(0x8b7)]=Date[_0x5afb41(0x610)](),_0x2b645c[_0x5afb41(0x8cd)]=_0x2ecb3b[_0x5afb41(0x82c)],_0x2b645c[_0x5afb41(0x2fe)]=_0x2b645c[_0x5afb41(0x8b7)]-_0x2ecb3b['timestamp'],_0x2b645c['dc']=_0x55fd2e,_0x2b645c['id']=_0x2ecb3b['id'],_0x2b645c[_0x5afb41(0x57d)]=null,_0x2b645c[_0x5afb41(0xa33)]=![],_0x2b645c[_0x5afb41(0x49b)]=createVideoElement(),_0x2b645c[_0x5afb41(0x49b)][_0x5afb41(0x740)]=!![],_0x2b645c[_0x5afb41(0x49b)][_0x5afb41(0x225)]=![],_0x2b645c[_0x5afb41(0x49b)]['setAttribute']('playsinline',''),_0x2b645c[_0x5afb41(0x49b)][_0x5afb41(0x2bc)]['UUID']=_0x3522b1,_0x2b645c['videoElement'][_0x5afb41(0x1ff)]=!![],_0x2b645c[_0x5afb41(0x49b)]['srcObject']=new MediaStream(),_0x311669['rpcs'][_0x3522b1][_0x5afb41(0x585)]=_0x2b645c['videoElement'][_0x5afb41(0x5c4)],_0x311669[_0x5afb41(0x16a)][_0x3522b1][_0x5afb41(0x49b)]=_0x2b645c[_0x5afb41(0x49b)];_0x311669[_0x5afb41(0x16a)][_0x3522b1]['mirrorState']&&applyMirrorGuest(_0x311669[_0x5afb41(0x16a)][_0x3522b1]['mirrorState'],_0x311669['rpcs'][_0x3522b1][_0x5afb41(0x49b)]);_0x311669[_0x5afb41(0x16a)][_0x3522b1][_0x5afb41(0x1c9)]!==![]&&(_0x311669[_0x5afb41(0x16a)][_0x3522b1][_0x5afb41(0x49b)][_0x5afb41(0x3bf)]=_0x311669[_0x5afb41(0x16a)][_0x3522b1][_0x5afb41(0x1c9)]);_0x2b645c[_0x5afb41(0x49b)][_0x5afb41(0x8bf)](_0x5afb41(0x462),_0x271ebd=>{var _0x12cef4=_0x5afb41;try{var _0x1ab590=document[_0x12cef4(0x5c8)](_0x12cef4(0x8d8));_0x1ab590&&_0x1ab590[_0x12cef4(0x826)][_0x12cef4(0x9d0)](_0x1ab590);}catch(_0x2e916c){}_0x2b645c['playing']=!![];if(_0x2b645c['audioContext'])_0x2b645c['audioContext'][_0x12cef4(0x327)]();else _0x311669[_0x12cef4(0xa6f)]&&_0x311669[_0x12cef4(0xa6f)][_0x12cef4(0x327)]();try{_0x311669[_0x12cef4(0x36b)]&&(v['readyState']>=0x3&&(!v[_0x12cef4(0x36b)]&&(v['pip']=!![],toggleSystemPip(v,!![]))));}catch(_0x59afd9){}},{'once':!![]}),_0x2b645c[_0x5afb41(0x49b)][_0x5afb41(0x8bf)]('error',function(_0x21e789){errorlog(_0x21e789);}),_0x2b645c[_0x5afb41(0x49b)][_0x5afb41(0x1d5)]=_0x1a70fc,_0x2b645c[_0x5afb41(0x49b)]['oncanplay']=function(){updateMixer();},_0x2b645c[_0x5afb41(0x203)]=![],_0x2b645c['frameMeta']=![],_0x2b645c[_0x5afb41(0x44e)]={},_0x2b645c[_0x5afb41(0x44e)][_0x5afb41(0x30c)]=![],_0x2b645c[_0x5afb41(0x44e)]['audio']=![],_0x2b645c[_0x5afb41(0x77a)]=![],_0x2b645c[_0x5afb41(0x79f)]=![],_0x2b645c[_0x5afb41(0x3db)]=![],_0x2b645c[_0x5afb41(0x82e)]=![],_0x2b645c['video']=![],_0x2b645c[_0x5afb41(0x433)]=![],_0x2b645c[_0x5afb41(0x75d)]=![],_0x2b645c[_0x5afb41(0x462)]=![];_0x2ecb3b[_0x5afb41(0x691)]&&(_0x311669[_0x5afb41(0x16a)][_0x3522b1][_0x5afb41(0x436)][_0x5afb41(0x702)]=_0x2ecb3b['configVideo'],_0x2b645c[_0x5afb41(0x77a)]={},_0x2b645c[_0x5afb41(0x77a)][_0x5afb41(0x530)]=_0x2ecb3b[_0x5afb41(0x691)][_0x5afb41(0x530)]+''||_0x5afb41(0x5ac),_0x2b645c[_0x5afb41(0x77a)][_0x5afb41(0x5ec)]=_0x2ecb3b[_0x5afb41(0x691)][_0x5afb41(0x5ec)]+''||_0x5afb41(0x5b7),_0x2b645c[_0x5afb41(0x77a)][_0x5afb41(0x24e)]=_0x2ecb3b[_0x5afb41(0x691)]['codec']||_0x5afb41(0x38f),_0x2b645c[_0x5afb41(0x44e)][_0x5afb41(0x30c)]=!![],_0x2b645c['writer_config'][_0x5afb41(0x530)]=parseInt(_0x2b645c[_0x5afb41(0x77a)]['width']),_0x2b645c[_0x5afb41(0x44e)]['height']=parseInt(_0x2b645c[_0x5afb41(0x77a)][_0x5afb41(0x5ec)]),_0x2ecb3b[_0x5afb41(0x691)][_0x5afb41(0x24e)]==_0x5afb41(0x38f)?_0x2b645c['writer_config'][_0x5afb41(0x24e)]='VP9':_0x2b645c[_0x5afb41(0x44e)][_0x5afb41(0x24e)]=_0x5afb41(0x3bd),_0x2b645c[_0x5afb41(0x3db)]={'output':_0x3809c7=>{var _0x2391f9=_0x5afb41;_0x2b645c[_0x2391f9(0x30c)][_0x2391f9(0x8c8)][_0x2391f9(0x44d)](_0x3809c7);},'error':_0x55b13f=>{var _0x237cf7=_0x5afb41;_0x2b645c[_0x237cf7(0x30c)][_0x237cf7(0x28b)][_0x237cf7(0x5ae)]==_0x237cf7(0x208)?warnlog(_0x237cf7(0x2a9)):errorlog(_0x55b13f[_0x237cf7(0x6d8)]);}},_0x2b645c[_0x5afb41(0x30c)]={},_0x2b645c['video'][_0x5afb41(0xa1e)]=new MediaStreamTrackGenerator({'kind':_0x5afb41(0x30c)}),_0x2b645c[_0x5afb41(0x30c)]['stream']=new MediaStream([_0x2b645c[_0x5afb41(0x30c)][_0x5afb41(0xa1e)]]),_0x2b645c[_0x5afb41(0x30c)][_0x5afb41(0x8c8)]=_0x2b645c[_0x5afb41(0x30c)][_0x5afb41(0xa1e)]['writable'][_0x5afb41(0x673)](),_0x2b645c[_0x5afb41(0x30c)][_0x5afb41(0x28b)]=new VideoDecoder(_0x2b645c[_0x5afb41(0x3db)]),_0x2b645c[_0x5afb41(0x30c)]['decoder'][_0x5afb41(0x50e)](_0x2b645c[_0x5afb41(0x77a)]),_0x2b645c['video'][_0x5afb41(0x64f)]=[],_0x2b645c[_0x5afb41(0x30c)]['nextQueue']=null,_0x2b645c[_0x5afb41(0x30c)]['playbackheader']=![],_0x2b645c['video'][_0x5afb41(0x6a7)]=![],_0x5afb41(0x8f0)in _0x2ecb3b&&(_0x2b645c[_0x5afb41(0x30c)]['realTime']=_0x2ecb3b[_0x5afb41(0x8f0)]),_0x2b645c[_0x5afb41(0x49b)]['srcObject'][_0x5afb41(0x58b)](_0x2b645c['video'][_0x5afb41(0x75f)][_0x5afb41(0x87a)]()[0x0]));_0x2ecb3b[_0x5afb41(0x7b1)]&&(_0x311669[_0x5afb41(0x16a)][_0x3522b1]['stats']['chunked_mode_audio']=_0x2ecb3b[_0x5afb41(0x7b1)],_0x2b645c[_0x5afb41(0x79f)]=_0x2ecb3b['configAudio'],_0x2b645c[_0x5afb41(0x44e)][_0x5afb41(0x433)]=!![],_0x2b645c[_0x5afb41(0x44e)][_0x5afb41(0x690)]=_0x2ecb3b['configAudio']['sampleRate']||0xbb80,_0x2b645c[_0x5afb41(0x44e)]['channels']=_0x2ecb3b[_0x5afb41(0x7b1)][_0x5afb41(0x512)]||0x1,_0x2b645c[_0x5afb41(0x79f)]['codec']&&_0x2b645c[_0x5afb41(0x79f)]['codec']==_0x5afb41(0x944)?(!_0x2b645c[_0x5afb41(0x9e2)]?_0x2b645c[_0x5afb41(0x9e2)]=_0x311669[_0x5afb41(0xa6f)][_0x5afb41(0x6c7)]():_0x2b645c['videoElement'][_0x5afb41(0x5c4)][_0x5afb41(0xa6c)]()['forEach'](_0x3187ff=>{var _0x48d939=_0x5afb41;_0x2b645c['videoElement'][_0x48d939(0x5c4)][_0x48d939(0x59b)](_0x3187ff);}),_0x2b645c[_0x5afb41(0x9e2)][_0x5afb41(0x75f)][_0x5afb41(0xa6c)]()[_0x5afb41(0x982)](_0x1b5ae8=>{var _0x4db9c6=_0x5afb41;_0x2b645c[_0x4db9c6(0x49b)]['srcObject'][_0x4db9c6(0x58b)](_0x1b5ae8);}),_0x2b645c[_0x5afb41(0x5a1)]=!![]):(_0x2b645c['audio']={},_0x2b645c[_0x5afb41(0x433)][_0x5afb41(0x64f)]=[],_0x2b645c[_0x5afb41(0x433)][_0x5afb41(0x265)]=null,'realTimeAudio'in _0x2ecb3b&&(_0x2b645c[_0x5afb41(0x433)][_0x5afb41(0x3c5)]=_0x2ecb3b['realTimeAudio']),_0x2b645c[_0x5afb41(0x82e)]={'output':_0x171afd=>{var _0xcd9409=_0x5afb41;_0x2b645c['audio']['frameWriter'][_0xcd9409(0x44d)](_0x171afd);if(_0x2b645c[_0xcd9409(0x5e2)])return;var _0x1b6670=_0x171afd[_0xcd9409(0x82c)]/0x3e8-(Date[_0xcd9409(0x610)]()-_0x2b645c[_0xcd9409(0x2fe)]-_0x2b645c[_0xcd9409(0x433)][_0xcd9409(0x3c5)]);_0x1b6670=_0x1b6670-(_0x311669['audioCtx']['baseLatency']||0x0)*0x3e8-(_0x311669[_0xcd9409(0xa6f)][_0xcd9409(0x3d8)]||0x0)*0x3e8;var _0x180e19=0x3e7;if(!_0x311669[_0xcd9409(0x16a)][_0x2b645c[_0xcd9409(0x54a)]])return;else{if(_0x311669[_0xcd9409(0x16a)][_0x2b645c[_0xcd9409(0x54a)]][_0xcd9409(0xa33)]!==![])_0x180e19=_0x311669['rpcs'][_0x2b645c[_0xcd9409(0x54a)]][_0xcd9409(0xa33)];else _0x311669[_0xcd9409(0xa33)]!==![]?_0x180e19=_0x311669[_0xcd9409(0xa33)]:_0x311669[_0xcd9409(0x16a)][_0x2b645c[_0xcd9409(0x54a)]][_0xcd9409(0xa33)]=_0x180e19;}_0x1b6670+=_0x180e19-0x78,_0x1b6670<=0x0&&(_0x1b6670=0x0),_0x2b645c[_0xcd9409(0x432)]['delayTime'][_0xcd9409(0x44a)](parseFloat(_0x1b6670/0x3e8),_0x311669['audioCtx']['currentTime']),_0x2b645c[_0xcd9409(0x5e2)]=setTimeout(function(){var _0x4cdea0=_0xcd9409;_0x2b645c[_0x4cdea0(0x5e2)]=null;},_0x1b6670);},'error':_0x26593b=>{var _0x138b0e=_0x5afb41;_0x2b645c[_0x138b0e(0x433)][_0x138b0e(0x28b)]['state']==_0x138b0e(0x208)?warnlog(_0x138b0e(0x2a9)):errorlog(_0x26593b[_0x138b0e(0x6d8)]);}},_0x2b645c[_0x5afb41(0x433)][_0x5afb41(0x28b)]=new AudioDecoder(_0x2b645c['init_audio']),_0x2b645c[_0x5afb41(0x433)][_0x5afb41(0x28b)]['configure'](_0x2b645c[_0x5afb41(0x79f)]),_0x2b645c[_0x5afb41(0x433)][_0x5afb41(0xa1e)]=new MediaStreamTrackGenerator({'kind':_0x5afb41(0x433)}),_0x2b645c[_0x5afb41(0x433)]['frameWriter']=_0x2b645c[_0x5afb41(0x433)]['generator'][_0x5afb41(0x70f)][_0x5afb41(0x673)](),_0x2b645c[_0x5afb41(0x433)][_0x5afb41(0x75f)]=new MediaStream([_0x2b645c[_0x5afb41(0x433)][_0x5afb41(0xa1e)]]),_0x2b645c[_0x5afb41(0x433)]['audioNode']=_0x311669['audioCtx'][_0x5afb41(0x9ab)](_0x2b645c[_0x5afb41(0x433)][_0x5afb41(0x75f)]),_0x2b645c[_0x5afb41(0x432)]=_0x311669[_0x5afb41(0xa6f)][_0x5afb41(0x9b2)](0x1e),_0x2b645c[_0x5afb41(0x432)][_0x5afb41(0xa88)][_0x5afb41(0x8d7)]=0x0,_0x2b645c[_0x5afb41(0x433)]['audioNode'][_0x5afb41(0x857)](_0x2b645c[_0x5afb41(0x432)]),_0x2b645c[_0x5afb41(0x9e2)]=_0x311669[_0x5afb41(0xa6f)][_0x5afb41(0x6c7)](),_0x2b645c[_0x5afb41(0x432)]['connect'](_0x2b645c[_0x5afb41(0x9e2)]),_0x2b645c['destination'][_0x5afb41(0x75f)][_0x5afb41(0xa6c)]()['forEach'](_0x5d395f=>{var _0x512100=_0x5afb41;_0x2b645c[_0x512100(0x49b)][_0x512100(0x5c4)][_0x512100(0x58b)](_0x5d395f);})));warnlog(_0x2ecb3b),setupIncomingVideoTracking(_0x311669[_0x5afb41(0x16a)][_0x3522b1][_0x5afb41(0x49b)],_0x3522b1);if(_0x2b645c[_0x5afb41(0x433)]&&_0x2b645c[_0x5afb41(0x30c)])updateIncomingVideoElement(_0x3522b1);else{if(_0x2b645c['video'])updateIncomingVideoElement(_0x3522b1,!![],![]);else _0x2b645c[_0x5afb41(0x433)]&&updateIncomingVideoElement(_0x3522b1,![],!![]);}transferList[_0x5afb41(0x505)](_0x2b645c),_0x2dab49=transferList[_0x5afb41(0x8e9)]-0x1,updateDownloadLink(_0x2dab49),_0x2b645c[_0x5afb41(0x4e3)]=async function(_0x2d457b){var _0x14c64e=_0x5afb41;if(_0x2d457b['type']=='audio')_0x311669['rpcs'][_0x3522b1]['stats'][_0x14c64e(0x688)]['time_seconds']=parseInt(_0x2d457b[_0x14c64e(0x82c)]/0x2710)/0x64,_0x2b645c['processFrameAudio'](_0x2d457b);else{if(_0x2d457b[_0x14c64e(0x78a)]==_0x14c64e(0x944)){var _0x9b4720=_0x311669[_0x14c64e(0xa6f)][_0x14c64e(0x2ac)]();_0x9b4720[_0x14c64e(0x857)](_0x2b645c[_0x14c64e(0x9e2)]),_0x9b4720[_0x14c64e(0x9ef)]=function(){this['disconnect']();};var _0x1c7635=_0x311669['audioCtx'][_0x14c64e(0x6ce)](0x2,_0x2d457b[_0x14c64e(0x3bc)][_0x14c64e(0x8e9)],_0x311669[_0x14c64e(0xa6f)][_0x14c64e(0x228)]/0x2);_0x9b4720['buffer']=_0x1c7635;var _0x42a798=_0x1c7635['getChannelData'](0x0)['set'](_0x2d457b[_0x14c64e(0x3bc)]);_0x9b4720[_0x14c64e(0x36c)](0x0);}else _0x311669[_0x14c64e(0x16a)][_0x3522b1][_0x14c64e(0x436)]['chunked_mode_video'][_0x14c64e(0x94f)]=parseInt(_0x2d457b['timestamp']/0x2710)/0x64,_0x2b645c[_0x14c64e(0x53a)](_0x2d457b);}},_0x2b645c['processFrameVideo']=async function(_0x11db69){var _0x1ea5f=_0x5afb41;try{_0x11db69=new EncodedVideoChunk(_0x11db69);}catch(_0x3d2aec){errorlog(_0x3d2aec),errorlog(_0x11db69);return;}if(_0x2b645c['videoWriter']&&_0x2b645c[_0x1ea5f(0x49b)]['stopWriter']){if(!_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x6a7)]&&_0x11db69['type']!==_0x1ea5f(0xa68))log(_0x1ea5f(0x52b)),log(_0x11db69),!_0x2b645c[_0x1ea5f(0x5ff)]&&(_0x55fd2e['send'](JSON[_0x1ea5f(0x479)]({'kf':!![]})),_0x2b645c[_0x1ea5f(0x5ff)]=setTimeout(function(){var _0x17488f=_0x1ea5f;clearTimeout(_0x2b645c['requestKeyframe']),_0x2b645c[_0x17488f(0x5ff)]=null;},0x3e8));else!_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x6a7)]?(_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x6a7)]=Date[_0x1ea5f(0x610)](),_0x2b645c[_0x1ea5f(0x203)]['addFrame'](_0x11db69),log(_0x1ea5f(0x8bd)),_0x311669[_0x1ea5f(0x75b)]&&!_0x2b645c[_0x1ea5f(0x57d)]&&(_0x2b645c['updateTime']=setInterval(function(_0x102fca){var _0x3399bf=_0x1ea5f,_0x1aacb7=(Date[_0x3399bf(0x610)]()-_0x2b645c['video']['header'])/0x3e8,_0x21b212=Math[_0x3399bf(0x3c1)](_0x1aacb7/0x3c),_0x2aabb5=Math[_0x3399bf(0x3c1)](_0x1aacb7-_0x21b212*0x3c);try{document[_0x3399bf(0xa5d)](_0x3399bf(0x955)+_0x102fca+'\x27]')[_0x3399bf(0x82f)]=_0x3399bf(0x6ed)+_0x21b212+_0x3399bf(0x85c)+zpadTime(_0x2aabb5)+'s';}catch(_0x28778b){log(_0x3399bf(0x86c));}},0x3e8,_0x3522b1))):_0x2b645c['videoWriter'][_0x1ea5f(0x641)](_0x11db69);}_0x2b645c['video'][_0x1ea5f(0x9fa)]&&_0x2b645c[_0x1ea5f(0x30c)]&&_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x28b)]['state']===_0x1ea5f(0x208)&&(warnlog(_0x1ea5f(0x572)),_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x9fa)]=![],_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x28b)]=new VideoDecoder(_0x2b645c['init_video']),await _0x2b645c[_0x1ea5f(0x30c)]['decoder'][_0x1ea5f(0x50e)](_0x2b645c[_0x1ea5f(0x77a)]),_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x9fa)]=![]);if(_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x9fa)]||_0x11db69[_0x1ea5f(0x78a)]===_0x1ea5f(0xa68)){_0x2b645c[_0x1ea5f(0x30c)]['playbackheader']=!![];try{if(_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x265)])_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x64f)][_0x1ea5f(0x505)](_0x11db69);else{if(_0x2b645c[_0x1ea5f(0x30c)]['queue'][_0x1ea5f(0x8e9)])_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x64f)][_0x1ea5f(0x505)](_0x11db69);else{if(_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x3c5)]){_0x2b645c['video'][_0x1ea5f(0x265)]=!![];function _0x4c8794(_0x3a6478,_0xebabf9){var _0xb675c2=_0x1ea5f,_0xeef4ec=_0x3a6478[_0xb675c2(0x82c)]/0x3e8-(Date['now']()-_0xebabf9[_0xb675c2(0x2fe)]-_0xebabf9[_0xb675c2(0x30c)]['realTime']),_0x345b43=0x3e7;if(!_0x311669[_0xb675c2(0x16a)][_0xebabf9[_0xb675c2(0x54a)]]){clearTimeout(_0xebabf9[_0xb675c2(0x30c)][_0xb675c2(0x265)]),_0xebabf9[_0xb675c2(0x30c)][_0xb675c2(0x265)]=null,_0xebabf9[_0xb675c2(0x30c)][_0xb675c2(0x64f)]=[];return;}else{if(_0x311669['rpcs'][_0xebabf9[_0xb675c2(0x54a)]][_0xb675c2(0xa33)]!==![])_0x345b43=_0x311669[_0xb675c2(0x16a)][_0xebabf9[_0xb675c2(0x54a)]]['buffer'];else _0x311669[_0xb675c2(0xa33)]!==![]?_0x345b43=_0x311669[_0xb675c2(0xa33)]:_0x311669[_0xb675c2(0x16a)][_0xebabf9[_0xb675c2(0x54a)]][_0xb675c2(0xa33)]=_0x345b43;}_0xeef4ec+=_0x345b43,_0xeef4ec<0x0&&(_0xeef4ec=0x0),_0xebabf9[_0xb675c2(0x30c)][_0xb675c2(0x265)]=setTimeout(function(_0x47e91b,_0x1d07c2){var _0x20ba88=_0xb675c2;_0x47e91b[_0x20ba88(0x30c)][_0x20ba88(0x28b)][_0x20ba88(0x5f5)](_0x1d07c2),_0x47e91b[_0x20ba88(0x30c)]['queue'][_0x20ba88(0x8e9)]?_0x4c8794(_0x47e91b[_0x20ba88(0x30c)][_0x20ba88(0x64f)][_0x20ba88(0x74d)](),_0x47e91b):_0x47e91b[_0x20ba88(0x30c)][_0x20ba88(0x265)]=null;},_0xeef4ec,_0xebabf9,_0x3a6478);}try{_0x4c8794(_0x11db69,_0x2b645c);}catch(_0x19ef8c){errorlog(_0x19ef8c),_0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x265)]=null,!_0x2b645c[_0x1ea5f(0x5ff)]&&(_0x55fd2e[_0x1ea5f(0x2b9)](JSON['stringify']({'kf':!![]})),_0x2b645c[_0x1ea5f(0x5ff)]=setTimeout(function(){var _0x10fe72=_0x1ea5f;clearTimeout(_0x2b645c[_0x10fe72(0x5ff)]),_0x2b645c[_0x10fe72(0x5ff)]=null;},0x3e8));}}else _0x2b645c[_0x1ea5f(0x30c)][_0x1ea5f(0x28b)][_0x1ea5f(0x5f5)](_0x11db69);}}}catch(_0x1a1504){errorlog(_0x1a1504),_0x2b645c['video'][_0x1ea5f(0x9fa)]=![];}}!_0x2b645c['video'][_0x1ea5f(0x9fa)]&&(!_0x2b645c['requestKeyframe']&&(_0x55fd2e[_0x1ea5f(0x2b9)](JSON[_0x1ea5f(0x479)]({'kf':!![]})),_0x2b645c['requestKeyframe']=setTimeout(function(){var _0x24916a=_0x1ea5f;clearTimeout(_0x2b645c[_0x24916a(0x5ff)]),_0x2b645c[_0x24916a(0x5ff)]=null;},0x3e8)));},_0x2b645c[_0x5afb41(0x7ed)]=async function(_0x3a6843){var _0x218965=_0x5afb41;if(!_0x2b645c[_0x218965(0x433)]){errorlog(_0x218965(0x946));return;}try{_0x3a6843['type']='key',_0x3a6843=new EncodedAudioChunk(_0x3a6843);}catch(_0xf4181e){return;}_0x2b645c[_0x218965(0x203)]&&_0x2b645c[_0x218965(0x30c)][_0x218965(0x6a7)]&&_0x2b645c[_0x218965(0x49b)][_0x218965(0x6da)]&&_0x2b645c[_0x218965(0x203)][_0x218965(0x641)](_0x3a6843),_0x2b645c['audio'][_0x218965(0x28b)][_0x218965(0x5ae)]==='closed'&&(_0x2b645c[_0x218965(0x433)][_0x218965(0x28b)]=new AudioDecoder(_0x2b645c[_0x218965(0x82e)]),_0x2b645c[_0x218965(0x433)][_0x218965(0x28b)][_0x218965(0x50e)](_0x2b645c['stream_configAudio'])),_0x2b645c[_0x218965(0x433)][_0x218965(0x28b)][_0x218965(0x5f5)](_0x3a6843);};}else{if(_0x2b645c['audio']&&_0x2ecb3b[_0x5afb41(0x747)])_0x2b645c['audio'][_0x5afb41(0x3c5)]=_0x2ecb3b['realTimeAudio'];else _0x2b645c['video']&&_0x2ecb3b[_0x5afb41(0x8f0)]?_0x2b645c['video']['realTime']=_0x2ecb3b[_0x5afb41(0x8f0)]:errorlog(_0x2ecb3b);}return;}catch(_0x3ed2e4){errorlog(_0x3ed2e4);}try{var _0x18043a=_0x4fe1ed[_0x5afb41(0x3bc)];if(typeof _0x18043a==_0x5afb41(0xa46)){if(_0x2b645c[_0x5afb41(0xa33)]){var _0x527795=new Int8Array(_0x18043a['buffer']);_0x2b645c['buffer']=![],await _0x2b645c[_0x5afb41(0x4e3)]({'data':_0x527795,'timestamp':_0x2b645c[_0x5afb41(0x481)][0x0],'type':_0x2b645c[_0x5afb41(0x481)][0x1]});}_0x2b645c[_0x5afb41(0x481)]=JSON[_0x5afb41(0x52d)](_0x18043a);}else{try{if(_0x18043a[_0x5afb41(0x7ce)]>=0x40000){if(_0x2b645c[_0x5afb41(0xa33)]){_0x18043a=new Int8Array(_0x18043a);var _0x527795=new Int8Array(_0x2b645c[_0x5afb41(0xa33)][_0x5afb41(0x8e9)]+_0x18043a[_0x5afb41(0x8e9)]);_0x527795[_0x5afb41(0x5d7)](_0x2b645c[_0x5afb41(0xa33)]),_0x527795[_0x5afb41(0x5d7)](_0x18043a,_0x2b645c['buffer'][_0x5afb41(0x8e9)]),_0x2b645c['buffer']=_0x527795;}else _0x2b645c[_0x5afb41(0xa33)]=new Int8Array(_0x18043a);return;}else{if(_0x2b645c['buffer']){_0x18043a=new Int8Array(_0x18043a);var _0x527795=new Int8Array(_0x2b645c[_0x5afb41(0xa33)][_0x5afb41(0x8e9)]+_0x18043a['length']);_0x527795[_0x5afb41(0x5d7)](_0x2b645c[_0x5afb41(0xa33)]),_0x527795[_0x5afb41(0x5d7)](_0x18043a,_0x2b645c[_0x5afb41(0xa33)][_0x5afb41(0x8e9)]),_0x2b645c[_0x5afb41(0xa33)]=![],await _0x2b645c[_0x5afb41(0x4e3)]({'data':_0x527795,'timestamp':_0x2b645c[_0x5afb41(0x481)][0x0],'type':_0x2b645c['frameMeta'][0x1]});}else await _0x2b645c['processFrame']({'data':new Uint8Array(_0x18043a),'timestamp':_0x2b645c[_0x5afb41(0x481)][0x0],'type':_0x2b645c[_0x5afb41(0x481)][0x1]}),_0x2b645c[_0x5afb41(0x906)]&&_0x2b645c[_0x5afb41(0x906)]();}}catch(_0x158406){errorlog(_0x158406);}return;}}catch(_0x7bfb8d){errorlog(_0x7bfb8d);}};return;},_0x311669['setupIncoming']=async function(_0x311f09){var _0x3106c2=_0x134a17;log(_0x3106c2(0x53e));var _0x5be36a=_0x311f09[_0x3106c2(0x54a)];if(_0x5be36a in _0x311669[_0x3106c2(0x16a)]){if(_0x3106c2(0x1fd)in _0x311f09&&_0x311f09['session']){if(_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x1fd)]==_0x311f09[_0x3106c2(0x1fd)]){log(_0x3106c2(0x7fd));return;}warnlog(_0x3106c2(0x59f)),_0x311669[_0x3106c2(0x7b8)](_0x5be36a);}}else log(_0x3106c2(0x3ea));try{for(var _0x4b22a6 in _0x311669[_0x3106c2(0x16a)]){_0x311669[_0x3106c2(0x16a)][_0x4b22a6]['streamID']==_0x311f09[_0x3106c2(0x9bb)]&&(_0x311669[_0x3106c2(0x16a)][_0x4b22a6][_0x3106c2(0x751)]&&errorlog(_0x3106c2(0x216)),_0x311669[_0x3106c2(0x16a)][_0x4b22a6]['videoElement']&&(_0x311669[_0x3106c2(0x16a)][_0x4b22a6][_0x3106c2(0x49b)]['style'][_0x3106c2(0x355)]=_0x3106c2(0x42d)),warnlog(_0x3106c2(0x37d)),_0x311669['closeRPC'](_0x4b22a6),_0x4b22a6!==_0x5be36a&&(_0x4b22a6 in _0x311669[_0x3106c2(0x859)]&&(_0x311f09[_0x3106c2(0x1fd)]&&_0x311f09[_0x3106c2(0x1fd)][_0x3106c2(0x30d)](0x0,0x6)!==_0x311669[_0x3106c2(0x56b)]?(warnlog('CLOSING\x20SECONDARY\x20CONNECTION;\x20matched\x20stream\x20ID\x20has\x20re-connected'),log(_0x3106c2(0x3a9)),_0x311669[_0x3106c2(0x9d2)](_0x4b22a6,![])):warnlog(_0x3106c2(0x473)))));}document[_0x3106c2(0x5c8)](_0x3106c2(0x212))&&(document[_0x3106c2(0x5c8)](_0x3106c2(0x212))[_0x3106c2(0x826)][_0x3106c2(0x9d0)](document[_0x3106c2(0x5c8)](_0x3106c2(0x212))),document[_0x3106c2(0x319)](_0x3106c2(0xa3b))[_0x3106c2(0x982)](_0x5e1e9a=>{var _0x309cde=_0x3106c2;_0x5e1e9a[_0x309cde(0x424)][_0x309cde(0x761)]('hidden2');}));}catch(_0x56a4fc){errorlog(_0x56a4fc);}if(_0x311669[_0x3106c2(0x96f)]!==![]){if(Object['keys'](_0x311669[_0x3106c2(0x16a)])[_0x3106c2(0x8e9)]>=_0x311669[_0x3106c2(0x96f)]){warnlog(_0x3106c2(0x380));return;}}else{if(_0x311669[_0x3106c2(0x6b1)]!==![]){if(Object[_0x3106c2(0x19b)](_0x311669[_0x3106c2(0x16a)])[_0x3106c2(0x8e9)]+Object[_0x3106c2(0x19b)](_0x311669[_0x3106c2(0x859)])[_0x3106c2(0x8e9)]>=_0x311669['maxconnections']){warnlog('Publisher\x20will\x20be\x20ignored\x20due\x20to\x20max\x20connections\x20already\x20hit');return;}}}if(_0x311669[_0x3106c2(0x64f)]){if(_0x311669[_0x3106c2(0x75b)])!(_0x5be36a in _0x311669[_0x3106c2(0x859)])&&_0x311669[_0x3106c2(0x49a)](_0x5be36a);else{if(_0x311669[_0x3106c2(0x95b)][_0x3106c2(0x49e)](_0x5be36a)==-0x1)return;}}!_0x311669['configuration']&&await chooseBestTURN();_0x311669['encodedInsertableStreams']&&(_0x311669['configuration'][_0x3106c2(0xa6e)]=!![]);_0x311669[_0x3106c2(0x70d)]&&(_0x311669[_0x3106c2(0x7db)][_0x3106c2(0x9de)]=_0x311669[_0x3106c2(0x70d)]);try{_0x311669[_0x3106c2(0x16a)][_0x5be36a]=new RTCPeerConnection(_0x311669[_0x3106c2(0x7db)]);}catch(_0x532b76){!_0x311669[_0x3106c2(0x78c)]&&warnUser(_0x3106c2(0x342));errorlog(_0x532b76);return;}if(_0x311669[_0x3106c2(0x4e5)]){if(Object[_0x3106c2(0x19b)](_0x311669[_0x3106c2(0x16a)])['length']>0x1){warnlog(_0x3106c2(0xa0a)),log(_0x311669[_0x3106c2(0x16a)]),delete _0x311669['rpcs'][_0x5be36a],updateUserList();return;}else warnlog(_0x3106c2(0x8d3));}_0x311f09[_0x3106c2(0x9bb)]in _0x311669[_0x3106c2(0x8fb)]&&delete _0x311669[_0x3106c2(0x8fb)][_0x311f09[_0x3106c2(0x9bb)]];try{_0x311669['rpcs'][_0x5be36a][_0x3106c2(0x9bb)]=_0x311f09[_0x3106c2(0x9bb)],await checkDirectorStreamID();}catch(_0x580a57){errorlog(_0x580a57);return;}_0x311f09['session']?_0x311669[_0x3106c2(0x16a)][_0x5be36a]['session']=_0x311f09[_0x3106c2(0x1fd)]:_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x1fd)]=null;_0x311669[_0x3106c2(0x16a)][_0x5be36a]['activelySpeaking']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x84b)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['allowMIDI']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['allowGraphs']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x436)]={},_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x97a)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['stats']['Audio_Loudness']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['showDirector']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x45c)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['canvasIntervalAction']=null,_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x9cc)]=-0x1,_0x311669['rpcs'][_0x5be36a]['bandwidthMuted']=![],_0x311669['rpcs'][_0x5be36a][_0x3106c2(0xa33)]=![],_0x311669['rpcs'][_0x5be36a][_0x3106c2(0x921)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['channelWidth']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x53c)]=-0x1,_0x311669['rpcs'][_0x5be36a]['manualBandwidth']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x49b)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x678)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x9eb)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x532)]=[],_0x311669['rpcs'][_0x5be36a][_0x3106c2(0x8ab)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['iframeVideo']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x7b9)]=![],_0x311669['rpcs'][_0x5be36a][_0x3106c2(0x621)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['virtualHangup']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x94d)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['remoteMuteElement']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x9a4)]=null,_0x311669['rpcs'][_0x5be36a][_0x3106c2(0x600)]=![],_0x311669['rpcs'][_0x5be36a][_0x3106c2(0x4fc)]=null,_0x311669[_0x3106c2(0x16a)][_0x5be36a]['mutedStateMixer']=null,_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x3e1)]=null,_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x350)]=null,_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x665)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['rotate']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['savedVolume']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['scaleHeight']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x863)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x482)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x46b)]=![],_0x311669['rpcs'][_0x5be36a]['volumeControl']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x585)]=null,_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x2ef)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x701)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['director']=null,_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x3be)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x6f2)]=0x64,_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x426)]=0x0,_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x1a1)]=0x0,_0x311669[_0x3106c2(0x16a)][_0x5be36a]['settings']=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a]['opacityDisconnect']='1',_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x644)]='1',_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x176)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x353)]=0x0,_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x89c)]=![],_0x311669['rpcs'][_0x5be36a][_0x3106c2(0xa63)]=![],_0x311669['rpcs'][_0x5be36a][_0x3106c2(0x237)]=null,_0x311669[_0x3106c2(0x16a)][_0x5be36a]['canvas']=null,_0x311669[_0x3106c2(0x16a)][_0x5be36a]['inboundAudioPipeline']={},_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x325)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x9b0)]=![],_0x311669['rpcs'][_0x5be36a][_0x3106c2(0x586)]=Date[_0x3106c2(0x610)](),_0x311669['rpcs'][_0x5be36a][_0x3106c2(0x87c)]=![],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x7af)]=_0x311669[_0x3106c2(0x7af)];(_0x311669[_0x3106c2(0x2d3)]==0x2||_0x311669[_0x3106c2(0x2d3)]==0x4)&&(_0x311669[_0x3106c2(0x16a)][_0x5be36a]['loudest']=!![]);if(_0x311669[_0x3106c2(0x277)]){var _0x5eee14=createRichVideoElement(_0x5be36a);_0x5eee14[_0x3106c2(0x886)][_0x3106c2(0x355)]=_0x3106c2(0xa87);}if(_0x311669['director']){if(_0x311669['customWSS']&&_0x3106c2(0x782)in _0x311f09&&_0x311f09['isScene']!==![]){}else{var _0x367427=soloLinkGenerator(_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x9bb)]);_0x3106c2(0x97a)in _0x311f09?createControlBox(_0x5be36a,_0x367427,_0x311669['rpcs'][_0x5be36a]['streamID'],_0x311f09['slot']):createControlBox(_0x5be36a,_0x367427,_0x311669['rpcs'][_0x5be36a]['streamID']);}}_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x54a)]=_0x5be36a;try{if(_0x311669[_0x3106c2(0x82b)]){if(_0x311669['view_set'][_0x3106c2(0x2c2)](_0x311669[_0x3106c2(0x16a)][_0x5be36a]['streamID'])){if(_0x311669[_0x3106c2(0x708)]!==![]){let _0x13b8a3=_0x311669[_0x3106c2(0x82b)][_0x3106c2(0x49e)](_0x311669['rpcs'][_0x5be36a][_0x3106c2(0x9bb)]);_0x311669['bitrate_set']['length']>_0x13b8a3&&(_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x3e4)]=parseInt(_0x311669['bitrate_set'][_0x13b8a3]),_0x311669[_0x3106c2(0x16a)][_0x5be36a]['manualBandwidth']<=0x0&&(_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x3e4)]=![]));}}}}catch(_0x5389e5){errorlog(_0x5389e5);}_0x311669[_0x3106c2(0x16a)][_0x5be36a]['onclose']=function(_0x4ff9d9){var _0x4b8997=_0x3106c2;log(_0x4b8997(0x169)),_0x311669[_0x4b8997(0x7b8)](_0x5be36a);},_0x311669['rpcs'][_0x5be36a]['iceTimer']=null,_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x2f5)]=[],_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x2a3)]=function(_0x2957e2){var _0x583488=_0x3106c2;if(_0x2957e2['candidate']==null){log(_0x583488(0x499));_0x311669[_0x583488(0x16a)][_0x5be36a]&&_0x311669[_0x583488(0x16a)][_0x5be36a][_0x583488(0x204)]&&(_0x311669['rpcs'][_0x5be36a][_0x583488(0x204)]([..._0x311669[_0x583488(0x16a)][_0x5be36a]['iceBundle']]),clearTimeout(_0x311669[_0x583488(0x16a)][_0x5be36a][_0x583488(0x44b)]),_0x311669[_0x583488(0x16a)][_0x5be36a]['iceTimer']=null,_0x311669['rpcs'][_0x5be36a]['iceBundle']=[],_0x311669[_0x583488(0x16a)][_0x5be36a][_0x583488(0x204)]=null);return;}try{if(_0x311669[_0x583488(0x242)]){if(_0x2957e2['candidate'][_0x583488(0x905)]['indexOf'](_0x311669[_0x583488(0x242)])===-0x1){log(_0x583488(0x6f4));return;}else log(_0x2957e2[_0x583488(0x905)]);}}catch(_0x555678){errorlog(_0x555678);}if(_0x311669[_0x583488(0x16a)][_0x5be36a]&&(_0x311669[_0x583488(0x16a)][_0x5be36a][_0x583488(0x204)]||_0x311669[_0x583488(0x16a)][_0x5be36a][_0x583488(0x44b)]!==null)){_0x311669[_0x583488(0x16a)][_0x5be36a][_0x583488(0x2f5)]['push'](_0x2957e2[_0x583488(0x905)]);return;}_0x311669['rpcs'][_0x5be36a][_0x583488(0x2f5)]['push'](_0x2957e2[_0x583488(0x905)]),_0x311669['rpcs'][_0x5be36a]['iceTimer']=setTimeout(function(_0x30bb85){var _0x139c4d=_0x583488;if(!(_0x30bb85 in _0x311669['rpcs']))return;if(_0x311669[_0x139c4d(0x16a)][_0x30bb85]['whipCallback2'])return;_0x311669[_0x139c4d(0x16a)][_0x30bb85]['iceTimer']=null;if(_0x311669[_0x139c4d(0x16a)][_0x30bb85][_0x139c4d(0x2f5)]==[])return;var _0x22b9dc={};_0x22b9dc[_0x139c4d(0x54a)]=_0x30bb85,_0x22b9dc[_0x139c4d(0x78a)]=_0x139c4d(0x24d),_0x22b9dc['candidates']=_0x311669['rpcs'][_0x30bb85][_0x139c4d(0x2f5)],_0x22b9dc[_0x139c4d(0x1fd)]=_0x311669[_0x139c4d(0x16a)][_0x30bb85]['session'],_0x311669['rpcs'][_0x30bb85]['iceBundle']=[],_0x311669['password']?_0x311669[_0x139c4d(0x331)](JSON[_0x139c4d(0x479)](_0x22b9dc[_0x139c4d(0x2a8)]))[_0x139c4d(0x619)](function(_0x1aac6a){var _0x2046f3=_0x139c4d;_0x22b9dc[_0x2046f3(0x2a8)]=_0x1aac6a[0x0],_0x22b9dc[_0x2046f3(0x9ed)]=_0x1aac6a[0x1],_0x311669['anyrequest'](_0x22b9dc);})[_0x139c4d(0x3b6)](errorlog):_0x311669['anyrequest'](_0x22b9dc);},0x190,_0x5be36a);},_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x6e5)]=function(_0x5975ac){var _0x26fdf6=_0x3106c2;switch(this[_0x26fdf6(0x3ee)]){case _0x26fdf6(0x6bd):log('new'),log(_0x26fdf6(0x81c)),clearInterval(_0x311669['rpcs'][this[_0x26fdf6(0x54a)]]['closeTimeout']);case _0x26fdf6(0x56c):log('checking'),log(_0x26fdf6(0x2be)),clearInterval(_0x311669['rpcs'][this[_0x26fdf6(0x54a)]][_0x26fdf6(0x9a4)]);case _0x26fdf6(0x7ea):log('**\x20connected'),log(_0x26fdf6(0x5da)),clearInterval(_0x311669[_0x26fdf6(0x16a)][this[_0x26fdf6(0x54a)]][_0x26fdf6(0x9a4)]);if(_0x311669[_0x26fdf6(0x4e5)]){if(_0x311669['ws'][_0x26fdf6(0x8ee)]!==0x1){_0x311669['ws'][_0x26fdf6(0x1cf)]();break;}_0x311669['ws'][_0x26fdf6(0x1cf)](),setTimeout(function(){var _0x421ac3=_0x26fdf6;_0x311669[_0x421ac3(0x78c)]!=!![]&&warnUser(getTranslation(_0x421ac3(0x320)));},0x1);}break;case _0x26fdf6(0x381):log(_0x26fdf6(0x5d9)),warnlog('rpcs\x20onconnectionstatechange\x20Disconnected;\x20retry\x20in\x205s'),clearInterval(_0x311669[_0x26fdf6(0x16a)][this[_0x26fdf6(0x54a)]]['closeTimeout']);if(_0x311669[_0x26fdf6(0x16a)][this[_0x26fdf6(0x54a)]][_0x26fdf6(0x87c)])return;this[_0x26fdf6(0x54a)]in _0x311669[_0x26fdf6(0x16a)]?_0x311669[_0x26fdf6(0x16a)][this[_0x26fdf6(0x54a)]][_0x26fdf6(0x9a4)]=setTimeout(function(_0x47e983){var _0x31719c=_0x26fdf6;log('no\x20reconnect\x20even\x20after\x205s;\x20closing'),_0x311669[_0x31719c(0x7b8)](_0x47e983);},0x1388,this['UUID']):log(_0x26fdf6(0x26f));break;case'failed':warnlog(_0x26fdf6(0x5b2)),log(_0x26fdf6(0xa3a)),clearInterval(_0x311669[_0x26fdf6(0x16a)][this[_0x26fdf6(0x54a)]][_0x26fdf6(0x9a4)]);this[_0x26fdf6(0x54a)]in _0x311669['rpcs']?_0x311669['rpcs'][this['UUID']][_0x26fdf6(0x9a4)]=setTimeout(function(_0x545ebc){var _0x4f4738=_0x26fdf6;log('No\x20reconnect\x20even\x20after\x205s;\x20closing'),_0x311669[_0x4f4738(0x7b8)](_0x545ebc);},0xbb8,this[_0x26fdf6(0x54a)]):log(_0x26fdf6(0x26f));break;case _0x26fdf6(0x208):warnlog('RTC\x20closed'),_0x311669[_0x26fdf6(0x7b8)](this[_0x26fdf6(0x54a)]);break;default:log(_0x26fdf6(0x253)),log('this.connectionState:\x20'+this[_0x26fdf6(0x3ee)]),clearInterval(_0x311669[_0x26fdf6(0x16a)][this[_0x26fdf6(0x54a)]][_0x26fdf6(0x9a4)]);break;}},_0x311669['rpcs'][_0x5be36a]['onicegatheringstatechange']=function(_0x774723){var _0x3bccac=_0x3106c2;let _0x109569=_0x774723[_0x3bccac(0x81f)];switch(_0x109569[_0x3bccac(0x8b8)]){case _0x3bccac(0x87e):log(_0x3bccac(0xa52));break;case _0x3bccac(0x73e):log(_0x3bccac(0x60c));_0x311669[_0x3bccac(0x16a)][_0x5be36a][_0x3bccac(0x204)]&&(_0x311669[_0x3bccac(0x16a)][_0x5be36a]['whipCallback2']([..._0x311669[_0x3bccac(0x16a)][_0x5be36a][_0x3bccac(0x2f5)]]),clearTimeout(_0x311669[_0x3bccac(0x16a)][_0x5be36a][_0x3bccac(0x44b)]),_0x311669['rpcs'][_0x5be36a]['iceTimer']=null,_0x311669[_0x3bccac(0x16a)][_0x5be36a][_0x3bccac(0x2f5)]=[],_0x311669[_0x3bccac(0x16a)][_0x5be36a][_0x3bccac(0x204)]=null);break;}},_0x311669[_0x3106c2(0x16a)][_0x5be36a]['oniceconnectionstatechange']=function(){var _0x24eba5=_0x3106c2;try{if(this[_0x24eba5(0x870)]==_0x24eba5(0x208))errorlog(_0x24eba5(0x2a9));else{if(this[_0x24eba5(0x870)]==_0x24eba5(0x381)){if(_0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x87c)])return;warnlog(_0x24eba5(0xa4e)),_0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x15c)]='0',_0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x49b)]['style']['opacity']='0',_0x311669['rpcs'][_0x5be36a][_0x24eba5(0x221)]=setTimeout(function(_0x35be26){updateMixer();},0x1f4,_0x5be36a);}else this[_0x24eba5(0x870)]==_0x24eba5(0x190)?errorlog('ICE\x20FAILED'):(log(_0x24eba5(0x3f0)+this[_0x24eba5(0x870)]),_0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x221)]&&clearTimeout(_0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x221)]),_0x311669['rpcs'][_0x5be36a][_0x24eba5(0x49b)]&&_0x24eba5(0x486)in _0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x49b)][_0x24eba5(0x886)]?_0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x15c)]=='0'&&_0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x644)]=='1'?(_0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x49b)][_0x24eba5(0x886)][_0x24eba5(0x486)]='1',_0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x15c)]='1',updateMixer()):_0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x15c)]='1':_0x311669[_0x24eba5(0x16a)][_0x5be36a][_0x24eba5(0x15c)]='1');}}catch(_0x2831a7){}},_0x311669[_0x3106c2(0x16a)][_0x5be36a]['ondatachannel']=function(_0x49b3f7){var _0x471263=_0x3106c2;log(_0x49b3f7);if(_0x49b3f7[_0x471263(0x27a)][_0x471263(0x89c)]&&_0x49b3f7[_0x471263(0x27a)][_0x471263(0x89c)]!==_0x471263(0x73a)){if(_0x311669[_0x471263(0x2dc)][_0x471263(0x2c2)](_0x311669[_0x471263(0x16a)][_0x5be36a][_0x471263(0x9bb)]))return;_0x49b3f7['channel'][_0x471263(0x89c)]===_0x471263(0x88e)?_0x311669[_0x471263(0xa51)](_0x311669[_0x471263(0x16a)],_0x5be36a,_0x49b3f7[_0x471263(0x27a)]):_0x311669[_0x471263(0x1b5)](_0x311669[_0x471263(0x16a)],_0x5be36a,_0x49b3f7[_0x471263(0x27a)]);return;}_0x311669[_0x471263(0x16a)][_0x5be36a][_0x471263(0x4a7)]=_0x49b3f7['channel'],_0x311669[_0x471263(0x16a)][_0x5be36a][_0x471263(0x4a7)][_0x471263(0x54a)]=_0x5be36a,_0x311669[_0x471263(0x16a)][_0x5be36a]['receiveChannel']['onerror']=_0x55179f=>{var _0x50f892=_0x471263;warnlog(_0x55179f),log(_0x50f892(0x77b)+_0x5be36a);},_0x311669[_0x471263(0x16a)][_0x5be36a][_0x471263(0x4a7)][_0x471263(0x777)]=_0x2f1780=>{var _0x3be940=_0x471263,_0x58ad64={};_0x58ad64[_0x3be940(0x5bf)]=![],_0x58ad64['allowmidi']=![],_0x58ad64[_0x3be940(0x54d)]=![],_0x58ad64[_0x3be940(0x8b0)]=![],_0x58ad64[_0x3be940(0x433)]=![],_0x58ad64[_0x3be940(0x30c)]=![],_0x58ad64[_0x3be940(0x254)]=![],_0x58ad64['allowwebp']=![],_0x58ad64[_0x3be940(0x343)]=![],_0x58ad64[_0x3be940(0x4ea)]=![],_0x58ad64['allowchunked']=![];_0x311669['audioCodec']&&(_0x311669['audioCodec']==='red'||_0x311669['audioCodec']===_0x3be940(0x61d))&&(_0x58ad64['preferAudioCodec']=_0x311669['audioCodec']);try{if(_0x311669[_0x3be940(0x308)]!==![]){if(_0x311669[_0x3be940(0x308)]===!![])_0x58ad64[_0x3be940(0x343)]=!![],_0x58ad64['allowscreenvideo']=!![];else _0x311669[_0x3be940(0x308)][_0x3be940(0x2c2)](_0x311669['rpcs'][_0x5be36a][_0x3be940(0x9bb)])?(_0x58ad64[_0x3be940(0x343)]=!![],_0x58ad64[_0x3be940(0x4ea)]=!![]):(_0x58ad64['allowscreenaudio']=![],_0x58ad64['allowscreenvideo']=![]);}else _0x58ad64[_0x3be940(0x343)]=!![],_0x58ad64['allowscreenvideo']=!![];if(_0x58ad64['allowscreenvideo']){if(_0x311669[_0x3be940(0x711)]!==![])!_0x311669[_0x3be940(0x711)][_0x3be940(0x2c2)](_0x311669[_0x3be940(0x16a)][_0x5be36a]['streamID']+':s')&&(_0x58ad64[_0x3be940(0x4ea)]=![]);else{if(_0x311669[_0x3be940(0x254)]!==![]){if(_0x311669[_0x3be940(0x254)]!==null)_0x311669[_0x3be940(0x16a)][_0x5be36a][_0x3be940(0x9bb)]+':s'===_0x311669['broadcast']?_0x58ad64['broadcast']=!![]:_0x58ad64[_0x3be940(0x4ea)]=![];else _0x311669[_0x3be940(0x2f3)]&&(_0x5be36a==_0x311669['directorUUID']?_0x58ad64[_0x3be940(0x254)]=!![]:_0x58ad64[_0x3be940(0x4ea)]=![]);}else _0x311669[_0x3be940(0x430)]!==![]&&(_0x311669[_0x3be940(0x430)][_0x3be940(0x2c2)](_0x311669['rpcs'][_0x5be36a]['streamID']+':s')&&(_0x58ad64['video']=![]));}}_0x58ad64['allowscreenaudio']&&(_0x311669[_0x3be940(0x542)]!==![]&&(!_0x311669[_0x3be940(0x542)][_0x3be940(0x2c2)](_0x311669[_0x3be940(0x16a)][_0x5be36a]['streamID']+':s')&&(_0x58ad64[_0x3be940(0x343)]=![])));}catch(_0xc3970b){errorlog(_0xc3970b);}try{if(_0x311669[_0x3be940(0x711)]!==![])_0x311669[_0x3be940(0x711)][_0x3be940(0x2c2)](_0x311669[_0x3be940(0x16a)][_0x5be36a][_0x3be940(0x9bb)])?_0x58ad64[_0x3be940(0x30c)]=!![]:_0x58ad64[_0x3be940(0x30c)]=![];else{if(_0x311669['broadcast']!==![]){if(_0x311669[_0x3be940(0x254)]!==null)_0x311669[_0x3be940(0x16a)][_0x5be36a][_0x3be940(0x9bb)]===_0x311669[_0x3be940(0x254)]?(_0x58ad64[_0x3be940(0x254)]=!![],_0x58ad64['video']=!![]):_0x58ad64['video']=![];else _0x311669[_0x3be940(0x2f3)]&&(_0x5be36a==_0x311669['directorUUID']?(_0x58ad64[_0x3be940(0x254)]=!![],_0x58ad64['video']=!![]):_0x58ad64[_0x3be940(0x30c)]=![]);}else _0x311669['exclude']!==![]?_0x311669[_0x3be940(0x430)][_0x3be940(0x2c2)](_0x311669[_0x3be940(0x16a)][_0x5be36a][_0x3be940(0x9bb)])?_0x58ad64[_0x3be940(0x30c)]=![]:_0x58ad64[_0x3be940(0x30c)]=!![]:_0x58ad64[_0x3be940(0x30c)]=!![];}_0x311669['noaudio']!==![]?_0x311669['noaudio'][_0x3be940(0x2c2)](_0x311669[_0x3be940(0x16a)][_0x5be36a][_0x3be940(0x9bb)])?_0x58ad64[_0x3be940(0x433)]=!![]:_0x58ad64[_0x3be940(0x433)]=![]:_0x58ad64[_0x3be940(0x433)]=!![];_0x311669[_0x3be940(0x3a2)]!==![]?_0x311669['noiframe']['includes'](_0x311669[_0x3be940(0x16a)][_0x5be36a][_0x3be940(0x9bb)])?_0x58ad64[_0x3be940(0x54d)]=!![]:_0x58ad64[_0x3be940(0x54d)]=![]:_0x58ad64['iframe']=!![];if(_0x311669[_0x3be940(0x924)]!==![])_0x311669[_0x3be940(0x924)]['includes'](_0x311669[_0x3be940(0x16a)][_0x5be36a][_0x3be940(0x9bb)])?_0x58ad64['widget']=!![]:_0x58ad64[_0x3be940(0x8b0)]=![];else{if(_0x311669[_0x3be940(0x98d)]!==![])_0x58ad64[_0x3be940(0x8b0)]=![];else _0x311669['view']&&!_0x311669[_0x3be940(0x75b)]&&_0x311669[_0x3be940(0x7cb)]===![]?_0x58ad64['widget']=![]:_0x58ad64[_0x3be940(0x8b0)]=!![];}_0x311669[_0x3be940(0x8b4)]&&(_0x58ad64[_0x3be940(0x1c0)]=![]);_0x311669['hideDirector']&&(_0x58ad64[_0x3be940(0x3cb)]=_0x311669[_0x3be940(0x9fc)]);_0x311669[_0x3be940(0x272)]!==![]&&(!_0x311669['allowVideos'][_0x3be940(0x2c2)](_0x311669['rpcs'][_0x5be36a][_0x3be940(0x9bb)])&&(_0x58ad64[_0x3be940(0x30c)]=![],_0x58ad64[_0x3be940(0x433)]=![]));(_0x311669[_0x3be940(0x5b9)]||_0x311669['midiRemote'])&&(_0x58ad64['allowmidi']=_0x311669['midiIn']||_0x311669[_0x3be940(0x734)]);_0x58ad64[_0x3be940(0x5bf)]=!![];_0x311669['nodownloads']&&(_0x58ad64[_0x3be940(0x5bf)]=![]);_0x311669[_0x3be940(0x1e7)]?_0x58ad64[_0x3be940(0x9b8)]=![]:_0x58ad64[_0x3be940(0x9b8)]=!![];_0x311669[_0x3be940(0x24e)]&&(_0x311669[_0x3be940(0x24e)]==_0x3be940(0x352)||_0x311669[_0x3be940(0x24e)]==_0x3be940(0x359)||_0x311669[_0x3be940(0x24e)]=='jpeg')&&(_0x58ad64['allowwebp']=!![]);_0x311669['accept_layouts']&&(_0x58ad64['layout']=!![]);if(_0x311669['badStreamList'][_0x3be940(0x2c2)](_0x311669[_0x3be940(0x16a)][_0x5be36a][_0x3be940(0x9bb)])){warnlog('new\x20connection\x20is\x20contained\x20in\x20badStreamList!\x20This\x20might\x20be\x20the\x20director\x27s\x20video/audio\x20->\x20this\x20a\x20scene?'),_0x58ad64[_0x3be940(0x5bf)]=![],_0x58ad64['allowmidi']=![],_0x58ad64['iframe']=![],_0x58ad64[_0x3be940(0x8b0)]=![],_0x58ad64[_0x3be940(0x433)]=![],_0x58ad64['video']=![],_0x58ad64[_0x3be940(0x254)]=![],_0x58ad64[_0x3be940(0x81a)]=![];;}}catch(_0x40e611){errorlog(_0x40e611);}try{_0x58ad64[_0x3be940(0x6ee)]={},_0x58ad64['info']['label']=_0x311669['label'],_0x58ad64['info']['order']=_0x311669[_0x3be940(0xa63)],_0x58ad64['info'][_0x3be940(0x9d9)]=_0x311669['stereo'],_0x58ad64[_0x3be940(0x6ee)][_0x3be940(0x2c0)]=_0x311669[_0x3be940(0x739)],_0x58ad64[_0x3be940(0x6ee)][_0x3be940(0x16b)]=_0x311669[_0x3be940(0x543)],_0x58ad64['info']['codec_url']=_0x311669[_0x3be940(0x24e)];_0x311669[_0x3be940(0x292)]&&(_0x58ad64[_0x3be940(0x6ee)]['audio_codec_url']=_0x311669[_0x3be940(0x292)]);_0x58ad64[_0x3be940(0x6ee)][_0x3be940(0x3d3)]=_0x311669[_0x3be940(0x3d3)],_0x58ad64[_0x3be940(0x6ee)][_0x3be940(0x920)]=_0x311669[_0x3be940(0x920)],_0x58ad64[_0x3be940(0x6ee)]['enhance_audio']=_0x311669[_0x3be940(0x1a9)],_0x58ad64[_0x3be940(0x6ee)][_0x3be940(0x72b)]=_0x311669[_0x3be940(0x72b)],_0x58ad64['info'][_0x3be940(0x38b)]=_0x311669[_0x3be940(0x38b)],_0x58ad64[_0x3be940(0x6ee)][_0x3be940(0x5e9)]=_0x311669[_0x3be940(0x5e9)];navigator&&navigator[_0x3be940(0x64e)]&&(_0x58ad64[_0x3be940(0x6ee)][_0x3be940(0xa77)]=navigator['userAgent']);navigator&&navigator[_0x3be940(0x9f2)]&&(_0x58ad64['info'][_0x3be940(0x9f2)]=navigator[_0x3be940(0x9f2)]);gpgpuSupport&&(_0x58ad64[_0x3be940(0x6ee)][_0x3be940(0x6f5)]=gpgpuSupport);cpuSupport&&(_0x58ad64[_0x3be940(0x6ee)]['CPU']=cpuSupport);if(_0x311669['disableOBS']===![]){if(window['obsstudio']){_0x58ad64[_0x3be940(0x6ee)]['obs']=window[_0x3be940(0x6ff)][_0x3be940(0x51a)];try{_0x58ad64=_0x311669[_0x3be940(0xa4c)](_0x58ad64,_0x5be36a);}catch(_0x41924c){errorlog(_0x41924c),warnUser(_0x41924c[_0x3be940(0x6d8)]);}}else _0x58ad64[_0x3be940(0x6ee)]['obs']=![];}else _0x58ad64[_0x3be940(0x6ee)][_0x3be940(0x34c)]=![];}catch(_0x270115){};_0x58ad64[_0x3be940(0x798)]=![],_0x58ad64[_0x3be940(0x98d)]=![],_0x58ad64[_0x3be940(0x75b)]=![],_0x58ad64[_0x3be940(0x6f0)]=![],_0x58ad64[_0x3be940(0x920)]=![];_0x311669[_0x3be940(0x24d)]&&(_0x58ad64['remote']=!![]);_0x311669[_0x3be940(0x1a9)]&&(_0x58ad64[_0x3be940(0x391)]=!![]);_0x311669[_0x3be940(0x288)]&&(_0x58ad64['degrade']=_0x311669['degrade']);_0x311669['solo']&&(_0x58ad64[_0x3be940(0x438)]=_0x311669['solo']);_0x311669[_0x3be940(0x458)]!==![]&&(_0x58ad64['keyframeRate']=_0x311669['keyframeRate']);if(_0x311669['director']){_0x58ad64['director']=!![],_0x58ad64[_0x3be940(0x920)]=_0x311669[_0x3be940(0x920)];if(_0x311669['directorUUID']&&_0x311669[_0x3be940(0x2f3)]===_0x5be36a)_0x311669[_0x3be940(0x6ab)]();else{var _0x1d15b7={};_0x1d15b7[_0x3be940(0x1d7)]=[];for(var _0x45d1d8 in _0x311669['pcs']){_0x311669[_0x3be940(0x859)][_0x45d1d8][_0x3be940(0x5b5)]===!![]&&_0x1d15b7[_0x3be940(0x1d7)][_0x3be940(0x505)](_0x45d1d8);}_0x1d15b7[_0x3be940(0x1d7)][_0x3be940(0x8e9)]&&(_0x58ad64[_0x3be940(0x8ce)]=_0x1d15b7);}if(_0x311669[_0x3be940(0xa3c)]&&_0x311669['roomTimer']>0x0)_0x58ad64['setClock']=_0x311669[_0x3be940(0xa3c)]-Date['now']()/0x3e8,_0x58ad64[_0x3be940(0x5e8)]=!![],_0x58ad64[_0x3be940(0x876)]=!![];else _0x311669[_0x3be940(0xa3c)]&&_0x311669[_0x3be940(0xa3c)]<0x0&&(_0x58ad64[_0x3be940(0x7b3)]=_0x311669[_0x3be940(0xa3c)]*-0x1,_0x58ad64[_0x3be940(0x5e8)]=!![],_0x58ad64['startClock']=!![],_0x58ad64[_0x3be940(0x4dc)]=!![]);_0x311669['showRoomTime']&&(_0x58ad64[_0x3be940(0x6f3)]=!![]);}else{if(_0x311669[_0x3be940(0x98d)]!==![])_0x58ad64[_0x3be940(0x98d)]=_0x311669[_0x3be940(0x98d)],(_0x311669[_0x3be940(0x560)]||_0x311669[_0x3be940(0x438)])&&(_0x58ad64['showDirector']=_0x311669[_0x3be940(0x560)]||_0x311669[_0x3be940(0x438)]);else _0x311669[_0x3be940(0x4e1)]!==![]&&_0x311669[_0x3be940(0x4e1)]!==''&&(_0x58ad64['forceios']=_0x311669[_0x3be940(0x920)],_0x58ad64['guest']=!![]);}if(_0x311669[_0x3be940(0x6b5)])_0x58ad64[_0x3be940(0x6b5)]=parseFloat(_0x311669[_0x3be940(0x6b5)]);else(_0x311669[_0x3be940(0x3f5)]||_0x311669[_0x3be940(0x97c)])&&(_0x58ad64[_0x3be940(0x37b)]={},_0x58ad64['requestResolution']['h']=null,_0x58ad64[_0x3be940(0x37b)]['w']=null,_0x311669[_0x3be940(0x3f5)]&&(_0x58ad64[_0x3be940(0x37b)]['h']=_0x311669[_0x3be940(0x3f5)],_0x311669[_0x3be940(0x16a)][_0x5be36a][_0x3be940(0x1c6)]=_0x311669[_0x3be940(0x3f5)]),_0x311669[_0x3be940(0x97c)]&&(_0x58ad64[_0x3be940(0x37b)]['w']=_0x311669[_0x3be940(0x97c)],_0x311669[_0x3be940(0x16a)][_0x5be36a][_0x3be940(0x863)]=_0x311669['viewwidth']));!_0x311669[_0x3be940(0x4e1)]&&(_0x311669[_0x3be940(0x3df)]&&(playtone(![],'jointone'),showNotification('There\x27s\x20a\x20new\x20incoming\x20connection.'))),_0x311669['rpcs'][_0x5be36a][_0x3be940(0x8e6)]=_0x58ad64,_0x311669[_0x3be940(0xa14)](_0x58ad64,_0x5be36a)?log(_0x3be940(0x945)):errorlog(_0x3be940(0x926)),pokeIframeAPI(_0x3be940(0x22d),!![],_0x5be36a),pokeIframeAPI(_0x3be940(0x759),!![],_0x5be36a),pokeAPI(_0x3be940(0x60a),_0x311669[_0x3be940(0x16a)][_0x5be36a]['streamID']);},_0x311669['rpcs'][_0x5be36a][_0x471263(0x4a7)][_0x471263(0x17a)]=async _0x2efe54=>{var _0x13d2e3=_0x471263;if(typeof _0x2efe54[_0x13d2e3(0x3bc)]=='object'){if(!_0x311669[_0x13d2e3(0x16a)][_0x5be36a][_0x13d2e3(0x678)]){_0x311669[_0x13d2e3(0x16a)][_0x5be36a]['imageElement']=document[_0x13d2e3(0xa4d)](_0x13d2e3(0x95d)),_0x311669[_0x13d2e3(0x16a)][_0x5be36a][_0x13d2e3(0x678)]['width']=0x10,_0x311669[_0x13d2e3(0x16a)][_0x5be36a][_0x13d2e3(0x678)][_0x13d2e3(0x5ec)]=0x9,_0x311669[_0x13d2e3(0x16a)][_0x5be36a][_0x13d2e3(0x678)]['style']['objectFit']='contain',_0x311669[_0x13d2e3(0x16a)][_0x5be36a]['imageElement'][_0x13d2e3(0x2bc)]['UUID']=_0x5be36a;try{_0x311669[_0x13d2e3(0x16a)][_0x5be36a]['imageElement'][_0x13d2e3(0x2bc)]['sid']=_0x311669[_0x13d2e3(0x16a)][_0x5be36a][_0x13d2e3(0x9bb)];}catch(_0x187310){}_0x311669[_0x13d2e3(0x16a)][_0x5be36a][_0x13d2e3(0x678)][_0x13d2e3(0x472)]=![],_0x311669[_0x13d2e3(0x16a)][_0x5be36a][_0x13d2e3(0x678)][_0x13d2e3(0x8bf)](_0x13d2e3(0x4dd),function(_0x2bbf05){var _0xce7059=_0x13d2e3;log(_0xce7059(0x779));try{if(_0x2bbf05[_0xce7059(0x65d)]||_0x2bbf05['metaKey']){_0x2bbf05['preventDefault']();if(_0x311669[_0xce7059(0x26d)]!==![]){var _0x2c282d=_0x2bbf05[_0xce7059(0x6c5)]['dataset'][_0xce7059(0x54a)];if('stats'in _0x311669['rpcs'][_0x2c282d]){var [_0x3a4073,_0x43d111]=statsMenuCreator();printViewStats(_0x43d111,_0x2c282d),_0x3a4073[_0xce7059(0x754)]=setInterval(printViewStats,_0x311669[_0xce7059(0x2ae)],_0x43d111,_0x2c282d);}}return _0x2bbf05[_0xce7059(0x890)](),![];}}catch(_0x37665e){errorlog(_0x37665e);}}),updateMixer();}else _0x311669['rpcs'][_0x5be36a][_0x13d2e3(0x678)]['hidden']&&(_0x311669[_0x13d2e3(0x16a)][_0x5be36a][_0x13d2e3(0x678)][_0x13d2e3(0x472)]=![],_0x311669[_0x13d2e3(0x16a)][_0x5be36a][_0x13d2e3(0x678)][_0x13d2e3(0x886)]['visibility']=_0x13d2e3(0x6ef));_0x311669[_0x13d2e3(0x16a)][_0x5be36a]['imageElement'][_0x13d2e3(0x95a)]=window[_0x13d2e3(0x188)][_0x13d2e3(0x2b3)](new Blob([new Uint8Array(_0x2efe54[_0x13d2e3(0x3bc)])],{'type':_0x13d2e3(0x658)}));return;}try{var _0xfdd301=JSON[_0x13d2e3(0x52d)](_0x2efe54[_0x13d2e3(0x3bc)]);}catch(_0x2fd216){_0xfdd301=_0x2fd216[_0x13d2e3(0x3bc)];}_0xfdd301[_0x13d2e3(0x54a)]=_0x5be36a,_0x13d2e3(0x67b)in _0xfdd301?await _0x311669[_0x13d2e3(0x84d)](_0xfdd301,_0x5be36a+'_screen'):await _0x311669['processRPCSOnMessage'](_0xfdd301,_0x5be36a);},_0x311669[_0x471263(0x84d)]=async function(_0x4576bd,_0x17d4b3){var _0x33269f=_0x471263;if(_0x33269f(0xa83)in _0x4576bd){warnlog(_0x33269f(0x752)),_0x311669['closeRPC'](_0x17d4b3,!![]);return;}else{if(_0x33269f(0x66a)in _0x4576bd){var _0x34f872={};_0x34f872['pong']=_0x4576bd[_0x33269f(0x66a)],_0x311669[_0x33269f(0xa14)](_0x34f872,_0x17d4b3),warnlog(_0x33269f(0x974));return;}else{if(_0x33269f(0x92d)in _0x4576bd){warnlog('PONGED');return;}}}log('incoming\x20message\x20from\x20publisher'),log(_0x4576bd);var _0x12282c=![],_0x155255=![];if(_0x33269f(0x9f5)in _0x4576bd)_0x311669[_0x33269f(0x6d1)](_0x4576bd);else{if(_0x33269f(0x905)in _0x4576bd)_0x4576bd[_0x33269f(0x54a)]=_0x17d4b3,log(_0x33269f(0x8e2)),_0x311669['processIce'](_0x4576bd);else _0x33269f(0x2a8)in _0x4576bd&&(_0x4576bd[_0x33269f(0x54a)]=_0x17d4b3,log(_0x33269f(0x61b)),_0x311669[_0x33269f(0x411)](_0x4576bd));}_0x33269f(0x4fb)in _0x4576bd&&_0x506aab(_0x4576bd[_0x33269f(0x4fb)]);if(_0x33269f(0x485)in _0x4576bd){if(_0x4576bd[_0x33269f(0x485)]===_0x33269f(0x873))_0x311669[_0x33269f(0x772)]=![],!_0x311669['cleanOutput']&&(warnUser(getTranslation('director-denied'),0xbb8),miniTranslate(getById(_0x33269f(0x9d4)),_0x33269f(0x939)));else{if(_0x4576bd[_0x33269f(0x485)]===_0x33269f(0x1a6))!_0x311669[_0x33269f(0x78c)]&&warnUser(getTranslation('only-main-director'),0xbb8);else{if(!_0x311669['cleanOutput']){if(_0x311669['directorUUID']===_0x17d4b3)warnUser(getTranslation('request-failed'),0x1388);else _0x311669[_0x33269f(0x24d)]&&!_0x311669[_0x33269f(0x75b)]?warnUser(getTranslation(_0x33269f(0x3dd)),0x1388):warnUser(getTranslation('token-not-director'),0x1388);}else{if(_0x311669[_0x33269f(0x75b)])!_0x311669[_0x33269f(0x78c)]&&warnUser(_0x33269f(0x55b)+_0x4576bd[_0x33269f(0x485)]+')\x20failed\x20due\x20to\x20permissions\x20or\x20it\x20was\x20rejected\x20by\x20the\x20user',0x1388);else{if(!_0x311669[_0x33269f(0x78c)])_0x311669[_0x33269f(0x24d)]?warnUser(getTranslation(_0x33269f(0x1e2)),0x1388):warnUser(getTranslation(_0x33269f(0x9f9)),0x1388);else{}}}}}errorlog(_0x33269f(0x578)+_0x4576bd[_0x33269f(0x485)]+_0x33269f(0x1ed)+_0x311669[_0x33269f(0x75b)]),pokeIframeAPI('rejected',_0x4576bd[_0x33269f(0x485)],_0x17d4b3);return;}else{if(_0x33269f(0x634)in _0x4576bd){if(_0x4576bd[_0x33269f(0x634)]==='requestCoDirector'){if(_0x311669[_0x33269f(0x75b)]){try{_0x311669[_0x33269f(0x89c)]===![]&&(document['title']=getTranslation(_0x33269f(0x977)));}catch(_0x1d7b58){errorlog(_0x1d7b58);}!_0x311669[_0x33269f(0x78c)]&&!_0x311669[_0x33269f(0x772)]&&(warnUser(getTranslation(_0x33269f(0x58f)),0xbb8),miniTranslate(getById(_0x33269f(0x9d4)),_0x33269f(0x72d)),miniTranslate(getById('yourDirectorStatus'),'this-is-you')),!_0x311669[_0x33269f(0x772)]&&(_0x311669[_0x33269f(0x772)]=!![],pokeAPI(_0x33269f(0x1e0),!![]),_0x311669[_0x33269f(0x464)](_0x17d4b3));}}log('approved:\x20'+_0x4576bd['approved']),pokeIframeAPI(_0x33269f(0x634),_0x4576bd[_0x33269f(0x634)],_0x17d4b3);return;}}if(_0x33269f(0x325)in _0x4576bd)try{_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x325)]=_0x4576bd[_0x33269f(0x325)]||![];if(_0x311669[_0x33269f(0x75b)]){if(_0x311669['rpcs'][_0x17d4b3]['iframeSrc']){var _0x188995=document[_0x33269f(0xa4d)](_0x33269f(0x45d));_0x188995['innerText']=_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x325)],_0x188995['innerText']=_0x188995[_0x33269f(0x82f)],_0x188995=_0x188995[_0x33269f(0x232)]||_0x188995['innerText']||'',getById(_0x33269f(0x496)+_0x17d4b3)['innerHTML']='Shared\x20website:\x20=0x0&&(_0x311669[_0x33269f(0x7a8)]&&lowerhand()));_0x33269f(0x559)in _0x4576bd&&(_0x311669[_0x33269f(0x95b)][_0x33269f(0x49e)](_0x17d4b3)>=0x0&&(_0x311669[_0x33269f(0x559)]=_0x4576bd[_0x33269f(0x559)],pokeIframeAPI(_0x33269f(0x276),_0x311669[_0x33269f(0x559)]),_0x12282c=!![]));if(_0x33269f(0x50b)in _0x4576bd){_0x311669[_0x33269f(0x50b)]=![],_0x311669[_0x33269f(0x4ed)]=![];if(_0x311669[_0x33269f(0x254)]===![]){log(_0x4576bd);if(_0x311669[_0x33269f(0x95b)][_0x33269f(0x49e)](_0x17d4b3)>=0x0){if(_0x4576bd[_0x33269f(0x50b)]!==![]){if(_0x4576bd[_0x33269f(0x50b)]===_0x311669[_0x33269f(0x9bb)])_0x311669[_0x33269f(0x50b)]=!![];else{if(_0x311669[_0x33269f(0x82b)][_0x33269f(0x8e9)]&&!(_0x4576bd[_0x33269f(0x50b)]in _0x311669[_0x33269f(0x82b)]))warnlog(_0x33269f(0x70c)),_0x311669['infocus']=![];else{if(_0x311669['view']&&_0x311669['view']!==_0x4576bd['infocus'])warnlog('NOT\x20VIEW\x20TARGET'),_0x311669[_0x33269f(0x50b)]=![];else{if(_0x311669[_0x33269f(0x98d)]!==![]&&_0x311669[_0x33269f(0x2f3)]&&_0x311669[_0x33269f(0x2f3)]in _0x311669[_0x33269f(0x16a)]&&!_0x311669[_0x33269f(0x16a)][_0x311669[_0x33269f(0x2f3)]][_0x33269f(0x560)]&&_0x4576bd[_0x33269f(0x50b)]===_0x311669[_0x33269f(0x16a)][_0x311669[_0x33269f(0x2f3)]][_0x33269f(0x9bb)])warnlog(_0x33269f(0x68c)),_0x311669[_0x33269f(0x50b)]=![];else{for(var _0x4b58dc in _0x311669[_0x33269f(0x16a)]){if(_0x311669[_0x33269f(0x16a)][_0x4b58dc][_0x33269f(0x9bb)]===_0x4576bd[_0x33269f(0x50b)]){_0x311669['infocus']=_0x4b58dc;break;}}warnlog(_0x33269f(0x96e));}}}}}else _0x311669[_0x33269f(0x50b)]=![];_0x12282c=!![],_0x155255=!![],_0x311669['infocus']?_0x311669[_0x33269f(0x17e)]=!![]:_0x311669[_0x33269f(0x17e)]=![];}}}else{if('infocus2'in _0x4576bd){_0x311669['infocus']=![],_0x311669[_0x33269f(0x4ed)]=![];if(_0x311669[_0x33269f(0x254)]===![]){log(_0x4576bd);if(_0x311669[_0x33269f(0x95b)][_0x33269f(0x49e)](_0x17d4b3)>=0x0){if(_0x4576bd[_0x33269f(0x4ed)]!==![]){if(_0x4576bd[_0x33269f(0x4ed)]===_0x311669[_0x33269f(0x9bb)])_0x311669[_0x33269f(0x4ed)]=!![];else{if(_0x311669[_0x33269f(0x82b)]['length']&&!(_0x4576bd[_0x33269f(0x4ed)]in _0x311669['view_set']))warnlog('NOT\x20IN\x20VIEW\x20SET'),_0x311669[_0x33269f(0x4ed)]=![];else{if(_0x311669[_0x33269f(0x311)]&&_0x311669[_0x33269f(0x311)]!==_0x4576bd[_0x33269f(0x4ed)])warnlog(_0x33269f(0x58a)),_0x311669['infocus2']=![];else{if(_0x311669[_0x33269f(0x98d)]!==![]&&_0x311669['directorUUID']&&_0x311669[_0x33269f(0x2f3)]in _0x311669[_0x33269f(0x16a)]&&!_0x311669['rpcs'][_0x311669[_0x33269f(0x2f3)]][_0x33269f(0x560)]&&_0x4576bd[_0x33269f(0x4ed)]===_0x311669['rpcs'][_0x311669[_0x33269f(0x2f3)]][_0x33269f(0x9bb)])warnlog(_0x33269f(0x68c)),_0x311669[_0x33269f(0x4ed)]=![];else{for(var _0x4b58dc in _0x311669['rpcs']){if(_0x311669[_0x33269f(0x16a)][_0x4b58dc][_0x33269f(0x9bb)]===_0x4576bd[_0x33269f(0x4ed)]){_0x311669[_0x33269f(0x4ed)]=_0x4b58dc;break;}}warnlog(_0x33269f(0x96e));}}}}}else _0x311669['infocus2']=![];_0x311669['infocus2']?_0x311669[_0x33269f(0x17e)]=!![]:_0x311669[_0x33269f(0x17e)]=![],_0x12282c=!![],_0x155255=!![];}}}}_0x33269f(0x893)in _0x4576bd&&(log(_0x4576bd),_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x436)][_0x33269f(0x893)]=_0x4576bd[_0x33269f(0x893)],isIFrame&&parent['postMessage']({'sensors':_0x4576bd['sensors']},_0x311669[_0x33269f(0xa2e)]));_0x33269f(0x1e4)in _0x4576bd&&playbackMIDI(_0x4576bd[_0x33269f(0x1e4)]);_0x33269f(0x2e8)in _0x4576bd&&_0x4576bd['fileList']&&addDownloadLink(_0x4576bd[_0x33269f(0x2e8)],_0x17d4b3,_0x311669[_0x33269f(0x16a)]);_0x33269f(0x5b1)in _0x4576bd&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x1c9)]!==_0x4576bd['rotate_video']&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x1c9)]=_0x4576bd[_0x33269f(0x5b1)],_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x49b)]&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3]['videoElement'][_0x33269f(0x3bf)]=_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x1c9)]),_0x12282c=!![]));if(_0x33269f(0x6ee)in _0x4576bd){warnlog(_0x4576bd),_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x436)]['info']=_0x4576bd[_0x33269f(0x6ee)];_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x16f)]&&(!_0x311669[_0x33269f(0x495)]&&(_0x311669[_0x33269f(0x495)]=_0x4576bd['info']['autoSync'],_0x311669[_0x33269f(0x469)](_0x17d4b3)));if(_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x46b)]){if(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x436)][_0x33269f(0x6ee)][_0x33269f(0x632)])_0x311669['rpcs'][_0x17d4b3]['signalMeter'][_0x33269f(0x2bc)][_0x33269f(0x801)]='1';else _0x33269f(0x632)in _0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x436)]['info']&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x46b)][_0x33269f(0x2bc)][_0x33269f(0x801)]='0');}_0x33269f(0x201)in _0x4576bd[_0x33269f(0x6ee)]&&(_0x4576bd['info'][_0x33269f(0x201)]!==![]?(_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x176)]=_0x4576bd['info']['obs_control'],_0x311669[_0x33269f(0x601)](_0x33269f(0x910),_0x17d4b3)):_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x176)]=![]);if(_0x33269f(0x89c)in _0x4576bd[_0x33269f(0x6ee)])try{typeof _0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x89c)]==_0x33269f(0xa46)?_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x89c)]=sanitizeLabel(_0x4576bd['info']['label']):_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x89c)]=![],applyStyleEffect(_0x17d4b3),_0x311669[_0x33269f(0x75b)]&&setupGuestLabelControl(_0x17d4b3);}catch(_0x4e35b6){errorlog(_0x4e35b6);}if(_0x33269f(0xa63)in _0x4576bd[_0x33269f(0x6ee)])try{_0x311669[_0x33269f(0x16a)][_0x17d4b3]['order']=parseInt(_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0xa63)])||0x0;if(_0x311669['director']){var _0x2f58a5=document[_0x33269f(0x319)](_0x33269f(0x1a7)+_0x17d4b3+'\x22]');_0x2f58a5[0x0]&&(_0x2f58a5[0x0][_0x33269f(0x882)]=_0x311669['rpcs'][_0x17d4b3][_0x33269f(0xa63)]);}}catch(_0x599b9e){errorlog(_0x599b9e);}else _0x311669[_0x33269f(0x16a)][_0x17d4b3]['order']=0x0;if(_0x4576bd['info'][_0x33269f(0x3ae)])try{if(_0x311669['director']&&!_0x311669[_0x33269f(0x64f)]){var _0x2f58a5=document[_0x33269f(0x319)](_0x33269f(0x90d)+_0x17d4b3+'\x22]');_0x2f58a5[0x0]&&_0x2f58a5[0x0][_0x33269f(0x424)][_0x33269f(0x761)](_0x33269f(0x472));}}catch(_0x6cdb11){errorlog(_0x6cdb11);}if(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x7f3)])try{if(_0x33269f(0xa3d)in _0x4576bd[_0x33269f(0x6ee)]){if(_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0xa3d)]!==null){var _0x3e29e8=_0x311669[_0x33269f(0x16a)][_0x17d4b3]['batteryMeter'][_0x33269f(0xa5d)]('.battery-level');if(_0x3e29e8){var _0x31de5c=parseInt(_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x436)][_0x33269f(0x6ee)][_0x33269f(0xa3d)])||0x0;_0x31de5c>0x64&&(_0x31de5c=0x64);_0x31de5c<0x0&&(_0x31de5c=0x0);_0x3e29e8[_0x33269f(0x886)]['height']=parseInt(_0x31de5c)+'%';if(_0x31de5c<0xa)_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x7f3)][_0x33269f(0x424)][_0x33269f(0x761)](_0x33269f(0x1ad)),_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x7f3)][_0x33269f(0x424)][_0x33269f(0x517)](_0x33269f(0x38d));else _0x31de5c<0x19?(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x7f3)][_0x33269f(0x424)]['remove'](_0x33269f(0x38d)),_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x7f3)][_0x33269f(0x424)][_0x33269f(0x517)](_0x33269f(0x1ad))):(_0x311669[_0x33269f(0x16a)][_0x17d4b3]['batteryMeter']['classList'][_0x33269f(0x761)]('alert'),_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x7f3)][_0x33269f(0x424)][_0x33269f(0x761)](_0x33269f(0x1ad)));_0x31de5c<0x64&&_0x311669[_0x33269f(0x16a)][_0x17d4b3]['batteryMeter'][_0x33269f(0x424)]['remove'](_0x33269f(0x472)),_0x311669[_0x33269f(0x16a)][_0x17d4b3]['batteryMeter'][_0x33269f(0x29a)]=_0x31de5c+_0x33269f(0x85b);}}}_0x33269f(0x2eb)in _0x4576bd[_0x33269f(0x6ee)]&&(_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x2eb)]===![]?(_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x7f3)][_0x33269f(0x2bc)]['plugged']='0',_0x311669[_0x33269f(0x16a)][_0x17d4b3]['batteryMeter'][_0x33269f(0x424)]['remove']('hidden')):_0x311669[_0x33269f(0x16a)][_0x17d4b3]['batteryMeter']['dataset'][_0x33269f(0x487)]='1');}catch(_0x51cac6){errorlog(_0x51cac6);}if(_0x33269f(0x868)in _0x4576bd[_0x33269f(0x6ee)])try{_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x868)]?_0x311669['rpcs'][_0x17d4b3]['group']=_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x868)][_0x33269f(0x256)](','):_0x311669['rpcs'][_0x17d4b3]['group']=[],_0x311669[_0x33269f(0x75b)]?(initGroupButtons(_0x17d4b3),_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x532)][_0x33269f(0x8e9)]&&syncGroup(_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x532)],_0x17d4b3)):_0x12282c=!![];}catch(_0x3b043d){errorlog(_0x3b043d);}if(_0x33269f(0x225)in _0x4576bd[_0x33269f(0x6ee)])try{_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x94d)]=_0x4576bd['info'][_0x33269f(0x225)],_0x311669[_0x33269f(0x98d)]===![]&&(_0x311669['roomid']&&((!_0x311669[_0x33269f(0x78c)]||_0x311669[_0x33269f(0x75b)])&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x40b)]?_0x311669['rpcs'][_0x17d4b3]['remoteMuteState']?_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x40b)][_0x33269f(0x424)][_0x33269f(0x761)]('hidden'):_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x40b)][_0x33269f(0x424)][_0x33269f(0x517)](_0x33269f(0x472)):(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x40b)]=getById(_0x33269f(0x6a1))[_0x33269f(0x66c)](!![]),_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x40b)]['id']=_0x33269f(0x466)+_0x17d4b3,_0x311669[_0x33269f(0x16a)][_0x17d4b3]['remoteMuteState']?_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x40b)]['classList'][_0x33269f(0x761)](_0x33269f(0x472)):_0x311669[_0x33269f(0x16a)][_0x17d4b3]['remoteMuteElement'][_0x33269f(0x424)][_0x33269f(0x517)]('hidden'),_0x12282c=!![])))),pokeIframeAPI(_0x33269f(0xa15),_0x311669[_0x33269f(0x16a)][_0x17d4b3]['remoteMuteState'],_0x17d4b3);}catch(_0x52f187){errorlog(_0x52f187);}if(_0x311669[_0x33269f(0x75b)]){try{_0x33269f(0x996)in _0x4576bd[_0x33269f(0x6ee)]&&(_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x996)]==![]&&initRecordingImpossible(_0x17d4b3));}catch(_0x56fa72){errorlog(_0x56fa72);}try{if('recording_audio_gain'in _0x4576bd[_0x33269f(0x6ee)]){if(_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x3a7)]!==![]){let _0x328b8e=parseInt(_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x3a7)])||0x0;initAudioButtons(_0x328b8e,_0x17d4b3);}}}catch(_0xe5d4b7){errorlog(_0xe5d4b7);}try{'directorSpeakerMuted'in _0x4576bd[_0x33269f(0x6ee)]&&(_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x5c9)]&&updateRemoteSpeakerMute(_0x17d4b3));}catch(_0x25c388){errorlog(_0x25c388);}try{_0x33269f(0x285)in _0x4576bd[_0x33269f(0x6ee)]&&(_0x4576bd[_0x33269f(0x6ee)]['directorDisplayMuted']&&updateRemoteDisplayMute(_0x17d4b3));}catch(_0x472405){errorlog(_0x472405);}}if(_0x33269f(0x3be)in _0x4576bd[_0x33269f(0x6ee)])try{_0x311669[_0x33269f(0x75b)]?_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x3be)]&&updateDirectorVideoMute(_0x17d4b3):(_0x311669['rpcs'][_0x17d4b3]['directorVideoMuted']=_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x3be)],_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x3be)]&&(_0x17d4b3 in _0x311669[_0x33269f(0x16a)]&&_0x311669['requestRateLimit'](0x0,_0x17d4b3)));}catch(_0x4119ce){errorlog(_0x4119ce);}if('directorMirror'in _0x4576bd['info'])try{_0x311669[_0x33269f(0x75b)]&&(_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x317)]&&(getById('container_'+_0x17d4b3)['querySelector'](_0x33269f(0x214))&&(getById(_0x33269f(0x2f7)+_0x17d4b3)[_0x33269f(0xa5d)]('[data-action-type=\x22mirror-guest\x22]')[_0x33269f(0x424)][_0x33269f(0x517)](_0x33269f(0x620)),getById('container_'+_0x17d4b3)[_0x33269f(0xa5d)](_0x33269f(0x214))[_0x33269f(0x238)]=_0x33269f(0x8fc)))),_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x350)]=_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x317)],_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x49b)]&&applyMirrorGuest(_0x311669[_0x33269f(0x16a)][_0x17d4b3]['mirrorState'],_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x49b)]);}catch(_0x4e006a){errorlog(_0x4e006a);}if(_0x33269f(0x3ed)in _0x4576bd['info'])try{_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x8ab)]=_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x3ed)],_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x8ab)]&&(_0x311669['director']&&_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x82a)][_0x33269f(0x424)]['remove']('hidden')),pokeIframeAPI('remote-video-mute-state',_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x8ab)],_0x17d4b3);}catch(_0x313480){errorlog(_0x313480);}_0x33269f(0x5b1)in _0x4576bd[_0x33269f(0x6ee)]&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x1c9)]!==_0x4576bd[_0x33269f(0x6ee)]['rotate_video']&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3]['rotate']=_0x4576bd['info'][_0x33269f(0x5b1)],_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x49b)]&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x49b)][_0x33269f(0x3bf)]=_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x1c9)]),_0x12282c=!![])),_0x33269f(0x864)in _0x4576bd[_0x33269f(0x6ee)]&&(_0x4576bd[_0x33269f(0x6ee)]['room_init']===![]&&soloLinkGeneratorInit(_0x17d4b3)),directorCoDirectorColoring(_0x17d4b3),_0x155255=!![],pokeAPI(_0x33269f(0x910),getDetailedState(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x9bb)])),pokeIframeAPI(_0x33269f(0x7cf),_0x4576bd['info'],_0x17d4b3);}_0x33269f(0x48f)in _0x4576bd&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x436)]&&_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x436)][_0x33269f(0x6ee)]&&processMiniInfoUpdate(_0x4576bd[_0x33269f(0x48f)],_0x17d4b3));if(_0x4576bd['directorSettings']){_0x311669['rpcs'][_0x17d4b3]['director']=!![];_0x4576bd['directorSettings'][_0x33269f(0xa73)]&&await checkToken();if(_0x311669['directorUUID']===_0x17d4b3){_0x33269f(0x32f)in _0x4576bd['directorSettings']&&(_0x311669['totalRoomBitrate']=parseInt(_0x4576bd[_0x33269f(0x8ce)][_0x33269f(0x32f)])||0x0,_0x12282c=!![]);if(_0x4576bd['directorSettings'][_0x33269f(0x936)]){if(_0x311669[_0x33269f(0x254)]===![]){if(_0x4576bd['directorSettings'][_0x33269f(0x936)]===_0x311669[_0x33269f(0x9bb)])_0x311669[_0x33269f(0x50b)]=!![];else for(var _0x4b58dc in _0x311669[_0x33269f(0x16a)]){if(_0x311669['rpcs'][_0x4b58dc]['streamID']===_0x4576bd[_0x33269f(0x8ce)][_0x33269f(0x936)]){if((_0x311669['directorList']['includes'](_0x4b58dc)||_0x311669[_0x33269f(0x16a)][_0x4b58dc][_0x33269f(0x75b)])&&!_0x311669[_0x33269f(0x560)])break;_0x311669[_0x33269f(0x50b)]=_0x4b58dc;break;}}_0x12282c=!![],_0x155255=!![];}}if('showDirector'in _0x4576bd[_0x33269f(0x8ce)]){if(_0x311669[_0x33269f(0x98d)]!==![]){if(_0x311669[_0x33269f(0x560)])_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x560)]=_0x311669[_0x33269f(0x560)];else _0x4576bd[_0x33269f(0x8ce)][_0x33269f(0x560)]&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3]['showDirector']=_0x4576bd[_0x33269f(0x8ce)][_0x33269f(0x560)]);}}if(_0x311669['scene']!==![]){if(_0x4576bd['directorSettings'][_0x33269f(0x98d)])for(var _0x4b58dc in _0x4576bd[_0x33269f(0x8ce)]['scene']){setTimeout(function(_0x45d740){var _0xec9b72=_0x33269f;_0x311669[_0xec9b72(0xa37)](_0x45d740);},0x3e8,_0x4576bd[_0x33269f(0x8ce)][_0x33269f(0x98d)][_0x4b58dc]);}if(_0x4576bd[_0x33269f(0x8ce)][_0x33269f(0xa26)])for(var _0x4b58dc in _0x4576bd[_0x33269f(0x8ce)][_0x33269f(0xa26)]){setTimeout(function(_0x5083c8){var _0x457923=_0x33269f;_0x311669[_0x457923(0xa37)](_0x5083c8);},0x3e8,_0x4576bd[_0x33269f(0x8ce)]['mute'][_0x4b58dc]);}}if(_0x33269f(0x1d7)in _0x4576bd[_0x33269f(0x8ce)])for(var _0x133441=0x0;_0x133441<_0x4576bd[_0x33269f(0x8ce)][_0x33269f(0x1d7)][_0x33269f(0x8e9)];_0x133441++){!_0x311669[_0x33269f(0x95b)][_0x33269f(0x2c2)](_0x4576bd[_0x33269f(0x8ce)][_0x33269f(0x1d7)][_0x133441]['toString'])&&(_0x311669[_0x33269f(0x95b)][_0x33269f(0x505)](_0x4576bd[_0x33269f(0x8ce)][_0x33269f(0x1d7)][_0x133441][_0x33269f(0x721)]()),addDirectorBlue(_0x4576bd[_0x33269f(0x8ce)][_0x33269f(0x1d7)][_0x133441][_0x33269f(0x721)]()));}}}if(_0x311669[_0x33269f(0x95b)][_0x33269f(0x49e)](_0x17d4b3)>=0x0){_0x311669[_0x33269f(0x98d)]!==![]&&(_0x33269f(0x95f)in _0x4576bd&&_0x311669[_0x33269f(0xa37)](_0x4576bd));_0x33269f(0x8ce)in _0x4576bd&&_0x4576bd[_0x33269f(0x8ce)][_0x33269f(0x9ba)]&&(!_0x311669[_0x33269f(0x75b)]&&(_0x311669[_0x33269f(0x98d)]===![]&&(_0x311669[_0x33269f(0x285)]=!![],_0x311669[_0x33269f(0x324)]())));if(_0x33269f(0x2d8)in _0x4576bd&&_0x33269f(0x402)in _0x4576bd){if(_0x4576bd[_0x33269f(0x402)]&&_0x4576bd[_0x33269f(0x402)]===!![]){_0x311669[_0x33269f(0xa7f)]=_0x4576bd['mirrorGuestState'],applyMirror(_0x311669['mirrorExclude']);if(_0x311669[_0x33269f(0x75b)]){if(_0x4576bd[_0x33269f(0x6ee)]['directorMirror']){if(getById(_0x33269f(0x2f1))[_0x33269f(0xa5d)]('[data-action-type=\x22mirror-guest\x22]'))getById(_0x33269f(0x2f1))[_0x33269f(0xa5d)](_0x33269f(0x214))['classList'][_0x33269f(0x517)](_0x33269f(0x620)),getById(_0x33269f(0x2f1))[_0x33269f(0xa5d)](_0x33269f(0x214))[_0x33269f(0x238)]='true';else getById(_0x33269f(0x2f1))[_0x33269f(0xa5d)](_0x33269f(0x214))&&(getById(_0x33269f(0x2f1))[_0x33269f(0xa5d)](_0x33269f(0x214))['classList'][_0x33269f(0x761)](_0x33269f(0x620)),getById(_0x33269f(0x2f1))[_0x33269f(0xa5d)](_0x33269f(0x214))['ariaPressed']=_0x33269f(0x309));}}}else{if(_0x4576bd[_0x33269f(0x402)]&&_0x4576bd[_0x33269f(0x402)]in _0x311669[_0x33269f(0x16a)]){_0x311669[_0x33269f(0x16a)][_0x4576bd[_0x33269f(0x402)]][_0x33269f(0x350)]=_0x4576bd[_0x33269f(0x2d8)];_0x311669['rpcs'][_0x4576bd[_0x33269f(0x402)]][_0x33269f(0x49b)]&&applyMirrorGuest(_0x4576bd['mirrorGuestState'],_0x311669[_0x33269f(0x16a)][_0x4576bd[_0x33269f(0x402)]]['videoElement']);if(_0x311669[_0x33269f(0x75b)]){if(_0x4576bd[_0x33269f(0x6ee)][_0x33269f(0x317)])getById(_0x33269f(0x2f7)+_0x17d4b3)[_0x33269f(0xa5d)](_0x33269f(0x214))&&(getById('container_'+_0x17d4b3)[_0x33269f(0xa5d)](_0x33269f(0x214))[_0x33269f(0x424)][_0x33269f(0x517)]('pressed'),getById(_0x33269f(0x2f7)+_0x17d4b3)['querySelector'](_0x33269f(0x214))[_0x33269f(0x238)]='true');else getById(_0x33269f(0x2f7)+_0x17d4b3)[_0x33269f(0xa5d)](_0x33269f(0x214))&&(getById(_0x33269f(0x2f7)+_0x17d4b3)[_0x33269f(0xa5d)](_0x33269f(0x214))[_0x33269f(0x424)][_0x33269f(0x761)]('pressed'),getById('container_'+_0x17d4b3)[_0x33269f(0xa5d)](_0x33269f(0x214))[_0x33269f(0x238)]=_0x33269f(0x309));}}}}if(_0x33269f(0x772)in _0x4576bd){_0x311669[_0x33269f(0x78e)]=_0x4576bd['directorState'],log(_0x4576bd);for(var _0xe307a5 in _0x311669[_0x33269f(0x78e)]){syncSceneState(_0xe307a5),syncOtherState(_0xe307a5);}}if(_0x33269f(0x2f6)in _0x4576bd){_0x311669['widget']=_0x4576bd[_0x33269f(0x2f6)]||![];let _0x43d2be=document['getElementById'](_0x33269f(0x8b0));try{_0x43d2be?!_0x311669[_0x33269f(0x8b0)]?(document['getElementById'](_0x33269f(0x8b0))[_0x33269f(0x761)](),_0x12282c=!![]):_0x43d2be[_0x33269f(0x95a)]=parseURL4Iframe(_0x311669[_0x33269f(0x8b0)]):_0x12282c=!![],_0x311669[_0x33269f(0x75b)]&&(getById(_0x33269f(0x15e))[_0x33269f(0x8d7)]=_0x311669[_0x33269f(0x8b0)]||'');}catch(_0x434f23){errorlog(_0x434f23);}pokeIframeAPI('widget-src',_0x311669['widget'],_0x17d4b3);}if(_0x33269f(0x364)in _0x4576bd){_0x311669[_0x33269f(0x9aa)]=_0x4576bd['slotsUpdate'];if(!_0x311669[_0x33269f(0x6b3)]()){if(_0x311669['layout']){_0x311669[_0x33269f(0x559)]=combinedLayoutSimple(_0x311669[_0x33269f(0x559)]);;updateMixer();}}warnlog(_0x4576bd);}'layouts'in _0x4576bd&&(_0x311669[_0x33269f(0x993)]=_0x4576bd[_0x33269f(0x993)],_0x33269f(0x692)in _0x4576bd?(_0x311669[_0x33269f(0x692)]=_0x4576bd['obsSceneTriggers'],_0x311669[_0x33269f(0x6b3)]()):_0x311669['obsSceneTriggers']=![]);}if(_0x33269f(0xa63)in _0x4576bd){_0x311669[_0x33269f(0x16a)][_0x17d4b3]['order']=parseInt(_0x4576bd['order'])||0x0;_0x17d4b3 in _0x311669[_0x33269f(0x859)]&&(_0x311669[_0x33269f(0x859)][_0x17d4b3]['order']=parseInt(_0x4576bd[_0x33269f(0xa63)])||0x0);if(_0x311669['director']){var _0x2f58a5=document['querySelectorAll'](_0x33269f(0x1a7)+_0x17d4b3+'\x22]');_0x2f58a5[0x0]&&(_0x2f58a5[0x0][_0x33269f(0x882)]=parseInt(_0x4576bd['order'])||0x0);}_0x12282c=!![];}if(_0x33269f(0x7c9)in _0x4576bd){log(_0x33269f(0x21c));if('value'in _0x4576bd){log(_0x33269f(0x7e2));if(typeof _0x4576bd[_0x33269f(0x8d7)]==_0x33269f(0xa46)){_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x89c)]=sanitizeLabel(_0x4576bd[_0x33269f(0x8d7)]);_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x89c)]['length']==0x0&&(_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x89c)]=![]);applyStyleEffect(_0x17d4b3);if(_0x311669[_0x33269f(0x75b)])updateLabelDirectors(_0x17d4b3);else _0x311669[_0x33269f(0x901)]&&(_0x12282c=!![]);}else{_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x89c)]=![],applyStyleEffect(_0x17d4b3);if(_0x311669[_0x33269f(0x75b)])updateLabelDirectors2(_0x17d4b3);else _0x311669[_0x33269f(0x901)]&&(_0x12282c=!![]);}_0x155255=!![],pokeIframeAPI('remote-label-changed',_0x311669['rpcs'][_0x17d4b3]['label'],_0x17d4b3);}}_0x33269f(0x3c2)in _0x4576bd&&(log(_0x4576bd),_0x311669[_0x33269f(0x16a)][_0x17d4b3]['remoteMuteState']=_0x4576bd['muteState'],_0x311669[_0x33269f(0x5d5)](![],_0x17d4b3),_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x436)][_0x33269f(0x6ee)]&&(_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x436)][_0x33269f(0x6ee)]['muted']=_0x311669[_0x33269f(0x16a)][_0x17d4b3]['remoteMuteState']),_0x311669[_0x33269f(0x98d)]===![]&&(_0x311669[_0x33269f(0x4e1)]&&((!_0x311669[_0x33269f(0x78c)]||_0x311669[_0x33269f(0x75b)])&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3]['remoteMuteElement']?_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x94d)]?_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x40b)]['classList'][_0x33269f(0x761)](_0x33269f(0x472)):_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x40b)][_0x33269f(0x424)]['add'](_0x33269f(0x472)):(_0x311669[_0x33269f(0x16a)][_0x17d4b3]['remoteMuteElement']=getById(_0x33269f(0x6a1))[_0x33269f(0x66c)](!![]),_0x311669[_0x33269f(0x16a)][_0x17d4b3]['remoteMuteElement']['id']=_0x33269f(0x466)+_0x17d4b3,_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x94d)]?_0x311669[_0x33269f(0x16a)][_0x17d4b3]['remoteMuteElement'][_0x33269f(0x424)]['remove'](_0x33269f(0x472)):_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x40b)]['classList'][_0x33269f(0x517)](_0x33269f(0x472)),_0x12282c=!![]),_0x155255=!![]))),pokeAPI(_0x33269f(0x725),_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x94d)],_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x9bb)]),pokeIframeAPI(_0x33269f(0xa15),_0x4576bd['muteState'],_0x17d4b3));if(_0x33269f(0x63f)in _0x4576bd){var _0x56bb50=getChromiumVersion();_0x56bb50&&(_0x56bb50<0x50&&(_0x12282c=!![]));}if(_0x33269f(0x8ab)in _0x4576bd){log(_0x33269f(0x2d9)+_0x4576bd[_0x33269f(0x8ab)]),_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x8ab)]=_0x4576bd[_0x33269f(0x8ab)];_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x8ab)]?(!_0x311669[_0x33269f(0x166)]&&_0x311669[_0x33269f(0x5d5)](0x0,_0x17d4b3),_0x311669[_0x33269f(0x16a)][_0x17d4b3]['imageElement']&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3]['imageElement'][_0x33269f(0x472)]=!![],_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x678)]['style'][_0x33269f(0x4eb)]=_0x33269f(0x472))):updateIncomingVideoElement(_0x17d4b3,!![],![]);_0x12282c=!![];_0x311669[_0x33269f(0x75b)]&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x8ab)]?_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x82a)][_0x33269f(0x424)][_0x33269f(0x761)]('hidden'):_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x82a)][_0x33269f(0x424)][_0x33269f(0x517)](_0x33269f(0x472)));if(_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x9cf)]&&_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x8ab)])setTimeout(function(){activeSpeaker();},0x0);else!_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x8ab)]&&setTimeout(function(){activeSpeaker();},0x0);_0x155255=!![],pokeAPI('remoteVideoMuted',_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x8ab)],_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0x9bb)]),pokeIframeAPI(_0x33269f(0x77e),_0x4576bd['videoMuted'],_0x17d4b3);}if('screenStopped'in _0x4576bd){if(_0x17d4b3+_0x33269f(0x33b)in _0x311669[_0x33269f(0x16a)]){_0x311669[_0x33269f(0x16a)][_0x17d4b3+_0x33269f(0x33b)]['virtualHangup']=_0x4576bd[_0x33269f(0x19c)];try{_0x311669['rpcs'][_0x17d4b3+_0x33269f(0x33b)]['virtualHangup']&&(!(SafariVersion&&SafariVersion>0x10)&&(iPad||iOS)&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3+_0x33269f(0x33b)][_0x33269f(0x49b)][_0x33269f(0x932)]=!![]));}catch(_0x5b913b){}_0x311669['director']&&(_0x4576bd[_0x33269f(0x19c)]?getById('container_'+_0x17d4b3+_0x33269f(0x33b))[_0x33269f(0x424)][_0x33269f(0x517)](_0x33269f(0x9f6)):getById(_0x33269f(0x2f7)+_0x17d4b3+_0x33269f(0x33b))[_0x33269f(0x424)][_0x33269f(0x761)](_0x33269f(0x9f6))),_0x12282c=!![],_0x155255=!![];}}_0x33269f(0x701)in _0x4576bd&&(_0x311669['rpcs'][_0x17d4b3][_0x33269f(0x701)]=_0x4576bd['screenShareState'],_0x12282c=!![],pokeIframeAPI(_0x33269f(0x7d5),_0x4576bd[_0x33269f(0x701)],_0x17d4b3));if(_0x33269f(0x912)in _0x4576bd){if(!_0x311669['director']){if(_0x33269f(0x81f)in _0x4576bd){if(_0x311669[_0x33269f(0x95b)][_0x33269f(0x49e)](_0x17d4b3)>=0x0){var _0x35a28d=_0x4576bd[_0x33269f(0x81f)];if(_0x35a28d===!![])_0x311669[_0x33269f(0x3be)]=_0x4576bd[_0x33269f(0x912)];else _0x35a28d in _0x311669[_0x33269f(0x16a)]&&(_0x311669['rpcs'][_0x35a28d]['directorVideoMuted']=_0x4576bd[_0x33269f(0x912)],_0x311669[_0x33269f(0x16a)][_0x35a28d][_0x33269f(0x3be)]&&_0x311669[_0x33269f(0x5d5)](0x0,_0x35a28d),_0x12282c=!![]);}}}_0x155255=!![];}_0x33269f(0xa39)in _0x4576bd&&(!_0x311669[_0x33269f(0x75b)]&&(_0x311669[_0x33269f(0x95b)][_0x33269f(0x49e)](_0x17d4b3)>=0x0&&(_0x17d4b3 in _0x311669[_0x33269f(0x16a)]&&(_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0xa39)]=_0x4576bd[_0x33269f(0xa39)],_0x311669[_0x33269f(0x16a)][_0x17d4b3][_0x33269f(0xa39)]&&(_0x17d4b3 in _0x311669[_0x33269f(0x16a)]&&_0x311669['requestRateLimit'](0x0,_0x17d4b3)),_0x12282c=!![]))),_0x155255=!![]);if('requestFile'in _0x4576bd){log(_0x33269f(0xa6d));try{_0x311669[_0x33269f(0x8ca)](_0x17d4b3,_0x4576bd[_0x33269f(0x91a)]);}catch(_0x2beb44){errorlog(_0x2beb44);}}_0x33269f(0x6db)in _0x4576bd&&remoteStats(_0x4576bd,_0x17d4b3);if(_0x12282c)setTimeout(function(){updateMixer(),updateUserList();},0x1);else _0x155255&&updateUserList();},_0x311669[_0x471263(0x16a)][_0x5be36a][_0x471263(0x4a7)][_0x471263(0x661)]=()=>{var _0x495631=_0x471263;warnlog(_0x495631(0x4b9));};},_0x311669[_0x3106c2(0x16a)][_0x5be36a][_0x3106c2(0x196)]=_0x17b813=>{var _0xce7d81=_0x3106c2;warnlog(_0xce7d81(0x1d8)),_0x311669[_0xce7d81(0x938)](_0x17b813,_0x5be36a);},log(_0x3106c2(0x41d));},_0x311669[_0x134a17(0x86b)]=function(_0x1250fc,_0x18d3f1){var _0x40d2a0=_0x134a17;log('session.setupScreenShareAddon'),!_0x311669[_0x40d2a0(0x16a)][_0x18d3f1][_0x40d2a0(0x549)]?(_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen']={},_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x65b)]=_0x18d3f1,_0x311669[_0x40d2a0(0x16a)][_0x18d3f1][_0x40d2a0(0x549)]=createVideoElement(),_0x311669[_0x40d2a0(0x16a)][_0x18d3f1][_0x40d2a0(0x549)]['needsLoading']=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1][_0x40d2a0(0x549)][_0x40d2a0(0x8bf)](_0x40d2a0(0x664),_0x437be8=>{var _0x1805b0=_0x40d2a0;log(_0x1805b0(0x18f)),_0x437be8[_0x1805b0(0x81f)][_0x1805b0(0x932)]=![];}),_0x311669[_0x40d2a0(0x16a)][_0x18d3f1][_0x40d2a0(0x549)][_0x40d2a0(0x5c4)]=createMediaStream(),_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x49b)]=_0x311669['rpcs'][_0x18d3f1][_0x40d2a0(0x549)],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)]['streamSrc']=createMediaStream(),_0x311669['rpcs'][_0x18d3f1][_0x40d2a0(0x9bb)]&&(_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)]['streamID']=_0x311669['rpcs'][_0x18d3f1][_0x40d2a0(0x9bb)]+':s'),_0x311669['rpcs'][_0x18d3f1+'_screen'][_0x40d2a0(0x436)]={},_0x311669[_0x40d2a0(0x16a)][_0x18d3f1][_0x40d2a0(0x436)][_0x40d2a0(0x375)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen']['getStats']=function(){return new Promise((_0x204b06,_0x4a183a)=>{_0x204b06([]);});},_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x9db)]=![],_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x87f)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x665)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)]['activelySpeaking']=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x84b)]=![],_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x79d)]=null,_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen'][_0x40d2a0(0x45c)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen'][_0x40d2a0(0xa33)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen'][_0x40d2a0(0x9cc)]=-0x1,_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x24a)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen'][_0x40d2a0(0x560)]=![],_0x311669['rpcs'][_0x18d3f1+'_screen']['channelOffset']=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x592)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x53c)]=-0x1,_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x3e4)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x678)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x9eb)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x532)]=_0x311669[_0x40d2a0(0x16a)][_0x18d3f1][_0x40d2a0(0x532)]||[],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x8ab)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x223)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x3be)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0xa39)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen']['remoteMuteState']=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen'][_0x40d2a0(0x40b)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x7b9)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen'][_0x40d2a0(0x621)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x9a4)]=null,_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen']['mutedState']=null,_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x896)]=null,_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x3e1)]=null,_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x350)]=null,_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)]['scaleHeight']=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)]['scaleWidth']=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x482)]=![],_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x97a)]=![],_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x46b)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)]['volumeControl']=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x2ef)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x701)]=!![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen'][_0x40d2a0(0x6f2)]=0x64,_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x426)]=0x0,_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)]['nackCount']=0x0,_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen'][_0x40d2a0(0x15c)]='1',_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x644)]='1',_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen']['obsControl']=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x353)]=0x0,_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x89c)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0xa63)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x237)]=null,_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x4e0)]=null,_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x6dc)]={},_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x325)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x9b0)]=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x586)]=Date[_0x40d2a0(0x610)](),_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)]['settings']=![],_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)]['savedVolume']=![],(_0x311669[_0x40d2a0(0x2d3)]==0x2||_0x311669['activeSpeaker']==0x4)&&(_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x84b)]=!![]),_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)]['videoElement'][_0x40d2a0(0x2bc)]['UUID']=_0x18d3f1+'_screen',_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen'][_0x40d2a0(0x49b)]['id']=_0x40d2a0(0x9c6)+_0x18d3f1+_0x40d2a0(0x33b),_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x9bb)]&&(_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x49b)]['dataset'][_0x40d2a0(0x34b)]=_0x311669['rpcs'][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x9bb)]),_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x49b)]['screenshare']=![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)]['voiceMeter']=![],setupIncomingScreenTracking(_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x49b)],_0x18d3f1+'_screen'),_0x1250fc[_0x40d2a0(0x982)](function(_0x3ff137){var _0x913462=_0x40d2a0;_0x311669['rpcs'][_0x18d3f1][_0x913462(0x549)][_0x913462(0x5c4)][_0x913462(0x58b)](_0x3ff137),_0x311669['rpcs'][_0x18d3f1+_0x913462(0x33b)][_0x913462(0x585)][_0x913462(0x58b)](_0x3ff137);}),_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x49b)][_0x40d2a0(0x740)]=!![],_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+'_screen'][_0x40d2a0(0x49b)][_0x40d2a0(0x4b0)](_0x40d2a0(0x35a),''),mediaSourceUpdated(_0x18d3f1+_0x40d2a0(0x33b),_0x311669[_0x40d2a0(0x16a)][_0x18d3f1+_0x40d2a0(0x33b)][_0x40d2a0(0x9bb)])):_0x1250fc[_0x40d2a0(0x982)](function(_0x2e25ff){var _0x1ea153=_0x40d2a0,_0x1ce77c=![];_0x311669[_0x1ea153(0x16a)][_0x18d3f1][_0x1ea153(0x549)][_0x1ea153(0x5c4)]['getTracks']()[_0x1ea153(0x982)](function(_0x332d8b){var _0x3f7dae=_0x1ea153;_0x332d8b['id']==_0x2e25ff['id']&&_0x332d8b[_0x3f7dae(0x7ad)]==_0x2e25ff[_0x3f7dae(0x7ad)]&&(_0x1ce77c=!![]);});!_0x1ce77c&&_0x311669[_0x1ea153(0x16a)][_0x18d3f1][_0x1ea153(0x549)][_0x1ea153(0x5c4)][_0x1ea153(0x58b)](_0x2e25ff);var _0x1ce77c=![];_0x311669['rpcs'][_0x18d3f1+_0x1ea153(0x33b)][_0x1ea153(0x585)][_0x1ea153(0x951)]()[_0x1ea153(0x982)](function(_0x47756b){var _0x40d456=_0x1ea153;_0x47756b['id']==_0x2e25ff['id']&&_0x47756b[_0x40d456(0x7ad)]==_0x2e25ff[_0x40d456(0x7ad)]&&(_0x1ce77c=!![]);}),!_0x1ce77c&&_0x311669[_0x1ea153(0x16a)][_0x18d3f1+_0x1ea153(0x33b)][_0x1ea153(0x585)]['addTrack'](_0x2e25ff);});},_0x311669;}());function getMeshcastCanvasTrack(_0x49759a=session[_0x2de9a7(0x638)]){var _0x171370=_0x2de9a7;!_0x49759a&&errorlog(_0x171370(0xa40));!_0x49759a['canvas']&&(_0x49759a[_0x171370(0x4e0)]=document[_0x171370(0xa4d)](_0x171370(0x4e0)),_0x49759a[_0x171370(0x4e0)][_0x171370(0x530)]=0x140,_0x49759a[_0x171370(0x4e0)][_0x171370(0x5ec)]=0xb4);!_0x49759a[_0x171370(0xa01)]&&(_0x49759a['ctx']=_0x49759a[_0x171370(0x4e0)][_0x171370(0x179)]('2d',{'alpha':![]}),_0x49759a['ctx'][_0x171370(0x6a5)]=_0x171370(0x8a2),_0x49759a['ctx'][_0x171370(0xa0c)](0x0,0x0,_0x49759a[_0x171370(0x4e0)][_0x171370(0x530)],_0x49759a['canvas'][_0x171370(0x5ec)]));!_0x49759a[_0x171370(0x6ad)]&&(function _0x163d53(){var _0x2dec03=_0x171370;_0x49759a[_0x2dec03(0xa01)][_0x2dec03(0xa0c)](0x0,0x0,_0x49759a[_0x2dec03(0x4e0)][_0x2dec03(0x530)],_0x49759a['canvas'][_0x2dec03(0x5ec)]),setTimeout(_0x163d53,0xfa);}(),_0x49759a[_0x171370(0x6ad)]=_0x49759a[_0x171370(0x4e0)][_0x171370(0x961)](0x4));var _0x15bb03=_0x49759a['canvasStream'][_0x171370(0x87a)]();if(_0x15bb03['length'])return _0x15bb03[0x0];return errorlog(_0x171370(0xa81)),![];}var meshcastServer=![];function _0x5c68(_0x4305d2,_0x59ee0d){var _0x2d5e64=_0x2d5e();return _0x5c68=function(_0x5c68d8,_0xa4daae){_0x5c68d8=_0x5c68d8-0x15c;var _0x23a63d=_0x2d5e64[_0x5c68d8];return _0x23a63d;},_0x5c68(_0x4305d2,_0x59ee0d);}function selectMeshcast(_0xcdfe2d){var _0x4b0f65=_0x2de9a7;meshcastServer={};var _0x3dbd90=_0xcdfe2d[_0x4b0f65(0x37f)],_0x27ee76=_0xcdfe2d[_0x4b0f65(0x7d3)];meshcastServer[_0x4b0f65(0x4e6)]=_0x27ee76[_0x3dbd90][_0x4b0f65(0x4e6)],meshcastServer[_0x4b0f65(0x7d2)]=_0x27ee76[_0x3dbd90]['code'];}async function meshcast(_0x5c9d1e=![]){var _0x1f8631=_0x2de9a7;async function _0x25b98b(_0x2c9705,_0x547584){var _0x3dbecb=_0x5c68;const _0x27e277=new XMLHttpRequest();_0x27e277[_0x3dbecb(0x19f)]=function(){var _0x42ebbd=_0x3dbecb;if(parseFloat(this[_0x42ebbd(0x80f)])>=0x0){if(parseFloat(this[_0x42ebbd(0x80f)])>0x32)_0x2c9705[_0x42ebbd(0x82f)]+='\x20(full)';else{if(parseFloat(this[_0x42ebbd(0x80f)])>0x19)_0x2c9705[_0x42ebbd(0x82f)]+='\x20(fair)';else{if(parseFloat(this[_0x42ebbd(0x80f)])>0xa)_0x2c9705[_0x42ebbd(0x82f)]+='\x20(ok)';else{if(parseFloat(this[_0x42ebbd(0x80f)])>0x0)_0x2c9705['innerHTML']+='\x20(good)';else{var _0x2c5083=![];_0x2c9705[_0x42ebbd(0xa16)]&&(_0x2c5083=!![]),_0x2c9705[_0x42ebbd(0x7a2)]=!![],_0x2c9705[_0x42ebbd(0x82f)]+=_0x42ebbd(0x61c),document[_0x42ebbd(0x5c8)](_0x42ebbd(0x3a1))[_0x42ebbd(0x40a)](_0x2c9705),_0x2c5083&&(document[_0x42ebbd(0x5c8)](_0x42ebbd(0x3a1))[_0x42ebbd(0x7d3)][0x0][_0x42ebbd(0xa16)]=!![]);}}}}}else{var _0x2c5083=![];_0x2c9705[_0x42ebbd(0xa16)]&&(_0x2c5083=!![]),document[_0x42ebbd(0x5c8)](_0x42ebbd(0x3a1))[_0x42ebbd(0x40a)](_0x2c9705),_0x2c9705[_0x42ebbd(0x82f)]+=_0x42ebbd(0x61c),_0x2c9705[_0x42ebbd(0x7a2)]=!![],_0x2c5083&&(document[_0x42ebbd(0x5c8)](_0x42ebbd(0x3a1))['options'][0x0][_0x42ebbd(0xa16)]=!![]);}session[_0x42ebbd(0x75b)]&&!session[_0x42ebbd(0x78c)]&&!session[_0x42ebbd(0x89e)]&&document[_0x42ebbd(0x5c8)]('meshcastMenu')[_0x42ebbd(0x424)][_0x42ebbd(0x761)](_0x42ebbd(0x472));},_0x27e277['onerror']=function(){var _0x4a5589=_0x3dbecb,_0x3700c7=![];_0x2c9705['selected']&&(_0x3700c7=!![]),document[_0x4a5589(0x5c8)](_0x4a5589(0x3a1))[_0x4a5589(0x40a)](_0x2c9705),_0x2c9705[_0x4a5589(0x82f)]+='\x20(fail)',_0x2c9705[_0x4a5589(0x7a2)]=!![],_0x3700c7&&(document[_0x4a5589(0x5c8)](_0x4a5589(0x3a1))['options'][0x0][_0x4a5589(0xa16)]=!![]);},_0x27e277[_0x3dbecb(0x6d4)]('GET',_0x547584,!![]),_0x27e277[_0x3dbecb(0x94c)]=0x3e8,_0x27e277[_0x3dbecb(0x48e)]=function(_0x49f646){var _0x543a52=_0x3dbecb,_0x4c8303=![];_0x2c9705['selected']&&(_0x4c8303=!![]),document[_0x543a52(0x5c8)]('edgelist')[_0x543a52(0x40a)](_0x2c9705),_0x2c9705[_0x543a52(0x82f)]+='\x20(timeout)',_0x4c8303&&(document[_0x543a52(0x5c8)]('edgelist')[_0x543a52(0x7d3)][0x0][_0x543a52(0xa16)]=!![]);},_0x27e277[_0x3dbecb(0x2b9)]();}async function _0x3e64f2(_0x3b3458=![]){var _0x36a6be=_0x5c68,_0x154b40=new Date(),_0xe948d4=_0x154b40[_0x36a6be(0x3c6)]();urlParams['has']('tz')&&(_0xe948d4=parseInt(urlParams[_0x36a6be(0x9a7)]('tz'))||_0xe948d4),fetch('https://meshcast.io/servers.json?ts='+Date[_0x36a6be(0x610)]())[_0x36a6be(0x619)](_0x151541=>_0x151541[_0x36a6be(0x6b6)]())[_0x36a6be(0x619)](async _0x2146ff=>{var _0x3beb7f=_0x36a6be;for(var _0x37628f=0x0;_0x37628f<_0x2146ff[_0x3beb7f(0x8e9)];_0x37628f++){var _0x353d94=Math['abs'](_0x2146ff[_0x37628f]['tz']-_0xe948d4);Math[_0x3beb7f(0x86a)](_0x353d94-0x3c*0x18)<_0x353d94&&(_0x353d94=Math[_0x3beb7f(0x86a)](_0x353d94-0x3c*0x18));_0x2146ff[_0x37628f][_0x3beb7f(0x44c)]=_0x353d94;if(session['meshcastCode']&&session['meshcastCode']!==_0x2146ff[_0x37628f][_0x3beb7f(0x7d2)])_0x2146ff[_0x37628f][_0x3beb7f(0x44c)]+=0x3e8;else!session[_0x3beb7f(0x4f5)]&&session[_0x3beb7f(0x98c)]!==_0x2146ff[_0x37628f][_0x3beb7f(0x7d2)]&&(_0x2146ff[_0x37628f][_0x3beb7f(0x44c)]+=0x3e8);}_0x2146ff[_0x3beb7f(0xa42)](compare_deltas),console[_0x3beb7f(0x828)](_0x2146ff);for(var _0x37628f=0x0;_0x37628f<_0x2146ff[_0x3beb7f(0x8e9)];_0x37628f++){var _0x5ccd6a=document['createElement'](_0x3beb7f(0x522));_0x5ccd6a[_0x3beb7f(0x7d2)]=_0x2146ff[_0x37628f][_0x3beb7f(0x7d2)],_0x5ccd6a[_0x3beb7f(0x4e6)]=_0x2146ff[_0x37628f][_0x3beb7f(0x4e6)],_0x5ccd6a[_0x3beb7f(0x82f)]=_0x2146ff[_0x37628f][_0x3beb7f(0x89c)],_0x25b98b(_0x5ccd6a,_0x2146ff[_0x37628f][_0x3beb7f(0x4e6)]+_0x3beb7f(0x428)),document[_0x3beb7f(0x5c8)]('edgelist')[_0x3beb7f(0x40a)](_0x5ccd6a);}meshcastServer=_0x2146ff[0x0],_0x3b3458&&_0x3b3458();});}if(!session['meshcast'])return;if(_0x5c9d1e){_0x3e64f2();return;}if(session['whipoutSettings']!==![])return;else{if(session[_0x1f8631(0x58c)]){}else{if(!session[_0x1f8631(0x49b)][_0x1f8631(0x5c4)]||!session[_0x1f8631(0x49b)][_0x1f8631(0x5c4)][_0x1f8631(0x951)]()['length'])return;}}session[_0x1f8631(0x948)]=null,warnlog(_0x1f8631(0x465));function _0x34a3a6(_0x37488f){var _0x420777=_0x1f8631;warnlog(_0x420777(0x9cd)),warnlog(_0x37488f);try{session[_0x420777(0x638)][_0x420777(0x903)]()['then'](function(_0x27bfb4){var _0x1dcd9f=_0x420777;try{_0x27bfb4=configureWhipOutSDP(_0x27bfb4);}catch(_0x2068ed){errorlog(_0x2068ed);}return session[_0x1dcd9f(0x638)][_0x1dcd9f(0xa1b)](_0x27bfb4);})[_0x420777(0x619)](function(){var _0xc8cb13=_0x420777;log(session[_0xc8cb13(0x638)][_0xc8cb13(0x1f3)]);var _0x25e4da=session['whipOut'][_0xc8cb13(0x1f3)][_0xc8cb13(0x648)];(iOS||iPad)&&(session[_0xc8cb13(0x971)]&&_0x25e4da[_0xc8cb13(0x2c2)](_0xc8cb13(0x168))&&(_0x25e4da=_0x25e4da[_0xc8cb13(0x2f9)](_0xc8cb13(0x168),''))),_0xa06b85(_0x25e4da,'sdp');})[_0x420777(0x3b6)](function(_0x2b5837){});}catch(_0x24adfa){errorlog(_0x24adfa);}}try{var _0x32d6bd=[],_0x426fa1=session[_0x1f8631(0xa76)](0xe);async function _0x2524e4(){var _0x402d9f=_0x1f8631;document[_0x402d9f(0x5c8)](_0x402d9f(0x3a1))[_0x402d9f(0x7a2)]=!![],document[_0x402d9f(0x5c8)](_0x402d9f(0x3a1))[_0x402d9f(0x29a)]=_0x402d9f(0x2d7);!session[_0x402d9f(0x7db)]&&await chooseBestTURN();try{session[_0x402d9f(0x638)]=new RTCPeerConnection(session['configuration']),session[_0x402d9f(0x638)][_0x402d9f(0x436)]={},session['whipOut'][_0x402d9f(0x9b5)]=null,session['whipOut'][_0x402d9f(0x6b5)]=![],session[_0x402d9f(0x638)][_0x402d9f(0x6f1)]=![],session[_0x402d9f(0x638)]['offerToReceiveVideo']=![];}catch(_0x16db3c){!session['cleanOutput']&&warnUser(_0x402d9f(0xa48));}try{if(session[_0x402d9f(0x98c)]!==_0x402d9f(0x30c)){var _0x23531a=![];session[_0x402d9f(0x49b)]&&session[_0x402d9f(0x49b)][_0x402d9f(0x5c4)]&&(_0x23531a=session[_0x402d9f(0x49b)][_0x402d9f(0x5c4)][_0x402d9f(0xa6c)]());if(!_0x23531a||!_0x23531a['length']){var _0x561554=new AudioContext(),_0x37f371=_0x561554[_0x402d9f(0x6c7)]();_0x37f371[_0x402d9f(0x75f)][_0x402d9f(0xa6c)]()[_0x402d9f(0x982)](_0x2f7fe8=>{_0x23531a=_0x2f7fe8;});}else _0x23531a=_0x23531a[0x0];if(session[_0x402d9f(0xa82)]&&_0x23531a[_0x402d9f(0x7ad)]==='audio')try{_0x23531a[_0x402d9f(0x34f)]=session[_0x402d9f(0xa82)];}catch(_0x16de11){errorlog(_0x16de11);}if(_0x23531a)try{session[_0x402d9f(0x638)][_0x402d9f(0x5d2)](_0x23531a,{'streams':[session[_0x402d9f(0x49b)][_0x402d9f(0x5c4)]],'direction':_0x402d9f(0x3a6)});}catch(_0x15cf5f){errorlog(_0x15cf5f),session['whipOut']['addTrack'](_0x23531a);}}if(session[_0x402d9f(0x98c)]!==_0x402d9f(0x433)){var _0x23531a=![];session[_0x402d9f(0x49b)]&&session[_0x402d9f(0x49b)][_0x402d9f(0x5c4)]&&(_0x23531a=session[_0x402d9f(0x49b)][_0x402d9f(0x5c4)][_0x402d9f(0x87a)]());!_0x23531a||!_0x23531a[_0x402d9f(0x8e9)]?_0x23531a=getMeshcastCanvasTrack(session[_0x402d9f(0x638)]):_0x23531a=_0x23531a[0x0];if(session['screenShareState']&&session[_0x402d9f(0x45e)]&&_0x23531a[_0x402d9f(0x7ad)]===_0x402d9f(0x30c))try{_0x23531a['contentHint']=session[_0x402d9f(0x45e)];}catch(_0x355f5b){errorlog(_0x355f5b);}else{if(session[_0x402d9f(0x34f)]&&_0x23531a['kind']==='video')try{_0x23531a[_0x402d9f(0x34f)]=session[_0x402d9f(0x34f)];}catch(_0x512cbe){errorlog(_0x512cbe);}}if(_0x23531a)try{session[_0x402d9f(0x638)][_0x402d9f(0x5d2)](_0x23531a,{'streams':[session[_0x402d9f(0x49b)]['srcObject']],'direction':_0x402d9f(0x3a6)});}catch(_0x2cb1ad){errorlog(_0x2cb1ad),session[_0x402d9f(0x638)][_0x402d9f(0x58b)](_0x23531a);}}session[_0x402d9f(0x638)][_0x402d9f(0x23b)]=_0x34a3a6,session[_0x402d9f(0x638)]['onicecandidate']=function(_0x27d61c){var _0x4e3cd8=_0x402d9f;if(_0x27d61c[_0x4e3cd8(0x905)]==null)return;log(_0x27d61c[_0x4e3cd8(0x905)]),_0x32d6bd[_0x4e3cd8(0x505)](_0x27d61c[_0x4e3cd8(0x905)]);};}catch(_0x45497a){errorlog(_0x45497a);}}!meshcastServer?_0x3e64f2(_0x2524e4):_0x2524e4();}catch(_0x5c8ff0){errorlog(_0x5c8ff0);}function _0xa06b85(_0x52c030,_0x267462,_0xdb811e=![]){var _0x51bb31=_0x1f8631;try{var _0x24e43a=new XMLHttpRequest();_0x24e43a[_0x51bb31(0x7a6)]=function(){var _0x3d5485=_0x51bb31;if(this[_0x3d5485(0x8ee)]==0x4&&(this[_0x3d5485(0xa13)]==0xc8||this['status']==0xc9)){var _0x379ee3=this[_0x3d5485(0x5f0)](_0x3d5485(0x937));if(_0x379ee3==_0x3d5485(0x916)){var _0x5c604b={};_0x5c604b[_0x3d5485(0x648)]=this['responseText'],_0x5c604b['type']=_0x3d5485(0x73b);try{_0x5c604b=configureWhipOutSDP(_0x5c604b);}catch(_0x5129e0){errorlog(_0x5129e0);}session[_0x3d5485(0x638)][_0x3d5485(0x5fb)](_0x5c604b)[_0x3d5485(0x619)](function(){var _0x1f45be=_0x3d5485;if(_0x32d6bd[_0x1f45be(0x8e9)]){var _0x2d7ab7=JSON[_0x1f45be(0x479)](_0x32d6bd[_0x1f45be(0x513)]());_0xa06b85(_0x2d7ab7,'ice',function(){var _0x301186=_0x1f45be;session[_0x301186(0xa35)](),_0x4e1c34();});}})[_0x3d5485(0x3b6)](function(_0x15329c){log(_0x15329c);});}else{if(_0x379ee3==_0x3d5485(0x224))this[_0x3d5485(0x80f)]==0x1b0?warnUser(_0x3d5485(0x76f)):warnUser('Unknown\x20Meshcast\x20error');else _0xdb811e&&_0xdb811e();}}};var _0x351c05=0x9c4;session[_0x51bb31(0x2a4)]!==![]&&(_0x351c05=session['whipOutVideoBitrate']);session['screenShareState']&&session['whipOutScreenShareBitrate']!==![]&&(_0x351c05=session[_0x51bb31(0x477)]);var _0x5a1ad8=parseInt(0x61a8/_0x351c05)||0xa,_0x2f2763='';if(session[_0x51bb31(0x701)]&&session['whipOutScreenShareCodec'])_0x2f2763=session['whipOutScreenShareCodec'];else{if(session[_0x51bb31(0x491)])_0x2f2763=session[_0x51bb31(0x491)];else(iOS||iPad)&&(_0x2f2763=_0x51bb31(0x7f9));}_0x24e43a[_0x51bb31(0x6d4)](_0x51bb31(0x742),meshcastServer[_0x51bb31(0x4e6)]+'/'+_0x5a1ad8+'/'+_0x2f2763,!![]),_0x24e43a[_0x51bb31(0x746)](_0x51bb31(0x40f),_0x51bb31(0x9b3)+_0x267462+_0x51bb31(0x34d)),_0x24e43a[_0x51bb31(0x746)](_0x51bb31(0x80a),_0x51bb31(0x1f0)+_0x426fa1),_0x24e43a[_0x51bb31(0x88c)]=function(_0x4a11f6){var _0x267626=_0x51bb31;errorlog(_0x4a11f6),warnUser(_0x267626(0x18d)),window['location'][_0x267626(0x456)]!=='vdo.ninja'?console[_0x267626(0x1ad)](_0x267626(0x7bf)):console[_0x267626(0x1ad)](_0x267626(0x576));},_0x24e43a['send'](_0x52c030);}catch(_0x44816a){errorlog(_0x44816a);}}async function _0x4e1c34(){var _0x48c08e=_0x1f8631;if(meshcastServer[_0x48c08e(0x7d2)])var _0x514487=_0x48c08e(0xa69)+meshcastServer[_0x48c08e(0x7d2)]+'&id='+_0x426fa1;else var _0x514487='https://meshcast.io/view.html?id='+_0x426fa1;console[_0x48c08e(0x828)](_0x48c08e(0x1ee)+_0x514487);!session['whipOut'][_0x48c08e(0x436)]&&(session[_0x48c08e(0x638)][_0x48c08e(0x436)]={});session[_0x48c08e(0x638)][_0x48c08e(0x436)][_0x48c08e(0x281)]=meshcastServer[_0x48c08e(0x7d2)],session[_0x48c08e(0x638)][_0x48c08e(0x436)][_0x48c08e(0x3d5)]=_0x514487,session[_0x48c08e(0x638)]['stats'][_0x48c08e(0x84f)]=_0x48c08e(0x1bc),session[_0x48c08e(0x638)]['stats'][_0x48c08e(0x50c)]=![],await sleep(0x1f4),session[_0x48c08e(0x948)]={'type':'meshcast','token':_0x426fa1,'url':meshcastServer[_0x48c08e(0x4e6)]};for(var _0xebde50 in session['pcs']){if(session['pcs'][_0xebde50][_0x48c08e(0x45a)]===null){var _0x226a1e={};_0x226a1e[_0x48c08e(0x2d2)]=session[_0x48c08e(0x948)],_0x226a1e[_0x48c08e(0x98c)]=session[_0x48c08e(0x948)],session[_0x48c08e(0x8a1)](_0x226a1e,_0xebde50)&&(session[_0x48c08e(0x859)][_0xebde50][_0x48c08e(0x45a)]=!![]);}}}}async function whepWatch(_0xef4573,_0xf7b6c){var _0x5e7218=_0x2de9a7;if(session[_0x5e7218(0x8b4)])return;log(_0xf7b6c);if(_0xf7b6c[_0x5e7218(0x78a)]=='meshcast')meshcastWatch(_0xef4573,_0xf7b6c[_0x5e7218(0x98c)]);else _0xf7b6c[_0x5e7218(0x78a)]=='whep'&&(_0xf7b6c&&_0xf7b6c[_0x5e7218(0x4e6)]&&whepIn(_0xf7b6c[_0x5e7218(0x4e6)],![],_0xef4573));}function _0x2d5e(){var _0x3d0c02=['showConnections','volumeControl','We\x20will\x20not\x20request\x20the\x20meshcast\x20as\x20no\x20audio\x20or\x20video\x20is\x20requested','ON\x20FOCUS\x20NOT\x20FOUND','maxpublishers','alpha','removeOrientationFlag','hostedFiles','cry','PINGED','directMigrateIssue','vdav','control-room-co-director','available_outgoing_bitrate_kbps','Not\x20director','slot','allowChunked','viewwidth','measureUnsignedInt','select','term','rain','mean','forEach','queueList','bandwidth\x20set\x20g!\x20','queue=false','station','gainNode','stun:stun.cloudflare.com:3478','audioMutedOverride','directorViewBitrate','broad','meshcast','scene','You\x27ve\x20been\x20transferred','step','resolution','design','binaryType','layouts','Someone\x20Joined\x20the\x20Room','base','recording_audio_pipeline','score','enough','isDirector\x20','major','teach','Someone','scale\x20scale','search','beforeunload','CONNECTEED!','aspectRatio','test','center','closeTimeout','thank','replaceAll','get','mother','anger','currentSlots','createMediaStreamSource','BROWER\x20DID\x20NOT\x20SUPPORT\x20LIMIT\x20BITRATE','down','publishing\x20SDP\x20Offer:\x20','stone','iframeEle','this','createDelay','application/','each','maxBandwidth','restartIce','born','allowchunked','noScaling','blindAllGuests','streamID','even','rope','turn:turn-eu1.vdo.ninja:3478','PCM\x20STARTED','next','autohide','leavetone','send\x20channel\x20closed','closing\x2018','salt','videosource_','received\x20data\x20from\x20viewer','setAudioBitrate','second','pound','once','bandwidth','ON\x20NEGO\x20NEEDED','BITRATE\x203:\x20','defaultSpeaker','removeChild','maxBitrate','closePC','encode','head4','session.watchTimeoutList\x20no\x20longer\x20exists;\x20won\x27t\x20retry.','empty\x20ice..','whether','disablePLI','stereo_url','afraid','allowGraphs','sendGenericData','scaleResolutionDownBy\x20set\x202b!','BundlePolicy','webCodecAudio','chick','disableNACK','destination','Offset\x20may\x20not\x20be\x20negative','differ','sleep','Bad\x20EBML\x20datatype\x20','recordedBlobs','alreadyJoinedMembers','.webm','darkmode','voiceMeter','room\x20rate\x20restriction\x20detected.\x20No\x20videos\x20will\x20be\x20published\x20to\x20other\x20guests','vector','stream\x20ID\x20is\x200\x20length','onended','calculateScale','toward','platform','setBitrate','controls','description','screenshareNotActive','token','degree','remote-control-failed','playbackheader','active','hideDirector','decide','behind','preloadbitrate','maxMobileBitrate','ctx','maxBufferSize','path','EOF2','check','weather','sky','come','processIce2','TOO\x20MANY\x20PUBLISHING\x20PEERS','drive','fillRect','limitAudio','stead','OBSNINJAFORLIFE','whipOutScale','optimizeBitrate','game','status','sendRequest','remote-mute-state','selected','dataMode','aec_url','include','wonder','setLocalDescription','gridlayout','initialPublish','generator','showList','feel','sharperScreen','near','post','\x20---\x20we\x20will\x20ask\x20again','https://temp.vdo.ninja/','mute','tabernac','push-connection-info','opposite','quick','went','history','deal','iframetarget','max_bandwidth_capped_kbps','data\x20channel\x20being\x20used\x20in\x20reverse;\x20this\x20shouldn\x27t\x20really\x20happen,\x20except\x20if\x20maybe\x20doing\x20a\x20file\x20transfer','throttle','square','buffer','ear','whipOutSetScale','getReader','directorActions','cow','virtualHangup','closeTimeout\x20cancelled;\x206\x27\x20retry\x20in\x203s?','.hidden2','roomTimer','power_level','trip','wave','Meshcast\x20(or\x20whip|?)\x20not\x20connected;\x20cant\x27\x20create\x20canvas\x20for\x20it','invalid-remote-code-obs','sort','yard','website','codirectorSettings','string','forceRotate','An\x20RTC\x20error\x20occured','OPEN','eventPlayActive','scaleResolutionDownBy','getOBSOptimization','createElement','ICE\x20DISCONNECTED','mainDirectorPasswor','save','recieveChunkedStream','ICE\x20GATHER\x20START','listing','chunks','off','ASKING\x20FOR\x20AUDIO\x20AND\x20VIDEO?','cell','sending\x20message\x20via\x20WSS\x20as\x20WebRTC\x20failed\x20to\x20send\x20message','effectValue_default','publicKey','sound','hiddenSceneViewBitrate','querySelector','noPLIs','favor','cover','there','closing\x2013','order','midiOffset','CriOS','directorStreamID','Failed\x20to\x20connect\x20to\x20service:\x20Error\x20503Possibly\x20too\x20many\x20connections\x20from\x20the\x20same\x20address\x20tried\x20to\x20connect.Visit\x20https://discord.vdo.ninja\x20for\x20support.','key','https://meshcast.io/view.html?api=','SENDING\x20FILE:\x20','allowScreenVideo','getAudioTracks','requestFile\x20in\x20reverse','encodedInsertableStreams','audioCtx','controlRoomBitrate','short','green','tokenDirector','transparent','consonant','generateStreamID','useragent','whole','girl','joinroom','seeding\x20!!','closing\x202','allowNoGroup','Unmute\x20video','permaMirrored','ISSUING\x20CALLBACK:\x20','Meschast\x20canvas\x20not\x20working','audioContentHint','bye','mainDirectorPassword','requested\x20file\x20has\x20been\x20removed.','shop','block','delayTime','Track\x20stopped','fear','opacityDisconnect','expect','widgetURL','slotmode','requestStatsContinuous','beat','three','lone','refreshScale','hideClock','manual','box','a=extmap:3\x20urn:3gpp:video-orientation\x0d\x0a','webrtc\x20connectioned\x20closed-event','rpcs','ab_url','SET\x20SCALING\x20IS\x20FIRING,\x20which\x20is\x20GOOD\x20!!!!!!\x20','several','keepIncomingVideosInLandscape','autoSync','processDescription2','effect','hash\x20is\x20','welcomeImage','sure','dance','obsControl','videoOptions','claim','getContext','onmessage','canvasSource','EncodedVideoChunk','govern','infocusForceMode','Seeking\x20beyond\x20the\x20end\x20of\x20file\x20is\x20not\x20allowed','PUBLISHER\x27s\x20RTC\x20Connection\x20seems\x20to\x20be\x20dead?\x20','tfliteModule','art','oniceconnectionstatechange','limitAudioBitrate','black','wall','blow','URL','crypto','sand','may','defaultMedia','Meshcast\x20not\x20available.','charAt','incoming\x20screen\x20share\x20started\x20loading','failed','Offset\x20may\x20not\x20be\x20NaN','closing\x201','closing\x204','directorView','createScriptProcessor','ontrack','bypass','speech','silver','enemy','keys','screenStopped','least','TRANSFERRING?','onload','leg','nackCount','Not\x20supported;\x20expected\x20\x27filetransfer\x27','broke','gas','radio','requestCoMigrate','[data-action-type=\x22order-value\x22][data--u-u-i-d=\x22','mirrored','enhance','fun','A\x20director\x20joined\x20the\x20room','war','warn','creating\x20answer','bone','wear','gave','safe','win','heavy','recieveFile','difficult','video_2_init_height','area','network_type','setupYourOwnPlease','fileWriter','Meshcast','father','hidesololinks','closing\x2010','allowmeshcast','quite','chatbutton','screensharequality','updateLocalStatsInterval','observe','scaleHeight','writeByte','label_','rotate','TrackNumber\x20must\x20be\x20>\x200\x20and\x20<\x20127','tainted','summer','travel','bank','close','customWSS','span','322564eFyzOF','processIce','defaultIframeSrc','startWriter','would','addCoDirector','New\x20ON\x20TRACK\x20event','micDelay','hold','gentle','from','timecode','rail','quiet','codirector','createDataChannel','remote-token-rejected','mobile','midi','what\x20is\x20this?','quality_wb','nochunk','product','similar','than','broadcast=false','port',',\x20isDirector:\x20','MESHCAST\x20LINK:\x20','magnet','Bearer\x20','will','application/json;\x20charset=utf-8','localDescription','ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789','197463wVromh','applySoloChat','final','east','bit\x20rate\x20being\x20munged','sourceActive','remember','rather','session','crop','chunkedtransfer','wind','obs_control','targetBitrate','videoWriter','whipCallback2','user\x20didn\x27t\x20have\x20access\x20for\x20this\x20file.','stereo\x20enabled','Bitrate\x20request:\x20','closed','maxviewers','isArray','changeParams','noisegateSettings','wish','together','offer','screenshareStereo','allowBroadcast','mainmenu','audioDevice','[data-action-type=\x22mirror-guest\x22]','digest','This\x20stream\x20token\x20is\x20already\x20connected.\x20Are\x20you\x20having\x20a\x20CORS\x20issue?\x20Also,\x20ensure\x20SSL\x20if\x20enforced\x20on\x20your\x20host\x20everywhere.','limitMeshcastBitrate','writeDoubleBE','done\x20setting\x20degrad\x20to\x20','focus','focusStyle','Change\x20Label','reduce','enqueue','scaleResolution','screenSrc','disconnectedTimeout','audioBitrate','iframeVideo','application/error','muted','clock24','north','sampleRate','though','pose','seeding','wss://debug.vdo.ninja:443','new-view-connection','forceTcpMode','sendChannel_','modifyDescPCM','record','textContent','better','hundred','that','keyframe','canvasCtx','ariaPressed','createWriteStream','unit','onnegotiationneeded','them','switchMode','safemode','group-set-updated','suppressLocalAudioPlayback','watchTimeoutList','icefilter','company','stand','saw','publish','want','requestAudioRateLimit','watch','bandwidthMuted','reconnected','join','remote','codec','screenshareAEC','roombitrate','cost','104685BmJJrm','closeTimeout\x20cancelled;\x207','broadcast','api','split','happen','road','sendframes','heat','requestRateLimit\x20RUN:\x20','allowscreen','egg','rampUpTime','AndroidFix','speakerMuted_default','populate','session.newMainDirectorSetup','decrypted','realtime','nextQueue','ground','UN-MUTED','disableViewerWebAudioPipeline','charging','https://www.youtube.com/','WebRTC\x20Connection\x20Closed.\x20Clean\x20up.\x20657','out','statsMenu','strong','UUID\x20not\x20found;\x20can\x27t\x20close.','EOF1','onceConnected','allowVideos','optimizedBitrate','directorChat','no\x20pcs[UUID]','layout-updated','showall','directorBlue','ball','channel','experimental','encoder','doctor','noise\x20gate\x20on','grow','cae1','publishing_region','raw','plane','top','directorDisplayMuted','preferCurrentTab','fresh','degrade','success','joinRoom','decoder','Can\x27t\x20play\x20your\x20own\x20stream\x20ID','mountain','outboundAudioBitrate','animatedMoves','iceTransportPolicy','scaleDueToBitrate','audioCodec','ended','die','limitMaxBandwidth','decodeInvite','minimumRoomBitrate','lady','allowIframe','title','acc','codecGroupFlag','fullscreenButton','chunkedVideoEnabled','good','fire','Pinging','cut','onicecandidate','whipOutVideoBitrate','and','remoteFocus','requestChangeEQ','candidates','CLOSED','well','field','createBufferSource','EastSideRepresentZ','statsInterval','clock','opus','with','channelCount','createObjectURL','doNotSeed','providing\x20answer','FileSystemWritableFileStream','panning','west','send','caught','connectPeer','dataset','show','closeTimeout\x20cancelled;\x203','closing\x2016','vb_url','wife','includes','ceil','forest','low','small','finish','resumeClock',')\x20failed\x20due\x20to\x20permissions\x20or\x20it\x20was\x20rejected\x20by\x20the\x20user','ondatachannel','language','TFJSModel','\x20---\x20PC\x20TIMED\x20OUT,\x20but\x20still\x20alive.\x20Killing\x20it.','steve','range','invent','recording_audio_mic_delay','whepSettings','activeSpeaker','Create\x20a\x20new\x20RTC\x20connection;\x20offering\x20SDP\x20on\x20request','piece','whipOutput','Can\x27t\x20change\x20the\x20location\x20once\x20started\x20streaming','mirrorGuestState','videoMuted:\x20','document','sendPeers','badStreamList','pol1','whipOutAudioBitrate','recording','dark','hard','getSettings','forcePLI','minute','und','hit','wood','fileList','six','audioLatency','plugged_in','ArrayBufferDataStream','cat','\x20as\x20preferred\x20audio\x20codec\x20by\x20viewer\x20via\x20API\x20(offer)','screenIndexes','verify','container_director','way','directorUUID','taintedSession','iceBundle','widgetSrc','container_','chat','replace','might','round','excite','GOT\x20ICEs!!','timedelta','disableOBS','A\x20Guest\x20joined\x20the\x20room','sceneType','turns:www.turn.vdo.ninja:443','filterOBSscenes','print','made','channels','subject','allowScreen','false','compressor','mediaDevices','video','substring','describe','roomhost','wss://proxywss.rtc.ninja:443','view','stun:stun.l.google.com:19302','closing\x2012','BITRATE\x202:\x20','video/webm','other','directorMirror','nothing','querySelectorAll','noun','undefined','UUID\x20not\x20found\x20in\x20pcs','defaultBackgroundImages','swim','&start=','remote-peer-connected','transferSettings','codirector\x20request\x20hash\x20failed','nomirror','directorDisplayMute','iframeSrc','dad','resume','arraybuffer','those','limitTotalBitrateAll','successfully\x20sent\x20message\x20vis\x20WebRTC\x20instead\x20of\x20WSS\x20to\x20all\x20RTC\x20Peers','tree','noise\x20gate\x20off','much','totalRoomBitrate','main-director','encryptMessage','totalSceneBitrate','PONGED','introButton','limitTotalBitrate','virtualcam','bird','unified-plan','updateurl','Transfer\x20was\x20completed\x20successfully','_screen','fig','limitAudioEncoder','care','both','fakeUser','forceScreenShareAspectRatio','An\x20RTC\x20error\x20occured.','allowscreenaudio','turn:turn-eu2.obs.ninja:3478','story','syllable','targetAudioBitrate','vision-disabled','requestZoomChange','obsState','sid','obs',';\x20charset=utf-8','also','contentHint','mirrorState','https://turnservers.vdo.ninja/','webp','pliCount','CPU','display','volume','BlobBuffer','gather','images','playsinline','./media/bg_sample2.webp','pingTimeout','writeString','equate','lake','remoteVideoMuted','zoom\x20success','son','FORCING\x20A\x20CHUNKED\x20KEY\x20FRAME:\x20','slotsUpdate','century','Opened\x20transfer\x20channel','children','allowWidget','divide','bought','pip','start','vdo.socialstream.ninja','rtc\x20data\x20channel\x20error:\x20','picture','world','reach','Max\x20bandwidth\x20controlling\x20bitrate:\x20','backup.vdo.ninja/','agc_url','Audio_Loudness','connectionState:\x20','socialstream','noise','friend','please','requestResolution','preferCodec','already\x20connected\x202.\x20disconnecting..','setVideoBitrate','selectedIndex','Publisher\x20will\x20be\x20ignored\x20due\x20to\x20max\x20connections\x20already\x20hit','disconnected','forceMediaSettings','RPCS\x20WINS\x20ICE','support','window','roomclaimed','city','subtle','getVideoSettings','run','minptime','null','alert','lowBitrateCutoff','vp09.00.10.08','Websockets\x20timed\x20out;\x2030\x20seconds','enhanceaudio','orientation','screenshare','experience','basic','voiceMeterTemplate','push-connection','produce','\x20x\x20','proxy','ask','during','perhaps','RECONNECTING\x20to\x20HSS;\x20DISCONNECTING\x20FROM\x20TRANSFERRED\x20ROOM','&code=','rise','edgelist','noiframe','mix','measureEBMLVarInt','decrypt','sendonly','recording_audio_gain','broadcastTransfer','closing\x2020','preferVideoCodec','Max\x20bandwidth\x20being\x20capped:\x20','sharp','neighbor','queued','micSampleSize','after','bad','truck','speak','width_url','controlTimer','catch','udp','encodeRemote','height_url','playback_audio_samplerate','meshcastAudioBitrate','data','VP9','directorVideoMuted','rotated','scaleResolutionDownBy\x20set\x202a!\x20','floor','muteState','KEY\x20FRAME\x20REQUESTED','accept_layouts','realTime','getTimezoneOffset','streamid-already-published','seven','tone','while','hidedirector','skin','joiningRoom','scale\x20set!','Safari\x20','face','SEND\x20BYE','visit','version','steel','watch_URL','rub','men','outputLatency','sensorDataFilter','limitBitrate','init_video','pcs\x20RTC\x20CLOSED','tokens-did-not-match','13968nOuRbw','beepToNotify','column','mutedStateScene','ago','quietOthers','manualBandwidth','RTC\x20Connection\x20seems\x20to\x20be\x20dead\x20or\x20not\x20yet\x20open?\x201','message\x20could\x20not\x20be\x20sent;\x20queuing\x20it','transferred\x20and\x20closing','period','blue','MAKING\x20A\x20NEW\x20RPCS\x20RTC\x20CONNECTION','changeMicrophone','past','video_muted_init','connectionState','pixelFix','ICE:\x20','bandwidth\x20set\x20i!\x20','spot','createJavaScriptNode','nodownloads','viewheight','food','trackNumber','hand','old','wss://wss.vdo.ninja:443','ship','mass','youtubeKey','migrate','requestChangeGating','love','batteryState','mirrorGuestTarget','remoteZoom','smell','cleaning\x20up\x20lost\x20connection','dataOffset','concat','this\x20unverified\x20director\x20was\x20already\x20connected;\x20not\x20going\x20to\x20send\x20my\x20director\x20state\x20to\x20them','section','appendChild','remoteMuteElement','ICE\x20closed?','music','act','Content-Type','enhanceAudio','processIceBundle','throttling','sit','could','people','current','put','probable','cook','compare','Bad\x20EBML\x20VINT\x20size\x20','ran','setup\x20peer\x20complete','noon','who','settle','playback_audio_volume_meter','invite','copyTo','classList','continent','directorMutedState','iceServers','/status','require','Second\x20Thread\x20Waiting\x20for\x20TURN\x20LIST\x20to\x20load','ever','listen','none','event','sceneDisplay','exclude','remoteRaisedHandElement','delayNode','audio','stunServers','borderRadius','stats','school','solo','guest-connected','webrtc-is-blocked','getChannelData','add-a-label','notifyScreenShare','screen','keep','arrayBuffer','cbr','INITIAL\x20PUBLISH\x20START:\x20','clothe','couldn\x27t\x20set\x20rate\x20limit','privateKey','copy','discuss','child','outputDevice','setValueAtTime','iceTimer','delta','write','writer_config','agree','fit','silent','flower','tire','waitImageTimeout','onaudioprocess','host','turns:www.turn.obs.ninja:443','keyframeRate','when','whipout','broadcastIFrame','codirectorRequested','div','screenshareContentHint','AudioContext','WebMWriter','day','playing','fair','initialDirectorSync','MESHCAST();','remoteMuteState_','star','map','autoSyncCallback','very','signalMeter','devicePixelRatio','was','canvasWebGL','wire','Chromium-based\x20v','transcript','hidden','Websocket\x20connection\x20failed\x20or\x20something;\x20this\x20is\x20a\x20split\x20connection.\x20not\x20ideal,\x20as\x20it\x20could\x20be\x20unstable.','restricted','closing\x2014','no\x20audio\x20track\x20to\x20poke','whipOutScreenShareBitrate','requestChangeCompressor','stringify','flat','brought','joy','request\x20rate\x20limit:\x20','year','abc123','encrypt','frameMeta','scaleSnap','IchBinSteveDerNinja','limitTotalBitrateGuests','rejected','opacity','plugged','screenShareLabel','surprise','book','you','splice','fromCharCode','ontimeout','miniInfo','fat','whipOutCodec','tube','requestFocusChange','material','autoSyncObject','iframeDetails_','build','midiHotkeys','null\x20ice\x20rpcs','offerSDP','videoElement','hssConnection','women','indexOf','42001f','cause','HANG\x20UP\x20COMPLETE','Transfer\x20ended','hostname','error','substance','winter','receiveChannel','dataReceived','drop','readable','bufferedAmount','audioMeterGuest','CHUNKED\x20DETAILS','Remote\x20TURN\x20LIST\x20Loaded\x20**\x20','teeth','setAttribute','letter','audioChannels','create','200055ZLKMrE','woman','actual\x20bitrate:','micSampleRate','need','rpc\x20datachannel\x20closed','sense','midiChannel','train','hill','human','desaltStreamID','Unknown','Only\x20the\x20main\x20director\x20can\x20use\x20this\x20setting','spread','added\x20video\x20track','whepInputToken','requestAudioHack','remoteHash','done\x20clearing\x20audio','writeEBMLVarIntWidth','did','have','prioritize-audio','seek','optionalMicOnly','great','mono','bitrateTimeoutFirefox','lowerVolume','Setting\x20pc\x20connection\x20timeout\x20in\x205\x20seconds\x20??','part','experiment','death','region','has','meat','miss','decryptMessage','break','pauseClock','click','house','drink','canvas','roomid','Does\x20Local\x20Stream\x20Source\x20EXIST?','processFrame','autoadd','security','url','codecs','bitrate\x20timeout;\x20ios/firefox\x20specific:\x20','Messaging\x20sent','allowscreenvideo','visibility','./media/bg_sample.webp','infocus2','cid:','age','parent','pick','duck','high','contentType','meshcastCode','symbol','bread','often','wide','limiting\x20AudioEncoder','cbid','mutedState','displayMute','plain','video_init_width','obs.ninja/','were','eight','sign','chunkedStream','push','sceneMute','pow','\x20as\x20preferred\x20video\x20codec\x20by\x20viewer\x20via\x20API\x20(offer)','forceAspectRatio','vDAv','infocus','whep_URL','requestStats','configure','pretty','obsCommand','enabled','numberOfChannels','pop','either','webPquality','town','add','automute','gold','pluginVersion','hear','any','line','law','mykey','Chunked_audio','farm','option','closing\x205','relay','setResolution\x20triggered;\x20','touch','above','videoMutedFlag','half','she','waiting\x20for\x20keyframe','requestChangeMicDelay','parse','micIsolate','Adjusting\x20Gain;\x20only\x20track\x200\x20in\x20all\x20likely\x20hood,\x20unless\x20more\x20than\x20track\x200\x20support\x20is\x20added.','width','group_alt','group','VDO-Ninja','especially','subarray','turnlist','setParameters','AES','video_init_frameRate','processFrameVideo','object','targetBandwidth','quotient','SETUP\x20INCOMING','track','verb','person','noaudio','audiobitrate','subtract','wrote','cleanup','page','apple','screenElement','UUID','maxframeRate','dynamicScale','iframe','look','hunt','stick','turns:turn.obs.ninja:443','twenty','totalBitrate:\x20','Checking\x20to\x20see\x20if\x20reconnectino\x20to\x20ws\x20lost\x20any\x20peers','his','WHY\x20ARE\x20YOU\x20GOD\x20DAMN\x20BEEPING','minipreview','enc','layout','screenShareElement','The\x20request\x20(','surfaceSwitching','Media','tallyStyle','addALabel','showDirector','intime','checkBasicStreamsExist','overlay','chunkedAudioEnabled','retrying\x20at\x20an\x20interval','Valid\x20co\x20director\x20trying\x20to\x20transfer\x20a\x20guest','failed\x20to\x20send\x20focus\x20change\x20request','msg\x20size\x20error','place','liquid','loadoutID','checking','selectImageTFLITE_contents','director-connected','air','number','Firefox','Restarting\x20since\x20closed','This\x20shouldn\x27t\x20happen','rich','grabFaceData','Please\x20contact\x20steve@seguin.email\x20or\x20join\x20https://discord.vdo.ninja\x20if\x20Meshcast\x20is\x20not\x20working.','left','ACTION\x20REJECTED:\x20','audioEffects','couldn\x27t\x20set\x20preferred\x20audio\x20codec','glad','midiDevice','updateTime','h264','zoom','getOpusBitrate','set-video-bitrate','element','hurry','street','streamSrc','startTime','strange','closing\x2019','viewDirectorOnly','NOT\x20VIEW\x20TARGET','addTrack','autostart','change','turn:turn-usw2.vdo.ninja:3478','approved-as-director','Keyframe\x20inserted','audioGain','channelWidth','screenshareid','end-view-connection','padStart','rotation','RSASSA-PKCS1-v1_5','chatname','general','director-share','removeTrack','onnegotiationneeded\x20triggered;\x20creating\x20offer','are','got','already\x20connected\x201','should','PCMSource','corner','bind','practice','fine','preLimitedBitrate','village','stereo','such','oil','surface','1280','watchStream','state','iPhone12Up','consent','rotate_video','FAIL\x20rpcs\x20onconnectionstatechange','beauty','remoteDescription','coDirector','nacks_per_second','720','proper','midiIn','broadcast_mode','setVideoBitrates','consider','frame','save\x20bandwidth:\x20','downloads','setUint32','land','could\x20not\x20be\x20sent;\x20queuing\x20it','requestAs','srcObject','heard','under','degradationPreference','getElementById','directorSpeakerMuted','vdoninja','locale','broadcastChannel','bell','failed\x20to\x20disconnect','roomenc','disableREMB','appear','addTransceiver','locate','red','requestRateLimit','party','set','four','closeTimeout\x20cancelled;\x205','closeTimeout\x20cancelled;\x204','seedAttempts','say','learn','vp8','welcomeMessage','eye','here','audioTime','AES-CBC','screenShareElementHidden','shape','particular','deferring\x20with\x20a\x20promise','showClock','maxptime','preferAudioCodec','speakerMuted','height','arrive','except','postMessage','getResponseHeader','getVideoBitrates','noFEC','autorecordremote','tie','decode','flagship','changeOrder','bear','science','writeFloatBE','setRemoteDescription','sugar','space','Trying\x20to\x20set\x20','requestKeyframe','whep','obsStateSync','some','Unhandeled\x20Error\x20occured','border','showRoomTime','getWrittenSize','possible','setOpusAttributes','vp9','newViewConnection','manualSink','ICE\x20GATHER\x20COMPLETED','meterStyle','announceCoDirector','borderColor','now','constructor','screenshare_url','ice','requestChangeSubGain','imagine','','audioOptions','quart','then','\x20is\x20not\x20defined;\x20skipping.','GOT\x20ICES!!','\x20(fail)','lyra','course','try','pressed','lockedAudioBitrate','allowmidi','hangupbutton','Refreshing\x20scale','stood','bed','SHA-256','local','pip3','yet','startsWith','toLowerCase','spell','bitrateTimeout','includeRTT','webCodec','optimize','cpuLimited','lay','approved','determine','body','turn:turn-cae1.vdo.ninja:3478','whipOut','videoEncoder','completed','err','throw','tuning','reason','requestSceneUpdate','gotGenericData','addFrame','find','decodeRemote','opacityMuted','applyIsolatedVolume','control','screensharecursor','sdp','more','mount','sendChunks','mid','charge','userAgent','queue','getParameters','requested\x20file\x20was\x20not\x20found','localMuteElement','obsninja','selfBrowserSurface','mixMinus','arrange','common','image/webp','original','stopClock','realUUID','dog','ctrlKey','noREMB','raise','chord','onclose','auth','sendMsg','loadstart','motionDetectionInterval','real','retryTimeout','anyrequest','STREAM\x20ID\x20desalted\x202:','ping','take','cloneNode','depend','\x20query\x20is\x20not\x20defined;\x20skipping.','utf-8','allowVideo','noExitPrompt','Someone\x20sent\x20us\x20an\x20ANSWER\x20sdp??','getWriter','our','gone','process','cotton','imageElement','setupIncoming','card','altUUID','writeBytes','solve','videosource','season','pos','allowDownloads','lost','set-meshcast-video-bitrate','obsRemotePassword','\x20as\x20preferred\x20codec\x20by\x20viewer\x20via\x20API','king','coDirectorEnable','chunked_mode_audio','nosettings','iOS\x20devices\x20do\x20not\x20support\x20dynamic\x20bitrates\x20correctly;\x20skipping','pass','not\x20allowed\x20to\x20show\x20the\x20director','study','video_encoder','bigmutebutton','samplingFrequency','configVideo','obsSceneTriggers','Publisher\x20is\x20being\x20sent\x20a\x20video\x20stream???\x20NOT\x20EXPECTED!','pushEffectsData','sync','SENDING\x20CHUNKS\x20TO:\x20','done','atom','for','serve','middle','successfully\x20sent\x20message\x20vis\x20WebRTC\x20instead\x20of\x20WSS','shine','told','EncodedAudioChunk','1240dkFXfV','muteStateTemplate','mile','PCS\x20WINS\x20ICE','note','fillStyle','getAudioSettings','header','play','nor','EBML\x20VINT\x20size\x20not\x20supported\x20','newMainDirectorSetup','Trying\x20to\x20join\x20at\x20least','canvasStream','pptControls','they','allowScreenAudio','maxconnections','isInteger','obsSceneSync','nation','scale','json','level','tall','rest','sendOnNewConnect','labelstyle','totalRoomBitrate_default','new','overlayNinja','dress','loadend','lowMobileBitrate','pitch','zoomedBitrate','believe','currentTarget','dtx','createMediaStreamDestination','privacy','request\x20zoom\x20change:\x20','character','band','getSenders','can\x27t\x20change\x20bitrate;\x20no\x20video\x20senders\x20found','createBuffer','Chunked_video','h264profile','processDescription','screenshareVideoOnly','iframeSrcs','open','cleanish','deep','scale\x20set!\x20','message','can\x27t\x20change\x20bitrate;\x20no\x20video\x20sender\x20found','stopWriter','remoteStats','inboundAudioPipeline','name','glass','seedStream','warm','idea','streaming','double','wing','onconnectionstatechange','video_2_init_width','msg','can','plant','BYE','Answer\x20SDP\x20does\x20not\x20have\x20a\x20matching\x20session\x20ID','sitePassword','\x20','info','visible','limitaudio','offerToReceiveAudio','directorVolumeState','showTime','dropped\x20candidate\x20due\x20to\x20filter','gpGPU','why','maxviewers_url','planet','brown','requested-stream','valley','forward','connected\x20to\x20video\x20server','importKey','obsstudio','getLocalStream','screenShareState','chunked_mode_video','lowcut','Video\x20File','pattern','kill','bottom','bitrate_set','lot','prepare','room=','NOT\x20IN\x20VIEW\x20SET','bundlePolicy','custom\x20layout\x20being\x20applied','writable','right','novideo','soon','pipWindow','led','changeSpeaker','ring','Stream\x20ID\x20is\x20already\x20in\x20use.','able','sail','suggest','enhanceAudioEncoder','sensorData','Inbound\x20User-based\x20Message\x20from\x20Room','theyBeSharksHere','multiply','fly','toString','SCREENS','case','chunkedInQueue','remoteMuted','meant','request','screen-share-state','fact','LOADING\x20UP\x20WAITING\x20WATCH\x20STREAM:\x20','ptime','foot','you-are-a-codirector','writeEBMLVarInt','against','de1','what','frameRate','end','midiRemote','life','Max\x20bandwidth\x20NOT\x20being\x20capped:\x20','mine','special','bitrate','sendChannel','answer','iceConnectionState\x20==\x20connected','de2','complete','written','autoplay','busy','POST','motion','receive','readAsArrayBuffer','setRequestHeader','realTimeAudio','video_init_height','eat','engine','transferred','chief','shift','their','true\x20.','directorBlindAllGuests','whip','BYE\x20RPCS','prompt-access-request','interval','garden','turn:turn-use1.vdo.ninja:3478','maintain-framerate','move','view-connection','over','director','been','promise_audio','boat','stream','allowAudio','remove','simple','groupAudio','motionSwitch','sheet','keyname','codirector_changeURL','firstPlayTriggered','cmd','session.limitMaxBandwidth\x20running:\x20','directorPassword','loud','collect','match','Meshcast\x20error:\x20432','directorBox','wont','directorState','onconnectionstatechange\x20pcs\x20ice\x20--\x20disconnected,\x20but\x20not\x20yet\x20closed?\x20','use1','endViewConnection','prototype','onopen','listPromise','clicked','stream_configVideo','rtc\x20data\x20channel\x20error\x202:\x20','nopreview','hss-connection','remote-video-mute-state','retryWatchInterval','contain','debug','isScene','her','roll','grass','watchTimeoutList2:','tail','burn','tiny','type','-kbps','cleanOutput','trouble','syncState','last','closing\x20rpc\x20due\x20to\x20hangup\x20event','changeCamera','sun','ptz','modifyDescLyra','ice\x20timer\x20no\x20longer\x20exists','writeUnsignedIntBE','about','guest','directorHash','requestStream','Created\x20transfer\x20channel','triangle','canvasIntervalAction','direct','stream_configAudio','screenStream','mark','disabled','prove','maxvb_url','song','onreadystatechange','webAudios','raisehands','jointone','A_OPUS','rule','organ','kind','white','wssid','createAnswer','configAudio','\x20---\x20PC\x20TIMED\x20OUT\x20and\x20already\x20deleted.\x20shouldn\x27t\x20happen','setClock','correct','talk','Someone\x20published\x20a\x20video\x20to\x20the\x20Room','slots','closeRPC','lockedVideoBitrate','resolution\x20scale:\x20','HANG\x20UP\x202\x20COMPLETE','pull','sat','noNacks','If\x20self-hosting\x20VDO.Ninja,\x20please\x20contact\x20steve@seguin.email\x20to\x20request\x20having\x20access\x20to\x20Meshcast.','turn:turn-eu4.vdo.ninja:3478','showSettings','speakerMute','allowWebp','starting\x20some\x20preload\x20bitrate\x20','studioSoftware','cross','allowwhipout','huge','changeLabel','captain','permaid','dollar','crease','byteLength','view-connection-info','represent','tool','code','options','\x27\x20target=\x27_blank\x27>','remote-screenshare-state','occur','streamSrcClone','laugh','operate','Remote\x20request\x20failed\x20to\x20decode;\x20continuing\x20still.','configuration','turn:www.turn.vdo.ninja:3478','division','writeU8','available-speedtest-servers','pipe','dictionary','value\x20there','6REUDVY','oxygen','thought','nature','removed\x20from\x20SDP:\x20\x27a=extmap:3\x20urn:3gpp:video-orientation\x0d\x0a\x27','month','board','connected','stereo\x20inbound\x20enabled','less','processFrameAudio','scaleFactor','approved:\x20','wssSetViaUrl','insect','sendroom','batteryMeter','effectsData','slice','lowerhand','five','estop','42e01f','needKeyFrame','vDav','video_bitrate_kbps','SDP\x20Sessions\x20Match.\x20I\x20assume\x20ADDING\x20TRACKS.\x20RPCS','closedCaptions','press','deviceId','cpu','showControls','vary','closing\x207','colorVideosBackground','showSaveFilePicker','seat','http://','WEBRTC\x20CONNECTION\x20OPEN','Authorization','no\x20video\x20track\x20to\x20control','separate','soft','filetransfer','responseText','outboundVideoBitrate','dedicatedControlBarSpace','does','hangup','timer','shore','count','justResetting','work','wss://api.vdo.ninja:443','allowwebp','boy','closeTimeout\x20cancelled;\x202','waitImage','equalizer','target','obsControls','h264profile\x20being\x20modified','full','gain','recordLocal','brother','parentNode','directorEnabledPPT','log','read','remoteVideoMuteElement','view_set','timestamp','flow','init_audio','innerHTML','long','began','coast','machine','focusDistance','orderby','yellow','audioContext','getRandomValues','Generate\x20Some\x20Crypto\x20keys\x20first','setResolution','side','sight','call','heart','single','processPCSOnMessage','adaptivePtime','far','back','equal','lockWindowSize','gray','know','locked','keyframeTimeout','createWritable','loudest','maxvideobitrate','processRPCSOnMessage','promptAccess','whip_Host','conn_type','wash','cloud','him','among','poem','whepInput','connect','instrument','pcs','screensharebutton','%\x20battery\x20remaining','m\x20:\x20','whipView','#obsRemotePassword>input','soloChatUUID','joining-room','ocean','Not\x20a\x20scene','scaleWidth','room_init','vowel','fr1','corn','initial_group','provideFileList','abs','setupScreenShareAddon','not\x20record\x20button\x20detected;\x20can\x27t\x20update\x20time\x20since\x20started\x20recording','closing\x206','stop','money','iceConnectionState','hands_','neck','requestCoDirector','forceRetry','stretch','startClock','certain','quality','camp','getVideoTracks','seed','whipCallback','provide','gathering','allowMIDI',',\x20mc?:\x20','little','innerText','starting\x20kicker','Browser','solo-scene-connected','style','audio\x20bandwidth\x20set\x20f!','bandwidth\x20set\x20h!\x20','help','wrong','setScale','onerror','guess','chunked','writer','stopPropagation','team','same','sensors','defaultPassword','encodings','mutedStateMixer','offsetChannel','hidehome','set-audio-bitrate','weight','mirrorExclude','label','choose','cleanDirector','requestVideoRecord','allow','sendMessage','#000','password','whipOutputToken','form','nocursor','already\x20closed\x20PCS','modern','audioInputChannels','indicate','videoMuted','dont','RPCS\x20for\x20MESHCAST\x20ISNT\x20MADE\x20YET??','chart','island','widget','RTCRtpSender','147934mdboeE','thin','noMeshcast','hostedTransfers','encodering\x20being\x20kicked','time','iceGatheringState','came','room-is-claimed','chance','sink','start\x20writing\x20frames','ctrl','addEventListener','UUID\x20not\x20found;\x20cant\x27\x20close','TRYING\x20TO\x20SYNC\x20WITH\x20SENDING:\x20','spring','358929WGKhVq','morning','resolve','bit','until','frameWriter','moon','sendFile','force','RUNNING\x20CALLBACK:\x20','theirtime','directorSettings','size','still','charCodeAt','electric','CONNECTED\x20TO\x20FIRST\x20PEER','offset','phrase','word','value','bigPlayButton','Connection\x20to\x20Control\x20Server\x20lost.\x0a\x0aWill\x20try\x20to\x20reconnect\x20in\x202\x20seconds.','coat','no\x20UUID\x20in\x20msg','closing\x209','transfer','sentence','tell','set-video-scale','webkitAudioContext','GOT\x20ICE!!','invalid-remote-code','request\x20focus\x20change:\x20','grand','settings','Meshcast\x20SET\x20SCALING\x20IS\x20FIRING,\x20which\x20is\x20GOOD\x20!!!!!!','result','length','noisegate','milk','Bad\x20UINT\x20size\x20','straight','readyState','applyIsolatedChat','realTimeVideo','directorSpeakerMute','solution','micIsolated','addIceCandidate','wss','obsfix','big','changeURL','sending\x20request\x20via\x20server','pastSlots','waitingWatchList','true','exercise','codirector_transfer','hash','cameraConstraints','showlabels','screenshareAutogain','createOffer','ruleOfThirds','candidate','fillDataBuffer','setVideoScale','stopping\x20some\x20preload\x20bitrate\x20','clean','ifs','recorder','wheel','[data-action-type=\x22remove-queue\x22][data--u-u-i-d=\x22','PROBLEM,\x20Senders\x20is\x20more\x20than\x200:\x20','remote-group-change','details','must','directVideoMuted','forceRetryTimeout','sending\x20message\x20via\x20WSS\x20as\x20WebRTC\x20failed\x20to\x20send\x20message;\x20RTC\x20peers\x20only','suit','application/sdp','playback_audio_pipeline','seedPlz','sceneSync','requestFile','already\x20watching\x20stream','unshift','speedtest','property','?ts=','forceios','channelOffset','site-not-responsive','mind','noWidget','signature','Failed\x20to\x20request\x20video\x20and\x20audio;\x20iOS\x20device\x20asking?','requestedStatsInterval','where','fill','hasOwnProperty','one','hanging\x20up','pong','total','activelySpeaking','door','priority','needsLoading','make','row','sceneType2','soloVideo','content-type','onTrack','not-the-director','knew','guide','list','stopping\x20old\x20track','apiserver','industry','requestChangeLowcut','requestUpload','ori','fakeFeeds','pcm','successfully\x20requested\x20audio\x20and\x20video?\x20maybe?','Audio\x20isn\x27t\x20setup\x20yet.','best','whipoutSettings','the','chair','most','timeout','remoteMuteState','reload','time_seconds','random','getTracks','hat','noiseSuppression','car','[data-action-type=\x27recorder-local\x27][data--u-u-i-d=\x27','done\x20setting\x20degrad','Requested_resolution','deferring\x20with\x20a\x20promise;\x20hashed\x20room','getAsDataArray','src','directorList','readAsText','img','snow','action','new-push-connection','captureStream','problem','buy','interest','broadcastChannelID','anysend','session.provideFileList','Overwrite\x20crosses\x20blob\x20boundaries','savedBitrate','cold'];_0x2d5e=function(){return _0x3d0c02;};return _0x2d5e();}async function meshcastWatch(_0x2a80ac, _0x1ea9d5){var _0xf745aa=_0x2de9a7;!(_0x2a80ac in session['rpcs'])&&(session[_0xf745aa(0x16a)][_0x2a80ac]={},session[_0xf745aa(0x16a)][_0x2a80ac][_0xf745aa(0x436)]={},session[_0xf745aa(0x16a)][_0x2a80ac][_0xf745aa(0x9db)]=![],session[_0xf745aa(0x16a)][_0x2a80ac][_0xf745aa(0x6dc)]={},session['rpcs'][_0x2a80ac][_0xf745aa(0x921)]=![],session[_0xf745aa(0x16a)][_0x2a80ac][_0xf745aa(0x592)]=![],session['rpcs'][_0x2a80ac][_0xf745aa(0x8e6)]=![],session['rpcs'][_0x2a80ac][_0xf745aa(0x350)]=null,session['rpcs'][_0x2a80ac][_0xf745aa(0x665)]=![],session['rpcs'][_0x2a80ac][_0xf745aa(0x7b9)]=![],session[_0xf745aa(0x16a)][_0x2a80ac][_0xf745aa(0x621)]=![],session[_0xf745aa(0x16a)][_0x2a80ac][_0xf745aa(0x3e4)]=![],errorlog(_0xf745aa(0x8ad)));var _0x157d02=!![],_0x5bdb16=!![];if(session[_0xf745aa(0x711)]!==![]&&!session['novideo'][_0xf745aa(0x2c2)](session[_0xf745aa(0x16a)][_0x2a80ac][_0xf745aa(0x9bb)]))_0x157d02=![];else session[_0xf745aa(0x16a)][_0x2a80ac][_0xf745aa(0x8e6)]&&!session[_0xf745aa(0x16a)][_0x2a80ac][_0xf745aa(0x8e6)][_0xf745aa(0x30c)]&&(_0x157d02=![]);if(session[_0xf745aa(0x542)]!==![]&&!session['noaudio'][_0xf745aa(0x2c2)](session['rpcs'][_0x2a80ac]['streamID']))_0x5bdb16=![];else session['rpcs'][_0x2a80ac]['settings']&&!session['rpcs'][_0x2a80ac][_0xf745aa(0x8e6)][_0xf745aa(0x433)]&&(_0x5bdb16=![]);if(!_0x5bdb16&&!_0x157d02){errorlog(_0xf745aa(0x96d));return;}disableQualityDirector(_0x2a80ac);!session[_0xf745aa(0x7db)]&&await chooseBestTURN();try{session[_0xf745aa(0x16a)][_0x2a80ac][_0xf745aa(0x600)]=new RTCPeerConnection(session[_0xf745aa(0x7db)]);}catch(_0x5f488a){!session['cleanOutput']&&warnUser(_0xf745aa(0xa48));}session[_0xf745aa(0x16a)][_0x2a80ac]['whep'][_0xf745aa(0x196)]=function(_0x34dae9){var _0x4c050a=_0xf745aa;session[_0x4c050a(0x938)](_0x34dae9,_0x2a80ac);};var _0x27c92c=session[_0xf745aa(0xa76)](0xe),_0x31ea3a={};_0x31ea3a[_0xf745aa(0x9bb)]=_0x1ea9d5['token'],_0x31ea3a['UUID']=_0x27c92c;function _0x282161(_0x5ddbd9){var _0xa4f57d=_0xf745aa,_0xceae3=new XMLHttpRequest();_0xceae3[_0xa4f57d(0x7a6)]=function(){var _0x2f56d4=_0xa4f57d;if(this[_0x2f56d4(0x8ee)]==0x4&&(this[_0x2f56d4(0xa13)]==0xc8||this[_0x2f56d4(0xa13)]==0xc9)){var _0xfd1744=this['getResponseHeader']('content-type');if(_0xfd1744==_0x2f56d4(0x916)){var _0xc1f7be={};_0xc1f7be[_0x2f56d4(0x648)]=this[_0x2f56d4(0x80f)],_0xc1f7be['type']=_0x2f56d4(0x20f),session[_0x2f56d4(0x16a)][_0x2a80ac][_0x2f56d4(0x600)]['setRemoteDescription'](_0xc1f7be)['then'](function(){_0x5181c6();})[_0x2f56d4(0x3b6)](function(_0x5dc6c4){log(_0x5dc6c4);});}}else log(this);},_0xceae3[_0xa4f57d(0x6d4)](_0xa4f57d(0x742),_0x1ea9d5['url'],!![]),_0xceae3[_0xa4f57d(0x746)](_0xa4f57d(0x40f),_0xa4f57d(0x1f2)),_0xceae3['setRequestHeader'](_0xa4f57d(0x80a),_0xa4f57d(0x1f0)+_0x1ea9d5[_0xa4f57d(0x9f7)]),_0xceae3[_0xa4f57d(0x2b9)](JSON[_0xa4f57d(0x479)](_0x5ddbd9));}function _0x5181c6(){var _0x9f8565=_0xf745aa;session[_0x9f8565(0x16a)][_0x2a80ac][_0x9f8565(0x600)][_0x9f8565(0x7b0)]()['then'](function(_0x184342){var _0x545fd6=_0x9f8565;return _0x184342['sdp']=CodecsHandler[_0x545fd6(0x608)](_0x184342[_0x545fd6(0x648)],{'stereo':0x1}),session[_0x545fd6(0x16a)][_0x2a80ac]['whep']['setLocalDescription'](_0x184342);})[_0x9f8565(0x619)](function(){var _0x4ee111=_0x9f8565,_0x36427d={};_0x36427d[_0x4ee111(0x54a)]=_0x27c92c,_0x36427d[_0x4ee111(0x73b)]=session[_0x4ee111(0x16a)][_0x2a80ac]['whep'][_0x4ee111(0x1f3)][_0x4ee111(0x648)],_0x282161(_0x36427d);})[_0x9f8565(0x3b6)](function(_0x21c658){});}_0x282161(_0x31ea3a);}(function(){'use strict';var _0x1048df=_0x2de9a7;let _0xb02573=function(_0x4042eb){this['data']=new Uint8Array(_0x4042eb),this['pos']=0x0;};_0xb02573[_0x1048df(0x776)]['seek']=function(_0x18d44f){var _0x34442a=_0x1048df;this[_0x34442a(0x680)]=_0x18d44f;},_0xb02573[_0x1048df(0x776)][_0x1048df(0x67c)]=function(_0x27cced){var _0x574bbc=_0x1048df;for(let _0x69b7a0=0x0; _0x69b7a0<_0x27cced[_0x574bbc(0x8e9)]; _0x69b7a0++){this[_0x574bbc(0x3bc)][this[_0x574bbc(0x680)]++]=_0x27cced[_0x69b7a0];}},_0xb02573[_0x1048df(0x776)][_0x1048df(0x1c7)]=function(_0xa27620){var _0x38f29a=_0x1048df;this[_0x38f29a(0x3bc)][this[_0x38f29a(0x680)]++]=_0xa27620;},_0xb02573[_0x1048df(0x776)]['writeU8']=_0xb02573['prototype'][_0x1048df(0x1c7)],_0xb02573[_0x1048df(0x776)]['writeU16BE']=function(_0x2cf3b9){var _0x537c23=_0x1048df;this[_0x537c23(0x3bc)][this[_0x537c23(0x680)]++]=_0x2cf3b9>>0x8,this[_0x537c23(0x3bc)][this[_0x537c23(0x680)]++]=_0x2cf3b9;},_0xb02573[_0x1048df(0x776)][_0x1048df(0x218)]=function(_0x51b961){var _0xd0b85b=_0x1048df;let _0x40d111=new Uint8Array(new Float64Array([_0x51b961])[_0xd0b85b(0xa33)]);for(let _0x58e10f=_0x40d111[_0xd0b85b(0x8e9)]-0x1; _0x58e10f>=0x0; _0x58e10f--){this[_0xd0b85b(0x1c7)](_0x40d111[_0x58e10f]);}},_0xb02573[_0x1048df(0x776)][_0x1048df(0x5fa)]=function(_0x3f3b96){var _0x40ffd3=_0x1048df;let _0x5d5c78=new Uint8Array(new Float32Array([_0x3f3b96])[_0x40ffd3(0xa33)]);for(let _0xa4803b=_0x5d5c78['length']-0x1; _0xa4803b>=0x0; _0xa4803b--){this[_0x40ffd3(0x1c7)](_0x5d5c78[_0xa4803b]);}},_0xb02573[_0x1048df(0x776)][_0x1048df(0x35d)]=function(_0xe8069c){var _0x1efdbe=_0x1048df;for(let _0x348311=0x0; _0x348311<_0xe8069c[_0x1efdbe(0x8e9)]; _0x348311++){this[_0x1efdbe(0x3bc)][this[_0x1efdbe(0x680)]++]=_0xe8069c[_0x1efdbe(0x8d1)](_0x348311);}},_0xb02573[_0x1048df(0x776)][_0x1048df(0x4c8)]=function(_0x2e4ea3, _0x5a7531){var _0x4f5207=_0x1048df;switch(_0x5a7531){case 0x1:this[_0x4f5207(0x7de)](0x1<<0x7|_0x2e4ea3);break;case 0x2:this[_0x4f5207(0x7de)](0x1<<0x6|_0x2e4ea3>>0x8),this[_0x4f5207(0x7de)](_0x2e4ea3);break;case 0x3:this[_0x4f5207(0x7de)](0x1<<0x5|_0x2e4ea3>>0x10),this[_0x4f5207(0x7de)](_0x2e4ea3>>0x8),this[_0x4f5207(0x7de)](_0x2e4ea3);break;case 0x4:this[_0x4f5207(0x7de)](0x1<<0x4|_0x2e4ea3>>0x18),this[_0x4f5207(0x7de)](_0x2e4ea3>>0x10),this[_0x4f5207(0x7de)](_0x2e4ea3>>0x8),this[_0x4f5207(0x7de)](_0x2e4ea3);break;case 0x5:this[_0x4f5207(0x7de)](0x1<<0x3|_0x2e4ea3/0x100000000&0x7),this[_0x4f5207(0x7de)](_0x2e4ea3>>0x18),this['writeU8'](_0x2e4ea3>>0x10),this[_0x4f5207(0x7de)](_0x2e4ea3>>0x8),this[_0x4f5207(0x7de)](_0x2e4ea3);break;default:throw new Error(_0x4f5207(0x41b)+_0x5a7531);}},_0xb02573[_0x1048df(0x776)][_0x1048df(0x3a4)]=function(_0xf50d9e){var _0x355dae=_0x1048df;if(_0xf50d9e<(0x1<<0x7)-0x1)return 0x1;else{if(_0xf50d9e<(0x1<<0xe)-0x1)return 0x2;else{if(_0xf50d9e<(0x1<<0x15)-0x1)return 0x3;else{if(_0xf50d9e<(0x1<<0x1c)-0x1)return 0x4;else{if(_0xf50d9e<0x7ffffffff)return 0x5;else throw new Error(_0x355dae(0x6aa)+_0xf50d9e);}}}}},_0xb02573[_0x1048df(0x776)][_0x1048df(0x72e)]=function(_0x11d6c7){var _0x4192b2=_0x1048df;this[_0x4192b2(0x4c8)](_0x11d6c7,this[_0x4192b2(0x3a4)](_0x11d6c7));},_0xb02573[_0x1048df(0x776)]['writeUnsignedIntBE']=function(_0x283350, _0x3b448c){var _0x5c3823=_0x1048df;_0x3b448c===undefined&&(_0x3b448c=this[_0x5c3823(0x97d)](_0x283350));switch(_0x3b448c){case 0x5:this[_0x5c3823(0x7de)](Math[_0x5c3823(0x3c1)](_0x283350/0x100000000));case 0x4:this[_0x5c3823(0x7de)](_0x283350>>0x18);case 0x3:this[_0x5c3823(0x7de)](_0x283350>>0x10);case 0x2:this['writeU8'](_0x283350>>0x8);case 0x1:this['writeU8'](_0x283350);break;default:throw new Error(_0x5c3823(0x8ec)+_0x3b448c);}},_0xb02573[_0x1048df(0x776)][_0x1048df(0x97d)]=function(_0x207f2d){if(_0x207f2d<0x1<<0x8)return 0x1;else{if(_0x207f2d<0x1<<0x10)return 0x2;else{if(_0x207f2d<0x1<<0x18)return 0x3;else return _0x207f2d<0x100000000?0x4:0x5;}}},_0xb02573['prototype']['getAsDataArray']=function(){var _0x513424=_0x1048df;if(this[_0x513424(0x680)]this['length'])throw new Error(_0x8c6f3a(0x17f));this[_0x8c6f3a(0x680)]=_0x139464;},this[_0x2f6764(0x44d)]=function(_0x833ac9){var _0x3e98c6=_0x2f6764;let _0x48bdc4={'offset':this[_0x3e98c6(0x680)],'data':_0x833ac9,'length':_0xf18960(_0x833ac9)},_0x26e421=_0x48bdc4['offset']>=this[_0x3e98c6(0x8e9)];this[_0x3e98c6(0x680)]+=_0x48bdc4[_0x3e98c6(0x8e9)],this[_0x3e98c6(0x8e9)]=Math['max'](this[_0x3e98c6(0x8e9)],this[_0x3e98c6(0x680)]),_0x33b208=_0x33b208[_0x3e98c6(0x619)](async function(){var _0x5ca49c=_0x3e98c6;if(_0x19fc93)return new Promise(function(_0x33166a, _0x1e6a05){var _0x15001b=_0x5c68;_0x20dc80(_0x48bdc4[_0x15001b(0x3bc)])['then'](function(_0x2ff33a){var _0x26adca=_0x15001b;let _0x307884=0x0,_0x4f75bc=Buffer['from'](_0x2ff33a['buffer']),_0x6766e4=function(_0x5daf95, _0x57db1f, _0x424619){var _0x37e2f0=_0x5c68;_0x307884+=_0x57db1f,_0x307884>=_0x424619[_0x37e2f0(0x8e9)]?_0x33166a():_0x3122fc[_0x37e2f0(0x44d)](_0x19fc93,_0x424619,_0x307884,_0x424619['length']-_0x307884,_0x48bdc4[_0x37e2f0(0x8d4)]+_0x307884,_0x6766e4);};_0x3122fc[_0x26adca(0x44d)](_0x19fc93,_0x4f75bc,0x0,_0x4f75bc[_0x26adca(0x8e9)],_0x48bdc4[_0x26adca(0x8d4)],_0x6766e4);});});else{if(_0xa6357c)return new Promise(function(_0x5463d7, _0x1cb93c){var _0x2b7146=_0x5c68;_0xa6357c[_0x2b7146(0x4cc)](_0x48bdc4['offset'])[_0x2b7146(0x619)](()=>{var _0x57fd8d=_0x2b7146;_0xa6357c[_0x57fd8d(0x44d)](new Blob([_0x48bdc4['data']]));})[_0x2b7146(0x619)](()=>{_0x5463d7();});});else{if(!_0x26e421)for(let _0x568e76=0x0; _0x568e76<_0x5a8774['length']; _0x568e76++){let _0x58a222=_0x5a8774[_0x568e76];if(!(_0x48bdc4['offset']+_0x48bdc4[_0x5ca49c(0x8e9)]<=_0x58a222[_0x5ca49c(0x8d4)]||_0x48bdc4['offset']>=_0x58a222[_0x5ca49c(0x8d4)]+_0x58a222[_0x5ca49c(0x8e9)])){if(_0x48bdc4[_0x5ca49c(0x8d4)]<_0x58a222[_0x5ca49c(0x8d4)]||_0x48bdc4[_0x5ca49c(0x8d4)]+_0x48bdc4[_0x5ca49c(0x8e9)]>_0x58a222['offset']+_0x58a222['length'])throw new Error(_0x5ca49c(0x968));if(_0x48bdc4[_0x5ca49c(0x8d4)]==_0x58a222['offset']&&_0x48bdc4[_0x5ca49c(0x8e9)]==_0x58a222[_0x5ca49c(0x8e9)]){_0x58a222['data']=_0x48bdc4['data'];return;}else return _0x20dc80(_0x58a222[_0x5ca49c(0x3bc)])['then'](function(_0x1fd8c5){var _0x1fe974=_0x5ca49c;return _0x58a222['data']=_0x1fd8c5,_0x20dc80(_0x48bdc4[_0x1fe974(0x3bc)]);})[_0x5ca49c(0x619)](function(_0x10df0a){var _0x3b68b8=_0x5ca49c;_0x48bdc4[_0x3b68b8(0x3bc)]=_0x10df0a,_0x58a222[_0x3b68b8(0x3bc)]['set'](_0x48bdc4[_0x3b68b8(0x3bc)],_0x48bdc4['offset']-_0x58a222[_0x3b68b8(0x8d4)]);});}}}}_0x5a8774[_0x5ca49c(0x505)](_0x48bdc4);});},this[_0x2f6764(0x73e)]=function(_0x333559){var _0x1c69dd=_0x2f6764;return _0x19fc93||_0xa6357c?_0x33b208=_0x33b208[_0x1c69dd(0x619)](function(){return null;}):_0x33b208=_0x33b208[_0x1c69dd(0x619)](function(){var _0x49eb98=_0x1c69dd;let _0x3d688f=[];for(let _0x587cce=0x0; _0x587cce<_0x5a8774[_0x49eb98(0x8e9)]; _0x587cce++){_0x3d688f['push'](_0x5a8774[_0x587cce][_0x49eb98(0x3bc)]);}return new Blob(_0x3d688f,{'type':_0x333559});}),_0x33b208;};};};window[_0x2d2b95(0x357)]=_0x38128b(null);}()),(function(){'use strict';var _0x4f505d=_0x2de9a7;function _0xf2aa72(_0x16b264){var _0x39719b=_0x5c68;this[_0x39719b(0x8d7)]=_0x16b264;}function _0x18bc03(_0x1770c7, _0xec7cdc){var _0x8eb305=_0x5c68;let _0x1d8bfe={};return[_0x1770c7,_0xec7cdc][_0x8eb305(0x982)](function(_0x5b7114){var _0x5179cb=_0x8eb305;for(let _0x1ac084 in _0x5b7114){Object['prototype'][_0x5179cb(0x92a)]['call'](_0x5b7114,_0x1ac084)&&(_0x1d8bfe[_0x1ac084]=_0x5b7114[_0x1ac084]);}}),_0x1d8bfe;}function _0x3a6d8a(_0x4c5dde, _0x2743d7, _0x38ca1a){var _0x28b834=_0x5c68;if(Array[_0x28b834(0x20a)](_0x38ca1a))for(let _0x14e401=0x0; _0x14e401<_0x38ca1a['length']; _0x14e401++){_0x3a6d8a(_0x4c5dde,_0x2743d7,_0x38ca1a[_0x14e401]);}else{if(typeof _0x38ca1a==='string')_0x4c5dde[_0x28b834(0x35d)](_0x38ca1a);else{if(_0x38ca1a instanceof Uint8Array)_0x4c5dde[_0x28b834(0x67c)](_0x38ca1a);else{if(_0x38ca1a['id']){_0x38ca1a[_0x28b834(0x8d4)]=_0x4c5dde['pos']+_0x2743d7,_0x4c5dde[_0x28b834(0x796)](_0x38ca1a['id']);if(Array[_0x28b834(0x20a)](_0x38ca1a[_0x28b834(0x3bc)])){let _0x5cfdbb,_0x4711b0,_0x1f9fcb;_0x38ca1a[_0x28b834(0x8cf)]===-0x1?_0x4c5dde[_0x28b834(0x1c7)](0xff):(_0x5cfdbb=_0x4c5dde[_0x28b834(0x680)],_0x4c5dde['writeBytes']([0x0,0x0,0x0,0x0])),_0x4711b0=_0x4c5dde[_0x28b834(0x680)],_0x38ca1a[_0x28b834(0x406)]=_0x4711b0+_0x2743d7,_0x3a6d8a(_0x4c5dde,_0x2743d7,_0x38ca1a[_0x28b834(0x3bc)]),_0x38ca1a[_0x28b834(0x8cf)]!==-0x1&&(_0x1f9fcb=_0x4c5dde['pos'],_0x38ca1a[_0x28b834(0x8cf)]=_0x1f9fcb-_0x4711b0,_0x4c5dde[_0x28b834(0x4cc)](_0x5cfdbb),_0x4c5dde[_0x28b834(0x4c8)](_0x38ca1a[_0x28b834(0x8cf)],0x4),_0x4c5dde[_0x28b834(0x4cc)](_0x1f9fcb));}else{if(typeof _0x38ca1a[_0x28b834(0x3bc)]===_0x28b834(0xa46))_0x4c5dde[_0x28b834(0x72e)](_0x38ca1a[_0x28b834(0x3bc)][_0x28b834(0x8e9)]),_0x38ca1a[_0x28b834(0x406)]=_0x4c5dde[_0x28b834(0x680)]+_0x2743d7,_0x4c5dde[_0x28b834(0x35d)](_0x38ca1a[_0x28b834(0x3bc)]);else{if(typeof _0x38ca1a[_0x28b834(0x3bc)]===_0x28b834(0x570))!_0x38ca1a['size']&&(_0x38ca1a[_0x28b834(0x8cf)]=_0x4c5dde[_0x28b834(0x97d)](_0x38ca1a[_0x28b834(0x3bc)])),_0x4c5dde[_0x28b834(0x72e)](_0x38ca1a[_0x28b834(0x8cf)]),_0x38ca1a[_0x28b834(0x406)]=_0x4c5dde['pos']+_0x2743d7,_0x4c5dde[_0x28b834(0x796)](_0x38ca1a[_0x28b834(0x3bc)],_0x38ca1a['size']);else{if(_0x38ca1a[_0x28b834(0x3bc)]instanceof _0xf2aa72)_0x4c5dde['writeEBMLVarInt'](0x8),_0x38ca1a[_0x28b834(0x406)]=_0x4c5dde['pos']+_0x2743d7,_0x4c5dde[_0x28b834(0x218)](_0x38ca1a[_0x28b834(0x3bc)][_0x28b834(0x8d7)]);else{if(_0x38ca1a[_0x28b834(0x3bc)]instanceof _0xf2aa72)_0x4c5dde[_0x28b834(0x72e)](0x4),_0x38ca1a[_0x28b834(0x406)]=_0x4c5dde[_0x28b834(0x680)]+_0x2743d7,_0x4c5dde[_0x28b834(0x5fa)](_0x38ca1a[_0x28b834(0x3bc)][_0x28b834(0x8d7)]);else{if(_0x38ca1a[_0x28b834(0x3bc)]instanceof Uint8Array)_0x4c5dde['writeEBMLVarInt'](_0x38ca1a[_0x28b834(0x3bc)][_0x28b834(0x7ce)]),_0x38ca1a[_0x28b834(0x406)]=_0x4c5dde[_0x28b834(0x680)]+_0x2743d7,_0x4c5dde[_0x28b834(0x67c)](_0x38ca1a[_0x28b834(0x3bc)]);else throw new Error(_0x28b834(0x9e6)+typeof _0x38ca1a[_0x28b834(0x3bc)]);}}}}}}else throw new Error(_0x28b834(0x9e6)+typeof _0x38ca1a[_0x28b834(0x3bc)]);}}}}let _0x2e4729=function(_0x1ee306, _0x17b493){return function(_0x1d5998){var _0x52dcb5=_0x5c68;let _0x2fc561=0x1388,_0x525486=![],_0x57575c=0x0,_0x88da70=0x0,_0x3cb890=!![],_0x58d674=0x0,_0x110b63=0xbb80,_0x1ac8c3=0x1,_0x108e76=[],_0x1e33ba=0x0,_0x42297d=0x0,_0x13134f=0x0,_0x5f4351={'fileWriter':null,'codec':_0x52dcb5(0x3bd)},_0x31c2da,_0x470d4b={'id':0x4489,'data':new _0xf2aa72(0x0)},_0x418cb6=new _0x17b493(_0x1d5998[_0x52dcb5(0x1bb)]);function _0x11a929(_0x28c46d, _0x359e4c){var _0x3359fd=_0x52dcb5;return _0x359e4c=new Uint8Array(_0x359e4c),_0x3ae9c0(_0x5ad0ff(_0x28c46d),_0x590e8f(_0x359e4c[_0x3359fd(0x7ce)]),_0x359e4c);}function _0x3ae9c0(){var _0x1c8ee3=_0x52dcb5,_0x397621,_0xca6cda=0x0,_0xb48277;for(_0x397621=0x0; _0x397621>>0x18&0xff,_0x218160>>>0x10&0xff,_0x218160>>>0x8&0xff,_0x218160&0xff]);if((_0x218160&0xff0000)!=0x0)return new Uint8Array([_0x218160>>>0x10&0xff,_0x218160>>>0x8&0xff,_0x218160&0xff]);if((_0x218160&0xff00)!=0x0)return new Uint8Array([_0x218160>>>0x8&0xff,_0x218160&0xff]);if((_0x218160&0xff)!=0x0)return new Uint8Array([_0x218160&0xff]);throw'InvalidOperationException';}function _0x590e8f(_0x19fe3e){if(_0x19fe3e<=0x7f)return new Uint8Array([0x80|_0x19fe3e&0x7f]);if(_0x19fe3e<=0x3fff)return new Uint8Array([0x40|_0x19fe3e>>0x8&0x3f,_0x19fe3e&0xff]);return new Uint8Array([0x8,_0x19fe3e>>>0x18&0xff,_0x19fe3e>>>0x10&0xff,_0x19fe3e>>>0x8&0xff,_0x19fe3e&0xff]);}function _0x158722(_0x449c8a, _0x56e421){var _0x2c316c=new DataView(new ArrayBuffer(0x4));return _0x2c316c['setFloat32'](0x0,_0x56e421,![]),_0x11a929(_0x449c8a,new Uint8Array(_0x2c316c['buffer']));}function _0x1ae1da(_0x15f2e4){var _0x3a7b37=_0x52dcb5;if(_0x15f2e4<=0xff)return new Uint8Array([_0x15f2e4&0xff]);if(_0x15f2e4<=0xffff)return new Uint8Array([_0x15f2e4>>>0x8&0xff,_0x15f2e4&0xff]);if(_0x15f2e4<=0xffffff)return new Uint8Array([_0x15f2e4>>0x10&0xff,_0x15f2e4>>0x8&0xff,_0x15f2e4&0xff]);return new Uint8Array([_0x15f2e4>>>0x18&0xff,_0x15f2e4>>>0x10&0xff,_0x15f2e4>>>0x8&0xff,_0x15f2e4&0xff]);var _0x348ed0=new DataView(new ArrayBuffer(0x4));return _0x348ed0[_0x3a7b37(0x5c0)](0x0,_0x15f2e4,![]),_0x348ed0;}function _0x912883(_0x2ff424, _0x2e9b16){return _0x11a929(_0x2ff424,_0x1ae1da(_0x2e9b16));}function _0x534564(_0x1fdf09, _0x16523b){var _0x5132de=_0x52dcb5;return _0x11a929(_0x1fdf09,new TextEncoder()[_0x5132de(0x9d3)](_0x16523b));}function _0x7556e5(){var _0x4ad562=_0x52dcb5;let _0x7c4ecc={'id':0x1a45dfa3,'data':[_0x912883(0x4286,0x1),_0x912883(0x42f7,0x1),_0x912883(0x42f2,0x4),_0x912883(0x42f3,0x8),_0x534564(0x4282,'webm'),_0x912883(0x4287,0x4),_0x912883(0x4285,0x2)]},_0x2a9066={'id':0x1549a966,'data':[_0x912883(0x2ad7b1,0xf4240),_0x534564(0x4d80,'VDO-Ninja'),_0x534564(0x5741,_0x4ad562(0x533)),_0x470d4b]},_0x2e95a7=[{'id':0xb0,'data':_0x57575c},{'id':0xba,'data':_0x88da70}],_0x5d4d61={'id':0x1654ae6b,'data':[{'id':0xae,'data':[_0x912883(0xd7,0x1),_0x912883(0x73c5,0x1),_0x912883(0x9c,0x0),_0x534564(0x22b59c,_0x4ad562(0x2e5)),_0x534564(0x86,'V_'+_0x1d5998[_0x4ad562(0x24e)]),_0x912883(0x83,0x1),{'id':0xe0,'data':[_0x912883(0xb0,_0x57575c),_0x912883(0xba,_0x88da70)]}]},{'id':0xae,'data':[_0x912883(0xd7,0x2),_0x912883(0x73c5,0x2),_0x912883(0x9c,0x0),_0x534564(0x22b59c,_0x4ad562(0x2e5)),_0x534564(0x86,_0x4ad562(0x7aa)),_0x912883(0x83,0x2),{'id':0xe1,'data':[_0x158722(0xb5,_0x110b63),_0x912883(0x9f,_0x1ac8c3)]},_0x11a929(0x63a2,new Uint8Array(['O'[_0x4ad562(0x8d1)](0x0),'p'['charCodeAt'](0x0),'u'[_0x4ad562(0x8d1)](0x0),'s'['charCodeAt'](0x0),'H'[_0x4ad562(0x8d1)](0x0),'e'[_0x4ad562(0x8d1)](0x0),'a'[_0x4ad562(0x8d1)](0x0),'d'['charCodeAt'](0x0),0x1,_0x1ac8c3&0xff,0x38,0x1,_0x110b63>>>0x0&0xff,_0x110b63>>>0x8&0xff,_0x110b63>>>0x10&0xff,_0x110b63>>>0x18&0xff,0x0,0x0,0x0]))]}]};_0x31c2da={'id':0x18538067,'size':-0x1,'data':[_0x2a9066,_0x5d4d61]};let _0x4f45f9=new _0x1ee306(0x200);_0x3a6d8a(_0x4f45f9,_0x418cb6[_0x4ad562(0x680)],[_0x7c4ecc,_0x31c2da]),_0x418cb6[_0x4ad562(0x44d)](_0x4f45f9[_0x4ad562(0x959)]()),_0x525486=!![];}function _0x3e12c2(_0x5f541d){var _0x383df7=_0x52dcb5;let _0x25e53f=new _0x1ee306(0x1+0x2+0x1);if(!(_0x5f541d['trackNumber']>0x0&&_0x5f541d[_0x383df7(0x3f7)]<0x7f))throw new Error(_0x383df7(0x1ca));return _0x25e53f[_0x383df7(0x72e)](_0x5f541d[_0x383df7(0x3f7)]),_0x25e53f['writeU16BE'](_0x5f541d[_0x383df7(0x1dd)]),_0x25e53f['writeByte']((_0x5f541d['type']==_0x383df7(0xa68)?0x1:0x0)<<0x7),{'id':0xa3,'data':[_0x25e53f[_0x383df7(0x959)](),_0x5f541d['frame']]};}function _0x596b02(_0x299752){var _0x2eb2bd=_0x52dcb5;return{'id':0x1f43b675,'data':[{'id':0xe7,'data':Math[_0x2eb2bd(0x2fb)](_0x299752[_0x2eb2bd(0x1dd)])}]};}function _0x5bca44(){var _0x57d17b=_0x52dcb5;if(_0x108e76['length']===0x0)return;let _0x3ff042=0x0;for(let _0x8f51c3=0x0; _0x8f51c3<_0x108e76[_0x57d17b(0x8e9)]; _0x8f51c3++){_0x3ff042+=_0x108e76[_0x8f51c3][_0x57d17b(0x5bd)][_0x57d17b(0x7ce)];}let _0x20a89c=new _0x1ee306(_0x3ff042+_0x108e76[_0x57d17b(0x8e9)]*0x40),_0x97a5b2=_0x596b02({'timecode':Math[_0x57d17b(0x2fb)](_0x1e33ba)});for(let _0x2ceef1=0x0; _0x2ceef1<_0x108e76[_0x57d17b(0x8e9)]; _0x2ceef1++){_0x97a5b2[_0x57d17b(0x3bc)][_0x57d17b(0x505)](_0x3e12c2(_0x108e76[_0x2ceef1]));}_0x3a6d8a(_0x20a89c,_0x418cb6['pos'],_0x97a5b2),_0x418cb6['write'](_0x20a89c[_0x57d17b(0x959)]()),_0x108e76=[],_0x42297d=0x0;}function _0xee3eab(_0x494fab, _0x484c9f){var _0x47a16e=_0x52dcb5;_0x494fab['trackNumber']=_0x484c9f;var _0x466d0f=_0x494fab[_0x47a16e(0x561)]/0x3e8;_0x3cb890?(_0x58d674=_0x466d0f,_0x466d0f=0x0,_0x3cb890=![]):_0x466d0f=_0x466d0f-_0x58d674;_0x13134f=_0x466d0f;if(_0x42297d==0x0)_0x1e33ba=_0x466d0f;_0x494fab[_0x47a16e(0x1dd)]=Math[_0x47a16e(0x2fb)](_0x466d0f-_0x1e33ba),_0x108e76[_0x47a16e(0x505)](_0x494fab),_0x42297d=_0x494fab['timecode']+0x1,_0x42297d>=_0x2fc561&&_0x5bca44();}function _0x1e47e4(){var _0x4e31f3=_0x52dcb5;let _0x3b1a94=new _0x1ee306(seekHead[_0x4e31f3(0x8cf)]),_0x4aa5e1=_0x418cb6[_0x4e31f3(0x680)];_0x3a6d8a(_0x3b1a94,seekHead[_0x4e31f3(0x406)],seekHead[_0x4e31f3(0x3bc)]),_0x418cb6[_0x4e31f3(0x4cc)](seekHead[_0x4e31f3(0x406)]),_0x418cb6[_0x4e31f3(0x44d)](_0x3b1a94[_0x4e31f3(0x959)]()),_0x418cb6[_0x4e31f3(0x4cc)](_0x4aa5e1);}function _0x212052(){var _0xeba8f3=_0x52dcb5;let _0x15a01e=new _0x1ee306(0x8),_0x110e16=_0x418cb6[_0xeba8f3(0x680)];_0x15a01e[_0xeba8f3(0x218)](_0x13134f),_0x418cb6[_0xeba8f3(0x4cc)](_0x470d4b['dataOffset']),_0x418cb6[_0xeba8f3(0x44d)](_0x15a01e['getAsDataArray']()),_0x418cb6[_0xeba8f3(0x4cc)](_0x110e16);}this[_0x52dcb5(0x641)]=function(_0x115a3b){var _0x2654ae=_0x52dcb5;!_0x525486&&(_0x57575c=_0x1d5998[_0x2654ae(0x530)],_0x88da70=_0x1d5998[_0x2654ae(0x5ec)],_0x110b63=_0x1d5998[_0x2654ae(0x690)],_0x1ac8c3=_0x1d5998[_0x2654ae(0x306)],_0x7556e5());if(_0x115a3b['constructor'][_0x2654ae(0x6dd)]==_0x2654ae(0x17c)){let _0x584695=new Uint8Array(_0x115a3b[_0x2654ae(0x7ce)]);_0x115a3b['copyTo'](_0x584695),_0xee3eab({'frame':_0x584695,'intime':_0x115a3b[_0x2654ae(0x82c)],'type':_0x115a3b[_0x2654ae(0x78a)]},0x1);return;}else{if(_0x115a3b[_0x2654ae(0x611)][_0x2654ae(0x6dd)]=='EncodedAudioChunk'){let _0x2a8539=new Uint8Array(_0x115a3b[_0x2654ae(0x7ce)]);_0x115a3b[_0x2654ae(0x423)](_0x2a8539),_0xee3eab({'frame':_0x2a8539,'intime':_0x115a3b['timestamp'],'type':_0x115a3b['type']},0x2);return;}}},this[_0x52dcb5(0x73e)]=function(){var _0x3242cf=_0x52dcb5;return!_0x525486&&_0x7556e5(),_0x3cb890=!![],_0x5bca44(),_0x212052(),_0x418cb6[_0x3242cf(0x73e)](_0x3242cf(0x315));},this[_0x52dcb5(0x606)]=function(){var _0x10fe16=_0x52dcb5;return _0x418cb6[_0x10fe16(0x8e9)];},_0x1d5998=_0x18bc03(_0x5f4351,_0x1d5998||{});};};window[_0x4f505d(0x460)]=_0x2e4729(window[_0x4f505d(0x2ec)],window[_0x4f505d(0x357)]);}()); \ No newline at end of file diff --git a/whip.html b/app/static/whip.html similarity index 99% rename from whip.html rename to app/static/whip.html index 049999e..25cd20a 100644 --- a/whip.html +++ b/app/static/whip.html @@ -2,7 +2,7 @@ - + - - - -
        -
        - -
        -
        - -
        -
        - - - - - - - - - - - - - - + + + + + + +
        +
        + +
        +
        + +
        +
        + + + + + + + + + + + + + +