Обновлен контекст

This commit is contained in:
astakhovp committed 2026-10-08 23:14:00 +03:00
1 parent ab4953ffd1
commit a51a7e1a76
1 file changed
+175 -142
+175 -142
View File
@@ -1,142 +1,175 @@
# Выжимка из контекста: Домашняя библиотека # Выжимка из контекста: Домашняя библиотека
## Что построено ## Что построено
**Локальный OPDS-каталог + веб-интерфейс** для копии библиотеки lib.rus.ec (~579 000 книг) на TV-приставке с Armbian. **Локальный OPDS-каталог + веб-интерфейс** для копии библиотеки lib.rus.ec (~579 000 книг) на TV-приставке с Armbian.
## Железо и окружение ## Железо и окружение
- **Устройство**: TV-приставка Vontar, Armbian, ARM - **Устройство**: TV-приставка Vontar, Armbian, ARM
- **Диски**: `/mnt/hdd` — основной - **Диски**: `/mnt/hdd` — основной
- **Docker**: контейнеры Immich (фото), Home Assistant, qBittorrent, Adminer - **Docker**: контейнеры Immich (фото), Home Assistant, qBittorrent, Adminer
- **PostgreSQL**: используется общий с Immich (`immich_postgres`), база `books` - **PostgreSQL**: используется общий с Immich (`immich_postgres`), база `books`
- Пароль: `Karabas@Barabas`, пользователь: `postgres` - Пароль: `Karabas@Barabas`, пользователь: `postgres`
- **Фиксированный IP**: `172.20.0.4` (сеть `immich_immich_net`, подсеть `172.20.0.0/16`) - **Фиксированный IP**: `172.20.0.4` (сеть `immich_immich_net`, подсеть `172.20.0.0/16`)
- **OPDS-сервер**: контейнер `opds_server` на порту **8085** (порт 8080 занят qBittorrent) - **OPDS-сервер**: контейнер `opds_server` на порту **8085** (порт 8080 занят qBittorrent)
- **Адрес библиотеки**: `http://192.168.30.2:8085/` - **Адрес библиотеки**: `http://192.168.30.2:8085/`
## Расположение файлов ## Расположение файлов
``` ```
/mnt/hdd/opds/ # проект OPDS /mnt/hdd/opds/ # проект OPDS
├── opds_server.py # главный Flask-сервер ├── opds_server.py # главный Flask-сервер
├── db_utils.py # подключение к БД ├── db_utils.py # подключение к БД
├── author_utils.py # работа с авторами ├── author_utils.py # работа с авторами
├── book_utils.py # извлечение fb2, обложки, конвертация ├── book_utils.py # извлечение fb2, обложки, конвертация
├── parse_inpx.py # импорт книг ├── parse_inpx.py # импорт книг
├── parse_genres.py # загрузка жанров ├── parse_genres.py # загрузка жанров
├── genres.xml # справочник жанров fb2 ├── genres.xml # справочник жанров fb2
├── Dockerfile ├── Dockerfile
└── docker-compose.yml └── docker-compose.yml
/mnt/hdd/share/Torrents/Downloaded/Libruks/ /mnt/hdd/share/Torrents/Downloaded/Libruks/
├── Библиотека Либрусек для архивов 17-08-2026.inpx ├── Библиотека Либрусек для архивов 17-08-2026.inpx
├── Библиотека Либрусек для архивов 18-09-2026.inpx ├── Библиотека Либрусек для архивов 18-09-2026.inpx
├── Libruks1086.zip ├── Libruks1086.zip
└── Архивы Либрусек/ # 3726 zip-архивов с fb2 └── Архивы Либрусек/ # 3726 zip-архивов с fb2
├── fb2-000065-005477.zip ├── fb2-000065-005477.zip
├── fb2-069327-070122.zip ├── fb2-069327-070122.zip
└── ... (по диапазонам ID) └── ... (по диапазонам ID)
/root/docker-compose.yml # Immich (с фиксированным IP postgres) /root/docker-compose.yml # Immich (с фиксированным IP postgres)
``` ```
## Структура базы данных ## Структура базы данных
| Таблица | Назначение | | Таблица | Назначение |
|---------|-----------| |---------|-----------|
| `authors` | Авторы (нормализованные: full_name, last_name, first_name, middle_name, name_hash, first_letter) | | `authors` | Авторы (нормализованные: full_name, last_name, first_name, middle_name, name_hash, first_letter) |
| `series` | Серии (name, name_hash, first_letter) | | `series` | Серии (name, name_hash, first_letter) |
| `genres` | Жанры (code, title_ru, title_en, parent_code, root_code, is_root, is_custom, genre_alt[]) | | `genres` | Жанры (code, title_ru, title_en, parent_code, root_code, is_root, is_custom, genre_alt[]) |
| `books` | Книги (title, internal_id, internal_id2, file_size, format, added_date, language, source, series_id, series_number, fts) | | `books` | Книги (title, internal_id, internal_id2, file_size, format, added_date, language, source, series_id, series_number, fts) |
| `book_authors` | Связь книг↔авторов (book_id, author_id, position) | | `book_authors` | Связь книг↔авторов (book_id, author_id, position) |
| `book_genres` | Связь книг↔жанров (book_id, genre_id) | | `book_genres` | Связь книг↔жанров (book_id, genre_id) |
**Особенности:** **Особенности:**
- Полнотекстовый поиск через `fts tsvector` (русский язык) - Полнотекстовый поиск через `fts tsvector` (русский язык)
- Хеши (MD5) для быстрого поиска - Хеши (MD5) для быстрого поиска
- Поле `source` для поддержки нескольких источников - Поле `source` для поддержки нескольких источников
- Нормализация: авторы/серии/жанры вынесены в отдельные таблицы - Нормализация: авторы/серии/жанры вынесены в отдельные таблицы
## Формат inpx (lib.rus.ec) ## Формат inpx (lib.rus.ec)
Текстовый файл с разделителем `\x04`: Текстовый файл с разделителем `\x04`:
``` ```
AUTHOR \x04 GENRE \x04 TITLE \x04 SERIES \x04 SERNO \x04 FILE \x04 SIZE \x04 LIBID \x04 DEL \x04 EXT \x04 DATE \x04 LANG \x04 KEYWORDS AUTHOR \x04 GENRE \x04 TITLE \x04 SERIES \x04 SERNO \x04 FILE \x04 SIZE \x04 LIBID \x04 DEL \x04 EXT \x04 DATE \x04 LANG \x04 KEYWORDS
``` ```
- Авторы: `Фамилия,Имя,Отчество` через `:` для нескольких - Авторы: `Фамилия,Имя,Отчество` через `:` для нескольких
- Жанры: коды fb2 через `:` (например, `sf_history:network_literature`) - Жанры: коды fb2 через `:` (например, `sf_history:network_literature`)
- Особенность архивов: **fb2 внутри zip-архивов может быть вложенным zip** — нужно распаковывать дважды - Особенность архивов: **fb2 внутри zip-архивов может быть вложенным zip** — нужно распаковывать дважды
## Что реализовано в веб-интерфейсе ## Что реализовано в веб-интерфейсе
- ✅ Главная с поиском и навигацией - ✅ Главная с поиском и навигацией
- ✅ Поиск (полнотекстовый + по автору) - ✅ Поиск (полнотекстовый + по автору)
- ✅ Авторы с группировкой по алфавиту (умная навигация: Па → Пел → Пелевин) - ✅ Авторы с группировкой по алфавиту (умная навигация: Па → Пел → Пелевин)
- ✅ Страница автора (серии, книги, сборники с пометкой соавторов) - ✅ Страница автора (серии, книги, сборники с пометкой соавторов)
- ✅ Страница книги (обложка, аннотация, оглавление) - ✅ Страница книги (обложка, аннотация, оглавление)
- ✅ Чтение глав (конвертация fb2→HTML на лету, встроенные картинки, навигация ← пред/оглавление↑/след →) - ✅ Чтение глав (конвертация fb2→HTML на лету, встроенные картинки, навигация ← пред/оглавление↑/след →)
- ✅ Скачивание fb2 (с обработкой вложенных zip) - ✅ Скачивание fb2 (с обработкой вложенных zip)
- ✅ OPDS 1.2 для читалок (FBReader) - ✅ OPDS 1.2 для читалок (FBReader)
## Что НЕ реализовано / в планах ## Что НЕ реализовано / в планах
- ❌ Страница серий (список + страница серии с книгами) - ❌ Страница серий (список + страница серии с книгами)
- ❌ Страница жанров (дерево + фильтрация) - ❌ Страница жанров (дерево + фильтрация)
- ❌ Обложки с ленивой загрузкой (обсуждали: заглушка → загрузка в фоне → кэш в `/mnt/hdd/opds/covers/`) - ❌ Обложки с ленивой загрузкой (обсуждали: заглушка → загрузка в фоне → кэш в `/mnt/hdd/opds/covers/`)
- ❌ OPDS 2.0 (обсуждали как второй эндпоинт `/opds2`) - ❌ OPDS 2.0 (обсуждали как второй эндпоинт `/opds2`)
- ❌ Редактирование жанров (кастомные жанры с `is_custom=TRUE` ждут ручного заполнения) - ❌ Редактирование жанров (кастомные жанры с `is_custom=TRUE` ждут ручного заполнения)
- ❌ Языковой фильтр (56 языков в базе, но основные — ru) - ❌ Языковой фильтр (56 языков в базе, но основные — ru)
## Ключевые решения ## Новый импорт: сканирование файловой системы
- **Язык в скобках**: для не-русских книг показывать `(en)`, `(uk)` и т.д. **Проблема INPX:** файл содержит ссылки на книги, которых нет на диске (удалены, не скачаны).
- **Сборники**: если авторов >1 — пометка `👥 соавторы: X, Y` (до 3) или `👥 N авторов` (больше)
- **Порядок книг автора**: от новых к старым (`added_date DESC NULLS LAST`) **Решение:** скрипт-сканер каталога — альтернатива INPX.
- **Серии на странице автора**: все, где автор участвует, по алфавиту
- **Название**: "Домашняя библиотека" (не "Моя библиотека") ### Логика работы
- **Модульная структура**: разбить на модули (db, author, book, series, genre utils) 1. Принимает каталог как параметр
- **Git**: хранить всю информацию в репозитории 2. Обходит все файлы в каталоге и подкаталогах
3. Определяет новые/изменённые файлы через таблицу `file_scan_log`
## Текущие проблемы / нюансы 4. Новые/изменённые файлы → распаковка → парсинг FB2 → добавление в БД
5. Исчезнувшие файлы → удаление связанных книг из БД
1. **IP PostgreSQL менялся** после перезагрузки → исправлено фиксированным IP `172.20.0.4`
2. **Сеть Immich** конфликтовала с adminer → adminer отключён, сеть пересоздана как `immich_immich_net` ### Поддержка форматов
3. **Вложенные zip в архивах** → `get_book_fb2` распаковывает дважды - `.zip` → внутри может быть `.zip` → внутри может быть `.fb2`
4. **VACUUM** нельзя в транзакции → `autocommit=True` перед VACUUM - Вложенность: до 3 уровней
5. **Дубликаты авторов** (`Лукьяненко,Сергей,` и `Лукьяненко,Сергей`) → исправлено нормализацией при импорте - RAR: бонусная поддержка
6. **string_agg(DISTINCT ... ORDER BY ...)** — ошибка в PostgreSQL → переписано на подзапросы
### Таблица `file_scan_log`
## Полезные команды | Поле | Тип | Назначение |
|------|-----|-----------|
```bash | file_path | TEXT | Полный путь к файлу |
# Обновление библиотеки новым inpx | file_size | INTEGER | Размер файла |
docker compose exec opds python parse_inpx.py \ | file_hash | TEXT | MD5/SHA256 хеш содержимого |
"/path/to/новый.inpx" \ | updated_at | TIMESTAMP | Последнее обновление |
"dbname=books user=postgres password=Karabas@Barabas host=172.20.0.4" \
"lib.rus.ec" ### Удаление книг
- Файл исчез с диска → книга удаляется из БД безвозвратно
# Проверка количества книг - Файл изменён → старые книги удаляются, парсится заново
docker exec -it immich_postgres psql -U postgres -d books -c "SELECT COUNT(*) FROM books;"
### Источник
# Кастомные жанры - Отдельная строка в `books.source` (например, `"file_scan"`)
docker exec -it immich_postgres psql -U postgres -d books -c "
SELECT g.code, COUNT(bg.book_id) as cnt ## Ключевые решения
FROM genres g JOIN book_genres bg ON bg.genre_id = g.id
WHERE g.is_custom = TRUE GROUP BY g.code ORDER BY cnt DESC;" - **Язык в скобках**: для не-русских книг показывать `(en)`, `(uk)` и т.д.
- **Сборники**: если авторов >1 — пометка `👥 соавторы: X, Y` (до 3) или `👥 N авторов` (больше)
# Пересборка OPDS - **Порядок книг автора**: от новых к старым (`added_date DESC NULLS LAST`)
cd /mnt/hdd/opds && docker compose up -d --build - **Серии на странице автора**: все, где автор участвует, по алфавиту
``` - **Название**: "Домашняя библиотека" (не "Моя библиотека")
- **Модульная структура**: разбить на модули (db, author, book, series, genre utils)
## Следующий шаг - **Git**: хранить всю информацию в репозитории
Создание Git-репозитория `opds-library` со структурой: ## Текущие проблемы / нюансы
- `sql/` — схема БД
- `scripts/` — парсеры 1. **IP PostgreSQL менялся** после перезагрузки → исправлено фиксированным IP `172.20.0.4`
- `app/` — Flask-сервер (модули) 2. **Сеть Immich** конфликтовала с adminer → adminer отключён, сеть пересоздана как `immich_immich_net`
- `data/` — genres.xml 3. **Вложенные zip в архивах** → `get_book_fb2` распаковывает дважды
- `docker-compose.yml`, `Dockerfile`, `README.md` 4. **VACUUM** нельзя в транзакции → `autocommit=True` перед VACUUM
5. **Дубликаты авторов** (`Лукьяненко,Сергей,` и `Лукьяненко,Сергей`) → исправлено нормализацией при импорте
С этого места продолжаем. 6. **string_agg(DISTINCT ... ORDER BY ...)** — ошибка в PostgreSQL → переписано на подзапросы
## Полезные команды
```bash
# Обновление библиотеки новым inpx
docker compose exec opds python parse_inpx.py \
"/path/to/новый.inpx" \
"dbname=books user=postgres password=Karabas@Barabas host=172.20.0.4" \
"lib.rus.ec"
# Проверка количества книг
docker exec -it immich_postgres psql -U postgres -d books -c "SELECT COUNT(*) FROM books;"
# Кастомные жанры
docker exec -it immich_postgres psql -U postgres -d books -c "
SELECT g.code, COUNT(bg.book_id) as cnt
FROM genres g JOIN book_genres bg ON bg.genre_id = g.id
WHERE g.is_custom = TRUE GROUP BY g.code ORDER BY cnt DESC;"
# Пересборка OPDS
cd /mnt/hdd/opds && docker compose up -d --build
```
## Следующий шаг
Создание Git-репозитория `opds-library` со структурой:
- `sql/` — схема БД
- `scripts/` — парсеры
- `app/` — Flask-сервер (модули)
- `data/` — genres.xml
- `docker-compose.yml`, `Dockerfile`, `README.md`
С этого места продолжаем.