New Buttons (#5)
Added two new buttons to the player: - Favorite song button - Queue mode button Reviewed-on: #5
This commit is contained in:
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 |
@@ -80,3 +80,18 @@ class NavidromeAPI:
|
|||||||
|
|
||||||
return [SubSonic.Album(album_d) for album_d in response.json()]
|
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
@@ -306,7 +306,7 @@ class SubpyConn:
|
|||||||
return starred
|
return starred
|
||||||
|
|
||||||
# Returns a requests Response object, the data stream can be found in Response.content
|
# 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?'
|
url = self.base_url + '/rest/stream?'
|
||||||
|
|
||||||
payload = self._getBasePayload()
|
payload = self._getBasePayload()
|
||||||
@@ -314,6 +314,9 @@ class SubpyConn:
|
|||||||
if maxBitRate:
|
if maxBitRate:
|
||||||
payload['maxBitRate'] = maxBitRate
|
payload['maxBitRate'] = maxBitRate
|
||||||
|
|
||||||
|
if format:
|
||||||
|
payload['format'] = format
|
||||||
|
|
||||||
payload['id'] = song_id
|
payload['id'] = song_id
|
||||||
|
|
||||||
return requests.get(url + self._encodePayload(payload), stream=True)
|
return requests.get(url + self._encodePayload(payload), stream=True)
|
||||||
|
|||||||
@@ -56,6 +56,12 @@ conn = SubSonic.SubpyConn(user, passw, MUSIC_SERVER_URL)
|
|||||||
|
|
||||||
pygame.mixer.init()
|
pygame.mixer.init()
|
||||||
|
|
||||||
|
SONG_LIKED_CHAR = '♥'
|
||||||
|
SONG_NOT_LIKED_CHAR = '♡'
|
||||||
|
LOOP_CHAR = '⮔'
|
||||||
|
INFINITY_CHAR = '∞'
|
||||||
|
THREE_BARS_CHAR = '≡'
|
||||||
|
|
||||||
class AlbumSongs(Screen):
|
class AlbumSongs(Screen):
|
||||||
BINDINGS = [
|
BINDINGS = [
|
||||||
('escape', 'die()', "Back"),
|
('escape', 'die()', "Back"),
|
||||||
@@ -1080,6 +1086,9 @@ class Player(Screen):
|
|||||||
def on_mount(self) -> None:
|
def on_mount(self) -> None:
|
||||||
app.sub_title = "Player"
|
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:
|
def update_song(self, song: SubSonic.Song) -> None:
|
||||||
if song.album and song.title and song.artist:
|
if song.album and song.title and song.artist:
|
||||||
|
|
||||||
@@ -1098,6 +1107,11 @@ class Player(Screen):
|
|||||||
if cover_id:
|
if cover_id:
|
||||||
cover_art.get_cover_art(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:
|
def compose(self) -> ComposeResult:
|
||||||
yield Header(icon="")
|
yield Header(icon="")
|
||||||
with Horizontal(id="headerbs"):
|
with Horizontal(id="headerbs"):
|
||||||
@@ -1121,9 +1135,11 @@ class Player(Screen):
|
|||||||
yield TotalDuration(classes='playerProgressBarTotalTime')
|
yield TotalDuration(classes='playerProgressBarTotalTime')
|
||||||
|
|
||||||
with Horizontal(id='playerButtons'):
|
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='prev_track', classes='playerButton')
|
||||||
yield Button('|>/||', id='play_pause_track', classes='playerButton')
|
yield Button('|>/||', id='play_pause_track', classes='playerButton')
|
||||||
yield Button('>|', id='next_track', classes='playerButton')
|
yield Button('>|', id='next_track', classes='playerButton')
|
||||||
|
yield Button(THREE_BARS_CHAR, id='loop_queue', classes='playerButton')
|
||||||
|
|
||||||
yield Footer()
|
yield Footer()
|
||||||
|
|
||||||
@@ -1132,6 +1148,9 @@ class Player(Screen):
|
|||||||
app.sub_title = "Player"
|
app.sub_title = "Player"
|
||||||
app.update_now_playing()
|
app.update_now_playing()
|
||||||
|
|
||||||
|
pause_play: Button = self.query_one("#play_pause_track", Button)
|
||||||
|
pause_play.focus()
|
||||||
|
|
||||||
@on(Button.Pressed, "#prev_track")
|
@on(Button.Pressed, "#prev_track")
|
||||||
def prev_track_pressed(self) -> None:
|
def prev_track_pressed(self) -> None:
|
||||||
app.action_rewind()
|
app.action_rewind()
|
||||||
@@ -1148,6 +1167,40 @@ class Player(Screen):
|
|||||||
# app.install_screen(QueueScreen, 'queue')
|
# app.install_screen(QueueScreen, 'queue')
|
||||||
app.push_screen(QueueScreen())
|
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
|
# Startup keyboard listener for media keys
|
||||||
def listen_media_keys(queue: multiprocessing.Queue):
|
def listen_media_keys(queue: multiprocessing.Queue):
|
||||||
|
|
||||||
@@ -1225,6 +1278,14 @@ class SubPy(App):
|
|||||||
check_playing_timer: Timer | None = None
|
check_playing_timer: Timer | None = None
|
||||||
check_music_commands: Timer
|
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:
|
def on_mount(self) -> None:
|
||||||
self.title = "SubPy"
|
self.title = "SubPy"
|
||||||
self.switch_mode('main')
|
self.switch_mode('main')
|
||||||
@@ -1298,9 +1359,16 @@ class SubPy(App):
|
|||||||
|
|
||||||
def check_done_playing(self) -> None:
|
def check_done_playing(self) -> None:
|
||||||
if not pygame.mixer.music.get_busy() and not self.is_paused and self.currently_playing:
|
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.play_history.append(self.currently_playing)
|
||||||
|
|
||||||
self.is_played = False
|
self.is_playing = False
|
||||||
|
|
||||||
next_song: SubSonic.Song | None = None
|
next_song: SubSonic.Song | None = None
|
||||||
if self.user_queue:
|
if self.user_queue:
|
||||||
@@ -1308,6 +1376,13 @@ class SubPy(App):
|
|||||||
elif self.song_queue:
|
elif self.song_queue:
|
||||||
next_song = self.song_queue.pop(0)
|
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:
|
if next_song:
|
||||||
self.play_song(next_song)
|
self.play_song(next_song)
|
||||||
|
|
||||||
@@ -1328,6 +1403,13 @@ class SubPy(App):
|
|||||||
elif self.song_queue:
|
elif self.song_queue:
|
||||||
next_song = self.song_queue.pop(0)
|
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:
|
if next_song:
|
||||||
self.play_song(next_song)
|
self.play_song(next_song)
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user