AlserFurma commited on
Commit
165135e
·
verified ·
1 Parent(s): 80564a8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +113 -38
app.py CHANGED
@@ -158,15 +158,12 @@ def make_talking_head(image_path: str, audio_path: str, max_retries=3):
158
  # Основные обработчики для Gradio
159
  # =========================
160
  def start_lesson(image: Image.Image, text: str, state):
161
- """Шаг 1: генерируем видео-лекцию с вопросом и вариантами ответа."""
162
  if image is None or not text.strip() or len(text) > 500:
163
- return None, "", [], [], state
164
 
165
  try:
166
- question, options, correct = generate_quiz(text)
167
- quiz_ru = f"Вопрос: {question} Варианты: 1) {options[0]} 2) {options[1]}"
168
- audio_path = synthesize_audio(quiz_ru)
169
-
170
  tmpimg = tempfile.NamedTemporaryFile(suffix='.png', delete=False)
171
  if image.mode != 'RGB':
172
  image = image.convert('RGB')
@@ -174,38 +171,85 @@ def start_lesson(image: Image.Image, text: str, state):
174
  tmpimg.close()
175
  image_path = tmpimg.name
176
 
 
 
 
 
 
177
  video_path = make_talking_head(image_path, audio_path)
178
 
179
- state_data = {'image_path': image_path, 'correct': correct, 'options': options}
 
 
 
 
 
 
 
180
 
181
- # удаляем временный аудио файл
182
  try:
183
  os.remove(audio_path)
184
  except:
185
  pass
186
 
187
- return video_path, question, gr.Button(options[0], visible=True), gr.Button(options[1], visible=True), state_data
188
 
189
  except Exception as e:
190
  traceback.print_exc()
191
- return None, f"Ошибка: {e}", gr.Button("Вариант 1", visible=True), gr.Button("Вариант 2", visible=True), state
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
 
193
 
194
  def answer_selected(selected_option: str, state):
195
- """Шаг 2: пользователь выбирает вариант — генерируем реакцию лектора."""
196
  if not state:
197
- return None, "Ошибка: отсутствует состояние урока. Сначала нажмите 'Запустить урок'."
 
198
  try:
199
  correct = state.get('correct')
200
  image_path = state.get('image_path')
201
- options = state.get('options', [])
202
 
203
  if selected_option == correct:
204
- reaction_ru = "Молодец!"
205
- display_message = "Дұрыс!"
206
  else:
207
  reaction_ru = f"Неправильно. Правильный ответ: {correct}"
208
- display_message = f"Қате. Дұрыс жауап: {correct}"
209
 
210
  audio_path = synthesize_audio(reaction_ru)
211
  reaction_video = make_talking_head(image_path, audio_path)
@@ -219,7 +263,7 @@ def answer_selected(selected_option: str, state):
219
 
220
  except Exception as e:
221
  traceback.print_exc()
222
- return None, f"Ошибка: {e}"
223
 
224
 
225
  # =========================
@@ -227,46 +271,77 @@ def answer_selected(selected_option: str, state):
227
  # =========================
228
  title = "🎓 Интерактивный бейне-лектор"
229
  description = (
230
- "Загрузите фото лектора и текст лекции (русский, до 500 символов). "
231
- "Система создаст видео-лектора, задаст вопрос и предложит 2 варианта ответа. "
232
- "Нажмите на один из вариантов — лектор коротко отреагирует (қазақша)."
 
 
233
  )
234
 
235
- with gr.Blocks() as demo:
236
  gr.Markdown(f"# {title}\n{description}")
237
 
238
  with gr.Row():
239
  with gr.Column(scale=1):
240
  inp_image = gr.Image(type='pil', label='📸 Фото лектора')
241
- inp_text = gr.Textbox(lines=5, label='📝 Текст лекции (рус.)', placeholder='Введите текст...')
242
- btn_start = gr.Button("Запустить урок")
 
 
 
 
 
 
243
 
244
  with gr.Column(scale=1):
245
  out_video = gr.Video(label='🎬 Видео лектора')
246
- out_question = gr.Markdown(label='Вопрос')
247
- btn_opt1 = gr.Button("Вариант 1")
248
- btn_opt2 = gr.Button("Вариант 2")
249
- out_reaction_video = gr.Video(label='🎥 Реакция лектора')
250
- out_status = gr.Textbox(label='ℹ️ Статус', interactive=False)
 
 
 
