Engage SDK for TV の統合ガイド

「続きを見る」では、継続クラスタを活用して、複数のアプリの未視聴の動画や、同じテレビ シーズンの次に視聴するエピソードを 1 つの UI グループに表示します。この継続クラスタでエンティティをハイライト表示できます。このガイドでは、Engage SDK を使用して「続きを見る」機能でユーザー エンゲージメントを高める方法について説明します。

事前作業

始める前に、次の手順を完了します。

  1. ターゲット API 19 以上に更新する

  2. アプリに com.google.android.engage ライブラリを追加します。

    統合で使用する SDK は、モバイルアプリ用とテレビアプリ用の 2 つに分かれています。

    モバイル

    
      dependencies {
        implementation 'com.google.android.engage:engage-core:1.5.5
      }
    

    テレビ

    
      dependencies {
        implementation 'com.google.android.engage:engage-tv:1.0.2
      }
    
  3. AndroidManifest.xml ファイルで Engage サービスの環境を本番環境に設定します。

    モバイル

    
    <meta-data
        android:name="com.google.android.engage.service.ENV"
        android:value="PRODUCTION" />
    

    テレビ

    
    <meta-data
        android:name="com.google.android.engage.service.ENV"
        android:value="PRODUCTION" />
    
  4. テレビ APK の WRITE_EPG_DATA の権限を追加

    <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
    
  5. androidx.work などのバックグラウンド サービスを使用して、信頼性の高いコンテンツ公開をスケジュール設定します。

  6. シームレスな視聴エクスペリエンスを提供するため、次のイベントが発生したときに視聴中のコンテンツのデータを公開します。

    1. 初回ログイン: ユーザーが初めてログインしたときに、視聴履歴がすぐに利用できるようにデータを公開します。
    2. プロファイルの作成または切り替え(マルチ プロファイル アプリ): アプリが複数のプロファイルをサポートしている場合は、ユーザーがプロファイルを作成または切り替えたときにデータを公開します。
    3. 動画再生の中断: ユーザーが中断したところから再開できるように、動画を一時停止または停止したとき、または再生中にアプリが終了したときにデータを公開します。
    4. [続きを見る] トレイの更新(サポートされている場合): ユーザーが [続きを見る] トレイからアイテムを削除した場合は、更新されたデータを公開してその変更を反映します。
    5. 動画視聴完了:
      1. 映画の場合は、視聴済みの映画を [続きを見る] トレイから削除します。映画がシリーズの一部である場合は、次の映画を追加してユーザーの関心を維持します。
      2. エピソードの場合は、視聴済みのエピソードを削除し、シリーズの次のエピソード(利用可能な場合)を追加して、視聴を継続するよう促します。

統合

AccountProfile

Google TV でパーソナライズされた「続きを見る」機能を利用するには、アカウントとプロフィール情報を提供します。AccountProfile を使用して、次の情報を指定します。

  1. アカウント ID: アプリケーション内のユーザーのアカウントを表す一意の識別子。実際のアカウント ID または適切に難読化されたバージョンを指定できます。

  2. プロフィール ID(省略可): アプリケーションが 1 つのアカウント内で複数のプロフィールをサポートしている場合は、特定のユーザー プロフィールの固有の識別子(実在の ID または難読化された ID)を指定します。

// If your app only supports account
val accountProfile = AccountProfile.Builder()
    .setAccountId("your_users_account_id")
    .build()

// If your app supports both account and profile
val accountProfile = AccountProfile.Builder()
    .setAccountId("your_users_account_id")
    .setProfileId("your_users_profile_id")
    .build()

エンティティを作成する

この SDK では、各アイテムタイプを表すさまざまなエンティティを定義しています。継続クラスタは次のエンティティをサポートしています。

  1. MovieEntity
  2. TvEpisodeEntity
  3. LiveStreamingVideoEntity
  4. VideoClipEntity

これらのエンティティのプラットフォーム固有の URI とポスター画像を指定します。

