本指南包含面向开发者的说明,介绍了如何使用 Engage SDK 集成推荐的 视频内容,以便在 Google 途径(例如电视、手机和平板电脑)中填充推荐 体验。
推荐功能利用推荐集群 在一个界面分组中显示来自多个应用的电影和电视节目。每个开发者合作伙伴都可以在每个推荐集群中广播最多 25 个实体 ,并且每个请求最多可以有 7 个 推荐集群。
准备工作
请按照入门指南中的准备工作说明完成相关操作。
- 在前台服务中执行发布操作。
- 每天最多发布一次推荐数据,触发条件如下:
- 用户当天首次登录。(或者)
- 用户开始与应用互动。
集成
AppEngagePublishClient 会发布推荐集群。使用 publishRecommendationClusters 方法发布推荐对象。
请务必按照 入门指南中的说明初始化客户端并检查服务可用性。
client.publishRecommendationClusters(recommendationRequest)
更新/插入推荐集群
集群是实体的逻辑分组。以下代码示例说明了如何根据您的偏好构建集群,以及如何创建发布请求并更新/插入所有集群。
RecommendationClusterType 决定了
集群的显示方式。
// cluster for popular movies
val recommendationCluster1 = RecommendationCluster
.Builder()
.addEntity(movie1)
.addEntity(movie2)
.addEntity(movie3)
.addEntity(movie4)
.addEntity(tvShow)
// This cluster is meant to be used as an individual provider row
.setRecommendationClusterType(TYPE_PROVIDER_ROW)
.setTitle("Popular Movies")
.build()
// cluster for live TV programs
val recommendationCluster2 = RecommendationCluster
.Builder()
.addEntity(liveTvProgramEntity1)
.addEntity(liveTvProgramEntity2)
.addEntity(liveTvProgramEntity3)
.addEntity(liveTvProgramEntity4)
.addEntity(liveTvProgramEntity5)
// This cluster is meant to be used as an individual provider row
.setRecommendationClusterType(TYPE_PROVIDER_ROW)
.setTitle("Popular Live TV Programs")
.build()
// creating a publishing request
val recommendationRequest = PublishRecommendationClustersRequest
.Builder()
.setSyncAcrossDevices(true)
.setAccountProfile(accountProfile)
.addRecommendationCluster(recommendationCluster1)
.addRecommendationCluster(recommendationCluster2)
.build()
当服务收到请求时,系统会在一项事务中执行以下操作:
- 系统会移除开发者合作伙伴的现有
RecommendationsCluster数据。 - 系统会解析请求中的数据,并将其存储在更新后的
RecommendationsCluster中。如果发生错误,系统将拒绝整个请求,并保留现有状态。
跨设备同步
SyncAcrossDevices 标志用于控制是否与 Google TV 分享用户的推荐集群数据,以及是否在用户的设备(例如电视、手机和平板电脑)上提供这些数据。为了使推荐功能正常运行,必须将此标志设置为 true。
征求同意
媒体应用必须提供明确的设置来启用或停用跨设备同步。向用户阐述相关好处,并存储用户的偏好设置,然后在 publishRecommendations 请求中相应地应用该设置。如需充分利用跨设备功能,请验证应用是否已征得用户同意,并将 SyncAcrossDevices 设置为 true。
删除视频发现数据
如需在标准 60 天保留期限之前从 Google TV 服务器手动删除用户的数据,请使用 client.deleteClusters() 方法。收到请求后,服务会删除账号个人资料或整个账号的所有现有视频发现数据。
The DeleteReason 枚举定义了删除数据的原因。
以下代码会在用户退出登录时移除推荐内容。
// If the user logs out from your media app, you must make the following call
// to remove recommendations data from the current Google TV device, otherwise,
// the recommendations data persists on the current Google TV device until 60
// days later.
client.deleteClusters(
new DeleteClustersRequest.Builder()
.setAccountProfile(AccountProfile())
.setReason(DeleteReason.DELETE_REASON_USER_LOG_OUT)
.build()
)
// If the user revokes the consent to share data with Google TV, you must make
// the following call to remove recommendations data from all current Google TV
// devices. Otherwise, the recommendations data persists until 60 days later.
client.deleteClusters(
new DeleteClustersRequest.Builder()
.setAccountProfile(AccountProfile())
.setReason(DeleteReason.DELETE_REASON_LOSS_OF_CONSENT)
.build()
)
创建实体
SDK 定义了不同的实体来代表每种商品种类。推荐集群支持以下实体:
提供说明
为每个实体提供简短说明;当用户将鼠标悬停在实体上时,系统会显示此说明,以便向用户提供更多详细信息。
号召性用语文字
为每个实体提供可选的号召性用语文字。系统会向用户显示此文字,以鼓励用户互动。
标签
您可以选择为每个实体提供标签列表。标签可用于分类和过滤。
平台专用的播放 URI
为每个受支持的平台(Android TV、Android 或 iOS)创建播放 URI。 这样,系统就可以在相应平台上选择合适的 URI 来播放视频。
如果所有平台的播放 URI 都相同,请为每个平台重复该 URI。
// Required. Set this when you want recommended 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()
// Optional. Set this when you want recommended entities to show up on
// Google TV Android app
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 recommended 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)
// Provide appropriate rating for the system.
val contentRating = new RatingSystem
.Builder()
.setAgencyName("MPAA")
.setRating("PG-13")
.build()
海报图片
海报图片需要 URI 和像素尺寸(高度和宽度)。您可以提供多张海报图片来针对不同的设备类型,但请验证所有图片都保持 16:9 的宽高比和至少 200 像素的高度,以便正确显示“推荐”实体,尤其是在 Google 的 Entertainment Space中。高度小于 200 像素的图片可能不会显示。
Image image1 = new Image.Builder()
.setImageUri(Uri.parse("http://www.example.com/entity_image1.png");)
.setImageHeightInPixel(300)
.setImageWidthInPixel(169)
.build()
Image image2 = new Image.Builder()
.setImageUri(Uri.parse("http://www.example.com/entity_image2.png");)
.setImageHeightInPixel(640)
.setImageWidthInPixel(360)
.build()
// And other images for different form factors.
val images = Arrays.asList(image1, image2)
推荐原因
您可以选择提供推荐原因,Google TV 可以使用该原因来构建向用户推荐特定电影或电视节目的理由。
//Allows us to construct reason: "Because it is top 10 on your Channel"
val topOnPartner = RecommendationReasonTopOnPartner
.Builder()
.setNum(10) //any valid integer value
.build()
//Allows us to construct reason: "Because it is popular on your Channel"
val popularOnPartner = RecommendationReasonPopularOnPartner
.Builder()
.build()
//Allows us to construct reason: "New to your channel, or Just added"
val newOnPartner = RecommendationReasonNewOnPartner
.Builder()
.build()
//Allows us to construct reason: "Because you watched Star Wars"
val watchedSimilarTitles = RecommendationReasonWatchedSimilarTitles
.addSimilarWatchedTitleName("Movie or TV Show Title")
.addSimilarWatchedTitleName("Movie or TV Show Title")
.Builder()
.build()
//Allows us to construct reason: "Recommended for you by ChannelName"
val recommendedForUser = RecommendationReasonRecommendedForUser
.Builder()
.build()
val watchAgain = RecommendationReasonWatchAgain
.Builder()
.build()
val fromUserWatchList = RecommendationReasonFromUserWatchlist
.Builder()
.build()
val userLikedOnPartner = RecommendationReasonUserLikedOnPartner
.Builder()
.setTitleName("Movie or TV Show Title")
.build()
val generic = RecommendationReasonGeneric.Builder().build()
展示时间窗口
如果某个实体仅在有限的时间内可用,请设置自定义到期时间。如果没有明确的到期时间,实体将在 60 天后自动过期并被删除。因此,只有在需要提前使实体过期时,才需要设置到期时间。您可以指定多个此类可用时间窗口。
val window1 = DisplayTimeWindow
.Builder()
.setStartTimeStampMillis(now()+ 1.days.toMillis())
.setEndTimeStampMillis(now()+ 30.days.toMillis())
val window2 = DisplayTimeWindow
.Builder()
.setEndTimeStampMillis(now()+ 30.days.toMillis())
val availabilityTimeWindows: List<DisplayTimeWindow> = listof(window1,window2)
DataFeedElementId
如果您已将媒体目录或媒体操作 Feed 与 Google TV 集成,
则无需为电影或电视节目创建单独的实体,而是可以
创建一个 MediaActionFeedEntity,其中包含
必填字段 DataFeedElementId。此 ID 必须是唯一的,并且必须与媒体操作 Feed 中的 ID 匹配,因为它可以帮助识别提取的 Feed 内容并执行媒体内容查找。
val id = "dataFeedElementId"
MovieEntity
以下示例展示了如何创建包含所有必填字段的 MovieEntity:
val movieEntity = MovieEntity.Builder()
.setName("Movie name")
.setDescription("A sentence describing movie.")
.addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
.addPosterImages(images)
// Suppose the duration is 2 hours, it is 72000000 in milliseconds
.setDurationMills(72000000)
.setCallToActionText("Watch Now")
.addTag("Action")
.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);
//Suppose release date is 11-02-2025
val releaseDate = 1739233800000L
val movieEntity = MovieEntity.Builder()
...
.addGenres(genres)
.setReleaseDateEpochMillis(releaseDate)
.addContentRatings(contentRatings)
.setRecommendationReason(topOnPartner or watchedSimilarTitles)
.addAllAvailabilityTimeWindows(availabilityTimeWindows)
.build()
TvShowEntity
以下示例展示了如何创建包含所有必填字段的 TvShowEntity:
val tvShowEntity = TvShowEntity.Builder()
.setName("Show title")
.setDescription("A sentence describing TV Show.")
.addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
.addPosterImages(images)
.setCallToActionText("Watch Now")
.addTag("Drama")
.build();
您可以选择提供其他数据,例如类型、内容分级、推荐原因、优惠价格、剧季数或可用时间窗口,Google TV 可能会使用这些数据来增强显示效果或进行过滤。
val genres = Arrays.asList("Action", "Science fiction");
val rating1 = RatingSystem.Builder()
.setAgencyName("MPAA")
.setRating("pg-13")
.build();
val price = Price.Builder()
.setCurrentPrice("$14.99")
.setStrikethroughPrice("$16.99")
.build();
val contentRatings = Arrays.asList(rating1);
val seasonCount = 5;
val tvShowEntity = TvShowEntity.Builder()
...
.addGenres(genres)
.addContentRatings(contentRatings)
.setRecommendationReason(topOnPartner or watchedSimilarTitles)
.addAllAvailabilityTimeWindows(availabilityTimeWindows)
.setSeasonCount(seasonCount)
.setPrice(price)
.build()
MediaActionFeedEntity
以下示例展示了如何创建包含所有必填字段的 MediaActionFeedEntity:
val mediaActionFeedEntity = MediaActionFeedEntity.Builder()
.setDataFeedElementId(id)
.setCallToActionText("Watch Now")
.addTag("Action")
.build()
您可以选择提供其他数据,例如说明、推荐原因和展示时间窗口,Google TV 可能会使用这些数据来增强显示效果或进行过滤。
val mediaActionFeedEntity = MediaActionFeedEntity.Builder()
.setName("Movie name or TV Show name")
.setDescription("A sentence describing an entity")
.setRecommendationReason(topOnPartner or watchedSimilarTitles)
.addPosterImages(images)
.build()
LiveTvChannelEntity
此实体表示直播电视频道。以下示例展示了如何创建包含所有必填字段的 LiveTvChannelEntity:
val liveTvChannelEntity = LiveTvChannelEntity.Builder()
.setName("Channel Name")
// ID of the live TV channel
.setEntityId("https://www.example.com/channel/12345")
.setDescription("A sentence describing this live TV channel.")
// channel playback uri must contain at least PlatformType.TYPE_ANDROID_TV
.addPlatformSpecificPlaybackUri(channelPlaybackUris)
.addLogoImage(logoImage)
.setCallToActionText("Watch Now")
.addTag("News")
.build()
您可以选择提供其他数据,例如内容分级或推荐原因。
val rating1 = RatingSystem.Builder()
.setAgencyName("MPAA")
.setRating("pg-13")
.build()
val contentRatings = Arrays.asList(rating1)
val liveTvChannelEntity = LiveTvChannelEntity.Builder()
...
.addContentRatings(contentRatings)
.setRecommendationReason(topOnPartner)
.build()
LiveTvProgramEntity
此实体表示直播电视频道上正在播出或计划播出的直播电视节目卡片。以下示例展示了如何创建包含所有必填字段的 LiveTvProgramEntity:
val liveTvProgramEntity = LiveTvProgramEntity.Builder()
// First set the channel information
.setChannelName("Channel Name")
.setChannelId("https://www.example.com/channel/12345")
// channel playback uri must contain at least PlatformType.TYPE_ANDROID_TV
.addPlatformSpecificPlaybackUri(channelPlaybackUris)
.setChannelLogoImage(channelLogoImage)
// Then set the program or card specific information.
.setName("Program Name")
.setEntityId("https://www.example.com/schedule/123")
.setDescription("Program Description")
.addAvailabilityTimeWindow(
DisplayTimeWindow.Builder()
.setStartTimestampMillis(1756713600000L)// 2025-09-01T07:30:00+0000
.setEndTimestampMillis(1756715400000L))// 2025-09-01T08:00:00+0000
.addPosterImage(programImage)
.setCallToActionText("Watch Now")
.addTag("Sports")
.build()
您可以选择提供其他数据,例如内容分级、类型或推荐原因。
val rating1 = RatingSystem.Builder()
.setAgencyName("MPAA")
.setRating("pg-13")
.build()
val contentRatings = Arrays.asList(rating1)
val genres = Arrays.asList("Action", "Science fiction")
val liveTvProgramEntity = LiveTvProgramEntity.Builder()
...
.addContentRatings(contentRatings)
.addGenres(genres)
.setRecommendationReason(topOnPartner)
.build()