"""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)