basic impl.; no websockets yet!

This commit is contained in:
ari melody 2025-08-01 14:41:23 +01:00
commit d8aa765bf7
Signed by: ari
GPG key ID: CF99829C92678188
5 changed files with 151 additions and 0 deletions

BIN
public/default-artwork.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

23
public/index.html Normal file
View file

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
<link href="main.css" rel="stylesheet">
</head>
<body>
<div id="app">
<div id="music-ticker">
<div class="artwork-container">
<img src="default-artwork.png" alt="" width="64" height="64" id="artwork">
</div>
<div id="metadata">
<h1 id="title">Unknown Track</h1>
<p id="artist-album">Unknown Artist • Unknown Album</p>
</div>
</div>
</div>
<script type="module" src="main.js"></script>
</body>
</html>

87
public/main.css Normal file
View file

@ -0,0 +1,87 @@
:root {
--ticker-width: 600px;
}
body {
margin: 0;
padding: 0;
font-family: 'Inter', sans-serif;
background: #888;
}
#music-ticker {
/* width: fit-content; */
max-width: var(--ticker-width);
margin: 32px;
padding: 8px;
display: flex;
flex-direction: row;
gap: 10px;
border-radius: 8px;
&.outline {
border: 1px solid white;
outline: 1px solid black;
}
.artwork-container {
width: 64px;
min-width: 64px;
height: 64px;
border-radius: 4px;
overflow: hidden;
img {
width: 100%;
height: 100%;
display: block;
object-fit: cover;
}
}
#metadata {
min-width: 0;
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: center;
color: #fff;
text-shadow: 1px 1px 0 #000a;
overflow-x: clip;
}
h1 {
width: fit-content;
min-width: 0;
margin: -2px 0;
overflow-x: hidden;
white-space: nowrap;
font-size: 30px;
}
p {
width: fit-content;
min-width: 0;
margin: -2px 0;
overflow-x: hidden;
white-space: nowrap;
font-size: 22px;
font-weight: 600;
}
.marquee {
animation: 20s ease-in-out infinite marquee;
}
}
@keyframes marquee {
20% {
transform: translateX(0);
}
60% {
transform: translateX(calc(-100% + var(--ticker-width) - 74px));
}
80% {
transform: translateX(calc(-100% + var(--ticker-width) - 74px));
}
}

39
public/main.js Normal file
View file

@ -0,0 +1,39 @@
import Stateful from './silver.min.js';
const musicMeta = new Stateful({
artworkURL: 'https://arimelody.space/uploads/musicart/free2play.png',
title: 'falling asleep - house version',
artist: 'ari melody',
album: 'free2play',
});
const elArtwork = document.getElementById('artwork');
const elTitle = document.getElementById('title');
const elArtistAlbum = document.getElementById('artist-album');
const elMetadata = document.getElementById('metadata');
function setMetadata(artworkURL, title, artist, album) {
musicMeta.set({ artworkURL, title, artist, album });
}
document.addEventListener('readystatechange', () => {
musicMeta.onUpdate(value => {
elArtwork.src = value.artworkURL;
elTitle.innerText = value.title;
elArtistAlbum.innerText = `${value.artist}${value.album}`;
document.title = `Now playing: ${value.artist} - ${value.title}`;
console.log(`Now playing: ${value.artist} - ${value.title}`);
const maxWidth = elMetadata.getBoundingClientRect().width;
elTitle.classList.remove('marquee');
elArtistAlbum.classList.remove('marquee');
if (elTitle.getBoundingClientRect().width > maxWidth) {
elTitle.classList.add('marquee');
}
if (elArtistAlbum.getBoundingClientRect().width > maxWidth) {
elArtistAlbum.classList.add('marquee');
}
});
});
window.setMetadata = setMetadata;

2
public/silver.min.js vendored Normal file
View file

@ -0,0 +1,2 @@
export default class Stateful{#e;#t=[];constructor(e){this.#e=e}get(){return this.#e}set(e){let t=this.#e;this.#e=e;for(let s in this.#t)this.#t[s](e,t)}update(e){this.set(e(this.#e))}onUpdate(e){return this.#t.push(e),e}removeListener(e){this.#t=this.#t.filter((t=>t!==e))}}