Rust-утилита для генерации совместимых с Flibusta файлов .inpx по SQL-дампам и локальным zip-архивам с поддержкой инкрементального merge.
  • Rust 80.6%
  • Shell 19.4%
Find a file
2026-05-01 20:52:17 +03:00
.forgejo/workflows Fix ci/cd upload artifact 2026-03-15 12:54:14 +03:00
data Initial commit 2026-03-15 12:36:48 +03:00
scripts Fix script error on second run at first month day 2026-05-01 20:52:17 +03:00
src Fix legal number holes 2026-03-25 10:11:47 +03:00
.gitignore Initial commit 2026-03-15 12:36:48 +03:00
Cargo.lock Fix cargo.lock file version 2026-03-25 10:31:59 +03:00
Cargo.toml Fix legal number holes 2026-03-25 10:11:47 +03:00
LICENSE Initial commit 2026-03-15 12:36:48 +03:00
README.md Fix legal number holes 2026-03-25 10:11:47 +03:00

inpx_creator_rs

Rust-утилита для генерации совместимых с Flibusta файлов .inpx по SQL-дампам и локальным zip-архивам с поддержкой инкрементального merge.

Проект также используется как backend для сценария обновления через scripts/run_cycle.sh: скачивание daily-архивов, скачивание SQL, сборка недельных архивов, месячная упаковка и итоговое обновление flibusta.inpx.

Проект рассчитан на большие библиотеки, где полная пересборка слишком дорогая и на практике нужен инкрементальный режим.

Что делает утилита

  • импортирует таблицы Flibusta из SQL-дампов во временную SQLite-базу
  • строит содержимое INPX по метаданным из БД и содержимому архивов
  • поддерживает локальную группированную раскладку classic, пригодную для использования с inpx-web: https://github.com/bookpauk/inpx-web
  • поддерживает раскладку archive, где каждому входному zip соответствует свой .inp
  • умеет инкрементально сливать новые данные с уже существующим .inpx
  • умеет читать метаданные FB2 из новых архивов глубже, чем просто по SQL
  • содержит pipeline-подкоманды для скачивания и сборки архивов, которые использует run_cycle.sh

Реализованные возможности

  • SQL import
    • читает SQL-дампы из каталога --sql-dir
  • Выходные метафайлы
    • collection.info
    • version.info
    • structure.info в локальном группированном режиме
    • archives.info в локальном группированном режиме
  • Раскладки
    • classic по умолчанию: fb2|usr + язык + ok|del + жанровый/meta-суффикс
    • archive: один .inp на один входной zip-архив
  • Инкрементальный merge
    • --merge-base existing.inpx
    • дедупликация и замена по LIBID
    • merge содержимого archives.info
    • удаление старых архивов из archives.info, если новый архив полностью перекрывает их числовой диапазон в том же потоке fb2 или usr
  • Обработка подмножества архивов
    • --archives-list path/to/list.txt — один архив в строке
    • полезно для weekly-обновлений, когда нужно обработать только новые архивы
  • MetaGenres
    • --metagenres /path/to/metagenres.xls
    • использует лист libgenrelist-meta (GenreCode -> meta) для более точного выбора суффиксов в classic
  • Глубокий разбор FB2
    • --read-fb2 none|last|all
    • --prefer-fb2 ignore|merge|complement|replace
    • читает FB2 прямо из zip для новых и выбранных записей и использует sequence и метаданные из самих книг

Файлы MetaGenres в репозитории

Оба файла лежат в data/:

  • data/metagenres.xls — исходный файл
  • data/metagenres.csv — UTF-8-копия для удобного diff и ревью

Поведение загрузчика для --metagenres:

  • если путь оканчивается на .csv, используется CSV-парсер
  • иначе, для .xls или .xlsx, используется парсер табличного файла

Для автоматизации рекомендуется:

  • использовать --metagenres ./data/metagenres.csv
  • хранить data/metagenres.xls как исходный upstream-артефакт

Сборка

cargo build --release

Сценарий обновления

Основная точка входа:

./scripts/run_cycle.sh

run_cycle.sh работает как оркестратор и делегирует загрузку и сборку бинарнику inpx_creator_rs.

Текущая логика работы:

  • при каждом запуске скачиваются daily-архивы
  • по воскресеньям скачиваются SQL, собираются недельные архивы и обновляется flibusta.inpx
  • первого числа месяца при необходимости скачиваются SQL, хвостовые daily-архивы превращаются в weekly, затем собираются месячные архивы и обновляется flibusta.inpx
  • если первое число выпадает на воскресенье, weekly-архивы все равно собираются, но промежуточный weekly merge в INPX пропускается, потому что сразу после него идет monthly merge

