feat: STEM Virtual Lab, English TTS companion, dynamic textbooks and zero-demo video fallback
This commit is contained in:
@@ -85,6 +85,7 @@ class LessonPlaybackData {
|
||||
final String storageType;
|
||||
final List<LessonVersionModel> availableVersions;
|
||||
final List<SocraticCheckpointModel> checkpoints;
|
||||
final int? lastPositionSeconds;
|
||||
|
||||
const LessonPlaybackData({
|
||||
required this.lessonId,
|
||||
@@ -94,6 +95,7 @@ class LessonPlaybackData {
|
||||
this.storageType = 'api_upload',
|
||||
this.availableVersions = const [],
|
||||
this.checkpoints = const [],
|
||||
this.lastPositionSeconds,
|
||||
});
|
||||
|
||||
factory LessonPlaybackData.fromJson(Map<String, dynamic> json) {
|
||||
@@ -124,6 +126,8 @@ class LessonPlaybackData {
|
||||
playback['hls_url']?.toString() ?? playback['stream_url']?.toString() ?? playback['video_url']?.toString() ?? '',
|
||||
);
|
||||
|
||||
final lastPos = (json['last_position_seconds'] ?? playback['last_position_seconds'] ?? json['progress']?['position_seconds']) as num?;
|
||||
|
||||
return LessonPlaybackData(
|
||||
lessonId: (lesson['id'] as num?)?.toInt() ?? 0,
|
||||
title: lesson['title']?.toString() ?? 'الدرس التفاعلي',
|
||||
@@ -132,6 +136,7 @@ class LessonPlaybackData {
|
||||
storageType: playback['storage_type']?.toString() ?? 'api_upload',
|
||||
availableVersions: versions,
|
||||
checkpoints: points,
|
||||
lastPositionSeconds: lastPos?.toInt(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user