AlserFurma commited on
Commit
ee9f075
·
verified ·
1 Parent(s): 29be5ba

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +345 -220
app.py CHANGED
@@ -100,7 +100,7 @@ def create_lesson(segments_state):
100
  'wrong': f"wrong_{i}.mp4",
101
  'duration': seg['duration'],
102
  'options': seg['options'],
103
- 'correct': seg['correct']
104
  })
105
 
106
  # Сохраняем метаданные
@@ -140,9 +140,14 @@ def generate_player_html(meta_path):
140
  for segment in metadata['timestamps']:
141
  idx = segment['index']
142
 
143
- lecture_path = os.path.join(lesson_dir, segment['lecture'])
144
- correct_path = os.path.join(lesson_dir, segment['correct'])
145
- wrong_path = os.path.join(lesson_dir, segment['wrong'])
 
 
 
 
 
146
 
147
  try:
148
  with open(lecture_path, 'rb') as f:
@@ -162,203 +167,231 @@ def generate_player_html(meta_path):
162
  'index': seg['index'],
163
  'duration': seg['duration'],
164
  'options': seg['options'],
165
- 'correct': seg['correct']
166
  } for seg in metadata['timestamps']], ensure_ascii=False)
167
 
168
  # Создаем словарь с видео для JavaScript
169
  videos_json = json.dumps(video_data)
170
 
