as

Settings
Sign out
Notifications
Alexa
Amazonアプリストア
AWS
ドキュメント
Support
Contact Us
My Cases
開発
設計と開発
公開
リファレンス
サポート
アクセスいただきありがとうございます。こちらのページは現在英語のみのご用意となっております。順次日本語化を進めてまいりますので、ご理解のほどよろしくお願いいたします。

Step 3: Define EPG Sync Task

Add the following files to your app package and define your business logic to push the latest EPG data into the system. See EPG Ingestion Best Practices for more guidance.

You can validate the steps by searching your app logs:

Copied to clipboard.

  loggingctl log --follow | grep "epg:\|ktf:\|ktf.\|EpgSync"

Step 1: Define your app's task

Define your app’s EPG Sync Task by creating a EpgSyncTask.ts file in your src folder. You can use the following skeleton code for your channel ingestion logic.

Copied to clipboard.

import {
ChannelDescriptorBuilder,
ChannelInfoBuilder,
ChannelLineupProvider,
ChannelMetadataBuilder,
EpgLineupInformation,
ExternalIdBuilder,
IChannelInfo,
IExternalId,
IProgram,
InvalidArgumentError,
} from '@amazon-devices/kepler-epg-provider';

const getLatestChannelLineupVersion = () => {
  // TODO: Query your latest channel lineup version from your backend.
  return '1.0';
};

// The 'progressCallback' parameter is used to provide feedback on the 
// progress of your ingestion and is used in the Sync Source integration
// you will add later on.
export const ingestChannelLineup = async (progressCallback?: (percent: number) => void): Promise<void> => {
  try {
    // Step 1: Compare your latest EPG Channel lineup version against
    //   EpgLineupInformation.getLastCommittedChannelLineupVersion()
    const lastCommitedVersion =
      await EpgLineupInformation.getLastCommittedChannelLineupVersion();
    const latestVersion = getLatestChannelLineupVersion();

    if (lastCommitedVersion === latestVersion) {
      // No changes to the Channel Lineup, so no need to update
      console.info(
        `Latest Channel Lineup version: ${latestVersion} same as last commited version: ${lastCommitedVersion}, skipping sync..`,
      );
      return;
    }

    // Step 2: If the version is mismatched, download the customer's entitled
    // Channel Lineup in small pages, incrementally parse the payload,
    // and set the lineup using ChannelLineupProvider interface.
    const channels_page_1 = getChannelData(1, 10);
    await ChannelLineupProvider.add(channels_page_1);
    if (progressCallback) {
      // TODO: Calculate percent progress of EPG Sync
      progressCallback(25);
    }

    // TODO: Add the remaining channels using the same method as above.
    if (progressCallback) {
      // TODO: Calculate percent progress of EPG Sync
      progressCallback(50);
    }

    // Step 3: Commit the Channel Lineup using ChannelLineupProvider.commit
    await ChannelLineupProvider.commit(latestVersion);
  } catch (error) {
    if (error instanceof InvalidArgumentError) {
      // TODO: Catch the error message and push it to your backend to resolve issues quickly.
      // The error message includes the total number of failed insertions and the reasons for the first 5 failed channels.
      console.error(
        `EpgSync - Channel lineup ingestion failed due to InvalidArgumentError: ${error}`,
      );
    } else {
      // TODO: Log these errors. Also consider propagating these errors to your backend so you can work with your Amazon contact to fix these errors.
      console.error(
        `EpgSync - Error during channel lineup ingestion: ${error}`,
      );
    }
    throw error;
  }
};

const getChannelData = (start_num: number, end_num: number): IChannelInfo[] => {
  // TODO: Add real channel data.
  const channels_list: IChannelInfo[] = [];
  for (let i = start_num; i <= end_num; i++) {
    const identifier = `Channel${i}`;
    const name = `Channel ${i}`;
    const descriptor = new ChannelDescriptorBuilder()
      .identifier(identifier)
      .majorNumber(0)
      .minorNumber(0)
      .build();
    const external_id_list: IExternalId[] = [];
    let external_id = new ExternalIdBuilder()
      .idType('type')
      .value('value')
      .build();
    external_id_list.push(external_id);
    const metadata = new ChannelMetadataBuilder()
      .name(name)
      .channelType(3)
      .externalIdList(external_id_list)
      .build();
    const channel = new ChannelInfoBuilder()
      .channelDescriptor(descriptor)
      .channelMetadata(metadata)
      .build();
    channels_list.push(channel);
  }

  return channels_list;
};

const doTask = async (): Promise<void> => {
  console.info('EpgSync task starting...');
  await ingestChannelLineup();

  // Add Program Lineup ingestion, if you provide program data in the EPG Sync Task.
  // Commit a new channel lineup (if needed) prior to updating programs/using ProgramLineupProvider.
  // Channel and programs updates cannot be interleaved.
  // await ingestProgramLineup();

  // Add Live Event ingestion, if you offer live events.
  // await ingestLiveEventLineup();

  // If no error was thrown during channel and program lineup ingestion,
  // then epg sync up task completed successfully.
  console.info('EPGSync completed successfully!');
};

