synxed-sdk 0.1.2 β†’ 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,12 +7,12 @@ The official Synxed SDK for frontend developers to integrate high-quality music
7
7
 
8
8
  ## Features
9
9
 
10
- - ** Simple Integration**: Play songs or playlists with just a few lines of code.
11
- - **🎧 High-Fidelity Streaming**: Robust HLS support via `hls.js` for seamless playback across all browsers.
12
- - **πŸ“Š Built-in Analytics**: Automatic session tracking and stream event reporting.
13
- - **πŸ›  Framework Agnostic**: Works with React, Vue, Angular, or Vanilla JS.
14
- - **πŸ”’ Secure**: Designed for private, signed-URL streaming with backend proxy support.
15
- - **πŸ’ͺ Type Safe**: Fully written in TypeScript with comprehensive definitions.
10
+ - **Simple integration**: Play songs, playlists, or **24/7 live radio** with a few lines of code.
11
+ - **High-fidelity streaming**: Robust HLS support via `hls.js` for seamless playback across browsers (on-demand content).
12
+ - **Built-in analytics**: Session `stream_start` plus automatic **`heartbeat`** intervals from the server `initAck` (including radio, which uses `positionMs: 0`).
13
+ - **Framework-agnostic core**: `SynxedPlayer` works in React, Vue, Angular, or vanilla JS.
14
+ - **Optional web UI**: Vanilla DOM overlay (`SynxedWebPlayer`) with **mini**, **wide**, and **large** layouts β€” works in any framework; **theme** and **placement** are fully configurable.
15
+ - **Type-safe**: Written in TypeScript with full definitions.
16
16
 
17
17
  ## Installation
18
18
 
@@ -28,116 +28,163 @@ npm install hls.js
28
28
 
29
29
  ## Quick Start
30
30
 
31
- ### Initialize the Player
31
+ ### Initialize the player
32
32
 
33
33
  ```typescript
34
34
  import { SynxedPlayer } from "synxed-sdk";
35
35
 
36
36
  const player = new SynxedPlayer({
37
37
  apiKey: "YOUR_SYNXED_API_KEY",
38
- serverUrl: "https://api.synxed.com", // or your local backend URL
38
+ serverUrl: "https://api.synxed.com",
39
39
  autoConnect: true,
40
40
  });
41
41
  ```
42
42
 
43
- ### Play a Playlist
44
-
45
- Provide a playlist code generated from the Synxed platform.
43
+ ### Play a playlist
46
44
 
47
45
  ```typescript
48
- // Start playback from a playlist
49
46
  player.playPlaylist({
50
47
  playlistCode: "sxpl_VZCCGQAQJV",
51
48
  });
52
49
  ```
53
50
 
54
- ### Play a Single Song
51
+ ### Play a single song
55
52
 
56
53
  ```typescript
57
- // Play a song from the global catalog
58
54
  player.playSong({
59
55
  catalogTrackId: "2dcad8e0-3695-4971-9e35-f762f3f9d3a5",
60
56
  });
61
57
 
62
- // Or an internal track from your own library
63
58
  player.playSong({
64
59
  internalTrackId: "track-uuid-here",
65
60
  });
66
61
  ```
67
62
 
