Files
manage/src/media_library_viewer/ui/preview.py
T

130 lines
4.8 KiB
Python

"""Selected-file preview and remote path tools UI."""
from __future__ import annotations
from typing import Any, Callable
import pandas as pd
import streamlit as st
from media_library_viewer.jobs import JOB_TEMPLATES, run_job
from media_library_viewer.utils import (
ffprobe_format_summary,
is_known_video_file,
summarize_audio_streams,
summarize_streams,
summarize_subtitle_streams,
summarize_video_streams,
)
def render_ffprobe_sections(ffprobe_data: dict[str, Any]) -> None:
"""Render ffprobe output in separate container/video/audio/subtitle sections."""
format_summary = ffprobe_format_summary(ffprobe_data)
video_rows = summarize_video_streams(ffprobe_data)
audio_rows = summarize_audio_streams(ffprobe_data)
subtitle_rows = summarize_subtitle_streams(ffprobe_data)
st.markdown("**Container**")
st.dataframe(pd.DataFrame([format_summary]), use_container_width=True, hide_index=True)
st.markdown("**Video**")
if video_rows:
st.dataframe(pd.DataFrame(video_rows), use_container_width=True, hide_index=True)
else:
st.caption("No video streams found.")
st.markdown("**Audio**")
if audio_rows:
st.dataframe(pd.DataFrame(audio_rows), use_container_width=True, hide_index=True)
else:
st.caption("No audio streams found.")
st.markdown("**Subtitles**")
if subtitle_rows:
st.dataframe(pd.DataFrame(subtitle_rows), use_container_width=True, hide_index=True)
else:
st.caption("No subtitle streams found.")
def render_selected_file_preview(
ssh_args: tuple,
selected_path: str | None,
cached_ffprobe_preview: Callable[..., dict[str, Any]],
) -> None:
"""Run and render a blocking ffprobe preview for selected known video files."""
with st.container(border=True):
st.markdown("**Selected file preview**")
if not selected_path:
st.caption("Select a file to preview media metadata.")
return
st.caption(f"Path: `{selected_path}`")
if not is_known_video_file(selected_path):
st.caption("Automatic ffprobe preview runs for known video file extensions only.")
return
refresh_col, status_col = st.columns([1.2, 5])
if refresh_col.button("Reload preview", key="preview_reload", use_container_width=True):
cached_ffprobe_preview.clear()
st.rerun()
host, username, port, key_filename, password = ssh_args
try:
with st.spinner("Running ffprobe preview..."):
ffprobe_data = cached_ffprobe_preview(host, username, port, key_filename, password, selected_path)
except Exception as exc:
status_col.error(f"ffprobe failed: {exc}")
return
status_col.success("ffprobe preview loaded.")
render_ffprobe_sections(ffprobe_data)
with st.expander("Raw ffprobe JSON"):
st.json(ffprobe_data)
def render_ssh_tools(
ssh,
ssh_args: tuple,
selected_path: str | None,
cached_ffprobe_preview: Callable[..., dict[str, Any]],
) -> None:
"""Render selected-path diagnostics and safe job templates."""
render_selected_file_preview(ssh_args, selected_path, cached_ffprobe_preview)
if not selected_path:
return
st.subheader("Disk metadata and jobs")
tabs = st.tabs(["ffprobe", "stat", "jobs"])
with tabs[0]:
if st.button("Run ffprobe on selected path", key="tools_run_ffprobe"):
try:
data = ssh.ffprobe_json(selected_path)
render_ffprobe_sections(data)
with st.expander("All streams table"):
st.dataframe(pd.DataFrame(summarize_streams(data)), use_container_width=True, hide_index=True)
with st.expander("Raw ffprobe JSON"):
st.json(data)
except Exception as exc:
st.error(str(exc))
with tabs[1]:
if st.button("Run stat", key="tools_run_stat"):
result = ssh.stat_path(selected_path)
st.code(result.stdout or result.stderr)
with tabs[2]:
st.warning("Jobs run commands on the remote server. Phase 1 includes safe/read-only templates only.")
job_key = st.selectbox("Job", list(JOB_TEMPLATES.keys()), format_func=lambda k: JOB_TEMPLATES[k].name)
st.caption(JOB_TEMPLATES[job_key].description)
command_preview = JOB_TEMPLATES[job_key].render({"path": selected_path})
st.code(command_preview, language="bash")
if st.button("Run selected job", key="tools_run_selected_job"):
result = run_job(ssh, job_key, selected_path)
st.write(f"Exit status: `{result.exit_status}`")
if result.stdout:
st.code(result.stdout)
if result.stderr:
st.error(result.stderr)