diff --git a/webroot/index.html b/webroot/index.html index f6a563140..5f411e80d 100644 --- a/webroot/index.html +++ b/webroot/index.html @@ -79,7 +79,7 @@ GW TODO: autoplay playsinline muted - poster="/thumbnail.jpg" + poster="./img/logo.png" > diff --git a/webroot/js/app.js b/webroot/js/app.js index d30963da4..a5110753f 100644 --- a/webroot/js/app.js +++ b/webroot/js/app.js @@ -4,7 +4,7 @@ async function setupApp() { window.app = new Vue({ el: "#app-container", data: { - streamStatus: "Stream is offline.", // Default state. + streamStatus: MESSAGE_OFFLINE, // Default state. viewerCount: 0, sessionMaxViewerCount: 0, overallMaxViewerCount: 0, @@ -59,7 +59,7 @@ async function setupApp() { app.extraUserContent = descriptionHTML; return this; } catch (error) { - console.log("Error",error); + console.log("Error", error); } } @@ -67,17 +67,13 @@ var websocketReconnectTimer; function setupWebsocket() { clearTimeout(websocketReconnectTimer); - // Uncomment to point to somewhere other than goth.land - const protocol = location.protocol == "https:" ? "wss" : "ws" - // var ws = new WebSocket(protocol + "://" + location.host + "/entry") - - var ws = new WebSocket("wss://goth.land/entry") + var ws = new WebSocket(URL_WEBSOCKET); ws.onmessage = (e) => { - const model = JSON.parse(e.data) + const model = JSON.parse(e.data); // Ignore non-chat messages (such as keepalive PINGs) - if (model.type !== SocketMessageTypes.CHAT) { return; } + if (model.type !== SOCKET_MESSAGE_TYPES.CHAT) { return; } const message = new Message(model); diff --git a/webroot/js/message.js b/webroot/js/message.js index fa4383d2b..ee5ef03f9 100644 --- a/webroot/js/message.js +++ b/webroot/js/message.js @@ -1,8 +1,3 @@ -const SocketMessageTypes = { - CHAT: 'CHAT', - PING: 'PING' -} - class Message { constructor(model) { this.author = model.author; @@ -39,10 +34,6 @@ class Messaging { this.maxMessageLength = 500; this.maxMessageBuffer = 20; - this.keyUsername = 'owncast_username'; - this.keyUserAvatar = 'owncast_avatar'; - this.keyChatDisplayed = 'owncast_chat'; - this.tagAppContainer = document.querySelector('#app-container'); this.tagChatToggle = document.querySelector('#chat-toggle'); this.tagUserInfoChanger = document.querySelector('#user-info-change'); @@ -89,16 +80,11 @@ class Messaging { } initLocalStates() { - this.username = getLocalStorage(this.keyUsername) || generateUsername(); - console.log("=== initt state", getLocalStorage(this.keyUserAvatar)) - this.imgUsernameAvatar.src = getLocalStorage(this.keyUserAvatar) || generateAvatar(this.username); - - - console.log("===",this.imgUsernameAvatar.src) - + this.username = getLocalStorage(KEY_USERNAME) || generateUsername(); + this.imgUsernameAvatar.src = getLocalStorage(KEY_AVATAR) || generateAvatar(this.username); this.updateUsernameFields(this.username); - this.chatDisplayed = getLocalStorage(this.keyChatDisplayed) || false; + this.chatDisplayed = getLocalStorage(KEY_CHAT_DISPLAYED) || false; this.displayChat(); } updateUsernameFields(username) { @@ -134,9 +120,9 @@ class Messaging { handleChatToggle() { this.chatDisplayed = !this.chatDisplayed; if (this.chatDisplayed) { - setLocalStorage(this.keyChatDisplayed, this.chatDisplayed); + setLocalStorage(KEY_CHAT_DISPLAYED, this.chatDisplayed); } else { - clearLocalStorage(this.keyChatDisplayed); + clearLocalStorage(KEY_CHAT_DISPLAYED); } this.displayChat(); } @@ -164,8 +150,8 @@ class Messaging { this.username = newValue; this.updateUsernameFields(newValue); this.imgUsernameAvatar.src = generateAvatar(newValue); - setLocalStorage(this.keyUsername, newValue); - setLocalStorage(this.keyUserAvatar, this.imgUsernameAvatar.src); + setLocalStorage(KEY_USERNAME, newValue); + setLocalStorage(KEY_AVATAR, this.imgUsernameAvatar.src); } this.handleHideChangeNameForm(); } diff --git a/webroot/js/player/player.js b/webroot/js/player/player.js index 23323f362..209fd9c56 100644 --- a/webroot/js/player/player.js +++ b/webroot/js/player/player.js @@ -1,11 +1,8 @@ -// const streamURL = '/hls/stream.m3u8'; -const streamURL = 'https://goth.land/hls/stream.m3u8'; // Uncomment me to point to remote video - // style hackings window.VIDEOJS_NO_DYNAMIC_STYLE = true; // Create the player for the first time -const player = videojs('video', null, function () { +const player = videojs(VIDEO_ID, null, function () { getStatus(); setInterval(getStatus, 5000); setupPlayerEventHandlers(); @@ -18,17 +15,12 @@ player.ready(function () { function resetPlayer(player) { player.reset(); - player.src({ type: 'application/x-mpegURL', src: streamURL }); - if (app.isOnline) { - player.poster('/thumbnail.jpg'); - } else { - // Change this to some kind of offline image. - player.poster('/img/logo.png'); - } + player.src({ type: 'application/x-mpegURL', src: URL_STREAM }); + setVideoPoster(app.isOnline); } function setupPlayerEventHandlers() { - const player = videojs('video'); + const player = videojs(VIDEO_ID); player.on('error', function (e) { console.log("Player error: ", e); @@ -68,8 +60,8 @@ function setupPlayerEventHandlers() { function restartPlayer() { try { - console.log('restarting') - const player = videojs('video'); + console.log('restarting', player.src()) + const player = videojs(VIDEO_ID); player.pause(); player.src(player.src()); // Reload the same video player.load(); @@ -78,4 +70,4 @@ function restartPlayer() { console.log(e) } -} \ No newline at end of file +} diff --git a/webroot/js/status.js b/webroot/js/status.js index eb393b3a7..54d05c5a5 100644 --- a/webroot/js/status.js +++ b/webroot/js/status.js @@ -1,42 +1,45 @@ var playerRestartTimer; -async function getStatus() { - const url = "/status"; - try { - const response = await fetch(url); - const status = await response.json(); - - clearTimeout(playerRestartTimer); - - if (!app.isOnline && status.online) { - // The stream was offline, but now it's online. Force start of playback after an arbitrary - // delay to make sure the stream has actual data ready to go. - playerRestartTimer = setTimeout(function () { - restartPlayer(); - }, 3000); - } - - app.streamStatus = status.online - ? "Stream is online." - : "Stream is offline." - - const player = videojs('video'); - if (app.isOnline) { - player.poster('/thumbnail.jpg'); - } else { - // Change this to some kind of offline image. - player.poster('/img/logo.png'); - } - - app.viewerCount = status.viewerCount; - app.sessionMaxViewerCount = status.sessionMaxViewerCount; - app.overallMaxViewerCount = status.overallMaxViewerCount; - app.isOnline = status.online; - - } catch (e) { - app.streamStatus = "Stream server is offline." - app.viewerCount = 0 +function handleStatus(status) { + clearTimeout(playerRestartTimer); + if (!app.isOnline && status.online) { + // The stream was offline, but now it's online. Force start of playback after an arbitrary delay to make sure the stream has actual data ready to go. + playerRestartTimer = setTimeout(restartPlayer, 3000); } -} \ No newline at end of file + app.streamStatus = status.online ? MESSAGE_ONLINE : MESSAGE_OFFLINE; + + app.viewerCount = status.viewerCount; + app.sessionMaxViewerCount = status.sessionMaxViewerCount; + app.overallMaxViewerCount = status.overallMaxViewerCount; + app.isOnline = status.online; + setVideoPoster(app.isOnline); + +} + +function handleOffline() { + const player = videojs(VIDEO_ID); + player.poster(POSTER_DEFAULT); + app.streamStatus = MESSAGE_OFFLINE; + app.viewerCount = 0; +} + +function getStatus() { + const options = { + // mode: 'no-cors', + } + fetch(URL_STATUS, options) + .then(response => { + if (!response.ok) { + throw new Error(`Network response was not ok ${response.ok}`); + } + return response.json(); + }) + .then(json => { + handleStatus(json); + }) + .catch(error => { + handleOffline(); + }); +} diff --git a/webroot/js/utils.js b/webroot/js/utils.js index eeec69656..f862dc137 100644 --- a/webroot/js/utils.js +++ b/webroot/js/utils.js @@ -1,3 +1,29 @@ +const MESSAGE_OFFLINE = 'Stream is offline.'; +const MESSAGE_ONLINE = 'Stream is online.'; + +// const URL_PREFIX = ''; +const URL_PREFIX = 'https://goth.land'; +const URL_STATUS = `${URL_PREFIX}/status`; +const URL_STREAM = `${URL_PREFIX}/hls/stream.m3u8`; + +const URL_WEBSOCKET = 'wss://goth.land/entry'; +// const URL_WEBSOCKET = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${location.host}/entry`; + +const POSTER_DEFAULT = `${URL_PREFIX}/img/logo.png`; +const POSTER_THUMB = `${URL_PREFIX}/thumbnail.jpg`; + +const SOCKET_MESSAGE_TYPES = { + CHAT: 'CHAT', + PING: 'PING' +} + +const KEY_USERNAME = 'owncast_username'; +const KEY_AVATAR = 'owncast_avatar'; +const KEY_CHAT_DISPLAYED = 'owncast_chat'; + +const VIDEO_ID = 'video'; + + function getLocalStorage(key) { try { return localStorage.getItem(key); @@ -87,4 +113,10 @@ function generateAvatar(hash) { function generateUsername() { return `User ${(Math.floor(Math.random() * 42) + 1)}`; -} \ No newline at end of file +} + +function setVideoPoster(online) { + const player = videojs(VIDEO_ID); + const poster = online ? POSTER_THUMB : POSTER_DEFAULT; + player.poster(poster); +}