68
- ### Control Playback
63
+ ### Live radio
64
+
65
+ Continuous **non-HLS** stream (not seekable). The SDK sends `CONTENT_KIND_RADIO` (`4`) over the same native `/sdk` WebSocket; the server returns a direct `playbackUrl` (e.g. MPEG stream).
66
+
67
+ ```typescript
68
+ await player.playRadio({ listenerId: "optional-stable-id" });
69
+ ```
70
+
71
+ - **`skip` / `previous` / `skipTo` / `seek`**: no-ops for radio (the live edge keeps moving on the server).
72
+ - **`pause` / `resume`**: local audio only; when the user resumes, they rejoin the live stream.
73
+ - **Metadata**: poll the REST endpoint (unauthenticated on most deployments):
74
+
75
+ ```typescript
76
+ import { fetchRadioNowPlaying } from "synxed-sdk";
77
+
78
+ const np = await fetchRadioNowPlaying("https://api.synxed.com");
79
+ // { title, station?, isLive? } β€” poll every 10–15s for β€œnow playing” UI
80
+ ```
81
+
82
+ ### Optional web player UI (vanilla β€” no React required)
83
+
84
+ ```typescript
85
+ import { SynxedWebPlayer } from "synxed-sdk";
86
+
87
+ const ui = SynxedWebPlayer.mount({
88
+ apiKey: "YOUR_SYNXED_API_KEY",
89
+ serverUrl: "https://api.synxed.com",
90
+ source: { type: "radio" },
91
+ mode: "wide",
92
+ avatarUrl: "/dj.webp",
93
+ attribution: "DJ Jesse Β· Synxed Γ— Your Brand",
94
+ theme: {
95
+ accent: "#ef4444",
96
+ glow: "rgba(239, 68, 68, 0.35)",
97
+ background: "#0a0707",
98
+ },
99
+ position: { placement: "top-left", offsetX: 16, offsetY: 16 },
100
+ });
101
+
102
+ // Later: ui.destroy();
103
+ ```
104
+
105
+ Mount into your own container instead of a fixed overlay:
106
+
107
+ ```typescript
108
+ SynxedWebPlayer.mount({
109
+ container: document.getElementById("player-slot")!,
110
+ apiKey: "...",
111
+ serverUrl: "...",
112
+ source: { type: "playlist", playlistCode: "sxpl_..." },
113
+ });
114
+ ```
115
+
116
+ **Modes**: `mini` (avatar only), `wide` (pill bar, default), `large` (card + visualizer). Skip controls are hidden for radio.
117
+
118
+ In React/Vue, create the player in `useEffect` / `onMounted` and call `destroy()` on teardown β€” same class, no separate React package.
119
+
120
+ ### Playback controls
69
121
 
70
122
  ```typescript
71
123
  player.pause();
72
124
  player.resume();
73
125
  player.stop();
74
- player.seek(30000); // Seek to 30 seconds
126
+ player.seek(30000); // on-demand only; no-op for radio
75
127
  player.setVolume(0.8);
76
- player.skip(); // Skip to next track in playlist
77
- player.previous(); // Go to previous track in playlist
78
- player.skipTo(2); // Jump directly to track at index 2
128
+ player.skip();
129
+ player.previous();
130
+ player.skipTo(2);
79
131
  ```
80
132
 
81
- ## Listening for Events
133
+ ## Listening for events
82
134
 
83
135
  ```typescript
84
- // Track state changes (idle, loading, playing, paused, error)
85
136
  player.on("stateChange", (state) => {
86
137
  console.log("Player status:", state.status);
87
138
  });
88
139
 
89
- // High-resolution time updates (60fps)
90
140
  player.on("timeUpdate", ({ currentTime, duration }) => {
91
141
  const progress = (currentTime / duration) * 100;
92
142
  console.log(`Progress: ${progress.toFixed(2)}%`);
93
143
  });
94
144
 
95
- // Error handling
96
145
  player.on("error", (err) => {
97
146
  console.error("Playback Error:", err.message);
98
147
  });
99
148
  ```
100
149
 
101
- ## API Reference
150
+ ## API reference
102
151
 
103
152
  ### `new SynxedPlayer(config)`
104
153
 
105
- - `apiKey`: `string` (Required) Your developer API key.
106
- - `serverUrl`: `string` (Required) Your Synxed backend URL.
107
- - `autoConnect`: `boolean` (Default: `true`) Connect to the streaming socket immediately.
154
+ - `apiKey`: `string` (required)
155
+ - `serverUrl`: `string` (required)
156
+ - `autoConnect`: `boolean` (default `true`)
108
157
 