Удержание weekly-архивов:

  • scripts/run_cycle.sh управляет этим через KEEP_WEEKLY
  • текущее значение по умолчанию — KEEP_WEEKLY=0, то есть weekly-архивы удаляются после включения в monthly

Режим обработки:

  • PROCESS_MODE="all" — обрабатывать и FB2, и USR
  • PROCESS_MODE="fb2" — обрабатывать только FB2
  • PROCESS_MODE="usr" — обрабатывать только USR

Если нужен облегченный вариант библиотеки без USR-архивов, можно использовать PROCESS_MODE="fb2".

Схема именования архивов:

  • daily FB2: f.fb2.*.zip
  • daily USR: f.n.*.zip
  • weekly FB2: a.fb2-<start>-<end>.zip
  • weekly USR: a.usr-<start>-<end>.zip
  • итоговые архивы библиотеки FB2: f.fb2-<start>-<end>.zip
  • итоговые архивы библиотеки USR: f.usr-<start>-<end>.zip

Для weekly и monthly используется сквозная нумерация по общей цепочке FB2 + non-FB2:

  • если у одного потока отсутствует первый или последний диапазон, имя архива все равно берется по общим границам обоих потоков
  • например, a.fb2-864865-865122.zip и a.usr-864865-865122.zip допустимы даже если в USR нет архива 864960-865019

Структура каталога после распаковки релиза

inpx_creator_rs-linux-x86_64/
  inpx_creator_rs
  README.md
  data/
    metagenres.csv
    metagenres.xls
  scripts/
    run_cycle.sh
  sql/                         # распакованные SQL-дампы
  runtime/
    state/                     # urllist, last_* маркеры, временные INPX, backup
    logs/                      # run_cycle.log
    fb2_daily/                 # daily FB2 zip до weekly-упаковки
    usr_daily/                 # daily USR zip до weekly-упаковки
    sql_daily/                 # скачанные .sql.gz
    tmp/

Обновлятор предполагается распаковывать в один каталог на NAS, например в /Media/Flibusta.Net/inpx_creator_rs-linux-x86_64. Все служебные данные остаются внутри этой папки, а боевая библиотека и production flibusta.inpx могут жить отдельно.

Модель путей по умолчанию в scripts/run_cycle.sh:

  • LIBRARY_ROOT=/volume1/Media/Flibusta.Net
  • недельные и месячные архивы пишутся в $LIBRARY_ROOT
  • production flibusta.inpx читается и обновляется по пути $LIBRARY_ROOT/flibusta.inpx
  • SQL-дампы и служебное состояние хранятся внутри каталога обновлятора

Недостающие внутренние каталоги создаются автоматически при запуске run_cycle.sh.

Webhook-уведомления

В scripts/run_cycle.sh есть переменная WEBHOOK_URL.

Она используется для отправки HTTP webhook-уведомлений в двух случаях:

  • если сам сценарий завершился ошибкой
  • если обнаружена дыра в сквозной нумерации архивов перед weekly или monthly сборкой

Проверка дыр учитывает общую последовательность FB2 и non-FB2:

  • если для диапазона есть только f.fb2.*.zip или только f.n.*.zip, это не считается дырой само по себе
  • ошибкой считается только настоящий разрыв в общей цепочке диапазонов или пересечение несовместимых диапазонов

Если уведомления не нужны, оставьте:

WEBHOOK_URL=""

Если webhook включен, скрипт отправляет JSON с типом события, текстом сообщения, именем хоста и временем события.

Развертывание

После распаковки пакета:

  1. При необходимости поправьте путь LIBRARY_ROOT в scripts/run_cycle.sh.
  2. При необходимости выберите режим обработки через PROCESS_MODE в scripts/run_cycle.sh.
  3. Проверьте запуск бинарника:
./inpx_creator_rs --help
  1. Запустите цикл обновления:
./scripts/run_cycle.sh

Справка по CLI

Основной режим сборки INPX:

--sql-dir <PATH>           Каталог SQL-дампов (по умолчанию: ./sql)
--out <PATH>               Путь к выходному .inpx
--db-name <NAME>           Имя библиотеки в метаданных (по умолчанию: flibusta)

--archives-dir <PATH>      Каталог локальных zip-архивов
--process <fb2|usr|all>    Фильтр типа книг (по умолчанию: fb2)

--layout <classic|archive> Режим раскладки (по умолчанию: classic)
--merge-base <PATH>        Существующий .inpx для инкрементального merge
--archives-list <PATH>     Список архивов для обработки, по одному в строке
--metagenres <PATH>        Путь к metagenres.xls или metagenres.csv

