import logging import os from enum import Enum LOG_FILE = 'textual.log' LOGGING_LEVEL = logging.DEBUG LOG_FORMAT = '[%(asctime)s] %(levelname)s in %(module)s: %(message)s' # Clear Log File os.remove(LOG_FILE) logging.basicConfig(filename=LOG_FILE, level=LOGGING_LEVEL, format=LOG_FORMAT) import SubSonic from textual import on, events from textual.app import App, ComposeResult from textual.widgets import Footer, Header, OptionList, DataTable from textual.widgets.option_list import Option u = 'pseudo' p = 'telegram9dock-MICHEL' conn = SubSonic.SubpyConn(u, p, "music.0x01fe.net") class Menus(Enum): MAIN = "MAIN" ARTISTS = "Artists" ARTISTSONGS = "ArtistSongs" ARTISTALBUMS = "ArtistAlbums" ALBUMS = "Albums" ALBUMSONGS = "AlbumSongs" class TextualSubpyPlayer(App): BINDINGS = [ ('backspace', 'menu_back()', 'Go back a menu') ] artists: list[SubSonic.Artist] selectedArtist: SubSonic.Artist albums: list[SubSonic.Album] conn: SubSonic.SubpyConn currentMenu: int lastMenu: int def compose(self) -> ComposeResult: self.currentMenu = Menus.MAIN self.lastMenu = Menus.MAIN yield OptionList( Option("Browse Artists", id="BrowseArtists"), Option("Browse Albums"), Option("Browse Songs"), Option("Browse Playlists"), id=Menus.MAIN.value ) async def action_menu_back(self) -> None: logging.debug('Menu Back Called') match self.lastMenu: case Menus.MAIN: await self.main_menu() case Menus.ARTISTALBUMS: await self.selectArtistAlbumView(self.selectedArtist.id) case Menus.ARTISTS: await self.selectArtistsView() def on_key(self, event: events.Key) -> None: logging.debug(f'Key Pressed {event.name}') def action_toggle_dark(self) -> None: """An action to toggle dark mode.""" self.theme = ( "textual-dark" if self.theme == "textual-light" else "textual-light" ) def _getArtistMenu(self) -> OptionList: self.artists = self.conn.getArtists() return OptionList(id="ArtistList") @on(OptionList.OptionSelected, "#mainMenu") async def selectMainMenu(self, event: OptionList.OptionSelected) -> None: match event.option_id: case "BrowseArtists": logging.debug('View Artists Selected') await self.selectArtistsView() @on(OptionList.OptionSelected, "#ArtistList") async def artistSelected(self, event: OptionList.OptionSelected) -> None: logging.debug(f'Artist {event.option.prompt} Selected') self.lastMenu = Menus.ARTISTS for artist in self.artists: if event.option_id == artist.id: self.selectedArtist = artist await self.selectArtistAlbumView(self.selectedArtist.id) @on(DataTable.RowSelected, "#ArtistAlbums") async def ArtistAlbumSelected(self, event: DataTable.RowSelected) -> None: self.lastMenu = Menus.ARTISTALBUMS row_key = event.row_key row = event.data_table.get_row(row_key) logging.debug(f'Album {row[0]} Selected') await self.selectAlbumSongsView(row_key.value) async def selectAlbumSongsView(self, album_id: str) -> None: logging.debug("Setting up album songs DataTable") self.currentMenu = Menus.ALBUMSONGS songs = conn.getAlbumSongs(album_id) albumSongs = DataTable(id="AlbumSongs") await self.mount(albumSongs) albumSongs.cursor_type = 'row' albumSongs.add_columns("#", "Title", "Artist", "Duration", "Bitrate") for song in songs: albumSongs.add_row(song.trackNumber, song.title, song.artist, song.duration, song.bitRate, key=song.id) self.query_one(f"#{self.lastMenu}").remove() async def selectArtistAlbumView(self, artist_id: str) -> None: logging.debug("Setting up album DataTable") self.currentMenu = Menus.ARTISTALBUMS albumTable = DataTable(id="ArtistAlbums") await self.mount(albumTable) albumTable.cursor_type = 'row' albumTable.add_columns("Title", "Songs") self.albums = conn.getArtistAlbums(artist_id) for album in self.albums: albumTable.add_row(album.title, album.songCount, key=album.id) self.query_one(f"#{self.lastMenu}").remove() async def main_menu(self) -> None: await self.mount(OptionList( Option("Browse Artists", id="BrowseArtists"), Option("Browse Albums"), Option("Browse Songs"), Option("Browse Playlists"), id=Menus.MAIN.value )) self.query_one(f"#{self.lastMenu}").remove() async def selectArtistsView(self) -> None: artistList: OptionList = self._getArtistMenu() await self.mount(artistList) for artist in self.artists: artistList.add_option(Option(artist.name, artist.id)) self.query_one(f"#{self.lastMenu.value}").remove() if __name__ == '__main__': app = TextualSubpyPlayer() app.conn = SubSonic.SubpyConn(u, p, "https://music.0x01fe.net") app.run()