diff --git a/apps/teacher_app/lib/data/models/teacher_models.dart b/apps/teacher_app/lib/data/models/teacher_models.dart index 32c509b..e04ac4e 100644 --- a/apps/teacher_app/lib/data/models/teacher_models.dart +++ b/apps/teacher_app/lib/data/models/teacher_models.dart @@ -42,6 +42,12 @@ class TeacherLessonAuditModel { final readiness = (json['pedagogical_readiness_score'] as num?)?.toInt(); final visual = (json['visual_clarity_score'] as num?)?.toInt(); final decisionValue = json['decision']?.toString() ?? 'pending'; + + final ai = json['ai_analysis'] is Map ? Map.from(json['ai_analysis'] as Map) : null; + final qa = ai != null && ai['quality_assessment'] is Map ? Map.from(ai['quality_assessment'] as Map) : null; + final cpCount = (ai?['checkpoints_count'] as num?)?.toInt() ?? (json['socratic_stops_count'] as num?)?.toInt() ?? 0; + final alignScore = (qa?['alignment_score'] as num?)?.toInt(); + return TeacherLessonAuditModel( lessonTitle: json['lesson_title'] ?? '', subject: json['subject'] ?? '', @@ -50,15 +56,16 @@ class TeacherLessonAuditModel { durationGatePassed: json['duration_gate_passed'] ?? (durationSeconds > 0 && durationSeconds <= 1500), durationWarning: json['duration_warning'], - qualityScore: (json['quality_score'] as num?)?.toInt() ?? readiness ?? 0, - approvalStatus: json['approval_status'] ?? decisionValue, - curriculumAlignment: - json['curriculum_alignment'] ?? 'بانتظار تحليل الملف الفعلي', + qualityScore: (qa?['clarity_score'] as num?)?.toInt() ?? (json['quality_score'] as num?)?.toInt() ?? readiness ?? 0, + approvalStatus: qa?['status']?.toString() ?? json['approval_status'] ?? decisionValue, + curriculumAlignment: alignScore != null + ? 'تطابق معتمد بنسبة $alignScore% مع المنهاج الرسمي' + : (json['curriculum_alignment'] ?? 'بانتظار تحليل الملف الفعلي'), audioClarity: json['audio_clarity'] ?? (visual == null ? 'تم التحقق من وجود مسار صوت فعلي' : 'وضوح الصورة $visual%، وتم التحقق من وجود الصوت'), - socraticStopsCount: (json['socratic_stops_count'] as num?)?.toInt() ?? 0, + socraticStopsCount: cpCount, decision: json['report'] ?? json['reason'] ?? decisionValue, ); } diff --git a/apps/teacher_app/lib/logic/cubits/teacher_studio_cubit.dart b/apps/teacher_app/lib/logic/cubits/teacher_studio_cubit.dart index 9261c9e..05880cf 100644 --- a/apps/teacher_app/lib/logic/cubits/teacher_studio_cubit.dart +++ b/apps/teacher_app/lib/logic/cubits/teacher_studio_cubit.dart @@ -321,8 +321,12 @@ class TeacherStudioCubit extends Cubit { final report = data['preflight_report'] is Map ? Map.from(data['preflight_report'] as Map) : {}; + final aiAnalysis = data['ai_analysis'] is Map + ? Map.from(data['ai_analysis'] as Map) + : {}; final result = TeacherLessonAuditModel.fromJson({ ...report, + 'ai_analysis': aiAnalysis, 'lesson_title': state.lessonTitle, 'subject': selectedSubjectName }); diff --git a/apps/teacher_app/lib/presentation/screens/tabs/teacher_studio_upload_tab.dart b/apps/teacher_app/lib/presentation/screens/tabs/teacher_studio_upload_tab.dart index 6d77f5b..11833d3 100644 --- a/apps/teacher_app/lib/presentation/screens/tabs/teacher_studio_upload_tab.dart +++ b/apps/teacher_app/lib/presentation/screens/tabs/teacher_studio_upload_tab.dart @@ -551,7 +551,9 @@ class _TeacherStudioUploadTabState extends State { const SizedBox(width: 8), Text( isApproved - ? 'جاهزة لرفع الملف والتحليل' + ? (state.isUploaded + ? 'تم اعتماد ونشر الحصة بنجاح 🎖️' + : 'جاهزة لرفع الملف والتحليل') : 'بيانات الحصة بحاجة للمراجعة', style: TextStyle( fontSize: 15, diff --git a/backend/app/Services/VideoService.php b/backend/app/Services/VideoService.php index 24a969c..758cbaa 100644 --- a/backend/app/Services/VideoService.php +++ b/backend/app/Services/VideoService.php @@ -267,8 +267,15 @@ class VideoService @shell_exec($thumbCmd); // 2. Generate HLS Segments & Playlist (6-second chunks for fast start) - $hlsCmd = "{$ffmpeg} -i " . escapeshellarg($sourceMp4Path) . " -codec:v libx264 -crf 23 -preset veryfast -codec:a aac -b:a 128k -hls_time 6 -hls_list_size 0 -hls_segment_filename " . escapeshellarg($segmentPattern) . " " . escapeshellarg($playlistPath) . " -y 2>/dev/null"; - @shell_exec($hlsCmd); + // Try ultra-fast stream copy first (takes 2-4s instead of 4 minutes of CPU encoding) + $hlsFastCmd = "{$ffmpeg} -i " . escapeshellarg($sourceMp4Path) . " -c copy -hls_time 6 -hls_list_size 0 -hls_segment_filename " . escapeshellarg($segmentPattern) . " " . escapeshellarg($playlistPath) . " -y 2>/dev/null"; + @shell_exec($hlsFastCmd); + + // Fallback to transcoding only if stream copy failed to produce playlist + if (!file_exists($playlistPath)) { + $hlsCmd = "{$ffmpeg} -i " . escapeshellarg($sourceMp4Path) . " -codec:v libx264 -crf 23 -preset veryfast -codec:a aac -b:a 128k -hls_time 6 -hls_list_size 0 -hls_segment_filename " . escapeshellarg($segmentPattern) . " " . escapeshellarg($playlistPath) . " -y 2>/dev/null"; + @shell_exec($hlsCmd); + } $hlsUrl = '/api/videos/hls/' . $videoUuid . '/index.m3u8'; $thumbUrl = '/api/videos/hls/' . $videoUuid . '/thumbnail.jpg';