Update Saqel Platform: 2026-09-09 00:12:31
This commit is contained in:
@@ -42,6 +42,12 @@ class TeacherLessonAuditModel {
|
|||||||
final readiness = (json['pedagogical_readiness_score'] as num?)?.toInt();
|
final readiness = (json['pedagogical_readiness_score'] as num?)?.toInt();
|
||||||
final visual = (json['visual_clarity_score'] as num?)?.toInt();
|
final visual = (json['visual_clarity_score'] as num?)?.toInt();
|
||||||
final decisionValue = json['decision']?.toString() ?? 'pending';
|
final decisionValue = json['decision']?.toString() ?? 'pending';
|
||||||
|
|
||||||
|
final ai = json['ai_analysis'] is Map ? Map<String, dynamic>.from(json['ai_analysis'] as Map) : null;
|
||||||
|
final qa = ai != null && ai['quality_assessment'] is Map ? Map<String, dynamic>.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(
|
return TeacherLessonAuditModel(
|
||||||
lessonTitle: json['lesson_title'] ?? '',
|
lessonTitle: json['lesson_title'] ?? '',
|
||||||
subject: json['subject'] ?? '',
|
subject: json['subject'] ?? '',
|
||||||
@@ -50,15 +56,16 @@ class TeacherLessonAuditModel {
|
|||||||
durationGatePassed: json['duration_gate_passed'] ??
|
durationGatePassed: json['duration_gate_passed'] ??
|
||||||
(durationSeconds > 0 && durationSeconds <= 1500),
|
(durationSeconds > 0 && durationSeconds <= 1500),
|
||||||
durationWarning: json['duration_warning'],
|
durationWarning: json['duration_warning'],
|
||||||
qualityScore: (json['quality_score'] as num?)?.toInt() ?? readiness ?? 0,
|
qualityScore: (qa?['clarity_score'] as num?)?.toInt() ?? (json['quality_score'] as num?)?.toInt() ?? readiness ?? 0,
|
||||||
approvalStatus: json['approval_status'] ?? decisionValue,
|
approvalStatus: qa?['status']?.toString() ?? json['approval_status'] ?? decisionValue,
|
||||||
curriculumAlignment:
|
curriculumAlignment: alignScore != null
|
||||||
json['curriculum_alignment'] ?? 'بانتظار تحليل الملف الفعلي',
|
? 'تطابق معتمد بنسبة $alignScore% مع المنهاج الرسمي'
|
||||||
|
: (json['curriculum_alignment'] ?? 'بانتظار تحليل الملف الفعلي'),
|
||||||
audioClarity: json['audio_clarity'] ??
|
audioClarity: json['audio_clarity'] ??
|
||||||
(visual == null
|
(visual == null
|
||||||
? 'تم التحقق من وجود مسار صوت فعلي'
|
? 'تم التحقق من وجود مسار صوت فعلي'
|
||||||
: 'وضوح الصورة $visual%، وتم التحقق من وجود الصوت'),
|
: 'وضوح الصورة $visual%، وتم التحقق من وجود الصوت'),
|
||||||
socraticStopsCount: (json['socratic_stops_count'] as num?)?.toInt() ?? 0,
|
socraticStopsCount: cpCount,
|
||||||
decision: json['report'] ?? json['reason'] ?? decisionValue,
|
decision: json['report'] ?? json['reason'] ?? decisionValue,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -321,8 +321,12 @@ class TeacherStudioCubit extends Cubit<TeacherStudioState> {
|
|||||||
final report = data['preflight_report'] is Map
|
final report = data['preflight_report'] is Map
|
||||||
? Map<String, dynamic>.from(data['preflight_report'] as Map)
|
? Map<String, dynamic>.from(data['preflight_report'] as Map)
|
||||||
: <String, dynamic>{};
|
: <String, dynamic>{};
|
||||||
|
final aiAnalysis = data['ai_analysis'] is Map
|
||||||
|
? Map<String, dynamic>.from(data['ai_analysis'] as Map)
|
||||||
|
: <String, dynamic>{};
|
||||||
final result = TeacherLessonAuditModel.fromJson({
|
final result = TeacherLessonAuditModel.fromJson({
|
||||||
...report,
|
...report,
|
||||||
|
'ai_analysis': aiAnalysis,
|
||||||
'lesson_title': state.lessonTitle,
|
'lesson_title': state.lessonTitle,
|
||||||
'subject': selectedSubjectName
|
'subject': selectedSubjectName
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -551,7 +551,9 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
|
|||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(
|
Text(
|
||||||
isApproved
|
isApproved
|
||||||
? 'جاهزة لرفع الملف والتحليل'
|
? (state.isUploaded
|
||||||
|
? 'تم اعتماد ونشر الحصة بنجاح 🎖️'
|
||||||
|
: 'جاهزة لرفع الملف والتحليل')
|
||||||
: 'بيانات الحصة بحاجة للمراجعة',
|
: 'بيانات الحصة بحاجة للمراجعة',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
|
|||||||
@@ -267,8 +267,15 @@ class VideoService
|
|||||||
@shell_exec($thumbCmd);
|
@shell_exec($thumbCmd);
|
||||||
|
|
||||||
// 2. Generate HLS Segments & Playlist (6-second chunks for fast start)
|
// 2. Generate HLS Segments & Playlist (6-second chunks for fast start)
|
||||||
|
// 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";
|
$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);
|
@shell_exec($hlsCmd);
|
||||||
|
}
|
||||||
|
|
||||||
$hlsUrl = '/api/videos/hls/' . $videoUuid . '/index.m3u8';
|
$hlsUrl = '/api/videos/hls/' . $videoUuid . '/index.m3u8';
|
||||||
$thumbUrl = '/api/videos/hls/' . $videoUuid . '/thumbnail.jpg';
|
$thumbUrl = '/api/videos/hls/' . $videoUuid . '/thumbnail.jpg';
|
||||||
|
|||||||
Reference in New Issue
Block a user