Mapvideo — веб-сервис для редакции телеканала. Позволяет создавать анимированные карты для новостных сюжетов в прямом эфире. Пользователь настраивает карту с точками, маршрутами и анимациями в браузере, затем скачивает готовый MP4/MXF файл.
- admin — единственный администратор. Доступ к
/adminпанели. - user — обычный пользователь редакции.
- Поля: логин, пароль
- Логотип и название "Mapvideo" на странице
- После успешного логина → редирект на
/ - Любая страница без авторизации → редирект на
/login - Сессия запоминается автоматически (httpOnly cookie
session, срок 30 дней) - Защита от CSRF: double-submit cookie (
csrf_tokencookie, не httpOnly) + заголовокX-CSRF-Tokenна мутирующих запросах GET /api/auth/csrfнужен только чтобы гарантировать наличиеcsrf_tokencookie; источник токена — cookie, а не response body- Rate limit на
POST /api/auth/login: 5 неудачных попыток с одного IP за 10 минут → 429 - Пароли хранятся в БД в хэшированном виде (bcrypt, 12 раундов)
- Доступна только пользователю
admin - Список всех пользователей: имя, дата регистрации
- Добавить пользователя (форма: логин + пароль)
- Удалить пользователя (с подтверждением)
┌──────────────────────────────────────────────────┐
│ [Имя пользователя ▼] │ ← шапка 48px
├────────────────────────────────────┬─────────────┤
│ │ │
│ ПРЕВЬЮ КАРТЫ 16:9 (строго) │ ПРАВАЯ │
│ (MapLibre подложка │ БОКОВАЯ │
│ + PixiJS анимации поверх) │ ПАНЕЛЬ │
│ │ 320px │
│ [⌂] кнопка сброса положения │ │
│ (правый нижний угол превью) │ │
├────────────────────────────────────┴─────────────┤
│ [+ Точка] [↗ Маршрут*] [⚙ Видео] [💾 Сохранить] │ ← нижняя панель 56px
└──────────────────────────────────────────────────┘
* кнопка "Маршрут" только при 2+ точках на карте
Слева поверх карты — список элементов (200px, полупрозрачный). Правая боковая панель — открывается при двойном клике на элемент.
- Соотношение сторон строго 16:9
- Пользователь видит в превью ровно то что будет на видео (строгое условие!)
- Все анимации живые постоянно (точки пульсируют, иконки движутся)
- Иконки на маршрутах зацикливаются (доехал до конца — начинает заново)
- Тема карты меняется сразу при изменении в настройках видео
- Кнопка "Сбросить положение" возвращает карту к охвату всех точек
Кнопка с именем пользователя в шапке справа:
- Тех. поддержка → модал: "Напишите в Telegram: @Requestin"
- Моя история → боковая панель с историей видео
- Выйти → разлогин + редирект на
/login
- Кнопка "Добавить точку"
- Модальное окно: текстбокс + геосаджест (Photon через backend proxy на публичный сервис)
- Геосаджест помогает, но пользователь редактирует текст сам
- "Продолжить" → карта перемещается к точке, появляется точка + подпись
- При наличии других точек — карта автомасштабируется под все точки
- Перетаскивание точки и подписи независимо друг от друга
- Двойной клик → меню настроек в правой панели
- Клик вне меню → меню закрывается
Анимация (radio): Мигающая точка | Взрыв | Огонь | Землетрясение
Мигающая точка (процедурная, без готового ассета):
- Цвет, размер, прозрачность, скорость пульсации
- Окантовка: вкл/выкл, цвет, размер, прозрачность
Взрыв (PNG иконка + процедурные круги):
- Размер иконки, прозрачность, скорость кругов
- Круги расходятся непрерывно весь ролик
Огонь (PNG секвенция 150 кадров, зациклена):
- Размер, прозрачность, скорость воспроизведения
Землетрясение (PNG иконка, статично):
- Размер, прозрачность
Кнопки: Сбросить настройки | Сбросить местоположение | Удалить (с подтверждением) При удалении точки — удаляются все связанные маршруты.
- Появляется автоматически при добавлении точки
- Текст по умолчанию — название из геокодера
- Перетаскивается независимо от точки
- Шрифт (выпадающий список из файлов
assets/fonts/), размер, цвет, прозрачность - Окантовка: цвет, размер, прозрачность
- Галочка "До запятой" — показывает текст до первой запятой
- Галочка "Только заглавные" — переводит в верхний регистр
Кнопки: Сбросить настройки | Сбросить местоположение | Удалить (с подтверждением)
- Кнопка "Маршрут" появляется при 2+ точках
- Клик на начальную точку → от неё тянется линия за курсором
- Escape — отменить построение
- Клик на другую отмеченную точку → маршрут между точками
- Клик на произвольное место → маршрут заканчивается стрелкой (без иконки)
- При перемещении точки — маршрут перестраивается автоматически
- Несколько маршрутов могут пересекаться (поздний рисуется поверх)
Вид: Прямая (пульсирующая) | Пунктирная (движение пунктира А→Б циклично) Параметры линии: цвет, толщина, прозрачность, окантовка (цвет/размер/прозрачность) Стрелка (если конец в произвольной точке): масштабируется с толщиной линии
Иконка (только для маршрутов между отмеченными точками): Нет | Автомобиль | Самолёт | Вертолёт | Корабль
- Иконка движется А→Б один раз за длительность видео
- Иконка поворачивается по направлению движения
- Иконка располагается чуть выше линии маршрута
- На каждом маршруте своя иконка
Автомобиль: галочка "Маршрут по дороге" → строит через OSRM Самолёт/Вертолёт/Корабль: переключатель Прямая | Дуга (горизонтальная кривая)
Кнопки: Сбросить настройки | Удалить (с подтверждением)
| Параметр | Варианты | По умолчанию |
|---|---|---|
| Разрешение | 1920×1080 / 3840×2160 | 1920×1080 |
| Кадры/с | 25p / 30p / 50i / 60p | 25p |
| Формат | MP4 / MXF | MP4 |
| Длительность | 3–60 секунд | 10 |
| Тема карты | Светлая / Тёмная | Тёмная |
| Дыхание камеры | Ползунок 0–100 | 0 |
Дыхание камеры: циклическая анимация приближения/отдаления. Активна только пока открыто меню настроек видео — превью показывает эффект. В остальное время карта статична.
Кнопки: Сохранить | Сбросить настройки
Примечание по профилям:
- MP4: 25/30/50/60 трактуются как progressive;
- MXF: 50 трактуется как 50i, а 25/30/60 — progressive MXF.
- Кнопка "Сохранить видео" запускает рендер
- Интерфейс блокируется, показывается прогресс-бар
- Рендер продолжается на сервере даже если пользователь закрыл браузер
- При возврате на сайт во время рендера — снова показывается прогресс-бар
- После завершения, если страница открыта у пользователя — файл автоматически скачивается (если страница закрыта, то автоматически переходит в "Моя история")
- Одновременно один рендер на пользователя
- На сервере одновременно выполняется один рендер (single worker); остальные — в очереди (до 5 одновременно в очереди, дальше 429)
- Боковая панель, открывается из меню пользователя
- Хранится 10 дней, затем автоматически удаляется
- Каждая запись: миниатюра (первый кадр 320×180), название (авто по дате), дата
- Кнопка "Скачать" для повторного скачивания
Имена таблиц и колонок — только английский (чтобы работать с ORM, миграциями и SQL без кавычек). Комментарии в коде и тексты ошибок — русский.
- users — (id, username, password_hash, role, created_at)
- sessions — (id, user_id, token_hash, expires_at, created_at)
- render_jobs — единый источник истины по рендерам и истории видео: (id, user_id, status, progress, state_json, output_path, thumbnail_path, error_message, created_at, updated_at). Статусы:
queued | running | done | error | cancelled. "Моя история" = выборкаstatus = 'done', отсортированная поupdated_at DESC. Отдельной таблицыvideosнет. - render_tokens — одноразовые токены внутреннего доступа к
GET /api/render/state/:jobId: (id, job_id, token_hash, expires_at, used_at, created_at) - gis — отдельная БД для PostGIS/Martin (геокодер Photon используется как внешний публичный сервис через бэкенд-прокси)
- Готовые видео и миниатюры:
/data/videos/{user_id}/ - Временные PNG-кадры не сохраняются: захват идёт через
image2pipe(Puppeteer -> stdin FFmpeg) - Cron: удаление файлов старше 10 дней
--color-bg-page: #0d0d0d;
--color-bg-panel: #1a1a1a;
--color-bg-panel-2: #222222;
--color-bg-input: #2a2a2a;
--color-bg-hover: #333333;
--color-text-primary: #f0f0f0;
--color-text-secondary: #888888;
--color-accent: #3d8bff;
--color-accent-hover: #5a9fff;
--color-danger: #ff4444;
--color-success: #44bb44;
--color-border: #333333;
--color-border-light: #444444;Минимальная ширина экрана: 1280px. Мобильная версия не нужна.
Базовый URL: https://mapvideo.gyhyry.ru/api
Публичные эндпоинты без сессии: /auth/login, /auth/csrf, /health.
Остальные эндпоинты используют cookie-based сессию (httpOnly session cookie).
Исключение: GET /render/state/:jobId допускает либо owner-сессию, либо валидный одноразовый render_token (внутренний доступ рендер-воркера).
Все ошибки возвращают: { "error": "Описание на русском" }
| Код | Описание |
|---|---|
| 400 | Неверный формат запроса |
| 401 | Не авторизован |
| 403 | Нет прав доступа |
| 404 | Ресурс не найден |
| 409 | Конфликт |
| 429 | Слишком много запросов / активный рендер уже есть / переполнена очередь |
| 500 | Внутренняя ошибка |
Все ассеты уже находятся на сервере в папке assets/.
| Ассет | Файл | Тип | Примечание |
|---|---|---|---|
| Огонь | assets/icons/fire_loop/ |
PNG секвенция | Готовая секвенция, зациклена на всё видео |
| Взрыв | assets/icons/explosion.png |
PNG иконка | Статично, анимация кругов процедурная |
| Землетрясение | assets/icons/earthquake.png |
PNG иконка | Статично, без анимации |
| Автомобиль | assets/icons/car.png |
PNG иконка | Нос смотрит вправо |
| Самолёт | assets/icons/airplane.png |
PNG иконка | Нос смотрит вправо |
| Вертолёт | assets/icons/helicopter.png |
PNG иконка | Нос смотрит вправо |
| Корабль | assets/icons/ship.png |
PNG иконка | Нос смотрит вправо |
Мигающая точка — процедурно через PixiJS, PNG ассет не нужен.
Шрифты используются для подписей точек на карте.
Список доступных шрифтов формируется из файлов в этой папке.
Фронтенд получает список через GET /api/fonts, подключает их через @font-face
и дожидается document.fonts.ready перед рендером.
- Отмена действий (Ctrl+Z)
- Редактирование сохранённых проектов
- Загрузка пользовательских иконок
- Кастомизация стилей карты (реализуется отдельно через Maputnik после завершения разработки)
Разработка ведётся строго последовательно по фазам:
- Начинаем с task1.md и не переходим к следующему пока текущий task не завершён полностью
- После завершения фазы — обновить её статус ниже с "Не начато" / "В работе" на "✅ Сделано"
- При возобновлении сессии после обновления лимита токенов:
- Найти фазу со статусом "🔄 В работе" — продолжать её
- Открыть соответствующий task*.md и найти "## Заметка для следующей сессии"
- Продолжить с того места где остановились
| # | Фаза | Файл | Статус |
|---|---|---|---|
| 1 | Инфраструктура и окружение | task1.md | ✅ Сделано |
| 2 | Авторизация (бэкенд) | task2.md | ✅ Сделано |
| 3 | Скелет фронтенда | task3.md | ✅ Сделано |
| 4 | Карта и базовый редактор | task4.md | ✅ Сделано |
| 5 | Точки и подписи | task5.md | ✅ Сделано |
| 6 | Маршруты | task6.md | ✅ Сделано |
| 7 | Настройки видео | task7.md | ✅ Сделано |
| 8 | Рендер видео | task8.md | ✅ Сделано |
| 9 | Полировка и деплой | task9.md | 🔄 В работе (код готов, деплой не выполнен) |
| 10 | Полировка визуала (UX-багфиксы 1) | task10.md | ✅ Сделано |
| 11 | Фикс слайдеров (UX-багфиксы 2) | task11.md | ✅ Сделано |
| 12 | Выпадающие/overlay-панели (UX-багфиксы 3) | task12.md | ✅ Сделано |
| 13 | Геокод, админка, начертания (UX-багфиксы 4) | task13.md | ✅ Сделано |
| 14 | Палитра цветов (UX-багфиксы 5) | task14.md | ✅ Сделано |
| 15 | Критические баги рендер-пайплайна | task15.md | ✅ Сделано |
| 16 | Исправления UX и обработки ошибок | task16.md | ✅ Сделано |
| 17 | Безопасность, валидация, мелкие баги | task17.md | ✅ Сделано |
| 18 | Синхронизация документации | task18.md | ✅ Сделано |