# app.py import streamlit as st import pandas as pd import plotly.express as px import subprocess import threading import re from io import StringIO # ----------------------------- # Data Preparation using Provided Data # ----------------------------- data_str = """ Original (Source) Audio Target Audio number of speakers whisper model Time required video length run comments french(fr) english(en) 2 large-v3 12:20 min 30s Subsequent Run Harshad french(fr) korean(ko) 2 large-v3 0:13 min 30s Subsequent Run Harshad french(fr) japanes(ja) 2 large-v3 3:05 min 30s Subsequent Run Harshad french(fr) chinese(zh) 2 large-v3 9:02 min 30s Subsequent Run Harshad french(fr) german(de) 2 large-v3 17:48 min 30s First Run Harshad french(fr) spanish(es) 2 large-v3 0:14 min 30s Subsequent Run Harshad french(fr) korean(ko) 2 large-v3 2:47 min 30s Subsequent Run Harshad french(fr) english(en) 2 medium 2:16 min 30s First Run om french(fr) korean(ko) 2 medium 20s 30s Subsequent Run om french(fr) japanes(ja) 2 medium 30s 30s Subsequent Run om french(fr) chinese(zh) 2 medium 17s 30s Subsequent Run om french(fr) german(de) 2 medium 19s 30s Subsequent Run om french(fr) spanish(es) 2 medium 12s 30s Subsequent Run om french(fr) spanish(es) 2 small 2:51 min 30s First Run Harshad french(fr) korean(ko) 2 small 0:09 min 30s Subsequent Run "Harshad,As per our observation for the 30 seconds video, the change in models is just affecting the processing time and the quality is not much affected" french(fr) english(en) 2 base 3min 28s 1 min First Run om french(fr) korean(ko) 2 base 3min 21s 1 min First Run om french(fr) japanes(ja) 2 base 18s 1 min Subsequent Run om french(fr) chinese(zh) 2 base 27s 1 min Subsequent Run om french(fr) german(de) 2 base 25s 1 min Subsequent Run om french(fr) spanish(es) 2 base 24s 1 min Subsequent Run om french(fr) korean(ko) 2 base 15s 1 min Subsequent Run om french(fr) english(en) 2 medium 24s 1 min Subsequent Run "om, It starts from translating step, skipping upto the diarization step in the subsequent run" french(fr) korean(ko) 2 medium 17s 1 min Subsequent Run om french(fr) japanes(ja) 2 medium 28s 1 min Subsequent Run om french(fr) chinese(zh) 2 medium 20s 1 min Subsequent Run om french(fr) german(de) 2 medium 30s 1 min Subsequent Run om french(fr) spanish(es) 2 medium 4min 57s 1 min First Run om french(fr) english(en) 2 medium 3min 31s 2 min First Run om french(fr) korean(ko) 2 medium 10min 41s 2 min Subsequent Run om french(fr) japanes(ja) 2 medium 1min 18s 2 min Subsequent Run om french(fr) chinese(zh) 2 medium 1min 23s 2 min Subsequent Run om french(fr) german(de) 2 medium 0:40 min 2 min Subsequent Run Harshad french(fr) spanish(es) 2 medium 10:16 min 2 min First Run Harshad french(fr) english(en) 2 small 8:53 min 2 min First Run Harshad french(fr) korean(ko) 2 small 1:09 min 2 min Subsequent Run Harshad french(fr) japanes(ja) 2 small 1:10 min 2 min Subsequent Run Harshad french(fr) german(de) 2 small 0:42 min 2 min Subsequent Run Harshad french(fr) spanish(es) 2 small 0:43 min 2 min Subsequent Run Harshad french(fr) chinese(zh) 2 small 0:22 min 2 min Subsequent Run Harshad french(fr) english(en) 2 small 18:42 min 5 min First Run Harshad french(fr) korean(ko) 2 small 1:33 min 5 min Subsequent Run "Harshad, Saw some error but processing continued" french(fr) japanes(ja) 2 small 1:50 min 5 min Subsequent Run Harshad french(fr) german(de) 2 small 2:11 min 5 min Subsequent Run Harshad french(fr) spanish(es) 2 small 2:06 min 5 min Subsequent Run Harshad french(fr) chinese(zh) 2 small 1:34 min 5 min Subsequent Run Harshad """ # Read the provided data from the string df = pd.read_csv(StringIO(data_str), delimiter='\t') # Rename "Original (Source) Audio" to "Original Audio" for consistency df = df.rename(columns={"Original (Source) Audio": "Original Audio"}) # ----------------------------- # Utility Function: Parse Time Strings # ----------------------------- def parse_time(time_str): """ Convert a time string (e.g., '17:48 min', '3min 28s', '30s', or '1 min') into total seconds. """ if pd.isna(time_str): return 0 time_str = str(time_str).strip() if not time_str: return 0 # If the string contains a colon, assume mm:ss or hh:mm:ss format. if ":" in time_str: clean_str = re.sub(r"[^\d:]", "", time_str) parts = clean_str.split(":") try: parts = [int(p) for p in parts] except ValueError: return 0 if len(parts) == 2: minutes, seconds = parts return minutes * 60 + seconds elif len(parts) == 3: hours, minutes, seconds = parts return hours * 3600 + minutes * 60 + seconds else: # Use regex to capture hours, minutes, seconds if available. pattern = r'(?:(?P\d+)\s*hr)?\s*(?:(?P\d+)\s*min)?\s*(?:(?P\d+)\s*s)?' match = re.search(pattern, time_str) if match: hours = int(match.group("hours")) if match.group("hours") else 0 minutes = int(match.group("minutes")) if match.group("minutes") else 0 seconds = int(match.group("seconds")) if match.group("seconds") else 0 return hours * 3600 + minutes * 60 + seconds return 0 # Create new numeric columns (processing times in seconds) df["Time_required_seconds"] = df["Time required"].apply(parse_time) df["Video_length_seconds"] = df["video length"].apply(parse_time) # ----------------------------- # Compute Aggregated Insights # ----------------------------- # Overall average processing time by whisper model avg_time_by_model = df.groupby("whisper model")["Time_required_seconds"].mean().reset_index() # Average processing time by target audio avg_time_by_target = df.groupby("Target Audio")["Time_required_seconds"].mean().reset_index() # Mark run type based on the "run" column (if "First" appears then it's First Run) df["Run_type"] = df["run"].apply(lambda x: "First Run" if "First" in str(x) else "Subsequent Run") # Run counts by whisper model and run type run_counts = df.groupby(["whisper model", "Run_type"]).size().reset_index(name="count") # ----------------------------- # Create Plotly Figures # ----------------------------- # 1. Bar chart: Average Processing Time by Whisper Model fig_model = px.bar( avg_time_by_model, x="whisper model", y="Time_required_seconds", title="Average Processing Time by Whisper Model", labels={"Time_required_seconds": "Avg Time (seconds)", "whisper model": "Whisper Model"} ) # 2. Bar chart: Average Processing Time by Target Audio fig_target = px.bar( avg_time_by_target, x="Target Audio", y="Time_required_seconds", title="Average Processing Time by Target Audio", labels={"Time_required_seconds": "Avg Time (seconds)", "Target Audio": "Target Audio"} ) # 3. Scatter Plot: Processing Time vs Video Length fig_scatter = px.scatter( df, x="Video_length_seconds", y="Time_required_seconds", color="whisper model", title="Processing Time vs Video Length", labels={"Video_length_seconds": "Video Length (seconds)", "Time_required_seconds": "Processing Time (seconds)"} ) # 4. Bar chart: Run Counts by Whisper Model and Run Type fig_run = px.bar( run_counts, x="whisper model", y="count", color="Run_type", barmode="group", title="Run Counts by Whisper Model", labels={"count": "Number of Runs", "whisper model": "Whisper Model", "Run_type": "Run Type"} ) # Additional Graphs # Histogram of Processing Times fig_hist_time = px.histogram( df, x="Time_required_seconds", nbins=10, title="Distribution of Processing Times", labels={"Time_required_seconds": "Processing Time (seconds)"} ) # Box Plot of Processing Times by Whisper Model fig_box_model = px.box( df, x="whisper model", y="Time_required_seconds", title="Processing Time Distribution by Whisper Model", labels={"Time_required_seconds": "Processing Time (seconds)"} ) # Pie Chart for Distribution of Whisper Models model_counts = df["whisper model"].value_counts().reset_index() model_counts.columns = ["whisper model", "count"] fig_pie_model = px.pie( model_counts, names="whisper model", values="count", title="Distribution of Whisper Models" ) # Histogram of Video Lengths fig_hist_video = px.histogram( df, x="Video_length_seconds", nbins=5, title="Distribution of Video Lengths", labels={"Video_length_seconds": "Video Length (seconds)"} ) # Scatter Plot: Processing Time vs Video Length by Run Type fig_scatter_run = px.scatter( df, x="Video_length_seconds", y="Time_required_seconds", color="Run_type", title="Processing Time vs Video Length by Run Type", labels={"Video_length_seconds": "Video Length (seconds)", "Time_required_seconds": "Processing Time (seconds)"} ) # Correlation Heatmap (using only numeric columns) corr = df[["Time_required_seconds", "Video_length_seconds"]].corr() fig_corr = px.imshow( corr, text_auto=True, title="Correlation Matrix: Processing Time & Video Length", labels=dict(color="Correlation") ) # 3D Scatter Plot: Whisper Model vs Processing Time vs Video Length fig_model_time_video = px.scatter_3d( df, x="whisper model", y="Time_required_seconds", z="Video_length_seconds", color="whisper model", title="Whisper Model vs Processing Time vs Video Length", labels={ "whisper model": "Whisper Model", "Time_required_seconds": "Processing Time (seconds)", "Video_length_seconds": "Video Length (seconds)" } ) # Enlarge the 3D graph layout fig_model_time_video.update_layout(height=1000, width=1200) # New Graph: # First Run Average Processing Time by Whisper Model Grouped by Video Duration first_run_df = df[df["Run_type"] == "First Run"] avg_time_first_run = first_run_df.groupby(["whisper model", "video length"])["Time_required_seconds"].mean().reset_index() fig_first_run = px.bar( avg_time_first_run, x="whisper model", y="Time_required_seconds", color="video length", barmode="group", title="First Run Average Processing Time by Whisper Model (Grouped by Video Duration)", labels={"Time_required_seconds": "Avg Time (seconds)", "whisper model": "Whisper Model"} ) # ----------------------------- # Build the Streamlit App Layout # ----------------------------- st.title("Video Processing Dashboard") st.markdown(""" This dashboard provides insights into audio processing tasks using different Whisper models. It displays processing times, target languages, video lengths, run types, and more. """) # Display the raw data st.subheader("Raw Data") st.dataframe(df) # Display the original Plotly figures st.subheader("Average Processing Time by Whisper Model") st.plotly_chart(fig_model, use_container_width=True) st.subheader("Average Processing Time by Target Audio") st.plotly_chart(fig_target, use_container_width=True) st.subheader("Processing Time vs Video Length") st.plotly_chart(fig_scatter, use_container_width=True) st.subheader("Run Counts by Whisper Model") st.plotly_chart(fig_run, use_container_width=True) # Display the additional graphs st.subheader("Distribution of Processing Times") st.plotly_chart(fig_hist_time, use_container_width=True) st.subheader("Processing Time Distribution by Whisper Model (Box Plot)") st.plotly_chart(fig_box_model, use_container_width=True) st.subheader("Distribution of Whisper Models") st.plotly_chart(fig_pie_model, use_container_width=True) st.subheader("Distribution of Video Lengths") st.plotly_chart(fig_hist_video, use_container_width=True) st.subheader("Processing Time vs Video Length by Run Type") st.plotly_chart(fig_scatter_run, use_container_width=True) st.subheader("Correlation Matrix: Processing Time & Video Length") st.plotly_chart(fig_corr, use_container_width=True) st.subheader("Whisper Model vs Processing Time vs Video Length (3D)") st.plotly_chart(fig_model_time_video, use_container_width=True) st.subheader("First Run Avg Processing Time by Whisper Model and Video Duration") st.plotly_chart(fig_first_run, use_container_width=True) # ----------------------------- # Optional: Start LocalTunnel for Public Access # ----------------------------- def start_localtunnel(port=8501): """ Launch LocalTunnel via the command line. (Make sure you have it installed: npm install -g localtunnel) """ try: proc = subprocess.Popen( ["lt", "--port", str(port)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, ) def read_tunnel_output(process): while True: line = process.stdout.readline() if not line: break st.write(line.strip()) if "your url is:" in line.lower(): public_url = line.split("your url is:")[-1].strip() st.success(f"LocalTunnel URL: {public_url}") thread = threading.Thread(target=read_tunnel_output, args=(proc,), daemon=True) thread.start() except Exception as e: st.error(f"Error starting LocalTunnel: {e}") # Uncomment the following line to start LocalTunnel when the app runs. # start_localtunnel(port=8501)