apm-react-audio-player 1.0.27 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +183 -4
- package/dist/index.js +96 -21
- package/dist/index.modern.js +508 -0
- package/package.json +29 -11
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;
|
|
@@ -83,32 +83,41 @@ var useAudioPlayer = function useAudioPlayer(audioRef, progressBarRef, volumeCtr
|
|
|
83
83
|
_useState10 = _slicedToArray(_useState9, 2),
|
|
84
84
|
isMuted = _useState10[0],
|
|
85
85
|
setIsMuted = _useState10[1];
|
|
86
|
-
var isStream = audioRef.current && audioRef.current.
|
|
86
|
+
var isStream = audioRef.current && audioRef.current.duration === Infinity;
|
|
87
87
|
React.useEffect(function () {
|
|
88
88
|
if (currentTime === Number(duration)) {
|
|
89
89
|
// restart()
|
|
90
90
|
setIsFinishedPlaying(true);
|
|
91
91
|
}
|
|
92
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]);
|
|
93
99
|
var onLoadedMetadata = function onLoadedMetadata() {
|
|
94
|
-
|
|
100
|
+
if (!audioRef.current) return;
|
|
95
101
|
var seconds = Math.floor(audioRef.current.duration);
|
|
96
102
|
setDuration(seconds);
|
|
97
|
-
if (
|
|
103
|
+
if (audioRef.current.duration !== Infinity && progressBarRef.current) {
|
|
98
104
|
progressBarRef.current.max = seconds;
|
|
99
105
|
}
|
|
100
106
|
};
|
|
101
107
|
var updateCurrentTime = function updateCurrentTime() {
|
|
102
|
-
|
|
108
|
+
if (progressBarRef.current) {
|
|
109
|
+
setCurrentTime(progressBarRef.current.value);
|
|
110
|
+
}
|
|
103
111
|
};
|
|
104
112
|
var _whilePlaying = function whilePlaying() {
|
|
105
|
-
|
|
106
|
-
if (!
|
|
107
|
-
|
|
108
|
-
|
|
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) {
|
|
109
118
|
progressBarRef.current.value = Math.floor(audioRef.current.currentTime);
|
|
119
|
+
progressBarRef.current.style.setProperty('--seek-before-width', "".concat(progressBarRef.current.value / duration * 100, "%"));
|
|
110
120
|
}
|
|
111
|
-
progressBarRef.current.style.setProperty('--seek-before-width', "".concat(progressBarRef.current.value / duration * 100, "%"));
|
|
112
121
|
updateCurrentTime();
|
|
113
122
|
|
|
114
123
|
// when you reach the end of the song
|
|
@@ -134,9 +143,22 @@ var useAudioPlayer = function useAudioPlayer(audioRef, progressBarRef, volumeCtr
|
|
|
134
143
|
var play = function play() {
|
|
135
144
|
setIsPlaying(true);
|
|
136
145
|
setIsFinishedPlaying(false);
|
|
146
|
+
|
|
147
|
+
// For live streams (duration === Infinity), reset the audio element before
|
|
148
|
+
// playing. The audioSrc useEffect in ReactAudioPlayerInner calls load() on
|
|
149
|
+
// mount, which causes the browser to pre-buffer the HLS stream. By the time
|
|
150
|
+
// the user clicks play the manifest's seekable window has moved forward and
|
|
151
|
+
// old segments are gone, so the browser auto-seeks to the earliest available
|
|
152
|
+
// position (e.g. 20s in), causing ads to start mid-stream. Calling load()
|
|
153
|
+
// here reconnects to the current live edge and gets a fresh manifest.
|
|
154
|
+
if (duration === Infinity) {
|
|
155
|
+
audioRef.current.load();
|
|
156
|
+
}
|
|
137
157
|
audioRef.current.play();
|
|
138
|
-
|
|
139
|
-
|
|
158
|
+
|
|
159
|
+
// Only start RAF loop for non-live streams with valid duration
|
|
160
|
+
var dur = audioRef.current.duration;
|
|
161
|
+
if (dur !== Infinity && !isNaN(dur) && isFinite(dur)) {
|
|
140
162
|
animationRef.current = window.requestAnimationFrame(_whilePlaying);
|
|
141
163
|
}
|
|
142
164
|
};
|
|
@@ -158,24 +180,29 @@ var useAudioPlayer = function useAudioPlayer(audioRef, progressBarRef, volumeCtr
|
|
|
158
180
|
}
|
|
159
181
|
};
|
|
160
182
|
var changePlayerCurrentTime = function changePlayerCurrentTime() {
|
|
183
|
+
if (!progressBarRef.current || !audioRef.current) return;
|
|
161
184
|
audioRef.current.currentTime = progressBarRef.current.value;
|
|
162
185
|
setCurrentTime(progressBarRef.current.value);
|
|
163
186
|
progressBarRef.current.style.setProperty('--seek-before-width', "".concat(progressBarRef.current.value / duration * 100, "%"));
|
|
164
187
|
};
|
|
165
188
|
var changeRange = function changeRange() {
|
|
189
|
+
if (!progressBarRef.current || !audioRef.current) return;
|
|
166
190
|
audioRef.current.currentTime = progressBarRef.current.value;
|
|
167
191
|
updateCurrentTime();
|
|
168
192
|
changePlayerCurrentTime();
|
|
169
193
|
};
|
|
170
194
|
var rewindControl = function rewindControl() {
|
|
195
|
+
if (!progressBarRef.current) return;
|
|
171
196
|
progressBarRef.current.value = Number(progressBarRef.current.value) - 15;
|
|
172
197
|
changeRange();
|
|
173
198
|
};
|
|
174
199
|
var forwardControl = function forwardControl() {
|
|
200
|
+
if (!progressBarRef.current) return;
|
|
175
201
|
progressBarRef.current.value = Number(progressBarRef.current.value) + 15;
|
|
176
202
|
changeRange();
|
|
177
203
|
};
|
|
178
204
|
var volumeControl = function volumeControl(e) {
|
|
205
|
+
if (!audioRef.current) return;
|
|
179
206
|
var value = e.target.value;
|
|
180
207
|
var volume = value / 100;
|
|
181
208
|
audioRef.current.volume = volume;
|
|
@@ -259,6 +286,24 @@ var Pause = function Pause() {
|
|
|
259
286
|
}));
|
|
260
287
|
};
|
|
261
288
|
|
|
289
|
+
var getTypeFromExtension = function getTypeFromExtension(url) {
|
|
290
|
+
var extension = url.split('.').pop().split('?')[0];
|
|
291
|
+
switch (extension) {
|
|
292
|
+
case 'm3u8':
|
|
293
|
+
return 'application/x-mpegURL';
|
|
294
|
+
case 'aac':
|
|
295
|
+
return 'audio/aac';
|
|
296
|
+
case 'mp3':
|
|
297
|
+
return 'audio/mpeg';
|
|
298
|
+
case 'ogg':
|
|
299
|
+
return 'audio/ogg';
|
|
300
|
+
case 'wav':
|
|
301
|
+
return 'audio/wav';
|
|
302
|
+
default:
|
|
303
|
+
return undefined;
|
|
304
|
+
// Let browser auto-detect
|
|
305
|
+
}
|
|
306
|
+
};
|
|
262
307
|
var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
263
308
|
var _props$audioPlayerRef, _props$progressBarRef;
|
|
264
309
|
// references
|
|
@@ -271,7 +316,6 @@ var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
|
271
316
|
volumeCtrl = props.volumeCtrl,
|
|
272
317
|
playBtnClass = props.playBtnClass,
|
|
273
318
|
customHtml = props.customHtml,
|
|
274
|
-
isLive = props.isLive,
|
|
275
319
|
onLoadedMetadata = props.onLoadedMetadata,
|
|
276
320
|
calculateTime = props.calculateTime,
|
|
277
321
|
togglePlaying = props.togglePlaying,
|
|
@@ -289,16 +333,46 @@ var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
|
289
333
|
prefix = props.prefix;
|
|
290
334
|
var audioDuration = duration && !isNaN(duration) && calculateTime(duration);
|
|
291
335
|
var formatDuration = duration && !isNaN(duration) && audioDuration && formatCalculateTime(audioDuration);
|
|
336
|
+
|
|
337
|
+
// Reload audio when audioSrc changes
|
|
338
|
+
// Use JSON.stringify to handle array comparisons by value instead of reference
|
|
339
|
+
React.useEffect(function () {
|
|
340
|
+
if (audioPlayerRef.current && audioSrc) {
|
|
341
|
+
try {
|
|
342
|
+
audioPlayerRef.current.load();
|
|
343
|
+
} catch (err) {
|
|
344
|
+
console.warn('Failed to reload audio source:', err);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}, [JSON.stringify(audioSrc)]);
|
|
348
|
+
|
|
349
|
+
// Set initial volume to 100%
|
|
350
|
+
React.useEffect(function () {
|
|
351
|
+
if (audioPlayerRef.current) {
|
|
352
|
+
audioPlayerRef.current.volume = 1.0;
|
|
353
|
+
}
|
|
354
|
+
}, []);
|
|
355
|
+
|
|
356
|
+
// Helper to determine if controls should show
|
|
357
|
+
var showControls = duration !== Infinity && duration !== undefined && !isNaN(duration) && isFinite(duration);
|
|
292
358
|
return audioSrc && /*#__PURE__*/React.createElement("div", {
|
|
293
359
|
className: "audioPlayer",
|
|
294
360
|
style: customStyles && customStyles.audioPlayer
|
|
295
361
|
}, /*#__PURE__*/React.createElement("audio", {
|
|
296
362
|
ref: audioPlayerRef,
|
|
297
|
-
src: audioSrc,
|
|
298
363
|
preload: "none",
|
|
299
364
|
onLoadedMetadata: onLoadedMetadata,
|
|
300
365
|
muted: isMuted
|
|
301
|
-
}
|
|
366
|
+
}, Array.isArray(audioSrc) ? audioSrc.map(function (src, index) {
|
|
367
|
+
return /*#__PURE__*/React.createElement("source", {
|
|
368
|
+
key: index,
|
|
369
|
+
src: src,
|
|
370
|
+
type: getTypeFromExtension(src)
|
|
371
|
+
});
|
|
372
|
+
}) : audioSrc ? /*#__PURE__*/React.createElement("source", {
|
|
373
|
+
src: audioSrc,
|
|
374
|
+
type: getTypeFromExtension(audioSrc)
|
|
375
|
+
}) : null, "Your browser does not support the audio element."), /*#__PURE__*/React.createElement("div", {
|
|
302
376
|
className: "player-layout"
|
|
303
377
|
}, volumeCtrl && /*#__PURE__*/React.createElement("div", {
|
|
304
378
|
className: "player-controls-secondary-outer"
|
|
@@ -324,6 +398,7 @@ var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
|
324
398
|
className: "player-volume-progress",
|
|
325
399
|
min: 0,
|
|
326
400
|
max: 100,
|
|
401
|
+
defaultValue: 100,
|
|
327
402
|
"aria-hidden": "true",
|
|
328
403
|
"aria-valuetext": "100%",
|
|
329
404
|
onChange: function onChange(e) {
|
|
@@ -336,7 +411,7 @@ var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
|
336
411
|
className: "player-volume-label"
|
|
337
412
|
}, "Volume")), /*#__PURE__*/React.createElement("div", {
|
|
338
413
|
className: "player-controls"
|
|
339
|
-
},
|
|
414
|
+
}, showControls && /*#__PURE__*/React.createElement("div", {
|
|
340
415
|
className: "player-backward-forward-controls"
|
|
341
416
|
}, /*#__PURE__*/React.createElement("button", {
|
|
342
417
|
onClick: rewindControl
|
|
@@ -350,14 +425,14 @@ var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
|
350
425
|
className: playBtnClass,
|
|
351
426
|
style: customStyles && customStyles.playPause,
|
|
352
427
|
id: "playbutton"
|
|
353
|
-
}, isPlaying ? /*#__PURE__*/React.createElement(Pause, null) : /*#__PURE__*/React.createElement(Play, null))),
|
|
428
|
+
}, isPlaying ? /*#__PURE__*/React.createElement(Pause, null) : /*#__PURE__*/React.createElement(Play, null))), showControls && /*#__PURE__*/React.createElement("div", {
|
|
354
429
|
className: "player-backward-forward-controls"
|
|
355
430
|
}, /*#__PURE__*/React.createElement("button", {
|
|
356
431
|
onClick: forwardControl
|
|
357
432
|
}, /*#__PURE__*/React.createElement("img", {
|
|
358
433
|
src: "/img/icon-forward-15.svg",
|
|
359
434
|
alt: "Forward 15 seconds"
|
|
360
|
-
})))),
|
|
435
|
+
})))), showControls && /*#__PURE__*/React.createElement("div", {
|
|
361
436
|
className: "player-timeline"
|
|
362
437
|
}, /*#__PURE__*/React.createElement("div", {
|
|
363
438
|
className: "player-currentTime"
|
|
@@ -378,7 +453,7 @@ var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
|
378
453
|
className: "player-content"
|
|
379
454
|
}, customHtml && customHtml, /*#__PURE__*/React.createElement("div", {
|
|
380
455
|
className: "player-audio-type type-sm"
|
|
381
|
-
},
|
|
456
|
+
}, duration === Infinity ? /*#__PURE__*/React.createElement("div", {
|
|
382
457
|
className: "player-live-label"
|
|
383
458
|
}, prefix ? prefix : 'On Air') : /*#__PURE__*/React.createElement("div", {
|
|
384
459
|
className: "player-label"
|
|
@@ -0,0 +1,508 @@
|
|
|
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
|
+
|
|
145
|
+
// For live streams (duration === Infinity), reset the audio element before
|
|
146
|
+
// playing. The audioSrc useEffect in ReactAudioPlayerInner calls load() on
|
|
147
|
+
// mount, which causes the browser to pre-buffer the HLS stream. By the time
|
|
148
|
+
// the user clicks play the manifest's seekable window has moved forward and
|
|
149
|
+
// old segments are gone, so the browser auto-seeks to the earliest available
|
|
150
|
+
// position (e.g. 20s in), causing ads to start mid-stream. Calling load()
|
|
151
|
+
// here reconnects to the current live edge and gets a fresh manifest.
|
|
152
|
+
if (duration === Infinity) {
|
|
153
|
+
audioRef.current.load();
|
|
154
|
+
}
|
|
155
|
+
audioRef.current.play();
|
|
156
|
+
|
|
157
|
+
// Only start RAF loop for non-live streams with valid duration
|
|
158
|
+
var dur = audioRef.current.duration;
|
|
159
|
+
if (dur !== Infinity && !isNaN(dur) && isFinite(dur)) {
|
|
160
|
+
animationRef.current = window.requestAnimationFrame(_whilePlaying);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
var toggleMute = function toggleMute() {
|
|
164
|
+
setIsMuted(!isMuted);
|
|
165
|
+
};
|
|
166
|
+
var calculateTime = function calculateTime(secs) {
|
|
167
|
+
var minutes = Math.floor(secs / 60);
|
|
168
|
+
var returnedMinutes = minutes < 10 ? "0".concat(minutes) : "".concat(minutes);
|
|
169
|
+
var seconds = Math.floor(secs % 60);
|
|
170
|
+
var returnedSeconds = seconds < 10 ? "0".concat(seconds) : "".concat(seconds);
|
|
171
|
+
return "".concat(returnedMinutes, ":").concat(returnedSeconds);
|
|
172
|
+
};
|
|
173
|
+
var togglePlaying = function togglePlaying() {
|
|
174
|
+
if (!isPlaying) {
|
|
175
|
+
play();
|
|
176
|
+
} else {
|
|
177
|
+
pause();
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
var changePlayerCurrentTime = function changePlayerCurrentTime() {
|
|
181
|
+
if (!progressBarRef.current || !audioRef.current) return;
|
|
182
|
+
audioRef.current.currentTime = progressBarRef.current.value;
|
|
183
|
+
setCurrentTime(progressBarRef.current.value);
|
|
184
|
+
progressBarRef.current.style.setProperty('--seek-before-width', "".concat(progressBarRef.current.value / duration * 100, "%"));
|
|
185
|
+
};
|
|
186
|
+
var changeRange = function changeRange() {
|
|
187
|
+
if (!progressBarRef.current || !audioRef.current) return;
|
|
188
|
+
audioRef.current.currentTime = progressBarRef.current.value;
|
|
189
|
+
updateCurrentTime();
|
|
190
|
+
changePlayerCurrentTime();
|
|
191
|
+
};
|
|
192
|
+
var rewindControl = function rewindControl() {
|
|
193
|
+
if (!progressBarRef.current) return;
|
|
194
|
+
progressBarRef.current.value = Number(progressBarRef.current.value) - 15;
|
|
195
|
+
changeRange();
|
|
196
|
+
};
|
|
197
|
+
var forwardControl = function forwardControl() {
|
|
198
|
+
if (!progressBarRef.current) return;
|
|
199
|
+
progressBarRef.current.value = Number(progressBarRef.current.value) + 15;
|
|
200
|
+
changeRange();
|
|
201
|
+
};
|
|
202
|
+
var volumeControl = function volumeControl(e) {
|
|
203
|
+
if (!audioRef.current) return;
|
|
204
|
+
var value = e.target.value;
|
|
205
|
+
var volume = value / 100;
|
|
206
|
+
audioRef.current.volume = volume;
|
|
207
|
+
};
|
|
208
|
+
var formatCalculateTime = function formatCalculateTime(timeString) {
|
|
209
|
+
var toString = String(timeString);
|
|
210
|
+
if (toString.split(':').length === 3) {
|
|
211
|
+
var _toString$split = toString.split(':'),
|
|
212
|
+
_toString$split2 = _slicedToArray(_toString$split, 3),
|
|
213
|
+
hours = _toString$split2[0],
|
|
214
|
+
minutes = _toString$split2[1],
|
|
215
|
+
seconds = _toString$split2[2];
|
|
216
|
+
return "".concat(parseInt(hours), "hr ").concat(parseInt(minutes), "min ").concat(parseInt(seconds), "sec");
|
|
217
|
+
} else if (toString.split(':').length === 2) {
|
|
218
|
+
var _toString$split3 = toString.split(':'),
|
|
219
|
+
_toString$split4 = _slicedToArray(_toString$split3, 2),
|
|
220
|
+
_minutes = _toString$split4[0],
|
|
221
|
+
_seconds = _toString$split4[1];
|
|
222
|
+
return "".concat(parseInt(_minutes), "min ").concat(parseInt(_seconds), "sec");
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
return {
|
|
226
|
+
onLoadedMetadata: onLoadedMetadata,
|
|
227
|
+
calculateTime: calculateTime,
|
|
228
|
+
togglePlaying: togglePlaying,
|
|
229
|
+
changePlayerCurrentTime: changePlayerCurrentTime,
|
|
230
|
+
rewindControl: rewindControl,
|
|
231
|
+
forwardControl: forwardControl,
|
|
232
|
+
play: play,
|
|
233
|
+
pause: pause,
|
|
234
|
+
isPlaying: isPlaying,
|
|
235
|
+
isFinishedPlaying: isFinishedPlaying,
|
|
236
|
+
currentTime: currentTime,
|
|
237
|
+
duration: duration,
|
|
238
|
+
volumeCtrl: volumeCtrl,
|
|
239
|
+
isMuted: isMuted,
|
|
240
|
+
volumeControl: volumeControl,
|
|
241
|
+
toggleMute: toggleMute,
|
|
242
|
+
formatCalculateTime: formatCalculateTime
|
|
243
|
+
};
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
var Play = function Play() {
|
|
247
|
+
return /*#__PURE__*/React.createElement("svg", {
|
|
248
|
+
stroke: "currentColor",
|
|
249
|
+
fill: "currentColor",
|
|
250
|
+
strokeWidth: "0",
|
|
251
|
+
viewBox: "0 0 448 512",
|
|
252
|
+
className: "play",
|
|
253
|
+
height: "1em",
|
|
254
|
+
width: "1em",
|
|
255
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
256
|
+
role: "img",
|
|
257
|
+
"aria-labelledby": "play playButton"
|
|
258
|
+
}, /*#__PURE__*/React.createElement("title", {
|
|
259
|
+
id: "play"
|
|
260
|
+
}, "Play"), /*#__PURE__*/React.createElement("desc", {
|
|
261
|
+
id: "playButton"
|
|
262
|
+
}, "Play Button"), /*#__PURE__*/React.createElement("path", {
|
|
263
|
+
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"
|
|
264
|
+
}));
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
var Pause = function Pause() {
|
|
268
|
+
return /*#__PURE__*/React.createElement("svg", {
|
|
269
|
+
stroke: "currentColor",
|
|
270
|
+
fill: "currentColor",
|
|
271
|
+
strokeWidth: "0",
|
|
272
|
+
viewBox: "0 0 448 512",
|
|
273
|
+
height: "1em",
|
|
274
|
+
width: "1em",
|
|
275
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
276
|
+
role: "img",
|
|
277
|
+
"aria-labelledby": "pause pauseButton"
|
|
278
|
+
}, /*#__PURE__*/React.createElement("title", {
|
|
279
|
+
id: "pause"
|
|
280
|
+
}, "Pause"), /*#__PURE__*/React.createElement("desc", {
|
|
281
|
+
id: "pauseButton"
|
|
282
|
+
}, "Pause Button"), /*#__PURE__*/React.createElement("path", {
|
|
283
|
+
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"
|
|
284
|
+
}));
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
var getTypeFromExtension = function getTypeFromExtension(url) {
|
|
288
|
+
var extension = url.split('.').pop().split('?')[0];
|
|
289
|
+
switch (extension) {
|
|
290
|
+
case 'm3u8':
|
|
291
|
+
return 'application/x-mpegURL';
|
|
292
|
+
case 'aac':
|
|
293
|
+
return 'audio/aac';
|
|
294
|
+
case 'mp3':
|
|
295
|
+
return 'audio/mpeg';
|
|
296
|
+
case 'ogg':
|
|
297
|
+
return 'audio/ogg';
|
|
298
|
+
case 'wav':
|
|
299
|
+
return 'audio/wav';
|
|
300
|
+
default:
|
|
301
|
+
return undefined;
|
|
302
|
+
// Let browser auto-detect
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
306
|
+
var _props$audioPlayerRef, _props$progressBarRef;
|
|
307
|
+
// references
|
|
308
|
+
var audioPlayerRef = (_props$audioPlayerRef = props.audioPlayerRef) !== null && _props$audioPlayerRef !== void 0 ? _props$audioPlayerRef : useRef(); // reference our audio component
|
|
309
|
+
var progressBarRef = (_props$progressBarRef = props.progressBarRef) !== null && _props$progressBarRef !== void 0 ? _props$progressBarRef : useRef(); // reference our progress bar
|
|
310
|
+
|
|
311
|
+
var customStyles = props ? props.style : '';
|
|
312
|
+
var title = props.title,
|
|
313
|
+
audioSrc = props.audioSrc,
|
|
314
|
+
volumeCtrl = props.volumeCtrl,
|
|
315
|
+
playBtnClass = props.playBtnClass,
|
|
316
|
+
customHtml = props.customHtml,
|
|
317
|
+
onLoadedMetadata = props.onLoadedMetadata,
|
|
318
|
+
calculateTime = props.calculateTime,
|
|
319
|
+
togglePlaying = props.togglePlaying,
|
|
320
|
+
changePlayerCurrentTime = props.changePlayerCurrentTime,
|
|
321
|
+
isPlaying = props.isPlaying,
|
|
322
|
+
currentTime = props.currentTime,
|
|
323
|
+
duration = props.duration,
|
|
324
|
+
volumeControl = props.volumeControl,
|
|
325
|
+
toggleMute = props.toggleMute,
|
|
326
|
+
isMuted = props.isMuted,
|
|
327
|
+
formatCalculateTime = props.formatCalculateTime,
|
|
328
|
+
rewindControl = props.rewindControl,
|
|
329
|
+
forwardControl = props.forwardControl,
|
|
330
|
+
subtitle = props.subtitle,
|
|
331
|
+
prefix = props.prefix;
|
|
332
|
+
var audioDuration = duration && !isNaN(duration) && calculateTime(duration);
|
|
333
|
+
var formatDuration = duration && !isNaN(duration) && audioDuration && formatCalculateTime(audioDuration);
|
|
334
|
+
|
|
335
|
+
// Reload audio when audioSrc changes
|
|
336
|
+
// Use JSON.stringify to handle array comparisons by value instead of reference
|
|
337
|
+
useEffect(function () {
|
|
338
|
+
if (audioPlayerRef.current && audioSrc) {
|
|
339
|
+
try {
|
|
340
|
+
audioPlayerRef.current.load();
|
|
341
|
+
} catch (err) {
|
|
342
|
+
console.warn('Failed to reload audio source:', err);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}, [JSON.stringify(audioSrc)]);
|
|
346
|
+
|
|
347
|
+
// Set initial volume to 100%
|
|
348
|
+
useEffect(function () {
|
|
349
|
+
if (audioPlayerRef.current) {
|
|
350
|
+
audioPlayerRef.current.volume = 1.0;
|
|
351
|
+
}
|
|
352
|
+
}, []);
|
|
353
|
+
|
|
354
|
+
// Helper to determine if controls should show
|
|
355
|
+
var showControls = duration !== Infinity && duration !== undefined && !isNaN(duration) && isFinite(duration);
|
|
356
|
+
return audioSrc && /*#__PURE__*/React.createElement("div", {
|
|
357
|
+
className: "audioPlayer",
|
|
358
|
+
style: customStyles && customStyles.audioPlayer
|
|
359
|
+
}, /*#__PURE__*/React.createElement("audio", {
|
|
360
|
+
ref: audioPlayerRef,
|
|
361
|
+
preload: "none",
|
|
362
|
+
onLoadedMetadata: onLoadedMetadata,
|
|
363
|
+
muted: isMuted
|
|
364
|
+
}, Array.isArray(audioSrc) ? audioSrc.map(function (src, index) {
|
|
365
|
+
return /*#__PURE__*/React.createElement("source", {
|
|
366
|
+
key: index,
|
|
367
|
+
src: src,
|
|
368
|
+
type: getTypeFromExtension(src)
|
|
369
|
+
});
|
|
370
|
+
}) : audioSrc ? /*#__PURE__*/React.createElement("source", {
|
|
371
|
+
src: audioSrc,
|
|
372
|
+
type: getTypeFromExtension(audioSrc)
|
|
373
|
+
}) : null, "Your browser does not support the audio element."), /*#__PURE__*/React.createElement("div", {
|
|
374
|
+
className: "player-layout"
|
|
375
|
+
}, volumeCtrl && /*#__PURE__*/React.createElement("div", {
|
|
376
|
+
className: "player-controls-secondary-outer"
|
|
377
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
378
|
+
className: "player-volume-control"
|
|
379
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
380
|
+
className: "player-volume-icon"
|
|
381
|
+
}, /*#__PURE__*/React.createElement("button", {
|
|
382
|
+
onClick: toggleMute,
|
|
383
|
+
"aria-label": isMuted === true ? 'Muted' : 'Not Muted',
|
|
384
|
+
title: isMuted === true ? 'Muted' : 'Not Muted'
|
|
385
|
+
}, !isMuted ? /*#__PURE__*/React.createElement("img", {
|
|
386
|
+
src: "/img/icon-volume-low.svg",
|
|
387
|
+
alt: "Volume Button"
|
|
388
|
+
}) : /*#__PURE__*/React.createElement("img", {
|
|
389
|
+
src: "/img/icon-volume-mute.svg",
|
|
390
|
+
alt: "Volume Mute Button"
|
|
391
|
+
}))), /*#__PURE__*/React.createElement("div", {
|
|
392
|
+
className: "player-timeline player-controls-secondary"
|
|
393
|
+
}, /*#__PURE__*/React.createElement("input", {
|
|
394
|
+
id: "player-volume",
|
|
395
|
+
type: "range",
|
|
396
|
+
className: "player-volume-progress",
|
|
397
|
+
min: 0,
|
|
398
|
+
max: 100,
|
|
399
|
+
defaultValue: 100,
|
|
400
|
+
"aria-hidden": "true",
|
|
401
|
+
"aria-valuetext": "100%",
|
|
402
|
+
onChange: function onChange(e) {
|
|
403
|
+
return volumeControl(e);
|
|
404
|
+
}
|
|
405
|
+
})), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("img", {
|
|
406
|
+
src: "/img/icon-volume-high.svg",
|
|
407
|
+
alt: "Volume Button"
|
|
408
|
+
}))), /*#__PURE__*/React.createElement("div", {
|
|
409
|
+
className: "player-volume-label"
|
|
410
|
+
}, "Volume")), /*#__PURE__*/React.createElement("div", {
|
|
411
|
+
className: "player-controls"
|
|
412
|
+
}, showControls && /*#__PURE__*/React.createElement("div", {
|
|
413
|
+
className: "player-backward-forward-controls"
|
|
414
|
+
}, /*#__PURE__*/React.createElement("button", {
|
|
415
|
+
onClick: rewindControl
|
|
416
|
+
}, /*#__PURE__*/React.createElement("img", {
|
|
417
|
+
src: "/img/icon-rewind-15.svg",
|
|
418
|
+
alt: "Backward 15 seconds"
|
|
419
|
+
}))), /*#__PURE__*/React.createElement("div", {
|
|
420
|
+
className: "".concat(isPlaying ? 'is-playing' : '', " player-btn-play-pause-outer")
|
|
421
|
+
}, /*#__PURE__*/React.createElement("button", {
|
|
422
|
+
onClick: togglePlaying,
|
|
423
|
+
className: playBtnClass,
|
|
424
|
+
style: customStyles && customStyles.playPause,
|
|
425
|
+
id: "playbutton"
|
|
426
|
+
}, isPlaying ? /*#__PURE__*/React.createElement(Pause, null) : /*#__PURE__*/React.createElement(Play, null))), showControls && /*#__PURE__*/React.createElement("div", {
|
|
427
|
+
className: "player-backward-forward-controls"
|
|
428
|
+
}, /*#__PURE__*/React.createElement("button", {
|
|
429
|
+
onClick: forwardControl
|
|
430
|
+
}, /*#__PURE__*/React.createElement("img", {
|
|
431
|
+
src: "/img/icon-forward-15.svg",
|
|
432
|
+
alt: "Forward 15 seconds"
|
|
433
|
+
})))), showControls && /*#__PURE__*/React.createElement("div", {
|
|
434
|
+
className: "player-timeline"
|
|
435
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
436
|
+
className: "player-currentTime"
|
|
437
|
+
}, calculateTime(currentTime)), /*#__PURE__*/React.createElement("div", {
|
|
438
|
+
className: "player-timeline-progress-outer"
|
|
439
|
+
}, /*#__PURE__*/React.createElement("input", {
|
|
440
|
+
type: "range",
|
|
441
|
+
className: "player-timeline-progress",
|
|
442
|
+
defaultValue: "0",
|
|
443
|
+
ref: progressBarRef,
|
|
444
|
+
onChange: changePlayerCurrentTime,
|
|
445
|
+
"aria-label": "Audio progress",
|
|
446
|
+
max: duration
|
|
447
|
+
})), /*#__PURE__*/React.createElement("div", {
|
|
448
|
+
className: "player-duration",
|
|
449
|
+
style: customStyles && customStyles.duration
|
|
450
|
+
}, duration && !isNaN(duration) ? calculateTime(duration) : '-- : --')), /*#__PURE__*/React.createElement("div", {
|
|
451
|
+
className: "player-content"
|
|
452
|
+
}, customHtml && customHtml, /*#__PURE__*/React.createElement("div", {
|
|
453
|
+
className: "player-audio-type type-sm"
|
|
454
|
+
}, duration === Infinity ? /*#__PURE__*/React.createElement("div", {
|
|
455
|
+
className: "player-live-label"
|
|
456
|
+
}, prefix ? prefix : 'On Air') : /*#__PURE__*/React.createElement("div", {
|
|
457
|
+
className: "player-label"
|
|
458
|
+
}, "listen", /*#__PURE__*/React.createElement("div", {
|
|
459
|
+
className: "player-label-duration"
|
|
460
|
+
}, "[".concat(formatDuration, "]"))), /*#__PURE__*/React.createElement("div", {
|
|
461
|
+
className: "player-title"
|
|
462
|
+
}, title || '', " ", subtitle && "by ".concat(subtitle), ' ')))));
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
var ReactAudioPlayer = function ReactAudioPlayer(props) {
|
|
466
|
+
var _props$audioPlayerRef, _props$progressBarRef;
|
|
467
|
+
// references
|
|
468
|
+
var audioPlayerRef = (_props$audioPlayerRef = props.audioPlayerRef) !== null && _props$audioPlayerRef !== void 0 ? _props$audioPlayerRef : useRef(); // reference our audio component
|
|
469
|
+
var progressBarRef = (_props$progressBarRef = props.progressBarRef) !== null && _props$progressBarRef !== void 0 ? _props$progressBarRef : useRef(); // reference our progress bar
|
|
470
|
+
|
|
471
|
+
var customStyles = props ? props.style : '';
|
|
472
|
+
|
|
473
|
+
// hooks
|
|
474
|
+
var _useAudioPlayer = useAudioPlayer(audioPlayerRef, progressBarRef),
|
|
475
|
+
isPlaying = _useAudioPlayer.isPlaying,
|
|
476
|
+
currentTime = _useAudioPlayer.currentTime,
|
|
477
|
+
duration = _useAudioPlayer.duration,
|
|
478
|
+
isMuted = _useAudioPlayer.isMuted,
|
|
479
|
+
onLoadedMetadata = _useAudioPlayer.onLoadedMetadata,
|
|
480
|
+
calculateTime = _useAudioPlayer.calculateTime,
|
|
481
|
+
togglePlaying = _useAudioPlayer.togglePlaying,
|
|
482
|
+
changePlayerCurrentTime = _useAudioPlayer.changePlayerCurrentTime,
|
|
483
|
+
volumeControl = _useAudioPlayer.volumeControl,
|
|
484
|
+
toggleMute = _useAudioPlayer.toggleMute,
|
|
485
|
+
formatCalculateTime = _useAudioPlayer.formatCalculateTime,
|
|
486
|
+
rewindControl = _useAudioPlayer.rewindControl,
|
|
487
|
+
forwardControl = _useAudioPlayer.forwardControl;
|
|
488
|
+
return /*#__PURE__*/React.createElement(ReactAudioPlayerInner, _extends({}, props, {
|
|
489
|
+
audioPlayerRef: audioPlayerRef,
|
|
490
|
+
progressBarRef: progressBarRef,
|
|
491
|
+
isPlaying: isPlaying,
|
|
492
|
+
isMuted: isMuted,
|
|
493
|
+
currentTime: currentTime,
|
|
494
|
+
duration: duration,
|
|
495
|
+
customStyles: customStyles,
|
|
496
|
+
onLoadedMetadata: onLoadedMetadata,
|
|
497
|
+
calculateTime: calculateTime,
|
|
498
|
+
togglePlaying: togglePlaying,
|
|
499
|
+
changePlayerCurrentTime: changePlayerCurrentTime,
|
|
500
|
+
volumeControl: volumeControl,
|
|
501
|
+
toggleMute: toggleMute,
|
|
502
|
+
formatCalculateTime: formatCalculateTime,
|
|
503
|
+
rewindControl: rewindControl,
|
|
504
|
+
forwardControl: forwardControl
|
|
505
|
+
}));
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
export { ReactAudioPlayer, ReactAudioPlayerInner, useAudioPlayer };
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apm-react-audio-player",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
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",
|
|
@@ -25,14 +27,11 @@
|
|
|
25
27
|
"prettier:fix": "prettier --check '**/**.js' --write",
|
|
26
28
|
"clean": "rm -rf node_modules dist package-lock.json yarn.lock"
|
|
27
29
|
},
|
|
28
|
-
"assert": {
|
|
29
|
-
"type": "json"
|
|
30
|
-
},
|
|
31
30
|
"peerDependencies": {
|
|
32
31
|
"react": "^16.0.0"
|
|
33
32
|
},
|
|
34
33
|
"dependencies": {
|
|
35
|
-
"rollup": "^4.
|
|
34
|
+
"rollup": "^4.59.0",
|
|
36
35
|
"rollup-plugin-babel": "^4.3.3"
|
|
37
36
|
},
|
|
38
37
|
"devDependencies": {
|
|
@@ -41,8 +40,9 @@
|
|
|
41
40
|
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
|
42
41
|
"@babel/preset-env": "^7.5.5",
|
|
43
42
|
"@babel/preset-react": "^7.0.0",
|
|
44
|
-
"@testing-library/react": "^
|
|
43
|
+
"@testing-library/react": "^14.0.0",
|
|
45
44
|
"cross-env": "^7.0.2",
|
|
45
|
+
"esbuild": "^0.27.4",
|
|
46
46
|
"eslint": "^6.8.0",
|
|
47
47
|
"eslint-config-prettier": "^6.7.0",
|
|
48
48
|
"eslint-config-standard": "^14.1.0",
|
|
@@ -53,8 +53,8 @@
|
|
|
53
53
|
"eslint-plugin-promise": "^4.2.1",
|
|
54
54
|
"eslint-plugin-react": "^7.17.0",
|
|
55
55
|
"eslint-plugin-standard": "^4.0.1",
|
|
56
|
-
"jest": "^
|
|
57
|
-
"jest-environment-jsdom
|
|
56
|
+
"jest": "^29.0.0",
|
|
57
|
+
"jest-environment-jsdom": "^30.3.0",
|
|
58
58
|
"jest-prop-type-error": "^1.1.0",
|
|
59
59
|
"prettier": "^2.0.4",
|
|
60
60
|
"react": "^18",
|
|
@@ -63,9 +63,27 @@
|
|
|
63
63
|
},
|
|
64
64
|
"resolutions": {
|
|
65
65
|
"braces": ">=3.0.3",
|
|
66
|
-
"react-scripts": "^5.0.1"
|
|
66
|
+
"react-scripts": "^5.0.1",
|
|
67
|
+
"micromatch": ">=4.0.8",
|
|
68
|
+
"minimatch": ">=3.1.4",
|
|
69
|
+
"tough-cookie": ">=4.1.3",
|
|
70
|
+
"ajv": ">=6.14.0",
|
|
71
|
+
"qs": ">=6.14.1",
|
|
72
|
+
"lodash": ">=4.17.21",
|
|
73
|
+
"js-yaml": ">=4.1.0",
|
|
74
|
+
"@babel/helpers": ">=7.26.0",
|
|
75
|
+
"@babel/runtime": ">=7.26.0",
|
|
76
|
+
"flatted": ">=3.3.3",
|
|
77
|
+
"brace-expansion": ">=2.0.1",
|
|
78
|
+
"tmp": ">=0.2.3"
|
|
67
79
|
},
|
|
68
80
|
"files": [
|
|
69
81
|
"dist"
|
|
70
|
-
]
|
|
82
|
+
],
|
|
83
|
+
"jest": {
|
|
84
|
+
"testEnvironment": "jsdom",
|
|
85
|
+
"setupFilesAfterEnv": [
|
|
86
|
+
"<rootDir>/jest.setup.js"
|
|
87
|
+
]
|
|
88
|
+
}
|
|
71
89
|
}
|