create bug.tools
This commit is contained in:
249
js/player.js
Normal file
249
js/player.js
Normal file
@@ -0,0 +1,249 @@
|
||||
(function () {
|
||||
const playlist = [
|
||||
//{ title: "Sanctuary OS", artist: "Dusqk", src: "music/SanctuaryOS.flac" },
|
||||
{ title: "Sanctuary 1", artist: "Dusqk", src: "music/sanctuary_1.m4a" },
|
||||
{ title: "cloud zone", artist: "oooz", src: "music/cloud_zone.m4a" },
|
||||
{ title: "ketamina", artist: "yaego", src: "music/ketamina.m4a" },
|
||||
{ title: "fine night", artist: "goreshit", src: "music/fine_night.m4a" },
|
||||
{ title: "what we did in the desert", artist: "eightiesheadachetape", src: "music/what_we_did_in_the_desert.m4a" },
|
||||
{ title: "Fleeting Frozen Heart", artist: "Xxtarlit⚸", src: "music/fleeting_frozen_heart.m4a" },
|
||||
{ title: "Heaven", artist: "i\\believe\\in\\angels", src: "music/heaven.m4a" },
|
||||
{ title: "i wish to have a happy end", artist: "イア / ia", src: "music/i_wish_to_have_a_happy_end.m4a" },
|
||||
{ title: "it is not my fault and never will be", artist: "Skeinn", src: "music/it_is_not_my_fault_and_never_will_be.m4a" },
|
||||
{ title: "Sanctuary 10", artist: "Dusqk", src: "music/sanctuary_10.m4a" },
|
||||
{ title: "Lexapro Delirium", artist: "sewerslvt", src: "music/lexapro_delirium.m4a" },
|
||||
];
|
||||
|
||||
let currentIdx = -1;
|
||||
let isPlaying = false;
|
||||
let isShuffle = false;
|
||||
let isLoop = false;
|
||||
let wasEnded = false;
|
||||
|
||||
const audio = new Audio();
|
||||
audio.volume = 0.7;
|
||||
audio.preload = "metadata";
|
||||
|
||||
const elTitle = document.getElementById('mp-title');
|
||||
const elArtist = document.getElementById('mp-artist');
|
||||
const elPlay = document.getElementById('mp-play');
|
||||
const elPrev = document.getElementById('mp-prev');
|
||||
const elNext = document.getElementById('mp-next');
|
||||
const elShuffle = document.getElementById('mp-shuffle');
|
||||
const elLoop = document.getElementById('mp-loop');
|
||||
const elSeek = document.getElementById('mp-seek');
|
||||
const elFill = document.getElementById('mp-progress-fill');
|
||||
const elCurrent = document.getElementById('mp-current');
|
||||
const elDur = document.getElementById('mp-duration');
|
||||
const elVol = document.getElementById('mp-volume');
|
||||
const elList = document.getElementById('mp-playlist');
|
||||
|
||||
function buildList() {
|
||||
elList.innerHTML = '';
|
||||
if (!playlist.length) {
|
||||
const li = document.createElement('li');
|
||||
li.className = 'mp-empty';
|
||||
li.textContent = 'playlist is empty';
|
||||
elList.appendChild(li);
|
||||
return;
|
||||
}
|
||||
|
||||
playlist.forEach((track, i) => {
|
||||
const li = document.createElement('li');
|
||||
li.textContent = (i + 1) + '. ' + track.title;
|
||||
li.title = track.title + (track.artist ? ' · ' + track.artist : '');
|
||||
li.addEventListener('click', () => loadTrack(i, true));
|
||||
elList.appendChild(li);
|
||||
});
|
||||
}
|
||||
|
||||
function formatTime(s) {
|
||||
if (isNaN(s) || s < 0) return '0:00';
|
||||
const m = Math.floor(s / 60);
|
||||
const sec = Math.floor(s % 60);
|
||||
return m + ':' + (sec < 10 ? '0' : '') + sec;
|
||||
}
|
||||
|
||||
function highlightRow(idx) {
|
||||
const items = elList.querySelectorAll('li');
|
||||
items.forEach((li, i) => li.classList.toggle('playing', i === idx));
|
||||
}
|
||||
|
||||
function setPlayIcon() {
|
||||
elPlay.innerHTML = isPlaying ? '▮▮' : '▶';
|
||||
}
|
||||
|
||||
function loadTrack(idx, autoplay) {
|
||||
if (!playlist.length) return;
|
||||
currentIdx = idx;
|
||||
const t = playlist[idx];
|
||||
audio.src = t.src;
|
||||
elTitle.textContent = t.title || 'untitled';
|
||||
elArtist.textContent = t.artist || '---';
|
||||
elFill.style.width = '0%';
|
||||
elSeek.value = 0;
|
||||
elCurrent.textContent = '0:00';
|
||||
elDur.textContent = '0:00';
|
||||
highlightRow(idx);
|
||||
|
||||
if (autoplay) {
|
||||
audio.play().then(() => { isPlaying = true; setPlayIcon(); })
|
||||
.catch(() => {});
|
||||
} else {
|
||||
isPlaying = false;
|
||||
setPlayIcon();
|
||||
}
|
||||
}
|
||||
|
||||
function nextTrack() {
|
||||
if (!playlist.length) return;
|
||||
let idx;
|
||||
if (isShuffle) {
|
||||
idx = Math.floor(Math.random() * playlist.length);
|
||||
} else {
|
||||
idx = (currentIdx + 1) % playlist.length;
|
||||
}
|
||||
loadTrack(idx, true);
|
||||
}
|
||||
|
||||
function prevTrack() {
|
||||
if (!playlist.length) return;
|
||||
|
||||
if (audio.currentTime > 3) {
|
||||
audio.currentTime = 0;
|
||||
return;
|
||||
}
|
||||
let idx = (currentIdx - 1 + playlist.length) % playlist.length;
|
||||
loadTrack(idx, true);
|
||||
}
|
||||
|
||||
elPlay.addEventListener('click', () => {
|
||||
if (!playlist.length) return;
|
||||
if (currentIdx === -1) { loadTrack(0, true); return; }
|
||||
if (isPlaying) {
|
||||
audio.pause();
|
||||
isPlaying = false;
|
||||
} else {
|
||||
audio.play().catch(() => {});
|
||||
isPlaying = true;
|
||||
}
|
||||
setPlayIcon();
|
||||
});
|
||||
|
||||
elNext.addEventListener('click', nextTrack);
|
||||
elPrev.addEventListener('click', prevTrack);
|
||||
|
||||
elShuffle.addEventListener('click', () => {
|
||||
isShuffle = !isShuffle;
|
||||
elShuffle.classList.toggle('active', isShuffle);
|
||||
});
|
||||
|
||||
elLoop.addEventListener('click', () => {
|
||||
isLoop = !isLoop;
|
||||
audio.loop = isLoop;
|
||||
elLoop.classList.toggle('active', isLoop);
|
||||
});
|
||||
|
||||
function updateVolFill() {
|
||||
const pct = (elVol.value / elVol.max) * 100;
|
||||
elVol.style.background = 'linear-gradient(90deg, #a9c6f0 0%, #74a4db ' + pct + '%, #e8ecf0 ' + pct + '%)';
|
||||
}
|
||||
|
||||
elVol.addEventListener('input', () => { audio.volume = elVol.value; updateVolFill(); });
|
||||
|
||||
elSeek.addEventListener('input', () => {
|
||||
if (audio.duration) {
|
||||
audio.currentTime = (elSeek.value / 100) * audio.duration;
|
||||
}
|
||||
});
|
||||
|
||||
audio.addEventListener('timeupdate', () => {
|
||||
if (!audio.duration) return;
|
||||
const pct = (audio.currentTime / audio.duration) * 100;
|
||||
elFill.style.width = pct + '%';
|
||||
elSeek.value = pct;
|
||||
elCurrent.textContent = formatTime(audio.currentTime);
|
||||
});
|
||||
|
||||
audio.addEventListener('loadedmetadata', () => {
|
||||
elDur.textContent = formatTime(audio.duration);
|
||||
});
|
||||
|
||||
audio.addEventListener('ended', () => {
|
||||
if (!isLoop) nextTrack();
|
||||
});
|
||||
|
||||
audio.addEventListener('play', () => { isPlaying = true; setPlayIcon(); });
|
||||
audio.addEventListener('pause', () => { isPlaying = false; setPlayIcon(); });
|
||||
|
||||
function setCookie(name, value, days) {
|
||||
const d = new Date();
|
||||
d.setTime(d.getTime() + days * 86400000);
|
||||
document.cookie = name + '=' + encodeURIComponent(value) + ';expires=' + d.toUTCString() + ';path=/;SameSite=Lax';
|
||||
}
|
||||
|
||||
function getCookie(name) {
|
||||
const match = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;]*)'));
|
||||
return match ? decodeURIComponent(match[1]) : null;
|
||||
}
|
||||
|
||||
function saveState() {
|
||||
setCookie('mp_track', currentIdx, 30);
|
||||
setCookie('mp_time', Math.floor(audio.currentTime), 30);
|
||||
setCookie('mp_volume', audio.volume, 30);
|
||||
setCookie('mp_shuffle', isShuffle ? '1' : '0', 30);
|
||||
setCookie('mp_loop', isLoop ? '1' : '0', 30);
|
||||
}
|
||||
|
||||
setInterval(saveState, 5000);
|
||||
window.addEventListener('beforeunload', saveState);
|
||||
|
||||
buildList();
|
||||
setPlayIcon();
|
||||
updateVolFill();
|
||||
|
||||
const savedTrack = getCookie('mp_track');
|
||||
const savedTime = getCookie('mp_time');
|
||||
const savedVol = getCookie('mp_volume');
|
||||
const savedShuf = getCookie('mp_shuffle');
|
||||
const savedLoop = getCookie('mp_loop');
|
||||
|
||||
if (savedVol !== null) {
|
||||
audio.volume = parseFloat(savedVol);
|
||||
elVol.value = audio.volume;
|
||||
updateVolFill();
|
||||
}
|
||||
|
||||
if (savedShuf === '1') {
|
||||
isShuffle = true;
|
||||
elShuffle.classList.add('active');
|
||||
}
|
||||
|
||||
if (savedLoop === '1') {
|
||||
isLoop = true;
|
||||
audio.loop = true;
|
||||
elLoop.classList.add('active');
|
||||
}
|
||||
|
||||
if (savedTrack !== null && playlist[parseInt(savedTrack)]) {
|
||||
const idx = parseInt(savedTrack);
|
||||
loadTrack(idx, false);
|
||||
if (savedTime !== null) {
|
||||
audio.addEventListener('loadedmetadata', function onceSeek() {
|
||||
audio.currentTime = Math.min(parseInt(savedTime), audio.duration || 0);
|
||||
audio.removeEventListener('loadedmetadata', onceSeek);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function autoplayOnce() {
|
||||
if (currentIdx >= 0 && !isPlaying) {
|
||||
audio.play().catch(() => {});
|
||||
}
|
||||
document.removeEventListener('click', autoplayOnce);
|
||||
document.removeEventListener('keydown', autoplayOnce);
|
||||
}
|
||||
|
||||
document.addEventListener('click', autoplayOnce);
|
||||
document.addEventListener('keydown', autoplayOnce);
|
||||
})();
|
||||
Reference in New Issue
Block a user