feat: Implement Zero-Touch Autonomous Forensic AI Video & Curriculum Analyzer (Gemini timeline chaptering, strictly chronological Socratic checkpoints, and curriculum grounding)
This commit is contained in:
@@ -7,16 +7,19 @@ use App\Core\Response;
|
||||
use App\Core\Database;
|
||||
use App\Core\Security;
|
||||
use App\Services\VideoService;
|
||||
use App\Services\AiVideoAnalyzerService;
|
||||
use App\Services\CurriculumService;
|
||||
|
||||
class VideoController
|
||||
{
|
||||
/**
|
||||
* Upload Video directly via API and create/attach to Lesson
|
||||
* Upload Video directly via API, transcode HLS, and trigger Autonomous Gemini AI Analysis
|
||||
* POST /api/teacher/videos/upload-direct
|
||||
*/
|
||||
public function uploadDirect(Request $request, Response $response): void
|
||||
{
|
||||
VideoService::ensureSchema();
|
||||
CurriculumService::ensureSchema();
|
||||
|
||||
$courseId = (int)($request->getBody()['course_id'] ?? $_POST['course_id'] ?? 0);
|
||||
$title = trim((string)($request->getBody()['title'] ?? $_POST['title'] ?? ''));
|
||||
@@ -67,13 +70,17 @@ class VideoController
|
||||
]
|
||||
);
|
||||
|
||||
// Autonomous Zero-Touch AI Analysis & Socratic Checkpoint Generation (Silent Background Execution)
|
||||
$aiReport = AiVideoAnalyzerService::processLessonAutonomously($lessonId);
|
||||
|
||||
$response->status(201)->json([
|
||||
'status' => 'success',
|
||||
'message' => 'تم رفع وحفظ ملف الفيديو وتقطيعه بتقنية HLS بنجاح!',
|
||||
'message' => 'تم رفع الفيديو وتقطيعه بتقنية HLS وتوليد الفحص السقراطي الذكي تلقائياً بنجاح!',
|
||||
'data' => array_merge($uploadResult, [
|
||||
'lesson_id' => $lessonId,
|
||||
'title' => $title,
|
||||
'course_id' => $courseId
|
||||
'lesson_id' => $lessonId,
|
||||
'title' => $title,
|
||||
'course_id' => $courseId,
|
||||
'ai_analysis' => $aiReport
|
||||
])
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
@@ -117,18 +124,19 @@ class VideoController
|
||||
}
|
||||
|
||||
/**
|
||||
* Link an existing or newly created Bunny Video ID to a Course Lesson
|
||||
* Link Bunny Video ID to a Course Lesson with Autonomous AI Socratic Generation
|
||||
* POST /api/teacher/videos/bunny-link
|
||||
*/
|
||||
public function linkBunnyLesson(Request $request, Response $response): void
|
||||
{
|
||||
VideoService::ensureSchema();
|
||||
CurriculumService::ensureSchema();
|
||||
|
||||
$body = $request->getBody();
|
||||
$courseId = (int)($body['course_id'] ?? 0);
|
||||
$title = trim((string)($body['title'] ?? ''));
|
||||
$bunnyVideoId = trim((string)($body['bunny_video_id'] ?? ''));
|
||||
$duration = (int)($body['duration_seconds'] ?? 0);
|
||||
$duration = (int)($body['duration_seconds'] ?? 600);
|
||||
$sequenceOrder = (int)($body['sequence_order'] ?? 1);
|
||||
|
||||
if (!$courseId || empty($title) || empty($bunnyVideoId)) {
|
||||
@@ -154,13 +162,17 @@ class VideoController
|
||||
[$courseId, $title, $sequenceOrder, $bunnyVideoId, $duration]
|
||||
);
|
||||
|
||||
// Autonomous AI Analysis for Bunny Lessons
|
||||
$aiReport = AiVideoAnalyzerService::processLessonAutonomously($lessonId);
|
||||
|
||||
$response->status(201)->json([
|
||||
'status' => 'success',
|
||||
'message' => 'تم ربط درس Bunny Stream بنجاح!',
|
||||
'message' => 'تم ربط درس Bunny Stream وتوليد نقاط الفحص السقراطي تلقائياً!',
|
||||
'data' => [
|
||||
'lesson_id' => $lessonId,
|
||||
'bunny_video_id' => $bunnyVideoId,
|
||||
'storage_type' => 'bunny_stream'
|
||||
'storage_type' => 'bunny_stream',
|
||||
'ai_analysis' => $aiReport
|
||||
]
|
||||
]);
|
||||
}
|
||||
@@ -272,12 +284,13 @@ class VideoController
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Lesson Playback Data with Signed DRM Tokens and Socratic Checkpoints
|
||||
* Get Lesson Playback Data with Chapters Roadmap and Socratic Checkpoints
|
||||
* GET /api/lessons/{id}/playback
|
||||
*/
|
||||
public function getPlaybackData(Request $request, Response $response): void
|
||||
{
|
||||
VideoService::ensureSchema();
|
||||
CurriculumService::ensureSchema();
|
||||
|
||||
$lessonId = (int)$request->getParam('id');
|
||||
if (!$lessonId) {
|
||||
@@ -291,6 +304,13 @@ class VideoController
|
||||
return;
|
||||
}
|
||||
|
||||
// If lesson has no checkpoints yet, generate them autonomously
|
||||
$existingCount = Database::selectOne("SELECT COUNT(*) as cnt FROM exams WHERE lesson_id = ? AND scope = 'in_video_checkpoint'", [$lessonId]);
|
||||
if (empty($existingCount['cnt'])) {
|
||||
AiVideoAnalyzerService::processLessonAutonomously($lessonId);
|
||||
$lesson = Database::selectOne("SELECT * FROM lessons WHERE id = ? LIMIT 1", [$lessonId]);
|
||||
}
|
||||
|
||||
// Fetch attached in-video Socratic Checkpoints with Questions and Options
|
||||
$exams = Database::select(
|
||||
"SELECT e.id as exam_id, e.uuid as exam_uuid, e.title, e.timestamp_seconds, e.rewind_on_fail_seconds, e.passing_percentage
|
||||
@@ -312,7 +332,8 @@ class VideoController
|
||||
'exam_id' => (int)$ex['exam_id'],
|
||||
'timestamp_seconds' => (int)$ex['timestamp_seconds'],
|
||||
'rewind_on_fail_seconds' => (int)$ex['rewind_on_fail_seconds'],
|
||||
'question_text' => $q['question_text'] ?? 'ما هي الإجابة الصحيحة؟',
|
||||
'question_text' => $q['question_text'] ?? 'سؤال فحص فهم الفكرة:',
|
||||
'explanation' => $q['explanation_text'] ?? '',
|
||||
'options' => array_map(function ($o) {
|
||||
return [
|
||||
'id' => (int)$o['id'],
|
||||
@@ -341,6 +362,8 @@ class VideoController
|
||||
$playbackInfo = array_merge(['storage_type' => 'bunny_stream'], $signedData);
|
||||
}
|
||||
|
||||
$chapters = !empty($lesson['timeline_chapters_json']) ? json_decode($lesson['timeline_chapters_json'], true) : [];
|
||||
|
||||
$response->json([
|
||||
'status' => 'success',
|
||||
'data' => [
|
||||
@@ -353,6 +376,7 @@ class VideoController
|
||||
'storage_type' => $storageType
|
||||
],
|
||||
'playback' => $playbackInfo,
|
||||
'chapters' => $chapters ?: [],
|
||||
'checkpoints' => $checkpoints ?: []
|
||||
]
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user