使ってみよう! Windows Live SDK/API

第22回Windows Live フォト ギャラリー ─⁠─ はてなフォトライフ プラグインの作成 Part I

はじめに

今回からは数回にわたりWindows Live Photo Gallery SDKsにある、Windows Live Photo Gallery Publishing Plug-in Platformを扱います。

本連載では、SDKを使用した具体的なアプリケーションとして はてなフォトライフへ写真を投稿するWindows Live フォト ギャラリーのプラグインを作成します。今回は、Live Photo Gallery SDKsと、関連するWindows Liveサービスのアプリケーションについて簡単に紹介し、プラグイン開発の準備まで行います。

Windows Live フォト ギャラリー

Windows Live フォト ギャラリー図1は、Windows XPに付属していた画像ビューワーの「Windows 画像とFAXビューワー」やWindows Vistaに付属している「Windows フォト ギャラリー」の後継となる無償の製品です。単に画像を表示するだけでなく、タグや撮影日による分類や、簡易レタッチ機能などの操作が可能です。

図1 Windows Live フォト ギャラリー Beta
図1 Windows Live フォト ギャラリー Beta

執筆時点では、Live フォト ギャラリー Betaを http://download.live.com/ からダウンロードできます。これは2008年9月にWindows Liveサービスのベータ版を一斉にリリースしたうちのひとつになります。このリリース前からLive フォト ギャラリーはありましたが、ベータ版ではインターフェースの改善や顔認識による人物タグ機能が追加されました。また、開発者向けにSDKの提供がされています。本連載ではこのSDKを使用した開発を行います。

Windows Live ムービー メーカー

Windows Live ムービー メーカー図2は、2008年9月のリリースから新しくWindows Liveブランドとして登場した製品です。写真、動画、音楽を組み合わせた動画の作成ができるシンプルなアプリケーションです。

図2 Windows Live ムービー メーカー Beta
図2 Windows Live ムービー メーカー Beta

Liveフォト ギャラリーと同じくベータ版を http://download.live.com/ からダウンロード可能です。現時点でのLive ムービー メーカーの機能は少なく今後が期待されます。

Live Photo Gallery SDKs は、Live フォト ギャラリーだけでなく、このLive ムービー メーカーの機能も拡張できるようになっています。少し言い換えると、SDKにより作成した拡張機能は、Live フォト ギャラリーとLive ムービー メーカーの両方から参照されることになります。

Windows Live Photo Gallery SDKs

Live Photo Gallery SDKsは次のふたつから構成されています。

  • Windows Live Photo Gallery Publishing Plug-in Platform
  • Windows Live Photo Gallery Simple Extensibility Points

Live Photo Gallery Publishing Plug-in Platform

Live Photo Gallery Publishing Plug-in Platformを利用するとWindows Live フォト ギャラリーとWindows Live ムービー メーカーの投稿用プラグインの作成が可能です。いずれのLiveアプリケーションにも「投稿」メニューがあり、ほかのサービスへ写真や動画を投稿(アップロード)できるようになっています図3⁠。この部分を拡張するプラグインとなります。

図3 投稿メニュー
図3 投稿メニュー

Liveアプリケーションでは、はじめからWindows Live Spaces、Soapbox on MSN Video、Flickrへの投稿が可能になっています。またFacebook、YouTube用や、各種写真共有サイトへの投稿プラグインが公開されています[1]⁠。

Live Photo Gallery Simple Extensibility Points

Live Photo Gallery Simple Extensibility Pointsは、Liveアプリケーションの「その他」メニューの拡張ができ、外部アプリケーションを開くために利用できます図4⁠。この拡張を利用しているものとして複数の写真から3次元空間を作成するPhotosynthなどがあります。

図4 その他メニュー
図4 その他メニュー

本連載では、Live Photo Gallery Publishing Plug-in Platformによる投稿用プラグインを作成します。

はてなフォトライフ

現在公開されている投稿プラグインは英語のものばかりですので、本連載では日本語の写真・動画共有サービスの はてなフォトライフ図5の投稿プラグインを作りたいと思います。

図5 はてなフォトライフ
図5 はてなフォトライフ

はてなフォトライフでは、はてなフォトライフAtomAPIというAPIが提供されていますので、これを利用します。このAPIの利用は本題ではありませんので、詳しくは公式のドキュメントを参照して頂きたいと思いますが、本連載でも一通りの必要なコードは示しています。

開発環境

投稿プラグインは、.NET Frameworkアプリケーションとなります。本連載ではVisual Studio 2008を使用し、Visual Basicで書いたコードを紹介します。連載では開発ツールとして有償のVisual Studioを使用していますが、無償のVisual Basic 2008 Express Editionでも十分に開発が可能です。

はてなフォトライフAtomAPI

具体的なプラグインの作成は次回からはじめます。今回は はてなフォトライフAtomAPIを利用して写真を投稿する部分を実装しておきましょう。

