as

Settings
Sign out
Notifications
Alexa
亚马逊应用商店
AWS
文档
Support
Contact Us
My Cases
新手入门
设计和开发
应用发布
参考
支持

开始使用Vega内容个性化

开始使用Vega内容个性化

要将Vega内容个性化集成到您的Fire TV应用中,您需要了解以下事项。如果您想查看用于此集成的某些数据类型,请参阅数据类型参考

如果数据无法访问或需要转换,则此集成可能需要更长时间。参考以下文档,确认系统是否以请求格式存储您的数据。

先决条件

  • 访问您的Fire TV应用的源代码。
  • 支持此集成的Fire TV设备。请咨询您的亚马逊联系人,获取当前支持的设备类型列表。
  • 必须在目录引入流程中加入您的应用,以便Fire TV识别内容ID。
  • 您的应用必须为每位客户共享权利,由此作为亚马逊内容发现体验的一部分,Fire TV会显示授权提供方。有关更多信息,请联系您的亚马逊联系人。
  • 您的应用必须完成内容启动器集成

集成步骤

步骤1:在应用中包含程序包依赖项

package.json文件中添加kepler-content-personalizationheadless-task-manager,以及amzn/kepler-epg-provider

已复制到剪贴板。

"dependencies": {
    "@amazon-devices/react-native-kepler": "~2.0.0",
    "react": "18.2.0",
    "react-native": "0.72.0",
    "@amazon-devices/kepler-content-personalization": "^1.2.0",
    "@amazon-devices/kepler-epg-provider": "^1.0.0",
    "@amazon-devices/headless-task-manager": "^1.0.0"
  }
  • kepler-content-personalization程序包会提供用于将内容个性化数据发送到系统的API。
  • amzn/kepler-epg-provider程序包会提供用于PlaybackEvent数据模型中channelDescriptor的依赖项。
  • headless-task-manager程序包提供用于在系统中注册数据拉取后台服务的API。更多详情如下所示。

步骤2:更新您的清单文件

更新manifest.toml文件,添加对内容个性化的支持。以下是示例应用的示例:

已复制到剪贴板。

schema-version = 1

## 定义您的程序包
[package]
title = "Vega视频应用"
version = "2.17.0"
id = "com.amazondeveloper.keplervideoapp"

[components]
## 定义应用的交互组件(如果尚不存在)
[[components.interactive]]
id = "com.amazondeveloper.keplervideoapp.main"
library-name = "com.amazondeveloper.keplervideoapp"
runtime-module = "/com.amazon.kepler.keplerscript.runtime.loader_2@IKeplerScript_2_0"
categories = ["com.amazon.category.kva"]
launch-type = "singleton"

## 定义可以处理数据请求的应用的服务组件。
[[components.service]]
id = "com.amazondeveloper.keplervideoapp.content.dataRefresh.provider"
runtime-module = "/com.amazon.kepler.headless.runtime.loader_2@IKeplerScript_2_0"
launch-type = "singleton"
categories = ["com.amazon.category.kepler.media"]

## 为每个组件定义一个进程组
[processes]
[[processes.group]]
component-ids = ["com.amazondeveloper.keplervideoapp.main"]

[[processes.group]]
component-ids = ["com.amazondeveloper.keplervideoapp.content.dataRefresh.provider"]

[wants]
## 定义应用包含对内容个性化数据服务的依赖项
[[wants.service]]
id = "com.amazon.tv.developer.dataservice"

[needs]
## 定义应用使用内容个性化接口以提供数据所需的权限
[[needs.privilege]]
id = "com.amazon.tv.content-personalization.privilege.provide-data"

[offers]
## 定义应用的数据刷新服务组件
[[offers.service]]
id = "com.amazondeveloper.keplervideoapp.content.dataRefresh.provider"

## 定义应用的交互组件
[[offers.interaction]]
id = "com.amazondeveloper.keplervideoapp.main"

## 添加额外信息,声明支持内容个性化
[[extras]]
key = "interface.provider"
## 如果您使用账户登录或内容启动器接口,
## 使用相应的服务组件替换组件ID。遵照账户登录集成指南
component-id = "com.amazondeveloper.keplervideoapp.content.dataRefresh.provider"

## 定义对内容个性化接口和属性的支持
[[extras.value.application.interface]]
interface_name = "com.amazon.kepler.media.IContentPersonalizationServer"
attribute_options = ["SupportedCustomerLists", "DataRefreshComponentId"]

