「繼續觀看」會運用接續叢集,在一個 UI 群組中顯示多個應用程式提供的未看完影片,以及同一電視影集季別中待觀看的下一集。您可以在這個延續性叢集中推薦他們的實體。請按照本指南操作,瞭解如何使用 Engage SDK,透過「繼續觀看」體驗提升使用者參與度。
事前作業
開始之前,請先完成下列步驟:
更新至目標 API 19 以上版本
將
com.google.android.engage
程式庫新增至應用程式:整合時需要使用不同的 SDK:一個用於行動應用程式,另一個用於 TV 應用程式。
行動裝置
dependencies { implementation 'com.google.android.engage:engage-core:1.5.5 }
電視
dependencies { implementation 'com.google.android.engage:engage-tv:1.0.2 }
在
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" />
為 TV APK 新增
WRITE_EPG_DATA
權限<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
使用背景服務 (例如
androidx.work
) 安排發布時間,確保內容發布作業可靠。為提供順暢的觀看體驗,請在發生下列事件時發布「繼續觀看」資料:
- 首次登入:使用者首次登入時,請發布資料,確保他們可以立即查看觀看記錄。
- 建立或切換設定檔 (多設定檔應用程式):如果應用程式支援多個設定檔,請在使用者建立或切換設定檔時發布資料。
- 影片播放中斷:為協助使用者接續上次的進度,請在使用者暫停或停止播放影片,或在播放期間離開應用程式時發布資料。
- 更新「繼續觀看」匣 (如支援):使用者從「繼續觀看」匣中移除項目時,請發布更新資料,反映這項變更。
- 影片觀看率:
- 如果是電影,請從「繼續觀看」專區移除已看完的電影。 如果電影屬於系列作品,請加入下一部電影,讓使用者持續觀看。
- 如果是劇集,請移除已看完的集數,並新增該系列節目的下一集 (如有),鼓勵使用者繼續觀看。
整合
AccountProfile
如要在 Google TV 上享有個人化的「繼續觀看」體驗,請提供帳戶和個人資料資訊。使用 AccountProfile 提供下列資訊:
帳戶 ID:代表應用程式內使用者帳戶的專屬 ID。可以是實際帳戶 ID,也可以是適當的模糊處理版本。
設定檔 ID (選用):如果應用程式支援單一帳戶內的多個設定檔,請提供特定使用者設定檔的專屬 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 定義了不同實體,用來代表各種項目類型。後續對話群組支援下列實體:
指定這些實體的平台專屬 URI 和海報圖片。
此外,如果尚未建立各個平台的播放 URI (例如 Android TV、Android 或 iOS),請一併建立。因此,當使用者在各個平台繼續觀看時,應用程式會使用目標播放 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。
為盡量提高觀眾在 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()
)
服務收到要求後,系統會在單一交易中執行以下動作:
- 移除開發合作夥伴提供的現有
ContinuationCluster
資料。 - 剖析要求所提供的資料並儲存在更新後的
ContinuationCluster
。
如果發生錯誤,整個要求都會遭到拒絕,現有狀態則維持不變。
發布 API 屬於更新/插入 API,會取代現有內容。如要更新 ContinuationCluster 中的特定實體,您必須重新發布所有實體。
請只為成人帳戶提供 ContinuationCluster 資料。 只有在 AccountProfile 屬於成人時發布。
跨裝置同步
SyncAcrossDevices
旗標可控制使用者的 ContinuationCluster
資料是否要在電視、手機、平板電腦等裝置之間同步。根據預設,跨裝置同步功能會停用。
值:
- true:在使用者所有裝置間分享 ContinuationCluster 資料,提供順暢的觀看體驗。強烈建議您選用這個選項,以獲得最佳跨裝置體驗。
- false:ContinuationCluster 資料僅限目前裝置使用。
取得同意聲明:
媒體應用程式必須提供明確的設定,讓使用者啟用/停用跨裝置同步功能。向使用者說明優點,並儲存使用者的偏好設定一次,然後在 publishContinuationCluster 中套用。
// Example to allow cross device syncing.
client.publishContinuationCluster(
PublishContinuationClusterRequest
.Builder()
.setContinuationCluster(
ContinuationCluster.Builder()
.setAccountProfile(accountProfile)
.setSyncAcrossDevices(true)
.build()
)
.build()
)
如要充分運用跨裝置功能,請確認應用程式已取得使用者同意聲明,並啟用 SyncAcrossDevices
true
。這樣一來,內容就能在裝置間順暢同步,進而提升使用者體驗和參與度。舉例來說,某合作夥伴導入這項功能後,由於內容顯示在多部裝置上,「繼續觀看」點擊次數增加了 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 Verify 應用程式
- 如果「
isServiceAvailable
」為「false
」,請按一下「切換」按鈕啟用。 - 輸入應用程式的套件名稱,開始發布後即可自動查看已發布的資料。
- 在應用程式中測試下列動作:
- 登入。
- 切換設定檔(如適用)。
- 開始播放影片、暫停播放,或返回首頁。
- 在影片播放期間關閉應用程式。
- 從「繼續觀看」列中移除項目 (如支援)。
- 每次執行動作後,請確認應用程式已叫用
publishContinuationClusters
API,且驗證應用程式中顯示的資料正確無誤。 如果實體實作正確,驗證應用程式會顯示綠色的「一切正常」勾號。
圖 1. 驗證應用程式成功 驗證應用程式會標示任何有問題的實體。
圖 2. 驗證應用程式錯誤 如要排解實體錯誤,請使用電視遙控器選取並點按驗證應用程式中的實體。系統會顯示特定問題,並以紅色醒目顯示供你查看 (請參閱下方範例)。
圖 3. 驗證應用程式錯誤詳細資料
REST API
Engage SDK 提供 REST API,可在 iOS、Roku TV 等非 Android 平台上,提供一致的「繼續觀看」體驗。開發人員可透過這項 API,從非 Android 平台更新選擇加入的使用者的「繼續觀看」狀態。
必要條件
- 您必須先完成以裝置端 Engage SDK 為基礎的整合。這個重要步驟會在 Google 的使用者 ID 和應用程式的
AccountProfile
之間建立必要的關聯。 - API 存取權和驗證:如要在 Google Cloud 專案中查看及啟用 API,必須先通過許可清單程序。所有 API 要求都必須通過驗證。
取得存取權
如要在 Google Cloud 控制台中查看及啟用 API,必須先註冊帳戶。
- 請提供 Google Workspace 客戶 ID。如果沒有,您可能需要設定 Google Workspace,以及要用於呼叫 API 的任何 Google 帳戶。
- 使用與 Google Workspace 相關聯的電子郵件,透過 Google Cloud 控制台設定帳戶。
- 建立新專案
- 建立用於 API 驗證的服務帳戶。建立服務帳戶後,您會取得兩項項目:
- 服務帳戶 ID。
- 內含服務帳戶金鑰的 JSON 檔案。請妥善保管這個檔案,後續步驟將會用到,以便向 API 驗證用戶端。
- Workspace 和相關聯的 Google 帳戶現在可以使用 REST API。 變更傳播完畢後,系統會通知您服務帳戶是否已可呼叫 API。
- 請按照這些步驟,準備發出委派的 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 相符。
不含個人資料的帳戶網址為:
https://tvvideodiscovery.googleapis.com/v1/packages/{package_name}/accounts/{account_id}/publishContinuationCluster
要求酬載會以 entities
欄位表示。entities
代表內容實體清單,可以是 MovieEntity
或 TVEpisodeEntity
。這是必填欄位。
要求主體
欄位 |
類型 |
必要 |
說明 |
實體 |
MediaEntity 物件清單 |
是 |
內容實體清單 (最多 5 個),系統只會保留前五個,其餘則會捨棄。允許空白清單,表示使用者已看完所有實體。 |
欄位 entities
包含個別的 movieEntity
和 tvEpisodeEntity
。
欄位 |
類型 |
必要 |
說明 |
movieEntity |
MovieEntity |
是 |
代表 ContinuationCluster 內電影的物件。 |
tvEpisodeEntity |
TvEpisodeEntity |
是 |
代表 ContinuationCluster 內電視節目的物件。 |
實體陣列中的每個物件都必須是可用的 MediaEntity 類型之一,即 MovieEntity
和 TvEpisodeEntity
,以及通用和類型專屬的欄位。
下列程式碼片段展示 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 網址從影片探索資料中移除實體。
如要刪除後續叢集資料,請使用下列語法,對 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 的酬載只包含一個欄位 reason
,其中包含 DeleteReason
,指定移除資料的原因。
{
"reason": "DELETE_REASON_LOSS_OF_CONSENT"
}
測試
成功發布資料後,請使用使用者測試帳戶,確認預期內容是否顯示在目標 Google 服務 (例如 Google TV、Android 和 iOS 版 Google TV 行動應用程式) 的「繼續觀看」列中。
測試時,請等待幾分鐘的合理傳播延遲時間,並遵守觀看規定,例如觀看部分電影或看完一集節目。詳情請參閱應用程式開發人員的「接下來請看」指南。