はてなフォトライフAtomAPIは、Webサービスで一般的に利用されているWebリソース編集のためのAtom出版プロトコルによるAPIです。LiveサービスのAPIでも、LiveユーザーデータAPIなどで使用されています。

現在 はてなフォトライフAtomAPIがサポートしている操作は、新規写真の投稿、写真のタイトル変更、写真の削除などがあります。このうちの新規写真の投稿のみ利用します。

WSSE認証

はてなフォトライフAtomAPIでは、ユーザーの認証にWSSE認証と呼ばれる認証が利用されています。APIを利用するアプリケーションはこの認証を実装する必要があります。まずはこの認証部分を作成します。

APIを利用して はてなのサーバーへアクセスする際には、HTTP GETまたはPOSTメソッドによるリクエストを行います。このときリクエストにX-WSSEヘッダを含めることで認証します。ヘッダの書式は以下のようになります。

X-WSSE: UsernameToken Username="***", PasswordDigest="XXX", Nonce="XXX", Created="2008-12-01T12:00:00"

ヘッダ内にある各パラメータは次の値となります。

Username
ユーザー名(はてなのアカウント)
Nonce
リクエストごとに生成する使い捨てのランダムな文字列(セキュリティ・トークン)をBase64エンコードしたもの
Created
Nonceが生成された日時をISO-8601表記したもの
PasswordDigest
NonceとCreatedと(はてなのアカウントの)パスワードを連結し、SHA-1によりハッシュ化した値をBase64エンコードしたもの

以上を踏まえた、X-WSSEヘッダの値を返すメソッドは次のように記述することができます。Visual Studioでは適当なプロジェクト(Windowsアプリケーションやコンソールアプリケーション)を作成して以下のコードを記述してください。

Protected Shared Function CreateWsseHeaderValue(ByVal userName As String, ByVal password As String) As String
     Dim sha1 = New System.Security.Cryptography.SHA1CryptoServiceProvider
     Dim created = Now.ToString("s") ' Created
     Dim nonce = Guid.NewGuid.ToString ' Nonce
     Dim passwordDigest = Convert.ToBase64String( _
         sha1.ComputeHash(System.Text.Encoding.ASCII.GetBytes(nonce & created & password))) ' PasswordDigest

     Return String.Format("UsernameToken Username=""{0}"", PasswordDigest=""{1}"", Nonce=""{2}"", Created=""{3}""", _
                          userName, passwordDigest, Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(nonce)), created)
 End Function

ユーザー名とパスワードを引数として渡して使用します。処理内容は上記の通りです。Nonce用の値はGUIDを利用しています。

新規投稿用のアドレス取得

続いて写真を新規投稿するためにアクセスするアドレスを取得する部分を実装します。まず、ルートAtomエンドポイントと呼ばれるアドレスへアクセスし、基本的な情報を取得します。はてなフォトライフAtomAPIでは「http://f.hatena.ne.jp/atom」へアクセスします。

応答されるデータは次のようなXML文書になっています。

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://purl.org/atom/ns#">
  <link type="application/x.atom+xml" rel="service.post"
        href="http://f.hatena.ne.jp/atom/post" title="sample">
  <link type="application/x.atom+xml" rel="service.feed"
        href="http://f.hatena.ne.jp/atom/feed" title="sample">
</feed>

<link rel="service.post">要素のhref属性の値が、写真を新規投稿するためのサーバーのアドレスPostURIになります。これを取得するコードを記述します。

Imports <xmlns="http://purl.org/atom/ns#">

Protected Shared Function GetPostUri(ByVal userName As String, ByVal password As String, ByRef title As String) As String
    ' リクエストの作成
    Dim request = DirectCast(WebRequest.Create("http://f.hatena.ne.jp/atom"), HttpWebRequest)
    With request
        .Method = "GET"

        ' WSSE認証用
        .Headers.Add(HttpRequestHeader.Authorization, "WSSE profile=""UsernameToken""")
        .Headers.Add("X-WSSE", CreateWsseHeaderValue(userName, password))

        .Accept = "application/x.atom+xml, application/xml, text/xml, */*"
        .ServicePoint.Expect100Continue = False
    End With

    ' レスポンスの取得
    Dim document As XDocument
    Using response = DirectCast(request.GetResponse, HttpWebResponse), _
          reader = New System.IO.StreamReader(response.GetResponseStream, System.Text.Encoding.UTF8)
        document = XDocument.Load(reader)
    End Using

    ' <link rel="service.post">要素の取得
    Dim elements = From e In document.<feed>.<link> Where e.@rel = "service.post"

    title = elements.First.@title ' title
    Return elements.First.@href ' PostUri
End Function

HttpWebRequestオブジェクトを作成してルートAtomエンドポイントにアクセスしています。リクエスト時に先ほど作成したCreateWsseHeaderValueメソッドを使用してWSSE認証用のヘッダを追加しています。

レスポンスは、読み取った文字列をXDocumentオブジェクトへ変換してから、必要な値を参照し戻り値としています。上記コードでは、PostURIだけでなくtitle属性も参照渡し として返すようにしています。