171
  html = f"""
172
- <style>
173
- * {{
174
- margin: 0;
175
- padding: 0;
176
- box-sizing: border-box;
177
- }}
178
-
179
- .player-wrapper {{
180
- width: 100%;
181
- max-width: 900px;
182
- margin: 20px auto;
183
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
184
- }}
185
-
186
- .player-container {{
187
- position: relative;
188
- width: 100%;
189
- background: #000;
190
- border-radius: 12px;
191
- overflow: hidden;
192
- box-shadow: 0 8px 32px rgba(0,0,0,0.3);
193
- }}
194
-
195
- #mainVideo {{
196
- width: 100%;
197
- height: auto;
198
- display: block;
199
- min-height: 400px;
200
- }}
201
-
202
- .quiz-overlay {{
203
- position: absolute;
204
- bottom: 0;
205
- left: 0;
206
- right: 0;
207
- background: linear-gradient(to top, rgba(0,0,0,0.95) 0%, rgba(0,0,0,0.8) 50%, transparent 100%);
208
- padding: 40px 30px 30px;
209
- display: none;
210
- }}
211
-
212
- .quiz-question {{
213
- color: #ffffff;
214
- font-size: 20px;
215
- font-weight: 600;
216
- text-align: center;
217
- margin-bottom: 20px;
218
- text-shadow: 0 2px 4px rgba(0,0,0,0.5);
219
- }}
220
-
221
- .quiz-options {{
222
- display: flex;
223
- gap: 15px;
224
- flex-direction: column;
225
- max-width: 600px;
226
- margin: 0 auto;
227
- }}
228
-
229
- .option-btn {{
230
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
231
- color: white;
232
- border: none;
233
- padding: 18px 30px;
234
- border-radius: 12px;
235
- font-size: 17px;
236
- font-weight: 500;
237
- cursor: pointer;
238
- transition: all 0.3s;
239
- box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
240
- }}
241
-
242
- .option-btn:hover {{
243
- transform: translateY(-3px);
244
- box-shadow: 0 6px 25px rgba(102, 126, 234, 0.6);
245
- }}
246
-
247
- .option-btn:active {{
248
- transform: translateY(-1px);
249
- }}
250
-
251
- .timer {{
252
- color: #ffd700;
253
- font-size: 15px;
254
- font-weight: 600;
255
- text-align: center;
256
- margin-top: 15px;
257
- text-shadow: 0 2px 4px rgba(0,0,0,0.5);
258
- }}
259
-
260
- .timer.warning {{
261
- color: #ff6b6b;
262
- }}
263
-
264
- .progress-bar {{
265
- position: absolute;
266
- top: 0;
267
- left: 0;
268
- height: 4px;
269
- background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
270
- width: 0%;
271
- transition: width 0.1s linear;
272
- z-index: 10;
273
- }}
274
-
275
- .segment-info {{
276
- position: absolute;
277
- top: 15px;
278
- right: 15px;
279
- background: rgba(0,0,0,0.7);
280
- color: white;
281
- padding: 8px 16px;
282
- border-radius: 20px;
283
- font-size: 14px;
284
- font-weight: 500;
285
- z-index: 10;
286
- }}
287
-
288
- .loading {{
289
- position: absolute;
290
- top: 50%;
291
- left: 50%;
292
- transform: translate(-50%, -50%);
293
- color: white;
294
- font-size: 18px;
295
- display: none;
296
- z-index: 20;
297
- }}
298
-
299
- .controls {{
300
- padding: 15px;
301
- background: #1a1a1a;
302
- display: flex;
303
- align-items: center;
304
- gap: 15px;
305
- }}
306
-
307
- .play-pause-btn {{
308
- background: #667eea;
309
- border: none;
310
- color: white;
311
- width: 40px;
312
- height: 40px;
313
- border-radius: 50%;
314
- cursor: pointer;
315
- display: flex;
316
- align-items: center;
317
- justify-content: center;
318
- transition: all 0.3s;
319
- font-size: 16px;
320
- }}
321
-
322
- .play-pause-btn:hover {{
323
- background: #764ba2;
324
- transform: scale(1.1);
325
- }}
326
-
327
- .time-display {{
328
- color: #ffffff;
329
- font-size: 14px;
330
- font-weight: 500;
331
- }}
332
- </style>
333
-
334
- <div class="player-wrapper">
335
- <div class="player-container">
336
- <div class="progress-bar" id="progressBar"></div>
337
- <div class="segment-info" id="segmentInfo">Сегмент 1 из {metadata['total_segments']}</div>
338
- <div class="loading" id="loading">Загрузка...</div>
339
-
340
- <video id="mainVideo" preload="auto">
341
- Ваш браузер не поддерживает видео
342
- </video>
343
-
344
- <div class="quiz-overlay" id="quizOverlay">
345
- <div class="quiz-question">Выберите правильный ответ:</div>
346
- <div class="quiz-options">
347
- <button class="option-btn" id="option1"></button>
348
- <button class="option-btn" id="option2"></button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
  </div>
350
- <div class="timer" id="timer">⏱ Времени осталось: <span id="timeLeft">7</span> сек</div>
 
 
 
 
 
351
  </div>
352
  </div>
353
 
354
- <div class="controls">
355
- <button class="play-pause-btn" id="playPauseBtn">▶</button>
356
- <div class="time-display" id="timeDisplay">0:00</div>
357
- </div>
358
- </div>
359
-
360
- <script>
361
- (function() {{
362
  const mainVideo = document.getElementById('mainVideo');
363
  const quizOverlay = document.getElementById('quizOverlay');
364
  const option1Btn = document.getElementById('option1');
@@ -370,6 +403,7 @@ def generate_player_html(meta_path):
370
  const playPauseBtn = document.getElementById('playPauseBtn');
371
  const timeDisplay = document.getElementById('timeDisplay');
372
  const timerElement = document.getElementById('timer');
 
373
 
374
  const segments = {segments_json};
375
  const videosBase64 = {videos_json};
@@ -389,6 +423,7 @@ def generate_player_html(meta_path):
389
 
390
  function updateSegmentInfo() {{
391
  segmentInfo.textContent = 'Сегмент ' + (currentSegmentIndex + 1) + ' из ' + segments.length;
 
392
  }}
393
 
394
  function showLoading() {{
@@ -487,6 +522,7 @@ def generate_player_html(meta_path):
487
  if (currentSegmentIndex >= segments.length) {{
488
  alert('🎉 Поздравляем! Вы завершили урок!');
489
  currentSegmentIndex = 0;
 
490
  return;
491
  }}
492
 
@@ -509,6 +545,12 @@ def generate_player_html(meta_path):
509
 
510
  const isCorrect = choice === segment.correct;
511
 
 
 
 
 
 
 
512
  playReaction(isCorrect).then(function() {{
513
  currentSegmentIndex++;
514
 
@@ -574,14 +616,15 @@ def generate_player_html(meta_path):
574
 
575
  // Запускаем первый сегмент
576
  loadNextSegment();
577
- }})();
578
- </script>
 
579
  """
