<?php 
// If you are curl-disabled but sockets work fine, you also need /sock.php?source
// Add &m3u to url to generate M3U playlist instead of RSS.
// Add &start=some_numberand/or &end=some_number to start from/end at that page instead of from the 1st/last.
// Add &vid=vid_id to get the FLV stream.
// Add &simplyrss to generate rss feed with only links to video pages
// Look below to OPTIONS, CONFIG, etc. fo more options

//
// Update: 05 may 2009
// Ok, Youtube tied "t" token generation with ip address.
// That means if page that generated "t" was requested from another ip,  youtube.com/get_video returns 403 -_-
// http://dennisjaamann.com/blog/?p=70
// For now going an extra step and returning the url from "Location" header seems to work.
// Oops, Youtube html update broke regex (wip)
//---
// RSS thumbnails through itunes namespace
// More info about playlist
//---
// Added &fmt support
// Finally tested in Miro and, suprise!, it didn't load videos, until now.

if(isset($_GET["source"])){highlight_file($_SERVER["SCRIPT_FILENAME"],false);return;}

//Youtube invalidates resolved url in about 60 minutes or so
//(like when VLC starts to play a url, gets redirected to some random slave server and ,unfortunately, replaces the url in playlist with the redirect)
//Try to force client to refresh (this might have no effect at all though). WIP!!!!!
header("Cache-Control: max-age=3600, must-revalidate");
header("Expires: ".date("r"strtotime("+60 minutes")));
header("Last-Modified: ".date("r"));

ob_start("ob_gzhandler");

if(!isset(
$_GET["pid"]) && !isset($_GET["vid"]))
{
    echo 
"
    Missing playlist id (pid) or video id (vid)!<br/>
    <a href=\""
.$_SERVER["PHP_SELF"]."?source\">View source code.</a>
    "
;
    return;
}

// OPTIONS , CONFIGS, CONSTANTS, VARIABLES
$self="http://".$_SERVER["SERVER_NAME"].$_SERVER["PHP_SELF"];
$base="http://www.youtube.com/watch";
$base_video="http://www.youtube.com";
$plurl="http://www.youtube.com/view_play_list";
$location null//curl header
$pid $_GET["pid"]; //if php globals are set, might not need this, or sumfin liek thwat
$vid $_GET["vid"];
$m3u = isset($_GET["m3u"]);
$simplyrss = isset($_GET["simplyrss"]); // If true ,don't redirect url to flv, only to YouTube page if using rss
$coralize = isset($_GET["coralize"]);
if(isset(
$_GET["start"])) { $psnr=$_GET["start"]; }else{ $psnr=1; }
if(isset(
$_GET["end"])) { $penr=$_GET["end"]; }else{ $penr=0; } //max is 200 vids in playlist ,that's 20 pages

$idx 0;
$max_rating 5;

if(isset(
$_GET["fmt"]))
    
$fmt= (($m3u || isset($_GET["vid"]))?'&':'&amp;'). "fmt=".$_GET["fmt"];
else
    
$fmt="";

$iconbase '<img style="border: 0px none; margin: 0px; padding: 0px; vertical-align: middle;" align="top" src="http://gdata.youtube.com/static/images/';
$fullicon 'icn_star_full_11x11.gif">';
$halficon 'icn_star_half_11x11.gif">';
$emptyicon 'icn_star_empty_11x11.gif">';

//
// Redirect client to video
//
if(isset($_GET["vid"])) //If vid id is set, redirect to FLV url.
{

    
$html=get_curl("$base?v=$vid");
    
//Confirm age
    //preg_match_all("/action_confirm.*?value=\"(?P<token>.*?)\"/ims",$html,$matches);
    //preg_match_all("/\"t\":\s+\"(.*?)\"/ims",$html,$matches );

    
if (!preg_match('#var swfArgs = (\{.*?\})#is'$html$matches))
    {
        
header("HTTP/1.0 404 Not Found");
        echo 
"Check the YouTube URL : $base?v=$vid <br/>\n";
        die(
"Couldnt detect swfArgs");
    }
 
    if (
function_exists(json_decode)) # >= PHP 5.2.0
    
{
    
$swfArgs json_decode($matches[1]);
        
$t $swfArgs->t;
    }
    else
    {
        
preg_match('#"t":.*?"(.*?)"#is'$matches[1], $submatches);
        
$t $submatches[1];
    }

    
/*if(count($matches[0])==0){
        header("HTTP/1.0 404 Not Found");
        die ("Sorry, unable to deliver you the goods.");
    }*/
    
    
$final_url $base_video."/get_video?video_id=$vid$fmt&t=".$t;
    
$html get_curl($final_url);
    
    if(
$location)
        
$final_url $location;
    
//die($location);
    
if($coralize)
        
header("Location: ".$base_video.".nyud.net/get_video?video_id=$vid$fmt&t=".$t);
    else
        
header("Location: ".$final_url);
    return;
}

