as

Settings
Sign out
Notifications
Alexa
Amazonアプリストア
AWS
ドキュメント
Support
Contact Us
My Cases
開発
設計と開発
公開
リファレンス
サポート

手順3: EPG同期タスクを定義する

手順3: EPG同期タスクを定義する

次のファイルをアプリパッケージに追加し、最新のEPGデータをシステムにプッシュするビジネスロジックを定義します。詳細なガイダンスについては、EPG統合のベストプラクティスを参照してください。

アプリログを検索することによって手順を検証できます。

クリップボードにコピーしました。

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

手順1: アプリのタスクを定義する

srcフォルダにEpgSyncTask.tsファイルを作成して、アプリのEPG同期タスクを定義します。以下のスケルトンコードをチャンネルの統合ロジックとして使用できます。

クリップボードにコピーしました。

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

const getLatestChannelLineupVersion = () => {
  // TODO: バックエンドから最新のチャンネルラインナップのバージョンを照会します。
  return '1.0';
};

// 「progressCallback」パラメーターは、統合の進捗状況のフィードバックを
// 提供するために使用されます。これは、後で追加するチャンネル提供元の同期の
// 統合で使用されます。
export const ingestChannelLineup = async (progressCallback?: (percent: number) => void): Promise<void> => {
  try {
    // 手順1: 最新のEPGチャンネルラインナップのバージョンを
    // EpgLineupInformation.getLastCommittedChannelLineupVersion()と比較します。
    const lastCommitedVersion =
      await EpgLineupInformation.getLastCommittedChannelLineupVersion();
    const latestVersion = getLatestChannelLineupVersion();

    if (lastCommitedVersion === latestVersion) {
      // チャンネルラインナップに変更がないため、更新の必要はありません。
      console.info(
        `最新のチャンネルラインナップのバージョン(${latestVersion})が最後にコミットされたバージョン(${lastCommitedVersion})と同じです。同期をスキップします。`,
      );
      return;
    }

    // 手順2: バージョンが一致しない場合は、ユーザーに視聴権限があるチャンネル
    // ラインナップを小さいページに分けてダウンロードし、ペイロードを段階的に解析して、
    // ChannelLineupProviderインターフェイスを使用してラインナップを設定します。
    const channels_page_1 = getChannelData(1, 10);
    await ChannelLineupProvider.add(channels_page_1);
    if (progressCallback) {
      // TODO: EPG同期の進行状況の割合を計算します。
      progressCallback(25);
    }

    // TODO: 上記と同じ方法で残りのチャンネルを追加します。
    if (progressCallback) {
      // TODO: EPG同期の進行状況の割合を計算します。
      progressCallback(50);
    }

    // 手順3: ChannelLineupProvider.commitを使用してチャンネルラインナップをコミットします。
    await ChannelLineupProvider.commit(latestVersion);
  } catch (error) {
    if (error instanceof InvalidArgumentError) {
      // TODO: エラーメッセージをキャッチし、バックエンドにプッシュして、問題をすばやく解決します。
      // エラーメッセージには、失敗した挿入の合計数と、失敗した最初の5つのチャンネルの理由が含まれます。
      console.error(
        `EpgSync - InvalidArgumentErrorが原因でチャンネルラインナップの統合に失敗しました:${error}`,
      );
    } else {
      // TODO: エラーをログに記録します。Amazonの担当者と協力してエラーを修正できるように、これらのエラーをバックエンドに伝達することも検討してください。
      console.error(
        `EpgSync - チャンネルランナップの統合中にエラーが発生しました:${error}`,
      );
    }
    throw error;
  }
};

