Образ Flibusta-at-home не содержит ни одной книги — только движок. Книги приходят в виде zip-архивов Флибусты, которые вы кладёте в директорию, смонтированную как /flibusta. Эта статья о том, где эти архивы взять, что значат их имена и как библиотека по имени архива понимает, какая книга в нём лежит.

Откуда берутся архивы

Флибуста раздаёт свою коллекцию через торренты: на сайте есть раздел с торрент-файлами и magnet-ссылками на архивы. Коллекция разбита на файлы по диапазонам идентификаторов книг, а не по датам или авторам; новые поступления выкладываются отдельными архивами и рано или поздно попадают в общий торрент.

Способа получить архивы два:

  • Любым торрент-клиентом на любом компьютере, потом положить файлы в /flibusta. Так удобно для первоначальной закачки полной коллекции, которая занимает сотни гигабайт и может идти сутками.
  • Прямо из библиотеки: раздел «Сервис», блок «Добавить книги из торрента». Вставьте magnet-ссылку или загрузите .torrent, и встроенный aria2c скачает только те файлы, которых у вас ещё нет, в /cache/local. Удобно для регулярных «дельт». Подробнее — в статье об административных разделах.

Есть и третий источник — ежедневные обновления (/daily/ на сайте Флибусты), которые кнопка «Скачать последние обновления» складывает в /cache/local. Это небольшие архивы за последние дни; когда те же книги появляются в торрент-архиве, ежедневные файлы становятся ненужными, и библиотека сама удаляет их (см. ниже).

Что означают имена файлов

f.fb2-000024-030559.zip
f.usr-000004-030559.zip
f.n-812345-813210.zip
f.fb2.815000-815999.zip
d.fb2-120000-125000.zip

Схема: <префикс>.<класс>[-.]<первый id>-<последний id>.zip.

  • Класс fb2 — книги в формате fb2, основная масса художественной литературы. Внутри архива файлы называются <id>.fb2.
  • Класс usr — все остальные форматы: pdf, djvu, epub, docx, mobi, rtf, txt, html. Внутри — <id>.<расширение>.
  • Класс n — «новые» архивы; библиотека относит их к fb2.
  • Два числа — диапазон идентификаторов книг, которые могут быть внутри. Это идентификаторы из базы Флибусты, те же, что в адресе книги на сайте (/b/123456). Не все id диапазона обязательно присутствуют в архиве — часть книг удалена, часть относится к другому классу.
  • Разделитель между классом и первым числом бывает и -, и . — библиотека понимает оба варианта.
  • Префикс d. тоже принимается (архив d.fb2-009… исключён явно, это историческая особенность).

Библиотеке важно только имя: в нём должен быть дефис, окончание .zip и подстрока fb2 для fb2-класса. Не переименовывайте и не распаковывайте архивы. Файл с именем не по схеме будет проигнорирован при сканировании.

Как строится индекс архивов

Кнопка «Сканирование ZIP» (скрипт update_zip_list.php) делает следующее:

  1. Читает имена файлов в /flibusta и в /cache/local, разбирает из имени класс и диапазон.
  2. Для каждого класса отдельно выбирает минимальный набор архивов, покрывающий все идентификаторы. Если один архив целиком перекрыт другими (типичный случай: ежедневная «дельта» в /cache/local, книги которой уже вошли в большой торрент-архив в /flibusta), он в индекс не попадает.
  3. Архивы из /cache/local, которые в индекс не попали, удаляются с диска — их содержимое уже есть в основной коллекции. Список удалённых пишется в /cache/clearlists/. Файлы из /flibusta не трогаются никогда: директория смонтирована только для чтения.
  4. Если между диапазонами есть разрыв, скрипт пишет Got a gap в /cache/log/update_zip_list.log и продолжает; книги из разрыва будут считаться отсутствующими (и, при включённом режиме, скачиваться на лету).
  5. Таблица book_zip очищается и заполняется заново: для каждого выбранного архива — путь, первый id, последний id и признак класса.
  6. Тот же список выгружается в файл /cache/zip_index.php — это и есть тот индекс, которым библиотека пользуется в работе (см. следующий раздел).