//
//
// Parse playlist
//
//
$only_once true;//For parsing playlist title
while(true){
    
$html get_curl("$plurl?p=$pid&page=$psnr");
    
//print_r($html);
    //die();
    
preg_match("/href=.*?page=.*?>Next/",$html,$hasnext);
    
//Descriptions might be 'huge'
    //Previous html
    //preg_match_all("/class=\"title\">\s*<a href=\"\/watch\?v=(.*?)&.*?\">(.*?)<\/a>\s*.*?runtime\">(\d+:\d+).*?class=\"desc\">\s*(.*?)\s*<\/div>/ims"  ,$html,$matches);
    
preg_match_all('/class="video-entry">.*?"video-run-time.*?>(?P<time>\d+:\d+).*?href="\/watch\?v=(?P<url>.*?)&.*?id="video-long-title.*?>(?P<title>.*?)<.*?video-description.*?>(?P<desc>.*?)<.*?ratingVS.*?title="(?P<rating>\d+.\d+)/ims'  ,$html,$matches);

    if(
$only_once){
        
$only_once false;
        
preg_match("/<h1>(?P<title>.*?)<\/h1>.*?<p>(?P<desc>.*?)<\/p>.*?video-username.*?href.*?>(?P<user>.*?)<\/a/ims",$html,$info);
        
printBegin();
    }

    
$len count($matches[1]); //....for...each
    
for( $i 0$i $len$i++ ){
        
//$url,$title,$time,$desc
        
printItem($matches['url'][$i], $matches['title'][$i], $matches['time'][$i], $matches['desc'][$i], $matches['rating'][$i]);
    }

    if(!
$hasnext)break;

    if(
$penr==$psnr)break;
    
$psnr++;    
}

printEnd();

//
// funcs
//

