How to make Expiring/Signed video embed urls

十年热恋 提交于 2019-12-10 11:58:10

问题


I'm new and learning web development and all. I only know how to embed my videos in the website and the source can be easily obtained by any noobs and they can embed it too. but in many website the video src is encoded with a redirector link, for example: https://redirector.googlevideo.com/videoplayback?requiressl=yes&id=0c5d32687bb8e7fd&itag=18&source=webdrive&ttl=transient&app=explorer&ip=2604:a880:0:1010::dc7:d001&ipbits=32&expire=1481329545&sparams=requiressl%2Cid%2Citag%2Csource%2Cttl%2Cip%2Cipbits%2Cexpire&signature=8094D8DEF3C98784DC5561980B5725379B61A804.4C63CCB219699C4A2C02FB2606425E50243F8D36&key=ck2&mm=31&mn=sn-ab5l6ne6&ms=au&mt=1481314943&mv=m&nh=IgpwcjA0LmxnYTA3KgkxMjcuMC4wLjE&pl=48

It expires after some time, in this case, a day. I've learnt that this is a signed url.

So, i would like to know how to create a signed url like this. Please dont give any plugin names coz i'm not a paid user or anything i'm using blogger only. i just wanna learn how to code it in javascript.

In short, I want to make lets say, source of my embeded youtube video to be a signed url which expires after an hour and the source should keep changing when the site is refreshed.


回答1:


Edit: After doing this, I noticed you were using YouTube video embeds, not actual video files. But nevermind, I'll just leave this here...


Since you did not mention NodeJS anywhere, I'm guessing you're expecting to do this in JS in the browser. But for that to happen in the browser, you would need the have the real video URL sent to the client, and expose to the public your URL signing functions. Which defeats the purpose of having signed URLs.

I gave it a try using PHP, and it's not very complicated. Here is the process:

When a user requests your page, you create a temporary URL for him. This URL contains a signature, which contains the filepath to the video and an expiration date. It leads to a page which will translate the signature, and serve the file if everything is correct.

I see you don't want to use libraries, but I used "home made" libraries, which will help keep things well organized. Here is the file structure I used:

/
├── libraries/
│   ├── VideoStream.php
│   └── VideoSignature.php
├── index.php
├── video.mp4
└── getVideo.php

libraries/VideoSignature.php

Note that you need to change the settings at the top to suit your needs

<?php

/*
 * This class allows you to:
 * - Encrypt filepaths and expiration dates into signatures
 * - Decrypt signatures into filepaths and expiration dates
 *
 *
 * Note: String encryption functions were found here:
 * http://stackoverflow.com/a/1289114/1913729
 */

class VideoSignature
{
    // Time before a URL expires, in seconds
    private $expires = 10;
    // Key used to sign your URLs
    private $encryption_key = 'soMeStr0ngP455W0rD!!';
    // Public URL used to serve the content
    private $proxy_url = '/getVideo.php';

    // Encrypts a string
    private function encryptStr($str){
        $iv = mcrypt_create_iv(
            mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC),
            MCRYPT_DEV_URANDOM
        );

        $encrypted = base64_encode(
            $iv .
            mcrypt_encrypt(
                MCRYPT_RIJNDAEL_128,
                hash('sha256', $this->encryption_key, true),
                $str,
                MCRYPT_MODE_CBC,
                $iv
            )
        );

        return $encrypted;
    }

    // Decrypts a String
    private function decryptStr($str){
        $data = base64_decode($str);
        $iv = substr($data, 0, mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));

        $decrypted = rtrim(
            mcrypt_decrypt(
                MCRYPT_RIJNDAEL_128,
                hash('sha256', $this->encryption_key, true),
                substr($data, mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)),
                MCRYPT_MODE_CBC,
                $iv
            ),
            "\0"
        );

        return $decrypted;
    }

    // Returns a temporary URL
    public function getSignedURL($filepath){
        $data = json_encode(
                array(
                    "filepath" => $filepath,
                    "expires" => time() + $this->expires
                )
            );

        $signature = $this->encryptStr($data);

        return $this->proxy_url . "?s=" . urlencode($signature);
    }

    // Returns a filepath from a signature if it did not expire
    public function getFilepath($signature){
        $data = json_decode( $this->decryptStr($signature), true);

        if($data !== null && $data['expires'] > time() && file_exists($data['filepath'])){
            return $data['filepath'];
        }

        return false;
    }
}

