expo-gliph-player 1.3.13 → 1.3.15

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.
@@ -8,11 +8,11 @@
8
8
  import { NativeEventEmitter, Platform } from 'react-native';
9
9
  import NativeGliphPlayer from './specs/NativeGliphPlayer';
10
10
  import type {
11
- Track,
12
- PlayerOptions,
13
- UpdateOptions,
14
- Progress,
15
- PlaybackState,
11
+ Track,
12
+ PlayerOptions,
13
+ UpdateOptions,
14
+ Progress,
15
+ PlaybackState,
16
16
  } from './types';
17
17
  import { RepeatMode, State } from './types';
18
18
 
@@ -21,318 +21,325 @@ import { RepeatMode, State } from './types';
21
21
  let _emitter: NativeEventEmitter | null = null;
22
22
 
23
23
  function getEmitter(): NativeEventEmitter {
24
- if (!NativeGliphPlayer) {
25
- throw new Error('[RNGliphPlayer] Native module not found. Is it linked correctly?');
26
- }
27
- if (!_emitter) {
28
- _emitter = new NativeEventEmitter(NativeGliphPlayer as any);
29
- }
30
- return _emitter;
24
+ if (!NativeGliphPlayer) {
25
+ throw new Error('[RNGliphPlayer] Native module not found. Is it linked correctly?');
26
+ }
27
+ if (!_emitter) {
28
+ _emitter = new NativeEventEmitter(NativeGliphPlayer as any);
29
+ }
30
+ return _emitter;
31
31
  }
32
32
 
33
33
  /** Resolve a require()'d asset to a URI string the native side can consume */
34
34
  function resolveAssetSource(source: string | number): string {
35
- if (typeof source === 'number') {
35
+ if (typeof source === 'number') {
36
36
 
37
- const { Image } = require('react-native');
38
- return Image.resolveAssetSource(source).uri as string;
39
- }
40
- return source;
37
+ const { Image } = require('react-native');
38
+ return Image.resolveAssetSource(source).uri as string;
39
+ }
40
+ return source;
41
41
  }
42
42
 
43
43
  /** Serialize a Track to a plain object safe for the TurboModule bridge */
44
44
  function serializeTrack(track: Track): Record<string, unknown> {
45
- return {
46
- id: track.id ?? `track_${Date.now()}_${Math.random().toString(36).slice(2)}`,
47
- url: resolveAssetSource(track.url as string | number),
48
- title: track.title,
49
- artist: track.artist,
50
- album: track.album ?? '',
51
- artwork: track.artwork != null ? resolveAssetSource(track.artwork as string | number) : '',
52
- duration: track.duration ?? -1,
53
- genre: track.genre ?? '',
54
- date: track.date ?? '',
55
- description: track.description ?? '',
56
- rating: track.rating ?? 0,
57
- isLiveStream: track.isLiveStream ?? false,
58
- headers: track.headers ?? {},
59
- pitchAlgorithm: track.pitchAlgorithm ?? 0,
60
- userAgent: track.userAgent ?? '',
61
- };
45
+ return {
46
+ id: track.id ?? `track_${Date.now()}_${Math.random().toString(36).slice(2)}`,
47
+ url: resolveAssetSource(track.url as string | number),
48
+ title: track.title,
49
+ artist: track.artist,
50
+ album: track.album ?? '',
51
+ artwork: track.artwork != null ? resolveAssetSource(track.artwork as string | number) : '',
52
+ duration: track.duration ?? -1,
53
+ genre: track.genre ?? '',
54
+ date: track.date ?? '',
55
+ description: track.description ?? '',
56
+ rating: track.rating ?? 0,
57
+ isLiveStream: track.isLiveStream ?? false,
58
+ headers: track.headers ?? {},
59
+ pitchAlgorithm: track.pitchAlgorithm ?? 0,
60
+ userAgent: track.userAgent ?? '',
61
+ };
62
62
  }
63
63
 
