|
|
import streamlit as st |
|
|
from pathlib import Path |
|
|
import tempfile, subprocess, threading, queue |
|
|
import textwrap |
|
|
import streamlit.components.v1 as components |
|
|
|
|
|
st.set_page_config(page_title="Lec2Note2 β Lecture-to-Notes", layout="wide") |
|
|
|
|
|
st.title("π Lec2Note β Automatic Lecture Notes Generator") |
|
|
|
|
|
|
|
|
MATHJAX = "<script src='https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js'></script>" |
|
|
components.html(MATHJAX, height=0) |
|
|
|
|
|
st.markdown( |
|
|
textwrap.dedent( |
|
|
""" |
|
|
Upload a lecture **video** and receive a fully-formatted **Markdown** study note β complete with key images and structured sections. |
|
|
The processing pipeline performs ASR transcription, vision & semantic segmentation, then invokes an LLM to produce rich notes. |
|
|
""" |
|
|
) |
|
|
) |
|
|
|
|
|
video_file = st.file_uploader("π¬ Upload MP4/MKV/AVI", type=["mp4", "mkv", "avi"]) |
|
|
|
|
|
run_btn = st.button("π Generate Notes", disabled=video_file is None) |
|
|
|
|
|
if run_btn and video_file: |
|
|
|
|
|
tmp_dir = tempfile.TemporaryDirectory() |
|
|
vid_path = Path(tmp_dir.name) / video_file.name |
|
|
with vid_path.open("wb") as f: |
|
|
f.write(video_file.read()) |
|
|
|
|
|
output_md = vid_path.with_suffix(".md") |
|
|
|
|
|
st.info("Processing started. This may take several minutes depending on video length β¦") |
|
|
|
|
|
log_container = st.container() |
|
|
log_placeholder = log_container.code("", language="bash", height=300) |
|
|
|
|
|
|
|
|
with st.spinner("Running Lec2Note2 pipeline β¦"): |
|
|
|
|
|
cmd = [ |
|
|
"python", |
|
|
"-u", |
|
|
"-m", |
|
|
"lec2note.scripts.run_pipeline", |
|
|
"--video", |
|
|
str(vid_path), |
|
|
"--output", |
|
|
str(output_md), |
|
|
] |
|
|
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) |
|
|
|
|
|
log_queue: "queue.Queue[str]" = queue.Queue() |
|
|
|
|
|
def _enqueue_output(pipe, q): |
|
|
for line in iter(pipe.readline, ""): |
|
|
q.put(line) |
|
|
pipe.close() |
|
|
|
|
|
threading.Thread(target=_enqueue_output, args=(proc.stdout, log_queue), daemon=True).start() |
|
|
|
|
|
logs = "" |
|
|
line_count = 0 |
|
|
while True: |
|
|
try: |
|
|
line = log_queue.get(timeout=0.1) |
|
|
except queue.Empty: |
|
|
if proc.poll() is not None: |
|
|
|
|
|
break |
|
|
continue |
|
|
logs += line |
|
|
line_count += 1 |
|
|
if line_count % 5 == 0: |
|
|
log_placeholder.code(logs, language="bash", height=300) |
|
|
|
|
|
|
|
|
log_placeholder.code(logs, language="bash", height=300) |
|
|
|
|
|
result_code = proc.wait() |
|
|
if result_code != 0: |
|
|
st.error("β Pipeline failed. See logs below.") |
|
|
with st.expander("Show logs"): |
|
|
st.code(logs) |
|
|
else: |
|
|
st.success("β
Notes generated!") |
|
|
md_content = output_md.read_text() |
|
|
with st.container(border=True): |
|
|
st.markdown(md_content, unsafe_allow_html=True) |
|
|
st.download_button( |
|
|
label="πΎ Download notes.md", |
|
|
data=md_content, |
|
|
file_name="lecture_notes.md", |
|
|
mime="text/markdown", |
|
|
) |
|
|
|
|
|
tmp_dir.cleanup() |
|
|
|