Отсюда практические следствия:

  • Перекрывающиеся архивы держать можно, вреда нет — индексатор выберет нужные.
  • После любого изменения набора файлов нужно сканирование: добавили торрент, удалили файл, перенесли архивы из /cache/local в /flibusta. Без сканирования библиотека будет искать книги по старой карте.
  • Порядок id в базе Флибусты и в именах архивов один и тот же, поэтому «свежие» книги — это архивы с большими номерами. Хотите держать только новинки — оставьте последние несколько архивов.

Как библиотека находит книгу

При открытии книги с идентификатором N и форматом из базы:

  1. Поиск в индексе архивов: архив нужного класса, у которого start_id ≤ N ≤ end_id.
  2. Внутри архива ищется запись N.fb2 (или N.<расширение> для usr). В некоторых архивах книга лежит вложенным zip-файлом (N.fb2.zip, а иногда с человекочитаемым именем, которое записано в таблице libfilename); тогда вложенный zip один раз распаковывается в /cache/local/N.fb2, и дальше используется этот файл.
  3. Если подходящего архива нет — срабатывает скачивание на лету, если оно включено; результат тоже оказывается в /cache/local/N.<формат>.

Файл /cache/local/N.<формат> всегда проверяется первым, до открытия архива. Поэтому /cache/local со временем растёт: там и распакованные вложенные zip, и скачанные на лету книги, и ежедневные архивы, и одно-книжные zip из раздела «Добавить». Всё, кроме последнего, можно безболезненно удалять — библиотека восстановит нужное при следующем обращении.

Где живёт сам индекс

Поиск архива происходит не в базе. Данных в индексе немного — одна строка на архив, обычно несколько тысяч, — и меняются они только при сканировании, поэтому индекс дополнительно выгружается в генерируемый PHP-файл /cache/zip_index.php. PHP держит его разобранным в разделяемой памяти (OPcache), так что поиск нужного архива — это двоичный поиск в массиве без единого обращения к Postgres. Открытие книги, её скачивание и показ обложки перестали из-за этого ходить в базу.

Таблица book_zip никуда не делась: она остаётся источником, из которого файл собирается, и запасным вариантом, если файла ещё нет. Практических следствий два:

  • При обновлении библиотеки с более старой версии индекс строится автоматически при первом запуске контейнера — отдельно запускать сканирование не нужно.
  • Если удалить /cache/zip_index.php руками, библиотека продолжит работать, обращаясь к таблице, и напишет об этом в лог; файл вернётся после следующего «Сканирования ZIP» или перезапуска контейнера.

Есть одна тонкость, которую стоит знать: если диапазоны двух архивов пересекаются и книга попадает в оба, выбирается архив с более поздним началом диапазона, то есть более узкий. Благодаря этому одно-книжный zip, созданный разделом «Добавить», выигрывает у большого торрент-архива, который формально накрывает тот же идентификатор.

Как разложить по дискам

  • /flibusta — только чтение (:ro в docker-compose). Это может быть отдельный большой диск, RAID, том NAS, даже сетевой ресурс по NFS или SMB: библиотека только читает из архивов, скорость сети редко становится узким местом. Нужны права на чтение для uid 82.
  • /cache — на запись, желательно на быстром диске: сюда пишутся распакованные книги, обложки, логи. Если планируете много скачивать на лету или торрентами через «Сервис», заложите место.
  • /sql — несколько гигабайт под распакованные дампы; растёт при каждом обновлении дампа только до размера текущего.

Полная коллекция fb2 на сегодня занимает несколько сотен гигабайт, usr — ещё столько же. Если диска на всё не хватает, начните с fb2 или вообще с пустой директории — библиотека умеет работать с частичным архивом, об этом отдельная статья.

Проверка, что архивы «увидены»

После сканирования на странице «Сервис» обновится «Последний скан ZIP» и «Размер архива». Точный список того, что попало в индекс, можно получить из базы (как подключиться — в статье о скриптах):

SELECT filename, start_id, end_id, usr FROM book_zip ORDER BY usr, start_id;

Или, без базы, прямо в файле индекса:

docker exec flibusta-fpm head -20 /cache/zip_index.php

Число архивов в файле и в таблице должно совпадать; если нет — запустите «Сканирование ZIP», оно перестраивает оба.

Если какого-то архива в списке нет — проверьте имя файла по схеме выше и права на чтение, и загляните в /cache/log/update_zip_list.log.