半自動はてなフォトライフアップローダー

ダウンロード ver.0.0.0.4 2009/12/28

はてなフォトライフに画像をワンクリックでアップロードするプログラムです。ドラッグアンドドロップや「送る」での転送のほかに、実行すると設定したフォルダの中から最新の更新画像一枚をアップロードするという機能があります。利用例としてデジカメ接続時やメモリーカード内の画像フォルダを指定することを想定しています。写真撮る→PCに繋げる→プログラムを実行する→アップロード完了。みたいな流れです。Twitterに載せるための写真とか最新一枚が基本、Blogに載せる場合でも、一枚で済む場合って結構多いよね。そんな感じに、サクサクッと写真と付き合えたらいいな、と。

なお、このアプリケーションの実行には.NET Framework 3.5 SP1のインストールが必須です。また、ソースコードを同梱していますのでご利用はご自由にどうぞ。言語はC#でVisual Studio 2008で作成しています。コンパイル時の注意事項等は同梱の説明書きを参照ください。

デジカメ写真の半リアルタイム確認システムの構築

写真撮る→PCに即座に転送される→ビューアーで転送された画像が自動的に開かれる→つまり撮影画像のプレビューがPCの画質で出来る、わーい。ふむ、何のこっちゃ。というわけで実際に使っている風景を動画をで撮ってみました。ケータイ画質で、風邪薬のビンを取るという適当なものですが、ただの説明なのでご勘弁を。画像転送に少し時間かかってるのでリアルタイムとは言いませんが、我慢できる範囲には収まっていると思います(もう少し速く、とは思いますが)

私は写真撮影に関して完全に素人で、ホワイトバランスがデタラメだったり露出があっていなかったり、そもそもピンボケだったりと失敗写真を繰り返しています。デジカメの液晶ではちゃんと撮れているように見えたのに!そんなわけで、撮ったものをちゃんとした画面で即座にモニタリングして、設定を煮詰め直すなり撮り直すなりが出来ればいいな、と思っていました。それが出来たら、特に室内での小物や料理撮影には便利だな、と。もちろん、Rawで撮って後から調整すれば良いという話もありますが、さすがにRawは手間がかかりすぎます。もっとカジュアルに付き合いたい。

Eye-Fi

Eye-Fiをご存じですか? Eye-Fi Japanに解説がありますが、無線LANを内蔵したSDカードで、写真を撮るとカードを抜くことなくその場で対応するオンラインサービス(はてなFotolifeやflickrなど)に送信してくれる、というものです。別にそんなに写真公開することなんてないし、そもそも外行かないしでイラナイよなー、なんて考えていたのですが、よーく解説を見ると、PCに送ることも出来るではないか。ということは、撮る→即座にPCで確認出来る。が実現出来る。こ、こんな当然のように思えることに今まで気づいていなかったなんて、悔しい。

ファイル監視ソフト

画像を自動でPCに転送出来る、となると、あとは転送された画像を自動で認識して画像ビューアーを起動するだけです。ちょっとソフトを探してみたのですが、良いものが見当たらない。まず、数分間隔でチェックするものは却下。何故なら、画像が転送されたら即座に起動してくれなければ意味がないから。他にもゴテゴテと機能が多すぎたりと、こういう単純な用途にフィットする監視ソフトが見当たらなかった。んー、ないなら自分で作ればいいぢゃない。というわけで、自分で作りました。シンプルなファイル監視ソフトを。

起動するとタスクトレイに常駐して、設定したフォルダを監視し、「新しいファイル」が作られると指定したアプリケーションにファイルパスを渡しながら自動的に起動します。例えば、指定フォルダをEye-Fiで画像が保存されるフォルダを指定し、実行するアプリケーションに画像ビューア(IrfanViewだったりPicasaだったり)を指定すると、写真を撮る度に画像が自動的に開かれる。ようするに冒頭の動画のような感じになる。というわけです。

アップロード

ついでに、Webサービスへのアップロードですが、私の場合は以前作成した半自動はてなフォトライフアップローダーを使ってワンクリックでアップロードしています。実行すると、事前に指定したフォルダの最新画像一枚だけをアップロードするプログラム(今回、少し更新してサブディレクトリもオプションで含められるようにしました)。なので、指定フォルダとしてEye-Fiが転送する画像フォルダを指定しておけば、Eye-Fiから転送されてビューアで開かれる画像を見て、採用!と思ったらクリックするだけ。それでアップロード完了。とてもお手軽です。

