前回に続いてPhoto Gallery Publishing Plug-in Platformについてです。サンプルとしてLiveフォト ギャラリーなどで使える はてなフォトライフへ写真をアップロードするプラグインを作ります。今回は実際にプラグインのコードの記述しアップロードができるところまで作成します。
プラグインの処理
コードを書く前に、
1. 設定ウィンドウの表示
プラグインは最初に設定ウィンドウを表示します。ウィンドウ表示は必須ではありませんが、
![図1 設定ウィンドウ 図1 設定ウィンドウ](/assets/images/dev/serial/01/wl-sdk/0023/thumb/TH800_0023-01.png)
この段階でのプラグインが、
- プラグイン固有の設定の参照・
保存 - アップロード対象の動画像情報の参照
- セッションに必要な情報の保存
「セッションに必要な情報の保存」
必要な処理を終えた後、
2. 動画像のアップロード
続いて、
![図2 進行通知ウィンドウ 図2 進行通知ウィンドウ](/assets/images/dev/serial/01/wl-sdk/0023/thumb/TH800_0023-02.png)
この段階でのプラグインは、
- アップロード対象の動画像情報の参照
- セッションに必要な情報の参照・
編集・ 保存 - 動画像のストリームの参照
- 進行状況の通知
- ユーザーによるアップロードのキャンセル通知の受信
- ウィンドウの表示
上のふたつは設定ウィンドウ表示のときと同じくセッション情報の参照と編集です。
この処理は、
3. 結果ウィンドウの表示
図3のようなアップロードの結果を示すウィンドウが最後に表示されて一連の処理は終了です。
![図3 結果ウィンドウ 図3 結果ウィンドウ](/assets/images/dev/serial/01/wl-sdk/0023/thumb/TH800_0023-03.png)
表示されるウィンドウはLiveアプリケーションが用意したものですが、
ここでのやり取りにより可能な処理は以下の通りです。
- アップロード対象の動画像情報の参照
- セッションに必要な情報の参照
- 表示ボタン・
詳細情報リンクの表示とクリック時の処理
表示ボタンと詳細情報リンクは以下の違いがあります。
- 詳細情報リンク
Liveアプリケーションを親ウィンドウとしてプラグインが用意したウィンドウを表示することができます。結果ウィンドウだけでは何枚中何枚がアップロードに成功したことのみしかユーザーはわかりません。これを補うために使用することができます。
- 表示ボタン
表示ボタンをクリックすると結果ウィンドウは閉じられます。アップロード先のサービスのWebページへ移動するために使用することが想定されています。
プロジェクトの作成
それではプラグインを作成していきましょう。プラグインは.NET Frameworkのクラスライブラリとして作成します。本連載では Visual Studio 2008とVisual Basicを使用しています。
プロジェクトの新規作成
まずはWindowsのクラスライブラリのプロジェクトを新規に作成します
![図4 新しいプロジェクトの作成 図4 新しいプロジェクトの作成](/assets/images/dev/serial/01/wl-sdk/0023/thumb/TH800_0023-04.png)
参照の追加
続いて必要な参照をふたつ追加します。ソリューションプロパティからプロジェクトを右クリックして表示されるメニューから追加が可能です。
参照の追加ウィンドウの参照タブを選択し、
この中にプラグインが実装すべきInterfaceの情報があります。
![図5 参照の追加 図5 参照の追加](/assets/images/dev/serial/01/wl-sdk/0023/thumb/TH800_0023-05.png)
.NETタブから
Interfaceの実装
IPublishPluginというInterfaceを通してプラグインとLiveアプリケーションはやり取りを行います。プラグインはこのInterfaceを実装する必要があります。プロジェクトにあるクラスファイルを適当な名前に変更しInterfaceを実装しましょう。コードは次のようになります。
Imports Microsoft.WindowsLive.PublishPlugins
Imports System.Windows.Forms
Imports System.Xml
Public Class HatenaFotolifePlugin
Implements IPublishPlugin
Public Function ShowConfigurationSettings(ByVal parentWindow As IWin32Window, ByVal sessionXml As XmlDocument, ByVal persistXml As XmlDocument, ByVal publishProperties As IPublishProperties) As Boolean Implements IPublishPlugin.ShowConfigurationSettings
End Function
Public Function PublishItem(ByVal parentWindow As IWin32Window, ByVal mediaObjectId As String, ByVal stream As System.IO.Stream, ByVal sessionXml As XmlDocument, ByVal publishProperties As IPublishProperties, ByVal callback As IPublishProgressCallback, ByVal cancelEvent As System.Threading.EventWaitHandle) As Boolean Implements IPublishPlugin.PublishItem
End Function
Public Function HasPublishResults(ByVal sessionXml As XmlDocument) As Boolean Implements IPublishPlugin.HasPublishResults
End Function
Public Function HasSummaryInformation(ByVal sessionXml As XmlDocument) As Boolean Implements IPublishPlugin.HasSummaryInformation
End Function
Public Sub LaunchPublishResults(ByVal sessionXml As XmlDocument) Implements IPublishPlugin.LaunchPublishResults
End Sub
Public Sub ShowSummaryInformation(ByVal parentWindow As IWin32Window, ByVal sessionXml As XmlDocument) Implements IPublishPlugin.ShowSummaryInformation
End Sub
End Class
本記事ではクラス名を
プラグインの登録
まだ何もメソッドの処理を記述していませんが、
プラグインの登録はレジストリを編集する必要があります。ここでは直接レジストリエディタ
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Live\PublishPlugins\[任意の名前]
名前 | 値 |
---|---|
AssemblyPath | DLLファイルへの絶対パス |
ClassName | 名前空間を含めたプラグインのクラス名 |
FriendlyName | Liveアプリケーションのメニューに表示される名前 |
![図6 レジストリエディタによる編集 図6 レジストリエディタによる編集](/assets/images/dev/serial/01/wl-sdk/0023/thumb/TH800_0023-06.png)
追加した後、
デバッグに関してですが、
はてなフォトライフAPIの利用
はてなフォトライフに写真をアップロードするコードは前回の記事中に書いています。前回のコードに少し変更を加えてプラグインクラスに追加します。
前回作成したメソッドは次の3個でした。
- CreateWsseHeaderValue
- GetPostUri
- PostPhoto
そして、
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/">
プラグインでもXMLを扱うためデフォルトの名前空間を次のように変更します。
Imports <xmlns:atom="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/">
この変更に併せてGetPostUriメソッドの以下の部分を変更します。
' <link rel="service.post">要素の取得
' 以下へ変更 Dim elements = From e In document.<feed>.<link> Where e.@rel = "service.post"
Dim elements = From e In document.<atom:feed>.<atom:link> Where e.@rel = "service.post"
PostPhotoメソッドは少し引数が多くなりますが、
Protected Shared Sub PostPhoto(ByVal userName As String, ByVal password As String, _
ByVal postUri As String, ByVal title As String, ByVal contentType As String, ByVal photoStream As System.IO.Stream)
' リクエストの作成
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 reader = New System.IO.BinaryReader(photoStream)
If photoStream.Length <= Integer.MaxValue Then
photoBuffer = reader.ReadBytes(CInt(photoStream.Length))
End If
End Using
' <entry>要素
Dim entry = <entry xmlns="http://purl.org/atom/ns#">
<title><%= title %></title>
<content mode="base64" type=<%= contentType %>><%= Convert.ToBase64String(photoBuffer) %></content>
<hatena:generator url="http://gihyo.jp/dev/serial/01/wl-sdk/" version="1.0">gihyo.jp sample tool</hatena:generator>
</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
作成するプラグインではアップロード先のフォルダを指定しませんので、
CreateWsseHeaderValueメソッドは変更せずに使用します。
設定ウィンドウの表示
それではInterfaceのメソッドを実装していきましょう。まず、
今回作成するプラグインでは最低限必要なものとしてユーザー名とパスワードをユーザーが入力するウィンドウを作成します。ユーザー名は一度入力されたものはプラグイン固有の設定として保存しておき、
ShowConfigurationSettingsメソッド内部で次の処理を順に行います。
- プラグイン設定から以前入力したユーザー名情報を取得
- 設定ウィンドウの表示
(ユーザー名とパスワードの入力) - 入力された情報から、
はてなフォトライフPostURIの取得 - ユーザー名をプラグイン設定として保存
- PostURI・
ユーザー名・ パスワードをセッション情報として保存
ウィンドウの作成
プロジェクトにユーザー名等を入力するためのWindowsフォームを追加します。作成するウィンドウを図7に示します。図7を参考にコントロールを配置してください。オレンジ色の文字は記事中で使用しているコントロール名です。
![図7 ユーザー名とパスワード入力用Form 図7 ユーザー名とパスワード入力用Form](/assets/images/dev/serial/01/wl-sdk/0023/thumb/TH400_0023-07.png)
Windows フォーム内のコードを以下に示します。
' ユーザー名
Public ReadOnly Property UserName() As String
Get
Return UserNameTextBox.Text
End Get
End Property
' パスワード
Public ReadOnly Property Password() As String
Get
Return PasswordTextBox.Text
End Get
End Property
' コンストラクタ
Public Sub New(ByVal userName As String)
Me.InitializeComponent()
UserNameTextBox.Text = userName ' ユーザー名をTextBoxに設定
PublishButton.Enabled = False ' 投稿ボタン無効化
Me.StartPosition = Windows.Forms.FormStartPosition.CenterParent ' 親ウィンドウの中央に表示
End Sub
' TextBoxに値が入力されている場合、投稿ボタンを有効にする
Private Sub UserNameComboBox_Or_PasswordTextBox_TextChanged( _
ByVal sender As Object, ByVal e As System.EventArgs) _
Handles UserNameTextBox.TextChanged, _
PasswordTextBox.TextChanged
' 投稿ボタンの有効・無効化
PublishButton.Enabled = CBool(UserNameTextBox.Text <> "" AndAlso PasswordTextBox.Text <> "")
End Sub
' 投稿ボタンクリック
Private Sub PublishButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles PublishButton.Click
Me.DialogResult = Windows.Forms.DialogResult.OK
End Sub
' キャンセルボタンクリック
Private Sub CancelButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CancelButton.Click
Me.DialogResult = Windows.Forms.DialogResult.Cancel
End Sub
ポイントは、
メソッドの引数と戻り値
ShowConfigurationSettingsメソッドには以下の引数があります。
引数名 | 説明 |
---|---|
parentWindow | Liveアプリケーションのウィンドウハンドル。設定ウィンドウを表示する際に親ウィンドウと指定するために使用します。 |
sessionXml | アップロード対象の動画像情報のXML文書。プラグインが使用する一時的な情報はこのXML文書を書き換えて保持します。 |
persistXml | プラグイン固有の設定保存用のXML文書。プラグインは自由に形式を決定し使用できます。 |
publishProperties | 動画像の関連データへアクセスするためのIPublishPropertiesというInterfaceのオブジェクト。 |
引数にあるようにLiveアプリケーションとプラグインと情報のやり取りにXML文書
メソッドの戻り値はBoolean型です。処理を続ける場合にはTrueを、
メソッドの実装
ShowConfigurationSettingsメソッドに処理を追記します。
引数のpersistXmlについてですが、
<UserName>[ユーザー名]</UserName>
初めてプラグインが実行されたとき、
PostURIを取得するところまで書いてみましょう。コードは次のように書くことができます。
' XDocument へ変換
Dim persist As XDocument
Try
If persistXml.InnerXml <> "" Then
persist = XDocument.Parse(persistXml.InnerXml)
Else
persist = New XDocument
End If
Catch xmlEx As Xml.XmlException
persist = New XDocument
End Try
Dim userName As String
Dim password As String
' ConfigureForm 表示
Dim result As DialogResult
Using frm = New ConfigureForm(persist.<UserName>.Value) ' <UserName>要素を指定
result = frm.ShowDialog(parentWindow)
If result = Windows.Forms.DialogResult.OK Then
' 投稿ボタンをクリックした場合
userName = frm.UserName
password = frm.Password
Else
' キャンセルボタンをクリックした場合
Return False
End If
End Using
' PostURI 取得
Dim title As String = Nothing
Dim postUri As String
Try
postUri = GetPostUri(userName, password, title)
Catch webEx As WebException
MessageBox.Show(webEx.Message, "Error")
Return False
End Try
次は、
<PhotoGalleryPublishSession>
<PublishParameters>
<PostUri>[PostURI]</PostUri>
<UserName>[ユーザー名]</UserName>
<Password>[パスワード]</Password>
</PublishParameters>
</PhotoGalleryPublishSession>
コードは次のようになります。コードの最後では、
' Session XML にユーザー情報とPostURI 保存
Dim session = XDocument.Parse(sessionXml.InnerXml)
With session.<PhotoGalleryPublishSession>.<PublishParameters>.First
.Add(<PostUri><%= postUri %></PostUri>)
.Add(<UserName><%= userName %></UserName>)
.Add(<Password><%= password %></Password>)
End With
' XmlDocument へ反映
persistXml.LoadXml(<UserName><%= userName %></UserName>.ToString)
sessionXml.LoadXml(session.ToString)
Return True
上記のコードでは引数のXmlDocumentオブジェクトからXDocumentオブジェクトへ変換してVisual BasicのXMLリテラルを使用してXMLの操作をしています。編集内容を反映させるため最後にXmlDocumentオブジェクトへ戻しています。
Session XMLとPersist XML
既に設定ウィンドウ表示処理に出てきたようにLiveアプリケーションとプラグインとのやり取りにはXLM文書
Session XML
Session XMLはユーザーが選択した動画像の情報を参照するためと、
Session XMLの例を以下に示します。
<PhotoGalleryPublishSession versionMajor="1" versionMinor="0">
<PublishParameters>
<MaxWidth>1024</MaxWidth>
<MaxHeight>1024</MaxHeight>
</PublishParameters>
<ItemSet>
<Item id="19">
<FullFilePath>C:\Users\UserName\Pictures\background.jpg</FullFilePath>
<OriginalFileName>background.jpg</OriginalFileName>
<OriginalFileExtension>.jpg</OriginalFileExtension>
<PerceivedType>image</PerceivedType>
<Title></Title>
<OriginalWidth>2560</OriginalWidth>
<OriginalHeight>1920</OriginalHeight>
<LengthMS>0</LengthMS>
<FileSize>1620000</FileSize>
<KeywordSet>
<Keyword>meatball</Keyword>
<Keyword>double tooth</Keyword>
</KeywordSet>
<PeopleRegionSet>
<PersonRegion left="0.6" top="0.1" width="0.1" height="0.2">M.K</PersonRegion>
</PeopleRegionSet>
</Item>
<Item id="20">
...
</Item>
<Item id="21">
...
</Item>
</ItemSet>
</PhotoGalleryPublishSession>
<ItemSet>要素内に選択された動画像の数だけ<Item>要素があり、
プラグインが使用するセッション中の情報は、
Persist XML
Persist XMLはプラグイン固有の設定を保存するために使用します。XML文書の内容はプラグインが自由に決定でき、
Persist XMLはプラグインの全般的な設定を保存するために用意されているもので、
動画像のアップロード
アップロード処理を行うメソッドはPublishItemです。ユーザーが選択した動画像の個数の回数だけメソッドが呼ばれます。作成するプラグインでは、
- Session XMLから画像の情報を取得
(画像形式、 ファイル名、 タイトルなど) - 同様に PostURI・
ユーザー名・ パスワードを取得 - はてなフォトライフへ画像をアップロード
メソッドの引数と戻り値
PublishItemメソッドには以下の引数があります。
引数名 | 説明 |
---|---|
parentWindow | Liveアプリケーションのウィンドウハンドル。各アップロード処理に何かしらのウィンドウ表示が可能です。 |
mediaObjectId | Liveアプリケーションが付けた動画像のID。Session XMLの<Item>要素id属性と一致しています。 |
stream | アップロードする動画像のバイナリストリーム。 |
sessionXml | ShowConfigurationSettingsメソッド内で編集されたSession XML。 |
publishProperties | IPublishPropertiesオブジェクト。 |
callback | 進行状況の通知およびユーザーによるキャンセル検出のためのIPublishProgressCallbackというInterfaceのオブジェクト。 |
cancelEvent | ユーザーによるキャンセル検出するためのEventWaitHandleオブジェクト。 |
publishProperties、
戻り値はShowConfigurationSettingsメソッドと同じくBoolean型です。アップロードが成功した場合はTrueを返し、
メソッドの実装
まず、
- 動画・
画像の判別 - 拡張子からContent Type文字列の作成
- タイトルまたはファイル名から写真名の決定
動画が選択されていた場合は処理を終了し、
以上のコードは次のように書くことができます。
' XDocument へ変換
Dim session = XDocument.Parse(sessionXml.InnerXml)
' <Item id="mediaObjectId">要素取得
Dim item = (From e In session.<PhotoGalleryPublishSession>.<ItemSet>.<Item> _
Where e.@id = mediaObjectId).First
' <PerceivedType>要素がimageの場合は処理を抜ける
If item.<PerceivedType>.Value <> "image" Then
Return False
End If
' 拡張子から ContentType 文字列作成
Dim contentType As String
Select Case item.<OriginalFileExtension>.Value.ToLower
Case ".jpg", ".jpeg"
contentType = "image/jpeg"
Case ".gif"
contentType = "image/gif"
Case ".png"
contentType = "image/png"
Case ".bmp"
contentType = "image/bmp"
Case Else
Return False ' サポート外の拡張子
End Select
' 写真名にはタイトルまたはファイル名を使用する
Dim title As String
If item.<Title>.Value <> "" Then
title = item.<Title>.Value
Else
title = item.<OriginalFileName>.Value
End If
画像自体のデータはStreamで渡されていますので、
Try
' アップロード
Dim params = session.<PhotoGalleryPublishSession>.<PublishParameters>
PostPhoto(params.<UserName>.Value, params.<Password>.Value, params.<PostUri>.Value, title, contentType, stream)
Return True
Catch webEx As WebException
Return False
End Try
結果ウィンドウの表示
最後に結果ウィンドウの表示です。結果ウィンドウ自体はLiveアプリケーションが表示しますので、
表示ボタンと詳細情報リンクの有効化はHasPublishResultsメソッドとHasSummaryInformationメソッドの戻り値の設定により行います。どちらのメソッドも戻り値がBoolean型で、
Public Function HasPublishResults(ByVal sessionXml As XmlDocument) As Boolean Implements IPublishPlugin.HasPublishResults
' 表示ボタン有効
Return True
End Function
Public Function HasSummaryInformation(ByVal sessionXml As XmlDocument) As Boolean Implements IPublishPlugin.HasSummaryInformation
' 詳細情報リンク無効
Return False
End Function
表示ボタンクリック時の動作はLaunchPublishResultsメソッドに記述します。引数はSession XMLです。
Public Sub LaunchPublishResults(ByVal sessionXml As XmlDocument) Implements IPublishPlugin.LaunchPublishResults
Dim session = XDocument.Parse(sessionXml.InnerXml)
Dim userName = session.<PhotoGalleryPublishSession>.<PublishParameters>.<UserName>.Value
Try
' はてなフォトライフのユーザーページを開く
Process.Start("http://f.hatena.ne.jp/" & userName)
Catch ex As Exception
' Do nothing
End Try
End Sub
詳細情報リンククリック時の動作はShowSummaryInformationメソッドに記述します。ウィンドウの表示が想定されているため引数には、
Public Sub ShowSummaryInformation(ByVal parentWindow As IWin32Window, ByVal sessionXml As XmlDocument) Implements IPublishPlugin.ShowSummaryInformation
' 呼ばれない
End Sub
プラグインの実行
以上でLive フォト ギャラリーからはてなフォトライフへ写真のアップロードが可能になりました。実際に動かしてみましょう
![図8 実行結果 図8 実行結果](/assets/images/dev/serial/01/wl-sdk/0023/thumb/TH800_0023-08.png)
もしセッション中に例外がスローされた場合はLiveアプリケーション側で吸収され詳細はわかりません。処理を細かく切り分けて失敗している原因を探すとよいでしょう。
次回もPhoto Gallery Publishing Plug-in Platformを利用したプラグイン作成の予定です。