1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
import { Astal, Gtk } from "astal/gtk3"
import Mpris from "gi://AstalMpris"
import { bind } from "astal"
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}`
}
function MediaPlayer({ player }: { player: Mpris.Player }) {
const { START, END } = Gtk.Align
const title = bind(player, "title").as(t =>
t || "Unknown Track")
const artist = bind(player, "artist").as(a =>
a || "Unknown Artist")
const coverArt = bind(player, "coverArt").as(c =>
`background-image: url('${c}')`)
const playerIcon = bind(player, "entry").as(e =>
Astal.Icon.lookup_icon(e) ? e : "audio-x-generic-symbolic")
const position = bind(player, "position").as(p => player.length > 0
? p / player.length : 0)
const playIcon = bind(player, "playbackStatus").as(s =>
s === Mpris.PlaybackStatus.PLAYING
? "media-playback-pause-symbolic"
: "media-playback-start-symbolic"
)
return <box className="MediaPlayer">
<box className="cover-art" css={coverArt} />
<box vertical>
<box className="title">
<label truncate hexpand halign={START} label={title} />
<icon icon={playerIcon} />
</box>
<label halign={START} valign={START} vexpand wrap label={artist} />
<slider
visible={bind(player, "length").as(l => l > 0)}
onDragged={({ value }) => player.position = value * player.length}
value={position}
/>
<centerbox className="actions">
<label
hexpand
className="position"
halign={START}
visible={bind(player, "length").as(l => l > 0)}
label={bind(player, "position").as(lengthStr)}
/>
<box>
<button
onClicked={() => player.previous()}
visible={bind(player, "canGoPrevious")}>
<icon icon="media-skip-backward-symbolic" />
</button>
<button
onClicked={() => player.play_pause()}
visible={bind(player, "canControl")}>
<icon icon={playIcon} />
</button>
<button
onClicked={() => player.next()}
visible={bind(player, "canGoNext")}>
<icon icon="media-skip-forward-symbolic" />
</button>
</box>
<label
className="length"
hexpand
halign={END}
visible={bind(player, "length").as(l => l > 0)}
label={bind(player, "length").as(l => l > 0 ? lengthStr(l) : "0:00")}
/>
</centerbox>
</box>
</box>
}
export default function MprisPlayers() {
const mpris = Mpris.get_default()
return <box vertical>
{bind(mpris, "players").as(arr => arr.map(player => (
<MediaPlayer player={player} />
)))}
</box>
}
|