Files
evening_detective_screen/README.md
T
2026-10-17 19:08:09 +07:00

136 lines
12 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.
# Вечерний детектив — приложение площадки
Статус: SCREEN-001 в работе; технический стенд реализован, аппаратная приёмка открыта.
SCREEN-002 и SCREEN-003 завершены: описание пакета через существующий API,
загрузка, проверка, восстановление и локальная выдача файлов.
Управление с телефона идёт через существующий сервер сайта; desktop получает
обновления с сервера. Прямое LAN-подключение не используется.
Цель: ведущий управляет показом на компьютере с macOS, Windows или Linux, а подключённый монитор или
проектор показывает материалы игрокам. Все материалы показа заранее находятся
на диске; локальная команда не ждёт сеть или ответ сервера.
Стек: Electron + Vue + TypeScript + Vite. Первая версия включает шесть сборок:
macOS Intel/Apple Silicon, Windows x64/ARM64 и Linux x64/ARM64.
Поддерживаемые версии ОС и условия проверки описаны в PLAN.md.
Использовать текущие паттерны фронтенда и переиспользовать подходящие компоненты,
не переносить весь сайт в приложение и не выделять общий пакет заранее.
План: [PLAN.md](PLAN.md). Очередь реализации: [TASKS.md](TASKS.md).
Эти документы находятся здесь по прямому указанию владельца — исключение из
общего правила хранения задач в `../docs/agent`. Не дублировать их там.
Идентификаторы `SCREEN-*` относятся только к этому приложению.
Для начала: `Выполнить задачу: SCREEN-001`.
Новые задачи: `Новая задача: <описание>`.
Commit и push выполняет только пользователь. Вложенные `.git` не изменять.
## Запуск стенда SCREEN-001
Нужны Node.js 24.15+ и ffmpeg с libx264 в PATH.
В Linux приложение использует X11; для сессии Wayland нужен XWayland, чтобы
размещать показ на выбранном мониторе.
```sh
npm ci
npx --no-install install-electron --no
npm run media:generate
npm run dev
```
До запуска подключите второй дисплей в режиме расширенного рабочего стола
и назначьте экран компьютера основным. В пульте выберите
его и откройте показ. Аудио и видео — синтетические локальные файлы длительностью
60 секунд; бинарники находятся в игнорируемом fixtures/ и не входят в Git.
Стенд поддерживает пуск с начала, паузу, остановку и перемотку. Полные игровые
режимы, интерфейс загрузки игр и авторизация пока не реализованы.
Управление телефоном через сервер, подключение игры и авторизация относятся к
SCREEN-006 и в техническом стенде ещё не реализованы. Интернет нужен для получения
команд телефона; локальное воспроизведение и пульт компьютера работают без него.
Проверки: npm run type-check, npm run lint, npm run lint:css, npm test, npm run build.
[Сборки и аппаратная приёмка](docs/SCREEN-001_ACCEPTANCE.md).
## Описание пакета SCREEN-002
`electron/screen-package.ts` экспортирует `prepareScreenPackage({ server, gameId,
token, signal? })` для вызова из основного процесса. Метод делает три GET-запроса:
`/api/games/{ID}`, `/api/games/{ID}/screen-plan`, `/api/games/{ID}/music`.
Авторизация — существующий Bearer JWT; токен используется только в запросах API,
не входит в результат. Редиректы API запрещены, ожидание ограничено 30 секундами;
поддерживается отмена через AbortSignal. Ответы с ошибками не выдают частичный пакет.
Интерфейс входа и подключение к пульту остаются в SCREEN-006; сейчас это программный
контракт, новых кнопок в стенде нет. Бэкенд, frontend и protobuf не изменены.
Результат `ScreenPackage` содержит `formatVersion: 1`, `metadataVersion`, адрес
сервера, ID/название игры, ссылки на материалы, шаги плана и связанные плейлисты.
`assets` содержит уникальные URL и их идентификаторы (SHA-256 адреса, не файла).
В `media` и треках используются эти идентификаторы. Фон по умолчанию — обложка.
Плейлисты сценария, игры и текущий выбранный включаются со всеми треками; прочая
библиотека, данные участников и живые команды не сохраняются. Ссылки с секретами
в параметрах и адреса с логином/паролем отклоняются.
`metadataVersion` — SHA-256 нормализованных метаданных. Он не замечает замену
байтов по прежнему URL. Само описание не означает готовность к показу. В SCREEN-003 размер и SHA-256
вычисляются при скачивании, после чего формируется итоговая версия и пакет
активируется атомарно. Внешним медиа нельзя передавать JWT сервера.
Существующий API не предоставляет атомарного снимка или серверных хешей файлов;
права доступа к играм и публичная выдача файлов остаются прежними.
## Локальное хранилище SCREEN-003
Основной процесс открывает `PackageStore` из `electron/package-store.ts` в
`app.getPath('userData')/packages`. Приложение допускает только один экземпляр,
чтобы две копии не изменяли хранилище одновременно. При запуске проверяются
манифесты, размеры и SHA-256 всех файлов; повреждённые пакеты не становятся готовыми.
Программный интерфейс (для основного процесса; подключение к UI — SCREEN-006):
```ts
const store = new PackageStore(packagesDirectory)
await store.initialize()
const description = await prepareScreenPackage({ server, gameId, token })
const ready = await store.download(description, { signal, onProgress })
// Другие операции: list(), cancel(), verify(key), remove(key), localFile(key, assetId).
```
Скачивание последовательно и потоково. Файл синхронизируется с диском и повторно
читается для проверки хеша. При обрыве текущий файл повторяется целиком: максимум
три попытки с паузами 1 и 3 секунды; завершённые файлы текущей попытки не повторяются.
Таймаут бездействия — 30 секунд. Ошибки 408/429/5xx повторяются; ошибки доступа,
неизвестные форматы и отсутствующие файлы останавливают подготовку. Незнакомый
общий размер в прогрессе обозначается `totalBytes: null`; итоговый размер известен
после загрузки. Поддерживаются MIME WAV/OGG, используемые текущим сервером.
Медиа запрашиваются без JWT и cookies. Разрешено до пяти проверенных HTTP(S)
редиректов; запрещены учётные данные в URL, секретные параметры и понижение
HTTPS до HTTP. Контроль места использует statfs перед загрузкой и перед записью
очередного блока, оставляя резерв 64 МиБ; фактическая ошибка ENOSPC также обрабатывается.
Каталог игры определяется хешем адреса сервера и ID игры; пути ресурсов — хешами
URL, независимо от исходного имени файла. Локальный манифест содержит исходное
описание, MIME, размеры и SHA-256 байтов; итоговая версия зависит от метаданных
и проверенных файлов. Секреты в манифест не записываются.
Новая версия готовится в `staging-*`, затем помещается в `v-<version>`. Указатель
`current.json` заменяется через временный файл и rename только после проверки.
Отмена и ошибки до переключения не затрагивают прежний готовый пакет. После
успеха прежняя версия удаляется; заблокированные ОС остатки повторно очищаются
при следующем запуске. Сбой после переключения не отменяет новую готовую версию.
Повреждённую текущую копию можно восстановить повторной загрузкой или удалить.
Симлинки при чтении не обходятся; удаление ограничено каталогом выбранной игры.
`screen-app://app/packages/<key>/<assetId>` выдаёт только ресурсы готового пакета,
с правильным MIME, запретом кеширования и поддержкой Range. Произвольные пути
и сетевые URL в протокол не передаются. Воспроизведение игровых пакетов подключается
в SCREEN-004; синтетический стенд пока использует прежние fixture-файлы.
`npm test` включает настоящий временный HTTP-сервер: после скачивания сервер
отключается, новое хранилище проверяет пакет, локальная выдача читает все ресурсы.
`npm run smoke -- --no-sandbox --mute-audio --disable-gpu` дополнительно готовит
WAV-пакет в отдельном временном userData, открывает его повторно через основной
процесс и проверяет Range и воспроизведение. Эти флаги используются только в тесте.
Серверных эталонных хешей нет: локальная проверка подтверждает целостность
скачанной копии, но не неизменность источников во время подготовки.