Android TV で「続きを見る」を統合する

book_path: /distribute/other-docs/_book.yaml project_path: /distribute/other-docs/_project.yaml

このガイドでは、Engage SDK を使用して Android TV アプリに「続きを見る」を統合する方法について説明します。

事前作業

スタートガイドの事前準備の手順を完了していること。

統合

エンティティを作成する

この 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 とピクセル サイズ(高さと幅)が必要です。複数のポスター画像を提供してさまざまなフォーム ファクタをターゲットに設定しますが、すべての画像が 16:9 のアスペクト比と 200 ピクセルの最小の高さを維持していることを確認してください。特に Google のエンターテイメント スペース内で「続きを見る」エンティティが正しく表示されるようにするためです。高さが 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 は、継続性クラスタの公開を行います。publishContinuationCluste メソッドを使用して ContinuationCluster オブジェクトを公開します。

スタートガイドの説明に沿って、クライアントを初期化し、サービスの可用性を確認してください。

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 です。継続クラスタ内の特定のエンティティを更新する必要がある場合は、すべてのエンティティを再度公開する必要があります。

継続クラスタ データは、成人アカウントに対してのみ提供する必要があります。アカウント プロフィールが成人の場合にのみ公開します。

しないでください。

クロスデバイスの同期

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

値:

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

メディア アプリは、デバイス間の同期を有効または無効にするための明確な設定を提供する必要があります。ユーザーにメリットを説明し、ユーザーの設定を一度保存して、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 サーバーからユーザーのデータを手動で削除するには、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 の統合が正しく機能していることを確認します。

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

  • アプリで次のアクションをテストします。
    • ログインします。
    • プロファイルを切り替えます(該当する場合)。
    • 動画の再生を開始して一時停止したり、ホームページに戻ったりします。
    • 動画の再生中にアプリを閉じます。
    • [続きを見る] 行からアイテムを削除します(サポートされている場合)。
  • 各アクションの後、アプリが publishContinuationClusters API を呼び出し、検証アプリにデータが正しく表示されていることを確認します。
  • 検証アプリには、正しく実装されたエンティティに対して緑色の「すべて正常」チェックマークが表示されます。

    Verification App Success Screenshot
    図 1. Verification App Success(検証アプリの成功)
  • 検証アプリは、問題のあるエンティティを報告します。

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

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