Skip to main content

HappyOyster Web SDK 統合ガイド

フロントエンド開発者およびフルスタック開発者向けに、HappyOyster Web SDK の Adventure、Directing、Acting モードを本番環境へ安定して統合する方法を説明します。

フロントエンド開発者およびフルスタック開発者向けに、本番環境へ SDK を安定して統合する方法を説明します。各 API の詳細は API リファレンスを参照してください。

0. 最小限の統合例

バックエンドから Model Studio の一時 API キー token と Travel の ticket が返されたら、フロントエンド SDK では次のフローを実行します。
import { HappyOysterEngine, isSdkError } from "@happy-oyster/js-sdk";

const engine = new HappyOysterEngine({
  APIHost: "open-platform.example.com",
  model: "happyoyster-1.0-adventure", // Required; Adventure model shown as an example
  logLevel: "warn",
});

engine.updateToken(token);

const videoElement = document.querySelector("video") as HTMLVideoElement;
const travel = engine.createTravel({
  ticket,
  videoElement,
  maxExperienceTimeSec: 90, // Optional, Adventure only: 60 / 90 / 120
});

const offStatus = travel.on("statusChanged", (status) => {
  // Use status to drive loading states, disabled buttons, and player UI.
});

const offError = travel.onError((error) => {
  // Usually log, show a toast, and refresh the token if it has expired.
  if (isSdkError(error)) console.error(error.code, error.message);
});

try {
  await travel.start();
  // Enable controls or Directing input with travel.can(action) after the session is running.
} finally {
  offStatus();
  offError();
  await travel.end();
}
この例は SDK 側の処理のみを示します。ワールドの作成、準備完了までの待機、ticket の取得、Model Studio の一時 API キー token の取得は、すべてアプリケーションのバックエンドで、またはバックエンドを介して行ってください。

1. 全体フローの概要

体験全体には、主に次の構成要素が関わります。

1.1 構成要素

構成要素

役割

アプリケーションのフロントエンド

UI、<video>、セッションのオーケストレーション、SDK の呼び出し

アプリケーションのバックエンド

プライマリ API キーを保管し、クライアントに代わって Open Platform のワールド API を呼び出し、Model Studio の一時認証情報を発行します

HappyOyster OpenAPI

HappyOyster ワールドモデルに必要な API を提供します。

HappyOyster SDK

アプリケーションのフロントエンドで使用する SDK です。主にクライアント側の Travel セッションと動画ストリームへのアクセスを管理します。

happy-oyster-overview.png

1.2 体験フロー

段階

実行主体

操作

説明

① ワールドを作成

アプリケーションのバックエンド → HappyOyster OpenAPI

POST /openapi/v1/worlds

encryptedWorldId、status、firstFrame を返します

② 準備完了まで待機

アプリケーションのバックエンド → HappyOyster OpenAPI

GET /openapi/v1/worlds/build-status

status === ready になるまでポーリングします(作成時点でワールドの準備が完了している場合は省略できます)

③ Travel 認証情報を取得

アプリケーションのバックエンド → HappyOyster OpenAPI

POST /openapi/v1/worlds/get-travel-credential

enter-travel 用の使い捨て ticket を返します

④ 一時 API キートークンを取得

アプリケーションのバックエンド → HappyOyster OpenAPI

POST /api/v1/tokens?expire_in_seconds=1800

一時 API キーの token を返します。有効期間を短くしすぎないでください。体験の完了前にトークンが期限切れになり、頻繁な更新が必要になる場合があります。

⑤ 一時 API キートークンと Travel 認証情報を渡す

アプリケーションのフロントエンド → アプリケーションのバックエンド → アプリケーションのフロントエンド

一時 API キーと Travel 認証情報をリクエストします

フロントエンドが token と ticket を受信すると、SDK のフローを開始します

⑥ 体験を開始

アプリケーションのフロントエンド → HappyOyster SDK

後述の「SDK 起動サブフロー」を参照してください

new HappyOysterEngine/updateToken → createTravel → start()

1.3 SDK 操作サブフロー(段階 ⑥ の詳細)

1.3.1 Travel の開始

travel-start-flow.png
呼び出し順序の主な要件

手順

API

説明

1

new HappyOysterEngine()

同じ APIHost + model には Engine を再利用します

2

updateToken()

start() の前に完了させます

3

createTravel()

videoElement は必須です

