apm-react-audio-player 1.0.27 → 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 +85 -21
- 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;
|
|
@@ -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
|
|
@@ -135,8 +144,10 @@ var useAudioPlayer = function useAudioPlayer(audioRef, progressBarRef, volumeCtr
|
|
|
135
144
|
setIsPlaying(true);
|
|
136
145
|
setIsFinishedPlaying(false);
|
|
137
146
|
audioRef.current.play();
|
|
138
|
-
|
|
139
|
-
|
|
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)) {
|
|
140
151
|
animationRef.current = window.requestAnimationFrame(_whilePlaying);
|
|
141
152
|
}
|
|
142
153
|
};
|
|
@@ -158,24 +169,29 @@ var useAudioPlayer = function useAudioPlayer(audioRef, progressBarRef, volumeCtr
|
|
|
158
169
|
}
|
|
159
170
|
};
|
|
160
171
|
var changePlayerCurrentTime = function changePlayerCurrentTime() {
|
|
172
|
+
if (!progressBarRef.current || !audioRef.current) return;
|
|
161
173
|
audioRef.current.currentTime = progressBarRef.current.value;
|
|
162
174
|
setCurrentTime(progressBarRef.current.value);
|
|
163
175
|
progressBarRef.current.style.setProperty('--seek-before-width', "".concat(progressBarRef.current.value / duration * 100, "%"));
|
|
164
176
|
};
|
|
165
177
|
var changeRange = function changeRange() {
|
|
178
|
+
if (!progressBarRef.current || !audioRef.current) return;
|
|
166
179
|
audioRef.current.currentTime = progressBarRef.current.value;
|
|
167
180
|
updateCurrentTime();
|
|
168
181
|
changePlayerCurrentTime();
|
|
169
182
|
};
|
|
170
183
|
var rewindControl = function rewindControl() {
|
|
184
|
+
if (!progressBarRef.current) return;
|
|
171
185
|
progressBarRef.current.value = Number(progressBarRef.current.value) - 15;
|
|
172
186
|
changeRange();
|
|
173
187
|
};
|
|
174
188
|
var forwardControl = function forwardControl() {
|
|
189
|
+
if (!progressBarRef.current) return;
|
|
175
190
|
progressBarRef.current.value = Number(progressBarRef.current.value) + 15;
|
|
176
191
|
changeRange();
|
|
177
192
|
};
|
|
178
193
|
var volumeControl = function volumeControl(e) {
|
|
194
|
+
if (!audioRef.current) return;
|
|
179
195
|
var value = e.target.value;
|
|
180
196
|
var volume = value / 100;
|
|
181
197
|
audioRef.current.volume = volume;
|
|
@@ -259,6 +275,24 @@ var Pause = function Pause() {
|
|
|
259
275
|
}));
|
|
260
276
|
};
|
|
261
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
|
+
};
|
|
262
296
|
var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
263
297
|
var _props$audioPlayerRef, _props$progressBarRef;
|
|
264
298
|
// references
|
|
@@ -271,7 +305,6 @@ var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
|
271
305
|
volumeCtrl = props.volumeCtrl,
|
|
272
306
|
playBtnClass = props.playBtnClass,
|
|
273
307
|
customHtml = props.customHtml,
|
|
274
|
-
isLive = props.isLive,
|
|
275
308
|
onLoadedMetadata = props.onLoadedMetadata,
|
|
276
309
|
calculateTime = props.calculateTime,
|
|
277
310
|
togglePlaying = props.togglePlaying,
|
|
@@ -289,16 +322,46 @@ var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
|
289
322
|
prefix = props.prefix;
|
|
290
323
|
var audioDuration = duration && !isNaN(duration) && calculateTime(duration);
|
|
291
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);
|
|
292
347
|
return audioSrc && /*#__PURE__*/React.createElement("div", {
|
|
293
348
|
className: "audioPlayer",
|
|
294
349
|
style: customStyles && customStyles.audioPlayer
|
|
295
350
|
}, /*#__PURE__*/React.createElement("audio", {
|
|
296
351
|
ref: audioPlayerRef,
|
|
297
|
-
src: audioSrc,
|
|
298
352
|
preload: "none",
|
|
299
353
|
onLoadedMetadata: onLoadedMetadata,
|
|
300
354
|
muted: isMuted
|
|
301
|
-
}
|
|
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", {
|
|
302
365
|
className: "player-layout"
|
|
303
366
|
}, volumeCtrl && /*#__PURE__*/React.createElement("div", {
|
|
304
367
|
className: "player-controls-secondary-outer"
|
|
@@ -324,6 +387,7 @@ var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
|
324
387
|
className: "player-volume-progress",
|
|
325
388
|
min: 0,
|
|
326
389
|
max: 100,
|
|
390
|
+
defaultValue: 100,
|
|
327
391
|
"aria-hidden": "true",
|
|
328
392
|
"aria-valuetext": "100%",
|
|
329
393
|
onChange: function onChange(e) {
|
|
@@ -336,7 +400,7 @@ var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
|
336
400
|
className: "player-volume-label"
|
|
337
401
|
}, "Volume")), /*#__PURE__*/React.createElement("div", {
|
|
338
402
|
className: "player-controls"
|
|
339
|
-
},
|
|
403
|
+
}, showControls && /*#__PURE__*/React.createElement("div", {
|
|
340
404
|
className: "player-backward-forward-controls"
|
|
341
405
|
}, /*#__PURE__*/React.createElement("button", {
|
|
342
406
|
onClick: rewindControl
|
|
@@ -350,14 +414,14 @@ var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
|
350
414
|
className: playBtnClass,
|
|
351
415
|
style: customStyles && customStyles.playPause,
|
|
352
416
|
id: "playbutton"
|
|
353
|
-
}, 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", {
|
|
354
418
|
className: "player-backward-forward-controls"
|
|
355
419
|
}, /*#__PURE__*/React.createElement("button", {
|
|
356
420
|
onClick: forwardControl
|
|
357
421
|
}, /*#__PURE__*/React.createElement("img", {
|
|
358
422
|
src: "/img/icon-forward-15.svg",
|
|
359
423
|
alt: "Forward 15 seconds"
|
|
360
|
-
})))),
|
|
424
|
+
})))), showControls && /*#__PURE__*/React.createElement("div", {
|
|
361
425
|
className: "player-timeline"
|
|
362
426
|
}, /*#__PURE__*/React.createElement("div", {
|
|
363
427
|
className: "player-currentTime"
|
|
@@ -378,7 +442,7 @@ var ReactAudioPlayerInner = function ReactAudioPlayerInner(props) {
|
|
|
378
442
|
className: "player-content"
|
|
379
443
|
}, customHtml && customHtml, /*#__PURE__*/React.createElement("div", {
|
|
380
444
|
className: "player-audio-type type-sm"
|
|
381
|
-
},
|
|
445
|
+
}, duration === Infinity ? /*#__PURE__*/React.createElement("div", {
|
|
382
446
|
className: "player-live-label"
|
|
383
447
|
}, prefix ? prefix : 'On Air') : /*#__PURE__*/React.createElement("div", {
|
|
384
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
|
}
|