const getChannelData = (start_num: number, end_num: number): IChannelInfo[] => {
  // TODO: 実際のチャンネルデータを追加します。
  const channels_list: IChannelInfo[] = [];
  for (let i = start_num; i <= end_num; i++) {
    const identifier = `チャンネル${i}`;
    const name = `チャンネル${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タスクを開始します。');
  await ingestChannelLineup();

  // EPG同期タスクで番組データを提供する場合は、番組ラインナップの統合を追加します。
  // ProgramLineupProviderを使用して番組を更新する前に、必要に応じて新しいチャンネルラインナップのcommitを実行します。
  // チャンネルと番組の更新を交互に行うことはできません。
  // await ingestProgramLineup();

  // ライブイベントを提供する場合は、ライブイベントの統合を追加します。
  // await ingestLiveEventLineup();

  // チャンネルと番組のラインナップの統合中にエラーがスローされなければ、
  // EPG同期タスクは正常に完了したことになります。
  console.info('EpgSyncが正常に完了しました。');
};

手順2: EPG同期タスクに番組データを提供する

EPG同期タスクに番組データを提供するには、以下のスケルトンコードを番組の統合ロジックとして使用できます。カタログをAmazonカタログサービスGracenoteなどのクラウドサービスと統合している場合は、これを実行する必要はありません。

クリップボードにコピーしました。

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

const getLatestProgramLineupVersion = () => {
  // TODO: バックエンドから最新の番組ラインナップのバージョンを照会します。
  return '1.0';
};

// 「progressCallback」パラメーターは、統合の進捗状況のフィードバックを
// 提供するために使用されます。これは、後で追加するチャンネル提供元の同期の
// 統合で使用されます。
export const ingestProgramLineup = async (progressCallback?: (percent: number) => void): Promise<void> => {
  try {
    // 手順1: 最新のEPG番組ラインナップのバージョンを
    // EpgLineupInformation.getLastCommittedProgramLineupVersion()と比較します。
    const lastCommitedVersion =
      await EpgLineupInformation.getLastCommittedProgramLineupVersion();
    const latestVersion = getLatestProgramLineupVersion();

    if (lastCommitedVersion === latestVersion) {
      // 最後にコミットされた番組ラインナップに変更がないため、更新の必要はありません。
      console.info(
        `最新の番組ラインナップのバージョン(${latestVersion})が最後にコミットされたバージョン(${lastCommitedVersion})と同じです。同期をスキップします。`,
      );
      return;
    }

    // 任意の手順: 誤って追加された番組を電子番組表(EPG)から削除する必要がある場合に限り、
    // clearAllPrograms()を使用します。これによってデータストアからチャンネル情報が消去されることはありません。
    // clearAllPrograms()関数呼び出しは、同じトランザクション内でupsert()を呼び出す前に実行する必要があります。
    // ProgramLineupProviderのアクションは、commit()が呼び出されるまで永続化されません。

    // 手順2: バージョンが一致しない場合は、番組ラインナップを小さいページに分けて
    // 分けてダウンロードし、段階的に解釈して、ProgramLineupProviderインターフェイスを
    // 使用してラインナップを設定します。

    // 番組のChannelDescriptorに対応するチャンネルがコミットされていない場合、
    // ProgramLineupProvider2.upsert操作は失敗します。
    const programs_page_1 = getProgramData(1, 10);
    const upsertProgramFailures_page_1 = await ProgramLineupProvider2.upsert(
      programs_page_1,
    );
    if (upsertProgramFailures_page_1.length > 0) {
      // TODO: 失敗した番組の情報をすべてキャッチし、バックエンドにプッシュして、番組データの問題をすばやく解決します。
      console.error(
        `EpgSync - 1ページ目でアップサートに失敗した番組が${upsertProgramFailures_page_1.length}個あります。`,
      );
      processUpsertProgramFailures(upsertProgramFailures_page_1);
      // TODO: 残りの番組のアップサートを続けるか、番組の統合プロセスを中止するかを選択できます。
      // throw Error(
      //   '1ページ目の番組データが無効なため、番組の統合プロセスを中止します',
      // );
    }
    if (progressCallback) {
      // TODO: EPG同期の進行状況の割合を計算します。
      progressCallback(75);
    }

    // TODO: 上記と同じ方法で残りの番組をアップサートします。

    if (progressCallback) {
      // TODO: EPG同期の進行状況の割合を計算します。
      progressCallback(100);
    }

    // 手順3: ProgramLineupProvider2.commitを使用して番組ラインナップをコミットします。
    const total_program_failures =
      upsertProgramFailures_page_1.length + upsertProgramFailures_page_2.length;

    console.info(
      `EpgSync - エラーのあった番組の合計数:${total_program_failures}`,
    );

    // TODO: いずれかの番組のアップサートに失敗し、それらのエラーを受け取ったときにプロセスを中止しなかった場合は、
    // 正常にアップサートされた番組のlatestVersionを更新し、失敗した番組については情報をバックエンドに送信して、
    // 次回の同期までに修正できます。
    await ProgramLineupProvider2.commit(latestVersion);
  } catch (error) {
    // TODO: エラーをログに記録します。Amazonの担当者と協力してエラーを修正できるように、これらのエラーをバックエンドに伝達することも検討してください。
    console.error(`EpgSync - 番組ランナップの統合中にエラーが発生しました:${error}`);
    throw error;
  }
};

const getProgramData = (
  channel_start_num: number,
  channel_end_num: number,
): IProgram[] => {
  // TODO: 実際の番組データを追加します。以下のサンプルコードでは、現在の時刻から始まるダミーの番組を作成します。
  const programs_list: IProgram[] = [];
  const current_time = Date.now();

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

    // 最初の番組は現在のシステム時刻の45分前から始まります。
    var temp_time = current_time - 45 * 60 * 1000;
    // 各チャンネルに50個の番組を追加します。
    for (let j = 1; j <= 50; j++) {
      const start_time = temp_time;
      const end_time = start_time + 60 * 60 * 1000; // 60分をミリ秒で表した値
      const program_title = `番組${j}`;
      const program = new ProgramBuilder()
        .identifier(`番組${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: 失敗した番組の情報をログに追加し、バックエンドにアップロードして、無効なデータをすばやく修正します。
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で、IDが${program_id}の番組(所属するチャンネルID:${channel.identifier})のアップサートに失敗しました。エラーメッセージ:${err}`,
    );
  });
};

手順3: ライブイベントデータを提供する

ライブイベントを提供する場合は、まずライブイベントについてを参照してください。その後、以下のスケルトンコードをライブイベントの統合ロジックとして使用できます。

クリップボードにコピーしました。

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: バックエンドから最新のライブイベントラインナップのバージョンを照会します。
  return '1.0';
};

const ingestLiveEventLineup = async (): Promise<void> => {
  try {
    // 手順1: 最新のEPGライブイベントラインナップのバージョンを
    //   EpgLineupInformation.getLastCommittedLiveEventLineupVersion()と比較します。
    const lastCommitedVersion =
      await EpgLineupInformation.getLastCommittedLiveEventLineupVersion();
    const latestVersion = getLatestLiveEventLineupVersion();

    if (lastCommitedVersion === latestVersion) {
      // 最後にコミットされたライブイベントに変更がないため、更新の必要はありません。
      console.info(
        `最新のライブイベントラインナップのバージョン(${latestVersion})が最後にコミットされたバージョン(${lastCommitedVersion})と同じです。同期をスキップします。`,
      );
      return;
    }

    // 手順2: バージョンが一致しない場合は、ライブイベントラインナップを小さいページに
    // 分けてダウンロードし、段階的に解釈して、LiveEventProviderインターフェイスを
    // 使用してラインナップを設定します。
    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: 失敗したライブイベントの情報をすべてキャッチし、バックエンドにプッシュして、ライブイベントデータの問題をすばやく解決します。
      console.error(
        `EpgSync - 1ページ目から追加できなかったライブイベントが${addLiveEventFailures_page_1.length}個あります`,
      );
      processAddLiveEventFailures(addLiveEventFailures_page_1);
      // TODO: 残りのライブイベントの追加を続けるか、ライブイベントの統合プロセスを中止するかを選択できます。
      // throw Error(
      //   '1ページ目のライブイベントデータが無効なため、ライブイベントの統合プロセスを中止します',
      // );
    }

    // TODO: 上記と同じ方法で残りのライブイベントを追加します。

    // 手順3: LiveEventProvider.commitを使用してライブイベントラインナップをコミットします。
    const total_live_event_failures =
      addLiveEventFailures_page_1.length + addLiveEventFailures_page_2.length;

    console.info(
      `EpgSync - エラーのあったライブイベントの合計数:${total_live_event_failures}`,
    );

    // TODO: いずれかのライブイベントの追加に失敗し、それらのエラーを受け取ったときにプロセスを中止しなかった場合は、
    // 正常に追加されたライブイベントのlatestVersionを更新し、失敗したライブイベントについては情報をバックエンドに
    // 送信して、次回の同期までに修正できます。
    await LiveEventProvider.commit(latestVersion);
  } catch (error) {
    // TODO: エラーをログに記録します。Amazonの担当者と協力してエラーを修正できるように、これらのエラーをバックエンドに伝達することも検討してください。
    console.error(`EpgSync - ライブイベントラインナップの統合中にエラーが発生しました:${error}`);
    throw error;
  }
};

const getLiveEventData = (start_num: number, end_num: number): ILiveEvent[] => {
  // TODO: 実際のライブイベントデータを追加します。
  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('タイトル')
      .build();
    live_events_list.push(live_event);
  }
  return live_events_list;
};

// TODO: 失敗したライブイベントの情報をログに追加し、バックエンドにアップロードして、無効なデータをすばやく修正します。
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でIDが${live_event_id}のライブイベントの追加に失敗しました。エラーメッセージ:${err}`,
    );
  });
};

次は、指定されたガイドラインに従ってEPG同期タスクにビジネスロジックを実装します。


Last updated: 2025年9月30日