UI improved
This commit is contained in:
+130
-11
@@ -1,7 +1,11 @@
|
||||
<?php
|
||||
// GET api/library.php?view=home -> courses + continue-watching
|
||||
// GET api/library.php?view=folder&id=N -> folder contents + progress
|
||||
// GET api/library.php?view=video&id=N -> video + playlist + resume info
|
||||
// GET api/library.php?view=meta -> sidebar/header chrome: totals, shelves, last scan
|
||||
// GET api/library.php?view=home -> resume card, stats, in progress, recent additions
|
||||
// GET api/library.php?view=library -> every course, grouped into shelves
|
||||
// GET api/library.php?view=folder&id=N -> folder contents + progress
|
||||
// GET api/library.php?view=video&id=N -> video + playlist + resume info
|
||||
// GET api/library.php?view=search&q=text -> matching folders, videos, quizzes
|
||||
// GET api/library.php?view=settings -> library paths, thresholds, progress summary
|
||||
require_once __DIR__ . '/../includes/auth.php';
|
||||
require_once __DIR__ . '/../includes/functions.php';
|
||||
|
||||
@@ -53,19 +57,83 @@ function crumbList(array $folder): array
|
||||
);
|
||||
}
|
||||
|
||||
/** The three shelves the library groups courses into. */
|
||||
function shelveCourses(array $courses): array
|
||||
{
|
||||
$shelves = [
|
||||
['key' => 'progress', 'name' => 'In progress', 'courses' => []],
|
||||
['key' => 'finished', 'name' => 'Finished', 'courses' => []],
|
||||
['key' => 'fresh', 'name' => 'Not started', 'courses' => []],
|
||||
];
|
||||
foreach ($courses as $c) {
|
||||
foreach ($shelves as $i => $shelf) {
|
||||
if ($shelf['key'] === $c['state']) {
|
||||
$shelves[$i]['courses'][] = $c;
|
||||
}
|
||||
}
|
||||
}
|
||||
return array_values(array_filter($shelves, fn($s) => $s['courses']));
|
||||
}
|
||||
|
||||
$view = $_GET['view'] ?? 'home';
|
||||
|
||||
switch ($view) {
|
||||
case 'home':
|
||||
case 'meta':
|
||||
$courses = courseCards($userId);
|
||||
$counts = ['progress' => 0, 'finished' => 0, 'fresh' => 0];
|
||||
foreach ($courses as $c) {
|
||||
$counts[$c['state']]++;
|
||||
}
|
||||
echo json_encode([
|
||||
'courses' => array_map(fn($c) => folderSummary($c, $userId), childFolders(null)),
|
||||
'continue' => array_map(fn($r) => [
|
||||
'totals' => libraryTotals(),
|
||||
'shelves' => [
|
||||
['key' => 'progress', 'name' => 'In progress', 'count' => $counts['progress']],
|
||||
['key' => 'finished', 'name' => 'Finished', 'count' => $counts['finished']],
|
||||
['key' => 'fresh', 'name' => 'Not started', 'count' => $counts['fresh']],
|
||||
],
|
||||
'last_scan' => appMetaGet('last_scan_at'),
|
||||
'username' => currentUsername(),
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'home':
|
||||
$courses = courseCards($userId);
|
||||
$resume = continueWatching($userId, 1);
|
||||
$resumeCourseId = null;
|
||||
if ($resume) {
|
||||
$resumeFolder = getFolder((int)$resume[0]['folder_id']);
|
||||
if ($resumeFolder) $resumeCourseId = courseIdOf($resumeFolder);
|
||||
}
|
||||
echo json_encode([
|
||||
'stats' => userStats($userId, $courses),
|
||||
'resume' => $resume ? [
|
||||
'id' => (int)$resume[0]['id'],
|
||||
'course_id' => $resumeCourseId,
|
||||
'name' => $resume[0]['name'],
|
||||
'folder_path' => $resume[0]['folder_path'],
|
||||
'position' => (float)$resume[0]['position_seconds'],
|
||||
'duration' => $resume[0]['duration_seconds'] !== null
|
||||
? (float)$resume[0]['duration_seconds'] : null,
|
||||
] : null,
|
||||
'continue' => array_map(fn($r) => [
|
||||
'id' => (int)$r['id'],
|
||||
'name' => $r['name'],
|
||||
'folder_path' => $r['folder_path'],
|
||||
'position' => (float)$r['position_seconds'],
|
||||
'duration' => $r['duration_seconds'] !== null ? (float)$r['duration_seconds'] : null,
|
||||
], continueWatching($userId)),
|
||||
'in_progress' => array_values(array_filter($courses, fn($c) => $c['state'] === 'progress')),
|
||||
'recent' => recentCourses($userId),
|
||||
'courses' => $courses,
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'library':
|
||||
$courses = courseCards($userId);
|
||||
echo json_encode([
|
||||
'courses' => $courses,
|
||||
'shelves' => shelveCourses($courses),
|
||||
'totals' => libraryTotals(),
|
||||
]);
|
||||
break;
|
||||
|
||||
@@ -76,11 +144,15 @@ switch ($view) {
|
||||
echo json_encode(['error' => 'Folder not found']);
|
||||
break;
|
||||
}
|
||||
$crumbs = crumbList($folder);
|
||||
echo json_encode([
|
||||
'folder' => ['id' => (int)$folder['id'], 'name' => $folder['name']],
|
||||
'breadcrumbs' => crumbList($folder),
|
||||
'folder' => ['id' => (int)$folder['id'], 'name' => $folder['name'],
|
||||
'path' => $folder['path']],
|
||||
'course_id' => $crumbs ? $crumbs[0]['id'] : (int)$folder['id'],
|
||||
'breadcrumbs' => $crumbs,
|
||||
'rollup' => folderRollup($userId, $folder['path']),
|
||||
'progress' => folderProgress($userId, $folder['path']),
|
||||
'subfolders' => array_map(fn($f) => folderSummary($f, $userId),
|
||||
'subfolders' => array_map(fn($f) => courseSummary($userId, $f),
|
||||
childFolders((int)$folder['id'])),
|
||||
'videos' => array_map('videoSummary',
|
||||
folderVideos((int)$folder['id'], $userId)),
|
||||
@@ -98,6 +170,7 @@ switch ($view) {
|
||||
}
|
||||
$folder = getFolder((int)$video['folder_id']);
|
||||
$playlist = folderVideos((int)$folder['id'], $userId);
|
||||
$crumbs = crumbList($folder);
|
||||
|
||||
$stmt = db()->prepare(
|
||||
'SELECT position_seconds, completed FROM video_progress
|
||||
@@ -118,10 +191,14 @@ switch ($view) {
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'video' => ['id' => (int)$video['id'], 'name' => $video['name']],
|
||||
'video' => ['id' => (int)$video['id'], 'name' => $video['name'],
|
||||
'duration' => $video['duration_seconds'] !== null
|
||||
? (float)$video['duration_seconds'] : null],
|
||||
'folder' => ['id' => (int)$folder['id'], 'name' => $folder['name']],
|
||||
'breadcrumbs' => crumbList($folder),
|
||||
'course_id' => $crumbs ? $crumbs[0]['id'] : (int)$folder['id'],
|
||||
'breadcrumbs' => $crumbs,
|
||||
'playlist' => array_map('videoSummary', $playlist),
|
||||
'quizzes' => array_map('quizSummary', folderQuizzes((int)$folder['id'], $userId)),
|
||||
'resume_at' => $resumeAt,
|
||||
'prev_id' => $prevId,
|
||||
'next_id' => $nextId,
|
||||
@@ -129,6 +206,48 @@ switch ($view) {
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'search':
|
||||
$q = trim((string)($_GET['q'] ?? ''));
|
||||
if ($q === '') {
|
||||
echo json_encode(['query' => '', 'folders' => [], 'videos' => [], 'quizzes' => []]);
|
||||
break;
|
||||
}
|
||||
$withPath = fn(array $row, array $summary) =>
|
||||
$summary + ['folder_path' => $row['folder_path']];
|
||||
|
||||
echo json_encode([
|
||||
'query' => $q,
|
||||
'folders' => array_map(
|
||||
fn($f) => courseSummary($userId, $f),
|
||||
searchFolders($q)
|
||||
),
|
||||
'videos' => array_map(
|
||||
fn($v) => $withPath($v, videoSummary($v)),
|
||||
searchVideos($userId, $q)
|
||||
),
|
||||
'quizzes' => array_map(
|
||||
fn($q2) => $withPath($q2, quizSummary($q2)),
|
||||
searchQuizzes($userId, $q)
|
||||
),
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'settings':
|
||||
$courses = courseCards($userId);
|
||||
$withSome = array_values(array_filter($courses, fn($c) => $c['progress']['percent'] > 0));
|
||||
echo json_encode([
|
||||
'root' => rtrim(VIDEOS_ROOT, '/'),
|
||||
'root_exists' => is_dir(VIDEOS_ROOT),
|
||||
'totals' => libraryTotals(),
|
||||
'last_scan' => appMetaGet('last_scan_at'),
|
||||
'complete_threshold' => (int)round(100 * COMPLETE_THRESHOLD),
|
||||
'registration_open' => (bool)ALLOW_REGISTRATION,
|
||||
'username' => currentUsername(),
|
||||
'progress_courses' => count($withSome),
|
||||
'courses' => count($courses),
|
||||
]);
|
||||
break;
|
||||
|
||||
default:
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Unknown view']);
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
// POST api/reset.php -> delete the logged-in user's progress for a scope.
|
||||
// JSON body: {scope: all|folder|video|quiz, id?}
|
||||
// Only ever touches the current user's rows; the library itself is untouched.
|
||||
require_once __DIR__ . '/../includes/auth.php';
|
||||
require_once __DIR__ . '/../includes/functions.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
$userId = requireLoginApi();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method not allowed']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true) ?: [];
|
||||
$scope = (string)($data['scope'] ?? '');
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
|
||||
try {
|
||||
$counts = resetProgress($userId, $scope, $id);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode(['ok' => true, 'reset' => $counts]);
|
||||
Reference in New Issue
Block a user