Our office jukebox is a big part of the culture in our organisation. So much so that when the jukebox isn't running in the office, it feels very strange and eerily quiet. This post is about how we replaced the ten-year-old existing version with a more modern approach. It gives an overview of the technologies used and why we chose them.
Tracking user plays
User Voting on music played
Track Search with highlighting popular office tracks
Track curation (choose a mix of tracks to add at once)
AutoPlay (choosing tracks that you'll like based on previous plays)
AutoStop at the end of the day
Go ahead and try it: https://github.com/kyan/jukebox-js
Back-end API: NodeJS – TypeScript
MongoDB: Used for storage and state and exposed as JSON
Mopidy – Specifically the WebSocket interface which exposes live data in JSON
Well, we want our jukebox to do a lot more than just play music. We want users to be able to search/choose/curate tracks to play and to be able to track that. We want other people to be able to vote on whether they like that music, so that when there is no music queued up, the jukebox will choose its own music based on what we all like and have played before. We use the MopidySpotify plugin and Recommendations API to help seed our initial recommendations and then filter that down.
Under the hood, what we've essentially built is a WebSocket proxy. Something that sits in the middle of the conversation between the Client and Mopidy and injects extra content, plus enabling extra functionality on top of that. Here's a quick overview of each moving part:
First a disclaimer. The current UI looks like it was designed by a developer. That's because it was – me! So whilst it works and is functional, a new design is in progress by the expert designers here at Kyan. I've seen the mockups and it looks slick.
The front-end is a React JS app created using the create-react-app project. This is a pretty standard React app. It connects to the backend API via a socketio client and waits for messages. These come in a defined format and are JSON. The Client can also send messages down that socket. These can optionally be secured via a JWT token (users need to login to do certain tasks). The socket is the only communication mechanism to the backend. The information coming into the frontend comes via different channels. For instance, a channel for Mopidy information, or a channel for Search information or State. So data coming from Mopidy would be parsed and stored in Redux which in turn triggers the UI to rerender when required.
The front-end is flat files served from Github using Github pages.
A NodeJS app. This backend is essentially a WebSocket proxy, allowing us to enrich data that comes in from Mopidy, adding our own information before it is broadcast to all the connected clients. We add things like voting information and metrics about when things have been played. The same goes for any messages sent to Mopidy. The API also handles extra communications directly with Spotify via their Web API. Using ExpressJS, the API starts a socketio server, as well as connecting to Mopidy using the Mopidy JS Library.
The back-end is served from a Raspberry Pi Model B that lives in our office.
We're using the exposed Websocket interface which we connect to using mopidy.js in the API. We run Mopidy on a Raspberry Pi Model B with the addition of a HIFIBerry Sound card. That sits on top of our amp, which powers four large speakers that are positioned around the office.
We use an instance of MongoDB to store information about tracks that have been played as well as user information to link with and app settings. We also use it to store the current state of the jukebox. Oh, and we use it to also cache album art urls so we don't hammer the Spotify API.
Make Developers Happy
Something really important for our new version of our jukebox was it should be developer friendly. Like all projects really, a developer should be able to get up and running as quickly as possible. It's taken quite a while to refine for this project, but I think we're in a pretty good place.
Someone can come along and check-out the project and have a fully working jukebox in minutes. We use Docker for this. The stumbling block was always Mopidy. For some time you had to have a working version of Mopidy running somewhere to connect to when developing. Whilst you can still do that, we've made it even easier by replacing this with a docker container running a silent version of Mopidy, so you can still run/interact with it all in a self container environment.
Another thing that makes developers happy is confidence. We have 100% test coverage throughout the project. It also uses ESLint for linting and prettier for a consistent style. CI is using Github Actions. Doing this from the start has meant we can now confidently make changes without the fear of breaking things.
That's all. I hope this was an interesting overview of our office jukebox. If you'd like to try the jukebox in your own office you can download the project from Github: https://github.com/kyan/jukebox-js.
Previously from our Engineering Team:
How to manage a project-specific PATH with direnv by Andrew Peng
Why do we love Next.js so much? by Dave Quilter
Dev Talks: End-to-end testing with Cypress by Damian Boni
Things I wish I’d known before I started with React Native by Carmen Lopez Guerra
How Ruby if statements can help you write better code by Dave Cocks
How do you solve a problem like caching in Progressive Web Apps? by Dave Quilter