Files
view-mate/README.md
T
Hermes Agent fafdddc121 feat(app-phone): синхронизированный звук на телефоне при просмотре на очках + README-статус
- AudioSyncPlayer (ExoPlayer media3): играет дорожку (локальный файл или presigned S3-URL), коррекция по PlaybackPosition очков (каждые 5с, порог 1000мс), мгновенная реакция на пульт, индикатор «🔊 звук: N мс»
- Вещдок-лог: audio sync: video=N audio=M diff=K (на стенде diff ~150мс)
- Смена озвучки во время просмотра — перезапуск с позиции очков
- README: секция «Статус реализации»
- Тесты 24/0 (8 новых AudioSyncPlayerTest)
2026-08-19 14:41:40 +03:00

142 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ViewMate
Очки RayNeo X2 + компаньон на телефон: кино и чат с ассистентом.
## Статус реализации (19.08.2026)
| Возможность | Статус |
|---|---|
| Бинокулярный видеорендер на очках (ExoPlayer → EGL-блит в оба глаза), fullscreen | ✅ этап 1 |
| Пульт: пауза/плей/±30с/закрыть (с телефона и из консоли) | ✅ |
| Связка очки↔телефон по WiFi (WS), два эмулятора + adb forward | ✅ |
| Каталог Jellyfin на телефоне (Фильмы/Сериалы/Папки, поиск, постеры, бейджи зеркала) | ✅ |
| Детали: выбор озвучки, статус конвертации, заказ зеркала | ✅ |
| Реальное скачивание с S3 (SigV4, прогресс, докачка с места, персистентный статус) | ✅ |
| Экран «Очки»: связь, батарея/память, пульт | ✅ |
| **Синхронизированный звук на телефоне** (очки без динамиков) | 🔧 в работе |
| Чат с ассистентом (режим CHAT, сессии Hermes, голосовой ассистент) | ❌ заглушка |
| Жесты RayNeo (пауза/перемотка жестом) | ❌ в планах |
| NSD-дискавери телефона (реальные очки находят телефон по сети) | ❌ в планах (сейчас адрес зашит для эмулятора) |
Подробный журнал решений и инцидентов: `/root/notes/2026-08-19.md` (внутреннее).
## Структура
```
app-glasses/ RayNeo X2 — AR-приложение: видео (ExoPlayer), жесты, связь по WiFi
app-phone/ компаньон на телефон — каталог, дубль-управление, чат
app-host/ отладочный хост — «бэк» без UI, к которому цепляются очки
lib-core/ общий слой (KMP): режимы, состояния, команды, DTO
sketches/ эскизы UI: раскладка экранов + сценарии использования
```
## Эскизы интерфейса
- `sketches/phone-ui/index.html` — эскизы 4 экранов телефона (раскладка компонентов, открыть в браузере)
- `sketches/phone-ui/preview.png` — скриншот эскизов
- `sketches/phone-ui/FLOW.md` — **сценарии использования**: как посмотреть фильм, проверить очки, поговорить с ассистентом, что видно при не-сконвертированном/частично скачанном контенте
## Режимы
### Два режима на очках
| Режим | Состояния | Что происходит |
|-------|-----------|----------------|
| **MOVIE** (кино) | `PLAYING`, `PAUSED` | видео на очках (ExoPlayer), звук выбранной дорожки на телефоне, синхронизация по позиции |
| **CHAT** (общение) | `SESSION_SELECT`, `TALKING` | выбор сессии Hermes, затем разговор в рамках сессии |
Переключение режимов — **с телефона** (и жестом на очках). Оба дёргают один локальный агент (lib-core).
### Каталог — НЕ режим очков
Каталог контента (Jellyfin) живёт **только на телефоне** (и в будущем — у голосового ассистента, который тоже будет по нему ходить). Выбрал в каталоге → «включить» → очки переходят в режим MOVIE с готовым контентом.
Очки — «экран кинотеатра»: они ждут, когда им скажут «включи», и не листают каталог сами.
### Локальный голосовой ассистент (в будущем)
Вызывается **в любом режиме** (двойной клик в чате), поверх всего. Умеет: ходить по каталогу, включать/ставить кино, переключать режимы, переключать сессии, отвечать на вопросы (локально, без сервера).
## Жесты (распознаёт RayNeo SDK, `TempleAction`)
| Жест (SDK) | Режим КИНО | Режим ЧАТ |
|------------|-----------|-----------|
| `Click` | пауза ↔ плей | старт / стоп диктовки |
| `DoubleClick` | игнор | отмена диктовки; вне диктовки — вызов локального ассистента |
| `SlideBackward` / `SlideForward` | перемотка | в `SESSION_SELECT` — листание сессий; в `TALKING` — скролл текста ответа |
| `LongClick` | (резерв) | показать / спрятать текст последнего ответа |
| `TripleClick` | (резерв) | (резерв) |
> Жесты распознаёт сам SDK (`TempleAction.Click/DoubleClick/TripleClick/SlideBackward/SlideForward/LongClick`) — своя детекция не нужна (проверено в pair-view, `MainActivity.kt`).
## Состояния ассистента (единые для обоих ассистентов)
```
IDLE ──► LISTENING ──► THINKING ──► SPEAKING ──► IDLE
(слушает) (думает) (говорит)
```
| Состояние | Индикатор |
|-----------|-----------|
| IDLE | ничего |
| LISTENING | красная точка ● мигает + время записи (0:07, 0:23…) |
| THINKING | спиннер / точки «думает» |
| SPEAKING | «говорит» (другим цветом / без мигания) |
Один и тот же индикатор для Порфирия и локального ассистента — очкам неважно, кто за ним стоит.
## Панель индикаторов
**Всегда видна, по центру снизу.** Содержимое меняется от режима:
| Режим/состояние | Содержимое панели |
|-----------------|-------------------|
| КИНО | `просмотрено / осталось` (12:34 / 56:00) + 🔋 батарея + ⏰ часы |
| ЧАТ, диктовка | `● 0:07` (красная точка + время записи) + 🔋 + ⏰ |
| ЧАТ, думает/говорит | индикатор состояния (⏳/🔊) + 🔋 + ⏰ |
| **Потеря связи с телефоном** | **вместо времени — «связь потеряна» (⚠️)** + 🔋 |
- Часы — **время очков** (часы очков, просто время)
- Батарея — очков
- Просмотрено/осталось — из `PlaybackState` (positionMs/durationMs)
- Если связи с телефоном нет — панель показывает ⚠️ вместо времени (см. Автоматика)
## Текст ответа на экране
Длинные ответы можно показать текстом (по `LongClick`) и скроллить свайпами. Полезно, когда ответ многословный — перечитать спокойно.
## Автоматика
| Событие | Действие |
|---------|----------|
| Очки выключились / экран погас | **пауза** (видео и аудио) |
| Потеря связи очки ↔ телефон (WiFi) | **пауза** (и на очках, и на телефоне) + на панели ⚠️ «связь потеряна» вместо времени |
| Связь восстановилась | продолжить с того же места, панель — как обычно |
## Связь и поток
```
очки (RayNeo X2) телефон (компаньон) сервер
┌─────────────────┐ ┌──────────────────┐
│ режимы: │ │ каталог Jellyfin │ Jellyfin
│ MOVIE/CHAT │◄──WiFi────►│ статусы зеркал │──media-mirror API──► координатор
│ жест = switch │ │ заказ конвертации │ → воркеры → S3
│ видео ExoPlayer │ │ переключение │
│ │ │ режимов очков │
│ │ │ чат с Hermes │──assistent/chat──► Hermes API
└─────────────────┘ └──────────────────┘ → TTS → в ухо
```
- **Один локальный агент** (на телефоне) — очки и телефон два рендера одного состояния
- **WiFi-связь** очки ↔ телефон (вместо Bluetooth в старом pair-view)
- **Кино**: видео 480p H.264 из S3 (media-mirror) на очках, звук выбранной дорожки на телефоне, синхронизация по позиции (очки — мастер, телефон подстраивает аудио)
- **Чат**: с Hermes через сервер; медиа-интенты («включи сериал X») — напрямую в media-mirror API, без LLM
## Медиа-конвейер (внешний, уже живой)
- Координатор: `https://mirror.binom.pw` (X-API-Key) — заказ конвертации, статусы, файлы
- Формат зеркала: `video.mkv` (H.264/NVENC, 480p, без звука) + `audio-N.ogg` (все дорожки, мета title/language)
- S3: `https://s3.binom.pw`, bucket `media`, ключи `mirror/{itemId}/...`; клиентский ключ (read-only) — в конфиге
Полное описание API: `media-mirror-api/README.md` (https://git.binom.pw/subochev/media-mirror-api).