still-alive/assets/js/modules/PlayerManager.mjs
Victor Westerlund 1185a744f2
Semi-stable release (#1)
* dev21w41a

* Added Monkeydo submodule

Added Monkeydo to .gitmodules

* dev21w41b

* Added functional code before manifest and CSS

The JS-part of this demo is now functional to the point where a Monkeydo manifest can be played on the different windows. CSS styling and an actual manifest will still have to be created.

* Create README.md

* Cake

* Update README.md

* Unsynced working example

Everything is cued up and good to go. Now comes the fun part of syncing the lyrics with the song

* Synchronized lyrics with music

So, yeah this totally didn't take 6 hours to do.

* Style changes and log removal

Fixed the scroll direction for lyrics, made the art window's font clamp (and centered) and also removed some loggers.

player.html will redirect to the main page if opened without a hash and will no longer be caught by crawlers that care about robots.txt
2021-10-31 12:40:49 +01:00

116 lines
No EOL
3.1 KiB
JavaScript

import { default as PlayerWindow } from "./PlayerWindow.mjs";
import { default as Monkeydo } from "./monkeydo/Monkeydo.mjs";
export default class WindowManager {
constructor(mediaElement) {
const self = this;
this.mediaElement = mediaElement;
// Bi-directional communcation to player windows
this.channels = {
"#lyrics": new BroadcastChannel("#lyrics"),
"#credits": new BroadcastChannel("#credits"),
"#art": new BroadcastChannel("#art")
};
for(const channel of Object.values(this.channels)) {
channel.addEventListener("message",event => this.message(event));
}
// Monkeydo methods
const methods = {
blank: (target) => {
self.channels[target].postMessage(["BLANK",target]);
},
lineFeed: (target) => {
self.channels[target].postMessage(["LINE_FEED"]);
},
textFeed: (text,target = "#lyrics") => {
self.channels[target].postMessage(["TEXT_FEED",text]);
},
drawArt: (index,target = "#art") => {
self.channels[target].postMessage(["DRAW_ART",index]);
},
playCredits: () => {
self.players.credits.do();
}
}
this.players = {
lyrics: new Monkeydo(methods),
credits: new Monkeydo(methods)
}
}
playbackFailed(promiseObject = false) {
console.log(promiseObject);
}
// Attempt to open a new window
async spawnPlayer(type) {
if(!type in this.channels) {
throw new Error(`Inavlid player type "${type}"`);
}
return await new Promise((resolve,reject) => {
const player = new PlayerWindow(type).open();
const channel = this.channels[type];
// Wait for window to emit ready state message before resolving
const ack = channel.addEventListener("message",event => {
if(event.data[0] === "WINDOW_READY" || event.data[1] === type) {
resolve("WINDOW_READY");
}
// Window failed to initialize
if(event.data[0] === "WINDOW_ERROR" || event.data[1][0] === type) {
reject(event.data[1]);
}
return false;
});
channel.removeEventListener("message",ack);
});
}
async play() {
for(const [key,player] of Object.entries(this.players)) {
const manifest = new URL(window.location.href + `monkeydo_${key}.json`);
await player.load(manifest.toString());
}
this.players.lyrics.do();
this.mediaElement.play();
}
// Open player windows and start playback
async init() {
const art = this.spawnPlayer("#art");
const credits = this.spawnPlayer("#credits");
const lyrics = this.spawnPlayer("#lyrics");
const timeout = new Promise(resolve => setTimeout(() => resolve("TIMEOUT")),3000);
const windows = await Promise.allSettled([lyrics,credits,art]);
// Wait for all windows to open and initialize (or timout and fail)
const status = Promise.race([windows,timeout]);
status.then(promises => {
promises.forEach(promiseObject => {
if(promiseObject.status !== "fulfilled") {
this.playbackFailed(promiseObject);
}
});
// Load Monkeydo manifest and start playback
this.play();
});
}
message(event) {
const type = event.data[0];
const data = event.data[1];
switch(type) {
case "PLAY": console.log("PLAY",event); break;
case "WINDOW_CLOSED": console.log("WINDOW_CLOSED",event); break;
}
}
}