Spaces:
Running
Running
Add provenance section below video player in theater mode
Browse files
app.py
CHANGED
|
@@ -63,30 +63,94 @@ def get_demo_4_html() -> str:
|
|
| 63 |
# Global store for custom simulations
|
| 64 |
custom_simulation_store = {
|
| 65 |
"video_id": "",
|
| 66 |
-
"chat_data": None
|
|
|
|
|
|
|
| 67 |
}
|
| 68 |
|
| 69 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
if selection == "Steve Jobs 1983 Speech (Demo)":
|
| 71 |
-
return get_demo_html()
|
| 72 |
elif selection == "Carl Sagan Demon-Haunted World (Demo)":
|
| 73 |
-
return get_demo_2_html()
|
| 74 |
elif selection == "Edward Teller - Schrödinger's Cat (Demo)":
|
| 75 |
-
return get_demo_3_html()
|
| 76 |
elif selection == "Sal Khan - Khanmigo AI Tutor (Demo)":
|
| 77 |
-
return get_demo_4_html()
|
| 78 |
elif selection == "My Custom Simulation":
|
| 79 |
if custom_simulation_store["video_id"] and custom_simulation_store["chat_data"]:
|
| 80 |
-
|
| 81 |
else:
|
| 82 |
-
|
| 83 |
-
|
|
|
|
|
|
|
| 84 |
|
| 85 |
def handle_generation(yt_url: str, pdf_file, doc_text: str, srt_text: str, hf_token: str, use_ocr: bool = False):
|
| 86 |
# 1. Validate YouTube Link
|
| 87 |
video_id = extract_youtube_video_id(yt_url)
|
| 88 |
if not video_id:
|
| 89 |
return (
|
|
|
|
| 90 |
gr.update(),
|
| 91 |
"### ❌ Error\nInvalid YouTube URL. Please provide a valid YouTube link or 11-character Video ID.",
|
| 92 |
gr.update()
|
|
@@ -95,12 +159,17 @@ def handle_generation(yt_url: str, pdf_file, doc_text: str, srt_text: str, hf_to
|
|
| 95 |
# 2. Identify Document Source
|
| 96 |
doc_path = None
|
| 97 |
document_content = None
|
|
|
|
|
|
|
| 98 |
if pdf_file is not None:
|
| 99 |
doc_path = pdf_file.name
|
|
|
|
| 100 |
elif doc_text.strip():
|
| 101 |
document_content = doc_text.strip()
|
|
|
|
| 102 |
else:
|
| 103 |
return (
|
|
|
|
| 104 |
gr.update(),
|
| 105 |
"### ❌ Error\nPlease upload a PDF/text file or paste some reference document text.",
|
| 106 |
gr.update()
|
|
@@ -110,6 +179,7 @@ def handle_generation(yt_url: str, pdf_file, doc_text: str, srt_text: str, hf_to
|
|
| 110 |
manual_transcript = srt_text.strip() if srt_text.strip() else None
|
| 111 |
if not manual_transcript:
|
| 112 |
return (
|
|
|
|
| 113 |
gr.update(),
|
| 114 |
"### ❌ Error\nPlease paste the timestamped video transcript. (Auto-fetching is disabled due to server IP blocks).",
|
| 115 |
gr.update()
|
|
@@ -139,6 +209,8 @@ def handle_generation(yt_url: str, pdf_file, doc_text: str, srt_text: str, hf_to
|
|
| 139 |
# Save to global store
|
| 140 |
custom_simulation_store["video_id"] = video_id
|
| 141 |
custom_simulation_store["chat_data"] = chat_data
|
|
|
|
|
|
|
| 142 |
|
| 143 |
success_msg = (
|
| 144 |
f"### 🎉 Success!\n"
|
|
@@ -148,9 +220,11 @@ def handle_generation(yt_url: str, pdf_file, doc_text: str, srt_text: str, hf_to
|
|
| 148 |
|
| 149 |
# Create updated HTML player
|
| 150 |
new_html = load_template_with_data(video_id, chat_data)
|
|
|
|
| 151 |
|
| 152 |
return (
|
| 153 |
new_html,
|
|
|
|
| 154 |
success_msg,
|
| 155 |
gr.update(choices=["Steve Jobs 1983 Speech (Demo)", "Carl Sagan Demon-Haunted World (Demo)", "Edward Teller - Schrödinger's Cat (Demo)", "Sal Khan - Khanmigo AI Tutor (Demo)", "My Custom Simulation"], value="My Custom Simulation")
|
| 156 |
)
|
|
@@ -163,11 +237,13 @@ def handle_generation(yt_url: str, pdf_file, doc_text: str, srt_text: str, hf_to
|
|
| 163 |
"document or a different video."
|
| 164 |
)
|
| 165 |
return (
|
|
|
|
| 166 |
gr.update(),
|
| 167 |
error_msg,
|
| 168 |
gr.update()
|
| 169 |
)
|
| 170 |
|
|
|
|
| 171 |
# Gradio Theme
|
| 172 |
custom_theme = gr.themes.Default(
|
| 173 |
primary_hue="purple",
|
|
@@ -265,7 +341,7 @@ button[role="tab"]:hover,
|
|
| 265 |
}
|
| 266 |
"""
|
| 267 |
|
| 268 |
-
with gr.Blocks(title="ReadStream"
|
| 269 |
gr.HTML(
|
| 270 |
"""
|
| 271 |
<div style="text-align: center; margin-bottom: 20px; padding-top: 10px;">
|
|
@@ -289,11 +365,14 @@ with gr.Blocks(title="ReadStream", theme=custom_theme, css=custom_css) as demo:
|
|
| 289 |
# The player frame
|
| 290 |
player_frame = gr.HTML(value=get_demo_html())
|
| 291 |
|
|
|
|
|
|
|
|
|
|
| 292 |
# Trigger updates when selection changes
|
| 293 |
sim_selector.change(
|
| 294 |
fn=play_simulation_selection,
|
| 295 |
inputs=[sim_selector],
|
| 296 |
-
outputs=[player_frame]
|
| 297 |
)
|
| 298 |
|
| 299 |
# Tab 2: Generator Config
|
|
@@ -358,6 +437,7 @@ with gr.Blocks(title="ReadStream", theme=custom_theme, css=custom_css) as demo:
|
|
| 358 |
],
|
| 359 |
outputs=[
|
| 360 |
player_frame,
|
|
|
|
| 361 |
status_output,
|
| 362 |
sim_selector
|
| 363 |
]
|
|
|
|
| 63 |
# Global store for custom simulations
|
| 64 |
custom_simulation_store = {
|
| 65 |
"video_id": "",
|
| 66 |
+
"chat_data": None,
|
| 67 |
+
"pdf_name": None,
|
| 68 |
+
"has_pasted_text": False
|
| 69 |
}
|
| 70 |
|
| 71 |
+
def get_provenance_html(selection: str) -> str:
|
| 72 |
+
apa_citations = {
|
| 73 |
+
"Steve Jobs 1983 Speech (Demo)": (
|
| 74 |
+
"Zohar, E., Bloom, P., & Inzlicht, M. (2026). Against frictionless AI. "
|
| 75 |
+
"<i>Communications Psychology</i>, 4(1), Article 402. "
|
| 76 |
+
"<a href='https://doi.org/10.1038/s44271-026-00402-1' target='_blank' style='color:#7f5af0; text-decoration:none;'>https://doi.org/10.1038/s44271-026-00402-1</a>"
|
| 77 |
+
),
|
| 78 |
+
"Carl Sagan Demon-Haunted World (Demo)": (
|
| 79 |
+
"Giroux, H. A. (2013). Beyond dystopian education in a neoliberal society. "
|
| 80 |
+
"<i>Fast Capitalism</i>, 10(1). "
|
| 81 |
+
"<a href='https://doi.org/10.32855/fcapital.201301.010' target='_blank' style='color:#7f5af0; text-decoration:none;'>https://doi.org/10.32855/fcapital.201301.010</a>"
|
| 82 |
+
),
|
| 83 |
+
"Edward Teller - Schrödinger's Cat (Demo)": (
|
| 84 |
+
"Claeys, G. (2010). The origins of dystopia: Wells, Huxley and Orwell. "
|
| 85 |
+
"In G. Claeys (Ed.), <i>The Cambridge Companion to Utopian Literature</i> (pp. 107–131). "
|
| 86 |
+
"Cambridge University Press. "
|
| 87 |
+
"<a href='https://doi.org/10.1017/CCOL9780521886659.005' target='_blank' style='color:#7f5af0; text-decoration:none;'>https://doi.org/10.1017/CCOL9780521886659.005</a>"
|
| 88 |
+
),
|
| 89 |
+
"Sal Khan - Khanmigo AI Tutor (Demo)": (
|
| 90 |
+
"Pepple, D., & Muthuthantrige, N. (2026). Artificial intelligence, innovation and the new "
|
| 91 |
+
"architecture of exploitation: Towards reconfiguring humanness in the age of algorithmic labour. "
|
| 92 |
+
"<i>Journal of Innovation & Knowledge</i>, 11(1), 100878. "
|
| 93 |
+
"<a href='https://doi.org/10.1016/j.jik.2025.100878' target='_blank' style='color:#7f5af0; text-decoration:none;'>https://doi.org/10.1016/j.jik.2025.100878</a>"
|
| 94 |
+
)
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
if selection in apa_citations:
|
| 98 |
+
citation = apa_citations[selection]
|
| 99 |
+
return (
|
| 100 |
+
f"<div style='margin-top: 16px; padding: 14px 18px; background-color: #161a23; "
|
| 101 |
+
f"border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 8px; font-family: sans-serif; "
|
| 102 |
+
f"color: #94a1b2; font-size: 0.9rem; line-height: 1.5;'>"
|
| 103 |
+
f"<span style='color: #fffffe; font-weight: 600; display: block; margin-bottom: 4px;'>📄 Provenance</span>"
|
| 104 |
+
f"Comments generated using AI from published works: {citation}"
|
| 105 |
+
f"</div>"
|
| 106 |
+
)
|
| 107 |
+
elif selection == "My Custom Simulation":
|
| 108 |
+
pdf_name = custom_simulation_store.get("pdf_name")
|
| 109 |
+
if pdf_name:
|
| 110 |
+
source_info = f"uploaded file <i>{html.escape(pdf_name)}</i>"
|
| 111 |
+
elif custom_simulation_store.get("has_pasted_text"):
|
| 112 |
+
source_info = "pasted custom reference text"
|
| 113 |
+
else:
|
| 114 |
+
source_info = None
|
| 115 |
+
|
| 116 |
+
if source_info:
|
| 117 |
+
return (
|
| 118 |
+
f"<div style='margin-top: 16px; padding: 14px 18px; background-color: #161a23; "
|
| 119 |
+
f"border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 8px; font-family: sans-serif; "
|
| 120 |
+
f"color: #94a1b2; font-size: 0.9rem; line-height: 1.5;'>"
|
| 121 |
+
f"<span style='color: #fffffe; font-weight: 600; display: block; margin-bottom: 4px;'>📄 Provenance</span>"
|
| 122 |
+
f"Comments generated using AI from {source_info}."
|
| 123 |
+
f"</div>"
|
| 124 |
+
)
|
| 125 |
+
|
| 126 |
+
return ""
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
def play_simulation_selection(selection: str) -> tuple[str, str]:
|
| 130 |
+
provenance_html = get_provenance_html(selection)
|
| 131 |
if selection == "Steve Jobs 1983 Speech (Demo)":
|
| 132 |
+
return get_demo_html(), provenance_html
|
| 133 |
elif selection == "Carl Sagan Demon-Haunted World (Demo)":
|
| 134 |
+
return get_demo_2_html(), provenance_html
|
| 135 |
elif selection == "Edward Teller - Schrödinger's Cat (Demo)":
|
| 136 |
+
return get_demo_3_html(), provenance_html
|
| 137 |
elif selection == "Sal Khan - Khanmigo AI Tutor (Demo)":
|
| 138 |
+
return get_demo_4_html(), provenance_html
|
| 139 |
elif selection == "My Custom Simulation":
|
| 140 |
if custom_simulation_store["video_id"] and custom_simulation_store["chat_data"]:
|
| 141 |
+
player_html = load_template_with_data(custom_simulation_store["video_id"], custom_simulation_store["chat_data"])
|
| 142 |
else:
|
| 143 |
+
player_html = "<div style='color:#ff0055; text-align:center; padding:50px; font-family:sans-serif;'>No custom simulation has been generated yet. Please choose 'Configure Custom Solution'.</div>"
|
| 144 |
+
return player_html, provenance_html
|
| 145 |
+
return "", ""
|
| 146 |
+
|
| 147 |
|
| 148 |
def handle_generation(yt_url: str, pdf_file, doc_text: str, srt_text: str, hf_token: str, use_ocr: bool = False):
|
| 149 |
# 1. Validate YouTube Link
|
| 150 |
video_id = extract_youtube_video_id(yt_url)
|
| 151 |
if not video_id:
|
| 152 |
return (
|
| 153 |
+
gr.update(),
|
| 154 |
gr.update(),
|
| 155 |
"### ❌ Error\nInvalid YouTube URL. Please provide a valid YouTube link or 11-character Video ID.",
|
| 156 |
gr.update()
|
|
|
|
| 159 |
# 2. Identify Document Source
|
| 160 |
doc_path = None
|
| 161 |
document_content = None
|
| 162 |
+
pdf_name = None
|
| 163 |
+
has_pasted_text = False
|
| 164 |
if pdf_file is not None:
|
| 165 |
doc_path = pdf_file.name
|
| 166 |
+
pdf_name = os.path.basename(pdf_file.name)
|
| 167 |
elif doc_text.strip():
|
| 168 |
document_content = doc_text.strip()
|
| 169 |
+
has_pasted_text = True
|
| 170 |
else:
|
| 171 |
return (
|
| 172 |
+
gr.update(),
|
| 173 |
gr.update(),
|
| 174 |
"### ❌ Error\nPlease upload a PDF/text file or paste some reference document text.",
|
| 175 |
gr.update()
|
|
|
|
| 179 |
manual_transcript = srt_text.strip() if srt_text.strip() else None
|
| 180 |
if not manual_transcript:
|
| 181 |
return (
|
| 182 |
+
gr.update(),
|
| 183 |
gr.update(),
|
| 184 |
"### ❌ Error\nPlease paste the timestamped video transcript. (Auto-fetching is disabled due to server IP blocks).",
|
| 185 |
gr.update()
|
|
|
|
| 209 |
# Save to global store
|
| 210 |
custom_simulation_store["video_id"] = video_id
|
| 211 |
custom_simulation_store["chat_data"] = chat_data
|
| 212 |
+
custom_simulation_store["pdf_name"] = pdf_name
|
| 213 |
+
custom_simulation_store["has_pasted_text"] = has_pasted_text
|
| 214 |
|
| 215 |
success_msg = (
|
| 216 |
f"### 🎉 Success!\n"
|
|
|
|
| 220 |
|
| 221 |
# Create updated HTML player
|
| 222 |
new_html = load_template_with_data(video_id, chat_data)
|
| 223 |
+
provenance_html = get_provenance_html("My Custom Simulation")
|
| 224 |
|
| 225 |
return (
|
| 226 |
new_html,
|
| 227 |
+
provenance_html,
|
| 228 |
success_msg,
|
| 229 |
gr.update(choices=["Steve Jobs 1983 Speech (Demo)", "Carl Sagan Demon-Haunted World (Demo)", "Edward Teller - Schrödinger's Cat (Demo)", "Sal Khan - Khanmigo AI Tutor (Demo)", "My Custom Simulation"], value="My Custom Simulation")
|
| 230 |
)
|
|
|
|
| 237 |
"document or a different video."
|
| 238 |
)
|
| 239 |
return (
|
| 240 |
+
gr.update(),
|
| 241 |
gr.update(),
|
| 242 |
error_msg,
|
| 243 |
gr.update()
|
| 244 |
)
|
| 245 |
|
| 246 |
+
|
| 247 |
# Gradio Theme
|
| 248 |
custom_theme = gr.themes.Default(
|
| 249 |
primary_hue="purple",
|
|
|
|
| 341 |
}
|
| 342 |
"""
|
| 343 |
|
| 344 |
+
with gr.Blocks(title="ReadStream") as demo:
|
| 345 |
gr.HTML(
|
| 346 |
"""
|
| 347 |
<div style="text-align: center; margin-bottom: 20px; padding-top: 10px;">
|
|
|
|
| 365 |
# The player frame
|
| 366 |
player_frame = gr.HTML(value=get_demo_html())
|
| 367 |
|
| 368 |
+
# The provenance/citation frame
|
| 369 |
+
provenance_frame = gr.HTML(value=get_provenance_html("Steve Jobs 1983 Speech (Demo)"))
|
| 370 |
+
|
| 371 |
# Trigger updates when selection changes
|
| 372 |
sim_selector.change(
|
| 373 |
fn=play_simulation_selection,
|
| 374 |
inputs=[sim_selector],
|
| 375 |
+
outputs=[player_frame, provenance_frame]
|
| 376 |
)
|
| 377 |
|
| 378 |
# Tab 2: Generator Config
|
|
|
|
| 437 |
],
|
| 438 |
outputs=[
|
| 439 |
player_frame,
|
| 440 |
+
provenance_frame,
|
| 441 |
status_output,
|
| 442 |
sim_selector
|
| 443 |
]
|