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 +97 -50
- package/dist/index.d.mts +154 -15
- package/dist/index.d.ts +154 -15
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +9 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -3
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
|
-
- **
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
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
|
|
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",
|
|
38
|
+
serverUrl: "https://api.synxed.com",
|
|
39
39
|
autoConnect: true,
|
|
40
40
|
});
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
### Play a
|
|
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
|
|
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
|
-
###
|
|
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); //
|
|
126
|
+
player.seek(30000); // on-demand only; no-op for radio
|
|
75
127
|
player.setVolume(0.8);
|
|
76
|
-
player.skip();
|
|
77
|
-
player.previous();
|
|
78
|
-
player.skipTo(2);
|
|
128
|
+
player.skip();
|
|
129
|
+
player.previous();
|
|
130
|
+
player.skipTo(2);
|
|
79
131
|
```
|
|
80
132
|
|
|
81
|
-
## Listening for
|
|
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
|
|
150
|
+
## API reference
|
|
102
151
|
|
|
103
152
|
### `new SynxedPlayer(config)`
|
|
104
153
|
|
|
105
|
-
- `apiKey`: `string` (
|
|
106
|
-
- `serverUrl`: `string` (
|
|
107
|
-
- `autoConnect`: `boolean` (
|
|
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>`
|
|
112
|
-
- `playPlaylist(options)`: `Promise<void>`
|
|
113
|
-
- `
|
|
114
|
-
- `
|
|
115
|
-
- `
|
|
116
|
-
- `
|
|
117
|
-
- `
|
|
118
|
-
- `
|
|
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
|
-
###
|
|
169
|
+
### Getters
|
|
170
|
+
|
|
171
|
+
- `currentTrack`: current `TrackInfo | null`
|
|
172
|
+
- `contentKind`: last `play*` kind (`ContentKind` enum, including `RADIO`)
|
|
124
173
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
180
|
+
- `stateChange`, `timeUpdate`, `trackChange`, `queueUpdated`, `error`, `connected`, `disconnected`
|
|
134
181
|
|
|
135
|
-
|
|
182
|
+
### `TrackInfo`
|
|
136
183
|
|
|
137
184
|
```typescript
|
|
138
185
|
interface TrackInfo {
|
|
139
186
|
id: string;
|
|
140
|
-
kind: "catalog" | "internal";
|
|
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:
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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:
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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 };
|