Функция «Продолжить просмотр» использует кластер «Продолжение» для отображения незаконченных видео и следующих серий того же сезона из нескольких приложений в одной группе пользовательского интерфейса. Вы можете включить их сущности в этот кластер «Продолжение». Следуйте этому руководству, чтобы узнать, как повысить вовлечённость пользователей с помощью функции «Продолжить просмотр» с помощью Engage SDK .
Предварительная работа
Прежде чем начать, выполните следующие шаги:
обновление до Target API 19 или выше
Добавьте библиотеку
com.google.android.engage
в свое приложение:Для интеграции используются отдельные SDK: один для мобильных приложений и один для телевизионных приложений.
Мобильный
dependencies { implementation 'com.google.android.engage:engage-core:1.5.5 }
ТВ
dependencies { implementation 'com.google.android.engage:engage-tv:1.0.2 }
Настройте среду службы Engage на работу в файле
AndroidManifest.xml
.Мобильный
<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" />
Добавить разрешение для
WRITE_EPG_DATA
для tv apk<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
Проверьте надежность публикации контента, используя для планирования фоновую службу, например
androidx.work
.Чтобы обеспечить бесперебойный просмотр, публикуйте данные о продолжении просмотра при возникновении следующих событий:
- Первый вход: когда пользователь входит в систему в первый раз, публикуйте данные, чтобы история его просмотров сразу же стала доступна.
- Создание или переключение профилей (приложения с несколькими профилями): если ваше приложение поддерживает несколько профилей, публикуйте данные, когда пользователь создает или переключает профили.
- Прерывание воспроизведения видео: чтобы помочь пользователям продолжить просмотр с того места, где они остановились, публикуйте данные при приостановке или остановке воспроизведения видео, а также при закрытии приложения во время воспроизведения.
- Обновления панели «Продолжить просмотр» (если поддерживается): когда пользователь удаляет элемент из панели «Продолжить просмотр», отразите это изменение, опубликовав обновленные данные.
- Завершение видео:
- Для фильмов удалите завершённый фильм из списка «Продолжить просмотр». Если фильм входит в серию, добавьте следующий фильм, чтобы поддерживать интерес пользователя.
- Для эпизодов удалите завершенный эпизод и добавьте следующий эпизод серии, если он доступен, чтобы стимулировать дальнейший просмотр.
Интеграция
Профиль учетной записи
Чтобы включить персонализированный режим «продолжения просмотра» на Google TV, предоставьте информацию об учётной записи и профиле. Используйте AccountProfile, чтобы предоставить:
Идентификатор учётной записи: уникальный идентификатор, представляющий учётную запись пользователя в вашем приложении. Это может быть фактический идентификатор учётной записи или его соответствующим образом замаскированная версия.
Идентификатор профиля (необязательно): если ваше приложение поддерживает несколько профилей в рамках одной учетной записи, укажите уникальный идентификатор для конкретного профиля пользователя (опять же, реальный или замаскированный).
// 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
отвечает за публикацию кластера Continuation. Для публикации объекта ContinuationCluster
используется метод publishContinuationCluster()
.
Во-первых, следует использовать 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%-ное увеличение количества кликов «продолжить просмотр», поскольку его контент отображался на нескольких устройствах.
Удалить данные обнаружения видео
Чтобы вручную удалить данные пользователя с сервера Google TV до истечения стандартного 60-дневного срока хранения, используйте метод 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 публикации убедитесь, что ваши данные публикуются корректно, проверив приложение проверки. Кластер продолжения должен отображаться отдельной строкой в интерфейсе приложения.
- Установите флаг Engage Service только для непроизводственных сборок в файле манифеста Android вашего приложения.
- Установите и откройте приложение Engage Verify
- Если
isServiceAvailable
имеетfalse
, нажмите кнопку «Переключить», чтобы включить. - Введите имя пакета вашего приложения, чтобы автоматически просматривать опубликованные данные после начала публикации.
- Протестируйте эти действия в своем приложении:
- Войти.
- Переключение между профилями (если применимо).
- Запустите, а затем поставьте видео на паузу или вернитесь на домашнюю страницу.
- Закройте приложение во время воспроизведения видео.
- Удалить элемент из строки «Продолжить просмотр» (если поддерживается).
- После каждого действия подтверждайте, что ваше приложение вызвало API
publishContinuationClusters
и что данные правильно отображаются в приложении проверки. Приложение для проверки покажет зеленую галочку «Все хорошо» для правильно реализованных объектов.
Рисунок 1. Успешное прохождение проверки приложения Приложение для проверки отметит все проблемные объекты.
Рисунок 2. Ошибка приложения проверки Чтобы устранить неполадки в объектах с ошибками, выберите объект в приложении проверки с помощью пульта телевизора и нажмите на него. Конкретные проблемы будут отображены и выделены красным цветом для вашего обзора (см. пример ниже).
Рисунок 3. Подробности ошибки приложения проверки
REST API
Engage SDK предлагает REST API для обеспечения стабильного непрерывного просмотра на платформах, отличных от Android, таких как iOS и Roku TV. API позволяет разработчикам обновлять статус «Продолжаю просмотр» для зарегистрированных пользователей с платформ, отличных от Android.
Предпосылки
- Сначала необходимо завершить интеграцию на устройстве с помощью Engage SDK . Этот важный шаг устанавливает необходимую связь между идентификатором пользователя Google и
AccountProfile
вашего приложения. - Доступ к API и аутентификация: Чтобы просмотреть и включить API в вашем проекте Google Cloud, необходимо пройти процедуру добавления в разрешённый список. Все запросы к API требуют аутентификации.
Получение доступа
Чтобы получить доступ к просмотру и включению API в Google Cloud Console, ваша учетная запись должна быть зарегистрирована.
- Идентификатор клиента Google Workspace должен быть доступен. Если он отсутствует, вам может потребоваться настроить Google Workspace, а также все учётные записи Google, которые вы хотите использовать для вызова API.
- Настройте учетную запись в Google Cloud Console, используя адрес электронной почты, связанный с Google Workspace.
- Создать новый проект
- Создайте учётную запись службы для аутентификации API. После создания учётной записи службы вам будут доступны два элемента:
- Идентификатор учетной записи службы.
- JSON-файл с ключом вашей учётной записи сервиса. Сохраните этот файл в безопасности: он понадобится вам позже для аутентификации клиента в API.
- Workspace и связанные с ним учётные записи Google теперь могут использовать REST API. После вступления изменений в силу вы получите уведомление о готовности API к вызову вашими учётными записями служб.
- Выполните следующие шаги для подготовки к выполнению делегированного вызова API.
Опубликовать продолжение кластера
Чтобы опубликовать данные Video Discovery, выполните запрос POST к API publishContinuationCluster
, используя следующий синтаксис.
https://tvvideodiscovery.googleapis.com/v1/packages/{package_name}/accounts/{account_id}/profiles/{profile_id}/publishContinuationCluster
Где:
-
package_name
: Имя пакета поставщика медиа -
accountId
: уникальный идентификатор учётной записи пользователя в вашей системе. Он должен совпадать сaccountId
указанным в пути на устройстве. -
profileId
: уникальный идентификатор профиля пользователя в учётной записи вашей системы. Он должен совпадать с profileId, указанным в пути на устройстве.
URL-адрес учетной записи без профиля:
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
, а также общие и специфичные для типа поля.
В следующем фрагменте кода показана полезная нагрузка тела запроса для API publishContinuationCluster
.
{
"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"
}
}
]
}
Удалить данные обнаружения видео
Используйте API clearClusters
для удаления данных обнаружения видео.
Используйте POST URL для удаления сущностей из данных обнаружения видео. Чтобы удалить данные кластера продолжения, выполните POST-запрос к API clearClusters
, используя следующий синтаксис.
https://tvvideodiscovery.googleapis.com/v1/packages/{package_name}/accounts/{account_id}/profiles/{profile_id}/clearClusters
Где:
-
package_name
: Имя пакета поставщика медиа. -
accountId
: уникальный идентификатор учётной записи пользователя в вашей системе. Он должен совпадать сaccountId
указанным в пути на устройстве. -
profileId
: уникальный идентификатор профиля пользователя в учётной записи вашей системы. Он должен совпадать с profileId, указанным в пути на устройстве.
Полезная нагрузка для API clearClusters
содержит только одно поле, reason
, которое содержит DeleteReason
, указывающий причину удаления данных.
{
"reason": "DELETE_REASON_LOSS_OF_CONSENT"
}
Тестирование
После успешной публикации данных используйте тестовую учетную запись пользователя, чтобы убедиться, что ожидаемый контент отображается в строке «Продолжить просмотр» на целевых платформах Google, таких как Google TV и мобильные приложения Google TV для Android и iOS.
При тестировании допускайте разумную задержку распространения сигнала в несколько минут и соблюдайте требования к просмотру, например, досмотрите часть фильма или досмотрите серию до конца. Подробнее см. в руководстве Watch Next для разработчиков приложений .