64
64
  function checkModule(): NonNullable<typeof NativeGliphPlayer> {
65
- if (!NativeGliphPlayer) {
66
- throw new Error(
67
- '[RNGliphPlayer] Native module not found. ' +
68
- 'Ensure you have rebuilt the app after installing the library and ' +
69
- 'that you are not calling player methods before the JSI bridge is ready.'
70
- );
71
- }
72
- return NativeGliphPlayer;
65
+ if (!NativeGliphPlayer) {
66
+ throw new Error(
67
+ '[RNGliphPlayer] Native module not found. ' +
68
+ 'Ensure you have rebuilt the app after installing the library and ' +
69
+ 'that you are not calling player methods before the JSI bridge is ready.'
70
+ );
71
+ }
72
+ return NativeGliphPlayer;
73
73
  }
74
74
 
75
75
  // ─── GliphPlayer API ─────────────────────────────────────────────────────────
76
76
 
77
77
  const GliphPlayer = {
78
- // ── Setup ──────────────────────────────────────────────────────────────────
79
-
80
- /**
81
- * Initialize the player. Must be called once before any other method.
82
- * Safe to call multiple times — subsequent calls are no-ops.
83
- */
84
- async setupPlayer(options: PlayerOptions = {}): Promise<void> {
85
- if (!NativeGliphPlayer) {
86
- throw new Error('[RNGliphPlayer] Native module not ready. Ensure setup is called after app initialization.');
87
- }
88
- const opts: Record<string, unknown> = {
89
- minBuffer: options.minBuffer ?? 15,
90
- maxBuffer: options.maxBuffer ?? 50,
91
- playBuffer: options.playBuffer ?? 2.5,
92
- backBuffer: options.backBuffer ?? 0,
93
- maxCacheSize: options.maxCacheSize ?? 0,
94
- iosCategory: options.iosCategory ?? 'playback',
95
- iosCategoryMode: options.iosCategoryMode ?? 'default',
96
- iosCategoryOptions: options.iosCategoryOptions ?? [],
97
- waitForBuffer: options.waitForBuffer ?? true,
98
- autoHandleInterruptions: options.autoHandleInterruptions ?? false,
99
- autoUpdateMetadata: options.autoUpdateMetadata ?? true,
100
- progressUpdateEventInterval: options.progressUpdateEventInterval ?? 1.0,
101
- android: options.android ?? {},
102
- };
103
- return NativeGliphPlayer.setupPlayer(opts);
104
- },
105
-
106
- /**
107
- * Destroy the player and release all resources.
108
- */
109
- destroy(): void {
110
- checkModule().destroy();
111
- _emitter = null;
112
- },
113
-
114
- /**
115
- * Returns true if the background service is running (Android).
116
- */
117
- async isServiceRunning(): Promise<boolean> {
118
- if (Platform.OS !== 'android') {return true;}
119
- return checkModule().isServiceRunning();
120
- },
121
-
122
- // ── Queue management ───────────────────────────────────────────────────────
123
-
124
- /**
125
- * Add one or more tracks to the queue.
126
- * @param tracks Single track or array of tracks.
127
- * @param insertBeforeIndex Insert position. Defaults to end of queue.
128
- * @returns The index of the first inserted track.
129
- */
130
- async add(
131
- tracks: Track | Track[],
132
- insertBeforeIndex?: number
133
- ): Promise<number | void> {
134
- const arr = Array.isArray(tracks) ? tracks : [tracks];
135
- const serialized = arr.map(serializeTrack);
136
- // Pass -1 as sentinel for "append to end" — Codegen doesn't support optional number params
137
- const result = await checkModule().add(serialized, insertBeforeIndex ?? -1);
138
- return result;
139
- },
140
-
141
- /**
142
- * Remove tracks by their IDs.
143
- */
144
- async remove(trackIds: string | string[]): Promise<void> {
145
- const ids = Array.isArray(trackIds) ? trackIds : [trackIds];
146
- return checkModule().remove(ids);
147
- },
148
-
149
- /**
150
- * Remove all tracks after the current one.
151
- */
152
- async removeUpcomingTracks(): Promise<void> {
153
- return checkModule().removeUpcomingTracks();
154
- },
155
-
156
- /**
157
- * Skip to a specific queue index.
158
- */
159
- async skip(index: number, initialPosition?: number): Promise<void> {
160
- return checkModule().skip(index, initialPosition ?? -1);
161
- },
162
-
163
- /**
164
- * Skip to the next track.
165
- */
166
- async skipToNext(initialPosition?: number): Promise<void> {
167
- return checkModule().skipToNext(initialPosition ?? -1);
168
- },
169
-
170
- /**
171
- * Skip to the previous track.
172
- */
173
- async skipToPrevious(initialPosition?: number): Promise<void> {
174
- return checkModule().skipToPrevious(initialPosition ?? -1);
175
- },
176
-
177
- /**
178
- * Move a track from one index to another.
179
- */
180
- async move(fromIndex: number, toIndex: number): Promise<void> {
181
- return checkModule().move(fromIndex, toIndex);
182
- },
183
-
184
- // ── Playback control ───────────────────────────────────────────────────────
185
-
186
- async play(): Promise<void> {
187
- return checkModule().play();
188
- },
189
-
190
- async pause(): Promise<void> {
191
- return checkModule().pause();
192
- },
193
-
194
- async stop(): Promise<void> {
195
- return checkModule().stop();
196
- },
197
-
198
- /**
199
- * Stop playback and clear the queue.
200
- */
201
- async reset(): Promise<void> {
202
- return checkModule().reset();
203
- },
204
-
205
- /**
206
- * Seek to an absolute position in seconds.
207
- */
208
- async seekTo(position: number): Promise<void> {
209
- return checkModule().seekTo(position);
210
- },
211
-
212
- /**
213
- * Seek by a relative offset in seconds (positive = forward, negative = back).
214
- */
215
- async seekBy(offset: number): Promise<void> {
216
- return checkModule().seekBy(offset);
217
- },
218
-
219
- /**
220
- * Set volume (0.0 – 1.0).
221
- */
222
- async setVolume(volume: number): Promise<void> {
223
- if (volume < 0 || volume > 1) {throw new Error('Volume must be between 0 and 1');}
224
- return checkModule().setVolume(volume);
225
- },
226
-
227
- async getVolume(): Promise<number> {
228
- return checkModule().getVolume();
229
- },
230
-
231
- /**
232
- * Set playback rate (1.0 = normal speed).
233
- */
234
- async setRate(rate: number): Promise<void> {
235
- return checkModule().setRate(rate);
236
- },
237
-
238
- async getRate(): Promise<number> {
239
- return checkModule().getRate();
240
- },
241
-
242
- async setRepeatMode(mode: RepeatMode): Promise<void> {
243
- return checkModule().setRepeatMode(mode);
244
- },
245
-
246
- async getRepeatMode(): Promise<RepeatMode> {
247
- const mode = await checkModule().getRepeatMode();
248
- return mode as RepeatMode;
249
- },
250
-
251
- // ── Queue getters ──────────────────────────────────────────────────────────
252
-
253
- async getQueue(): Promise<Track[]> {
254
- const queue = await checkModule().getQueue();
255
- return queue as Track[];
256
- },
257
-
258
- async getActiveTrackIndex(): Promise<number | null> {
259
- const idx = await checkModule().getActiveTrackIndex();
260
- // Native returns -1 as sentinel when no track is active
261
- return (idx as number) < 0 ? null : (idx as number);
262
- },
263
-
264
- async getActiveTrack(): Promise<Track | null> {
265
- const track = await checkModule().getActiveTrack();
266
- return track as Track | null;
267
- },
268
-
269
- async getTrack(index: number): Promise<Track | null> {
270
- const track = await checkModule().getTrack(index);
271
- return track as Track | null;
272
- },
273
-
274
- async getQueueSize(): Promise<number> {
275
- return checkModule().getQueueSize();
276
- },
277
-
278
- // ── State / progress ───────────────────────────────────────────────────────
279
-
280
- async getPlaybackState(): Promise<PlaybackState> {
281
- const state = await checkModule().getPlaybackState();
282
- return state as PlaybackState;
283
- },
284
-
285
- async getProgress(): Promise<Progress> {
286
- const progress = await checkModule().getProgress();
287
- return progress as Progress;
288
- },
289
-
290
- // ── Metadata ───────────────────────────────────────────────────────────────
291
-
292
- async updateMetadataForTrack(
293
- index: number,
294
- metadata: Partial<Track>
295
- ): Promise<void> {
296
- return checkModule().updateMetadataForTrack(index, metadata as Record<string, unknown>);
297
- },
298
-
299
- async clearNowPlayingMetadata(): Promise<void> {
300
- return checkModule().clearNowPlayingMetadata();
301
- },
302
-
303
- async updateNowPlayingMetadata(metadata: Partial<Track>): Promise<void> {
304
- return checkModule().updateNowPlayingMetadata(metadata as Record<string, unknown>);
305
- },
306
-
307
- // ── Options / notification ─────────────────────────────────────────────────
308
-
309
- /**
310
- * Configure notification capabilities, jump intervals, etc.
311
- */
312
- async updateOptions(options: UpdateOptions): Promise<void> {
313
- return checkModule().updateOptions(options as Record<string, unknown>);
314
- },
315
-
316
- // ── Event subscription ─────────────────────────────────────────────────────
317
-
318
- /**
319
- * Subscribe to a player event.
320
- * Returns an unsubscribe function — call it to remove the listener.
321
- *
322
- * @example
323
- * const unsub = GliphPlayer.addEventListener(Event.PlaybackState, ({ state }) => {
324
- * console.log('State:', state);
325
- * });
326
- * // later:
327
- * unsub();
328
- */
329
- addEventListener<E extends string>(
330
- event: E,
331
- listener: (data: any) => void
332
- ): () => void {
333
- const subscription = getEmitter().addListener(event, listener);
334
- return () => subscription.remove();
335
- },
78
+ // ── Setup ──────────────────────────────────────────────────────────────────
79
+
80
+ /**
81
+ * Initialize the player. Must be called once before any other method.
82
+ * Safe to call multiple times — subsequent calls are no-ops.
83
+ */
84
+ async setupPlayer(options: PlayerOptions = {}): Promise<void> {
85
+ if (!NativeGliphPlayer) {
86
+ throw new Error('[RNGliphPlayer] Native module not ready. Ensure setup is called after app initialization.');
87
+ }
88
+ const opts: Record<string, unknown> = {
89
+ minBuffer: options.minBuffer ?? 15,
90
+ maxBuffer: options.maxBuffer ?? 50,
91
+ playBuffer: options.playBuffer ?? 2.5,
92
+ backBuffer: options.backBuffer ?? 0,
93
+ maxCacheSize: options.maxCacheSize ?? 0,
94
+ iosCategory: options.iosCategory ?? 'playback',
95
+ iosCategoryMode: options.iosCategoryMode ?? 'default',
96
+ iosCategoryOptions: options.iosCategoryOptions ?? [],
97
+ waitForBuffer: options.waitForBuffer ?? true,
98
+ autoHandleInterruptions: options.autoHandleInterruptions ?? false,
99
+ autoUpdateMetadata: options.autoUpdateMetadata ?? true,
100
+ progressUpdateEventInterval: options.progressUpdateEventInterval ?? 1.0,
101
+ android: options.android ?? {},
102
+ };
103
+ return NativeGliphPlayer.setupPlayer(opts);
104
+ },
105
+
106
+ /**
107
+ * Destroy the player and release all resources.
108
+ */
109
+ destroy(): void {
110
+ checkModule().destroy();
111
+ _emitter = null;
112
+ },
113
+
114
+ /**
115
+ * Returns true if the background service is running (Android).
116
+ */
117
+ async isServiceRunning(): Promise<boolean> {
118
+ if (Platform.OS !== 'android') {return true;}
119
+ return checkModule().isServiceRunning();
120
+ },
121
+
122
+ // ── Queue management ───────────────────────────────────────────────────────
123
+
124
+ /**
125
+ * Add one or more tracks to the queue.
126
+ * @param tracks Single track or array of tracks.
127
+ * @param insertBeforeIndex Insert position. Defaults to end of queue.
128
+ * @returns The index of the first inserted track.
129
+ */
130
+ async add(
131
+ tracks: Track | Track[],
132
+ insertBeforeIndex?: number
133
+ ): Promise<number | void> {
134
+ const arr = Array.isArray(tracks) ? tracks : [tracks];
135
+ const serialized = arr.map(serializeTrack);
136
+ // Pass -1 as sentinel for "append to end" — Codegen doesn't support optional number params
137
+ const result = await checkModule().add(serialized, insertBeforeIndex ?? -1);
138
+ return result;
139
+ },
140
+
141
+ /**
142
+ * Remove tracks by their IDs.
143
+ */
144
+ async remove(trackIds: string | string[]): Promise<void> {
145
+ const ids = Array.isArray(trackIds) ? trackIds : [trackIds];
146
+ return checkModule().remove(ids);
147
+ },
148
+
149
+ /**
150
+ * Remove all tracks after the current one.
151
+ */
152
+ async removeUpcomingTracks(): Promise<void> {
153
+ return checkModule().removeUpcomingTracks();
154
+ },
155
+
156
+ /**
157
+ * Skip to a specific queue index.
158
+ * @param index - Index of the track to skip to
159
+ * @param initialPosition - Position in seconds to start from (default: -1 = start)
160
+ * @param autoPlay - Whether to automatically start playback (default: true)
161
+ */
162
+ async skip(index: number, initialPosition?: number, autoPlay: boolean = true): Promise<void> {
163
+ return checkModule().skip(index, initialPosition ?? -1, autoPlay);
164
+ },
165
+
166
+ /**
167
+ * Skip to the next track.
168
+ * @param initialPosition - Position in seconds to start from (default: -1 = start)
169
+ * @param autoPlay - Whether to automatically start playback (default: true)
170
+ */
171
+ async skipToNext(initialPosition?: number, autoPlay: boolean = true): Promise<void> {
172
+ return checkModule().skipToNext(initialPosition ?? -1, autoPlay);
173
+ },
174
+
175
+ /**
176
+ * Skip to the previous track.
177
+ * @param initialPosition - Position in seconds to start from (default: -1 = start)
178
+ * @param autoPlay - Whether to automatically start playback (default: true)
179
+ */
180
+ async skipToPrevious(initialPosition?: number, autoPlay: boolean = true): Promise<void> {
181
+ return checkModule().skipToPrevious(initialPosition ?? -1, autoPlay);
182
+ },
183
+
184
+ /**
185
+ * Move a track from one index to another.
186
+ */
187
+ async move(fromIndex: number, toIndex: number): Promise<void> {
188
+ return checkModule().move(fromIndex, toIndex);
189
+ },
190
+
191
+ // ── Playback control ───────────────────────────────────────────────────────
192
+
193
+ async play(): Promise<void> {
194
+ return checkModule().play();
195
+ },
196
+
197
+ async pause(): Promise<void> {
198
+ return checkModule().pause();
199
+ },
200
+
201
+ async stop(): Promise<void> {
202
+ return checkModule().stop();
203
+ },
204
+
205
+ /**
206
+ * Stop playback and clear the queue.
207
+ */
208
+ async reset(): Promise<void> {
209
+ return checkModule().reset();
210
+ },
211
+
212
+ /**
213
+ * Seek to an absolute position in seconds.
214
+ */
215
+ async seekTo(position: number): Promise<void> {
216
+ return checkModule().seekTo(position);
217
+ },
218
+
219
+ /**
220
+ * Seek by a relative offset in seconds (positive = forward, negative = back).
221
+ */
222
+ async seekBy(offset: number): Promise<void> {
223
+ return checkModule().seekBy(offset);
224
+ },
225
+
226
+ /**
227
+ * Set volume (0.0 – 1.0).
228
+ */
229
+ async setVolume(volume: number): Promise<void> {
230
+ if (volume < 0 || volume > 1) {throw new Error('Volume must be between 0 and 1');}
231
+ return checkModule().setVolume(volume);
232
+ },
233
+
234
+ async getVolume(): Promise<number> {
235
+ return checkModule().getVolume();
236
+ },
237
+
238
+ /**
239
+ * Set playback rate (1.0 = normal speed).
240
+ */
241
+ async setRate(rate: number): Promise<void> {
242
+ return checkModule().setRate(rate);
243
+ },
244
+
245
+ async getRate(): Promise<number> {
246
+ return checkModule().getRate();
247
+ },
248
+
249
+ async setRepeatMode(mode: RepeatMode): Promise<void> {
250
+ return checkModule().setRepeatMode(mode);
251
+ },
252
+
253
+ async getRepeatMode(): Promise<RepeatMode> {
254
+ const mode = await checkModule().getRepeatMode();
255
+ return mode as RepeatMode;
256
+ },
257
+
258
+ // ── Queue getters ──────────────────────────────────────────────────────────
259
+
260
+ async getQueue(): Promise<Track[]> {
261
+ const queue = await checkModule().getQueue();
262
+ return queue as Track[];
263
+ },
264
+
265
+ async getActiveTrackIndex(): Promise<number | null> {
266
+ const idx = await checkModule().getActiveTrackIndex();
267
+ // Native returns -1 as sentinel when no track is active
268
+ return (idx as number) < 0 ? null : (idx as number);
269
+ },
270
+
271
+ async getActiveTrack(): Promise<Track | null> {
272
+ const track = await checkModule().getActiveTrack();
273
+ return track as Track | null;
274
+ },
275
+
276
+ async getTrack(index: number): Promise<Track | null> {
277
+ const track = await checkModule().getTrack(index);
278
+ return track as Track | null;
279
+ },
280
+
281
+ async getQueueSize(): Promise<number> {
282
+ return checkModule().getQueueSize();
283
+ },
284
+
285
+ // ── State / progress ───────────────────────────────────────────────────────
286
+
287
+ async getPlaybackState(): Promise<PlaybackState> {
288
+ const state = await checkModule().getPlaybackState();
289
+ return state as PlaybackState;
290
+ },
291
+
292
+ async getProgress(): Promise<Progress> {
293
+ const progress = await checkModule().getProgress();
294
+ return progress as Progress;
295
+ },
296
+
297
+ // ── Metadata ───────────────────────────────────────────────────────────────
298
+
299
+ async updateMetadataForTrack(
300
+ index: number,
301
+ metadata: Partial<Track>
302
+ ): Promise<void> {
303
+ return checkModule().updateMetadataForTrack(index, metadata as Record<string, unknown>);
304
+ },
305
+
306
+ async clearNowPlayingMetadata(): Promise<void> {
307
+ return checkModule().clearNowPlayingMetadata();
308
+ },
309
+
310
+ async updateNowPlayingMetadata(metadata: Partial<Track>): Promise<void> {
311
+ return checkModule().updateNowPlayingMetadata(metadata as Record<string, unknown>);
312
+ },
313
+
314
+ // ── Options / notification ─────────────────────────────────────────────────
315
+
316
+ /**
317
+ * Configure notification capabilities, jump intervals, etc.
318
+ */
319
+ async updateOptions(options: UpdateOptions): Promise<void> {
320
+ return checkModule().updateOptions(options as Record<string, unknown>);
321
+ },
322
+
323
+ // ── Event subscription ─────────────────────────────────────────────────────
324
+
325
+ /**
326
+ * Subscribe to a player event.
327
+ * Returns an unsubscribe function — call it to remove the listener.
328
+ *
329
+ * @example
330
+ * const unsub = GliphPlayer.addEventListener(Event.PlaybackState, ({ state }) => {
331
+ * console.log('State:', state);
332
+ * });
333
+ * // later:
334
+ * unsub();
335
+ */
336
+ addEventListener<E extends string>(
337
+ event: E,
338
+ listener: (data: any) => void
339
+ ): () => void {
340
+ const subscription = getEmitter().addListener(event, listener);
341
+ return () => subscription.remove();
342
+ },
336
343
  };
337
344
 
338
345
  export default GliphPlayer;
@@ -340,17 +347,17 @@ export default GliphPlayer;
340
347
  // ─── Convenience state helpers ────────────────────────────────────────────────
341
348
 
342
349
  export function isPlaying(state: State): boolean {
343
- return state === State.Playing;
350
+ return state === State.Playing;
344
351
  }
345
352
 
346
353
  export function isPaused(state: State): boolean {
347
- return state === State.Paused;
354
+ return state === State.Paused;
348
355
  }
349
356
 
350
357
  export function isBuffering(state: State): boolean {
351
- return state === State.Buffering || state === State.Loading;
358
+ return state === State.Buffering || state === State.Loading;
352
359
  }
353
360
 
354
361
  export function isStopped(state: State): boolean {
355
- return state === State.Stopped || state === State.None;
356
- }
362
+ return state === State.Stopped || state === State.None;
363
+ }