# Design ## Context См. `proposal.md` — Why. Текущее состояние, которое формирует подход: - Загрузка читает multipart целиком в память (`photos/handlers.go:71`) и держит единый лимит 100 МБ (`handlers.go:16`); генерация вариантов — чистый Go (`imaging`/`webp`) в `photos/pipeline.go`, роли объекта — `original|thumb|medium|preview` (`migrations/000008`, `sqlite/000001`). - Очередь `variant_jobs` — одно задание на фото, лизинг `interval*4` (`worker.go:47`), параллелизм — общий (`WORKER_CONCURRENCY`). - Отдача объектов уже поддерживает `Range` и тип содержимого (`storage.go:233`), значит плеер получит перемотку без изменений. - Ограничения проекта: без Docker (ffmpeg — бинарник на хосте), один Go-бинарник, паритет метаданных PostgreSQL и SQLite, непрозрачные ключи и журнал объектов. ## Goals / Non-Goals **Goals:** - Видео становится медиа-записью в той же сущности `photos` с видом `kind`, длительностью и адресом воспроизведения. - Гарантированное воспроизведение в браузере: remux для web-совместимых источников, транскод для остальных; постер переиспользует существующую генерацию изображений. - Длительная обработка не вытесняет генерацию вариантов изображений. - Паритет обоих бэкендов метаданных и обоих бэкендов хранилища. **Non-Goals:** - Адаптивный стриминг (HLS/DASH) и несколько rendition'ов. - Обрезка, монтаж, субтитры, несколько звуковых дорожек в playback. - Чанковая/возобновляемая загрузка и фоновая догрузка больших файлов. ## Decisions ### 1. Модель: расширяем `photos`, а не новая сущность Добавляем `photos.kind` (`image`|`video`, default `image`) и `photos.duration_ms`. Причина: журнал `photo_objects`, альбомы, права, корзина, share, перенос/копия и social не знают о пикселях. Альтернатива — отдельная `videos` — дублирует всю эту логику; обобщение в `media` — широкий рефактор схемы и UI без выгоды для MVP. ### 2. Новая роль объекта `playback`; постер — обычные image-варианты ``` original = загруженный файл (скачивание, fallback) playback = remux/транскод mp4 (источник