capture.tumblr.jsをgelato対応に2008年02月29日 23時55分54秒

とりあえずやってみました

ブラウザでキャプチャしてアップロードするJSActionsスクリプトシリーズのtumblrにアップロードするバージョンです。
という、激しく便利なJSActionスクリプトをgelatoに対応させてみた。

キャプチャサンプル
図はtumblrのダッシュボードをキャプチャしてgelatoに放り込んだものをさらにgelatoにキャプチャして、それをキャプチャしてtumblrに放り込んだもの。何いってんだかわかんなくなってきた。

どのあたりをどう変えるか

さて、一所懸命ソースを見てみましたよ。

URLの体系が違う

まず、URLの生成方法がまったく違う。capture.tumblrはTumblr write APIを使っているが、gelatoはそんな機能はないのでadmin/index.phpに直接POSTする必要がある。

このあたりを担当しているのが、269行目からの「Tumblr」オブジェクトらしい。このオブジェクトはベースURLになる「endpoint」プロパティとリクエストを実行する「post」メソッドが実装されているが、どちらも変更する必要がある。

POSTする内容が違う

そして当然POSTする内容が違うので、このあたりを扱う部分にも変更を加える必要がある。これは「ImageSaver」オブジェクト(296行目から)の「post」メソッド(451行目から)だ。

gelatoでphotoをPOSTする場合は

  • title → コンテンツのタイトル
  • description → オプションの説明文
  • type → コンテンツタイプ。photoは「2」になる
  • date → 投稿日時。UNIXタイムスタンプ形式
  • btnAdd → submitボタンなのだが、POST時にadmin/index.phpでこれをissetチェックしているのでつけてやる必要がある
と、イメージストリーム本体の「photo」を送る必要がある。

ちょっとだけ修正してやる必要もある

イメージストリームの流し込み方などはcapture.tumblr.jsそのままになるので、Tumblr.post()に渡すオブジェクトを上記のように整えてやればよいのだが、tumblrでは必要としないfilenameを与えてやる必要もある。

08.03.01付けの0.0.1bで修正されたようです。HTTP.Request.prototype.create_streamへの修正作業は不要になりました。(08.03.02追記)

この「filename」を与えてやるにあたり、もう一箇所修正が出てくる。マルチパートのストリームを生成している、HTTP.Requestクラスのcreate_streamメソッドだ。

173行目でfilenameが与えられていた場合にそれをリクエストに追加するのだが、ここでパラメータオブジェクト名を除いた変数でアクセスしているため、これを修正しないと例外になる。tumblrの場合はこのロジックを通らないので見つからなかったバグなのだろう。

そんなわけでこんなやり方

こんな感じで手を加える部分がちょっと散っているので、

  • Tumblrオブジェクトはまるまる再定義
  • ImageSaver.postメソッドもまるまる再定義
  • create_streamメソッドは173行目を修正
といった感じで実装してみた。

修正コードのみですが

こんな感じで。

Tumblrオブジェクトの再定義

Tumblr = {
  // set your gelato admin page.
  endpoint : "http://localhost/gelato/admin/index.php",
  post : function(params, opts) {
    opts.multipart = true;
    return new HTTP.Request(this.endpoint, opts).post(params);
  }
}

opts.multipartへの値の割り当ては、オリジナルではparams.typeの内容をチェックしていたのだが、どうせマルチパート確定なので直接trueをセットしている。あとはURLはendpointに何も手を加える必要がないのでこんな感じになった。

ImageSaver.postメソッドの再定義

ImageSaver.post = function(title, bstream) {
  try {
    var uri = window.content.document.location.href;
    var file_name = (function() {
      var buf = [];
      for(var i = 0; i < 3; i++) {
        var s = ( Math.random() * ( new Date().valueOf() ) ).toString().replace(".", "");
        buf.push( Number(s).toString(36) );
      }
      return "capture-" + buf.join("").slice( -10 ) + ".png";
    })();

    Tumblr.post( {
      url: "",
      title: title,
      description: "(via: " + title.link(uri) + ")",
      type: 2,
      date: parseInt( new Date().valueOf() / 1000 ),
      btnAdd : "Create post",
      photo: {contentType: 'image/png', filename: file_name, stream: bstream}
    }, {
      async: true,
      onStopRequest: function (request, context, statusCode) {
        request.QueryInterface(Components.interfaces.nsIHttpChannel);
        var text = request.responseStatusText;
        trace(this.response_body);
        if ( ImageSaver.conf.id ) {
          text = text.fontcolor("white").link(
            'http://' + ImageSaver.conf.id + '.tumblr.com/post/' +
            this.response_body
          );
        }
        ImageSaver.close_text_delayed(text);
      }
    } );
  } catch(e) {
    Firebug.Console.log(e);
  }
}

Tumblr.post()に渡しているオブジェクトの構成は前述の通り。ローカル変数「file_name」はphotoに割り当てるファイル名なのだが、gelatoはかなり何も考えてなくて、同じ名前のファイルをアップロードすると無条件に上書きするというステキ仕様なので、仕方なくランダム生成している。 あとの例外ハンドリングなどはオリジナルコードそのまま。

上記2つのコードブロックを、capture.tumblr.jsの実行コード(new function() ~から始まる部分。542行目)の直前にそのまま差し込むことで再定義ができる。

そしてHTTP.Requestのcreate_streamだが、

chunk += 'filename="' + filename + '"' +  '\r\n';
chunk += 'filename="' + stream_params.filename + '"' +  '\r\n';
と修正すればよい。

あとはこの追加・修正を適用したファイルを「capture.gelato.js」とでもしてJSAスクリプトのglobalフォルダに放り込めば使える。ハズ。動かなかったらコメント下さい。といってもgelato使ってる人なんざほとんどいないだろうけど。

