Bri-Tunes/public/js/player.js

148 lines
5.0 KiB
JavaScript

(function () {
const audio = document.getElementById('player-audio');
const titleEl = document.getElementById('player-title');
const artistEl = document.getElementById('player-artist');
const coverEl = document.getElementById('player-cover');
const queueInfoEl = document.getElementById('player-queue-info');
const nextBtn = document.getElementById('player-next');
const repeatBtn = document.getElementById('player-repeat');
const playerLikeBtn = document.getElementById('player-like');
const playerFavBtn = document.getElementById('player-favorite');
let repeatOn = false;
function updatePlayerSocial(song) {
if (!playerLikeBtn || !playerFavBtn) return;
const active = !!song;
playerLikeBtn.disabled = playerFavBtn.disabled = !active;
playerLikeBtn.dataset.socialId = playerFavBtn.dataset.socialId = active ? song.id : '';
playerLikeBtn.classList.toggle('active', active && !!song.userLiked);
playerLikeBtn.setAttribute('aria-pressed', String(active && !!song.userLiked));
playerFavBtn.classList.toggle('active', active && !!song.userFavorited);
playerFavBtn.setAttribute('aria-pressed', String(active && !!song.userFavorited));
const lc = playerLikeBtn.querySelector('.social-count');
const fc = playerFavBtn.querySelector('.social-count');
if (lc) lc.textContent = active ? (song.likeCount ?? 0) : 0;
if (fc) fc.textContent = active ? (song.favoriteCount ?? 0) : 0;
}
const state = {
queue: [], // array of {id, title, artist, cover}
index: -1,
};
function render() {
const cur = state.queue[state.index];
if (!cur) {
titleEl.textContent = 'Nothing playing';
artistEl.textContent = '';
coverEl.src = '/static/vendor/cover-placeholder.svg';
queueInfoEl.textContent = '';
updatePlayerSocial(null);
return;
}
titleEl.textContent = cur.title;
artistEl.textContent = cur.artist || '';
coverEl.src = cur.cover || '/static/vendor/cover-placeholder.svg';
queueInfoEl.textContent = state.queue.length > 1
? `${state.index + 1} / ${state.queue.length}`
: '';
updatePlayerSocial(cur);
}
function highlightNowPlaying(id) {
document.querySelectorAll('.song-row').forEach(row => {
row.classList.toggle('now-playing', String(row.dataset.songId) === String(id));
});
}
function playCurrent() {
const cur = state.queue[state.index];
if (!cur) return;
window.briTunesNowPlaying = cur;
audio.src = `/stream/${cur.id}`;
audio.play()
.then(() => window.dispatchEvent(new CustomEvent('briTunes:play', { detail: cur })))
.catch((err) => console.warn('play failed', err));
render();
highlightNowPlaying(cur.id);
}
function playOne(song) {
state.queue = [song];
state.index = 0;
playCurrent();
}
function playList(list) {
if (!Array.isArray(list) || list.length === 0) return;
state.queue = list;
state.index = 0;
window.dispatchEvent(new CustomEvent('briTunes:queue', { detail: { queue: list.slice() } }));
playCurrent();
}
function next() {
if (state.index + 1 < state.queue.length) {
state.index += 1;
playCurrent();
} else if (repeatOn && state.queue.length > 0) {
state.index = 0;
playCurrent();
}
}
audio.addEventListener('ended', next);
nextBtn.addEventListener('click', next);
repeatBtn.addEventListener('click', () => {
repeatOn = !repeatOn;
repeatBtn.classList.toggle('active', repeatOn);
repeatBtn.setAttribute('aria-pressed', repeatOn);
});
window.addEventListener('briTunes:jumpto', (e) => {
const id = e.detail && e.detail.id;
if (!id) return;
const idx = state.queue.findIndex(s => String(s.id) === String(id));
if (idx === -1) return;
state.index = idx;
playCurrent();
});
window.addEventListener('briTunes:navigate', () => {
if (window.briTunesNowPlaying) highlightNowPlaying(window.briTunesNowPlaying.id);
});
// Delegated click handler for any [data-play-song] / [data-play-playlist] button.
document.addEventListener('click', (e) => {
const songBtn = e.target.closest('[data-play-song]');
if (songBtn) {
try {
const song = JSON.parse(songBtn.getAttribute('data-play-song'));
playOne(song);
} catch (err) { console.error(err); }
return;
}
const listBtn = e.target.closest('[data-play-playlist]');
if (listBtn) {
try {
const list = JSON.parse(listBtn.getAttribute('data-play-playlist'));
playList(list);
} catch (err) { console.error(err); }
return;
}
const shuffleBtn = e.target.closest('[data-shuffle-playlist]');
if (shuffleBtn) {
try {
const list = JSON.parse(shuffleBtn.getAttribute('data-shuffle-playlist'));
for (let i = list.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[list[i], list[j]] = [list[j], list[i]];
}
playList(list);
} catch (err) { console.error(err); }
}
});
render();
})();