Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The architecture of a music player with playlists, using Rails, Redis and HTML5

I'm developing an app that has multiple music playlists, a view for each playlist and a player.

I'm making use of the HTML5 History API in my system already (for other functionality) and will be using it further to prevent page reloads between requests and therefore the music stopping on each page.

I am stuck as to the best approach for managing the tracks the player will be playing between views though. At present, as you would expect, a user clicks a link, gets a list of tracks. The tracks are loaded into the player and played sequentially, simple. However, with the continuously playing music i need to ensure the correct playlist of tracks plays in order despite the changing content on the page and it's now dynamic URL.

However, when the user does navigate to another page, presses play on a track in the new playlist, i need to be able to load that item into the player and effectively load in the rest of the playlist while the user continues to navigate.

I am using Redis to store a list of the track id's for a playlist, for speedy referencing to reduce lag between tracks. As a result i have a different Redis set for each playlist. I have also built my Next and Previous Track API calls based on the current track playing, so that the next track from the Redis set can be loaded into the player.

As mentioned though i just can't decide on the best way to reference which playlist is currently playing so the player knows from which Redis set to call the tracks from. My thinking has left me with a few different ideas:

a) HTML5 custom data attributes - i could set the currently playing playlist as a data-attribute on the player and update it as and when. I could then reference this attribute when deciding which Redis set to load my next track from.

Alternatively i could dump the current playlist, and all the tracks (and their attributes) as JSON objects into data attributes on the page. Playlists could be thousands of tracks long though so i'm dismissing this one because the source code would look horrible, let alone the likely performance issues associated. Am i right?

b) LocalStorage - Cross browser support is limited. The more support for this solution the better in this particular case. Despite this i could save the current playlist in the users browser. This has led me to wonder whether it would also be practical to save the tracks JSON objects in LocalStorage too, to prevent the additional DB calls.

c) In Session - I could update the session to store a variable for the currently playing playlist.

d) Redis - I could expand my usage of Redis to save a string that references the name of the current playlist. I could then check it between each Next/Previous track call.

Through writing this question i already have a better idea of which route i am going to take but if anyone has any advice for this scenario then i'd love to hear please.

Thanks.

UPDATE: I have implemented 95% of a solution that makes use of Redis for this. I am having a few performance issues though with pages taking ~10s to load. Not great at all.

Essentially each user has 2 playlists: Current and Armed. Each request loads the track id's into the Redis Armed playlist and if the play button is pressed on a track, the Current Redis playlist is expired and replaced with the Armed one.

My Next and Previous buttons then just fetch the ID of the next or previous track in the Current playlist and load in the music source for the player. The concept works fine and i'm pleased with it.

However, as mentioned performance is slow between page requests and needs significant improvement. My SQL is optimimised, i'm only pulling out the required attributes and i have SQL indexes where necessary so i'm looking for other alternatives at the moment.

Options i'm considering:

  1. Only populate the Armed playlist if a track is clicked on the new page. This would save additional processing if the user doesn't actually want to listen to one of the new tracks.

  2. Making more use of Redis and storing the lean track objects within the Redis playlists instead of just the track ID - the performance lag is largely between page requests though and not in the actual playing of tracks and navigating a playlist.

  3. Make use of a master Redis playlist that contains all of the applications tracks from which the Current and Armed playlists can pick from. This could be maintained via an hourly rake task and would prevent lengthy DB calls on page requests. I'm just nervous has to how far this would scale in terms of memory usage on the server and the amount of tracks in the DB.

like image 519
Pete Avatar asked Jan 28 '12 17:01

Pete


2 Answers

If you need to model client-side application state it's best to have one source of the state. This is the one of the problems client side javascript MV(V)C frameworks attempt to solve. I'm mostly familiar with backbone.js and ember.js so I can speak to those.

Backbone.js

Create a model called Playlist and a collection called Playlists. Add a property to your Playlists collection currentPlaylist that holds the current playlist. Then define a view called PlaylistView and define a render method on it. Wire up event triggers and bindings such that when currentPlaylist changes, the playlist is automatically re-rendered. This would require moving your template rendering to the client, but you'll probably want to do that anyway to reduce server roundtrips and reduce the load on the server caused by rendering.

Ember.js

Create a controller (similar to backbone collection) with a property called currentPlaylist and populate the controller with all of the playlists represented as Ember.objects. Then in a playlist handlebars template included on the page you can bind to playlists.currentPlaylist and the template will re-render automatically when playlists.currentPlaylist changes.

I'm obviously leaving out the vast majority of the details, but those are best left the the framework documentation, examples and tutorials.

disclaimer: I'm new to client-side frameworks which is part of the reason I left out most of the details. I appreciate anyone who can correct me if I'm in error.

like image 171
Patrick Klingemann Avatar answered Nov 02 '22 00:11

Patrick Klingemann


A mix of session and local storage would be a good approach.

But instead of fetching the whole playlist just fetch the X (for example 10) next tracks and maybe also the X previous ones. Once the player gets to the last song, it fetches the next 10 songs, the previous ones can be calculated in the client.

The data model could be just a hash where element [0] is the current song, elements [X] are the next songs and [-X] the previous ones.

Storing the playlist information client-side seems reasonable to me, but you can also use the session to reference the current song and playlist, so when a user comes back or really reloads your site you still get the song without doing a database call.

like image 1
yagooar Avatar answered Nov 01 '22 23:11

yagooar