smooth-player 1.0.4 → 2.0.0

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
@@ -6,13 +6,12 @@ Smooth Player is a TypeScript audio player for the web with built-in playlist ha
6
6
 
7
7
  - Single track and playlist playback
8
8
  - Nested playlists (`AudioPlaylist` inside `PlaylistEntry`)
9
- - Automatic playlist behavior:
10
- - If there is only one track, playlist controls stay hidden
11
- - If there are multiple tracks, next/previous + playlist panel are available
12
- - Visualizer modes: `spectrum`, `waveform`, `none`
13
- - Circular draggable progress ring support
14
- - Configurable `accentColor` through player config
15
- - Built-in debug panel support
9
+ - Drag and drop audio files and playlist files (`.m3u`, `.m3u8`)
10
+ - Built-in visualizer support and configurable visual styles
11
+ - Automatic UI behavior for single-track vs multi-track playlists
12
+ - Accent + background theming via config/API (with generated gradients)
13
+ - Standard UI helpers with optional user preference persistence
14
+ - Debug hooks and runtime telemetry bindings
16
15
  - Typed API (`.d.ts`) with ESM + CJS builds
17
16
 
18
17
  ## Install
@@ -26,7 +25,7 @@ npm install smooth-player
26
25
  ## Quick Start
27
26
 
28
27
  ```ts
29
- import { SmoothPlayer, mountStandardPlayerUI } from "smooth-player";
28
+ import { SmoothPlayer, mountPlayerUI } from "smooth-player";
30
29
  import "smooth-player/dist/smooth-player.css";
31
30
 
32
31
  const tracks = [
@@ -47,21 +46,25 @@ const player = new SmoothPlayer({
47
46
  initialVolume: 0.8,
48
47
  visualizer: "spectrum",
49
48
  accentColor: "#0ed2a4",
49
+ backgroundColor: "#0b1220",
50
50
  debug: false,
51
51
  });
52
52
 
53
53
  const root = document.querySelector("#player-root");
54
54
  if (!(root instanceof HTMLElement)) throw new Error("Missing #player-root");
55
55
 
56
- mountStandardPlayerUI(player, root);
56
+ mountPlayerUI(player, root);
57
57
  ```
58
58
 
59
59
  ## Core Config (`SmoothPlayerOptions`)
60
60
 
61
+ - `audio?: HTMLAudioElement`
61
62
  - `playlist?: PlaylistEntry[]`
62
63
  - `visualizer?: "spectrum" | "waveform" | "none"` (default: `"spectrum"`)
63
64
  - `accentColor?: string` (default: `#0ed2a4`)
65
+ - `backgroundColor?: string` (default: `#0b1220`)
64
66
  - `debug?: boolean` (default: `false`)
67
+ - `crossOrigin?: HTMLMediaElement["crossOrigin"]` (default: `"anonymous"`)
65
68
  - `initialVolume?: number`
66
69
  - `initialTrackIndex?: number`
67
70
  - `initialShuffle?: boolean`
@@ -101,7 +104,7 @@ const player = new SmoothPlayer({
101
104
  });