また、まだ作成していない場合は、Android TV、Android、iOS などの各プラットフォームの再生 URI を作成します。そのため、ユーザーが各プラットフォームで視聴を続行すると、アプリはターゲット設定された再生 URI を使用して動画コンテンツを再生します。

// Required. Set this when you want continue watching entities to show up on
// Google TV
val playbackUriTv = PlatformSpecificUri.Builder()
    .setPlatformType(PlatformType.TYPE_ANDROID_TV)
    .setActionUri(Uri.parse("https://www.example.com/entity_uri_for_tv"))
    .build()

// Required. Set this when you want continue watching entities to show up on
// Google TV Android app, Entertainment Space, Playstore Widget
val playbackUriAndroid = PlatformSpecificUri.Builder()
    .setPlatformType(PlatformType.TYPE_ANDROID_MOBILE)
    .setActionUri(Uri.parse("https://www.example.com/entity_uri_for_android"))
    .build()

// Optional. Set this when you want continue watching entities to show up on
// Google TV iOS app
val playbackUriIos = PlatformSpecificUri.Builder()
    .setPlatformType(PlatformType.TYPE_IOS)
    .setActionUri(Uri.parse("https://www.example.com/entity_uri_for_ios"))
    .build()

val platformSpecificPlaybackUris =
    Arrays.asList(playbackUriTv, playbackUriAndroid, playbackUriIos)

ポスター画像には、URI とピクセル サイズ(高さと幅)が必要です。複数のポスター画像を提供してさまざまなフォーム ファクタをターゲットに設定しますが、特に Google のエンターテイメント スペース内で「視聴を続ける」エンティティが正しく表示されるように、すべてのアスペクト比が 16:9 で、高さが 200 ピクセル以上であることを確認してください。高さが 200 ピクセル未満の画像は表示されないことがあります。

val images = Arrays.asList(
    Image.Builder()
        .setImageUri(Uri.parse("http://www.example.com/entity_image1.png"))
        .setImageHeightInPixel(300)
        .setImageWidthInPixel(169)
        .build(),
    Image.Builder()
        .setImageUri(Uri.parse("http://www.example.com/entity_image2.png"))
        .setImageHeightInPixel(640)
        .setImageWidthInPixel(360)
        .build()
    // Consider adding other images for different form factors
)
MovieEntity

この例は、必要なすべてのフィールドを含む MovieEntity を作成する方法を示しています。

val movieEntity = MovieEntity.Builder()
   .setWatchNextType(WatchNextType.TYPE_CONTINUE)
   .setName("Movie name")
   .addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
   .addPosterImages(images)
   // Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
   .setLastEngagementTimeMillis(1701388800000)
   // Suppose the duration is 2 hours, it is 72000000 in milliseconds
   .setDurationMills(72000000)
   // Suppose last playback offset is 1 hour, 36000000 in milliseconds
   .setLastPlayBackPositionTimeMillis(36000000)
   .build()

ジャンルやコンテンツのレーティングなどの詳細情報を提供することで、Google TV はコンテンツをよりダイナミックに紹介し、適切な視聴者と結び付けることができます。

val genres = Arrays.asList("Action", "Science fiction")
val rating1 = RatingSystem.Builder().setAgencyName("MPAA").setRating("PG-13").build()
val contentRatings = Arrays.asList(rating1)
val movieEntity = MovieEntity.Builder()
    ...
    .addGenres(genres)
    .addContentRatings(contentRatings)
    .build()

有効期限を短く指定しない限り、エンティティは自動的に 60 日間利用可能になります。このデフォルト期間より前にエンティティを削除する必要がある場合にのみ、カスタムの有効期限を設定します。

// Set the expiration time to be now plus 30 days in milliseconds
val expirationTime = DisplayTimeWindow.Builder()
    .setEndTimestampMillis(now().toMillis()+2592000000).build()
val movieEntity = MovieEntity.Builder()
    ...
    .addAvailabilityTimeWindow(expirationTime)
    .build()
TvEpisodeEntity