Step 2: Provide program data in the EPG Sync Task

To provide programs data in the EPG Sync Task, the following is the skeleton code for your program ingestion logic. You do not need to do this if you have integrated your catalog with a cloud service such as Amazon Catalog Service or Gracenote.

Copied to clipboard.

import {
  IProgram,
  IUpsertProgramFailure,
  ProgramBuilder,
  ProgramLineupProvider2,
} from '@amazon-devices/kepler-epg-provider';

const getLatestProgramLineupVersion = () => {
  // TODO: Query your latest program lineup version from your backend.
  return '1.0';
};

// The 'progressCallback' parameter is used to provide feedback on the 
// progress of your ingestion and is used in the Sync Source integration
// you will add later on.
export const ingestProgramLineup = async (progressCallback?: (percent: number) => void): Promise<void> => {
  try {
    // Step 1: Compare your latest EPG Program lineup version against
    //   EpgLineupInformation.getLastCommittedProgramLineupVersion()
    const lastCommitedVersion =
      await EpgLineupInformation.getLastCommittedProgramLineupVersion();
    const latestVersion = getLatestProgramLineupVersion();

    if (lastCommitedVersion === latestVersion) {
      // No changes to the last commited programs, no update required
      console.info(
        `Latest Program Lineup version: ${latestVersion} same as last commited version: ${lastCommitedVersion}, skipping sync..`,
      );
      return;
    }

    // Optional step: Utilize 'clearAllPrograms()' exclusively when there is a need to remove mistakenly
    // added programs from the the Electronic Program Guide (EPG). This will not clear channel information from the data store.
    // This function call 'clearAllPrograms()' must precede calls to 'upsert()' within the same transaction.
    // ProgramLineupProvider actions are not persisted until 'commit()' is called.

    // Step 2: If the version is mismatched, download your Program Lineup in
    // small pages, incrementally parse, and set the lineup using
    // ProgramLineupProvider interface.

    // The ProgramLineupProvider2.upsert operation will fail if the channel corresponding to
    // the program’s ChannelDescriptor has not been committed.
    const programs_page_1 = getProgramData(1, 10);
    const upsertProgramFailures_page_1 = await ProgramLineupProvider2.upsert(
      programs_page_1,
    );
    if (upsertProgramFailures_page_1.length > 0) {
      // TODO: Catch all the failed programs information and push it to your backend to quickly resolve the issues with the program data.
      console.error(
        `EpgSync - there are ${upsertProgramFailures_page_1.length} programs from page 1 which failed to be upserted`,
      );
      processUpsertProgramFailures(upsertProgramFailures_page_1);
      // TODO: You can choose to continue upserting the remaining programs or abort the program ingestion process.
      // throw Error(
      //   'Abort the program ingestion process due to invalid program data in page 1',
      // );
    }
    if (progressCallback) {
      // TODO: Calculate percent progress of EPG Sync
      progressCallback(75);
    }

    // TODO: Upsert the remaining programs using the same method as above.

    if (progressCallback) {
      // TODO: Calculate percent progress of EPG Sync
      progressCallback(100);
    }

    // Step 3: Commit the Program Lineup using ProgramLineupProvider2.commit
    const total_program_failures =
      upsertProgramFailures_page_1.length + upsertProgramFailures_page_2.length;

    console.info(
      `EpgSync - total number of errored programs ${total_program_failures}`,
    );

    // TODO: If any programs failed to upsert, and you did not abort the process when receiving those failure errors,
    // then you can update the latestVersion for the successfully upserted programs and send the information about
    // the failed programs back to your backend so they can be fixed before the next sync.
    await ProgramLineupProvider2.commit(latestVersion);
  } catch (error) {
    // TODO: Log these errors. Also consider propagating these errors to your backend so you can work with your Amazon contact to fix these errors.
    console.error(`EpgSync - Error during program lineup ingestion: ${error}`);
    throw error;
  }
};

const getProgramData = (
  channel_start_num: number,
  channel_end_num: number,
): IProgram[] => {
  // TODO: Add real program data. The sample code below creates dummy programs starting from the current time.
  const programs_list: IProgram[] = [];
  const current_time = Date.now();

  for (let i = channel_start_num; i <= channel_end_num; i++) {
    const identifier = `Channel${i}`;
    const descriptor = new ChannelDescriptorBuilder()
      .identifier(identifier)
      .majorNumber(0)
      .minorNumber(0)
      .build();

    // The first program starts 45 mins earlier than current system time.
    var temp_time = current_time - 45 * 60 * 1000;
    // Add 50 programs for each channel
    for (let j = 1; j <= 50; j++) {
      const start_time = temp_time;
      const end_time = start_time + 60 * 60 * 1000; // 60 minutes in milliseconds
      const program_title = `Program ${j}`;
      const program = new ProgramBuilder()
        .identifier(`Program${j}`)
        .channelDescriptor(descriptor)
        .title(program_title)
        .startTimeMs(start_time)
        .endTimeMs(end_time)
        .build();
      programs_list.push(program);
      temp_time = end_time;
    }
  }

  return programs_list;
};

