> For the complete documentation index, see [llms.txt](https://catenoid-support.gitbook.io/kollus-dev-jp/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://catenoid-support.gitbook.io/kollus-dev-jp/callback-gai-yao/play-callback.md).

# Play Callback

### 概要 <a href="#playcallback-gai-yao" id="playcallback-gai-yao"></a>

Kollus Security Playerで再生する際に顧客側で指定したURLを呼出す(Callback)機能を説明します。Agentインストール型及びモバイルPlayerアプリ, Player SDKの使用が前提になります。

### Expire option <a href="#playcallback-expireoption" id="playcallback-expireoption"></a>

設定項目のdata-typeや値が範囲から外れた場合、エンドユーザーのコンテンツの利用に問題が発生する可能性があります。また、誤った設定値による使用回数の過剰などを回収する方法はありませんので設定に注意してください。

* Expire date : 再生有効期限
  * data-type : integer, unixtime stamp
  * コンテンツの有効期限(再生可能期限)
    * 終了日(日時)ex) 2014. 3. 3.  5時 45分 30秒 GMT → 1393825531
    * 0 : unlimited (無制限)
    * 最大値 : 2029年 12月 31日 23時 59分 59秒 (1893455999)

### Play callback <a href="#playcallback-playcallback" id="playcallback-playcallback"></a>

Play callbackを使用するにはチャンネル編集ページの運用ポリシー、Play Callback項目にCallbackを受け取るURLを事前に登録する必要があります。

チャンネルにPlay Callbackを指定するとこのチャンネルから再生される全てのコンテンツはCallbackに対したレスポンスが確認されてから再生されるため、Callback URLは常にレスポンスができる状態を維持する必要があります。

注意:

1. 顧客側のユーザID(サービスに登録された会員が特定できる情報)を含むメディアトークン(Media token)を生成してリクエストするとPlay Callbackが呼出されます。(メディアトークンの生成は別途文書を参考にしてください。)
2. Play Callback URLからレスポンスがないと再生されません。
3. Play Callbackはダウンロードされたコンテンツ(DRM Callback)とは動作が異なります。ダウンロードされたコンテンツはPlay Callbackではなく、DRM Callbackが適用されます。必要に応じてPlay CallbackとDRM Callbackに同一なURLを指定する場合、ストリーミングとダウンロードに対してのレスポンスを同時に受け取ることができます。

### Callback flow <a href="#playcallback-callbackflow" id="playcallback-callbackflow"></a>

<figure><img src="/files/84VEQMT2yCQCPwxWb4u9" alt=""><figcaption></figcaption></figure>

1. 配信チャンネルにPlay CallbackのURL設定します。
   * ex> <http://www.foo.com/auth.php> を顧客のPlay Callback サーバーとする
2. Media tokenを生成して再生(ダウンロード)をリクエストします。
   * Kollus crypt SDKを使ってMedia tokenを生成します。
3. Kollus Security playerが<http://www.foo.com/auth.php> に以下の情報をPOST転送します。
   * kind : 1, 3
   * client\_user\_id : ユーザID(サービス会員情報)
     * Media token 生成の際に含まれたID
   * player\_id : ユーザのデバイスID
   * device\_name : ユーザのデバイス名
   * media\_content\_key : 再生するコンテンツキー
     * チャンネルに登録されたコンテンツのメディアコンテンツキー(ユニーク)
     * 同じコンテンツを複数のチャンネルに登録する場合、それぞれのmedia\_content\_keyは全て異なります。
   * 顧客のPlay Callbackサーバーは転送された上記の情報に基づいて以下のjson フォーマットのdataを次の方式に合わせてHttp Bodyに転送します。(顧客が認証データをPlayerに転送する全てのデータは必ずinteger型で転送しなければなりません。)
     * 対応方式
       * Kollus crypt SDKで暗号化して転送
       * JWT Encodeして転送、アルゴリズムはHS256のみ対応しており、jsonフォーマットのdataをJWTのpayloadに追加してEncodingします。ヘッダーに指定された \*ユーザーキー(X-KOLLUs-USERKEY)"を共に転送します。secret keyはCallbackリクエストの際に転送するcustom\_keyパラメータ値を使用します。