この例では、必要なすべてのフィールドを含む TvEpisodeEntity を作成する方法を示します。

val tvEpisodeEntity = TvEpisodeEntity.Builder()
    .setWatchNextType(WatchNextType.TYPE_CONTINUE)
    .setName("Episode name")
    .addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
    .addPosterImages(images)
    // Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
    .setLastEngagementTimeMillis(1701388800000)
    .setDurationMills(72000000) // 2 hours in milliseconds
    // 45 minutes and 15 seconds in milliseconds is 2715000
    .setLastPlayBackPositionTimeMillis(2715000)
    .setEpisodeNumber("2")
    .setSeasonNumber("1")
    .setShowTitle("Title of the show")
    .build()

エピソード番号文字列("2" など)とシーズン番号文字列("1" など)は、[続きを見る] カードに表示される前に適切な形式に展開されます。数値文字列にする必要があります。「e2」、「episode 2」、「s1」、「season 1」は使用しないでください。

特定のテレビ番組にシーズンが 1 つしかない場合は、シーズン番号を 1 に設定します。

Google TV で視聴者がコンテンツを見つけやすくなるよう、ジャンル、コンテンツのレーティング、利用可能な時間帯などの追加データを提供することを検討してください。これらの詳細情報は、表示とフィルタリング オプションを強化できます。

val genres = Arrays.asList("Action", "Science fiction")
val rating1 = RatingSystem.Builder().setAgencyName("MPAA").setRating("PG-13").build()
val contentRatings = Arrays.asList(rating1)
val tvEpisodeEntity = TvEpisodeEntity.Builder()
    ...
    .addGenres(genres)
    .addContentRatings(contentRatings)
    .setSeasonTitle("Season Title")
    .setShowTitle("Show Title")
    .build()
VideoClipEntity

次に、すべての必須フィールドを含む VideoClipEntity を作成する例を示します。

VideoClipEntity は、YouTube 動画などのユーザー作成クリップを表します。

