Files
dotfiles/home/natto/ags/windows/music-box/index.js
2024-06-01 18:10:31 +05:30

184 lines
4.5 KiB
JavaScript

// Mostly taken from https://github.com/Aylur/ags/blob/11150225e62462bcd431d1e55185e810190a730a/example/media-widget/Media.js
import Popup, { Padding, Revealer } from "../../utils/popup.js";
import { shrinkText } from "../../utils/text.js";
import { lengthStr, blurBg } from "../../utils/music.js";
import { findPlayer } from "../../utils/music.js";
import { WindowNames } from "../../constants.js";
import Controls from "./music-controls.js";
const FALLBACK_ICON = "audio-x-generic-symbolic";
const { MUSIC_BOX } = WindowNames;
const mpris = await Service.import("mpris");
const Player = (player) => {
const img = Widget.Box({
visible: player.bind("cover_path"),
className: "cover-art",
vpack: "start",
css: player
.bind("cover_path")
.as(
(p) =>
(p ? "min-width: 200px; min-height: 200px;" : "") +
`background-image: url('${p}');`,
),
});
const title = Widget.Label({
className: "title",
wrap: true,
hpack: "start",
label: player.bind("track_title").as((t) => shrinkText(t, 40)),
});
const artist = Widget.Label({
className: "artist",
wrap: true,
hpack: "start",
label: player.bind("track_artists").as((a) => shrinkText(a.join(", "), 80)),
});
const positionSlider = Widget.Slider({
className: "position",
drawValue: false,
onChange: ({ value }) => (player.position = value * player.length),
visible: player.bind("length").as((l) => l > 0),
setup: (self) => {
const update = () => {
if (self.dragging) return;
const value = player.position / player.length;
self.value = value > 0 ? value : 0;
};
self
.hook(player, update)
.hook(player, update, "position")
.poll(1000, update);
},
});
const positionLabel = Widget.Label({
ypad: 0,
hpack: "start",
className: "position-label",
setup: (self) => {
const update = (_, time) => {
self.label = lengthStr(time || player.position);
self.visible = player.length > 0;
};
self.hook(player, update, "position");
self.poll(1000, update);
},
});
const lengthLabel = Widget.Label({
ypad: 0,
hpack: "end",
className: "length-label",
visible: player.bind("length").as((l) => l > 0),
label: player.bind("length").as(lengthStr),
});
const icon = Widget.Icon({
className: "icon",
hexpand: true,
hpack: "end",
vpack: "start",
tooltipText: player.identity || "",
icon: player.bind("entry").as((entry) => {
const name = `${entry}-symbolic`;
return Utils.lookUpIcon(name) ? name : FALLBACK_ICON;
}),
});
return Widget.Box(
{
className: "music-player",
visible: player.bus_name === findPlayer(mpris.players).bus_name,
css: player.bind("cover_path").as(blurBg),
},
img,
Widget.CenterBox({
className: "music-details",
vertical: true,
hexpand: true,
spacing: 25,
startWidget: Widget.Box(
{
vertical: true,
spacing: 6,
},
title,
Widget.Box({
className: "icon-wrapper",
spacing: 10,
children: [artist, icon],
}),
),
centerWidget: positionSlider,
endWidget: Widget.CenterBox({
spacing: 6,
startWidget: positionLabel,
centerWidget: Controls(player),
endWidget: lengthLabel,
}),
}),
)
.hook(
mpris,
(self, bus_name) => {
self.visible = player.bus_name === bus_name;
},
"player-changed",
)
.hook(
mpris,
(self) => {
self.visible = player.bus_name === findPlayer(mpris.players).bus_name;
},
"player-closed",
);
};
const PlayerBox = () =>
Widget.Box({
className: `${MUSIC_BOX}-unwrapped`,
vertical: true,
css: "padding: 1px;",
children: mpris.bind("players").as((ps) => ps.map(Player)),
});
export default (monitor = 0) => {
const name = `${MUSIC_BOX}-${monitor}`;
return Popup({
name,
className: MUSIC_BOX,
monitor,
layout: Widget.Box({
children: [
Padding(name), // left
Widget.Box({
hexpand: false,
vexpand: false,
vertical: true,
children: [
Revealer({
name,
child: PlayerBox(),
transition: "slide_down",
transitionDuration: 400,
}),
Padding(name), // down
],
}),
Padding(name), // right
],
}),
});
};