AudioIgniter – A React.js powered audio player for your WordPress site

ai_react

Throughout CSSIgniter’s course we’ve always had a certain sweet spot for the music industry and everything related to the promotion of that market (as is evident from our very popular music themes both at Themeforest and on our own marketplace), albeit up until now mostly limited to theme development .

The idea of expanding into plugin territory has been floating around for a while, and during the past few weeks we’ve been working on creating a solid audio playing and playlist management experience for WordPress powered websites.

Enter AudioIgniter.

AudioIgniter is an audio player plugin for WordPress which aims to simplify the task of playlist and track management within the WordPress dashboard while providing a performant and fully customizable audio player for the end user.

You can download the free version and check out a demo here.

Features

The audio player supports all the usual features you’d expect in an audio player (play, pause, skip, volume control etc) plus cover images for every track and a track listing component (where its visibility can be turned on or off by either the user or the administrator).

audioigniter_830x465

Behind the scenes, in the WordPress dashboard, a custom post type stores all the playlists (your different audio players) with separate settings for each one (screenshot below). Tracks are fed into the player via a custom endpoint.

audioigniter_ui02

Tracks can be easily added to each playlist using a simple interface we’ve built specifically for AudioIgniter. Uploading them is managed via the native WordPress media uploader interface.

All track fields are collapsible and in the AudioIgniter Pro version there are a few UI add-ons, like drag & drop re-ordering, bulk uploading, and automatically syncing data (titles and images) from SoundCloud.

ai_sc

Under the hood

While the track administration on the dashboard is a basic implementation using jQuery (for now), the AudioIgniter player is powered by React.

For the (probably few by now) people that have no idea what that is, React is an extremely popular, open source Javascript library for building user interfaces created and maintained by Facebook. It enables great flexibility via a clear UI structure definition using encapsulated components, and its declarative/functional nature makes it easier to write predictable and easier to debug code. WordPress.com itself is already heavily invested in React on the Calypso project, which is entirely built on it and powers both the user administrative site of the WordPress.com portal and the desktop applications on all operating systems.

While we certainly don’t foresee that AudioIgniter will ever scale to the point which React was designed to solve problems for, we believe that it’s a great piece of technology worth investing in and giving back to, and it will be extremely interesting to see how it’ll fit inside the WordPress ecosystem as it matures.

Sound as a component

The AudioIgniter player is powered by a single container component (the player itself) which holds most of the audio player’s state and renders react-sound which is a simple React component interfacing with Soundmanager2 (the audio engine).

render() {
  return (
    <Sound
      url={this.state.currentTrack.audio}
      playStatus={this.state.playStatus}
      position={this.state.position}
      volume={this.state.volume}
      onPlaying={({ duration, position }) => this.setState({ duration, position })}
      onFinishedPlaying={this.nextTrack}
    />
  );
};

The sound component itself accepts a few sensible props that control its internal state (like the audio file, position, volume, or the playing status) which are passed down by the Player component’s state (the main data store). Playing a sound is as simple as rendering it with a valid audio file url and a PLAYING status. Similarly, removing a sound is just a matter of not rendering it.

Managing settings

AudioIgniter supports quite a few user-adjusted settings specific to each playlist and while in most full-fledged Javascript apps settings like these are usually fetched from a REST API or a custom endpoint we chose to directly mount the Player component itself on the DOM and simply pass the settings as initial props from data attributes on the player’s root HTML element (mounting point) instead of using a container (wrapping) application component.

const nodes = document.getElementsByClassName('audioigniter-root');

Array.prototype.slice.call(nodes).forEach(node => {
  render(
    <Player
      tracksUrl={node.getAttribute('data-tracks-url')}
      displayActiveCover={node.getAttribute('data-display-active-cover')}
      displayArtistNames={node.getAttribute('data-display-artist-names')}
      displayTracklist={node.getAttribute('data-display-tracklist')}
      // ... etc
    />,
    node
  );
});

This scheme avoids having a wrapper component just to handle fetching the settings and delegates all power to the Player component itself. It’s also a decision which might change in the future as development progresses (more on that further below) or the number of settings increases, but for our first version it’s simple enough and works well.

SoundCloud support

The PRO version of the plugin supports SoundCloud tracks by simply resolving SoundCloud URLs into their streamable URLs (audio formats) on the fly. Since the SoundCloud Javascript SDK is too big to be acceptable in such a small application (about 230kb!), we used a very simple custom track resolving function based on the SoundCloud API.

const resolve = (url, clientId) => {
  /*
  * Tell the SoundCloud API not to serve a redirect. This is to get around
  * CORS issues on Safari 7+, which likes to send pre-flight requests
  * before following redirects, which has problems.
  *
  * https://github.com/soundcloud/soundcloud-javascript/issues/27
  */
  const statusCodeMap = encodeURIComponent('_status_code_map[302]=200');

  return fetch(`https://api.soundcloud.com/resolve?url=${url}&client_id=${clientId}&${statusCodeMap}`)
    .then(res => res.json())
    .then(res => fetch(res.location))
    .then(res => res.json());
};
Resolving a SoundCloud URL

In case you ever need, it the code is above. Note that you have to explicitly tell the SoundCloud API not to serve a redirect, to avoid a breaking issue in Safari.

Further development

While we’ve only just recently released AudioIgniter, its next version is almost ready and features many improvements and architecture optimizations such as:

  • We’re dropping dependency on react-sound and Soundmanager2 in lieu of a custom solution using the more modern Howler.js. In the future, we plan on dropping Howler as well in favor of a custom, smaller HTML5 audio implementation we’ve been working on.
  • The main audio player logic is extracted into its own Higher Order Component (HOC). The use of an HOC provides better separation of concerns, cleaner code, and better reusability if the need rises. We’ve also split data fetching and SoundCloud into their own components as well.
  • SoundCloud support is extended to SoundCloud playlists as well (only single tracks are supported currently). This is done by unfolding the playlist into its tracks.

It goes without saying that back-compatibility among versions will always be preserved.

We’re preparing an additional write-up on the most interesting parts of the above points just after releasing the next version. Until then we hope you enjoy the plugin as much as we enjoyed developing it!

Leave a Reply

Your email address will not be published. Required fields are marked *