more modules
This commit is contained in:
738
bar/modules/player.py
Normal file
738
bar/modules/player.py
Normal file
@@ -0,0 +1,738 @@
|
||||
import os
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
import tempfile
|
||||
from gi.repository import Gtk, GLib, Gio, Gdk
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.centerbox import CenterBox
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.button import Button
|
||||
from fabric.widgets.circularprogressbar import CircularProgressBar
|
||||
from fabric.widgets.overlay import Overlay
|
||||
from fabric.widgets.stack import Stack
|
||||
from ..widgets.circle_image import CircleImage
|
||||
import bar.modules.icons as icons
|
||||
from bar.services.mpris import MprisPlayerManager, MprisPlayer
|
||||
|
||||
# from bar.modules.cavalcade import SpectrumRender
|
||||
|
||||
|
||||
def get_player_icon_markup_by_name(player_name):
|
||||
if player_name:
|
||||
pn = player_name.lower()
|
||||
if pn == "firefox":
|
||||
return icons.firefox
|
||||
elif pn == "spotify":
|
||||
return icons.spotify
|
||||
elif pn in ("chromium", "brave"):
|
||||
return icons.chromium
|
||||
return icons.disc
|
||||
|
||||
|
||||
def add_hover_cursor(widget):
|
||||
widget.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK | Gdk.EventMask.LEAVE_NOTIFY_MASK)
|
||||
widget.connect(
|
||||
"enter-notify-event",
|
||||
lambda w, event: w.get_window().set_cursor(
|
||||
Gdk.Cursor.new_from_name(Gdk.Display.get_default(), "pointer")
|
||||
),
|
||||
)
|
||||
widget.connect(
|
||||
"leave-notify-event", lambda w, event: w.get_window().set_cursor(None)
|
||||
)
|
||||
|
||||
|
||||
class PlayerBox(Box):
|
||||
def __init__(self, mpris_player=None):
|
||||
super().__init__(
|
||||
orientation="v", h_align="fill", spacing=0, h_expand=False, v_expand=True
|
||||
)
|
||||
self.mpris_player = mpris_player
|
||||
self._progress_timer_id = None # Initialize timer ID
|
||||
|
||||
self.cover = CircleImage(
|
||||
name="player-cover",
|
||||
image_file=os.path.expanduser("~/Pictures/wallpaper/background.jpg"),
|
||||
size=162,
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
)
|
||||
self.cover_placerholder = CircleImage(
|
||||
name="player-cover",
|
||||
size=198,
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
)
|
||||
self.title = Label(
|
||||
name="player-title",
|
||||
h_expand=True,
|
||||
h_align="fill",
|
||||
ellipsization="end",
|
||||
max_chars_width=1,
|
||||
)
|
||||
self.album = Label(
|
||||
name="player-album",
|
||||
h_expand=True,
|
||||
h_align="fill",
|
||||
ellipsization="end",
|
||||
max_chars_width=1,
|
||||
)
|
||||
self.artist = Label(
|
||||
name="player-artist",
|
||||
h_expand=True,
|
||||
h_align="fill",
|
||||
ellipsization="end",
|
||||
max_chars_width=1,
|
||||
)
|
||||
self.progressbar = CircularProgressBar(
|
||||
name="player-progress",
|
||||
size=198,
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
start_angle=180,
|
||||
end_angle=360,
|
||||
)
|
||||
self.time = Label(name="player-time", label="--:-- / --:--")
|
||||
self.overlay = Overlay(
|
||||
child=self.cover_placerholder,
|
||||
overlays=[self.progressbar, self.cover],
|
||||
)
|
||||
self.overlay_container = CenterBox(
|
||||
name="player-overlay", center_children=[self.overlay]
|
||||
)
|
||||
self.title.set_label("Nothing Playing")
|
||||
self.album.set_label("Enjoy the silence")
|
||||
self.artist.set_label("¯\\_(ツ)_/¯")
|
||||
self.progressbar.set_value(0.0)
|
||||
self.prev = Button(
|
||||
name="player-btn",
|
||||
child=Label(name="player-btn-label", markup=icons.prev),
|
||||
)
|
||||
self.backward = Button(
|
||||
name="player-btn",
|
||||
child=Label(name="player-btn-label", markup=icons.skip_back),
|
||||
)
|
||||
self.play_pause = Button(
|
||||
name="player-btn",
|
||||
child=Label(name="player-btn-label", markup=icons.play),
|
||||
)
|
||||
self.forward = Button(
|
||||
name="player-btn",
|
||||
child=Label(name="player-btn-label", markup=icons.skip_forward),
|
||||
)
|
||||
self.next = Button(
|
||||
name="player-btn",
|
||||
child=Label(name="player-btn-label", markup=icons.next),
|
||||
)
|
||||
# Add hover effect to buttons
|
||||
add_hover_cursor(self.prev)
|
||||
add_hover_cursor(self.backward)
|
||||
add_hover_cursor(self.play_pause)
|
||||
add_hover_cursor(self.forward)
|
||||
add_hover_cursor(self.next)
|
||||
self.btn_box = CenterBox(
|
||||
name="player-btn-box",
|
||||
orientation="h",
|
||||
center_children=[
|
||||
Box(
|
||||
orientation="h",
|
||||
spacing=8,
|
||||
h_expand=True,
|
||||
h_align="fill",
|
||||
children=[
|
||||
self.prev,
|
||||
self.backward,
|
||||
self.play_pause,
|
||||
self.forward,
|
||||
self.next,
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
self.player_box = Box(
|
||||
name="player-box",
|
||||
orientation="v",
|
||||
spacing=8,
|
||||
children=[
|
||||
self.overlay_container,
|
||||
self.title,
|
||||
self.album,
|
||||
self.artist,
|
||||
self.btn_box,
|
||||
self.time,
|
||||
],
|
||||
)
|
||||
self.add(self.player_box)
|
||||
if mpris_player:
|
||||
self._apply_mpris_properties() # This will handle starting the timer if needed
|
||||
self.prev.connect("clicked", self._on_prev_clicked)
|
||||
self.play_pause.connect("clicked", self._on_play_pause_clicked)
|
||||
self.backward.connect("clicked", self._on_backward_clicked)
|
||||
self.forward.connect("clicked", self._on_forward_clicked)
|
||||
self.next.connect("clicked", self._on_next_clicked)
|
||||
self.mpris_player.connect("changed", self._on_mpris_changed)
|
||||
else:
|
||||
self.play_pause.get_child().set_markup(icons.stop)
|
||||
# Ensure buttons are disabled visually if no player
|
||||
self.backward.add_style_class("disabled")
|
||||
self.forward.add_style_class("disabled")
|
||||
self.prev.add_style_class("disabled")
|
||||
self.next.add_style_class("disabled")
|
||||
self.progressbar.set_value(0.0)
|
||||
self.time.set_text("--:-- / --:--")
|
||||
|
||||
def _apply_mpris_properties(self):
|
||||
mp = self.mpris_player
|
||||
self.title.set_visible(bool(mp.title and mp.title.strip()))
|
||||
if mp.title and mp.title.strip():
|
||||
self.title.set_text(mp.title)
|
||||
self.album.set_visible(bool(mp.album and mp.album.strip()))
|
||||
if mp.album and mp.album.strip():
|
||||
self.album.set_text(mp.album)
|
||||
self.artist.set_visible(bool(mp.artist and mp.artist.strip()))
|
||||
if mp.artist and mp.artist.strip():
|
||||
self.artist.set_text(mp.artist)
|
||||
if mp.arturl:
|
||||
parsed = urllib.parse.urlparse(mp.arturl)
|
||||
if parsed.scheme == "file":
|
||||
local_arturl = urllib.parse.unquote(parsed.path)
|
||||
self._set_cover_image(local_arturl)
|
||||
elif parsed.scheme in ("http", "https"):
|
||||
GLib.Thread.new(
|
||||
"download-artwork", self._download_and_set_artwork, mp.arturl
|
||||
)
|
||||
else:
|
||||
self._set_cover_image(mp.arturl)
|
||||
else:
|
||||
fallback = os.path.expanduser("~/Pictures/wallpaper/background.jpg")
|
||||
self._set_cover_image(fallback)
|
||||
file_obj = Gio.File.new_for_path(fallback)
|
||||
monitor = file_obj.monitor_file(Gio.FileMonitorFlags.NONE, None)
|
||||
monitor.connect("changed", self.on_wallpaper_changed)
|
||||
self._wallpaper_monitor = monitor
|
||||
self.update_play_pause_icon()
|
||||
# Keep progress bar and time visible always
|
||||
self.progressbar.set_visible(True)
|
||||
self.time.set_visible(True)
|
||||
|
||||
player_name = (
|
||||
mp.player_name.lower()
|
||||
if hasattr(mp, "player_name") and mp.player_name
|
||||
else ""
|
||||
)
|
||||
can_seek = hasattr(mp, "can_seek") and mp.can_seek
|
||||
|
||||
if player_name == "firefox" or not can_seek:
|
||||
# Disable seeking buttons and reset progress/time display
|
||||
self.backward.add_style_class("disabled")
|
||||
self.forward.add_style_class("disabled")
|
||||
self.progressbar.set_value(0.0)
|
||||
self.time.set_text("--:-- / --:--")
|
||||
# Stop the timer if it's running
|
||||
if self._progress_timer_id:
|
||||
GLib.source_remove(self._progress_timer_id)
|
||||
self._progress_timer_id = None
|
||||
else:
|
||||
# Enable seeking buttons
|
||||
self.backward.remove_style_class("disabled")
|
||||
self.forward.remove_style_class("disabled")
|
||||
# Start the timer if it's not already running
|
||||
if not self._progress_timer_id:
|
||||
self._progress_timer_id = GLib.timeout_add(1000, self._update_progress)
|
||||
# Initial progress update if possible
|
||||
self._update_progress() # Call once for immediate update
|
||||
|
||||
# Enable/disable prev/next based on capabilities
|
||||
if hasattr(mp, "can_go_previous") and mp.can_go_previous:
|
||||
self.prev.remove_style_class("disabled")
|
||||
else:
|
||||
self.prev.add_style_class("disabled")
|
||||
|
||||
if hasattr(mp, "can_go_next") and mp.can_go_next:
|
||||
self.next.remove_style_class("disabled")
|
||||
else:
|
||||
self.next.add_style_class("disabled")
|
||||
|
||||
def _set_cover_image(self, image_path):
|
||||
if image_path and os.path.isfile(image_path):
|
||||
self.cover.set_image_from_file(image_path)
|
||||
else:
|
||||
fallback = os.path.expanduser("~/Pictures/wallpaper/background.jpg")
|
||||
self.cover.set_image_from_file(fallback)
|
||||
file_obj = Gio.File.new_for_path(fallback)
|
||||
monitor = file_obj.monitor_file(Gio.FileMonitorFlags.NONE, None)
|
||||
monitor.connect("changed", self.on_wallpaper_changed)
|
||||
self._wallpaper_monitor = monitor
|
||||
|
||||
def _download_and_set_artwork(self, arturl):
|
||||
"""
|
||||
Download the artwork from the given URL asynchronously and update the cover image
|
||||
using GLib.idle_add to ensure UI updates occur on the main thread.
|
||||
"""
|
||||
try:
|
||||
parsed = urllib.parse.urlparse(arturl)
|
||||
suffix = os.path.splitext(parsed.path)[1] or ".png"
|
||||
with urllib.request.urlopen(arturl) as response:
|
||||
data = response.read()
|
||||
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=suffix)
|
||||
temp_file.write(data)
|
||||
temp_file.close()
|
||||
local_arturl = temp_file.name
|
||||
except Exception:
|
||||
local_arturl = os.path.expanduser("~/.current.wall")
|
||||
GLib.idle_add(self._set_cover_image, local_arturl)
|
||||
return None
|
||||
|
||||
def update_play_pause_icon(self):
|
||||
if self.mpris_player.playback_status == "playing":
|
||||
self.play_pause.get_child().set_markup(icons.pause)
|
||||
else:
|
||||
self.play_pause.get_child().set_markup(icons.play)
|
||||
|
||||
def on_wallpaper_changed(self, monitor, file, other_file, event):
|
||||
self.cover.set_image_from_file(os.path.expanduser("~/.current.wall"))
|
||||
|
||||
# --- Control methods, defined only once each ---
|
||||
def _on_prev_clicked(self, button):
|
||||
if self.mpris_player:
|
||||
self.mpris_player.previous()
|
||||
|
||||
def _on_play_pause_clicked(self, button):
|
||||
if self.mpris_player:
|
||||
self.mpris_player.play_pause()
|
||||
self.update_play_pause_icon()
|
||||
|
||||
def _on_backward_clicked(self, button):
|
||||
# Only seek if player exists, can seek, and button is not disabled
|
||||
if (
|
||||
self.mpris_player
|
||||
and self.mpris_player.can_seek
|
||||
and "disabled" not in self.backward.get_style_context().list_classes()
|
||||
):
|
||||
new_pos = max(0, self.mpris_player.position - 5000000) # 5 seconds backward
|
||||
self.mpris_player.position = new_pos
|
||||
|
||||
def _on_forward_clicked(self, button):
|
||||
# Only seek if player exists, can seek, and button is not disabled
|
||||
if (
|
||||
self.mpris_player
|
||||
and self.mpris_player.can_seek
|
||||
and "disabled" not in self.forward.get_style_context().list_classes()
|
||||
):
|
||||
new_pos = self.mpris_player.position + 5000000 # 5 seconds forward
|
||||
self.mpris_player.position = new_pos
|
||||
|
||||
def _on_next_clicked(self, button):
|
||||
if self.mpris_player:
|
||||
self.mpris_player.next()
|
||||
|
||||
def _update_progress(self):
|
||||
# Timer is now only active if can_seek is true, so no need for the initial check
|
||||
if not self.mpris_player: # Still need to check if player exists
|
||||
# Should not happen if timer logic is correct, but good safeguard
|
||||
if self._progress_timer_id:
|
||||
GLib.source_remove(self._progress_timer_id)
|
||||
self._progress_timer_id = None
|
||||
return False # Stop timer
|
||||
|
||||
try:
|
||||
current = self.mpris_player.position
|
||||
except Exception:
|
||||
current = 0
|
||||
try:
|
||||
total = int(self.mpris_player.length or 0)
|
||||
except Exception:
|
||||
total = 0
|
||||
|
||||
# Prevent division by zero or invalid updates
|
||||
if total <= 0:
|
||||
progress = 0.0
|
||||
self.time.set_text("--:-- / --:--")
|
||||
# Don't stop the timer here, length might become available later
|
||||
else:
|
||||
progress = current / total
|
||||
self.time.set_text(
|
||||
f"{self._format_time(current)} / {self._format_time(total)}"
|
||||
)
|
||||
|
||||
self.progressbar.set_value(progress)
|
||||
return True # Continue the timer
|
||||
|
||||
def _format_time(self, us):
|
||||
seconds = int(us / 1000000)
|
||||
minutes = seconds // 60
|
||||
seconds = seconds % 60
|
||||
return f"{minutes}:{seconds:02}"
|
||||
|
||||
def _update_metadata(self):
|
||||
if not self.mpris_player:
|
||||
return False
|
||||
self._apply_mpris_properties()
|
||||
return True
|
||||
|
||||
def _on_mpris_changed(self, *args):
|
||||
# Debounce metadata updates to avoid excessive work on the main thread.
|
||||
if not hasattr(self, "_update_pending") or not self._update_pending:
|
||||
self._update_pending = True
|
||||
# Use idle_add for potentially faster UI response than timeout_add(100)
|
||||
GLib.idle_add(self._apply_mpris_properties_debounced)
|
||||
|
||||
def _apply_mpris_properties_debounced(self):
|
||||
# Ensure player still exists before applying properties
|
||||
if self.mpris_player:
|
||||
self._apply_mpris_properties()
|
||||
else:
|
||||
# Player vanished, ensure timer is stopped if it was running
|
||||
if self._progress_timer_id:
|
||||
GLib.source_remove(self._progress_timer_id)
|
||||
self._progress_timer_id = None
|
||||
self._update_pending = False
|
||||
return False
|
||||
|
||||
|
||||
class Player(Box):
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
name="player",
|
||||
orientation="v",
|
||||
h_align="fill",
|
||||
spacing=0,
|
||||
h_expand=False,
|
||||
v_expand=True,
|
||||
)
|
||||
self.player_stack = Stack(
|
||||
name="player-stack",
|
||||
transition_type="slide-left-right",
|
||||
transition_duration=500,
|
||||
v_align="center",
|
||||
v_expand=True,
|
||||
)
|
||||
self.switcher = Gtk.StackSwitcher(
|
||||
name="player-switcher",
|
||||
spacing=8,
|
||||
)
|
||||
self.switcher.set_stack(self.player_stack)
|
||||
self.switcher.set_halign(Gtk.Align.CENTER)
|
||||
self.mpris_manager = MprisPlayerManager()
|
||||
players = self.mpris_manager.players
|
||||
if players:
|
||||
for p in players:
|
||||
mp = MprisPlayer(p)
|
||||
pb = PlayerBox(mpris_player=mp)
|
||||
self.player_stack.add_titled(pb, mp.player_name, mp.player_name)
|
||||
else:
|
||||
pb = PlayerBox(mpris_player=None)
|
||||
self.player_stack.add_titled(pb, "nothing", "Nothing Playing")
|
||||
self.mpris_manager.connect("player-appeared", self.on_player_appeared)
|
||||
self.mpris_manager.connect("player-vanished", self.on_player_vanished)
|
||||
self.switcher.set_visible(True)
|
||||
self.add(self.player_stack)
|
||||
self.add(self.switcher)
|
||||
GLib.idle_add(self._replace_switcher_labels)
|
||||
|
||||
def on_player_appeared(self, manager, player):
|
||||
children = self.player_stack.get_children()
|
||||
if len(children) == 1 and not getattr(children[0], "mpris_player", None):
|
||||
self.player_stack.remove(children[0])
|
||||
mp = MprisPlayer(player)
|
||||
pb = PlayerBox(mpris_player=mp)
|
||||
self.player_stack.add_titled(pb, mp.player_name, mp.player_name)
|
||||
# Timer is now started conditionally within PlayerBox.__init__
|
||||
self.switcher.set_visible(True)
|
||||
GLib.idle_add(lambda: self._update_switcher_for_player(mp.player_name))
|
||||
GLib.idle_add(self._replace_switcher_labels)
|
||||
|
||||
def on_player_vanished(self, manager, player_name):
|
||||
for child in self.player_stack.get_children():
|
||||
if (
|
||||
hasattr(child, "mpris_player")
|
||||
and child.mpris_player
|
||||
and child.mpris_player.player_name == player_name
|
||||
):
|
||||
self.player_stack.remove(child)
|
||||
break
|
||||
if not any(
|
||||
getattr(child, "mpris_player", None)
|
||||
for child in self.player_stack.get_children()
|
||||
):
|
||||
pb = PlayerBox(mpris_player=None)
|
||||
self.player_stack.add_titled(pb, "nothing", "Nothing Playing")
|
||||
self.switcher.set_visible(True)
|
||||
GLib.idle_add(self._replace_switcher_labels)
|
||||
|
||||
def _replace_switcher_labels(self):
|
||||
buttons = self.switcher.get_children()
|
||||
for btn in buttons:
|
||||
if isinstance(btn, Gtk.ToggleButton):
|
||||
default_label = None
|
||||
for child in btn.get_children():
|
||||
if isinstance(child, Gtk.Label):
|
||||
default_label = child
|
||||
break
|
||||
if default_label:
|
||||
label_player_name = getattr(
|
||||
default_label, "player_name", default_label.get_text().lower()
|
||||
)
|
||||
icon_markup = get_player_icon_markup_by_name(label_player_name)
|
||||
btn.remove(default_label)
|
||||
new_label = Label(name="player-label", markup=icon_markup)
|
||||
new_label.player_name = label_player_name
|
||||
btn.add(new_label)
|
||||
new_label.show_all()
|
||||
return False
|
||||
|
||||
def _update_switcher_for_player(self, player_name):
|
||||
for btn in self.switcher.get_children():
|
||||
if isinstance(btn, Gtk.ToggleButton):
|
||||
default_label = None
|
||||
for child in btn.get_children():
|
||||
if isinstance(child, Gtk.Label):
|
||||
default_label = child
|
||||
break
|
||||
if default_label:
|
||||
label_player_name = getattr(
|
||||
default_label, "player_name", default_label.get_text().lower()
|
||||
)
|
||||
if label_player_name == player_name.lower():
|
||||
icon_markup = get_player_icon_markup_by_name(player_name)
|
||||
btn.remove(default_label)
|
||||
new_label = Label(name="player-label", markup=icon_markup)
|
||||
new_label.player_name = player_name.lower()
|
||||
btn.add(new_label)
|
||||
new_label.show_all()
|
||||
return False
|
||||
|
||||
|
||||
class PlayerSmall(CenterBox):
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
name="player-small", orientation="h", h_align="fill", v_align="center"
|
||||
)
|
||||
self._show_artist = False # toggle flag
|
||||
self._display_options = ["cavalcade", "title", "artist"]
|
||||
self._display_index = 0
|
||||
self._current_display = "cavalcade"
|
||||
|
||||
self.mpris_icon = Button(
|
||||
name="compact-mpris-icon",
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
child=Label(name="compact-mpris-icon-label", markup=icons.disc),
|
||||
)
|
||||
# Remove scroll events; instead, add button press events.
|
||||
self.mpris_icon.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)
|
||||
self.mpris_icon.connect("button-press-event", self._on_icon_button_press)
|
||||
# Prevent the child from propagating events.
|
||||
child = self.mpris_icon.get_child()
|
||||
child.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)
|
||||
child.connect("button-press-event", lambda widget, event: True)
|
||||
# Add hover effect
|
||||
add_hover_cursor(self.mpris_icon)
|
||||
|
||||
self.mpris_label = Label(
|
||||
name="compact-mpris-label",
|
||||
label="Nothing Playing",
|
||||
ellipsization="end",
|
||||
max_chars_width=26,
|
||||
h_align="center",
|
||||
)
|
||||
self.mpris_button = Button(
|
||||
name="compact-mpris-button",
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
child=Label(name="compact-mpris-button-label", markup=icons.play),
|
||||
)
|
||||
self.mpris_button.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)
|
||||
self.mpris_button.connect(
|
||||
"button-press-event", self._on_play_pause_button_press
|
||||
)
|
||||
# Add hover effect
|
||||
add_hover_cursor(self.mpris_button)
|
||||
|
||||
# self.cavalcade = SpectrumRender()
|
||||
# self.cavalcade_box = self.cavalcade.get_spectrum_box()
|
||||
|
||||
self.center_stack = Stack(
|
||||
name="compact-mpris",
|
||||
transition_type="crossfade",
|
||||
transition_duration=100,
|
||||
v_align="center",
|
||||
v_expand=False,
|
||||
children=[
|
||||
# self.cavalcade_box,
|
||||
self.mpris_label,
|
||||
],
|
||||
)
|
||||
# self.center_stack.set_visible_child(self.cavalcade_box) # default to cavalcade
|
||||
|
||||
# Create additional compact view.
|
||||
self.mpris_small = CenterBox(
|
||||
name="compact-mpris",
|
||||
orientation="h",
|
||||
h_expand=True,
|
||||
h_align="fill",
|
||||
v_align="center",
|
||||
v_expand=False,
|
||||
start_children=self.mpris_icon,
|
||||
center_children=self.center_stack, # Changed to center_stack to handle stack switching
|
||||
end_children=self.mpris_button,
|
||||
)
|
||||
|
||||
self.add(self.mpris_small)
|
||||
|
||||
self.mpris_manager = MprisPlayerManager()
|
||||
self.mpris_player = None
|
||||
# Almacenar el índice del reproductor actual
|
||||
self.current_index = 0
|
||||
|
||||
players = self.mpris_manager.players
|
||||
if players:
|
||||
mp = MprisPlayer(players[self.current_index])
|
||||
self.mpris_player = mp
|
||||
self._apply_mpris_properties()
|
||||
self.mpris_player.connect("changed", self._on_mpris_changed)
|
||||
else:
|
||||
self._apply_mpris_properties()
|
||||
|
||||
self.mpris_manager.connect("player-appeared", self.on_player_appeared)
|
||||
self.mpris_manager.connect("player-vanished", self.on_player_vanished)
|
||||
self.mpris_button.connect("clicked", self._on_play_pause_clicked)
|
||||
|
||||
def _apply_mpris_properties(self):
|
||||
if not self.mpris_player:
|
||||
self.mpris_label.set_text("Nothing Playing")
|
||||
self.mpris_button.get_child().set_markup(icons.stop)
|
||||
self.mpris_icon.get_child().set_markup(icons.disc)
|
||||
if self._current_display != "cavalcade":
|
||||
self.center_stack.set_visible_child(
|
||||
self.mpris_label
|
||||
) # if was title or artist, keep showing label
|
||||
# else:
|
||||
# self.center_stack.set_visible_child(
|
||||
# self.cavalcade_box
|
||||
# ) # default to cavalcade if no player
|
||||
return
|
||||
|
||||
mp = self.mpris_player
|
||||
|
||||
# Choose icon based on player name.
|
||||
player_name = (
|
||||
mp.player_name.lower()
|
||||
if hasattr(mp, "player_name") and mp.player_name
|
||||
else ""
|
||||
)
|
||||
icon_markup = get_player_icon_markup_by_name(player_name)
|
||||
self.mpris_icon.get_child().set_markup(icon_markup)
|
||||
self.update_play_pause_icon()
|
||||
|
||||
if self._current_display == "title":
|
||||
text = mp.title if mp.title and mp.title.strip() else "Nothing Playing"
|
||||
self.mpris_label.set_text(text)
|
||||
self.center_stack.set_visible_child(self.mpris_label)
|
||||
elif self._current_display == "artist":
|
||||
text = mp.artist if mp.artist else "Nothing Playing"
|
||||
self.mpris_label.set_text(text)
|
||||
self.center_stack.set_visible_child(self.mpris_label)
|
||||
# else: # default cavalcade
|
||||
# self.center_stack.set_visible_child(self.cavalcade_box)
|
||||
|
||||
def _on_icon_button_press(self, widget, event):
|
||||
from gi.repository import Gdk
|
||||
|
||||
if event.type == Gdk.EventType.BUTTON_PRESS:
|
||||
players = self.mpris_manager.players
|
||||
if not players:
|
||||
return True
|
||||
|
||||
if event.button == 2: # Middle-click: cycle display
|
||||
self._display_index = (self._display_index + 1) % len(
|
||||
self._display_options
|
||||
)
|
||||
self._current_display = self._display_options[self._display_index]
|
||||
self._apply_mpris_properties() # Re-apply to update label/cavalcade
|
||||
return True
|
||||
|
||||
# Cambiar de reproductor según el botón presionado.
|
||||
if event.button == 1: # Left-click: next player
|
||||
self.current_index = (self.current_index + 1) % len(players)
|
||||
elif event.button == 3: # Right-click: previous player
|
||||
self.current_index = (self.current_index - 1) % len(players)
|
||||
if self.current_index < 0:
|
||||
self.current_index = len(players) - 1
|
||||
|
||||
mp_new = MprisPlayer(players[self.current_index])
|
||||
self.mpris_player = mp_new
|
||||
# Conectar el evento "changed" para que se actualice
|
||||
self.mpris_player.connect("changed", self._on_mpris_changed)
|
||||
self._apply_mpris_properties()
|
||||
return True # Se consume el evento
|
||||
return True
|
||||
|
||||
def _on_play_pause_button_press(self, widget, event):
|
||||
if event.type == Gdk.EventType.BUTTON_PRESS:
|
||||
if event.button == 1: # Click izquierdo -> track anterior
|
||||
if self.mpris_player:
|
||||
self.mpris_player.previous()
|
||||
self.mpris_button.get_child().set_markup(icons.prev)
|
||||
GLib.timeout_add(500, self._restore_play_pause_icon)
|
||||
elif event.button == 3: # Click derecho -> siguiente track
|
||||
if self.mpris_player:
|
||||
self.mpris_player.next()
|
||||
self.mpris_button.get_child().set_markup(icons.next)
|
||||
GLib.timeout_add(500, self._restore_play_pause_icon)
|
||||
elif event.button == 2: # Click medio -> play/pausa
|
||||
if self.mpris_player:
|
||||
self.mpris_player.play_pause()
|
||||
self.update_play_pause_icon()
|
||||
return True
|
||||
return True
|
||||
|
||||
def _restore_play_pause_icon(self):
|
||||
self.update_play_pause_icon()
|
||||
return False
|
||||
|
||||
def _on_icon_clicked(
|
||||
self, widget
|
||||
): # No longer used, logic moved to _on_icon_button_press
|
||||
pass
|
||||
|
||||
def update_play_pause_icon(self):
|
||||
if self.mpris_player and self.mpris_player.playback_status == "playing":
|
||||
self.mpris_button.get_child().set_markup(icons.pause)
|
||||
else:
|
||||
self.mpris_button.get_child().set_markup(icons.play)
|
||||
|
||||
def _on_play_pause_clicked(self, button):
|
||||
if self.mpris_player:
|
||||
self.mpris_player.play_pause()
|
||||
self.update_play_pause_icon()
|
||||
|
||||
def _on_mpris_changed(self, *args):
|
||||
# Update properties when the player's state changes.
|
||||
self._apply_mpris_properties()
|
||||
|
||||
def on_player_appeared(self, manager, player):
|
||||
# When a new player appears, use it if no player is active.
|
||||
if not self.mpris_player:
|
||||
mp = MprisPlayer(player)
|
||||
self.mpris_player = mp
|
||||
self._apply_mpris_properties()
|
||||
self.mpris_player.connect("changed", self._on_mpris_changed)
|
||||
|
||||
def on_player_vanished(self, manager, player_name):
|
||||
players = self.mpris_manager.players
|
||||
if (
|
||||
players
|
||||
and self.mpris_player
|
||||
and self.mpris_player.player_name == player_name
|
||||
):
|
||||
if players: # Check if players is not empty after vanishing
|
||||
self.current_index = self.current_index % len(players)
|
||||
new_player = MprisPlayer(players[self.current_index])
|
||||
self.mpris_player = new_player
|
||||
self.mpris_player.connect("changed", self._on_mpris_changed)
|
||||
else:
|
||||
self.mpris_player = None # No players left
|
||||
elif not players:
|
||||
self.mpris_player = None
|
||||
self._apply_mpris_properties()
|
||||
Reference in New Issue
Block a user