Eye-Fi Pro

そんなわけで、無線LANがインターネットにつながっている必要はなく、PCと一対一に通信出来ればそれでいい。のですが、残念ながら現在日本で販売されているEye-Fiカードは一対一では転送出来ません。海外で既に発売されているEye-Fi Proというモデルではアドホックモード(無線LANを搭載したデバイス、ノートPCとかと直接転送可能になる)が使えます。これがあれば、撮影→ノートPCの画面でプレビューが山でも川でも居酒屋でも、どこでも出来るのに!というわけで、早く発売されて欲しいです、Eye-Fi Pro。

GXR

Eye-Fiは大抵のカメラで使えるようなのですが、たまーに使えないカメラもあるようです。私が以前使っていたカメラは、残念なことにそのたまーに使えないカメラ、に該当してしまったようです。しょうがないので、Eye-Fiのためにカメラを買い換えました。ついでのついでなので合体機構がそそるGXRを購入。GXRは素晴らしいよ!何といってもEye-Fiが使えます(笑) 画質良し、操作感良し、そして何よりも(一眼画質のカメラとしては)軽い。よって、こういったお手軽プレビュー&アップロードには大変適している、気がします。一昨日来たばかりなうえに、私は写真ド素人なので詳しい評価は出来ませんが、かなり気に入ってます。

ver 0.0.0.3

拡張子が大文字だとアップロード出来なかったので直しました。XboxInfoTwitの時も同じのやってたのにまたかよって感じです。拡張子判定部分は、ちゃんとIgnoreCaseにしたしあれえ?と思ってたんですがContentType作るところで漏れがあって、ウッカリ。てへ。

まあ、そんなこんなでちゃんと一眼レフも買いました。わざわざこのためだけに!neuecc’s fotolife 。それで、しかし撮るものが悲しいほど無いんですよね、やっぱり。とはいえ、自分で撮って自分で上げてかないと、何をどうすれば良くなるのか分からないので、室内写真で栄える何かを探し中です。現在は多肉植物でも育てようかな、と思ってるんですがどうでしょうかねえ。

ちなみに現在までのDL数は余裕で一桁。べ、別に悲しくないもん! そういえばこの半自動ってぶっちゃけ機能的にいらなくね?むしろフォルダ監視で自動化したほうがよくね?とも思ってきたので、まあ、そのうち。そのうち。

LINQ to XMLのNamespaceと書き出し時のEncodingについて

ver 0.0.0.2に更新。アップロードするフォルダが指定出来るようになりました。アップロードツール名(FotolifeUploader)が利用されるようになりました。フォルダ指定は再設定が必要なので、前のバージョンを使っている方はsettings.xmlを削除して、再度設定し直してください。あとは間抜けだったUploadToFotolifeメソッドを手直ししたり。

私自身が、そもそもフォトライフのヘビーユーザーではないので、細かいところに気が利いてないかもですね……。そういうのは、よくない。というわけで、当分はFotolifeをちゃんと利用しようキャンペーンを張ることにします。なので、デジタル一眼を買う。と言いたいのだけど、何か微妙なのよねん。いや、そもそも引き籠って家から出ないので撮影するものがないので。かといって熱帯魚や食虫植物とかフィギュアとか、撮影に適した趣味があるわけでもなく。困った困った。まあ、考えます。食虫植物を育てる方向で(?) 部屋が殺風景なので何かは入れたいのだけど、手間はかけたくない。ううむ、難しい。

LINQ to XML

アップロードにはAtomAPIを利用しているので、XMLです。つまりLINQ to XMLの出番です。出力結果がこんな感じなので、そこから逆に考えると……

<entry xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns="http://purl.org/atom/n
s#">
  <title>タイトル</title>
  <content mode="base64" type="image/jpeg">画像BASE64</content>
  <generator>FotolifeUploader</generator>
  <dc:subject>フォルダ名</dc:subject>
</entry>

XElementは、Namespaceの利用が少しややこしいんですよね。最初引っかかりました。「XNamespace.Xmlns + “接頭辞”」で登録できます。

