WebExtension as alternative to Chromecast
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

358 lines
11 KiB

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 = '<i class="fa fa-fw fa-television"></i>';
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 ? '<i class="fa fa-fw fa-pause"></i>' : '<i class="fa fa-fw fa-play"></i>';;
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 ? '<i class="fa fa-fw fa-pause"></i>' : '<i class="fa fa-fw fa-play"></i>';
})
var stop = document.createElement("a");
stop.id = "muffcast-stop";
stop.innerHTML = '<i class="fa fa-fw fa-stop"></i>';
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 ? '<i class="fa fa-fw fa-volume-off"></i>' : (player.volume < 0.5 ? '<i class="fa fa-fw fa-volume-down"></i>' : '<i class="fa fa-fw fa-volume-up"></i>');
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 ? '<i class="fa fa-fw fa-volume-off"></i>' : (player.volume < 0.5 ? '<i class="fa fa-fw fa-volume-down"></i>' : '<i class="fa fa-fw fa-volume-up"></i>');
})
var icon = document.createElement("span");
icon.id = "muffcast-icon";
icon.innerHTML = '<i class="fa fa-television"></i>';
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 ? '<i class="fa fa-fw fa-pause"></i>' : '<i class="fa fa-fw fa-play"></i>';
volume.value = currentStatus.muted ? 0 : currentStatus.volume;
mute.innerHTML = currentStatus.muted ? '<i class="fa fa-fw fa-volume-off"></i>' : (currentStatus.volume < 0.5 ? '<i class="fa fa-fw fa-volume-down"></i>' : '<i class="fa fa-fw fa-volume-up"></i>');
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 = '<i class="fa fa-fw fa-play"></i>';
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 = '<i class="fa fa-fw fa-play"></i>';
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 ? '<i class="fa fa-fw fa-volume-off"></i>' : (player.volume < 0.5 ? '<i class="fa fa-fw fa-volume-down"></i>' : '<i class="fa fa-fw fa-volume-up"></i>');
}
})
}, 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;
}
})