the queue now actually works

This commit is contained in:
JISAUAY committed 2025-01-24 17:04:06 -06:00
1 parent 2d97c921af
commit c1c640f837
5 files changed
+96 -27

No files matched your search

+2 -1
View File
@@ -1 +1,2 @@
__pycache__ __pycache__
art/*
+3 -1
View File
@@ -1,3 +1,5 @@
requests==2.31.0 requests==2.31.0
textual==1.0.0 textual==1.0.0
pygame==2.6.0 pygame==2.6.0
Pillow==10.0.0
rich-pixels==3.0.1
+3 -1
View File
@@ -4,7 +4,9 @@ from PIL import Image
console = Console() console = Console()
size = 70
with Image.open("cover.png") as image: with Image.open("cover.png") as image:
pixels = Pixels.from_image(image, resize=(60, 60)) pixels = Pixels.from_image(image, resize=(size, size))
console.print(pixels) console.print(pixels)
+77 -15
View File
@@ -16,12 +16,15 @@ import SubSonic
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide" os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
import pygame import pygame
from textual import on, events from textual import on, events, work
from textual.app import App, ComposeResult from textual.app import App, ComposeResult, RenderResult
from textual.widgets import Footer, Header, OptionList, DataTable, ProgressBar, Label from textual.widgets import Footer, Header, OptionList, DataTable, ProgressBar, Label
from textual.containers import Horizontal, Vertical from textual.containers import Horizontal, Vertical, Container
from textual.screen import Screen from textual.screen import Screen
from textual.widgets.option_list import Option from textual.widgets.option_list import Option
from textual.timer import Timer
from rich_pixels import Pixels
u = 'pseudo' u = 'pseudo'
p = 'telegram9dock-MICHEL' p = 'telegram9dock-MICHEL'
@@ -241,7 +244,13 @@ class PlaylistSongs(Screen):
return i return i
class Favourites(Screen): class Favourites(Screen):
BINDINGS = [
('p', 'play()', 'Play Song'),
('q', 'queue()', 'Queue Song')
]
songs: list[SubSonic.Song] | None = None songs: list[SubSonic.Song] | None = None
selected_song_id: str | None = None
def compose(self) -> ComposeResult: def compose(self) -> ComposeResult:
yield Header(icon="") yield Header(icon="")
@@ -259,13 +268,52 @@ class Favourites(Screen):
for song in self.songs: for song in self.songs:
songTable.add_row(song.title, song.album, song.artist, song.playCount, song.duration, song.bitRate, key=song.id) 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:
app.queue_song(self._find_selected_song())
# 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
# TODO : Maybe should just take bytes instead of saving it as a file
class CoverArtPixels(Container):
def __init__(self, cover_art_id: str):
super().__init__()
self.cover_art_id = cover_art_id
conn.getCoverArt(cover_art_id, path=f'art/{cover_art_id}.png')
def render(self) -> RenderResult:
# Make the size square
width, height = self.size
if width > height:
width = height
elif height > width:
height = width
return Pixels().from_image_path(f'art/{self.cover_art_id}.png', resize=(width, height))
class MainView(Screen): class MainView(Screen):
CSS_PATH = 'main.tcss' CSS_PATH = 'main.tcss'
def compose(self) -> ComposeResult: def compose(self) -> ComposeResult:
yield Header(icon="") yield Header(icon="")
with Horizontal(): with Horizontal():
with Vertical(classes="info"): with Vertical(classes="info", id='info'):
yield Label("Playing") yield Label("Playing")
yield DataTable(id="currently_playing") yield DataTable(id="currently_playing")
yield ProgressBar() yield ProgressBar()
@@ -284,12 +332,8 @@ class MainView(Screen):
currentlyPlaying.cursor_type = "row" currentlyPlaying.cursor_type = "row"
currentlyPlaying.add_columns("", "") currentlyPlaying.add_columns("", "")
@on(events.ScreenSuspend)
def sus(self, event: events.ScreenSuspend) -> None:
logging.debug('sus')
@on(events.ScreenResume) @on(events.ScreenResume)
def screen_resumed(self, event: events.ScreenResume) -> None: async def screen_resumed(self, event: events.ScreenResume) -> None:
app.sub_title = "Overview" app.sub_title = "Overview"
# Update Queue Table # Update Queue Table
@@ -315,6 +359,8 @@ class MainView(Screen):
currentlyPlaying.add_row("Track Number", song.trackNumber) currentlyPlaying.add_row("Track Number", song.trackNumber)
currentlyPlaying.add_row("Bit Rate", song.bitRate) currentlyPlaying.add_row("Bit Rate", song.bitRate)
await self.query_one('#info').mount(CoverArtPixels(app.currently_playing.id))
def _clear_table(self, table_id: str) -> None: def _clear_table(self, table_id: str) -> None:
table: DataTable = self.query_one(table_id) table: DataTable = self.query_one(table_id)
@@ -347,9 +393,10 @@ class TextualSubpyPlayer(App):
currently_playing: SubSonic.Song | None = None currently_playing: SubSonic.Song | None = None
song_queue: list[SubSonic.Song] = [] song_queue: list[SubSonic.Song] = []
user_queue: list[SubSonic.Song] = [] user_queue: list[SubSonic.Song] = []
isPlaying: bool = False is_paused: bool = False
place: int = 0 place: int = 0
check_playing_timer: Timer | None = None
def on_mount(self) -> None: def on_mount(self) -> None:
self.title = "SubPy" self.title = "SubPy"
@@ -359,9 +406,10 @@ class TextualSubpyPlayer(App):
# logging.debug(f'Key Pressed {event.name}') # logging.debug(f'Key Pressed {event.name}')
pass pass
def play_song(self, song: SubSonic.Song) -> None: @work(exclusive=True)
async def play_song(self, song: SubSonic.Song) -> None:
self.currently_playing = song self.currently_playing = song
self.isPlaying = True self.is_paused = False
logging.debug(f"Playing song {song.title} by {song.artist}.") logging.debug(f"Playing song {song.title} by {song.artist}.")
self.notify(f"Playing \"{song.title}\" by {song.artist}.") self.notify(f"Playing \"{song.title}\" by {song.artist}.")
@@ -372,17 +420,31 @@ class TextualSubpyPlayer(App):
pygame.mixer.music.load(audio_data) pygame.mixer.music.load(audio_data)
pygame.mixer.music.play() pygame.mixer.music.play()
self.check_playing_timer = self.set_interval(1 / 5, self.check_done_playing, pause=False)
# playingLabel: Label = self.query_one(Label) # playingLabel: Label = self.query_one(Label)
# playingLabel. # playingLabel.
# Set callback loop for progress bar # Set callback loop for progress bar
# self.progress_timer = self.set_interval(1 / 10, self.update_progress, pause=False) # self.progress_timer = self.set_interval(1 / 10, self.update_progress, pause=False)
def check_done_playing(self) -> None:
if not pygame.mixer.music.get_busy() and not self.is_paused:
queue = self.user_queue
queue.extend(self.song_queue)
next_song = queue.pop(0)
self.play_song(next_song)
self.check_playing_timer.stop()
# def update_progress(self) -> None: # def update_progress(self) -> None:
# self.query_one(ProgressBar).advance(1) # self.query_one(ProgressBar).advance(1)
def queue_song(self, song: SubSonic.Song) -> None: def queue_song(self, song: SubSonic.Song) -> None:
logging.debug(f'Queue\'d song "{song.title}" by {song.artist}.') logging.debug(f'Queue\'d song "{song.title}" by {song.artist}.')
self.notify(f'Queue\'d song "{song.title}" by {song.artist}.')
self.user_queue.append(song) self.user_queue.append(song)
def action_play_pause_song(self) -> None: def action_play_pause_song(self) -> None:
@@ -392,13 +454,13 @@ class TextualSubpyPlayer(App):
if not self.currently_playing: if not self.currently_playing:
return None return None
if self.isPlaying: if not self.is_paused:
self.place = pygame.mixer.music.get_pos() self.place = pygame.mixer.music.get_pos()
pygame.mixer.music.pause() pygame.mixer.music.pause()
self.isPlaying = False self.is_paused = True
else: else:
pygame.mixer.music.unpause() pygame.mixer.music.unpause()
self.isPlaying = True self.is_paused = False
if __name__ == '__main__': if __name__ == '__main__':
app = TextualSubpyPlayer() app = TextualSubpyPlayer()
+11 -9
View File
@@ -1,9 +1,11 @@
[2025-01-24 14:38:51,125] DEBUG in selector_events: Using selector: EpollSelector [2025-01-24 17:00:13,155] DEBUG in selector_events: Using selector: EpollSelector
[2025-01-24 14:38:51,902] DEBUG in screens-test: sus [2025-01-24 17:00:15,224] DEBUG in connectionpool: Starting new HTTPS connection (1): music.0x01fe.net:443
[2025-01-24 14:38:51,930] DEBUG in connectionpool: Starting new HTTPS connection (1): music.0x01fe.net:443 [2025-01-24 17:00:15,621] DEBUG in connectionpool: https://music.0x01fe.net:443 "GET /rest/getStarred?u=pseudo&t=df47a647004f9967d54a717354d758bd&s=OEYQY6IP&v=1.16.1&c=testing&f=json HTTP/1.1" 200 None
[2025-01-24 14:38:52,091] DEBUG in connectionpool: https://music.0x01fe.net:443 "GET /rest/getPlaylists?u=pseudo&t=3b3e07bd0a56711c9db81acfb74b5560&s=2Q2MV419&v=1.16.1&c=testing&f=json HTTP/1.1" 200 705 [2025-01-24 17:00:18,614] DEBUG in screens-test: Playing song rap music by Lil Darkie.
[2025-01-24 14:38:52,947] DEBUG in connectionpool: Starting new HTTPS connection (1): music.0x01fe.net:443 [2025-01-24 17:00:18,615] DEBUG in connectionpool: Starting new HTTPS connection (1): music.0x01fe.net:443
[2025-01-24 14:38:53,121] DEBUG in connectionpool: https://music.0x01fe.net:443 "GET /rest/getPlaylist?u=pseudo&t=174305d05353398cf608a65562db736f&s=GQLIDGEJ&v=1.16.1&c=testing&f=json&id=e6e8ff27-2a60-4e33-8aa1-09586191166a HTTP/1.1" 200 None [2025-01-24 17:00:18,843] DEBUG in connectionpool: https://music.0x01fe.net:443 "GET /rest/stream?u=pseudo&t=17ea6e0ce1111a102699f10662381805&s=UUJ177KV&v=1.16.1&c=testing&id=62196e9d0d531adff738bf47349cbfc6 HTTP/1.1" 200 8464191
[2025-01-24 14:38:54,045] DEBUG in screens-test: Playing song Theia by King Gizzard & The Lizard Wizard. [2025-01-24 17:00:20,392] DEBUG in connectionpool: Starting new HTTPS connection (1): music.0x01fe.net:443
[2025-01-24 14:38:54,046] DEBUG in connectionpool: Starting new HTTPS connection (1): music.0x01fe.net:443 [2025-01-24 17:00:20,628] DEBUG in connectionpool: https://music.0x01fe.net:443 "GET /rest/getCoverArt?u=pseudo&t=36a4b74c419b83821131781619811886&s=I4SC818D&v=1.16.1&c=testing&id=62196e9d0d531adff738bf47349cbfc6 HTTP/1.1" 200 None
[2025-01-24 14:38:54,249] DEBUG in connectionpool: https://music.0x01fe.net:443 "GET /rest/stream?u=pseudo&t=61c596042c3be872619f0f39ddcc645d&s=LAPQRCLD&v=1.16.1&c=testing&id=9e28af41e8bfcf423a5a958cd7aca5cc HTTP/1.1" 200 8314893 [2025-01-24 17:03:36,276] DEBUG in screens-test: Playing song Pegasus Pro Ultra Fusion by Dan Terminus.
[2025-01-24 17:03:36,278] DEBUG in connectionpool: Starting new HTTPS connection (1): music.0x01fe.net:443
[2025-01-24 17:03:36,763] DEBUG in connectionpool: https://music.0x01fe.net:443 "GET /rest/stream?u=pseudo&t=3b4bd01e32919d4f7868d0f89e702b93&s=94XX1BQN&v=1.16.1&c=testing&id=49f56133cf2f7bc6462be7620113ee79 HTTP/1.1" 200 None