Для чего
Курсы с Udemy и Coursera, записи конференций и воркшопов часто лежат на диске обычными папками. Course Shelf сканирует такую папку и собирает из неё каталог курсов, секций и уроков. Каталог открывается в браузере и в мобильном приложении. Файлы остаются на месте: Course Shelf их не копирует и не перекодирует.
Библиотека
- Каталог из структуры папок. Папки и имена файлов задают курсы, секции и порядок уроков. Скан привязывает к урокам субтитры и материалы, которые лежат рядом с видео.
course.jsonдля точной настройки. Файл в папке курса задаёт названия, порядок уроков и метаданные поверх того, что вывел скан.- Метаданные из сети. Скраперы берут обложки, описания, авторов и теги с Udemy, Coursera и YouTube. Для других сайтов подходят декларативные определения скраперов и общий разбор schema.org и OpenGraph.
- Пересканирование курса. Один курс обновляется без прогона всей библиотеки.
Просмотр
- Стриминг без перекодирования. Видео идёт по HTTP с byte-range, перемотка срабатывает сразу.
- Прогресс. Плеер запоминает позицию, продолжает с неё и переходит к следующему уроку. После 90% урок отмечается пройденным.
- Главная показывает начатые уроки, свежие поступления, завершённые курсы и статистику просмотра за неделю.
- Каталог с фильтрами по статусу, библиотеке, длительности и автору. Фильтры хранятся в адресной строке, и ссылка открывает ту же выборку.
- Заметки и закладки. К каждому уроку можно вести заметку в Markdown и ставить закладки на нужную секунду. Заметки видит только их автор.
Транскрипты и поиск
- Локальная расшифровка. whisper.cpp расшифровывает уроки без субтитров на том же сервере, без облачных API.
- Поиск находит курсы и отдельные уроки одним запросом и показывает фрагмент с найденным местом. На десктопе он открывается командной палитрой.
- Транскрипт рядом с плеером. Ссылка с
?t=открывает урок на нужной секунде. - Квизы. Вопросы по уроку генерирует LLM: локальный llama.cpp или модель через OpenRouter.
Мобильное приложение
- Flutter-клиент для iOS и Android с тем же каталогом.
- Загрузки. Уроки скачиваются с докачкой после обрыва и хранятся на устройстве в зашифрованном виде.
- Офлайн. Позиция, заметки и закладки сохраняются на устройстве и уходят на сервер, когда появляется связь. Если на сервере прогресс новее, остаётся серверная версия.
Несколько пользователей
- Доступ по выдаче. Роли USER и ADMIN, доступ выдаётся на библиотеку целиком или на отдельный курс.
- Администрирование. Сводка по инстансу, управление пользователями и вход от имени пользователя, чтобы увидеть его каталог.
- Бэкап. Одна кнопка запускает
pg_dumpи отдаёт ссылку на архив. Медиафайлы в бэкап не входят: Course Shelf ими не владеет.
Под капотом
Монорепо из трёх приложений: бэкенд на NestJS, веб на Nuxt и клиент на Flutter. Контракт API описан один раз, в OpenAPI 3.1 и AsyncAPI 3.0, и клиенты для TypeScript и Dart генерируются из него. Бэкенд отклоняет запросы, которых нет в спецификации, а CI падает, если код разошёлся с контрактом. События скана и фоновых задач приходят в клиенты через Centrifugo. Интерфейс переведён на русский и английский.
Общий каркас Course Shelf, Coriolis и burkmak разобран в посте «Контракт, от которого код не может уйти».
История первого импорта настоящей библиотеки — в посте «26% уроков пропали молча».
Установка
Релизы выходят Docker-образами в GHCR, стек поднимается через Docker Compose.
В релизный архив входит агент-установщик для Claude Code: распакуйте архив,
запустите claude в каталоге, и агент поднимет стек в диалоге.



