From 4c6976b2df9a67977b9deea6fdd53ee453b92010 Mon Sep 17 00:00:00 2001 From: 0x01FE <0x01fe@noreply.localhost> Date: Mon, 3 Nov 2025 14:07:50 -0600 Subject: [PATCH] Album Screen Redesign (#4) Album screen redo Reviewed-on: https://git.0x01fe.net/0x01FE/subpy/pulls/4 --- .attachments/albums-screen-random.png | Bin 0 -> 57420 bytes Navidrome.py | 22 ++++ README.md | 4 + SubSonic.py | 10 +- app.py | 180 +++++++++++++++----------- main.tcss | 23 +++- 6 files changed, 155 insertions(+), 84 deletions(-) create mode 100644 .attachments/albums-screen-random.png diff --git a/.attachments/albums-screen-random.png b/.attachments/albums-screen-random.png new file mode 100644 index 0000000000000000000000000000000000000000..1e9cfbb73ac7a04fb09777d31969b627bf5802be GIT binary patch literal 57420 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 literal 0 HcmV?d00001 diff --git a/Navidrome.py b/Navidrome.py index 5c0c4ac..ea0a837 100644 --- a/Navidrome.py +++ b/Navidrome.py @@ -57,4 +57,26 @@ class NavidromeAPI: return [SubSonic.Song(song_d) for song_d in response.json()] + def getAlbums(self, end: int, start: int, order: str = 'ACS', sort: str = 'name', name: str | None = None, seed: float | None = None) -> list[SubSonic.Album]: + url = self.base_url + '/api/album?' + + payload = { + "_end": end, + "_order": order, + "_sort" : sort, + "_start": start, + "seed": 0.0 + } + + if seed: + payload['seed'] = seed + + if name: + payload['name'] = name + + response = requests.get(url + self._encodePayload(payload), headers=self.auth_header) + + print(response.content) + + return [SubSonic.Album(album_d) for album_d in response.json()] diff --git a/README.md b/README.md index a3296f6..873a2b4 100644 --- a/README.md +++ b/README.md @@ -36,3 +36,7 @@ Artists Page View of favorite songs ![View of favorited songs](.attachments/songs-screen-favourites.png) + +View of some random albums + +![View of some random albums](.attachments/albums-screen-random.png) diff --git a/SubSonic.py b/SubSonic.py index bec6de4..b298d5e 100644 --- a/SubSonic.py +++ b/SubSonic.py @@ -32,7 +32,7 @@ class Album: name: str album: str artist: str - coverArtId: str + coverArtId: str | None duration: int artistId: str songCount: int @@ -42,11 +42,11 @@ class Album: def __init__(self, album_info: dict): self.id = album_info['id'] - self.title = album_info['title'] self.name = album_info['name'] - self.album = album_info['album'] + self.title = album_info['title'] if 'title' in album_info else self.name + self.album = album_info['album'] if 'album' in album_info else self.name self.artist = album_info['artist'] - self.coverArtId = album_info['coverArt'] + self.coverArtId = album_info['coverArt'] if 'coverArt' in album_info else None self.duration = int(album_info['duration']) self.artistId = album_info['artistId'] self.songCount = int(album_info['songCount']) @@ -448,5 +448,3 @@ class SubpyConn: return random_songs - - diff --git a/app.py b/app.py index 51494b3..1441aee 100644 --- a/app.py +++ b/app.py @@ -561,11 +561,24 @@ class ArtistsGUI(Screen): artist_panel.set_artist(filtered_artists[self.artists_displayed]) self.artists_displayed += 1 - - class Albums(Screen): - albums: list[SubSonic.Album] + BINDINGS = [ + ("a", "page_backward()", "Prev. Page"), + ("d", "page_forward()", "Next Page") + ] + + modes: list[str] = ['All', 'Random', 'Recently Added'] + # list[page number, albums] + albums: list[tuple[int, list[SubSonic.Album]]] = [(0, []) for n in range(len(modes))] + mode: int = 0 + page: list[int] = [0 for n in range(len(modes))] + DATATABLE_SIZE: int + seed: float = random.random() + last_mode: int = 0 + + SPACE_NEEDED: int = 12 + search_value: str = '' def compose(self) -> ComposeResult: yield Header(icon='') @@ -575,21 +588,96 @@ class Albums(Screen): yield Label(Text("* F3 ", style="bold orange").append_text(Text("Albums", style="white")), classes="headerb") yield Label(Text("F4 ", style="bold orange").append_text(Text("Songs", style="white")), classes="headerb") yield Label(Text("F5 ", style="bold orange").append_text(Text("Playlists", style="white")), classes="headerb") - with Vertical(id="albumContainer"): - yield Input(placeholder="Search", id="albumSearchBar") - yield DataTable(id="albumList") + + with Horizontal(id='albumScreen'): + yield ListView( + *[ListItem(Label(mode_name)) for mode_name in self.modes], + id='albumModeSelectorList' + ) + + with Vertical(id="albumContainer"): + yield Input(placeholder="Search", id="albumSearchBar") + yield DataTable(id="albumList") + yield Label(id="pager") + yield Footer() def on_mount(self) -> None: app.sub_title = "Albums" - self.albums = conn.getAllAlbums() - albumTable = self.query_one(DataTable) + dt = self.query_one(DataTable) - albumTable.cursor_type = 'row' - albumTable.add_columns("Title", "Artist", "Songs", "Plays", "Year", "Time") + dt.cursor_type = 'row' + dt.add_columns("Title", "Artist", "Songs", "Plays", "Time") - for album in self.albums: - albumTable.add_row(album.title, album.artist, album.songCount, album.playCount, album.year, album.duration, key=album.id) + self.DATATABLE_SIZE = self.size.height - self.SPACE_NEEDED + self._update_datatable() + + self._update_pager() + + @on(events.Resize) + def on_resize(self) -> None: + self.DATATABLE_SIZE = self.size.height - self.SPACE_NEEDED + self._update_datatable() + + def action_page_forward(self) -> None: + self.page[self.mode] += 1 + self._update_datatable() + + def action_page_backward(self) -> None: + if self.page[self.mode] - 1 < 0: + return + + self.page[self.mode] -= 1 + self._update_datatable() + + def get_current_page(self) -> int: + return self.page[self.mode] + + def _get_offset(self) -> int: + return (self.get_current_page()) * self.DATATABLE_SIZE + + def _update_pager(self) -> None: + pager = self.query_one("#pager", Label) + pager.update(f'Page {self.page[self.mode] + 1}') + + def _update_datatable(self, force_update: bool = False) -> None: + dt = self.query_one(DataTable) + + space = self.DATATABLE_SIZE + + offset = self._get_offset() + current_page: int = self.get_current_page() + + page_n, albums = self.albums[self.mode] + + if self.page[self.mode] == 0 and self.mode == 1 and self.last_mode != 1: + self.seed = random.random() + albums = [] + + if page_n != current_page or not albums or len(albums) < space or force_update or current_page == 1: + + if self.mode == 0: + albums = app.navidrome.getAlbums(offset + space, offset, name=self.search_value) + elif self.mode == 1: + albums = app.navidrome.getAlbums(offset + space, offset, sort='random', seed=self.seed, name=self.search_value) + elif self.mode == 2: + albums = app.navidrome.getAlbums(offset + space, offset, order='DESC', sort='recently_added', name=self.search_value) + + self.albums[self.mode] = (current_page, albums) + + dt.clear() + + for album in albums[:space]: + dt.add_row(album.title, album.artist, album.songCount, album.playCount, album.duration, key=album.id) + + self.last_mode = self.mode + + self._update_pager() + + @on(ListView.Selected) + def mode_selected(self, e: ListView.Selected) -> None: + self.mode = e.index + self._update_datatable() @on(events.ScreenResume) def on_screen_resume(self, event: events.ScreenResume) -> None: @@ -602,14 +690,13 @@ class Albums(Screen): @on(Input.Submitted) def search_submit(self, event: Input.Submitted) -> None: - search_value = event.value.lower() + self.search_value = event.value.lower() - album_table = self.query_one(DataTable) + if self.search_value: + self.page[self.mode] = 0 + + self._update_datatable(force_update=True) - album_table.clear() - for album in self.albums: - if search_value in album.title.lower() or search_value in album.artist.lower(): - album_table.add_row(album.title, album.artist, album.songCount, album.playCount, album.year, album.duration, key=album.id) class Playlists(Screen): @@ -829,63 +916,6 @@ class SongsScreen(Screen): for song in self.songs: dt.add_row(song.title, song.album, song.artist, song.playCount, song.duration) -class Favourites(Screen): - BINDINGS = [ - ('p', 'play()', 'Play Song'), - ('q', 'queue()', 'Queue Song'), - ('m', 'shuffle_mix()', 'Shuffle Play') - ] - - songs: list[SubSonic.Song] - selected_song_id: str | None = None - - def compose(self) -> ComposeResult: - yield Header(icon="") - with Horizontal(id="headerbs"): - yield Label(Text("F1 ", style="bold orange").append_text(Text("Player", style="white")), classes="headerb") - yield Label(Text("F2 ", style="bold orange").append_text(Text("Artists", style="white")), classes="headerb") - yield Label(Text("F3 ", style="bold orange").append_text(Text("Albums", style="white")), classes="headerb") - yield Label(Text("* F4 ", style="bold orange").append_text(Text("Songs", style="white")), classes="headerb") - yield Label(Text("F5 ", style="bold orange").append_text(Text("Playlists", style="white")), classes="headerb") - yield DataTable() - yield Footer() - - def on_mount(self) -> None: - app.sub_title = "Favourites" - self.songs = conn.getStarredSongs() - songTable = self.query_one(DataTable) - - songTable.cursor_type = "row" - songTable.add_columns("Title", "Album", "Artist", "Plays", "Duration", "Bitrate") - - for song in self.songs: - songTable.add_row(song.title, song.album, song.artist, song.playCount, song.duration, song.bitRate, key=song.id) - - @on(DataTable.RowHighlighted) - def row_highlighted(self, event: DataTable.RowHighlighted) -> None: - self.selected_song_id = event.row_key.value - - def action_play(self) -> None: - song_index = self._find_selected_song() - app.play_song(self.songs[song_index]) - app.song_queue = self.songs[song_index + 1:] - - def action_queue(self) -> None: - song_index = self._find_selected_song() - app.queue_song(self.songs[song_index]) - - # Returns index of selected song - def _find_selected_song(self) -> int: - for i, song in enumerate(self.songs): - if song.id == self.selected_song_id: - return i - - def action_shuffle_mix(self) -> None: - q = self.songs.copy() - random.shuffle(q) - app.play_song(q.pop(0)) - app.song_queue = q - class CoverArtPixels(Container): cover = reactive[Image.Image | None](None) diff --git a/main.tcss b/main.tcss index c3f24c4..d7d2770 100644 --- a/main.tcss +++ b/main.tcss @@ -183,6 +183,23 @@ QueueScreen { width: 1fr; } -# SongsScreen { -# background: red; -# } +#albumModeSelectorList { + height: 100%; + width: 0.2fr; +} + +#albumModeSelectorList Label { + padding: 1 2; +} + +#albumSearchBar { + margin: 1 0; +} + +#albumList { + min-height: 1fr; + height: 1fr; + # border: white; + margin: 1 1; +} +