MacBookをPicLens対応サーバにしてiPhoto Libraryを覗いてみる

PicLensは脳みそに直接響くソフトウェアだ。一度使うと、その感動的な操作性にハマってしまう。最初は今までにない操作感にちょっと戸惑うが、3D空間に魅せられて触っているうちにすぐに慣れた。2本指スクロール可能なMacBooKトラックパッドとの相性はとてもいい。*1慣性の法則が働く、滑らかなスクロールと角度の変化は、現実にはあり得ない空間を実際に操作している気分にしてくれる。これが本当の3Dデスクトップだ!*2と感じた。そして、これがFirefoxプラグインで実現されているのかという驚き。*3
こうなると、web上に限らず、自分のMacBookの中もPicLensで覗いてみたい!という欲求が湧いてくる。これは自然な欲求のようで、公式サイトにもPicLens Publisherという、まさにそれ用のアプリケーションが紹介されている。しかし、使ってみると、これは自分が求めていた仕組みではないと感じた。
例えば、自分のとあるiPhoto Libraryの2008フォルダには500枚の写真が保存されている。これをPicLens PublisherでPicLens対応サイトとして加工すると、別途240MBのフォルダが出来上がってしまう...。500枚というのはほんの一部で、実際には10000枚以上の写真が保存されており、それを加工したら、単純に20倍すると別途5GBくらいの領域が必要になってしまうのだ。しかも写真は次々と追加される。その都度、PicLens Publisherを起動して、Import Folder、Add Image、Export Folderを繰り返し処理しなくてはならない...。面倒くさい。
そして、気付いた。Railsがあるじゃないか!と。Railsで動的にPicLens対応サイトを作ってしまえばいいのだと。深く考えずに早速、作業開始!

PicLens対応に必要なこと

まずはPicLens Publisherが生成したサイトを調べてみる。PicLensが機能することを確認しながら、適当に予想して不要と思われる部分をどんどん削除していく。残った部分がPicLensに最低限必要な部分になるはず。やってみると以下のようになった。

  • images
    • サムネールの拡大、全画面表示で利用される大きな画像。
  • thumbs
    • 3行表示のスクロール空間で利用される小さな(サムネール)画像。
  • gallery.html
    • 必要なのは以下のhead部分のみ。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
	"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
	<title>PicLens_test</title>
	<link id="gallery" rel="alternate" href="photos.rss" type="application/rss+xml">
	<script type="text/javascript" src="http://lite.piclens.com/current/piclens.js"> </script>
</head>
</html>
  • photos.rss
    • ここで指定した情報を元にして、PicLensが表示するようだ。
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss xmlns:media="http://search.yahoo.com/mrss" version="2.0">
    <channel>
        <generator>PicLens Publisher/Mac 1.0.4</generator>
        <item>
            <title>DSC_0001.JPG</title>
            <link>images/DSC_0001.jpg</link>
            <media:thumbnail url="thumbs/DSC_0001.jpg"/>
            <media:content url="images/DSC_0001.jpg"/>
            <guid isPermaLink="false">F188115A-F733-4B41-B61E-2E86311C322F</guid>
        </item>
    </channel>
</rss>
      • title...マウスオーバーした時や、拡大した時に表示される文字列。(無くても大丈夫)
      • link...表示している画像へのリンクURL。拡大した時に画面下の地球アイコンをクリックすると、PicLensモードを終了して、そのURLの画像をブラウザで表示。(無くても大丈夫)
      • media:thumbnail...小さな(サムネール)画像のURL。(必須)
      • media:content...大きな画像のURL。(必須)
      • guid...一意な識別子。何に利用されるか不明...。(不要?)

ここまでやって、なんと!公式サイトでちゃんと説明されていることに気付く...。(いつもながら、無駄な労力を使ってしまった。英語のドキュメント、ちゃんと読むべきですね。まあ、大体のところは予想通りであると確認できた。)

iPhoto Libraryへ直接リンク

  • media:thumbnail、media:contentのURLをiPhoto Libraryへ直接リンクしてみる。
  • media:thumbnailのURLを、media:contentのURLと同じにしてみる。

以上の設定で、ちゃんと表示されるか試してみると...予想通り表示された!嬉しい。つまり、imagesとthumbsフォルダの画像は不要になるのだ。これならRSSの生成とheadタグだけでPicLensに対応できるかもしれない。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
	"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
	<title>PicLens_test</title>
	<link id="gallery" rel="alternate" href="photos.rss" type="application/rss+xml">
</head>
</html>
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss xmlns:media="http://search.yahoo.com/mrss" version="2.0">
    <channel>
        <generator>PicLens Publisher/Mac 1.0.4</generator>
        <item>
            <title>DSC_0001.JPG</title>
            <link>/Users/zari/Pictures/iPhoto%20Library/Originals/2008/ロール%20400/DSC_0001.JPG</link>
            <media:thumbnail url="/Users/zari/Pictures/iPhoto%20Library/Originals/2008/ロール%20400/DSC_0001.jpg"/>
            <media:content url="/Users/zari/Pictures/iPhoto%20Library/Originals/2008/ロール%20400/DSC_0001.jpg"/>
        </item>
    </channel>