251
 
252
  lesson_state = gr.State({})
253
 
254
- # Привязки
255
  btn_start.click(
256
  fn=start_lesson,
257
  inputs=[inp_image, inp_text, lesson_state],
258
- outputs=[out_video, out_question, btn_opt1, btn_opt2, lesson_state]
259
  )
260
 
261
- btn_opt1.click(fn=lambda state: answer_selected(state.get('options', [''])[0] if state else '', state),
262
- inputs=[lesson_state],
263
- outputs=[out_reaction_video, out_status])
264
-
265
- btn_opt2.click(fn=lambda state: answer_selected(state.get('options', [''])[1] if state and len(state.get('options', [])) > 1 else '', state),
266
- inputs=[lesson_state],
267
- outputs=[out_reaction_video, out_status])
268
 
269
- demo.load(lambda: "Готово", outputs=out_status)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
270
 
271
  if __name__ == '__main__':
272
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
158
  # Основные обработчики для Gradio
159
  # =========================
160
  def start_lesson(image: Image.Image, text: str, state):
161
+ """Шаг 1: лектор читает текст лекции."""
162
  if image is None or not text.strip() or len(text) > 500:
163
+ return None, "Пожалуйста, загрузите фото и введите текст лекции (до 500 символов)", gr.update(visible=False), state
164
 
165
  try:
166
+ # Сохраняем изображение
 
 
 
167
  tmpimg = tempfile.NamedTemporaryFile(suffix='.png', delete=False)
168
  if image.mode != 'RGB':
169
  image = image.convert('RGB')
 
171
  tmpimg.close()
172
  image_path = tmpimg.name
173
 
174
+ # Генерируем вопрос заранее (но не озвучиваем)
175
+ question, options, correct = generate_quiz(text)
176
+
177
+ # Лектор читает текст лекции
178
+ audio_path = synthesize_audio(text)
179
  video_path = make_talking_head(image_path, audio_path)
180
 
181
+ # Сохраняем состояние
182
+ state_data = {
183
+ 'image_path': image_path,
184
+ 'correct': correct,
185
+ 'options': options,
186
+ 'question': question,
187
+ 'text': text
188
+ }
189
 
190
+ # Удаляем временный аудио файл
191
  try:
192
  os.remove(audio_path)
193
  except:
194
  pass
195
 
196
+ return video_path, "✅ Лекция прочитана. Нажмите 'Задать вопрос' для проверки знаний.", gr.update(visible=True), state_data
197
 
198
  except Exception as e:
199
  traceback.print_exc()
200
+ return None, f"Ошибка: {e}", gr.update(visible=False), state
201
+
202
+
203
+ def ask_question(state):
204
+ """Шаг 2: лектор задает вопрос с вариантами ответа."""
205
+ if not state:
206
+ return None, "❌ Ошибка: сначала запустите урок", gr.update(visible=False), gr.update(visible=False)
207
+
208
+ try:
209
+ image_path = state.get('image_path')
210
+ question = state.get('question')
211
+ options = state.get('options', [])
212
+
213
+ # Формируем текст вопроса с вариантами
214
+ quiz_text = f"{question}. Первый вариант: {options[0]}. Второй вариант: {options[1]}"
215
+
216
+ # Генерируем аудио и видео с вопросом
217
+ audio_path = synthesize_audio(quiz_text)
218
+ video_path = make_talking_head(image_path, audio_path)
219
+
220
+ # Удаляем временный аудио файл
221
+ try:
222
+ os.remove(audio_path)
223
+ except:
224
+ pass
225
+
226
+ return (
227
+ video_path,
228
+ f"**Вопрос:** {question}",
229
+ gr.update(value=options[0], visible=True),
230
+ gr.update(value=options[1], visible=True)
231
+ )
232
+
233
+ except Exception as e:
234
+ traceback.print_exc()
235
+ return None, f"❌ Ошибка: {e}", gr.update(visible=False), gr.update(visible=False)
236
 
237
 
238
  def answer_selected(selected_option: str, state):
239
+ """Шаг 3: пользователь выбирает вариант — генерируем реакцию лектора."""
240
  if not state:
