Files
saqel/backend/scripts/video_upload_worker.php
T

60 lines
3.9 KiB
PHP

<?php
require_once __DIR__ . '/../app/bootstrap.php';
use App\Core\Database;
use App\Services\CurriculumService;
use App\Services\VideoService;
use App\Services\AiVideoAnalyzerService;
$taskId = $argv[1] ?? '';
$dir = __DIR__ . '/../storage/curriculum/processing';
$stateFile = $dir . '/' . basename($taskId) . '.json';
function updateVideoTask(string $file, array $changes): void {
$state = file_exists($file) ? (json_decode(file_get_contents($file), true) ?: []) : [];
file_put_contents($file, json_encode(array_merge($state, $changes), JSON_UNESCAPED_UNICODE));
}
if ($taskId === '' || !file_exists($stateFile)) exit(1);
$task = json_decode(file_get_contents($stateFile), true) ?: [];
// Legacy worker has no teacher_submission/video_version binding and therefore
// cannot prove which curriculum lesson or candidate it is publishing. Keep the
// uploaded file for an operator; do not create or overwrite a lesson by title.
updateVideoTask($stateFile, [
'status' => 'needs_submission_pipeline',
'progress' => 0,
'message' => 'هذا العامل موقوف حتى يمرر مسار الإرسال نسخة فيديو مرتبطة بالدرس المنهجي.',
]);
exit(2);
try {
updateVideoTask($stateFile, ['status' => '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()]);
}