adelevett commited on
Commit
e9d4821
·
1 Parent(s): 9c65e45

Add provenance section below video player in theater mode

Browse files
Files changed (1) hide show
  1. app.py +91 -11
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 play_simulation_selection(selection: str) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- return load_template_with_data(custom_simulation_store["video_id"], custom_simulation_store["chat_data"])
81
  else:
82
- return "<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>"
83
- return ""
 
 
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", theme=custom_theme, css=custom_css) as demo:
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
  ]