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
This commit is contained in:
parent
6ae8f3befc
commit
7842cb1f7f
@ -131,7 +131,7 @@ class YouTubeTUI(App):
|
|||||||
)
|
)
|
||||||
self.download_manager.start_processing()
|
self.download_manager.start_processing()
|
||||||
|
|
||||||
self.push_search_screen()
|
self.push_home_screen()
|
||||||
self.current_screen = self.screen
|
self.current_screen = self.screen
|
||||||
|
|
||||||
def on_screen_stack_changed(self) -> None:
|
def on_screen_stack_changed(self) -> None:
|
||||||
@ -141,6 +141,12 @@ class YouTubeTUI(App):
|
|||||||
self.current_search_term = current_screen.search_term
|
self.current_search_term = current_screen.search_term
|
||||||
self.current_screen = current_screen
|
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:
|
def push_search_screen(self) -> None:
|
||||||
"""Push the search screen"""
|
"""Push the search screen"""
|
||||||
from youtube_tui.screens.search import SearchScreen
|
from youtube_tui.screens.search import SearchScreen
|
||||||
|
|||||||
@ -121,9 +121,16 @@ class DownloadScreen(Screen):
|
|||||||
async def start_download(self) -> None:
|
async def start_download(self) -> None:
|
||||||
"""Start the download process"""
|
"""Start the download process"""
|
||||||
try:
|
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(
|
success = await self.youtube_service.download_video(
|
||||||
self.video, self.category
|
self.video, self.category, progress_callback=progress_callback
|
||||||
)
|
)
|
||||||
|
|
||||||
if success:
|
if success:
|
||||||
|
|||||||
192
youtube_tui/screens/home.py
Normal file
192
youtube_tui/screens/home.py
Normal file
@ -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()
|
||||||
@ -130,6 +130,21 @@ class QueueScreen(Screen):
|
|||||||
self.update_stats()
|
self.update_stats()
|
||||||
self.update_status("[green]Queue loaded[/green]")
|
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:
|
def action_refresh_screen(self) -> None:
|
||||||
"""Refresh the screen"""
|
"""Refresh the screen"""
|
||||||
self.update_table()
|
self.update_table()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user