nixos-configs/devices/wim/config/ags/js/media-player/player.js

230 lines
6.5 KiB
JavaScript

import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js';
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
import { Box, CenterBox } from 'resource:///com/github/Aylur/ags/widget.js';
import * as mpris from './mpris.js';
import PlayerGesture from './gesture.js';
import Separator from '../misc/separator.js';
const FAVE_PLAYER = 'org.mpris.MediaPlayer2.spotify';
const SPACING = 8;
/**
* @typedef {import('types/service/mpris').MprisPlayer} Player
* @typedef {import('types/widgets/overlay').default} Overlay
* @typedef {import('types/variable').Variable} Variable
*/
/**
* @param {Player} player
* @param {Overlay} overlay
*/
const Top = (player, overlay) => Box({
class_name: 'top',
hpack: 'start',
vpack: 'start',
children: [
mpris.PlayerIcon(player, overlay),
],
});
/**
* @param {Player} player
* @param {Variable} colors
*/
const Center = (player, colors) => Box({
class_name: 'center',
children: [
CenterBox({
// @ts-expect-error
vertical: true,
children: [
Box({
class_name: 'metadata',
vertical: true,
hpack: 'start',
vpack: 'center',
hexpand: true,
children: [
mpris.TitleLabel(player),
mpris.ArtistLabel(player),
],
}),
null,
null,
],
}),
CenterBox({
// @ts-expect-error
vertical: true,
children: [
null,
mpris.PlayPauseButton(player, colors),
null,
],
}),
],
});
/**
* @param {Player} player
* @param {Variable} colors
*/
const Bottom = (player, colors) => Box({
class_name: 'bottom',
children: [
mpris.PreviousButton(player, colors),
Separator(SPACING),
mpris.PositionSlider(player, colors),
Separator(SPACING),
mpris.NextButton(player, colors),
Separator(SPACING),
mpris.ShuffleButton(player, colors),
Separator(SPACING),
mpris.LoopButton(player, colors),
],
});
/**
* @param {Player} player
* @param {Variable} colors
* @param {Overlay} overlay
*/
const PlayerBox = (player, colors, overlay) => {
const widget = mpris.CoverArt(player, colors, {
class_name: `player ${player.name}`,
hexpand: true,
start_widget: Top(player, overlay),
center_widget: Center(player, colors),
end_widget: Bottom(player, colors),
});
widget.visible = false;
return widget;
};
export default () => {
const content = PlayerGesture({
attribute: {
players: new Map(),
setup: false,
},
setup: /** @param {any} self */ (self) => {
self
.hook(Mpris,
/**
* @param {Overlay} overlay
* @param {string} bus_name
*/
(overlay, bus_name) => {
const players = overlay.attribute.players;
if (players.has(bus_name)) {
return;
}
// Sometimes the signal doesn't give the bus_name
if (!bus_name) {
const player = Mpris.players.find((p) => {
return !players.has(p.bus_name);
});
if (player) {
bus_name = player.bus_name;
}
else {
return;
}
}
// Get the one on top so we can move it up later
const previousFirst = overlay.attribute.list().at(-1);
// Make the new player
const player = Mpris.getPlayer(bus_name);
const Colors = Variable(null);
if (!player) {
return;
}
players.set(
bus_name,
// @ts-expect-error
PlayerBox(player, Colors, content.child),
);
overlay.overlays = Array.from(players.values())
.map((widget) => widget);
const includes = overlay.attribute
.includesWidget(previousFirst);
// Select favorite player at startup
const attrs = overlay.attribute;
if (!attrs.setup && players.has(FAVE_PLAYER)) {
attrs.moveToTop(players.get(FAVE_PLAYER));
attrs.setup = true;
}
// Move previousFirst on top again
else if (includes) {
attrs.moveToTop(previousFirst);
}
},
'player-added')
.hook(Mpris,
/**
* @param {Overlay} overlay
* @param {string} bus_name
*/
(overlay, bus_name) => {
const players = overlay.attribute.players;
if (!bus_name || !players.has(bus_name)) {
return;
}
// Get the one on top so we can move it up later
const previousFirst = overlay.attribute.list().at(-1);
// Remake overlays without deleted one
players.delete(bus_name);
overlay.overlays = Array.from(players.values())
.map((widget) => widget);
// Move previousFirst on top again
const includes = overlay.attribute
.includesWidget(previousFirst);
if (includes) {
overlay.attribute.moveToTop(previousFirst);
}
},
'player-closed');
},
});
return Box({
class_name: 'media',
child: content,
});
};