241
+ return None, "Ошибка: отсутствует состояние урока"
242
+
243
  try:
244
  correct = state.get('correct')
245
  image_path = state.get('image_path')
 
246
 
247
  if selected_option == correct:
248
+ reaction_ru = "Правильно! Молодец!"
249
+ display_message = "Дұрыс! Жарайсың!"
250
  else:
251
  reaction_ru = f"Неправильно. Правильный ответ: {correct}"
252
+ display_message = f"Қате. Дұрыс жауап: {correct}"
253
 
254
  audio_path = synthesize_audio(reaction_ru)
255
  reaction_video = make_talking_head(image_path, audio_path)
 
263
 
264
  except Exception as e:
265
  traceback.print_exc()
266
+ return None, f"Ошибка: {e}"
267
 
268
 
269
  # =========================
 
271
  # =========================
272
  title = "🎓 Интерактивный бейне-лектор"
273
  description = (
274
+ "**Как работает:**\n"
275
+ "1. Загрузите фото лектора и введите текст лекции (русский, до 500 символов)\n"
276
+ "2. Нажмите 'Запустить урок' — лектор прочитает текст\n"
277
+ "3. Нажмите 'Задать вопрос' — лектор задаст вопрос с двумя вариантами ответа\n"
278
+ "4. Выберите правильный ответ — лектор отреагирует на қазақша"
279
  )
280
 
281
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
282
  gr.Markdown(f"# {title}\n{description}")
283
 
284
  with gr.Row():
285
  with gr.Column(scale=1):
286
  inp_image = gr.Image(type='pil', label='📸 Фото лектора')
287
+ inp_text = gr.Textbox(
288
+ lines=5,
289
+ label='📝 Текст лекции (рус.)',
290
+ placeholder='Введите текст лекции...',
291
+ info="Максимум 500 символов"
292
+ )
293
+ btn_start = gr.Button("🚀 Запустить урок", variant="primary")
294
+ btn_question = gr.Button("❓ Задать вопрос", visible=False, variant="secondary")
295
 
296
  with gr.Column(scale=1):
297
  out_video = gr.Video(label='🎬 Видео лектора')
298
+ out_status = gr.Markdown("ℹ️ Загрузите фото и текст, затем нажмите 'Запустить урок'")
299
+
300
+ with gr.Row():
301
+ btn_opt1 = gr.Button("Вариант 1", visible=False, size="lg")
302
+ btn_opt2 = gr.Button("Вариант 2", visible=False, size="lg")
303
+
304
+ out_reaction_video = gr.Video(label='🎥 Реакция лектора', visible=False)
305
+ out_result = gr.Markdown("")
306
 
307
  lesson_state = gr.State({})
308
 
309
+ # Шаг 1: Запуск урока (чтение текста)
310
  btn_start.click(
311
  fn=start_lesson,
312
  inputs=[inp_image, inp_text, lesson_state],
313
+ outputs=[out_video, out_status, btn_question, lesson_state]
314
  )
315
 
316
+ # Шаг 2: Задать вопрос
317
+ btn_question.click(
318
+ fn=ask_question,
319
+ inputs=[lesson_state],
320
+ outputs=[out_video, out_status, btn_opt1, btn_opt2]
321
+ )
 
322
 
323
+ # Шаг 3: Обработка ответов
324
+ def handle_answer_1(state):
325
+ option = state.get('options', [''])[0] if state else ''
326
+ video, msg = answer_selected(option, state)
327
+ return video, msg, gr.update(visible=True)
328
+
329
+ def handle_answer_2(state):
330
+ option = state.get('options', [''])[1] if state and len(state.get('options', [])) > 1 else ''
331
+ video, msg = answer_selected(option, state)
332
+ return video, msg, gr.update(visible=True)
333
+
334
+ btn_opt1.click(
335
+ fn=handle_answer_1,
336
+ inputs=[lesson_state],
337
+ outputs=[out_reaction_video, out_result, out_reaction_video]
338
+ )
339
+
340
+ btn_opt2.click(
341
+ fn=handle_answer_2,
342
+ inputs=[lesson_state],
343
+ outputs=[out_reaction_video, out_result, out_reaction_video]
344
+ )
345
 
346
  if __name__ == '__main__':
347
  demo.launch(server_name="0.0.0.0", server_port=7860)