Флибуста большая, но не бесконечная: своя рукопись, книга, которой там нет, файл в лучшем качестве — всё это можно положить в собственную библиотеку через раздел «Добавить». В статье об административных разделах форма описана коротко; здесь — подробно, с тем, что происходит «под капотом», потому что от этого зависит, переживёт ли книга следующее обновление базы.

Чем это отличается от «Добавить книги из торрента»

В «Сервисе» есть блок для торрентов — он добавляет архивы Флибусты целиком, то есть файлы вида f.fb2-…zip, и книги из них уже есть в дампе базы. Раздел «Добавить» — про одну книгу, которой в базе нет: для неё создаётся запись с названием, авторами и жанрами, и упаковывается файл.

Форма шаг за шагом

1. Название. До 254 символов. Когда вы уходите из поля, библиотека ищет похожие названия (с допуском на опечатки) и показывает список со ссылками на страницы книг — посмотрите, не та ли это книга. При точном совпадении названия форма не примет книгу без галочки «Всё равно добавить (это другая книга с таким же названием)». Одноимённых книг у разных авторов много, так что галочка — норма, а не исключение.

2. Авторы. Поле поиска с подсказками начиная с двух символов. Ищет по всем авторам базы, включая тех, у кого сейчас нет ни одной книги (в обычном поиске библиотеки такие авторы скрыты), с допуском на опечатки; в подсказке показано число книг и пометка «локальный» для авторов, добавленных через этот же раздел. Если автора нет — «+ Новый автор»: фамилия, имя, отчество, псевдоним; обязательна фамилия или псевдоним. Авторов от одного до десяти, порядок сохраняется.

3. Файл. Плюс язык (две-три латинские буквы, по умолчанию ru), год и до десяти жанров из дерева Флибусты.

Размер загрузки — до 500 МБ на стороне PHP; reverse proxy может ограничивать сильнее.

Как распознаётся формат

Расширение файла библиотеке почти безразлично — она смотрит на содержимое:

