Skip to content

foxzi/pushkinlib

Repository files navigation

Pushkinlib

Web-сервис для просмотра локальной библиотеки книг по INPX-индексу с поддержкой OPDS и встроенным ридером.

Возможности

  • 📚 Парсинг INPX — поддержка индексных файлов библиотек
  • 🔍 Полнотекстовый поиск — SQLite FTS5 по названию, серии, авторам и аннотации
  • 🌐 Web-интерфейс — современный SPA на Vue.js
  • 📱 Адаптивный дизайн — работает на мобильных устройствах
  • 📖 OPDS каталог — совместимость с читалками (FBReader, KyBook, Moon+ Reader и др.)
  • 🗂️ Фильтрация — по жанрам, сериям, авторам, годам
  • Быстрая работа — SQLite база данных с индексами
  • 📕 Встроенный ридер FB2 — чтение книг прямо в браузере с тремя режимами отображения
  • 💾 Запоминание позиции — автоматическое сохранение и восстановление места чтения
  • 📊 История чтения — отслеживание прогресса, статусы «Читаю» и «Прочитано», автоопределение завершения
  • 🔊 Озвучка текста (TTS) — синтез речи через Silero TTS: чтение с текущей позиции, выбор голоса, регулировка скорости, непрерывное чтение
  • 🔐 Аутентификация — опциональная многопользовательская авторизация с разделением истории чтения по пользователям

Быстрый старт

Запуск через Docker Compose (рекомендуется)

# Скопируйте пример конфигурации
cp .env.example .env

# Отредактируйте .env — укажите путь к библиотеке и имя INPX файла
nano .env

В .env задаются пути к библиотеке и основные параметры:

# Путь на хосте к папке с книгами (монтируется в контейнер)
LIBRARY_PATH=./books

# Имя файла индекса INPX внутри папки с книгами
INPX_FILE=test_library.inpx

Docker Compose автоматически подхватывает .env файл и подставляет переменные.

# Запустите контейнер
docker compose -f docker-compose.dev.yaml up -d --build

Сервис будет доступен по адресу http://localhost:9090:

Аутентификация

Pushkinlib поддерживает опциональную многопользовательскую авторизацию. По умолчанию авторизация выключена — сервис работает без логина, история чтения общая.

При включении авторизации:

  • появляется экран логина
  • история чтения и позиции привязываются к пользователю
  • OPDS-каталог защищается HTTP Basic Auth (для читалок)
  • каталог книг, поиск и содержимое книг остаются публичными

Пошаговая настройка

Шаг 1. Сгенерируйте секрет сессий

Секрет нужен для подписи cookie. Без него сессии не переживут перезапуск контейнера.

# Linux / macOS
openssl rand -hex 32

# или
head -c 32 /dev/urandom | base64

Сохраните полученную строку — она понадобится на следующем шаге.

Шаг 2. Добавьте переменные окружения

Откройте ваш 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

Шаг 3. Запустите (или перезапустите) сервис

# Docker Compose
docker compose up -d

# или из исходников
./pushkinlib

В логах вы увидите:

Auth enabled, ensuring admin user exists...
Admin user "admin" created successfully

Если пользователь admin уже существовал, пароль будет обновлён.

Шаг 4. Войдите в веб-интерфейс

Откройте http://localhost:9090 — вы увидите экран логина:

  1. Введите логин (admin) и пароль, заданный в ADMIN_PASS
  2. Нажмите Войти
  3. После успешного входа — библиотека, имя пользователя в шапке и кнопка Выйти

Сессия хранится в httpOnly cookie и действует 30 дней.

Шаг 5. Создайте дополнительных пользователей

В шапке сайта нажмите Пользователи (кнопка видна только администраторам).

В админ-панели можно:

  • Создать пользователя — заполните логин, пароль (от 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}

Шаг 6. Настройте читалку (OPDS)

При включённой авторизации 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.

Запуск из исходников

1. Сборка

