diff --git a/SeaPlayer.py b/SeaPlayer.py index ec43f1d..be0b3b7 100644 --- a/SeaPlayer.py +++ b/SeaPlayer.py @@ -1,7 +1,13 @@ from seaplayer.seaplayer import SeaPlayer +from rich.console import Console + +console = Console() # ! Start if __name__ == "__main__": - app = SeaPlayer() - app.run() - app.started = False \ No newline at end of file + try: + app = SeaPlayer() + app.run() + app.started = False + #console.print(app.styles.background) + except: console.print_exception(word_wrap=True, show_locals=True) \ No newline at end of file diff --git a/seaplayer/css/objects.css b/seaplayer/css/objects.css new file mode 100644 index 0000000..175f958 --- /dev/null +++ b/seaplayer/css/objects.css @@ -0,0 +1,60 @@ +/* ! Music List */ +.music-list-view { + height: 1fr; +} + +.music-list-view-item { + height: 4; +} + +.music-list-view-item-title-label { + height: 1; + color: #cacaca; +} + +.music-list-view-item-subtitle-label { + height: 1; + color: #a9a9a9; +} + +.music-list-screen-add-box { + height: 3; +} + +.music-list-screen-add-input { + width: 3fr; +} + +.music-list-screen-add-button { + width: 1fr; +} + +/* ! IndeterminateProgress */ +.indeterminate-progress-bar { height: 1; } + +/* ! Image Label */ +.image-label { + height: 1fr; + width: 1fr; + align: center middle; + text-align: center; +} + +/* ! Configurate List */ +.configurate-list-view { + border: solid cadetblue; + /*border: solid darkcyan;*/ + height: 1fr; + width: 1fr; +} + +.configurate-list-view-item { + border: solid dodgerblue; + background: #0000; + border-title-color: #aaaaaa; + border-subtitle-color: #6b6b6b; +} + +/* ! Any Elements CSS */ +.pass-one-width { width: 1; } + diff --git a/seaplayer/css/seaplayer.css b/seaplayer/css/seaplayer.css index 07dbd6c..ad78b09 100644 --- a/seaplayer/css/seaplayer.css +++ b/seaplayer/css/seaplayer.css @@ -9,66 +9,10 @@ height: 100%; } -/* ! TEST CSS */ -.test-box-1 { - height: 4; -} - -.test-box-2 { - height: 4; -} - .test-box-border { border: solid cadetblue; } -/* ! Elements CSS */ -.indeterminate-progress-bar { - height: 1; -} - -.pass-one-width { - width: 1; -} - -.image-label { - height: 1fr; - width: 1fr; - align: center middle; - text-align: center; -} - -/* ! MUSIC LIST */ -.music-list-view { - height: 1fr; -} - -.music-list-view-item { - height: 4; -} - -.music-list-view-item-title-label { - height: 1; - color: #cacaca; -} - -.music-list-view-item-subtitle-label { - height: 1; - color: #a9a9a9; -} - -.music-list-screen-add-box { - height: 3; -} - -.music-list-screen-add-input { - width: 3fr; -} - -.music-list-screen-add-button { - width: 1fr; -} - /* ! Play Screen */ .player-visual-panel { border: solid cadetblue; diff --git a/seaplayer/objects.py b/seaplayer/objects.py index 9b93732..58b0f27 100644 --- a/seaplayer/objects.py +++ b/seaplayer/objects.py @@ -10,6 +10,8 @@ from .modules.asynctpng import AsyncTPNG # ! Objects + +# ! Music List class MusicListViewItem(ListItem): def __init__( self, @@ -38,11 +40,11 @@ async def update_labels( if first_subtitle is not None: self.first_subtitle_label.update(title) if second_subtitle is not None: self.second_subtitle_label.update(title) - class MusicListView(ListView): def __init__(self, **kwargs) -> None: + kwargs["classes"] = "music-list-view" super().__init__(**kwargs) - self.music_list: MusicList = MusicList(classes="music-list-view") + self.music_list: MusicList = MusicList() def add_sound(self, sound: Sound) -> str: sound_uuid = self.music_list.add(sound) @@ -138,7 +140,7 @@ async def aio_select_list_item_from_sound_uuid(self, sound_uuid: str) -> None: ) except: pass - +# ! ProgressBar class IndeterminateProgress(Static): def __init__(self, getfunc=get_bar_status, fps: int=15): super().__init__("", classes="indeterminate-progress-bar") @@ -161,6 +163,7 @@ async def update_progress_bar(self) -> None: await self.upgrade_task(completed=c, total=t, description=d) self.update(self._bar) +# ! Image Label class ImageLabel(Label): def __init__(self, image: Optional[Image.Image]=None, fps: int=2): super().__init__("", classes="image-label") @@ -193,4 +196,27 @@ async def update_image(self, image: Optional[Image.Image]=None) -> None: if self.tpng_image is not None: await self.tpng_image.reset() await self.tpng_image.resize((self.size[0]-4, self.size[1])) - self.image_text = await self.tpng_image.to_rich_image() \ No newline at end of file + self.image_text = await self.tpng_image.to_rich_image() + +# ! Configurate List +class ConfigurateListItem(ListItem): + def __init__( + self, + title: str="", + desc: str="", + *children, **kwargs + ): + kwargs["classes"] = "configurate-list-view-item" + super(ConfigurateListItem, self).__init__(*children, **kwargs) + self.border_title = title + self.border_subtitle = desc + + async def updating(self, title: Optional[str]="", desc: Optional[str]="") -> None: + if title is not None: self.border_title = title + if desc is not None: self.border_subtitle = desc + +class ConfigurateListView(ListView): + def __init__(self, *children, **kwargs): + kwargs["classes"] = "configurate-list-view" + super().__init__(*children, **kwargs) + self.border_title = "Configurate" diff --git a/seaplayer/screens.py b/seaplayer/screens.py index 2f40239..e174a04 100644 --- a/seaplayer/screens.py +++ b/seaplayer/screens.py @@ -4,7 +4,10 @@ # > Graphics from textual.app import ComposeResult from textual.screen import Screen -from textual.widgets import Static +from textual.widgets import Static, ListView, ListItem, Header, Footer, Input +# > Local Imports +from .config import SeaPlayerConfig +from .objects import ConfigurateListView, ConfigurateListItem # ! Constants TEXT = b'CkFuIGVycm9yIGhhcyBvY2N1cnJlZC4gVG8gY29udGludWU6CgpQcmVzcyBFbnRlciB0byByZXR1cm4gdG8ge3N5c3RlbX0sIG9yCgpQcmVzcyBDVFJMK0FMVCtERUwgdG8gcmVzdGFydCB5b3VyIGNvbXB1dGVyLiBJZiB5b3UgZG8gdGhpcywKeW91IHdpbGwgbG9zZSBhbnkgdW5zYXZlZCBpbmZvcm1hdGlvbiBpbiBhbGwgb3BlbiBhcHBsaWNhdGlvbnMuCgpFcnJvcjogMEUgOiAwMTZGIDogQkZGOUIzRDQK' @@ -14,9 +17,7 @@ # ! Screens class Unknown(Screen): """This Unknown screen.""" - CSS_PATH = os.path.join(os.path.dirname(__file__), "css", "unknown.css") - - BINDINGS = [("escape", "app.pop_screen", "Exit")] + BINDINGS = [("escape", "app.pop_screen", "Back")] def compose(self) -> ComposeResult: yield Static(f" {platform.system()} ", id="unknown-title") @@ -25,11 +26,17 @@ def compose(self) -> ComposeResult: class Configurate(Screen): """This Configurate Menu screen.""" - CSS_PATH = os.path.join(os.path.dirname(__file__), "css", "configurate.css") - - BINDINGS = [("escape", "app.pop_screen", "Exit")] + BINDINGS = [("escape", "app.pop_screen", "Back")] def compose(self) -> ComposeResult: - yield Static(f" {platform.system()} ", id="unknown-title") - yield Static(TEXT) - yield Static("Press any key to continue [blink]_[/]", id="unknown-any-key") \ No newline at end of file + self.app_config: SeaPlayerConfig = self.app.config + + yield Header() + yield ConfigurateListView( + ConfigurateListItem("(Key): QUIT", "Сlose the app."), + ConfigurateListItem("(Key): Rewind Forward", "Forwards rewinding."), + ConfigurateListItem("(Key): Rewind Back", "Backwards rewinding."), + ConfigurateListItem("(Key): Volume +", "Turn up the volume."), + ConfigurateListItem("(Key): Volume -", "Turn down the volume.") + ) + yield Footer() \ No newline at end of file diff --git a/seaplayer/seaplayer.py b/seaplayer/seaplayer.py index ad50471..31e759f 100644 --- a/seaplayer/seaplayer.py +++ b/seaplayer/seaplayer.py @@ -15,11 +15,11 @@ # > Local Imports from .objects import * from .config import * -from .screens import Unknown, UNKNOWN_OPEN_KEY +from .screens import Unknown, UNKNOWN_OPEN_KEY, Configurate # ! Metadata __title__ = "SeaPlayer" -__version__ = "v0.3.0-unrelease.1" +__version__ = "0.3.0-unrelease.2" __author__ = "Romanin" __email__ = "semina054@gmail.com" __url__ = "https://github.com/romanin-rf/SeaPlayer" @@ -37,9 +37,10 @@ class SeaPlayer(App): CSS_PATH = [ os.path.join(CSS_LOCALDIR, "seaplayer.css"), os.path.join(CSS_LOCALDIR, "configurate.css"), - os.path.join(CSS_LOCALDIR, "unknown.css") + os.path.join(CSS_LOCALDIR, "unknown.css"), + os.path.join(CSS_LOCALDIR, "objects.css") ] - SCREENS = {"unknown": Unknown()} + SCREENS = {"unknown": Unknown(), "configurate": Configurate()} # ! SeaPlayer Configuration config = SeaPlayerConfig(CONFIG_PATH) @@ -48,8 +49,9 @@ class SeaPlayer(App): # ! Textual Keys Configuration BINDINGS = [ - Binding(key=UNKNOWN_OPEN_KEY, action="push_screen('unknown')", description="None", show=False), Binding(key=config.key_quit, action="quit", description="Quit"), + Binding(key=UNKNOWN_OPEN_KEY, action="push_screen('unknown')", description="None", show=False), + Binding(key="c,с", action="push_screen('configurate')", description="Configurate"), Binding(key=config.key_rewind_back, action="minus_rewind", description=f"Rewind -{config.rewind_count_seconds} sec"), Binding(key=config.key_rewind_forward, action="plus_rewind", description=f"Rewind +{config.rewind_count_seconds} sec"), Binding(key=config.key_volume_down, action="minus_volume", description=f"Volume -{round(config.volume_change_percent*100)}%"), @@ -99,7 +101,7 @@ def switch_playback_mode(self) -> None: if self.playback_mode == 2: self.playback_mode = 0 else: self.playback_mode += 1 - async def update_selected_label_text(self) -> None: + async def update_loop_playback(self) -> None: while self.started: if (sound:=await self.aio_gcs()) is not None: status = check_status(sound) @@ -115,7 +117,7 @@ async def update_selected_label_text(self) -> None: sound.play() self.last_playback_status = status - await asyncio.sleep(0.5) + await asyncio.sleep(0.33) def compose(self) -> ComposeResult: # * Play Screen @@ -129,7 +131,7 @@ def compose(self) -> ComposeResult: self.music_list_screen = Static(classes="screen-box") self.music_list_screen.border_title = "Playlist" - self.music_list_view = MusicListView(classes="music-list-view") + self.music_list_view = MusicListView() self.music_list_add_input = Input(placeholder="FilePath", classes="music-list-screen-add-input") # * Adding @@ -154,7 +156,12 @@ def compose(self) -> ComposeResult: yield Button("+", id="plus-sound", variant="error", classes="music-list-screen-add-button") yield Footer() - self.run_worker(self.update_selected_label_text, name="UPDATE_SELECTED_LABEL", group="UPDATE") + self.run_worker( + self.update_loop_playback, + name="PLAYBACK_CONTROLLER", + group="CONTROL_UPDATER-LOOP", + description="Control of playback modes and status updates." + ) async def add_sounds_to_list(self) -> None: async for path in aiter(self.last_paths_globalized): @@ -203,7 +210,12 @@ async def on_button_pressed(self, event: Button.Pressed) -> None: try: self.last_paths_globalized = glob.glob(path, recursive=self.config.recursive_search) except: self.last_paths_globalized = [] if len(self.last_paths_globalized) > 0: - self.run_worker(self.add_sounds_to_list, name="ADD_MUSIC", group="ADD") + self.run_worker( + self.add_sounds_to_list, + name="ADD_SOUND", + group="PLAYLIST_UPDATE", + description="The process of adding sounds to a playlist." + ) elif (event.button.id == "button-play-stop") or (event.button.id == "button-pause-unpause"): if (sound:=await self.aio_gcs()) is not None: @@ -241,7 +253,8 @@ async def set_sound_for_playback( return sound async def on_list_view_selected(self, selected: MusicListView.Selected): - await self.set_sound_for_playback(getattr(selected.item, "sound_uuid", None)) + if selected.list_view.has_class("music-list-view"): + await self.set_sound_for_playback(getattr(selected.item, "sound_uuid", None)) async def action_plus_rewind(self): if (sound:=await self.aio_gcs()) is not None: