0

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.

Andruraj
  • 67
  • 2
  • 11
  • 1
    One thing is for sure, if you're going to create signed URLs, don't do it in JS, because that happens in the browser, and requires to send the permanent URL to the client (easy to get by looking at the code). You should do it serverside. Are you using PHP? – blex Dec 09 '16 at 20:42
  • This video claims to explain the concept and show how the author implemented: [VideoYoutube](https://www.youtube.com/watch?v=xAGc8cxdiVM). Note: JS can run on server side. – DIEGO CARRASCAL Dec 09 '16 at 20:46

1 Answers1

7

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.";
}
blex
  • 24,941
  • 5
  • 39
  • 72
  • Not only youtube videos, but video embeds like yourupload, mp4upload videos like http://www.mp4upload.com/6dfaomg9r5nw. Im not familiar with NodeJS so sorry, but which method is the easiest? im willing to learn it. – Andruraj Dec 10 '16 at 08:36
  • @Andruraj You don't need to use NodeJS. PHP or any other serverside language would be ok. It's just that if you wanted to do it in JS, you would need to do it serverside (with NodeJS), not in the browser. That being said, unfortunately, I don't see any way to do it with third party site embed codes. Your iframe's URL is going to end up being the site's one, and there is generaly a link to the hosting website, in the player :( – blex Dec 10 '16 at 10:40
  • +1 Very amazing description .. your code is amazing and very detailed but i prefer nodejs i am not php guy but i got the idea – Hisham Dec 20 '19 at 18:27
  • if you are not a very good intentional user, could it be relatively easy to get the source of the video while you're watching it? – TrOnNe Apr 09 '20 at 17:34
  • @TrOnNe Yes, this method is not very secure. You do need to have an active, signed URL, but once you do, it would be really easy to download the video while the link is still valid. All you would need to do would be to open the network tab in your browser's developer tools, right click on the request serving the video, and choose "Save as..." – blex Apr 09 '20 at 20:24