summaryrefslogtreecommitdiff
path: root/examples/js/media-player/widget/MediaPlayer.tsx
diff options
context:
space:
mode:
authorAylur <[email protected]>2024-11-07 01:45:07 +0000
committerAylur <[email protected]>2024-11-07 01:45:07 +0000
commit51514347ef3bc1d7a6f37e798a43111b2f2fc9e0 (patch)
treec5f3d355b25d640e98bed5f89ad2ecc4de8eb36b /examples/js/media-player/widget/MediaPlayer.tsx
parent5074a76b8c7405f77cfab8674c4e5e26c5d05da6 (diff)
example: media player
Diffstat (limited to 'examples/js/media-player/widget/MediaPlayer.tsx')
-rw-r--r--examples/js/media-player/widget/MediaPlayer.tsx94
1 files changed, 94 insertions, 0 deletions
diff --git a/examples/js/media-player/widget/MediaPlayer.tsx b/examples/js/media-player/widget/MediaPlayer.tsx
new file mode 100644
index 0000000..06c7e77
--- /dev/null
+++ b/examples/js/media-player/widget/MediaPlayer.tsx
@@ -0,0 +1,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>
+}