あ、あと38行目のconffileの設定は別にどうでもいいです。内容を参照しないので、ファイルさえあれば大丈夫なはずなので。

gelatoを試してみたり。2008年02月25日 21時32分03秒

久々の更新なのに全然 Zend じゃないし。

前々から興味があったのだ

しばらく前にMOONGIFTで紹介されていて、激しく興味があったオープンソースCMS「gelato」。これ、要するにtumblrクローン。

まぁ、まだバージョンが0.95なので鋭意開発中、って感じなんだろうけど、tumblr感覚でローカルにぽこぽこPOSTできるとちょっといいよな、と思って試しにインストールしてみた。

インストール

インストールはこちらの記事を参考に。つか、まんまです。PHPのアプリケーションインストールしたことあるならほとんど迷わないと思うけど、install.phpでDBがらみの情報入力させる割りに先に「config.php」にDB設定書き込んどかなきゃなんなかったのがイマイチ腑に落ちないけど。

それから先ほどのLiner Noteさんとこから、日本語化ファイルをダウンロードできるのでそちらも忘れずに。ありがたや、ありがたや。

使ってみる

管理画面はtumblrのダッシュボードのようにはいかず、ちょっぴり使いづらいんだけど、まぁ普通にPOSTできるわな。

ただ、設定でタイムゾーン選択できるんだけど、POSTに上手く反映されてないような。

あと、わかりづらいけど一応ブックマークレットもついてます。図の赤枠のところがリンクになってて、これをブックマークすれば一応ブックマークレット。

POST画面
「一応」って断り書きをしたのは、ちょっとクセがあってtumblr感覚で使うとわりと戸惑うかと。 試した限りでは、
  • 普通にクリックするとlink
  • なにか選択してると、選択部分のテキストを本文にしてregular post
  • 画像を表示しているとphoto
といった感じ。tumblrみたいに親切にページ中の画像を列挙してくれたり、選択部分からquoteしてくれたりはしない上に、ブックマークレットから起動したPOSTフォームは単機能なので切替もできず。もうちょっとがんばって欲しいところ。

JSActionのスクリプトを書いてみる

んで、実はこっからが本番。そもそもローカルで使いたい動機は、社内に設置している内向きのブログ書くときに、スクリーンショットとかを手間なくホストしときたいなーってとこだったわけ。なので、

  1. まずはJSActionでphoto直行
  2. その次はcapture.tumblr.jsをローカルgelato対応に
なんて野望を抱いているわけで。

capture.tumblrはちとソース読むのが大変そうなので、まずはフォームの構造を調べがてらJSActionスクリプトを作ってみた。こんな感じ。

// post photo to gelato
new function(base) {
	function h2q(h) {
		var buf = [];
		for(var key in h) {
			if( typeof(h[key]) != "function" ) {
				buf.push( encodeURIComponent(key) + "=" + encodeURIComponent( h[key] == null ? "" : h[key].toString() ) );
			}
		}
		return buf.join( "&" );
	}
	var url = window.location.href;
	var title = window.title || window.document.title || window.location.href;
	var img = _jsaCScript.context.target;
	if( ! /^img$/i.test( img.localName ) ) return;
	
	var xhr = new XMLHttpRequest();
	xhr.open( "post", base, false );
	var hash = {
		title : title,
		url : img.src,
		date : parseInt( new Date().valueOf() / 1000 ),
		type : 2,
		description : "<a href=\"" + url + "\" title=\"" + title + "\">(via: " + title + " )</a>",
		btnAdd : "Create post"
	};
	xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
	xhr.send( h2q( hash ) );

} ( "http://localhost/gelato/admin/index.php" );

あ、gelatoってGPL v2なんだけど、こういうスクリプトってGPLに引きずられるのかなぁ、と心配になったのでこのスクリプトも「GPL v2」ということにします。

このスクリプトをJSActionのimageフォルダに保存して、一番最後の関数呼び出しに使ってる引数にPOST先のURLに変更して使う。この例では「http://localhost/gelato/」がgelatoのURLね。

実はちょっと注意点があって、POSTデータの「description」(linkのcaption)に元ページのURLをanchorでくっつけてるんだけど、gelatoはdescriptionをstrip_tags()しちゃうのでこのままだとリンクにならない。

そのままでもいいやってなら別になにもする必要はないけど、admin/index.phpの72行目あたりの

$_POST["description"] = strip_tags($_POST["description"]);
$_POST["description"] = $_POST['description'];
に変更すればHTMLタグ使い放題。あー、自己責任でおながいします。

あ、あとほんとに最低限の実装なので、例えばflickrで「all sizes」がある場合はそっちへ飛んでからじゃないと上手くPOSTできません。あとPOSTエラーも特に何もしてないので、「POSTされてなかったらなんかエラーでたんだなー」といった感じで使ってください。使う人いるかわからんけど。

その他の情報

  • 一応read apiも実装してるっぽいけど、コメントアウトの仕方が悪くてエラーになったり、そのあたり解消してもまともに情報返してくれなかったり。
  • なぜか編集ができない。新規POST扱いになる。
  • photoはlightbox使って表示するんだけど、上手く表示できないときがある。
  • lightboxとかで使ってる画像リソースのパス解決が上手くいかない場合があるっぽい。
  • アカウントを複数作れるけど、アカウント別に表示できたりするわけじゃないし、他のアカウントのPOST削除できちゃうし。
  • svnから最新ソースをチェックアウトしてみたけどクラスだか関数の重複定義とかでエラーがでて、もうぐだぐだ。
などなど、まだまだかなりバタバタした感じですが、ローカル(or イントラ)画像サーバと思えばなんとか使えなくもないかな。

次はがんばってcapture.tumblr.jsの改造だ。多分。