Files
arsam 758dd8fbff Import source: js/player
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
2026-09-18 23:20:58 +03:30

221 lines
5.2 KiB
JavaScript

"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();