Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
react-use-audio-player
Advanced tools
Typescript package exporting custom React hooks for controlling audio in the browser. Built on top of the amazing howler.js library.
The intent of this package is to provide an idiomatic way to create and manipulate sounds in a React application.
Note that v2 is a major upgrade and thus contains breaking changes for your applications. Overall the migration to v2 will involve you taking a few refactoring steps:
useAudioPosition
and replace it with your own implementation (see the examples/recipes section below)useAudioPlayer
with useGlobalAudioPlayer
which is exported from the v2 package (alongside a net-new hook using the old name of useAudioPlayer
- more on that below)useGlobalAudioPlayer
below since some API improvements have been made. Most notably, the hook is no longer called with any load arguments/options and instead returns an explicit load
function that you must use.yarn add react-use-audio-player
To play a sound, import either useAudioPlayer
or useGlobalAudioPlayer
into a React component. Grab the load
function from its return and get jamming!
import { useGlobalAudioPlayer } from 'react-use-audio-player';
function MyComponent() {
const { load } = useGlobalAudioPlayer();
// ... later in a callback, effect, etc.
load('/mySound.wav', {
autoplay: true
});
}
useAudioPlayer
and useGlobalAudioPlayer
share a lot of similarities. In fact, they return the same AudioPlayer
interface (see details below).
Your use-case will determine which hook is the most appropriate for you to use.
useGlobalAudioPlayer
has some unique functionality. It's purpose is to manage a single, global sound across your entire app.
The inspiration for this came from a desire to easily build applications like SoundCloud or Spotify where no matter where you are in the app you can access and control the sound.
When you are using this hook you can call it from anywhere in your component tree and it will synchronize with the same audio source as every other instance of useGlobalAudioPlayer.
For example, you could write a Playlist component where clicking a track loads that song and begins playback. Then, on a totally different branch in your component tree, write a PlaybackControls component which calls useGlobalAudioPlayer and uses its play and pause members to start and stop the same song previously loaded by Playlist.
To quickly determine if useGlobalAudioPlayer is right for you, ask yourself these two questions:
useAudioPlayer
is the best choice for when you have a simple use-case. Each instance of the useAudioPlayer hook represents its own sound.
This means that you can load and play multiple sounds from the same component.
For example, you could add separate, unique sound effects for the success and error responses of a fetch request.
Note: Unlike useGlobalAudioPlayer, useAudioPlayer returns an additional method for cleaning up audio if you wish to stop playing and destroy the sound after some interaction (i.e. component unmount, user navigates to a different route, etc.). Without cleaning up, sounds may live on even after the components that created them unmount possibly leading to memory leaks.
useGlobalAudioPlayer and useAudioPlayer can be used simultaneously without one affecting the other.
This is the interface implemented by the returned object of both useAudioPlayer and useGlobalAudioPlayer. The interface defines all the state for a sound and a set of methods to manipulate the state/sound.
string
(the src used to load the audio)boolean
(is the audio looping)boolean
(is the sound loaded and ready to play)boolean
(is the sound paused)boolean
(is the sound stopped i.e. not playing & position 0)boolean
(is the sound playing)number
(the length in seconds)boolean
(is the sound muted)number
(the playback rate)number
(the volume level 0 - 1.0)string | null
(error message if any, after attempted load)() => void
Plays the loaded sound. You must invoke this to start playback if autoplay
was set to false
() => void
Pauses the playing sound
() => void
Toggles the play/pause state
() => void
Stops the playing sound and resets the position to 0.
(volume: number) => void
Sets the volume level of the loaded audio. Accepts a floating point number between 0 and 1.0 (muted to loudest)
(muteOnOff: boolean) => void
Mutes/unmutes the loaded sound
(from: number, to: number, duration: number) => void
Fades the sound's volume level from the value of the first argument to the value of the second, over a number of milliseconds as set by the final argument
(speed: number) => void
Sets the playback speed of the loaded sound. Accepts a floating point value between 0.5 and 2.0. Currently half speed is the slowest and double is the fastest supported rates
(position: number) => void
Sets the playback position of the loaded sound to the argument. The position argument is floating point number representing the time the audio should move to
(loopOnOff: boolean) => void
Sets or unsets whether the sound should loop once it ends
() => number
Returns the current position of the loaded sound as a floating point number
(src: string, options?: AudioLoadOptions) => void
Downloads and loads a new sound. The first argument, src is a URI of the sound to be played. The second argument is a set of options applied to the sound once it loads.
These options can be used to initialize certain pieces of state on the AudioPlayer
interface or be used to set up lifecycle callbacks if needed.
AudioLoadOptions
For full, example applications see the runnable examples in the repo. Below are a few snippets to help with some of the trickier use-cases.
Switching from one sound the next is a common use-case (i.e. a playlist queue). This can be done in a couple of different ways:
// the same solution will work with useGlobalAudioPlayer
const { load } = useAudioPlayer();
const nextTrack = () => {
load(nextSong, { autoPlay: true });
};
return <button onClick={nextTrack}>Start next track</button>;
Alternatively, you can queue up the next song to play when the current sound ends. You can see a full, working example of this in the AutoPlayNextSong
component in /examples.
const songs = [songA, songB];
const [songIndex, setSongIndex] = useState(0);
const { load } = useAudioPlayer();
useEffect(() => {
load(songs[songIndex], {
autoplay: true,
onend: () => setSongIndex(songIndex + 1)
});
}, [songIndex, load]);
In previous 1.x versions of the library, a separate hook was exported from the package which tracked state representing the current seek time of a playing sound. While this was helpful it ultimately fell outside the scope of the hook as the v2 rewrite took shape. This is mainly due to the difficulty of supporting the feature for both forms of the hook useAudioPlayer/useGlobalAudioPlayer.
Luckily, even without a dedicated hook, it is trivial to implement the same functionality yourself. Below is one method for how you might write your own hook for tracking seek time.
function useAudioTime() {
const frameRef = useRef<number>()
const [pos, setPos] = useState(0)
const { getPosition } = useGlobalAudioPlayer()
useEffect(() => {
const animate = () => {
setPos(getPosition())
frameRef.current = requestAnimationFrame(animate)
}
frameRef.current = window.requestAnimationFrame(animate)
return () => {
if (frameRef.current) {
cancelAnimationFrame(frameRef.current)
}
}
}, [getPosition])
return pos;
}
To stream or play large audio files, the audio player must be forced to use HTML5 as opposed to the Web Audio API which is Howler's default. This is because the Web Audio API must download the entirety of the sound before playing anything.
When streaming or working with large files make sure to use the html5
option of the #load
function.
Also, if your sound src string does not contain an extension (like if you are fetching a stream from an API), be sure to set it with the format
option of the #load
function.
More information in this Howler thread
const { load } = useAudioPlayer();
load('https://stream.toohotradio.net/128', {
autoplay: true,
html5: true,
format: 'mp3'
});
Eventually I would like to host & run the examples somewhere on the web, but for now to run them yourself locally, follow the following steps:
git clone
the repositorycd useAudioPlayer/examples
yarn install
yarn start
Please consider opening an Issue or Pull Request on the Github and I will do my best to respond to these in a timely manner.
The most basic npm release strategy is being followed for now. A good explanation can be found here.
Steps
yarn/npm version
(preversion script will ensure code is tested and built)yarn/npm publish
git push
& git push --tags
FAQs
React hook for building custom audio playback controls
We found that react-use-audio-player demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.