Merge pull request 'feat: enhance TUI with home screen, download progress, queue auto-refresh' (#15) from fix/issue-2 into main

Reviewed-on: https://git.example.com/jarianc/youtube-cli/pulls/15
This commit is contained in:
Jarian Cottingham 2026-07-05 01:56:35 -05:00
commit 483611f357
4 changed files with 223 additions and 3 deletions

View File

@ -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

View File

@ -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
View 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()

View File

@ -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()