Bluesky API(XRPC)でビデオアップロード

By びるず (@bills-appworks.blue)
Published:

Bluesky API(XRPC)でビデオアップロードする方法の情報です。2025年2月現在の情報になります。

内容のほとんどは参考情報に挙げている、すぎゃーんさんとSamuelさんと山貂さんの情報に基づいております。すぎゃーんさんとSamuelさんと山貂さんに御礼申し上げます。ただし当記事の編集の責は当方にあります。

概要

ビデオサービス関連のapp.bsky.video.*で定義されているAPI(XRPC)群を利用します。

| APIメソッド | 概要 | | ---- | ---- | | app.bsky.video.getUploadLimits | アップロード制限情報取得 | | app.bsky.video.uploadVideo | ビデオアップロード | | app.bsky.video.getJobStatus | ビデオアップロードジョブ状態取得 |

ビデオサービス関連APIでは、他の多くのAPIと異なりリクエスト先はビデオサービスのエンドポイント(公式提供の場合https://video.bsky.app/xrpc/)となります。

また多くのAPIでは、リクエスト時の認可トークンとしてcom.atproto.server.createSessionによるセッションJWTを指定しますが、ビデオサービスのリクエストではcom.atproto.server.getServiceAuthによって取得した認可トークンを指定する必要があります。

なおuploadVideoを除き、atproto-proxyを用いてPDS経由のリクエストとすることも可能です(本稿では説明しません)。

以降にこれらの利用について説明を記します。必要に応じて後半に記載している処理フロー図も合わせて参照すると理解の助けになるかもしれません。

API呼び出しとパラメタ

概要で前述しましたようにリクエスト先エンドポイントについては注意してください。

アップロード制限情報取得 (app.bsky.video.getUploadLimits)

リクエスト先は他の多くのAPIと同じエンドポイントです。

| APIメソッド | パラメタ名 | パラメタ値 | 備考 | | ---- | ---- | ---- | ---- | | com.atproto.server.getServiceAuth | aud | 'did:web:video.bsky.app' | ビデオサービスを示す識別子 | | | exp | 認可有効期限 | 期限を示すUNIX時刻<br>省略時は60秒後 | | | lxm | 'app.bsky.video.getUploadLimits' | 認可取得対象APIメソッドのlexicon |

リクエスト先はビデオサービスのエンドポイントです。

| APIメソッド | パラメタ名 | パラメタ値 | 備考 | | ---- | ---- | ---- | ---- | | app.bsky.video.getUploadLimits | Bearer | 取得したサービス認可トークン | HTTP Authorizationヘッダへの設定 |

ビデオアップロード (app.bsky.video.uploadVideo)

リクエスト先は他の多くのAPIと同じエンドポイントです。

| APIメソッド | パラメタ名 | パラメタ値 | 備考 | | ---- | ---- | ---- | ---- | | com.atproto.server.getServiceAuth | aud | 'did:web:<サービスエンドポイントドメイン>' | 所属PDSサーバDID 1 | | | exp | 認可有効期限 | 期限を示すUNIX時刻<br>省略時は60秒後 | | | lxm | 'com.atproto.repo.uploadBlob' | 認可取得対象APIメソッドのlexicon 2 |

<span id="1" style="font-size:smaller">1: https://plc.directory/<ユーザDID>へのHTTPリクエストのレスポンス(service[].serviceEndpoint)に含まれる、(ビデオアップロードする)ユーザが所属するPDS URLのドメイン部</span>

<span id="2" style="font-size:smaller">2: app.bsky.video.uploadVideoではないので注意</span>

リクエスト先はビデオサービスのエンドポイントです。

| APIメソッド | パラメタ名 | パラメタ値 | 備考 | | ---- | ---- | ---- | ---- | | app.bsky.video.uploadVideo | Bearer | 取得したサービス認可トークン | HTTP Authorizationヘッダへの設定 | | | did | (ビデオアップロードする)ユーザDID | | | | name | ビデオ名 | ビデオファイル名など | | | BODY | ビデオデータ | HTTP POSTボディ |

ビデオアップロードジョブ状態取得 (app.bsky.video.getJobStatus)

リクエスト先は他の多くのAPIと同じエンドポイントです。

| APIメソッド | パラメタ名 | パラメタ値 | 備考 | | ---- | ---- | ---- | ---- | | com.atproto.server.getServiceAuth | aud | 'did:web:video.bsky.app' | ビデオサービスを示す識別子 | | | exp | 認可有効期限 | 期限を示すUNIX時刻<br>省略時は60秒後 | | | lxm | 'app.bsky.video.getJobStatus' | 認可取得対象APIメソッドのlexicon |

リクエスト先はビデオサービスのエンドポイントです。

| APIメソッド | パラメタ名 | パラメタ値 | 備考 | | ---- | ---- | ---- | ---- | | app.bsky.video.getJobStatus | Bearer | 取得したサービス認可トークン | HTTP Authorizationヘッダへの設定 | | | jobId | uploadVideoのレスポンスに含まれるjobStats.jobIdの値 | |

各処理の詳細

詳細:アップロード制限情報取得 (app.bsky.video.getUploadLimits)

サーバ負荷軽減やユーザ体験向上のために可能な処理ですが、アップロードのために必須の処理ではありません。

まず前述の通り、getServiceAuthを呼び出して認可トークンを取得します。その認可トークンをgetUploadLimits呼び出し時にHTTP AuthorizationヘッダのBearerトークンとして指定します。

レスポンスには、アップロード可否、1日当たりの残存アップロードサイズ、1日当たりの残存アップロード数等が含まれるので参考にします。

レスポンス例:

{
  "canUpload":true,
  "message":"User can upload",
  "remainingDailyBytes":40000000000,
  "remainingDailyVideos":100
}

なおatproto-proxyを用いてPDS経由でgetUploadLimitsをリクエストする場合は、getServiceAuthの呼び出しは不要となります。

詳細:ビデオアップロード (app.bsky.video.uploadVideo)

まず前述の通り、getServiceAuthを呼び出して認可トークンを取得します。その認可トークンをuploadVideo呼び出し時にHTTP AuthorizationヘッダのBearerトークンとして指定します。

注意が必要なのは、getServiceAuth呼び出し時のaudパラメタとして指定するサービスDIDとして、'did:web:video.bsky.app'ではなく、ビデオアップロードを行うユーザが所属するPDSのDIDであることです。ユーザのPDS URLはhttps://plc.directory/<ユーザのDID>にリクエストするとレスポンス(service[].serviceEndpoint)として得られます。PDS URLからドメインを抽出し、プレフィクスとしてdid:web:を付与することによって、audパラメタに指定するサービスDIDを構成します(did:web:<PDSドメイン>)。

例:
(PDS URL)
https://verpa.us-west.host.bsky.network
(サービスDID)
did:web:verpa.us-west.host.bsky.network

またlxmパラメタとして指定するXRPC lexicon文字列が、'app.bsky.video.uploadVideo'ではなく、'com.atproto.repo.uploadBlob'であることも注意が必要です。

これらについては、uploadVideoのリクエストを受けたビデオサービスが、その中でPDSへのアップロード(uploadBlob)を行うことによります。後述の処理フロー図においてその背景がイメージできるかと思います。

app.bsky.video.uploadVideoのAPIリファレンスでは、特にクエリパラメタは示されていませんが、実際には前述のようにdid、nameのクエリパラメタを指定する必要があります。didにはビデオアップロードを行うユーザのDIDを指定し、nameにはビデオの名前(ファイル名でよいでしょう)を指定します。

またAPIリファレンスではレスポンスのjobStatus.stateが取りうる値としてJOB_STATE_COMPLETED、JOB_STATE_FAILEDが挙げられていますが、正常な場合の多くはJOB_STATE_CREATEDとなるようです。この場合は後続のapp.bsky.video.getJobStatusにて処理完了を待ち合わせてjobStatus.blobの値を得るようにします。

同じビデオデータをアップロードすると、レスポンスのHTTPステータスコードが409(Conflict)となります。その場合レスポンスペイロードのjobStatus.stateがJOB_STATE_COMPLETED、jobStatus.errorが"already_exists"、jobStatus.messageが"Video already processed"という値になるようです。この場合もjobStatus.jobIdにjobIdが入っており、app.bsky.video.getJobStatusに指定して呼び出すことによって、jobStatus.blobの値を得ることができます。

なおgetUploadLimits、getJobStatusと異なり、uploadVideoではatproto-proxyを用いてPDS経由のリクエストをすることはできません。

詳細:ビデオアップロードジョブ状態取得 (app.bsky.video.getJobStatus)

まず前述の通り、getServiceAuthを呼び出して認可トークンを取得します。その認可トークンをgetJobStatus呼び出し時にHTTP AuthorizationヘッダのBearerトークンとして指定します。

app.bsky.video.getJobStatusのAPIリファレンスでは、レスポンスのjobStatus.stateが取りうる値としてJOB_STATE_COMPLETED、JOB_STATE_FAILEDが挙げられていますが、以下のようなバリエーションがあるようです。

| jobStatus.state | 概要 | 備考 | | ---- | ---- | ---- | | JOB_STATE_COMPLETED | ジョブ完了 | jobStatus.blobにポスト(createRecord)での$type:app.bsky.embed.video配下のvideoに指定するJSONオブジェクトが格納される | | JOB_STATE_FAILED | ジョブ失敗終了 | | | JOB_STATE_ENCODING | エンコード処理中 | jobStatus.progressに進捗割合(%)が格納される | | JOB_STATE_SCANNING | スキャン処理中 | エンコード処理完了後にこの状態になり、スキャン処理の後に処理完了となる |

uploadVideo後に適切な頻度でgetJobStatusを呼び出して処理完了を待つことになります。

getJobStatusに指定する認可トークンは、getServiceAuthで指定した認可有効期限に依存します。そのため同じ認可トークンを使い続けると、ジョブが認可有効期限を超過した場合にgetJobStatus呼び出しが(おそらく)失敗します。このため認可有効期限を長めにしたり、適度に認可トークンを再取得することも考えられます。

なおatproto-proxyを用いてPDS経由でgetJobStatusをリクエストする場合は、getServiceAuthの呼び出しは不要となります。

処理フロー図

image

image

ビデオサービス(app.bsky.video.*)を利用しない方法

当記事の元となる、すぎゃーんさんの解説からリンクされているSamuelさん情報によると、これまでに記載したビデオサービスを利用した方法の他に、com.atproto.repo.uploadBlobでアップロードする方法もあるようです。

ただしgetJobStatusを利用した方法では非同期処理を制御できますが、uploadBlobによる方法ではポスト処理との間で制御できないタイムラグが生じるため、推奨されないようです。

参考:ポスト時のaspectRatio

ポスト(createRecord)での$type:app.bsky.embed.video配下にアスペクト比(aspectRatio)要素が存在します。この要素は省略可能ですが、たとえば縦長のビデオではクライアントによっては左右両側に黒い帯が表示されるなど、見た目が最適化されない可能性があります。

参考情報

すぎゃーんさん情報

Samuelさん情報

山貂さん情報

APIリファレンス

lexicon


変更履歴

(2025-02-17)

(2025-02-21)