XNamespace ns = "http://purl.org/atom/ns#";
XNamespace dc = "http://purl.org/dc/elements/1.1/";
var xml =
    new XDocument(new XDeclaration("1.0", "UTF-8", null),
    new XElement(ns + "entry", new XAttribute(XNamespace.Xmlns + "dc", dc),
        new XElement(ns + "title", "タイトル"),
        new XElement(ns + "content", new XAttribute("mode", "base64"), new XAttribute("type", "image/jpeg"), "画像BASE64"),
        new XElement(ns + "generator", "FotolifeUploader"),
        new XElement(dc + "subject", "フォルダ名")
    ));
Console.WriteLine(xml); // 出力確認、DeclarationはToStringでは出力されない

少し独特ですが、ほとんど1:1で対応させられるので慣れるとサクサク書けます。非常に快適。個人的にはXMLリテラル的なものよりも好き。Linqがあってほんと良かった……。で、Declarationを出力したい場合の話に続く。(hatena (diary ’Nobuhisa))にもあるように、ToStringでは出力されないのでSaveを使う、と……

var sb = new StringBuilder();
var sw = new StringWriter(sb);
xml.Save(sw);
Console.WriteLine(sb); // UTF-16になる

これでencodingがUTF-16になるのは、Saveメソッド呼ぶとDeclarationは作りなおしているから。.Save(”string fileName”)ではXDeclarationのエンコーディングを見て、それで保存するけれど、それ以外の場合はXDeclaration無視で再構築される。XDocumentというかXmlWriterのほうの話でしょうか。実際にファイル出力してみると分かる。

var fs = new FileStream(@"C:\text.xml", FileMode.Create);
var sw = new StreamWriter(fs, Encoding.GetEncoding("x-mac-turkish"));
xml.Save(sw);

出力先のエンコードに合わせてくれる、のを便利と見るか、むしろ気が利かない、Writer部分もC#3.0に合わせて作りなおせ、なのかは不明。まあ、嘘エンコード宣言は許しませんよってことですかね。じゃあどうするか、って言ったら

// これで別に何も問題ないと思います、文字列として吐くんだからToStringでいいと思ふ
var xmlString = string.Format("{0}{1}{2}",
    xml.Declaration, Environment.NewLine, xml);
Console.WriteLine(xmlString);
 
// ToStringがどうしても嫌ならMemoryStream経由で、とか?
string result;
var encoding = Encoding.UTF8;
using (var ms = new MemoryStream())
using (var sw = new StreamWriter(ms, encoding))
{
    xml.Save(sw);
    result = encoding.GetString(ms.ToArray());
}
Console.WriteLine(result); // 望み通りのUTF-8で出力されてます

結論は、普通にToStringでいいんじゃないかな、と。ToStringメソッドだけではXmlWriterSettingsで言うところのOmitXmlDeclarationを設定出来ないから、デフォルトでは付加しないようにしてる。削除は無理だけど、追加なら簡単だから。XmlDeclarationを付加したい時は別途、自分でくっつければいい。というだけのお話かなー? ToStringで一発で終わらせられないからStringBuilder使って組み立てるってのは、何でそうなるの?と、とても思った。ついでにもう一つ。

// こんなXElementがあるとして
var xElement = XElement.Parse("<hoge>3</hoge>");
// intとして値を取り出す時は
var num1 = int.Parse(xElement.Value); // これダメ。
var num2 = (int)xElement; // こう書こう。

です。LINQ to XMLは既存のものを上手く使ってシンプルに書けるように作られてる。気がする。このキャストもそうだし、ToStringもそう。Parseは頻繁に行うから汚くなるよね→キャストでよくね? 文字列化はよくやるけどSaveもXmlWriterSettingsも面倒くさいよね→ToStringでよくね? といった感じ。関数型構築もそうだけど、今までのもの(XmlDocument)を踏まえて、よく練り直されているなー、と思います。

半自動はてなフォトライフアップローダー ver 0.0.0.1

はてなフォトライフに画像をワンクリックでアップロードするプログラムです。ワンクリックの手間があるので、半自動。主な機能は、実行すると設定したフォルダの最新の更新画像一枚をアップロード。利用例としてデジカメ接続時やメモリーカード内の画像フォルダを指定することを想定しています。写真撮る→PCに繋げる→プログラムを実行する→アップロード完了。みたいな流れです。Twitterに載せるための写真とか最新一枚で十分でしょう? Blogに載せる場合でも、一枚で済む場合って結構多いよね。そんな感じに、サクサクッと写真と付き合えたらいいな、と。

