diff --git a/SubSonic.py b/SubSonic.py index e3abe80..1b63e17 100644 --- a/SubSonic.py +++ b/SubSonic.py @@ -386,6 +386,16 @@ class SubpyConn: with open(path if path else 'cover.png', 'wb') as file: file.write(r.content) + def getCoverArtRaw(self, cover_art_id: str) -> bytes: + url = self.base_url + '/rest/getCoverArt?' + + payload = self._getBasePayload() + payload['id'] = cover_art_id + + r = requests.get(url + self._encodePayload(payload)) + + return r.content + def getSong(self, song_id: str) -> Song: url = self.base_url + '/rest/getSong?' diff --git a/screens-test.py b/screens-test.py index ab5d4d2..a6e93a3 100644 --- a/screens-test.py +++ b/screens-test.py @@ -25,6 +25,7 @@ from textual.widgets.option_list import Option from textual.timer import Timer from rich_pixels import Pixels +from PIL import Image u = 'pseudo' p = 'telegram9dock-MICHEL' @@ -286,17 +287,18 @@ class Favourites(Screen): 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__() + cover_art_id: str | None = None + def set_cover_art(self, cover_art_id: str) -> None: self.cover_art_id = cover_art_id - conn.getCoverArt(cover_art_id, path=f'art/{cover_art_id}.png') def render(self) -> RenderResult: + if not self.cover_art_id: + return "" + # Make the size square width, height = self.size @@ -305,7 +307,8 @@ class CoverArtPixels(Container): elif height > width: height = width - return Pixels().from_image_path(f'art/{self.cover_art_id}.png', resize=(width, height)) + image = Image.open(io.BytesIO(conn.getCoverArtRaw(self.cover_art_id))) + return Pixels().from_image(image, resize=(width, height)) class MainView(Screen): CSS_PATH = 'main.tcss' @@ -317,6 +320,7 @@ class MainView(Screen): yield Label("Playing") yield DataTable(id="currently_playing") yield ProgressBar() + yield CoverArtPixels() with Vertical(classes="column"): yield Label("Queue") yield DataTable(id="queue") @@ -333,7 +337,7 @@ class MainView(Screen): currentlyPlaying.add_columns("", "") @on(events.ScreenResume) - async def screen_resumed(self, event: events.ScreenResume) -> None: + def screen_resumed(self, event: events.ScreenResume) -> None: app.sub_title = "Overview" # Update Queue Table @@ -359,7 +363,7 @@ class MainView(Screen): currentlyPlaying.add_row("Track Number", song.trackNumber) currentlyPlaying.add_row("Bit Rate", song.bitRate) - await self.query_one('#info').mount(CoverArtPixels(app.currently_playing.id)) + self.query_one(CoverArtPixels).set_cover_art(app.currently_playing.id) def _clear_table(self, table_id: str) -> None: table: DataTable = self.query_one(table_id) @@ -374,7 +378,9 @@ class MainView(Screen): class TextualSubpyPlayer(App): BINDINGS = [ + ("r", "rewind()", "⏪"), ("space", "play_pause_song()", "⏯️"), + ("s", "skip()", "⏭️"), ("f1", "switch_mode('main')", "Overview"), ("f2", "switch_mode('artists')", "Artists"), ("f3", "switch_mode('albums')", "Albums"), @@ -393,6 +399,7 @@ class TextualSubpyPlayer(App): currently_playing: SubSonic.Song | None = None song_queue: list[SubSonic.Song] = [] user_queue: list[SubSonic.Song] = [] + play_history: list[SubSonic.Song] = [] is_paused: bool = False place: int = 0 @@ -430,6 +437,7 @@ class TextualSubpyPlayer(App): def check_done_playing(self) -> None: if not pygame.mixer.music.get_busy() and not self.is_paused: + self.play_history.append(self.currently_playing) queue = self.user_queue queue.extend(self.song_queue) @@ -439,6 +447,25 @@ class TextualSubpyPlayer(App): self.check_playing_timer.stop() + def action_skip(self) -> None: + + if self.check_playing_timer: + self.check_playing_timer.stop() + + if self.currently_playing: + self.play_history.append(self.currently_playing) + + queue = self.user_queue + queue.extend(self.song_queue) + + self.play_song(queue.pop(0)) + + def action_rewind(self) -> None: + if self.check_playing_timer: + self.check_playing_timer.stop() + + self.play_song(self.play_history.pop()) + # def update_progress(self) -> None: # self.query_one(ProgressBar).advance(1) diff --git a/textual.log b/textual.log index c41e81a..9892a4c 100644 --- a/textual.log +++ b/textual.log @@ -1,11 +1,12 @@ -[2025-01-24 17:00:13,155] DEBUG in selector_events: Using selector: EpollSelector -[2025-01-24 17:00:15,224] 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 17:00:18,614] DEBUG in screens-test: Playing song rap music by Lil Darkie. -[2025-01-24 17:00:18,615] DEBUG in connectionpool: Starting new HTTPS connection (1): music.0x01fe.net:443 -[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 17:00:20,392] 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 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 +[2025-01-24 21:34:24,903] DEBUG in selector_events: Using selector: EpollSelector +[2025-01-24 21:34:26,609] DEBUG in connectionpool: Starting new HTTPS connection (1): music.0x01fe.net:443 +[2025-01-24 21:34:26,646] DEBUG in connectionpool: https://music.0x01fe.net:443 "GET /rest/getPlaylists?u=pseudo&t=117b12afec7228071d8e5423a81373a8&s=K6WPAIKV&v=1.16.1&c=testing&f=json HTTP/1.1" 200 705 +[2025-01-24 21:34:28,309] DEBUG in connectionpool: Starting new HTTPS connection (1): music.0x01fe.net:443 +[2025-01-24 21:34:28,345] DEBUG in connectionpool: https://music.0x01fe.net:443 "GET /rest/getPlaylist?u=pseudo&t=93ebc246f6838084d07ac50c4bf787be&s=Z3D6WNNK&v=1.16.1&c=testing&f=json&id=9d76a86b-901f-4727-a2ec-01210157ff01 HTTP/1.1" 200 None +[2025-01-24 21:34:30,632] DEBUG in screens-test: Playing song Midian by Yakui the Maid. +[2025-01-24 21:34:30,633] DEBUG in connectionpool: Starting new HTTPS connection (1): music.0x01fe.net:443 +[2025-01-24 21:34:30,662] DEBUG in connectionpool: https://music.0x01fe.net:443 "GET /rest/stream?u=pseudo&t=d515d35181a76782a65a285398a605ea&s=WT0U93U8&v=1.16.1&c=testing&id=a57e00930bb5a1b50dcaf7c573f4ba66 HTTP/1.1" 200 20522391 +[2025-01-24 21:34:31,867] DEBUG in connectionpool: Starting new HTTPS connection (1): music.0x01fe.net:443 +[2025-01-24 21:34:31,899] DEBUG in connectionpool: https://music.0x01fe.net:443 "GET /rest/getCoverArt?u=pseudo&t=74f61a986314858ae80b67755cbb79cf&s=28V8171Q&v=1.16.1&c=testing&id=a57e00930bb5a1b50dcaf7c573f4ba66 HTTP/1.1" 200 None +[2025-01-24 21:34:37,182] DEBUG in connectionpool: Starting new HTTPS connection (1): music.0x01fe.net:443 +[2025-01-24 21:34:37,216] DEBUG in connectionpool: https://music.0x01fe.net:443 "GET /rest/getCoverArt?u=pseudo&t=d113293159ba7ab8bddc4749fe099c7c&s=8EL8LGE3&v=1.16.1&c=testing&id=a57e00930bb5a1b50dcaf7c573f4ba66 HTTP/1.1" 200 None