### Response JSON spec. <a href="#playcallback-responsejsonspec." id="playcallback-responsejsonspec."></a>

| Json Tag         | Description                                                                                    |
| ---------------- | ---------------------------------------------------------------------------------------------- |
| expiration\_date | <p>unixtime stamp (有効期限終了日のunixtime stamp)<br>最大値 : 2029年 12月 31日 23時 59分 59秒 (1893455999)</p> |
| result           | 結果が正常の場合は1、異常の場合は0をリターンする。                                                                     |
| content\_expired | DRM コンテンツを強制的にexpireします。                                                                       |

### Callback Kind <a href="#playcallback-callbackkind" id="playcallback-callbackkind"></a>

Play Callbackが呼出される状況は以下の三つのケースになります。

#### **コンテンツのExpire情報をリクエストする場合 (kind:1)**

**Request**

| 区分                  | Description                                         |
| ------------------- | --------------------------------------------------- |
| POST                | Http POSTでリクエスト (parameterではない)                     |
| kind                | 1                                                   |
| client\_user\_id    | ユーザーID, media\_token 生成に使用されたclient\_user\_idと同一です。 |
| player\_id          | ユーザーのデバイスID                                         |
| hardware\_id        | デバイスのhardware ID (PC, 入力内容がある場合)                    |
| device\_name        | ユーザーデバイスのモデル名                                       |
| media\_content\_key | 再生するコンテンツのメディアコンテンツキー                               |
| uservalues          | JSON format (VideoGatewayの呼出に使用されたuservalue0\~9)    |