設定

まさかのCUI設定画面(笑) 初回起動時にこの画面になります。設定し直したい時は、生成されるsettings.xmlを削除してください。レトロでアナログで半自動を貫く感じがいいかなー、と思ったんですが、どうでしょう。

最新画像一枚のアップロード

設定終了後にexeファイルを実行すると、設定時に指定したフォルダの中の、拡張子が「jpg/jpeg/gif/png/bmp」で更新日時が最も新しいもの一枚をアップロードします。設定によってはアップロード後にブラウザでフォトライフのURLが開きます。なので、そこからそのままTwitterにURLをポストするなりBlog書くなりがシームレスに行えるわけです。キリッ。ちなみにリサイズ等はこちら側では一切しません、そのまま丸投げ。リサイズ処理もはてな任せ。

任意画像複数枚のアップロード

フォルダ/画像をまとめてexeファイル(本体じゃなくてショートカットでもOKです)にドラッグアンドドロップすると、そのファイルをアップロードします。フォルダはサブディレクトリを含めて全てのファイルをアップロードします。拡張子が「jpg/jpeg/gif/png/bmp」以外のものはちゃんと無視しますので、多少適当でも大丈夫。また、いわゆる「送る」にショートカットを登録することで、このドラッグアンドドロップと同様の結果になります。Vistaの場合はエクスプローラー上で「%AppData%\Microsoft\Windows\SendTo」と入力するとSendToのフォルダに飛べますので、ここにショートカットを登録してみてください。

今回コンソールアプリにしたのは、実行にかかる手間を最小にしたかった、というのがあります。普通のアップロードアプリだと、「アプリを起動→画像フォルダを開く→ドラッグアンドドロップで画像を乗っける→アップロードボタンを押す→アプリを閉じる→Fotolifeにアップロードされた画像を確認しにいく」 これじゃあ工程多すぎであまりにも面倒くさい。というわけで、最新画像一枚ならば、アプリ起動だけで完了。複数毎でも画像フォルダ→ドラッグアンドドロップだけで完了という、考え得る限りの最短を目指しています。

ソースコード

ソースコードも同梱してあります。csファイル一つだけの、200行ちょいのちっぽいコンソールアプリです。好きに改変とか突っ込みとかディスとかしてください。しいていえば、Linqだらけです。個人的には

.SelectMany(s => (Directory.Exists(s))
  ? Directory.GetFiles(s, "*", SearchOption.AllDirectories)
  : Enumerable.Repeat(s, 1))
.Select(s => new FileInfo(s))
.Where(fi => fi.Exists && FotolifeExtensionPattern.IsMatch(fi.Extension))

この部分が気に入ってます。ドラッグアンドドロップで来る文字列配列からファイル抜き出しの部分。SelectManyでディレクトリをファイル名配列に、ディレクトリじゃない場合はEnumerable.Repeatで繰り返し回数が1回のファイル名配列にする。あとはまあ普通に、SelectしてWhereしてToArray。Linqがあって良かったーと本当に思う。逆にAtomPub APIでアップロードする部分はLinqでやる意味がなかったというか、当初予定と変わってあれ追加これ追加で肥大化してしまった結果でして……。

LLの人はこの手のちょっとしたスクリプトをほいほい公開しているわけだから、C#もコンソールアプリぐらいほいほい公開出来ないといかんのぅ、と思いつつもページ用意して云々かんぬんは面倒くさくて、そうホイホイってわけにもいかない感じ。もちっと軽くやれる環境作らないとね……。まあ、でも、このちょっとした重苦しさも悪くはないんだ。だってほら、Rubyでスクリプトがホイッって転がってても、普通の人は動かせもしないわけですよ。だから、少し面倒くさいなー、と思いつつ設定画面つけてexeの形式にして、それだけで幸せになれないかな、どうだろう。

私はプログラム書き始めたのがほんとつい最近で、利用するだけ人間の歴が何年も何年もあるので、その辺は極力優しくやりたいなあ、と思ってます。

Software