580
 
581
  return html
582
 
583
  # Создаем интерфейс
584
- with gr.Blocks(title="Интерактивный Урок") as demo:
585
  gr.Markdown("""
586
  # 🎓 Интерактивный Урок (Coursera-style)
587
 
@@ -594,6 +637,8 @@ with gr.Blocks(title="Интерактивный Урок") as demo:
594
  3. Нажмите **"Добавить Сегмент"** (повторите для каждого фрагмента)
595
  4. Когда все сегменты добавлены, нажмите **"Создать Урок"**
596
  5. Нажмите **"Запустить Плеер"** для прохождения урока
 
 
597
  """)
598
 
599
  # Состояния
@@ -601,31 +646,63 @@ with gr.Blocks(title="Интерактивный Урок") as demo:
601
  meta_state = gr.State(None)
602
 
603
  with gr.Tab("📝 Создание Урока"):
604
- gr.Markdown("### Добавление сегмента")
605
-
606
  with gr.Row():
607
- with gr.Column(scale=1):
608
- lecture_input = gr.Video(label="🎥 Видео Лекции")
609
- correct_input = gr.Video(label="✅ Реакция: Правильный ответ")
610
- wrong_input = gr.Video(label=" Реакция: Неправильный ответ")
 
 
 
 
 
 
 
 
 
 
 
611
 
612
  with gr.Column(scale=1):
 
613
  option1_input = gr.Textbox(
614
- label="Вариант 1",
615
- placeholder="Например: Париж"
 
616
  )
617
  option2_input = gr.Textbox(
618
- label="Вариант 2",
619
- placeholder="Например: Лондон"
 
620
  )
621
  correct_radio = gr.Radio(
622
  ["Вариант 1", "Вариант 2"],
623
  label="✅ Правильный вариант",
624
- value="Вариант 1"
 
 
 
 
 
 
 
625
  )
626
 
627
- add_btn = gr.Button("➕ Добавить Сегмент", variant="primary")
628
- segments_info = gr.Markdown("*Нет добавленных сегментов*")
 
 
 
 
 
 
 
 
 
 
 
 
 
629
 
630
  add_btn.click(
631
  fn=add_segment,
@@ -633,11 +710,6 @@ with gr.Blocks(title="Интерактивный Урок") as demo:
633
  outputs=[segments_state, segments_info, lecture_input, correct_input, wrong_input]
634
  )
635
 
636
- gr.Markdown("---")
637
-
638
- create_btn = gr.Button("🎬 Создать Урок", variant="primary")
639
- create_status = gr.Markdown("")
640
-
641
  create_btn.click(
642
  fn=create_lesson,
643
  inputs=[segments_state],
@@ -647,22 +719,75 @@ with gr.Blocks(title="Интерактивный Урок") as demo:
647
  with gr.Tab("▶️ Прохождение Урока"):
648
  gr.Markdown("""
649
  ### Как проходить урок:
650
- - 🎬 Видео проигрывается автоматически
 
651
  - ⏱ **За 7 секунд** до конца появятся кнопки с вариантами ответа
652
- - 🎯 Выберите правильный ответ до окончания времени
653
- - ✅ После ответа покажется видео реакции лектора
654
- - ➡️ Затем автоматически начнется следующий сегмент
655
- - 🏆 Пройдите все сегменты до конца!
 
 
656
  """)
657
 
658
- start_btn = gr.Button("🚀 Запустить Плеер", variant="primary")
659
- player_html = gr.HTML()
 
 
 
 
 
 
 
660
 
661
  start_btn.click(
662
  fn=generate_player_html,
663
  inputs=[meta_state],
664
  outputs=[player_html]
665
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
666
 
667
  if __name__ == '__main__':
668
- demo.launch()
 
 
 
 
 
 
100
  'wrong': f"wrong_{i}.mp4",
101
  'duration': seg['duration'],
102
  'options': seg['options'],
103
+ 'correct': seg['correct'] # Это 0 или 1, не имя файла!
104
  })
