initial commit, you are welcome odette
This commit is contained in:
44
services/brightness.js
Normal file
44
services/brightness.js
Normal file
@ -0,0 +1,44 @@
|
||||
class Brightness extends Service {
|
||||
static {
|
||||
Service.register(
|
||||
this,
|
||||
{},
|
||||
{
|
||||
screen: ["float", "rw"],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
_screen = 0;
|
||||
|
||||
get screen() {
|
||||
return this._screen;
|
||||
}
|
||||
|
||||
set screen(percent) {
|
||||
if (percent < 0) percent = 0;
|
||||
|
||||
if (percent > 1) percent = 1;
|
||||
|
||||
Utils.execAsync(`brightnessctl s ${percent * 100}% -q`)
|
||||
.then(() => {
|
||||
this._screen = percent;
|
||||
this.changed("screen");
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
try {
|
||||
this._screen =
|
||||
Number(Utils.exec("brightnessctl g")) /
|
||||
Number(Utils.exec("brightnessctl m"));
|
||||
} catch (error) {
|
||||
console.error("missing dependancy: brightnessctl");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const service = new Brightness();
|
||||
export default service;
|
361
services/mpd.js
Normal file
361
services/mpd.js
Normal file
@ -0,0 +1,361 @@
|
||||
import Gio from "gi://Gio";
|
||||
|
||||
Gio._promisify(Gio.DataInputStream.prototype, "read_line_async");
|
||||
|
||||
class Mpd extends Service {
|
||||
static {
|
||||
Service.register(
|
||||
this,
|
||||
{},
|
||||
{
|
||||
//TODO: parse some properties like duration into number?
|
||||
partition: ["string", "r"],
|
||||
volume: ["string", "r"],
|
||||
repeat: ["string", "r"],
|
||||
random: ["string", "r"],
|
||||
single: ["string", "r"],
|
||||
consume: ["string", "r"],
|
||||
playlist: ["string", "r"],
|
||||
playlistlength: ["string", "r"],
|
||||
state: ["string", "r"],
|
||||
song: ["string", "r"],
|
||||
songid: ["string", "r"],
|
||||
nextsong: ["string", "r"],
|
||||
nextsongid: ["string", "r"],
|
||||
elapsed: ["string", "r"],
|
||||
duration: ["string", "r"],
|
||||
bitrate: ["string", "r"],
|
||||
mixrampdb: ["string", "r"],
|
||||
audio: ["string", "r"],
|
||||
|
||||
file: ["string", "r"],
|
||||
"Last-Modified": ["string", "r"],
|
||||
Artist: ["string", "r"],
|
||||
Title: ["string", "r"],
|
||||
Album: ["string", "r"],
|
||||
Pos: ["string", "r"],
|
||||
Id: ["string", "r"],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#socket;
|
||||
|
||||
#inputStream;
|
||||
#outputStream;
|
||||
|
||||
_decoder = new TextDecoder();
|
||||
_encoder = new TextEncoder();
|
||||
_messageHandlerQueue = [];
|
||||
|
||||
//TODO: more properties?
|
||||
|
||||
// Status
|
||||
_partition;
|
||||
_volume;
|
||||
_repeat;
|
||||
_random;
|
||||
_single;
|
||||
_consume;
|
||||
_playlist;
|
||||
_playlistlength;
|
||||
_state;
|
||||
_song;
|
||||
_songid;
|
||||
_nextsong;
|
||||
_nextsongid;
|
||||
_elapsed;
|
||||
_duration;
|
||||
_bitrate;
|
||||
_mixrampdb;
|
||||
_audio;
|
||||
|
||||
_file;
|
||||
_LastModified;
|
||||
_Artist;
|
||||
_Title;
|
||||
_Album;
|
||||
_Pos;
|
||||
_Id;
|
||||
|
||||
get partition() {
|
||||
return this._partition;
|
||||
}
|
||||
|
||||
get volume() {
|
||||
return this._volume;
|
||||
}
|
||||
|
||||
get repeat() {
|
||||
return this._repeat;
|
||||
}
|
||||
|
||||
get random() {
|
||||
return this._random;
|
||||
}
|
||||
|
||||
get single() {
|
||||
return this._single;
|
||||
}
|
||||
|
||||
get consume() {
|
||||
return this._consume;
|
||||
}
|
||||
|
||||
get playlist() {
|
||||
return this._playlist;
|
||||
}
|
||||
|
||||
get playlistlength() {
|
||||
return this._playlistlength;
|
||||
}
|
||||
|
||||
get state() {
|
||||
return this._state;
|
||||
}
|
||||
|
||||
get song() {
|
||||
return this._song;
|
||||
}
|
||||
|
||||
get songid() {
|
||||
return this._songid;
|
||||
}
|
||||
|
||||
get nextsong() {
|
||||
return this._nextsong;
|
||||
}
|
||||
|
||||
get nextsongid() {
|
||||
return this._nextsongid;
|
||||
}
|
||||
|
||||
get elapsed() {
|
||||
return this._elapsed;
|
||||
}
|
||||
|
||||
get duration() {
|
||||
return this._duration;
|
||||
}
|
||||
|
||||
get bitrate() {
|
||||
return this._bitrate;
|
||||
}
|
||||
|
||||
get mixrampdb() {
|
||||
return this._mixrampdb;
|
||||
}
|
||||
|
||||
get audio() {
|
||||
return this._audio;
|
||||
}
|
||||
|
||||
get file() {
|
||||
return this._file;
|
||||
}
|
||||
|
||||
get Last_Modified() {
|
||||
return this._LastModified;
|
||||
}
|
||||
|
||||
get Artist() {
|
||||
return this._Artist;
|
||||
}
|
||||
|
||||
get Title() {
|
||||
return this._Title;
|
||||
}
|
||||
|
||||
get Album() {
|
||||
return this._Album;
|
||||
}
|
||||
|
||||
get Pos() {
|
||||
return this._Pos;
|
||||
}
|
||||
|
||||
get Id() {
|
||||
return this._Id;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._initSocket();
|
||||
}
|
||||
|
||||
async _initSocket() {
|
||||
try {
|
||||
this.#socket = new Gio.SocketClient().connect_to_host(
|
||||
"localhost",
|
||||
6600,
|
||||
null,
|
||||
);
|
||||
|
||||
this.#inputStream = new Gio.DataInputStream({
|
||||
base_stream: this.#socket.get_input_stream(),
|
||||
});
|
||||
|
||||
this.#outputStream = new Gio.DataOutputStream({
|
||||
base_stream: this.#socket.get_output_stream(),
|
||||
});
|
||||
|
||||
this._watchSocket();
|
||||
|
||||
//init properties
|
||||
//[TODO): init more properties?
|
||||
|
||||
this.send("status")
|
||||
.then(this._updateProperties.bind(this))
|
||||
.catch(logError);
|
||||
this.send("currentsong")
|
||||
.then(this._updateProperties.bind(this))
|
||||
.catch(logError);
|
||||
} catch (e) {
|
||||
logError(e);
|
||||
}
|
||||
}
|
||||
|
||||
async _watchSocket() {
|
||||
let bufferedLines = [];
|
||||
while (true) {
|
||||
const [rawData] = await this.#inputStream.read_line_async(0, null);
|
||||
const data = this._decoder.decode(rawData);
|
||||
if (data == null) continue;
|
||||
bufferedLines.push(data);
|
||||
const { response, remain } = this._parseResponse(bufferedLines);
|
||||
bufferedLines = remain;
|
||||
|
||||
if (!response) continue;
|
||||
switch (response.type) {
|
||||
case "version":
|
||||
console.log(`MPD Server Version ${response.payload}`);
|
||||
break;
|
||||
case "error":
|
||||
this._handleMessage(new Error(response.payload), null);
|
||||
break;
|
||||
case "data":
|
||||
this._handleMessage(null, response.payload);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_parseResponse(lines) {
|
||||
let response;
|
||||
let beginLine = 0;
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
|
||||
const version = line.match(/^OK MPD (.+)/);
|
||||
const error = line.match(/^ACK \[.*] {.*} (.+)/);
|
||||
|
||||
if (version) {
|
||||
response = { type: "version", payload: version[1] };
|
||||
beginLine = i + 1;
|
||||
} else if (error) {
|
||||
response = { type: "error", payload: error[1] };
|
||||
beginLine = i + 1;
|
||||
} else if (line === "OK") {
|
||||
response = {
|
||||
type: "data",
|
||||
payload: lines.slice(beginLine, i).join("\n"),
|
||||
};
|
||||
beginLine = i + 1;
|
||||
}
|
||||
}
|
||||
return { response, remain: lines.slice(beginLine) };
|
||||
}
|
||||
|
||||
_handleMessage(err, msg) {
|
||||
const { func } = this._messageHandlerQueue.shift();
|
||||
func(err, msg);
|
||||
if (this._messageHandlerQueue.length === 0) {
|
||||
this._idle();
|
||||
}
|
||||
}
|
||||
|
||||
async send(data) {
|
||||
data = data.trim();
|
||||
const isIdle = data === "idle";
|
||||
|
||||
if (this._messageHandlerQueue[0]?.isIdle) {
|
||||
this.#outputStream.write(this._encoder.encode("noidle\n"), null);
|
||||
}
|
||||
this.#outputStream.write(this._encoder.encode(`${data}\n`), null);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this._messageHandlerQueue.push({
|
||||
isIdle,
|
||||
func: (err, msg) => {
|
||||
if (err != null) reject(err);
|
||||
resolve(msg);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_idle() {
|
||||
this.send("idle")
|
||||
.then((msg) => {
|
||||
for (const line of msg.split("\n")) {
|
||||
const subsystem = /changed: (\w+)/.exec(line);
|
||||
if (subsystem == null) continue;
|
||||
|
||||
//TODO: only update those things that could have
|
||||
//changed using a switch over the subsystems
|
||||
this.send("status")
|
||||
.then(this._updateProperties.bind(this))
|
||||
.catch(logError);
|
||||
this.send("currentsong")
|
||||
.then(this._updateProperties.bind(this))
|
||||
.catch(logError);
|
||||
/*
|
||||
switch(subsystem[1]) {
|
||||
case "player":
|
||||
break;
|
||||
}*/
|
||||
}
|
||||
})
|
||||
.catch(logError);
|
||||
}
|
||||
|
||||
_updateProperties(msg) {
|
||||
for (const line of msg.split("\n")) {
|
||||
const keyValue = line.match(/(.*): (.*)/);
|
||||
if (keyValue == null) continue;
|
||||
const deprecatedKeys = [
|
||||
"time",
|
||||
"Time", //deprecated
|
||||
"Format", //same as audio
|
||||
];
|
||||
if (deprecatedKeys.includes(keyValue[1])) continue;
|
||||
if (!this.hasOwnProperty(`_${keyValue[1]}`)) continue;
|
||||
this.updateProperty(keyValue[1], keyValue[2]);
|
||||
this.emit("changed");
|
||||
}
|
||||
}
|
||||
|
||||
setCrossfade = (seconds) => this.send(`crossfade ${seconds}`);
|
||||
setVolume = (volume) => this.send(`setvol ${volume}`);
|
||||
|
||||
toggleShuffle = () => this.send(`random ${+this._random ? "0" : "1"}`);
|
||||
toggleRepeat = () => this.send(`repeat ${+this._repeat ? "0" : "1"}`);
|
||||
|
||||
next = () => this.send("next");
|
||||
playPause = () => this.send(`pause ${this._state === "pause" ? "0" : "1"}`);
|
||||
pause = () => this.send("pause 1");
|
||||
play = () => this.send("pause 0");
|
||||
playSong = (songpos) => this.send(`play ${songpos}`);
|
||||
playSongId = (songid) => this.send(`playid ${songid}`);
|
||||
seekSong = (songpos, time) => this.send(`seek ${songpos} ${time}`);
|
||||
seekSongId = (songid, time) => this.send(`seekid ${songid} ${time}`);
|
||||
seekCur = (time) => this.send(`seekcur ${time}`);
|
||||
previous = () => this.send("previous");
|
||||
stop = () => this.send("stop");
|
||||
|
||||
clearQueue = () => this.send("clear");
|
||||
}
|
||||
|
||||
const service = new Mpd;
|
||||
export default service;
|
Reference in New Issue
Block a user