例外処理は記述していませんので注意してください。

写真の新規投稿

最後に写真を新規投稿する部分を実装します。この部分は作成する投稿プラグインとも密接に関わってくる部分ですので、次回もコードを示します。今回は、とりあえず投稿できるものを作ってみましょう。

取得したPostURIにHTTP POSTメソッドによりアクセスします。POSTデータには次の書式の<entry>要素を指定します。

<entry xmlns="http://purl.org/atom/ns#">
    <title>[写真のタイトル]</title>
    <content mode="base64" type="[Content-Type]">[画像データをBase64エンコードしたもの]</content>
    <hatena:generator xmlns:hatena="http://www.hatena.ne.jp/info/xmlns#" url="example.jp" version="1.0">[アップロードツール名]</hatena:generator>
    <dc:subject xmlns:dc="http://purl.org/dc/elements/1.1/">[フォルダ名]</dc:subject>
</entry>

レスポンスは、正常に投稿できた場合はHTTPステータスコード201(Created)が返ります。Locationヘッダには、投稿した写真の編集用のURLが格納されています。また、<entry>要素から成るXML文書も返り、投稿した写真の表示用のURLなどの情報も取得できます。この後に示すコードではレスポンスのXML文書の利用はしていませんので内容については省略します。

新規に写真を投稿できる最低限のコードを以下に示します。メソッドの引数にユーザー名、パスワード、PostURI、JPEGファイルのパスを渡して使用することを想定しています。

Imports <xmlns="http://purl.org/atom/ns#">
Imports <xmlns:hatena="http://www.hatena.ne.jp/info/xmlns#">
Imports <xmlns:dc="http://purl.org/dc/elements/1.1/">

Protected Shared Sub PostPhoto(ByVal userName As String, ByVal password As String, _
                                  ByVal postUri As String, ByVal photoPath As String)
    ' リクエストの作成
    Dim request = DirectCast(WebRequest.Create(postUri), HttpWebRequest)
    With request
        .Method = "POST"

        ' WSSE認証用
        .Headers.Add(HttpRequestHeader.Authorization, "WSSE profile=""UsernameToken""")
        .Headers.Add("X-WSSE", CreateWsseHeaderValue(userName, password))

        .ContentType = "application/x.atom+xml"
        .Accept = "application/x.atom+xml, application/xml, text/xml, */*"
        .ServicePoint.Expect100Continue = False
    End With

    ' 写真をバイト配列として読み込み
    Dim photoBuffer = New Byte() {}
    Using stream = New System.IO.FileStream(photoPath, IO.FileMode.Open, IO.FileAccess.Read), _
          reader = New System.IO.BinaryReader(stream)
        If stream.Length <= Integer.MaxValue Then
            photoBuffer = reader.ReadBytes(CInt(stream.Length))
        End If
    End Using

    ' <entry>要素
    Dim entry = <entry>
                    <title>Sample</title>
                    <content mode="base64" type="image/jpeg"><%= Convert.ToBase64String(photoBuffer) %></content>
                    <hatena:generator url="http://gihyo.jp/dev/serial/01/wl-sdk/" version="1.0">gihyo.jp sample tool</hatena:generator>
                    <dc:subject>Sample</dc:subject>
                </entry>

    ' <entry>要素をバイト配列へ変換
    Dim entryBuffer = System.Text.Encoding.UTF8.GetBytes(entry.ToString)
    request.ContentLength = entryBuffer.Length

    ' POSTデータ書き込み
    Using stream = request.GetRequestStream
        stream.Write(entryBuffer, 0, entryBuffer.Length)
    End Using

    ' レスポンス取得
    Dim document As XDocument
    Using response = DirectCast(request.GetResponse, HttpWebResponse), _
          reader = New System.IO.StreamReader(response.GetResponseStream, System.Text.Encoding.UTF8)
        document = XDocument.Load(reader)
    End Using
End Sub

コードを確認するとわかるように、POSTデータとなる<entry>要素は、写真のタイトルなどが直接記述されています。またレスポンスのXML文書は取得だけして利用していません。例外処理も省略しています。次回はこのあたりを投稿プラグインにあわせて変更する予定です。

上記のように操作は限定されていますが、次のようにメソッドを呼び出すと写真の投稿ができます。

Dim userName = "ユーザー名"
Dim password = "パスワード"
Dim title = ""

Dim postUri = GetPostUri(userName, password, title)
PostPhoto(userName, password, postUri, "C:\Users\UserName\Pictures\Sample.jpg")

うまくできたでしょうか? うまくいかない場合はリクエスト時など例外が起きていないか確認し、また、WebExceptionがスローされている場合はHTTPステータスコードとその内容を確認するとよいでしょう。


今回はここまでです。次回からはLive フォト ギャラリー用の投稿プラグインを作成していきます。

おすすめ記事

記事・ニュース一覧