apm-react-audio-player 1.0.26 → 1.1.0
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 +183 -4
- package/dist/index.js +88 -23
- package/dist/index.modern.js +497 -0
- package/package.json +27 -8
package/README.md
CHANGED
|
@@ -21,7 +21,21 @@ The library was designed to add a audio player to a body of a story which will n
|
|
|
21
21
|
- [Props](#props)
|
|
22
22
|
- [Example](#example)
|
|
23
23
|
|
|
24
|
-
[
|
|
24
|
+
[Breaking Changes](#breaking-changes)
|
|
25
|
+
|
|
26
|
+
[HLS Support](#hls-support)
|
|
27
|
+
- [Browser Compatibility](#browser-compatibility)
|
|
28
|
+
- [HLS Usage Examples](#hls-usage-examples)
|
|
29
|
+
- [Live Stream Detection](#live-stream-detection)
|
|
30
|
+
- [Supported Audio Formats](#supported-audio-formats)
|
|
31
|
+
|
|
32
|
+
[Examples](#examples)
|
|
33
|
+
- [Running the Examples](#running-the-examples)
|
|
34
|
+
- [Building the Examples](#building-the-examples)
|
|
35
|
+
|
|
36
|
+
[Publishing](#publishing)
|
|
37
|
+
|
|
38
|
+
[License](#license)
|
|
25
39
|
|
|
26
40
|
|
|
27
41
|
## Dependencies
|
|
@@ -67,7 +81,7 @@ See the [audio tag documentation](https://developer.mozilla.org/en-US/docs/Web/H
|
|
|
67
81
|
Prop | Type | Default | Notes
|
|
68
82
|
--- | --- | --- | ---
|
|
69
83
|
`title` | String | *empty string* | The title of the audio track
|
|
70
|
-
`audioSrc` | String | *empty string* |
|
|
84
|
+
`audioSrc` | String or Array | *empty string* | **String:** Single audio source URL<br>**Array:** Multiple source URLs for progressive enhancement (e.g., `['stream.m3u8', 'audio.aac', 'audio.mp3']`)
|
|
71
85
|
`description` | String | *empty string* | The description of the audio track
|
|
72
86
|
`audioPlayerRef` | Object | *empty object* | A ref object for the audio player
|
|
73
87
|
`progressBarRef` | Object | *empty object* | A ref object for the progress bar
|
|
@@ -76,7 +90,8 @@ Prop | Type | Default | Notes
|
|
|
76
90
|
`isPlaying` | Boolean | false | Whether the audio is currently playing
|
|
77
91
|
`isMuted` | Boolean | false | Whether the audio is currently muted
|
|
78
92
|
`toggleMute` | Function | --- | A function to toggle the mute state
|
|
79
|
-
`volumeCtrl` |
|
|
93
|
+
`volumeCtrl` | Boolean | false | Whether to show volume controls
|
|
94
|
+
`volumeControl` | Function | --- | A function to handle volume changes
|
|
80
95
|
`currentTime` | Number | null | The current time of the audio track
|
|
81
96
|
`duration` | Number | null | The duration of the audio track
|
|
82
97
|
`rewindControl` | Function | --- | A function to rewind the audio track
|
|
@@ -133,7 +148,8 @@ const Example = () => {
|
|
|
133
148
|
duration={duration}
|
|
134
149
|
isAudioFinished={isFinishedPlaying}
|
|
135
150
|
toggleMute={toggleMute}
|
|
136
|
-
volumeCtrl={
|
|
151
|
+
volumeCtrl={true}
|
|
152
|
+
volumeControl={volumeControl}
|
|
137
153
|
changePlayerCurrentTime={changePlayerCurrentTime}
|
|
138
154
|
rewindControl={rewindControl}
|
|
139
155
|
forwardControl={forwardControl}
|
|
@@ -145,6 +161,169 @@ const Example = () => {
|
|
|
145
161
|
}
|
|
146
162
|
```
|
|
147
163
|
|
|
164
|
+
## Breaking Changes
|
|
165
|
+
|
|
166
|
+
### Version 1.0.29+
|
|
167
|
+
|
|
168
|
+
**Removed `isLive` prop:** The `isLive` prop has been removed from `ReactAudioPlayerInner`. Live stream detection is now **automatic** based on the HTML5 audio `duration` property.
|
|
169
|
+
|
|
170
|
+
- **Old behavior:** You had to manually pass `isLive={true}` for live streams
|
|
171
|
+
- **New behavior:** The player automatically detects live streams when `duration === Infinity`
|
|
172
|
+
|
|
173
|
+
**Migration:**
|
|
174
|
+
```javascript
|
|
175
|
+
// ❌ Old - Remove the isLive prop
|
|
176
|
+
<ReactAudioPlayerInner
|
|
177
|
+
audioSrc="https://example.com/live.m3u8"
|
|
178
|
+
isLive={true} // <-- Remove this
|
|
179
|
+
// ... other props
|
|
180
|
+
/>
|
|
181
|
+
|
|
182
|
+
// ✅ New - Detection is automatic
|
|
183
|
+
<ReactAudioPlayerInner
|
|
184
|
+
audioSrc="https://example.com/live.m3u8"
|
|
185
|
+
// ... other props
|
|
186
|
+
/>
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The UI will automatically adapt based on the audio metadata - no manual configuration needed.
|
|
190
|
+
|
|
191
|
+
## HLS Support
|
|
192
|
+
|
|
193
|
+
The audio player now supports HLS (HTTP Live Streaming) for both live streams and pre-recorded content using native browser support. The player uses HTML5 `<source>` tags for progressive enhancement, allowing browsers to fall back to alternate formats if HLS is not supported.
|
|
194
|
+
|
|
195
|
+
### Browser Compatibility
|
|
196
|
+
|
|
197
|
+
| Browser | HLS Support | Fallback Behavior |
|
|
198
|
+
|---------|------------|-------------------|
|
|
199
|
+
| Safari 11+ | Native ✓ | N/A |
|
|
200
|
+
| iOS Safari | Native ✓ | N/A |
|
|
201
|
+
| Chrome 142+ | Native ✓ | N/A |
|
|
202
|
+
| Edge 142+ | Native ✓ | N/A |
|
|
203
|
+
| Mobile Chrome/Android | Native ✓ | N/A |
|
|
204
|
+
| Chrome <142 | None | Automatically uses AAC/MP3 |
|
|
205
|
+
| Firefox | None | Automatically uses AAC/MP3 |
|
|
206
|
+
| Edge <142 | None | Automatically uses AAC/MP3 |
|
|
207
|
+
| Safari <11 | None | Automatically uses AAC/MP3 |
|
|
208
|
+
|
|
209
|
+
**Note:** Chrome and Edge added native HLS support in version 142 (December 2024). Older versions will automatically skip HLS sources and use alternative formats.
|
|
210
|
+
|
|
211
|
+
### HLS Usage Examples
|
|
212
|
+
|
|
213
|
+
**See `examples/hls-example.jsx` for a complete working example with multiple HLS scenarios.**
|
|
214
|
+
|
|
215
|
+
#### Basic HLS with Progressive Enhancement
|
|
216
|
+
|
|
217
|
+
Provide multiple source formats to ensure compatibility across all browsers:
|
|
218
|
+
|
|
219
|
+
```javascript
|
|
220
|
+
<ReactAudioPlayerInner
|
|
221
|
+
title="Podcast Episode"
|
|
222
|
+
audioSrc={[
|
|
223
|
+
'https://example.com/episode.m3u8',
|
|
224
|
+
'https://example.com/episode.aac',
|
|
225
|
+
'https://example.com/episode.mp3'
|
|
226
|
+
]}
|
|
227
|
+
// ... other props
|
|
228
|
+
/>
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Browsers will try sources in order:
|
|
232
|
+
1. Modern browsers (Safari, Chrome 142+, Edge 142+) will play the HLS stream
|
|
233
|
+
2. Older browsers will skip the .m3u8 and fall back to AAC or MP3
|
|
234
|
+
|
|
235
|
+
#### Live Radio Stream
|
|
236
|
+
|
|
237
|
+
```javascript
|
|
238
|
+
<ReactAudioPlayerInner
|
|
239
|
+
title="Live Radio"
|
|
240
|
+
audioSrc={['https://stream.example.com/live.m3u8']}
|
|
241
|
+
// ... other props
|
|
242
|
+
/>
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
#### Single Source (Backward Compatible)
|
|
246
|
+
|
|
247
|
+
The player maintains backward compatibility with the original single-source API:
|
|
248
|
+
|
|
249
|
+
```javascript
|
|
250
|
+
<ReactAudioPlayerInner
|
|
251
|
+
title="Audio Track"
|
|
252
|
+
audioSrc="https://example.com/audio.mp3"
|
|
253
|
+
// ... other props
|
|
254
|
+
/>
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Live Stream Detection
|
|
258
|
+
|
|
259
|
+
The player automatically detects live streams using the HTML5 audio `duration` property:
|
|
260
|
+
|
|
261
|
+
- **Live streams:** `duration === Infinity`
|
|
262
|
+
- Timeline/seek controls are hidden
|
|
263
|
+
- Rewind/forward buttons are hidden
|
|
264
|
+
- "On Air" label is displayed
|
|
265
|
+
|
|
266
|
+
- **Pre-recorded audio:** `duration` is a finite number
|
|
267
|
+
- Timeline/seek controls are shown
|
|
268
|
+
- Rewind/forward buttons are shown
|
|
269
|
+
- Duration is displayed
|
|
270
|
+
|
|
271
|
+
No additional props are needed - the player automatically adapts its UI based on whether the content is live or pre-recorded.
|
|
272
|
+
|
|
273
|
+
### Supported Audio Formats
|
|
274
|
+
|
|
275
|
+
The player automatically detects MIME types from file extensions:
|
|
276
|
+
|
|
277
|
+
| Extension | MIME Type |
|
|
278
|
+
|-----------|-----------|
|
|
279
|
+
| `.m3u8` | `application/x-mpegURL` |
|
|
280
|
+
| `.mp3` | `audio/mpeg` |
|
|
281
|
+
| `.aac` | `audio/aac` |
|
|
282
|
+
| `.ogg` | `audio/ogg` |
|
|
283
|
+
| `.wav` | `audio/wav` |
|
|
284
|
+
|
|
285
|
+
## Examples
|
|
286
|
+
|
|
287
|
+
The `examples/` directory contains working demonstrations of the audio player with HLS support.
|
|
288
|
+
|
|
289
|
+
### Running the Examples
|
|
290
|
+
|
|
291
|
+
**Option 1: Using the serve script (recommended)**
|
|
292
|
+
```bash
|
|
293
|
+
# Start local server and open examples in browser
|
|
294
|
+
yarn serve:examples
|
|
295
|
+
|
|
296
|
+
# Or using npm
|
|
297
|
+
npm run serve:examples
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
This will start a local server on port 8000 and automatically open the examples page in your browser.
|
|
301
|
+
|
|
302
|
+
**Option 2: Open HTML file directly**
|
|
303
|
+
1. Open `examples/index.html` in a web browser
|
|
304
|
+
2. The page includes three examples:
|
|
305
|
+
- HLS with progressive enhancement (fallback sources)
|
|
306
|
+
- Live HLS stream (MPR Current)
|
|
307
|
+
- Regular MP3 (backward compatible)
|
|
308
|
+
|
|
309
|
+
### Building the Examples
|
|
310
|
+
|
|
311
|
+
The examples use a pre-built bundle (`examples/bundle.js`). If you modify the source code and want to rebuild the examples:
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
# Rebuild the examples bundle
|
|
315
|
+
yarn build:examples
|
|
316
|
+
|
|
317
|
+
# Or using npm
|
|
318
|
+
npm run build:examples
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**Files:**
|
|
322
|
+
- `examples/index.html` - Main example page with CSS and layout
|
|
323
|
+
- `examples/hls-example.jsx` - React components demonstrating HLS features
|
|
324
|
+
- `examples/bundle.js` - Compiled bundle (auto-generated, don't edit directly)
|
|
325
|
+
- `examples/hls-test.html` - Additional test page for development
|
|
326
|
+
|
|
148
327
|
## Publishing
|
|
149
328
|
|
|
150
329
|
1. Ensure every merge request and/or change to `apm-react-audio-player` should always come with an updated version (ex. 1.0.17 to 1.0.18) in the package.json.
|
package/dist/index.js
CHANGED
|
@@ -27,15 +27,15 @@ function _iterableToArrayLimit(r, l) {
|
|
|
27
27
|
i,
|
|
28
28
|
u,
|
|
29
29
|
a = [],
|
|
30
|
-
f =
|
|
31
|
-
o =
|
|
30
|
+
f = true,
|
|
31
|
+
o = false;
|
|
32
32
|
try {
|
|
33
33
|
if (i = (t = t.call(r)).next, 0 === l) {
|
|
34
34
|
if (Object(t) !== t) return;
|
|
35
35
|
f = !1;
|
|
36
36
|
} else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
|
|
37
37
|
} catch (r) {
|
|
38
|
-
o =
|
|
38
|
+
o = true, n = r;
|
|
39
39
|
} finally {
|
|
40
40
|
try {
|
|
41
41
|
if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
|
|
@@ -61,11 +61,12 @@ function _unsupportedIterableToArray(r, a) {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
var useAudioPlayer = function useAudioPlayer(audioRef, progressBarRef, volumeCtrl) {
|
|
64
|
+
var initialDuration = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : undefined;
|
|
64
65
|
var _useState = React.useState(false),
|
|
65
66
|
_useState2 = _slicedToArray(_useState, 2),
|
|
66
67
|
isPlaying = _useState2[0],
|
|
67
68
|
setIsPlaying = _useState2[1];
|
|
68
|
-
var _useState3 = React.useState(
|
|
69
|
+
var _useState3 = React.useState(initialDuration),
|
|
69
70
|
_useState4 = _slicedToArray(_useState3, 2),
|
|
70
71
|
duration = _useState4[0],
|
|
71
72
|
setDuration = _useState4[1];
|
|
@@ -82,32 +83,41 @@ var useAudioPlayer = function useAudioPlayer(audioRef, progressBarRef, volumeCtr
|
|
|
82
83
|
_useState10 = _slicedToArray(_useState9, 2),
|
|
83
84
|
isMuted = _useState10[0],
|
|
84
85
|
setIsMuted = _useState10[1];
|
|
85
|
-
var isStream = audioRef.current && audioRef.current.
|
|
86
|
+
var isStream = audioRef.current && audioRef.current.duration === Infinity;
|
|
86
87
|
React.useEffect(function () {
|
|
87
88
|
if (currentTime === Number(duration)) {
|
|
88
89
|
// restart()
|
|
89
90
|
setIsFinishedPlaying(true);
|
|
90
91
|
}
|
|
91
92
|
}, [currentTime]);
|
|
93
|
+
React.useEffect(function () {
|
|
94
|
+
// Cancel RAF loop if duration changes to Infinity (live stream metadata loaded)
|
|
95
|
+
if (duration === Infinity && animationRef.current) {
|
|
96
|
+
window.cancelAnimationFrame(animationRef.current);
|
|
97
|
+
}
|
|
98
|
+
}, [duration]);
|
|
92
99
|
var onLoadedMetadata = function onLoadedMetadata() {
|
|
93
|
-
|
|
100
|
+
if (!audioRef.current) return;
|
|
94
101
|
var seconds = Math.floor(audioRef.current.duration);
|
|
95
102
|
setDuration(seconds);
|
|
96
|
-
if (
|
|
103
|
+
if (audioRef.current.duration !== Infinity && progressBarRef.current) {
|
|
97
104
|
progressBarRef.current.max = seconds;
|
|
98
105
|
}
|
|
99
106
|
};
|
|
100
107
|
var updateCurrentTime = function updateCurrentTime() {
|
|
101
|
-
|
|
108
|
+
if (progressBarRef.current) {
|
|
109
|
+
setCurrentTime(progressBarRef.current.value);
|
|
110
|
+
}
|
|
102
111
|
};
|
|
103
112
|
var _whilePlaying = function whilePlaying() {
|
|
104
|
-
|
|
105
|
-
if (!
|
|
106
|
-
|
|
107
|
-
|
|
113
|
+
// Guard against null refs (can happen when timeline unmounts for live streams)
|
|
114
|
+
if (!progressBarRef.current || !audioRef.current) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (audioRef.current.duration !== Infinity) {
|
|
108
118
|
progressBarRef.current.value = Math.floor(audioRef.current.currentTime);
|
|
119
|
+
progressBarRef.current.style.setProperty('--seek-before-width', "".concat(progressBarRef.current.value / duration * 100, "%"));
|
|
109
120
|
}
|
|
110
|
-
progressBarRef.current.style.setProperty('--seek-before-width', "".concat(progressBarRef.current.value / duration * 100, "%"));
|
|
111
121
|
updateCurrentTime();
|
|
112
122
|
|
|
113
123
|
// when you reach the end of the song
|
|
@@ -134,8 +144,10 @@ var useAudioPlayer = function useAudioPlayer(audioRef, progressBarRef, volumeCtr
|
|
|
134
144
|
setIsPlaying(true);
|
|
135
145
|
setIsFinishedPlaying(false);
|
|
136
146
|
audioRef.current.play();
|
|
137
|
-
|
|
138
|
-
|
|
147
|
+
|
|
148
|
+
// Only start RAF loop for non-live streams with valid duration
|
|
149
|
+
var dur = audioRef.current.duration;
|
|
150
|
+
if (dur !== Infinity && !isNaN(dur) && isFinite(dur)) {
|
|
139
151
|
animationRef.current = window.requestAnimationFrame(_whilePlaying);
|
|
140
152
|
}
|
|
141
153
|
};
|
|
@@ -157,24 +169,29 @@ var useAudioPlayer = function useAudioPlayer(audioRef, progressBarRef, volumeCtr
|
|
|
157
169
|
}
|
|
158
170
|
};
|
|
159
171
|
var changePlayerCurrentTime = function changePlayerCurrentTime() {
|
|
172
|
+
if (!progressBarRef.current || !audioRef.current) return;
|
|
160
173
|
audioRef.current.currentTime = progressBarRef.current.value;
|
|
161
174
|
setCurrentTime(progressBarRef.current.value);
|
|
162
175
|
progressBarRef.current.style.setProperty('--seek-before-width', "".concat(progressBarRef.current.value / duration * 100, "%"));
|
|
163
176
|
};
|
|
164
177
|
var changeRange = function changeRange() {
|
|
178
|
+
if (!progressBarRef.current || !audioRef.current) return;
|
|
165
179
|
audioRef.current.currentTime = progressBarRef.current.value;
|
|
166
180
|
updateCurrentTime();
|
|
167
181
|
changePlayerCurrentTime();
|
|
168
182
|
};
|
|
169
183
|
var rewindControl = function rewindControl() {
|
|
184
|
+
if (!progressBarRef.current) return;
|
|
170
185
|
progressBarRef.current.value = Number(progressBarRef.current.value) - 15;
|
|
171
186
|
changeRange();
|
|
172
187
|
};
|
|
173
188
|
var forwardControl = function forwardControl() {
|
|
189
|
+
if (!progressBarRef.current) return;
|
|
174
190
|
progressBarRef.current.value = Number(progressBarRef.current.value) + 15;
|
|
175
191
|
changeRange();
|
|
176
192
|
};
|
|
177
193
|
var volumeControl = function volumeControl(e) {
|
|
194
|
+
if (!audioRef.current) return;
|
|
178
195
|
var value = e.target.value;
|
|
179
196
|
var volume = value / 100;
|
|
180
197
|
audioRef.current.volume = volume;
|
|
@@ -258,6 +275,24 @@ var Pause = function Pause() {
|
|
|
258
275
|
}));
|
|
259
276
|
};
|
|
260
277
|
|
|
278
|
+
var getTypeFromExtension = function getTypeFromExtension(url) {
|
|
279
|
+
var extension = url.split('.').pop().split('?')[0];
|
|
280
|
+
switch (extension) {
|
|
281
|
+
case 'm3u8':
|
|
282
|
+
return 'application/x-mpegURL';
|
|
283
|
+
case 'aac':
|
|
284
|
+
return 'audio/aac';
|
|
285
|
+
case 'mp3':
|
|
286
|
+
return 'audio/mpeg';
|
|
287
|
+
case 'ogg':
|
|
288
|
+
return 'audio/ogg';
|
|
289
|
+
case 'wav':
|
|
290
|
+
return 'audio/wav';
|
|
291
|
+
default:
|
|
292
|
+
return undefined;
|
|
293
|
+
// Let browser auto-detect
|
|
294
|
+
}
|
|
295
|
+
};
|
|
261
296
|
var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
262
297
|
var _props$audioPlayerRef, _props$progressBarRef;
|
|
263
298
|
// references
|
|
@@ -270,7 +305,6 @@ var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
|
270
305
|
volumeCtrl = props.volumeCtrl,
|
|
271
306
|
playBtnClass = props.playBtnClass,
|
|
272
307
|
customHtml = props.customHtml,
|
|
273
|
-
isLive = props.isLive,
|
|
274
308
|
onLoadedMetadata = props.onLoadedMetadata,
|
|
275
309
|
calculateTime = props.calculateTime,
|
|
276
310
|
togglePlaying = props.togglePlaying,
|
|
@@ -288,16 +322,46 @@ var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
|
288
322
|
prefix = props.prefix;
|
|
289
323
|
var audioDuration = duration && !isNaN(duration) && calculateTime(duration);
|
|
290
324
|
var formatDuration = duration && !isNaN(duration) && audioDuration && formatCalculateTime(audioDuration);
|
|
325
|
+
|
|
326
|
+
// Reload audio when audioSrc changes
|
|
327
|
+
// Use JSON.stringify to handle array comparisons by value instead of reference
|
|
328
|
+
React.useEffect(function () {
|
|
329
|
+
if (audioPlayerRef.current && audioSrc) {
|
|
330
|
+
try {
|
|
331
|
+
audioPlayerRef.current.load();
|
|
332
|
+
} catch (err) {
|
|
333
|
+
console.warn('Failed to reload audio source:', err);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}, [JSON.stringify(audioSrc)]);
|
|
337
|
+
|
|
338
|
+
// Set initial volume to 100%
|
|
339
|
+
React.useEffect(function () {
|
|
340
|
+
if (audioPlayerRef.current) {
|
|
341
|
+
audioPlayerRef.current.volume = 1.0;
|
|
342
|
+
}
|
|
343
|
+
}, []);
|
|
344
|
+
|
|
345
|
+
// Helper to determine if controls should show
|
|
346
|
+
var showControls = duration !== Infinity && duration !== undefined && !isNaN(duration) && isFinite(duration);
|
|
291
347
|
return audioSrc && /*#__PURE__*/React.createElement("div", {
|
|
292
348
|
className: "audioPlayer",
|
|
293
349
|
style: customStyles && customStyles.audioPlayer
|
|
294
350
|
}, /*#__PURE__*/React.createElement("audio", {
|
|
295
351
|
ref: audioPlayerRef,
|
|
296
|
-
src: audioSrc,
|
|
297
352
|
preload: "none",
|
|
298
353
|
onLoadedMetadata: onLoadedMetadata,
|
|
299
354
|
muted: isMuted
|
|
300
|
-
}
|
|
355
|
+
}, Array.isArray(audioSrc) ? audioSrc.map(function (src, index) {
|
|
356
|
+
return /*#__PURE__*/React.createElement("source", {
|
|
357
|
+
key: index,
|
|
358
|
+
src: src,
|
|
359
|
+
type: getTypeFromExtension(src)
|
|
360
|
+
});
|
|
361
|
+
}) : audioSrc ? /*#__PURE__*/React.createElement("source", {
|
|
362
|
+
src: audioSrc,
|
|
363
|
+
type: getTypeFromExtension(audioSrc)
|
|
364
|
+
}) : null, "Your browser does not support the audio element."), /*#__PURE__*/React.createElement("div", {
|
|
301
365
|
className: "player-layout"
|
|
302
366
|
}, volumeCtrl && /*#__PURE__*/React.createElement("div", {
|
|
303
367
|
className: "player-controls-secondary-outer"
|
|
@@ -323,6 +387,7 @@ var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
|
323
387
|
className: "player-volume-progress",
|
|
324
388
|
min: 0,
|
|
325
389
|
max: 100,
|
|
390
|
+
defaultValue: 100,
|
|
326
391
|
"aria-hidden": "true",
|
|
327
392
|
"aria-valuetext": "100%",
|
|
328
393
|
onChange: function onChange(e) {
|
|
@@ -335,7 +400,7 @@ var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
|
335
400
|
className: "player-volume-label"
|
|
336
401
|
}, "Volume")), /*#__PURE__*/React.createElement("div", {
|
|
337
402
|
className: "player-controls"
|
|
338
|
-
},
|
|
403
|
+
}, showControls && /*#__PURE__*/React.createElement("div", {
|
|
339
404
|
className: "player-backward-forward-controls"
|
|
340
405
|
}, /*#__PURE__*/React.createElement("button", {
|
|
341
406
|
onClick: rewindControl
|
|
@@ -349,14 +414,14 @@ var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
|
349
414
|
className: playBtnClass,
|
|
350
415
|
style: customStyles && customStyles.playPause,
|
|
351
416
|
id: "playbutton"
|
|
352
|
-
}, isPlaying ? /*#__PURE__*/React.createElement(Pause, null) : /*#__PURE__*/React.createElement(Play, null))),
|
|
417
|
+
}, isPlaying ? /*#__PURE__*/React.createElement(Pause, null) : /*#__PURE__*/React.createElement(Play, null))), showControls && /*#__PURE__*/React.createElement("div", {
|
|
353
418
|
className: "player-backward-forward-controls"
|
|
354
419
|
}, /*#__PURE__*/React.createElement("button", {
|
|
355
420
|
onClick: forwardControl
|
|
356
421
|
}, /*#__PURE__*/React.createElement("img", {
|
|
357
422
|
src: "/img/icon-forward-15.svg",
|
|
358
423
|
alt: "Forward 15 seconds"
|
|
359
|
-
})))),
|
|
424
|
+
})))), showControls && /*#__PURE__*/React.createElement("div", {
|
|
360
425
|
className: "player-timeline"
|
|
361
426
|
}, /*#__PURE__*/React.createElement("div", {
|
|
362
427
|
className: "player-currentTime"
|
|
@@ -373,11 +438,11 @@ var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
|
373
438
|
})), /*#__PURE__*/React.createElement("div", {
|
|
374
439
|
className: "player-duration",
|
|
375
440
|
style: customStyles && customStyles.duration
|
|
376
|
-
}, duration && !isNaN(duration)
|
|
441
|
+
}, duration && !isNaN(duration) ? calculateTime(duration) : '-- : --')), /*#__PURE__*/React.createElement("div", {
|
|
377
442
|
className: "player-content"
|
|
378
443
|
}, customHtml && customHtml, /*#__PURE__*/React.createElement("div", {
|
|
379
444
|
className: "player-audio-type type-sm"
|
|
380
|
-
},
|
|
445
|
+
}, duration === Infinity ? /*#__PURE__*/React.createElement("div", {
|
|
381
446
|
className: "player-live-label"
|
|
382
447
|
}, prefix ? prefix : 'On Air') : /*#__PURE__*/React.createElement("div", {
|
|
383
448
|
className: "player-label"
|
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
import React, { useState, useRef, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
function _arrayLikeToArray(r, a) {
|
|
4
|
+
(null == a || a > r.length) && (a = r.length);
|
|
5
|
+
for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
|
|
6
|
+
return n;
|
|
7
|
+
}
|
|
8
|
+
function _arrayWithHoles(r) {
|
|
9
|
+
if (Array.isArray(r)) return r;
|
|
10
|
+
}
|
|
11
|
+
function _extends() {
|
|
12
|
+
return _extends = Object.assign ? Object.assign.bind() : function (n) {
|
|
13
|
+
for (var e = 1; e < arguments.length; e++) {
|
|
14
|
+
var t = arguments[e];
|
|
15
|
+
for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
|
|
16
|
+
}
|
|
17
|
+
return n;
|
|
18
|
+
}, _extends.apply(null, arguments);
|
|
19
|
+
}
|
|
20
|
+
function _iterableToArrayLimit(r, l) {
|
|
21
|
+
var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
|
|
22
|
+
if (null != t) {
|
|
23
|
+
var e,
|
|
24
|
+
n,
|
|
25
|
+
i,
|
|
26
|
+
u,
|
|
27
|
+
a = [],
|
|
28
|
+
f = true,
|
|
29
|
+
o = false;
|
|
30
|
+
try {
|
|
31
|
+
if (i = (t = t.call(r)).next, 0 === l) {
|
|
32
|
+
if (Object(t) !== t) return;
|
|
33
|
+
f = !1;
|
|
34
|
+
} else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
|
|
35
|
+
} catch (r) {
|
|
36
|
+
o = true, n = r;
|
|
37
|
+
} finally {
|
|
38
|
+
try {
|
|
39
|
+
if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
|
|
40
|
+
} finally {
|
|
41
|
+
if (o) throw n;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return a;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function _nonIterableRest() {
|
|
48
|
+
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
49
|
+
}
|
|
50
|
+
function _slicedToArray(r, e) {
|
|
51
|
+
return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
|
|
52
|
+
}
|
|
53
|
+
function _unsupportedIterableToArray(r, a) {
|
|
54
|
+
if (r) {
|
|
55
|
+
if ("string" == typeof r) return _arrayLikeToArray(r, a);
|
|
56
|
+
var t = {}.toString.call(r).slice(8, -1);
|
|
57
|
+
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
var useAudioPlayer = function useAudioPlayer(audioRef, progressBarRef, volumeCtrl) {
|
|
62
|
+
var initialDuration = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : undefined;
|
|
63
|
+
var _useState = useState(false),
|
|
64
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
65
|
+
isPlaying = _useState2[0],
|
|
66
|
+
setIsPlaying = _useState2[1];
|
|
67
|
+
var _useState3 = useState(initialDuration),
|
|
68
|
+
_useState4 = _slicedToArray(_useState3, 2),
|
|
69
|
+
duration = _useState4[0],
|
|
70
|
+
setDuration = _useState4[1];
|
|
71
|
+
var _useState5 = useState(0),
|
|
72
|
+
_useState6 = _slicedToArray(_useState5, 2),
|
|
73
|
+
currentTime = _useState6[0],
|
|
74
|
+
setCurrentTime = _useState6[1];
|
|
75
|
+
var _useState7 = useState(false),
|
|
76
|
+
_useState8 = _slicedToArray(_useState7, 2),
|
|
77
|
+
isFinishedPlaying = _useState8[0],
|
|
78
|
+
setIsFinishedPlaying = _useState8[1];
|
|
79
|
+
var animationRef = useRef(); // reference the animation
|
|
80
|
+
var _useState9 = useState(false),
|
|
81
|
+
_useState10 = _slicedToArray(_useState9, 2),
|
|
82
|
+
isMuted = _useState10[0],
|
|
83
|
+
setIsMuted = _useState10[1];
|
|
84
|
+
var isStream = audioRef.current && audioRef.current.duration === Infinity;
|
|
85
|
+
useEffect(function () {
|
|
86
|
+
if (currentTime === Number(duration)) {
|
|
87
|
+
// restart()
|
|
88
|
+
setIsFinishedPlaying(true);
|
|
89
|
+
}
|
|
90
|
+
}, [currentTime]);
|
|
91
|
+
useEffect(function () {
|
|
92
|
+
// Cancel RAF loop if duration changes to Infinity (live stream metadata loaded)
|
|
93
|
+
if (duration === Infinity && animationRef.current) {
|
|
94
|
+
window.cancelAnimationFrame(animationRef.current);
|
|
95
|
+
}
|
|
96
|
+
}, [duration]);
|
|
97
|
+
var onLoadedMetadata = function onLoadedMetadata() {
|
|
98
|
+
if (!audioRef.current) return;
|
|
99
|
+
var seconds = Math.floor(audioRef.current.duration);
|
|
100
|
+
setDuration(seconds);
|
|
101
|
+
if (audioRef.current.duration !== Infinity && progressBarRef.current) {
|
|
102
|
+
progressBarRef.current.max = seconds;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
var updateCurrentTime = function updateCurrentTime() {
|
|
106
|
+
if (progressBarRef.current) {
|
|
107
|
+
setCurrentTime(progressBarRef.current.value);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
var _whilePlaying = function whilePlaying() {
|
|
111
|
+
// Guard against null refs (can happen when timeline unmounts for live streams)
|
|
112
|
+
if (!progressBarRef.current || !audioRef.current) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (audioRef.current.duration !== Infinity) {
|
|
116
|
+
progressBarRef.current.value = Math.floor(audioRef.current.currentTime);
|
|
117
|
+
progressBarRef.current.style.setProperty('--seek-before-width', "".concat(progressBarRef.current.value / duration * 100, "%"));
|
|
118
|
+
}
|
|
119
|
+
updateCurrentTime();
|
|
120
|
+
|
|
121
|
+
// when you reach the end of the song
|
|
122
|
+
if (!isStream && progressBarRef.current.value === duration) {
|
|
123
|
+
// restart()
|
|
124
|
+
setIsFinishedPlaying(true);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
animationRef.current = window.requestAnimationFrame(_whilePlaying);
|
|
128
|
+
};
|
|
129
|
+
var pause = function pause() {
|
|
130
|
+
setIsPlaying(false);
|
|
131
|
+
audioRef.current.pause();
|
|
132
|
+
window.cancelAnimationFrame(animationRef.current);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// const restart = () => {
|
|
136
|
+
// progressBarRef.current.value = 0
|
|
137
|
+
// updateCurrentTime()
|
|
138
|
+
// pause()
|
|
139
|
+
// }
|
|
140
|
+
|
|
141
|
+
var play = function play() {
|
|
142
|
+
setIsPlaying(true);
|
|
143
|
+
setIsFinishedPlaying(false);
|
|
144
|
+
audioRef.current.play();
|
|
145
|
+
|
|
146
|
+
// Only start RAF loop for non-live streams with valid duration
|
|
147
|
+
var dur = audioRef.current.duration;
|
|
148
|
+
if (dur !== Infinity && !isNaN(dur) && isFinite(dur)) {
|
|
149
|
+
animationRef.current = window.requestAnimationFrame(_whilePlaying);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
var toggleMute = function toggleMute() {
|
|
153
|
+
setIsMuted(!isMuted);
|
|
154
|
+
};
|
|
155
|
+
var calculateTime = function calculateTime(secs) {
|
|
156
|
+
var minutes = Math.floor(secs / 60);
|
|
157
|
+
var returnedMinutes = minutes < 10 ? "0".concat(minutes) : "".concat(minutes);
|
|
158
|
+
var seconds = Math.floor(secs % 60);
|
|
159
|
+
var returnedSeconds = seconds < 10 ? "0".concat(seconds) : "".concat(seconds);
|
|
160
|
+
return "".concat(returnedMinutes, ":").concat(returnedSeconds);
|
|
161
|
+
};
|
|
162
|
+
var togglePlaying = function togglePlaying() {
|
|
163
|
+
if (!isPlaying) {
|
|
164
|
+
play();
|
|
165
|
+
} else {
|
|
166
|
+
pause();
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
var changePlayerCurrentTime = function changePlayerCurrentTime() {
|
|
170
|
+
if (!progressBarRef.current || !audioRef.current) return;
|
|
171
|
+
audioRef.current.currentTime = progressBarRef.current.value;
|
|
172
|
+
setCurrentTime(progressBarRef.current.value);
|
|
173
|
+
progressBarRef.current.style.setProperty('--seek-before-width', "".concat(progressBarRef.current.value / duration * 100, "%"));
|
|
174
|
+
};
|
|
175
|
+
var changeRange = function changeRange() {
|
|
176
|
+
if (!progressBarRef.current || !audioRef.current) return;
|
|
177
|
+
audioRef.current.currentTime = progressBarRef.current.value;
|
|
178
|
+
updateCurrentTime();
|
|
179
|
+
changePlayerCurrentTime();
|
|
180
|
+
};
|
|
181
|
+
var rewindControl = function rewindControl() {
|
|
182
|
+
if (!progressBarRef.current) return;
|
|
183
|
+
progressBarRef.current.value = Number(progressBarRef.current.value) - 15;
|
|
184
|
+
changeRange();
|
|
185
|
+
};
|
|
186
|
+
var forwardControl = function forwardControl() {
|
|
187
|
+
if (!progressBarRef.current) return;
|
|
188
|
+
progressBarRef.current.value = Number(progressBarRef.current.value) + 15;
|
|
189
|
+
changeRange();
|
|
190
|
+
};
|
|
191
|
+
var volumeControl = function volumeControl(e) {
|
|
192
|
+
if (!audioRef.current) return;
|
|
193
|
+
var value = e.target.value;
|
|
194
|
+
var volume = value / 100;
|
|
195
|
+
audioRef.current.volume = volume;
|
|
196
|
+
};
|
|
197
|
+
var formatCalculateTime = function formatCalculateTime(timeString) {
|
|
198
|
+
var toString = String(timeString);
|
|
199
|
+
if (toString.split(':').length === 3) {
|
|
200
|
+
var _toString$split = toString.split(':'),
|
|
201
|
+
_toString$split2 = _slicedToArray(_toString$split, 3),
|
|
202
|
+
hours = _toString$split2[0],
|
|
203
|
+
minutes = _toString$split2[1],
|
|
204
|
+
seconds = _toString$split2[2];
|
|
205
|
+
return "".concat(parseInt(hours), "hr ").concat(parseInt(minutes), "min ").concat(parseInt(seconds), "sec");
|
|
206
|
+
} else if (toString.split(':').length === 2) {
|
|
207
|
+
var _toString$split3 = toString.split(':'),
|
|
208
|
+
_toString$split4 = _slicedToArray(_toString$split3, 2),
|
|
209
|
+
_minutes = _toString$split4[0],
|
|
210
|
+
_seconds = _toString$split4[1];
|
|
211
|
+
return "".concat(parseInt(_minutes), "min ").concat(parseInt(_seconds), "sec");
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
return {
|
|
215
|
+
onLoadedMetadata: onLoadedMetadata,
|
|
216
|
+
calculateTime: calculateTime,
|
|
217
|
+
togglePlaying: togglePlaying,
|
|
218
|
+
changePlayerCurrentTime: changePlayerCurrentTime,
|
|
219
|
+
rewindControl: rewindControl,
|
|
220
|
+
forwardControl: forwardControl,
|
|
221
|
+
play: play,
|
|
222
|
+
pause: pause,
|
|
223
|
+
isPlaying: isPlaying,
|
|
224
|
+
isFinishedPlaying: isFinishedPlaying,
|
|
225
|
+
currentTime: currentTime,
|
|
226
|
+
duration: duration,
|
|
227
|
+
volumeCtrl: volumeCtrl,
|
|
228
|
+
isMuted: isMuted,
|
|
229
|
+
volumeControl: volumeControl,
|
|
230
|
+
toggleMute: toggleMute,
|
|
231
|
+
formatCalculateTime: formatCalculateTime
|
|
232
|
+
};
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
var Play = function Play() {
|
|
236
|
+
return /*#__PURE__*/React.createElement("svg", {
|
|
237
|
+
stroke: "currentColor",
|
|
238
|
+
fill: "currentColor",
|
|
239
|
+
strokeWidth: "0",
|
|
240
|
+
viewBox: "0 0 448 512",
|
|
241
|
+
className: "play",
|
|
242
|
+
height: "1em",
|
|
243
|
+
width: "1em",
|
|
244
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
245
|
+
role: "img",
|
|
246
|
+
"aria-labelledby": "play playButton"
|
|
247
|
+
}, /*#__PURE__*/React.createElement("title", {
|
|
248
|
+
id: "play"
|
|
249
|
+
}, "Play"), /*#__PURE__*/React.createElement("desc", {
|
|
250
|
+
id: "playButton"
|
|
251
|
+
}, "Play Button"), /*#__PURE__*/React.createElement("path", {
|
|
252
|
+
d: "M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z"
|
|
253
|
+
}));
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
var Pause = function Pause() {
|
|
257
|
+
return /*#__PURE__*/React.createElement("svg", {
|
|
258
|
+
stroke: "currentColor",
|
|
259
|
+
fill: "currentColor",
|
|
260
|
+
strokeWidth: "0",
|
|
261
|
+
viewBox: "0 0 448 512",
|
|
262
|
+
height: "1em",
|
|
263
|
+
width: "1em",
|
|
264
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
265
|
+
role: "img",
|
|
266
|
+
"aria-labelledby": "pause pauseButton"
|
|
267
|
+
}, /*#__PURE__*/React.createElement("title", {
|
|
268
|
+
id: "pause"
|
|
269
|
+
}, "Pause"), /*#__PURE__*/React.createElement("desc", {
|
|
270
|
+
id: "pauseButton"
|
|
271
|
+
}, "Pause Button"), /*#__PURE__*/React.createElement("path", {
|
|
272
|
+
d: "M144 479H48c-26.5 0-48-21.5-48-48V79c0-26.5 21.5-48 48-48h96c26.5 0 48 21.5 48 48v352c0 26.5-21.5 48-48 48zm304-48V79c0-26.5-21.5-48-48-48h-96c-26.5 0-48 21.5-48 48v352c0 26.5 21.5 48 48 48h96c26.5 0 48-21.5 48-48z"
|
|
273
|
+
}));
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
var getTypeFromExtension = function getTypeFromExtension(url) {
|
|
277
|
+
var extension = url.split('.').pop().split('?')[0];
|
|
278
|
+
switch (extension) {
|
|
279
|
+
case 'm3u8':
|
|
280
|
+
return 'application/x-mpegURL';
|
|
281
|
+
case 'aac':
|
|
282
|
+
return 'audio/aac';
|
|
283
|
+
case 'mp3':
|
|
284
|
+
return 'audio/mpeg';
|
|
285
|
+
case 'ogg':
|
|
286
|
+
return 'audio/ogg';
|
|
287
|
+
case 'wav':
|
|
288
|
+
return 'audio/wav';
|
|
289
|
+
default:
|
|
290
|
+
return undefined;
|
|
291
|
+
// Let browser auto-detect
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
295
|
+
var _props$audioPlayerRef, _props$progressBarRef;
|
|
296
|
+
// references
|
|
297
|
+
var audioPlayerRef = (_props$audioPlayerRef = props.audioPlayerRef) !== null && _props$audioPlayerRef !== void 0 ? _props$audioPlayerRef : useRef(); // reference our audio component
|
|
298
|
+
var progressBarRef = (_props$progressBarRef = props.progressBarRef) !== null && _props$progressBarRef !== void 0 ? _props$progressBarRef : useRef(); // reference our progress bar
|
|
299
|
+
|
|
300
|
+
var customStyles = props ? props.style : '';
|
|
301
|
+
var title = props.title,
|
|
302
|
+
audioSrc = props.audioSrc,
|
|
303
|
+
volumeCtrl = props.volumeCtrl,
|
|
304
|
+
playBtnClass = props.playBtnClass,
|
|
305
|
+
customHtml = props.customHtml,
|
|
306
|
+
onLoadedMetadata = props.onLoadedMetadata,
|
|
307
|
+
calculateTime = props.calculateTime,
|
|
308
|
+
togglePlaying = props.togglePlaying,
|
|
309
|
+
changePlayerCurrentTime = props.changePlayerCurrentTime,
|
|
310
|
+
isPlaying = props.isPlaying,
|
|
311
|
+
currentTime = props.currentTime,
|
|
312
|
+
duration = props.duration,
|
|
313
|
+
volumeControl = props.volumeControl,
|
|
314
|
+
toggleMute = props.toggleMute,
|
|
315
|
+
isMuted = props.isMuted,
|
|
316
|
+
formatCalculateTime = props.formatCalculateTime,
|
|
317
|
+
rewindControl = props.rewindControl,
|
|
318
|
+
forwardControl = props.forwardControl,
|
|
319
|
+
subtitle = props.subtitle,
|
|
320
|
+
prefix = props.prefix;
|
|
321
|
+
var audioDuration = duration && !isNaN(duration) && calculateTime(duration);
|
|
322
|
+
var formatDuration = duration && !isNaN(duration) && audioDuration && formatCalculateTime(audioDuration);
|
|
323
|
+
|
|
324
|
+
// Reload audio when audioSrc changes
|
|
325
|
+
// Use JSON.stringify to handle array comparisons by value instead of reference
|
|
326
|
+
useEffect(function () {
|
|
327
|
+
if (audioPlayerRef.current && audioSrc) {
|
|
328
|
+
try {
|
|
329
|
+
audioPlayerRef.current.load();
|
|
330
|
+
} catch (err) {
|
|
331
|
+
console.warn('Failed to reload audio source:', err);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}, [JSON.stringify(audioSrc)]);
|
|
335
|
+
|
|
336
|
+
// Set initial volume to 100%
|
|
337
|
+
useEffect(function () {
|
|
338
|
+
if (audioPlayerRef.current) {
|
|
339
|
+
audioPlayerRef.current.volume = 1.0;
|
|
340
|
+
}
|
|
341
|
+
}, []);
|
|
342
|
+
|
|
343
|
+
// Helper to determine if controls should show
|
|
344
|
+
var showControls = duration !== Infinity && duration !== undefined && !isNaN(duration) && isFinite(duration);
|
|
345
|
+
return audioSrc && /*#__PURE__*/React.createElement("div", {
|
|
346
|
+
className: "audioPlayer",
|
|
347
|
+
style: customStyles && customStyles.audioPlayer
|
|
348
|
+
}, /*#__PURE__*/React.createElement("audio", {
|
|
349
|
+
ref: audioPlayerRef,
|
|
350
|
+
preload: "none",
|
|
351
|
+
onLoadedMetadata: onLoadedMetadata,
|
|
352
|
+
muted: isMuted
|
|
353
|
+
}, Array.isArray(audioSrc) ? audioSrc.map(function (src, index) {
|
|
354
|
+
return /*#__PURE__*/React.createElement("source", {
|
|
355
|
+
key: index,
|
|
356
|
+
src: src,
|
|
357
|
+
type: getTypeFromExtension(src)
|
|
358
|
+
});
|
|
359
|
+
}) : audioSrc ? /*#__PURE__*/React.createElement("source", {
|
|
360
|
+
src: audioSrc,
|
|
361
|
+
type: getTypeFromExtension(audioSrc)
|
|
362
|
+
}) : null, "Your browser does not support the audio element."), /*#__PURE__*/React.createElement("div", {
|
|
363
|
+
className: "player-layout"
|
|
364
|
+
}, volumeCtrl && /*#__PURE__*/React.createElement("div", {
|
|
365
|
+
className: "player-controls-secondary-outer"
|
|
366
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
367
|
+
className: "player-volume-control"
|
|
368
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
369
|
+
className: "player-volume-icon"
|
|
370
|
+
}, /*#__PURE__*/React.createElement("button", {
|
|
371
|
+
onClick: toggleMute,
|
|
372
|
+
"aria-label": isMuted === true ? 'Muted' : 'Not Muted',
|
|
373
|
+
title: isMuted === true ? 'Muted' : 'Not Muted'
|
|
374
|
+
}, !isMuted ? /*#__PURE__*/React.createElement("img", {
|
|
375
|
+
src: "/img/icon-volume-low.svg",
|
|
376
|
+
alt: "Volume Button"
|
|
377
|
+
}) : /*#__PURE__*/React.createElement("img", {
|
|
378
|
+
src: "/img/icon-volume-mute.svg",
|
|
379
|
+
alt: "Volume Mute Button"
|
|
380
|
+
}))), /*#__PURE__*/React.createElement("div", {
|
|
381
|
+
className: "player-timeline player-controls-secondary"
|
|
382
|
+
}, /*#__PURE__*/React.createElement("input", {
|
|
383
|
+
id: "player-volume",
|
|
384
|
+
type: "range",
|
|
385
|
+
className: "player-volume-progress",
|
|
386
|
+
min: 0,
|
|
387
|
+
max: 100,
|
|
388
|
+
defaultValue: 100,
|
|
389
|
+
"aria-hidden": "true",
|
|
390
|
+
"aria-valuetext": "100%",
|
|
391
|
+
onChange: function onChange(e) {
|
|
392
|
+
return volumeControl(e);
|
|
393
|
+
}
|
|
394
|
+
})), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("img", {
|
|
395
|
+
src: "/img/icon-volume-high.svg",
|
|
396
|
+
alt: "Volume Button"
|
|
397
|
+
}))), /*#__PURE__*/React.createElement("div", {
|
|
398
|
+
className: "player-volume-label"
|
|
399
|
+
}, "Volume")), /*#__PURE__*/React.createElement("div", {
|
|
400
|
+
className: "player-controls"
|
|
401
|
+
}, showControls && /*#__PURE__*/React.createElement("div", {
|
|
402
|
+
className: "player-backward-forward-controls"
|
|
403
|
+
}, /*#__PURE__*/React.createElement("button", {
|
|
404
|
+
onClick: rewindControl
|
|
405
|
+
}, /*#__PURE__*/React.createElement("img", {
|
|
406
|
+
src: "/img/icon-rewind-15.svg",
|
|
407
|
+
alt: "Backward 15 seconds"
|
|
408
|
+
}))), /*#__PURE__*/React.createElement("div", {
|
|
409
|
+
className: "".concat(isPlaying ? 'is-playing' : '', " player-btn-play-pause-outer")
|
|
410
|
+
}, /*#__PURE__*/React.createElement("button", {
|
|
411
|
+
onClick: togglePlaying,
|
|
412
|
+
className: playBtnClass,
|
|
413
|
+
style: customStyles && customStyles.playPause,
|
|
414
|
+
id: "playbutton"
|
|
415
|
+
}, isPlaying ? /*#__PURE__*/React.createElement(Pause, null) : /*#__PURE__*/React.createElement(Play, null))), showControls && /*#__PURE__*/React.createElement("div", {
|
|
416
|
+
className: "player-backward-forward-controls"
|
|
417
|
+
}, /*#__PURE__*/React.createElement("button", {
|
|
418
|
+
onClick: forwardControl
|
|
419
|
+
}, /*#__PURE__*/React.createElement("img", {
|
|
420
|
+
src: "/img/icon-forward-15.svg",
|
|
421
|
+
alt: "Forward 15 seconds"
|
|
422
|
+
})))), showControls && /*#__PURE__*/React.createElement("div", {
|
|
423
|
+
className: "player-timeline"
|
|
424
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
425
|
+
className: "player-currentTime"
|
|
426
|
+
}, calculateTime(currentTime)), /*#__PURE__*/React.createElement("div", {
|
|
427
|
+
className: "player-timeline-progress-outer"
|
|
428
|
+
}, /*#__PURE__*/React.createElement("input", {
|
|
429
|
+
type: "range",
|
|
430
|
+
className: "player-timeline-progress",
|
|
431
|
+
defaultValue: "0",
|
|
432
|
+
ref: progressBarRef,
|
|
433
|
+
onChange: changePlayerCurrentTime,
|
|
434
|
+
"aria-label": "Audio progress",
|
|
435
|
+
max: duration
|
|
436
|
+
})), /*#__PURE__*/React.createElement("div", {
|
|
437
|
+
className: "player-duration",
|
|
438
|
+
style: customStyles && customStyles.duration
|
|
439
|
+
}, duration && !isNaN(duration) ? calculateTime(duration) : '-- : --')), /*#__PURE__*/React.createElement("div", {
|
|
440
|
+
className: "player-content"
|
|
441
|
+
}, customHtml && customHtml, /*#__PURE__*/React.createElement("div", {
|
|
442
|
+
className: "player-audio-type type-sm"
|
|
443
|
+
}, duration === Infinity ? /*#__PURE__*/React.createElement("div", {
|
|
444
|
+
className: "player-live-label"
|
|
445
|
+
}, prefix ? prefix : 'On Air') : /*#__PURE__*/React.createElement("div", {
|
|
446
|
+
className: "player-label"
|
|
447
|
+
}, "listen", /*#__PURE__*/React.createElement("div", {
|
|
448
|
+
className: "player-label-duration"
|
|
449
|
+
}, "[".concat(formatDuration, "]"))), /*#__PURE__*/React.createElement("div", {
|
|
450
|
+
className: "player-title"
|
|
451
|
+
}, title || '', " ", subtitle && "by ".concat(subtitle), ' ')))));
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
var ReactAudioPlayer = function ReactAudioPlayer(props) {
|
|
455
|
+
var _props$audioPlayerRef, _props$progressBarRef;
|
|
456
|
+
// references
|
|
457
|
+
var audioPlayerRef = (_props$audioPlayerRef = props.audioPlayerRef) !== null && _props$audioPlayerRef !== void 0 ? _props$audioPlayerRef : useRef(); // reference our audio component
|
|
458
|
+
var progressBarRef = (_props$progressBarRef = props.progressBarRef) !== null && _props$progressBarRef !== void 0 ? _props$progressBarRef : useRef(); // reference our progress bar
|
|
459
|
+
|
|
460
|
+
var customStyles = props ? props.style : '';
|
|
461
|
+
|
|
462
|
+
// hooks
|
|
463
|
+
var _useAudioPlayer = useAudioPlayer(audioPlayerRef, progressBarRef),
|
|
464
|
+
isPlaying = _useAudioPlayer.isPlaying,
|
|
465
|
+
currentTime = _useAudioPlayer.currentTime,
|
|
466
|
+
duration = _useAudioPlayer.duration,
|
|
467
|
+
isMuted = _useAudioPlayer.isMuted,
|
|
468
|
+
onLoadedMetadata = _useAudioPlayer.onLoadedMetadata,
|
|
469
|
+
calculateTime = _useAudioPlayer.calculateTime,
|
|
470
|
+
togglePlaying = _useAudioPlayer.togglePlaying,
|
|
471
|
+
changePlayerCurrentTime = _useAudioPlayer.changePlayerCurrentTime,
|
|
472
|
+
volumeControl = _useAudioPlayer.volumeControl,
|
|
473
|
+
toggleMute = _useAudioPlayer.toggleMute,
|
|
474
|
+
formatCalculateTime = _useAudioPlayer.formatCalculateTime,
|
|
475
|
+
rewindControl = _useAudioPlayer.rewindControl,
|
|
476
|
+
forwardControl = _useAudioPlayer.forwardControl;
|
|
477
|
+
return /*#__PURE__*/React.createElement(ReactAudioPlayerInner, _extends({}, props, {
|
|
478
|
+
audioPlayerRef: audioPlayerRef,
|
|
479
|
+
progressBarRef: progressBarRef,
|
|
480
|
+
isPlaying: isPlaying,
|
|
481
|
+
isMuted: isMuted,
|
|
482
|
+
currentTime: currentTime,
|
|
483
|
+
duration: duration,
|
|
484
|
+
customStyles: customStyles,
|
|
485
|
+
onLoadedMetadata: onLoadedMetadata,
|
|
486
|
+
calculateTime: calculateTime,
|
|
487
|
+
togglePlaying: togglePlaying,
|
|
488
|
+
changePlayerCurrentTime: changePlayerCurrentTime,
|
|
489
|
+
volumeControl: volumeControl,
|
|
490
|
+
toggleMute: toggleMute,
|
|
491
|
+
formatCalculateTime: formatCalculateTime,
|
|
492
|
+
rewindControl: rewindControl,
|
|
493
|
+
forwardControl: forwardControl
|
|
494
|
+
}));
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
export { ReactAudioPlayer, ReactAudioPlayerInner, useAudioPlayer };
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apm-react-audio-player",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"author": "Jason Phan <jphan@mpr.org>",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "APMG/apm-react-audio-player.git"
|
|
9
|
+
"url": "git+https://github.com/APMG/apm-react-audio-player.git"
|
|
10
10
|
},
|
|
11
11
|
"main": "dist/index.js",
|
|
12
12
|
"module": "dist/index.modern.js",
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
},
|
|
17
17
|
"scripts": {
|
|
18
18
|
"build": "rollup -c rollup.config.mjs",
|
|
19
|
+
"build:examples": "esbuild examples/hls-example.jsx --bundle --format=esm --outfile=examples/bundle.js --external:react --external:react-dom --external:react/jsx-runtime --loader:.js=jsx",
|
|
20
|
+
"serve:examples": "npx http-server . -p 8000 -o /examples/index.html",
|
|
19
21
|
"dev": "rollup -c -w",
|
|
20
22
|
"test": "jest --watch",
|
|
21
23
|
"test:ci": "jest",
|
|
@@ -32,7 +34,7 @@
|
|
|
32
34
|
"react": "^16.0.0"
|
|
33
35
|
},
|
|
34
36
|
"dependencies": {
|
|
35
|
-
"rollup": "^4.
|
|
37
|
+
"rollup": "^4.59.0",
|
|
36
38
|
"rollup-plugin-babel": "^4.3.3"
|
|
37
39
|
},
|
|
38
40
|
"devDependencies": {
|
|
@@ -41,8 +43,9 @@
|
|
|
41
43
|
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
|
42
44
|
"@babel/preset-env": "^7.5.5",
|
|
43
45
|
"@babel/preset-react": "^7.0.0",
|
|
44
|
-
"@testing-library/react": "^
|
|
46
|
+
"@testing-library/react": "^14.0.0",
|
|
45
47
|
"cross-env": "^7.0.2",
|
|
48
|
+
"esbuild": "^0.27.4",
|
|
46
49
|
"eslint": "^6.8.0",
|
|
47
50
|
"eslint-config-prettier": "^6.7.0",
|
|
48
51
|
"eslint-config-standard": "^14.1.0",
|
|
@@ -53,8 +56,8 @@
|
|
|
53
56
|
"eslint-plugin-promise": "^4.2.1",
|
|
54
57
|
"eslint-plugin-react": "^7.17.0",
|
|
55
58
|
"eslint-plugin-standard": "^4.0.1",
|
|
56
|
-
"jest": "^
|
|
57
|
-
"jest-environment-jsdom
|
|
59
|
+
"jest": "^29.0.0",
|
|
60
|
+
"jest-environment-jsdom": "^30.3.0",
|
|
58
61
|
"jest-prop-type-error": "^1.1.0",
|
|
59
62
|
"prettier": "^2.0.4",
|
|
60
63
|
"react": "^18",
|
|
@@ -63,9 +66,25 @@
|
|
|
63
66
|
},
|
|
64
67
|
"resolutions": {
|
|
65
68
|
"braces": ">=3.0.3",
|
|
66
|
-
"react-scripts": "^5.0.1"
|
|
69
|
+
"react-scripts": "^5.0.1",
|
|
70
|
+
"micromatch": ">=4.0.8",
|
|
71
|
+
"minimatch": ">=3.1.4",
|
|
72
|
+
"tough-cookie": ">=4.1.3",
|
|
73
|
+
"ajv": ">=6.14.0",
|
|
74
|
+
"qs": ">=6.14.1",
|
|
75
|
+
"lodash": ">=4.17.21",
|
|
76
|
+
"js-yaml": ">=4.1.0",
|
|
77
|
+
"@babel/helpers": ">=7.26.0",
|
|
78
|
+
"@babel/runtime": ">=7.26.0",
|
|
79
|
+
"flatted": ">=3.3.3",
|
|
80
|
+
"brace-expansion": ">=2.0.1",
|
|
81
|
+
"tmp": ">=0.2.3"
|
|
67
82
|
},
|
|
68
83
|
"files": [
|
|
69
84
|
"dist"
|
|
70
|
-
]
|
|
85
|
+
],
|
|
86
|
+
"jest": {
|
|
87
|
+
"testEnvironment": "jsdom",
|
|
88
|
+
"setupFilesAfterEnv": ["<rootDir>/jest.setup.js"]
|
|
89
|
+
}
|
|
71
90
|
}
|