libraries/VideoStream.php

<?php

/*
 * This class was found here:
 * http://stackoverflow.com/a/39897793/1913729
 *
 * It allows you to stream video without giving the real file URL
 */

class VideoStream
{
    private $path = "";
    private $stream = "";
    private $buffer = 102400;
    private $start  = -1;
    private $end    = -1;
    private $size   = 0;

    function __construct($filePath) 
    {
        $this->path = $filePath;
    }

    /**
     * Open stream
     */
    private function open()
    {
        if (!($this->stream = fopen($this->path, 'rb'))) {
            die('Could not open stream for reading');
        }

    }

    /**
     * Set proper header to serve the video content
     */
    private function setHeader()
    {
        ob_get_clean();
        header("Content-Type: video/mp4");
        header("Cache-Control: max-age=2592000, public");
        header("Expires: ".gmdate('D, d M Y H:i:s', time()+2592000) . ' GMT');
        header("Last-Modified: ".gmdate('D, d M Y H:i:s', @filemtime($this->path)) . ' GMT' );
        $this->start = 0;
        $this->size  = filesize($this->path);
        $this->end   = $this->size - 1;
        header("Accept-Ranges: 0-".$this->end);

        if (isset($_SERVER['HTTP_RANGE'])) {

            $c_start = $this->start;
            $c_end = $this->end;

            list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
            if (strpos($range, ',') !== false) {
                header('HTTP/1.1 416 Requested Range Not Satisfiable');
                header("Content-Range: bytes $this->start-$this->end/$this->size");
                exit;
            }
            if ($range == '-') {
                $c_start = $this->size - substr($range, 1);
            }else{
                $range = explode('-', $range);
                $c_start = $range[0];

                $c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $c_end;
            }
            $c_end = ($c_end > $this->end) ? $this->end : $c_end;
            if ($c_start > $c_end || $c_start > $this->size - 1 || $c_end >= $this->size) {
                header('HTTP/1.1 416 Requested Range Not Satisfiable');
                header("Content-Range: bytes $this->start-$this->end/$this->size");
                exit;
            }
            $this->start = $c_start;
            $this->end = $c_end;
            $length = $this->end - $this->start + 1;
            fseek($this->stream, $this->start);
            header('HTTP/1.1 206 Partial Content');
            header("Content-Length: ".$length);
            header("Content-Range: bytes $this->start-$this->end/".$this->size);
        }
        else
        {
            header("Content-Length: ".$this->size);
        }  

    }

    /**
     * close curretly opened stream
     */
    private function end()
    {
        fclose($this->stream);
        exit;
    }

    /**
     * perform the streaming of calculated range
     */
    private function stream()
    {
        $i = $this->start;
        set_time_limit(0);
        while(!feof($this->stream) && $i <= $this->end) {
            $bytesToRead = $this->buffer;
            if(($i+$bytesToRead) > $this->end) {
                $bytesToRead = $this->end - $i + 1;
            }
            $data = fread($this->stream, $bytesToRead);
            echo $data;
            flush();
            $i += $bytesToRead;
        }
    }

    /**
     * Start streaming video content
     */
    function start()
    {
        $this->open();
        $this->setHeader();
        $this->stream();
        $this->end();
    }
}

index.php

<?php

// Load the signature helper functions
include "libraries/VideoSignature.php";
$vs = new VideoSignature();

?><!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Check out my video!</title>
</head>
<body>
    <!-- /!\ Here, you need to insert a path to your video,
         relative to the getVideo.php file, not an actual URL! -->
    <video src="<?=$vs->getSignedURL("server/path/to/video.mp4");?>"></video>
</body>
</html>

getVideo.php

<?php

// Load the signature helper functions
include "libraries/VideoSignature.php";
$vs = new VideoSignature();
// Load the video streaming functions
include "libraries/VideoStream.php";


if(isset($_REQUEST['s']) && $filepath = $vs->getFilepath($_REQUEST['s'])){
    $stream = new VideoStream($filepath);
    $stream->start();
} else {
    header("HTTP/1.0 403 Forbidden");
    echo "This URL has expired.";
}


来源:https://stackoverflow.com/questions/41068432/how-to-make-expiring-signed-video-embed-urls

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!