"use strict"; const Player = { audio: new Audio(), ctx: null, analyser: null, freq: null, queue: [], index: -1, dur: 0, cur: 0, playing: false, endedFlag: false, onState: null, onTime: null, onTrack: null, init() { this.volume = Store.volume; this.shuffle = Store.shuffle; this.repeat = Store.repeat; this.audio.volume = this.volume; this.audio.preload = "auto"; this.audio.addEventListener("loadedmetadata", () => { this.dur = isFinite(this.audio.duration) ? this.audio.duration : 0; this.emitTime(); }); this.audio.addEventListener("durationchange", () => { this.dur = isFinite(this.audio.duration) ? this.audio.duration : this.dur; this.emitTime(); }); this.audio.addEventListener("timeupdate", () => { this.cur = this.audio.currentTime; this.emitTime(); }); this.audio.addEventListener("play", () => { this.playing = true; if (this.onState) this.onState(true); }); this.audio.addEventListener("pause", () => { this.playing = false; if (this.onState) this.onState(false); }); this.audio.addEventListener("ended", () => this.onEnded()); this.audio.addEventListener("error", () => { if (this.endedFlag) return; }); }, emitTime() { if (this.onTime) this.onTime(this.cur, this.dur); }, current() { return this.queue[this.index] || null; }, playContext(ids, start) { if (!ids || !ids.length) return; this.queue = ids.map((x) => (typeof x === "object" && x != null ? x : D.song(x))).filter(Boolean); this.index = start || 0; this.loadCurrent(); }, loadCurrent() { const s = this.current(); if (!s) return; this.endedFlag = false; this.audio.src = s.audio; this.audio.play().catch(() => {}); Store.pushHistory(s.id); if (this.onTrack) this.onTrack(s); }, togglePlay() { if (!this.current()) { this.playContext(D.topSongs(40).map((s) => s.id), 0); return; } if (this.audio.paused) this.audio.play().catch(() => {}); else this.audio.pause(); }, next() { if (!this.queue.length) return; if (this.shuffle) { if (this.queue.length < 2) return; let i; do { i = Math.floor(Math.random() * this.queue.length); } while (i === this.index); this.index = i; this.loadCurrent(); return; } if (this.index + 1 < this.queue.length) { this.index++; this.loadCurrent(); } else if (this.repeat === 1) { this.index = 0; this.loadCurrent(); } else { this.audio.pause(); this.cur = 0; this.dur = this.current() ? this.current().dur : 0; this.emitTime(); } }, prev() { if (!this.queue.length) return; if (this.audio.currentTime > 3) { this.seek(0); return; } if (this.index - 1 >= 0) { this.index--; this.loadCurrent(); } else if (this.repeat === 1 && this.queue.length > 1) { this.index = this.queue.length - 1; this.loadCurrent(); } else { this.seek(0); } }, onEnded() { if (this.repeat === 2) { this.audio.currentTime = 0; this.audio.play().catch(() => {}); return; } this.next(); }, seek(t) { if (!this.current()) return; const clamp = Math.max(0, Math.min(t, this.dur || t)); this.audio.currentTime = clamp; this.cur = clamp; this.emitTime(); }, setVolume(v) { this.volume = Math.max(0, Math.min(1, v)); this.audio.volume = this.volume; Store.volume = this.volume; Store.save(); }, toggleMute() { if (this.audio.volume > 0) { this._lastVol = this.audio.volume; this.setVolume(0); } else { this.setVolume(this._lastVol || 0.85); } }, toggleShuffle() { this.shuffle = !this.shuffle; Store.shuffle = this.shuffle; Store.save(); }, cycleRepeat() { this.repeat = (this.repeat + 1) % 3; Store.repeat = this.repeat; Store.save(); }, removeAt(i) { if (i < 0 || i >= this.queue.length) return; const removedCurrent = i === this.index; this.queue.splice(i, 1); if (!this.queue.length) { this.audio.pause(); this.audio.removeAttribute("src"); this.index = -1; this.cur = 0; this.dur = 0; this.emitTime(); if (this.onTrack) this.onTrack(null); return; } if (removedCurrent) { if (i >= this.queue.length) this.index = this.queue.length - 1; else this.index = i; this.loadCurrent(); } else if (i < this.index) { this.index--; } }, ensureGraph() { if (this.ctx) return; const Ctx = window.AudioContext || window.webkitAudioContext; if (!Ctx) return; try { this.ctx = new Ctx(); const src = this.ctx.createMediaElementSource(this.audio); this.analyser = this.ctx.createAnalyser(); this.analyser.fftSize = 256; this.analyser.smoothingTimeConstant = 0.82; src.connect(this.analyser); this.analyser.connect(this.ctx.destination); this.freq = new Uint8Array(this.analyser.frequencyBinCount); } catch (e) {} }, resumeGraph() { if (this.ctx && this.ctx.state === "suspended") { this.ctx.resume().catch(() => {}); } } }; Player.init();