diff --git a/public/embed.js b/public/embed.js
index a5b6241..11b84a8 100644
--- a/public/embed.js
+++ b/public/embed.js
@@ -79,72 +79,255 @@
return STREAM_BASE_URL + "/stream/" + encodeURIComponent(mountpoint);
}
+ const PLAY_ICON = `
+
+ `;
+
+ const PAUSE_ICON = `
+
+ `;
+
+ const VOLUME_ICON = `
+
+ `;
+
+ const MUTED_ICON = `
+
+ `;
+
+ const SPINNER = '';
+
function createTemplate(width, height) {
- return (
- '" +
- '
' +
- '
' +
- '" +
- '' +
- '" +
- '
Connecting...
Checking station status.
' +
- '
" +
- '
' +
- '
' +
- "
" +
- "" +
- "
"
- );
+ return `
+
+
+
+
+
+
+
+
+
+
Connecting...
+
Checking station status.
+
+
+
+
+
+
+
+
+
+
+
+ `;
}
const script = document.currentScript;
@@ -169,24 +352,24 @@
script.insertAdjacentElement("afterend", mount);
const elements = {
- art: shadow.querySelector(".art"),
- logoMedia: shadow.querySelector(".brand-mark--media"),
- title: shadow.querySelector(".title"),
- status: shadow.querySelector(".status"),
+ art: shadow.querySelector(".artwork"),
+ logoMedia: shadow.querySelector(".waxlive-logo--media"),
+ titleLink: shadow.querySelector(".title-link"),
+ status: shadow.querySelector(".status-pill"),
+ statusDot: shadow.querySelector(".status-dot"),
statusText: shadow.querySelector(".status-text"),
trackTitle: shadow.querySelector(".track-title"),
- meta: shadow.querySelector(".meta"),
- play: shadow.querySelector(".play"),
- mute: shadow.querySelector(".mute"),
- link: shadow.querySelector(".link"),
+ meta: shadow.querySelector(".track-meta"),
+ play: shadow.querySelector(".play-button"),
+ mute: shadow.querySelector(".mute-button"),
+ volume: shadow.querySelector(".volume-input"),
hint: shadow.querySelector(".hint"),
audio: shadow.querySelector("audio"),
};
- elements.link.textContent = "Open station";
- elements.link.setAttribute("aria-label", titleText);
elements.art.src = FALLBACK_ART;
elements.logoMedia.src = logoUrl;
+ elements.audio.volume = Number(elements.volume.value);
elements.audio.muted = defaultMuted;
const state = {
@@ -233,50 +416,89 @@
return FALLBACK_ART;
}
+ function setTrack(title, meta, wrap) {
+ elements.trackTitle.textContent = title || "";
+ elements.trackTitle.className = wrap
+ ? "track-title track-title--wrap"
+ : "track-title";
+
+ if (meta) {
+ elements.meta.textContent = meta;
+ elements.meta.className = "track-meta";
+ } else {
+ elements.meta.textContent = "";
+ elements.meta.className = "track-meta track-meta--hidden";
+ }
+ }
+
+ function isMuted() {
+ return elements.audio.muted || Number(elements.volume.value) === 0;
+ }
+
function render() {
const hasRecognition =
state.recognition && state.recognition.song && state.recognition.artist;
-
- elements.title.textContent = getDisplayTitle();
- elements.statusText.textContent = state.onAir ? "On air" : "Off air";
- elements.status.className = state.onAir ? "status live" : "status";
- elements.art.src = getDisplayArt();
- elements.link.href =
+ const stationUrl =
"https://wax.live/" +
encodeURIComponent(
state.station ? state.station.station_name : stationName || "",
);
+ elements.titleLink.textContent = getDisplayTitle();
+ elements.titleLink.href = stationUrl;
+ elements.titleLink.setAttribute("aria-label", titleText);
+ elements.statusText.textContent = state.onAir ? "LIVE" : "OFF AIR";
+ elements.status.className = state.onAir
+ ? "status-pill status-pill--live"
+ : "status-pill";
+ elements.statusDot.style.display = state.onAir ? "block" : "none";
+ elements.art.src = getDisplayArt();
+
if (!stationName) {
- elements.trackTitle.textContent = "Station not configured";
- elements.meta.textContent = "Add data-station=\"my-station\" to the embed script.";
+ setTrack(
+ "Station not configured",
+ 'Add data-station="my-station" to the embed script.',
+ false,
+ );
} else if (state.onAir && hasRecognition) {
- elements.trackTitle.textContent = state.recognition.song;
- elements.meta.textContent =
+ setTrack(
+ state.recognition.song,
state.recognition.artist +
- (state.recognition.album ? " • " + state.recognition.album : "");
+ (state.recognition.album ? " • " + state.recognition.album : ""),
+ false,
+ );
} else if (state.onAir) {
- elements.trackTitle.textContent = "Live now";
- elements.meta.textContent = "Waiting for current track recognition.";
+ setTrack("Listening live", "Waiting for current track recognition.", false);
} else if (state.station) {
- elements.trackTitle.textContent = "Station is currently off air";
- elements.meta.textContent = "Playback will become available automatically when live.";
- } else {
- elements.trackTitle.textContent = "Loading station...";
- elements.meta.textContent = "Checking station status.";
- }
-
- if (state.busy) {
- elements.play.textContent = "Loading";
- } else if (state.playing) {
- elements.play.textContent = "Pause";
+ setTrack(
+ state.station.description || "Station is currently off air",
+ "",
+ true,
+ );
} else {
- elements.play.textContent = "Play";
+ setTrack("Loading station...", "Checking station status.", false);
}
+ elements.play.className = state.playing
+ ? "play-button play-button--playing"
+ : "play-button";
+ elements.play.innerHTML = state.busy
+ ? SPINNER
+ : state.playing
+ ? PAUSE_ICON
+ : PLAY_ICON;
+ elements.play.setAttribute(
+ "aria-label",
+ state.playing ? "Pause stream" : "Play stream",
+ );
elements.play.disabled = !state.station || !state.onAir || state.busy;
- elements.mute.disabled = !state.station;
- elements.mute.textContent = elements.audio.muted ? "Unmute" : "Mute";
+ elements.mute.disabled = !state.station || state.busy;
+ elements.mute.innerHTML = isMuted() ? MUTED_ICON : VOLUME_ICON;
+ elements.mute.setAttribute(
+ "aria-label",
+ isMuted() ? "Unmute stream" : "Mute stream",
+ );
+ elements.volume.disabled = !state.station;
}
async function loadRecognition() {
@@ -363,8 +585,7 @@
? "Station not found."
: "Could not load station data.",
);
- elements.trackTitle.textContent = "Unable to load station";
- elements.meta.textContent = "Check the station name and try again.";
+ setTrack("Unable to load station", "Check the station name and try again.", false);
}
}
@@ -400,6 +621,15 @@
render();
});
+ elements.volume.addEventListener("input", function () {
+ const nextVolume = Number(elements.volume.value);
+ elements.audio.volume = nextVolume;
+ if (nextVolume > 0 && elements.audio.muted) {
+ elements.audio.muted = false;
+ }
+ render();
+ });
+
elements.audio.addEventListener("play", function () {
state.playing = true;
render();