Play or DRM callback : Sample Source
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
data
logs
public
src
templates
.config.yml
.gitignore
ALTERNATIVE.md
LICENSE
README.md
composer.json

README.md

Kollus DRM Callback By PHP

Play or DRM callback : Sample Source

Requirement

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

  1. Set Play callback at Kollus CMS.
  2. Activate video after Login.
  3. Play video using Kollus Play Video By PHP
  4. Before Kollus Player play video, call play callback kind 1.
    • '/api/callback/play' in public/index.php
  5. Before Kollus Player play video, call play callback kind 3.
    • '/api/callback/play' in public/index.php

DRM callback

  1. Set Play callback at Kollus CMS.
  2. Activate video after Login.
  3. Download video using Kollus Play Video By PHP
  4. Before Kollus Player download video, call drm callback kind 1.
    • '/api/callback/drm' in public/index.php
  5. After Kollus Player download video, call drm callback kind 2.
    • '/api/callback/drm' in public/index.php
  6. 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