cyp/app/js/elements/player.js

180 lines
5.1 KiB
JavaScript
Raw Normal View History

2020-03-10 05:24:31 +08:00
import * as art from "../art.js";
import * as html from "../html.js";
import * as format from "../format.js";
import Component from "../component.js";
2019-03-21 17:32:58 +08:00
2020-03-14 06:01:16 +08:00
2020-05-06 05:03:36 +08:00
const ELAPSED_PERIOD = 500;
2019-03-21 17:32:58 +08:00
2020-03-09 05:11:46 +08:00
class Player extends Component {
constructor() {
2020-03-10 17:42:13 +08:00
super();
2020-05-06 05:03:36 +08:00
this._current = {
song: {},
elapsed: 0,
at: 0,
volume: 0
};
2020-05-07 21:43:14 +08:00
this._toggleVolume = 0;
2020-05-06 05:03:36 +08:00
const DOM = {};
const all = this.querySelectorAll("[class]");
[...all].forEach(node => DOM[node.className] = node);
DOM.progress = DOM.timeline.querySelector("x-range");
DOM.volume = DOM.volume.querySelector("x-range");
this._dom = DOM;
2020-03-09 16:26:10 +08:00
}
2020-05-06 05:03:36 +08:00
handleEvent(e) {
switch (e.type) {
case "idle-change":
let hasOptions = e.detail.includes("options");
let hasPlayer = e.detail.includes("player");
2020-05-09 16:55:41 +08:00
let hasMixer = e.detail.includes("mixer");
(hasOptions || hasPlayer || hasMixer) && this._updateStatus();
2020-05-06 05:03:36 +08:00
hasPlayer && this._updateCurrent();
break;
}
2020-03-09 21:26:39 +08:00
}
2020-03-09 16:26:10 +08:00
_onAppLoad() {
2020-05-06 05:03:36 +08:00
this._addEvents();
this._updateStatus();
this._updateCurrent();
2020-05-08 02:06:43 +08:00
this._app.addEventListener("idle-change", this);
2020-05-06 05:03:36 +08:00
setInterval(() => this._updateElapsed(), ELAPSED_PERIOD);
2020-03-09 05:11:46 +08:00
}
2019-03-21 17:32:58 +08:00
2020-05-06 05:03:36 +08:00
async _updateStatus() {
const data = await this._mpd.status();
2019-04-12 19:34:28 +08:00
2020-05-06 05:03:36 +08:00
this._updateFlags(data);
this._updateVolume(data);
// rebase the time sync
this._current.elapsed = Number(data["elapsed"] || 0);
this._current.at = performance.now();
}
2019-04-12 19:34:28 +08:00
2020-05-06 05:03:36 +08:00
async _updateCurrent() {
const data = await this._mpd.currentSong();
const DOM = this._dom;
2019-04-12 19:34:28 +08:00
2020-05-06 05:03:36 +08:00
if (data["file"] != this._current.song["file"]) { // changed song
if (data["file"]) { // is there a song at all?
DOM.title.textContent = data["Title"] || format.fileName(data["file"]);
DOM.subtitle.textContent = format.subtitle(data, {duration:false});
2020-05-07 21:43:14 +08:00
let duration = Number(data["duration"]);
DOM.duration.textContent = format.time(duration);
DOM.progress.max = duration;
DOM.progress.disabled = false;
2020-05-06 05:03:36 +08:00
} else {
DOM.title.textContent = "";
DOM.subtitle.textContent = "";
2020-05-07 21:43:14 +08:00
DOM.progress.value = 0;
DOM.progress.disabled = true;
2020-05-06 05:03:36 +08:00
}
this._dispatchSongChange(data);
}
2019-04-12 19:34:28 +08:00
2020-12-27 03:46:42 +08:00
let artistNew = data["Artist"] || data["AlbumArtist"];
let artistOld = this._current.song["Artist"] || this._current.song["AlbumArtist"];
2020-05-06 05:03:36 +08:00
let albumNew = data["Album"];
let albumOld = this._current.song["Album"];
2019-04-09 22:08:09 +08:00
2020-05-06 05:03:36 +08:00
Object.assign(this._current.song, data);
2019-03-30 19:57:01 +08:00
2020-05-06 05:03:36 +08:00
if (artistNew != artistOld || albumNew != albumOld) { // changed album (art)
html.clear(DOM.art);
let src = await art.get(this._mpd, artistNew, data["Album"], data["file"]);
if (src) {
html.node("img", {src}, "", DOM.art);
} else {
html.icon("music", DOM.art);
}
}
2019-03-21 17:32:58 +08:00
}
2020-05-06 05:03:36 +08:00
_updateElapsed() {
const DOM = this._dom;
2019-03-21 17:32:58 +08:00
2020-05-06 05:03:36 +08:00
let elapsed = 0;
if (this._current.song["file"]) {
elapsed = this._current.elapsed;
if (this.dataset.state == "play") { elapsed += (performance.now() - this._current.at)/1000; }
}
DOM.progress.value = elapsed;
DOM.elapsed.textContent = format.time(elapsed);
this._app.style.setProperty("--progress", DOM.progress.value/DOM.progress.max);
2020-03-09 05:11:46 +08:00
}
2019-03-21 17:32:58 +08:00
2020-05-06 05:03:36 +08:00
_updateFlags(data) {
let flags = [];
if (data["random"] == "1") { flags.push("random"); }
if (data["repeat"] == "1") { flags.push("repeat"); }
2020-05-06 19:55:01 +08:00
if (data["volume"] === "0") { flags.push("mute"); } // strict, because volume might be missing
2020-05-06 05:03:36 +08:00
this.dataset.flags = flags.join(" ");
this.dataset.state = data["state"];
2020-03-09 05:11:46 +08:00
}
2019-03-21 17:32:58 +08:00
2020-05-06 05:03:36 +08:00
_updateVolume(data) {
2020-03-09 05:11:46 +08:00
const DOM = this._dom;
2020-05-06 05:03:36 +08:00
2020-03-09 05:11:46 +08:00
if ("volume" in data) {
2020-05-06 05:03:36 +08:00
let volume = Number(data["volume"]);
2019-03-22 22:35:04 +08:00
2020-03-09 05:11:46 +08:00
DOM.mute.disabled = false;
DOM.volume.disabled = false;
2020-05-06 05:03:36 +08:00
DOM.volume.value = volume;
2019-03-21 17:32:58 +08:00
2020-05-07 21:43:14 +08:00
if (volume == 0 && this._current.volume > 0) { this._toggleVolume = this._current.volume; } // muted
if (volume > 0 && this._current.volume == 0) { this._toggleVolume = 0; } // restored
2020-05-06 05:03:36 +08:00
this._current.volume = volume;
2020-03-09 05:11:46 +08:00
} else {
DOM.mute.disabled = true;
DOM.volume.disabled = true;
DOM.volume.value = 50;
}
2020-05-06 05:03:36 +08:00
}
2019-03-21 17:32:58 +08:00
2020-05-06 05:03:36 +08:00
_addEvents() {
const DOM = this._dom;
2019-04-12 19:34:28 +08:00
2020-05-06 05:03:36 +08:00
DOM.play.addEventListener("click", _ => this._app.mpd.command("play"));
DOM.pause.addEventListener("click", _ => this._app.mpd.command("pause 1"));
DOM.prev.addEventListener("click", _ => this._app.mpd.command("previous"));
DOM.next.addEventListener("click", _ => this._app.mpd.command("next"));
2020-03-17 18:02:38 +08:00
2020-05-06 05:03:36 +08:00
DOM.random.addEventListener("click", _ => {
let isRandom = this.dataset.flags.split(" ").includes("random");
this._app.mpd.command(`random ${isRandom ? "0" : "1"}`);
});
DOM.repeat.addEventListener("click", _ => {
let isRepeat = this.dataset.flags.split(" ").includes("repeat");
this._app.mpd.command(`repeat ${isRepeat ? "0" : "1"}`);
});
2020-03-14 06:01:16 +08:00
2020-05-06 19:27:44 +08:00
DOM.progress.addEventListener("input", e => {
let elapsed = e.target.valueAsNumber;
this._current.elapsed = elapsed;
this._current.at = performance.now();
this._app.mpd.command(`seekcur ${elapsed}`);
});
2020-03-09 01:06:54 +08:00
2020-05-06 19:27:44 +08:00
DOM.volume.addEventListener("input", e => this._app.mpd.command(`setvol ${e.target.valueAsNumber}`));
2020-05-09 16:55:41 +08:00
DOM.mute.addEventListener("click", _ => this._app.mpd.command(`setvol ${this._toggleVolume}`));
2020-03-09 05:11:46 +08:00
}
2020-03-09 01:06:54 +08:00
2020-03-09 16:26:10 +08:00
_dispatchSongChange(detail) {
2020-03-09 05:11:46 +08:00
const e = new CustomEvent("song-change", {detail});
2020-03-09 16:26:10 +08:00
this._app.dispatchEvent(e);
2020-03-09 05:11:46 +08:00
}
2020-03-09 01:06:54 +08:00
}
2020-03-09 05:11:46 +08:00
customElements.define("cyp-player", Player);