102
105
  ```
103
106
 
104
- Use `mountDebugPanel(...)` to bind debug metrics to your elements, or call `mountStandardPlayerUI(player, root, { debugEnabled: true })` when your DOM includes the debug panel nodes.
107
+ Use `mountDebugPanel(...)` to bind debug metrics to your own elements, or call `mountPlayerUI(player, root, { debugEnabled: true })` to auto-generate the debug panel in the mounted UI.
105
108
 
106
109
  Runtime methods:
107
110
 
@@ -157,9 +160,63 @@ Subscribe with `player.on(eventName, handler)`:
157
160
  - `volumechange`
158
161
  - `error`
159
162
 
163
+ ## Player API Overview
164
+
165
+ Playback:
166
+
167
+ - `play(index?)`
168
+ - `pause()`
169
+ - `toggle()`
170
+ - `next()`
171
+ - `previous()`
172
+ - `seek(seconds)`
173
+ - `setVolume(volume)`
174
+ - `setLoop(loop)`
175
+
176
+ Playlist and track management:
177
+
178
+ - `setPlaylist(entries, startIndex?)`
179
+ - `selectPlaylist(playlistId, startIndex?)`
180
+ - `loadTrack(track)`
181
+ - `getPlaylist()`
182
+ - `getPlaylists()`
183
+ - `getCurrentPlaylist()`
184
+ - `getCurrentTrack()`
185
+ - `getCurrentTrackIndex()`
186
+
187
+ Visualizer and style:
188
+
189
+ - `setVisualizer(mode)`
190
+ - `getVisualizer()`
191
+ - `setSpectrumStyle(options)`
192
+ - `getSpectrumStyle()`
193
+ - `setWaveformStyle(options)`
194
+ - `getWaveformStyle()`
195
+ - `configureAnalyzer(options)`
196
+ - `getSpectrumData()`
197
+ - `getWaveformData()`
198
+
199
+ State and utility:
200
+
201
+ - `getState()`
202
+ - `formatTime(seconds)`
203
+ - `getDuration()`
204
+ - `getCurrentTime()`
205
+ - `getAudioElement()`
206
+ - `setAccentColor(color)`
207
+ - `getAccentColor()`
208
+ - `applyAccentColor(targetElement)`
209
+ - `setDebug(enabled)`
210
+ - `getDebug()`
211
+ - `setBackgroundColor(color)`
212
+ - `getBackgroundColor()`
213
+ - `applyBackgroundColor(targetElement)`
214
+ - `applyTheme(targetElement)` (accent + background)
215
+
160
216
  ## UI Mount Helpers
161
217
 
162
- - `mountStandardPlayerUI(player, root, options?)`
218
+ - `mountPlayerUI(player, root, options?)`
219
+ - `mountAudioDrop(target, options?)`
163
220
  - `mountTrackInfo(titleElement, artistElement, options?)`
164
221
  - `mountPlayButton(buttonElement, options?)`
165
222
  - `mountProgress(options)`
@@ -171,23 +228,63 @@ Subscribe with `player.on(eventName, handler)`:
171
228
  - `mountPlaylistTitle(element, options?)`
172
229
  - `mountDebugPanel(options)`
173
230
 
231
+ ### Player UI options
232
+
233
+ `mountPlayerUI(player, root, options?)` supports:
234
+
235
+ - `debugEnabled?: boolean`
236
+ - `enableAudioDrop?: boolean` (default: `true`)
237
+ - `enableErrorNotice?: boolean` (default: `true`)
238
+ - `showLogo?: boolean` (default: `true`)
239
+ - `persistUserPreferences?: boolean` (default: `true`)
240
+
241
+ When preference persistence is enabled, user choices can override initial UI configuration on next sessions.
242
+
243
+ ### Drag and drop behavior
244
+
245
+ `mountAudioDrop(target, options?)` accepts:
246
+
247
+ - `activeClassName?: string` (default: `is-drag-over`)
248
+ - `onDrop?: ({ file, track, tracks, kind }) => void`
249
+ - `kind` is `"audio"` or `"playlist"`
250
+ - `tracks` contains all resolved tracks (single/multiple audio files or parsed M3U/M3U8)
251
+
252
+ Notes:
253
+
254
+ - Dropping audio files builds a playlist from dropped files and starts playback from the first track.
255
+ - Dropping an `.m3u`/`.m3u8` parses entries and starts playback from the first valid track.
256
+ - If the drop includes both playlist + local audio files, local files are used when playlist entries match file names.
257
+
258
+ ### Error notices (including CORS)
259
+
260
+ The standard UI listens for player `error` events and shows a temporary notice banner.
261
+ For cross-origin sources that fail with `MEDIA_ERR_SRC_NOT_SUPPORTED`, the message indicates a likely CORS issue.
262
+
174
263
  ## Utility Methods
175
264
 
176
265
  - `setAccentColor(color)`
177
266
  - `getAccentColor()`
178
267
  - `applyAccentColor(targetElement)`
268
+ - `setBackgroundColor(color)`
269
+ - `getBackgroundColor()`
270
+ - `applyBackgroundColor(targetElement)`
271
+ - `applyTheme(targetElement)`
179
272
  - `formatTime(seconds)`
180
273
  - `getState()`
181
274
  - `getAudioElement()`
182
275
 
183
276
  ## Scripts
184
277
 
278
+ - `npm run i18n:sync`
185
279
  - `npm run dev`
186
280
  - `npm run build`
187
281
  - `npm run build:css`
188
282
  - `npm run typecheck`
189
283
  - `npm run demo`
190
284
 
285
+ `i18n:sync` generates `src/i18n/en.generated.ts` from `src/i18n/en.json`.
286
+ This keeps runtime modules JavaScript-only while preserving JSON as the source of UI strings.
287
+
191
288
  ## Local Demo
192
289
 
193
290
  ```bash