function printBegin(){
    global 
$self,$base,$basew,$plurl,$pid,$vid,$m3u,$simplyrss,$setctype,$coralize,$fmt,$idx,$info,$fmt
    
    if(
$m3u)
    {
        
header("Content-Type: audio/x-mpegurl");//Let plugin handle it
        //header("Content-Type: text/plain");
        
        
header('Content-Disposition: attachment; filename="YouTube-'.$pid.'.m3u"');
        
//attachment - browser opens file download dialog, 
        //inline - browser handles url somewhat like usually, but save page as.. dialog may have the suggested filename ,FF does anyway :P
        //http://www.ietf.org/rfc/rfc2183.txt
        
        
echo "#EXTM3U\r\n";
    }
    else
    {
        
header("Content-Type: application/xml");
        
header('Content-Disposition: inline; filename="YouTube-'.$pid.'.rss"');

echo 
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>
    <rss version=\"2.0\" 
        xmlns:atom=\"http://www.w3.org/2005/Atom\"
        xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" 
        xmlns:itunes=\"http://www.itunes.com/dtds/podcast-1.0.dtd\">
    <channel>
    <atom:link href=\"$self?pid=$pid$fmt\" rel=\"self\" type=\"application/rss+xml\" />
    <title>$info[title] from $info[user] [YouTube"
.($simplyrss?"":" video")." Playlist]</title>
    <description>$info[desc]</description>
    <link>$plurl?p=$pid</link>
    <language>en-us</language>
    <itunes:owner>
        <itunes:name>$info[user]</itunes:name>
        <itunes:email>a@b.com</itunes:email>
    </itunes:owner>
    <itunes:category text=\"Arts\">
        <itunes:category text=\"Visual Arts\"/>
    </itunes:category>
    <itunes:explicit>no</itunes:explicit>
    "
;

    }
}

function 
printItem($url,$title,$time,$desc,$rating){
    
//Oh globals :[
    
global $self,$base,$basew,$plurl,$pid,$vid,$m3u,$simplyrss,$setctype,$coralize,$fmt,$idx,$info
    if(
$m3u)
    {
        
$ms=explode(":",$time);
        
$len=$ms[0]*60+$ms[1];
            echo 
"#EXTINF:$len,".str_replace("&amp;","&",$title)." - $rating\r\n";
            echo 
"$self?".($coralize?"coralize&":"")."vid=$url$fmt\r\n";
    }
    else
    {
        
$title str_replace(array("&amp;""&"),array("&""&amp;"), $title);
        if(
$simplyrss//Just links to video page
        
{
echo 
"
<item>
    <title>$title [ $time ]</title><description><![CDATA[ $desc ]]></description>
    <link rel=\"self\">$base?v=$url&amp;p=$pid&amp;index=$idx$fmt&amp;feature=PlayList</link>
    <itunes:image href=\"http://i.ytimg.com/vi/$url/default.jpg\"/>
    <itunes:duration>$time</itunes:duration>
</item>"
;
            
$idx++;
        }
        else
        {
echo
"
<item>
    <title>$title [ $time ]</title>
    <link>$self?"
.($coralize?"coralize&amp;":"")."vid=$url$fmt</link>
    <guid>$self?"
.($coralize?"coralize&amp;":"")."vid=$url$fmt</guid>
    <enclosure url=\"$self?"
.($coralize?"coralize&amp;":"")."vid=$url$fmt\" type=\"video/x-m4v\" length=\"".estimateLength($time)."\"/>
    <description><![CDATA[ <img src=\"http://i.ytimg.com/vi/$url/default.jpg\"><br/>
        <div style=\"white-space: nowrap;text-align: left\">"
.getRatings($rating,5)."
        &nbsp;<a href=\"http://www.youtube.com/watch?v=$url\">Page</a></div>
        $desc]]></description>
    <itunes:image href=\"http://i.ytimg.com/vi/$url/default.jpg\"/>
    <itunes:duration>$time</itunes:duration>
</item>"
//itunes:subtitle ?

//<itunes:subtitle><![CDATA[ $desc ]]></itunes:subtitle>

        
}
    }
}

function 
printEnd(){
    global 
$m3u;
    if(!
$m3u)
        echo 
"</channel></rss>";
}
function 
estimateLength($time){
    
$kbits 260;
    switch(
$_GET["fmt"]){
        case 
6$kbits 900 /*1000*/;break;
        case 
18$kbits 600 /*640*/;break;
        case 
22$kbits 2000 /*2232*/;break;
    }
    
$ms=explode(":",$time);
    
$len=$ms[0]*60+$ms[1];
    return (
$kbits/8) * $len 1024;
}
function 
getRatings($rating$max_rating){
    global 
$iconbase,$fullicon,$emptyicon,$halficon;
    
$html '';
    
//$rating = (float)$rating;
    
$rounded floor($rating);
    for(
$i 0$i $rounded$i++)
        
//$html .= $iconbase.$fullicon;
        
$html .="*";
    
//if($rating - $rounded >0)
        //$html .= $iconbase.$halficon;
    //for($i = 0; $i < $max_rating - ceil($rating); $i++)
        //$html .= $iconbase.$emptyicon;
    
return $html;
}

// disguises the curl using fake headers and a fake user agent. straight from php.net example, kudos
function get_curl($url)
{
    global 
$location;
    if(
function_exists("curl_init")){
        
$ch curl_init();

        
$header[0] = "Accept: */*";
        
//$header[] = "Cache-Control: max-age=1";
        
$header[] = "Connection: keep-alive";
        
$header[] = "Keep-Alive: 300";
        
$header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
        
$header[] = "Accept-Language: en-us,en;q=0.5";
        
$header[] = "Referer: http://www.youtube.com/";
        
$header[] = "Pragma: "// browsers keep this blank.

        
$opts = array(
            
CURLOPT_URL => $url,
            
CURLOPT_HEADERFUNCTION =>'readHeader',
            
CURLOPT_USERAGENT => 'Mozilla/5.0 (compatible) Firefox/3.0',
            
CURLOPT_HTTPHEADER => $header,
            
CURLOPT_REFERER => 'http://www.youtube.com/',
            
CURLOPT_ENCODING => 'gzip,deflate',
            
CURLOPT_AUTOREFERER => false,
            
CURLOPT_FOLLOWLOCATION => 0// unavail in safe mode and we don't want either
            
CURLOPT_RETURNTRANSFER => 1,
            
CURLOPT_TIMEOUT => 10
        
);
        
curl_setopt_array($ch,$opts);

        
$html curl_exec($ch); 
        
curl_close($ch); 
    }
    elseif (
function_exists("socket_create")){
        include_once(
"sock.php");
        
$ch = new Curly($url);
        
$html $ch->request();
        
        if(
array_key_exists("Location",$ch->header))
            
$location $ch->header["Location"];
    }
    return 
$html;
}

function 
readHeader($ch$header) {
    global 
$location;
    if(
preg_match("/(.*?):\s+(.*)/ims",$header,$match)){
        
//echo $match[1] . ": ". $match[2]."\r\n";
        
if(trim($match[1])=="Location")
            
$location $match[2];
    }
    return 
strlen($header);
}

ob_flush();
?>