CGO_ENABLED=1 go build -tags sqlite_fts5 -o pushkinlib ./cmd/pushkinlib

Поиск использует модуль SQLite FTS5, поэтому сборка должна выполняться с включённым CGO и тегом sqlite_fts5.

2. Конфигурация

Скопируйте и настройте конфигурацию:

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

3. Запуск

./pushkinlib

Для отображения дружественных названий жанров в OPDS и веб-интерфейсе используется CSV-файл GENRES_CSV_PATH (по умолчанию ./web/static/genres.csv). Обновите его, если нужно скорректировать переводы жанров.

Встроенный ридер

Pushkinlib включает полноценный ридер для чтения FB2-книг прямо в браузере. Для открытия книги нажмите кнопку «Читать» на карточке.

Режимы отображения

  • Одна страница — по центру экрана, как в Kindle
  • Книжный разворот — две страницы рядом, имитация бумажной книги
  • Бесконечная прокрутка — непрерывный поток текста с ленивой подгрузкой

Настройки ридера

  • Размер шрифта (увеличение/уменьшение)
  • Переключение между светлой и тёмной темой
  • Выбор режима отображения
  • Навигация по оглавлению (TOC)
  • Горячие клавиши: стрелки ←/→ для перелистывания, Escape для закрытия

Сохранение позиции

Позиция чтения автоматически сохраняется в SQLite при каждом перелистывании. При повторном открытии книги чтение продолжается с того же места. Хранятся: номер секции, прогресс внутри секции и общее число секций.

История чтения

Сервис отслеживает все открытые книги и их прогресс:

  • «Читаю» — книга, которую пользователь начал читать, но не закончил
  • «Прочитано» — статус присваивается автоматически при достижении последней секции книги
  • Главная страница — блоки «Продолжить чтение» и «Прочитано» с карточками книг и прогресс-барами
  • Страница истории — полный список с фильтрацией по статусу (Все / Читаю / Прочитано) и процентом прогресса

API истории чтения

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
}

Озвучка текста (TTS)

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

TTS API

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 файла, используйте генератор каталога:

1. Сборка генератора

CGO_ENABLED=1 go build -tags sqlite_fts5 -o catalog-generator ./cmd/catalog-generator

2. Подготовка книг

Поместите книги в папку ./sample-data/books/:

sample-data/books/
├── book1.fb2
├── book2.zip
├── book3.epub
└── subfolder/
    └── book4.fb2

3. Генерация каталога

./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)

4. Использование сгенерированного каталога

После генерации обновите .env:

INPX_PATH=./sample-data/my_catalog.inpx
BOOKS_DIR=./sample-data

Поддерживаемые форматы

Извлечение метаданных

  • FB2 - полная поддержка метаданных
  • FB2.ZIP - FB2 файлы в ZIP архивах
  • EPUB - базовая поддержка (название, автор)

Файлы каталога

  • INPX - стандартный формат индексов
  • INP - отдельные файлы индексов

API

При включённой авторизации защищённые эндпоинты возвращают 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>"

В ответе возвращается статистика: количество импортированных книг, название коллекции и время выполнения в миллисекундах.

Управление пользователями (API)

Все эндпоинты требуют авторизации с правами администратора.

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=0

TTS (публичный)

GET  /api/v1/tts/status                  # Статус TTS-сервера
GET  /api/v1/tts/voices                  # Доступные голоса
POST /api/v1/tts/speech                  # Синтез речи (возвращает аудио)

OPDS

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/books

Playwright e2e тесты

End-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

Вклад в проект

  1. Fork проекта
  2. Создайте feature branch (git checkout -b feature/amazing-feature)
  3. Commit изменения (git commit -m 'Add amazing feature')
  4. Push в branch (git push origin feature/amazing-feature)
  5. Откройте Pull Request

Поддержка

Если у вас возникли вопросы или проблемы, создайте issue в репозитории GitHub.

About

Local inpx lib pushkinlib

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors