Files
ds-galo/openspec/changes/archive/2026-10-13-add-phone-album-sync/proposal.md
T
2026-10-13 17:10:50 +03:00

79 lines
6.9 KiB
Markdown

# Proposal
## Why
Android-клиент работает только с серверными альбомами и ручной загрузкой через
системный Photo Picker: он не видит альбомы самого телефона, не следит за их
изменениями и не освобождает место. Основной мобильный сценарий — «снимаю на
телефон и не думаю о переносе» — не покрыт. Нужна синхронизация альбомов
устройства с серверным альбомом в трёх режимах, с отслеживанием изменений.
## What Changes
- Смешанный список альбомов: альбомы устройства (MediaStore bucket'ы) и серверные
альбомы в одном списке; связанная пара «телефонный → серверный» показывается
одной карточкой устройства с указателем связи и меткой режима, а связанный
серверный альбом из списка скрывается.
- Экран альбома устройства с настройками по свайпу вправо: только выбор режима
синхронизации; целевой серверный альбом выбирается (или создаётся) при включении,
допускается n:1.
- Три режима синхронизации альбома:
- **A — выключена**: оригиналы на телефоне, превью генерируются и хранятся на
телефоне; сервера не касается.
- **B — копии оригиналов**: оригиналы остаются на телефоне, копии загружаются на
сервер.
- **C — перенос оригиналов**: оригиналы уходят на сервер, на телефоне вместо них
остаются превью; замена файла в галерее выполняется автоматически (широкий
доступ к файлам запрашивается при включении режима, без системного диалога);
видео заменяется постером.
- Отслеживание и синхронизация изменений (добавление, удаление, переименование,
перенос между альбомами, правка контента как «новый + удалить старый»)
автоматически в фоне (`MediaStore.getGeneration`/`ContentObserver` +
периодический WorkManager) и при открытом приложении (SSE + `?since=`).
- Идентичность и дедупликация по content-hash (sha256) относительно контрольной
суммы объекта на сервере; удаление на сервере — только для «наших» загрузок.
- Провенанс, переживающий переустановку: в EXIF разгруженных в C превью пишется
метка (серверный альбом/фото, hash оригинала), по которой связь и провенанс
восстанавливаются.
- Возврат оригиналов из C («вернуть оригиналы»); отвязка и выключение
синхронизации никогда не удаляют фотографии с сервера.
- Сервер начинает отдавать контрольную сумму оригинала в данных фото (малая
правка API, без миграции БД — `checksum` уже хранится).
- **BREAKING**: нет (добавляемое поле ответа; существующие клиенты не ломаются).
## Capabilities
### New Capabilities
- `phone-album-sync`: синхронизация альбома устройства с существующим серверным
альбомом — режимы A/B/C, отслеживание изменений, идентичность и дедуп по
content-hash, политика удалений, разгрузка места и провенанс.
### Modified Capabilities
- `android-client`: смешанный список альбомов устройства и сервера, экран и
настройки синхронизации телефонного альбома, широкий доступ к медиа
(`READ_MEDIA_*`) и фоновая работа синхронизации.
- `photos`: данные фото включают контрольную сумму оригинала, чтобы клиент мог
сопоставлять локальный ассет с серверным фото и не загружать дубли.
## Impact
- `android/`: новый источник MediaStore (bucket'ы и ассеты), движок синхронизации
и очередь на существующем `UploadRepository`/`UploadWorker`, локальный индекс и
провенанс в Room, кэш превью, EXIF-метка, чтение/запись превью (широкий доступ
к файлам `MANAGE_EXTERNAL_STORAGE`), правки навигации (`GaloNavHost`), манифеста
(права) и `AppContainer`.
- `backend/`: отдать `checksum` в моделях/запросах/ответах фото. Миграции БД не
требуются.
- Зависимости клиента: MediaStore, ExifInterface, MediaMetadataRetriever,
WorkManager, Room, Coil — большинство уже подключено.
- Платформенные ограничения: согласие ОС на изменение/удаление чужого медиа;
фоновая работа ограничена WorkManager; Google Play ограничивает `READ_MEDIA_*`.
- **Предпосылка (побочно, вне объёма этой правки)**: `.gitignore` содержит правило
`data/`, из-за которого весь Kotlin-слой `android/.../com/galo/android/data/`
не отслеживается git и отсутствует в git-истории. До реализации это нужно
устранить, иначе новые файлы синхронизации тоже не попадут в коммит.
- Вне объёма: iOS; синхронизация на уровне сервера (устройства/сессии на
бэкенде); многопользовательские конфликты владения; редактирование изображений.