mirror of
https://github.com/eliasstepanik/vdo.ninja.git
synced 2026-01-11 13:48:38 +00:00
pre-checkin before linds commit
This commit is contained in:
parent
0b2fbf7462
commit
a1747fd7e4
@ -83,7 +83,7 @@
|
||||
|
||||
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/CodecsHandler.js?ver=45"></script>
|
||||
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/aes.js"></script>
|
||||
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=572"></script>
|
||||
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=573"></script>
|
||||
<input id="zoomSlider" type="range" style="display: none;" />
|
||||
<span id="electronDragZone" style="pointer-events: none; z-index:-10; position:absolute;top:0;left:0;width:100%;height:2%;-webkit-app-region: drag;min-height:20px;"></span>
|
||||
<div id="header">
|
||||
@ -2288,7 +2288,7 @@
|
||||
|
||||
|
||||
var session = WebRTC.Media; // session is a required global variable if configuring manually. Run before loading main.js but after webrtc.js.
|
||||
session.version = "23.0b";
|
||||
session.version = "23.1b";
|
||||
session.streamID = session.generateStreamID(); // randomly generates a streamID for this session. You can set your own programmatically if needed
|
||||
|
||||
session.defaultPassword = "someEncryptionKey123"; // Change this password if self-deploying for added security/privacy
|
||||
@ -2400,11 +2400,11 @@
|
||||
// session.defaultBackgroundImages = ["./media/bg_sample1.webp", "./media/bg_sample2.webp"]; // for &effects=5 (virtual backgrounds)
|
||||
// session.hidehome = true; // If used, 'hide home' will make the landing page inaccessible, along with hiding a few go-home elements.
|
||||
</script>
|
||||
<script type="text/javascript" crossorigin="anonymous" id="lib-js" src="./lib.js?ver=639"></script>
|
||||
<script type="text/javascript" crossorigin="anonymous" id="lib-js" src="./lib.js?ver=646"></script>
|
||||
<!--
|
||||
// If you wish to change branding, blank offers a good clean start.
|
||||
<script type="text/javascript" id="main-js" src="./main.js" data-translation="blank"></script>
|
||||
-->
|
||||
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=533"></script>
|
||||
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=536"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
312
lib.js
312
lib.js
@ -10903,56 +10903,62 @@ function getDetailedState(sid=false){
|
||||
item.streamID = session.rpcs[UUID].streamID;
|
||||
item.label = session.rpcs[UUID].label;
|
||||
item.group = session.rpcs[UUID].group;
|
||||
item.videoMuted = session.rpcs[UUID].videoMuted;
|
||||
item.muted = session.rpcs[UUID].remoteMuteState;
|
||||
item.iframeSrc = session.rpcs[UUID].iframeSrc;
|
||||
item.localStream = false;
|
||||
item.muted = session.rpcs[UUID].remoteMuteState;
|
||||
item.videoMuted = session.rpcs[UUID].videoMuted;
|
||||
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 (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;
|
||||
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<scenes.length;i++){
|
||||
if (scenes[i].value==1){
|
||||
sceneState[scenes[i].dataset.scene] = true;
|
||||
} else {
|
||||
sceneState[scenes[i].dataset.scene] = false;
|
||||
}
|
||||
}
|
||||
item.scenes = sceneState;
|
||||
|
||||
var others = getById("container_" + UUID).querySelectorAll('[data-action-type][data--u-u-i-d="'+UUID+'"]');
|
||||
var otherState = {};
|
||||
for (var i=0;i<others.length;i++){
|
||||
if ("scene" in others[i].dataset){continue;}
|
||||
if ("toggle-group" == others[i].dataset.actionType){continue;}
|
||||
if ("value" in others[i]){
|
||||
if (others[i].value!==""){
|
||||
otherState[others[i].dataset.actionType] = others[i].value;
|
||||
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<scenes.length;i++){
|
||||
if (scenes[i].value==1){
|
||||
sceneState[scenes[i].dataset.scene] = true;
|
||||
} else {
|
||||
sceneState[scenes[i].dataset.scene] = false;
|
||||
}
|
||||
}
|
||||
item.scenes = sceneState;
|
||||
var others = getById("container_" + UUID).querySelectorAll('[data-action-type][data--u-u-i-d="'+UUID+'"]');
|
||||
var otherState = {};
|
||||
for (var i=0;i<others.length;i++){
|
||||
if ("scene" in others[i].dataset){continue;}
|
||||
if ("toggle-group" == others[i].dataset.actionType){continue;}
|
||||
if ("value" in others[i]){
|
||||
if (others[i].value!==""){
|
||||
otherState[others[i].dataset.actionType] = others[i].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
item.others = otherState;
|
||||
}
|
||||
item.others = otherState;
|
||||
|
||||
} catch(e){}
|
||||
|
||||
streamList[session.rpcs[UUID].streamID] = item;
|
||||
}
|
||||
}
|
||||
@ -10960,22 +10966,26 @@ function getDetailedState(sid=false){
|
||||
if (sid && (sid!==session.streamID)){return streamList;}
|
||||
|
||||
streamList[session.streamID] = {};
|
||||
var sceneState = {};
|
||||
|
||||
|
||||
try {
|
||||
var scenes = getById("container_director").querySelectorAll('[data-action-type="addToScene"][data-scene]');
|
||||
for (var i=0;i<scenes.length;i++){
|
||||
if (scenes[i].value==1){
|
||||
sceneState[scenes[i].dataset.scene] = true;
|
||||
} else {
|
||||
sceneState[scenes[i].dataset.scene] = false;
|
||||
if (session.director){
|
||||
var sceneState = {};
|
||||
var scenes = getById("container_director").querySelectorAll('[data-action-type="addToScene"][data-scene]');
|
||||
for (var i=0;i<scenes.length;i++){
|
||||
if (scenes[i].value==1){
|
||||
sceneState[scenes[i].dataset.scene] = true;
|
||||
} else {
|
||||
sceneState[scenes[i].dataset.scene] = false;
|
||||
}
|
||||
}
|
||||
streamList[session.streamID].scenes = sceneState;
|
||||
}
|
||||
} catch(e){}
|
||||
streamList[session.streamID].label = session.label;
|
||||
streamList[session.streamID].group = session.group;
|
||||
streamList[session.streamID].groupView = session.groupView;
|
||||
streamList[session.streamID].scenes = sceneState;
|
||||
streamList[session.streamID].scene = session.scene;
|
||||
streamList[session.streamID].streamID = session.streamID;
|
||||
streamList[session.streamID].iframeSrc = session.iframeSrc;
|
||||
streamList[session.streamID].director = session.directorState; //session.director is what you want to be; session.directorState is what you are
|
||||
@ -10984,10 +10994,11 @@ function getDetailedState(sid=false){
|
||||
streamList[session.streamID].seeding = session.seeding;
|
||||
streamList[session.streamID].muted = session.muted;
|
||||
streamList[session.streamID].videoMuted = session.videoMuted;
|
||||
streamList[session.streamID].videoVisible = session.videoElement && session.videoElement.checkVisibility();
|
||||
streamList[session.streamID].speakerMuted = session.speakerMuted;
|
||||
streamList[session.streamID].position = null;
|
||||
|
||||
if (session.showDirector){
|
||||
if (session.showDirector && session.director){
|
||||
var child = document.getElementById('container_director');
|
||||
if (child){
|
||||
var parent = child.parentNode;
|
||||
@ -13420,6 +13431,68 @@ function outboundAudioPipeline(){ // this function isn't letting me change the a
|
||||
webAudio.lowcut2.connect(webAudio.lowcut3);
|
||||
anonNode = webAudio.lowcut3;
|
||||
}
|
||||
|
||||
|
||||
if (session.voicechanger) {
|
||||
|
||||
|
||||
function makeDistortionCurve(amount=10) {
|
||||
var sampleRate = audioContext.sampleRate || 48000;
|
||||
var curve = new Float32Array(sampleRate);
|
||||
var x;
|
||||
for (let i = 0; i < sampleRate; ++i ) {
|
||||
x = i * 2 / sampleRate - 1;
|
||||
curve[i] = ( 3 + amount ) * x * 20 * (Math.PI / 180) / (Math.PI + amount * Math.abs(x));
|
||||
}
|
||||
return curve;
|
||||
}
|
||||
|
||||
let waveShaper = audioContext.createWaveShaper();
|
||||
waveShaper.curve = makeDistortionCurve(5);
|
||||
|
||||
var realCoeffs = new Float32Array([1,0]);
|
||||
var imagCoeffs = new Float32Array([0,1]);
|
||||
|
||||
var numCoeffs = 20; // The more coefficients you use, the better the approximation
|
||||
var realCoeffs = new Float32Array(numCoeffs);
|
||||
var imagCoeffs = new Float32Array(numCoeffs);
|
||||
|
||||
realCoeffs[0] = 0.5;
|
||||
for (var i = 1; i < numCoeffs; i++) { // note i starts at 1
|
||||
imagCoeffs[i] = 1 / (i * Math.PI) * (1 - Math.random()/2);
|
||||
}
|
||||
|
||||
let oscillator = audioContext.createOscillator();
|
||||
oscillator.frequency.value = 10;
|
||||
const wave = audioContext.createPeriodicWave(realCoeffs, imagCoeffs);
|
||||
oscillator.setPeriodicWave(wave);
|
||||
|
||||
let oscillatorGain = audioContext.createGain();
|
||||
oscillatorGain.gain.value = 0.005;
|
||||
oscillator.connect(oscillatorGain);
|
||||
oscillator.start(0);
|
||||
|
||||
let delay = audioContext.createDelay();
|
||||
delay.delayTime.value = 0.01;
|
||||
oscillatorGain.connect(delay.delayTime);
|
||||
|
||||
let lowEQ = audioContext.createBiquadFilter();
|
||||
lowEQ.type = "peaking";
|
||||
lowEQ.frequency.value = 200;
|
||||
lowEQ.Q.value = 0.5;
|
||||
lowEQ.gain.value = 6;
|
||||
|
||||
let mid = audioContext.createBiquadFilter();
|
||||
mid.type = "peaking";
|
||||
mid.frequency.value = 500;
|
||||
mid.Q.value = 0.5;
|
||||
mid.gain.value = -10;
|
||||
anonNode.connect(delay)
|
||||
delay.connect(waveShaper)
|
||||
waveShaper.connect(mid);
|
||||
mid.connect(lowEQ);
|
||||
anonNode = lowEQ;
|
||||
}
|
||||
|
||||
|
||||
if (session.equalizer) { // https://webaudioapi.com/samples/frequency-response/ for a tool to help set values
|
||||
@ -15919,7 +15992,7 @@ function createControlBox(UUID, soloLink, streamID) {
|
||||
if (value<100){
|
||||
session.rpcs[UUID].batteryMeter.classList.remove("hidden");
|
||||
}
|
||||
session.rpcs[UUID].batteryMeter.title = value+"% battery remaining";
|
||||
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)){
|
||||
@ -30807,6 +30880,8 @@ function audioMeterGuest(mediaStreamSource, UUID, trackid){
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
try{
|
||||
clearTimeout(session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.interval);
|
||||
session.rpcs[UUID].inboundAudioPipeline[trackid].analyser.interval = setTimeout(function(){updateLevels();},100);
|
||||
@ -30815,12 +30890,17 @@ function audioMeterGuest(mediaStreamSource, UUID, trackid){
|
||||
}
|
||||
|
||||
if (session.style==3 || session.meterStyle){ // overrides style
|
||||
// continue
|
||||
if (session.meterStyle==4){
|
||||
if (session.rpcs[UUID].videoElement){
|
||||
session.rpcs[UUID].videoElement.dataset.loudness = total;
|
||||
}
|
||||
return; // this is cause we are using the data-loudness
|
||||
}
|
||||
} 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;
|
||||
@ -31061,6 +31141,144 @@ async function loadScript(url, callback=false){
|
||||
return await promise;
|
||||
}
|
||||
|
||||
var tokenClient=false;
|
||||
function YoutubeChatInterface(){ // 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) {
|
||||
warnUser("<button onclick='(function(){tokenClient.requestAccessToken({prompt: \"consent\"});})()'>Grant Access to Youtube Chat</button>", false, false);
|
||||
} else {
|
||||
warnUser("<button onclick='(function(){this.remove();tokenClient.requestAccessToken({prompt: \"\"});})()'>Grant Access to Youtube Chat</button>", 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;
|
||||
|
||||
7
main.css
7
main.css
@ -592,7 +592,9 @@ hr {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#gridlayout{
|
||||
z-index:-1;
|
||||
}
|
||||
.invite_setting_group {
|
||||
margin: 20px 0px;
|
||||
background-color: #d2d2d2;
|
||||
@ -943,6 +945,9 @@ button.glyphicon-button.active.focus {
|
||||
background-attachment: fixed;
|
||||
background-position: center;
|
||||
overflow-x: hidden;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
32
main.js
32
main.js
@ -246,7 +246,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
}
|
||||
|
||||
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') || false;
|
||||
var avatarImg = urlParams.get('avatarimg') || urlParams.get('bgimage') || urlParams.get('bgimg') || "./media/avatar.webp";
|
||||
if (avatarImg){
|
||||
try {
|
||||
avatarImg = decodeURIComponent(avatarImg);
|
||||
@ -256,7 +256,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
document.documentElement.style.setProperty('--video-background-image', avatarImg);
|
||||
} 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;
|
||||
@ -2364,13 +2364,21 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
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;
|
||||
} else if (urlParams.has('cbr')) {
|
||||
@ -2990,7 +2998,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
log(session.frameRate);
|
||||
}
|
||||
|
||||
if (urlParams.has('tz')){
|
||||
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;
|
||||
@ -3477,7 +3485,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
}
|
||||
|
||||
if (urlParams.has('privacy') || urlParams.has('private') || urlParams.has('relay')) { // please only use if you are also using your own TURN service.
|
||||
session.privacy = true;
|
||||
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
|
||||
@ -4219,7 +4227,11 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
// 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
|
||||
warnlog(e);
|
||||
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;
|
||||
@ -4453,6 +4465,14 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
YoutubeChatInterface();
|
||||
}
|
||||
|
||||
if ("nextSlide" in e.data){ // panning adjusts the stereo pan , although current its UUID based. can add stream ID based if requested.
|
||||
nextSlide();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user