Kollus DRM Callback By PHP
Play or DRM callback : Sample Source
Requirement
- php : 5.5 above
- module
- slimphp : for sample code's web framework
- slim php-view
- guzzle php http client
- firebase/php-jwt
- illuminate/database
- module
- jQuery : 3.2.1
- Boostrap 3 : for sample code
- Sqlite 3 : for db
Installation
git clone https://github.com/kollus-service/kollus-drm-callback-php
cd kollus-drm-callback-php
composer install
Copy .config.yml to config.yml and Edit this.
kollus:
domain: [kollus domain]
version: 0
service_account:
key : [service account key]
api_access_token: [api access token]
custom_key: [custom key]
security_key: [security key]
play_options:
expire_time: 86400 # 1day
How to use
composer initialize # make db and chmod logs/ data/
composer start
...
> php -S 0.0.0.0:8080 -t public public/index.php
Open browser 'http://localhost:8080'
Supported php version issue
Best way is using php7.
If you use php5.5 below, see Alternative Ways
- can't use jwt library by composer
- can't use json library
- can't use hash_hmac
- JWT Webtoken Sample Codes for php5.5 below
Development flow
Play callback
- Set Play callback at Kollus CMS.
- Activate video after Login.
- Play video using Kollus Play Video By PHP
- Before Kollus Player play video, call play callback kind 1.
- '/api/callback/play' in public/index.php
- Before Kollus Player play video, call play callback kind 3.
- '/api/callback/play' in public/index.php
DRM callback
- Set Play callback at Kollus CMS.
- Activate video after Login.
- Download video using Kollus Play Video By PHP
- Before Kollus Player download video, call drm callback kind 1.
- '/api/callback/drm' in public/index.php
- After Kollus Player download video, call drm callback kind 2.
- '/api/callback/drm' in public/index.php
- Before Kollus Player play video, call drm callback kind 3.
- '/api/callback/drm' in public/index.php
Important code
Common library
src/Callback.php
class Callback
{
/**
* @var array|object
*/
protected $data = [];
/**
* @var Container\ServiceAccount $serviceAccount
*/
protected $serviceAccount;
/**
* Callback constructor.
* @param Container\ServiceAccount $serviceAccount
*/
public function __construct(Container\ServiceAccount $serviceAccount)
{
$this->serviceAccount = $serviceAccount;
}
/**
* @return string
*/
public function getJwtData()
{
return JWT::encode($this->data, $this->serviceAccount->getSecurityKey());
}
/**
* @return array|object
*/
public function getData()
{
return $this->data;
}
/**
* @return string
*/
public function getCustomKey()
{
return $this->serviceAccount->getCustomKey();
}
/**
* @param \Closure $callable
* @return self
*/
public function play(\Closure $callable)
{
$this->data = [];
$kind = isset($_POST['kind']) ? (int)$_POST['kind'] : null;
$clientUserId = isset($_POST['client_user_id']) ? $_POST['client_user_id'] : null;
$playerId = isset($_POST['player_id']) ? $_POST['player_id'] : null;
$mediaContentKey = isset($_POST['media_content_key']) ? $_POST['media_content_key'] : null;
$uservalues = isset($_POST['uservalues']) ?
new Container\Uservalues(json_decode($_POST['uservalues'])) : null;
$data['device_name'] = isset($_POST['device_name']) ? $_POST['device_name'] : null;
$data['hardware_id'] = isset($_POST['hardware_id']) ? $_POST['hardware_id'] : null;
$this->data['data'] = $callable($kind, $clientUserId, $playerId, $mediaContentKey, $uservalues, $data);
return $this;
}
/**
* @param \Closure $callable
* @return self
*/
public function drm(\Closure $callable)
{
$this->data = [
'data' => []
];
$items = isset($_POST['items']) ? $_POST['items'] : '';
$items = empty($items) ? [] : json_decode($items, true);
foreach ($items as $item) {
$kind = isset($item['kind']) ? (int)$item['kind'] : null;
$clientUserId = isset($item['client_user_id']) ? $item['client_user_id'] : null;
$playerId = isset($item['player_id']) ? $item['player_id'] : null;
$mediaContentKey = isset($item['media_content_key']) ? $item['media_content_key'] : null;
$uservalues = isset($_POST['uservalues']) ?
new Container\Uservalues(json_decode($_POST['uservalues'])) : null;
$data['device_name'] = isset($item['device_name']) ? $item['device_name'] : null;
$data['hardware_id'] = isset($item['hardware_id']) ? $item['hardware_id'] : null;
$data['session_key'] = isset($item['session_key']) ? $item['session_key'] : null;
$data['start_at'] = isset($item['start_at']) ? $item['start_at'] : null;
$data['content_expired'] = isset($item['content_expired']) ? $item['content_expired'] : null;
$data['reset_req'] = isset($item['reset_req']) ? $item['reset_req'] : null;
$result = $callable($kind, $clientUserId, $playerId, $mediaContentKey, $uservalues, $data);
if (!empty($result)) {
$this->data['data'][] = $result;
}
}
return $this;
}
}
data/schema.sql
CREATE TABLE "users"
(
id INTEGER PRIMARY KEY,
client_user_id VARCHAR(32) NOT NULL
);
CREATE TABLE "videos"
(
id INTEGER PRIMARY KEY,
media_content_key VARCHAR(32),
upload_file_key VARCHAR(32)
);
CREATE TABLE "callback_relations"
(
id INTEGER PRIMARY KEY,
video_id INT NOT NULL,
user_id INT NOT NULL,
is_active INT NOT NULL DEFAULT 0,
player_id TEXT,
device_name VARCHAR(255),
updated_at INT,
CONSTRAINT callback_relations_user_id_fk FOREIGN KEY (user_id) REFERENCES users (id),
CONSTRAINT callback_relations_video_id_fk FOREIGN KEY (video_id) REFERENCES videos (id)
);
CREATE UNIQUE INDEX callback_relations_video_id_user_id_uindex ON "callback_relations" (video_id DESC, user_id DESC);
CREATE TABLE "callback_datas"
(
id INTEGER PRIMARY KEY,
callback_relation_id INTEGER,
callback_kind INT NOT NULL DEFAULT 0,
kind INT NOT NULL DEFAULT 0,
player_id TEXT,
device_name VARCHAR(255),
callback_result INT NOT NULL DEFAULT 0,
created_at INT,
CONSTRAINT callback_datas_callback_relations_id_fk FOREIGN KEY (callback_relation_id) REFERENCES callback_relations (id)
);
Play callback
public/index.php
$app->post('/api/callback/play', function (Request $request, Response $response) use ($container) {
...
$kollusCallback->play(function (
$kind,
$clientUserId,
$playerId,
$mediaContentKey,
$uservalue,
$data
) {
/**
* @var int $kind
* @var string|null $clientUserId
* @var string $playId
* @var string $mediaContentKey
* @var Container\Uservalues
* @var array $data
*/
...
$callbackResult = false;
if (!empty($video) && !empty($user)) {
$callbackRelation = Capsule::table('callback_relations')
->where(['video_id' => $video->id])
->where(['user_id' => $user->id])
->first();
if (!empty($callbackRelation)) {
$callbackResult = isset($callbackRelation->is_active) && $callbackRelation->is_active;
Capsule::table('callback_relations')
->where(['id' => $callbackRelation->id])
->update([
'player_id' => $playerId,
'device_name' => $data['device_name'],
'updated_at' => time(),
]);
Capsule::table('callback_datas')
->insert([
'callback_relation_id' => $callbackRelation->id,
'callback_kind' => 1, // play
'kind' => $kind,
'player_id' => $playerId,
'device_name' => $data['device_name'],
'callback_result' => $callbackResult,
'created_at' => time(),
]);
}
}
$result = [
'result' => (int)$callbackResult,
];
switch($kind) {
case 1:
$result['expiration_date'] = time() + 60 * 10; // 10 min
// TODO: try more options
if (!$result['result']) {
$result['message'] = 'This video is not permitted to you';
}
break;
case 3:
// TODO: try more options
if (!$result['result']) {
$result['message'] = 'This video is not permitted to you';
}
break;
}
return $result;
});
$jwtData = $kollusCallback->getJwtData();
$customKey = $kollusCallback->getCustomKey();
...
return $response->withStatus($httpStatus)
->withHeader('Content-Type', 'plain/text; charset=utf-8')
->withHeader('X-Kollus-UserKey', $customKey);
})->setName('api-callback-play');
DRM callback
public/index.php
$app->post('/api/callback/drm', function (Request $request, Response $response) use ($container) {
$container->get('db');
$kollusCallback = $container->get('kollusCallback');
/** @var \Kollus\Component\Callback $kollusCallback */
// for debug
$logger = $container->get('logger');
$logger->warning(json_encode([
'path' => 'drm',
'post_params' => $_POST,
]));
$kollusCallback->drm(function (
$kind,
$clientUserId,
$playerId,
$mediaContentKey,
$uservalue,
$data
) {
/**
* @var int $kind
* @var string|null $clientUserId
* @var string $playId
* @var string $mediaContentKey
* @var Container\Uservalues
* @var array $data
*/
...
$callbackResult = false;
if (!empty($video) && !empty($user)) {
$callbackRelation = Capsule::table('callback_relations')
->where(['video_id' => $video->id])
->where(['user_id' => $user->id])
->first();
if (!empty($callbackRelation)) {
$callbackResult = isset($callbackRelation->is_active) && $callbackRelation->is_active;
Capsule::table('callback_relations')
->where(['id' => $callbackRelation->id])
->update([
'player_id' => $playerId,
'device_name' => $data['device_name'],
'updated_at' => time(),
]);
Capsule::table('callback_datas')
->insert([
'callback_relation_id' => $callbackRelation->id,
'callback_kind' => 2, // drm
'kind' => $kind,
'player_id' => $playerId,
'device_name' => $data['device_name'],
'callback_result' => $callbackResult,
'created_at' => time(),
]);
}
}
$result = [
'result' => (int)$callbackResult,
'media_content_key' => $mediaContentKey,
];
switch($kind) {
case 1:
$result['kind'] = 1;
// TODO: try more options
if (!$result['result']) {
$result['message'] = 'This video is not permitted to you';
}
break;
case 2:
// TODO: marking download 'done' to db.
$result['kind'] = 2;
// TODO: try more options
if (!$result['result']) {
$result['message'] = 'This video is not permitted to you';
}
break;
case 3:
$result['kind'] = 3;
// TODO: try more options
if (isset($data['start_at'])) {
$result['start_at'] = $data['start_at'];
}
if (!$result['result']) {
$result['message'] = 'This video is not permitted to you';
}
break;
}
return $result;
});
$jwtData = $kollusCallback->getJwtData();
$customKey = $kollusCallback->getCustomKey();
...
return $response->withStatus($httpStatus)
->withHeader('Content-Type', 'plain/text; charset=utf-8')
->withHeader('X-Kollus-UserKey', $customKey);
})->setName('api-callback-drm');
License
See LICENSE
for more information