Skip to content
Snippets Groups Projects
website.go 7.46 KiB
Newer Older
  • Learn to ignore specific revisions
  • Simon Kirsten's avatar
    Simon Kirsten committed
    package website
    
    import (
    	"net/http"
    )
    
    
    // website is the html source of the website.
    // It is possible to have this in a serperate .html file and include it at compile time but for simplicity and portability we just keep it here inline.
    
    Simon Kirsten's avatar
    Simon Kirsten committed
    // No React, Vue, etc. or jQuery is used.
    
    // Also there are some Visual Studio Code plugins in development that should enable html syntax highlighting here.
    
    Simon Kirsten's avatar
    Simon Kirsten committed
    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>
    
    	<script src="https://player.twitch.tv/js/embed/v1.js"></script> <!-- twitch embed -->
    
    	<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: fixed;
    
    			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;
    		}
    
    		a:link, a:active, a:visited, a:hover {
    			color: inherit;
    		}
    	</style>
    </head>
    <body>
    
    	<div id="large-player" class="hidden">
    
    			<div id="small-player" class="hidden"></div>
    
    	<iframe id="chat" frameborder="0" scrolling="no" src="about:blank"></iframe>
    
    	<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>
    		</p>
    		<small>
    			<a href="javascript:toggleFullscreen()">toggle fullscreen</a>
    		</small>
    	</div>
    
    
    		var large_player;
    		var small_player;
    
    		function newLargePlayer(channel) {
    			return new Twitch.Player('large-player', {
    				channel: channel,
    
    				width: '',
    				height: '',
    
    				controls: false,
    				muted: false
    			});
    		}
    
    		function newSmallPlayer(channel) {
    			return new Twitch.Player('small-player', {
    				channel: channel,
    
    				width: '',
    				height: '',
    
    
    		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: '',
    			small_channel: '',
    
    			volume: 0.5,
    			small_scale: 0.3,
    			show_chat: false
    		};
    
    		let state = defaultState;
    
    		function updateState(newState) {
    
    			state = newState;
    			
    			console.log("State:", state);
    
    			if (state.large_channel == '' && large_player) {
    
    				large_player.destroy();
    				large_player = undefined;
    				chat_elem.src = 'about:blank';
    
    			// we should be playing something
    
    			if (state.large_channel != '') {
    
    				// we need to create the large player
    				if (!large_player) {
    					large_player = newLargePlayer(state.large_channel);
    				}
    
    				// the channel needs to be changed
    				if (large_player.getChannel() != state.large_channel) {
    					large_player.setChannel(state.large_channel);
    				}
    
    				// the volume needs to be changed
    				if (large_player.getVolume() != state.volume) {
    					large_player.setVolume(state.volume);
    				}
    
    				var chat_elem_src = 'https://www.twitch.tv/embed/' + state.large_channel + '/chat?darkpopout';
    
    				if (chat_elem_src != chat_elem.src) {
    					chat_elem.src = chat_elem_src;
    				}
    
    			// small player:
    			
    			// we need to destroy the player
    
    			if (state.small_channel == '' && small_player) {
    
    				small_player.destroy();
    				small_player = undefined;
    			}
    
    			// we should be playing something
    
    			if (state.small_channel != '') {
    
    				// we need to create the small player
    				if (!small_player) {
    					small_player = newSmallPlayer(state.small_channel);
    				}
    
    				// the channel needs to be changed
    				if (small_player.getChannel() != state.small_channel) {
    					small_player.setChannel(state.small_channel);
    				}
    			}
    
    			const small_player_size = state.small_scale * 100 + '%';
    			small_player_elem.style.width = small_player_size;
    			small_player_elem.style.height = small_player_size;
    
    			small_player_elem.className = (state.small_channel == '') ? 'hidden' : '';
    			large_player_elem.className = (state.large_channel == '') ? 'hidden' : '';
    			overlay_elem.className = (state.large_channel != '' || state.small_channel != '') ? 'hidden' : '';
    
    			document.body.className = state.show_chat ? 'with-chat' : '';
    		}
    
    		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');
    
    			events.onmessage = m => {
    				updateState(JSON.parse(m.data));
    			};
    
    			events.onopen = () => {
    				overlay_elem.firstElementChild.innerText = "Nothing playing";
    
    			events.onerror = err => {
    				console.error(err);
    
    				updateState(defaultState);
    				overlay_elem.firstElementChild.innerText = "Connection to stream-tv-server failed. Make sure it's running and reload the page.";
    			};
    		}
    
    
    		function toggleFullscreen() {
    			if (!document.fullscreenElement) { // we are not in fullscreen
    				document.documentElement.requestFullscreen()
    			} else {
    				if (document.exitFullscreen) {
    					document.exitFullscreen();
    				}
    			}
    		}
    
    
    		large_player_elem.addEventListener('mousedown', (event) => {
    			if (event.detail > 1) { // double click
    
    				toggleFullscreen();
    
    Simon Kirsten's avatar
    Simon Kirsten committed
    </html>
    
    Simon Kirsten's avatar
    Simon Kirsten committed
    `)
    
    
    // websiteHandleFunc just serves the website
    func websiteHandleFunc(w http.ResponseWriter, r *http.Request) {
    
    Simon Kirsten's avatar
    Simon Kirsten committed
    	// The "/" pattern (in main.go) matches everything that isn't handled by somebody else, so  we need to check
    
    Simon Kirsten's avatar
    Simon Kirsten committed
    	if r.URL.Path != "/" {
    
    Simon Kirsten's avatar
    Simon Kirsten committed
    	} else {
    		w.Header().Set("Content-Type", "text/html")
    		w.Write(website)
    	}
    }
    
    
    // Handler returns a http.Handler that serves requests for the / backend
    
    Simon Kirsten's avatar
    Simon Kirsten committed
    func Handler() http.Handler {
    
    	return http.HandlerFunc(websiteHandleFunc)
    
    Simon Kirsten's avatar
    Simon Kirsten committed
    }