New Buttons (#5)

Added two new buttons to the player:
 - Favorite song button
 - Queue mode button

Reviewed-on: #5
This commit is contained in:
0x01FE committed 2025-11-05 13:16:51 -06:00
1 parent 4c6976b2df
commit 0ca2547b54
4 files changed
+102 -2

No files matched your search

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 33 KiB

+15
View File
@@ -80,3 +80,18 @@ class NavidromeAPI:
return [SubSonic.Album(album_d) for album_d in response.json()]
def getSong(self, id: str):
url = self.base_url + '/api/song?'
payload = {
"id" : id
}
response = requests.get(url + self._encodePayload(payload), headers=self.auth_header)
return response.json()
def isSongStarred(self, id: str) -> bool:
info = self.getSong(id)
return info[0]['starred']
+4 -1
View File
@@ -306,7 +306,7 @@ class SubpyConn:
return starred
# Returns a requests Response object, the data stream can be found in Response.content
def stream(self, song_id: str, maxBitRate: int | None = None) -> requests.Response:
def stream(self, song_id: str, maxBitRate: int | None = None, format: str | None = None) -> requests.Response:
url = self.base_url + '/rest/stream?'
payload = self._getBasePayload()
@@ -314,6 +314,9 @@ class SubpyConn:
if maxBitRate:
payload['maxBitRate'] = maxBitRate
if format:
payload['format'] = format
payload['id'] = song_id
return requests.get(url + self._encodePayload(payload), stream=True)
+83 -1
View File
@@ -56,6 +56,12 @@ conn = SubSonic.SubpyConn(user, passw, MUSIC_SERVER_URL)
pygame.mixer.init()
SONG_LIKED_CHAR = '♥'
SONG_NOT_LIKED_CHAR = '♡'
LOOP_CHAR = '⮔'
INFINITY_CHAR = '∞'
THREE_BARS_CHAR = '≡'
class AlbumSongs(Screen):
BINDINGS = [
('escape', 'die()', "Back"),
@@ -1080,6 +1086,9 @@ class Player(Screen):
def on_mount(self) -> None:
app.sub_title = "Player"
pause_play: Button = self.query_one("#play_pause_track", Button)
pause_play.focus()
def update_song(self, song: SubSonic.Song) -> None:
if song.album and song.title and song.artist:
@@ -1098,6 +1107,11 @@ class Player(Screen):
if cover_id:
cover_art.get_cover_art(cover_id)
if song.id:
is_song_starred: bool = app.navidrome.isSongStarred(song.id)
like_song_button: Button = self.query_one('#like_track', Button)
like_song_button.label = SONG_LIKED_CHAR if is_song_starred else SONG_NOT_LIKED_CHAR
def compose(self) -> ComposeResult:
yield Header(icon="")
with Horizontal(id="headerbs"):
@@ -1121,9 +1135,11 @@ class Player(Screen):
yield TotalDuration(classes='playerProgressBarTotalTime')
with Horizontal(id='playerButtons'):
yield Button(SONG_NOT_LIKED_CHAR, id='like_track', classes='playerButton')
yield Button('|<', id='prev_track', classes='playerButton')
yield Button('|>/||', id='play_pause_track', classes='playerButton')
yield Button('>|', id='next_track', classes='playerButton')
yield Button(THREE_BARS_CHAR, id='loop_queue', classes='playerButton')
yield Footer()
@@ -1132,6 +1148,9 @@ class Player(Screen):
app.sub_title = "Player"
app.update_now_playing()
pause_play: Button = self.query_one("#play_pause_track", Button)
pause_play.focus()
@on(Button.Pressed, "#prev_track")
def prev_track_pressed(self) -> None:
app.action_rewind()
@@ -1148,6 +1167,40 @@ class Player(Screen):
# app.install_screen(QueueScreen, 'queue')
app.push_screen(QueueScreen())
@on(Button.Pressed, '#like_track')
def like_track(self) -> None:
song = app.currently_playing
if song:
is_song_starred: bool = app.navidrome.isSongStarred(song.id)
like_song_button: Button = self.query_one('#like_track', Button)
if not is_song_starred:
conn.star(song.id)
app.notify(f'Starred song "{song.title}"')
like_song_button.label = SONG_LIKED_CHAR
else:
conn.unstar(song.id)
app.notify(f'Unstarred song "{song.title}"')
like_song_button.label = SONG_NOT_LIKED_CHAR
@on(Button.Pressed, '#loop_queue')
def loop_queue(self) -> None:
queue_button: Button = self.query_one('#loop_queue', Button)
if app.queue_mode == 0:
app.queue_mode = 1
queue_button.label = LOOP_CHAR
app.notify('Queue mode set to "Loop Queue"')
elif app.queue_mode == 1:
app.queue_mode = 2
queue_button.label = INFINITY_CHAR
app.notify('Queue mode set to "Loop Song"')
elif app.queue_mode == 2:
app.queue_mode = 0
queue_button.label = THREE_BARS_CHAR
app.notify('Queue mode set to "Queue"')
# Startup keyboard listener for media keys
def listen_media_keys(queue: multiprocessing.Queue):
@@ -1225,6 +1278,14 @@ class SubPy(App):
check_playing_timer: Timer | None = None
check_music_commands: Timer
"""
Modes:
0: standard queue, is done when queue is done
1: loop, loops queue after queue is done
2: loop song, loops song
"""
queue_mode: int = 0
def on_mount(self) -> None:
self.title = "SubPy"
self.switch_mode('main')
@@ -1298,9 +1359,16 @@ class SubPy(App):
def check_done_playing(self) -> None:
if not pygame.mixer.music.get_busy() and not self.is_paused and self.currently_playing:
if self.queue_mode == 2:
self.play_song(app.currently_playing)
if self.check_playing_timer:
self.check_playing_timer.stop()
return
self.play_history.append(self.currently_playing)
self.is_played = False
self.is_playing = False
next_song: SubSonic.Song | None = None
if self.user_queue:
@@ -1308,6 +1376,13 @@ class SubPy(App):
elif self.song_queue:
next_song = self.song_queue.pop(0)
if not next_song and self.queue_mode == 1:
self.song_queue = self.play_history.copy()
if self.user_queue:
next_song = self.user_queue.pop(0)
elif self.song_queue:
next_song = self.song_queue.pop(0)
if next_song:
self.play_song(next_song)
@@ -1328,6 +1403,13 @@ class SubPy(App):
elif self.song_queue:
next_song = self.song_queue.pop(0)
if not next_song and self.queue_mode == 1:
self.song_queue = self.play_history.copy()
if self.user_queue:
next_song = self.user_queue.pop(0)
elif self.song_queue:
next_song = self.song_queue.pop(0)
if next_song:
self.play_song(next_song)