Streamlit two panel layout with embedded player
Streamlit is perfect for prototypes like this. We get session state, chat input, and reactive reruns almost for free. The layout we want is a two-column split: a YouTube iframe on the left, a chat thread on the right, and a small header for the video title and switcher.
The Streamlit layout
Two columns, session-scoped state for each video.
import streamlit as st
from dotenv import load_dotenv
import os
load_dotenv()
from transcript import extract_video_id, fetch_transcript, chunk_transcript
from metadata import fetch_metadata
from embedder import build_index
from keyword_index import build_keyword_index
from chat import chat_with_video
st.set_page_config(
page_title="YT Chat",
layout="wide",
initial_sidebar_state="collapsed",
)
def init_state():
defaults = {
"videos": {}, # video_id -> {chunks, index, keyword_index, meta}
"active_video_id": None,
"conversations": {}, # video_id -> list of chat messages
"client": None,
}
for k, v in defaults.items():
if k not in st.session_state:
st.session_state[k] = v
init_state()Every per-video artifact lives in st.session_state.videos keyed by video id. That keeps the chat state isolated per video and makes the switcher trivial to implement. chat_with_video is the single entry point in chat.py that routes the query, retrieves context, and calls the LLM; you will see it in full when we wire inline citations.
left, right = st.columns([5, 6], gap="large")
with left:
video_id = st.session_state.active_video_id
if video_id:
iframe_src = (
f"https://www.youtube.com/embed/{video_id}?enablejsapi=1"
)
st.components.v1.iframe(iframe_src, height=420)
else:
st.info("Paste a YouTube URL to get started.")
with right:
# chat messages and input go here
render_chat_panel()Two columns, 5 to 6 ratio. The iframe uses the YouTube embed URL with enablejsapi so we can later deep-link into specific timestamps. The chat panel lives on the right.
So the user can switch between multiple indexed videos in one session without re-indexing. Each video owns its own chunks, FAISS index, BM25 index, and conversation history. Swapping video_id flips the view and the chat picks up where it left off.
Quiz: Quiz
Loading practice…