console.log("muffcast client v0.1"); var currentStatus; var syncIntervals = []; var syncIntervalTime = 30000; var seekIntervals = []; var muffcastUrl = "http://localhost:8128"; browser.storage.local.get("muffcast").then(function(result) { muffcastUrl = result.muffcast && result.muffcast.url || muffcastUrl; syncIntervalTime = result.muffcast && result.muffcast.syncInterval && parseInt(result.muffcast.syncInterval) || syncIntervalTime; }) var getStatus = function() { return new Promise(function(resolve, reject) { var xhttp = new XMLHttpRequest(); xhttp.open("GET", muffcastUrl, true); xhttp.onreadystatechange = function() { if (this.readyState == 4) { if (this.status == 200) { var response = this.responseText ? JSON.parse(this.responseText) : false; resolve(response); } else { reject({ status: this.status, error: this.statusText, body: this.responseText, }); } } } xhttp.setRequestHeader("Content-type", "application/json"); xhttp.send(); }) } var getPlayer = function(type, index, sleep) { return new Promise(function(resolve, reject) { setTimeout(function() { var player = document.getElementsByTagName(type)[index]; if (player) { resolve(player); } else if (sleep < 3000) { return getPlayer(type, index, sleep + 500); } else { reject(player); } }, sleep); }) } var getTimeString = function(seconds) { var hours = parseInt(seconds / 3600); var minutes = parseInt((seconds % 3600) / 60); var seconds = parseInt(seconds % 60); return (hours > 0 ? hours + ":" : "") + minutes + ":" + (seconds < 10 ? "0" : "") + seconds; } var addCastLinks = function(type) { // add cast links var elements = document.getElementsByTagName(type); for (var i = 0; i < elements.length; i++) { var element = elements[i]; var position = element.getBoundingClientRect(); var castLink = document.createElement("a"); castLink.id = "muffcast-cast-link_" + i; castLink.index = i; castLink.classList.add("muffcast-loader"); castLink.style["top"] = position.top + "px"; castLink.style["left"] = position.left + "px"; castLink.innerHTML = ''; document.body.appendChild(castLink); castLink.addEventListener("click", function(event) { var index = event.target.parentNode.index; var player = document.getElementsByTagName(type)[index]; player.pause(); browser.runtime.sendMessage({ "command": "load", "url": encodeURIComponent(window.location.href), "type": type, "index": index, "seek": player.currentTime, "volume": player.volume, "muted": player.muted }); }) } } var setStatus = function() { for (let seekInterval of seekIntervals) { clearInterval(seekInterval); } for (let syncInterval of syncIntervals) { clearInterval(syncInterval); } var overlay = document.getElementById("muffcast-overlay"); if (overlay) { overlay.parentNode.removeChild(overlay); } // remove all cast links for (let castLink of document.getElementsByClassName("muffcast-loader")) { document.body.removeChild(castLink); } addCastLinks("video"); addCastLinks("audio"); getStatus().then(function(status) { currentStatus = status; if (currentStatus.url && currentStatus.url == encodeURIComponent(window.location.href)) { getPlayer(currentStatus.type, currentStatus.index, 0).then(function(player) { player.muted = currentStatus.muted; player.currentTime = currentStatus.currentTime; player.addEventListener("canplaythrough", function() { if (currentStatus.playing && currentStatus.url == encodeURIComponent(window.location.href)) { player.pause(); } }) overlay = document.createElement("div"); overlay.id = "muffcast-overlay"; document.body.appendChild(overlay); var play = document.createElement("a"); play.id = "muffcast-play"; play.innerHTML = currentStatus.playing ? '' : '';; play.addEventListener("click", function(event) { if (currentStatus.playing) { browser.runtime.sendMessage({ "command": "pause", "seek": player.currentTime }); } else { browser.runtime.sendMessage({ "command": "play", "seek": player.currentTime }); player.pause(); } currentStatus.playing = !currentStatus.playing; play.innerHTML = currentStatus.playing ? '' : ''; }) var stop = document.createElement("a"); stop.id = "muffcast-stop"; stop.innerHTML = ''; stop.addEventListener("click", function(event) { browser.runtime.sendMessage({ "command": "stop" }); setStatus(); }) var duration = document.createElement("span"); duration.id = "muffcast-duration"; duration.classList.add("time"); duration.textContent = getTimeString(player.duration); var currentTime = document.createElement("span"); currentTime.id = "muffcast-currenttime"; currentTime.classList.add("time"); currentTime.textContent = getTimeString(player.currentTime); var seek = document.createElement("input"); seek.id = "muffcast-seek"; seek.setAttribute("type", "range"); seek.setAttribute("min", 0); seek.setAttribute("max", currentStatus.duration); seek.setAttribute("value", currentStatus.currentTime); seek.addEventListener("change", function(event) { browser.runtime.sendMessage({ "command": "seek", "seek": seek.value }); player.currentTime = seek.value; currentTime.textContent = getTimeString(player.currentTime); }); seekIntervals.push(setInterval(function() { if (seek.value < currentStatus.duration) { if (!player.paused || currentStatus.playing) { seek.value++; currentTime.textContent = getTimeString(seek.value); } } else { seek.value = 0; clearInterval(seekInterval); } }, 1000)); var audio = document.createElement("div"); audio.id = "muffcast-audio"; var volume = document.createElement("input"); volume.id = "muffcast-volume"; volume.setAttribute("type", "range"); volume.setAttribute("min", 0); volume.setAttribute("max", 1); volume.setAttribute("step", 0.01); volume.setAttribute("value", status.muted ? 0 : status.volume); volume.addEventListener("change", function(event) { browser.runtime.sendMessage({ "command": "volume", "volume": volume.value }); player.volume = volume.value; player.muted = volume.value == 0; }); var mute = document.createElement("a"); mute.id = "muffcast-mute"; mute.innerHTML = status.muted ? '' : (player.volume < 0.5 ? '' : ''); mute.addEventListener("click", function(event) { browser.runtime.sendMessage({ "command": "mute", "muted": !player.muted }); player.muted = !player.muted; volume.value = player.muted ? 0 : player.volume; mute.innerHTML = player.muted ? '' : (player.volume < 0.5 ? '' : ''); }) var icon = document.createElement("span"); icon.id = "muffcast-icon"; icon.innerHTML = ''; overlay.appendChild(icon); overlay.appendChild(play); audio.appendChild(mute); audio.appendChild(volume); overlay.appendChild(audio); overlay.appendChild(currentTime); overlay.appendChild(seek); overlay.appendChild(duration); overlay.appendChild(stop); var castLink = document.getElementById("muffcast-cast-link_" + status.index); castLink.classList.add("active"); syncIntervals.push(setInterval(function() { // sync status if (!player.isSyncInterval) { getStatus().then(function(status) { player.isSyncInterval = true; currentStatus.playing = status.playing; currentStatus.currentTime = status.currentTime; currentStatus.volume = status.volume; currentStatus.muted = status.muted; currentTime.textContent = getTimeString(currentStatus.currentTime); seek.value = currentStatus.currentTime play.innerHTML = currentStatus.playing ? '' : ''; volume.value = currentStatus.muted ? 0 : currentStatus.volume; mute.innerHTML = currentStatus.muted ? '' : (currentStatus.volume < 0.5 ? '' : ''); player.volume = currentStatus.volume; player.muted = currentStatus.muted; player.currentTime = currentStatus.currentTime; }) } }, syncIntervalTime)); setTimeout(function() { player.addEventListener("play", function(event) { if (status.playing && status.url == encodeURIComponent(window.location.href)) { browser.runtime.sendMessage({ "command": "pause", "seek": player.currentTime }); play.innerHTML = ''; status.playing = !status.playing; } }) player.addEventListener("pause", function(event) { if (!status.playing && status.url == encodeURIComponent(window.location.href.href)) { browser.runtime.sendMessage({ "command": "play", "seek": player.currentTime }); play.innerHTML = ''; status.playing = !status.playing; } }) player.addEventListener("seeked", function(event) { if (status.playing && status.url == encodeURIComponent(window.location.href)) { if (!player.isSyncInterval) { browser.runtime.sendMessage({ "command": "seek", "seek": player.currentTime }); seek.value = player.currentTime; currentTime.textContent = getTimeString(player.currentTime); } else { player.isSyncInterval = false; }; } }) player.addEventListener("volumechange", function(event) { if (status.url == encodeURIComponent(window.location.href)) { browser.runtime.sendMessage({ "command": "volume", "volume": player.volume }); volume.value = player.muted ? 0 : player.volume; mute.innerHTML = player.muted ? '' : (player.volume < 0.5 ? '' : ''); } }) }, 1500); }) } }) } browser.runtime.onMessage.addListener(function(message) { var videos = document.getElementsByTagName("video"); switch (message.command) { case "update": setStatus(); break; case "load": var player = videos[message.index]; player.pause(); player.style.border = "none"; browser.runtime.sendMessage({ "command": "load", "url": encodeURIComponent(window.location.href), "type": "video", "index": message.index, "seek": player.currentTime, "volume": player.volume, "muted": player.muted }); break; case "mark": var player = videos[message.index]; player.style.border = "5px solid red"; break; case "unmark": var player = videos[message.index]; player.style.border = "none"; break; } })