react-use-audio-player
Advanced tools
Comparing version 0.0.6 to 0.0.7
/// <reference types="howler" /> | ||
import React from "react"; | ||
declare const noop: () => void; | ||
interface AudioSrcProps { | ||
src: string; | ||
format?: string; | ||
autoplay?: boolean; | ||
} | ||
interface AudioPlayer { | ||
player: Howl | null; | ||
load: (args: AudioSrcProps) => void; | ||
error: Error | null; | ||
loading: boolean; | ||
loadAudio: (url: string) => void; | ||
playbackReady: boolean; | ||
error: Error; | ||
play: () => void; | ||
isPlaying: () => boolean; | ||
pause: () => void; | ||
stop: () => void; | ||
seek: (pos: number) => void; | ||
mute: (muted: boolean) => void; | ||
sound: Howl; | ||
playing: boolean; | ||
stopped: boolean; | ||
ready: boolean; | ||
} | ||
declare type UseAudioPlayer = Omit<AudioPlayer, "player" | "load"> & { | ||
play: Howl["play"] | typeof noop; | ||
pause: Howl["pause"] | typeof noop; | ||
stop: Howl["stop"] | typeof noop; | ||
mute: Howl["mute"] | typeof noop; | ||
seek: Howl["seek"] | typeof noop; | ||
}; | ||
interface AudioPlayerProviderProps { | ||
@@ -24,5 +33,5 @@ value?: AudioPlayer; | ||
} | ||
export declare function AudioPlayerProvider(props: AudioPlayerProviderProps): JSX.Element; | ||
export declare const useAudioPlayer: (url?: string) => AudioPlayer; | ||
export declare function AudioPlayerProvider({ children, value }: AudioPlayerProviderProps): JSX.Element; | ||
export declare const useAudioPlayer: (props?: AudioSrcProps) => UseAudioPlayer; | ||
export declare const useAudioPosition: () => AudioPosition; | ||
export {}; |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
var __rest = (this && this.__rest) || function (s, e) { | ||
var t = {}; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) | ||
t[p] = s[p]; | ||
if (s != null && typeof Object.getOwnPropertySymbols === "function") | ||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { | ||
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) | ||
t[p[i]] = s[p[i]]; | ||
} | ||
return t; | ||
}; | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||
result["default"] = mod; | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const react_1 = __importDefault(require("react")); | ||
const react_1 = __importStar(require("react")); | ||
const howler_1 = require("howler"); | ||
const lodash_1 = require("lodash"); | ||
const noop = () => { }; | ||
const AudioPlayerContext = react_1.default.createContext(null); | ||
function AudioPlayerProvider(props) { | ||
const [playbackReady, setPlaybackReady] = react_1.default.useState(false); | ||
const [loading, setLoading] = react_1.default.useState(false); | ||
const [error, setError] = react_1.default.useState(null); | ||
const player = react_1.default.useRef(null); | ||
const loadAudio = react_1.default.useCallback((src) => { | ||
setError(null); | ||
if (playbackReady && player.current.playing()) { | ||
player.current.unload(); | ||
setPlaybackReady(false); | ||
function AudioPlayerProvider({ children, value }) { | ||
const [player, setPlayer] = react_1.useState(null); | ||
const playerRef = react_1.useRef(); | ||
const [error, setError] = react_1.useState(null); | ||
const [loading, setLoading] = react_1.useState(true); | ||
const [playing, setPlaying] = react_1.useState(false); | ||
const [stopped, setStopped] = react_1.useState(true); | ||
const load = react_1.useCallback(({ src, format, autoplay = false }) => { | ||
let wasPlaying = false; | ||
if (playerRef.current) { | ||
// don't do anything if we're asked to reload the same source | ||
// @ts-ignore the _src argument actually exists | ||
if (playerRef.current._src === src) | ||
return; | ||
wasPlaying = playerRef.current.playing(); | ||
// destroys the previous player | ||
playerRef.current.unload(); | ||
} | ||
setLoading(true); | ||
player.current = new howler_1.Howl({ | ||
// create a new player | ||
const howl = new howler_1.Howl({ | ||
src, | ||
onload() { | ||
setLoading(false); | ||
setPlaybackReady(true); | ||
format, | ||
autoplay: wasPlaying || autoplay, | ||
onload: () => void setLoading(false), | ||
onplay: () => { | ||
// prevents howl from playing the same song twice | ||
if (!howl.playing()) | ||
return; | ||
setPlaying(true); | ||
setStopped(false); | ||
}, | ||
onloaderror(id, error) { | ||
onpause: () => void setPlaying(false), | ||
onstop: () => { | ||
setStopped(true); | ||
setPlaying(false); | ||
}, | ||
onplayerror: (_id, error) => { | ||
setError(new Error("[Play error] " + error)); | ||
setPlaying(false); | ||
setStopped(true); | ||
}, | ||
onloaderror: (_id, error) => { | ||
setError(new Error("[Load error] " + error)); | ||
setLoading(false); | ||
setPlaybackReady(false); | ||
setError(new Error(error)); | ||
} | ||
}); | ||
}, [player, playbackReady]); | ||
const play = react_1.default.useCallback(() => { | ||
if (!player.current.playing()) { | ||
player.current.play(); | ||
} | ||
}, [player]); | ||
const pause = react_1.default.useCallback(() => { | ||
player.current.pause(); | ||
}, [player]); | ||
const stop = react_1.default.useCallback(() => { | ||
player.current.stop(); | ||
}, [player]); | ||
const seek = react_1.default.useCallback((pos) => { | ||
if (playbackReady) { | ||
player.current.seek(pos); | ||
} | ||
}, [player, playbackReady]); | ||
const isPlaying = react_1.default.useCallback(() => { | ||
if (!playbackReady) | ||
return false; | ||
return player.current.playing(); | ||
}, [player, playbackReady]); | ||
const mute = react_1.default.useCallback((muted) => { | ||
if (playbackReady) { | ||
player.current.mute(muted); | ||
} | ||
}, [player, playbackReady]); | ||
const contextValue = react_1.default.useMemo(() => { | ||
return lodash_1.isNil(props.value) | ||
? { | ||
error, | ||
playbackReady, | ||
loading, | ||
loadAudio, | ||
play, | ||
pause, | ||
stop, | ||
seek, | ||
isPlaying, | ||
mute, | ||
sound: player.current | ||
} | ||
: props.value; | ||
}, [ | ||
props.value, | ||
playbackReady, | ||
error, | ||
loading, | ||
loadAudio, | ||
play, | ||
pause, | ||
stop, | ||
seek, | ||
mute, | ||
isPlaying, | ||
player | ||
]); | ||
return (react_1.default.createElement(AudioPlayerContext.Provider, { value: contextValue }, props.children)); | ||
setPlayer(howl); | ||
playerRef.current = howl; | ||
}, []); | ||
react_1.useEffect(() => { | ||
// unload the player on unmount | ||
return () => { | ||
if (playerRef.current) | ||
playerRef.current.unload(); | ||
}; | ||
}, []); | ||
const contextValue = value | ||
? value | ||
: { | ||
player, | ||
load, | ||
error, | ||
loading, | ||
playing, | ||
stopped, | ||
ready: !loading && !error | ||
}; | ||
return (react_1.default.createElement(AudioPlayerContext.Provider, { value: contextValue }, children)); | ||
} | ||
exports.AudioPlayerProvider = AudioPlayerProvider; | ||
// gives programmer access to the audio context | ||
exports.useAudioPlayer = (url) => { | ||
const value = react_1.default.useContext(AudioPlayerContext); | ||
react_1.default.useEffect(() => { | ||
if (url) { | ||
value.loadAudio(url); | ||
} | ||
}, [url, value]); | ||
return value; | ||
exports.useAudioPlayer = (props) => { | ||
const _a = react_1.useContext(AudioPlayerContext), { player, load } = _a, context = __rest(_a, ["player", "load"]); | ||
const { src, format, autoplay } = props || {}; | ||
react_1.useEffect(() => { | ||
// if useAudioPlayer is called without arguments | ||
// don't do anything: the user will have access | ||
// to the current context | ||
if (!src) | ||
return; | ||
load({ src, format, autoplay }); | ||
}, [src, format, autoplay, load]); | ||
return Object.assign(Object.assign({}, context), { play: player ? player.play.bind(player) : noop, pause: player ? player.pause.bind(player) : noop, stop: player ? player.stop.bind(player) : noop, mute: player ? player.mute.bind(player) : noop, seek: player ? player.seek.bind(player) : noop }); | ||
}; | ||
// gives current audio position state updates in an animation frame loop for animating visualizations | ||
// gives current audio position state - updates in an animation frame loop for animating audio visualizations | ||
exports.useAudioPosition = () => { | ||
const { sound, playbackReady } = exports.useAudioPlayer(); | ||
const [position, setPosition] = react_1.default.useState(0); | ||
const [duration, setDuration] = react_1.default.useState(0); | ||
const frameRequest = react_1.default.useRef(); | ||
const animate = react_1.default.useCallback(() => { | ||
if (playbackReady && sound.playing()) { | ||
setPosition(sound.seek()); | ||
frameRequest.current = requestAnimationFrame(animate); | ||
const { player, playing } = react_1.useContext(AudioPlayerContext); | ||
const [position, setPosition] = react_1.useState(0); | ||
const [duration, setDuration] = react_1.useState(0); | ||
// sets position and duration on player initialization | ||
react_1.useEffect(() => { | ||
if (player) { | ||
setPosition(player.seek()); | ||
setDuration(player.duration()); | ||
} | ||
else { | ||
cancelAnimationFrame(frameRequest.current); | ||
} | ||
}, [playbackReady, sound]); | ||
react_1.default.useEffect(() => { | ||
if (playbackReady) { | ||
sound.on("play", () => { | ||
setDuration(sound.duration()); | ||
frameRequest.current = requestAnimationFrame(animate); | ||
}); | ||
sound.on("pause", () => { | ||
cancelAnimationFrame(frameRequest.current); | ||
}); | ||
sound.on("stop", () => { | ||
setPosition(0); | ||
cancelAnimationFrame(frameRequest.current); | ||
}); | ||
return () => { | ||
sound.off("play"); | ||
sound.off("pause"); | ||
sound.off("stop"); | ||
}; | ||
} | ||
}, [playbackReady, sound, animate]); | ||
return { | ||
position, | ||
duration | ||
}; | ||
}, [player]); | ||
// updates position on a one second loop | ||
react_1.useEffect(() => { | ||
let timeout; | ||
if (player && playing) | ||
timeout = window.setInterval(() => setPosition(player.seek()), 1000); | ||
return () => clearTimeout(timeout); | ||
}, [player, playing]); | ||
return { position, duration }; | ||
}; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "react-use-audio-player", | ||
"version": "0.0.6", | ||
"version": "0.0.7", | ||
"description": "React hook for building custom audio playback controls", | ||
@@ -57,4 +57,3 @@ "main": "dist/index.js", | ||
"dependencies": { | ||
"howler": "^2.1.2", | ||
"lodash": "^4.17.15" | ||
"howler": "^2.1.2" | ||
}, | ||
@@ -61,0 +60,0 @@ "husky": { |
@@ -19,3 +19,3 @@ # react-use-audio-player | ||
This library exports a context Provider and two hooks for controlling the audio source. | ||
This library exports a context Provider and two hooks for controlling an audio source, giving you the tools you need to build you own audio player or visualization. | ||
@@ -57,8 +57,10 @@ <br/> | ||
const AudioPlayer = ({ file }) => { | ||
const { play, pause, playbackReady, loading, isPlaying } = useAudioPlayer( | ||
file | ||
) | ||
const { play, pause, ready, loading, playing } = useAudioPlayer({ | ||
src: file, | ||
format: "mp3", | ||
autoplay: false | ||
}) | ||
const togglePlay = () => { | ||
if (isPlaying()) { | ||
if (playing) { | ||
pause() | ||
@@ -70,3 +72,3 @@ } else { | ||
if (!playbackReady && !loading) return <div>No audio to play</div> | ||
if (!ready && !loading) return <div>No audio to play</div> | ||
if (loading) return <div>Loading audio</div> | ||
@@ -76,5 +78,3 @@ | ||
<div> | ||
<button onClick={togglePlay}> | ||
{isPlaying() ? "Pause" : "Play"} | ||
</button> | ||
<button onClick={togglePlay}>{playing ? "Pause" : "Play"}</button> | ||
</div> | ||
@@ -87,42 +87,44 @@ ) | ||
##### `loading: boolean` | ||
#### Arguments | ||
true if audio is being fetched | ||
- `audioPlayerConfig: { src: string, format?: string, autoplay?: boolean }` | ||
<br/>`autoplay` and `format` are optional. `autoplay` will default to false. | ||
##### `playbackReady: boolean` | ||
#### Return Value | ||
true if the audio has been loaded and can be played | ||
`useAudioPlayer` returns a single object containing the following members: | ||
##### `error: Error` | ||
- `load: (config: { src: string, format?: string, autoplay?: boolean }) => void` | ||
<br/>loads an audio file | ||
set when audio has failed to load | ||
- `loading: boolean` | ||
<br/>true if audio is being fetched | ||
##### `play: function` | ||
- `ready: boolean` | ||
<br/>true if the audio has been loaded and can be played | ||
plays the loaded audio | ||
- `playing: boolean` | ||
<br/>true is the audio is currently playing | ||
##### `pause: function` | ||
- `stopped: boolean` | ||
<br/>true if the audio has been stopped | ||
pauses audio | ||
- `error: Error` | ||
<br/>set when audio has failed to load | ||
##### `stop: function` | ||
- `play: () => void` | ||
<br/>plays the loaded audio | ||
stops the audio, returning the position to 0 | ||
- `pause: () => void` | ||
<br/>pauses the audio | ||
##### `loadAudio: function(url: string)` | ||
- `stop: () => void` | ||
<br/>stops the audio, returning the position to 0 | ||
loads an audio file | ||
- `seek: (position: number) => void` | ||
<br/>sets the position of the audio to position (seconds) | ||
##### `isPlaying: function: boolean` | ||
- `mute: () => void` | ||
<br/>mutes the audio | ||
true is the audio is currently playing | ||
##### `seek: function(position: number)` | ||
sets the position of the audio to position (seconds) | ||
##### `mute: function` | ||
mutes the audio | ||
<br/> | ||
@@ -132,3 +134,3 @@ | ||
This hooks exposes the current position and duration of the audio instance as its playing in real time (60 fps via [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame)). | ||
This hooks exposes the current position and duration of the audio instance as its playing in real time. | ||
This data may be useful when animating a visualization for your audio like a seek bar. | ||
@@ -156,9 +158,11 @@ A separate hook was created to manage this state in order to avoid many rerenders of components that don't need the live data feed. | ||
##### `position: number` | ||
#### Return Value | ||
the current playback position of the audio in seconds | ||
`useAudioPosition` returns an object containing the following members: | ||
##### `duration: number` | ||
- `position: number` | ||
<br/>the current playback position of the audio in seconds | ||
the total length of the audio in seconds | ||
- `duration: number` | ||
<br/>the total length of the audio in seconds | ||
@@ -165,0 +169,0 @@ ## Examples |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
17164
2
182
165
1
- Removedlodash@^4.17.15
- Removedlodash@4.17.21(transitive)