'processing', 'progress' => 15, 'message' => 'جاري تحويل الفيديو إلى HLS...']); $file = (string)($task['file'] ?? ''); $manifestLesson = CurriculumService::findLessonByFile($file); $title = $manifestLesson['title'] ?? basename($file, '.md'); $existing = Database::selectOne('SELECT id, course_id FROM lessons WHERE curriculum_key = ? OR title = ? OR markdown_content LIKE ? LIMIT 1', [$file, $title, '%' . $file . '%']); $courseId = $existing ? (int)$existing['course_id'] : CurriculumService::getOrCreateSystemCourse(); $uploaded = VideoService::handleDirectUpload([ 'error' => UPLOAD_ERR_OK, 'tmp_name' => $task['video_path'], 'name' => $task['original_name'] ?? 'lesson.mp4', 'size' => filesize($task['video_path']) ], $courseId, $title); updateVideoTask($stateFile, ['progress' => 80, 'message' => 'تم رفع حزمة HLS إلى Cloudflare R2، جاري تثبيت الدرس...']); $videoUrl = $uploaded['hls_url'] ?? $uploaded['r2_url'] ?? null; if ($existing) { $lessonId = (int)$existing['id']; Database::query("UPDATE lessons SET curriculum_key = ?, 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 = ?", [$file, $uploaded['video_uuid'], $uploaded['local_path'], $uploaded['hls_url'], $uploaded['thumbnail_url'], $uploaded['duration'] ?? 0, $videoUrl, $lessonId]); } else { $lessonId = Database::insert("INSERT INTO lessons (course_id, title, curriculum_key, 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, $file, $uploaded['video_uuid'], $uploaded['local_path'], $uploaded['hls_url'], $uploaded['thumbnail_url'], $uploaded['duration'] ?? 0, $videoUrl]); } try { AiVideoAnalyzerService::processLessonAutonomously((int)$lessonId); } catch (Throwable $e) { error_log('Video AI analysis deferred: ' . $e->getMessage()); } $assets = CurriculumService::getLessonAiAssets($file); $assets['ai_video_url'] = $videoUrl; CurriculumService::saveLessonAiAssets($file, $assets); updateVideoTask($stateFile, ['status' => 'completed', 'progress' => 100, 'message' => 'تم رفع الفيديو وربطه بالدرس بنجاح.', 'lesson_id' => (int)$lessonId, 'video_url' => $videoUrl]); } catch (Throwable $e) { if (!empty($task['video_path']) && is_file($task['video_path'])) @unlink($task['video_path']); updateVideoTask($stateFile, ['status' => 'error', 'progress' => 100, 'message' => $e->getMessage()]); }