130 lines
4.8 KiB
Python
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)
|