@yetric/audioplayer 1.1.0 → 1.1.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 CHANGED
@@ -6,7 +6,7 @@ Headless TypeScript audio player for modern browsers.
6
6
 
7
7
  ## Status
8
8
 
9
- `1.1.0` is the current stable release.
9
+ `1.1.1` is the current stable release.
10
10
 
11
11
  Recommended current focus:
12
12
 
@@ -119,6 +119,13 @@ The public API should stay small. Extra behavior like persistence, progress ping
119
119
 
120
120
  - [`docs/ROADMAP.md`](./docs/ROADMAP.md)
121
121
  - [`docs/V1_ISSUES.md`](./docs/V1_ISSUES.md)
122
+ - [`docs/RELEASE_1_2.md`](./docs/RELEASE_1_2.md)
123
+ - [`docs/RELEASE_1_3.md`](./docs/RELEASE_1_3.md)
124
+ - [`docs/RELEASE_1_4.md`](./docs/RELEASE_1_4.md)
125
+ - [`docs/RELEASE_2_0.md`](./docs/RELEASE_2_0.md)
126
+ - [`docs/DESIGN_CUES.md`](./docs/DESIGN_CUES.md)
127
+ - [`docs/DOCS_PLAN.md`](./docs/DOCS_PLAN.md)
128
+ - [`docs/OPEN_SOURCE.md`](./docs/OPEN_SOURCE.md)
122
129
  - [`docs/API_CONTRACT.md`](./docs/API_CONTRACT.md)
123
130
  - [`docs/GETTING_STARTED.md`](./docs/GETTING_STARTED.md)
