feat: STEM Virtual Lab, English TTS companion, dynamic textbooks and zero-demo video fallback

This commit is contained in:
Hamza-Ayed
2026-09-03 17:37:15 +03:00
parent 445a1aa0e3
commit 58c3c48f69
19 changed files with 2162 additions and 258 deletions
@@ -201,6 +201,7 @@ class CurriculumLessonItemModel {
final int durationSeconds;
final int checkpointsCount;
final bool isCompleted;
final bool hasVideo;
const CurriculumLessonItemModel({
required this.id,
@@ -210,6 +211,7 @@ class CurriculumLessonItemModel {
this.durationSeconds = 1200, // 20 mins default
this.checkpointsCount = 3,
this.isCompleted = false,
this.hasVideo = false,
});
factory CurriculumLessonItemModel.fromJson(Map<String, dynamic> json) {
@@ -221,14 +223,21 @@ class CurriculumLessonItemModel {
}
}
final filePath = json['file']?.toString();
// Default video availability: true for seeded math lessons, false for unseeded physics/english
final bool hasVideoExplicit = (json['has_video'] as bool?) ??
((json['video_url'] != null && json['video_url'].toString().isNotEmpty) ||
(filePath != null && filePath.contains('math_10/semester_1/unit_01/lesson_01')));
return CurriculumLessonItemModel(
id: json['id']?.toString() ?? 'lesson_${DateTime.now().millisecondsSinceEpoch}',
title: json['title']?.toString() ?? 'درس بدون عنوان',
outcomes: outs,
markdownFilePath: json['file']?.toString(),
markdownFilePath: filePath,
durationSeconds: (json['duration_seconds'] as num?)?.toInt() ?? 1200,
checkpointsCount: (json['checkpoints_count'] as num?)?.toInt() ?? 3,
isCompleted: (json['is_completed'] as bool?) ?? false,
hasVideo: hasVideoExplicit,
);
}
}