105
 
106
  # Сохраняем метаданные
 
140
  for segment in metadata['timestamps']:
141
  idx = segment['index']
142
 
143
+ # Используем правильные имена файлов из метаданных
144
+ lecture_file = segment['lecture']
145
+ correct_file = segment['correct']
146
+ wrong_file = segment['wrong']
147
+
148
+ lecture_path = os.path.join(lesson_dir, lecture_file)
149
+ correct_path = os.path.join(lesson_dir, correct_file)
150
+ wrong_path = os.path.join(lesson_dir, wrong_file)
151
 
152
  try:
153
  with open(lecture_path, 'rb') as f:
 
167
  'index': seg['index'],
168
  'duration': seg['duration'],
169
  'options': seg['options'],
170
+ 'correct': seg['correct'] # Это целое число (0 или 1), не имя файла
171
  } for seg in metadata['timestamps']], ensure_ascii=False)
172
 
173
  # Создаем словарь с видео для JavaScript
174
  videos_json = json.dumps(video_data)
175
 
176
  html = f"""
177
+ <!DOCTYPE html>
178
+ <html lang="ru">
179
+ <head>
180
+ <meta charset="UTF-8">
181
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
182
+ <title>Интерактивный Урок</title>
183
+ <style>
184
+ * {{
185
+ margin: 0;
186
+ padding: 0;
187
+ box-sizing: border-box;
188
+ }}
189
+
190
+ body {{
191
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
192
+ background: #f5f5f5;
193
+ padding: 20px;
194
+ }}
195
+
196
+ .player-wrapper {{
197
+ width: 100%;
198
+ max-width: 900px;
199
+ margin: 0 auto;
200
+ background: white;
201
+ border-radius: 16px;
202
+ overflow: hidden;
203
+ box-shadow: 0 10px 40px rgba(0,0,0,0.1);
204
+ }}
205
+
206
+ .player-container {{
207
+ position: relative;
208
+ width: 100%;
209
+ background: #000;
210
+ overflow: hidden;
211
+ }}
212
+
213
+ #mainVideo {{
214
+ width: 100%;
215
+ height: auto;
216
+ display: block;
217
+ max-height: 600px;
218
+ }}
219
+
220
+ .quiz-overlay {{
221
+ position: absolute;
222
+ bottom: 0;
223
+ left: 0;
224
+ right: 0;
225
+ background: linear-gradient(to top, rgba(0,0,0,0.95) 0%, rgba(0,0,0,0.8) 50%, transparent 100%);
226
+ padding: 40px 30px 30px;
227
+ display: none;
228
+ }}
229
+
230
+ .quiz-question {{
231
+ color: #ffffff;
232
+ font-size: 22px;
233
+ font-weight: 600;
234
+ text-align: center;
235
+ margin-bottom: 25px;
236
+ text-shadow: 0 2px 4px rgba(0,0,0,0.5);
237
+ }}
238
+
239
+ .quiz-options {{
240
+ display: flex;
241
+ gap: 15px;
242
+ flex-direction: column;
243
+ max-width: 600px;
244
+ margin: 0 auto;
245
+ }}
246
+
247
+ .option-btn {{
248
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
249
+ color: white;
250
+ border: none;
251
+ padding: 20px 35px;
252
+ border-radius: 12px;
253
+ font-size: 18px;
254
+ font-weight: 500;
255
+ cursor: pointer;
256
+ transition: all 0.3s;
257
+ box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
258
+ }}
259
+
260
+ .option-btn:hover {{
261
+ transform: translateY(-3px);
262
+ box-shadow: 0 6px 25px rgba(102, 126, 234, 0.6);
263
+ }}
264
+
265
+ .option-btn:active {{
266
+ transform: translateY(-1px);
267
+ }}
268
+
269
+ .timer {{
270
+ color: #ffd700;
271
+ font-size: 16px;
272
+ font-weight: 600;
273
+ text-align: center;
274
+ margin-top: 20px;
275
+ text-shadow: 0 2px 4px rgba(0,0,0,0.5);
276
+ }}
277
+
278
+ .timer.warning {{
279
+ color: #ff6b6b;
280
+ animation: pulse 1s infinite;
281
+ }}
282
+
283
+ @keyframes pulse {{
284
+ 0% {{ opacity: 1; }}
285
+ 50% {{ opacity: 0.7; }}
286
+ 100% {{ opacity: 1; }}
287
+ }}
288
+
289
+ .progress-bar {{
290
+ position: absolute;
291
+ top: 0;
292
+ left: 0;
293
+ height: 4px;
294
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
295
+ width: 0%;
296
+ transition: width 0.1s linear;
297
+ z-index: 10;
298
+ }}
299
+
300
+ .segment-info {{
301
+ position: absolute;
302
+ top: 15px;
303
+ right: 15px;
304
+ background: rgba(0,0,0,0.7);
305
+ color: white;
306
+ padding: 8px 16px;
307
+ border-radius: 20px;
308
+ font-size: 14px;
309
+ font-weight: 500;
310
+ z-index: 10;
311
+ }}
312
+
313
+ .loading {{
314
+ position: absolute;
315
+ top: 50%;
316
+ left: 50%;
317
+ transform: translate(-50%, -50%);
318
+ color: white;
319
+ font-size: 18px;
320
+ display: none;
321
+ z-index: 20;
322
+ }}
323
+
324
+ .controls {{
325
+ padding: 20px;
326
+ background: #1a1a1a;
327
+ display: flex;
328
+ align-items: center;
329
+ gap: 20px;
330
+ }}
331
+
332
+ .play-pause-btn {{
333
+ background: #667eea;
334
+ border: none;
335
+ color: white;
336
+ width: 50px;
337
+ height: 50px;
338
+ border-radius: 50%;
339
+ cursor: pointer;
340
+ display: flex;
341
+ align-items: center;
342
+ justify-content: center;
343
+ transition: all 0.3s;
344
+ font-size: 18px;
345
+ }}
346
+
347
+ .play-pause-btn:hover {{
348
+ background: #764ba2;
349
+ transform: scale(1.1);
350
+ }}
351
+
352
+ .time-display {{
353
+ color: #ffffff;
354
+ font-size: 16px;
355
+ font-weight: 500;
356
+ flex-grow: 1;
357
+ }}
358
+
359
+ .lesson-progress {{
360
+ color: #aaa;
361
+ font-size: 14px;
362
+ margin-left: 10px;
363
+ }}
364
+ </style>
365
+ </head>
366
+ <body>
367
+ <div class="player-wrapper">
368
+ <div class="player-container">
369
+ <div class="progress-bar" id="progressBar"></div>
370
+ <div class="segment-info" id="segmentInfo">Сегмент 1 из {metadata['total_segments']}</div>
371
+ <div class="loading" id="loading">Загрузка...</div>
372
+
373
+ <video id="mainVideo" preload="auto">
374
+ Ваш браузер не поддерживает видео
375
+ </video>
376
+
377
+ <div class="quiz-overlay" id="quizOverlay">
378
+ <div class="quiz-question">Выберите правильный ответ:</div>
379
+ <div class="quiz-options">
380
+ <button class="option-btn" id="option1"></button>
381
+ <button class="option-btn" id="option2"></button>
382
+ </div>
383
+ <div class="timer" id="timer">⏱ Времени осталось: <span id="timeLeft">7</span> сек</div>
384
  </div>
385
+ </div>
386
+
387
+ <div class="controls">
388
+ <button class="play-pause-btn" id="playPauseBtn">▶</button>
389
+ <div class="time-display" id="timeDisplay">0:00</div>
390
+ <div class="lesson-progress" id="lessonProgress">1/{metadata['total_segments']}</div>
391
  </div>
392
  </div>
393
 
394
+ <script>
 
 
 
 
 
 
 
395
  const mainVideo = document.getElementById('mainVideo');
396
  const quizOverlay = document.getElementById('quizOverlay');
397
  const option1Btn = document.getElementById('option1');
 
403
  const playPauseBtn = document.getElementById('playPauseBtn');
404
  const timeDisplay = document.getElementById('timeDisplay');
405
  const timerElement = document.getElementById('timer');
406
+ const lessonProgress = document.getElementById('lessonProgress');
407
 
408
  const segments = {segments_json};
409
  const videosBase64 = {videos_json};
 
423
 
424
  function updateSegmentInfo() {{
425
  segmentInfo.textContent = 'Сегмент ' + (currentSegmentIndex + 1) + ' из ' + segments.length;
426
+ lessonProgress.textContent = (currentSegmentIndex + 1) + '/' + segments.length;
427
  }}
428
 
429
  function showLoading() {{
 
522
  if (currentSegmentIndex >= segments.length) {{
523
  alert('🎉 Поздравляем! Вы завершили урок!');
524
  currentSegmentIndex = 0;
525
+ updateSegmentInfo();
526
  return;
527
  }}
528
 
 
545
 
546
  const isCorrect = choice === segment.correct;
547
 
548
+ if (isCorrect) {{
549
+ console.log('✅ Правильный ответ!');
550
+ }} else {{
551
+ console.log('❌ Неправильный ответ');
552
+ }}
553
+
554
  playReaction(isCorrect).then(function() {{
555
  currentSegmentIndex++;
556
 
 
616
 
617
  // Запускаем первый сегмент
618
  loadNextSegment();
619
+ </script>
620
+ </body>
621
+ </html>
622
  """
