expo-native-track-player 0.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/ExpoNativeTrackPlayer.podspec +21 -0
- package/LICENSE +20 -0
- package/README.md +267 -0
- package/android/build.gradle +71 -0
- package/android/src/main/AndroidManifest.xml +12 -0
- package/android/src/main/java/com/exponativetrackplayer/ExpoNativeTrackPlayerModule.kt +528 -0
- package/android/src/main/java/com/exponativetrackplayer/ExpoNativeTrackPlayerPackage.kt +34 -0
- package/android/src/main/java/com/exponativetrackplayer/ExpoNativeTrackPlayerService.kt +118 -0
- package/ios/ExpoNativeTrackPlayer.h +6 -0
- package/ios/ExpoNativeTrackPlayer.mm +475 -0
- package/lib/module/NativeExpoNativeTrackPlayer.js +5 -0
- package/lib/module/NativeExpoNativeTrackPlayer.js.map +1 -0
- package/lib/module/constants.js +31 -0
- package/lib/module/constants.js.map +1 -0
- package/lib/module/events.js +56 -0
- package/lib/module/events.js.map +1 -0
- package/lib/module/hooks.js +105 -0
- package/lib/module/hooks.js.map +1 -0
- package/lib/module/index.js +150 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/types/ResourceObject.js +2 -0
- package/lib/module/types/ResourceObject.js.map +1 -0
- package/lib/module/types/Track.js +4 -0
- package/lib/module/types/Track.js.map +1 -0
- package/lib/module/types/TrackMetadataBase.js +2 -0
- package/lib/module/types/TrackMetadataBase.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativeExpoNativeTrackPlayer.d.ts +57 -0
- package/lib/typescript/src/NativeExpoNativeTrackPlayer.d.ts.map +1 -0
- package/lib/typescript/src/constants.d.ts +23 -0
- package/lib/typescript/src/constants.d.ts.map +1 -0
- package/lib/typescript/src/events.d.ts +163 -0
- package/lib/typescript/src/events.d.ts.map +1 -0
- package/lib/typescript/src/hooks.d.ts +12 -0
- package/lib/typescript/src/hooks.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +65 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/types/ResourceObject.d.ts +5 -0
- package/lib/typescript/src/types/ResourceObject.d.ts.map +1 -0
- package/lib/typescript/src/types/Track.d.ts +22 -0
- package/lib/typescript/src/types/Track.d.ts.map +1 -0
- package/lib/typescript/src/types/TrackMetadataBase.d.ts +18 -0
- package/lib/typescript/src/types/TrackMetadataBase.d.ts.map +1 -0
- package/package.json +172 -0
- package/src/NativeExpoNativeTrackPlayer.ts +70 -0
- package/src/constants.ts +40 -0
- package/src/events.ts +136 -0
- package/src/hooks.ts +149 -0
- package/src/index.tsx +250 -0
- package/src/types/ResourceObject.ts +4 -0
- package/src/types/Track.ts +23 -0
- package/src/types/TrackMetadataBase.ts +17 -0
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
#import "ExpoNativeTrackPlayer.h"
|
|
2
|
+
#import <AVFoundation/AVFoundation.h>
|
|
3
|
+
#import <React/RCTBridge.h>
|
|
4
|
+
|
|
5
|
+
@interface ExpoNativeTrackPlayer ()
|
|
6
|
+
|
|
7
|
+
@property (nonatomic, strong) AVPlayer *player;
|
|
8
|
+
@property (nonatomic, strong) NSMutableArray<NSDictionary *> *queue;
|
|
9
|
+
@property (nonatomic, assign) NSInteger currentIndex;
|
|
10
|
+
@property (nonatomic, copy) NSString *repeatMode;
|
|
11
|
+
@property (nonatomic, strong, nullable) NSNumber *loopStartMs;
|
|
12
|
+
@property (nonatomic, strong, nullable) NSNumber *loopEndMs;
|
|
13
|
+
@property (nonatomic, copy) NSString *playbackState;
|
|
14
|
+
@property (nonatomic, assign) float playbackRate;
|
|
15
|
+
@property (nonatomic, strong, nullable) id timeObserver;
|
|
16
|
+
@property (nonatomic, strong, nullable) id endObserver;
|
|
17
|
+
|
|
18
|
+
@end
|
|
19
|
+
|
|
20
|
+
@implementation ExpoNativeTrackPlayer
|
|
21
|
+
|
|
22
|
+
RCT_EXPORT_MODULE();
|
|
23
|
+
@synthesize bridge = _bridge;
|
|
24
|
+
|
|
25
|
+
static NSString *const ExpoNativeTrackPlayerEventPlaybackState = @"playback-state";
|
|
26
|
+
static NSString *const ExpoNativeTrackPlayerEventPlaybackProgress = @"playback-progress-updated";
|
|
27
|
+
static NSString *const ExpoNativeTrackPlayerEventPlaybackActiveTrackChanged =
|
|
28
|
+
@"playback-active-track-changed";
|
|
29
|
+
static NSString *const ExpoNativeTrackPlayerEventPlaybackQueueEnded =
|
|
30
|
+
@"playback-queue-ended";
|
|
31
|
+
static NSString *const ExpoNativeTrackPlayerEventQueueUpdated = @"queue-updated";
|
|
32
|
+
|
|
33
|
+
- (instancetype)init
|
|
34
|
+
{
|
|
35
|
+
if (self = [super init]) {
|
|
36
|
+
_player = [[AVPlayer alloc] init];
|
|
37
|
+
_queue = [NSMutableArray array];
|
|
38
|
+
_currentIndex = -1;
|
|
39
|
+
_repeatMode = @"off";
|
|
40
|
+
_playbackState = @"stopped";
|
|
41
|
+
_playbackRate = 1.0f;
|
|
42
|
+
[self configureAudioSession];
|
|
43
|
+
[self startLoopWatcher];
|
|
44
|
+
}
|
|
45
|
+
return self;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
- (void)dealloc
|
|
49
|
+
{
|
|
50
|
+
if (_timeObserver != nil) {
|
|
51
|
+
[_player removeTimeObserver:_timeObserver];
|
|
52
|
+
_timeObserver = nil;
|
|
53
|
+
}
|
|
54
|
+
if (_endObserver != nil) {
|
|
55
|
+
[[NSNotificationCenter defaultCenter] removeObserver:_endObserver];
|
|
56
|
+
_endObserver = nil;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
- (NSDictionary *)dictionaryFromTrack:(JS::NativeExpoNativeTrackPlayer::TrackMetadata &)track
|
|
61
|
+
{
|
|
62
|
+
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
|
63
|
+
dictionary[@"id"] = track.id_();
|
|
64
|
+
dictionary[@"url"] = track.url();
|
|
65
|
+
|
|
66
|
+
if (track.title().length > 0) dictionary[@"title"] = track.title();
|
|
67
|
+
if (track.artist().length > 0) dictionary[@"artist"] = track.artist();
|
|
68
|
+
if (track.albumName().length > 0) dictionary[@"albumName"] = track.albumName();
|
|
69
|
+
if (track.artworkUri().length > 0) dictionary[@"artworkUri"] = track.artworkUri();
|
|
70
|
+
if (track.type().length > 0) dictionary[@"type"] = track.type();
|
|
71
|
+
if (track.userAgent().length > 0) dictionary[@"userAgent"] = track.userAgent();
|
|
72
|
+
if (track.contentType().length > 0) dictionary[@"contentType"] = track.contentType();
|
|
73
|
+
if (track.pitchAlgorithm().length > 0) dictionary[@"pitchAlgorithm"] = track.pitchAlgorithm();
|
|
74
|
+
if (track.composer().length > 0) dictionary[@"composer"] = track.composer();
|
|
75
|
+
if (track.conductor().length > 0) dictionary[@"conductor"] = track.conductor();
|
|
76
|
+
if (track.genre().length > 0) dictionary[@"genre"] = track.genre();
|
|
77
|
+
if (track.compilation().length > 0) dictionary[@"compilation"] = track.compilation();
|
|
78
|
+
if (track.subtitle().length > 0) dictionary[@"subtitle"] = track.subtitle();
|
|
79
|
+
if (track.description().length > 0) dictionary[@"description"] = track.description();
|
|
80
|
+
if (track.station().length > 0) dictionary[@"station"] = track.station();
|
|
81
|
+
if (track.trackNumber().has_value()) {
|
|
82
|
+
dictionary[@"trackNumber"] = @(track.trackNumber().value());
|
|
83
|
+
}
|
|
84
|
+
if (track.duration().has_value()) {
|
|
85
|
+
dictionary[@"duration"] = @(track.duration().value());
|
|
86
|
+
}
|
|
87
|
+
if (track.mediaType().has_value()) {
|
|
88
|
+
dictionary[@"mediaType"] = @(track.mediaType().value());
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return dictionary;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
- (NSNumber * _Nullable)nullableNumberFrom:(NSNumber *)value
|
|
95
|
+
{
|
|
96
|
+
if (value == (id)kCFNull) return nil;
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
- (void)addListener:(NSString *)eventName
|
|
101
|
+
{
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
- (void)removeListeners:(double)count
|
|
105
|
+
{
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
- (void)emitEvent:(NSString *)name body:(NSDictionary *)body
|
|
109
|
+
{
|
|
110
|
+
if (self.bridge == nil || name.length == 0) return;
|
|
111
|
+
NSArray *args = body != nil ? @[name, body] : @[name, [NSNull null]];
|
|
112
|
+
[self.bridge enqueueJSCall:@"RCTDeviceEventEmitter" method:@"emit" args:args completion:NULL];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
- (void)emitPlaybackState
|
|
116
|
+
{
|
|
117
|
+
[self emitEvent:ExpoNativeTrackPlayerEventPlaybackState body:@{
|
|
118
|
+
@"state": self.playbackState ?: @"stopped"
|
|
119
|
+
}];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
- (void)emitQueueUpdated
|
|
123
|
+
{
|
|
124
|
+
NSDictionary *payload = @{
|
|
125
|
+
@"count": @(self.queue.count),
|
|
126
|
+
@"trackIndex": @(self.currentIndex),
|
|
127
|
+
@"queue": [self.queue copy]
|
|
128
|
+
};
|
|
129
|
+
[self emitEvent:ExpoNativeTrackPlayerEventQueueUpdated body:payload];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
- (void)emitTrackChanged
|
|
133
|
+
{
|
|
134
|
+
NSDictionary *track = nil;
|
|
135
|
+
if (self.currentIndex >= 0 && self.currentIndex < (NSInteger)self.queue.count) {
|
|
136
|
+
track = self.queue[(NSUInteger)self.currentIndex];
|
|
137
|
+
}
|
|
138
|
+
NSDictionary *payload = @{
|
|
139
|
+
@"trackIndex": @(self.currentIndex),
|
|
140
|
+
@"track": track ?: (id)kCFNull
|
|
141
|
+
};
|
|
142
|
+
[self emitEvent:ExpoNativeTrackPlayerEventPlaybackActiveTrackChanged body:payload];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
- (void)emitPlaybackPositionWithPosition:(double)position
|
|
146
|
+
duration:(double)duration
|
|
147
|
+
trackIndex:(NSInteger)trackIndex
|
|
148
|
+
{
|
|
149
|
+
NSDictionary *payload = @{
|
|
150
|
+
@"position": @(position),
|
|
151
|
+
@"duration": @(duration),
|
|
152
|
+
@"trackIndex": @(trackIndex)
|
|
153
|
+
};
|
|
154
|
+
[self emitEvent:ExpoNativeTrackPlayerEventPlaybackProgress body:payload];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
- (void)emitPlaybackQueueEnded
|
|
158
|
+
{
|
|
159
|
+
NSDictionary *payload = @{
|
|
160
|
+
@"trackIndex": @(self.currentIndex),
|
|
161
|
+
@"position": @([self getPosition].doubleValue)
|
|
162
|
+
};
|
|
163
|
+
[self emitEvent:ExpoNativeTrackPlayerEventPlaybackQueueEnded body:payload];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
- (void)addToQueue:(JS::NativeExpoNativeTrackPlayer::TrackMetadata &)track
|
|
167
|
+
{
|
|
168
|
+
NSDictionary *dictionary = [self dictionaryFromTrack:track];
|
|
169
|
+
[self.queue addObject:dictionary];
|
|
170
|
+
[self emitQueueUpdated];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
- (void)addQueue:(NSArray *)tracks
|
|
174
|
+
{
|
|
175
|
+
if (tracks == nil) return;
|
|
176
|
+
for (NSDictionary *track in tracks) {
|
|
177
|
+
if (![track isKindOfClass:[NSDictionary class]]) continue;
|
|
178
|
+
[self.queue addObject:track];
|
|
179
|
+
}
|
|
180
|
+
[self emitQueueUpdated];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
- (NSArray<NSDictionary *> *)getQueue
|
|
184
|
+
{
|
|
185
|
+
return [self.queue copy];
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
- (void)removeFromQueue:(NSString *)trackId
|
|
189
|
+
{
|
|
190
|
+
if (trackId.length == 0) return;
|
|
191
|
+
__block NSInteger indexToRemove = NSNotFound;
|
|
192
|
+
[self.queue enumerateObjectsUsingBlock:^(NSDictionary *track, NSUInteger idx, BOOL *stop) {
|
|
193
|
+
if ([[track objectForKey:@"id"] isEqualToString:trackId]) {
|
|
194
|
+
indexToRemove = (NSInteger)idx;
|
|
195
|
+
*stop = YES;
|
|
196
|
+
}
|
|
197
|
+
}];
|
|
198
|
+
if (indexToRemove == NSNotFound) return;
|
|
199
|
+
|
|
200
|
+
[self.queue removeObjectAtIndex:indexToRemove];
|
|
201
|
+
if (indexToRemove == self.currentIndex) {
|
|
202
|
+
[self stopInternal];
|
|
203
|
+
self.currentIndex = -1;
|
|
204
|
+
[self emitTrackChanged];
|
|
205
|
+
} else if (indexToRemove < self.currentIndex) {
|
|
206
|
+
self.currentIndex -= 1;
|
|
207
|
+
[self emitTrackChanged];
|
|
208
|
+
}
|
|
209
|
+
[self emitQueueUpdated];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
- (void)clearQueue
|
|
213
|
+
{
|
|
214
|
+
[self.queue removeAllObjects];
|
|
215
|
+
[self stopInternal];
|
|
216
|
+
self.currentIndex = -1;
|
|
217
|
+
[self emitQueueUpdated];
|
|
218
|
+
[self emitTrackChanged];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
- (void)play:(NSNumber *)index
|
|
222
|
+
{
|
|
223
|
+
[self playInternal:[self nullableNumberFrom:index]];
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
- (void)pause
|
|
227
|
+
{
|
|
228
|
+
[self.player pause];
|
|
229
|
+
[self setPlaybackState:@"paused"];
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
- (void)stop
|
|
233
|
+
{
|
|
234
|
+
[self stopInternal];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
- (void)skipToNext
|
|
238
|
+
{
|
|
239
|
+
[self playInternal:@(self.currentIndex + 1)];
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
- (void)skipToPrevious
|
|
243
|
+
{
|
|
244
|
+
[self playInternal:@(self.currentIndex - 1)];
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
- (void)skipToIndex:(double)index
|
|
248
|
+
{
|
|
249
|
+
[self playInternal:@((NSInteger)index)];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
- (void)seekTo:(double)positionMs
|
|
253
|
+
{
|
|
254
|
+
CMTime position = CMTimeMakeWithSeconds(positionMs / 1000.0, 600);
|
|
255
|
+
[self.player seekToTime:position];
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
- (void)reset
|
|
259
|
+
{
|
|
260
|
+
[self.queue removeAllObjects];
|
|
261
|
+
[self stopInternal];
|
|
262
|
+
self.currentIndex = -1;
|
|
263
|
+
[self emitQueueUpdated];
|
|
264
|
+
[self emitTrackChanged];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
- (void)setRepeatMode:(NSString *)mode startMs:(NSNumber *)startMs endMs:(NSNumber *)endMs
|
|
268
|
+
{
|
|
269
|
+
self.repeatMode = mode ?: @"off";
|
|
270
|
+
self.loopStartMs = [self nullableNumberFrom:startMs];
|
|
271
|
+
self.loopEndMs = [self nullableNumberFrom:endMs];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
- (NSString *)getRepeatMode
|
|
275
|
+
{
|
|
276
|
+
return self.repeatMode;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
- (NSDictionary * _Nullable)getCurrentTrack
|
|
280
|
+
{
|
|
281
|
+
if (self.currentIndex < 0 || self.currentIndex >= (NSInteger)self.queue.count) return nil;
|
|
282
|
+
return self.queue[self.currentIndex];
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
- (NSNumber *)getCurrentTrackIndex
|
|
286
|
+
{
|
|
287
|
+
return @(self.currentIndex);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
- (NSNumber *)getPosition
|
|
291
|
+
{
|
|
292
|
+
return @(CMTimeGetSeconds([self.player currentTime]) * 1000.0);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
- (NSNumber *)getDuration
|
|
296
|
+
{
|
|
297
|
+
double duration = CMTimeGetSeconds(self.player.currentItem.duration);
|
|
298
|
+
return @(isfinite(duration) ? duration * 1000.0 : 0.0);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
- (NSString *)getPlaybackState
|
|
302
|
+
{
|
|
303
|
+
return self.playbackState;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
- (void)setVolume:(double)volume
|
|
307
|
+
{
|
|
308
|
+
self.player.volume = (float)volume;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
- (NSNumber *)getVolume
|
|
312
|
+
{
|
|
313
|
+
return @(self.player.volume);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
- (void)setRate:(double)rate
|
|
317
|
+
{
|
|
318
|
+
self.playbackRate = (float)rate;
|
|
319
|
+
if ([self.playbackState isEqualToString:@"playing"]) {
|
|
320
|
+
[self.player playImmediatelyAtRate:self.playbackRate];
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
- (NSNumber *)getRate
|
|
325
|
+
{
|
|
326
|
+
return @(self.playbackRate);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
- (void)playInternal:(NSNumber * _Nullable)index
|
|
330
|
+
{
|
|
331
|
+
if (self.queue.count == 0) return;
|
|
332
|
+
if (index != nil) {
|
|
333
|
+
NSInteger target = [index integerValue];
|
|
334
|
+
if (target < 0 || target >= (NSInteger)self.queue.count) return;
|
|
335
|
+
[self loadTrackAtIndex:target];
|
|
336
|
+
} else if (self.currentIndex < 0) {
|
|
337
|
+
[self loadTrackAtIndex:0];
|
|
338
|
+
}
|
|
339
|
+
[self.player playImmediatelyAtRate:self.playbackRate];
|
|
340
|
+
[self setPlaybackState:@"playing"];
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
- (void)loadTrackAtIndex:(NSInteger)index
|
|
344
|
+
{
|
|
345
|
+
NSDictionary *track = self.queue[index];
|
|
346
|
+
NSString *urlString = [track objectForKey:@"url"];
|
|
347
|
+
if (urlString.length == 0) return;
|
|
348
|
+
NSURL *url = [NSURL URLWithString:urlString];
|
|
349
|
+
if (!url) return;
|
|
350
|
+
|
|
351
|
+
AVPlayerItem *item = [AVPlayerItem playerItemWithURL:url];
|
|
352
|
+
[self.player replaceCurrentItemWithPlayerItem:item];
|
|
353
|
+
self.currentIndex = index;
|
|
354
|
+
[self observeItemEnd:item];
|
|
355
|
+
[self emitTrackChanged];
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
- (void)observeItemEnd:(AVPlayerItem *)item
|
|
359
|
+
{
|
|
360
|
+
if (self.endObserver != nil) {
|
|
361
|
+
[[NSNotificationCenter defaultCenter] removeObserver:self.endObserver];
|
|
362
|
+
}
|
|
363
|
+
__weak ExpoNativeTrackPlayer *weakSelf = self;
|
|
364
|
+
self.endObserver = [[NSNotificationCenter defaultCenter]
|
|
365
|
+
addObserverForName:AVPlayerItemDidPlayToEndTimeNotification
|
|
366
|
+
object:item
|
|
367
|
+
queue:[NSOperationQueue mainQueue]
|
|
368
|
+
usingBlock:^(NSNotification *note) {
|
|
369
|
+
[weakSelf handleTrackEnded];
|
|
370
|
+
}];
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
- (void)handleTrackEnded
|
|
374
|
+
{
|
|
375
|
+
if ([self.repeatMode isEqualToString:@"track"]) {
|
|
376
|
+
[self.player seekToTime:kCMTimeZero];
|
|
377
|
+
[self.player playImmediatelyAtRate:self.playbackRate];
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if ([self.repeatMode isEqualToString:@"queue"]) {
|
|
381
|
+
NSInteger nextIndex = self.currentIndex + 1;
|
|
382
|
+
if (nextIndex >= (NSInteger)self.queue.count) {
|
|
383
|
+
[self loadTrackAtIndex:0];
|
|
384
|
+
} else {
|
|
385
|
+
[self loadTrackAtIndex:nextIndex];
|
|
386
|
+
}
|
|
387
|
+
[self.player playImmediatelyAtRate:self.playbackRate];
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
if ([self.repeatMode isEqualToString:@"loop_portion"]) {
|
|
391
|
+
[self seekToLoopStart];
|
|
392
|
+
[self.player playImmediatelyAtRate:self.playbackRate];
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
NSInteger nextIndex = self.currentIndex + 1;
|
|
396
|
+
if (nextIndex < (NSInteger)self.queue.count) {
|
|
397
|
+
[self loadTrackAtIndex:nextIndex];
|
|
398
|
+
[self.player playImmediatelyAtRate:self.playbackRate];
|
|
399
|
+
} else {
|
|
400
|
+
[self setPlaybackState:@"ended"];
|
|
401
|
+
[self emitPlaybackQueueEnded];
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
- (void)startLoopWatcher
|
|
406
|
+
{
|
|
407
|
+
__weak ExpoNativeTrackPlayer *weakSelf = self;
|
|
408
|
+
CMTime interval = CMTimeMakeWithSeconds(0.1, 600);
|
|
409
|
+
self.timeObserver = [self.player addPeriodicTimeObserverForInterval:interval
|
|
410
|
+
queue:dispatch_get_main_queue()
|
|
411
|
+
usingBlock:^(CMTime time) {
|
|
412
|
+
double positionMs = CMTimeGetSeconds(time) * 1000.0;
|
|
413
|
+
[weakSelf handleLoopPortionIfNeeded:positionMs];
|
|
414
|
+
double durationMs = 0.0;
|
|
415
|
+
AVPlayerItem *item = weakSelf.player.currentItem;
|
|
416
|
+
if (item != nil) {
|
|
417
|
+
durationMs = CMTimeGetSeconds(item.duration) * 1000.0;
|
|
418
|
+
if (isnan(durationMs) || isinf(durationMs)) {
|
|
419
|
+
durationMs = 0.0;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
[weakSelf emitPlaybackPositionWithPosition:positionMs
|
|
423
|
+
duration:durationMs
|
|
424
|
+
trackIndex:weakSelf.currentIndex];
|
|
425
|
+
}];
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
- (void)handleLoopPortionIfNeeded:(double)positionMs
|
|
429
|
+
{
|
|
430
|
+
if (![self.repeatMode isEqualToString:@"loop_portion"]) return;
|
|
431
|
+
if (self.loopStartMs == nil || self.loopEndMs == nil) return;
|
|
432
|
+
double startMs = [self.loopStartMs doubleValue];
|
|
433
|
+
double endMs = [self.loopEndMs doubleValue];
|
|
434
|
+
if (endMs <= startMs) return;
|
|
435
|
+
if (positionMs >= endMs) {
|
|
436
|
+
[self seekToLoopStart];
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
- (void)seekToLoopStart
|
|
441
|
+
{
|
|
442
|
+
double startMs = self.loopStartMs != nil ? [self.loopStartMs doubleValue] : 0.0;
|
|
443
|
+
CMTime position = CMTimeMakeWithSeconds(startMs / 1000.0, 600);
|
|
444
|
+
[self.player seekToTime:position];
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
- (void)stopInternal
|
|
448
|
+
{
|
|
449
|
+
[self.player pause];
|
|
450
|
+
[self.player seekToTime:kCMTimeZero];
|
|
451
|
+
[self setPlaybackState:@"stopped"];
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
- (void)setPlaybackState:(NSString *)state
|
|
455
|
+
{
|
|
456
|
+
NSString *nextState = state ?: @"stopped";
|
|
457
|
+
_playbackState = [nextState copy];
|
|
458
|
+
[self emitPlaybackState];
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
- (void)configureAudioSession
|
|
462
|
+
{
|
|
463
|
+
AVAudioSession *session = [AVAudioSession sharedInstance];
|
|
464
|
+
NSError *error = nil;
|
|
465
|
+
[session setCategory:AVAudioSessionCategoryPlayback error:&error];
|
|
466
|
+
[session setActive:YES error:&error];
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
470
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
471
|
+
{
|
|
472
|
+
return std::make_shared<facebook::react::NativeExpoNativeTrackPlayerSpecJSI>(params);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
@end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeExpoNativeTrackPlayer.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;AAqEpE,eAAeA,mBAAmB,CAACC,YAAY,CAAO,uBAAuB,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
export const TrackTypes = {
|
|
4
|
+
Default: 'default',
|
|
5
|
+
Dash: 'dash',
|
|
6
|
+
Hls: 'hls',
|
|
7
|
+
SmoothStreaming: 'smoothstreaming'
|
|
8
|
+
};
|
|
9
|
+
export const PitchAlgorithms = {
|
|
10
|
+
Linear: 'linear',
|
|
11
|
+
Music: 'music',
|
|
12
|
+
Voice: 'voice'
|
|
13
|
+
};
|
|
14
|
+
export const State = {
|
|
15
|
+
None: 'none',
|
|
16
|
+
Ready: 'ready',
|
|
17
|
+
Playing: 'playing',
|
|
18
|
+
Paused: 'paused',
|
|
19
|
+
Stopped: 'stopped',
|
|
20
|
+
Loading: 'loading',
|
|
21
|
+
Buffering: 'buffering',
|
|
22
|
+
Error: 'error',
|
|
23
|
+
Ended: 'ended'
|
|
24
|
+
};
|
|
25
|
+
export const RepeatModes = {
|
|
26
|
+
Off: 'off',
|
|
27
|
+
Track: 'track',
|
|
28
|
+
Queue: 'queue',
|
|
29
|
+
LoopPortion: 'loop_portion'
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["TrackTypes","Default","Dash","Hls","SmoothStreaming","PitchAlgorithms","Linear","Music","Voice","State","None","Ready","Playing","Paused","Stopped","Loading","Buffering","Error","Ended","RepeatModes","Off","Track","Queue","LoopPortion"],"sourceRoot":"../../src","sources":["constants.ts"],"mappings":";;AAAA,OAAO,MAAMA,UAAU,GAAG;EACxBC,OAAO,EAAE,SAAS;EAClBC,IAAI,EAAE,MAAM;EACZC,GAAG,EAAE,KAAK;EACVC,eAAe,EAAE;AACnB,CAAU;AAIV,OAAO,MAAMC,eAAe,GAAG;EAC7BC,MAAM,EAAE,QAAQ;EAChBC,KAAK,EAAE,OAAO;EACdC,KAAK,EAAE;AACT,CAAU;AAKV,OAAO,MAAMC,KAA6B,GAAG;EAC3CC,IAAI,EAAE,MAAM;EACZC,KAAK,EAAE,OAAO;EACdC,OAAO,EAAE,SAAS;EAClBC,MAAM,EAAE,QAAQ;EAChBC,OAAO,EAAE,SAAS;EAClBC,OAAO,EAAE,SAAS;EAClBC,SAAS,EAAE,WAAW;EACtBC,KAAK,EAAE,OAAO;EACdC,KAAK,EAAE;AACT,CAAU;AAIV,OAAO,MAAMC,WAAW,GAAG;EACzBC,GAAG,EAAE,KAAK;EACVC,KAAK,EAAE,OAAO;EACdC,KAAK,EAAE,OAAO;EACdC,WAAW,EAAE;AACf,CAAU","ignoreList":[]}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { NativeEventEmitter } from 'react-native';
|
|
4
|
+
import ExpoNativeTrackPlayer from "./NativeExpoNativeTrackPlayer.js";
|
|
5
|
+
export const Event = {
|
|
6
|
+
PlayerError: 'player-error',
|
|
7
|
+
PlaybackState: 'playback-state',
|
|
8
|
+
PlaybackError: 'playback-error',
|
|
9
|
+
PlaybackQueueEnded: 'playback-queue-ended',
|
|
10
|
+
PlaybackActiveTrackChanged: 'playback-active-track-changed',
|
|
11
|
+
PlaybackPlayWhenReadyChanged: 'playback-play-when-ready-changed',
|
|
12
|
+
PlaybackProgressUpdated: 'playback-progress-updated',
|
|
13
|
+
PlaybackResume: 'android-playback-resume',
|
|
14
|
+
RemotePlay: 'remote-play',
|
|
15
|
+
RemotePlayPause: 'remote-play-pause',
|
|
16
|
+
RemotePause: 'remote-pause',
|
|
17
|
+
RemoteStop: 'remote-stop',
|
|
18
|
+
RemoteNext: 'remote-next',
|
|
19
|
+
RemotePrevious: 'remote-previous',
|
|
20
|
+
RemoteJumpForward: 'remote-jump-forward',
|
|
21
|
+
RemoteJumpBackward: 'remote-jump-backward',
|
|
22
|
+
RemoteSeek: 'remote-seek',
|
|
23
|
+
RemoteSetRating: 'remote-set-rating',
|
|
24
|
+
RemoteDuck: 'remote-duck',
|
|
25
|
+
RemoteLike: 'remote-like',
|
|
26
|
+
RemoteDislike: 'remote-dislike',
|
|
27
|
+
RemoteBookmark: 'remote-bookmark',
|
|
28
|
+
RemotePlayId: 'remote-play-id',
|
|
29
|
+
RemotePlaySearch: 'remote-play-search',
|
|
30
|
+
RemoteSkip: 'remote-skip',
|
|
31
|
+
MetadataChapterReceived: 'metadata-chapter-received',
|
|
32
|
+
MetadataTimedReceived: 'metadata-timed-received',
|
|
33
|
+
MetadataCommonReceived: 'metadata-common-received',
|
|
34
|
+
AndroidConnectorConnected: 'android-controller-connected',
|
|
35
|
+
AndroidConnectorDisconnected: 'android-controller-disconnected'
|
|
36
|
+
};
|
|
37
|
+
export const TrackPlayerEvents = {
|
|
38
|
+
...Event,
|
|
39
|
+
QueueUpdated: 'queue-updated'
|
|
40
|
+
};
|
|
41
|
+
const emitter = new NativeEventEmitter(ExpoNativeTrackPlayer);
|
|
42
|
+
export function addEventListener(eventName, listener) {
|
|
43
|
+
return emitter.addListener(eventName, event => listener(event));
|
|
44
|
+
}
|
|
45
|
+
export function addTypedEventListener(eventName, listener) {
|
|
46
|
+
return emitter.addListener(eventName, event => listener(event));
|
|
47
|
+
}
|
|
48
|
+
export function removeAllEventListeners(eventName) {
|
|
49
|
+
emitter.removeAllListeners(eventName);
|
|
50
|
+
}
|
|
51
|
+
export const AudioEvents = {
|
|
52
|
+
addListener: addEventListener,
|
|
53
|
+
addTypedListener: addTypedEventListener,
|
|
54
|
+
removeAllListeners: removeAllEventListeners
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["NativeEventEmitter","ExpoNativeTrackPlayer","Event","PlayerError","PlaybackState","PlaybackError","PlaybackQueueEnded","PlaybackActiveTrackChanged","PlaybackPlayWhenReadyChanged","PlaybackProgressUpdated","PlaybackResume","RemotePlay","RemotePlayPause","RemotePause","RemoteStop","RemoteNext","RemotePrevious","RemoteJumpForward","RemoteJumpBackward","RemoteSeek","RemoteSetRating","RemoteDuck","RemoteLike","RemoteDislike","RemoteBookmark","RemotePlayId","RemotePlaySearch","RemoteSkip","MetadataChapterReceived","MetadataTimedReceived","MetadataCommonReceived","AndroidConnectorConnected","AndroidConnectorDisconnected","TrackPlayerEvents","QueueUpdated","emitter","addEventListener","eventName","listener","addListener","event","addTypedEventListener","removeAllEventListeners","removeAllListeners","AudioEvents","addTypedListener"],"sourceRoot":"../../src","sources":["events.ts"],"mappings":";;AAAA,SAASA,kBAAkB,QAAkC,cAAc;AAC3E,OAAOC,qBAAqB,MAGrB,kCAA+B;AAEtC,OAAO,MAAMC,KAAK,GAAG;EACnBC,WAAW,EAAE,cAAc;EAC3BC,aAAa,EAAE,gBAAgB;EAC/BC,aAAa,EAAE,gBAAgB;EAC/BC,kBAAkB,EAAE,sBAAsB;EAC1CC,0BAA0B,EAAE,+BAA+B;EAC3DC,4BAA4B,EAAE,kCAAkC;EAChEC,uBAAuB,EAAE,2BAA2B;EACpDC,cAAc,EAAE,yBAAyB;EACzCC,UAAU,EAAE,aAAa;EACzBC,eAAe,EAAE,mBAAmB;EACpCC,WAAW,EAAE,cAAc;EAC3BC,UAAU,EAAE,aAAa;EACzBC,UAAU,EAAE,aAAa;EACzBC,cAAc,EAAE,iBAAiB;EACjCC,iBAAiB,EAAE,qBAAqB;EACxCC,kBAAkB,EAAE,sBAAsB;EAC1CC,UAAU,EAAE,aAAa;EACzBC,eAAe,EAAE,mBAAmB;EACpCC,UAAU,EAAE,aAAa;EACzBC,UAAU,EAAE,aAAa;EACzBC,aAAa,EAAE,gBAAgB;EAC/BC,cAAc,EAAE,iBAAiB;EACjCC,YAAY,EAAE,gBAAgB;EAC9BC,gBAAgB,EAAE,oBAAoB;EACtCC,UAAU,EAAE,aAAa;EACzBC,uBAAuB,EAAE,2BAA2B;EACpDC,qBAAqB,EAAE,yBAAyB;EAChDC,sBAAsB,EAAE,0BAA0B;EAClDC,yBAAyB,EAAE,8BAA8B;EACzDC,4BAA4B,EAAE;AAChC,CAAU;AAIV,OAAO,MAAMC,iBAAiB,GAAG;EAC/B,GAAG/B,KAAK;EACRgC,YAAY,EAAE;AAChB,CAAU;AAiEV,MAAMC,OAAO,GAAG,IAAInC,kBAAkB,CAACC,qBAAqB,CAAC;AAE7D,OAAO,SAASmC,gBAAgBA,CAC9BC,SAA+B,EAC/BC,QAA4B,EACP;EACrB,OAAOH,OAAO,CAACI,WAAW,CAACF,SAAS,EAAGG,KAAU,IAAKF,QAAQ,CAACE,KAAU,CAAC,CAAC;AAC7E;AAEA,OAAO,SAASC,qBAAqBA,CACnCJ,SAAY,EACZC,QAA6C,EACxB;EACrB,OAAOH,OAAO,CAACI,WAAW,CAACF,SAAS,EAAGG,KAAU,IAC/CF,QAAQ,CAACE,KAA2B,CACtC,CAAC;AACH;AAEA,OAAO,SAASE,uBAAuBA,CAACL,SAA+B,EAAQ;EAC7EF,OAAO,CAACQ,kBAAkB,CAACN,SAAS,CAAC;AACvC;AAEA,OAAO,MAAMO,WAAW,GAAG;EACzBL,WAAW,EAAEH,gBAAgB;EAC7BS,gBAAgB,EAAEJ,qBAAqB;EACvCE,kBAAkB,EAAED;AACtB,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
|
+
import ExpoNativeTrackPlayer from "./NativeExpoNativeTrackPlayer.js";
|
|
5
|
+
import { addEventListener, Event, TrackPlayerEvents } from "./events.js";
|
|
6
|
+
export function usePlaybackState() {
|
|
7
|
+
const [state, setState] = useState('none');
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const subscription = addEventListener(Event.PlaybackState, event => setState(event.state));
|
|
10
|
+
return () => subscription.remove();
|
|
11
|
+
}, []);
|
|
12
|
+
return state;
|
|
13
|
+
}
|
|
14
|
+
export function useProgress(intervalMs = 1000) {
|
|
15
|
+
const [progress, setProgress] = useState({
|
|
16
|
+
position: 0,
|
|
17
|
+
duration: 0,
|
|
18
|
+
trackIndex: -1
|
|
19
|
+
});
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!Number.isFinite(intervalMs) || intervalMs <= 0) return;
|
|
22
|
+
let isActive = true;
|
|
23
|
+
const update = () => {
|
|
24
|
+
if (!isActive) return;
|
|
25
|
+
try {
|
|
26
|
+
const position = ExpoNativeTrackPlayer.getPosition();
|
|
27
|
+
const duration = ExpoNativeTrackPlayer.getDuration();
|
|
28
|
+
const trackIndex = ExpoNativeTrackPlayer.getCurrentTrackIndex();
|
|
29
|
+
setProgress({
|
|
30
|
+
position,
|
|
31
|
+
duration,
|
|
32
|
+
trackIndex
|
|
33
|
+
});
|
|
34
|
+
} catch (error) {
|
|
35
|
+
setProgress({
|
|
36
|
+
position: 0,
|
|
37
|
+
duration: 0,
|
|
38
|
+
trackIndex: -1
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
update();
|
|
43
|
+
const timer = setInterval(update, intervalMs);
|
|
44
|
+
return () => {
|
|
45
|
+
isActive = false;
|
|
46
|
+
clearInterval(timer);
|
|
47
|
+
};
|
|
48
|
+
}, [intervalMs]);
|
|
49
|
+
return progress;
|
|
50
|
+
}
|
|
51
|
+
export function useQueue() {
|
|
52
|
+
const [queue, setQueue] = useState([]);
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
let isActive = true;
|
|
55
|
+
try {
|
|
56
|
+
const result = ExpoNativeTrackPlayer.getQueue();
|
|
57
|
+
if (isActive) setQueue(result);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
if (isActive) setQueue([]);
|
|
60
|
+
}
|
|
61
|
+
const subscription = addEventListener(TrackPlayerEvents.QueueUpdated, event => setQueue(event.queue));
|
|
62
|
+
return () => {
|
|
63
|
+
isActive = false;
|
|
64
|
+
subscription.remove();
|
|
65
|
+
};
|
|
66
|
+
}, []);
|
|
67
|
+
return queue;
|
|
68
|
+
}
|
|
69
|
+
export function useCurrentTrack() {
|
|
70
|
+
const [track, setTrack] = useState(null);
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
let isActive = true;
|
|
73
|
+
try {
|
|
74
|
+
const result = ExpoNativeTrackPlayer.getCurrentTrack();
|
|
75
|
+
if (isActive) setTrack(result);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (isActive) setTrack(null);
|
|
78
|
+
}
|
|
79
|
+
const subscription = addEventListener(Event.PlaybackActiveTrackChanged, event => setTrack(event.track));
|
|
80
|
+
return () => {
|
|
81
|
+
isActive = false;
|
|
82
|
+
subscription.remove();
|
|
83
|
+
};
|
|
84
|
+
}, []);
|
|
85
|
+
return track;
|
|
86
|
+
}
|
|
87
|
+
export function useCurrentTrackIndex() {
|
|
88
|
+
const [index, setIndex] = useState(-1);
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
let isActive = true;
|
|
91
|
+
try {
|
|
92
|
+
const result = ExpoNativeTrackPlayer.getCurrentTrackIndex();
|
|
93
|
+
if (isActive) setIndex(result);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
if (isActive) setIndex(-1);
|
|
96
|
+
}
|
|
97
|
+
const subscription = addEventListener(Event.PlaybackActiveTrackChanged, event => setIndex(event.trackIndex));
|
|
98
|
+
return () => {
|
|
99
|
+
isActive = false;
|
|
100
|
+
subscription.remove();
|
|
101
|
+
};
|
|
102
|
+
}, []);
|
|
103
|
+
return index;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useEffect","useState","ExpoNativeTrackPlayer","addEventListener","Event","TrackPlayerEvents","usePlaybackState","state","setState","subscription","PlaybackState","event","remove","useProgress","intervalMs","progress","setProgress","position","duration","trackIndex","Number","isFinite","isActive","update","getPosition","getDuration","getCurrentTrackIndex","error","timer","setInterval","clearInterval","useQueue","queue","setQueue","result","getQueue","QueueUpdated","useCurrentTrack","track","setTrack","getCurrentTrack","PlaybackActiveTrackChanged","useCurrentTrackIndex","index","setIndex"],"sourceRoot":"../../src","sources":["hooks.ts"],"mappings":"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","ignoreList":[]}
|