124
131
  - [`docs/CORE_GUIDE.md`](./docs/CORE_GUIDE.md)
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- type PlayerStatus = "idle" | "loading" | "ready" | "playing" | "paused" | "ended" | "error";
2
- type RepeatMode = "off" | "one" | "all";
3
- type AudioPlayerEventName = "sourcechange" | "queuechange" | "play" | "pause" | "seeking" | "seeked" | "timeupdate" | "ratechange" | "volumechange" | "ended" | "error" | "destroy";
4
- interface AudioSource {
1
+ export type PlayerStatus = "idle" | "loading" | "ready" | "playing" | "paused" | "ended" | "error";
2
+ export type RepeatMode = "off" | "one" | "all";
3
+ export type AudioPlayerEventName = "sourcechange" | "queuechange" | "play" | "pause" | "seeking" | "seeked" | "timeupdate" | "ratechange" | "volumechange" | "ended" | "error" | "destroy";
4
+ export interface AudioSource {
5
5
  id: string;
6
6
  src: string;
7
7
  title?: string;
@@ -10,21 +10,21 @@ interface AudioSource {
10
10
  album?: string;
11
11
  durationHint?: number;
12
12
  }
13
- interface AudioPlayerErrorState {
13
+ export interface AudioPlayerErrorState {
14
14
  code: "LOAD_ERROR" | "PLAY_ERROR" | "SEEK_ERROR" | "INVALID_VOLUME" | "INVALID_RATE" | "INVALID_QUEUE_ITEM" | "NO_ACTIVE_SOURCE" | "UNSUPPORTED_ENVIRONMENT" | "UNKNOWN_ERROR";
15
15
  message: string;
16
16
  }
17
- interface AudioPlayerRuntimeError extends AudioPlayerErrorState {
17
+ export interface AudioPlayerRuntimeError extends AudioPlayerErrorState {
18
18
  cause?: unknown;
19
19
  }
20
- interface AudioPlayerProgressState {
20
+ export interface AudioPlayerProgressState {
21
21
  currentTime: number;
22
22
  duration: number;
23
23
  buffered: number;
24
24
  playedFraction: number;
25
25
  bufferedFraction: number;
26
26
  }
27
- interface AudioPlayerQueuePositionState {
27
+ export interface AudioPlayerQueuePositionState {
28
28
  currentIndex: number;
29
29
  currentSourceId: string | null;
30
30
  length: number;
@@ -32,12 +32,12 @@ interface AudioPlayerQueuePositionState {
32
32
  hasPrevious: boolean;
33
33
  repeatMode: RepeatMode;
34
34
  }
35
- interface AudioPlayerQueueState {
35
+ export interface AudioPlayerQueueState {
36
36
  items: AudioSource[];
37
37
  itemIds: string[];
38
38
  position: AudioPlayerQueuePositionState;
39
39
  }
40
- interface AudioPlayerState {
40
+ export interface AudioPlayerState {
41
41
  status: PlayerStatus;
42
42
  currentSource: AudioSource | null;
43
43
  currentSourceId: string | null;
@@ -51,7 +51,7 @@ interface AudioPlayerState {
51
51
  queue: AudioPlayerQueueState;
52
52
  error: AudioPlayerErrorState | null;
53
53
  }
54
- interface AudioPlayerEventMap {
54
+ export interface AudioPlayerEventMap {
55
55
  sourcechange: {
56
56
  source: AudioSource | null;
57
57
  state: AudioPlayerState;
@@ -103,20 +103,20 @@ interface AudioPlayerEventMap {
103
103
  state: AudioPlayerState;
104
104
  };
105
105
  }
106
- interface CreateAudioPlayerOptions {
106
+ export interface CreateAudioPlayerOptions {
107
107
  initialVolume?: number;
108
108
  initialRate?: number;
109
109
  initialMuted?: boolean;
110
110
  initialRepeatMode?: RepeatMode;
111
111
  }
112
- interface SetQueueOptions {
112
+ export interface SetQueueOptions {
113
113
  startAtId?: string;
114
114
  autoplay?: boolean;
115
115
  }
116
- interface PreviousOptions {
116
+ export interface PreviousOptions {
117
117
  restartThresholdSeconds?: number;
118
118
  }
119
- interface PersistedPlayerState {
119
+ export interface PersistedPlayerState {
120
120
  version: 1;
121
121
  savedAt: number;
122
122
  currentSource: AudioSource | null;
@@ -134,24 +134,24 @@ interface PersistedPlayerState {
134
134
  };
135
135
  };
136
136
  }
137
- interface AudioPlayerPersistenceAdapter {
137
+ export interface AudioPlayerPersistenceAdapter {
138
138
  load(): PersistedPlayerState | null;
139
139
  save(state: PersistedPlayerState): void;
140
140
  clear(): void;
141
141
  }
142
- interface AudioPlayerPersistenceOptions {
142
+ export interface AudioPlayerPersistenceOptions {
143
143
  adapter: AudioPlayerPersistenceAdapter;
144
144
  autoRestore?: boolean;
145
145
  maxAgeMs?: number;
146
146
  saveIntervalMs?: number;
147
147
  }
148
- interface AudioPlayerPersistenceController {
148
+ export interface AudioPlayerPersistenceController {
149
149
  restore(): Promise<PersistedPlayerState | null>;
150
150
  save(): void;
151
151
  clear(): void;
152
152
  destroy(): void;
153
153
  }
154
- interface AudioPlayerProgressSnapshot {
154
+ export interface AudioPlayerProgressSnapshot {
155
155
  source: AudioSource | null;
156
156
  sourceId: string | null;
157
157
  currentTime: number;
@@ -160,24 +160,24 @@ interface AudioPlayerProgressSnapshot {
160
160
  status: PlayerStatus;
161
161
  queue: AudioPlayerQueuePositionState;
162
162
  }
163
- interface AudioPlayerProgressReporterOptions {
163
+ export interface AudioPlayerProgressReporterOptions {
164
164
  intervalMs?: number;
165
165
  onReport: (snapshot: AudioPlayerProgressSnapshot) => void;
166
166
  }
167
- interface AudioPlayerProgressReporterController {
167
+ export interface AudioPlayerProgressReporterController {
168
168
  report(): void;
169
169
  destroy(): void;
170
170
  }
171
- interface AudioPlayerMediaSessionOptions {
171
+ export interface AudioPlayerMediaSessionOptions {
172
172
  artworkSizes?: number[];
173
173
  seekOffsetSeconds?: number;
174
174
  }
175
- interface AudioPlayerMediaSessionController {
175
+ export interface AudioPlayerMediaSessionController {
176
176
  sync(): void;
177
177
  destroy(): void;
178
178
  }
179
- type AudioPlayerEventListener<K extends AudioPlayerEventName> = (event: AudioPlayerEventMap[K]) => void;
180
- interface AudioPlayer {
179
+ export type AudioPlayerEventListener<K extends AudioPlayerEventName> = (event: AudioPlayerEventMap[K]) => void;
180
+ export interface AudioPlayer {
181
181
  getState(): AudioPlayerState;
182
182
  subscribe(listener: (state: AudioPlayerState) => void): () => void;
183
183
  on<K extends AudioPlayerEventName>(eventName: K, listener: AudioPlayerEventListener<K>): () => void;
@@ -199,10 +199,9 @@ interface AudioPlayer {
199
199
  setRepeatMode(mode: RepeatMode): void;
200
200
  destroy(): void;
201
201
  }
202
- declare const createLocalStoragePersistenceAdapter: (key: string) => AudioPlayerPersistenceAdapter;
203
- declare const createAudioPlayer: (options?: CreateAudioPlayerOptions) => AudioPlayer;
204
- declare const attachAudioPlayerPersistence: (player: AudioPlayer, options: AudioPlayerPersistenceOptions) => AudioPlayerPersistenceController;
205
- declare const attachAudioPlayerProgressReporter: (player: AudioPlayer, options: AudioPlayerProgressReporterOptions) => AudioPlayerProgressReporterController;
206
- declare const attachAudioPlayerMediaSession: (player: AudioPlayer, options?: AudioPlayerMediaSessionOptions) => AudioPlayerMediaSessionController;
207
-
208
- export { type AudioPlayer, type AudioPlayerErrorState, type AudioPlayerEventListener, type AudioPlayerEventMap, type AudioPlayerEventName, type AudioPlayerMediaSessionController, type AudioPlayerMediaSessionOptions, type AudioPlayerPersistenceAdapter, type AudioPlayerPersistenceController, type AudioPlayerPersistenceOptions, type AudioPlayerProgressReporterController, type AudioPlayerProgressReporterOptions, type AudioPlayerProgressSnapshot, type AudioPlayerProgressState, type AudioPlayerQueuePositionState, type AudioPlayerQueueState, type AudioPlayerRuntimeError, type AudioPlayerState, type AudioSource, type CreateAudioPlayerOptions, type PersistedPlayerState, type PlayerStatus, type PreviousOptions, type RepeatMode, type SetQueueOptions, attachAudioPlayerMediaSession, attachAudioPlayerPersistence, attachAudioPlayerProgressReporter, createAudioPlayer, createLocalStoragePersistenceAdapter };
202
+ export declare const createLocalStoragePersistenceAdapter: (key: string) => AudioPlayerPersistenceAdapter;
203
+ export declare const createAudioPlayer: (options?: CreateAudioPlayerOptions) => AudioPlayer;
204
+ export declare const attachAudioPlayerPersistence: (player: AudioPlayer, options: AudioPlayerPersistenceOptions) => AudioPlayerPersistenceController;
205
+ export declare const attachAudioPlayerProgressReporter: (player: AudioPlayer, options: AudioPlayerProgressReporterOptions) => AudioPlayerProgressReporterController;
206
+ export declare const attachAudioPlayerMediaSession: (player: AudioPlayer, options?: AudioPlayerMediaSessionOptions) => AudioPlayerMediaSessionController;
207
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react.tsx","../src/index.ts"],"sourcesContent":["import {\n createContext,\n type ReactNode,\n useContext,\n useEffect,\n useRef,\n useSyncExternalStore,\n} from \"react\";\nimport {\n createAudioPlayer,\n type AudioPlayer,\n type AudioPlayerState,\n type CreateAudioPlayerOptions,\n} from \"./index\";\n\nconst AudioPlayerContext = createContext<AudioPlayer | null>(null);\n\nexport interface AudioPlayerProviderProps {\n children: ReactNode;\n options?: CreateAudioPlayerOptions;\n player?: AudioPlayer;\n}\n\nexport const AudioPlayerProvider = ({\n children,\n options,\n player,\n}: AudioPlayerProviderProps) => {\n const playerRef = useRef<AudioPlayer | null>(player ?? null);\n\n if (playerRef.current === null) {\n playerRef.current = createAudioPlayer(options);\n }\n\n const mountedRef = useRef(false);\n\n useEffect(() => {\n if (player) {\n return undefined;\n }\n\n const owned = playerRef.current;\n mountedRef.current = true;\n\n return () => {\n mountedRef.current = false;\n // StrictMode re-runs effects right after cleanup, so only destroy if not re-mounted\n queueMicrotask(() => {\n if (!mountedRef.current) {\n owned?.destroy();\n }\n });\n };\n }, [player]);\n\n return (\n <AudioPlayerContext.Provider value={playerRef.current}>\n {children}\n </AudioPlayerContext.Provider>\n );\n};\n\nexport const useAudioPlayer = (): AudioPlayer => {\n const player = useContext(AudioPlayerContext);\n\n if (!player) {\n throw new Error(\"useAudioPlayer must be used inside AudioPlayerProvider.\");\n }\n\n return player;\n};\n\nexport const useAudioPlayerState = (): AudioPlayerState => {\n const player = useAudioPlayer();\n\n return useSyncExternalStore(\n player.subscribe,\n player.getState,\n player.getState,\n );\n};\n","export type PlayerStatus =\n | \"idle\"\n | \"loading\"\n | \"ready\"\n | \"playing\"\n | \"paused\"\n | \"ended\"\n | \"error\";\n\nexport type RepeatMode = \"off\" | \"one\" | \"all\";\n\nexport type AudioPlayerEventName =\n | \"sourcechange\"\n | \"queuechange\"\n | \"play\"\n | \"pause\"\n | \"seeking\"\n | \"seeked\"\n | \"timeupdate\"\n | \"ratechange\"\n | \"volumechange\"\n | \"ended\"\n | \"error\"\n | \"destroy\";\n\nexport interface AudioSource {\n id: string;\n src: string;\n title?: string;\n artist?: string;\n artwork?: string;\n album?: string;\n durationHint?: number;\n}\n\nexport interface AudioPlayerErrorState {\n code:\n | \"LOAD_ERROR\"\n | \"PLAY_ERROR\"\n | \"SEEK_ERROR\"\n | \"INVALID_VOLUME\"\n | \"INVALID_RATE\"\n | \"INVALID_QUEUE_ITEM\"\n | \"NO_ACTIVE_SOURCE\"\n | \"UNSUPPORTED_ENVIRONMENT\"\n | \"UNKNOWN_ERROR\";\n message: string;\n}\n\nexport interface AudioPlayerRuntimeError extends AudioPlayerErrorState {\n cause?: unknown;\n}\n\nexport interface AudioPlayerProgressState {\n currentTime: number;\n duration: number;\n buffered: number;\n playedFraction: number;\n bufferedFraction: number;\n}\n\nexport interface AudioPlayerQueuePositionState {\n currentIndex: number;\n currentSourceId: string | null;\n length: number;\n hasNext: boolean;\n hasPrevious: boolean;\n repeatMode: RepeatMode;\n}\n\nexport interface AudioPlayerQueueState {\n items: AudioSource[];\n itemIds: string[];\n position: AudioPlayerQueuePositionState;\n}\n\nexport interface AudioPlayerState {\n status: PlayerStatus;\n currentSource: AudioSource | null;\n currentSourceId: string | null;\n currentTime: number;\n duration: number;\n buffered: number;\n progress: AudioPlayerProgressState;\n rate: number;\n volume: number;\n muted: boolean;\n queue: AudioPlayerQueueState;\n error: AudioPlayerErrorState | null;\n}\n\nexport interface AudioPlayerEventMap {\n sourcechange: { source: AudioSource | null; state: AudioPlayerState };\n queuechange: { queue: AudioPlayerQueueState; state: AudioPlayerState };\n play: { source: AudioSource | null; state: AudioPlayerState };\n pause: { source: AudioSource | null; state: AudioPlayerState };\n seeking: { from: number; to: number; state: AudioPlayerState };\n seeked: { currentTime: number; state: AudioPlayerState };\n timeupdate: { currentTime: number; duration: number; state: AudioPlayerState };\n ratechange: { rate: number; state: AudioPlayerState };\n volumechange: { volume: number; muted: boolean; state: AudioPlayerState };\n ended: { source: AudioSource | null; state: AudioPlayerState };\n error: { error: AudioPlayerRuntimeError; state: AudioPlayerState };\n destroy: { state: AudioPlayerState };\n}\n\nexport interface CreateAudioPlayerOptions {\n initialVolume?: number;\n initialRate?: number;\n initialMuted?: boolean;\n initialRepeatMode?: RepeatMode;\n}\n\nexport interface SetQueueOptions {\n startAtId?: string;\n autoplay?: boolean;\n}\n\nexport interface PreviousOptions {\n restartThresholdSeconds?: number;\n}\n\nexport interface PersistedPlayerState {\n version: 1;\n savedAt: number;\n currentSource: AudioSource | null;\n currentSourceId: string | null;\n currentTime: number;\n rate: number;\n volume: number;\n muted: boolean;\n queue: {\n items: AudioSource[];\n position: {\n currentIndex: number;\n currentSourceId: string | null;\n repeatMode: RepeatMode;\n };\n };\n}\n\nexport interface AudioPlayerPersistenceAdapter {\n load(): PersistedPlayerState | null;\n save(state: PersistedPlayerState): void;\n clear(): void;\n}\n\nexport interface AudioPlayerPersistenceOptions {\n adapter: AudioPlayerPersistenceAdapter;\n autoRestore?: boolean;\n maxAgeMs?: number;\n saveIntervalMs?: number;\n}\n\nexport interface AudioPlayerPersistenceController {\n restore(): Promise<PersistedPlayerState | null>;\n save(): void;\n clear(): void;\n destroy(): void;\n}\n\nexport interface AudioPlayerProgressSnapshot {\n source: AudioSource | null;\n sourceId: string | null;\n currentTime: number;\n duration: number;\n playedFraction: number;\n status: PlayerStatus;\n queue: AudioPlayerQueuePositionState;\n}\n\nexport interface AudioPlayerProgressReporterOptions {\n intervalMs?: number;\n onReport: (snapshot: AudioPlayerProgressSnapshot) => void;\n}\n\nexport interface AudioPlayerProgressReporterController {\n report(): void;\n destroy(): void;\n}\n\nexport interface AudioPlayerMediaSessionOptions {\n artworkSizes?: number[];\n seekOffsetSeconds?: number;\n}\n\nexport interface AudioPlayerMediaSessionController {\n sync(): void;\n destroy(): void;\n}\n\nexport type AudioPlayerEventListener<K extends AudioPlayerEventName> = (\n event: AudioPlayerEventMap[K],\n) => void;\n\nexport interface AudioPlayer {\n getState(): AudioPlayerState;\n subscribe(listener: (state: AudioPlayerState) => void): () => void;\n on<K extends AudioPlayerEventName>(\n eventName: K,\n listener: AudioPlayerEventListener<K>,\n ): () => void;\n\n load(source: AudioSource): Promise<void>;\n play(source?: AudioSource): Promise<void>;\n pause(): void;\n toggle(): Promise<void>;\n unload(): void;\n seek(time: number): void;\n setRate(rate: number): void;\n setVolume(volume: number): void;\n setMuted(muted: boolean): void;\n\n setQueue(items: AudioSource[], options?: SetQueueOptions): Promise<void>;\n appendToQueue(items: AudioSource[]): void;\n removeFromQueue(id: string): void;\n clearQueue(): void;\n next(): Promise<void>;\n previous(options?: PreviousOptions): Promise<void>;\n setRepeatMode(mode: RepeatMode): void;\n\n destroy(): void;\n}\n\nconst DEFAULT_PREVIOUS_RESTART_THRESHOLD_SECONDS = 5;\nconst DEFAULT_PERSISTENCE_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000;\nconst DEFAULT_PERSISTENCE_SAVE_INTERVAL_MS = 5000;\nconst DEFAULT_PROGRESS_REPORT_INTERVAL_MS = 5000;\nconst DEFAULT_MEDIA_SESSION_ARTWORK_SIZES = [96, 128, 192, 256, 384, 512];\nconst DEFAULT_MEDIA_SESSION_SEEK_OFFSET_SECONDS = 10;\n\ntype AudioEngineEventName =\n | \"loadedmetadata\"\n | \"timeupdate\"\n | \"progress\"\n | \"play\"\n | \"pause\"\n | \"seeked\"\n | \"ratechange\"\n | \"volumechange\"\n | \"ended\"\n | \"error\";\n\ninterface AudioEngineSnapshot {\n currentTime: number;\n duration: number;\n buffered: number;\n rate: number;\n volume: number;\n muted: boolean;\n}\n\ninterface AudioEngine {\n readonly available: boolean;\n getSnapshot(durationHint?: number): AudioEngineSnapshot;\n getRuntimeError(): unknown;\n on(eventName: AudioEngineEventName, listener: EventListener): () => void;\n setSource(src: string): void;\n clearSource(): void;\n load(): void;\n play(): Promise<void>;\n pause(): void;\n setCurrentTime(time: number): void;\n setPlaybackRate(rate: number): void;\n setVolume(volume: number): void;\n setMuted(muted: boolean): void;\n destroy(): void;\n}\n\nconst createInitialState = (\n options: CreateAudioPlayerOptions,\n): AudioPlayerState => ({\n status: \"idle\",\n currentSource: null,\n currentSourceId: null,\n currentTime: 0,\n duration: 0,\n buffered: 0,\n progress: {\n currentTime: 0,\n duration: 0,\n buffered: 0,\n playedFraction: 0,\n bufferedFraction: 0,\n },\n rate: options.initialRate ?? 1,\n volume: options.initialVolume ?? 1,\n muted: options.initialMuted ?? false,\n queue: {\n items: [],\n itemIds: [],\n position: {\n currentIndex: -1,\n currentSourceId: null,\n length: 0,\n repeatMode: options.initialRepeatMode ?? \"off\",\n hasNext: false,\n hasPrevious: false,\n },\n },\n error: null,\n});\n\nconst createPlayerError = (\n code: AudioPlayerRuntimeError[\"code\"],\n message: string,\n cause?: unknown,\n): AudioPlayerRuntimeError => ({\n code,\n message,\n cause,\n});\n\nconst isRuntimePlayerError = (\n value: unknown,\n): value is AudioPlayerRuntimeError =>\n !!value &&\n typeof value === \"object\" &&\n \"code\" in value &&\n \"message\" in value;\n\nconst normalizePlayerError = (\n fallbackCode: AudioPlayerRuntimeError[\"code\"],\n fallbackMessage: string,\n cause: unknown,\n): AudioPlayerRuntimeError =>\n isRuntimePlayerError(cause)\n ? cause\n : createPlayerError(fallbackCode, fallbackMessage, cause);\n\nconst assertStableSource = (source: AudioSource): void => {\n if (!source.id || !source.src) {\n throw createPlayerError(\n \"INVALID_QUEUE_ITEM\",\n \"AudioSource requires stable id and src.\",\n );\n }\n};\n\nconst isFiniteNumber = (value: number): boolean =>\n Number.isFinite(value) && !Number.isNaN(value);\n\nconst safeDuration = (audio: HTMLAudioElement | null, hint?: number): number => {\n const duration = audio?.duration;\n return duration !== undefined && isFiniteNumber(duration) ? duration : hint ?? 0;\n};\n\nconst safeBuffered = (audio: HTMLAudioElement | null): number => {\n if (!audio || audio.buffered.length === 0) {\n return 0;\n }\n\n try {\n return audio.buffered.end(audio.buffered.length - 1);\n } catch {\n return 0;\n }\n};\n\nconst cloneQueueState = (state: AudioPlayerState): AudioPlayerQueueState => ({\n items: [...state.queue.items],\n itemIds: [...state.queue.itemIds],\n position: {\n ...state.queue.position,\n },\n});\n\nconst updateQueueFlags = (state: AudioPlayerState): AudioPlayerState => {\n const { items, position } = state.queue;\n const { currentIndex, repeatMode } = position;\n const lastIndex = items.length - 1;\n const hasItems = items.length > 0;\n const hasNext =\n !hasItems || currentIndex < 0\n ? false\n : repeatMode === \"all\"\n ? items.length > 0\n : currentIndex < lastIndex;\n const hasPrevious =\n !hasItems || currentIndex < 0\n ? false\n : repeatMode === \"all\"\n ? items.length > 0\n : currentIndex > 0;\n\n return {\n ...state,\n queue: {\n ...state.queue,\n itemIds: items.map((item) => item.id),\n position: {\n ...position,\n currentSourceId: state.currentSource?.id ?? null,\n length: items.length,\n hasNext,\n hasPrevious,\n },\n },\n };\n};\n\nconst createAudioElement = (): HTMLAudioElement | null => {\n if (typeof Audio === \"undefined\") {\n return null;\n }\n\n return new Audio();\n};\n\nconst createUnavailableAudioEngine = (): AudioEngine => {\n const unavailableError = (): AudioPlayerRuntimeError =>\n createPlayerError(\n \"UNSUPPORTED_ENVIRONMENT\",\n \"HTMLAudioElement is not available in this environment.\",\n );\n\n return {\n available: false,\n getSnapshot(durationHint) {\n return {\n currentTime: 0,\n duration: durationHint ?? 0,\n buffered: 0,\n rate: 1,\n volume: 1,\n muted: false,\n };\n },\n getRuntimeError() {\n return unavailableError();\n },\n on() {\n return () => {};\n },\n setSource() {\n throw unavailableError();\n },\n clearSource() {\n throw unavailableError();\n },\n load() {\n throw unavailableError();\n },\n play() {\n throw unavailableError();\n },\n pause() {\n throw unavailableError();\n },\n setCurrentTime() {\n throw unavailableError();\n },\n setPlaybackRate() {\n throw unavailableError();\n },\n setVolume() {\n throw unavailableError();\n },\n setMuted() {\n throw unavailableError();\n },\n destroy() {},\n };\n};\n\nconst createHtmlAudioEngine = (): AudioEngine => {\n const audio = createAudioElement();\n if (!audio) {\n return createUnavailableAudioEngine();\n }\n\n audio.preload = \"metadata\";\n const clearSource = (): void => {\n const element = audio as HTMLAudioElement & {\n removeAttribute?: (name: string) => void;\n };\n if (typeof element.removeAttribute === \"function\") {\n element.removeAttribute(\"src\");\n return;\n }\n\n audio.src = \"\";\n };\n\n return {\n available: true,\n getSnapshot(durationHint) {\n return {\n currentTime: audio.currentTime,\n duration: safeDuration(audio, durationHint),\n buffered: safeBuffered(audio),\n rate: audio.playbackRate,\n volume: audio.volume,\n muted: audio.muted,\n };\n },\n getRuntimeError() {\n return audio.error;\n },\n on(eventName, listener) {\n audio.addEventListener(eventName, listener);\n return () => {\n audio.removeEventListener(eventName, listener);\n };\n },\n setSource(src) {\n audio.src = src;\n },\n clearSource() {\n clearSource();\n },\n load() {\n audio.load();\n },\n play() {\n return audio.play();\n },\n pause() {\n audio.pause();\n },\n setCurrentTime(time) {\n audio.currentTime = time;\n },\n setPlaybackRate(rate) {\n audio.playbackRate = rate;\n },\n setVolume(volume) {\n audio.volume = volume;\n },\n setMuted(muted) {\n audio.muted = muted;\n },\n destroy() {\n audio.pause();\n clearSource();\n audio.load();\n },\n };\n};\n\nconst isBrowserStorageAvailable = (): boolean =>\n typeof window !== \"undefined\" && typeof window.localStorage !== \"undefined\";\n\nconst isPersistedPlayerState = (value: unknown): value is PersistedPlayerState => {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const candidate = value as Partial<PersistedPlayerState>;\n return (\n candidate.version === 1 &&\n typeof candidate.savedAt === \"number\" &&\n \"currentSource\" in candidate &&\n typeof candidate.currentTime === \"number\" &&\n typeof candidate.rate === \"number\" &&\n typeof candidate.volume === \"number\" &&\n typeof candidate.muted === \"boolean\" &&\n !!candidate.queue &&\n Array.isArray(candidate.queue.items) &&\n !!candidate.queue.position\n );\n};\n\nconst toPersistedPlayerState = (state: AudioPlayerState): PersistedPlayerState => ({\n version: 1,\n savedAt: Date.now(),\n currentSource: state.currentSource,\n currentSourceId: state.currentSourceId,\n currentTime: state.currentTime,\n rate: state.rate,\n volume: state.volume,\n muted: state.muted,\n queue: {\n items: [...state.queue.items],\n position: {\n currentIndex: state.queue.position.currentIndex,\n currentSourceId: state.queue.position.currentSourceId,\n repeatMode: state.queue.position.repeatMode,\n },\n },\n});\n\nconst toProgressSnapshot = (\n state: AudioPlayerState,\n): AudioPlayerProgressSnapshot => ({\n source: state.currentSource,\n sourceId: state.currentSourceId,\n currentTime: state.currentTime,\n duration: state.duration,\n playedFraction: state.progress.playedFraction,\n status: state.status,\n queue: {\n ...state.queue.position,\n },\n});\n\nconst hasMediaSession = (): boolean =>\n typeof navigator !== \"undefined\" && \"mediaSession\" in navigator;\n\nconst createMediaSessionArtwork = (\n source: AudioSource | null,\n sizes: number[],\n): MediaImage[] => {\n if (!source?.artwork) {\n return [];\n }\n\n return sizes.map((size) => ({\n src: source.artwork as string,\n sizes: `${size}x${size}`,\n }));\n};\n\nexport const createLocalStoragePersistenceAdapter = (\n key: string,\n): AudioPlayerPersistenceAdapter => ({\n load() {\n if (!isBrowserStorageAvailable()) {\n return null;\n }\n\n try {\n const raw = window.localStorage.getItem(key);\n if (!raw) {\n return null;\n }\n\n const parsed: unknown = JSON.parse(raw);\n return isPersistedPlayerState(parsed) ? parsed : null;\n } catch {\n return null;\n }\n },\n\n save(state) {\n if (!isBrowserStorageAvailable()) {\n return;\n }\n\n window.localStorage.setItem(key, JSON.stringify(state));\n },\n\n clear() {\n if (!isBrowserStorageAvailable()) {\n return;\n }\n\n window.localStorage.removeItem(key);\n },\n});\n\nexport const createAudioPlayer = (\n options: CreateAudioPlayerOptions = {},\n): AudioPlayer => {\n let state = updateQueueFlags(createInitialState(options));\n let destroyed = false;\n let shouldAutoplayAfterLoad = false;\n let suppressPauseEvent = false;\n\n const engine = createHtmlAudioEngine();\n const subscribers = new Set<(nextState: AudioPlayerState) => void>();\n const eventListeners: {\n [K in AudioPlayerEventName]: Set<AudioPlayerEventListener<K>>;\n } = {\n sourcechange: new Set(),\n queuechange: new Set(),\n play: new Set(),\n pause: new Set(),\n seeking: new Set(),\n seeked: new Set(),\n timeupdate: new Set(),\n ratechange: new Set(),\n volumechange: new Set(),\n ended: new Set(),\n error: new Set(),\n destroy: new Set(),\n };\n\n const getState = (): AudioPlayerState => state;\n\n const publishState = (): void => {\n for (const subscriber of subscribers) {\n subscriber(state);\n }\n };\n\n const emit = <K extends AudioPlayerEventName>(\n eventName: K,\n event: AudioPlayerEventMap[K],\n ): void => {\n for (const listener of eventListeners[eventName]) {\n listener(event);\n }\n };\n\n const setState = (\n updater: (currentState: AudioPlayerState) => AudioPlayerState,\n ): AudioPlayerState => {\n state = updateQueueFlags(updater(state));\n publishState();\n return state;\n };\n\n const setError = (error: AudioPlayerRuntimeError): void => {\n shouldAutoplayAfterLoad = false;\n const nextState = setState((currentState) => ({\n ...currentState,\n status: \"error\",\n error: {\n code: error.code,\n message: error.message,\n },\n }));\n emit(\"error\", { error, state: nextState });\n };\n\n const clearError = (): void => {\n if (!state.error) {\n return;\n }\n\n setState((currentState) => ({\n ...currentState,\n error: null,\n }));\n };\n\n const syncAudioSnapshot = (\n statusOverride?: PlayerStatus,\n ): AudioPlayerState => {\n const snapshot = engine.getSnapshot(state.currentSource?.durationHint);\n const currentTime = snapshot.currentTime;\n const duration = snapshot.duration;\n const buffered = snapshot.buffered;\n const playedFraction =\n duration > 0 ? Math.min(1, Math.max(0, currentTime / duration)) : 0;\n const bufferedFraction =\n duration > 0 ? Math.min(1, Math.max(0, buffered / duration)) : 0;\n\n const nextState = setState((currentState) => ({\n ...currentState,\n currentSourceId: currentState.currentSource?.id ?? null,\n currentTime,\n duration,\n buffered,\n progress: {\n currentTime,\n duration,\n buffered,\n playedFraction,\n bufferedFraction,\n },\n rate: snapshot.rate,\n volume: snapshot.volume,\n muted: snapshot.muted,\n status: statusOverride ?? currentState.status,\n error:\n statusOverride === \"error\" ? currentState.error : null,\n }));\n\n return nextState;\n };\n\n const findQueueIndexById = (id: string | null | undefined): number =>\n id ? state.queue.items.findIndex((item) => item.id === id) : -1;\n\n const resolveNextQueueIndex = (\n mode: \"manual\" | \"ended\",\n ): number | null => {\n const { items, position } = state.queue;\n const { currentIndex, repeatMode } = position;\n\n if (items.length === 0 || currentIndex < 0) {\n return null;\n }\n\n if (repeatMode === \"one\") {\n return currentIndex;\n }\n\n if (currentIndex < items.length - 1) {\n return currentIndex + 1;\n }\n\n if (repeatMode === \"all\") {\n return 0;\n }\n\n return mode === \"ended\" ? -1 : null;\n };\n\n const resolvePreviousQueueIndex = (): number | null => {\n const { items, position } = state.queue;\n const { currentIndex, repeatMode } = position;\n\n if (items.length === 0 || currentIndex < 0) {\n return null;\n }\n\n if (currentIndex > 0) {\n return currentIndex - 1;\n }\n\n if (repeatMode === \"all\") {\n return items.length - 1;\n }\n\n return null;\n };\n\n const updateCurrentSource = (\n source: AudioSource | null,\n nextStatus: PlayerStatus,\n ): AudioPlayerState => {\n const queueIndex = findQueueIndexById(source?.id);\n const nextState = setState((currentState) => ({\n ...currentState,\n currentSource: source,\n currentSourceId: source?.id ?? null,\n currentTime: 0,\n duration: source?.durationHint ?? 0,\n buffered: 0,\n progress: {\n currentTime: 0,\n duration: source?.durationHint ?? 0,\n buffered: 0,\n playedFraction: 0,\n bufferedFraction: 0,\n },\n status: nextStatus,\n error: null,\n queue: {\n ...currentState.queue,\n position: {\n ...currentState.queue.position,\n currentIndex: queueIndex,\n currentSourceId: source?.id ?? null,\n },\n },\n }));\n emit(\"sourcechange\", { source, state: nextState });\n return nextState;\n };\n\n const setQueueState = (\n items: AudioSource[],\n currentIndex: number,\n ): AudioPlayerState =>\n setState((currentState) => ({\n ...currentState,\n queue: {\n ...currentState.queue,\n items,\n position: {\n ...currentState.queue.position,\n currentIndex,\n },\n },\n }));\n\n const emitQueueChange = (nextState: AudioPlayerState): void => {\n emit(\"queuechange\", { queue: cloneQueueState(nextState), state: nextState });\n };\n\n const attachSourceToAudio = (\n source: AudioSource,\n autoplayAfterLoad: boolean,\n ): void => {\n shouldAutoplayAfterLoad = autoplayAfterLoad;\n suppressPauseEvent = true;\n try {\n engine.pause();\n } finally {\n suppressPauseEvent = false;\n }\n engine.setSource(source.src);\n engine.setCurrentTime(0);\n engine.setPlaybackRate(state.rate);\n engine.setVolume(state.volume);\n engine.setMuted(state.muted);\n engine.load();\n };\n\n const loadInternal = async (\n source: AudioSource,\n autoplayAfterLoad = false,\n ): Promise<void> => {\n assertStableSource(source);\n updateCurrentSource(source, \"loading\");\n attachSourceToAudio(source, autoplayAfterLoad);\n };\n\n const selectQueueIndex = async (\n index: number,\n autoplayAfterLoad = false,\n ): Promise<AudioSource | null> => {\n const item = state.queue.items[index] ?? null;\n if (!item) {\n return null;\n }\n\n await loadInternal(item, autoplayAfterLoad);\n const nextState = setQueueState(state.queue.items, index);\n emitQueueChange(nextState);\n return item;\n };\n\n const playLoadedAudio = async (): Promise<void> => {\n try {\n await engine.play();\n } catch (cause) {\n setError(createPlayerError(\"PLAY_ERROR\", \"Failed to start playback.\", cause));\n }\n };\n\n const handlePlaybackEnded = async (): Promise<void> => {\n if (destroyed) {\n return;\n }\n\n if (state.queue.items.length > 0 && state.queue.position.currentIndex >= 0) {\n const nextIndex = resolveNextQueueIndex(\"ended\");\n if (nextIndex === -1 || nextIndex === null) {\n const endedState = syncAudioSnapshot(\"ended\");\n emit(\"ended\", { source: endedState.currentSource, state: endedState });\n return;\n }\n\n await selectQueueIndex(nextIndex, true);\n return;\n }\n\n const endedState = syncAudioSnapshot(\"ended\");\n emit(\"ended\", { source: endedState.currentSource, state: endedState });\n };\n\n const detachEngineListeners = [\n engine.on(\"loadedmetadata\", () => {\n if (destroyed || !state.currentSource) {\n return;\n }\n\n syncAudioSnapshot(\"ready\");\n if (shouldAutoplayAfterLoad) {\n shouldAutoplayAfterLoad = false;\n void playLoadedAudio();\n }\n }),\n engine.on(\"timeupdate\", () => {\n if (destroyed) {\n return;\n }\n\n const nextState = syncAudioSnapshot(\n state.status === \"ended\" ? \"playing\" : undefined,\n );\n emit(\"timeupdate\", {\n currentTime: nextState.currentTime,\n duration: nextState.duration,\n state: nextState,\n });\n }),\n engine.on(\"progress\", () => {\n if (!destroyed) {\n syncAudioSnapshot();\n }\n }),\n engine.on(\"play\", () => {\n if (destroyed) {\n return;\n }\n\n const nextState = syncAudioSnapshot(\"playing\");\n emit(\"play\", { source: nextState.currentSource, state: nextState });\n }),\n engine.on(\"pause\", () => {\n if (destroyed || suppressPauseEvent) {\n return;\n }\n\n const nextStatus =\n state.status === \"ended\" ? \"ended\" : state.currentSource ? \"paused\" : \"idle\";\n const nextState = syncAudioSnapshot(nextStatus);\n emit(\"pause\", { source: nextState.currentSource, state: nextState });\n }),\n engine.on(\"seeked\", () => {\n if (destroyed) {\n return;\n }\n\n const nextState = syncAudioSnapshot();\n emit(\"seeked\", { currentTime: nextState.currentTime, state: nextState });\n emit(\"timeupdate\", {\n currentTime: nextState.currentTime,\n duration: nextState.duration,\n state: nextState,\n });\n }),\n engine.on(\"ratechange\", () => {\n if (destroyed) {\n return;\n }\n\n const nextState = syncAudioSnapshot();\n emit(\"ratechange\", { rate: nextState.rate, state: nextState });\n }),\n engine.on(\"volumechange\", () => {\n if (destroyed) {\n return;\n }\n\n const nextState = syncAudioSnapshot();\n emit(\"volumechange\", {\n volume: nextState.volume,\n muted: nextState.muted,\n state: nextState,\n });\n }),\n engine.on(\"ended\", () => {\n void handlePlaybackEnded();\n }),\n engine.on(\"error\", () => {\n if (destroyed) {\n return;\n }\n\n setError(\n createPlayerError(\n \"LOAD_ERROR\",\n \"Audio element reported a playback error.\",\n engine.getRuntimeError(),\n ),\n );\n }),\n ];\n\n return {\n getState,\n\n subscribe(listener) {\n subscribers.add(listener);\n listener(state);\n return () => {\n subscribers.delete(listener);\n };\n },\n\n on(eventName, listener) {\n eventListeners[eventName].add(\n listener as AudioPlayerEventListener<typeof eventName>,\n );\n return () => {\n eventListeners[eventName].delete(\n listener as AudioPlayerEventListener<typeof eventName>,\n );\n };\n },\n\n async load(source) {\n try {\n await loadInternal(source);\n } catch (cause) {\n setError(\n normalizePlayerError(\"LOAD_ERROR\", \"Failed to load source.\", cause),\n );\n }\n },\n\n async play(source) {\n try {\n if (source) {\n await loadInternal(source, true);\n return;\n }\n\n if (!state.currentSource) {\n setError(\n createPlayerError(\n \"NO_ACTIVE_SOURCE\",\n \"Cannot play without an active source.\",\n ),\n );\n return;\n }\n\n if (state.status === \"loading\") {\n shouldAutoplayAfterLoad = true;\n return;\n }\n\n await playLoadedAudio();\n } catch (cause) {\n setError(normalizePlayerError(\"PLAY_ERROR\", \"Failed to start playback.\", cause));\n }\n },\n\n pause() {\n if (!engine.available) {\n return;\n }\n\n shouldAutoplayAfterLoad = false;\n engine.pause();\n },\n\n async toggle() {\n if (state.status === \"playing\") {\n this.pause();\n return;\n }\n\n await this.play();\n },\n\n unload() {\n shouldAutoplayAfterLoad = false;\n\n if (engine.available) {\n suppressPauseEvent = true;\n try {\n engine.pause();\n } finally {\n suppressPauseEvent = false;\n }\n engine.clearSource();\n engine.load();\n }\n\n const nextState = updateCurrentSource(null, \"idle\");\n emitQueueChange(nextState);\n },\n\n seek(time) {\n if (!state.currentSource) {\n setError(\n createPlayerError(\"SEEK_ERROR\", \"Cannot seek without an active source.\"),\n );\n return;\n }\n\n const max = engine.getSnapshot(state.currentSource.durationHint).duration;\n const normalized = Math.max(\n 0,\n max > 0 ? Math.min(time, max) : (isFiniteNumber(time) ? time : 0),\n );\n emit(\"seeking\", { from: state.currentTime, to: normalized, state });\n\n try {\n engine.setCurrentTime(normalized);\n } catch (cause) {\n setError(normalizePlayerError(\"SEEK_ERROR\", \"Failed to seek audio.\", cause));\n }\n },\n\n setRate(rate) {\n if (!engine.available) {\n setError(\n createPlayerError(\"INVALID_RATE\", \"Audio element is unavailable.\"),\n );\n return;\n }\n\n if (!isFiniteNumber(rate) || rate <= 0) {\n setError(\n createPlayerError(\"INVALID_RATE\", \"Playback rate must be greater than 0.\"),\n );\n return;\n }\n\n engine.setPlaybackRate(rate);\n clearError();\n syncAudioSnapshot();\n },\n\n setVolume(volume) {\n if (!engine.available) {\n setError(\n createPlayerError(\"INVALID_VOLUME\", \"Audio element is unavailable.\"),\n );\n return;\n }\n\n if (!isFiniteNumber(volume) || volume < 0 || volume > 1) {\n setError(\n createPlayerError(\"INVALID_VOLUME\", \"Volume must be between 0 and 1.\"),\n );\n return;\n }\n\n engine.setVolume(volume);\n clearError();\n syncAudioSnapshot();\n },\n\n setMuted(muted) {\n if (!engine.available) {\n return;\n }\n\n engine.setMuted(muted);\n clearError();\n syncAudioSnapshot();\n },\n\n async setQueue(items, queueOptions = {}) {\n try {\n for (const item of items) {\n assertStableSource(item);\n }\n\n const startIndex =\n queueOptions.startAtId === undefined\n ? items.length > 0\n ? 0\n : -1\n : items.findIndex((item) => item.id === queueOptions.startAtId);\n\n if (queueOptions.startAtId && startIndex < 0) {\n throw createPlayerError(\n \"INVALID_QUEUE_ITEM\",\n `Queue start item ${queueOptions.startAtId} was not found.`,\n );\n }\n\n const nextState = setState((currentState) => ({\n ...currentState,\n currentSource: startIndex >= 0 ? items[startIndex] ?? null : null,\n currentSourceId: startIndex >= 0 ? (items[startIndex]?.id ?? null) : null,\n currentTime: 0,\n duration: startIndex >= 0 ? (items[startIndex]?.durationHint ?? 0) : 0,\n buffered: 0,\n progress: {\n currentTime: 0,\n duration: startIndex >= 0 ? (items[startIndex]?.durationHint ?? 0) : 0,\n buffered: 0,\n playedFraction: 0,\n bufferedFraction: 0,\n },\n status: startIndex >= 0 ? \"ready\" : \"idle\",\n error: null,\n queue: {\n ...currentState.queue,\n items: [...items],\n position: {\n ...currentState.queue.position,\n currentIndex: startIndex,\n currentSourceId:\n startIndex >= 0 ? (items[startIndex]?.id ?? null) : null,\n },\n },\n }));\n\n emitQueueChange(nextState);\n emit(\"sourcechange\", {\n source: nextState.currentSource,\n state: nextState,\n });\n\n if (nextState.currentSource) {\n await loadInternal(nextState.currentSource, queueOptions.autoplay === true);\n const syncedState = setQueueState(items, startIndex);\n emitQueueChange(syncedState);\n } else if (engine.available) {\n shouldAutoplayAfterLoad = false;\n suppressPauseEvent = true;\n engine.pause();\n suppressPauseEvent = false;\n engine.clearSource();\n engine.load();\n }\n } catch (cause) {\n setError(\n normalizePlayerError(\n \"INVALID_QUEUE_ITEM\",\n \"Failed to set queue.\",\n cause,\n ),\n );\n }\n },\n\n appendToQueue(items) {\n try {\n for (const item of items) {\n assertStableSource(item);\n }\n\n const nextState = setState((currentState) => ({\n ...currentState,\n queue: {\n ...currentState.queue,\n items: [...currentState.queue.items, ...items],\n },\n }));\n emitQueueChange(nextState);\n } catch (cause) {\n setError(\n normalizePlayerError(\n \"INVALID_QUEUE_ITEM\",\n \"Failed to append queue items.\",\n cause,\n ),\n );\n }\n },\n\n removeFromQueue(id) {\n const nextItems = state.queue.items.filter((item) => item.id !== id);\n const nextIndex = nextItems.findIndex(\n (item) => item.id === state.currentSource?.id,\n );\n\n const nextState = setState((currentState) => ({\n ...currentState,\n queue: {\n ...currentState.queue,\n items: nextItems,\n position: {\n ...currentState.queue.position,\n currentIndex: nextIndex,\n currentSourceId: nextIndex >= 0 ? (nextItems[nextIndex]?.id ?? null) : null,\n },\n },\n }));\n\n emitQueueChange(nextState);\n },\n\n clearQueue() {\n const nextState = setState((currentState) => ({\n ...currentState,\n queue: {\n ...currentState.queue,\n items: [],\n position: {\n ...currentState.queue.position,\n currentIndex: -1,\n currentSourceId: null,\n },\n },\n }));\n\n emitQueueChange(nextState);\n },\n\n async next() {\n const nextIndex = resolveNextQueueIndex(\"manual\");\n if (nextIndex === null) {\n return;\n }\n\n await selectQueueIndex(nextIndex, true);\n },\n\n async previous(previousOptions = {}) {\n if (!state.currentSource) {\n return;\n }\n\n const threshold =\n previousOptions.restartThresholdSeconds ??\n DEFAULT_PREVIOUS_RESTART_THRESHOLD_SECONDS;\n\n if (state.currentTime > threshold) {\n this.seek(0);\n return;\n }\n\n const previousIndex = resolvePreviousQueueIndex();\n if (previousIndex === null) {\n this.seek(0);\n return;\n }\n\n await selectQueueIndex(previousIndex, true);\n },\n\n setRepeatMode(mode) {\n const nextState = setState((currentState) => ({\n ...currentState,\n queue: {\n ...currentState.queue,\n position: {\n ...currentState.queue.position,\n repeatMode: mode,\n },\n },\n }));\n emitQueueChange(nextState);\n },\n\n destroy() {\n if (destroyed) {\n return;\n }\n\n destroyed = true;\n shouldAutoplayAfterLoad = false;\n for (const detach of detachEngineListeners) {\n detach();\n }\n suppressPauseEvent = true;\n engine.destroy();\n suppressPauseEvent = false;\n\n const nextState = setState(() => createInitialState(options));\n emit(\"destroy\", { state: nextState });\n subscribers.clear();\n for (const eventName of Object.keys(eventListeners) as AudioPlayerEventName[]) {\n eventListeners[eventName].clear();\n }\n },\n };\n};\n\nexport const attachAudioPlayerPersistence = (\n player: AudioPlayer,\n options: AudioPlayerPersistenceOptions,\n): AudioPlayerPersistenceController => {\n const maxAgeMs = options.maxAgeMs ?? DEFAULT_PERSISTENCE_MAX_AGE_MS;\n const saveIntervalMs =\n options.saveIntervalMs ?? DEFAULT_PERSISTENCE_SAVE_INTERVAL_MS;\n let destroyed = false;\n let playIntervalId: ReturnType<typeof setInterval> | null = null;\n\n const save = (): void => {\n if (destroyed) {\n return;\n }\n\n const state = player.getState();\n if (!state.currentSource) {\n options.adapter.clear();\n return;\n }\n\n options.adapter.save(toPersistedPlayerState(state));\n };\n\n const clear = (): void => {\n options.adapter.clear();\n };\n\n const stopPlayInterval = (): void => {\n if (playIntervalId !== null) {\n clearInterval(playIntervalId);\n playIntervalId = null;\n }\n };\n\n const startPlayInterval = (): void => {\n if (playIntervalId !== null) {\n return;\n }\n\n playIntervalId = setInterval(() => {\n save();\n }, saveIntervalMs);\n };\n\n const waitForRestoredSource = (sourceId: string): Promise<void> =>\n new Promise((resolve) => {\n let settled = false;\n let unsubscribe: (() => void) | undefined;\n unsubscribe = player.subscribe((state) => {\n if (settled || state.currentSourceId !== sourceId) {\n return;\n }\n\n if (state.status === \"loading\") {\n return;\n }\n\n settled = true;\n unsubscribe?.();\n resolve();\n });\n\n // subscribe emits synchronously, so the source may already be settled\n if (settled) {\n unsubscribe();\n }\n });\n\n const restore = async (): Promise<PersistedPlayerState | null> => {\n const persisted = options.adapter.load();\n if (!persisted) {\n return null;\n }\n\n if (Date.now() - persisted.savedAt > maxAgeMs) {\n options.adapter.clear();\n return null;\n }\n\n player.setRepeatMode(persisted.queue.position.repeatMode);\n player.setVolume(persisted.volume);\n player.setMuted(persisted.muted);\n player.setRate(persisted.rate);\n\n if (persisted.queue.items.length > 0) {\n await player.setQueue(persisted.queue.items, {\n startAtId: persisted.queue.position.currentSourceId ?? undefined,\n });\n } else if (persisted.currentSource) {\n await player.load(persisted.currentSource);\n } else {\n return persisted;\n }\n\n if (persisted.currentSourceId) {\n await waitForRestoredSource(persisted.currentSourceId);\n }\n\n if (persisted.currentTime > 0) {\n player.seek(persisted.currentTime);\n }\n\n return persisted;\n };\n\n const stateUnsubscribe = player.subscribe((state) => {\n if (state.status === \"playing\") {\n startPlayInterval();\n return;\n }\n\n stopPlayInterval();\n });\n\n const pauseUnsubscribe = player.on(\"pause\", () => {\n save();\n });\n const endedUnsubscribe = player.on(\"ended\", () => {\n save();\n });\n const sourceUnsubscribe = player.on(\"sourcechange\", () => {\n save();\n });\n const queueUnsubscribe = player.on(\"queuechange\", () => {\n save();\n });\n const rateUnsubscribe = player.on(\"ratechange\", () => {\n save();\n });\n const volumeUnsubscribe = player.on(\"volumechange\", () => {\n save();\n });\n\n const beforeUnloadHandler = (): void => {\n save();\n };\n\n const visibilityHandler = (): void => {\n if (typeof document === \"undefined\") {\n return;\n }\n\n if (document.visibilityState === \"hidden\") {\n save();\n }\n };\n\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"beforeunload\", beforeUnloadHandler);\n }\n\n if (typeof document !== \"undefined\") {\n document.addEventListener(\"visibilitychange\", visibilityHandler);\n }\n\n if (options.autoRestore !== false) {\n void restore();\n }\n\n return {\n restore,\n save,\n clear,\n destroy() {\n destroyed = true;\n stopPlayInterval();\n stateUnsubscribe();\n pauseUnsubscribe();\n endedUnsubscribe();\n sourceUnsubscribe();\n queueUnsubscribe();\n rateUnsubscribe();\n volumeUnsubscribe();\n\n if (typeof window !== \"undefined\") {\n window.removeEventListener(\"beforeunload\", beforeUnloadHandler);\n }\n\n if (typeof document !== \"undefined\") {\n document.removeEventListener(\"visibilitychange\", visibilityHandler);\n }\n },\n };\n};\n\nexport const attachAudioPlayerProgressReporter = (\n player: AudioPlayer,\n options: AudioPlayerProgressReporterOptions,\n): AudioPlayerProgressReporterController => {\n const intervalMs = options.intervalMs ?? DEFAULT_PROGRESS_REPORT_INTERVAL_MS;\n let destroyed = false;\n let intervalId: ReturnType<typeof setInterval> | null = null;\n\n const report = (): void => {\n if (destroyed) {\n return;\n }\n\n const state = player.getState();\n if (!state.currentSource) {\n return;\n }\n\n options.onReport(toProgressSnapshot(state));\n };\n\n const stopInterval = (): void => {\n if (intervalId !== null) {\n clearInterval(intervalId);\n intervalId = null;\n }\n };\n\n const startInterval = (): void => {\n if (intervalId !== null) {\n return;\n }\n\n intervalId = setInterval(() => {\n report();\n }, intervalMs);\n };\n\n const stateUnsubscribe = player.subscribe((state) => {\n if (state.status === \"playing\") {\n startInterval();\n return;\n }\n\n stopInterval();\n });\n\n const pauseUnsubscribe = player.on(\"pause\", () => {\n report();\n });\n const endedUnsubscribe = player.on(\"ended\", () => {\n report();\n });\n const seekedUnsubscribe = player.on(\"seeked\", () => {\n report();\n });\n const sourceUnsubscribe = player.on(\"sourcechange\", () => {\n report();\n });\n\n return {\n report,\n destroy() {\n destroyed = true;\n stopInterval();\n stateUnsubscribe();\n pauseUnsubscribe();\n endedUnsubscribe();\n seekedUnsubscribe();\n sourceUnsubscribe();\n },\n };\n};\n\nexport const attachAudioPlayerMediaSession = (\n player: AudioPlayer,\n options: AudioPlayerMediaSessionOptions = {},\n): AudioPlayerMediaSessionController => {\n if (!hasMediaSession()) {\n return {\n sync() {},\n destroy() {},\n };\n }\n\n const artworkSizes =\n options.artworkSizes ?? DEFAULT_MEDIA_SESSION_ARTWORK_SIZES;\n const seekOffsetSeconds =\n options.seekOffsetSeconds ?? DEFAULT_MEDIA_SESSION_SEEK_OFFSET_SECONDS;\n let destroyed = false;\n\n const sync = (): void => {\n if (destroyed || !hasMediaSession()) {\n return;\n }\n\n const state = player.getState();\n const source = state.currentSource;\n\n navigator.mediaSession.metadata = source\n ? new MediaMetadata({\n title: source.title ?? \"\",\n artist: source.artist ?? \"\",\n album: source.album ?? \"\",\n artwork: createMediaSessionArtwork(source, artworkSizes),\n })\n : null;\n\n navigator.mediaSession.playbackState =\n state.status === \"playing\"\n ? \"playing\"\n : state.currentSource\n ? \"paused\"\n : \"none\";\n\n if (state.currentSource && state.duration > 0) {\n try {\n navigator.mediaSession.setPositionState({\n duration: state.duration,\n playbackRate: state.rate,\n position: Math.min(state.currentTime, state.duration),\n });\n } catch {\n // Ignore unsupported position state implementations.\n }\n } else {\n try {\n navigator.mediaSession.setPositionState();\n } catch {\n // Ignore unsupported position state implementations.\n }\n }\n };\n\n const setActionHandler = (\n action: MediaSessionAction,\n handler: MediaSessionActionHandler | null,\n ): void => {\n try {\n navigator.mediaSession.setActionHandler(action, handler);\n } catch {\n // Ignore unsupported actions.\n }\n };\n\n setActionHandler(\"play\", () => {\n void player.play();\n });\n setActionHandler(\"pause\", () => {\n player.pause();\n });\n setActionHandler(\"seekbackward\", () => {\n const state = player.getState();\n player.seek(Math.max(0, state.currentTime - seekOffsetSeconds));\n });\n setActionHandler(\"seekforward\", () => {\n const state = player.getState();\n const max = state.duration > 0 ? state.duration : state.currentTime + seekOffsetSeconds;\n player.seek(Math.min(max, state.currentTime + seekOffsetSeconds));\n });\n setActionHandler(\"previoustrack\", () => {\n void player.previous();\n });\n setActionHandler(\"nexttrack\", () => {\n void player.next();\n });\n setActionHandler(\"seekto\", (details) => {\n if (details.seekTime === undefined) {\n return;\n }\n\n player.seek(details.seekTime);\n });\n setActionHandler(\"stop\", () => {\n player.pause();\n });\n\n const stateUnsubscribe = player.subscribe(() => {\n sync();\n });\n\n sync();\n\n return {\n sync,\n destroy() {\n destroyed = true;\n stateUnsubscribe();\n if (!hasMediaSession()) {\n return;\n }\n\n const actions: MediaSessionAction[] = [\n \"play\",\n \"pause\",\n \"seekbackward\",\n \"seekforward\",\n \"previoustrack\",\n \"nexttrack\",\n \"seekto\",\n \"stop\",\n ];\n\n for (const action of actions) {\n setActionHandler(action, null);\n }\n\n navigator.mediaSession.metadata = null;\n navigator.mediaSession.playbackState = \"none\";\n try {\n navigator.mediaSession.setPositionState();\n } catch {\n // Ignore unsupported position state implementations.\n }\n },\n };\n};\n"],"mappings":"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","names":["endedState"]}
1
+ {"version":3,"sources":["../src/react.tsx","../src/index.ts"],"sourcesContent":["import {\n createContext,\n type ReactNode,\n useContext,\n useEffect,\n useRef,\n useSyncExternalStore,\n} from \"react\";\nimport {\n createAudioPlayer,\n type AudioPlayer,\n type AudioPlayerState,\n type CreateAudioPlayerOptions,\n} from \"./index.js\";\n\nconst AudioPlayerContext = createContext<AudioPlayer | null>(null);\n\nexport interface AudioPlayerProviderProps {\n children: ReactNode;\n options?: CreateAudioPlayerOptions;\n player?: AudioPlayer;\n}\n\nexport const AudioPlayerProvider = ({\n children,\n options,\n player,\n}: AudioPlayerProviderProps) => {\n const playerRef = useRef<AudioPlayer | null>(player ?? null);\n\n if (playerRef.current === null) {\n playerRef.current = createAudioPlayer(options);\n }\n\n const mountedRef = useRef(false);\n\n useEffect(() => {\n if (player) {\n return undefined;\n }\n\n const owned = playerRef.current;\n mountedRef.current = true;\n\n return () => {\n mountedRef.current = false;\n // StrictMode re-runs effects right after cleanup, so only destroy if not re-mounted\n queueMicrotask(() => {\n if (!mountedRef.current) {\n owned?.destroy();\n }\n });\n };\n }, [player]);\n\n return (\n <AudioPlayerContext.Provider value={playerRef.current}>\n {children}\n </AudioPlayerContext.Provider>\n );\n};\n\nexport const useAudioPlayer = (): AudioPlayer => {\n const player = useContext(AudioPlayerContext);\n\n if (!player) {\n throw new Error(\"useAudioPlayer must be used inside AudioPlayerProvider.\");\n }\n\n return player;\n};\n\nexport const useAudioPlayerState = (): AudioPlayerState => {\n const player = useAudioPlayer();\n\n return useSyncExternalStore(\n player.subscribe,\n player.getState,\n player.getState,\n );\n};\n","export type PlayerStatus =\n | \"idle\"\n | \"loading\"\n | \"ready\"\n | \"playing\"\n | \"paused\"\n | \"ended\"\n | \"error\";\n\nexport type RepeatMode = \"off\" | \"one\" | \"all\";\n\nexport type AudioPlayerEventName =\n | \"sourcechange\"\n | \"queuechange\"\n | \"play\"\n | \"pause\"\n | \"seeking\"\n | \"seeked\"\n | \"timeupdate\"\n | \"ratechange\"\n | \"volumechange\"\n | \"ended\"\n | \"error\"\n | \"destroy\";\n\nexport interface AudioSource {\n id: string;\n src: string;\n title?: string;\n artist?: string;\n artwork?: string;\n album?: string;\n durationHint?: number;\n}\n\nexport interface AudioPlayerErrorState {\n code:\n | \"LOAD_ERROR\"\n | \"PLAY_ERROR\"\n | \"SEEK_ERROR\"\n | \"INVALID_VOLUME\"\n | \"INVALID_RATE\"\n | \"INVALID_QUEUE_ITEM\"\n | \"NO_ACTIVE_SOURCE\"\n | \"UNSUPPORTED_ENVIRONMENT\"\n | \"UNKNOWN_ERROR\";\n message: string;\n}\n\nexport interface AudioPlayerRuntimeError extends AudioPlayerErrorState {\n cause?: unknown;\n}\n\nexport interface AudioPlayerProgressState {\n currentTime: number;\n duration: number;\n buffered: number;\n playedFraction: number;\n bufferedFraction: number;\n}\n\nexport interface AudioPlayerQueuePositionState {\n currentIndex: number;\n currentSourceId: string | null;\n length: number;\n hasNext: boolean;\n hasPrevious: boolean;\n repeatMode: RepeatMode;\n}\n\nexport interface AudioPlayerQueueState {\n items: AudioSource[];\n itemIds: string[];\n position: AudioPlayerQueuePositionState;\n}\n\nexport interface AudioPlayerState {\n status: PlayerStatus;\n currentSource: AudioSource | null;\n currentSourceId: string | null;\n currentTime: number;\n duration: number;\n buffered: number;\n progress: AudioPlayerProgressState;\n rate: number;\n volume: number;\n muted: boolean;\n queue: AudioPlayerQueueState;\n error: AudioPlayerErrorState | null;\n}\n\nexport interface AudioPlayerEventMap {\n sourcechange: { source: AudioSource | null; state: AudioPlayerState };\n queuechange: { queue: AudioPlayerQueueState; state: AudioPlayerState };\n play: { source: AudioSource | null; state: AudioPlayerState };\n pause: { source: AudioSource | null; state: AudioPlayerState };\n seeking: { from: number; to: number; state: AudioPlayerState };\n seeked: { currentTime: number; state: AudioPlayerState };\n timeupdate: { currentTime: number; duration: number; state: AudioPlayerState };\n ratechange: { rate: number; state: AudioPlayerState };\n volumechange: { volume: number; muted: boolean; state: AudioPlayerState };\n ended: { source: AudioSource | null; state: AudioPlayerState };\n error: { error: AudioPlayerRuntimeError; state: AudioPlayerState };\n destroy: { state: AudioPlayerState };\n}\n\nexport interface CreateAudioPlayerOptions {\n initialVolume?: number;\n initialRate?: number;\n initialMuted?: boolean;\n initialRepeatMode?: RepeatMode;\n}\n\nexport interface SetQueueOptions {\n startAtId?: string;\n autoplay?: boolean;\n}\n\nexport interface PreviousOptions {\n restartThresholdSeconds?: number;\n}\n\nexport interface PersistedPlayerState {\n version: 1;\n savedAt: number;\n currentSource: AudioSource | null;\n currentSourceId: string | null;\n currentTime: number;\n rate: number;\n volume: number;\n muted: boolean;\n queue: {\n items: AudioSource[];\n position: {\n currentIndex: number;\n currentSourceId: string | null;\n repeatMode: RepeatMode;\n };\n };\n}\n\nexport interface AudioPlayerPersistenceAdapter {\n load(): PersistedPlayerState | null;\n save(state: PersistedPlayerState): void;\n clear(): void;\n}\n\nexport interface AudioPlayerPersistenceOptions {\n adapter: AudioPlayerPersistenceAdapter;\n autoRestore?: boolean;\n maxAgeMs?: number;\n saveIntervalMs?: number;\n}\n\nexport interface AudioPlayerPersistenceController {\n restore(): Promise<PersistedPlayerState | null>;\n save(): void;\n clear(): void;\n destroy(): void;\n}\n\nexport interface AudioPlayerProgressSnapshot {\n source: AudioSource | null;\n sourceId: string | null;\n currentTime: number;\n duration: number;\n playedFraction: number;\n status: PlayerStatus;\n queue: AudioPlayerQueuePositionState;\n}\n\nexport interface AudioPlayerProgressReporterOptions {\n intervalMs?: number;\n onReport: (snapshot: AudioPlayerProgressSnapshot) => void;\n}\n\nexport interface AudioPlayerProgressReporterController {\n report(): void;\n destroy(): void;\n}\n\nexport interface AudioPlayerMediaSessionOptions {\n artworkSizes?: number[];\n seekOffsetSeconds?: number;\n}\n\nexport interface AudioPlayerMediaSessionController {\n sync(): void;\n destroy(): void;\n}\n\nexport type AudioPlayerEventListener<K extends AudioPlayerEventName> = (\n event: AudioPlayerEventMap[K],\n) => void;\n\nexport interface AudioPlayer {\n getState(): AudioPlayerState;\n subscribe(listener: (state: AudioPlayerState) => void): () => void;\n on<K extends AudioPlayerEventName>(\n eventName: K,\n listener: AudioPlayerEventListener<K>,\n ): () => void;\n\n load(source: AudioSource): Promise<void>;\n play(source?: AudioSource): Promise<void>;\n pause(): void;\n toggle(): Promise<void>;\n unload(): void;\n seek(time: number): void;\n setRate(rate: number): void;\n setVolume(volume: number): void;\n setMuted(muted: boolean): void;\n\n setQueue(items: AudioSource[], options?: SetQueueOptions): Promise<void>;\n appendToQueue(items: AudioSource[]): void;\n removeFromQueue(id: string): void;\n clearQueue(): void;\n next(): Promise<void>;\n previous(options?: PreviousOptions): Promise<void>;\n setRepeatMode(mode: RepeatMode): void;\n\n destroy(): void;\n}\n\nconst DEFAULT_PREVIOUS_RESTART_THRESHOLD_SECONDS = 5;\nconst DEFAULT_PERSISTENCE_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000;\nconst DEFAULT_PERSISTENCE_SAVE_INTERVAL_MS = 5000;\nconst DEFAULT_PROGRESS_REPORT_INTERVAL_MS = 5000;\nconst DEFAULT_MEDIA_SESSION_ARTWORK_SIZES = [96, 128, 192, 256, 384, 512];\nconst DEFAULT_MEDIA_SESSION_SEEK_OFFSET_SECONDS = 10;\n\ntype AudioEngineEventName =\n | \"loadedmetadata\"\n | \"timeupdate\"\n | \"progress\"\n | \"play\"\n | \"pause\"\n | \"seeked\"\n | \"ratechange\"\n | \"volumechange\"\n | \"ended\"\n | \"error\";\n\ninterface AudioEngineSnapshot {\n currentTime: number;\n duration: number;\n buffered: number;\n rate: number;\n volume: number;\n muted: boolean;\n}\n\ninterface AudioEngine {\n readonly available: boolean;\n getSnapshot(durationHint?: number): AudioEngineSnapshot;\n getRuntimeError(): unknown;\n on(eventName: AudioEngineEventName, listener: EventListener): () => void;\n setSource(src: string): void;\n clearSource(): void;\n load(): void;\n play(): Promise<void>;\n pause(): void;\n setCurrentTime(time: number): void;\n setPlaybackRate(rate: number): void;\n setVolume(volume: number): void;\n setMuted(muted: boolean): void;\n destroy(): void;\n}\n\nconst createInitialState = (\n options: CreateAudioPlayerOptions,\n): AudioPlayerState => ({\n status: \"idle\",\n currentSource: null,\n currentSourceId: null,\n currentTime: 0,\n duration: 0,\n buffered: 0,\n progress: {\n currentTime: 0,\n duration: 0,\n buffered: 0,\n playedFraction: 0,\n bufferedFraction: 0,\n },\n rate: options.initialRate ?? 1,\n volume: options.initialVolume ?? 1,\n muted: options.initialMuted ?? false,\n queue: {\n items: [],\n itemIds: [],\n position: {\n currentIndex: -1,\n currentSourceId: null,\n length: 0,\n repeatMode: options.initialRepeatMode ?? \"off\",\n hasNext: false,\n hasPrevious: false,\n },\n },\n error: null,\n});\n\nconst createPlayerError = (\n code: AudioPlayerRuntimeError[\"code\"],\n message: string,\n cause?: unknown,\n): AudioPlayerRuntimeError => ({\n code,\n message,\n cause,\n});\n\nconst isRuntimePlayerError = (\n value: unknown,\n): value is AudioPlayerRuntimeError =>\n !!value &&\n typeof value === \"object\" &&\n \"code\" in value &&\n \"message\" in value;\n\nconst normalizePlayerError = (\n fallbackCode: AudioPlayerRuntimeError[\"code\"],\n fallbackMessage: string,\n cause: unknown,\n): AudioPlayerRuntimeError =>\n isRuntimePlayerError(cause)\n ? cause\n : createPlayerError(fallbackCode, fallbackMessage, cause);\n\nconst assertStableSource = (source: AudioSource): void => {\n if (!source.id || !source.src) {\n throw createPlayerError(\n \"INVALID_QUEUE_ITEM\",\n \"AudioSource requires stable id and src.\",\n );\n }\n};\n\nconst isFiniteNumber = (value: number): boolean =>\n Number.isFinite(value) && !Number.isNaN(value);\n\nconst safeDuration = (audio: HTMLAudioElement | null, hint?: number): number => {\n const duration = audio?.duration;\n return duration !== undefined && isFiniteNumber(duration) ? duration : hint ?? 0;\n};\n\nconst safeBuffered = (audio: HTMLAudioElement | null): number => {\n if (!audio || audio.buffered.length === 0) {\n return 0;\n }\n\n try {\n return audio.buffered.end(audio.buffered.length - 1);\n } catch {\n return 0;\n }\n};\n\nconst cloneQueueState = (state: AudioPlayerState): AudioPlayerQueueState => ({\n items: [...state.queue.items],\n itemIds: [...state.queue.itemIds],\n position: {\n ...state.queue.position,\n },\n});\n\nconst updateQueueFlags = (state: AudioPlayerState): AudioPlayerState => {\n const { items, position } = state.queue;\n const { currentIndex, repeatMode } = position;\n const lastIndex = items.length - 1;\n const hasItems = items.length > 0;\n const hasNext =\n !hasItems || currentIndex < 0\n ? false\n : repeatMode === \"all\"\n ? items.length > 0\n : currentIndex < lastIndex;\n const hasPrevious =\n !hasItems || currentIndex < 0\n ? false\n : repeatMode === \"all\"\n ? items.length > 0\n : currentIndex > 0;\n\n return {\n ...state,\n queue: {\n ...state.queue,\n itemIds: items.map((item) => item.id),\n position: {\n ...position,\n currentSourceId: state.currentSource?.id ?? null,\n length: items.length,\n hasNext,\n hasPrevious,\n },\n },\n };\n};\n\nconst createAudioElement = (): HTMLAudioElement | null => {\n if (typeof Audio === \"undefined\") {\n return null;\n }\n\n return new Audio();\n};\n\nconst createUnavailableAudioEngine = (): AudioEngine => {\n const unavailableError = (): AudioPlayerRuntimeError =>\n createPlayerError(\n \"UNSUPPORTED_ENVIRONMENT\",\n \"HTMLAudioElement is not available in this environment.\",\n );\n\n return {\n available: false,\n getSnapshot(durationHint) {\n return {\n currentTime: 0,\n duration: durationHint ?? 0,\n buffered: 0,\n rate: 1,\n volume: 1,\n muted: false,\n };\n },\n getRuntimeError() {\n return unavailableError();\n },\n on() {\n return () => {};\n },\n setSource() {\n throw unavailableError();\n },\n clearSource() {\n throw unavailableError();\n },\n load() {\n throw unavailableError();\n },\n play() {\n throw unavailableError();\n },\n pause() {\n throw unavailableError();\n },\n setCurrentTime() {\n throw unavailableError();\n },\n setPlaybackRate() {\n throw unavailableError();\n },\n setVolume() {\n throw unavailableError();\n },\n setMuted() {\n throw unavailableError();\n },\n destroy() {},\n };\n};\n\nconst createHtmlAudioEngine = (): AudioEngine => {\n const audio = createAudioElement();\n if (!audio) {\n return createUnavailableAudioEngine();\n }\n\n audio.preload = \"metadata\";\n const clearSource = (): void => {\n const element = audio as HTMLAudioElement & {\n removeAttribute?: (name: string) => void;\n };\n if (typeof element.removeAttribute === \"function\") {\n element.removeAttribute(\"src\");\n return;\n }\n\n audio.src = \"\";\n };\n\n return {\n available: true,\n getSnapshot(durationHint) {\n return {\n currentTime: audio.currentTime,\n duration: safeDuration(audio, durationHint),\n buffered: safeBuffered(audio),\n rate: audio.playbackRate,\n volume: audio.volume,\n muted: audio.muted,\n };\n },\n getRuntimeError() {\n return audio.error;\n },\n on(eventName, listener) {\n audio.addEventListener(eventName, listener);\n return () => {\n audio.removeEventListener(eventName, listener);\n };\n },\n setSource(src) {\n audio.src = src;\n },\n clearSource() {\n clearSource();\n },\n load() {\n audio.load();\n },\n play() {\n return audio.play();\n },\n pause() {\n audio.pause();\n },\n setCurrentTime(time) {\n audio.currentTime = time;\n },\n setPlaybackRate(rate) {\n audio.playbackRate = rate;\n },\n setVolume(volume) {\n audio.volume = volume;\n },\n setMuted(muted) {\n audio.muted = muted;\n },\n destroy() {\n audio.pause();\n clearSource();\n audio.load();\n },\n };\n};\n\nconst isBrowserStorageAvailable = (): boolean =>\n typeof window !== \"undefined\" && typeof window.localStorage !== \"undefined\";\n\nconst isPersistedPlayerState = (value: unknown): value is PersistedPlayerState => {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const candidate = value as Partial<PersistedPlayerState>;\n return (\n candidate.version === 1 &&\n typeof candidate.savedAt === \"number\" &&\n \"currentSource\" in candidate &&\n typeof candidate.currentTime === \"number\" &&\n typeof candidate.rate === \"number\" &&\n typeof candidate.volume === \"number\" &&\n typeof candidate.muted === \"boolean\" &&\n !!candidate.queue &&\n Array.isArray(candidate.queue.items) &&\n !!candidate.queue.position\n );\n};\n\nconst toPersistedPlayerState = (state: AudioPlayerState): PersistedPlayerState => ({\n version: 1,\n savedAt: Date.now(),\n currentSource: state.currentSource,\n currentSourceId: state.currentSourceId,\n currentTime: state.currentTime,\n rate: state.rate,\n volume: state.volume,\n muted: state.muted,\n queue: {\n items: [...state.queue.items],\n position: {\n currentIndex: state.queue.position.currentIndex,\n currentSourceId: state.queue.position.currentSourceId,\n repeatMode: state.queue.position.repeatMode,\n },\n },\n});\n\nconst toProgressSnapshot = (\n state: AudioPlayerState,\n): AudioPlayerProgressSnapshot => ({\n source: state.currentSource,\n sourceId: state.currentSourceId,\n currentTime: state.currentTime,\n duration: state.duration,\n playedFraction: state.progress.playedFraction,\n status: state.status,\n queue: {\n ...state.queue.position,\n },\n});\n\nconst hasMediaSession = (): boolean =>\n typeof navigator !== \"undefined\" && \"mediaSession\" in navigator;\n\nconst createMediaSessionArtwork = (\n source: AudioSource | null,\n sizes: number[],\n): MediaImage[] => {\n if (!source?.artwork) {\n return [];\n }\n\n return sizes.map((size) => ({\n src: source.artwork as string,\n sizes: `${size}x${size}`,\n }));\n};\n\nexport const createLocalStoragePersistenceAdapter = (\n key: string,\n): AudioPlayerPersistenceAdapter => ({\n load() {\n if (!isBrowserStorageAvailable()) {\n return null;\n }\n\n try {\n const raw = window.localStorage.getItem(key);\n if (!raw) {\n return null;\n }\n\n const parsed: unknown = JSON.parse(raw);\n return isPersistedPlayerState(parsed) ? parsed : null;\n } catch {\n return null;\n }\n },\n\n save(state) {\n if (!isBrowserStorageAvailable()) {\n return;\n }\n\n window.localStorage.setItem(key, JSON.stringify(state));\n },\n\n clear() {\n if (!isBrowserStorageAvailable()) {\n return;\n }\n\n window.localStorage.removeItem(key);\n },\n});\n\nexport const createAudioPlayer = (\n options: CreateAudioPlayerOptions = {},\n): AudioPlayer => {\n let state = updateQueueFlags(createInitialState(options));\n let destroyed = false;\n let shouldAutoplayAfterLoad = false;\n let suppressPauseEvent = false;\n\n const engine = createHtmlAudioEngine();\n const subscribers = new Set<(nextState: AudioPlayerState) => void>();\n const eventListeners: {\n [K in AudioPlayerEventName]: Set<AudioPlayerEventListener<K>>;\n } = {\n sourcechange: new Set(),\n queuechange: new Set(),\n play: new Set(),\n pause: new Set(),\n seeking: new Set(),\n seeked: new Set(),\n timeupdate: new Set(),\n ratechange: new Set(),\n volumechange: new Set(),\n ended: new Set(),\n error: new Set(),\n destroy: new Set(),\n };\n\n const getState = (): AudioPlayerState => state;\n\n const publishState = (): void => {\n for (const subscriber of subscribers) {\n subscriber(state);\n }\n };\n\n const emit = <K extends AudioPlayerEventName>(\n eventName: K,\n event: AudioPlayerEventMap[K],\n ): void => {\n for (const listener of eventListeners[eventName]) {\n listener(event);\n }\n };\n\n const setState = (\n updater: (currentState: AudioPlayerState) => AudioPlayerState,\n ): AudioPlayerState => {\n state = updateQueueFlags(updater(state));\n publishState();\n return state;\n };\n\n const setError = (error: AudioPlayerRuntimeError): void => {\n shouldAutoplayAfterLoad = false;\n const nextState = setState((currentState) => ({\n ...currentState,\n status: \"error\",\n error: {\n code: error.code,\n message: error.message,\n },\n }));\n emit(\"error\", { error, state: nextState });\n };\n\n const clearError = (): void => {\n if (!state.error) {\n return;\n }\n\n setState((currentState) => ({\n ...currentState,\n error: null,\n }));\n };\n\n const syncAudioSnapshot = (\n statusOverride?: PlayerStatus,\n ): AudioPlayerState => {\n const snapshot = engine.getSnapshot(state.currentSource?.durationHint);\n const currentTime = snapshot.currentTime;\n const duration = snapshot.duration;\n const buffered = snapshot.buffered;\n const playedFraction =\n duration > 0 ? Math.min(1, Math.max(0, currentTime / duration)) : 0;\n const bufferedFraction =\n duration > 0 ? Math.min(1, Math.max(0, buffered / duration)) : 0;\n\n const nextState = setState((currentState) => ({\n ...currentState,\n currentSourceId: currentState.currentSource?.id ?? null,\n currentTime,\n duration,\n buffered,\n progress: {\n currentTime,\n duration,\n buffered,\n playedFraction,\n bufferedFraction,\n },\n rate: snapshot.rate,\n volume: snapshot.volume,\n muted: snapshot.muted,\n status: statusOverride ?? currentState.status,\n error:\n statusOverride === \"error\" ? currentState.error : null,\n }));\n\n return nextState;\n };\n\n const findQueueIndexById = (id: string | null | undefined): number =>\n id ? state.queue.items.findIndex((item) => item.id === id) : -1;\n\n const resolveNextQueueIndex = (\n mode: \"manual\" | \"ended\",\n ): number | null => {\n const { items, position } = state.queue;\n const { currentIndex, repeatMode } = position;\n\n if (items.length === 0 || currentIndex < 0) {\n return null;\n }\n\n if (repeatMode === \"one\") {\n return currentIndex;\n }\n\n if (currentIndex < items.length - 1) {\n return currentIndex + 1;\n }\n\n if (repeatMode === \"all\") {\n return 0;\n }\n\n return mode === \"ended\" ? -1 : null;\n };\n\n const resolvePreviousQueueIndex = (): number | null => {\n const { items, position } = state.queue;\n const { currentIndex, repeatMode } = position;\n\n if (items.length === 0 || currentIndex < 0) {\n return null;\n }\n\n if (currentIndex > 0) {\n return currentIndex - 1;\n }\n\n if (repeatMode === \"all\") {\n return items.length - 1;\n }\n\n return null;\n };\n\n const updateCurrentSource = (\n source: AudioSource | null,\n nextStatus: PlayerStatus,\n ): AudioPlayerState => {\n const queueIndex = findQueueIndexById(source?.id);\n const nextState = setState((currentState) => ({\n ...currentState,\n currentSource: source,\n currentSourceId: source?.id ?? null,\n currentTime: 0,\n duration: source?.durationHint ?? 0,\n buffered: 0,\n progress: {\n currentTime: 0,\n duration: source?.durationHint ?? 0,\n buffered: 0,\n playedFraction: 0,\n bufferedFraction: 0,\n },\n status: nextStatus,\n error: null,\n queue: {\n ...currentState.queue,\n position: {\n ...currentState.queue.position,\n currentIndex: queueIndex,\n currentSourceId: source?.id ?? null,\n },\n },\n }));\n emit(\"sourcechange\", { source, state: nextState });\n return nextState;\n };\n\n const setQueueState = (\n items: AudioSource[],\n currentIndex: number,\n ): AudioPlayerState =>\n setState((currentState) => ({\n ...currentState,\n queue: {\n ...currentState.queue,\n items,\n position: {\n ...currentState.queue.position,\n currentIndex,\n },\n },\n }));\n\n const emitQueueChange = (nextState: AudioPlayerState): void => {\n emit(\"queuechange\", { queue: cloneQueueState(nextState), state: nextState });\n };\n\n const attachSourceToAudio = (\n source: AudioSource,\n autoplayAfterLoad: boolean,\n ): void => {\n shouldAutoplayAfterLoad = autoplayAfterLoad;\n suppressPauseEvent = true;\n try {\n engine.pause();\n } finally {\n suppressPauseEvent = false;\n }\n engine.setSource(source.src);\n engine.setCurrentTime(0);\n engine.setPlaybackRate(state.rate);\n engine.setVolume(state.volume);\n engine.setMuted(state.muted);\n engine.load();\n };\n\n const loadInternal = async (\n source: AudioSource,\n autoplayAfterLoad = false,\n ): Promise<void> => {\n assertStableSource(source);\n updateCurrentSource(source, \"loading\");\n attachSourceToAudio(source, autoplayAfterLoad);\n };\n\n const selectQueueIndex = async (\n index: number,\n autoplayAfterLoad = false,\n ): Promise<AudioSource | null> => {\n const item = state.queue.items[index] ?? null;\n if (!item) {\n return null;\n }\n\n await loadInternal(item, autoplayAfterLoad);\n const nextState = setQueueState(state.queue.items, index);\n emitQueueChange(nextState);\n return item;\n };\n\n const playLoadedAudio = async (): Promise<void> => {\n try {\n await engine.play();\n } catch (cause) {\n setError(createPlayerError(\"PLAY_ERROR\", \"Failed to start playback.\", cause));\n }\n };\n\n const handlePlaybackEnded = async (): Promise<void> => {\n if (destroyed) {\n return;\n }\n\n if (state.queue.items.length > 0 && state.queue.position.currentIndex >= 0) {\n const nextIndex = resolveNextQueueIndex(\"ended\");\n if (nextIndex === -1 || nextIndex === null) {\n const endedState = syncAudioSnapshot(\"ended\");\n emit(\"ended\", { source: endedState.currentSource, state: endedState });\n return;\n }\n\n await selectQueueIndex(nextIndex, true);\n return;\n }\n\n const endedState = syncAudioSnapshot(\"ended\");\n emit(\"ended\", { source: endedState.currentSource, state: endedState });\n };\n\n const detachEngineListeners = [\n engine.on(\"loadedmetadata\", () => {\n if (destroyed || !state.currentSource) {\n return;\n }\n\n syncAudioSnapshot(\"ready\");\n if (shouldAutoplayAfterLoad) {\n shouldAutoplayAfterLoad = false;\n void playLoadedAudio();\n }\n }),\n engine.on(\"timeupdate\", () => {\n if (destroyed) {\n return;\n }\n\n const nextState = syncAudioSnapshot(\n state.status === \"ended\" ? \"playing\" : undefined,\n );\n emit(\"timeupdate\", {\n currentTime: nextState.currentTime,\n duration: nextState.duration,\n state: nextState,\n });\n }),\n engine.on(\"progress\", () => {\n if (!destroyed) {\n syncAudioSnapshot();\n }\n }),\n engine.on(\"play\", () => {\n if (destroyed) {\n return;\n }\n\n const nextState = syncAudioSnapshot(\"playing\");\n emit(\"play\", { source: nextState.currentSource, state: nextState });\n }),\n engine.on(\"pause\", () => {\n if (destroyed || suppressPauseEvent) {\n return;\n }\n\n const nextStatus =\n state.status === \"ended\" ? \"ended\" : state.currentSource ? \"paused\" : \"idle\";\n const nextState = syncAudioSnapshot(nextStatus);\n emit(\"pause\", { source: nextState.currentSource, state: nextState });\n }),\n engine.on(\"seeked\", () => {\n if (destroyed) {\n return;\n }\n\n const nextState = syncAudioSnapshot();\n emit(\"seeked\", { currentTime: nextState.currentTime, state: nextState });\n emit(\"timeupdate\", {\n currentTime: nextState.currentTime,\n duration: nextState.duration,\n state: nextState,\n });\n }),\n engine.on(\"ratechange\", () => {\n if (destroyed) {\n return;\n }\n\n const nextState = syncAudioSnapshot();\n emit(\"ratechange\", { rate: nextState.rate, state: nextState });\n }),\n engine.on(\"volumechange\", () => {\n if (destroyed) {\n return;\n }\n\n const nextState = syncAudioSnapshot();\n emit(\"volumechange\", {\n volume: nextState.volume,\n muted: nextState.muted,\n state: nextState,\n });\n }),\n engine.on(\"ended\", () => {\n void handlePlaybackEnded();\n }),\n engine.on(\"error\", () => {\n if (destroyed) {\n return;\n }\n\n setError(\n createPlayerError(\n \"LOAD_ERROR\",\n \"Audio element reported a playback error.\",\n engine.getRuntimeError(),\n ),\n );\n }),\n ];\n\n return {\n getState,\n\n subscribe(listener) {\n subscribers.add(listener);\n listener(state);\n return () => {\n subscribers.delete(listener);\n };\n },\n\n on(eventName, listener) {\n eventListeners[eventName].add(\n listener as AudioPlayerEventListener<typeof eventName>,\n );\n return () => {\n eventListeners[eventName].delete(\n listener as AudioPlayerEventListener<typeof eventName>,\n );\n };\n },\n\n async load(source) {\n try {\n await loadInternal(source);\n } catch (cause) {\n setError(\n normalizePlayerError(\"LOAD_ERROR\", \"Failed to load source.\", cause),\n );\n }\n },\n\n async play(source) {\n try {\n if (source) {\n await loadInternal(source, true);\n return;\n }\n\n if (!state.currentSource) {\n setError(\n createPlayerError(\n \"NO_ACTIVE_SOURCE\",\n \"Cannot play without an active source.\",\n ),\n );\n return;\n }\n\n if (state.status === \"loading\") {\n shouldAutoplayAfterLoad = true;\n return;\n }\n\n await playLoadedAudio();\n } catch (cause) {\n setError(normalizePlayerError(\"PLAY_ERROR\", \"Failed to start playback.\", cause));\n }\n },\n\n pause() {\n if (!engine.available) {\n return;\n }\n\n shouldAutoplayAfterLoad = false;\n engine.pause();\n },\n\n async toggle() {\n if (state.status === \"playing\") {\n this.pause();\n return;\n }\n\n await this.play();\n },\n\n unload() {\n shouldAutoplayAfterLoad = false;\n\n if (engine.available) {\n suppressPauseEvent = true;\n try {\n engine.pause();\n } finally {\n suppressPauseEvent = false;\n }\n engine.clearSource();\n engine.load();\n }\n\n const nextState = updateCurrentSource(null, \"idle\");\n emitQueueChange(nextState);\n },\n\n seek(time) {\n if (!state.currentSource) {\n setError(\n createPlayerError(\"SEEK_ERROR\", \"Cannot seek without an active source.\"),\n );\n return;\n }\n\n const max = engine.getSnapshot(state.currentSource.durationHint).duration;\n const normalized = Math.max(\n 0,\n max > 0 ? Math.min(time, max) : (isFiniteNumber(time) ? time : 0),\n );\n emit(\"seeking\", { from: state.currentTime, to: normalized, state });\n\n try {\n engine.setCurrentTime(normalized);\n } catch (cause) {\n setError(normalizePlayerError(\"SEEK_ERROR\", \"Failed to seek audio.\", cause));\n }\n },\n\n setRate(rate) {\n if (!engine.available) {\n setError(\n createPlayerError(\"INVALID_RATE\", \"Audio element is unavailable.\"),\n );\n return;\n }\n\n if (!isFiniteNumber(rate) || rate <= 0) {\n setError(\n createPlayerError(\"INVALID_RATE\", \"Playback rate must be greater than 0.\"),\n );\n return;\n }\n\n engine.setPlaybackRate(rate);\n clearError();\n syncAudioSnapshot();\n },\n\n setVolume(volume) {\n if (!engine.available) {\n setError(\n createPlayerError(\"INVALID_VOLUME\", \"Audio element is unavailable.\"),\n );\n return;\n }\n\n if (!isFiniteNumber(volume) || volume < 0 || volume > 1) {\n setError(\n createPlayerError(\"INVALID_VOLUME\", \"Volume must be between 0 and 1.\"),\n );\n return;\n }\n\n engine.setVolume(volume);\n clearError();\n syncAudioSnapshot();\n },\n\n setMuted(muted) {\n if (!engine.available) {\n return;\n }\n\n engine.setMuted(muted);\n clearError();\n syncAudioSnapshot();\n },\n\n async setQueue(items, queueOptions = {}) {\n try {\n for (const item of items) {\n assertStableSource(item);\n }\n\n const startIndex =\n queueOptions.startAtId === undefined\n ? items.length > 0\n ? 0\n : -1\n : items.findIndex((item) => item.id === queueOptions.startAtId);\n\n if (queueOptions.startAtId && startIndex < 0) {\n throw createPlayerError(\n \"INVALID_QUEUE_ITEM\",\n `Queue start item ${queueOptions.startAtId} was not found.`,\n );\n }\n\n const nextState = setState((currentState) => ({\n ...currentState,\n currentSource: startIndex >= 0 ? items[startIndex] ?? null : null,\n currentSourceId: startIndex >= 0 ? (items[startIndex]?.id ?? null) : null,\n currentTime: 0,\n duration: startIndex >= 0 ? (items[startIndex]?.durationHint ?? 0) : 0,\n buffered: 0,\n progress: {\n currentTime: 0,\n duration: startIndex >= 0 ? (items[startIndex]?.durationHint ?? 0) : 0,\n buffered: 0,\n playedFraction: 0,\n bufferedFraction: 0,\n },\n status: startIndex >= 0 ? \"ready\" : \"idle\",\n error: null,\n queue: {\n ...currentState.queue,\n items: [...items],\n position: {\n ...currentState.queue.position,\n currentIndex: startIndex,\n currentSourceId:\n startIndex >= 0 ? (items[startIndex]?.id ?? null) : null,\n },\n },\n }));\n\n emitQueueChange(nextState);\n emit(\"sourcechange\", {\n source: nextState.currentSource,\n state: nextState,\n });\n\n if (nextState.currentSource) {\n await loadInternal(nextState.currentSource, queueOptions.autoplay === true);\n const syncedState = setQueueState(items, startIndex);\n emitQueueChange(syncedState);\n } else if (engine.available) {\n shouldAutoplayAfterLoad = false;\n suppressPauseEvent = true;\n engine.pause();\n suppressPauseEvent = false;\n engine.clearSource();\n engine.load();\n }\n } catch (cause) {\n setError(\n normalizePlayerError(\n \"INVALID_QUEUE_ITEM\",\n \"Failed to set queue.\",\n cause,\n ),\n );\n }\n },\n\n appendToQueue(items) {\n try {\n for (const item of items) {\n assertStableSource(item);\n }\n\n const nextState = setState((currentState) => ({\n ...currentState,\n queue: {\n ...currentState.queue,\n items: [...currentState.queue.items, ...items],\n },\n }));\n emitQueueChange(nextState);\n } catch (cause) {\n setError(\n normalizePlayerError(\n \"INVALID_QUEUE_ITEM\",\n \"Failed to append queue items.\",\n cause,\n ),\n );\n }\n },\n\n removeFromQueue(id) {\n const nextItems = state.queue.items.filter((item) => item.id !== id);\n const nextIndex = nextItems.findIndex(\n (item) => item.id === state.currentSource?.id,\n );\n\n const nextState = setState((currentState) => ({\n ...currentState,\n queue: {\n ...currentState.queue,\n items: nextItems,\n position: {\n ...currentState.queue.position,\n currentIndex: nextIndex,\n currentSourceId: nextIndex >= 0 ? (nextItems[nextIndex]?.id ?? null) : null,\n },\n },\n }));\n\n emitQueueChange(nextState);\n },\n\n clearQueue() {\n const nextState = setState((currentState) => ({\n ...currentState,\n queue: {\n ...currentState.queue,\n items: [],\n position: {\n ...currentState.queue.position,\n currentIndex: -1,\n currentSourceId: null,\n },\n },\n }));\n\n emitQueueChange(nextState);\n },\n\n async next() {\n const nextIndex = resolveNextQueueIndex(\"manual\");\n if (nextIndex === null) {\n return;\n }\n\n await selectQueueIndex(nextIndex, true);\n },\n\n async previous(previousOptions = {}) {\n if (!state.currentSource) {\n return;\n }\n\n const threshold =\n previousOptions.restartThresholdSeconds ??\n DEFAULT_PREVIOUS_RESTART_THRESHOLD_SECONDS;\n\n if (state.currentTime > threshold) {\n this.seek(0);\n return;\n }\n\n const previousIndex = resolvePreviousQueueIndex();\n if (previousIndex === null) {\n this.seek(0);\n return;\n }\n\n await selectQueueIndex(previousIndex, true);\n },\n\n setRepeatMode(mode) {\n const nextState = setState((currentState) => ({\n ...currentState,\n queue: {\n ...currentState.queue,\n position: {\n ...currentState.queue.position,\n repeatMode: mode,\n },\n },\n }));\n emitQueueChange(nextState);\n },\n\n destroy() {\n if (destroyed) {\n return;\n }\n\n destroyed = true;\n shouldAutoplayAfterLoad = false;\n for (const detach of detachEngineListeners) {\n detach();\n }\n suppressPauseEvent = true;\n engine.destroy();\n suppressPauseEvent = false;\n\n const nextState = setState(() => createInitialState(options));\n emit(\"destroy\", { state: nextState });\n subscribers.clear();\n for (const eventName of Object.keys(eventListeners) as AudioPlayerEventName[]) {\n eventListeners[eventName].clear();\n }\n },\n };\n};\n\nexport const attachAudioPlayerPersistence = (\n player: AudioPlayer,\n options: AudioPlayerPersistenceOptions,\n): AudioPlayerPersistenceController => {\n const maxAgeMs = options.maxAgeMs ?? DEFAULT_PERSISTENCE_MAX_AGE_MS;\n const saveIntervalMs =\n options.saveIntervalMs ?? DEFAULT_PERSISTENCE_SAVE_INTERVAL_MS;\n let destroyed = false;\n let playIntervalId: ReturnType<typeof setInterval> | null = null;\n\n const save = (): void => {\n if (destroyed) {\n return;\n }\n\n const state = player.getState();\n if (!state.currentSource) {\n options.adapter.clear();\n return;\n }\n\n options.adapter.save(toPersistedPlayerState(state));\n };\n\n const clear = (): void => {\n options.adapter.clear();\n };\n\n const stopPlayInterval = (): void => {\n if (playIntervalId !== null) {\n clearInterval(playIntervalId);\n playIntervalId = null;\n }\n };\n\n const startPlayInterval = (): void => {\n if (playIntervalId !== null) {\n return;\n }\n\n playIntervalId = setInterval(() => {\n save();\n }, saveIntervalMs);\n };\n\n const waitForRestoredSource = (sourceId: string): Promise<void> =>\n new Promise((resolve) => {\n let settled = false;\n let unsubscribe: (() => void) | undefined;\n unsubscribe = player.subscribe((state) => {\n if (settled || state.currentSourceId !== sourceId) {\n return;\n }\n\n if (state.status === \"loading\") {\n return;\n }\n\n settled = true;\n unsubscribe?.();\n resolve();\n });\n\n // subscribe emits synchronously, so the source may already be settled\n if (settled) {\n unsubscribe();\n }\n });\n\n const restore = async (): Promise<PersistedPlayerState | null> => {\n const persisted = options.adapter.load();\n if (!persisted) {\n return null;\n }\n\n if (Date.now() - persisted.savedAt > maxAgeMs) {\n options.adapter.clear();\n return null;\n }\n\n player.setRepeatMode(persisted.queue.position.repeatMode);\n player.setVolume(persisted.volume);\n player.setMuted(persisted.muted);\n player.setRate(persisted.rate);\n\n if (persisted.queue.items.length > 0) {\n await player.setQueue(persisted.queue.items, {\n startAtId: persisted.queue.position.currentSourceId ?? undefined,\n });\n } else if (persisted.currentSource) {\n await player.load(persisted.currentSource);\n } else {\n return persisted;\n }\n\n if (persisted.currentSourceId) {\n await waitForRestoredSource(persisted.currentSourceId);\n }\n\n if (persisted.currentTime > 0) {\n player.seek(persisted.currentTime);\n }\n\n return persisted;\n };\n\n const stateUnsubscribe = player.subscribe((state) => {\n if (state.status === \"playing\") {\n startPlayInterval();\n return;\n }\n\n stopPlayInterval();\n });\n\n const pauseUnsubscribe = player.on(\"pause\", () => {\n save();\n });\n const endedUnsubscribe = player.on(\"ended\", () => {\n save();\n });\n const sourceUnsubscribe = player.on(\"sourcechange\", () => {\n save();\n });\n const queueUnsubscribe = player.on(\"queuechange\", () => {\n save();\n });\n const rateUnsubscribe = player.on(\"ratechange\", () => {\n save();\n });\n const volumeUnsubscribe = player.on(\"volumechange\", () => {\n save();\n });\n\n const beforeUnloadHandler = (): void => {\n save();\n };\n\n const visibilityHandler = (): void => {\n if (typeof document === \"undefined\") {\n return;\n }\n\n if (document.visibilityState === \"hidden\") {\n save();\n }\n };\n\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"beforeunload\", beforeUnloadHandler);\n }\n\n if (typeof document !== \"undefined\") {\n document.addEventListener(\"visibilitychange\", visibilityHandler);\n }\n\n if (options.autoRestore !== false) {\n void restore();\n }\n\n return {\n restore,\n save,\n clear,\n destroy() {\n destroyed = true;\n stopPlayInterval();\n stateUnsubscribe();\n pauseUnsubscribe();\n endedUnsubscribe();\n sourceUnsubscribe();\n queueUnsubscribe();\n rateUnsubscribe();\n volumeUnsubscribe();\n\n if (typeof window !== \"undefined\") {\n window.removeEventListener(\"beforeunload\", beforeUnloadHandler);\n }\n\n if (typeof document !== \"undefined\") {\n document.removeEventListener(\"visibilitychange\", visibilityHandler);\n }\n },\n };\n};\n\nexport const attachAudioPlayerProgressReporter = (\n player: AudioPlayer,\n options: AudioPlayerProgressReporterOptions,\n): AudioPlayerProgressReporterController => {\n const intervalMs = options.intervalMs ?? DEFAULT_PROGRESS_REPORT_INTERVAL_MS;\n let destroyed = false;\n let intervalId: ReturnType<typeof setInterval> | null = null;\n\n const report = (): void => {\n if (destroyed) {\n return;\n }\n\n const state = player.getState();\n if (!state.currentSource) {\n return;\n }\n\n options.onReport(toProgressSnapshot(state));\n };\n\n const stopInterval = (): void => {\n if (intervalId !== null) {\n clearInterval(intervalId);\n intervalId = null;\n }\n };\n\n const startInterval = (): void => {\n if (intervalId !== null) {\n return;\n }\n\n intervalId = setInterval(() => {\n report();\n }, intervalMs);\n };\n\n const stateUnsubscribe = player.subscribe((state) => {\n if (state.status === \"playing\") {\n startInterval();\n return;\n }\n\n stopInterval();\n });\n\n const pauseUnsubscribe = player.on(\"pause\", () => {\n report();\n });\n const endedUnsubscribe = player.on(\"ended\", () => {\n report();\n });\n const seekedUnsubscribe = player.on(\"seeked\", () => {\n report();\n });\n const sourceUnsubscribe = player.on(\"sourcechange\", () => {\n report();\n });\n\n return {\n report,\n destroy() {\n destroyed = true;\n stopInterval();\n stateUnsubscribe();\n pauseUnsubscribe();\n endedUnsubscribe();\n seekedUnsubscribe();\n sourceUnsubscribe();\n },\n };\n};\n\nexport const attachAudioPlayerMediaSession = (\n player: AudioPlayer,\n options: AudioPlayerMediaSessionOptions = {},\n): AudioPlayerMediaSessionController => {\n if (!hasMediaSession()) {\n return {\n sync() {},\n destroy() {},\n };\n }\n\n const artworkSizes =\n options.artworkSizes ?? DEFAULT_MEDIA_SESSION_ARTWORK_SIZES;\n const seekOffsetSeconds =\n options.seekOffsetSeconds ?? DEFAULT_MEDIA_SESSION_SEEK_OFFSET_SECONDS;\n let destroyed = false;\n\n const sync = (): void => {\n if (destroyed || !hasMediaSession()) {\n return;\n }\n\n const state = player.getState();\n const source = state.currentSource;\n\n navigator.mediaSession.metadata = source\n ? new MediaMetadata({\n title: source.title ?? \"\",\n artist: source.artist ?? \"\",\n album: source.album ?? \"\",\n artwork: createMediaSessionArtwork(source, artworkSizes),\n })\n : null;\n\n navigator.mediaSession.playbackState =\n state.status === \"playing\"\n ? \"playing\"\n : state.currentSource\n ? \"paused\"\n : \"none\";\n\n if (state.currentSource && state.duration > 0) {\n try {\n navigator.mediaSession.setPositionState({\n duration: state.duration,\n playbackRate: state.rate,\n position: Math.min(state.currentTime, state.duration),\n });\n } catch {\n // Ignore unsupported position state implementations.\n }\n } else {\n try {\n navigator.mediaSession.setPositionState();\n } catch {\n // Ignore unsupported position state implementations.\n }\n }\n };\n\n const setActionHandler = (\n action: MediaSessionAction,\n handler: MediaSessionActionHandler | null,\n ): void => {\n try {\n navigator.mediaSession.setActionHandler(action, handler);\n } catch {\n // Ignore unsupported actions.\n }\n };\n\n setActionHandler(\"play\", () => {\n void player.play();\n });\n setActionHandler(\"pause\", () => {\n player.pause();\n });\n setActionHandler(\"seekbackward\", () => {\n const state = player.getState();\n player.seek(Math.max(0, state.currentTime - seekOffsetSeconds));\n });\n setActionHandler(\"seekforward\", () => {\n const state = player.getState();\n const max = state.duration > 0 ? state.duration : state.currentTime + seekOffsetSeconds;\n player.seek(Math.min(max, state.currentTime + seekOffsetSeconds));\n });\n setActionHandler(\"previoustrack\", () => {\n void player.previous();\n });\n setActionHandler(\"nexttrack\", () => {\n void player.next();\n });\n setActionHandler(\"seekto\", (details) => {\n if (details.seekTime === undefined) {\n return;\n }\n\n player.seek(details.seekTime);\n });\n setActionHandler(\"stop\", () => {\n player.pause();\n });\n\n const stateUnsubscribe = player.subscribe(() => {\n sync();\n });\n\n sync();\n\n return {\n sync,\n destroy() {\n destroyed = true;\n stateUnsubscribe();\n if (!hasMediaSession()) {\n return;\n }\n\n const actions: MediaSessionAction[] = [\n \"play\",\n \"pause\",\n \"seekbackward\",\n \"seekforward\",\n \"previoustrack\",\n \"nexttrack\",\n \"seekto\",\n \"stop\",\n ];\n\n for (const action of actions) {\n setActionHandler(action, null);\n }\n\n navigator.mediaSession.metadata = null;\n navigator.mediaSession.playbackState = \"none\";\n try {\n navigator.mediaSession.setPositionState();\n } catch {\n // Ignore unsupported position state implementations.\n }\n },\n };\n};\n"],"mappings":"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","names":["endedState"]}
package/dist/react.d.ts CHANGED
@@ -1,14 +1,11 @@
1
- import * as react from 'react';
2
- import { ReactNode } from 'react';
3
- import { CreateAudioPlayerOptions, AudioPlayer, AudioPlayerState } from './index.js';
4
-
5
- interface AudioPlayerProviderProps {
1
+ import { type ReactNode } from "react";
2
+ import { type AudioPlayer, type AudioPlayerState, type CreateAudioPlayerOptions } from "./index.js";
3
+ export interface AudioPlayerProviderProps {
6
4
  children: ReactNode;
7
5
  options?: CreateAudioPlayerOptions;
8
6
  player?: AudioPlayer;
9
7
  }
10
- declare const AudioPlayerProvider: ({ children, options, player, }: AudioPlayerProviderProps) => react.JSX.Element;
11
- declare const useAudioPlayer: () => AudioPlayer;
12
- declare const useAudioPlayerState: () => AudioPlayerState;
13
-
14
- export { AudioPlayerProvider, type AudioPlayerProviderProps, useAudioPlayer, useAudioPlayerState };
8
+ export declare const AudioPlayerProvider: ({ children, options, player, }: AudioPlayerProviderProps) => import("react").JSX.Element;
9
+ export declare const useAudioPlayer: () => AudioPlayer;
10
+ export declare const useAudioPlayerState: () => AudioPlayerState;
11
+ //# sourceMappingURL=react.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,SAAS,EAKf,MAAM,OAAO,CAAC;AACf,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAC9B,MAAM,YAAY,CAAC;AAIpB,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,SAAS,CAAC;IACpB,OAAO,CAAC,EAAE,wBAAwB,CAAC;IACnC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,eAAO,MAAM,mBAAmB,mCAI7B,wBAAwB,gCAiC1B,CAAC;AAEF,eAAO,MAAM,cAAc,QAAO,WAQjC,CAAC;AAEF,eAAO,MAAM,mBAAmB,QAAO,gBAQtC,CAAC"}
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react.tsx"],"sourcesContent":["import {\n createContext,\n type ReactNode,\n useContext,\n useEffect,\n useRef,\n useSyncExternalStore,\n} from \"react\";\nimport {\n createAudioPlayer,\n type AudioPlayer,\n type AudioPlayerState,\n type CreateAudioPlayerOptions,\n} from \"./index\";\n\nconst AudioPlayerContext = createContext<AudioPlayer | null>(null);\n\nexport interface AudioPlayerProviderProps {\n children: ReactNode;\n options?: CreateAudioPlayerOptions;\n player?: AudioPlayer;\n}\n\nexport const AudioPlayerProvider = ({\n children,\n options,\n player,\n}: AudioPlayerProviderProps) => {\n const playerRef = useRef<AudioPlayer | null>(player ?? null);\n\n if (playerRef.current === null) {\n playerRef.current = createAudioPlayer(options);\n }\n\n const mountedRef = useRef(false);\n\n useEffect(() => {\n if (player) {\n return undefined;\n }\n\n const owned = playerRef.current;\n mountedRef.current = true;\n\n return () => {\n mountedRef.current = false;\n // StrictMode re-runs effects right after cleanup, so only destroy if not re-mounted\n queueMicrotask(() => {\n if (!mountedRef.current) {\n owned?.destroy();\n }\n });\n };\n }, [player]);\n\n return (\n <AudioPlayerContext.Provider value={playerRef.current}>\n {children}\n </AudioPlayerContext.Provider>\n );\n};\n\nexport const useAudioPlayer = (): AudioPlayer => {\n const player = useContext(AudioPlayerContext);\n\n if (!player) {\n throw new Error(\"useAudioPlayer must be used inside AudioPlayerProvider.\");\n }\n\n return player;\n};\n\nexport const useAudioPlayerState = (): AudioPlayerState => {\n const player = useAudioPlayer();\n\n return useSyncExternalStore(\n player.subscribe,\n player.getState,\n player.getState,\n );\n};\n"],"mappings":";;;;;AAAA;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAiDH;AAzCJ,IAAM,qBAAqB,cAAkC,IAAI;AAQ1D,IAAM,sBAAsB,CAAC;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AACF,MAAgC;AAC9B,QAAM,YAAY,OAA2B,UAAU,IAAI;AAE3D,MAAI,UAAU,YAAY,MAAM;AAC9B,cAAU,UAAU,kBAAkB,OAAO;AAAA,EAC/C;AAEA,QAAM,aAAa,OAAO,KAAK;AAE/B,YAAU,MAAM;AACd,QAAI,QAAQ;AACV,aAAO;AAAA,IACT;AAEA,UAAM,QAAQ,UAAU;AACxB,eAAW,UAAU;AAErB,WAAO,MAAM;AACX,iBAAW,UAAU;AAErB,qBAAe,MAAM;AACnB,YAAI,CAAC,WAAW,SAAS;AACvB,iBAAO,QAAQ;AAAA,QACjB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAEX,SACE,oBAAC,mBAAmB,UAAnB,EAA4B,OAAO,UAAU,SAC3C,UACH;AAEJ;AAEO,IAAM,iBAAiB,MAAmB;AAC/C,QAAM,SAAS,WAAW,kBAAkB;AAE5C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AAEA,SAAO;AACT;AAEO,IAAM,sBAAsB,MAAwB;AACzD,QAAM,SAAS,eAAe;AAE9B,SAAO;AAAA,IACL,OAAO;AAAA,IACP,OAAO;AAAA,IACP,OAAO;AAAA,EACT;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/react.tsx"],"sourcesContent":["import {\n createContext,\n type ReactNode,\n useContext,\n useEffect,\n useRef,\n useSyncExternalStore,\n} from \"react\";\nimport {\n createAudioPlayer,\n type AudioPlayer,\n type AudioPlayerState,\n type CreateAudioPlayerOptions,\n} from \"./index.js\";\n\nconst AudioPlayerContext = createContext<AudioPlayer | null>(null);\n\nexport interface AudioPlayerProviderProps {\n children: ReactNode;\n options?: CreateAudioPlayerOptions;\n player?: AudioPlayer;\n}\n\nexport const AudioPlayerProvider = ({\n children,\n options,\n player,\n}: AudioPlayerProviderProps) => {\n const playerRef = useRef<AudioPlayer | null>(player ?? null);\n\n if (playerRef.current === null) {\n playerRef.current = createAudioPlayer(options);\n }\n\n const mountedRef = useRef(false);\n\n useEffect(() => {\n if (player) {\n return undefined;\n }\n\n const owned = playerRef.current;\n mountedRef.current = true;\n\n return () => {\n mountedRef.current = false;\n // StrictMode re-runs effects right after cleanup, so only destroy if not re-mounted\n queueMicrotask(() => {\n if (!mountedRef.current) {\n owned?.destroy();\n }\n });\n };\n }, [player]);\n\n return (\n <AudioPlayerContext.Provider value={playerRef.current}>\n {children}\n </AudioPlayerContext.Provider>\n );\n};\n\nexport const useAudioPlayer = (): AudioPlayer => {\n const player = useContext(AudioPlayerContext);\n\n if (!player) {\n throw new Error(\"useAudioPlayer must be used inside AudioPlayerProvider.\");\n }\n\n return player;\n};\n\nexport const useAudioPlayerState = (): AudioPlayerState => {\n const player = useAudioPlayer();\n\n return useSyncExternalStore(\n player.subscribe,\n player.getState,\n player.getState,\n );\n};\n"],"mappings":";;;;;AAAA;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAiDH;AAzCJ,IAAM,qBAAqB,cAAkC,IAAI;AAQ1D,IAAM,sBAAsB,CAAC;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AACF,MAAgC;AAC9B,QAAM,YAAY,OAA2B,UAAU,IAAI;AAE3D,MAAI,UAAU,YAAY,MAAM;AAC9B,cAAU,UAAU,kBAAkB,OAAO;AAAA,EAC/C;AAEA,QAAM,aAAa,OAAO,KAAK;AAE/B,YAAU,MAAM;AACd,QAAI,QAAQ;AACV,aAAO;AAAA,IACT;AAEA,UAAM,QAAQ,UAAU;AACxB,eAAW,UAAU;AAErB,WAAO,MAAM;AACX,iBAAW,UAAU;AAErB,qBAAe,MAAM;AACnB,YAAI,CAAC,WAAW,SAAS;AACvB,iBAAO,QAAQ;AAAA,QACjB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAEX,SACE,oBAAC,mBAAmB,UAAnB,EAA4B,OAAO,UAAU,SAC3C,UACH;AAEJ;AAEO,IAAM,iBAAiB,MAAmB;AAC/C,QAAM,SAAS,WAAW,kBAAkB;AAE5C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AAEA,SAAO;AACT;AAEO,IAAM,sBAAsB,MAAwB;AACzD,QAAM,SAAS,eAAe;AAE9B,SAAO;AAAA,IACL,OAAO;AAAA,IACP,OAAO;AAAA,IACP,OAAO;AAAA,EACT;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yetric/audioplayer",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Headless TypeScript audio player library for modern browsers.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -71,13 +71,25 @@
71
71
  "devDependencies": {
72
72
  "@types/react": "^19.2.14",
73
73
  "@types/react-dom": "^19.3.0",
74
- "jsdom": "^30.1.2",
75
- "react": "^19.2.4",
76
- "react-dom": "^19.3.0",
77
- "tsup": "^8.5.1",
78
- "typescript": "^6.0.2",
79
- "vite": "^8.0.3",
80
- "vitest": "^4.1.4"
74
+ "jsdom": "^30.1.3",
75
+ "react": "^19.3.1",
76
+ "react-dom": "^19.3.1",
77
+ "tsup": "^9.0.0",
78
+ "typescript": "^7.0.2",
79
+ "vite": "^8.3.4",
80
+ "vitest": "^5.0.4"
81
+ },
82
+ "overrides": {
83
+ "@docusaurus/core": {
84
+ "tinypool": "^2.2.0"
85
+ },
86
+ "serialize-javascript": "^7.1.4",
87
+ "express": {
88
+ "qs": "^6.16.2"
89
+ },
90
+ "sockjs": {
91
+ "uuid": "^11.1.1"
92
+ }
81
93
  },
82
94
  "peerDependencies": {
83
95
  "react": ">=18"
package/dist/index.d.cts DELETED
@@ -1,208 +0,0 @@
1
- type PlayerStatus = "idle" | "loading" | "ready" | "playing" | "paused" | "ended" | "error";
2
- type RepeatMode = "off" | "one" | "all";
3
- type AudioPlayerEventName = "sourcechange" | "queuechange" | "play" | "pause" | "seeking" | "seeked" | "timeupdate" | "ratechange" | "volumechange" | "ended" | "error" | "destroy";
4
- interface AudioSource {
5
- id: string;
6
- src: string;
7
- title?: string;
8
- artist?: string;
9
- artwork?: string;
10
- album?: string;
11
- durationHint?: number;
12
- }
13
- interface AudioPlayerErrorState {
14
- code: "LOAD_ERROR" | "PLAY_ERROR" | "SEEK_ERROR" | "INVALID_VOLUME" | "INVALID_RATE" | "INVALID_QUEUE_ITEM" | "NO_ACTIVE_SOURCE" | "UNSUPPORTED_ENVIRONMENT" | "UNKNOWN_ERROR";
15
- message: string;
16
- }
17
- interface AudioPlayerRuntimeError extends AudioPlayerErrorState {
18
- cause?: unknown;
19
- }
20
- interface AudioPlayerProgressState {
21
- currentTime: number;
22
- duration: number;
23
- buffered: number;
24
- playedFraction: number;
25
- bufferedFraction: number;
26
- }
27
- interface AudioPlayerQueuePositionState {
28
- currentIndex: number;
29
- currentSourceId: string | null;
30
- length: number;
31
- hasNext: boolean;
32
- hasPrevious: boolean;
33
- repeatMode: RepeatMode;
34
- }
35
- interface AudioPlayerQueueState {
36
- items: AudioSource[];
37
- itemIds: string[];
38
- position: AudioPlayerQueuePositionState;
39
- }
40
- interface AudioPlayerState {
41
- status: PlayerStatus;
42
- currentSource: AudioSource | null;
43
- currentSourceId: string | null;
44
- currentTime: number;
45
- duration: number;
46
- buffered: number;
47
- progress: AudioPlayerProgressState;
48
- rate: number;
49
- volume: number;
50
- muted: boolean;
51
- queue: AudioPlayerQueueState;
52
- error: AudioPlayerErrorState | null;
53
- }
54
- interface AudioPlayerEventMap {
55
- sourcechange: {
56
- source: AudioSource | null;
57
- state: AudioPlayerState;
58
- };
59
- queuechange: {
60
- queue: AudioPlayerQueueState;
61
- state: AudioPlayerState;
62
- };
63
- play: {
64
- source: AudioSource | null;
65
- state: AudioPlayerState;
66
- };
67
- pause: {
68
- source: AudioSource | null;
69
- state: AudioPlayerState;
70
- };
71
- seeking: {
72
- from: number;
73
- to: number;
74
- state: AudioPlayerState;
75
- };
76
- seeked: {
77
- currentTime: number;
78
- state: AudioPlayerState;
79
- };
80
- timeupdate: {
81
- currentTime: number;
82
- duration: number;
83
- state: AudioPlayerState;
84
- };
85
- ratechange: {
86
- rate: number;
87
- state: AudioPlayerState;
88
- };
89
- volumechange: {
90
- volume: number;
91
- muted: boolean;
92
- state: AudioPlayerState;
93
- };
94
- ended: {
95
- source: AudioSource | null;
96
- state: AudioPlayerState;
97
- };
98
- error: {
99
- error: AudioPlayerRuntimeError;
100
- state: AudioPlayerState;
101
- };
102
- destroy: {
103
- state: AudioPlayerState;
104
- };
105
- }
106
- interface CreateAudioPlayerOptions {
107
- initialVolume?: number;
108
- initialRate?: number;
109
- initialMuted?: boolean;
110
- initialRepeatMode?: RepeatMode;
111
- }
112
- interface SetQueueOptions {
113
- startAtId?: string;
114
- autoplay?: boolean;
115
- }
116
- interface PreviousOptions {
117
- restartThresholdSeconds?: number;
118
- }
119
- interface PersistedPlayerState {
120
- version: 1;
121
- savedAt: number;
122
- currentSource: AudioSource | null;
123
- currentSourceId: string | null;
124
- currentTime: number;
125
- rate: number;
126
- volume: number;
127
- muted: boolean;
128
- queue: {
129
- items: AudioSource[];
130
- position: {
131
- currentIndex: number;
132
- currentSourceId: string | null;
133
- repeatMode: RepeatMode;
134
- };
135
- };
136
- }
137
- interface AudioPlayerPersistenceAdapter {
138
- load(): PersistedPlayerState | null;
139
- save(state: PersistedPlayerState): void;
140
- clear(): void;
141
- }
142
- interface AudioPlayerPersistenceOptions {
143
- adapter: AudioPlayerPersistenceAdapter;
144
- autoRestore?: boolean;
145
- maxAgeMs?: number;
146
- saveIntervalMs?: number;
147
- }
148
- interface AudioPlayerPersistenceController {
149
- restore(): Promise<PersistedPlayerState | null>;
150
- save(): void;
151
- clear(): void;
152
- destroy(): void;
153
- }
154
- interface AudioPlayerProgressSnapshot {
155
- source: AudioSource | null;
156
- sourceId: string | null;
157
- currentTime: number;
158
- duration: number;
159
- playedFraction: number;
160
- status: PlayerStatus;
161
- queue: AudioPlayerQueuePositionState;
162
- }
163
- interface AudioPlayerProgressReporterOptions {
164
- intervalMs?: number;
165
- onReport: (snapshot: AudioPlayerProgressSnapshot) => void;
166
- }
167
- interface AudioPlayerProgressReporterController {
168
- report(): void;
169
- destroy(): void;
170
- }
171
- interface AudioPlayerMediaSessionOptions {
172
- artworkSizes?: number[];
173
- seekOffsetSeconds?: number;
174
- }
175
- interface AudioPlayerMediaSessionController {
176
- sync(): void;
177
- destroy(): void;
178
- }
179
- type AudioPlayerEventListener<K extends AudioPlayerEventName> = (event: AudioPlayerEventMap[K]) => void;
180
- interface AudioPlayer {
181
- getState(): AudioPlayerState;
182
- subscribe(listener: (state: AudioPlayerState) => void): () => void;
183
- on<K extends AudioPlayerEventName>(eventName: K, listener: AudioPlayerEventListener<K>): () => void;
184
- load(source: AudioSource): Promise<void>;
185
- play(source?: AudioSource): Promise<void>;
186
- pause(): void;
187
- toggle(): Promise<void>;
188
- unload(): void;
189
- seek(time: number): void;
190
- setRate(rate: number): void;
191
- setVolume(volume: number): void;
192
- setMuted(muted: boolean): void;
193
- setQueue(items: AudioSource[], options?: SetQueueOptions): Promise<void>;
194
- appendToQueue(items: AudioSource[]): void;
195
- removeFromQueue(id: string): void;
196
- clearQueue(): void;
197
- next(): Promise<void>;
198
- previous(options?: PreviousOptions): Promise<void>;
199
- setRepeatMode(mode: RepeatMode): void;
200
- destroy(): void;
201
- }
202
- declare const createLocalStoragePersistenceAdapter: (key: string) => AudioPlayerPersistenceAdapter;
203
- declare const createAudioPlayer: (options?: CreateAudioPlayerOptions) => AudioPlayer;
204
- declare const attachAudioPlayerPersistence: (player: AudioPlayer, options: AudioPlayerPersistenceOptions) => AudioPlayerPersistenceController;
205
- declare const attachAudioPlayerProgressReporter: (player: AudioPlayer, options: AudioPlayerProgressReporterOptions) => AudioPlayerProgressReporterController;
206
- declare const attachAudioPlayerMediaSession: (player: AudioPlayer, options?: AudioPlayerMediaSessionOptions) => AudioPlayerMediaSessionController;
207
-
208
- export { type AudioPlayer, type AudioPlayerErrorState, type AudioPlayerEventListener, type AudioPlayerEventMap, type AudioPlayerEventName, type AudioPlayerMediaSessionController, type AudioPlayerMediaSessionOptions, type AudioPlayerPersistenceAdapter, type AudioPlayerPersistenceController, type AudioPlayerPersistenceOptions, type AudioPlayerProgressReporterController, type AudioPlayerProgressReporterOptions, type AudioPlayerProgressSnapshot, type AudioPlayerProgressState, type AudioPlayerQueuePositionState, type AudioPlayerQueueState, type AudioPlayerRuntimeError, type AudioPlayerState, type AudioSource, type CreateAudioPlayerOptions, type PersistedPlayerState, type PlayerStatus, type PreviousOptions, type RepeatMode, type SetQueueOptions, attachAudioPlayerMediaSession, attachAudioPlayerPersistence, attachAudioPlayerProgressReporter, createAudioPlayer, createLocalStoragePersistenceAdapter };
package/dist/react.d.cts DELETED
@@ -1,14 +0,0 @@
1
- import * as react from 'react';
2
- import { ReactNode } from 'react';
3
- import { CreateAudioPlayerOptions, AudioPlayer, AudioPlayerState } from './index.cjs';
4
-
5
- interface AudioPlayerProviderProps {
6
- children: ReactNode;
7
- options?: CreateAudioPlayerOptions;
8
- player?: AudioPlayer;
9
- }
10
- declare const AudioPlayerProvider: ({ children, options, player, }: AudioPlayerProviderProps) => react.JSX.Element;
11
- declare const useAudioPlayer: () => AudioPlayer;
12
- declare const useAudioPlayerState: () => AudioPlayerState;
13
-
14
- export { AudioPlayerProvider, type AudioPlayerProviderProps, useAudioPlayer, useAudioPlayerState };