623
 
624
  return html
625
 
626
  # Создаем интерфейс
627
+ with gr.Blocks(title="Интерактивный Урок", theme=gr.themes.Soft()) as demo:
628
  gr.Markdown("""
629
  # 🎓 Интерактивный Урок (Coursera-style)
630
 
 
637
  3. Нажмите **"Добавить Сегмент"** (повторите для каждого фрагмента)
638
  4. Когда все сегменты добавлены, нажмите **"Создать Урок"**
639
  5. Нажмите **"Запустить Плеер"** для прохождения урока
640
+
641
+ ---
642
  """)
643
 
644
  # Состояния
 
646
  meta_state = gr.State(None)
647
 
648
  with gr.Tab("📝 Создание Урока"):
 
 
649
  with gr.Row():
650
+ with gr.Column(scale=2):
651
+ gr.Markdown("### 1. Загрузите видео")
652
+ lecture_input = gr.Video(
653
+ label="🎥 Видео Лекции",
654
+ info="Основное видео лекции с вопросом в конце"
655
+ )
656
+ with gr.Row():
657
+ correct_input = gr.Video(
658
+ label="✅ Реакция: Правильный ответ",
659
+ info="Короткое видео реакции лектора на правильный ответ"
660
+ )
661
+ wrong_input = gr.Video(
662
+ label="❌ Реакция: Неправильный ответ",
663
+ info="Короткое видео реакции лектора на неправильный ответ"
664
+ )
665
 
