182 lines
17 KiB
Markdown
182 lines
17 KiB
Markdown
|
|
# MusicBridge — план проекта
|
|||
|
|
|
|||
|
|
Обновлено: 2026-09-09. Репозиторий находится в `MusicBridge.Agent/`.
|
|||
|
|
|
|||
|
|
## Цель
|
|||
|
|
|
|||
|
|
Личный пульт на **нативном iPhone-приложении** для музыки на Windows в одной
|
|||
|
|
Wi-Fi сети. Звук играет на ПК. Телефон показывает трек, исполнителя, обложку,
|
|||
|
|
позицию и позволяет управлять воспроизведением и системной громкостью ПК.
|
|||
|
|
|
|||
|
|
## Решения пользователя — не менять без его указания
|
|||
|
|
|
|||
|
|
- Windows + iPhone; своего Mac сейчас нет.
|
|||
|
|
- **Не делать Safari, PWA, веб-страницы или веб-приложение.** Клиент — Swift/SwiftUI.
|
|||
|
|
- iPhone-часть пока не начинать. Сначала довести Windows-агент и протокол.
|
|||
|
|
- Работать по этапам, самостоятельно редактировать файлы и проверять сборку.
|
|||
|
|
- Основные источники: Яндекс Музыка и SoundCloud в браузере, через текущую
|
|||
|
|
системную медиасессию Windows.
|
|||
|
|
- Нужны безопасное сопряжение, автоматическое переподключение, затем поиск ПК.
|
|||
|
|
- В будущем желательны Lock Screen, Dynamic Island и системные команды iOS.
|
|||
|
|
|
|||
|
|
## Границы возможностей
|
|||
|
|
|
|||
|
|
- «Любой звук» относится к системной громкости. Метаданные и команды зависят от
|
|||
|
|
того, что конкретный плеер передаёт в Windows Media Session.
|
|||
|
|
- Позиция вычисляется из Position + времени после LastUpdatedTime с учётом
|
|||
|
|
PlaybackRate. Нельзя считать Position постоянно обновляемым значением.
|
|||
|
|
- Не обещать работу перемотки или переключений без проверки возможностей плеера.
|
|||
|
|
- Для сборки/подписи нативного iOS-приложения потребуется доступ к macOS/Xcode;
|
|||
|
|
способ установки на личный iPhone нужно выбрать перед началом iOS-этапа.
|
|||
|
|
- Возможности iOS в фоне, Lock Screen и Dynamic Island исследовать отдельно.
|
|||
|
|
Не имитировать воспроизведение беззвучным аудио ради фоновой работы.
|
|||
|
|
- HTTPS/WebSocket внутри агента — транспорт для нативного клиента, не веб-интерфейс.
|
|||
|
|
|
|||
|
|
## Архитектура
|
|||
|
|
|
|||
|
|
Windows Media Session → общий MediaService → HTTPS/WSS → будущий SwiftUI-клиент.
|
|||
|
|
Windows Core Audio → системная громкость устройства вывода по умолчанию.
|
|||
|
|
Консоль и сеть используют одни команды и одни правила расчёта позиции.
|
|||
|
|
|
|||
|
|
## Этапы и TODO
|
|||
|
|
|
|||
|
|
### 1. Windows: чтение и локальное управление
|
|||
|
|
|
|||
|
|
- [x] Название, исполнитель, источник, состояние и длительность.
|
|||
|
|
- [x] Вычисление текущей позиции с учётом времени и скорости.
|
|||
|
|
- [x] Play/pause, следующий/предыдущий трек, перемотка ±10 секунд.
|
|||
|
|
- [x] Системная громкость ±5%, mute.
|
|||
|
|
- [x] Проверки поддержки команд и обработка отказов.
|
|||
|
|
- [x] Сборка исходной консольной версии: 0 ошибок, 0 предупреждений.
|
|||
|
|
- [x] Проверен запуск с реальным плеером и чтение громкости.
|
|||
|
|
- [ ] Полная ручная проверка кнопок в Яндекс Музыке и SoundCloud.
|
|||
|
|
|
|||
|
|
### 2. Windows: общий сервис и сетевой протокол
|
|||
|
|
|
|||
|
|
- [x] Вынести чтение/команды из Program.cs в общий сервис.
|
|||
|
|
- [x] Добавить абсолютную перемотку и установку громкости для ползунков iPhone.
|
|||
|
|
- [x] Версионированное JSON-состояние, возможности плеера, результаты команд.
|
|||
|
|
- [x] HTTPS для команд, WSS для обновлений состояния; без веб-интерфейса.
|
|||
|
|
- [x] Ограниченное по времени сопряжение по коду, аутентификация команд.
|
|||
|
|
- [x] Сертификат агента и отпечаток для будущего закрепления сертификата на iPhone.
|
|||
|
|
- [x] Проверки протокола, ошибочного ввода и доступа без авторизации.
|
|||
|
|
- [x] Обновить этот файл и документацию по фактическим результатам.
|
|||
|
|
|
|||
|
|
### 3. Довести агент перед iPhone
|
|||
|
|
|
|||
|
|
- [x] Обложка из медиасессии; ограничение размера, кеш и смена трека.
|
|||
|
|
- [x] Системные события Windows для быстрых обновлений; таймер для прогресса.
|
|||
|
|
- [x] Постоянные доверенные устройства, безопасное хранение хешей токенов, отзыв доступа.
|
|||
|
|
- [x] Постоянный сертификат, защищённое хранилище Windows DPAPI, блокировка второго агента.
|
|||
|
|
- [ ] Обновление истекающего сертификата с явным повторным сопряжением.
|
|||
|
|
- [ ] QR-сопряжение с адресом и отпечатком сертификата.
|
|||
|
|
- [x] Заготовки: payload для QR, проверка адреса/срока, клавиша I.
|
|||
|
|
- [x] Заготовки: DNS-SD descriptor и авторизованный /v1/info; без multicast.
|
|||
|
|
- [ ] Bonjour/mDNS для поиска из нативного приложения.
|
|||
|
|
- [ ] Проверить подключение со второго устройства в домашней сети.
|
|||
|
|
- [ ] Проверить смену плеера/аудиоустройства, отсутствие плеера, сон/пробуждение,
|
|||
|
|
отключение Wi-Fi и несколько клиентов.
|
|||
|
|
- [ ] Tray-интерфейс, автозапуск, удобная установка; правила брандмауэра только
|
|||
|
|
для нужной локальной сети, без открытия доступа в интернет.
|
|||
|
|
|
|||
|
|
### 4. Нативное приложение iPhone — отложено пользователем
|
|||
|
|
|
|||
|
|
- [ ] Согласовать macOS/Xcode, подпись и установку на личный iPhone.
|
|||
|
|
- [ ] SwiftUI: подключение, список компьютеров, сопряжение и сохранение в Keychain.
|
|||
|
|
- [ ] Обложка, трек, исполнитель, плавный прогресс, кнопки и ползунки.
|
|||
|
|
- [ ] Проверка сертификата ПК, переподключение и восстановление состояния.
|
|||
|
|
- [ ] Отключать недоступные команды, отображать ошибки и потерю связи.
|
|||
|
|
- [ ] Проверка на физическом iPhone.
|
|||
|
|
|
|||
|
|
### 5. Дополнительные возможности iOS
|
|||
|
|
|
|||
|
|
- [ ] Исследовать и проверить Live Activities / Dynamic Island / App Intents.
|
|||
|
|
- [ ] Зафиксировать реальные ограничения системного управления и фона.
|
|||
|
|
|
|||
|
|
## Как продолжать работу
|
|||
|
|
|
|||
|
|
1. Прочитать этот файл, затем текущие исходники и `git status`.
|
|||
|
|
2. Не переписывать уже работающие функции без необходимости.
|
|||
|
|
3. Выполнить конкретный этап, проверить сборку и соответствующие проверки.
|
|||
|
|
4. Отдельно указать: что проверено автоматически, что на реальной Windows,
|
|||
|
|
а что ещё требует телефона/ручной проверки.
|
|||
|
|
5. Обновить чекбоксы, результаты и следующий шаг здесь до завершения работы.
|
|||
|
|
|
|||
|
|
## Проверки и следующий шаг
|
|||
|
|
|
|||
|
|
2026-09-09:
|
|||
|
|
|
|||
|
|
- Последний этап — заготовки подключения: Release-сборка без ошибок/предупреждений,
|
|||
|
|
**82 автоматические проверки прошли** (`dotnet run --project tests/MusicBridge.Agent.Tests.csproj -c Release --no-build`).
|
|||
|
|
Проверены payload, адреса, истёкший код, descriptor и авторизация /v1/info.
|
|||
|
|
Debug-сборку удерживал запущенный агент; его не останавливали. Новая сборка в Release.
|
|||
|
|
Реальная медиасессия в этом прогоне не проверялась; проверки ниже — с прошлого этапа.
|
|||
|
|
|
|||
|
|
- Сборка агента и тестового проекта: **0 ошибок, 0 предупреждений**.
|
|||
|
|
- `dotnet run --project tests/MusicBridge.Agent.Tests.csproj -- --live-read`:
|
|||
|
|
**74 проверки прошло** (71 общая + 3 с реальной медиасессией/обложкой).
|
|||
|
|
Проверены расчёт позиции, валидация, срок/одноразовость кода, блокировка после
|
|||
|
|
10 попыток, авторизация HTTPS/WSS, ошибки JSON и размер запроса, доставка
|
|||
|
|
абсолютной перемотки тестовому сервису, поток состояния и закрытие WebSocket.
|
|||
|
|
Добавлены проверки размера/формата обложки, отмены чтения, очистки старого кеша,
|
|||
|
|
авторизации скачивания и 404, доставки событий без ожидания таймера,
|
|||
|
|
объединения 1000 уведомлений и снятия подписки сервером при остановке.
|
|||
|
|
Проверены сохранение/повторное открытие сертификата и устройств, реальный HTTPS
|
|||
|
|
со восстановленным сертификатом/токеном, DPAPI, блокировка второго экземпляра,
|
|||
|
|
ошибки записи, отказ от подмены повреждённого хранилища, постоянный отзыв доступа,
|
|||
|
|
отключение открытого WSS и запрет HTTP-команд отозванным токеном.
|
|||
|
|
- В тестах добавлена обработка исключений верхнего уровня. Проверен
|
|||
|
|
`--test-failure`: текст `TEST FAILED` и выход 1 вместо необработанного исключения,
|
|||
|
|
вызвавшего ранее окно ошибки Windows. Тестовые файлы создаются отдельно от
|
|||
|
|
рабочего хранилища и удаляются после проверки.
|
|||
|
|
- Эти проверки не меняют реальную музыку/громкость. Ручная проверка команд
|
|||
|
|
конкретных плееров и физического iPhone всё ещё не выполнена.
|
|||
|
|
- Обновлённый агент запущен в обычном сеансе Windows на loopback-порту 18765:
|
|||
|
|
HTTPS-сервер стартовал, реальная медиасессия, состояние Paused, длительность
|
|||
|
|
и системная громкость прочитаны. После проверки тестовый запуск остановлен.
|
|||
|
|
- После добавления обложки в режиме `--live-read` прочитан реальный PNG размером
|
|||
|
|
438760 байт. Повторное чтение состояния сохранило тот же ID кешированной обложки.
|
|||
|
|
Подписки WinRT создаются успешно. Доставка событий проверена тестовым сервисом;
|
|||
|
|
ручная смена треков/плееров во время работы агента ещё требует проверки.
|
|||
|
|
- В песочнице Windows Media Session может быть недоступна. Не выдавать такую
|
|||
|
|
ошибку за отсутствие Windows API на машине пользователя.
|
|||
|
|
|
|||
|
|
Текущие ограничения сетевого этапа:
|
|||
|
|
|
|||
|
|
- Состояние обновляется по событиям Windows и резервному таймеру ~500 мс.
|
|||
|
|
Метаданные/обложка перечитываются по событию либо через 10 секунд.
|
|||
|
|
- В кеше только текущая обложка, максимум 2 МиБ; PNG/JPEG/GIF/WebP по сигнатуре,
|
|||
|
|
без декодирования на сервере. Скачивание требует авторизации.
|
|||
|
|
- Сертификат и хеши токенов сохраняются в `%LOCALAPPDATA%\MusicBridge\identity.dat`
|
|||
|
|
под Windows DPAPI текущего пользователя. При обычном перезапуске доверие сохраняется.
|
|||
|
|
В консоли `1`…`8` отзывают доступ устройства по номеру; `B` открывает сопряжение.
|
|||
|
|
- Сертификат действует 5 лет; автоматическое обновление ещё не реализовано.
|
|||
|
|
При повреждении/недоступности хранилища или истечении сертификата агент завершается
|
|||
|
|
с ошибкой и не создаёт молча новую идентичность. Восстановление описано в PROTOCOL.md.
|
|||
|
|
- По умолчанию только loopback. `--lan` включает IPv4-сеть; брандмауэр не меняется.
|
|||
|
|
- QR, mDNS, iPhone UI и интеграции iOS ещё не реализованы.
|
|||
|
|
|
|||
|
|
Следующий шаг: этап 3 — QR-сопряжение (адрес, код, закрепляемый отпечаток),
|
|||
|
|
затем Bonjour/mDNS и проверка с другого устройства. iPhone-клиент пока не начинать.
|
|||
|
|
Заготовки готовы; точные границы и следующие шаги — в `CONNECTION-ROADMAP.md`.
|
|||
|
|
Начать разработку iOS только после новой команды пользователя.
|
|||
|
|
|
|||
|
|
## Карта файлов
|
|||
|
|
|
|||
|
|
- `Program.cs`: запуск, аргументы, консольные клавиши и отображение состояния.
|
|||
|
|
- `Media/MediaService.cs`: чтение Windows Media Session и выполнение всех команд.
|
|||
|
|
- `Media/TimelineMath.cs`: вычисление позиции и границ перемотки.
|
|||
|
|
- `Media/Contracts.cs`: состояние, возможности, команды и их валидация.
|
|||
|
|
- `Media/ArtworkCache.cs`: ограниченное чтение обложки, определение типа и кеш по хешу.
|
|||
|
|
- `SystemVolume.cs`: Windows Core Audio, громкость устройства вывода по умолчанию.
|
|||
|
|
- `Network/RemoteServer.cs`: HTTPS API и обложки, WSS, события/таймер и лимиты.
|
|||
|
|
- `Network/PairingService.cs`: код сопряжения, доверенные устройства, проверка/отзыв токенов.
|
|||
|
|
- `Network/IdentityStore.cs`: постоянный сертификат и устройства, атомарная запись и блокировка.
|
|||
|
|
- `Network/UserProtection.cs`: Windows DPAPI текущего пользователя.
|
|||
|
|
- `Network/AgentCertificate.cs`: создание сертификата TLS для первого запуска.
|
|||
|
|
- `PROTOCOL.md`: команды запуска и точный контракт будущего iPhone-клиента.
|
|||
|
|
- `CONNECTION-ROADMAP.md`: заготовки QR/DNS-SD и шаги будущего подключения.
|
|||
|
|
- `Network/ConnectionInvitation.cs`: данные для QR, без генерации картинки.
|
|||
|
|
- `Network/DiscoveryDescriptor.cs`: описание DNS-SD, без сетевой рассылки.
|
|||
|
|
- `tests/`: исполняемые проверки без дополнительных тестовых NuGet-пакетов.
|