Skip to content

Commit

Permalink
use mpris xseam:asText as lyrics source as per #17
Browse files Browse the repository at this point in the history
cancel the pending lyrics request before each download
improve light theme integration with the panel lyrics
use binary search on sorted timestamps
cleanup
  • Loading branch information
tuberry committed Dec 29, 2023
1 parent 6f92040 commit f076e03
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 207 deletions.
32 changes: 16 additions & 16 deletions po/zh_CN.po
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: gnome-shell-extension-desktop-lyric 8\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-09-24 19:11+0800\n"
"POT-Creation-Date: 2023-12-28 05:29+0800\n"
"PO-Revision-Date: 2021-08-18 14:05+0800\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
Expand All @@ -17,39 +17,39 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

#: src/ui.js:285
#: src/ui.js:281
msgid "All"
msgstr "全部"

#: src/ui.js:285
#: src/ui.js:281
msgid "Normal"
msgstr "常规"

#: src/ui.js:285
#: src/ui.js:281
msgid "Symbolic"
msgstr "符号"

#: src/ui.js:330
#: src/ui.js:326
msgid "Clear"
msgstr "清除"

#: src/ui.js:430
#: src/ui.js:425
msgid "Mismatched filetype"
msgstr "文件类型不匹配"

#: src/ui.js:564
#: src/ui.js:565
msgid "Click or press ENTER to commit changes"
msgstr "单击或按回车提交更改"

#: src/prefs.js:29
#: src/prefs.js:28
msgid "Active color"
msgstr "已唱颜色"

#: src/prefs.js:30
#: src/prefs.js:29
msgid "Outline color"
msgstr "轮廓颜色"

#: src/prefs.js:31
#: src/prefs.js:30
msgid "Inactive color"
msgstr "未唱颜色"

Expand All @@ -73,7 +73,7 @@ msgstr "中间"
msgid "Right"
msgstr "右侧"

#: src/prefs.js:39 src/extension.js:209
#: src/prefs.js:39 src/extension.js:192
msgid "Mobilize"
msgstr "解锁位置"

Expand Down Expand Up @@ -105,23 +105,23 @@ msgstr "歌词颜色"
msgid "Font name"
msgstr "字体名称"

#: src/extension.js:207
#: src/extension.js:190
msgid "Invisiblize"
msgstr "隐藏"

#: src/extension.js:208
#: src/extension.js:191
msgid "Minimize"
msgstr "最小化"

#: src/extension.js:211
#: src/extension.js:194
msgid "Redownload"
msgstr "重新下载"

#: src/extension.js:212
#: src/extension.js:195
msgid "Resynchronize"
msgstr "重新同步"

#: src/extension.js:214
#: src/extension.js:197
msgid "Settings"
msgstr "设置"

Expand Down
65 changes: 24 additions & 41 deletions src/extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';

import { Field } from './const.js';
import { Lyric } from './lyric.js';
import { id, xnor } from './util.js';
import { xnor, noop, homolog } from './util.js';
import { MprisPlayer as Mpris } from './mpris.js';
import { DesktopPaper, PanelPaper } from './paper.js';
import { SwitchItem, MenuItem, TrayIcon } from './menu.js';
import { Fulu, ExtensionBase, Destroyable, symbiose, omit, onus, getSelf, _ } from './fubar.js';
import { Fulu, ExtensionBase, Destroyable, symbiose, omit, connect, getSelf, _ } from './fubar.js';

