mirror of
https://github.com/wkulhanek/bonob.git
synced 2025-12-21 17:33:29 +01:00
Save tokens
This commit is contained in:
187
CLAUDE.md
Normal file
187
CLAUDE.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
bonob is a Sonos SMAPI (Sonos Music API) implementation that bridges Subsonic API clones (like Navidrome and Gonic) with Sonos devices. It acts as a middleware service that translates between the Subsonic API and Sonos's proprietary music service protocol, allowing users to stream their personal music libraries to Sonos speakers.
|
||||
|
||||
## Development Commands
|
||||
|
||||
### Building and Running
|
||||
```bash
|
||||
# Build TypeScript to JavaScript
|
||||
npm run build
|
||||
|
||||
# Development mode with auto-reload (requires environment variables)
|
||||
npm run dev
|
||||
# OR with auto-registration
|
||||
npm run devr
|
||||
|
||||
# Register bonob service with Sonos devices
|
||||
npm run register-dev
|
||||
```
|
||||
|
||||
### Testing
|
||||
```bash
|
||||
# Run all tests
|
||||
npm test
|
||||
|
||||
# Run tests in watch mode
|
||||
npm run testw
|
||||
|
||||
# Set custom test timeout (default: 5000ms)
|
||||
JEST_TIMEOUT=10000 npm test
|
||||
```
|
||||
|
||||
### Environment Variables for Development
|
||||
When running locally, you need to set several environment variables:
|
||||
- `BNB_DEV_HOST_IP`: Your machine's IP address (so Sonos can reach bonob)
|
||||
- `BNB_DEV_SONOS_DEVICE_IP`: IP address of a Sonos device for discovery
|
||||
- `BNB_DEV_SUBSONIC_URL`: URL of your Subsonic API server (e.g., Navidrome)
|
||||
|
||||
## Architecture
|
||||
|
||||
### Core Components
|
||||
|
||||
**`src/app.ts`** - Application entry point
|
||||
- Reads configuration from environment variables
|
||||
- Initializes all services (Subsonic, Sonos, authentication)
|
||||
- Wires together the Express server with appropriate dependencies
|
||||
- Handles SIGTERM for graceful shutdown
|
||||
|
||||
**`src/server.ts`** - Express HTTP server
|
||||
- Serves web UI for service registration and login
|
||||
- Handles music streaming (`/stream/track/:id`)
|
||||
- Generates icons and cover art (`/icon/...`, `/art/...`)
|
||||
- Serves Sonos-specific XML files (strings, presentation map)
|
||||
- Binds SOAP service for Sonos SMAPI communication
|
||||
|
||||
**`src/smapi.ts`** - Sonos SMAPI SOAP implementation (1200+ lines)
|
||||
- Implements the Sonos Music API Protocol via SOAP/XML
|
||||
- Core operations: `getMetadata`, `getMediaURI`, `search`, `getExtendedMetadata`
|
||||
- Handles authentication flow with link codes and device auth tokens
|
||||
- Manages token refresh and session management
|
||||
- Maps music library concepts to Sonos browse hierarchy
|
||||
|
||||
**`src/subsonic.ts`** - Subsonic API client
|
||||
- Implements `MusicService` and `MusicLibrary` interfaces
|
||||
- Handles authentication with Subsonic servers using token-based auth
|
||||
- Translates between Subsonic data models and bonob's domain types
|
||||
- Supports custom player configurations for transcoding
|
||||
- Special handling for Navidrome (bearer token authentication)
|
||||
- Implements artist image fetching with optional caching
|
||||
|
||||
**`src/music_service.ts`** - Core domain types and interfaces
|
||||
- Defines `MusicService` interface (auth and login)
|
||||
- Defines `MusicLibrary` interface (browsing, search, streaming, rating, scrobbling)
|
||||
- Domain types: `Artist`, `Album`, `Track`, `Playlist`, `Genre`, `Rating`, etc.
|
||||
- Uses `fp-ts` for functional programming patterns (`TaskEither`, `Option`, `Either`)
|
||||
|
||||
**`src/sonos.ts`** - Sonos device discovery and registration
|
||||
- Discovers Sonos devices on the network using SSDP/UPnP
|
||||
- Registers/unregisters bonob as a music service with Sonos systems
|
||||
- Supports both auto-discovery and seed-host based discovery
|
||||
- Uses `@svrooij/sonos` library for device communication
|
||||
|
||||
**`src/smapi_auth.ts`** - Authentication token management
|
||||
- Implements JWT-based SMAPI tokens (token + key pairs)
|
||||
- Handles token verification and expiry
|
||||
- Token refresh flow using `fp-ts` `TaskEither`
|
||||
|
||||
**`src/config.ts`** - Configuration management
|
||||
- Reads and validates environment variables (all prefixed with `BNB_`)
|
||||
- Legacy environment variable support (BONOB_ prefix)
|
||||
- Type-safe configuration with defaults
|
||||
|
||||
### Key Abstractions
|
||||
|
||||
**BUrn (Bonob URN)** - Resource identifier system (`src/burn.ts`)
|
||||
- Format: `{ system: string, resource: string }`
|
||||
- Systems: `subsonic` (for cover art), `external` (for URLs like Spotify images)
|
||||
- Used for abstracting art/image sources across different backends
|
||||
|
||||
**URL Builder** (`src/url_builder.ts`)
|
||||
- Wraps URL manipulation with a builder pattern
|
||||
- Handles context path for reverse proxy deployments
|
||||
- Used throughout for generating URLs that Sonos devices can access
|
||||
|
||||
**Custom Players** (`src/subsonic.ts`)
|
||||
- Allows mime-type specific transcoding configurations
|
||||
- Maps source mime types to transcoded types
|
||||
- Creates custom "client" names in Subsonic (e.g., "bonob+audio/flac")
|
||||
- Example: `BNB_SUBSONIC_CUSTOM_CLIENTS="audio/flac>audio/mp3"`
|
||||
|
||||
### Data Flow
|
||||
|
||||
1. **Sonos App Request** → SOAP endpoint (`/ws/sonos`)
|
||||
2. **SOAP Service** → Verifies auth token, calls `MusicLibrary` methods
|
||||
3. **MusicLibrary** → Makes Subsonic API calls, transforms data
|
||||
4. **SOAP Response** → Returns XML formatted for Sonos
|
||||
|
||||
For streaming:
|
||||
1. **Sonos Device** → `GET /stream/track/:id` with custom headers (bnbt, bnbk)
|
||||
2. **Stream Handler** → Verifies token, calls `MusicLibrary.stream()`
|
||||
3. **Subsonic Stream** → Proxies audio with proper mime-type handling
|
||||
4. **Response** → Streams audio to Sonos, reports "now playing"
|
||||
|
||||
### Icon System (`src/icon.ts`)
|
||||
- SVG-based icon generation with dynamic colors
|
||||
- Supports foreground/background color customization via `BNB_ICON_FOREGROUND_COLOR` and `BNB_ICON_BACKGROUND_COLOR`
|
||||
- Genre-specific icons
|
||||
- Text overlay support (e.g., year icons like "1984")
|
||||
- Holiday/festival decorations (auto-applied based on date)
|
||||
- Legacy mode: renders to 80x80 PNG for older Sonos systems
|
||||
|
||||
### Authentication Flow
|
||||
1. Sonos app requests link code via `getAppLink()`
|
||||
2. User visits login URL with link code
|
||||
3. User enters Subsonic credentials
|
||||
4. bonob validates with Subsonic, generates service token
|
||||
5. bonob associates link code with service token
|
||||
6. Sonos polls `getDeviceAuthToken()` with link code
|
||||
7. bonob returns SMAPI token (JWT) to Sonos
|
||||
8. Subsequent requests use SMAPI token, which maps to service token
|
||||
|
||||
### Testing Philosophy
|
||||
- Jest with ts-jest preset
|
||||
- In-memory implementations for `LinkCodes`, `APITokens` for testing
|
||||
- Mocking with `ts-mockito`
|
||||
- Test helpers in `tests/` directory
|
||||
- Console.log suppressed in tests (see `tests/setup.js`)
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Error Handling
|
||||
- Use `fp-ts` `TaskEither<AuthFailure, T>` for async operations that can fail with auth errors
|
||||
- SOAP faults for Sonos-specific errors (see SMAPI_FAULT_* constants)
|
||||
- Promise-based error handling with `.catch()` for most async operations
|
||||
|
||||
### Type Safety
|
||||
- Strict TypeScript (`strict: true`, `noImplicitAny: true`, `noUncheckedIndexedAccess: true`)
|
||||
- Extensive use of discriminated unions
|
||||
- Interface-based design for pluggable services
|
||||
|
||||
### Logging
|
||||
- Winston-based logger (`src/logger.ts`)
|
||||
- Log level controlled by `BNB_LOG_LEVEL`
|
||||
- Request logging optional via `BNB_SERVER_LOG_REQUESTS`
|
||||
|
||||
### Functional Programming
|
||||
- Heavy use of `fp-ts` for `Option`, `Either`, `TaskEither`
|
||||
- Pipe-based composition (`pipe(data, fn1, fn2, ...)`)
|
||||
- Immutable data transformations
|
||||
|
||||
## File Organization
|
||||
- `src/` - TypeScript source code
|
||||
- `tests/` - Jest test files (mirrors src/ structure)
|
||||
- `build/` - Compiled JavaScript (gitignored)
|
||||
- `web/` - HTML templates (Eta templating) and static assets
|
||||
- `typings/` - Custom TypeScript definitions
|
||||
|
||||
## Important Constraints
|
||||
- bonob must be accessible from Sonos devices at `BNB_URL`
|
||||
- `BNB_URL` cannot contain "localhost" (validation error)
|
||||
- Sonos requires specific XML formats (SMAPI WSDL v1.19.6)
|
||||
- Streaming must handle HTTP range requests for seek functionality
|
||||
- Token lifetime (`BNB_AUTH_TIMEOUT`) should be less than Subsonic session timeout
|
||||
Reference in New Issue
Block a user