// TODO: Add the failed programs' information in the log and upload to your backend to fix the invalid data quickly.
const processUpsertProgramFailures = (
  upsertProgramFailures: IUpsertProgramFailure[],
): void => {
  upsertProgramFailures.forEach((element: IUpsertProgramFailure) => {
    const program = element.program;
    const program_id = program.identifier;
    const channel = element.program.channelDescriptor;
    const err =
      element.error.message === undefined ? '' : element.error.message;
    console.error(
      `EpgSync failed to upsert program with id ${program_id} which belongs to channel id ${channel.identifier} with error message ${err}`,
    );
  });
};

Step 3: For live events

If you offer live events, first read Get Started with Live Events and then see the following skeleton code for your live event ingestion logic.

Copied to clipboard.

import {
EventType,
IAddLiveEventFailure,
ILiveEvent,
LiveEventBuilder,
LiveEventProvider,
PlaybackReferenceBuilder,
PlaybackType,
} from '@amazon-devices/kepler-epg-provider';
import { MediaId } from '@amazon-devices/kepler-media-types';

const getLatestLiveEventLineupVersion = () => {
  // TODO: Query your latest live event lineup version from your backend.
  return '1.0';
};

const ingestLiveEventLineup = async (): Promise<void> => {
  try {
    // Step 1: Compare your latest EPG Live Event lineup version against
    //   EpgLineupInformation.getLastCommittedLiveEventLineupVersion()
    const lastCommitedVersion =
      await EpgLineupInformation.getLastCommittedLiveEventLineupVersion();
    const latestVersion = getLatestLiveEventLineupVersion();

    if (lastCommitedVersion === latestVersion) {
      // No changes to the last commited live events, no update required
      console.info(
        `Latest Live Event Lineup version: ${latestVersion} same as last commited version: ${lastCommitedVersion}, skipping sync..`,
      );
      return;
    }

    // Step 2: If the version is mismatched, download your Live Event Lineup in
    // small pages, incrementally parse, and set the lineup using
    // LiveEventProvider interface.
    const live_events_page_1 = getLiveEventData(1, 10);
    const addLiveEventFailures_page_1 = await LiveEventProvider.add(
      live_events_page_1,
    );
    if (addLiveEventFailures_page_1.length > 0) {
      // TODO: Catch all the failed live event information and push it to your backend to quickly resolve the issues with the live event data.
      console.error(
        `EpgSync - there are ${addLiveEventFailures_page_1.length} live events from page 1 which failed to be added`,
      );
      processAddLiveEventFailures(addLiveEventFailures_page_1);
      // TODO: You can choose to continue adding the remaining live events or abort the live event ingestion process.
      // throw Error(
      //   'Abort the live event ingestion process due to invalid live event data in page 1',
      // );
    }

    // TODO: Add the remaining live events using the same method as above.

    // Step 3: Commit the Live Event Lineup using LiveEventProvider.commit
    const total_live_event_failures =
      addLiveEventFailures_page_1.length + addLiveEventFailures_page_2.length;

    console.info(
      `EpgSync - total number of errored live events ${total_live_event_failures}`,
    );

    // TODO: If any live events failed to add, and you did not abort the process when receiving those failure errors,
    // then you can update the latestVersion for the successfully added live events and send the information about
    // the failed live events back to your backend so they can be fixed before the next sync.
    await LiveEventProvider.commit(latestVersion);
  } catch (error) {
    // TODO: Log these errors. Also consider propagating these errors to your backend so you can work with your Amazon contact to fix these errors.
    console.error(`EpgSync - Error during live event lineup ingestion: ${error}`);
    throw error;
  }
};

const getLiveEventData = (start_num: number, end_num: number): ILiveEvent[] => {
  // TODO: Add real live event data.
  const live_events_list: ILiveEvent[] = [];
  for (let i = start_num; i <= end_num; i++) {
    const playbackReference = new PlaybackReferenceBuilder()
      .playbackType(PlaybackType.CONTENT)
      .mediaId(new MediaId('contentId', 'catalogName'))
      .build();
    const start_time = Date.now() - 45 * 60 * 1000;
    const end_time = start_time + 60 * 60 * 1000; 
    const live_event = new LiveEventBuilder()
      .identifier(`LiveEvent${i}`)
      .eventType(EventType.SCHEDULED_EVENT)
      .playbackReference(playbackReference)
      .startTimeMs(start_time)
      .endTimeMs(end_time)
      .title('title')
      .build();
    live_events_list.push(live_event);
  }
  return live_events_list;
};

// TODO: Add the failed live events' information in the log and upload to your backend to fix the invalid data quickly.
const processAddLiveEventFailures = (
  addLiveEventFailures: IAddLiveEventFailure[],
): void => {
  addLiveEventFailures.forEach((element: IAddLiveEventFailure) => {
    const live_event = element.liveEvent;
    const live_event_id = live_event.identifier;
    const err =
      element.error.message === undefined ? '' : element.error.message;
    console.error(
      `EpgSync failed to add live event with id ${live_event_id} with error message ${err}`,
    );
  });
};

Next, implement your business logic in the EPG Sync task while following the specified guidelines.


Last updated: Sep 30, 2025