Публикация была переведена автоматически. Исходный язык: Русский
Система видеонаблюдения пишет 24/7. За год накапливается 5TB видео. База данных знает про 80% файлов, остальные 20% — "потерянные": файл есть на диске, в базе нет.
Нужно найти эти файлы и добавить в индекс.
Написал скрипт индексации. Рекурсивно обходит директории, находит .flv и .mp4, проверяет есть ли в базе, если нет — запускает ffprobe для получения длительности, добавляет запись в PostgreSQL.
Первый запуск — скрипт завис через 200 файлов. Смотрю htop — 15 процессов ffprobe висят, CPU 0%, не отвечают. Убиваю вручную, скрипт продолжает, через 100 файлов
опять зависает.
Проблема в битых файлах. Камеры иногда пишут FLV с поврежденными метаданными: запись оборвалась, заголовок неполный, индексы потеряны. ffprobe пытается прочитать — и зависает навсегда, ждёт данных которых нет.
Решение: таймаут на каждый процесс. Запускаю ffprobe через spawn, добавляю в Set, ставлю setTimeout на 10 секунд. Если за 10 секунд не вернул результат —
kill('SIGKILL'), удаляю из Set, следующий файл.
const ffprobeProcesses = new Set();
function ffprobeWithTimeout(filePath, timeout = 10000) {
return new Promise((resolve, reject) => {
const ffprobeProcess = spawn('ffprobe', ['-v', 'quiet', filePath]);
ffprobeProcesses.add(ffprobeProcess);
const timeoutId = setTimeout(() => {
ffprobeProcess.kill('SIGKILL');
ffprobeProcesses.delete(ffprobeProcess);
reject(new Error('ffprobe timeout'));
}, timeout);
// ... обработка результата
});
}
Запустил снова. Файлы обрабатываются, битые — пропускаются с ошибкой timeout. За час проиндексировал 10000 файлов. Из них ~300 битых (3%), остальные успешно добавлены в базу.
Но осталась проблема: битые файлы нужны, они содержат видео, просто метаданные повреждены. Нельзя просто удалить.
Добавил восстановление через конвертацию: если ffprobe не смог прочитать FLV, конвертирую его в MP4 через ffmpeg -c copy (без перекодирования, только ремукс контейнера). MP4 восстанавливает временные метки, после этого ffprobe читает нормально.
// FLV битый → конвертируем в temp.mp4
ffmpeg -i broken.flv -c copy -movflags faststart temp.mp4
// ffprobe читает temp.mp4 успешно
ffprobe temp.mp4 → длительность 15:23
// Сохраняем длительность в БД, удаляем temp.mp4
Это медленно (конвертация 15-минутного файла занимает ~20 секунд), но лучше медленно чем никогда. Из 300 битых файлов восстановил 280. Остальные 20 — совсем мертвые, даже ffmpeg не помог.
Сейчас скрипт индексации запускается по cron раз в день ночью. Проверяет новые файлы, добавляет в базу, восстанавливает битые. Без нагрузки на диск днём, когда идет
запись. Ночью система простаивает — самое время для индексации.
Важная деталь: валидация имени файла. Камеры пишут в формате 2024-11-18T14-30-00.flv. Но иногда появляются левые файлы: временные, тестовые, с неправильными именами.
Regex фильтрует:
const regex = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}\.(flv|mp4)$/;
if (!regex.test(fileName)) return; // пропускаем
За год работы проиндексировал 500,000+ файлов. База PostgreSQL хранит: путь, камера, начало записи, длительность, размер. Поиск видео за любую дату занимает миллисекунды. Без индексации — пришлось бы вручную лазить по папкам.
Система видеонаблюдения пишет 24/7. За год накапливается 5TB видео. База данных знает про 80% файлов, остальные 20% — "потерянные": файл есть на диске, в базе нет.
Нужно найти эти файлы и добавить в индекс.
Написал скрипт индексации. Рекурсивно обходит директории, находит .flv и .mp4, проверяет есть ли в базе, если нет — запускает ffprobe для получения длительности, добавляет запись в PostgreSQL.
Первый запуск — скрипт завис через 200 файлов. Смотрю htop — 15 процессов ffprobe висят, CPU 0%, не отвечают. Убиваю вручную, скрипт продолжает, через 100 файлов
опять зависает.
Проблема в битых файлах. Камеры иногда пишут FLV с поврежденными метаданными: запись оборвалась, заголовок неполный, индексы потеряны. ffprobe пытается прочитать — и зависает навсегда, ждёт данных которых нет.
Решение: таймаут на каждый процесс. Запускаю ffprobe через spawn, добавляю в Set, ставлю setTimeout на 10 секунд. Если за 10 секунд не вернул результат —
kill('SIGKILL'), удаляю из Set, следующий файл.
const ffprobeProcesses = new Set();
function ffprobeWithTimeout(filePath, timeout = 10000) {
return new Promise((resolve, reject) => {
const ffprobeProcess = spawn('ffprobe', ['-v', 'quiet', filePath]);
ffprobeProcesses.add(ffprobeProcess);
const timeoutId = setTimeout(() => {
ffprobeProcess.kill('SIGKILL');
ffprobeProcesses.delete(ffprobeProcess);
reject(new Error('ffprobe timeout'));
}, timeout);
// ... обработка результата
});
}
Запустил снова. Файлы обрабатываются, битые — пропускаются с ошибкой timeout. За час проиндексировал 10000 файлов. Из них ~300 битых (3%), остальные успешно добавлены в базу.
Но осталась проблема: битые файлы нужны, они содержат видео, просто метаданные повреждены. Нельзя просто удалить.
Добавил восстановление через конвертацию: если ffprobe не смог прочитать FLV, конвертирую его в MP4 через ffmpeg -c copy (без перекодирования, только ремукс контейнера). MP4 восстанавливает временные метки, после этого ffprobe читает нормально.
// FLV битый → конвертируем в temp.mp4
ffmpeg -i broken.flv -c copy -movflags faststart temp.mp4
// ffprobe читает temp.mp4 успешно
ffprobe temp.mp4 → длительность 15:23
// Сохраняем длительность в БД, удаляем temp.mp4
Это медленно (конвертация 15-минутного файла занимает ~20 секунд), но лучше медленно чем никогда. Из 300 битых файлов восстановил 280. Остальные 20 — совсем мертвые, даже ffmpeg не помог.
Сейчас скрипт индексации запускается по cron раз в день ночью. Проверяет новые файлы, добавляет в базу, восстанавливает битые. Без нагрузки на диск днём, когда идет
запись. Ночью система простаивает — самое время для индексации.
Важная деталь: валидация имени файла. Камеры пишут в формате 2024-11-18T14-30-00.flv. Но иногда появляются левые файлы: временные, тестовые, с неправильными именами.
Regex фильтрует:
const regex = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}\.(flv|mp4)$/;
if (!regex.test(fileName)) return; // пропускаем
За год работы проиндексировал 500,000+ файлов. База PostgreSQL хранит: путь, камера, начало записи, длительность, размер. Поиск видео за любую дату занимает миллисекунды. Без индексации — пришлось бы вручную лазить по папкам.