4

travel.on(...)

start() の前にリスナーを登録します

5

await travel.start()

正常に開始すると running に移行します

1.3.2 Adventure モードでの操作

adventure-command-flow.png

1.3.3 Directing / Acting モードでの操作

adventure-control-flow.png
Adventure、Directing、Acting の違いを次の表に示します。

モード

主な API

主な用途

注意点

Adventure (1)

sendCommand()

キーボード、ジョイスティック、ボタンによるリアルタイム制御

running になってから送信してください。maxExperienceTimeSec はこのモードにのみ適用されます

Directing (2)

sendInstruct(), pause(), resume(), rewind()

自然言語による演出と再生制御

一時停止、再開、巻き戻しはセッションの機能に依存します。まず travel.can(action) を確認してください

Acting (3)

sendInstruct(), pause(), resume()

演技中心の体験とアスペクト比の設定

一時停止と再開はセッションの機能に依存します。rewind() は使用できず、scriptlist では sendInstruct() を使用できません

1.4 アプリケーションのバックエンド実装の要点

バックエンドでは、次のルールを厳守してください。
  • プライマリ API キーはアプリケーションのバックエンドでのみ保管します。ブラウザには有効期間の短い token のみを渡してください(デモ:POST /server-api/temp-api-key)。
  • Travel 認証情報はバックエンドを介して取得します。ticket の発行前にワールドとユーザーの権限を確認し、フロントエンドにハードコードしたり長期間キャッシュしたりしないでください。
  • ワールドが ready になるまで認証情報を発行しないでください。それ以前に発行すると、enter-travel が失敗したり体験が不完全になったりする場合があります。
  • アプリケーションのバックエンドと SDK は同じ Open Platform 環境に接続してください。バックエンドでは完全な OpenAPI URL を使用できますが、SDK の APIHost には open-platform.example.com のようにホスト名のみを指定します。

2. SDK の主要概念

第 1 節のフローのうち、SDK が担当するのは「Travel に入る → 再生 → 制御 → 終了」の部分です。主要なオブジェクトは次の 2 つです。

オブジェクト

用途

HappyOysterEngine

APIHost と model を設定し、Model Studio の一時 API キートークンを管理(updateToken)し、Travel セッションを作成します

Travel

1 つのセッションを表します。start が RTC に接続し、sendCommand、sendInstruct、ライフサイクルメソッドがセッションを制御します

セッションの状態(statusChanged):
idle ──start()──► prepare ──video playable──► running ⇄ paused ──end()──► idle
3 つの制約(後述):
  • 各 Engine で同時にアクティブにできる Travel は 1 つだけです。
  • videoElement を createTravel に渡す必要があります。
  • Model Studio の一時 API キートークンと Travel チケットは用途が異なります(第 1.2 節の段階 ④~⑤、および第 3.2 節を参照)。

3. 初期化とトークン管理

3.1 Engine のインスタンス化

必須事項
  • APIHost には、https:// などのスキームやパスを含めず、open-platform.example.com のように Open Platform のホスト名のみを設定します。
  • HappyOysterEngine は同じ APIHost + model に対して再利用します。ホストまたはモデルを変更する前に、アクティブな Travel を終了し、新しい接続先に合わせて設定した Engine を使用してください。
推奨事項
  • 開発環境では logLevel: 'debug'、本番環境では 'warn' または 'none' を使用します。
  • 開始のたびに新規作成せず、セッションのオーケストレーション層で Engine を作成します。
主な設定フィールドを次の表に示します。

フィールド

必須事項

設定方法

APIHost

はい

open-platform.example.com のようにホスト名のみを渡します。https:// やパスを含めないでください

model

はい

必須で、デフォルト値はありません。ワールドの作成とチケットの発行にバックエンドが使用するモデルと一致させてください。Engine ごとに固定され、mode やチケットからは推定されません

token

いいえ

コンストラクターに渡すか、後から updateToken(token) で設定できます。期限切れトークンの更新にも updateToken を使用します

logLevel

いいえ

開発環境では debug、本番環境では warn または none を使用します

streamReadyTimeout

いいえ

ネットワーク接続やレンダリングの準備に時間がかかる場合は値を増やし、タイムアウト後に再試行できるようにします