--read-fb2 <none|last|all> Режим глубокого чтения FB2 (по умолчанию: none)
--prefer-fb2 <ignore|merge|complement|replace>
                           Приоритет FB2 по отношению к данным из БД (по умолчанию: ignore)

Pipeline-подкоманды:

download-daily --source-url <URL> [--process <fb2|usr|all>] --data-dir <PATH> --fb2-daily-dir <PATH> --usr-daily-dir <PATH>
download-sql --source-url <URL> --data-dir <PATH> --sql-daily-dir <PATH> --output-sql-dir <PATH>
make-archives --fb2-daily-dir <PATH> --usr-daily-dir <PATH> --output-dir <PATH> [--fb2-prefix <PREFIX>] [--usr-prefix <PREFIX>]
pack-archives --archives-dir <PATH> --input-prefix <PREFIX> --output-prefix <PREFIX> [--range-prefix <PREFIX>]... [--keep-source]

Значение параметров

  • --sql-dir <PATH>
    • каталог с SQL-дампами lib.libbook.sql и другими таблицами
    • используется для построения временного SQLite-каталога
  • --out <PATH>
    • путь к итоговому .inpx
    • если параметр не задан, имя генерируется автоматически из имени базы и даты дампа
  • --db-name <NAME>
    • логическое имя коллекции, записываемое в collection.info
  • --archives-dir <PATH>
    • каталог с локальными zip-архивами *.zip
    • включает локальный offline-режим сборки INPX
  • --process <fb2|usr|all>
    • fb2: включать только FB2-книги и FB2-архивы
    • usr: включать только non-FB2-книги и архивы
    • all: включать оба потока
  • --layout <classic|archive>
    • classic: группирует строки по type + lang + del + suffix
    • archive: создает по одному .inp на каждый входной архив
  • --merge-base <PATH>
    • существующий .inpx, который используется как базовый для merge
    • merge удаляет старые записи по LIBID, добавляет новые и переиспользует исторические suffix-группы
  • --archives-list <PATH>
    • текстовый файл со списком архивов, по одному имени в строке
    • в текущем запуске будут обработаны только эти архивы
    • это основной режим для weekly/monthly инкрементальных обновлений
  • --metagenres <PATH>
    • путь к metagenres.xls или metagenres.csv
    • используется для более точного выбора meta-категории в classic
  • --read-fb2 <none|last|all>
    • none: не читать FB2 XML из архивов
    • last: читать FB2 только для пропущенных книг с book_id > max_db_book_id
    • all: читать FB2 для всех подходящих новых или отсутствующих записей
  • --prefer-fb2 <ignore|merge|complement|replace>
    • задает приоритет между данными БД и FB2
    • ignore: доверять только БД
    • merge: если в FB2 есть sequence, заменить sequence-поля из БД
    • complement: использовать sequence из FB2 только если в БД оно пустое
    • replace: заменять author/genre/title/sequence/lang/keywords значениями из FB2, если они есть

Типовые сценарии

1. Полная локальная сборка grouped INPX

./inpx_creator_rs \
  --sql-dir ./sql \
  --archives-dir /volume1/Media/Flibusta.Net \
  --layout classic \
  --metagenres ./data/metagenres.csv \
  --process all \
  --out ./flibusta_full_local.inpx

2. Недельное инкрементальное обновление

Подготовьте список новых архивов, например ./runtime/state/archives_weekly.txt.

./inpx_creator_rs \
  --sql-dir ./sql \
  --archives-dir /volume1/Media/Flibusta.Net \
  --archives-list ./runtime/state/archives_weekly.txt \
  --merge-base /volume1/Media/Flibusta.Net/flibusta.inpx \
  --layout classic \
  --metagenres ./data/metagenres.csv \
  --process all \
  --read-fb2 all \
  --prefer-fb2 merge \
  --out ./runtime/state/flibusta_next.inpx

Пояснение по параметрам:

  • ./inpx_creator_rs — запуск бинарника
  • --sql-dir ./sql — использовать локально скачанные SQL-дампы внутри обновлятора
  • --archives-dir /volume1/Media/Flibusta.Net — читать архивы из боевой библиотеки
  • --archives-list ./runtime/state/archives_weekly.txt — обрабатывать только новые архивы текущего цикла
  • --merge-base /volume1/Media/Flibusta.Net/flibusta.inpx — брать существующий production INPX как базу
  • --layout classic — использовать классическую группировку
  • --metagenres ./data/metagenres.csv — использовать локальное отображение жанров в meta-категории
  • --process all — обрабатывать и FB2, и USR
  • --read-fb2 all — глубоко читать FB2 для новых и отсутствующих записей
  • --prefer-fb2 merge — sequence из FB2 имеет приоритет при наличии
  • --out ./runtime/state/flibusta_next.inpx — писать временный результат внутрь служебного каталога обновлятора

