
Как я в одиночку написал сервис для расшифровки видео — и три раза чуть не забросил из-за одной и той же ошибки
Дисклеймер сразу: я не собирался писать «ещё один стартап». Мне просто было лень руками пересматривать видео, чтобы выписать из него тезисы. Дальше — история о том, как решение личной проблемы обросло LLM-цепочками, защитой от SSRF и вопросами вроде «а что если сертификат не выпустится».Сервис называется postvid.ru: ссылка на видео или файл на входе, расшифровка, тезисы, таблица, презентация и карточки для карусели на выходе.Стек, если коротко- FastAPI — веб-приложение и API, ничего экзотического.- faster-whisper, модель small — расшифровка речи. Брал именно small, потому что сервер без GPU, а medium на CPU превращает ожидание в пытку.- YandexGPT / любой OpenAI-совместимый API — выделение тезисов, с фолбэком на локальную модель через Ollama и на офлайн-алгоритм без нейросети вообще.- Pillow — генерация карточек карусели.- openpyxl, python-pptx — таблица и презентация из тех же тезисов.- SQLite — аккаунты, сессии, подписки, платежи. Да, SQLite, не Postgres — для текущей нагрузки хватает с запасом, а лишнюю инфраструктуру я себе усложнять не стал.- Docker Compose + Caddy — деплой и автовыпуск TLS-сертификатов сразу для нескольких доменов.- ЮKassa — оплата и автопродление.Пайплайн```ссылка/файл → проверка SSRF → faster-whisper (текст + таймкоды) → LLM (тезисы + цитаты + время) → {xlsx, pptx, PNG-карусель, srt}```Задача обрабатывается в пуле потоков, статус лежит в памяти процесса и отдаётся polling'ом через /api/jobs/{id}. На бумаге — просто. На практике каждый пункт этой схемы принёс свой сюрприз, и вот четыре самых запоминающихся.Грабли №1: облако решило отдохнуть, а сервис — вместе с нимПервая версия дёргала облачную LLM напрямую, без всякой защиты. Всё было отлично, пока однажды ночью модель не легла на пару минут. Для меня это была строчка в логе. Для пользователей в этот момент — «сервис сломан, ничего не работает». Неприятное ощущение, когда узнаёшь об этом постфактум.Сделал цепочку:1. Облачная модель.2. Если недоступна — локальная модель через Ollama на том же сервере.3. Если и её нет — офлайн-алгоритм: рубит текст на предложения и выбирает самые «весомые» по простым эвристикам.Плюс кулдаун: если облако упало, следующая минута задач сразу идёт в запасной вариант, не тратя время на повторный обречённый запрос. На странице результата честно пишется, какой движок сработал — cloud, local или offline. Мне кажется, это важнее любой красивой анимации загрузки: пользователь должен понимать, почему результат вдруг стал немного хуже.Грабли №2: «просто вставь ссылку» — хаКазалось бы, что сложного в том, чтобы скачать видео по ссылке. Сложного оказалось много: нужно отсечь запросы во внутреннюю сеть сервера (привет, SSRF), проверить, что схема — именно http(s), и не пускать всё подряд.```pythondef validate_public_url(url: str, allowed_hosts: set[str] | None = None) -> None: parsed = urlparse(url.strip()) if parsed.scheme not in ("http", "https"): raise SourceRejected("Разрешены только http(s) ссылки") # резолвинг хоста, отказ на приватные/ Читать далее