import { Variable } from 'astal'; import { Gtk } from 'astal/gtk3'; import { Box, CenterBox } from 'astal/gtk3/widget'; import Mpris from 'gi://AstalMpris'; import Separator from '../misc/separator'; import * as mpris from './mpris'; import PlayerGesture, { PlayerGesture as PlayerGestureClass, PlayerBox as PlayerBoxClass, } from './gesture'; const FAVE_PLAYER = 'org.mpris.MediaPlayer2.spotify'; const SPACING = 8; const Top = ( player: Mpris.Player, overlay: PlayerGestureClass, ) => new Box({ className: 'top', halign: Gtk.Align.START, valign: Gtk.Align.START, children: [ mpris.PlayerIcon(player, overlay), ], }); const Center = ( player: Mpris.Player, colors: Variable, ) => new Box({ className: 'center', children: [ (new CenterBox({ vertical: true, start_widget: new Box({ className: 'metadata', vertical: true, halign: Gtk.Align.START, valign: Gtk.Align.CENTER, hexpand: true, children: [ mpris.TitleLabel(player), mpris.ArtistLabel(player), ], }), })), (new CenterBox({ vertical: true, center_widget: mpris.PlayPauseButton(player, colors), })), ], }); const Bottom = ( player: Mpris.Player, colors: Variable, ) => new Box({ className: 'bottom', children: [ mpris.PreviousButton(player, colors), Separator({ size: SPACING }), mpris.PositionSlider(player, colors), Separator({ size: SPACING }), mpris.NextButton(player, colors), Separator({ size: SPACING }), mpris.ShuffleButton(player, colors), Separator({ size: SPACING }), mpris.LoopButton(player, colors), ], }); const PlayerBox = ( player: Mpris.Player, colors: Variable, overlay: PlayerGestureClass, ) => { const widget = mpris.CoverArt(player, colors, { className: `player ${player.identity}`, hexpand: true, start_widget: Top(player, overlay), center_widget: Center(player, colors), end_widget: Bottom(player, colors), }); widget.visible = false; return widget; }; export default () => { const content = PlayerGesture({ setup: (self) => { const addPlayer = (player: Mpris.Player) => { if (!player || self.players.has(player.bus_name)) { return; } // Get the one on top so we can move it up later const previousFirst = self.overlays.at(-1) as PlayerBoxClass; // Make the new player const colorsVar = Variable({ imageAccent: '#6b4fa2', buttonAccent: '#ecdcff', buttonText: '#25005a', hoverAccent: '#d4baff', }); self.players.set( player.bus_name, PlayerBox(player, colorsVar, self), ); self.add_overlay(self.players.get(player.bus_name)); // Select favorite player at startup if (!self.setup && self.players.has(FAVE_PLAYER)) { self.moveToTop(self.players.get(FAVE_PLAYER)); self.setup = true; } // Move previousFirst on top again else { self.moveToTop(previousFirst); } }; const removePlayer = (player: Mpris.Player) => { if (!player || !self.players.has(player.bus_name)) { return; } const toDelete = self.players.get(player.bus_name); // Get the one on top so we can move it up later const previousFirst = self.overlays.at(-1) as PlayerBoxClass; // Move previousFirst on top again if (previousFirst !== toDelete) { self.moveToTop(previousFirst); } else { self.moveToTop(self.players.has(FAVE_PLAYER) ? self.players.get(FAVE_PLAYER) : self.overlays[0]); } // Remake overlays without deleted one self.remove(toDelete); self.players.delete(player.bus_name); }; const mprisDefault = Mpris.get_default(); self.hook(mprisDefault, 'player-added', (_, player) => addPlayer(player)); self.hook(mprisDefault, 'player-closed', (_, player) => removePlayer(player)); mprisDefault.players.forEach(addPlayer); }, }); return new Box({ className: 'media-player', child: content, }); };