</rss>

Railsで静的サイトを作る

  • まずは、お決まりの手順を実行した。
$ rails iphotolens
$ cd iPhotoLens
$ script/generate scaffold picture path:string
$ rake db:migrate
  • rssへのlinkタグは、レイアウトファイルに記述することにした。
<%# レイアウト: app/views/layouts/pictures.html.erb %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html>
<head>
  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
  <title>Pictures: <%= controller.action_name %></title>
  <%= stylesheet_link_tag 'scaffold' %>

  <link id="gallery" rel="alternate" href="photos" type="application/rss+xml"><%#<--- 追記 %>
</head>
<body>

<p style="color: green"><%= flash[:notice] %></p>

<%= yield  %>

</body>
</html>
  • photosアクションを追加した。(名詞がアクションなんて、ちょっと問題ありだが...。)
# コントローラー: app/controllers/pictures_controller.rb
class PicturesController < ApplicationController
...(中略)...
  def photos
    render :layout=>false
  end
...(中略)...
  • photos.html.erbとしてRSSファイルを追加した。ポイントは以下。
    • URLの指定に「file://」を追記したこと。
    • URLにURI.escapeしたこと。(ファイル名の半角スペースや日本語にも対応させるため)
<%# ビュー: app/views/pictures/photos.html.erb %>
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss xmlns:media="http://search.yahoo.com/mrss" version="2.0">
    <channel>
        <generator>PicLens Publisher/Mac 1.0.4</generator>
        <item>
            <title>DSC_0001.JPG</title>
            <link>file:///Users/zari/Pictures/iPhoto%20Library/Originals/2008/%E3%83%AD%E3%83%BC%E3%83%AB%20400/DSC_0001.JPG</link>
            <media:thumbnail url="file:///Users/zari/Pictures/iPhoto%20Library/Originals/2008/%E3%83%AD%E3%83%BC%E3%83%AB%20400/DSC_0001.jpg"/>
            <media:content url="file:///Users/zari/Pictures/iPhoto%20Library/Originals/2008/%E3%83%AD%E3%83%BC%E3%83%AB%20400/DSC_0001.jpg"/>
        </item>
    </channel>
</rss>
ActiveRecord::RecordNotFound in PicturesController#show
  Couldn't find Picture with ID=photos
  • そうか、Rails2.0からはRESTなroutingになっていたことに気付く。アクションを追加したら、ルートの設定も必要だ。
# ルート: config/routes.rb
ActionController::Routing::Routes.draw do |map|
  map.resources :pictures, :collection=>{:photos=>:get}

  map.connect ':controller/:action/:id'
  map.connect ':controller/:action/:id.:format'
end
  • これでRSSがちゃんと取得できるようになった。(ソースで確認した。)
  • さらに「http://localhost:3000/pictures」にアクセスして、ブックマークバー右のLunch PicLensボタンを押してみると...1枚だけだが、写真がPicLensで表示された!成功!

Railsで動的にRSSを更新する

あとは、上記RSSの部分をPicLensで見たいフォルダに合わせて更新されるように工夫すれば良いはず。以下のようにやってみた。

  • パラメーターparams[:path]を取得して、そのフォルダパス以下に含まれる写真ファイルのRSSを生成する。
# コントローラー: app/controllers/pictures_controller.rb
class PicturesController < ApplicationController
...(中略)...
  def photos
    type = "jpg,JPG,png,PNG,gif,GIF"
    path = File.expand_path(params[:path] || "~/Desktop")
    exclude_path = File.expand_path("~/Pictures/iPhoto Library/Data")
    @pictures = Dir::glob("#{path}/*.{#{type}}") + Dir::glob("#{path}/**/*.{#{type}}") - Dir::glob("#{exclude_path}/**/*.{#{type}}")
    render :layout=>false
  end
...(中略)...
  • Builder::XmlMarkupを利用して、RSSの生成を行う。
# ビュー: app/views/pictures/photos.rxml
xml.instruct!
xml.rss('version' => '2.0', 'xmlns:media' => "http://search.yahoo.com/mrss") do
  xml.channel do
    xml.generator("PicLens Publisher/Mac 1.0.4")
    @pictures.each do |f|
      file = URI.escape(f)
      file_path = "file://" + file
      thumbnail_path = file_path.gsub("/iPhoto%20Library/Originals/", "/iPhoto%20Library/Data/")
      xml.item do
        xml.title(file)
        xml.link(file_path)
        xml.media(:thumbnail, :url=>thumbnail_path)
        xml.media(:content, :url=>file_path)
      end
    end
  end
