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.
200 lines
4.5 KiB
200 lines
4.5 KiB
const audio = document.querySelector("audio");
|
|
const playButton = document.querySelector(".play-stream");
|
|
const playButtonAnimation = document.getElementById("play-button-animation");
|
|
const artistBlock = document.querySelector(".now-playing_artist");
|
|
const titleBlock = document.querySelector(".now-playing_song");
|
|
const logoDot = document.querySelector(".logo_dot");
|
|
|
|
const STOP_STATE_POINTS = "650,0, 1350,0, 1350,500, 650,500";
|
|
const PLAY_STATE_POINTS = "0,0, 2000,250, 2000,250, 0,500";
|
|
|
|
const startIcebergsAnimation = () => {
|
|
[1, 2, 3].map((i) => {
|
|
const polygon = document.getElementById(`iceberg${i}`);
|
|
polygon.classList.add("active");
|
|
});
|
|
};
|
|
|
|
const stopIcebergsAnimation = () => {
|
|
[1, 2, 3].map((i) => {
|
|
const polygon = document.getElementById(`iceberg${i}`);
|
|
polygon.classList.remove("active");
|
|
});
|
|
};
|
|
|
|
const hideAudio = () => {
|
|
audio.hidden = true;
|
|
};
|
|
|
|
const playAudio = () => {
|
|
audio?.play();
|
|
};
|
|
|
|
const pauseAudio = () => {
|
|
audio?.pause();
|
|
};
|
|
|
|
const setButtonPlayState = () => {
|
|
playButtonAnimation.setAttribute("from", PLAY_STATE_POINTS);
|
|
playButtonAnimation.setAttribute("to", STOP_STATE_POINTS);
|
|
playButtonAnimation.beginElement();
|
|
};
|
|
|
|
const setButtonStopState = () => {
|
|
playButtonAnimation.setAttribute("from", STOP_STATE_POINTS);
|
|
playButtonAnimation.setAttribute("to", PLAY_STATE_POINTS);
|
|
playButtonAnimation.beginElement();
|
|
};
|
|
|
|
const handleAudioPlay = () => {
|
|
playAudio();
|
|
setButtonPlayState();
|
|
startIcebergsAnimation();
|
|
};
|
|
|
|
const handleAudioStop = () => {
|
|
pauseAudio();
|
|
setButtonStopState();
|
|
stopIcebergsAnimation();
|
|
};
|
|
|
|
const addPlayerInteractions = () => {
|
|
playButton.addEventListener("click", () => {
|
|
audio?.paused ? handleAudioPlay() : handleAudioStop();
|
|
});
|
|
};
|
|
|
|
const parseMessageFromString = (message) => {
|
|
try {
|
|
return JSON.parse(message);
|
|
} catch {
|
|
return {};
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @typedef {Object} MessageLike
|
|
* @property {string} action - every string valid for JS function name.
|
|
* @property {Object} data - The user's last name.
|
|
*/
|
|
|
|
/**
|
|
* @param {string|MessageLike} data - Message itself of JSON representation of Message
|
|
*/
|
|
function Message(data = {}) {
|
|
switch(Object.prototype.toString.call(data)) {
|
|
case "[object String]":
|
|
const parsedMessage = parseMessageFromString(data);
|
|
|
|
this.action = parsedMessage?.action;
|
|
this.data = parsedMessage?.data;
|
|
|
|
break;
|
|
case "[object Object]":
|
|
this.action = data?.action;
|
|
this.data = data?.data;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
Message.prototype.isValid = function() {
|
|
return (
|
|
this?.action !== undefined
|
|
);
|
|
};
|
|
|
|
/**
|
|
* @typedef {Object} MessageLike
|
|
* @property {string} action - every string valid for JS function name.
|
|
* @property {Object} data - The user's last name.
|
|
*/
|
|
|
|
/**
|
|
* @param {string|MessageLike} message - Message itself of JSON representation of Message
|
|
*/
|
|
function processMessage(messageLike = {}) {
|
|
const message = new Message(messageLike);
|
|
|
|
if(!message.isValid()) {
|
|
console.error("Got mallformed message:", message);
|
|
return;
|
|
}
|
|
if (this.actions[message.action]) {
|
|
this.actions[message.action](message.data);
|
|
} else {
|
|
this.fallbackAction(message);
|
|
}
|
|
}
|
|
const messageProcessor = {
|
|
// Use Map instead?
|
|
actions: {},
|
|
processMessage,
|
|
fallbackAction: function(x) {
|
|
console.log("No actions for message:", x);
|
|
}
|
|
};
|
|
|
|
const socketProtocol = location.protocol === "http:" ? "ws:" : "wss:";
|
|
|
|
async function connectToServer() {
|
|
const ws = new WebSocket(`${socketProtocol}//${location.host}/meta`);
|
|
ws.addEventListener("message", (event) =>
|
|
messageProcessor.processMessage(event.data),
|
|
);
|
|
}
|
|
|
|
const changeTitles = ({ artist, title }) => {
|
|
if (artist) {
|
|
artistBlock.innerHTML = artist;
|
|
} else {
|
|
artistBlock.innerHTML = "";
|
|
}
|
|
|
|
if (title) {
|
|
titleBlock.innerHTML = title;
|
|
document.title = `${title} on Radioiceberg`;
|
|
} else {
|
|
titleBlock.innerHTML = "";
|
|
}
|
|
|
|
if (artist && title) {
|
|
document.title = `${artist} - ${title} on Radioiceberg`;
|
|
}
|
|
};
|
|
|
|
const metadataChange = (data) => {
|
|
changeTitles(data);
|
|
};
|
|
|
|
const liveStarted = () => {
|
|
logoDot.classList.add("live");
|
|
};
|
|
|
|
const liveEnded = () => {
|
|
logoDot.classList.remove("live");
|
|
};
|
|
|
|
const setState = (data) => {
|
|
const { lastPlayed, isOnline } = data;
|
|
if (lastPlayed) changeTitles(lastPlayed);
|
|
if (isOnline) liveStarted();
|
|
};
|
|
|
|
const actions = {
|
|
metadataChange,
|
|
liveStarted,
|
|
liveEnded,
|
|
setState,
|
|
};
|
|
|
|
messageProcessor.actions = actions;
|
|
|
|
const main = () => {
|
|
hideAudio();
|
|
addPlayerInteractions();
|
|
connectToServer();
|
|
};
|
|
|
|
document.addEventListener("DOMContentLoaded", main);
|