[extras.value.application.interface.static_values]
SupportedCustomerLists = ["Watchlist"]
DataRefreshComponentId = "com.amazondeveloper.keplervideoapp.content.dataRefresh.provider"

以下是定义多个接口的正确方法示例。

已复制到剪贴板。

[extras.value.application]
[[extras.value.application.interface]]
interface_name = "com.amazon.kepler.media.IContentPersonalizationServer"
attribute_options = ["SupportedCustomerLists", "DataRefreshComponentId"]

[extras.value.application.interface.static_values]
SupportedCustomerLists = ["Watchlist"]
DataRefreshComponentId = "com.amazondeveloper.keplervideoapp.content.dataRefresh.provider"

[[extras.value.application.interface]]
interface_name = "com.amazon.kepler.media.IContentLauncherServer"
override_command_component = { LaunchContent = "com.amazondeveloper.keplervideoapp.main" }
attribute_options = ["partner-id"]

[extras.value.application.interface.static_values]
partner-id = "<合作伙伴ID>"

下面是一种不正确的方式。请不要这样做。

已复制到剪贴板。

[extras.value.application]
[[extras.value.application.interface]]
interface_name = "com.amazon.kepler.media.IContentPersonalizationServer"
attribute_options = ["SupportedCustomerLists", "DataRefreshComponentId"]

[[extras.value.application.interface]]
interface_name = "com.amazon.kepler.media.IContentLauncherServer"
override_command_component = { LaunchContent = "com.amazondeveloper.keplervideoapp.main" }
attribute_options = ["partner-id"]

[extras.value.application.interface.static_values]
partner-id = "<合作伙伴ID>"
SupportedCustomerLists = ["Watchlist"]
DataRefreshComponentId = "com.amazondeveloper.keplervideoapp.content.dataRefresh.provider"

步骤3:进行示例API调用

在应用启动时生成的示例/模拟事件开始。要构建并发送事件,请使用以下代码:

已复制到剪贴板。

// 用户开始观看内容时生成的示例播放事件

const playbackEvent: IPlaybackEvent = new PlaybackEventBuilder()
    .playbackPositionMs(0)
    .playbackState(PlaybackState.PLAYING)
    .durationMs(2000)
    .eventTimestamp(new Date())
    .contentId(
    new ContentIdBuilder()
        .id('content_CDF_ID')
        .idNamespace(ContentIdNamespaces.NAMESPACE_CDF_ID)
        .build(),
    )
    .profileId(
    new ProfileIdBuilder()
        .id('myProfileId')
        .idNamespace(ProfileIdNamespaces.NAMESPACE_APP_INTERNAL)
        .build(),
    )
    .buildActiveEvent();

// 发送事件
ContentPersonalizationServer.reportNewPlaybackEvent(playbackEvent);

步骤4:验证集成

触发您构建的示例事件代码以在应用中运行。成功运行代码后,查看日志以验证SDK已链接到您的应用并且正在处理消息。

可以通过搜索应用日志来验证这些步骤:

已复制到剪贴板。

journalctl --follow |grep -Ei 'kepler.tv.personalization'

收到的有关步骤3中所示事件的日志消息至少应包含以下日志之一:

已复制到剪贴板。

Dec 15 00:53:15.705114 carat-3fc19fb5ae526e4d local0.info keplerscript-ru[22814]: 1483317178 INFO kepler.tv.personalization.ancp.turbomodule:reportNewPlaybackEvent called
Dec 15 00:53:15.711945 carat-3fc19fb5ae526e4d local0.info keplerscript-ru[22814]: 1483317178 INFO kepler.tv.personalization:PlaybackEventBuilder buildActiveEvent() called
Dec 15 00:53:15.711990 carat-3fc19fb5ae526e4d local0.info keplerscript-ru[22814]: 1483317178 INFO kepler.tv.personalization:reportNewPlaybackEvent call received

步骤5:将API调用作为应用内功能的一部分

对于每种数据类型,请查看数据类型的“何时发送”部分,以了解您需要在代码中的哪个位置调用Vega内容个性化。查找客户执行每项操作时运行的代码的相关部分,然后添加API调用。每种数据类型的触发器各不相同。

