123 lines
4.1 KiB
TypeScript
123 lines
4.1 KiB
TypeScript
import { App } from "astal/gtk3";
|
|
import { bind } from "astal";
|
|
import { Astal, Gtk } from "astal/gtk3";
|
|
import Mpris from "gi://AstalMpris";
|
|
|
|
function lengthStr(length: number) {
|
|
const min = Math.floor(length / 60);
|
|
const sec = Math.floor(length % 60);
|
|
const sec0 = sec < 10 ? "0" : "";
|
|
return `${min}:${sec0}${sec}`;
|
|
}
|
|
|
|
export default function Media() {
|
|
const { TOP, RIGHT } = Astal.WindowAnchor;
|
|
const mpris = Mpris.get_default();
|
|
|
|
return (
|
|
<window
|
|
anchor={TOP | RIGHT}
|
|
name="Mediamenu"
|
|
className="Media"
|
|
application={App}
|
|
visible={false}
|
|
>
|
|
<box>
|
|
{bind(mpris, "players").as((ps) =>
|
|
ps[0] ? (
|
|
<box>
|
|
<box
|
|
className="Cover"
|
|
valign={Gtk.Align.CENTER}
|
|
css={bind(ps[0], "coverArt").as(
|
|
(cover) => `background-image: url('${cover}');`,
|
|
)}
|
|
/>
|
|
<box vertical>
|
|
<box className="title">
|
|
<label
|
|
truncate
|
|
hexpand
|
|
halign={Gtk.Align.START}
|
|
label={bind(ps[0], "metadata").as(() => `${ps[0].title}`)}
|
|
/>
|
|
<icon
|
|
icon={bind(ps[0], "entry").as((e) =>
|
|
Astal.Icon.lookup_icon(e)
|
|
? e
|
|
: "audio-x-generic-symbolic",
|
|
)}
|
|
/>
|
|
</box>
|
|
<label
|
|
halign={Gtk.Align.START}
|
|
valign={Gtk.Align.START}
|
|
className="artist"
|
|
vexpand
|
|
wrap
|
|
label={bind(ps[0], "metadata").as(() => `${ps[0].artist}`)}
|
|
/>
|
|
<slider
|
|
visible={bind(ps[0], "length").as((l) => l > 0)}
|
|
onDragged={({ value }) =>
|
|
(ps[0].position = value * ps[0].length)
|
|
}
|
|
value={bind(ps[0], "position").as((p) =>
|
|
ps[0].length > 0 ? p / ps[0].length : 0,
|
|
)}
|
|
/>
|
|
<centerbox className="actions">
|
|
<label
|
|
hexpand
|
|
className="position"
|
|
halign={Gtk.Align.START}
|
|
visible={bind(ps[0], "length").as((l) => l > 0)}
|
|
label={bind(ps[0], "position").as(lengthStr)}
|
|
/>
|
|
<box>
|
|
<button
|
|
onClicked={() => ps[0].previous()}
|
|
visible={bind(ps[0], "canGoPrevious")}
|
|
>
|
|
<icon icon="media-skip-backward-symbolic" />
|
|
</button>
|
|
<button
|
|
onClicked={() => ps[0].play_pause()}
|
|
visible={bind(ps[0], "canControl")}
|
|
>
|
|
<icon
|
|
icon={bind(ps[0], "playbackStatus").as((s) =>
|
|
s === Mpris.PlaybackStatus.PLAYING
|
|
? "media-playback-pause-symbolic"
|
|
: "media-playback-start-symbolic",
|
|
)}
|
|
/>
|
|
</button>
|
|
<button
|
|
onClicked={() => ps[0].next()}
|
|
visible={bind(ps[0], "canGoNext")}
|
|
>
|
|
<icon icon="media-skip-forward-symbolic" />
|
|
</button>
|
|
</box>
|
|
<label
|
|
className="length"
|
|
hexpand
|
|
halign={Gtk.AlignEND}
|
|
visible={bind(ps[0], "length").as((l) => l > 0)}
|
|
label={bind(ps[0], "length").as((l) =>
|
|
l > 0 ? lengthStr(l) : "0:00",
|
|
)}
|
|
/>
|
|
</centerbox>
|
|
</box>
|
|
</box>
|
|
) : (
|
|
<label label="Nothing Playing" />
|
|
),
|
|
)}
|
|
</box>
|
|
</window>
|
|
);
|
|
}
|