@scarlett-player/playlist 1.8.0 → 1.8.1

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 ADDED
@@ -0,0 +1,126 @@
1
+ # @scarlett-player/playlist
2
+
3
+ Playlist plugin for [Scarlett Player](https://scarlettplayer.com). Queue management (add, insert, remove, move, clear), shuffle with a Fisher-Yates order, repeat modes, auto-advance when a track ends, optional localStorage persistence, and control-bar buttons for previous, next and a queue panel when `@scarlett-player/ui` is installed.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @scarlett-player/playlist @scarlett-player/core
9
+ ```
10
+
11
+ `@scarlett-player/core` is a required peer dependency. `@scarlett-player/ui` is an optional peer: the control-bar controls register only when it is present, and everything else works without it.
12
+
13
+ ## Usage
14
+
15
+ This matches the audio example in the root README.
16
+
17
+ ```ts
18
+ import { createPlayer } from '@scarlett-player/core';
19
+ import { createNativePlugin } from '@scarlett-player/native';
20
+ import { createAudioUIPlugin } from '@scarlett-player/audio-ui';
21
+ import { createPlaylistPlugin, type IPlaylistPlugin } from '@scarlett-player/playlist';
22
+ import { createMediaSessionPlugin } from '@scarlett-player/media-session';
23
+
24
+ const player = await createPlayer({
25
+ container: document.getElementById('audio-player'),
26
+ plugins: [
27
+ createNativePlugin(),
28
+ createAudioUIPlugin({ layout: 'full' }),
29
+ createPlaylistPlugin({
30
+ tracks: [
31
+ { id: '1', src: '/track1.mp3', title: 'Track 1', artist: 'Artist', artwork: '/art1.jpg' },
32
+ { id: '2', src: '/track2.mp3', title: 'Track 2', artist: 'Artist', artwork: '/art2.jpg' },
33
+ ],
34
+ }),
35
+ createMediaSessionPlugin(),
36
+ ],
37
+ });
38
+
39
+ // Start the first track through the playlist, not through `src` or
40
+ // player.load(). The playlist owns the current index: play() sets it, writes
41
+ // the track's title and artwork into state, and emits `media:load-request`,
42
+ // which the player loads and plays. Loading a source behind the playlist's
43
+ // back leaves its index pointing at nothing, so next/previous and
44
+ // auto-advance start from the wrong place.
45
+ const playlist = player.getPlugin<IPlaylistPlugin>('playlist');
46
+ playlist?.play(0);
47
+ ```
48
+
49
+ If you do pass `src` to `createPlayer()` for the first track, set `initialIndex: 0` so the playlist knows which track is already loaded.
50
+
51
+ ## Configuration
52
+
53
+ | Option | Type | Default | Description |
54
+ |---|---|---|---|
55
+ | `tracks` | `PlaylistTrack[]` | `[]` | Initial queue. Tracks without an `id` get a generated one |
56
+ | `autoAdvance` | `boolean` | `true` | Select the next track on `playback:ended` |
57
+ | `advanceDelay` | `number` | `0` | Milliseconds to wait before auto-advancing |
58
+ | `autoLoad` | `boolean` | `true` | Emit `media:load-request` with `autoplay: true` whenever the current track changes, so the player loads it. Set to `false` to load from `playlist:change` yourself |
59
+ | `shuffle` | `boolean` | `false` | Initial shuffle state |
60
+ | `repeat` | `'none' \| 'one' \| 'all'` | `'none'` | Initial repeat mode |
61
+ | `initialIndex` | `number` | `-1` | Index of the track already loaded by the player. `-1` means no track is active. Out-of-range values fall back to `-1` |
62
+ | `persist` | `boolean` | `false` | Save tracks, index, shuffle and repeat to localStorage and restore them on init |
63
+ | `persistKey` | `string` | `'scarlett-playlist'` | localStorage key used by `persist` |
64
+ | `preloadNext` | `boolean` | `true` | Accepted and stored, but no code path currently reads it |
65
+
66
+ A `PlaylistTrack` is `{ id, src, title?, artist?, album?, artwork?, duration?, type?, mimeType?, metadata? }` plus any extra properties you want to carry. `type` defaults to `'audio'` in player state when omitted.
67
+
68
+ ## Plugin API
69
+
70
+ Registered under the id `playlist`; retrieve it with `player.getPlugin<IPlaylistPlugin>('playlist')`.
71
+
72
+ | Method | Description |
73
+ |---|---|
74
+ | `add(track \| track[])` | Append to the queue |
75
+ | `insert(index, track)` | Insert at a position (clamped to the queue length) |
76
+ | `remove(idOrIndex)` | Remove by id or index. Removing the current track selects the track that slides into its slot |
77
+ | `move(fromIndex, toIndex)` | Reorder |
78
+ | `clear()` | Empty the queue and reset the index to `-1` |
79
+ | `play(idOrIndex?)` | Select a track. With no argument, resumes the current track or starts the first |
80
+ | `next()` / `previous()` | Step through the queue honouring shuffle and repeat. `previous()` restarts the current track when more than 3 seconds in |
81
+ | `toggleShuffle()` / `setShuffle(enabled)` | Shuffle control. The current track stays first in the new order |
82
+ | `cycleRepeat()` / `setRepeat(mode)` | Repeat control. `cycleRepeat()` goes none, all, one |
83
+ | `getState()` | `{ tracks, currentIndex, currentTrack, shuffle, repeat, shuffleOrder, hasNext, hasPrevious }` |
84
+ | `getTracks()` / `getCurrentTrack()` / `getTrack(id)` | Read the queue |
85
+
86
+ With the player focused, the `N` and `P` keys call `next()` and `previous()`. Keystrokes inside inputs or with a modifier held are left alone.
87
+
88
+ ## Events
89
+
90
+ All payload types live in `PlayerEventMap` in `@scarlett-player/core`.
91
+
92
+ | Event | Payload | When |
93
+ |---|---|---|
94
+ | `playlist:change` | `{ track, index }` | The current track changed. `track` is `null` after `clear()` |
95
+ | `playlist:add` | `{ track, index }` | `add()` (once per track) or `insert()` |
96
+ | `playlist:remove` | `{ track, index }` | `remove()` |
97
+ | `playlist:reorder` | `{ tracks }` | `move()` |
98
+ | `playlist:clear` | `void` | `clear()` |
99
+ | `playlist:shuffle` | `{ enabled }` | Shuffle changed |
100
+ | `playlist:repeat` | `{ mode }` | Repeat mode changed |
101
+ | `playlist:ended` | `void` | The last track ended with nothing to advance to |
102
+
103
+ On every track change the plugin also writes `title`, `poster` (from `artwork`) and `mediaType` into player state, clearing title and poster when the track has none so a previous track's values never leak.
104
+
105
+ ## Control-bar controls
106
+
107
+ When `@scarlett-player/ui` is installed, three controls are registered. Nothing is placed until you list them in the UI layout:
108
+
109
+ ```ts
110
+ uiPlugin({ controls: ['playlist-previous', 'play', 'playlist-next', 'progress', 'spacer', 'playlist', 'fullscreen'] })
111
+ ```
112
+
113
+ | Control id | Renders |
114
+ |---|---|
115
+ | `playlist-previous` / `playlist-next` | Skip buttons, disabled at the ends of the queue |
116
+ | `playlist` | A button that opens the queue as a list |
117
+
118
+ All three hide themselves when the queue has one track or fewer. The classes `PlaylistSkipButton`, `PlaylistPanel` and the `PLAYLIST_ICONS` map are exported for custom layouts.
119
+
120
+ ## Styling
121
+
122
+ A stylesheet is injected once per document (style element id `sp-playlist-styles`). Override these classes to restyle: `sp-playlist-skip` (with `--previous` / `--next` and the `[disabled]` state), `sp-playlist`, `sp-playlist--open`, `sp-playlist__button`, `sp-playlist__panel`, `sp-playlist__item`, `sp-playlist__item--active`, `sp-playlist__position`, `sp-playlist__text`, `sp-playlist__title` and `sp-playlist__artist`. No CSS custom properties are used.
123
+
124
+ ## License
125
+
126
+ MIT
package/dist/index.cjs CHANGED
@@ -309,7 +309,7 @@ function injectStyles() {
309
309
  }
310
310
 
311
311
  // src/version.ts
312
- var PKG_VERSION = true ? "1.7.1" : "0.0.0-dev";
312
+ var PKG_VERSION = true ? "1.8.1" : "0.0.0-dev";
313
313
 
314
314
  // src/index.ts
315
315
  var DEFAULT_CONFIG = {
package/dist/index.js CHANGED
@@ -269,7 +269,7 @@ function injectStyles() {
269
269
  }
270
270
 
271
271
  // src/version.ts
272
- var PKG_VERSION = true ? "1.7.1" : "0.0.0-dev";
272
+ var PKG_VERSION = true ? "1.8.1" : "0.0.0-dev";
273
273
 
274
274
  // src/index.ts
275
275
  var DEFAULT_CONFIG = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scarlett-player/playlist",
3
- "version": "1.8.0",
3
+ "version": "1.8.1",
4
4
  "description": "Playlist Plugin for Scarlett Player - Queue management, shuffle, repeat, and gapless playback",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -22,16 +22,16 @@
22
22
  "dist"
23
23
  ],
24
24
  "peerDependencies": {
25
- "@scarlett-player/core": "^1.7.0",
26
- "@scarlett-player/ui": "^1.7.0"
25
+ "@scarlett-player/core": "^1.8.0",
26
+ "@scarlett-player/ui": "^1.8.0"
27
27
  },
28
28
  "devDependencies": {
29
29
  "tsup": "^8.0.0",
30
30
  "typescript": "^5.3.0",
31
31
  "vitest": "^1.6.0",
32
32
  "jsdom": "^24.0.0",
33
- "@scarlett-player/core": "1.8.0",
34
- "@scarlett-player/ui": "1.8.0"
33
+ "@scarlett-player/core": "1.8.1",
34
+ "@scarlett-player/ui": "1.8.1"
35
35
  },
36
36
  "keywords": [
37
37
  "video",