例如,当客户将项目添加到他们的观看列表时,您应通过相关信息调用reportNewCustomerListEntry API。首先在您的应用中找到将项目添加到观看列表的代码,然后将API调用作为您逻辑中的组成部分来进行。

步骤6:实现面向后台或设备外数据的数据拉取服务

要允许亚马逊从您的应用拉取数据,请按如下所述实现该服务。该服务包含了设置和连接所需的一切,您只需实现向接收器对象发送数据的函数。此对象允许您根据需要以数据块的形式共享数据。这样可以防止将大型列表加载到内存中。

  1. package.json所在的同一路径上创建一个包含以下内容的service.js文件。这样可以提醒系统注意您的content.dataRefresh.provider服务入口点的相关信息。

    已复制到剪贴板。

    import { HeadlessEntryPointRegistry } from '@amazon-devices/headless-task-manager';
    
    import {
    onStartService,
    onStopService,
    } from './src/headless/HeadlessService';
    
    HeadlessEntryPointRegistry.registerHeadlessEntryPoint2(
    'com.amazondeveloper.keplervideoapp.content.dataRefresh.provider::onStartService',
    () => onStartService,
    );
    
    HeadlessEntryPointRegistry.registerHeadlessEntryPoint2(
    'com.amazondeveloper.keplervideoapp.content.dataRefresh.provider::onStopService',
    () => onStopService,
    );
    
    
  2. src/headless/HeadlessServiceInterface.ts下创建包含以下内容的无头服务接口文件:

    已复制到剪贴板。

    import {
    IComponentInstance,
    } from "@amazon-devices/react-native-kepler";
    
    export interface HeadlessServiceInterface {
    /**
     * 调用本机服务onStart时会调用此函数。
     * @param {IComponentInstance} componentInstance - 无头组件实例。
     */
    onStart(componentInstance: IComponentInstance): Promise<void>;
    
    /**
     * 调用本机服务onStop时会调用此函数。
     * @param {IComponentInstance} componentInstance - 无头组件实例。
     */
    onStop(componentInstance: IComponentInstance): Promise<void>;
    }
    
  3. src/headless/HeadlessService.ts下创建包含以下内容的数据拉取服务:

    已复制到剪贴板。

    import { ContentPersonalizationServer, CustomerListType, IContentEntitlementsHandler, IContentEntitlementsProvider, ICustomerListEntriesHandler, ICustomerListEntriesProvider, IPlaybackEventsHandler, IPlaybackEventsProvider } from "@amazon-devices/kepler-content-personalization";
    import { HeadlessServiceInterface } from "./HeadlessInterface";
    import { IComponentInstance } from "@amazon-devices/react-native-kepler";
    
    /*********** 内容个性化处理程序*************/
    const contentEntitlementsHandler: IContentEntitlementsHandler = {
       getAllContentEntitlements: (
          contentEntitlementsProvider: IContentEntitlementsProvider,
       ) => {
          contentEntitlementsProvider.addContentEntitlementChunk(<添加权利>);
          contentEntitlementsProvider.commit();
       },
    };
    
    const customerListEntriesHandler: ICustomerListEntriesHandler = {
       getAllCustomerListEntries: (
          listType: CustomerListType,
          customerListEntriesProvider: ICustomerListEntriesProvider,
       ) => {
          customerListEntriesProvider.addCustomerListChunk(listType, <添加列表条目>);
          customerListEntriesProvider.commit();
       },
    };
    
    const playbackEventsHandler: IPlaybackEventsHandler = {
       getPlaybackEventsSince: (
          sinceTimestamp: Date,
          playbackEventsProvider: IPlaybackEventsProvider,
       ) => {
          playbackEventsProvider.addPlaybackEventChunk(<添加播放事件>);
          playbackEventsProvider.commit();
       },
    };  
    
    /*********** 分配内容个性化处理程序 *************/
    class HeadlessService implements HeadlessServiceInterface {
       onStart(componentInstance: IComponentInstance): Promise<void> {
          ContentPersonalizationServer.setContentEntitlementsHandlerForComponent(
          contentEntitlementsHandler, componentInstance
          );
          ContentPersonalizationServer.setCustomerListEntriesHandlerForComponent(
          customerListEntriesHandler, componentInstance
          );
          ContentPersonalizationServer.setPlaybackEventsHandlerForComponent(
          playbackEventsHandler, componentInstance
          );
    
          return Promise.resolve();
       }
    
       onStop(componentInstance: IComponentInstance): Promise<void> {
          return Promise.resolve();
       }
    }
    
    const HeadlessServiceInstance = new HeadlessService() as HeadlessServiceInterface;
    
    export const onStartService = (componentInstance: IComponentInstance): Promise<void> => {
       return HeadlessServiceInstance.onStart(componentInstance);
    };
    
    export const onStopService = (componentInstance: IComponentInstance): Promise<void> => {
       return HeadlessServiceInstance.onStop(componentInstance);
    };
    
    

