-
Notifications
You must be signed in to change notification settings - Fork 33
Expand file tree
/
Copy pathRmxAudioPlayer.js
More file actions
497 lines (392 loc) · 17.7 KB
/
RmxAudioPlayer.js
File metadata and controls
497 lines (392 loc) · 17.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
/**
* This file has been generated by Babel. DO NOT EDIT IT DIRECTLY
*
* Edit the source file at src/js/RmxAudioPlayer.ts
**/
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.AudioPlayer = exports.RmxAudioPlayer = void 0;
var _Constants = require("./Constants");
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
var exec = typeof cordova !== 'undefined' ? cordova.require('cordova/exec') : null; // const channel = typeof cordova !== 'undefined' ? cordova.require('cordova/channel') : null;
var log = console;
var itemStatusChangeTypes = [_Constants.RmxAudioStatusMessage.RMXSTATUS_PLAYBACK_POSITION, _Constants.RmxAudioStatusMessage.RMXSTATUS_DURATION, _Constants.RmxAudioStatusMessage.RMXSTATUS_BUFFERING, _Constants.RmxAudioStatusMessage.RMXSTATUS_CANPLAY, _Constants.RmxAudioStatusMessage.RMXSTATUS_LOADING, _Constants.RmxAudioStatusMessage.RMXSTATUS_LOADED, _Constants.RmxAudioStatusMessage.RMXSTATUS_PAUSE, _Constants.RmxAudioStatusMessage.RMXSTATUS_COMPLETED, _Constants.RmxAudioStatusMessage.RMXSTATUS_ERROR];
/**
* AudioPlayer class implementation. A singleton of this class is exported for use by Cordova,
* but nothing stops you from creating another instance. Keep in mind that the native players
* are in fact singletons, so the only thing the separate instance gives you would be
* separate onStatus callback streams.
*/
var RmxAudioPlayer =
/*#__PURE__*/
function () {
_createClass(RmxAudioPlayer, [{
key: "currentState",
/**
* The current summarized state of the player, as a string. It is preferred that you use the 'isX' accessors,
* because they properly interpret the range of these values, but this field is exposed if you wish to observe
* or interrogate it.
*/
get: function get() {
return this._currentState;
}
/**
* True if the plugin has been initialized. You'll likely never see this state; it is handled internally.
*/
}, {
key: "isInitialized",
get: function get() {
return this._inititialized;
}
}, {
key: "currentTrack",
get: function get() {
return this._currentItem;
}
/**
* If the playlist is currently playling a track.
*/
}, {
key: "isPlaying",
get: function get() {
return this._currentState === 'playing';
}
/**
* True if the playlist is currently paused
*/
}, {
key: "isPaused",
get: function get() {
return this._currentState === 'paused' || this._currentState === 'stopped';
}
/**
* True if the plugin is currently loading its *current* track.
* On iOS, many tracks are loaded in parallel, so this only reports for the *current item*, e.g.
* the item that will begin playback if you press pause.
* If you need track-specific data, it is better to watch the onStatus stream and watch for RMXSTATUS_LOADING,
* which will be raised independently & simultaneously for every track in the playlist.
* On Android, tracks are only loaded as they begin playback, so this value and RMXSTATUS_LOADING should always
* apply to the same track.
*/
}, {
key: "isLoading",
get: function get() {
return this._currentState === 'loading';
}
/**
* True if the *currently playing track* has been loaded and can be played (this includes if it is *currently playing*).
*/
}, {
key: "hasLoaded",
get: function get() {
return this._hasLoaded;
}
/**
* True if the *current track* has reported an error. In almost all cases,
* the playlist will automatically skip forward to the next track, in which case you will also receive
* an RMXSTATUS_TRACK_CHANGED event.
*/
}, {
key: "hasError",
get: function get() {
return this._hasError;
}
/**
* Creates a new RmxAudioPlayer instance.
*/
}]);
function RmxAudioPlayer() {
var _this = this;
_classCallCheck(this, RmxAudioPlayer);
_defineProperty(this, "handlers", {});
_defineProperty(this, "options", {
verbose: false,
resetStreamOnPause: true
});
_defineProperty(this, "_inititialized", false);
_defineProperty(this, "_initPromise", void 0);
_defineProperty(this, "_readyResolve", void 0);
_defineProperty(this, "_readyReject", void 0);
_defineProperty(this, "_currentState", 'unknown');
_defineProperty(this, "_hasError", false);
_defineProperty(this, "_hasLoaded", false);
_defineProperty(this, "_currentItem", null);
_defineProperty(this, "ready", function () {
return _this._initPromise;
});
_defineProperty(this, "initialize", function () {
// Initialize the plugin to send and receive messages
// channel.createSticky('onRmxAudioPlayerReady');
// channel.waitForInitialization('onRmxAudioPlayerReady');
var onNativeStatus = function onNativeStatus(msg) {
// better or worse, we got an answer back from native, so we resolve.
_this._inititialized = true;
_this._readyResolve(true);
if (msg.action === 'status') {
_this.onStatus(msg.status.trackId, msg.status.msgType, msg.status.value);
} else {
console.warn('Unknown audio player onStatus message:', msg.action);
}
}; // channel.onCordovaReady.subscribe(() => {
var error = function error(args) {
var message = 'CORDOVA RMXAUDIOPLAYER: Error storing message channel:';
console.warn(message, args);
_this._readyReject({
message,
args
});
};
exec(onNativeStatus, error, 'RmxAudioPlayer', 'initialize', []); // channel.initializationComplete('onRmxAudioPlayerReady');
// });
return _this._initPromise;
});
_defineProperty(this, "setOptions", function (successCallback, errorCallback, options) {
_this.options = _objectSpread({}, _this.options, options);
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'setOptions', [options]);
});
_defineProperty(this, "setPlaylistItems", function (successCallback, errorCallback, items, options) {
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'setPlaylistItems', [_this.validateTracks(items), options || {}]);
});
_defineProperty(this, "addItem", function (successCallback, errorCallback, trackItem) {
var validTrackItem = _this.validateTrack(trackItem);
if (!validTrackItem) {
return errorCallback(new Error('Provided track is null or not an audio track'));
}
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'addItem', [validTrackItem]);
});
_defineProperty(this, "addAllItems", function (successCallback, errorCallback, items) {
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'addAllItems', [_this.validateTracks(items)]);
});
_defineProperty(this, "removeItem", function (successCallback, errorCallback, removeItem) {
if (!removeItem) {
return errorCallback(new Error('Track removal spec is empty'));
}
if (!removeItem.trackId && !removeItem.trackIndex) {
return errorCallback(new Error('Track removal spec is invalid'));
}
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'removeItem', [removeItem.trackIndex, removeItem.trackId]);
});
_defineProperty(this, "removeItems", function (successCallback, errorCallback, items) {
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'removeItems', [items]);
});
_defineProperty(this, "clearAllItems", function (successCallback, errorCallback) {
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'clearAllItems', []);
});
_defineProperty(this, "play", function (successCallback, errorCallback) {
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'play', []);
});
_defineProperty(this, "playTrackByIndex", function (successCallback, errorCallback, index, position) {
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'playTrackByIndex', [index, position || 0]);
});
_defineProperty(this, "playTrackById", function (successCallback, errorCallback, trackId, position) {
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'playTrackById', [trackId, position || 0]);
});
_defineProperty(this, "pause", function (successCallback, errorCallback) {
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'pause', []);
});
_defineProperty(this, "skipForward", function (successCallback, errorCallback) {
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'skipForward', []);
});
_defineProperty(this, "skipBack", function (successCallback, errorCallback) {
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'skipBack', []);
});
_defineProperty(this, "seekTo", function (successCallback, errorCallback, position) {
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'seekTo', [position]);
});
_defineProperty(this, "seekToQueuePosition", function (successCallback, errorCallback, position) {
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'seekToQueuePosition', [position]);
});
_defineProperty(this, "setPlaybackRate", function (successCallback, errorCallback, rate) {
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'setPlaybackRate', [rate]);
});
_defineProperty(this, "setVolume", function (successCallback, errorCallback, volume) {
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'setPlaybackVolume', [volume]);
});
_defineProperty(this, "setLoop", function (successCallback, errorCallback, loop) {
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'setLoopAll', [!!loop]);
});
_defineProperty(this, "getPlaybackRate", function (successCallback, errorCallback) {
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'getPlaybackRate', []);
});
_defineProperty(this, "getVolume", function (successCallback, errorCallback) {
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'getPlaybackVolume', []);
});
_defineProperty(this, "getPosition", function (successCallback, errorCallback) {
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'getPlaybackPosition', []);
});
_defineProperty(this, "getCurrentBuffer", function (successCallback, errorCallback) {
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'getCurrentBuffer', []);
});
_defineProperty(this, "getQueuePosition", function (successCallback, errorCallback) {
exec(successCallback, errorCallback, 'RmxAudioPlayer', 'getQueuePosition', []);
});
_defineProperty(this, "validateTracks", function (items) {
if (!items || !Array.isArray(items)) {
return [];
}
return items.map(_this.validateTrack).filter(function (x) {
return x;
}); // may produce an empty array!
});
_defineProperty(this, "validateTrack", function (track) {
if (!track) {
return null;
} // For now we will rely on TS to do the heavy lifting, but we can add a validation here
// that all the required fields are valid. For now we just take care of the unique ID.
track.trackId = track.trackId || _this.generateUUID();
return track;
});
this.handlers = {};
this._initPromise = new Promise(function (resolve, reject) {
_this._readyResolve = resolve;
_this._readyReject = reject;
});
}
/**
* Player interface
*/
/**
* Returns a promise that resolves when the plugin is ready.
*/
_createClass(RmxAudioPlayer, [{
key: "onStatus",
/**
* Status event handling
*/
/**
* @internal
* Call this function to emit an onStatus event via the on('status') handler.
* Internal use only, to raise events received from the native interface.
*/
value: function onStatus(trackId, type, value) {
var status = {
type,
trackId,
value
};
if (this.options.verbose) {
log.log("RmxAudioPlayer.onStatus: " + _Constants.RmxAudioStatusMessageDescriptions[type] + "(" + type + ") [" + trackId + "]: ", value);
}
if (status.type === _Constants.RmxAudioStatusMessage.RMXSTATUS_TRACK_CHANGED) {
this._hasError = false;
this._hasLoaded = false;
this._currentState = 'loading';
this._currentItem = status.value.currentItem;
} // The plugin's status changes only in response to specific events.
if (itemStatusChangeTypes.indexOf(status.type) >= 0) {
// Only change the plugin's *current status* if the event being raised is for the current active track.
if (this._currentItem && this._currentItem.trackId === trackId) {
if (status.value && status.value.status) {
this._currentState = status.value.status;
}
if (status.type === _Constants.RmxAudioStatusMessage.RMXSTATUS_CANPLAY) {
this._hasLoaded = true;
}
if (status.type === _Constants.RmxAudioStatusMessage.RMXSTATUS_ERROR) {
this._hasError = true;
}
}
}
this.emit('status', status);
}
/**
* Subscribe to events raised by the plugin, e.g. on('status', (data) => { ... }),
* For now, only 'status' is supported.
*
* @param eventName Name of event to subscribe to.
* @param callback The callback function to receive the event data
*/
}, {
key: "on",
value: function on(eventName, callback) {
if (!Object.prototype.hasOwnProperty.call(this.handlers, eventName)) {
this.handlers[eventName] = [];
}
this.handlers[eventName].push(callback);
}
/**
* Remove an event handler from the plugin
* @param eventName The name of the event whose subscription is to be removed
* @param handle The event handler to destroy. Ensure that this is the SAME INSTANCE as the handler
* that was passed in to create the subscription!
*/
}, {
key: "off",
value: function off(eventName, handle) {
if (Object.prototype.hasOwnProperty.call(this.handlers, eventName)) {
var handleIndex = this.handlers[eventName].indexOf(handle);
if (handleIndex >= 0) {
this.handlers[eventName].splice(handleIndex, 1);
}
}
}
/**
* @internal
* Raises an event via the corresponding event handler. Internal use only.
* @param args Event args to pass through to the handler.
*/
}, {
key: "emit",
value: function emit() {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
var eventName = args.shift();
if (!Object.prototype.hasOwnProperty.call(this.handlers, eventName)) {
return false;
}
var handler = this.handlers[eventName];
for (var i = 0; i < handler.length; i++) {
var _callback = this.handlers[eventName][i];
if (typeof _callback === 'function') {
_callback.apply(void 0, args);
}
}
return true;
}
/**
* Validates the list of AudioTrack items to ensure they are valid.
* Used internally but you can call this if you need to :)
*
* @param items The AudioTrack items to validate
*/
}, {
key: "generateUUID",
/**
* Generate a v4 UUID for use as a unique trackId. Used internally, but you can use this to generate track ID's if you want.
*/
value: function generateUUID() {
// Doesn't need to be perfect or secure, just good enough to give each item an ID.
var d = new Date().getTime();
if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
d += performance.now(); //use high-precision timer if available
} // There are better ways to do this in ES6, we are intentionally avoiding the import
// of an ES6 polyfill here.
var template = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
return [].slice.call(template).map(function (c) {
if (c === '-' || c === '4') {
return c;
}
var r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c === 'x' ? r : r & 0x3 | 0x8).toString(16);
}).join('');
}
}]);
return RmxAudioPlayer;
}();
exports.RmxAudioPlayer = RmxAudioPlayer;
var playerInstance = new RmxAudioPlayer();
/*!
* AudioPlayer Plugin instance.
*/
var AudioPlayer = playerInstance;
exports.AudioPlayer = AudioPlayer;
var _default = playerInstance; // keep typescript happy
exports.default = _default;