Commit b631df01 authored by Simon Kirsten's avatar Simon Kirsten
Browse files

Major improvements to the website - now simpler, more portable and has chat

parent b354df9c
......@@ -10,177 +10,217 @@ import (
var website = []byte(`
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Stream TV</title>
<!-- Twitch embed -->
<script src="https://player.twitch.tv/js/embed/v1.js"></script>
<style>
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
font-family: Roboto, Helvetica, Arial, sans-serif;
color: white;
background-color: black;
overflow: hidden;
}
iframe {
width: 100%;
height: 100%;
pointer-events: none;
}
#small-player iframe {
position: absolute;
left: 0;
top: 0;
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Stream TV</title>
<script src="https://player.twitch.tv/js/embed/v1.js"></script> <!-- twitch embed -->
<script src="https://unpkg.com/whatwg-fetch@3.0.0/dist/fetch.umd.js"></script> <!-- fetch polyfill -->
<style>
body {
display: flex;
margin: 0;
padding: 0;
height: 100vh;
width: calc(100vw + 21.25rem); /* without chat the body is 21.25rem (the size of the chat) wider */
background-color: black;
overflow: hidden;
transition: width 0.3s; /* width changes are animated */
}
body.with-chat {
width: 100vw; /* with chat the width is the normal 100vw */
}
#large-player {
position: relative; /* while this 'relative' has no impact for #large-player it is the reference point for the #small-player-container which is positioned absolute to this element */
display: flex;
flex-grow: 1; /* #large-player fills remaining space while #chat does not */
}
#small-player-container { /* this container is necessary for the #small-player to keep an 16:9 aspect ratio */
position: absolute;
width: 100%;
height: 0;
padding-top: 56.25%; /* this is the trick that keeps the child in a 16:9 ratio - read more here https://www.w3schools.com/howto/howto_css_aspect_ratio.asp */
}
#small-player {
position: absolute;
top: 0; /* pin small player to the top right */
right: 0;
/* Note: the width and height property get set via script */
transition: width 0.3s, height 0.3s; /* width and height changes are animated */
display: flex;
}
#small-player > iframe,
#large-player > iframe { /* the iframes always grow to the parents size */
flex-grow: 1;
pointer-events: none;
}
.hidden > iframe {
display: none;
}
#overlay {
font-family: Roboto, Helvetica, Arial, sans-serif;
color: white;
/* center the content */
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
#overlay.hidden {
display: none;
}
#chat {
width: 21.25rem;
background-color: #0f0e11; /* from twitch */
}
a:link, a:active, a:visited, a:hover {
color: inherit;
}
</style>
</head>
<body>
<div id="large-player">
<div id="small-player-container">
<div id="small-player"></div>
</div>
<div id="overlay">
<h2>Loading...</h2>
<p><a href="https://simons-nzse-2.h-da.io/stream-tv/server">Documentation</a> · <a href="https://simons-nzse-2.h-da.io/stream-tv/server/reference/">Reference</a> · <a href="https://simons-nzse-2.h-da.io/stream-tv/server/contributing/">Contributing</a></p>
</div>
</div>
<iframe id="chat" frameborder="0" scrolling="no"></iframe>
<script>
const large_player = new Twitch.Player('large-player', {
width: "",
height: "",
controls: false,
muted: false
});
const small_player = new Twitch.Player('small-player', {
width: "",
height: "",
controls: false,
muted: true
});
const large_player_elem = document.getElementById('large-player');
const small_player_elem = document.getElementById('small-player');
const overlay_elem = document.getElementById('overlay');
const chat_elem = document.getElementById('chat');
const defaultState = {
large_channel: null,
small_channel: null,
volume: 0.5,
small_scale: 0.3,
show_chat: false
};
let state = defaultState;
function updateState(newState) {
// channel and chat setting only if they change
if (state.large_channel != newState.large_channel) {
large_player.setChannel(newState.large_channel);
large_player.setMuted(false); // TODO: investigate with autoplay
if (newState.large_channel) {
chat_elem.src = 'https://www.twitch.tv/embed/' + newState.large_channel + '/chat?darkpopout';
} else {
chat_elem.src = 'about:blank';
}
}
#large-player {
width: 100%;
height: 100%;
if (state.small_channel != newState.small_channel) {
small_player.setChannel(newState.small_channel);
}
#small-player {
position: absolute;
state = newState;
console.log("State:", state);
display: none;
// the other options are lightweight and can be simply overwritten
width: 100%;
height: 0;
padding-bottom: 56.25%;
large_player.setVolume(state.volume);
transition: transform 300ms;
transform-origin: top right;
transform: scale(0.2) translate(-16%, 16%); /* will be changed by js */
}
const small_player_size = state.small_scale * 100 + '%';
small_player_elem.style.width = small_player_size;
small_player_elem.style.height = small_player_size;
#overlay {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
text-align: center;
}
small_player_elem.className = (state.small_channel == null) ? 'hidden' : '';
large_player_elem.className = (state.large_channel == null) ? 'hidden' : '';
overlay_elem.className = (state.large_channel != null || state.small_channel != null) ? 'hidden' : '';
a:link, a:active, a:visited, a:hover {
color: inherit;
}
document.body.className = state.show_chat ? 'with-chat' : '';
}
</style>
</head>
<body>
<div id="large-player" onmousedown="toggleFullscreen(event)">
<div id="small-player"></div>
if (!window.EventSource) {
overlay_elem.firstElementChild.innerText = "Your browser (probably IE / Edge) does not support EventSource. Please try any other modern browser.";
} else {
const events = new EventSource('/tv/events');
<div id="overlay">
<h3>Nothing playing</h3>
<p>Documentation at <a href="https://simons-nzse-2.h-da.io/stream-tv">https://simons-nzse-2.h-da.io/stream-tv</a></p>
</div>
</div>
events.onmessage = m => {
updateState(JSON.parse(m.data));
};
<script>
const large_player = new Twitch.Player('large-player', {
width: 0,
height: 0,
controls: false,
muted: false
});
const small_player = new Twitch.Player('small-player', {
width: 0,
height: 0,
controls: false,
muted: true
});
const small_player_elem = document.getElementById('small-player');
const overlay_elem = document.getElementById('overlay');
let state = {
large_channel: null,
small_channel: null
events.onopen = () => {
overlay_elem.firstElementChild.innerText = "Nothing playing";
};
function updateState(newState) {
if (state.large_channel != newState.large_channel) {
large_player.setChannel(newState.large_channel);
large_player.setMuted(false); // TODO: investigate with autoplay
}
events.onerror = err => {
console.error(err);
if (state.small_channel != newState.small_channel) {
small_player.setChannel(newState.small_channel);
updateState(defaultState);
overlay_elem.firstElementChild.innerText = "Connection to stream-tv-server failed. Make sure it's running and reload the page.";
};
}
large_player_elem.addEventListener('mousedown', (event) => {
if (event.detail > 1) { // double click
if (!document.fullscreenElement) { // we are not in fullscreen
document.documentElement.requestFullscreen()
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
}
}
state = newState;
console.log("State:", state);
large_player.setVolume(state.volume);
const inset_percent = (1.0 - state.small_scale) * 20;
small_player_elem.style.transform = 'scale(' + state.small_scale + ') translate(-' + inset_percent + '%, ' + inset_percent + '%)';
small_player_elem.style.display = (state.small_channel != null) ? 'block' : 'none';
overlay_elem.style.display = (state.large_channel != null || state.small_channel != null) ? 'none' : 'block';
event.preventDefault();
}
});
fetch("/tv/state").then(resp => {
if (!resp.ok) {
throw Error(resp.statusText);
}
return resp;
}).then(resp => resp.json()).then(state => {
updateState(state)
const updateEventSource = new EventSource("/tv/events");
updateEventSource.onmessage = m => {
updateState(JSON.parse(m.data));
};
// TODO this could not exist
updateEventSource.onerror = err => {
console.error(err);
};
}).catch(err => {
console.error(err);
overlay_elem.firstElementChild.innerText = "Failed to connect to stream-tv-server. Make sure it's running and reload the page.";
});
// TODO: what happens if user returns after channel(s) went offline?
large_player.addEventListener(Twitch.Player.ENDED, () => {
});
small_player.addEventListener(Twitch.Player.ENDED, () => {
});
function toggleFullscreen(event) {
if (event.detail > 1) {
if (!document.fullscreenElement) {
document.getElementById('large-player').requestFullscreen();
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
}
}
</script>
event.preventDefault();
}
}
</script>
</body>
</body>
</html>
`)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment