SitePoint Sponsor

User Tag List

Results 1 to 16 of 16
  1. #1
    SitePoint Guru
    Join Date
    Nov 2004
    Location
    England
    Posts
    695
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)

    Media Proxy Question

    Guys, I have a question about whether something can be done or not, and if so, how:

    Basically, I have been converting standard YouTube URLs in to a URL that will provide an .MP4 file. It's quite simple, as this blog post explains. Here is the JavaScript that this uses:

    Code JavaScript:
    if(document.location.href.match(/http:\/\/[a-zA-Z\.]*youtube\.com\/watch/)){
        document.location.href='http://www.youtube.com/get_video?fmt='+(isHDAvailable?'22':'18')+'&video_id='+swfArgs['video_id']+'
    &t='+swfArgs['t']}

    I have done some more work with this in PHP and come up with this:

    Code PHP:
    <?php
        /*
        Get YouTube MP4 URL from URL
        */
     
        $yt        = $_GET['yt'];
     
        if (eregi('([url]http://[a-zA-Z\.]*youtube\.com/watch[/url])', $yt)){    // Ensure provided URL is of valid format
            if (function_exists('curl_init')){
                $c = curl_init();
                curl_setopt ($c, CURLOPT_URL, $_GET['yt']);
                curl_setopt ($c, CURLOPT_VERBOSE, 0);
                curl_setopt ($c, CURLOPT_RETURNTRANSFER, 1);
                $content = curl_exec($c);
                if (curl_errno($c) <> 0) {
                    echo 'Error reading page';
                    echo curl_error($c);
                    curl_close($c);
                }
                curl_close($c);
            } else {
                $content = file_get_contents($yt);
            }
     
            $pattern     = '<meta name="title" content="(.?)*">';
            preg_match($pattern, $content, $matches);
            $name        = substr($matches[0], 27, -1);
     
            preg_match('~var swfArgs = {(.*?)}~', $content, $match);
            $json = json_decode('{' . $match[1] . '}');
     
            $id        = $json->video_id;
            $t        = $json->t;
            $time    = $json->l;
     
            preg_match('~var isHDAvailable = (.*?);~', $content, $match);
     
            $isHDAvailable    = $match[1];
     
            if ($isHDAvailable == 'true'){
                $fmt    = 22;
            } else {
                $fmt    = 18;
            }
     
            echo "http://www.youtube.com/get_video?fmt=$fmt&video_id=$id&t=$t";
     
        } else {
            echo 'ERROR: Invalid URL Sent';
        }

    This is probably far from the best way to do it, but it works. BUT, there is a problem now, as of yesterday, and I need to find a solution. Basically, the process goes something like this (the URLs expire BTW):

    Original YouTube URL:
    http://www.youtube.com/watch?v=fXSovfzyx28

    Generated URL:
    http://www.youtube.com/get_video?fmt=22&video_id=fXSovfzyx28&t=vjVQa1PpcFMjQlXllJCeP5DiV7QYy1m9UITV1iO3JkI=

    Redirects to:
    http://74.125.4.157/videoplayback?id=7d74a8bdfcf2c76f&itag=22&sparams=id%2Cexpire%2Cip%2Cipbits%2Citag&ip=MY_IP_ADDRESS&signature=10BB16DCC90489488F341AFA99746AD4E2F834DA .C92D9B4C27F848E914CAC0AE80EAFD67FA8B1470&sver=3&expire=1238940802&key=yt1&ipbits=0

    Recently it seems that YouTube have started to restrict access to the video to the IP of the machine that requested it. This means that if you make the JavaScript above in to a link and drag it to your shortcut bar that you can click it on the YouTube page and you will be prompted to download the video. It also means that if I run my script on my machine that I get a working URL, which I can use in other places on the web to allow me to download it, but nobody else can use it. Changing the IP is not good enough.

    A solution? Well I figure that if I can have a file that acts as a proxy on my server, that requests the file, so my web server's IP is recorded, and then streams the video through, then anyone will be able to watch the video. But is this feasible, and if so, how do I do it?

    Many thanks for your help guys

  2. #2
    Twitter: @AnthonySterling silver trophy AnthonySterling's Avatar
    Join Date
    Apr 2008
    Location
    North-East, UK.
    Posts
    6,111
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    Sure this is possible, however, as you're now acting as a proxy for the data it will now count towards your bandwidth.

    If you can live with that, just have a script send the appropriate headers and feed the data to the requesting user.
    Last edited by AnthonySterling; Apr 5, 2009 at 14:33. Reason: Typo! :-S
    @AnthonySterling: I'm a PHP developer, a consultant for oopnorth.com and the organiser of @phpne, a PHP User Group covering the North-East of England.

  3. #3
    SitePoint Guru
    Join Date
    Nov 2004
    Location
    England
    Posts
    695
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Sorry? It WILL or WON'T count towards my bandwidth? I have unlimited bandwidth so it shouldn't be too bad, and the throughput is hardly massive. How do I do it then? Please

  4. #4
    Twitter: @AnthonySterling silver trophy AnthonySterling's Avatar
    Join Date
    Apr 2008
    Location
    North-East, UK.
    Posts
    6,111
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    On second thoughts, could you not just redirect the user to the download direct?

    Something along the lines of...

    downloadVideo.php
    PHP Code:
    <?php
    #obtain video id
    $sVideoID = (isset($_GET['videoID']) && ! empty($_GET['videoID'])) ? $_GET['videoID'] : null ;

    if(! 
    is_null($sVideoID))
    {
        
    #obtain video data
        
    $sPageData = @file_get_contents(
            
    sprintf(
                
    'http://www.youtube.com/watch?v=%s',
                
    $sVideoID
            
    )
        );
        if(
    strstr($http_response_header[0], '200') !== false)
        {
            
    #parse video parameters
            
    $oJSON = (=== preg_match('~(?<=var swfArgs = )(?<json>{[^}]+})~',    $sPageData$aMatches)) ? json_decode($aMatches['json']) : null ;
            
    $bIsHD = (=== preg_match('~(?<=isHDAvailable = )true~',            $sPageData$aMatches)) ? true false ;

            
    #build download url
            
    $sURL sprintf(
                
    'http://www.youtube.com/get_video?fmt=%s&video_id=%s&t=%s',
                (
    $bIsHD) ? 22 18,
                
    $oJSON->video_id,
                
    $oJSON->t
            
    );
            
            
    #download video
            
    header('Location: ' $sURL);
            exit;
        }
        else
        {
            
    #bad request
        
    }
    }
    else
    {
        
    #no id
    }
    ?>
    You could then just provide links in the format of downloadVideo.php?videoID=to9xb2sNo5Q, downloadVideo.php?videoID=7oEYH7m1cmo and downloadVideo.php?videoID=sJTJR83wWfI for example.
    @AnthonySterling: I'm a PHP developer, a consultant for oopnorth.com and the organiser of @phpne, a PHP User Group covering the North-East of England.

  5. #5
    SitePoint Guru
    Join Date
    Nov 2004
    Location
    England
    Posts
    695
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    A direct download won't work for another user, as YouTube are specifying the IP address in the URL. As far as YouTube is concerned, it must be my server that is downloading it. I need it to work for a streaming media system you see. What I've got right now is a page that loads the .MP4 in to memory, and then sends it to the client with no problems, but it's time and memory consuming, and I can't do that on a public web server

  6. #6
    Twitter: @AnthonySterling silver trophy AnthonySterling's Avatar
    Join Date
    Apr 2008
    Location
    North-East, UK.
    Posts
    6,111
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    Ah I see, I thought you just wanted to provide a download link for your users. The code above negates the IP issue because it sends the user to youtube to download the file, rather that have your server, well, serve it.

    Right, back to the IDE.
    @AnthonySterling: I'm a PHP developer, a consultant for oopnorth.com and the organiser of @phpne, a PHP User Group covering the North-East of England.

  7. #7
    SitePoint Guru
    Join Date
    Nov 2004
    Location
    England
    Posts
    695
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Yeah, that works already for me though, so it's not a problem. I just can't work out a way to not fill my servers memory when I'm doing this. There's also the execution time that is causing a problem. I guess I effectively want a pipe, maybe? If it's not feasible then it's not feasible and I'll have to let it go

  8. #8
    Twitter: @AnthonySterling silver trophy AnthonySterling's Avatar
    Join Date
    Apr 2008
    Location
    North-East, UK.
    Posts
    6,111
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    It should be possible using a combination of different functions, I have a few bits-n-bobs to get out of the way then I'll take a stab at it for you.

    Should you want to give it a whirl, the basic flow would be...

    Build request URL -> Request with cURL -> Obtain IP Locked URL -> Send Media Headers -> Pass URL to cURL -> Use cUrl callback to output MP4 data in blocks until EOF -> close.

    @AnthonySterling: I'm a PHP developer, a consultant for oopnorth.com and the organiser of @phpne, a PHP User Group covering the North-East of England.

  9. #9
    SitePoint Guru
    Join Date
    Nov 2004
    Location
    England
    Posts
    695
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    I think I'm doing all of that, bar the block bit at the end. Not sure how to do that. I've just been loading the whole thing in to a variable and echoing it when it's ready (after sending an MP4 header first)

  10. #10
    Twitter: @AnthonySterling silver trophy AnthonySterling's Avatar
    Join Date
    Apr 2008
    Location
    North-East, UK.
    Posts
    6,111
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    Sorry to make this short and sweet, but we've got pandemonium here at work!

    PHP Code:
    <?php
    $rCurl 
    curl_init('url');
    curl_setopt($rCurlCURLOPT_WRITEFUNCTION'sendChunk');

    function 
    sendChunk($sData)
    {
        echo 
    $sData;
        return 
    strlen($sData);
    }
    ?>
    @AnthonySterling: I'm a PHP developer, a consultant for oopnorth.com and the organiser of @phpne, a PHP User Group covering the North-East of England.

  11. #11
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2008
    Posts
    5,757
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    readfile() should chunk 8k at a time. You still need to make sure php's output buffering is off. The webserver could buffer it too.

  12. #12
    Twitter: @AnthonySterling silver trophy AnthonySterling's Avatar
    Join Date
    Apr 2008
    Location
    North-East, UK.
    Posts
    6,111
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    Heres the logic to it, it's quite a mess, but I'm sure you could refactor alot of it and introduce some error capturing etc...

    PHP Code:
    <?php
    #obtain video id
    $sVideoID = (isset($_GET['videoID']) && ! empty($_GET['videoID'])) ? $_GET['videoID'] : null ;

    if(! 
    is_null($sVideoID))
    {
        
    #obtain video data
        
    $sPageData = @file_get_contents(
            
    sprintf(
                
    'http://www.youtube.com/watch?v=%s',
                
    $sVideoID
            
    )
        );
        if(
    strstr($http_response_header[0], '200') !== false)
        {
            
    #parse video parameters
            
    $oJSON = (=== preg_match('~(?<=var swfArgs = )(?<json>{[^}]+})~',    $sPageData$aMatches)) ? json_decode($aMatches['json']) : null ;
            
    $bIsHD = (=== preg_match('~(?<=isHDAvailable = )true~',            $sPageData$aMatches)) ? true false ;

            
    #build download url
            
    $sURL sprintf(
                
    'http://www.youtube.com/get_video?fmt=%s&video_id=%s&t=%s',
                (
    $bIsHD) ? 22 18,
                
    $oJSON->video_id,
                
    $oJSON->t
            
    );
            
    $rCurl curl_init($sURL);
            
    $aCurlOptions = array(
                
    CURLOPT_FOLLOWLOCATION        =>    true,
                
    CURLOPT_RETURNTRANSFER        =>    true,
                
    CURLOPT_WRITEFUNCTION        =>    'sendChunk',
                
    CURLOPT_MAXREDIRS            =>    5
            
    );
            
    curl_setopt_array($rCurl$aCurlOptions);
            
    header('content-disposition: attachment; filename=video.mp4');
            
    header('Content-Type: video/mp4');
            
    curl_exec($rCurl);
            exit;
        }
        else
        {
            die(
    'Bad Request');
        }
    }
    else
    {
        die(
    'No videoID');
    }

    function 
    sendChunk($rHandle$sChunk)
    {
        echo 
    $sChunk;
        return 
    strlen($sChunk);
    }
    ?>
    Tested, seems to work just fine.
    @AnthonySterling: I'm a PHP developer, a consultant for oopnorth.com and the organiser of @phpne, a PHP User Group covering the North-East of England.

  13. #13
    SitePoint Guru
    Join Date
    Nov 2004
    Location
    England
    Posts
    695
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Thanks, I'll give it a shot later. I'm at work until 10pm

  14. #14
    Twitter: @AnthonySterling silver trophy AnthonySterling's Avatar
    Join Date
    Apr 2008
    Location
    North-East, UK.
    Posts
    6,111
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    Great, I should still be lurking at that god-awful hour.
    @AnthonySterling: I'm a PHP developer, a consultant for oopnorth.com and the organiser of @phpne, a PHP User Group covering the North-East of England.

  15. #15
    SitePoint Guru
    Join Date
    Nov 2004
    Location
    England
    Posts
    695
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Just tried it and it's not working as needed.

    Firstly, I need the video to play in browser, and not download, so I removed this line:
    header('content-disposition: attachment; filename=video.mp4');

    Secondly, I'm still sat waiting for the video to buffer and then I get a question mark in a Quicktime logo. I removed the content-type header to get the output in a browser by my FF self destructed. I'm guessing that the script timed out. Either way, I can't have it buffer so much, it needs to be outputting some content within seconds

  16. #16
    SitePoint Guru
    Join Date
    Nov 2004
    Location
    England
    Posts
    695
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Any idea why I'm buffering the whole video? Is there a way to split it into small chunks, say 128kb at a time, send it to the client, then clear what's already in memory and continue? I need to do this without killing my web server


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •