279 lines
9.6 KiB
JavaScript
279 lines
9.6 KiB
JavaScript
|
import Gio from "gi://Gio";
|
||
|
import GLib from "gi://GLib";
|
||
|
import GObject from "gi://GObject";
|
||
|
import St from 'gi://St';
|
||
|
|
||
|
|
||
|
import {Slider} from 'resource:///org/gnome/shell/ui/slider.js';
|
||
|
|
||
|
import { loadInterfaceXML } from 'resource:///org/gnome/shell/misc/fileUtils.js';
|
||
|
|
||
|
let timeout;
|
||
|
|
||
|
export class ProgressBarManager extends Slider {
|
||
|
_init(mediaSection) {
|
||
|
super._init(0);
|
||
|
|
||
|
const DBusIface = loadInterfaceXML('org.freedesktop.DBus');
|
||
|
const DBusProxy = Gio.DBusProxy.makeProxyWrapper(DBusIface);
|
||
|
|
||
|
this._dbusProxy = new DBusProxy(Gio.DBus.session,
|
||
|
'org.freedesktop.DBus',
|
||
|
'/org/freedesktop/DBus',
|
||
|
this._onProxyReady.bind(this));
|
||
|
|
||
|
|
||
|
this._mediaSection = mediaSection;
|
||
|
|
||
|
|
||
|
this.signals = [];
|
||
|
this.bars = {};
|
||
|
}
|
||
|
|
||
|
_addProgress(name, owners, newOwner, oldOwner) {
|
||
|
for (let i of this._mediaSection._messages) {
|
||
|
if (i._player._busName === name) {
|
||
|
if (owners && !newOwner && oldOwner)
|
||
|
return;
|
||
|
|
||
|
try {
|
||
|
if (i.get_child().get_last_child().get_child_at_index(1) instanceof ProgressBar)
|
||
|
return;
|
||
|
} catch {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
let length;
|
||
|
|
||
|
const MprisPlayerIface = loadInterfaceXML('org.mpris.MediaPlayer2.Player');
|
||
|
const MprisPlayerProxy = Gio.DBusProxy.makeProxyWrapper(MprisPlayerIface);
|
||
|
|
||
|
let playerProxy = new MprisPlayerProxy(Gio.DBus.session, name, '/org/mpris/MediaPlayer2');
|
||
|
|
||
|
try {
|
||
|
length = playerProxy.get_connection().call_sync(
|
||
|
name,
|
||
|
"/org/mpris/MediaPlayer2",
|
||
|
"org.freedesktop.DBus.Properties",
|
||
|
"Get",
|
||
|
new GLib.Variant("(ss)", ["org.mpris.MediaPlayer2.Player", "Metadata"]),
|
||
|
null,
|
||
|
Gio.DBusCallFlags.NONE,
|
||
|
50,
|
||
|
null
|
||
|
).recursiveUnpack()[0]['mpris:length'] / 60000000;
|
||
|
if (!length)
|
||
|
return;
|
||
|
} catch (e) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
let timestamp1 = new St.Label({
|
||
|
style_class: "progressbar-timestamp"
|
||
|
});
|
||
|
timestamp1.set_text("0:00");
|
||
|
|
||
|
let timestamp2 = new St.Label({
|
||
|
style_class: "progressbar-timestamp"
|
||
|
});
|
||
|
|
||
|
let progressBar = new ProgressBar(0, this, name, [timestamp1, timestamp2]);
|
||
|
let box = new St.BoxLayout();
|
||
|
playerProxy = null;
|
||
|
timestamp2.set_text(`${Math.floor(length)}:${Math.floor((length - Math.floor(length))*60).toLocaleString('en-US', {minimumIntegerDigits: 2, useGrouping:false})}`);
|
||
|
box.add_child(timestamp1);
|
||
|
box.add_child(progressBar);
|
||
|
box.add_child(timestamp2);
|
||
|
i.get_child().add_child(box);
|
||
|
this.bars[name] = progressBar;
|
||
|
|
||
|
this.signals.push(i._player.connect('closed', (() => {
|
||
|
if (timeout)
|
||
|
clearInterval(timeout);
|
||
|
if (!this.bars[name])
|
||
|
return;
|
||
|
this.bars[name].destroy();
|
||
|
delete this.bars[name];
|
||
|
})));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async _onProxyReady() {
|
||
|
const [names] = await this._dbusProxy.ListNamesAsync().catch();
|
||
|
names.forEach(name => {
|
||
|
if (!name.startsWith('org.mpris.MediaPlayer2.'))
|
||
|
return;
|
||
|
|
||
|
this._addProgress(name, false);
|
||
|
});
|
||
|
this.signals.push(this._dbusProxy.connectSignal("NameOwnerChanged", (pproxy, sender, [name, oldOwner, newOwner]) => {
|
||
|
if (!name.startsWith('org.mpris.MediaPlayer2.'))
|
||
|
return;
|
||
|
this.signals.push(this._mediaSection._players.get(name).connect('changed', () => {
|
||
|
this._addProgress(name, true, newOwner, oldOwner);
|
||
|
}));
|
||
|
|
||
|
this.timeout = setTimeout(() => {
|
||
|
this._addProgress(name, true, newOwner, oldOwner);
|
||
|
}, 500);
|
||
|
}));
|
||
|
}
|
||
|
|
||
|
destroy() {
|
||
|
clearTimeout(this.timeout);
|
||
|
|
||
|
for (let i in this.bars) {
|
||
|
if (!this.bars[i])
|
||
|
continue;
|
||
|
this.bars[i].destroy();
|
||
|
delete this.bars[i];
|
||
|
}
|
||
|
|
||
|
this.signals.map((i) => {
|
||
|
this.disconnect(i);
|
||
|
});
|
||
|
|
||
|
for (let i of this._mediaSection._messages) {
|
||
|
try {
|
||
|
if (i.get_child().get_last_child().get_child_at_index(1) instanceof ProgressBar)
|
||
|
i.get_child().get_last_child().get_child_at_index(1).destroy();
|
||
|
} catch {}
|
||
|
}
|
||
|
|
||
|
super.destroy();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export class ProgressBar extends Slider {
|
||
|
_init(value, manager, busName, timestamps) {
|
||
|
super._init(value);
|
||
|
|
||
|
this._busName = busName;
|
||
|
this.manager = manager;
|
||
|
this.timestamps = timestamps;
|
||
|
this._updateSettings();
|
||
|
this.updateSignal = St.Settings.get().connect('notify', () => this._updateSettings());
|
||
|
this.track_hover = true;
|
||
|
|
||
|
this.signals = [];
|
||
|
|
||
|
const MprisPlayerIface = loadInterfaceXML('org.mpris.MediaPlayer2.Player');
|
||
|
const MprisPlayerProxy = Gio.DBusProxy.makeProxyWrapper(MprisPlayerIface);
|
||
|
|
||
|
this._playerProxy = MprisPlayerProxy(Gio.DBus.session, this._busName, '/org/mpris/MediaPlayer2', this._onPlayerProxyReady.bind(this));
|
||
|
|
||
|
const position = this.getProperty("Position");
|
||
|
this.value = position / this._length;
|
||
|
|
||
|
timeout = setInterval(() => {
|
||
|
if (this._dragging || this.getProperty("PlaybackStatus") !== "Playing")
|
||
|
return;
|
||
|
if (!this) {
|
||
|
clearInterval(timeout);
|
||
|
return;
|
||
|
}
|
||
|
let position = this.getProperty("Position");
|
||
|
|
||
|
this.value = position / this._length;
|
||
|
position = position / 60000000;
|
||
|
this.timestamps[0].set_text(`${Math.floor(position)}:${Math.floor((position - Math.floor(position))*60).toLocaleString('en-US', {minimumIntegerDigits: 2, useGrouping:false})}`);
|
||
|
}, 1000);
|
||
|
|
||
|
this.signals.push(this.connect("drag-end", () => {
|
||
|
if (this._dragging)
|
||
|
return;
|
||
|
this.setPosition(this.value * this._length);
|
||
|
}));
|
||
|
}
|
||
|
|
||
|
_updateInfo() {
|
||
|
this._trackId = this.getProperty("Metadata")['mpris:trackid'];
|
||
|
if (!this._trackId)
|
||
|
this.reactive = false;
|
||
|
if (this._trackId !== 0 && this.getProperty("CanSeek"))
|
||
|
this.reactive = true;
|
||
|
if (!this._length)
|
||
|
this._length = this.getProperty("Metadata")['mpris:length'];
|
||
|
if (!this._length) {
|
||
|
this.visible = false;
|
||
|
this.timestamps[0].visible = false;
|
||
|
this.timestamps[1].visible = false;
|
||
|
}
|
||
|
else {
|
||
|
this.visible = true;
|
||
|
this.timestamps[0].visible = true;
|
||
|
this.timestamps[1].visible = true;
|
||
|
}
|
||
|
this.timestamps[1].set_text(`${Math.floor(this._length / 60000000)}:${Math.floor((this._length / 60000000 - Math.floor(this._length / 60000000))*60).toLocaleString('en-US', {minimumIntegerDigits: 2, useGrouping:false})}`);
|
||
|
}
|
||
|
|
||
|
getProperty(prop) {
|
||
|
try {
|
||
|
return this._playerProxy.get_connection().call_sync(
|
||
|
this._busName,
|
||
|
"/org/mpris/MediaPlayer2",
|
||
|
"org.freedesktop.DBus.Properties",
|
||
|
"Get",
|
||
|
new GLib.Variant("(ss)", ["org.mpris.MediaPlayer2.Player", prop]),
|
||
|
null,
|
||
|
Gio.DBusCallFlags.NONE,
|
||
|
50,
|
||
|
null
|
||
|
).recursiveUnpack()[0];
|
||
|
} catch {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
setPosition(value) {
|
||
|
this._playerProxy.get_connection().call_sync(
|
||
|
this._busName,
|
||
|
"/org/mpris/MediaPlayer2",
|
||
|
"org.mpris.MediaPlayer2.Player",
|
||
|
"SetPosition",
|
||
|
new GLib.Variant("(ox)", [this._trackId, value.toString()]),
|
||
|
null,
|
||
|
Gio.DBusCallFlags.NONE,
|
||
|
50,
|
||
|
null
|
||
|
);
|
||
|
}
|
||
|
|
||
|
_onPlayerProxyReady() {
|
||
|
this.signals.push(this._playerProxy.connectObject('g-properties-changed', () => this._updateInfo(), this));
|
||
|
this._updateInfo();
|
||
|
}
|
||
|
|
||
|
_updateSettings() {
|
||
|
if (St.Settings.get().color_scheme === 0 && GLib.get_os_info("NAME").includes("Ubuntu")) {
|
||
|
this.remove_style_class_name('progress-bar');
|
||
|
this.add_style_class_name('progress-bar-light');
|
||
|
} else if (St.Settings.get().color_scheme === 2) {
|
||
|
this.remove_style_class_name('progress-bar');
|
||
|
this.add_style_class_name('progress-bar-light');
|
||
|
} else {
|
||
|
this.remove_style_class_name('progress-bar-light');
|
||
|
this.add_style_class_name('progress-bar');
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
destroy() {
|
||
|
this.signals.map((i) => {
|
||
|
this.disconnect(i);
|
||
|
});
|
||
|
St.Settings.get().disconnect(this.updateSignal);
|
||
|
clearInterval(timeout);
|
||
|
this._playerProxy = null;
|
||
|
this.timestamps[0].destroy();
|
||
|
this.timestamps[1].destroy();
|
||
|
if (this.manager.bars[this._busName])
|
||
|
delete this.manager.bars[this._busName];
|
||
|
super.destroy();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
GObject.registerClass(ProgressBarManager);
|
||
|
GObject.registerClass(ProgressBar);
|