dnd-music-console/src/croaker/gui.py
2025-09-14 15:23:15 -07:00

186 lines
6.0 KiB
Python

import threading
import time
import gi
import vlc
from croaker import path
from croaker.playlist import Playlist, load_playlist
gi.require_version("Gtk", "4.0")
gi.require_version("Gdk", "4.0")
from gi.repository import Gdk, GLib, GObject, Gtk, Pango # noqa E402
class PlayerWindow(Gtk.ApplicationWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._max_width = 300
self._max_height = 330
self._artwork_width = self._max_width
self._artwork_height = 248
css_provider = Gtk.CssProvider()
css_provider.load_from_path(str(path.assets() / "style.css"))
Gtk.StyleContext.add_provider_for_display(
Gdk.Display.get_default(), css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
)
self.set_title("Croaker Radio")
self._root = Gtk.Fixed()
self._root.set_size_request(self._max_width, self._max_height)
self.set_child(self._root)
self._artwork = Gtk.Fixed()
self._track = None
self._artist = None
self._album = None
self._draw_window()
def _draw_window(self):
margin_size = 8
label_width = self._max_width - (2 * margin_size)
label_height = 16
label_spacing = 8
self._artwork.set_size_request(self._artwork_width, self._artwork_height)
self._root.put(self._artwork, 0, 0)
self.draw_artwork()
def label(text: str):
l = Gtk.Label()
l.set_ellipsize(Pango.EllipsizeMode.END)
l.add_css_class("label")
l.set_text(text)
l.set_size_request(label_width, label_height)
l.set_justify(Gtk.Justification.LEFT)
l.set_hexpand(True)
l.set_xalign(0)
return l
self._track = label("CROAKER RADIO")
self._track.add_css_class("now_playing")
self._root.put(self._track, margin_size, self._artwork_height + label_spacing)
self._artist = label("Artist")
self._root.put(self._artist, margin_size, self._artwork_height + (2 * label_spacing) + label_height)
self._album = label("Album")
self._root.put(self._album, margin_size, self._artwork_height + (3 * label_spacing) + (2 * label_height))
def now_playing(self, track: str, artist: str, album: str):
self._track.set_text(f"🎵 {track}")
self._artist.set_text(f"🐸 {artist}")
self._album.set_text(f"💿 {album}")
def draw_artwork(self):
image1 = Gtk.Image()
image1.set_from_file(str(path.assets() / "froghat.png"))
image1.set_size_request(self._artwork_width, self._artwork_height)
image1.add_css_class("artwork")
self._artwork.put(image1, 0, 0)
class GUI(Gtk.Application):
"""
A simple GTK application that instaniates a VLC player and listens for commands.
"""
def __init__(self):
super().__init__()
self._playlist: Playlist | None = None
self._vlc_instance = vlc.Instance("--loop")
self._media_list_player = vlc.MediaListPlayer()
self._player.audio_set_volume(30)
self._signal_handler = threading.Thread(target=self._wait_for_signals)
self._signal_handler.daemon = True
self.play_requested = threading.Event()
self.back_requested = threading.Event()
self.ffwd_requested = threading.Event()
self.stop_requested = threading.Event()
self.load_requested = threading.Event()
self.clear_requested = threading.Event()
self.shutdown_requested = threading.Event()
GLib.set_application_name("Croaker Radio")
@property
def _player(self):
return self._media_list_player.get_media_player()
def do_activate(self):
self._signal_handler.start()
self._window = PlayerWindow(application=self)
self._window.present()
def load(self, playlist_name: str):
self.clear()
self._playlist = load_playlist(playlist_name)
media = self._vlc_instance.media_list_new()
for track in self._playlist.tracks:
media.add_media(self._vlc_instance.media_new(track))
self._media_list_player.set_media_list(media)
self._media_list_player.play()
self._update_now_playing()
events = self._player.event_manager()
events.event_attach(vlc.EventType.MediaPlayerMediaChanged, self._update_now_playing)
def _update_now_playing(self, event=None):
track = "[NOTHING PLAYING]"
artist = "artist"
album = "album"
media = self._player.get_media()
if media:
media.parse()
track = media.get_meta(vlc.Meta.Title)
artist = media.get_meta(vlc.Meta.Artist)
album = media.get_meta(vlc.Meta.Album)
self._window.now_playing(track, artist, album)
def _wait_for_signals(self):
while not self.shutdown_requested.is_set():
if self.play_requested.is_set():
self.play_requested.clear()
GLib.idle_add(self._media_list_player.play)
if self.back_requested.is_set():
self.back_requested.clear()
GLib.idle_add(self._media_list_player.previous)
if self.ffwd_requested.is_set():
self.ffwd_requested.clear()
GLib.idle_add(self._media_list_player.next)
if self.stop_requested.is_set():
self.stop_requested.clear()
GLib.idle_add(self._media_list_player.stop)
if self.load_requested.is_set():
self.load_requested.clear()
GLib.idle_add(self._media_list_player.load)
if self.clear_requested.is_set():
self.clear_requested.clear()
GLib.idle_add(self.clear)
time.sleep(0.25)
GLib.idle_add(self.quit)
exit()
def clear(self):
if self._media_list_player:
self._media_list_player.stop()
self._playlist = None
def quit(self):
self.clear()
self._vlc_instance.release()
exit()