Node.js Knockoutに参加した

8/27, 28と、Node.js Knockoutにチャレンジしていました。

Node.js Knockoutとは

Node.jsを使って、48時間のうちに何か作る、というコンテスト。
Node.js Knockout | Nov 7-8th UTC, 2015
参加者にはGithubのプライベートレポジトリやデプロイする先のクラウドサービスまで準備してもらえて、好きなようにWebサービスを作ったり出来る。
去年、第1回が開催され、今回は第2回。昨年の様子はこちらとか。
48時間ハッカソンNode.js Knockout Competitionの優勝候補たち–マルチプレーヤゲームの傑作多し | TechCrunch Japan
Node.js Knockout に参加しました - 自分の感受性くらい
昨年はNode.jsなんて名前すらきいたことあったかなかったか、くらいだった僕は今年初参加。

作ったもの

http://sugyan.no.de/ ←こんなの作りました。
東京Node学園 2時限目のアイデアソンのときにid:badatmathさんたちと一緒に考えたものが、「ニコニコ動画みたいにプレゼンを再生しながらコメントとかをリアルタイムに共有できるもの」というもので(うろ覚えなのでちょっと違うかも…)、それ以降 特に誰も作っていなかったようだったのでこの機会に試しに作ってみることにしました。
結局出来上がったのは、

  • githubアカウントでログインできる
  • slideshareから検索してスライドを取ってきて、自動で再生する(1枚あたり0〜30秒で遷移)よう調整して登録できる
  • 登録されたスライドが再生されるのを見ながら、好きなタイミングでコメントを残すことができる
  • コメントは右から左にスライド上を流れていく(ニコニコ風)
  • コメントされたタイミングが記録され、次に見たときにはそのタイミングでそのコメントが流れる

というもの。まぁ結局のところニコニコ動画を動画使わずに自動再生のスライドでやる、というようなものですね。

技術的なところ

slideshareAPIがあることを知ったので、それを使ってスライドを引っ張ってきて使うことに。
SlideShare » Developer & API
コレを使ってキーワードやユーザー、タグ検索で好きなスライドを探し出すことができる。jsonpのような形式では取れないのでクライアント側だけではどうにもならなそう。API化してサーバ側で取得するようにした。
slideshare APIの認証は登録するとすぐにメールでAPI keyが送られてくるので、それで簡単に使い始めることができるようになる。secret keyとtimestampを使ってhashを生成してパラメータで送らないといけない、とかでちょっと面倒だったけど、以下のようなコードで意外と簡単にできました。

var config = {
    slideshare: {
        key: '********',
        secret: '********'
    }
};

var _ = require('underscore');
var qs = require('qs');
var http = require('http');
var crypto = require('crypto');
var xml2json = require('xml2json');

var createQuery = function (query) {
    var ts = String(Math.floor(new Date().getTime() / 1000));
    var shasum = crypto.createHash('sha1');
    shasum.update(config.slideshare.secret + ts);
    var params = {
        api_key: config.slideshare.key,
        ts: ts,
        hash: shasum.digest('hex')
    };
    _.extend(params, query);
    return qs.stringify(params);
};

var searchSlides = function (query) {
    http.get({
        host: 'www.slideshare.net',
        path: '/api/2/search_slideshows?' + createQuery({ q: query })
    }, function (res) {
        var buf = '';
        res.on('data', function (chunk) {
            buf += chunk;
        });
        res.on('end', function () {
            console.log(xml2json.toJson(buf));
        });
    });
};

searchSlides('node.js');


Player APIというのもあって、FlashのPlayerをJavaScriptからページ移動など操作することが出来る。ということでslideshareからスライドを持ってきて、それを適当な時間間隔で勝手に再生する、ということができるようになる。ただし手動でページめくるボタンもついていてそれを操作されるとタイミングが図れなくなってしまうので、再生ページではFlashの上に透明なdivをかぶせて無理矢理マウス操作が効かないようにしたw

コメントは投稿されるたびに投稿者と対象スライド、タイミングを結びつけてMongoDBに突っ込むようにした。コンテスト参加者はMongoHQで無料でそれなりのDBを使わせてもらえる、ということだったので有り難く使わせていただきました。再生ページを開く際に最新のコメント数百件を取り出して、保存されているタイミングになるとスライド上に流れ出すようにうまいことクライアント側で操作。

var displayMessage = function (message) {
    var hex = '0123456789ABCDEF'.split('');
    var color = '#' + $.map([0, 0, 0, 0, 0, 0], function (i, e) {
        return hex[Math.floor(Math.random() * 16)];
    }).join('');
    var div = $('<div>').addClass('overlay').css({
        color: color,
        left: 600,
        top: Math.floor(Math.random() * $('#overlay').height() * 0.9)
    }).text(message);
    $('#overlay').append(div);
    setTimeout(function () {
        div.remove();
    }, 7000);
};
setInterval(function () {
    $('.overlay').each(function (i, e) {
        var offset = $(e).offset();
        offset.left -= 15;
        $(e).offset(offset);
    });
}, 100);

スライド上を流れるコメントはこんなかんじで、ランダムな高さで右端に配置して100msごとに左に動くようstyle調整する、という方法で適当に実現した。意外とこんなんでもそれっぽく見えるもんですね。


この時点で作ろうと思ってたものがだいたい出来てしまったのだけど、考えてみれば"リアルタイム"という要素が全然ないw 普通のwebアプリだしNode.jsで作る必要もないような… ということで折角だしちょっと何か入れたいな、と半ば無理矢理Socket.IOを導入して、閲覧中の人数をリアルタイム表示して コメントも他に見ている人にリアルタイム共有されるようにした。このへんは普通のSocket.IOでのチャットみたいなかんじ。


サーバへのdeployは、Joyent no.deインスタンスを取得して、そこを使うようにしました。選択肢としては他にHerokuLinodeがあり、好きなように選んで良いようでした。Gitのレポジトリからサクっとデプロイできるのは便利ですね。ただ今回はプライベートレポジトリを使えたから気軽にsecret keyとかも含む設定ファイルなんかも丸ごとレポジトリに入れてしまったけど、そうしたくない場合はどうするのが良いのだろう… Herokuの場合は環境変数を操作する形になる、と思うけど
これかな? http://wiki.joyent.com/display/node/Node.js+SmartMachine+FAQ#Node.jsSmartMachineFAQ-IneedtopasssomeAPIcredentialstomyNode.jsprocess

終わってみて

48時間あったとは言え最初の約十時間は捨てて#isuconに行っていたわけで(覚悟の上でのダブルヘッダー)、その後それなりに疲れた状態からスタートしていきなり酒を飲み始めたりで、結局実質コード書いていたのは24時間にも満たなかったと思うけど、その割には思っていたより出来上がったかなー、という個人的な感想。やっぱり気軽にサクっと簡単なwebサービスとかを作れてしまうのはNode.jsの魅力なのかな、と改めて思いました。

今回はNode.js日本ユーザグループで東京地方の参加者が集まれる場所としてVASDAQグループ様に会場を提供していただいて、他のNoderの皆様と一緒に作業に打ち込むことができました。
Node.js 日本ユーザグループ Blog: Node.js Knockout にて 東京Camp を開催します
本当にありがとうございました!

とても濃い時間を過ごして楽しく作ることができました。また来年もチャンスがあれば参加してみたいと思います。