666
  with gr.Column(scale=1):
667
+ gr.Markdown("### 2. Настройте вопрос")
668
  option1_input = gr.Textbox(
669
+ label="Вариант ответа 1",
670
+ placeholder="Например: Париж",
671
+ info="Первый вариант ответа"
672
  )
673
  option2_input = gr.Textbox(
674
+ label="Вариант ответа 2",
675
+ placeholder="Например: Лондон",
676
+ info="Второй вариант ответа"
677
  )
678
  correct_radio = gr.Radio(
679
  ["Вариант 1", "Вариант 2"],
680
  label="✅ Правильный вариант",
681
+ value="Вариант 1",
682
+ info="Выберите правильный вариант ответа"
683
+ )
684
+
685
+ add_btn = gr.Button(
686
+ "➕ Добавить Сегмент",
687
+ variant="primary",
688
+ size="lg"
689
  )
690
 
691
+ gr.Markdown("---")
692
+
693
+ segments_info = gr.Markdown(
694
+ "### 📋 Добавленные сегменты\n\n*Нет добавленных сегментов*"
695
+ )
696
+
697
+ gr.Markdown("---")
698
+
699
+ with gr.Row():
700
+ create_btn = gr.Button(
701
+ "🎬 Создать Урок",
702
+ variant="primary",
703
+ size="lg"
704
+ )
705
+ create_status = gr.Markdown("**Статус:** Урок еще не создан")
706
 