109
158
  ### Methods
110
159
 
111
- - `playSong(options)`: `Promise<void>` Plays a single track by `catalogTrackId` or `internalTrackId`.
112
- - `playPlaylist(options)`: `Promise<void>` Starts playback from a `playlistCode`.
113
- - `pause()`: `void` Pauses the current stream.
114
- - `resume()`: `void` Resumes a paused stream.
115
- - `stop()`: `void` Stops playback and releases audio resources.
116
- - `skip()`: `void` Skips to the next track in the playlist.
117
- - `previous()`: `void` Goes to the previous track in the playlist.
118
- - `skipTo(index)`: `void` Jumps directly to a specific track index in the playlist.
119
- - `seek(ms)`: `void` Seeks to a specific position in milliseconds.
120
- - `setVolume(0-1)`: `void` Sets the volume.
121
- - `destroy()`: `void` Cleans up the player, listeners, and disconnects.
160
+ - `playSong(options)`: `Promise<void>`
161
+ - `playPlaylist(options)`: `Promise<void>`
162
+ - `playRadio(options?)`: `Promise<void>` β€” live radio (`listenerId` optional)
163
+ - `pause()` / `resume()` / `stop()`
164
+ - `skip()` / `previous()` / `skipTo(index)` β€” playlist / on-demand only
165
+ - `seek(ms)` β€” on-demand only (no-op for radio)
166
+ - `setVolume(0–1)`
167
+ - `destroy()`
122
168
 
123
- ### Events
169
+ ### Getters
170
+
171
+ - `currentTrack`: current `TrackInfo | null`
172
+ - `contentKind`: last `play*` kind (`ContentKind` enum, including `RADIO`)
124
173
 
125
- - `stateChange`: `(state: PlayerState)` Fired when the engine state changes. Includes `currentTrack`, `currentTime`, `duration`, `volume`, and `status`.
126
- - `timeUpdate`: `({ currentTime, duration })` Fired during playback at ~60fps.
127
- - `trackChange`: `(track: TrackInfo)` Fired when a new track starts playing in a playlist.
128
- - `queueUpdated`: `(tracks: TrackInfo[])` Fired when the playlist queue changes (initial load, skip, reset).
129
- - `error`: `(error: Error)` Fired on playback or connection failures.
130
- - `connected`: `()` Fired when the socket connection is established.
131
- - `disconnected`: `(reason: string)` Fired when the socket disconnects.
174
+ ### `fetchRadioNowPlaying(serverUrl, init?)`
175
+
176
+ `Promise<RadioNowPlaying | null>` β€” wraps `GET {serverUrl}/radio/now-playing`.
177
+
178
+ ### Events
132
179
 
133
- ### TrackInfo
180
+ - `stateChange`, `timeUpdate`, `trackChange`, `queueUpdated`, `error`, `connected`, `disconnected`
134
181
 
135
- Each track in the playlist has the following shape:
182
+ ### `TrackInfo`
136
183
 