class LyricButton extends PanelMenu.Button {
static {
Expand All @@ -24,20 +24,16 @@ class LyricButton extends PanelMenu.Button {

constructor(callback) {
super(0.5);
this._onXbuttonClick = callback;
this.menu.actor.add_style_class_name('desktop-lyric-menu');
this._box = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
this._box.add_child(new TrayIcon('lyric-symbolic', true));
this.add_child(this._box);
}

set_paper(paper) {
if(paper) this._box.add_child(paper);
this._onXButtonClick = callback;
let box = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
box.add_child(new TrayIcon('lyric-symbolic', true));
this.set_paper = x => x && box.add_child(x);
this.add_child(box);
}

vfunc_event(event) {
if(event.type() === Clutter.EventType.BUTTON_PRESS && (event.get_button() === 8 || event.get_button() === 9)) {
this._onXbuttonClick();
if(event.type() === Clutter.EventType.BUTTON_PRESS && event.get_button() >>> 1 === 4) {
this._onXButtonClick();
return Clutter.EVENT_STOP;
}
return super.vfunc_event(event);
Expand Down Expand Up @@ -70,10 +66,10 @@ class DesktopLyric extends Destroyable {
path: [Field.PATH, 'string'],
span: [Field.SPAN, 'uint'],
}, this);
this._mpris.connectObject('update', this._update.bind(this),
connect(this, [this._mpris, 'update', this._update.bind(this),
'closed', (_p, closed) => { this.closed = closed; },
'status', (_p, status) => { this.playing = status === 'Playing'; },
'seeked', (_p, position) => this.setPosition(position / 1000), onus(this));
'seeked', (_p, position) => this.setPosition(position / 1000)]);
}

set path(path) {
Expand All @@ -91,7 +87,7 @@ class DesktopLyric extends Destroyable {
this._paper = new DesktopPaper(this._fulu);
this._menus?.drag.show();
}
if(this._song) this.loadLyric();
this.loadLyric();
}

set systray(systray) {
Expand Down Expand Up @@ -139,7 +135,7 @@ class DesktopLyric extends Destroyable {
if(this._syncing) return;
this._syncing = true;
let pos = await this._mpris.getPosition() / 1000;
for(let i = 0; pos && (pos === this._pos || !this._length || this._length - pos < 2000) && i < 7; i++) { // FIXME: workaround for stale positions from buggy NCM mpris when changing songs
for(let i = 0; pos && (pos === this._pos || !this._length || this._length - pos < 2000) && i < 7; i++) { // HACK: workaround for stale positions from buggy NCM mpris when changing songs
await new Promise(resolve => this._sbt.sync.revive(resolve));
pos = await this._mpris.getPosition() / 1000;
}
Expand All @@ -148,11 +144,10 @@ class DesktopLyric extends Destroyable {
}

_update(_player, song, length) {
if(JSON.stringify(song) === JSON.stringify(this._song)) {
if(homolog(this._song, song, undefined, (x, y, k) => k === 'lyric' ? x?.length === y?.length : x === y)) {
this.syncPosition();
} else {
let { title, artist } = song;
this._subject = [title, artist.join('/')].filter(id).join(' - ');
this._title = Lyric.format(song, ' - ', '/');
this._length = length;
this._song = song;
this.loadLyric();
Expand All @@ -163,37 +158,25 @@ class DesktopLyric extends Destroyable {
this._paper.moment = pos;
}

async loadLyric() {
try {
this.setLyric(await this._lyric.find(this._song), this._song);
} catch(e) {
this.setLyric('');
}
}

async reloadLyric() {
try {
this.setLyric(await this._lyric.find(this._song, true));
} catch(e) {
logError(e);
this.setLyric('');
this._lyric.delete(this._song);
}
loadLyric(reload) {
if(!this._song) return;
this.setLyric('');
if(this._song.lyric !== null) this.setLyric(this._song.lyric);
else this._lyric.find(this._song, reload).then(x => this.setLyric(x)).catch(noop);
}

setLyric(text) {
if(!this._paper) return;
let span = this._length ?? 0;
this._paper.span = span;
this._paper.span = this._length ?? 0;
this._paper.text = text;
this._paper.song = this._mini ? this._subject : '';
this._paper.song = this._mini ? this._title : '';
this.playing = this._mpris.status === 'Playing';
this.syncPosition();
}

clearLyric() {
this.playing = false;
this._paper?.clear();
this._paper.clear();
this._song = null;
}

Expand All @@ -208,7 +191,7 @@ class DesktopLyric extends Destroyable {
mini: new SwitchItem(_('Minimize'), this._mini, x => this._fulu.set('mini', x, this)),
drag: new SwitchItem(_('Mobilize'), this._drag, x => this._fulu.set('drag', x, this)),
sep0: new PopupMenu.PopupSeparatorMenuItem(),
reload: new MenuItem(_('Redownload'), () => this.reloadLyric()),
reload: new MenuItem(_('Redownload'), () => this.loadLyric(true)),
resync: new MenuItem(_('Resynchronize'), () => this.syncPosition()),
sep1: new PopupMenu.PopupSeparatorMenuItem(),
prefs: new MenuItem(_('Settings'), () => getSelf().openPreferences()),
Expand Down
38 changes: 19 additions & 19 deletions src/fubar.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@ import { loadInterfaceXML } from 'resource:///org/gnome/shell/misc/fileUtils.js'
import { TransientSignalHolder } from 'resource:///org/gnome/shell/misc/signalTracker.js';
import { Extension, gettext as _ } from 'resource:///org/gnome/shell/extensions/extension.js';

import { vmap, raise } from './util.js';

// roll back to the previous workaround for the read-only signalTracker since 45.beta
// TODO: wait for https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2542
const _isDestroyable = x => GObject.type_is_a(x, GObject.Object) && GObject.signal_lookup('destroy', x);
import { vmap } from './util.js';

export { _ };
export const getSelf = () => Extension.lookupByURL(import.meta.url);
export const omit = (o, ...ks) => ks.forEach(k => { o[k]?.destroy?.(); o[k] = null; });
export const onus = o => [o, o.$scapegoat].find(x => _isDestroyable(x)) ?? raise('undestroyable');

// TODO: wait for https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2542
const onus = o => [o, o.$scapegoat].find(x => GObject.type_is_a(x, GObject.Object) && GObject.signal_lookup('destroy', x)) ??
(() => { throw Error('undestroyable'); })(); // NOTE: https://github.com/tc39/proposal-throw-expressions#todo
export const connect = (tracker, ...args) => (x => args.forEach(([emitter, ...argv]) => emitter.connectObject(...argv, x)))(onus(tracker));
export const disconnect = (tracker, ...args) => (x => args.forEach(emitter => emitter?.disconnectObject(x)))(onus(tracker));

export class Destroyable extends EventEmitter {
$scapegoat = new TransientSignalHolder(this);
Expand All @@ -36,16 +37,15 @@ export function symbiose(host, doom, obj) {
}

export function lightProxy(callback, obj) {
let BUS_NAME = 'org.gnome.SettingsDaemon.Color',
colorInfo = Gio.DBusInterfaceInfo.new_for_xml(loadInterfaceXML(BUS_NAME)),
proxy = new Gio.DBusProxy({
g_name: BUS_NAME,
g_connection: Gio.DBus.session,
g_object_path: '/org/gnome/SettingsDaemon/Color',
g_interface_name: colorInfo.name,
g_interface_info: colorInfo,
});
proxy.connectObject('g-properties-changed', callback, onus(obj));
let iface = Gio.DBusInterfaceInfo.new_for_xml(loadInterfaceXML('org.gnome.SettingsDaemon.Color'));
let proxy = new Gio.DBusProxy({
g_interface_info: iface,
g_interface_name: iface.name,
g_connection: Gio.DBus.session,
g_name: 'org.gnome.SettingsDaemon.Color',
g_object_path: '/org/gnome/SettingsDaemon/Color',
});
connect(obj, [proxy, 'g-properties-changed', callback]);
proxy.init_async(GLib.PRIORITY_DEFAULT, null).catch(logError);

return proxy;
Expand All @@ -63,7 +63,7 @@ export class ExtensionBase extends Extension {

export class Symbiont {
constructor(host, dispel, summon) {
host.connectObject('destroy', () => this.dispel(), onus(host));
connect(host, [host, 'destroy', () => this.dispel()]);
this.dispel = () => { dispel(this._delegate); this._delegate = null; };
this.summon = (...args) => (this._delegate = summon(...args));
this.revive = (...args) => { this.dispel(); return this.summon(...args); };
Expand All @@ -88,11 +88,11 @@ export class Fulu {
attach(props, obj, cluster) { // cluster && props <- { fulu: [key, type, output] }
this.#map.has(obj) ? Object.assign(this.#map.get(obj), props) : this.#map.set(obj, props);
let callback = cluster ? x => { obj[cluster] = [x, this.get(x, obj), this.#map.get(obj)[x][2]]; } : x => { obj[x] = this.get(x, obj); };
Object.entries(props).forEach(([k, [x]]) => { callback(k); this.gset.connectObject(`changed::${x}`, () => callback(k), onus(obj)); });
Object.entries(props).forEach(([k, [x]]) => { callback(k); connect(obj, [this.gset, `changed::${x}`, () => callback(k)]); });
return this;
}

detach(obj) {
this.gset.disconnectObject(onus(obj));
disconnect(obj, this.gset);
}
}
Loading

0 comments on commit f076e03

Please sign in to comment.