watch-party/frontend/lib/video.mjs

164 lines
4.8 KiB
JavaScript

const loadVolume = () => {
try {
const savedVolume = localStorage.getItem("watch-party-volume");
if (savedVolume != null && savedVolume != "") {
return +savedVolume;
}
} catch (_err) {
// Sometimes localStorage is blocked from use
}
// default
return 0.5;
};
/**
* @param {number} volume
*/
const saveVolume = (volume) => {
try {
localStorage.setItem("watch-party-volume", volume);
} catch (_err) {
// see loadVolume
}
};
const loadCaptionTrack = () => {
try {
const savedTrack = localStorage.getItem("watch-party-captions");
if (savedTrack != null && savedTrack != "") {
return +savedTrack;
}
} catch (_err) {
// Sometimes localStorage is blocked from use
}
// default
return -1;
};
/**
* @param {number} track
*/
const saveCaptionsTrack = (track) => {
try {
localStorage.setItem("watch-party-captions", track);
} catch (_err) {
// see loadCaptionsTrack
}
};
/**
* @param {string} videoUrl
* @param {{name: string, url: string}[]} subtitles
*/
const createVideoElement = (videoUrl, subtitles) => {
const oldVideo = document.getElementById("video");
if (oldVideo) {
oldVideo.remove();
}
const video = document.createElement("video");
video.id = "video";
video.controls = true;
video.autoplay = false;
video.volume = loadVolume();
video.crossOrigin = "anonymous";
video.addEventListener("volumechange", async () => {
saveVolume(video.volume);
});
const source = document.createElement("source");
source.src = videoUrl;
video.appendChild(source);
const storedTrack = loadCaptionTrack();
let id = 0;
for (const { name, url } of subtitles) {
const track = document.createElement("track");
track.label = name;
track.src = url;
track.kind = "captions";
if (id == storedTrack || storedTrack == -1) {
track.default = true;
}
video.appendChild(track);
id++;
}
video.textTracks.addEventListener("change", async () => {
let id = 0;
for (const track of video.textTracks) {
if (track.mode != "disabled") {
saveCaptionsTrack(id);
return;
}
id++;
}
saveCaptionsTrack(-1);
});
// watch for attribute changes on the video object to detect hiding/showing of controls
// as far as i can tell this is the least hacky solutions to get control visibility change events
const observer = new MutationObserver(async (mutations) => {
for (const mutation of mutations) {
if (mutation.attributeName == "controls") {
if (video.controls) {
// enable media button support
navigator.mediaSession.setActionHandler("play", null);
navigator.mediaSession.setActionHandler("pause", null);
navigator.mediaSession.setActionHandler("stop", null);
navigator.mediaSession.setActionHandler("seekbackward", null);
navigator.mediaSession.setActionHandler("seekforward", null);
navigator.mediaSession.setActionHandler("seekto", null);
navigator.mediaSession.setActionHandler("previoustrack", null);
navigator.mediaSession.setActionHandler("nexttrack", null);
} else {
// disable media button support by ignoring the events
navigator.mediaSession.setActionHandler("play", () => {});
navigator.mediaSession.setActionHandler("pause", () => {});
navigator.mediaSession.setActionHandler("stop", () => {});
navigator.mediaSession.setActionHandler("seekbackward", () => {});
navigator.mediaSession.setActionHandler("seekforward", () => {});
navigator.mediaSession.setActionHandler("seekto", () => {});
navigator.mediaSession.setActionHandler("previoustrack", () => {});
navigator.mediaSession.setActionHandler("nexttrack", () => {});
}
return;
}
}
});
observer.observe(video, { attributes: true });
return video;
};
/**
* @param {string} videoUrl
* @param {{name: string, url: string}[]} subtitles
* @param {number} currentTime
* @param {boolean} playing
*/
export const setupVideo = async (videoUrl, subtitles, currentTime, playing) => {
document.querySelector("#pre-join-controls").style["display"] = "none";
const video = createVideoElement(videoUrl, subtitles);
const videoContainer = document.querySelector("#video-container");
videoContainer.style.display = "block";
videoContainer.appendChild(video);
video.currentTime = currentTime / 1000.0;
try {
if (playing) {
await video.play();
} else {
video.pause();
}
} catch (err) {
// Auto-play is probably disabled, we should uhhhhhhh do something about it
}
return video;
};