end
      • 最初、小さな(サムネール)画像の準備をしなかったが、写真の枚数が多い時、それだとPicLensの滑らかな動きがぎこちなくなってしまった...。iPhoto Libraryの閲覧時のみ、サムネールが対で用意されていたのでそれを利用することにした。「thumbnail_path = file_path.gsub("/iPhoto%20Library/Originals/", "/iPhoto%20Library/Data/")」の部分。
  • headタグ内のRSSへのlinkタグは、RSS auto-discoveryというお約束の機能らしく、Railsにもヘルパメソッドが用意されていた。
  • パラメーターとしてフォルダパスを指定するようにして、フォルダ内の写真に対するRSSを生成するようにした。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html>
<head>
  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
  <title>Pictures: <%= controller.action_name %></title>
  <%= stylesheet_link_tag 'scaffold' %>

  <!--link rel="alternate" href="photos" type="application/rss+xml"-->
  <%#= auto_discovery_link_tag :rss, {:action=>'photos', :path=>params[:path]} %><%#<---変更 %>
  <%= auto_discovery_link_tag :rss, photos_pictures_path(:path=>params[:path]) %><%#<---さらに変更 %>
  <script type="text/javascript" src="http://lite.piclens.com/current/piclens.js"> </script>
</head>
<body>

<p style="color: green"><%= flash[:notice] %></p>

<%= yield  %>

</body>
</html>
  • picturesテーブルにはPicLensで見たいディレクトリ情報を保存しておく。
  • リストごとにPicLensで再生するボタンを付けてみた。
<%# ビュー: app/views/pictures/index.html.erb %>

<h1>Listing pictures</h1>

<table>
  <tr>
    <th>Path</th>
  </tr>

<% for picture in @pictures %>
  <tr>
    <td><%=h picture.path %></td>
    <td>
      <%= link_to 'Show' + image_tag("http://lite.piclens.com/images/PicLensButton.png", 
                                     :alt=>"PicLens", 
                                     :width=>"16", :height=>"12", 
                                     :border=>"0", :align=>"baseline"), 
          picture_path(picture.id, :path=>picture.path) %>
    </td>
    <td><%= link_to 'Edit', edit_picture_path(picture) %></td>
    <td><%= link_to 'Destroy', picture, :confirm => 'Are you sure?', :method => :delete %></td>
  </tr>
<% end %>
</table>

<br />

<%= link_to 'New picture', new_picture_path %>
  • showページを表示した時、PicLensがスタートするようにした。
<%# ビュー: app/views/pictures/show.html.erb %>

<p>
  <b>Path:</b>
  <%= link_to h(@picture.path) + image_tag("http://lite.piclens.com/images/PicLensButton.png", 
                                           :alt=>"PicLens", 
                                           :width=>"16", :height=>"12", 
                                           :border=>"0", :align=>"baseline"), 
              picture_path(@picture.id, :path=>@picture.path) %>
</p>


<%= link_to 'Edit', edit_picture_path(@picture) %> |
<%= link_to 'Back', pictures_path %>

<%# パラメーターに:pathが指定されていたら、直ちにPicLensを実行する %>
<% if params[:path] %>
  <%= javascript_tag "PicLensLite.start();" %>
<% end %>

以上で、かなり手抜きだが、作業完了。後日、上記コードを書き直した...。iPhotoLensの書き直しへ続く。

所感

  • 写真の枚数が多いと、PicLensで表示されるまでにかなり時間がかかる。一度に指定する枚数は、せいぜい1000枚くらいにした方が良いかも。(そうは言ってもフォルダ指定なので、写真が何枚含まれているかなんて調べるのは面倒だが...。)
  • 同じく写真が多いと多量のメモリを消費するらしく、いつもは軽快なMacBookの動きが途端にぎこちなくなる。開発中に突然再起動するというトラブルも一回発生した。(ルートに近いフォルダを指定した時は注意が必要だ。フォルダ以下の写真すべてを表示するので、枚数がとてつもなく多くなる可能性がある。)
  • ハードディスク内に眠っている未知の写真がたくさん見つかるかもしれない。
  • やはり、公式ページのPicLens Publisherが生成したサイトの動きは滑らか。サムネールや大きな画像のサイズがPicLensに最適化されているのだと思う。
  • PicLens想定外の、大き過ぎる画像は表示されない?(自分のMacBook環境で、3008x2000の縦長画像は表示されなかった...。なぜだろう?)

*1:左右方向のスクロールが使えるのが気持ちいい。

*2:それに比べて、Windows Vistaのウィンドウサーチモードなんて、動きはカクカク、重なり過ぎて逆に探し難い、3Dが無意味...。

*3:自分が知らないだけかもしれないが、webブラウザ以外のデスクトップアプリケーションとして、似たような仕組みで写真や動画を閲覧するソフトってあるのだろうか?