避けるべき操作
  • レンダリング中や頻繁に実行される処理で new HappyOysterEngine() を繰り返し呼び出すこと。
  • https:// を含むような完全な URL を渡すこと、または APIHost を省略したまま SDK のバックエンド API を呼び出すこと。

3.2 2 種類の認証情報

第 1.2 節の段階 ③~⑤ に対応します。

認証情報

取得元

設定方法

用途

Model Studio の一時 API キートークン

アプリケーションのバックエンドが発行する有効期間の短いキー

sdk.updateToken(token)

SDK から Open Platform へのリクエストを認証します

Travel チケット

アプリケーションのバックエンドがリクエストする Travel 認証情報

createTravel({ ticket })

Travel セッション用の使い捨て認証情報

必須事項
  • travel.start() の前に両方の認証情報を準備します。
  • SDK はトークンを永続化も自動更新もしません。
推奨事項
  • トークンが期限切れになった場合、または onError でハートビートエラーを受信した場合は、アプリケーションのバックエンドを介して新しいトークンを取得し、updateToken を呼び出します。
避けるべき操作
  • 2 種類の認証情報を混同すること。
  • プライマリ API キーをアプリケーションのフロントエンドに公開すること。

4. Travel のライフサイクル

4.1 作成と開始

必須事項
  • createTravel({ ticket, videoElement }) を呼び出します。ticket は使い捨ての Travel 認証情報で、videoElement もこの時点で渡します。
  • statusChanged と onError は await travel.start() の前に登録し、エラーや状態変更を確実に監視できるようにします。
推奨事項
  • prepare 中は接続中の UI を表示し、重複クリックを防ぎます。travelInfoReady でセッションのメタデータを早期に取得し、firstFrameGenerated が利用可能な場合は最初のフレームをプレースホルダーとして使用します。
避けるべき操作
  • DOM 要素の準備ができる前に Travel を作成すること。

4.2 実行中の処理

  • statusChanged に基づいて UI を更新します。prepare は動画の待機中、running は操作可能であることを示します。
  • pause、resume、rewind の用途と前提条件に従ってください。これらはセッションの機能に依存します。また、rewind にはセッションが paused であり、ストリームが停止していることも必要です。

4.3 終了と再作成

必須事項
  • await travel.end() でセッションを終了します。
  • 新しい Travel を作成する前に、前の Travel が終了していることを確認します。
  • ページのアンロード時やルートの変更時に、クリーンアップ処理で travel.end() を呼び出します。
推奨事項
  • 失敗後は「終了 → リスナーの削除 → 第 1.2 節の段階 ③ または ⑤ から再試行」の順で処理します。
  • 認証情報が使い捨ての場合、再試行時に新しい Travel 認証情報を取得します。
避けるべき操作
  • 最初の Travel を終了せずに createTravel を再度呼び出すこと。この場合、同期的に例外が発生します。
  • クリーンアップせずにページを離れること。RTC リソースのリークにつながります。

5. 動画要素とブラウザのポリシー

必須事項
  • playsInline を <video> に設定します(iOS では必須)。
  • ブラウザの自動再生ポリシーが autoPlay と muted に与える影響を把握してください。
推奨事項
  • prepare 中は最初のフレーム画像をオーバーレイ表示し、セッションが running に移行したら非表示にします。
  • 必要に応じて streamReadyTimeout(デフォルト:15,000 ms)を設定し、タイムアウト後に再試行できるようにします。
  • プレーヤーのコンテナーに固定のアスペクト比を確保します。
避けるべき操作
  • 動画要素が DOM に追加される前に、その参照を渡すこと。
  • セッションが paused であるかストリームが切断されているのに、UI を操作可能な状態のままにすること。

6. イベント購読とエラー処理

6.1 イベント購読

必須事項
  • イベントは Travel に属し、HappyOysterEngine には属しません。
  • travel.on(...) と travel.onError(...) が返す購読解除関数を保存します。
推奨事項
  • リスナーの登録とクリーンアップを一か所で管理します。Travel セッションを切り替える前に古いリスナーを削除してください。

イベント

用途

statusChanged

状態の表示、動画の準備完了(running)の検出、ボタンの有効化と無効化

travelInfoReady

RTC 接続前に encryptedTravelId、モード、最初のフレーム、体験時間、Acting のアスペクト比を読み取ります。travel.getInfo() も同じデータを返します

firstFrameGenerated

空でない最初のフレームが存在する場合、travelInfoReady の後に発生します。prepare 中のプレースホルダーとして使用します

