feat(video): add video upload with remux/transcode playback

Add video as a media kind in photos: streaming upload with per-kind
limits, ffprobe metadata, poster frame reusing the image variant
pipeline, and a playback object (remux when web-compatible, else
h264/yuv420p/aac transcode). Video jobs run on a separate pool with a
long lease. Frontend gains a video viewer, grid badge/duration and
video upload.
This commit is contained in:
iu
2026-10-11 20:37:53 +03:00
parent d059d042f0
commit d38ede0201
52 changed files with 2717 additions and 82 deletions
+30 -1
View File
@@ -19,6 +19,8 @@ SvelteKit через `go:embed`, запуск через systemd.
- Go 1.22+ (сборка проверена на 1.26)
- Node.js 20+ (только для сборки фронтенда)
- Доступ к `pg.localnet:5432` (PostgreSQL) и `s3.localnet:3900` (Garage)
- `ffmpeg` и `ffprobe` на хосте — только для видео; без них приём видео отключается,
а изображения продолжают работать (см. «Видео»)
- `make`
## Конфигурация
@@ -68,6 +70,13 @@ cp .env.example .env
| `WORKER_INTERVAL` | `5s` | Период проверки воркером очереди задач |
| `WORKER_BATCH` | `10` | Размер батча обрабатываемых задач |
| `WORKER_PENDING_TTL` | `10m` | TTL задачи в статусе `pending` до повторной попытки |
| **Загрузка и видео** | | |
| `IMAGE_MAX_UPLOAD_SIZE` | `100MB` | Максимальный размер изображения (байты или суффикс `KB`/`MB`/`GB`) |
| `VIDEO_MAX_UPLOAD_SIZE` | `2GB` | Максимальный размер видео |
| `FFMPEG_PATH` | `ffmpeg` | Путь к `ffmpeg` (нужен для приёма видео) |
| `FFPROBE_PATH` | `ffprobe` | Путь к `ffprobe` |
| `VIDEO_TRANSCODE_CONCURRENCY` | `1` | Параллельных транскодов (отдельно от воркера изображений) |
| `VIDEO_TEMP_DIR` | *(системный tmp)* | Каталог для временных файлов загрузки/транскода |
| **Логирование** | | |
| `LOG_LEVEL` | `info` | Уровень логов: `debug`, `info`, `warn`, `error` |
| `LOG_FORMAT` | `json` | Формат логов: `json` или `text` |
@@ -89,6 +98,25 @@ cp .env.example .env
**только для одного экземпляра приложения на файл** (без горизонтального
масштабирования).
### Видео
Видео загружается в те же альбомы, что и фото (`kind=video`), и готовится для
воспроизведения в браузере фоновым воркером:
- **Постер**: из видео извлекается кадр, из которого генерируются обычные варианты
`thumb`/`medium`/`preview` (WebP) — сетка и обложка показывают постер.
- **Воспроизведение**: web-совместимый источник (h264/yuv420p, профиль Baseline/Main/High,
звук aac или без звука, контейнер mp4/mov/mkv, разрешение ≤ 1080p) ремуксится без
перекодирования; иначе выполняется транскод в h264/yuv420p/aac с `+faststart`. Готовый
вариант хранится как объект роли `playback` и отдаётся с поддержкой `Range` (перемотка).
- **Оригинал** сохраняется отдельно и используется для скачивания.
- Транскод идёт в отдельном малом пуле (`VIDEO_TRANSCODE_CONCURRENCY`) с длинным лизингом и
не вытесняет генерацию вариантов изображений. Роль `playback` появляется по готовности и
транслируется подписчикам альбома (`photo.updated`).
Без `ffmpeg`/`ffprobe` приём видео отклоняется, а `/api/health` сообщает
`"video": "unavailable"`; изображения работают как прежде.
## Сборка
```bash
@@ -162,7 +190,7 @@ GALO_EMAIL=owner@example.com GALO_PASSWORD=secret \
Значения по умолчанию: `ALBUMS=2`, `PHOTOS=10`, `SEED=0`, `TIMEOUT=30`. Булевы
параметры включаются значением `1`: `TAGS`, `COMMENTS`, `LIKES`, `SHARE`, `INVITES`,
`WAIT`, `DRY`. Дополнительные флаги передаются через `ARGS`.
`WAIT`, `DRY`, `VIDEO`. Дополнительные флаги передаются через `ARGS`.
Переменные окружения:
@@ -185,6 +213,7 @@ GALO_EMAIL=owner@example.com GALO_PASSWORD=secret \
| `--share-links` | выкл. | Создавать share-ссылки на альбомы |
| `--invites` | выкл. | Создавать инвайты (роль `member`) |
| `--wait-variants` | выкл. | Дожидаться генерации превью каждого фото |
| `--video` | выкл. | Загружать видео-фикстуры (требует `ffmpeg`; иначе пропускаются) |
| `--timeout` | `30` | Таймаут ожидания и сетевых запросов, сек |
| `--dry-run` | выкл. | Показать план и не отправлять изменяющих запросов |