fix(playback): isolate curriculum keys and prevent non-uploaded lessons from falling back to previous video

This commit is contained in:
Hamza-Ayed
2026-09-09 01:33:56 +03:00
parent a4d6e34a40
commit 78ad245ee5
4 changed files with 196 additions and 77 deletions
+41 -52
View File
@@ -533,14 +533,23 @@ class VideoController
// 1. Match by curriculum_key if provided
if ($curriculumKey !== '') {
$baseKey = basename($curriculumKeyNoExt);
$matched = Database::select(
"SELECT * FROM lessons
WHERE curriculum_key = ? OR curriculum_key = ?
OR curriculum_key LIKE ? OR local_path LIKE ? OR markdown_content LIKE ?
ORDER BY (hls_url IS NOT NULL AND hls_url != '') DESC, id DESC LIMIT 5",
[$curriculumKey, $curriculumKeyNoExt, '%' . $baseKey . '%', '%' . $curriculumKeyNoExt . '%', '%' . $curriculumKeyNoExt . '%']
);
if (str_contains($curriculumKeyNoExt, '/')) {
// Structured hierarchical key: match exact or qualified path suffix
$matched = Database::select(
"SELECT * FROM lessons
WHERE curriculum_key = ? OR curriculum_key = ?
OR curriculum_key LIKE ? OR local_path LIKE ? OR markdown_content LIKE ?
ORDER BY (hls_url IS NOT NULL AND hls_url != '') DESC, id DESC LIMIT 5",
[$curriculumKey, $curriculumKeyNoExt, "%{$curriculumKeyNoExt}%", "%{$curriculumKeyNoExt}%", "%{$curriculumKeyNoExt}%"]
);
} else {
$matched = Database::select(
"SELECT * FROM lessons
WHERE curriculum_key = ? OR curriculum_key LIKE ?
ORDER BY (hls_url IS NOT NULL AND hls_url != '') DESC, id DESC LIMIT 5",
[$curriculumKey, "%/{$curriculumKeyNoExt}%"]
);
}
if (!empty($matched)) {
$candidates = array_merge($candidates, $matched);
}
@@ -549,14 +558,16 @@ class VideoController
// 2. Match by title if provided
if (!empty($title)) {
$cleanTitle = trim(preg_replace('/^(الدرس\s*\d+:\s*|معملُ\s*[^:]+:\s*|مقدمة\s*[^:]+:\s*)/u', '', $title));
$matchedTitle = Database::select(
"SELECT * FROM lessons
WHERE title = ? OR title LIKE ? OR title LIKE ?
ORDER BY (hls_url IS NOT NULL AND hls_url != '') DESC, id DESC LIMIT 5",
[$title, "%{$title}%", "%{$cleanTitle}%"]
);
if (!empty($matchedTitle)) {
$candidates = array_merge($candidates, $matchedTitle);
if (mb_strlen($cleanTitle) >= 6) {
$matchedTitle = Database::select(
"SELECT * FROM lessons
WHERE title = ? OR title LIKE ?
ORDER BY (hls_url IS NOT NULL AND hls_url != '') DESC, id DESC LIMIT 5",
[$title, "%{$cleanTitle}%"]
);
if (!empty($matchedTitle)) {
$candidates = array_merge($candidates, $matchedTitle);
}
}
}
@@ -570,28 +581,17 @@ class VideoController
// 4. Manifest slug lookup
if (empty($candidates) && !empty($rawId)) {
$rawIdNoExt = preg_replace('/\.md$/i', '', $rawId);
$matchedRaw = Database::select(
"SELECT * FROM lessons
WHERE curriculum_key = ? OR curriculum_key = ? OR title LIKE ? OR local_path LIKE ? OR markdown_content LIKE ?
ORDER BY (hls_url IS NOT NULL AND hls_url != '') DESC, id DESC LIMIT 5",
[$rawId, $rawIdNoExt, "%{$rawId}%", "%{$rawIdNoExt}%", "%{$rawIdNoExt}%"]
);
if (!empty($matchedRaw)) {
$candidates = array_merge($candidates, $matchedRaw);
}
if (empty($candidates)) {
$manifestLesson = CurriculumService::findLessonById($rawId);
if ($manifestLesson && !empty($manifestLesson['title'])) {
$matchedManifest = Database::select(
"SELECT * FROM lessons
WHERE title = ? OR title LIKE ? OR markdown_content LIKE ?
ORDER BY (hls_url IS NOT NULL AND hls_url != '') DESC, id DESC LIMIT 5",
[$manifestLesson['title'], '%' . $manifestLesson['title'] . '%', '%' . ($manifestLesson['file'] ?? '') . '%']
);
if (!empty($matchedManifest)) {
$candidates = array_merge($candidates, $matchedManifest);
}
$manifestLesson = CurriculumService::findLessonById($rawId);
if ($manifestLesson && !empty($manifestLesson['file'])) {
$fileNoExt = preg_replace('/\.md$/i', '', $manifestLesson['file']);
$matchedManifest = Database::select(
"SELECT * FROM lessons
WHERE curriculum_key = ? OR curriculum_key = ? OR local_path LIKE ?
ORDER BY (hls_url IS NOT NULL AND hls_url != '') DESC, id DESC LIMIT 5",
[$manifestLesson['file'], $fileNoExt, "%{$fileNoExt}%"]
);
if (!empty($matchedManifest)) {
$candidates = array_merge($candidates, $matchedManifest);
}
}
}
@@ -604,24 +604,13 @@ class VideoController
break;
}
}
if (!$lesson) {
$lesson = $candidates[0];
}
}
if (!$lesson) {
// Fallback to latest available lesson in DB with a video
$lesson = Database::selectOne(
"SELECT * FROM lessons
WHERE hls_url IS NOT NULL AND hls_url != ''
ORDER BY id DESC LIMIT 1"
);
}
if (!$lesson) {
// Strict verification: if no matching lesson with an uploaded video is found, return 404
if (!$lesson || empty($lesson['hls_url'])) {
$response->status(404)->json([
'status' => 'error',
'message' => 'الدرس غير موجود أو لم يتم نشر الفيديو الخاص به بعد'
'message' => 'لم يتم رفع ونشر فيديو شرح لهذا الدرس بعد.'
]);
return;
}
+17 -6
View File
@@ -224,12 +224,23 @@ class CurriculumService
$dbTitle = (string)($dbl['title'] ?? '');
$matched = false;
if ($currKey !== '' && ($currKey === $lessonFile || $currKeyNoExt === $lessonFileNoExt)) {
$matched = true;
} elseif ($currKeyNoExt !== '' && str_ends_with($currKeyNoExt, $lessonId)) {
$matched = true;
} elseif ($dbTitle !== '' && ($dbTitle === $lessonTitle || str_contains($lessonTitle, $dbTitle) || str_contains($dbTitle, $lessonTitle))) {
$matched = true;
if ($currKeyNoExt !== '' && $lessonFileNoExt !== '') {
if ($currKeyNoExt === $lessonFileNoExt) {
$matched = true;
} elseif (str_contains($currKeyNoExt, '/') && (
str_ends_with($lessonFileNoExt, '/' . ltrim($currKeyNoExt, '/')) ||
str_ends_with($currKeyNoExt, '/' . ltrim($lessonFileNoExt, '/'))
)) {
$matched = true;
}
}
if (!$matched && $dbTitle !== '' && $lessonTitle !== '') {
$normDb = preg_replace('/[\s\p{P}]+/u', '', mb_strtolower($dbTitle));
$normLes = preg_replace('/[\s\p{P}]+/u', '', mb_strtolower($lessonTitle));
if ($normDb === $normLes && mb_strlen($normDb) > 8) {
$matched = true;
}
}
if ($matched) {