实现详情

更改您的目录集成(在您的亚马逊联系人指示如此操作时)

要使用权利和活动数据,Fire TV需要从现有Fire TV目录集成中获取以下数据。我们会在未来更新Fire TV的公共目录集成文档,在其中加入这些元素,您可在此一览有关Vega内容个性化集成的所需内容。您的亚马逊联系人将告知您这些目录更改的开始时间。

  • TVOD(购买和租赁)- 向适用作品添加新的购买和租赁优惠。这类优惠看起来与订阅优惠相同,但没有订阅ID。
  • 国际化 - 为了向其他国家/地区扩张,Fire TV将从按国家/地区划分的目录格式转变为Fire TV的全球目录格式。如果在您的全球目录中加入上述目录更改,并将这些更改作为全球目录的一部分发布,则可以更轻松地进行过渡。不过,Fire TV仍然支持按国家/地区划分的目录。

亚马逊内容ID

Vega内容个性化支持跨不同命名空间的内容识别。必须通过关联的命名空间传递所有内容ID,以便亚马逊识别内容项目。亚马逊支持以下命名空间:

命名空间 描述
cdf_id 这是ID空间,数据是在您的亚马逊目录集成中共享的。这些是您为每段内容指定的ID。它对应于目录数据格式 (CDF) 的CommonWorkType/ID字段

亚马逊个人资料ID

所有数据类型都允许您共享关联的个人资料ID。由于个人资料类型不同,请指定命名空间。亚马逊目前支持以下个人资料ID命名空间:

命名空间 描述
app_internal 此命名空间为字符串形式,表示您共享的有效个人资料的内部个人资料ID。此ID用于识别您应用中的特定个人资料,以便我们将活动归因于正确的Fire TV个人资料。请勿提供您在内部使用的实际ID。提供的值不应包含客户身份识别信息。我们建议使用您的内部个人资料ID的哈希值,该值在所有设备上都应相同。另外,请勿向我们发送客户提供的个人资料名称。即使您的应用不使用个人资料,也要提供一致的值。

报告更新

该SDK包含通过三类操作共享信息的功能:

  • New(示例:reportNewCustomerListEntry)- 表示在数据集中添加新条目的增量更新。
  • Removed(示例:reportRemovedCustomerListEntry)- 表示从数据集中删除条目的减量更新。
  • Refreshed(示例:reportRefreshedCustomerList)- 表示由于设备外操作,数据集发生了变化,需要使用数据拉取服务来获取经过更新的新版列表。

当客户与应用中的内容进行交互并执行添加或删除操作时,NewRemove非常有用。在由于客户登录、设备外活动导致列表发生重大变化,或者您怀疑列表由于其他原因而没有同步的情况下,Refreshed更新非常有用。

提供大型数据集

通过数据拉取服务提供大量数据时,从云后端拉取较小页面中的数据,并在拉取数据时调用提供方API。这样可以限制服务的内存使用量,并防止您的应用被系统终止。

示例流程如下所示:

已复制到剪贴板。

const contentEntitlementsHandler: IContentEntitlementsHandler = {
    getAllContentEntitlements: (
      provider: IContentEntitlementsProvider,
    ) => {
        let myCloudHasMoreData = true;
        while(myCloudHasMoreData){
            let myCloudResponse = getNextPageFromMyBackend(); 
            let myCloudData = myCloudResponse.getData();
            let entitlements: IContentEntitlements[] = [];
            for(let element in myCloudData){
                let entitlement = <使用ContentEntitlementBuilder生成数据>
                entitlements.push(entitlement)
            }
            provider.addContentEntitlementChunk(entitlements);

            myCloudHasMoreData = myCloudResponse.hasMoreData();
        }
        provider.commit();
    },
  };

Last updated: 2025年9月30日