val videoClipEntity = VideoClipEntity.Builder()
    .setPlaybackUri(Uri.parse("https://www.example.com/uri_for_current_platform")
    .setWatchNextType(WatchNextType.TYPE_CONTINUE)
    .setName("Video clip name")
    .addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
    .addPosterImages(images)
    // Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
    .setLastEngagementTimeMillis(1701388800000)
    .setDurationMills(600000) //10 minutes in milliseconds
    .setLastPlayBackPositionTimeMillis(300000) //5 minutes in milliseconds
    .addContentRating(contentRating)
    .build()

必要に応じて、作成者、作成者の画像、作成時間(ミリ秒単位)、利用可能時間枠を設定できます。

LiveStreamingVideoEntity

次に、すべての必須フィールドを含む LiveStreamingVideoEntity を作成する例を示します。

val liveStreamingVideoEntity = LiveStreamingVideoEntity.Builder()
    .setPlaybackUri(Uri.parse("https://www.example.com/uri_for_current_platform")
    .setWatchNextType(WatchNextType.TYPE_CONTINUE)
    .setName("Live streaming name")
    .addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
    .addPosterImages(images)
    // Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
    .setLastEngagementTimeMillis(1701388800000)
    .setDurationMills(72000000) //2 hours in milliseconds
    .setLastPlayBackPositionTimeMillis(36000000) //1 hour in milliseconds
    .addContentRating(contentRating)
    .build()

必要に応じて、ライブ配信エンティティの開始時間、配信者、配信者アイコン、利用可能時間枠を設定できます。

属性と要件の詳細については、API リファレンスをご覧ください。

継続クラスタのデータを提供する

AppEngagePublishClient は、継続性クラスタの公開を行います。publishContinuationCluster() メソッドを使用して ContinuationCluster オブジェクトを公開します。

まず、isServiceAvailable() を使用して、サービスを統合に使用できるかどうかを確認する必要があります。

client.publishContinuationCluster(
    PublishContinuationClusterRequest
        .Builder()
        .setContinuationCluster(
            ContinuationCluster.Builder()
                .setAccountProfile(accountProfile)
                .addEntity(movieEntity1)
                .addEntity(movieEntity2)
                .addEntity(tvEpisodeEntity1)
                .addEntity(tvEpisodeEntity2)
                .setSyncAcrossDevices(true)
                .build()
        )
        .build()
)

サービスがリクエストを受信すると、1 つのトランザクション内で次のアクションが行われます。

  • デベロッパー パートナーが提供した既存の ContinuationCluster データが削除されます。
  • リクエストのデータが解析されて、更新された ContinuationCluster に保存されます。

エラーが発生した場合は、リクエスト全体が拒否され、それまでの状態が維持されます。

公開 API は既存のコンテンツを置き換える upsert API です。ContinuationCluster 内の特定のエンティティを更新する必要がある場合は、すべてのエンティティを再度公開する必要があります。

ContinuationCluster データは、成人アカウントに対してのみ提供する必要があります。AccountProfile が成人に属している場合にのみ公開します。

クロスデバイスの同期

SyncAcrossDevices フラグは、ユーザーの ContinuationCluster データをテレビ、スマートフォン、タブレットなどのデバイス間で同期するかどうかを制御します。デバイス間の同期はデフォルトで無効になっています。

値:

  • true: ContinuationCluster データは、シームレスな視聴体験を実現するために、ユーザーのすべてのデバイスで共有されます。デバイスをまたいで快適に利用するには、このオプションを強くおすすめします。
  • false: ContinuationCluster データは現在のデバイスに限定されます。

メディア アプリは、デバイス間の同期を有効/無効にするための明確な設定を提供する必要があります。ユーザーにメリットを説明し、ユーザーの設定を 1 回保存して、それに応じて publishContinuationCluster に適用します。

// Example to allow cross device syncing.
client.publishContinuationCluster(
    PublishContinuationClusterRequest
        .Builder()
        .setContinuationCluster(
            ContinuationCluster.Builder()
                .setAccountProfile(accountProfile)
                .setSyncAcrossDevices(true)
                .build()
        )
        .build()
)

クロスデバイス機能を最大限に活用するには、アプリがユーザーの同意を取得していることを確認し、SyncAcrossDevicestrue に設定します。これにより、コンテンツをデバイス間でシームレスに同期できるようになり、ユーザー エクスペリエンスの向上とエンゲージメントの増加につながります。たとえば、この機能を実装したパートナーは、コンテンツが複数のデバイスで表示されるようになったため、「視聴を続ける」のクリック数が 40% 増加しました。

動画の検出データを削除する

標準の 60 日間の保持期間より前に Google TV サーバーからユーザーのデータを手動で削除するには、client.deleteClusters() メソッドを使用します。リクエストを受信すると、サービスはアカウント プロファイルまたはアカウント全体の既存の動画検出データをすべて削除します。

DeleteReason 列挙型は、データ削除の理由を定義します。次のコードは、ログアウト時に視聴を再開するデータを削除します。


// If the user logs out from your media app, you must make the following call
// to remove continue watching data from the current google TV device,
// otherwise, the continue watching data will persist on the current
// google TV device until 60 days later.
client.deleteClusters(
    DeleteClustersRequest.Builder()
        .setAccountProfile(AccountProfile())
        .setReason(DeleteReason.DELETE_REASON_USER_LOG_OUT)
        .setSyncAcrossDevices(true)
        .build()
)

テスト

検証用アプリを使用して、Engage SDK の統合が正しく機能していることを確認します。この Android アプリケーションは、データを検証し、ブロードキャスト インテントが適切に処理されていることを確認するためのツールを提供します。

公開 API を呼び出した後、検証アプリでデータが正しく公開されていることを確認します。継続クラスタは、アプリのインターフェース内で個別の行として表示されます。

  • アプリの Android マニフェスト ファイルで、非本番環境ビルドに対してのみ Engage Service Flag を設定します。
  • Engage Verify アプリをインストールして開く
  • isServiceAvailablefalse の場合は、[切り替え] ボタンをクリックして有効にします。
  • アプリのパッケージ名を入力すると、公開を開始したときに公開されたデータが自動的に表示されます。
  • アプリで次のアクションをテストします。
    • ログインします。
    • プロファイルを切り替えます(該当する場合)。
    • 動画の再生を開始して一時停止したり、ホームページに戻ったりします。
    • 動画の再生中にアプリを閉じます。
    • [続きを見る] 行からアイテムを削除します(サポートされている場合)。
  • 各アクションの後、アプリが publishContinuationClusters API を呼び出し、検証アプリにデータが正しく表示されていることを確認します。
  • 検証アプリには、正しく実装されたエンティティに対して緑色の「すべて良好」チェックが表示されます。

    確認アプリの成功画面のスクリーンショット
    図 1. Verification App Success
  • 検証アプリは、問題のあるエンティティを報告します。

    確認アプリのエラーのスクリーンショット
    図 2. Verification App Error(確認アプリのエラー)
  • エラーのあるエンティティのトラブルシューティングを行うには、テレビのリモコンを使用して検証アプリでエンティティを選択してクリックします。具体的な問題が表示され、赤色でハイライト表示されるので、確認できます(下の例を参照)。

    Verification App のエラーの詳細
    図 3. Verification App Error Details(確認アプリのエラーの詳細)

REST API

Engage SDK は、iOS や Roku TV などの Android 以外のプラットフォームで一貫した視聴継続体験を提供するための REST API を提供します。この API を使用すると、デベロッパーは Android 以外のプラットフォームから、オプトインしたユーザーの [続きを見る] ステータスを更新できます。

前提条件

  • まず、オンデバイス Engage SDK ベースの統合を完了する必要があります。この重要なステップでは、Google のユーザー ID とアプリの AccountProfile の間に必要な関連付けを確立します。
  • API アクセスと認証: Google Cloud プロジェクトで API を表示して有効にするには、許可リスト登録プロセスを行う必要があります。すべての API リクエストで認証が必要です。

アクセスを取得する

Google Cloud コンソールで API を表示して有効にするには、アカウントを登録する必要があります。

  1. Google Workspace お客様 ID を取得できる必要があります。利用できない場合は、Google Workspace と、API の呼び出しに使用する Google アカウントを設定する必要があります。
  2. Google Workspace に関連付けられているメールアドレスを使用して、Google Cloud コンソールでアカウントを設定します。
  3. 新しいプロジェクトを作成する
  4. API 認証用のサービス アカウントを作成します。サービス アカウントを作成すると、次の 2 つの項目が作成されます。
    • サービス アカウント ID。
    • サービス アカウント キーを含む JSON ファイル。このファイルは安全に保管してください。後で API に対してクライアントを認証するために必要になります。
  5. ワークスペースと関連付けられた Google アカウントで REST API を使用できるようになりました。変更が伝播されると、サービス アカウントから API を呼び出す準備ができたかどうかを通知します。
  6. 委任された API 呼び出しを行う準備をするには、こちらの手順に沿って操作します。

継続クラスタを公開する

動画検出データを公開するには、次の構文を使用して publishContinuationCluster API に POST リクエストを実行します。

https://tvvideodiscovery.googleapis.com/v1/packages/{package_name}/accounts/{account_id}/profiles/{profile_id}/publishContinuationCluster

ここで

  • package_name: メディア プロバイダのパッケージ名
  • accountId: システム内のユーザー アカウントの一意の ID。デバイス上のパスで使用される accountId と一致する必要があります。
  • profileId: システム内のアカウント内のユーザー プロファイルの一意の ID。デバイス上のパスで使用されている profileId と一致する必要があります。

プロファイルのないアカウントの URL は次のとおりです。

https://tvvideodiscovery.googleapis.com/v1/packages/{package_name}/accounts/{account_id}/publishContinuationCluster

リクエストのペイロードは entities フィールドで表されます。entities は、MovieEntity または TVEpisodeEntity のいずれかであるコンテンツ エンティティのリストを表します。これは必須項目です。

リクエストの本文

フィールド

文字入力

必須

説明

entities

MediaEntity オブジェクトのリスト

はい

コンテンツ エンティティのリスト(最大 5 個)。上位 5 個のみが保持され、残りは削除されます。ユーザーがすべてのエンティティの視聴を完了したことを示すために、空のリストが許可されます。

フィールド entities には、個々の movieEntitytvEpisodeEntity が含まれます。

フィールド

文字入力

必須

説明

movieEntity

MovieEntity

はい

ContinuationCluster 内の映画を表すオブジェクト。

tvEpisodeEntity

TvEpisodeEntity

はい

ContinuationCluster 内のテレビ番組のエピソードを表すオブジェクト。

entities 配列内の各オブジェクトは、共通フィールドとタイプ固有のフィールドとともに、利用可能な MediaEntity タイプ(MovieEntityTvEpisodeEntity)のいずれかである必要があります。

次のコード スニペットは、publishContinuationCluster API のリクエスト本文のペイロードを示しています。

{
  "entities": [
    {
      "movieEntity": {
        "watch_next_type": "WATCH_NEXT_TYPE_CONTINUE",
        "name": "Movie1",
        "platform_specific_playback_uris": [
          "https://www.example.com/entity_uri_for_android",
          "https://www.example.com/entity_uri_for_iOS"
        ],
        "poster_images": [
          "http://www.example.com/movie1_img1.png",
          "http://www.example.com/movie1_imag2.png"
        ],
        "last_engagement_time_millis": 864600000,
        "duration_millis": 5400000,
        "last_play_back_position_time_millis": 3241111
      }
    },
    {
      "tvEpisodeEntity": {
        "watch_next_type": "WATCH_NEXT_TYPE_CONTINUE",
        "name": "TV SERIES EPISODE 1",
        "platform_specific_playback_uris": [
          "https://www.example.com/entity_uri_for_android",
          "https://www.example.com/entity_uri_for_iOS"
        ],
        "poster_images": [
          "http://www.example.com/episode1_img1.png",
          "http://www.example.com/episode1_imag2.png"
        ],
        "last_engagement_time_millis": 864600000,
        "duration_millis": 1800000,
        "last_play_back_position_time_millis": 2141231,
        "episode_display_number": "1",
        "season_number": "1",
        "show_title": "title"
      }
    }
  ]
}

動画の検出データを削除する

clearClusters API を使用して、動画検出データを削除します。

POST URL を使用して、動画検出データからエンティティを削除します。継続クラスタのデータを削除するには、次の構文を使用して clearClusters API に POST リクエストを実行します。

https://tvvideodiscovery.googleapis.com/v1/packages/{package_name}/accounts/{account_id}/profiles/{profile_id}/clearClusters

ここで

  • package_name: メディア プロバイダのパッケージ名。
  • accountId: システム内のユーザー アカウントの一意の ID。デバイス上のパスで使用される accountId と一致する必要があります。
  • profileId: システム内のアカウント内のユーザー プロファイルの一意の ID。デバイス上のパスで使用されている profileId と一致する必要があります。

clearClusters API のペイロードには 1 つのフィールド reason のみが含まれます。このフィールドには、データを削除する理由を指定する DeleteReason が含まれます。

{
  "reason": "DELETE_REASON_LOSS_OF_CONSENT"
}

テスト

データの投稿が成功したら、ユーザー テスト アカウントを使用して、Google TV や Android および iOS の Google TV モバイルアプリなどの対象の Google サーフェスの [続きを見る] 行に、期待どおりのコンテンツが表示されることを確認します。

テストでは、数分の妥当な伝播遅延を許容し、映画の一部を視聴する、エピソードを視聴し終えるなどの視聴要件を遵守します。詳しくは、アプリ デベロッパー向け Watch Next ガイドラインをご覧ください。