Web-сервис для просмотра локальной библиотеки книг по INPX-индексу с поддержкой OPDS и встроенным ридером.
- 📚 Парсинг INPX — поддержка индексных файлов библиотек
- 🔍 Полнотекстовый поиск — SQLite FTS5 по названию, серии, авторам и аннотации
- 🌐 Web-интерфейс — современный SPA на Vue.js
- 📱 Адаптивный дизайн — работает на мобильных устройствах
- 📖 OPDS каталог — совместимость с читалками (FBReader, KyBook, Moon+ Reader и др.)
- 🗂️ Фильтрация — по жанрам, сериям, авторам, годам
- ⚡ Быстрая работа — SQLite база данных с индексами
- 📕 Встроенный ридер FB2 — чтение книг прямо в браузере с тремя режимами отображения
- 💾 Запоминание позиции — автоматическое сохранение и восстановление места чтения
- 📊 История чтения — отслеживание прогресса, статусы «Читаю» и «Прочитано», автоопределение завершения
- 🔊 Озвучка текста (TTS) — синтез речи через Silero TTS: чтение с текущей позиции, выбор голоса, регулировка скорости, непрерывное чтение
- 🔐 Аутентификация — опциональная многопользовательская авторизация с разделением истории чтения по пользователям
# Скопируйте пример конфигурации
cp .env.example .env
# Отредактируйте .env — укажите путь к библиотеке и имя INPX файла
nano .envВ .env задаются пути к библиотеке и основные параметры:
# Путь на хосте к папке с книгами (монтируется в контейнер)
LIBRARY_PATH=./books
# Имя файла индекса INPX внутри папки с книгами
INPX_FILE=test_library.inpxDocker Compose автоматически подхватывает .env файл и подставляет переменные.
# Запустите контейнер
docker compose -f docker-compose.dev.yaml up -d --buildСервис будет доступен по адресу http://localhost:9090:
- Web интерфейс: http://localhost:9090/
- API: http://localhost:9090/api/v1/books
- OPDS каталог: http://localhost:9090/opds
Pushkinlib поддерживает опциональную многопользовательскую авторизацию. По умолчанию авторизация выключена — сервис работает без логина, история чтения общая.
При включении авторизации:
- появляется экран логина
- история чтения и позиции привязываются к пользователю
- OPDS-каталог защищается HTTP Basic Auth (для читалок)
- каталог книг, поиск и содержимое книг остаются публичными
Секрет нужен для подписи cookie. Без него сессии не переживут перезапуск контейнера.
# Linux / macOS
openssl rand -hex 32
# или
head -c 32 /dev/urandom | base64Сохраните полученную строку — она понадобится на следующем шаге.
Откройте ваш docker-compose.yaml (или .env файл) и добавьте четыре переменные:
services:
pushkinlib:
environment:
# ... существующие переменные (PORT, BOOKS_DIR и т.д.) ...
# Включить авторизацию
- AUTH_ENABLED=true
# Логин администратора (по умолчанию: admin)
- ADMIN_USER=admin
# Пароль администратора — ОБЯЗАТЕЛЬНО замените!
- ADMIN_PASS=MyStr0ng_Passw0rd!
# Секрет сессий (из шага 1)
- SESSION_SECRET=вставьте-сюда-строку-из-шага-1Если запускаете без Docker, добавьте те же переменные в .env:
AUTH_ENABLED=true
ADMIN_USER=admin
ADMIN_PASS=MyStr0ng_Passw0rd!
SESSION_SECRET=вставьте-сюда-строку-из-шага-1# Docker Compose
docker compose up -d
# или из исходников
./pushkinlibВ логах вы увидите:
Auth enabled, ensuring admin user exists...
Admin user "admin" created successfully
Если пользователь admin уже существовал, пароль будет обновлён.
Откройте http://localhost:9090 — вы увидите экран логина:
- Введите логин (
admin) и пароль, заданный вADMIN_PASS - Нажмите Войти
- После успешного входа — библиотека, имя пользователя в шапке и кнопка Выйти
Сессия хранится в httpOnly cookie и действует 30 дней.
В шапке сайта нажмите Пользователи (кнопка видна только администраторам).
В админ-панели можно:
- Создать пользователя — заполните логин, пароль (от 6 символов), опционально отображаемое имя и флаг «Администратор»
- Удалить пользователя (кроме самого себя)
- Сменить пароль любому пользователю
Те же операции доступны через API:
# Список пользователей
curl -b cookies.txt http://localhost:9090/api/v1/admin/users
# Создание пользователя
curl -b cookies.txt -X POST http://localhost:9090/api/v1/admin/users \
-H 'Content-Type: application/json' \
-d '{"username":"alice","password":"secret123","display_name":"Алиса"}'
# Смена пароля
curl -b cookies.txt -X PUT http://localhost:9090/api/v1/admin/users/{id}/password \
-H 'Content-Type: application/json' \
-d '{"password":"newpass789"}'
# Удаление пользователя
curl -b cookies.txt -X DELETE http://localhost:9090/api/v1/admin/users/{id}При включённой авторизации OPDS-каталог требует HTTP Basic Auth. В настройках вашей читалки:
| Параметр | Значение |
|---|---|
| URL каталога | http://ваш-сервер:9090/opds |
| Логин | admin (или другой ADMIN_USER) |
| Пароль | тот же, что в ADMIN_PASS |
Проверенные читалки: KOReader, Moon+ Reader, FBReader, KyBook, Bookari.
| Переменная | По умолчанию | Описание |
|---|---|---|
LIBRARY_PATH |
./books |
Путь на хосте к папке с книгами (для Docker, монтируется в контейнер) |
INPX_FILE |
test_library.inpx |
Имя файла индекса INPX внутри папки с книгами |
PORT |
9090 |
Порт веб-сервера |
CATALOG_TITLE |
Pushkinlib |
Название каталога |
PAGE_SIZE |
30 |
Количество книг на странице |
LOG_LEVEL |
info |
Уровень логирования |
PUBLIC_BASE_URL |
http://localhost:9090 |
Публичный URL (для OPDS ссылок) |
AUTH_ENABLED |
false |
Включить авторизацию. При false все маршруты открыты, история общая |
ADMIN_USER |
admin |
Логин администратора. Создаётся автоматически при первом запуске |
ADMIN_PASS |
— | Пароль администратора. Обязателен при AUTH_ENABLED=true |
SESSION_SECRET |
(автогенерация) | Секрет для подписи сессий. Без явного значения сессии сбрасываются при перезапуске |
TTS_SERVER_URL |
— | URL TTS-сервера (например, http://tts-server:8000) |
TTS_API_KEY |
— | API-ключ для TTS-сервера (опционально) |
| Маршрут | Без авторизации | С авторизацией |
|---|---|---|
| Каталог книг, поиск, содержимое | Открыто | Открыто |
| TTS (озвучка) | Открыто | Открыто |
| Скачивание книг | Открыто | Открыто |
| Позиции чтения (сохранение/чтение) | Открыто (общие) | Требует логина (привязаны к пользователю) |
| История чтения | Открыта (общая) | Требует логина (привязана к пользователю) |
| OPDS-каталог | Открыт | HTTP Basic Auth |
Переиндексация (/api/v1/admin/reindex) |
Открыта | Только администратор |
Управление пользователями (/api/v1/admin/users) |
— | Только администратор |
Чтобы вернуться в режим без авторизации:
- AUTH_ENABLED=falseПерезапустите сервис. Все маршруты станут открытыми. Данные пользователей сохраняются в базе — при повторном включении всё вернётся.
Для тестирования с авторизацией используйте готовый overlay-файл:
docker compose -f docker-compose.dev.yaml -f docker-compose.auth.yaml up -d --buildТестовые данные: логин admin, пароль admin123.
CGO_ENABLED=1 go build -tags sqlite_fts5 -o pushkinlib ./cmd/pushkinlibПоиск использует модуль SQLite FTS5, поэтому сборка должна выполняться с включённым CGO и тегом
sqlite_fts5.
Скопируйте и настройте конфигурацию:
cp .env.example .envОсновные параметры в .env:
# Путь к библиотеке (при локальном запуске — без Docker)
LIBRARY_PATH=./books
INPX_FILE=my_catalog.inpx
PORT=9090
CATALOG_TITLE=Моя библиотека
PUBLIC_BASE_URL=http://localhost:9090
# Авторизация (опционально)
AUTH_ENABLED=false
ADMIN_USER=admin
ADMIN_PASS=changeme
SESSION_SECRET=random-secret-32-chars./pushkinlibДля отображения дружественных названий жанров в OPDS и веб-интерфейсе используется CSV-файл GENRES_CSV_PATH (по умолчанию ./web/static/genres.csv). Обновите его, если нужно скорректировать переводы жанров.
Pushkinlib включает полноценный ридер для чтения FB2-книг прямо в браузере. Для открытия книги нажмите кнопку «Читать» на карточке.
- Одна страница — по центру экрана, как в Kindle
- Книжный разворот — две страницы рядом, имитация бумажной книги
- Бесконечная прокрутка — непрерывный поток текста с ленивой подгрузкой
- Размер шрифта (увеличение/уменьшение)
- Переключение между светлой и тёмной темой
- Выбор режима отображения
- Навигация по оглавлению (TOC)
- Горячие клавиши: стрелки ←/→ для перелистывания, Escape для закрытия
Позиция чтения автоматически сохраняется в SQLite при каждом перелистывании. При повторном открытии книги чтение продолжается с того же места. Хранятся: номер секции, прогресс внутри секции и общее число секций.
Сервис отслеживает все открытые книги и их прогресс:
- «Читаю» — книга, которую пользователь начал читать, но не закончил
- «Прочитано» — статус присваивается автоматически при достижении последней секции книги
- Главная страница — блоки «Продолжить чтение» и «Прочитано» с карточками книг и прогресс-барами
- Страница истории — полный список с фильтрацией по статусу (Все / Читаю / Прочитано) и процентом прогресса
GET /api/v1/reading-history?status=reading&limit=20&offset=0Параметры:
status— фильтр:reading,finishedили пустое (все)limit— количество результатов (по умолчанию: 20)offset— смещение для пагинации
PUT /api/v1/books/{id}/position
GET /api/v1/books/{id}/positionТело запроса PUT:
{
"section": 5,
"progress": 0.42,
"total_sections": 12
}Pushkinlib может озвучивать книги через встроенный проксируемый TTS-сервер на базе Silero. Синтез речи работает на стороне сервера — браузер отправляет текст секции и получает аудио обратно.
Для работы TTS необходим отдельный TTS-сервер (OpenAI-совместимый API). Укажите его адрес через переменные окружения:
TTS_SERVER_URL=http://tts-server:8000
TTS_API_KEY=sk-your-key # опционально, если на TTS-сервере включена авторизацияВ docker-compose.dev.yaml TTS-сервер уже включён как отдельный сервис и стартует автоматически.
- Два режима воспроизведения: озвучка текущей секции и непрерывное чтение (автопереход к следующим секциям)
- Чтение с текущей позиции: озвучка начинается с параграфа, видимого на экране, а не с начала секции — удобно для длинных глав
- Выбор голоса: русские голоса Silero (aidar, baya, kseniya, xenia, eugene)
- Регулировка скорости: от 0.5x до 2.0x
- Сохранение настроек: выбранный голос и скорость запоминаются в localStorage
GET /api/v1/tts/status # Проверка доступности TTS-сервера
GET /api/v1/tts/voices # Список доступных голосов и моделей
POST /api/v1/tts/speech # Синтез речиТело запроса POST /api/v1/tts/speech:
{
"input": "Текст для озвучки",
"voice": "xenia",
"speed": 1.0,
"model": "v5_ru",
"response_format": "mp3"
}Ответ — аудиофайл (audio/mpeg для mp3). Максимальная длина текста — 5000 символов; длинные секции автоматически разбиваются на фрагменты ~2000 символов. Все поля кроме input опциональны (по умолчанию: голос xenia, скорость 1.0, формат mp3, модель v5_ru).
Если у вас есть папка с книгами, но нет INPX файла, используйте генератор каталога:
CGO_ENABLED=1 go build -tags sqlite_fts5 -o catalog-generator ./cmd/catalog-generatorПоместите книги в папку ./sample-data/books/:
sample-data/books/
├── book1.fb2
├── book2.zip
├── book3.epub
└── subfolder/
└── book4.fb2
./catalog-generator -books=./sample-data/books -name=my_catalogОпции генератора:
-books- папка с книгами (по умолчанию:./sample-data/books)-output- папка для результатов (по умолчанию:./sample-data)-name- имя каталога (по умолчанию:generated_catalog)-prefix- префикс для архивов (по умолчанию:books)-max-books- максимум книг в архиве (по умолчанию: 1000)-formats- форматы файлов (по умолчанию:.fb2,.zip,.epub)
После генерации обновите .env:
INPX_PATH=./sample-data/my_catalog.inpx
BOOKS_DIR=./sample-data- FB2 - полная поддержка метаданных
- FB2.ZIP - FB2 файлы в ZIP архивах
- EPUB - базовая поддержка (название, автор)
- INPX - стандартный формат индексов
- INP - отдельные файлы индексов
При включённой авторизации защищённые эндпоинты возвращают 401 Unauthorized, если пользователь не аутентифицирован. Публичные эндпоинты доступны всегда.
GET /api/v1/auth/info # Статус авторизации (публичный)
POST /api/v1/auth/login # Вход (публичный)
POST /api/v1/auth/logout # Выход (требует авторизации)
GET /api/v1/auth/me # Информация о текущем пользователе (требует авторизации)GET /api/v1/auth/info возвращает { "auth_enabled": true/false } — используется фронтендом для определения необходимости показа экрана логина.
POST /api/v1/auth/login принимает { "username": "...", "password": "..." } и устанавливает httpOnly сессионный cookie.
Административная переиндексация очищает текущую SQLite-базу и заново импортирует книги из указанного INPX:
POST /api/v1/admin/reindex # Требует авторизации + права администратораПример запроса:
# Без авторизации (AUTH_ENABLED=false)
curl -X POST http://localhost:9090/api/v1/admin/reindex
# С авторизацией (AUTH_ENABLED=true) — сначала получите сессию через /api/v1/auth/login
curl -X POST http://localhost:9090/api/v1/admin/reindex -b "session=<token>"В ответе возвращается статистика: количество импортированных книг, название коллекции и время выполнения в миллисекундах.
Все эндпоинты требуют авторизации с правами администратора.
GET /api/v1/admin/users # Список пользователей
POST /api/v1/admin/users # Создать пользователя
DELETE /api/v1/admin/users/{id} # Удалить пользователя
PUT /api/v1/admin/users/{id}/password # Сменить пароль пользователяСоздание пользователя (POST):
{
"username": "alice",
"password": "secret123",
"display_name": "Алиса",
"is_admin": false
}Смена пароля (PUT):
{
"password": "newpass789"
}GET /api/v1/books?q=запрос&limit=30&offset=0Параметры:
q- поисковый запросlimit- количество результатов (по умолчанию: 30)offset- смещение для пагинацииauthors[]- фильтр по авторамseries[]- фильтр по сериямgenres[]- фильтр по жанрамyear_from,year_to- фильтр по годамsort_by- сортировка (title,year,date_added,relevance)sort_order- порядок (asc,desc)
Фронтенд отображает дружественные названия жанров, подгружая отображение код → имя из web/static/genres.csv. При необходимости добавьте или скорректируйте пары в этом файле, изменения применяются без пересборки.
GET /api/v1/books/{id}GET /api/v1/books/{id}/content # Метаданные + оглавление (публичный)
GET /api/v1/books/{id}/sections/{index} # Содержимое секции (публичный)
GET /api/v1/books/{id}/position # Получить позицию (требует авторизации)
PUT /api/v1/books/{id}/position # Сохранить позицию (требует авторизации)
GET /api/v1/books/{id}/images/{name} # Изображение из книги (публичный)GET /api/v1/reading-history?status=reading&limit=20&offset=0GET /api/v1/tts/status # Статус TTS-сервера
GET /api/v1/tts/voices # Доступные голоса
POST /api/v1/tts/speech # Синтез речи (возвращает аудио)OPDS каталог доступен по адресу /opds и поддерживает:
- Навигацию - по авторам, сериям, жанрам
- Поиск - совместим с OpenSearch
- Пагинацию - для больших каталогов
- Скачивание - прямые ссылки на файлы
- HTTP Basic Auth - при включённой авторизации (
AUTH_ENABLED=true) OPDS требует логин/пароль
Добавьте в вашу читалку OPDS каталог:
http://your-server:9090/opds
Если авторизация включена, укажите логин и пароль в настройках читалки (HTTP Basic Auth).
Протестированные читалки:
- FBReader
- KyBook
- Bookari
- Moon+ Reader
pushkinlib/
├── cmd/
│ ├── pushkinlib/ # Основное приложение
│ └── catalog-generator/ # Генератор каталогов
├── internal/
│ ├── api/ # HTTP API handlers
│ ├── auth/ # Аутентификация
│ ├── catalog/ # Генерация каталогов
│ ├── config/ # Конфигурация
│ ├── covers/ # Обработка обложек
│ ├── inpx/ # Парсинг INPX
│ ├── metadata/ # Извлечение метаданных
│ ├── opds/ # OPDS каталог
│ ├── reader/ # FB2 парсер, конвертер, ридер
│ ├── search/ # Поиск и индексация
│ └── storage/ # База данных (SQLite, миграции)
├── web/static/ # Frontend (Vue.js SPA)
│ └── vendor/ # Локальные JS зависимости
├── tests/e2e/ # Playwright end-to-end тесты
├── scripts/ # Вспомогательные скрипты
└── sample-data/ # Тестовые данные
Web-интерфейс использует локальные копии JavaScript библиотек (Vue.js, Axios) для работы без внешних CDN. Для обновления зависимостей:
./scripts/download-deps.shСкрипт автоматически скачивает актуальные версии библиотек из unpkg.com в директорию web/static/vendor/.
# Юнит-тесты (ридер, парсеры)
go test ./internal/reader/... -v
# Все юнит-тесты (требуется SQLite с FTS5)
CGO_ENABLED=1 go test -tags sqlite_fts5 ./...
# Тест парсера INPX
go test ./internal/inpx -v
# Генерация тестового каталога
./catalog-generator -books=./sample-data/booksEnd-to-end тесты проверяют ридер, историю чтения, TTS и аутентификацию в реальном браузере. Тесты автоматически определяют режим авторизации и адаптируются.
# Установка зависимостей (однократно)
cd tests/e2e && npm install && npx playwright install --with-deps chromium
# Запуск без авторизации
docker compose -f docker-compose.dev.yaml up -d --build
cd tests/e2e && npx playwright test
# Запуск с авторизацией
docker compose -f docker-compose.dev.yaml -f docker-compose.auth.yaml up -d --build
cd tests/e2e && npx playwright testПри AUTH_ENABLED=true тесты автоматически логинятся через API перед каждым тестом. Тесты аутентификации (auth.spec.js) содержат две группы: «Auth Enabled» и «Auth Disabled», каждая из которых пропускается в неподходящем режиме.
MIT License
- Fork проекта
- Создайте feature branch (
git checkout -b feature/amazing-feature) - Commit изменения (
git commit -m 'Add amazing feature') - Push в branch (
git push origin feature/amazing-feature) - Откройте Pull Request
Если у вас возникли вопросы или проблемы, создайте issue в репозитории GitHub.