What I’ve been up to… (a.k.a making an audio player using jQuery Mobile & NodeJS) Part 1

So I recently signed up for a VPS from AlienVPS at a ridiculously low price and guess what, it crashed on me twice today… -_- But $19/month is still pretty darn cheap. At least it gave me some sandbox to play around with NodeJS and jQuery Mobile.

OK so far NodeJS has been rather simple and straightforward. I actually use Express framework on top of NodeJS which ease the work a little bit. However, I can really see how this can become complicated really really fast. 1st of all, I sorta have to implement all the HTTP protocol code manually in NodeJS (except for 500 Internal Error and 200 OK I think). So that includes 404, 403, blah blah. Not that it matters that much except I wanna maximize my site traffic by taking advantage of Search Engine bots. Well I disallow everything in robots.txt right now so if those bots behave, I should be good. You can check it out at http://208.93.155.39:8080/playlist.html but please please don’t spread it around or I’m gonna have to shut it down due to my limited bandwidth. The app is still buggy since it’s a work in progress but a refresh should make it behave a bit better.

Anyway, now the 1st thing a web server should be able to do is to serve static web pages and it can be achieved pretty easily:

var app = require('express').createServer();
var fs = require('fs');
var public_path = 'public/';
var PORT = 8080;
app.get('/', function(req, res) {
        res.sendfile(public_path + 'index.html');
});
app.get('/*', function(req, res) {
        var page = req.params[0];
        res.sendfile(public_path + page);
});

Easy enuf… Now I want to query some specific stuff like, idk my KOREAN POP playlist!!

var songs;
app.post('/playlist.html', function(req, res){
        Controller.handlePlaylist(req, res);
});
var Playlist = {
        get : function(name) {
                return fs.readdirSync(public_path + MUSIC_PATH + name);
        }
};
var Controller = {
        handlePlaylist : function(req, res) {
                if (!songs) { //lazy-initialize this
                        songs = Playlist.get(req.param('playlist'));
                }
                var index = req.param('song');
                if (index && index >=0 && index < songs.length) { //if I query a specific song number, give me the path to the song
                        res.send({ 'result' : MUSIC_PATH + 'kpop/' + songs[index] });
                } else { //otherwise give me the whole list
                        res.send({ 'filenames' : songs });
                }
        }
};

Now when I hit up playlist.html?playlist=kpop with POST, I can get my playlist and playlist.html?song=1 with POST gives me the 2nd song. This is a simple enuf song serving mechanism that will help me build my audio player.

Playlist Page

Playlist Page

Since I’m not using any view rendering engine, in playlist.html I actually have to use the trick of loading the file 1st, then make an ajax call to populate the data. This gets very very tricky with jQuery mobile since it doesn’t have a lot of control events on when it’s done rendering and what not. This combines with ambiguous timing from AJAX callbacks can lead to a pretty disruptive UX (I’m still having trouble with synchronizing stuff in JavaScript). But anyway, the playlist.html has a pretty simple structure:

<div data-role="page" class="playlist">
    <div data-role="header">
        <h1>My collection</h1>
    </div><!-- /header -->

    <div data-role="content">
        <ul data-role="listview" data-inset="true">
                <li data-role="list-divider">Kpop</li>
        </ul>
    </div><!-- /content -->
    <script type='text/javascript'> 
    $('div.playlist').bind('pageshow', function() {
        var $page = $(this); // to use inside callback since "this" in the callback function is different
        if (!($page.attr('data-init'))) { // Initialize once
                $.post('playlist.html?playlist=kpop', null, function(data) { //retrieve the data
                        var i;
                        var filenames = data['filenames'];
                        var $playlist = $page.find('ul[data-role="listview"]');
                        for (i in filenames) { //populate the list of songs
                                $playlist.append('<li><a href="player.html?song=' + i + '">' + filenames[i] + '</a></li>'); 
                        }
                        $page.attr('data-init', 'true');
                        $playlist.listview('refresh'); //refresh the view
                }, 'json');
        }
    });
    </script>

</div><!-- /page -->

The HTML structure itself is simple the the JavaScript is kind of a hack. “Pageshow” event in jQuery Mobile gets invoked after page has been initialized (a.k.a after jQuery converts basic elements into its themed mobile looks). Why not “pagecreate” or “pagebeforecreate”? Because the callback is actually an AJAX call to grab the data and listview can only be refresh after it’s been initialized (also not guaranteed in the previous 2 event hooks). If I were to use a view rendering engine to populate the data, then send across the wire, I wouldn’t have had this problem so… something to look at next time.

OK so that’s the easy part, I write next time about how to actually make the player cause that took me like 3 days… >.< sleep now!

Tagged , , , , , ,

8 thoughts on “What I’ve been up to… (a.k.a making an audio player using jQuery Mobile & NodeJS) Part 1

  1. Varun Chopra says:

    Nice article Long. You should check out VPS.NET also. That is what I use at my work and its pretty darn decent for the service that they provide. Also, like your post, interesting. You might find jplayer interesting with its implementation using jquery mobile 🙂

    • Long Ho says:

      lol thanks Varun. I’ll def checkout vps.net 🙂 It was pretty frustrating that my VPS went offline and no attempt to reboot was successful… after like a hr it came back up! >.< I've seen jplayer but I personally wanna make my own just for the sake of learning how to do it 🙂 I'll dig into jplayer source code to see if they're doin it in a better way (which I'm sure they are).

      • Varun Chopra says:

        hehe, i feel it too. when i was looking for vps, i wanted them on cloud for the same reason. i hate to see lags and random issues jumping out. As far as better way, hehe there is always a better way, just that no one thought of it before 😛
        we should catch up some time and talk of tech. will be interesting to see what you plan for future.

  2. John L says:

    First thanks for this great info, I have few questions though

    How do you load the Mp3s I have tried for days its not working what layout did you use to arrange to music, where did you put them, and how do one make it work

    • Long Ho says:

      In order to load the mp3 you just need to set the “src” attribute of the audio tag. For example:
      It also depends on the music format since default encoding supports a few (which I can’t remember off the top of my head). Typical mp3 should be fine.
      In terms of where I put my music, I put it in a subfolder of the public folder so that it’s accessible via the relative path (also to avoid arbitrary directory traversal). I read in all the files in the music folder, store the filenames in a list and use the index as the song key. Therefore, in part 2 when I query for song=3, it retrieves the 4th song. After that song is finished, it autoincrements to 5th one. You can implement Repeat/Repeat All/Shuffle using a similar mechanism.

      Good luck!

  3. John L says:

    Thanks for your reply, I tried but still yet not woking….when you make the list of the music, did you use or some sor of text file? and how do you call the list from the player.html using src?

    • Long Ho says:

      No I actually use readfile function in NodeJS if you take a look at my server code. It reads all the filenames from a known location into an array of string. Then I use the filename index in the array as the key to pass around between server/client to determine which song I’m gonna play.

      For example, the list of song is [‘song1.mp3’, ‘song2.mp3’] then if I send a request to playlist.html?song=0 it will give me back the path to song1.mp3. Based on that I can populate the src attribute of the audio element

  4. Dish network says:

    Dish network…

    […]What I’ve been up to… (a.k.a making an audio player using jQuery Mobile & NodeJS) Part 1 « LLH…azndezign[…]…

Leave a reply to Long Ho Cancel reply