@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 +126 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/package.json +5 -5
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
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scarlett-player/playlist",
|
|
3
|
-
"version": "1.8.
|
|
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.
|
|
26
|
-
"@scarlett-player/ui": "^1.
|
|
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.
|
|
34
|
-
"@scarlett-player/ui": "1.8.
|
|
33
|
+
"@scarlett-player/core": "1.8.1",
|
|
34
|
+
"@scarlett-player/ui": "1.8.1"
|
|
35
35
|
},
|
|
36
36
|
"keywords": [
|
|
37
37
|
"video",
|