We love our hackathons at Axelerant, and we are always trying to build something that helps spread happiness. We thought it would be nice to have an internal web-based online Drupal radio, where people would be able to listen to songs along with some fun social characteristics.
Thus, one of our hackathons saw us getting down to build a Drupal radio website, with the following features.
- Ability to upload songs
- Ability to dedicate a song to anyone with a message
- Play a random song when the dedicated song queue is empty
- Integrate the radio site with Slack, and display the current track being played
- Display the Up Next queue
- Build the radio site using only Drupal, without any radio streaming software
Described below is how we went about putting the various components of the online radio together.
Uploading a song on the online radio
We created a content type called Song with the following fields:
- Song field of type File to upload music files
- Art field of type Image, to upload the album art
- Tags of type Term Reference to tag artists, genres, etc.
- Duration of type Number to track the song duration
An interesting part was the song duration. The getID3 library helped us fetch the song duration, and so, in the content type song feature I created a hook that added song duration on the creation of a new song. As it is automatically calculated and added, the song duration field is hidden from end users.
Dedicating a song on the Drupal radio
We created a content type called Request with the following three fields:
- Song field of type Entity Reference that points to the song nodes
- Dedicated-to of type Entity Reference that points to the User to whom the song is dedicated
- Message of type Text for the message he/she wants to dedicate.
- Played-at of type Date to keep track of when the song started playing
- Default-request of type Boolean to know if this node is a song that is played by default or if its a request.
After a request is saved, the resulting node would be added to a nodequeue. The fields Played-at and Default-request are hidden.
We created a method that simulated a constantly playing playlist. It did three things
- Check if the requested song is playing.
- If not, play it.
- If the song is done playing, remove it and pull up the next requested song and play it.
The detailed logic looks like this:
- Check if there are any songs in the node_queue.
- If there are any, then check if the first song has already started playing by seeing if the Played-at is set or not.
- If it is not playing, set Played-at field to the current time, which means that this song has started playing.
- If it is playing, check if the current time has passed the Played-at + Duration time of the song being played. If it has passed, it means the song is done playing.
- If the song is done playing, remove the request, populate Played-at field for the top request with the current time.
- If there is no request, get a node marked as Default-request and put some random song, update Played-at to the current time and add it to the top of the queue.
This above function would be called twice, once by the cron and once just before the page that serves the song would be rendered.
Whenever a new song would get pulled, the site would send a Slack channel notification.
The now playing song is rendered by the Views module. The view displays whichever song is at the top of the node_queue. We wrote a tiny
.tpl file to stream the song rather than display the link to the song file. We used the HTML5
<audio> tag to render the MP3.
There was one more hurdle that we had to cross before we were done – we wanted the song being heard by everybody to be in sync. If we had rendered as-is, everybody would start hearing the song from the beginning. To avoid this, we also rendered the Played-at field to the front-end.
Thanks to the HTML5 audio object’s CurrentTime attribute, we could start a song with an offset. A tiny
.js file would compute the difference between the current time and the Played-at time, and add that as an offset to the audio object, and voila! The song starts playing with a certain offset, achieving a sync across requests, and simulating an actual online radio.
Once the song ends, the js callback would reload the page, and our magic function would run again. The latest song would be pulled up or a newly dedicated song will be rendered by the view.
If no one is listening, the
cron keeps playing the next song at random, and Slack keeps notifying us of the currently played songs.
We do a lot more cool stuff like this Drupal radio at Axelerant. To join us, apply via our careers page.