Files
upscale-ui/src/ui/components/history_tab.py

272 lines
8.9 KiB
Python
Raw Normal View History

"""History gallery tab component."""
import logging
from pathlib import Path
from typing import Optional
import gradio as gr
from src.storage.history import history_manager, HistoryItem
logger = logging.getLogger(__name__)
def get_history_gallery(type_filter: str = "All") -> list[tuple[str, str]]:
"""Get history items for gallery display."""
items = history_manager.get_recent(limit=100)
gallery_items = []
for item in items:
# Apply type filter
if type_filter == "Images" and item.type != "image":
continue
if type_filter == "Videos" and item.type != "video":
continue
output_path = Path(item.output_path)
if not output_path.exists():
continue
# For images, show the image directly
if item.type == "image":
caption = f"{item.output_filename}\n{item.output_width}x{item.output_height}"
gallery_items.append((str(output_path), caption))
# For videos, show output path (Gradio Gallery will handle video thumbnails)
elif item.type == "video":
caption = f"🎬 {item.output_filename}\n{item.output_width}x{item.output_height}"
gallery_items.append((str(output_path), caption))
return gallery_items
def get_history_stats() -> str:
"""Get history statistics."""
stats = history_manager.get_statistics()
return (
f"**History Statistics**\n\n"
f"- Total Items: {stats['total_items']}\n"
f"- Images: {stats['images']}\n"
f"- Videos: {stats['videos']}\n"
f"- Total Processing Time: {stats['total_processing_time']/60:.1f} min\n"
f"- Total Input Size: {stats['total_input_size_mb']:.1f} MB\n"
f"- Total Output Size: {stats['total_output_size_mb']:.1f} MB"
)
def search_history(query: str, type_filter: str = "All") -> list[tuple[str, str]]:
"""Search history by filename."""
if not query:
return get_history_gallery(type_filter)
items = history_manager.search(query)
gallery_items = []
for item in items:
# Apply type filter
if type_filter == "Images" and item.type != "image":
continue
if type_filter == "Videos" and item.type != "video":
continue
output_path = Path(item.output_path)
if not output_path.exists():
continue
if item.type == "image":
caption = f"{item.output_filename}\n{item.output_width}x{item.output_height}"
gallery_items.append((str(output_path), caption))
elif item.type == "video":
caption = f"🎬 {item.output_filename}\n{item.output_width}x{item.output_height}"
gallery_items.append((str(output_path), caption))
return gallery_items
def get_item_details(evt: gr.SelectData, type_filter: str = "All") -> tuple[Optional[tuple], str]:
"""Get details for selected history item."""
if evt.index is None:
return None, "Select an item to view details"
items = history_manager.get_recent(limit=100)
# Filter items same as gallery
filtered_items = []
for item in items:
if type_filter == "Images" and item.type != "image":
continue
if type_filter == "Videos" and item.type != "video":
continue
if Path(item.output_path).exists():
filtered_items.append(item)
if evt.index >= len(filtered_items):
return None, "Item not found"
item = filtered_items[evt.index]
# Load images for slider (only for images)
slider_images = None
if item.type == "image":
input_path = Path(item.input_path)
output_path = Path(item.output_path)
if input_path.exists() and output_path.exists():
slider_images = (str(input_path), str(output_path))
# Format details
details = f"**{item.output_filename}**\n\n"
details += f"- **Type:** {item.type.title()}\n"
details += f"- **Model:** {item.model}\n"
details += f"- **Scale:** {item.scale}x\n"
details += f"- **Face Enhancement:** {'Yes' if item.face_enhance else 'No'}\n"
if item.type == "video" and item.video_codec:
details += f"- **Codec:** {item.video_codec}\n"
if item.frames_count:
details += f"- **Frames:** {item.frames_count}\n"
details += f"- **Input:** {item.input_width}x{item.input_height}\n"
details += f"- **Output:** {item.output_width}x{item.output_height}\n"
details += f"- **Processing Time:** {item.processing_time_seconds:.1f}s\n"
details += f"- **Input Size:** {item.input_size_bytes/1024:.1f} KB\n"
details += f"- **Output Size:** {item.output_size_bytes/1024/1024:.1f} MB\n" if item.output_size_bytes > 1024*1024 else f"- **Output Size:** {item.output_size_bytes/1024:.1f} KB\n"
details += f"- **Created:** {item.created_at.strftime('%Y-%m-%d %H:%M')}"
return slider_images, details
def delete_selected(evt: gr.SelectData, type_filter: str = "All") -> tuple[list, str]:
"""Delete selected history item."""
if evt.index is None:
return get_history_gallery(type_filter), "No item selected"
items = history_manager.get_recent(limit=100)
# Filter items same as gallery
filtered_items = []
for item in items:
if type_filter == "Images" and item.type != "image":
continue
if type_filter == "Videos" and item.type != "video":
continue
if Path(item.output_path).exists():
filtered_items.append(item)
if evt.index >= len(filtered_items):
return get_history_gallery(type_filter), "Item not found"
item = filtered_items[evt.index]
history_manager.delete_item(item.id)
return get_history_gallery(type_filter), f"Deleted: {item.output_filename}"
def create_history_tab():
"""Create the history gallery tab component."""
with gr.Tab("History", id="history-tab"):
gr.Markdown("## Processing History")
with gr.Row():
# Search
search_box = gr.Textbox(
label="Search",
placeholder="Search by filename...",
scale=3,
)
filter_dropdown = gr.Dropdown(
choices=["All", "Images", "Videos"],
value="All",
label="Filter",
scale=1,
)
refresh_btn = gr.Button("Refresh", variant="secondary", scale=1)
with gr.Row():
# Gallery
with gr.Column(scale=2):
gallery = gr.Gallery(
value=get_history_gallery(),
label="History",
columns=4,
object_fit="cover",
height=400,
allow_preview=True,
)
# Details panel
with gr.Column(scale=1):
stats_display = gr.Markdown(
get_history_stats(),
elem_classes=["info-card"],
)
# Selected item details
with gr.Row():
with gr.Column(scale=2):
try:
from gradio_imageslider import ImageSlider
comparison_slider = ImageSlider(
label="Before / After",
type="filepath",
)
except ImportError:
comparison_slider = gr.Image(
label="Selected Image",
type="filepath",
)
with gr.Column(scale=1):
item_details = gr.Markdown(
"Select an item to view details",
elem_classes=["info-card"],
)
with gr.Row():
download_btn = gr.Button("Download", variant="secondary")
delete_btn = gr.Button("Delete", variant="stop")
status_text = gr.Markdown("")
# Event handlers
search_box.change(
fn=search_history,
inputs=[search_box, filter_dropdown],
outputs=[gallery],
)
filter_dropdown.change(
fn=get_history_gallery,
inputs=[filter_dropdown],
outputs=[gallery],
)
gallery.select(
fn=get_item_details,
inputs=[filter_dropdown],
outputs=[comparison_slider, item_details],
)
delete_btn.click(
fn=lambda f: (get_history_gallery(f), get_history_stats()),
inputs=[filter_dropdown],
outputs=[gallery, stats_display],
)
refresh_btn.click(
fn=lambda f: (get_history_gallery(f), get_history_stats()),
inputs=[filter_dropdown],
outputs=[gallery, stats_display],
)
return {
"gallery": gallery,
"comparison_slider": comparison_slider,
"item_details": item_details,
"stats_display": stats_display,
"filter_dropdown": filter_dropdown,
"refresh_btn": refresh_btn,
}