Engage SDK для рекомендаций видео

В этом руководстве рассказывается, как разработчики могут интегрировать рекомендуемый видеоконтент с помощью Engage SDK, чтобы показывать рекомендации на платформах Google, например на телевизорах, мобильных устройствах и планшетах.

Рекомендации используют кластер рекомендаций, чтобы показывать фильмы и сериалы из нескольких приложений в одной группе интерфейса. Каждый партнер-разработчик может транслировать не более 25 объектов в каждом кластере рекомендаций, а в каждом запросе может быть не более 7 кластеров рекомендаций.

Подготовка

Выполните инструкции из раздела Предварительная работа в руководстве по началу работы.

  1. Выполните публикацию в активной службе.
  2. Публикуйте данные рекомендаций не чаще одного раза в день. Это может происходить по одной из следующих причин:
    • Первый вход пользователя в систему за день. (or)
    • Когда пользователь начинает взаимодействовать с приложением.

Интеграция

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.

Удалить данные Engage

Чтобы вручную удалить данные пользователя с сервера Google TV до истечения стандартного 60-дневного срока хранения, используйте метод client.deleteClusters(). После получения запроса сервис удаляет все существующие данные Engage для профиля аккаунта или для всего аккаунта.

Перечисление 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(
  DeleteClustersRequest.Builder()
    .setAccountProfile(accountProfile)
    .setDeleteReason(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(
  DeleteClustersRequest.Builder()
    .setAccountProfile(accountProfile)
    .setDeleteReason(DeleteReason.DELETE_REASON_LOSS_OF_CONSENT)
    .build()
)

Создание объектов

В SDK определены разные объекты, представляющие каждый тип товара. Для кластера рекомендаций поддерживаются следующие объекты:

  1. MediaActionFeedEntity
  2. MovieEntity
  3. TvShowEntity
  4. LiveTvChannelEntity
  5. LiveTvProgramEntity

Добавьте описания

Добавьте краткое описание для каждого объекта. Оно будет показываться, когда пользователи наведут указатель мыши на объект.

Текст призыва к действию

При желании добавьте текст призыва к действию для каждого объекта. Этот текст будет показываться пользователям, чтобы побудить их к взаимодействию.

Теги

При необходимости укажите список тегов для каждого объекта. Теги можно использовать для категоризации и фильтрации.

URI воспроизведения для определенных платформ

Создайте URI воспроизведения для каждой поддерживаемой платформы: Android TV, Android или iOS. Это позволяет системе выбрать подходящий 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 = RatingSystem
  .Builder()
  .setAgencyName("MPAA")
  .setRating("PG-13")
  .build()

Постеры

Для постеров необходимо указать URI и размеры в пикселях (высоту и ширину). Чтобы охватить разные форм-факторы, предоставьте несколько постеров. Убедитесь, что все изображения имеют соотношение сторон 16:9 и высоту не менее 200 пикселей. Это необходимо для корректного показа объекта "Рекомендации", особенно в Entertainment Space от Google. Изображения высотой менее 200 пикселей могут не показываться.

val image1 = Image.Builder()
  .setImageUri(Uri.parse("http://www.example.com/entity_image1.png"))
  .setImageHeightInPixel(300)
  .setImageWidthInPixel(169)
  .build()

val image2 = 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
  .Builder()
  .addSimilarWatchedTitleName("Movie or TV Show Title")
  .addSimilarWatchedTitleName("Movie or TV Show Title")
  .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 userLikedTitle = RecommendationReasonUserLikedTitle
  .Builder()
  .setLikedTitleName("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())
  .build()

val window2 = DisplayTimeWindow
  .Builder()
  .setEndTimestampMillis(now() + 30.days.toMillis())
  .build()

val availabilityTimeWindows: List<DisplayTimeWindow> = listOf(window1, window2)

DataFeedElementId

Если вы интегрировали каталог медиаконтента или фид действий в медиа с Google TV, вам не нужно создавать отдельные объекты для фильмов или сериалов. Вместо этого вы можете создать объект MediaActionFeedEntity, который включает обязательное поле DataFeedElementId. Этот идентификатор должен быть уникальным и совпадать с идентификатором в фиде действий с медиаконтентом, поскольку он помогает идентифицировать загруженный контент фида и выполнять поиск медиаконтента.

val id = "dataFeedElementId"

MovieEntity

Вот пример создания MovieEntity со всеми обязательными полями:

val movieEntity = MovieEntity.Builder()
  .setName("Movie name")
  .setDescription("A sentence describing movie.")
  .addPlatformSpecificPlaybackUris(platformSpecificPlaybackUris)
  .addPosterImages(images)
  // Suppose the duration is 2 hours, it is 72000000 in milliseconds
  .setDurationMillis(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.")
  .addPlatformSpecificPlaybackUris(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
  .addPlatformSpecificPlaybackUris(channelPlaybackUris)
  .setLogoImage(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
  .addPlatformSpecificPlaybackUris(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
        .build()
  )
  .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()