Enhance video playback cubit resilience and K-12 curriculum flow
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
/// Model representing an In-Video Socratic Gatekeeping Checkpoint (فحص الفهم السقراطي)
|
||||
class SocraticCheckpointModel {
|
||||
final int id;
|
||||
final String questionText;
|
||||
final int timestampSeconds;
|
||||
final int rewindSecondsOnFail;
|
||||
final String hint;
|
||||
final String pedagogicalExplanation;
|
||||
final List<SocraticOptionModel> options;
|
||||
|
||||
const SocraticCheckpointModel({
|
||||
required this.id,
|
||||
required this.questionText,
|
||||
required this.timestampSeconds,
|
||||
this.rewindSecondsOnFail = 45,
|
||||
this.hint = '',
|
||||
this.pedagogicalExplanation = '',
|
||||
this.options = const [],
|
||||
});
|
||||
|
||||
factory SocraticCheckpointModel.fromJson(Map<String, dynamic> json) {
|
||||
final List<SocraticOptionModel> parsedOptions = [];
|
||||
if (json['options'] != null && json['options'] is List) {
|
||||
for (var opt in json['options']) {
|
||||
if (opt is Map) {
|
||||
parsedOptions.add(SocraticOptionModel.fromJson(Map<String, dynamic>.from(opt)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SocraticCheckpointModel(
|
||||
id: (json['id'] as num?)?.toInt() ?? 0,
|
||||
questionText: json['question']?.toString() ?? json['question_text']?.toString() ?? 'سؤال فحص الفهم السقراطي',
|
||||
timestampSeconds: (json['timestamp_seconds'] as num?)?.toInt() ?? 120,
|
||||
rewindSecondsOnFail: (json['rewind_seconds'] as num?)?.toInt() ?? 45,
|
||||
hint: json['hint']?.toString() ?? '',
|
||||
pedagogicalExplanation: json['explanation']?.toString() ?? '',
|
||||
options: parsedOptions,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Model representing a single choice option in a checkpoint
|
||||
class SocraticOptionModel {
|
||||
final int id;
|
||||
final String text;
|
||||
final bool isCorrect;
|
||||
|
||||
const SocraticOptionModel({
|
||||
required this.id,
|
||||
required this.text,
|
||||
required this.isCorrect,
|
||||
});
|
||||
|
||||
factory SocraticOptionModel.fromJson(Map<String, dynamic> json) {
|
||||
return SocraticOptionModel(
|
||||
id: (json['id'] as num?)?.toInt() ?? 0,
|
||||
text: json['text']?.toString() ?? json['option_text']?.toString() ?? '',
|
||||
isCorrect: (json['is_correct'] as bool?) ?? false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Model for Lesson Playback Data (Streams, Versions, Chapters, Checkpoints)
|
||||
class LessonPlaybackData {
|
||||
final int lessonId;
|
||||
final String title;
|
||||
final int durationSeconds;
|
||||
final String videoUrl;
|
||||
final String storageType;
|
||||
final List<LessonVersionModel> availableVersions;
|
||||
final List<SocraticCheckpointModel> checkpoints;
|
||||
|
||||
const LessonPlaybackData({
|
||||
required this.lessonId,
|
||||
required this.title,
|
||||
required this.durationSeconds,
|
||||
required this.videoUrl,
|
||||
this.storageType = 'api_upload',
|
||||
this.availableVersions = const [],
|
||||
this.checkpoints = const [],
|
||||
});
|
||||
|
||||
factory LessonPlaybackData.fromJson(Map<String, dynamic> json) {
|
||||
final lesson = json['lesson'] is Map ? json['lesson'] as Map : {};
|
||||
final playback = json['playback'] is Map ? json['playback'] as Map : {};
|
||||
|
||||
// Parse versions
|
||||
final List<LessonVersionModel> versions = [];
|
||||
if (json['available_versions'] is List) {
|
||||
for (var v in json['available_versions']) {
|
||||
if (v is Map) {
|
||||
versions.add(LessonVersionModel.fromJson(Map<String, dynamic>.from(v)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse checkpoints
|
||||
final List<SocraticCheckpointModel> points = [];
|
||||
if (json['checkpoints'] is List) {
|
||||
for (var c in json['checkpoints']) {
|
||||
if (c is Map) {
|
||||
points.add(SocraticCheckpointModel.fromJson(Map<String, dynamic>.from(c)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final vidUrl = playback['hls_url']?.toString() ??
|
||||
playback['stream_url']?.toString() ??
|
||||
playback['video_url']?.toString() ?? '';
|
||||
|
||||
return LessonPlaybackData(
|
||||
lessonId: (lesson['id'] as num?)?.toInt() ?? 0,
|
||||
title: lesson['title']?.toString() ?? 'الدرس التفاعلي',
|
||||
durationSeconds: (lesson['duration_seconds'] as num?)?.toInt() ?? 0,
|
||||
videoUrl: vidUrl,
|
||||
storageType: playback['storage_type']?.toString() ?? 'api_upload',
|
||||
availableVersions: versions,
|
||||
checkpoints: points,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Model for Teacher vs AI Lesson versions
|
||||
class LessonVersionModel {
|
||||
final int lessonId;
|
||||
final bool isAi;
|
||||
final String teacherName;
|
||||
final String schoolName;
|
||||
final String label;
|
||||
final bool isRecommended;
|
||||
final String videoUrl;
|
||||
|
||||
const LessonVersionModel({
|
||||
required this.lessonId,
|
||||
required this.isAi,
|
||||
required this.teacherName,
|
||||
required this.schoolName,
|
||||
required this.label,
|
||||
required this.isRecommended,
|
||||
required this.videoUrl,
|
||||
});
|
||||
|
||||
factory LessonVersionModel.fromJson(Map<String, dynamic> json) {
|
||||
final playback = json['playback'] is Map ? json['playback'] as Map : {};
|
||||
final vidUrl = playback['hls_url']?.toString() ??
|
||||
playback['stream_url']?.toString() ??
|
||||
playback['video_url']?.toString() ?? '';
|
||||
|
||||
return LessonVersionModel(
|
||||
lessonId: (json['lesson_id'] as num?)?.toInt() ?? 0,
|
||||
isAi: (json['is_ai'] as bool?) ?? false,
|
||||
teacherName: json['teacher_name']?.toString() ?? '',
|
||||
schoolName: json['school_name']?.toString() ?? '',
|
||||
label: json['label']?.toString() ?? 'شرح معتمد',
|
||||
isRecommended: (json['is_recommended'] as bool?) ?? false,
|
||||
videoUrl: vidUrl,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user