* uservalues sample
  * uservalues={"uservalue0":"商品種類コード01","uservalue1":"商品名コード02","uservalue9":"生成コード03"}
  * VideoGateway([v.jp.kollus.com](http://v.kr.kollus.com))の呼出に使用されたuservalue0\~9 情報が一緒にリターンされます。

**Response**

<table><thead><tr><th width="135.33333333333331">カテゴリ</th><th width="221">区分</th><th>Description</th></tr></thead><tbody><tr><td>Data</td><td>(int) expiration_date</td><td>有効期限終了日のunixtime stamp<br>最大値 : 2029年 12月 31日 23時 59分 59秒 (1893455999)</td></tr><tr><td><br></td><td>(int) result</td><td>0 (エラー), 1 (正常)</td></tr><tr><td><br></td><td>(string) message</td><td><p>0 (エラー)の場合、messageを追加すると状況に合うメッセージが表示されます。<br></p><ul><li>PCで再生する場合、再生URLにloadcheck=0 パラメータを追加する必要があります。<br>EX. <a href="http://v.kr.kollus.com/uyoeFDU9">http://v.jp.kollus.com/{</a>メディアコンテンツキー}?loadcheck=0</li></ul></td></tr><tr><td><br></td><td>(int) vmcheck</td><td>0 (使用しない), 1 (使用する, default) virtual machineの確認有無、PC(v3)のみ使用可能</td></tr><tr><td><br></td><td>(array)<br>play_section{<br>start_time,<br>end_time<br>}</td><td>プレビュー区間、秒単位で区分(end_timeをstart_timeより大きく指定)</td></tr><tr><td><br></td><td>(int) disable_tvout</td><td>0 (tvout 遮断なし), 1 (tvout 遮断) ここで指定しなかった場合、チャンネルポリシーのdisable_tvoutポリシーが適用されます。</td></tr><tr><td><br></td><td>(int)<br>expiration_playtime</td><td>空白または0の場合、再生時間が適用されません。0より大きい場合該当秒だけ再生後に終了します。</td></tr><tr><td>exp</td><td>(int)</td><td>使用可能期限 unixtime stamp(オプション)</td></tr></tbody></table>

**Example**

```
{
    “data” : {
        "expiration_date": 1402444800,
        "vmcheck": 1,
        "play_section": [
            “start_time”: 0,
            “end_time” : 60
        ],
        "disable_tvout": 1,
        "expiration_playtime": 1800,
        "result": 1
    },
    “exp” : 1477558242
}
```

#### **コンテンツを再生する場合 (kind:3)**

注意) Play Callbackに対したレスポンスが確認されてから再生が始まります。レスポンスがなければ再生できません。

**Request**

<table><thead><tr><th width="246">区分</th><th>Description</th></tr></thead><tbody><tr><td>POST</td><td>Http POSTでリクエスト (parameterではない)</td></tr><tr><td>kind</td><td>3</td></tr><tr><td>client_user_id</td><td>ユーザーID, media_token 生成に使用されたclient_user_idと同一です。</td></tr><tr><td>player_id</td><td>ユーザーのデバイスID</td></tr><tr><td>device_name</td><td>ユーザーデバイスのモデル名</td></tr><tr><td>media_content_key</td><td>再生するコンテンツのメディアコンテンツキー</td></tr><tr><td>uservalues</td><td>JSON format (VideoGatewayの呼出に使用されたuservalue0~9)</td></tr></tbody></table>

**Response**

<table><thead><tr><th width="136.33333333333331">カテゴリ</th><th width="231">区分</th><th>Description</th></tr></thead><tbody><tr><td>data</td><td>(int) content_expired</td><td>0 (再生可能), 1 (再生不可)<br>再生を遮断します。DRM Callbackと同一な仕様を維持するため同一項目で管理されます。</td></tr><tr><td><br></td><td>(int) result</td><td>0 (エラー), 1 (正常)<br>0の場合再生されません。たとえconent_expiredが1になっていてもexpireが適用されません。</td></tr><tr><td><br></td><td>(string) message</td><td><p>0 (エラー)の場合、またはcontent_expiredが1(再生不可)の場合にmessageを追加すると内容に合わせてメッセージが表示されます。<br></p><ul><li>PCで再生する場合、再生URLにloadcheck=0 パラメータを追加する必要があります。<br>EX. <a href="http://v.kr.kollus.com/uyoeFDU9">http://v.jp.kollus.com/{</a>メディアコンテンツキー}?loadcheck=0</li></ul></td></tr><tr><td>exp</td><td>(int) expiration_date</td><td>使用可能期限 unixtime stamp(オプション)<br>最大値 : 2029年 12月 31日 23時 59分 59秒 (1893455999)</td></tr></tbody></table>

**Example**

```
{
    “data” : {
        "content_expired": 1,
        "result": 1
    },
    "exp" : 1477558242
}
```

#### **Sample**

* Play callback url
  * <http://www.foo.com/auth.php>
* コンテンツ有効期限 : 2014年 6月 10日 24:00まで
  * DATE (M/D/Y @ h : m : s): 6 / 10 / 2014 @ 24:0:0 UTC
  * Unix time stamp : 1402444800
  * <http://www.epochconverter.com> から1402444800値を変更することができます。※UTC基準(GMTではない)

**kind1 : request expire option**

**request**

* URL : <http://www.foo.com/auth.php>
* post data
  * kind=1
  * client\_user\_id=guest1
  * media\_content\_key=VXBW1VdY
  * uservalues={"uservalue0":"商品種類コード01","uservalue1":"商品名コード02","uservalue9":"生成コード03"}

**response**

```
{
    “data” : {
        "expiration_date": 1402444800,
        "result": 1
    },
    "exp" : 1477558242
}
```

**kind3 : play content (expire content)**

**request**

* URL : <http://www.foo.com/auth.php>
* post data
  * kind=3
  * client\_user\_id=guest1
  * media\_content\_key=VXBW1VdY
  * uservalues={"uservalue0":"商品種類コード01","uservalue1":"商品名コード02","uservalue9":"生成コード03"}

**response**

```
{
    “data” : {
        "content_expired": 1,
        "result": 1
    },
    "exp" : 1477558242
}
```

**Code sample**

以下のように顧客のDBが構成されていると想定したサンプルコードになります。

<figure><img src="/files/syj7HRyoPGGgBAhw1kcW" alt=""><figcaption></figcaption></figure>

**PHP sample**

{% code fullWidth="true" %}

```
<?php
    /**
    * PHP Version : 5.4 above
    * by yupmin
    */
    include "./config.php";
    
    // 注意 : kindが1の場合、自動的にexpiration_dateが生成されるサンプルページとなります。
    // 再生制限の回数
    $_default_expiration_count = 3;
    $_expired_duration = 60 * 60 * 24; // 1 day
    // DB Connection
    $_db_conn = mysql_connect($_hostname, $_username, $_password);
    if (!$_db_conn) {
   		die('Could not connect: ' . mysql_error());
    }
    $_db_selected = mysql_select_db($_database, $_db_conn);
    if (!$_db_selected) {
    	die ('Can\'t use database : ' . mysql_error());
    }
    $_kind = isset($_POST['kind']) ? ((int) $_POST['kind']) : NULL;
    $_media_content_key = isset($_POST['media_content_key']) ? $_POST['media_content_key'] : 		NULL;
    $_client_user_id = isset($_POST['client_user_id']) ? $_POST['client_user_id'] : NULL;
    
    $channel = NULL;
    $_query = sprintf("SELECT * FROM `channels` WHERE `media_content_key` = '%s'",
   		mysql_real_escape_string($_media_content_key, $_db_conn));
    $_result = mysql_query($_query, $_db_conn);
    if ($_result) {
   		$channel = mysql_fetch_array($_result, MYSQL_ASSOC);
    	if ($channel === FALSE) $channel = NULL;
    } else {
    	die('Invalid query: ' . mysql_error());
    }
    
    $user = NULL;
    $_query = sprintf("SELECT * FROM `users` WHERE `client_user_id` = '%s'",
    	mysql_real_escape_string($_client_user_id, $_db_conn));
    $_result = mysql_query($_query, $_db_conn);
    if ($_result) {
    	$channel = mysql_fetch_array($_result, MYSQL_ASSOC);
    	if ($channel === FALSE) $channel = NULL;
    } else {
    	die('Invalid query: ' . mysql_error());
    }
    
    $channel_user = NULL;
    if (!is_null($_media_content_key) && !is_null($_client_user_id)) {
    	$_query = sprintf("SELECT * FROM `channel_users` WHERE `user_id` = '%s', `channel_id` = 			'%s'",
    	$user['id'], $channel['id']);
    	$_result = mysql_query($_query, $_db_conn);
        if ($_result) {
        	$channel_user = mysql_fetch_array($_result, MYSQL_ASSOC);
            if ($channel_user === FALSE) $channel_user = NULL;
        } else {
        	die('Invalid query: ' . mysql_error());
        }
   	}
    $_json_result = array('result' => 0);
    switch($_kind) {
    case 1:
        if (is_null($channel_user)){
            $_expiration_date = time() + $_expired_duration;
            $_expiration_count = $_default_expiration_count;
            $_query = sprintf("INSERT INTO `channel_users`(`user_id`, `channel_id`, 						`expiration_date`
                ,`expiration_count` , `created_at`, `updated_at`) VALUES('%s', '%s', '%s', '%s', 			 UNIX_TIMESTAMP(),
                UNIX_TIMESTAMP())", $user['id'], $channel['id'], $_expiration_date, 						$_expiration_count);

            $_result = mysql_query($_query, $_db_conn);
             if (!$_result) {
                die('Invalid query: ' . mysql_error());
            }
        } else {
            $_expiration_date = $channel_user['expiration_date'];
            $_expiration_count = $channel_user['$expiration_count'];
        }
        $_json_result['expiration_date'] = (int) $_expiration_date;
        $_json_result['expiration_count'] = (int) $_expiration_count;
        break;
    case 3:
    	if (!is_null($channel_user) && $channel_user['is_expired']) {
    		$_json_result['content_expired'] = 1;
    	}
    	break;
    }
    
    // DB Close
    mysql_close($_db_conn);
    
    // json_encodeされた結果をkollus_encryptで暗号化
    echo kollus_encrypt(json_encode($_json_encode));
}
```

{% endcode %}

**JSP ​sample**

{% code fullWidth="true" %}

```
<%@page import="org.codehaus.jettison.json.JSONObject"%>
<%@page import="java.util.Locale"%>
<%@page import="java.util.Date"%>
<%@page import="java.text.SimpleDateFormat"%>
<%@page import="java.util.ArrayList"%>
<%@page import="org.codehaus.jackson.map.ObjectMapper"%>
<%@page import="java.util.HashMap"%>
<%@page import="java.sql.*"%>
<%@ page import="test.*"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
    String kind_str = request.getParameter("kind");
    String media_content_key = request.getParameter("media_content_key");
    String client_user_id = request.getParameter("client_user_id");
    int kind = Integer.parseInt(kind_str);
    int _default_expiration_count = 3;
    int _expired_duration = 60 * 60 * 24; //one day
    int channel_id = 0;
    int user_id = 0;
    int channel_user_id = 0;
    int expiration_count = 0;
    int is_expired = 0;
    int download_times = 0;

    Long expiration_date = 0l;
    Connection conn = null; // nullで初期化
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    JSONObject jsonobj = new JSONObject();
	try {
        String url = "jdbc:mysql://localcost:3306/kollus_base";
        String id = "test"; // ユーザアカウントID
        String pw = "test"; // ユーザアカウントパスワード
        Class.forName("com.mysql.jdbc.Driver").newInstance();
        conn = DriverManager.getConnection(url, id, pw);
        String sql = "SELECT * FROM `channels` WHERE `media_content_key` = ? ";
        pstmt = conn.prepareStatement(sql);
        pstmt.setString(1,media_content_key);
        rs = pstmt.executeQuery();
		while(rs.next()){
			channel_id = rs.getInt("id");
		}
        rs.close();
        pstmt.close();
        sql = "SELECT * FROM `users` WHERE `client_user_id` = ?";
        pstmt = conn.prepareStatement(sql);
        pstmt.setString(1,client_user_id);
        rs = pstmt.executeQuery();
        while(rs.next()){
        	user_id = rs.getInt("id");
        }
		rs.close();
		pstmt.close();
		if (channel_id > 0 && user_id > 0) {
            sql = "SELECT * FROM `channel_users` WHERE `user_id` = ?, `channel_id` = ? ";
            pstmt = conn.prepareStatement(sql);
            pstmt.setInt(1, channel_id);
            pstmt.setInt(2, user_id);
            rs = pstmt.executeQuery();
		   while(rs.next()){
                channel_user_id = rs.getInt("id");
                expiration_date = rs.getLong("expiration_date");
                expiration_count = rs.getInt("expiration_date");
                is_expired = rs.getInt("is_expired");
                download_times = rs.getInt("download_times");
	       }	
           rs.close();
           pstmt.close();
	    }
		jsonobj.put("result", "0");
        switch(kind) {
            case 1:
                if (channel_user_id == 0) {
                    Long starttime = System.currentTimeMillis()/1000;
                    expiration_date = starttime + _expired_duration;
                    expiration_count = _default_expiration_count;
                    sql = "INSERT INTO `channel_users`(`user_id`, `channel_id`,
                    `expiration_date` ,`expiration_count` , `created_at`, `updated_at`) VALUES 						(?,?,?,?,?,?)"; // sql クエリ
                    pstmt = conn.prepareStatement(sql); // prepareStatementから該当sqlを先にコンパイルする。

                    pstmt.setInt(1, user_id);
                    pstmt.setInt(2, channel_id);
                    pstmt.setLong(3, expiration_date);
                    pstmt.setInt(4, expiration_count);
                    pstmt.setLong(5, starttime); // 現在日付と時刻
                    pstmt.setLong(6, starttime); // 現在日付と時刻
                    pstmt.executeUpdate();
                    pstmt.close();
                 }
                 jsonobj.put("expiration_date", expiration_date);
                 jsonobj.put("expiration_count", expiration_count);
                 break;
            case 3:
                 if (channel_user_id > 0 && is_expired > 0) {
                    jsonobj.put("expiration_date", expiration_date);
                }
        }
		break;
		conn.close();
	} catch (Exception e) { // 例外が発生した場合例外状況を処理する。
		e.printStackTrace();
	}
	String sendMsg = jsonobj.toString();
	// System.out.println(sendMsg);
%>
<%= kollus_encrypt(sendMsg)%>
```

{% endcode %}

{% code fullWidth="true" %}

```
```

{% endcode %}

### **サービスサポート**

> サンプルコードについてのお問い合わせは担当までご連絡ください。
>
> E-mail お問い合わせ > <jp_team@catenoid.net>
>
> 電話番号 > 03-4405-8462


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://catenoid-support.gitbook.io/kollus-dev-jp/callback-gai-yao/play-callback.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