Формат По чему определяется
pdf сигнатура %PDF-
djvu сигнатура AT&TFORM + DJVU
mobi метка BOOKMOBI или TEXtREAd в заголовке
rtf {\rtf
fb2 тег <FictionBook в начале файла
html <html или <!DOCTYPE html
txt расширение .txt и отсутствие нулевых байтов
epub zip с записью mimetype = application/epub+zip
docx zip с [Content_Types].xml и word/document.xml
zip-обёртка любой другой zip, содержащий ровно один файл одного из форматов выше

Zip-обёртка распаковывается, и в библиотеку попадает то, что внутри; zip с двумя книгами или с вложенной папкой будет отвергнут. Файл, который не подошёл ни под одно правило, не принимается — например, fb2 в кодировке, где <FictionBook не встречается в первых 8 КБ, или «txt» с бинарным мусором.

Дубликаты

Два уровня проверки:

  • Жёсткий — по md5 файла. Если точно такой же файл уже есть в библиотеке (в дампе или среди локальных), добавление отклоняется с указанием, какая это книга. Обойти нельзя, и не нужно.
  • Мягкий — по названию, описан выше: требует подтверждения галочкой.

Проверка по авторам не делается: библиотека не пытается решить, что «Война и мир» Толстого и «Война и мир» другого автора — разные книги; это решаете вы галочкой.

Что происходит при сохранении

Всё в одной транзакции:

  1. Книге выдаётся идентификатор из последовательности, начинающейся с 10 000 000. Идентификаторы Флибусты до этого значения не доходят, поэтому по id ≥ 10 000 000 библиотека везде отличает локальные книги от книг дампа. Новые авторы получают id из такой же последовательности.
  2. Запись сохраняется в таблицы local_books, local_authors, local_book_authors, local_book_genres — это постоянное хранилище локальных книг.
  3. Те же данные вставляются в обычные таблицы библиотеки (libbook, libavtor, libavtorname, libgenre) и в индекс полнотекстового поиска — поэтому книга видна и находится сразу, без пересканирования.
  4. Файл упаковывается в одно-книжный zip в /cache/local: f.fb2.<id>-<id>.zip для fb2 или f.usr-<id>-<id>.zip для остальных, с записью <id>.<расширение> внутри — ровно по соглашению Флибусты. В таблицу индекса архивов добавляется строка с диапазоном из одного id.

Благодаря последнему пункту для чтения, скачивания, конвертации и OPDS локальная книга ничем не отличается от книги из дампа. Обложка для fb2 извлекается из самого файла, как и у обычных книг.

Что происходит при следующем импорте дампа

Кнопка «Обновить базу данных» очищает обычные таблицы и заливает их заново из дампа Флибусты. Локальные книги при этом пропали бы — если бы не шаг «Восстановление локально добавленных книг» (merge_local_books.php), который импорт выполняет автоматически после заливки дампа:

  1. Для каждой книги из local_books ищется двойник в новом дампе: сильное совпадение по md5 или слабое — одинаковое название плюс общий автор (по фамилии и имени).
  2. Если двойник найден, дальнейшее решает переменная FLIBUSTA_LOCAL_DUPLICATE_POLICY:
    • keep_both (по умолчанию) — обе книги остаются, в лог пишется заметка;
    • prefer_dump — локальная копия удаляется вместе с её zip, а избранное и позиции чтения переносятся на книгу из дампа (там, где не конфликтуют с уже существующими записями).
  3. Книга без двойника вставляется обратно в обычные таблицы. Дата добавления берётся из local_books, поэтому книга не всплывает наверх списка новинок при каждом импорте.
  4. Если автор книги из дампа исчез (на Флибусте объединили или удалили автора), создаётся запись-заглушка «(автор удалён из дампа)», чтобы книга не осталась без автора.
  5. Локальные авторы, у которых в новом дампе появился полный тёзка, только перечисляются в логе — автоматически они не объединяются.

Отчёт восстановления попадает в /cache/log/dbupdate.log и в вывод операции на странице «Сервис»: сколько восстановлено, сколько удалено как дубликаты, были ли ошибки. Если zip какой-то книги не найден в /cache/local, в логе будет предупреждение: книга останется в каталоге, но открыть её будет нельзя.

Как удалить локальную книгу

Кнопки удаления в интерфейсе пока нет. Удалить можно через psql (как подключиться — в статье о скриптах), причём достаточно убрать книгу из постоянного хранилища и из текущих таблиц:

-- id книги виден в адресе её страницы: /book/view/10000005
DELETE FROM local_books WHERE bookid = 10000005;   -- связи с авторами и жанрами удалятся каскадно
DELETE FROM libbook    WHERE bookid = 10000005;
DELETE FROM libbook_ts WHERE bookid = 10000005;
DELETE FROM libavtor   WHERE bookid = 10000005;
DELETE FROM libgenre   WHERE bookid = 10000005;
DELETE FROM book_zip   WHERE start_id = 10000005 AND end_id = 10000005;

и удалить файл /cache/local/f.fb2.10000005-10000005.zip (или f.usr-…). Если лень вычищать обычные таблицы — можно удалить только из local_books и файл, а потом запустить «Обновить базу данных»: обычные таблицы пересоздадутся без этой книги. Локальный автор без книг останется в базе как кандидат для будущих добавлений; это безвредно.

Бэкап и переезд

Локальные книги — единственное содержимое библиотеки, которое нельзя скачать заново. Их два компонента:

  • таблицы local_books, local_authors, local_book_authors, local_book_genres и последовательности local_book_id_seq, local_author_id_seq в Postgres;
  • файлы f.fb2.<id>-<id>.zip и f.usr-<id>-<id>.zip в /cache/local.

Достаточно сохранить эти таблицы и файлы; всё остальное восстанавливается импортом дампа. Как именно снимать и восстанавливать дамп — в статье о бэкапе и обновлении.