From 7842cb1f7f1ffddae808ea909aa5161eab80cee9 Mon Sep 17 00:00:00 2001 From: Jarian Cottingham Date: Sun, 5 Jul 2026 06:54:42 +0000 Subject: [PATCH] feat: enhance TUI with home screen, download progress, queue auto-refresh - Add home screen as default landing page with quick actions - Wire download progress callback to TUI progress bar - Add queue auto-refresh every 2s during active downloads - Improve UX with keyboard shortcuts and button navigation --- youtube_tui/app.py | 8 +- youtube_tui/screens/download.py | 11 +- youtube_tui/screens/home.py | 192 ++++++++++++++++++++++++++++++++ youtube_tui/screens/queue.py | 15 +++ 4 files changed, 223 insertions(+), 3 deletions(-) create mode 100644 youtube_tui/screens/home.py diff --git a/youtube_tui/app.py b/youtube_tui/app.py index 18b9882..f520c29 100644 --- a/youtube_tui/app.py +++ b/youtube_tui/app.py @@ -131,7 +131,7 @@ class YouTubeTUI(App): ) self.download_manager.start_processing() - self.push_search_screen() + self.push_home_screen() self.current_screen = self.screen def on_screen_stack_changed(self) -> None: @@ -141,6 +141,12 @@ class YouTubeTUI(App): self.current_search_term = current_screen.search_term self.current_screen = current_screen + def push_home_screen(self) -> None: + """Push the home screen""" + from youtube_tui.screens.home import HomeScreen + + self.push_screen(HomeScreen()) + def push_search_screen(self) -> None: """Push the search screen""" from youtube_tui.screens.search import SearchScreen diff --git a/youtube_tui/screens/download.py b/youtube_tui/screens/download.py index 987527d..f84416f 100644 --- a/youtube_tui/screens/download.py +++ b/youtube_tui/screens/download.py @@ -121,9 +121,16 @@ class DownloadScreen(Screen): async def start_download(self) -> None: """Start the download process""" try: - # Perform the download (async method) + async def progress_callback(percentage: int) -> bool: + """Update progress bar during download""" + self.update_progress(percentage) + self.update_status( + f"[blue]Downloading... {percentage}%[/blue]" + ) + return True + success = await self.youtube_service.download_video( - self.video, self.category + self.video, self.category, progress_callback=progress_callback ) if success: diff --git a/youtube_tui/screens/home.py b/youtube_tui/screens/home.py new file mode 100644 index 0000000..01ca5a4 --- /dev/null +++ b/youtube_tui/screens/home.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python3 +""" +Home Screen for YouTube TUI +Main dashboard with quick actions +""" + +from textual.app import ComposeResult +from textual.containers import Container +from textual.screen import Screen +from textual.widgets import ( + Button, + Footer, + Header, + Static, +) + + +class HomeScreen(Screen): + """Main dashboard screen with quick actions""" + + CSS = """ + HomeScreen { + align: center middle; + } + + #welcome-container { + width: 60%; + height: auto; + border: double #555555; + padding: 2 3; + margin: 2 0; + } + + #welcome-title { + width: 100%; + height: 3; + content-align: center middle; + margin-bottom: 1; + } + + #welcome-info { + width: 100%; + height: auto; + margin-bottom: 2; + color: $text-muted; + } + + #quick-actions { + width: 100%; + layout: grid; + grid-gutter: 1; + grid-columns: 2; + margin: 1 0; + } + + Button { + width: 100%; + margin: 0 1; + } + + #status-info { + width: 100%; + height: auto; + margin-top: 2; + color: $text-muted; + } + + #status-bar { + dock: bottom; + height: 1; + background: $surface; + color: $text-muted; + padding: 0 1; + } + """ + + BINDINGS = [ + ("s", "open_search", "Search"), + ("q", "open_queue", "Queue"), + ("h", "open_history", "History"), + ("ctrl+h", "show_help", "Help"), + ("q", "quit", "Quit"), + ] + + def __init__(self): + super().__init__() + self.download_queue = None + self.download_manager = None + + def compose(self) -> ComposeResult: + """Compose the home screen""" + yield Header() + yield Container( + Static( + "[bold cyan]YouTube CLI[/bold cyan] - Browse and download videos", + id="welcome-title", + ), + Static( + "Use keyboard shortcuts or buttons to navigate", + id="welcome-info", + ), + Container( + Button("Search Videos", id="search-btn"), + Button("Download Queue", id="queue-btn"), + Button("Search History", id="history-btn"), + Button("Help", id="help-btn"), + id="quick-actions", + ), + Static(id="status-info"), + id="welcome-container", + ) + yield Static(id="status-bar") + yield Footer() + + def on_mount(self) -> None: + """Called when screen is mounted""" + if hasattr(self.app, "download_queue"): + self.download_queue = self.app.download_queue + if hasattr(self.app, "download_manager"): + self.download_manager = self.app.download_manager + + self.update_queue_status() + self.update_status("[green]Welcome to YouTube TUI - Press 's' to search[/green]") + + def update_queue_status(self) -> None: + """Update queue status info""" + if self.download_manager: + status = self.download_manager.get_queue_status() + status_text = ( + f"Queue: {status['pending_count']} pending, " + f"{status['downloading_count']} downloading, " + f"{status['total_count']} total" + ) + else: + status_text = "Queue: No active downloads" + + status_info = self.query_one("#status-info", Static) + status_info.update(f"[dim]{status_text}[/dim]") + + def update_status(self, message: str) -> None: + """Update the status bar message""" + status_bar = self.query_one("#status-bar", Static) + status_bar.update(f"[bold white]{message}[/bold white]") + + def action_open_search(self) -> None: + """Open search screen""" + if hasattr(self.app, "push_search_screen"): + self.app.push_search_screen() + else: + from youtube_tui.screens.search import SearchScreen + + self.app.push_screen(SearchScreen()) + + def action_open_queue(self) -> None: + """Open queue screen""" + if hasattr(self.app, "action_open_queue"): + self.app.action_open_queue() + else: + from youtube_tui.screens.queue import QueueScreen + + self.app.push_screen(QueueScreen()) + + def action_open_history(self) -> None: + """Open search history""" + from youtube_tui.screens.history import SearchHistoryScreen + + self.app.push_screen(SearchHistoryScreen()) + + def action_show_help(self) -> None: + """Show help screen""" + from youtube_tui.screens.help import HelpScreen + + self.app.push_screen(HelpScreen()) + + def action_quit(self) -> None: + """Quit the application""" + self.app.exit() + + def action_cancel(self) -> None: + """Handle escape key""" + pass + + def on_button_pressed(self, event: Button.Pressed) -> None: + """Handle button presses""" + if event.button.id == "search-btn": + self.action_open_search() + elif event.button.id == "queue-btn": + self.action_open_queue() + elif event.button.id == "history-btn": + self.action_open_history() + elif event.button.id == "help-btn": + self.action_show_help() diff --git a/youtube_tui/screens/queue.py b/youtube_tui/screens/queue.py index 64b534e..7b76fb4 100644 --- a/youtube_tui/screens/queue.py +++ b/youtube_tui/screens/queue.py @@ -130,6 +130,21 @@ class QueueScreen(Screen): self.update_stats() self.update_status("[green]Queue loaded[/green]") + # Start auto-refresh if downloads active + if self.download_manager and self.download_manager.is_processing(): + self.set_interval(2, self._auto_refresh) + + def _auto_refresh(self) -> None: + """Auto-refresh queue table every 2 seconds""" + self.update_table() + self.update_stats() + + # Stop auto-refresh when no downloads active + if self.download_manager: + status = self.download_manager.get_queue_status() + if not status.get("has_active_download") and not status.get("pending_count"): + self.clear_interval(self._auto_refresh) + def action_refresh_screen(self) -> None: """Refresh the screen""" self.update_table()