mirror of
https://github.com/SrIzan10/vdo.ninja.git
synced 2026-05-01 11:05:24 +00:00
410 lines
10 KiB
HTML
410 lines
10 KiB
HTML
<html>
|
|
<head><title>Twitch + Video</title>
|
|
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0,user-scalable=no" />
|
|
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
|
|
<style>
|
|
body {
|
|
padding: 0;
|
|
margin: 0;
|
|
background-color: #000;
|
|
width: 100vw;
|
|
height: 100vh;
|
|
color: white;
|
|
overscroll-behavior: contain;
|
|
overflow: hidden;
|
|
display: block;
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
}
|
|
|
|
iframe {
|
|
width: 100%;
|
|
height: 100%;
|
|
border: 0;
|
|
margin: 0;
|
|
padding: 0;
|
|
display: block;
|
|
}
|
|
|
|
input {
|
|
padding: 12px 16px;
|
|
width: 100%;
|
|
font-size: clamp(16px, 4vw, 20px);
|
|
margin: 12px 0;
|
|
z-index: 1000;
|
|
color: black;
|
|
display: block;
|
|
border: 2px solid #333;
|
|
border-radius: 8px;
|
|
background: #fff;
|
|
transition: border-color 0.2s;
|
|
}
|
|
|
|
input:focus {
|
|
outline: none;
|
|
border-color: #666;
|
|
}
|
|
|
|
#clean {
|
|
max-width: 800px;
|
|
width: 90vw;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
}
|
|
|
|
h1 {
|
|
color: white;
|
|
font-size: clamp(24px, 6vw, 32px);
|
|
margin: 16px 0 24px 0;
|
|
line-height: 1.2;
|
|
}
|
|
|
|
button {
|
|
font-size: clamp(16px, 4vw, 24px);
|
|
color: black;
|
|
display: inline-block;
|
|
padding: 12px 24px;
|
|
border: none;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
transition: transform 0.1s, opacity 0.1s;
|
|
}
|
|
|
|
button:active {
|
|
transform: scale(0.98);
|
|
opacity: 0.9;
|
|
}
|
|
|
|
#controlbar {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
background-color: rgba(0, 0, 0, 0.8);
|
|
backdrop-filter: blur(8px);
|
|
height: max(7vh, 50px);
|
|
width: 100%;
|
|
display: none;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
padding: 4px;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
#controlbar button {
|
|
height: calc(100% - 8px);
|
|
background-color: #222;
|
|
color: white;
|
|
box-shadow: none;
|
|
font-size: 14px;
|
|
border-radius: 6px;
|
|
padding: 0 12px;
|
|
white-space: nowrap;
|
|
border: 1px solid #444;
|
|
}
|
|
|
|
.pressed {
|
|
background-color: #800 !important;
|
|
}
|
|
|
|
.loading {
|
|
width: 100%;
|
|
height: 100%;
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
opacity: 1;
|
|
animation: fadeIn 1s ease-out;
|
|
}
|
|
|
|
.fullwindow {
|
|
height: calc(100% - max(7vh, 50px)) !important;
|
|
width: 100% !important;
|
|
position: absolute;
|
|
top: max(7vh, 50px) !important;
|
|
left: 0 !important;
|
|
}
|
|
|
|
@keyframes fadeIn {
|
|
from { opacity: 0; }
|
|
to { opacity: 1; }
|
|
}
|
|
|
|
@media screen and (orientation:portrait) {
|
|
#container2 {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: none;
|
|
}
|
|
#container1 {
|
|
width: 50vw;
|
|
height: 50vh;
|
|
display: none;
|
|
top: 0;
|
|
right: 0;
|
|
position: absolute;
|
|
}
|
|
#controlbar {
|
|
flex-wrap: wrap;
|
|
height: auto;
|
|
padding: 8px;
|
|
}
|
|
#controlbar button {
|
|
flex: 1 1 auto;
|
|
min-width: 80px;
|
|
}
|
|
}
|
|
|
|
@media screen and (orientation:landscape) {
|
|
#container2 {
|
|
width: 60vw;
|
|
height: 100%;
|
|
display: none;
|
|
z-index: 5;
|
|
}
|
|
#container1 {
|
|
width: 50vw;
|
|
height: 100%;
|
|
max-height: calc(100vh - 100px);
|
|
display: none;
|
|
position: fixed;
|
|
top: 0;
|
|
right: -10vw;
|
|
}
|
|
}
|
|
|
|
.hide {
|
|
width: 1px !important;
|
|
height: 1px !important;
|
|
opacity: 0.5;
|
|
transition: all 0.3s ease;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="container2" ></div>
|
|
<div id="container1" class="loading"></div>
|
|
<div id="controlbar" >
|
|
<button id="hidepreview">Hide Preview</button>
|
|
<button id="mutemic">Mute Mic</button>
|
|
<button id="mutevideo">Mute Video</button>
|
|
<button id="togglesettings">Settings</button>
|
|
<button id="hangup">Hangup</button>
|
|
</div>
|
|
<div id="clean">
|
|
<h1>Use VDO.Ninja and Twitch chat at the same time</h1>
|
|
VDO.Ninja Stream ID or URL:
|
|
<input placeholder="Enter a VDON stream ID or VDON URL" id="viewlink" type="text" />
|
|
Twitch Username or URL:
|
|
<input placeholder="Enter the Twitch channel name" id="twitch" type="text" />
|
|
<button onclick="loadIframes()" style="background-color: #d1fed1;; padding:10px;margin:10px;">START</button>
|
|
<button onclick="clearInput()" style="background-color: #f4cccc;margin:10px 0px 10px 10vh;padding:10px;">CLEAR</button>
|
|
<br /><br /><br />
|
|
<p>
|
|
This app lets you publish video/audio via VDO.Ninja at the same time as viewing your Twitch chat.<br /><br />If you have feature requests or suggestions, please report them at https://discord.vdo.ninja in the #feature-request channel.
|
|
</p>
|
|
</div>
|
|
<script>
|
|
|
|
window.addEventListener("orientationchange", function() {
|
|
// Announce the new orientation number
|
|
// alert(window.orientation);
|
|
}, false);
|
|
|
|
function removeStorage(cname){
|
|
localStorage.removeItem(cname);
|
|
}
|
|
|
|
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;
|
|
}
|
|
if (getStorage("twitchChatLink")){
|
|
document.getElementById("twitch").value = getStorage("twitchChatLink");
|
|
}
|
|
if (getStorage("vdoNinjaTwitchURL")){
|
|
document.getElementById("viewlink").value = getStorage("vdoNinjaTwitchURL");
|
|
}
|
|
function clearInput(){
|
|
var confirmit = confirm("Are you sure you want to clear the input fields and local storage?");
|
|
if (confirmit){
|
|
removeStorage("twitchChatLink");
|
|
removeStorage("vdoNinjaTwitchURL");
|
|
document.getElementById("viewlink").value = "";
|
|
document.getElementById("twitch").value = "";
|
|
}
|
|
}
|
|
|
|
var iframe = null;
|
|
function sendSelfCommand(action, value=null){
|
|
iframe.contentWindow.postMessage({"target":null, "action":action, "value":value}, '*');
|
|
}
|
|
|
|
var injectCSS = `
|
|
#controlButtons{
|
|
display:none!important;
|
|
}
|
|
`;
|
|
|
|
injectCSS = encodeURIComponent(btoa(injectCSS));
|
|
|
|
function loadIframes(url=false){
|
|
|
|
var roomname = document.getElementById("viewlink").value;
|
|
var twitch = document.getElementById("twitch").value;
|
|
|
|
document.getElementById("clean").parentNode.removeChild(document.getElementById("clean"));
|
|
document.getElementById("container1").style.display="inline-block";
|
|
document.getElementById("container2").style.display="inline-block";
|
|
|
|
|
|
var path = window.location.host+window.location.pathname.split("/").slice(0,-1).join("/");
|
|
path = path.replace("/examples","");
|
|
|
|
if (roomname.startsWith("https://")){
|
|
var room1 = roomname;
|
|
} else {
|
|
var room1 = "https://"+path+"/?push="+roomname+"&webcam&autostart&vd=front&ad=1&transparent&noheader&fullscreen&cleanish&b64css="+injectCSS;
|
|
}
|
|
|
|
var room2 = twitch.startsWith("https://") ? twitch : `https://www.twitch.tv/embed/${twitch}/chat?darkpopout&parent=${location.hostname}`;
|
|
|
|
iframe = document.createElement("iframe");
|
|
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
|
|
iframe.src = room1;
|
|
|
|
document.getElementById("container1").appendChild(iframe);
|
|
|
|
//////////// LISTEN FOR EVENTS
|
|
|
|
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
|
|
var eventer = window[eventMethod];
|
|
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
|
|
|
|
|
|
/// If you have a routing system setup, you could have just one global listener for all iframes instead.
|
|
|
|
eventer(messageEvent, function (e) {
|
|
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
|
|
console.warn(e.data);
|
|
|
|
if ("action" in e.data){
|
|
if (e.data.action === "seeding-started"){
|
|
document.getElementById("controlbar").style.display="inline-flex";
|
|
document.getElementById("container1").classList.remove("loading");
|
|
}
|
|
|
|
if (e.data.action === "settings-menu-state"){
|
|
if (e.data.value==true){
|
|
togglesettings.dataset.value = "true";
|
|
togglesettings.classList.add("pressed");
|
|
document.getElementById("container1").classList.add("fullwindow");
|
|
} else {
|
|
togglesettings.dataset.value = "false";
|
|
togglesettings.classList.remove("pressed");
|
|
document.getElementById("container1").classList.remove("fullwindow");
|
|
}
|
|
}
|
|
|
|
}
|
|
});
|
|
|
|
|
|
setStorage("twitchChatLink", room2);
|
|
|
|
setStorage("vdoNinjaTwitchURL", room1);
|
|
|
|
|
|
setTimeout(function(){
|
|
var iframe2 = document.createElement("iframe");
|
|
iframe2.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
|
|
iframe2.src = room2;
|
|
document.getElementById("container2").appendChild(iframe2);
|
|
},1000);
|
|
|
|
}
|
|
|
|
var hangup = document.getElementById("hangup");
|
|
hangup.onclick = function(){
|
|
iframe.contentWindow.postMessage({"hangup":true}, '*');
|
|
}
|
|
|
|
var togglesettings = document.getElementById("togglesettings");
|
|
togglesettings.onclick = function(){
|
|
iframe.contentWindow.postMessage({"toggleSettings":"toggle"}, '*');
|
|
}
|
|
|
|
var mutemic = document.getElementById("mutemic");
|
|
mutemic.onclick = function(){
|
|
if (this.dataset.value!=="false"){
|
|
this.dataset.value = "false";
|
|
this.classList.add("pressed");
|
|
this.innerText = "Un-Mute Mic";
|
|
sendSelfCommand("mic",false);
|
|
} else {
|
|
this.classList.remove("pressed");
|
|
this.innerText = "Mute Mic";
|
|
this.dataset.value = "true";
|
|
sendSelfCommand("mic",true);
|
|
}
|
|
}
|
|
|
|
var mutevideo = document.getElementById("mutevideo");
|
|
mutevideo.onclick = function(){
|
|
if (this.dataset.value!=="false"){
|
|
this.dataset.value = "false";
|
|
this.classList.add("pressed");
|
|
this.innerText = "Un-Mute camera";
|
|
sendSelfCommand("camera",false);
|
|
} else {
|
|
this.classList.remove("pressed");
|
|
this.innerText = "Mute Camera";
|
|
this.dataset.value = "true";
|
|
sendSelfCommand("camera",true);
|
|
}
|
|
}
|
|
|
|
var hidepreview = document.getElementById("hidepreview");
|
|
hidepreview.onclick = function(){
|
|
if (this.dataset.value!=="false"){
|
|
this.dataset.value = "false";
|
|
this.classList.add("pressed");
|
|
this.innerText = "Show Preview";
|
|
document.getElementById("container1").classList.add("hide");
|
|
document.getElementById("container2").classList.add("fullwindow");
|
|
} else {
|
|
this.classList.remove("pressed");
|
|
this.innerText = "Hide Preview";
|
|
this.dataset.value = "true";
|
|
document.getElementById("container1").classList.remove("hide");
|
|
document.getElementById("container2").classList.remove("fullwindow");
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|
|
</body>
|
|
</html> |