Update Saqel Platform: 2026-09-02 07:43:58

This commit is contained in:
Hamza-Ayed
2026-09-02 07:43:58 +03:00
parent 310d4b57e9
commit 739f934a26
2 changed files with 42 additions and 17 deletions
@@ -264,29 +264,35 @@ class CurriculumController
// Resolve a valid FK-safe course_id for the master ministry curriculum.
// IMPORTANT: course_id = 0 causes SQLSTATE[23000] FK violation because
// the `lessons` table enforces courses(id) ON DELETE CASCADE.
$courseId = CurriculumService::getOrCreateSystemCourse();
$title = basename($file, '.md');
$manifestLesson = CurriculumService::findLessonByFile($file);
$title = $manifestLesson['title'] ?? basename($file, '.md');
$existingLesson = \App\Core\Database::selectOne(
"SELECT id, course_id FROM lessons WHERE title = ? OR markdown_content LIKE ? LIMIT 1",
[$title, '%' . $file . '%']
);
$courseId = $existingLesson
? (int)$existingLesson['course_id']
: CurriculumService::getOrCreateSystemCourse();
// 1. Upload & Transcode to HLS & Push to R2 (handled by VideoService)
$uploadResult = \App\Services\VideoService::handleDirectUpload($_FILES['video_file'], $courseId, $title);
$videoUrl = $uploadResult['r2_url'] ?? $uploadResult['hls_url'];
// 2. Insert into lessons table as AI-generated ministry version
// 2. Bind the upload to the synchronized Manifest lesson when present.
if ($existingLesson) {
$lessonId = (int)$existingLesson['id'];
\App\Core\Database::query(
"UPDATE lessons SET storage_type = 'api_upload', video_uuid = ?, bunny_video_id = '', local_path = ?, hls_url = ?, thumbnail_url = ?, duration_seconds = ?, encoding_status = 'ready', ai_video_url = ? WHERE id = ?",
[$uploadResult['video_uuid'], $uploadResult['local_path'], $uploadResult['hls_url'], $uploadResult['thumbnail_url'], $uploadResult['duration'] ?? 0, $videoUrl, $lessonId]
);
} else {
$lessonId = \App\Core\Database::insert(
"INSERT INTO lessons (course_id, title, sequence_order, storage_type, video_uuid, bunny_video_id, local_path, hls_url, thumbnail_url, duration_seconds, is_free_preview, encoding_status, ai_video_url)
VALUES (?, ?, 1, 'api_upload', ?, '', ?, ?, ?, ?, 0, 'ready', ?)",
[
$courseId,
$title,
$uploadResult['video_uuid'],
$uploadResult['local_path'],
$uploadResult['hls_url'],
$uploadResult['thumbnail_url'],
$uploadResult['duration'] ?? 0,
$videoUrl
]
[$courseId, $title, $uploadResult['video_uuid'], $uploadResult['local_path'], $uploadResult['hls_url'], $uploadResult['thumbnail_url'], $uploadResult['duration'] ?? 0, $videoUrl]
);
}
// 3. Trigger Autonomous Zero-Touch AI Analysis & Socratic Checkpoint Generation
\App\Services\AiVideoAnalyzerService::processLessonAutonomously($lessonId);
@@ -211,6 +211,25 @@ class CurriculumService
return $walk(self::getCurriculumTree());
}
/** Resolve a manifest file path to its canonical lesson record. */
public static function findLessonByFile(string $file): ?array
{
$walk = function ($node) use (&$walk, $file): ?array {
if (!is_array($node)) return null;
if (isset($node['lessons']) && is_array($node['lessons'])) {
foreach ($node['lessons'] as $lesson) {
if (is_array($lesson) && (string)($lesson['file'] ?? '') === $file) return $lesson;
}
}
foreach ($node as $value) {
$found = $walk($value);
if ($found !== null) return $found;
}
return null;
};
return $walk(self::getCurriculumTree());
}
/**
* Save/Update Complete Curriculum Tree Manifest
*/