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()