@@ -198,13 +295,20 @@ npm run demo
198
295
  Open:
199
296
 
200
297
  - `http://127.0.0.1:4173/examples/demo.html`
298
+ - `http://127.0.0.1:4173/examples/demo.html?debug=1`
299
+
300
+ ## Roadmap
301
+
302
+ - Split reusable UI pieces into dedicated component packages
303
+ - Keep `SmoothPlayer` core focused on engine + typed API
304
+ - Provide clearer package boundaries for framework-specific integrations
201
305
 
202
306
  ## Media Attribution and CORS
203
307
 
204
308
  Audio tracks included in this repository are provided for demonstration purposes only.
205
309
 
206
310
  - Part of the demo media is sourced from [Pixabay](https://pixabay.com/).
207
- - Additional demo files are SoundHelix songs available in `examples/audio`.
311
+ - Additional demo files are [SoundHelix songs](https://soundcloud.com/soundhelix) available in `examples/audio`.
208
312
 
209
313
  If you load audio from external hosts, those sources must be CORS-enabled for browser playback and analysis features.
210
314
  The media server should return a valid `Access-Control-Allow-Origin` header for your application origin (or `*` when appropriate).
@@ -0,0 +1,28 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220 220" fill="none" role="img" aria-labelledby="title desc">
2
+ <title id="title">Smooth Player Radial EQ Logo Preview</title>
3
+ <desc id="desc">Simple stylized radial equalizer icon.</desc>
4
+
5
+ <g transform="translate(110 110)" fill="currentColor">
6
+ <rect x="-1.8" y="-66" width="3.6" height="14" rx="1.8" transform="rotate(0)" />
7
+ <rect x="-1.8" y="-67" width="3.6" height="15" rx="1.8" transform="rotate(20)" />
8
+ <rect x="-1.8" y="-70" width="3.6" height="18" rx="1.8" transform="rotate(40)" />
9
+ <rect x="-1.8" y="-74" width="3.6" height="22" rx="1.8" transform="rotate(60)" />
10
+ <rect x="-1.8" y="-79" width="3.6" height="27" rx="1.8" transform="rotate(80)" />
11
+ <rect x="-1.8" y="-84" width="3.6" height="32" rx="1.8" transform="rotate(100)" />
12
+ <rect x="-1.8" y="-78" width="3.6" height="26" rx="1.8" transform="rotate(120)" />
13
+ <rect x="-1.8" y="-73" width="3.6" height="21" rx="1.8" transform="rotate(140)" />
14
+ <rect x="-1.8" y="-69" width="3.6" height="17" rx="1.8" transform="rotate(160)" />
15
+ <rect x="-1.8" y="-66" width="3.6" height="14" rx="1.8" transform="rotate(180)" />
16
+ <rect x="-1.8" y="-69" width="3.6" height="17" rx="1.8" transform="rotate(200)" />
17
+ <rect x="-1.8" y="-73" width="3.6" height="21" rx="1.8" transform="rotate(220)" />
18
+ <rect x="-1.8" y="-78" width="3.6" height="26" rx="1.8" transform="rotate(240)" />
19
+ <rect x="-1.8" y="-84" width="3.6" height="32" rx="1.8" transform="rotate(260)" />
20
+ <rect x="-1.8" y="-79" width="3.6" height="27" rx="1.8" transform="rotate(280)" />
21
+ <rect x="-1.8" y="-74" width="3.6" height="22" rx="1.8" transform="rotate(300)" />
22
+ <rect x="-1.8" y="-70" width="3.6" height="18" rx="1.8" transform="rotate(320)" />
23
+ <rect x="-1.8" y="-67" width="3.6" height="15" rx="1.8" transform="rotate(340)" />
24
+ </g>
25
+
26
+ <circle cx="110" cy="110" r="42" stroke="currentColor" stroke-width="6" />
27
+ <circle cx="110" cy="110" r="3.5" fill="currentColor" />
28
+ </svg>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 719.94 512.71">
3
+ <g id="Layer_1-2" data-name="Layer 1">
4
+ <path d="M413.16,490.25c-57.26-21.12-103.19-70.69-126.86-127.41-11.89-27.4-19.05-56.99-25.69-85.52-2.08-8.77-4.14-17.52-6.59-26.22-3.13-10.29-6.52-24.33-15.95-29.79-13.13-6.64-28.03,10.64-37.54,18.38-17.69,15.88-36.21,31.98-57.01,44.46-13.51,8.56-33.41,15.43-35.37,33.35-.57,5,.27,10.58,1.95,16.42,40.81,126.49,190.52,206.15,322.7,170.08,9.61-5.13-13.86-11.13-19.44-13.68l-.19-.07Z"/>
5
+ <path d="M365.89,57.43c40.66,33.34,67.71,80.89,81.89,131.02,6.63,22.73,11.15,46.69,17.35,69.53,2.09,7.53,4.48,15.8,7.46,22.22,11.54,25.37,30.17,8.02,44.78-5.19,17.9-16.25,36.18-32.23,56.89-44.91,14.97-9.95,34.46-15.14,37.51-33.81.75-5.1.02-10.83-1.64-16.85C571.45,57.39,436.21-17.82,306.42,3.64c-5.43,1.02-28.88,4.41-20.84,10.41,5.26,3.54,13.28,5.63,19.56,8.12,21.83,8.01,42.56,20.25,60.59,35.12l.16.13Z"/>
6
+ <path d="M0,256.33c.16-2.17,2.42-3.29,4.42-4.67,4.31-2.51,9.11-4.6,13.93-6.71,24.87-10.21,57.01-25.84,77.89-49.13,44.3-48.31,66.45-112.27,140.9-121.77,68.74-8.25,124.54,43.84,146.34,105.42,22.37,52.67,19.47,160.73,85.38,177.19,48.3,8.84,82.28-45.34,117.64-71.14,16.82-13.13,35.7-23,56.52-28.47,19.86-5.57,52.31-8.41,69.25-4.55,11.81,2.68,9.57,6.94-3.92,12.92-5.19,2.41-11.94,5.22-19.53,8.62-21.23,9.29-43.05,21.6-59.76,38.27-32.53,31.3-51.43,75.95-88.23,103.12-32.42,24.86-77.3,31.49-115.31,16.15-35.32-13.64-62.84-43.07-79.55-76.74-19.72-38.62-24.46-83.52-35.94-124.85-5.57-19.35-13.14-38.79-26.57-54.02-26.91-30.22-60.79-24.68-89.72-1.85-28.45,22.05-53.78,54.65-87.58,70.86-20.88,10.66-45.96,16.01-68.93,17.21-10.29.51-35.49.73-37.21-5.8v-.07h-.02Z"/>
7
+ </g>
8
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
2
+ <rect x="6" y="6" width="12" height="12" rx="2" fill="currentColor"/>
3
+ </svg>
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
2
+ <path d="M12 16V6" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M8.5 9.5L12 6l3.5 3.5" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"/>
4
+ <path d="M6 14.5v2.1c0 1 .8 1.9 1.9 1.9h8.2c1 0 1.9-.8 1.9-1.9v-2.1" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"/>
5
+ </svg>
@@ -0,0 +1,7 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
2
+ <path d="M4 14V10" stroke="currentColor" stroke-width="1.9" stroke-linecap="round"/>
3
+ <path d="M8 17V7" stroke="currentColor" stroke-width="1.9" stroke-linecap="round"/>
4
+ <path d="M12 15V9" stroke="currentColor" stroke-width="1.9" stroke-linecap="round"/>
5
+ <path d="M16 19V5" stroke="currentColor" stroke-width="1.9" stroke-linecap="round"/>
6
+ <path d="M20 13V11" stroke="currentColor" stroke-width="1.9" stroke-linecap="round"/>
7
+ </svg>
@@ -1,4 +1,4 @@
1
- import { type AnalyzerOptions, type AudioTrack, type DebugPanelMountOptions, type PlaylistEntry, type PlaylistMountOptions, type PlaylistPanelController, type PlaylistPanelMountOptions, type PlaylistSwitcherMountOptions, type PlaylistTitleMountOptions, type PlayButtonMountOptions, type ProgressMountOptions, type PlaybackState, type PlayerEvents, type SmoothPlayerOptions, type ShuffleToggleMountOptions, type TrackInfoMountOptions, type TransportControlsMountOptions, type VisualizerMode } from "./types.js";
1
+ import { type AnalyzerOptions, type AudioTrack, type DebugPanelMountOptions, type AudioDropMountOptions, type PlaylistEntry, type PlaylistMountOptions, type PlaylistPanelController, type PlaylistPanelMountOptions, type PlaylistSwitcherMountOptions, type PlaylistTitleMountOptions, type PlayButtonMountOptions, type ProgressMountOptions, type PlaybackState, type PlayerEvents, type SmoothPlayerOptions, type SpectrumStyleOptions, type ShuffleToggleMountOptions, type TrackInfoMountOptions, type TransportControlsMountOptions, type VisualizerMode, type WaveformStyleOptions } from "./types.js";
2
2
  export declare class SmoothPlayer {
3
3
  private readonly audio;
4
4
  private readonly context;
@@ -9,7 +9,10 @@ export declare class SmoothPlayer {
9
9
  private activePlaylistId;
10
10
  private currentTrackIndex;
11
11
  private visualizerMode;
12
+ private spectrumStyle;
13
+ private waveformStyle;
12
14
  private accentColor;
15
+ private backgroundColor;
13
16
  private shuffleEnabled;
14
17
  private debugEnabled;
15
18
  private durationFallbackEnabled;
@@ -23,6 +26,10 @@ export declare class SmoothPlayer {
23
26
  setAccentColor(color: string): void;
24
27
  getAccentColor(): string;
25
28
  applyAccentColor(target: HTMLElement): void;
29
+ setBackgroundColor(color: string): void;
30
+ getBackgroundColor(): string;
31
+ applyBackgroundColor(target: HTMLElement): void;
32
+ applyTheme(target: HTMLElement): void;
26
33
  setShuffle(enabled: boolean): void;
27
34
  getShuffle(): boolean;
28
35
  setDebug(enabled: boolean): void;
@@ -53,6 +60,7 @@ export declare class SmoothPlayer {
53
60
  mountPlaylistPanel(options: PlaylistPanelMountOptions): PlaylistPanelController;
54
61
  mountDebugPanel(options: DebugPanelMountOptions): () => void;
55
62
  mountProgress(options: ProgressMountOptions): () => void;
63
+ mountAudioDrop(target: HTMLElement, options?: AudioDropMountOptions): () => void;
56
64
  play(index?: number): Promise<void>;
57
65
  pause(): void;
58
66
  toggle(): Promise<void>;
@@ -70,12 +78,19 @@ export declare class SmoothPlayer {
70
78
  getWaveformData(): Uint8Array;
71
79
  setVisualizer(mode: VisualizerMode): void;
72
80
  getVisualizer(): VisualizerMode;
81
+ setSpectrumStyle(options: Partial<SpectrumStyleOptions>): void;
82
+ getSpectrumStyle(): SpectrumStyleOptions;
83
+ setWaveformStyle(options: Partial<WaveformStyleOptions>): void;
84
+ getWaveformStyle(): WaveformStyleOptions;
73
85
  configureAnalyzer(options?: AnalyzerOptions): void;
86
+ private buildSurfaceGradient;
87
+ private buildPanelGradient;
74
88
  private getActivePlaylist;
75
89
  private getActiveTracks;
76
90
  private loadTrackByIndex;
77
91
  private emitPlaylistChange;
78
92
  private bindAudioEvents;
93
+ private isCrossOriginSource;
79
94
  private clamp;
80
95
  private pickRandomTrackIndex;
81
96
  private emitTimeUpdate;