# Streaming services architecture

Этот каталог содержит модули отдельных стриминговых сервисов, общий
контракт между ними и реестр-заглушку под будущий цикл `for (svc of enabled)`.

## Структура

```
src/services/
├── types.ts        ← общий интерфейс StreamingService
├── registry.ts     ← реестр зарегистрированных сервисов (заглушка под этап 4)
├── apple/          ← адаптер Apple Music (JSONP к iTunes Search API)
├── deezer/         ← адаптер Deezer (JSONP)
├── tidal/          ← адаптер Tidal (через CORS-прокси, x-tidal-token)
├── qobuz/          ← адаптер Qobuz (через CORS-прокси, public app_id)
├── musicbrainz/    ← MB как мета-источник (rate-limit 1 RPS)
└── songlink/       ← Songlink/Odesli — особый enrichment-сервис
```

Каждый адаптер `<svc>/api.ts` экспортирует низкоуровневые функции к
конкретному API; `<svc>/index.ts` (если есть) собирает их в формат
`StreamingService` из `types.ts`.

## CORS-стратегия

Tidal и Qobuz не разрешают браузерные запросы. Используется общий хелпер
`src/lib/cors-proxies.ts`:

1. Внутренний серверный прокси `/api/proxy?url=...` (см. `artifacts/api-server/src/routes/proxy.ts`).
   Whitelisted hosts: `www.qobuz.com`, `api.tidal.com`. Rate-limit: 60 req/min/IP.
2. Direct fetch (пропускается для Qobuz/Tidal через `skipDirectFetch: true`).
3. RACE из 8 публичных CORS-прокси — побеждает первый успешный.

Apple, Deezer — JSONP (`<script>` с `?callback=…`), CORS не нужен.

## Состояние модуляризации

**Сделано:**
- ✅ Утилиты, JSONP-загрузчик, регионы, иконки — вынесены в `src/lib`/`src/icons`.
- ✅ Все 6 сервисов вынесены в собственные папки (`apple/`, `deezer/`, `tidal/`,
  `qobuz/`, `musicbrainz/`, `songlink/`).
- ✅ UI-компоненты (`AlbumCard`, `PlatformToggle`, `*ProgressOverlay`,
  `SonglinkLinksModal`) — в `src/components/`.
- ✅ Контракт `StreamingService` — в `types.ts`.

**В планах:**
- 🔲 Доделать `registry.ts` — каждый сервис экспортирует объект
  `{ meta, searchArtist, fetchDiscography, mergeIntoMap, getDiagnostics }`.
- 🔲 Свести `startScan` в `App.tsx` к циклу
  `for (const svc of enabledServices) await runService(svc, …)`.
- 🔲 Параллелизовать независимые этапы (Apple + Deezer одновременно).

## Добавление нового сервиса (после доделки registry)

```ts
// src/services/spotify/index.ts
import type { StreamingService } from '../types';
import { SpotifyIcon } from '@/icons/SpotifyIcon';

export const spotify: StreamingService = {
  meta: {
    id: 'spotify',
    label: 'Spotify',
    accentVar: '--spotify',
    icon: SpotifyIcon,
    hintKey: 'sources.spotifyHint',
    toggleable: true,
    defaultEnabled: false
  },

  async searchArtist(name) { /* … */ },
  async fetchDiscography(artist, onProgress) { /* … */ },
  async enrichAlbum(album) { /* … */ }
};
```

И зарегистрировать в `registry.ts`. Пока регистрация делается вручную внутри
`App.tsx::startScan` — это и есть основной техдолг этого модуля.
