ステップ1-概要
Infinite Toolkit sandbox は、クライアントアプリにTVチャンネル、ビデオオンデマンド ( VOD ) アセットやユーザ設定についての整合性のある情報にアクセスさせるクラウドインフラストラクチャーです。
REF APIは、TVチャンネル、ビデオオンデマンド ( VOD ) アセット、レコードの管理とユーザ設定の修正について、Infinite Video Platform ( IVP ) からTVクライアントアプリをメタデータとビデオコンテンツにアクセスさせる Infinite Toolkit ( ITK ) から提供されたコマンド一式です。
このAPIは様々なフレームワーク ( HTML5、JavaScript、Angular JS、RDK ) を簡単に構築し、タブレット、スマートフォンラップトップ、セットトップボックス ( STB ) 向けのマルチデバイス指向です。
備考: IVP ソリューションの完全なプレゼンテーションは、こちらからご確認いただけます。
https://developer.cisco.com/site/infinite-toolkit/
備考:予約は一つのイベントに関連するトリガーです。( もしくは、一連の複数のエピソードです。) イベントの開始時にタイマーが起動すると、
ビデオコンテンツが CDVR 機能によってサーバ再度で録画されます。
( CDVR = Cloud Digital Video Recorder )
一旦ビデオが録画されると、ユーザはそのビデオコンテンツを再生できます。
10オブジェクト
このラボの目的は、 ITK 初心者がイベントに関連した予約の実行とAngular JS 1.5サンプルアプリケーションを使ったビデオコンテンツの再生を行うことができるようにすることです。
前提条件
このラボを始める前に、下記の項目が必要です
【必須】:'Start using ITK Sandbox and discover REF-API'ラボをまず最初に完了する
ラボ101を達成したあと、全てのの sandboxユーザはREF-API内で全ての定義された API を使うことができる。
【オプション】:次に 'Play video content with REF-API' ラボを完了する
ラボ102を達成すると、全ての sandbox user は全てののブラウザ上で実行中の HTML5 アプリケーションを使い、プレイセッションの管理とビデオコンテンツをみることができます。
【必須】 REF-API 機能
この API の特徴は、こちらでご確認いただけます。
https://anypoint.mulesoft.com/apiplatform/apx/#/portals
【必須】:Angular 1.5 JS skill
Infinite EPG アプリケーションが Angular JS でどのように動くのか理解するためにいくつかの基礎知識が必要です。こちらに便利なリンクがあります。
https://docs.angularjs.org/guide/ ( Angular portal )
https://docs.angularjs.org/tutorial/index ( official tutorial )
ステップ2-REF API でイベントを予約する。
sandbox portal でREF-API のドキュメンテーションと特徴をご確認いただけます。
こちらの利用可能なリストから、sandbox のバージョンを選択してください。
https://anypoint.mulesoft.com/apiplatform/apx/#/portals
備考:最新の sandbox は "IVP Toolkit - Client REF.API" "sandbox_0.4.1" ですが、新しいバージョンがすぐにリリースされます。
ITK にいる場合、Eメールで新しいバージョンのリリースが通知されます。
sandbox ポータルを開くと、メインページの左側に sandbox から提供されたメイン機能についての情報をご確認いただけます。
このラボのために、CDNR Use Cases セクションをご一読ください。
外部コンテンツインスタンスIDを管理する
繰り返し予約の管理
単一予約の管理
トランスコード予約の管理
トランスコードの繰り返し予約の管理
リマインダー予約の管理
ハウスホールドディスククオータ
API Reference セクションに遷移し、/agg/library/bookingse エンドポイントを見つける。
このエンドポイントは予約の管理を行います。
/agg/library/bookings セクション内で、ユーザは下記のモードを使うことができます。
このエンドポイント上の GET リクエストは全ての予約をリストしています。
このエンドポイント上の POST リクエストは新しい予約を作ります。
/agg/library/bookings/{bookingId} セクションの存在する予約は下記のモードを提供していることに注意してください。
/agg/library/bookings/{bookingId}上の DELETE リクエストは、特定の予約を削除します。
/agg/library/bookings/{bookingId}上の GET リクエストは、特定の予約のステータスを返します。
CDVR 機能は全ての sandbox 上に必須ではないか、利用不可能にすることができます。
この機能がアクティブでない場合、予約と録画 の再生は実行することができません。
CDVR ( CDVR= Cloud Digital Video Recorder )
備考:予約としてとは、”サーバ側で TV イベントのレコードをするタイマーをセットする”ということで、それは生中継のTVイベントを探す方法を理解するのと同じです。
ステップ3-グリッドを表示する
予約機能は、ライブTVイベント上で実行され、ライブ TV イベントを見つける良い方法は /agg/grid エンドポイント上のGTE リクエストでグリッドを表示することです。
API リファレンスでこの API を見つけ、”POSTボタン”を押下し、下記のページにアクセスします。
/agg/grid が成功し、もしそのクエリパラメータが正しくセットされていない場合は、どれが大きなものになるか返します。
我々はチャンネル毎に返ってきたイベントの数とチャンネル数を制限することをお勧めします。
効果的なリクエストは下記のようになります。
https://apx.cisco.com/spvss/infinitehome/ivptoolkit/clientrefapi/sandbox_0.4.1/agg/grid?eventsLimit=5&limit=10&offset=0
スタンダード JSON の答えは下記のようです。
{
"count": 5,
"total": 5,
"channels": [
{
"schedule": [
{
"id": "uri:prg:EP012174000104~uri:scd:50747:EP012174000104:27042017140000~ltv",
"resource": "contentInstance",
"source": "ltv",
"type": "event",
"contentType": "standalone",
"title": "Cupcake Wars",
"startDateTime": "2017-04-27T14:00:00.000Z",
"contentFlags": [
"recordableWithoutInteractive"
],
"duration": 3600000,
"content": {
"id": "uri:prg:EP012174000104~uri:scd:50747:EP012174000104:27042017140000~ltv",
"resource": "content",
"type": "standalone",
"contentType": "standalone",
"title": "Cupcake Wars",
"media": [
{
"type": "None",
"mimeType": "image/jpg",
"url": "http://csco.tmsimg.com/assets/p7954198_i_h13_ab.jpg",
"height": 270,
"width": 480
}
],
"credits": [],
"duration": 3600000,
"synopsis": {
"shortSynopsis": "A party for music icon Taylor Dayne."
},
"isErotic": false,
"parentalRating": {
"name": "no mapping",
"value": 0
},
"_links": {
"self": {
"href": "/content/uri%3Aprg%3AEP012174000104"
}
}
},
"_links": {
"self": {
"href": "/contentInstances/uri%3Aprg%3AEP012174000104~uri%3Ascd%3A50747%3AEP012174000104%3A27042017140000~ltv"
}
}
},
{
"id": "uri:prg:EP012174000115~uri:scd:50747:EP012174000115:27042017150000~ltv",
"resource": "contentInstance",
"source": "ltv",
"type": "event",
"contentType": "standalone",
"title": "Cupcake Wars",
"startDateTime": "2017-04-27T15:00:00.000Z",
"contentFlags": [
"recordableWithoutInteractive"
],
"duration": 3600000,
"content": {
"id": "uri:prg:EP012174000115~uri:scd:50747:EP012174000115:27042017150000~ltv",
"resource": "content",
"type": "standalone",
"contentType": "standalone",
"title": "Cupcake Wars",
"media": [
{
"type": "None",
"mimeType": "image/jpg",
"url": "http://csco.tmsimg.com/assets/p10250009_e_h13_aa.jpg",
"height": 270,
"width": 480
}
],
"credits": [],
"duration": 3600000,
"synopsis": {
"shortSynopsis": "\"The Miss America Pageant\" party."
},
"isErotic": false,
"parentalRating": {
"name": "no mapping",
"value": 0
},
"_links": {
"self": {
"href": "/content/uri%3Aprg%3AEP012174000115"
}
}
},
"_links": {
"self": {
"href": "/contentInstances/uri%3Aprg%3AEP012174000115~uri%3Ascd%3A50747%3AEP012174000115%3A27042017150000~ltv"
}
}
},
{
"id": "uri:prg:EP012174000085~uri:scd:50747:EP012174000085:27042017160000~ltv",
"resource": "contentInstance",
"source": "ltv",
"type": "event",
"contentType": "standalone",
"title": "Cupcake Wars",
"startDateTime": "2017-04-27T16:00:00.000Z",
"contentFlags": [
"recordableWithoutInteractive"
],
"duration": 3600000,
"content": {
"id": "uri:prg:EP012174000085~uri:scd:50747:EP012174000085:27042017160000~ltv",
"resource": "content",
"type": "standalone",
"contentType": "standalone",
"title": "Cupcake Wars",
"media": [
{
"type": "None",
"mimeType": "image/jpg",
"url": "http://csco.tmsimg.com/assets/p7954198_i_h13_ab.jpg",
"height": 270,
"width": 480
}
],
"credits": [],
"duration": 3600000,
"synopsis": {
"shortSynopsis": "A huge celebration honoring the country's best cheerleaders."
},
"isErotic": false,
"parentalRating": {
"name": "no mapping",
"value": 0
},
"_links": {
"self": {
"href": "/content/uri%3Aprg%3AEP012174000085"
}
}
},
"_links": {
"self": {
"href": "/contentInstances/uri%3Aprg%3AEP012174000085~uri%3Ascd%3A50747%3AEP012174000085%3A27042017160000~ltv"
}
}
},
{
"id": "uri:prg:EP014328710138~uri:scd:50747:EP014328710138:27042017170000~ltv",
"resource": "contentInstance",
"source": "ltv",
"type": "event",
"contentType": "standalone",
"title": "The Pioneer Woman",
"startDateTime": "2017-04-27T17:00:00.000Z",
"contentFlags": [
"recordableWithoutInteractive"
],
"duration": 1800000,
"content": {
"id": "uri:prg:EP014328710138~uri:scd:50747:EP014328710138:27042017170000~ltv",
"resource": "content",
"type": "standalone",
"contentType": "standalone",
"title": "The Pioneer Woman",
"media": [
{
"type": "None",
"mimeType": "image/jpg",
"url": "http://csco.tmsimg.com/assets/p12047508_e_h13_aa.jpg",
"height": 270,
"width": 480
}
],
"credits": [],
"duration": 1800000,
"synopsis": {
"shortSynopsis": "Bartlesville cream pie; lollipops; lemon bread pudding."
},
"isErotic": false,
"parentalRating": {
"name": "no mapping",
"va0lue": 0
},
"_links": {
"self": {
"href": "/content/uri%3Aprg%3AEP014328710138"
}
}
},
"_links": {
"self": {
"href": "/contentInstances/uri%3Aprg%3AEP014328710138~uri%3Ascd%3A50747%3AEP014328710138%3A27042017170000~ltv"
}
}
},
{
"id": "uri:prg:EP014328710079~uri:scd:50747:EP014328710079:27042017173000~ltv",
"resource": "contentInstance",
"source": "ltv",
"type": "event",
"contentType": "standalone",
"title": "The Pioneer Woman",
"startDateTime": "2017-04-27T17:30:00.000Z",
"contentFlags": [
"recordableWithoutInteractive"
],
"duration": 1800000,
"content": {
"id": "uri:prg:EP014328710079~uri:scd:50747:EP014328710079:27042017173000~ltv",
"resource": "content",
"type": "standalone",
"contentType": "standalone",
"title": "The Pioneer Woman",
"media": [
{
"type": "None",
"mimeType": "image/jpg",
"url": "http://csco.tmsimg.com/assets/p10511574_e_h13_ad.jpg",
"height": 270,
"width": 480
}
],
"credits": [],
"duration": 1800000,
"synopsis": {
"shortSynopsis": "Mexican hot chocolate; Eggbert's Sunrisers; orange muffins."
},
"isErotic": false,
"parentalRating": {
"name": "no mapping",
"value": 0
},
"_links": {
"self": {
"href": "/content/uri%3Aprg%3AEP014328710079"
}
}
},
"_links": {
"self": {
"href": "/contentInstances/uri%3Aprg%3AEP014328710079~uri%3Ascd%3A50747%3AEP014328710079%3A27042017173000~ltv"
}
}
}
],
"id": "150747",
"name": "FOODHD",
"logicalChannelNumber": 91,
"isFavorite": false,
"media": [
{
"type": "regular",
"mimeType": "image/jpg",
"url": "http://origin.ztv.systems/logo/foodhd.jpg"
}
]
},
}
答えはスケジュールのアレーを含むチャンネルのアレーです。
下記のチャンネルメタデータはレスポンスの一部として提供されます。
Channel ID
Channel name
Logical channel number
isFavorite
チャンネルロゴ
下記のスケジュールメタデータは、レスポンスの一部として提供されます。
schedule ID
schedule title
schedule start time
source ltv" is expected)
Type ("event" is expected)
Duration
Event pooster
Remark:
備考:もうひとつのライブTVイベントを取得する方法は、 /agg/content もしくは /contentInstances を使うことです。
with filter source=ltv.
ステップ4-予約を作成する
このステージでは、予約をする必要があるライブTVイベントを見つけることができます。
/agg/library/bookings エンドポイント上のPOSTリクエストのために必要なクエリパラメータを確認しましょう。
”POST”ボタンを押下し、下記のページにアクセスしてください。
備考: POST リクエストの場合を理解することは重要です。クエリパラメータはボディリクエストに追加されます。
{
"contentInstanceId": "CIID_234234",
"bookingType": "event",
"conflictDetectOption": "none",
"targetDevices": [
"cDVR"
]
}
予約の作成が成功したときに、下記のような JSON の答えが返ってきます。
{
"id": "BID_32525234",
"bookingType": "event",
"contentInstanceId": "CIID_234234",
"recordingState": "notStarted",
"recordingContentState": "empty",
"targetDevices": [
"cDVR"
],
"created": "2015-10-05T14:30:00Z",
"lastModified": "2015-10-10T08:11:00Z",
"_links": {
"plannerEvent": {
"href": "/contentInstances/CIID_234234"
}
}
}
下記の予約のメタデータは、答えの一部として提供されます。
Booking ID
Booking Type (ライブTVイベント用のイベント)
Recording State ("notStarted", "inProgress", "Completed" もしくは "Failed")
ターゲットデバイス
Creation Date
/contentInstances エンドポイントを通して予約の詳細にアクセスするリンク
ステップ5-予約を取得する
このステージでは、我々は、前のステップでいくつかの予約を作成したことを仮定しています。
/agg/library/bookings エンドポイント上でGETリクエストのために必要なクエリパラメータをチェックしましょう。
GET ボタンを押下し、下記のページにアクセスしてください。
予約リストが空でないときに、下記のような JSON の答えが返ってきます。
”count”は、答えの中に返ってきた予約の数です。
”total”は、利用可能な予約の総数です。
"bookings[]"は予約のアレーです。
{
"count": 4,
"total": 4,
"bookings": [
{
"bookingType": "event",
"contentInstanceId": "CIID_325232",
"recordingState": "inProgress",
"recordingContentState": "partial",
"targetDevices": [
"cDVR"
],
"id": "BID_1234",
"created": "2015-10-05T14:30:00Z",
"lastModified": "2015-10-10T08:11:00Z",
"_links": {
"plannerEvent": {
"href": "/contentInstances/CIID_325232"
},
"playSession": {
"href": "/devices/me/playSessions?instanceId=CIID_325232{&playPosition}",
"templated": true,
"method": "POST"
}
}
},
{
"bookingType": "event",
"contentInstanceId": "CIID_324532",
"recordingState": "notStarted",
"recordingContentState": "empty",
"targetDevices": [
"cDVR"
],
"id": "BID_1235",
"created": "2015-05-10T10:12:00Z",
"lastModified": "2015-10-10T11:12:00Z",
"_links": {
"plannerEvent": {
"href": "/contentInstances/CIID_324532"
}
}
},
{
"bookingType": "event",
"contentInstanceId": "CIID_324682",
"recordingState": "ended",
"recordingContentState": "complete",
"targetDevices": [
"cDVR"
],
"id": "BID_1236",
"created": "2015-05-10T10:10:00Z",
"lastModified": "2015-10-10T11:11:00Z",
"_links": {
"plannerEvent": {
"href": "/contentInstances/CIID_324682"
},
"playSession": {
"href": "/devices/me/playSessions?instanceId=CIID_324682{&playPosition}",
"templated": true,
"method": "POST"
}
}
},
{
"bookingType": "season",
"contentInstanceId": "CIID_324685",
"recordingState": "ended",
"recordingContentState": "complete",
"targetDevices": [
"cDVR"
],
"id": "BID_1238",
"created": "2015-06-10T10:10:00Z",
"lastModified": "2015-10-10T11:11:00Z",
"_links": {
"plannerEvent": {
"href": "/contentInstances/CIID_324682"
},
"episodeBookings": {
"href": "/agg/library/bookings&bookingType=event&seasonId=IID_321&offset=0&limit=10{&sort}",
"templated": true,
"method": "POST"
}
}
}
],
"locators": {
"start": "abcd1234efgh",
"end": "zxy321"
},
"_links": {
"self": {
"href": "/agg/library/bookings&offset=0&limit=10{&sort}",
"templated": true
},
"page": {
"href": "/agg/library/bookings&offset=0&limit=10{&sort}",
"templated": true
}
}
}
下記の予約のメタデータは、答えの一部として提供されます。
Booking ID
Booking Content ID
Booking Type (ライブ TV イベント用のイベント)
Recording State ("notStarted", "inProgress", "Completed" もしくは "Failed")
Target Devices ("cDVR")
Creation Date
/contentInstances エンドポイントを通じて予約詳細にアクセスする plannerEvent リンク
チップ:もしご自身が既に /agg/library/bookings/{BookingId} エンドポイント上の BookingId を取得している場合、同様の情報は特定の予約にアクセスできます。
ステップ6-プランナー API を使う。
完全な詳細の予約を取得し情報を登録するもう一つの方法は、/agg/library/planner エンドポイントです。
”GET” ボタンを押下して下記のページにアクセスしてください。
プランナーリストが空でない時に、下記のような JSON が返ってきます。
”count” は回答の中に返ってきたコンテンツの数です。
”total” は、利用可能なコンテンツの総数です。
”content[]”は、コンテンツのアレーです。
{
"count": 10,
"total": 183,
"content": [
{
"id": "CIID_435132",
"resource": "contentInstance",
"source": "ltv",
"expirationDateTime": "2015-09-20T00:00:00Z",
"content": {
"id": "IID_65900",
"resource": "content",
"contentType": "standalone",
"title": "Spider-Man 2",
"isAdult:": false,
"isErotic": false,
"genres": [
{
"name": "action"
},
{
"name": "adventure"
}
],
"synopsis": {
"short": "Peter Parker is beset with troubles in his failing personal life as he battles a brilliant scientist named Doctor Otto Octavius."
},
"duration": 127,
"isEntitled": false,
"media": [
{
"mimeType": "image/jpeg",
"url": "http://img.server.com/65900.jpg",
"height": 300,
"width": 200
}
],
"_links": {
"self": {
"href": "/content/IID_65900"
}
}
},
"_links": {
"self": {
"href": "/contentInstances/CIID_435132"
},
"eventBooking": {
"href": "/agg/library/bookings/BID_7545453"
},
"seasonBooking": {
"href": "/agg/library/bookings/BID_8765432"
}
}
},
{
"id": "CIID_6343234",
"resource": "contentInstance",
"source": "vod",
"content": {
"id": "IID_45668",
"resource": "content",
"contentType": "episode",
"title": "Pilot",
"isAdult:": false,
"isErotic": false,
"genres": [
{
"name": "drama"
},
{
"name": "crime"
}
],
"synopsis": {
"short": "FBI Agent Booth needs forensic anthropologist Dr. Brennan's help to solve the case of a decomposed body found
in a pond in Arlington National Cemetery. She agrees to help if she is a full partner in the case."
},
"duration": 42,
"isEntitled": true,
"media": [
{
"mimeType": "image/jpeg",
"url": "http://img.server.com/45668.jpg",
"height": 300,
"width": 200
}
],
"seasonNumber": "1",
"episodeNumber": "1",
"_links": {
"self": {
"href": "/content/IID_45668"
},
"season": {
"href": "/content/IID_543234"
},
"show": {
"href": "/content/IID_193923"
}
}
},
"_links": {
"self": {
"href": "/contentInstances/CIID_6343234"
},
"eventBooking": {
"href": "/agg/library/bookings/BID_2342342"
}
}
}
],
"locators": {
"start": "abcd1234efgh",
"end": "zxy321"
},
"_links": {
"self": {
"href": "/agg/content?categoryId=12345&offset=0&limit=10{&sort}",
"templated": true
},
"page": {
"href": "/agg/content?categoryId=12345&sort=+title{&offset,limit,locator}",
"templated": true
}
}
}
下記のコンテンツメタデータは、回答の一部として提供されます。
Content ID
Title
Synopsis
Genre
Poster
Duration
Content type ("event", "episode", ...)
isAdult, isErotic flags
Creation Date
チップ:もしご自身が既に /agg/library/bookings/{BookingId} エンドポイント上の BookingId を取得している場合、同様の情報は特定の予約にアクセスできます。
ステップ7-'Infinite EPG' サンプルアップ ( Angular JS ) を使う
7.1 Infinite EPG Sample App をインストールする。
ローカルリポジトリでGITからソースコードファイルをコピーする
git clone git@github3.cisco.com:InfiniteToolkit/Infinite-Toolkit-RefAPI-TestApp.git
サブディレクトリの "Infinite-Toolkit-RefAPI-TestApp" に遷移する
nmp を実行し、全てのパッケージが必要な 'NodeJS' をインストールする。
npm start を実行し、メインプロセスを実行する。
ご自身のブラウザから ”infinite EPG” にアクセスする (I.E., Chrome, Firefox, Safari, ..;)
次のURLで : http://localhost:3000
チップ:ソースコードの修正をせずに Infinite EPG で再生することに興味があるだけの場合は、
こちらにアクセスできます。
http://sample-apps.spvss-infinitetoolkit.ciscolabs.com/epg/#/
ご自身のお好きなブラウザを下記の URL で開始することで Infinite EPG アプリを開始します。
URL : http://localhost:3000
次のページにアクセスするために ”Infinite Platform にログインする” を選択するより、ランディングページをするほかにありません。
認証ページでは、下記の情報を入力する必要があります。
- CLient ID : xxxxxxxxxxxx
- Client Secret : yyyyyyyyyyyy
- Press "Log In" button
Client ID と Client Secret は、Mulesoft アプリケーションクリエーションで取得します。
Mulesoft アプリを作成するためのインストラクションはラボ101で取得できます。
存在するアプリはこちらで利用可能です。
https://anypoint.mulesoft.com/apiplatform/apx/#/apps
もしクレデンシャルが、正しく入力されていなかった場合は、下記のエラーメッセージが表示されます。
もし、クレデンシャルが正しい場合、メインページは表示されます。
Infinite EPG メインページからユーザは REF-API エントリーポイントによって全ての提案されたメイン機能にアクセスすることができます。
ボタン エンドポイント ディスクリプション
チャンネル /channels は、チャンネルリストとそれらのロゴを表示します。
/agg/grid は TV グリッド、チャンネルと時間順に表示します。 グリッド
/agg/content はTVイベント、VODアセットとPVRレコードの集約されたコンテンツを表示します。 コンテンツ
/agg/categories はカテゴリ毎にソートされたVODストアを表示します。 カテゴリー
/agg/library/bookings は、TVイベントスケジュールに対応する予約を表示します。 予約
/household はハウスホールドセッティングを表示します。 ハウスホールド
/platfroms ハウスホールドセッティングを表示します。 プラットフォーム
ユーザプロファイル/セッティングは、UI 言語、audi language 用のユーザプリファレンスを表示します。
ステップ8-”Infinite EPG” でイベントを予約する
メインページのグリッドボタンを選択する。
下記のクエリの結果
https://apx.cisco.com/spvss/infinitehome/ivptoolkit/clientrefapi/sandbox_0.4.1/agg/grid?eventsLimit=5&limit=10&offset=0
はリストの最初の10のチャンネルから最初の5つのイベントのリストをリストする
最初の画面は名前とロジカルナンバーでチャンネルリストを表示するだけです。
注意:"Show Raw JSON" はもし、/agg/grid リクエストに返ってきた JSON で toggle することを許可できます。
もし、ユーザがこれらのチャンネルのひとつを選択する場合、サブパネルはそのチャンネルの最初の5つを表示します。
もし、ユーザがこれらのイベント上で選択した場合、サブパネルはこのイベントとこのイベントの予約を許可する ”Event Booking” ボタンに関連する詳細情報を表示します。
もしリクエストが確認できたら、メッセージが表示されます。
もし何かしらの理由で、予約が失敗した場合、エラーメッセージがさらに詳細な情報を知らせます。
注意:予約機能は、いくつかのチャンネルのみ許可することができ、その他は拒否することができます。
しかし、その情報を取得する API はありません。
現在の sandbox 0.4.1では、イベントを予約するチャンネルはありません。
Infinite EPG メインページに遷移し、”Bookings” オプションを選択し、現在の予約を表示してください。
コメント
0件のコメント
サインインしてコメントを残してください。