import { Accessor, createBinding, createConnection, createState, onCleanup, With } from "ags"; import { Gtk } from "ags/gtk4"; import { Separator } from "../Separator"; import { Windows } from "../../windows"; import { Clipboard } from "../../scripts/clipboard"; import GObject from "ags/gobject"; import AstalMpris from "gi://AstalMpris"; import Pango from "gi://Pango?version=1.0"; import { decoder, getPlayerIconFromBusName, variableToBoolean } from "../../scripts/utils"; export const dummyPlayer = AstalMpris.Player.new("colorshellDummy"); export let [player, setPlayer] = createState(dummyPlayer); export const Media = () => { const connections: Map|number> = new Map(); if(AstalMpris.get_default().players[0]) setPlayer(AstalMpris.get_default().players[0]); onCleanup(() => connections.forEach((id, obj) => Array.isArray(id) ? id.forEach(id => obj.disconnect(id)) : obj.disconnect(id) )); connections.set(AstalMpris.get_default(), [ AstalMpris.get_default().connect("player-added", (_, player) => player.available && setPlayer(player)), AstalMpris.get_default().connect("player-closed", (_, closedPlayer) => { const players = AstalMpris.get_default().players.filter(pl => pl?.available && pl.busName !== closedPlayer.busName); if(players.length > 0) { setPlayer(players[0]); return; } setPlayer(dummyPlayer); }) ]); return pl.available)} $={(self) => { const gestureClick = Gtk.GestureClick.new(), controllerMotion = Gtk.EventControllerMotion.new(), controllerScroll = Gtk.EventControllerScroll.new( Gtk.EventControllerScrollFlags.VERTICAL); self.add_controller(gestureClick); self.add_controller(controllerMotion); self.add_controller(controllerScroll); connections.set(gestureClick, gestureClick.connect("released", () => Windows.getDefault().toggle("center-window"))); connections.set(controllerScroll, controllerScroll.connect("scroll", (_, _dx, dy) => { if(AstalMpris.get_default().players.length === 1 && player.get()?.busName === AstalMpris.get_default().players[0].busName) return true; const players = AstalMpris.get_default().players; for(let i = 0; i < players.length; i++) { const pl = players[i]; if(pl.busName !== player.get().busName) continue; if(dy > 0 && players[i-1]) { setPlayer(players[i-1]); break; } if(dy < 0 && players[i+1]) { setPlayer(players[i+1]); break; } } return true; }) ); connections.set(controllerMotion, [ controllerMotion.connect("enter", () => { const revealer = self.get_last_child() as Gtk.Revealer; revealer.set_reveal_child(true); }), controllerMotion.connect("leave", () => { const revealer = self.get_last_child() as Gtk.Revealer; revealer.set_reveal_child(false); }) ]); connections.set(self, self.connect("destroy", () => connections.forEach((ids, obj) => Array.isArray(ids) ? ids.forEach(id => obj.disconnect(id)) : obj.disconnect(ids)) )); }}> pl.available)}> pl.available)}> {(available: boolean) => available && title ?? "No Title")} maxWidthChars={20} ellipsize={Pango.EllipsizeMode.END} /> artist ?? "No Artist")} maxWidthChars={18} ellipsize={Pango.EllipsizeMode.END} /> } pl.available)}> {(available: boolean) => available && { const url = getMediaUrl(player.get()).get(); url && Clipboard.getDefault().copyAsync(url); }} /> player.get().canGoPrevious && player.get().previous()} /> status === AstalMpris.PlaybackStatus.PAUSED ? "media-playback-start-symbolic" : "media-playback-pause-symbolic")} tooltipText={ createBinding(player.get(), "playbackStatus").as(status => status === AstalMpris.PlaybackStatus.PAUSED ? "Play" : "Pause") } onClicked={() => player.get().play_pause()} /> player.get().canGoNext && player.get().next()} /> } } export function getMediaUrl(player: AstalMpris.Player): Accessor { return createConnection(player.get_meta("xesam:url"), [player, "notify::metadata", () => player.get_meta("xesam:url")] ).as(url => { const byteString = url?.get_data_as_bytes(); return byteString ? decoder.decode(byteString.toArray()) : undefined; }) }