Каждый из вас хоть раз ловил себя на мысли: «А почему бы не начать слушать книги вместо того, чтобы их читать?». Пока едешь в метро, стоишь в пробке, занимаешься домашней рутиной или вместо приевшейся музыки в спортзале — сценариев масса.
В сети полно литературы, но если вы эстет и ищете что-то глубже «Онегина» или модных бестселлеров, то наверняка сталкивались с проблемой: нужной книжки в аудио просто не существует.
Так вот, тут мы попадаем в ловушку. Технологий синтеза речи (TTS) сейчас море, а вменяемого инструмента, чтобы массово превращать текст в звук, нет.
Либо вы платите корпорациям за каждый символ через официальные API, превращая чтение в дорогую привычку, либо ковыряете софт, застрявший в эпохе Windows XP, который озвучивает файлы дольше, чем вы бы читали их вслух сами.
Протокол личного ада: почему «Балаболка» больше не тянет
Каждый, кто хоть раз задавался целью превратить увесистый FB2-файл в аудио, так или иначе приходил к такой программе, как «Балаболка». Для тех, кто видит это название впервые: это бесплатная утилита для чтения текстовых файлов вслух. Она работает как посредник: вы скачиваете сторонние голосовые движки, закидываете в программу книгу, а она прогоняет текст через эти голоса и сохраняет результат в MP3 или WAV.
Честно скажу: это великолепный софт, который годами спасал мои (и, уверен, ваши) уши. Но сегодня он упирается в системный потолок, а попытка использовать его для «промышленной» озвучки превращается в протокол личного ада.
Главный враг — линейность. Программа спотыкается, читая текст глава за главой, страница за страницей.
Когда вы подключаете к ней качественные нейронные голоса (например, Microsoft Neural), вы попадаете в ловушку ограничений API. Софт обращается к облаку в один-единственный поток. В итоге скорость генерации лишь немногим превышает скорость человеческой речи. Если книга рассчитана на 15 часов прослушивания, «Балаболка» будет ее обрабатывать те же 15 часов, а то и дольше.
Наконец, есть ресурсный голод и ограничения GUI. На длинных дистанциях интерфейсные приложения Windows часто демонстрируют утечки памяти. Интерфейс начинает подтормаживать, а любой системный сбой или случайная перезагрузка обрывают многочасовую запись без возможности восстановления прогресса.
Я ощутил этот «голод» физически, когда нужно было озвучить пласт научной литературы. Пока домашний десктоп шумел вентиляторами, а прогресс двигался крайне медленно, мой сервер в дата-центре простаивал без нагрузки.
Стало очевидно: озвучивать книгу объемом в миллион знаков через «окна» и кнопки — это в 10–15 раз медленнее, чем позволяет пропускная способность современных сетей. Мне нужно было решение, способное работать автономно и утилизировать гигабитный канал связи на 100%.
Архитектура «Прометея»
Идея «Прометея» родилась из простого админского вопроса: «Зачем ждать одного диктора, если я могу нанять 16?». Если облако Microsoft (через edge-tts) ограничивает скорость на одно соединение, значит, нам нужно много соединений. Было решено применить принцип «Divide et impera» — разделяй и властвуй. Вместо того чтобы просить нейросеть прочитать книгу целиком, мы рубим ее на куски (мелкие чанки) и запускаем армию виртуальных дикторов одновременно.
Параллелизм на стероидах (xargs -P)
Облако Microsoft (Edge TTS) ограничивает скорость отдачи потока на одно соединение, но практически не ограничивает количество одновременных запросов с одного IP. То есть использование инструмента xargs с флагом -P (parallel) позволяет запустить 12–16 потоков одновременно.
В таком режиме мы заставляем облако работать на пике производительности, утилизируя доступный канал связи, а не ожидая своей очереди в однопоточном режиме. Это превращает процесс из изнурительного ожидания в высокоскоростную конвейерную сборку.
Проблема «битого символа» и fold -s
Первые тесты параллельной сборки выявили проблему. Кириллица в UTF-8 не так проста: каждый символ занимает два байта. Если резать файл как есть, по размеру (байтам), разрез обязательно попадает на середину буквы. В итоге вместо текста Python получает «мусорный» байт, вылетает с ошибкой «UnicodeDecodeError», и процесс сразу крашится.
Решение нашлось в классической утилите fold с флагом -s. Она умеет делать «мягкие» переносы строк, ориентируясь на пробелы. Это гарантирует, что каждый чанк текста останется валидным для Python-декодера, а слова не будут превращены в нечитаемый набор байтов. Теперь данные уходят в облако в идеальном состоянии.
Инфраструктурный минимализм
Предрекаю ваш вопрос: «Почему не через виртуальное окружение Python?». Мой подход строится на принципе «одна задача — один сервер». Разворачивая «Прометей» на выделенном воркере, я превращаю его в узкоспециализированный вычислительный модуль.
Изоляция через venv здесь избыточна. Она лишь усложняет цепочку вызовов в Bash-скриптах и добавляет лишний слой абстракции там, где нужна прямая работа с системными бинарниками. Нам нужен максимально короткий путь от текста к звуку.
Disk-First против RAM-First
Многие админы по привычке используют /tmp (tmpfs) для хранения временных данных. Однако при озвучке тяжелых книг на 170+ чанков объем промежуточных MP3-данных легко переваливает за 2 ГБ. На бюджетных инстансах это приведет к ошибке «No space left on device».
Чтобы не зависеть от объемов RAM, весь рабочий цикл «Прометея» перенесен на физический диск (NVMe). Это гарантирует стабильность на любых объемах: будь то короткая статья или многотомный архив научной литературы.
Бенчмарки и стресс-тест
Для испытаний был выбран выделенный сервер Selectel конфигурации CL13. Под капотом у него:
- CPU: 12th Gen Intel Core i3-12100 (4 ядра / 8 потоков);
- RAM: 16 ГБ DDR4;
- Disk: Samsung 970 EVO Plus 500 ГБ (NVMe);
- OS: Debian 12 Stable.
Именно его NVMe-шина должна была выдержать шквал одновременных записей. В качестве подопытного объекта я взял книгу Адама Хиггинботама «Чернобыль. История катастрофы». Почему именно ее? Это сложный текст с обилием технических терминов, фамилий и цифр — идеальный стресс-тест для интонаций нейронки.
В 13:50 мы запустили конвейер в 12 потоков. Книга объемом около 1,2 млн знаков была разбита на 172 чанка. К 13:57 система вышла на пик сетевой активности, показывая 91% готовности, а еще через минуту генерация всех чанков была завершена. Спустя еще три минуты, в 14:01, ffmpeg в режиме copy mode склеил их в один файл.
В итоге на 24 часа готового аудио ушло всего 11 минут. За это время я мог заварить себе чай, пока система выдает результат с коэффициентом ускорения x130. После этого возвращаться к «Балаболке», где тот же процесс растянулся бы на сутки ожидания, кажется просто бессмысленным.
Анализ системных метрик
Пока на экране мелькали проценты прогресса, мне было интересно заглянуть в систему изнутри и узнать, как сервер Selectel переваривает этот массив из 12 потоков озвучки.
Первым делом я проверил дисковую активность через iostat. При суммарной скорости записи около 21 МБ/с утилизация NVMe-накопителя составила всего 1,6%. Задержка записи (w_await) в 2,4 мс подтверждает: для современного диска такая нагрузка — это даже не разминка, он ее просто не замечает.
Нагрузка на диск (iostat):
Device w/s wkB/s w_await %util
nvme0n1 203.00 21540.00 2.44 1.60
Процессор i3-12100 (Alder Lake) вполне неплохо и стабильно управляет 12 потоками. Текущая вычислительная мощность используется не полностью, так что тут есть небольшой запас на разбег в случае необходимости — сохраняется запас производительности и низкое время отклика.
Нагрузка на CPU (top):
load average: 0.23, 0.08, 0.02
%Cpu(s): 2.3 us, 2.3 sy, 0.0 ni, 95.5 id
Вопросы «аудиофилов»
Слышу вопрос от эстетов: «А как же связность речи на стыках чанков?» Технически каждая нейронная сессия в edge-tts — это независимый акт генерации. Однако благодаря тому, что мы используем fold -s, разрез файла происходит строго на границах слов и предложений.
Нейросетевой движок Microsoft Neural достаточно умен: он имеет встроенные алгоритмы «вдоха» и «выдоха» в начале и конце каждой фразы. В итоге стыки чанков воспринимаются слушателем как естественные паузы диктора между абзацами. На скорости 1,2x с включенной функцией «Skip Silence» в плеере переходы становятся абсолютно бесшовными.
Инструкция по развертыванию
Я делюсь этой технологией по принципу «не жалко». Ресурс должен приносить пользу, а не гнить в приватных папках. Ниже — ваш пошаговый маршрут к созданию собственной системы генерации контента (или фабрики аудиокниг, кому как больше нравится) и детальный разбор того, как это работает изнутри.
Подготовка среды
Сначала необходимо зайти на сервер под пользователем `root`. Мы развернем базовый набор инструментов: Python для работы движка, FFmpeg для обработки звука, Pandoc для парсинга книг из FB2 и Aria2 для скоростной закачки моделей и данных.
Весь процесс подготовки занимает меньше минуты. Последовательно выполните следующие команды.
Установка системных пакетов и движка
apt update && apt install -y python3-pip ffmpeg pandoc aria2
pip install edge-tts --break-system-packages
Флаг –break-system-packages необходим в современных версиях Debian/Ubuntu для установки Python-пакетов глобально, минуя виртуальные окружения.
Создание структуры директорий
Чтобы файлы не перемешивались, создадим рабочую иерархию папок для текстов, готового аудио и временных файлов:
mkdir -p /root/books/{text,audio,tmp}
Скрипт prometheus.sh (v7.2.1 Master Edition)
Теперь создадим управляющий скрипт. Вам нужно создать файл в папке /root/, вставить в него код и разрешить системе его запуск (дать право на запуск) командой chmod +x. Для этого:
- создайте файл: nano /root/prometheus.sh;
- вставьте код скрипта;
- сохраните (Ctrl+O, Enter) и выйдите (Ctrl+X);
- сделайте файл исполняемым (код ниже).
chmod +x /root/prometheus.sh
Показать код скрипта (кликните, чтобы развернуть).
#!/bin/bash
# ======================================================
# PROJECT PROMETHEUS v7.2.1 | MASTER EDITION
# ======================================================
# КОНФИГУРАЦИЯ
THREADS=12 # Оптимально для стабильности без бана по IP
VOICE="ru-RU-DmitryNeural" # Эталонный мужской голос
RATE="+15%" # Сжатие пауз и темп
OUTPUT_BASE="/root/books/audio"
TEMP_BASE="/root/books/tmp"
# 1. ВВОД ДАННЫХ (Поддерживает автозаполнение через Tab)
read -e -p "Путь к файлу (txt/fb2): " INPUT_PATH
INPUT_PATH=$(echo "$INPUT_PATH" | sed -e 's/^"//' -e 's/"$//' -e "s/^'//" -e "s/'$//")
[ ! -f "$INPUT_PATH" ] && echo "ERR: Файл не найден!" && exit 1
# 2. ПОДГОТОВКА
WORKDIR="${TEMP_BASE}/work_$(date +%s)"
mkdir -p "$WORKDIR" "$OUTPUT_BASE"
# 3. ПАРСИНГ МЕТАДАННЫХ (Вытаскиваем автора и название из FB2)
FILE_EXT="${INPUT_PATH##*.}"
ORIG_NAME=$(basename "$INPUT_PATH" ."$FILE_EXT")
if [[ "$FILE_EXT" == "fb2" ]]; then
F_NAME=$(grep -i -oP '(?<=<first-name>).*?(?=</first-name>)' "$INPUT_PATH" | head -1)
L_NAME=$(grep -i -oP '(?<=<last-name>).*?(?=</last-name>)' "$INPUT_PATH" | head -1)
TITLE=$(grep -i -oP '(?<=<book-title>).*?(?=</book-title>)' "$INPUT_PATH" | head -1)
BOOK_NAME="${L_NAME}_${F_NAME}__${TITLE}"
# Fallback если метаданные кривые
[ ${#BOOK_NAME} -lt 10 ] && BOOK_NAME="$ORIG_NAME"
pandoc -f fb2 -t plain "$INPUT_PATH" -o "$WORKDIR/raw.txt"
else
BOOK_NAME="$ORIG_NAME"
cp "$INPUT_PATH" "$WORKDIR/raw.txt"
fi
# Санитизация имени файла (убираем мусорные символы)
BOOK_NAME=$(echo "$BOOK_NAME" | sed 's/[[:space:]]/_/g' | sed 's/[\\/:"*?<>|]//g')
FINAL_FILE="$OUTPUT_BASE/${BOOK_NAME}.mp3"
echo -e ">>> ЗАПУСК ПРОТОКОЛА: ${BOOK_NAME}.mp3"
# 4. ОБРАБОТКА ТЕКСТА И НАРЕЗКА
sed '/^$/d' "$WORKDIR/raw.txt" > "$WORKDIR/source.txt"
fold -s -w 2000 "$WORKDIR/source.txt" | tr -d '\r' > "$WORKDIR/formatted.txt"
split -l 100 -d -a 4 "$WORKDIR/formatted.txt" "$WORKDIR/part_"
TOTAL_PARTS=$(ls "$WORKDIR"/part_[0-9]* | wc -l)
# 5. КОНВЕЙЕРНАЯ ОЗВУЧКА
export VOICE WORKDIR RATE
do_tts() {
local file=$1
edge-tts --rate="$RATE" --voice "$VOICE" --file "$file" --write-media "$file.mp3" > /dev/null 2>&1
}
export -f do_tts
# Очередь через xargs (вот здесь живет скорость)
ls "$WORKDIR"/part_[0-9]* | xargs -P $THREADS -I {} bash -c 'do_tts "{}"'
# 6. СКЛЕЙКА (БЕЗ ПОТЕРИ КАЧЕСТВА)
ls "$WORKDIR"//6ef4e6a1-9d49-47ac-bfed-170f67a815cf.selcdn.net/part_*.mp3 | sort | xargs -I {} echo "file '{}'" > "$WORKDIR/list.txt"
ffmpeg -f concat -safe 0 -i "$WORKDIR/list.txt" -c copy -y "$FINAL_FILE" > /dev/null 2>&1
rm -rf "$WORKDIR"
echo "ГОТОВО! Файл: $FINAL_FILE"
Заключение
«Прометей» — это манифест эффективности. Пока корпорации строят заборы вокруг своих API, мы строим мосты из Bash-скриптов. Я озвучил более 40 часов эксклюзивного контента за один вечер просто потому, что могу. Теперь это можете сделать и вы.
Не ждите, пока за вас решат, что и как вам слушать. Делайте это сами.