707
  add_btn.click(
708
  fn=add_segment,
 
710
  outputs=[segments_state, segments_info, lecture_input, correct_input, wrong_input]
711
  )
712
 
 
 
 
 
 
713
  create_btn.click(
714
  fn=create_lesson,
715
  inputs=[segments_state],
 
719
  with gr.Tab("▶️ Прохождение Урока"):
720
  gr.Markdown("""
721
  ### Как проходить урок:
722
+
723
+ - 🎬 **Видео проигрывается автоматически**
724
  - ⏱ **За 7 секунд** до конца появятся кнопки с вариантами ответа
725
+ - 🎯 **Выберите правильный ответ** до окончания времени (7 секунд)
726
+ - ✅ **После ответа** покажется видео реакции лектора
727
+ - ➡️ **Затем автоматически** начнется следующий сегмент
728
+ - 🏆 **Пройдите все сегменты** до конца!
729
+
730
+ ---
731
  """)
732
 
733
+ start_btn = gr.Button(
734
+ "🚀 Запустить Плеер",
735
+ variant="primary",
736
+ size="lg"
737
+ )
738
+
739
+ player_html = gr.HTML(
740
+ value="<div style='text-align: center; padding: 40px; color: #666;'>Нажмите 'Запустить Плеер', чтобы начать урок</div>"
741
+ )
742
 
743
  start_btn.click(
744
  fn=generate_player_html,
745
  inputs=[meta_state],
746
  outputs=[player_html]
747
  )
748
+
749
+ with gr.Tab("ℹ️ Информация"):
750
+ gr.Markdown("""
751
+ ### 📚 О приложении
752
+
753
+ **Интерактивный Урок** - это система для создания и прохождения образовательных видеоуроков в стиле Coursera.
754
+
755
+ ### ✨ Особенности:
756
+
757
+ - 🎥 **Интерактивные видео** с вопросами
758
+ - ⏱ **Таймер ответа** (7 секунд)
759
+ - ✅ **Мгновенная обратная связь** через видео реакции
760
+ - 📊 **Пошаговое прохождение** уроков
761
+ - 🎨 **Современный интерфейс** с анимациями
762
+
763
+ ### 🛠 Технические требования:
764
+
765
+ - Видео в формате **MP4**
766
+ - Размер каждого видео: **до 500 MB**
767
+ - **Оптимальная длина** видео лекции: 30-120 секунд
768
+ - **Оптимальная длина** видео реакций: 5-15 секунд
769
+
770
+ ### 🔧 Установка зависимостей:
771
+
772
+ ```bash
773
+ pip install gradio moviepy
774
+ ```
775
+
776
+ Для работы с видео также рекомендуется установить ffmpeg.
777
+
778
+ ### 📁 Структура урока:
779
+
780
+ 1. **Видео лекции** - основной контент
781
+ 2. **Видео реакции на правильный ответ**
782
+ 3. **Видео реакции на неправильный ответ**
783
+ 4. **Варианты ответов** (2 варианта)
784
+ 5. **Правильный ответ** (выбирается при создании)
785
+ """)
786
 
787
  if __name__ == '__main__':
788
+ demo.launch(
789
+ server_name="0.0.0.0",
790
+ server_port=7860,
791
+ share=False,
792
+ debug=True
793
+ )