top2000/public/index.js
2025-12-27 13:48:06 +01:00

210 lines
6.5 KiB
JavaScript

window.onload = async (e) => {
const res = await (await fetch("/ranking", {})).json();
window.ranking = res.positions;
const offset = +window.location.search.slice(1);
window.offset = offset;
const canvas = document.querySelector('#graph');
canvas.width = 0.35 * window.innerWidth;
canvas.height = 0.50 * window.innerHeight;
const now = Date.now() - offset * 1000;
const curr = window.ranking.find(it => it.broadcastUnixTime < now);
var current = curr.position.current;
update(curr)
setInterval(() => {
const now = Date.now() - offset * 1000;
const curr = window.ranking.find(it => it.broadcastUnixTime < now);
if (curr.position.current != current) {
current = curr.position.current;
update(curr);
}
}, 1000);
};
function update(curr) {
console.log("Updating");
document.querySelector("#song-image").src = curr.track.coverUrl;
const diff = document.querySelector("#song-diff");
diff.innerText = curr.position.label;
diff.classList = curr.position.type;
document.querySelector("#title").innerText = curr.track.title;
document.querySelector("#artist").innerText = `${curr.track.artist} (#${curr.position.current})`;
getGraph(curr.track.artist, curr.track.title);
queue(curr.position.current);
getTrivia(curr)
}
async function getGraph(artist, title) {
const res = await (await fetch(`/graph/${artist.replace("?", "")}/${title}`, {})).json();
const cv = document.querySelector("#graph");
const ctx = cv.getContext('2d');
const width = cv.width;
const labelSpace = 100;
const barWidth = (width - labelSpace) / res.length;
const height = cv.height;
const graphHeight = height - 60;
ctx.clearRect(0, 0, width, height);
// Dots
let minPos = 2001;
let maxPos = -1;
let minYear = undefined;
let maxYear = undefined;
for (let i = 0; i < res.length; i++) {
const position = res[i].position;
if (position != undefined) {
if (position < minPos) {
minPos = position;
minYear = i;
}
if (position > maxPos) {
maxPos = position;
maxYear = i;
}
}
const fraction = (position / 2000) * graphHeight;
ctx.beginPath();
ctx.fillStyle = "white";
ctx.arc(labelSpace + barWidth * i + 10, fraction, 2.5, 0, 2*Math.PI);
ctx.fill();
}
// Min and Max lines
for (let p of [minPos, maxPos]) {
const fraction = (p / 2000) * graphHeight;
ctx.beginPath();
ctx.strokeStyle = "#808080";
ctx.lineWidth = 1;
ctx.moveTo(labelSpace, fraction);
ctx.lineTo(width, fraction);
ctx.stroke();
ctx.font = "40px sans-serif";
ctx.fillStyle = "#808080";
ctx.fillText(p.toString(), 0, fraction);
}
// Y axis
for (let i = 200; i <= 2000; i += 200) {
const fraction = (i / 2000) * graphHeight;
const lineDist = 70;
if (Math.abs(i-minPos) <= lineDist || Math.abs(i-maxPos) <= lineDist) {
continue;
}
ctx.beginPath();
ctx.strokeStyle = "#808080";
ctx.lineWidth = 1;
ctx.moveTo(labelSpace, fraction);
ctx.lineTo(width, fraction);
ctx.stroke();
ctx.font = "40px sans-serif";
ctx.fillStyle = "#808080";
ctx.fillText(i.toString(), 0, fraction);
}
// X axis
const startYear = 1999;
const endYear = 2025;
const years = endYear-startYear+1;
for (let i of [...new Set([res.findIndex(it => it.position != undefined), minYear, maxYear])]) {
const fraction = 10 + labelSpace + (i * (width - labelSpace)) / (years);
console.log(fraction)
let year = (100 + i - 1) % 100;
ctx.beginPath();
ctx.strokeStyle = "#808080";
ctx.lineWidth = 1;
ctx.moveTo(fraction, 0);
ctx.lineTo(fraction, graphHeight);
ctx.stroke();
ctx.font = "40px sans-serif";
ctx.fillStyle = "#808080";
ctx.fillText(year.toString(), fraction-3, height-10);
}
// Line plot
for (let i = 0; i < res.length-1; i++) {
const curr = res[i].position;
const next = res[i+1].position;
const currFraction = (curr / 2000) * graphHeight;
const nextFraction = (next / 2000) * graphHeight;
// ctx.fillRect(barWidth * i, 0, barWidth, position / 10)
ctx.beginPath();
ctx.strokeStyle = "white";
ctx.lineWidth = 5;
ctx.lineCap = "round";
ctx.moveTo(labelSpace + barWidth * i + 10, currFraction);
ctx.lineTo(labelSpace + barWidth * (i+1) + 10, nextFraction);
ctx.stroke();
}
}
function queue(currPos) {
const upcoming = window.ranking.filter(it => currPos - it.position.current < 10 && currPos - it.position.current >= -1)
const queueEl = document.querySelector('#queue')
queueEl.innerHTML = "";
for (const song of upcoming.reverse()) {
const broadcastDT = new Date(song.broadcastUnixTime + offset * 1000);
queueEl.innerHTML += `
<div class="queue-item${song.position.current == currPos ? " playing" : ""}">
<img class="queue-image" src="${song.track.coverUrl}">
<div class="queue-right-wrapper">
<p class="song-title">${song.track.title}</p>
<p class="song-artist">${song.track.artist}</p>
<span class="song-time">
<span class="song-diff queue-${song.position.type}">${song.position.label}</span>
<span class="song-time">${broadcastDT.getHours().toString().padStart(2, "0")}:${broadcastDT.getMinutes().toString().padStart(2, "0")}</span>
</span>
</div>
</div>
`
}
}
async function getTrivia(curr) {
const res = await (await fetch(`/trivia/${curr.track.artist.replace("?", "")}`, {})).json();
document.querySelector("#trivia-songcount").innerText = `${curr.track.artist} staat ${res.length} keer in de lijst.`;
const artistsSongs = res.map(function (it) {
return {
position: window.ranking.find(rank => rank.track.title == it.title)?.position?.current,
title: it.title
}
}).filter(it => it.position != undefined).sort((a,b) => a.position - b.position)
if (artistsSongs[0].position == curr.position.current) {
document.querySelector("#trivia-first").innerText = `Dit is het hoogste nummer van ${curr.track.artist} in de lijst.`;
} else {
document.querySelector("#trivia-first").innerText = `Het hoogste nummer van ${curr.track.artist} is ${artistsSongs[0].title} (#${artistsSongs[0].position}).`;
}
if (artistsSongs.at(-1).position == curr.position.current) {
document.querySelector("#trivia-last").innerText = `Dit is het laagste nummer van ${curr.track.artist} in de lijst.`;
} else {
document.querySelector("#trivia-last").innerText = `Het laagste nummer van ${curr.track.artist} is ${artistsSongs.at(-1).title} (#${artistsSongs.at(-1).position}).`;
}
}