137
184
  ```typescript
138
185
  interface TrackInfo {
139
186
  id: string;
140
- kind: "catalog" | "internal"; // distinguishes catalog vs internal tracks
187
+ kind: "catalog" | "internal";
141
188
  title?: string;
142
189
  artist?: string;
143
190
  duration?: number;
package/dist/index.d.mts CHANGED
@@ -7,10 +7,28 @@ declare class EventEmitter<Events extends Record<string, any>> {
7
7
  removeAllListeners(): void;
8
8
  }
9
9
 
10
+ declare enum ContentKind {
11
+ UNSPECIFIED = 0,
12
+ SONG = 1,
13
+ PLAYLIST = 2,
14
+ CATEGORY = 3,
15
+ RADIO = 4
16
+ }
17
+ declare enum ErrorCode {
18
+ UNSPECIFIED = 0,
19
+ UNAUTHORIZED = 1,
20
+ VALIDATION_ERROR = 2,
21
+ NOT_FOUND = 3,
22
+ PROCESSING = 4,
23
+ SERVICE_UNAVAILABLE = 5,
24
+ BAD_PROTOBUF = 6
25
+ }
26
+
10
27
  interface SynxedConfig {
11
28
  apiKey: string;
12
29
  serverUrl: string;
13
30
  autoConnect?: boolean;
31
+ debug?: boolean;
14
32
  }
15
33
  interface PlaySongOptions {
16
34
  catalogTrackId?: string;
@@ -21,6 +39,15 @@ interface PlayPlaylistOptions {
21
39
  playlistCode: string;
22
40
  listenerId?: string;
23
41
  }
42
+ interface PlayRadioOptions {
43
+ listenerId?: string;
44
+ }
45
+ /** Response shape from `GET /radio/now-playing` */
46
+ interface RadioNowPlaying {
47
+ title: string;
48
+ station?: string;
49
+ isLive?: boolean;
50
+ }
24
51
  interface TrackInfo {
25
52
  id: string;
26
53
  kind: "catalog" | "internal";
@@ -30,7 +57,7 @@ interface TrackInfo {
30
57
  albumArt?: string;
31
58
  }
32
59
  interface PlayerState {
33
- status: 'idle' | 'loading' | 'playing' | 'paused' | 'error';
60
+ status: "idle" | "loading" | "playing" | "paused" | "error";
34
61
  currentTrack: TrackInfo | null;
35
62
  currentTime: number;
36
63
  duration: number;
@@ -56,12 +83,18 @@ declare class SynxedPlayer extends EventEmitter<SynxedEvents> {
56
83
  private config;
57
84
  private status;
58
85
  private volume;
86
+ private activeContentKind;
87
+ private heartbeatTimer;
59
88
  constructor(config: SynxedConfig);
60
89
  get currentTrack(): TrackInfo | null;
90
+ /** Active playback init kind from the last `play*` call (RADIO, PLAYLIST, SONG, …). */
91
+ get contentKind(): ContentKind;
92
+ private controlPositionMs;
61
93
  private setupListeners;
62
94
  private connect;
63
95
  playSong(options: PlaySongOptions): Promise<void>;
64
96
  playPlaylist(options: PlayPlaylistOptions): Promise<void>;
97
+ playRadio(options?: PlayRadioOptions): Promise<void>;
65
98
  pause(): void;
66
99
  resume(): void;
67
100
  stop(): void;
@@ -70,7 +103,11 @@ declare class SynxedPlayer extends EventEmitter<SynxedEvents> {
70
103
  skipTo(index: number): void;
71
104
  seek(ms: number): void;
72
105
  setVolume(volume: number): void;
106
+ private positionMsForAnalytics;
73
107
  private emitAnalytics;
108
+ private clearHeartbeat;
109
+ private startHeartbeat;
110
+ private applyContentSummary;
74
111
  private handleServerMessage;
75
112
  private handleTrackEnded;
76
113
  private updateStatus;
@@ -91,20 +128,122 @@ declare class SynxedProtocolError extends SynxedError {
91
128
  constructor(message: string);
92
129
  }
93
130
 
94
- declare enum ContentKind {
95
- UNSPECIFIED = 0,
96
- SONG = 1,
97
- PLAYLIST = 2,
98
- CATEGORY = 3
131
+ interface TransportEvents {
132
+ connected: () => void;
133
+ disconnected: (reason: string) => void;
134
+ error: (error: any) => void;
135
+ message: (payload: any) => void;
99
136
  }
100
- declare enum ErrorCode {
101
- UNSPECIFIED = 0,
102
- UNAUTHORIZED = 1,
103
- VALIDATION_ERROR = 2,
104
- NOT_FOUND = 3,
105
- PROCESSING = 4,
106
- SERVICE_UNAVAILABLE = 5,
107
- BAD_PROTOBUF = 6
137
+ declare function buildSdkWebSocketUrl(serverUrl: string, apiKey: string): string;
138
+ declare class TransportManager extends EventEmitter<TransportEvents> {
139
+ private ws;
140
+ private connectPromise;
141
+ connect(apiKey: string, serverUrl: string): void;
142
+ waitForConnection(): Promise<void>;
143
+ disconnect(): void;
144
+ sendInit(params: any): void;
145
+ sendControl(params: any): void;
146
+ sendAnalytics(params: any): void;
147
+ get isConnected(): boolean;
148
+ private sendBytes;
149
+ }
150
+
151
+ /**
152
+ * Fetches live radio "now playing" metadata from the Synxed backend.
153
+ * Poll every 10–15 seconds to keep UI in sync (see integration docs).
154
+ */
155
+ declare function fetchRadioNowPlaying(serverUrl: string, init?: RequestInit): Promise<RadioNowPlaying | null>;
156
+
157
+ type SynxedWebPlayerMode = 'mini' | 'wide' | 'large';
158
+ type SynxedWebPlayerPlacement = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'bottom-center';
159
+ interface SynxedWebPlayerTheme {
160
+ accent?: string;
161
+ accentMuted?: string;
162
+ background?: string;
163
+ backgroundInner?: string;
164
+ border?: string;
165
+ text?: string;
166
+ textMuted?: string;
167
+ stationText?: string;
168
+ liveDot?: string;
169
+ glow?: string;
170
+ }
171
+ interface SynxedWebPlayerPosition {
172
+ placement?: SynxedWebPlayerPlacement;
173
+ offsetX?: number;
174
+ offsetY?: number;
175
+ }
176
+ type SynxedWebPlayerSource = {
177
+ type: 'playlist';
178
+ playlistCode: string;
179
+ } | {
180
+ type: 'radio';
181
+ };
182
+ interface SynxedWebPlayerOptions {
183
+ /** Mount target. If omitted, a fixed overlay root is appended to `document.body`. */
184
+ container?: HTMLElement;
185
+ apiKey: string;
186
+ serverUrl: string;
187
+ source: SynxedWebPlayerSource;
188
+ mode?: SynxedWebPlayerMode;
189
+ theme?: SynxedWebPlayerTheme;
190
+ position?: SynxedWebPlayerPosition;
191
+ avatarUrl?: string;
192
+ attribution?: string;
193
+ nowPlayingPollMs?: number;
194
+ powerByLabel?: string;
195
+ className?: string;
196
+ /** Extra inline styles on the outer shell */
197
+ style?: Partial<CSSStyleDeclaration>;
198
+ onMiniClick?: () => void;
199
+ }
200
+
201
+ /**
202
+ * Framework-agnostic web overlay player (vanilla DOM).
203
+ * Works in React, Vue, Angular, or plain HTML β€” no React dependency.
204
+ */
205
+ declare class SynxedWebPlayer {
206
+ private readonly options;
207
+ private readonly root;
208
+ private readonly ownsRoot;
209
+ private readonly theme;
210
+ private engine;
211
+ private pollTimer;
212
+ private destroyed;
213
+ private isPlaying;
214
+ private currentTrack;
215
+ private nowPlaying;
216
+ private avatarRing;
217
+ private titleEl;
218
+ private artistInlineEl;
219
+ private artistBlockEl;
220
+ private footerEl;
221
+ private playBtn;
222
+ constructor(options: SynxedWebPlayerOptions);
223
+ /** Convenience factory β€” same as `new SynxedWebPlayer(options)`. */
224
+ static mount(options: SynxedWebPlayerOptions): SynxedWebPlayer;
225
+ /** Underlying streaming engine (advanced integrations). */
226
+ get player(): SynxedPlayer | null;
227
+ get element(): HTMLElement;
228
+ destroy(): void;
229
+ private get isRadio();
230
+ private get mode();
231
+ private mountShell;
232
+ private buildAvatar;
233
+ private buildMini;
234
+ private buildWide;
235
+ private buildLarge;
236
+ private roundControlStyle;
237
+ private createPlayButton;
238
+ private buildVisualizer;
239
+ private decoLines;
240
+ private initEngine;
241
+ private startNowPlayingPoll;
242
+ private stopNowPlayingPoll;
243
+ private displayLine;
244
+ private refreshLabels;
245
+ private setPlayingVisual;
246
+ private togglePlay;
108
247
  }
109
248
 
110
- export { ContentKind, ErrorCode, type PlayPlaylistOptions, type PlaySongOptions, type PlayerState, type SynxedConfig, SynxedConnectionError, SynxedError, type SynxedEvents, SynxedPlaybackError, SynxedPlayer, SynxedProtocolError, type TrackInfo };
249
+ export { ContentKind, ErrorCode, type PlayPlaylistOptions, type PlayRadioOptions, type PlaySongOptions, type PlayerState, type RadioNowPlaying, type SynxedConfig, SynxedConnectionError, SynxedError, type SynxedEvents, SynxedPlaybackError, SynxedPlayer, SynxedProtocolError, SynxedWebPlayer, type SynxedWebPlayerMode, type SynxedWebPlayerOptions, type SynxedWebPlayerPlacement, type SynxedWebPlayerPosition, type SynxedWebPlayerSource, type SynxedWebPlayerTheme, type TrackInfo, type TransportEvents, TransportManager, buildSdkWebSocketUrl, fetchRadioNowPlaying };
package/dist/index.d.ts CHANGED
@@ -7,10 +7,28 @@ declare class EventEmitter<Events extends Record<string, any>> {
7
7
  removeAllListeners(): void;
8
8
  }
9
9
 
10
+ declare enum ContentKind {
11
+ UNSPECIFIED = 0,
12
+ SONG = 1,
13
+ PLAYLIST = 2,
14
+ CATEGORY = 3,
15
+ RADIO = 4
16
+ }
17
+ declare enum ErrorCode {
18
+ UNSPECIFIED = 0,
19
+ UNAUTHORIZED = 1,
20
+ VALIDATION_ERROR = 2,
21
+ NOT_FOUND = 3,
22
+ PROCESSING = 4,
23
+ SERVICE_UNAVAILABLE = 5,
24
+ BAD_PROTOBUF = 6
25
+ }
26
+
10
27
  interface SynxedConfig {
11
28
  apiKey: string;
12
29
  serverUrl: string;
13
30
  autoConnect?: boolean;
31
+ debug?: boolean;
14
32
  }
15
33
  interface PlaySongOptions {
16
34
  catalogTrackId?: string;
@@ -21,6 +39,15 @@ interface PlayPlaylistOptions {
21
39
  playlistCode: string;
22
40
  listenerId?: string;
23
41
  }
42
+ interface PlayRadioOptions {
43
+ listenerId?: string;
44
+ }
45
+ /** Response shape from `GET /radio/now-playing` */
46
+ interface RadioNowPlaying {
47
+ title: string;
48
+ station?: string;
49
+ isLive?: boolean;
50
+ }
24
51
  interface TrackInfo {
25
52
  id: string;
26
53
  kind: "catalog" | "internal";
@@ -30,7 +57,7 @@ interface TrackInfo {
30
57
  albumArt?: string;
31
58
  }
32
59
  interface PlayerState {
33
- status: 'idle' | 'loading' | 'playing' | 'paused' | 'error';
60
+ status: "idle" | "loading" | "playing" | "paused" | "error";
34
61
  currentTrack: TrackInfo | null;
35
62
  currentTime: number;
36
63
  duration: number;
@@ -56,12 +83,18 @@ declare class SynxedPlayer extends EventEmitter<SynxedEvents> {
56
83
  private config;
57
84
  private status;
58
85
  private volume;
86
+ private activeContentKind;
87
+ private heartbeatTimer;
59
88
  constructor(config: SynxedConfig);
60
89
  get currentTrack(): TrackInfo | null;
90
+ /** Active playback init kind from the last `play*` call (RADIO, PLAYLIST, SONG, …). */
91
+ get contentKind(): ContentKind;
92
+ private controlPositionMs;
61
93
  private setupListeners;
62
94
  private connect;
63
95
  playSong(options: PlaySongOptions): Promise<void>;
64
96
  playPlaylist(options: PlayPlaylistOptions): Promise<void>;
97
+ playRadio(options?: PlayRadioOptions): Promise<void>;
65
98
  pause(): void;
66
99
  resume(): void;
67
100
  stop(): void;
@@ -70,7 +103,11 @@ declare class SynxedPlayer extends EventEmitter<SynxedEvents> {
70
103
  skipTo(index: number): void;
71
104
  seek(ms: number): void;
72
105
  setVolume(volume: number): void;
106
+ private positionMsForAnalytics;
73
107
  private emitAnalytics;
108
+ private clearHeartbeat;
109
+ private startHeartbeat;
110
+ private applyContentSummary;
74
111
  private handleServerMessage;
75
112
  private handleTrackEnded;
76
113
  private updateStatus;
@@ -91,20 +128,122 @@ declare class SynxedProtocolError extends SynxedError {
91
128
  constructor(message: string);
92
129
  }
93
130
 
94
- declare enum ContentKind {
95
- UNSPECIFIED = 0,
96
- SONG = 1,
97
- PLAYLIST = 2,
98
- CATEGORY = 3
131
+ interface TransportEvents {
132
+ connected: () => void;
133
+ disconnected: (reason: string) => void;
134
+ error: (error: any) => void;
135
+ message: (payload: any) => void;
99
136
  }
100
- declare enum ErrorCode {
101
- UNSPECIFIED = 0,
102
- UNAUTHORIZED = 1,
103
- VALIDATION_ERROR = 2,
104
- NOT_FOUND = 3,
105
- PROCESSING = 4,
106
- SERVICE_UNAVAILABLE = 5,
107
- BAD_PROTOBUF = 6
137
+ declare function buildSdkWebSocketUrl(serverUrl: string, apiKey: string): string;
138
+ declare class TransportManager extends EventEmitter<TransportEvents> {
139
+ private ws;
140
+ private connectPromise;
141
+ connect(apiKey: string, serverUrl: string): void;
142
+ waitForConnection(): Promise<void>;
143
+ disconnect(): void;
144
+ sendInit(params: any): void;
145
+ sendControl(params: any): void;
146
+ sendAnalytics(params: any): void;
147
+ get isConnected(): boolean;
148
+ private sendBytes;
149
+ }
150
+
151
+ /**
152
+ * Fetches live radio "now playing" metadata from the Synxed backend.
153
+ * Poll every 10–15 seconds to keep UI in sync (see integration docs).
154
+ */
155
+ declare function fetchRadioNowPlaying(serverUrl: string, init?: RequestInit): Promise<RadioNowPlaying | null>;
156
+
157
+ type SynxedWebPlayerMode = 'mini' | 'wide' | 'large';
158
+ type SynxedWebPlayerPlacement = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'bottom-center';
159
+ interface SynxedWebPlayerTheme {
160
+ accent?: string;
161
+ accentMuted?: string;
162
+ background?: string;
163
+ backgroundInner?: string;
164
+ border?: string;
165
+ text?: string;
166
+ textMuted?: string;
167
+ stationText?: string;
168
+ liveDot?: string;
169
+ glow?: string;
170
+ }
171
+ interface SynxedWebPlayerPosition {
172
+ placement?: SynxedWebPlayerPlacement;
173
+ offsetX?: number;
174
+ offsetY?: number;
175
+ }
176
+ type SynxedWebPlayerSource = {
177
+ type: 'playlist';
178
+ playlistCode: string;
179
+ } | {
180
+ type: 'radio';
181
+ };
182
+ interface SynxedWebPlayerOptions {
183
+ /** Mount target. If omitted, a fixed overlay root is appended to `document.body`. */
184
+ container?: HTMLElement;
185
+ apiKey: string;
186
+ serverUrl: string;
187
+ source: SynxedWebPlayerSource;
188
+ mode?: SynxedWebPlayerMode;
189
+ theme?: SynxedWebPlayerTheme;
190
+ position?: SynxedWebPlayerPosition;
191
+ avatarUrl?: string;
192
+ attribution?: string;
193
+ nowPlayingPollMs?: number;
194
+ powerByLabel?: string;
195
+ className?: string;
196
+ /** Extra inline styles on the outer shell */
197
+ style?: Partial<CSSStyleDeclaration>;
198
+ onMiniClick?: () => void;
199
+ }
200
+
201
+ /**
202
+ * Framework-agnostic web overlay player (vanilla DOM).
203
+ * Works in React, Vue, Angular, or plain HTML β€” no React dependency.
204
+ */
205
+ declare class SynxedWebPlayer {
206
+ private readonly options;
207
+ private readonly root;
208
+ private readonly ownsRoot;
209
+ private readonly theme;
210
+ private engine;
211
+ private pollTimer;
212
+ private destroyed;
213
+ private isPlaying;
214
+ private currentTrack;
215
+ private nowPlaying;
216
+ private avatarRing;
217
+ private titleEl;
218
+ private artistInlineEl;
219
+ private artistBlockEl;
220
+ private footerEl;
221
+ private playBtn;
222
+ constructor(options: SynxedWebPlayerOptions);
223
+ /** Convenience factory β€” same as `new SynxedWebPlayer(options)`. */
224
+ static mount(options: SynxedWebPlayerOptions): SynxedWebPlayer;
225
+ /** Underlying streaming engine (advanced integrations). */
226
+ get player(): SynxedPlayer | null;
227
+ get element(): HTMLElement;
228
+ destroy(): void;
229
+ private get isRadio();
230
+ private get mode();
231
+ private mountShell;
232
+ private buildAvatar;
233
+ private buildMini;
234
+ private buildWide;
235
+ private buildLarge;
236
+ private roundControlStyle;
237
+ private createPlayButton;
238
+ private buildVisualizer;
239
+ private decoLines;
240
+ private initEngine;
241
+ private startNowPlayingPoll;
242
+ private stopNowPlayingPoll;
243
+ private displayLine;
244
+ private refreshLabels;
245
+ private setPlayingVisual;
246
+ private togglePlay;
108
247
  }
109
248
 
110
- export { ContentKind, ErrorCode, type PlayPlaylistOptions, type PlaySongOptions, type PlayerState, type SynxedConfig, SynxedConnectionError, SynxedError, type SynxedEvents, SynxedPlaybackError, SynxedPlayer, SynxedProtocolError, type TrackInfo };
249
+ export { ContentKind, ErrorCode, type PlayPlaylistOptions, type PlayRadioOptions, type PlaySongOptions, type PlayerState, type RadioNowPlaying, type SynxedConfig, SynxedConnectionError, SynxedError, type SynxedEvents, SynxedPlaybackError, SynxedPlayer, SynxedProtocolError, SynxedWebPlayer, type SynxedWebPlayerMode, type SynxedWebPlayerOptions, type SynxedWebPlayerPlacement, type SynxedWebPlayerPosition, type SynxedWebPlayerSource, type SynxedWebPlayerTheme, type TrackInfo, type TransportEvents, TransportManager, buildSdkWebSocketUrl, fetchRadioNowPlaying };