Почему это быстро:

  • сканируются только архивы, перечисленные в --archives-list
  • используется merge с существующим .inpx, а не полная пересборка
  • глубокий разбор FB2 ограничен новыми или нужными файлами

Для режима только FB2 в run_cycle.sh установите:

PROCESS_MODE="fb2"

3. Месячная упаковка с перекрывающимися weekly-диапазонами

Если monthly-архив перекрывает диапазоны, уже присутствующие в weekly-архивах, merge оставляет более новый архив и удаляет полностью перекрытые старые диапазоны из archives.info.

Команда та же, что и для weekly, но в --archives-list нужно передать список monthly-архивов.

4. Режим archive-per-inp

./inpx_creator_rs \
  --sql-dir ./sql \
  --archives-dir /volume1/Media/Flibusta.Net \
  --layout archive \
  --process all \
  --out ./flibusta_archive_layout.inpx

5. Скачивание daily-архивов

./inpx_creator_rs download-daily \
  --source-url https://flibusta.is/daily/ \
  --process fb2 \
  --data-dir ./runtime/state \
  --fb2-daily-dir ./runtime/fb2_daily \
  --usr-daily-dir ./runtime/usr_daily

6. Скачивание и распаковка SQL-дампов

./inpx_creator_rs download-sql \
  --source-url https://flibusta.is/sql/ \
  --data-dir ./runtime/state \
  --sql-daily-dir ./runtime/sql_daily \
  --output-sql-dir ./sql

7. Сборка weekly-архивов из daily-хвоста

./inpx_creator_rs make-archives \
  --fb2-daily-dir ./runtime/fb2_daily \
  --usr-daily-dir ./runtime/usr_daily \
  --output-dir /volume1/Media/Flibusta.Net \
  --fb2-prefix a.fb2 \
  --usr-prefix a.usr

8. Сборка monthly-архива из weekly-архивов

./inpx_creator_rs pack-archives \
  --archives-dir /volume1/Media/Flibusta.Net \
  --input-prefix a.fb2 \
  --output-prefix f.fb2

Глубокий разбор FB2

Из секции title-info читаются:

  • genre
  • author (last-name, first-name, middle-name)
  • book-title
  • sequence (name, number)
  • lang
  • keywords

Режимы:

  • --read-fb2 none — не разбирать FB2 XML
  • --read-fb2 last — разбирать FB2 только для отсутствующих книг с book_id > max_db_book_id
  • --read-fb2 all — разбирать все подходящие новые книги

Приоритет:

  • ignore — доверять только БД
  • merge — предпочитать sequence из FB2
  • complement — брать sequence из FB2 только если в БД его нет
  • replace — заменять поля БД данными FB2, если они присутствуют

Совместимость

  • строки .inp используют разделитель \x04 и окончания \r\n
  • локальная grouped-структура использует формат:
    • AUTHOR;GENRE;TITLE;SERIES;SERNO;FILE;SIZE;LIBID;DEL;EXT;DATE;INSNO;FOLDER;LANG;LIBRATE;KEYWORDS;
  • именование групп в classic улучшается за счет:
    1. наследования suffix из --merge-base
    2. отображения через metagenres.xls
    3. fallback-логики по keyword и genre

Цель этой комбинации — быть максимально близкой к существующему поведению именования в flibusta.inpx.

Практические замечания для больших библиотек

  • для NAS и слабых серверов предпочтителен инкрементальный режим с --archives-list и --merge-base
  • metagenres.xls стоит хранить рядом с остальными релизными файлами обновлятора
  • режим --read-fb2 all --prefer-fb2 merge полезен, когда weekly-данные требуют исправления sequence без полной пересборки
  • старый .inpx имеет смысл сохранять как rollback-точку перед заменой production-файла
  • для обычной эксплуатации лучше использовать run_cycle.sh, а подкоманды вызывать вручную только для диагностики, восстановления или разовых операций

Что было основой

Проект не возник с нуля. В качестве ориентиров и практической базы использовались следующие материалы:

  • InpxCreator: https://github.com/rupor-github/InpxCreator
  • исходные shell-скрипты для обновления библиотеки: https://booktracker.org/viewtopic.php?t=102232

Итоговая реализация написана отдельно, потому что в исходных решениях были ограничения, которые мешали текущему сценарию эксплуатации, инкрементальному обновлению и более удобной автоматизации.

Быстрая проверка

cargo check
cargo test