error(onError 経由)

ログ記録、トースト通知、トークン更新の開始

6.2 エラーの分類

  • 起動時の失敗:travel.start() が reject されます(enter-travel / RTC / タイムアウト)。
  • 実行時のエラー:travel.onError で通知されます(RTC エラー、ハートビート認証の失敗など)。
  • 第 1.2 節の段階 ①~④ で発生するアプリケーションのバックエンドと Open Platform のエラーはアプリケーション層で処理し、SDK には渡されません。
避けるべき操作
  • start() のエラーのみを捕捉し、onError を購読しないこと。
  • 購読解除せずにリスナーを繰り返し登録すること。

7. 指示の送信:Command と Instruct

7.1 sendCommand(リアルタイム制御)

sendCommand() は Adventure モード(1)のリアルタイム制御に使用します。RTC DataChannel を介してコマンドを送信し、セッションが running の間のみ使用できます。 SDK が送信するコマンドは約 42 ms(約 24 FPS)ごとに最大 1 件です。前回の送信から 1 周期が経過していれば、新しいコマンドを即座に送信します。同じ周期内に sendCommand() が複数回呼び出された場合、最後の呼び出しの完全なコマンドのみを送信します。translation、rotation、interaction は独立した制御軸です。省略したフィールドは None として送信されます。SDK は呼び出し間でフィールドをマージせず、前のコマンドを定期的に繰り返したり、None を自動送信したりもしません。 単発の操作 ジャンプや攻撃などの単発の操作では、sendCommand() を 1 回呼び出します。
await travel.sendCommand({ interaction: "Jump" });
継続的な操作 移動や連続した視点回転の操作が押されている間は、sendCommand() を呼び出し続けます。フロントエンドのアプリケーションで呼び出しを 42 ms 間隔に制限する必要はありません。SDK が高頻度の呼び出しをまとめ、約 24 FPS のレートで出力します。操作を離したら、None を明示的に送信して制御をリセットしてください。
// Keep calling while the control is pressed
void travel.sendCommand({ translation: "Front" });

// Stop moving on release
void travel.sendCommand({ translation: "None" });
避けるべき操作
  • prepare 中に大量のコマンドを送信すること。
  • 自然言語の Directing 指示に sendCommand を使用すること。

7.2 sendInstruct(演出指示)

必須事項
  • Directing セッション(2)、または Acting セッション(3)のうち scriptlist ではないものに使用します。await travel.sendInstruct({ content }) を呼び出してください。
推奨事項
  • 送信前に content が空でないことを確認し、重複送信を防ぎます。
  • accepted などの戻り値に基づいてユーザーに結果を伝えます。

8. トラブルシューティング

現象

主な原因

対処方法

SDK の初期化が引数エラーで失敗する

APIHost に https:// やパスを含む完全な URL を渡している

open-platform.example.com のようにホスト名のみを渡します

チケットが無効または使用済みのため travel.start() が失敗する

ticket は使い捨てです。有効期限切れ、再使用、またはワールドの準備完了前に発行された可能性があります

バックエンドから新しい ticket を取得し、入場前にワールドの準備が完了していることを確認します

セッションの開始後に onError が認証エラーを通知する

Model Studio の一時 API キー token の有効期限が切れた

バックエンドから新しいトークンを取得し、engine.updateToken(newToken) を呼び出します

プレーヤーが読み込み中のままになる、または start() がタイムアウトする

リモートストリームの準備ができていない、ネットワークが遅い、または streamReadyTimeout が短すぎる

再試行できるようにし、必要に応じて streamReadyTimeout を増やして、バックエンドと RTC のログを確認します

アクティブな Travel が存在するため、2 回目の createTravel() が失敗する

前の Travel が終了していない

終了、再試行、セッションの切り替え時に await travel.end() を呼び出し、リスナーをクリーンアップします

制御や指示に反応しない

セッションが running ではない、または現在の Adventure、Directing、Acting モードに適していない API を使用している

statusChanged に基づいてボタンの有効・無効を切り替え、モードに依存する操作の前に travel.can(action) を呼び出します

デバッグ時は、SDK の ErrorCode、現在の TravelStatus、バックエンドのリクエストログをまとめて記録してください。通常、認証情報、状態、ネットワーク、レンダリングのどこに問題があるかを判断できます。