上个礼拜发现优酷改版了,各种过滤优酷广告的插件都失效了,于是我百度了一下(谷歌也不能用了)发现优酷改算法了,在ckplayer论坛发现有人在6月25号发了个php 的优酷代理文件,下载下来发现,能用但只能获取mp4格式的视频地址,而且php还加密了,没办法查看源码,后来通过微盾解密发现其中的源码,结合以前自己写的一个优酷视频解析类。。。。
感谢 3shi大大 具体分析请见 3shi大大的文章 优酷视频真实地址解析 (当然现在不能用了,主要看分析)
ps. 新算法是从别人那里解密出来的所以有可能存在错误,当然也没有注释,不过我试了几个视频都可以解析。
下面是源码:
文件名为:youku.class.php
1 php 2 3 class Youku { 4 5 const USER_AGENT = "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36"; 6 const REFERER = "http://www.youku.com"; 7 const FORM_ENCODE = "GBK"; 8 const TO_ENCODE = "UTF-8"; 9 private static $base = "http://v.youku.com/player/getPlaylist/VideoIDS/"; 10 private static $source = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/\\:._-1234567890"; 11 private static $sz = '-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1'; 12 private static $str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; 13 14 public static function parse($url){ 15 preg_match("#id\_([\w=]+)#", $url, $matches); //id里可以有=号 16 if (empty($matches)){ 17 $html = self::_cget($url); 18 preg_match("#videoId2\s*=\s*\'(\w+)\'#", $html, $matches); 19 if(!$matches) return false; 20 } 21 //根据you vid 获取相应的视频地址 22 return self::_getYouku(trim($matches[1])); 23 } 24 /** 25 * [_cget curl获取数据] 26 * @param [type] $url [url地址] 27 * @param boolean $convert [是否转换编码] 28 * @param integer $timeout [超时时间] 29 * @return [type] [description] 30 */ 31 public static function _cget($url,$convert=false,$timeout=10){ 32 $ch=curl_init($url); 33 curl_setopt($ch,CURLOPT_RETURNTRANSFER,1); 34 curl_setopt($ch,CURLOPT_TIMEOUT,$timeout); 35 curl_setopt($ch,CURLOPT_CONNECTTIMEOUT,$timeout); 36 curl_setopt($ch,CURLOPT_USERAGENT,self::USER_AGENT); 37 curl_setopt($ch,CURLOPT_REFERER,self::REFERER); 38 curl_setopt($ch,CURLOPT_FOLLOWLOCATION,1); //跟随301跳转 39 curl_setopt($ch,CURLOPT_AUTOREFERER,1); //自动设置referer 40 $res=curl_exec($ch); 41 curl_close($ch); 42 if($convert){ 43 $res=mb_convert_encoding($res,self::TO_ENCODE,self::FORM_ENCODE); 44 } 45 return $res; 46 } 47 48 //start 获得优酷视频需要用到的方法 49 private static function getSid(){ 50 $sid = time().(mt_rand(0,9000)+10000); 51 return $sid; 52 } 53 54 private static function getKey($key1,$key2){ 55 $a = hexdec($key1); 56 $b = $a ^0xA55AA5A5; 57 $b = dechex($b); 58 return $key2.$b; 59 } 60 61 private static function getFileid($fileId,$seed){ 62 $mixed = self::getMixString($seed); 63 $ids = explode("*",rtrim($fileId,'*')); //去掉末尾的*号分割为数组 64 $realId = ""; 65 for ($i=0;$i<count($ids);$i++){ 66 $idx = $ids[$i]; 67 $realId .= substr($mixed,$idx,1); 68 } 69 return $realId; 70 } 71 72 private static function getMixString($seed){ 73 $mixed = ""; 74 $source = self::$source; 75 $len = strlen($source); 76 for($i=0;$i<$len;$i++){ 77 $seed = ($seed * 211 + 30031)%65536; 78 $index = ($seed / 65536 * strlen($source)); 79 $c = substr($source,$index,1); 80 $mixed .= $c; 81 $source = str_replace($c,"",$source); 82 } 83 return $mixed; 84 } 85 86 private static function yk_d($a){ 87 if (!$a) { 88 return ''; 89 } 90 $f = strlen($a); 91 $b = 0; 92 $str = self::$str; 93 for ($c = ''; $b < $f;) { 94 $e = self::charCodeAt($a, $b++) & 255; 95 if ($b == $f) { 96 $c .= self::charAt($str, $e >> 2); 97 $c .= self::charAt($str, ($e & 3) << 4); 98 $c .= '=='; 99 break; 100 } 101 $g = self::charCodeAt($a, $b++); 102 if ($b == $f) { 103 $c .= self::charAt($str, $e >> 2); 104 $c .= self::charAt($str, ($e & 3) << 4 | ($g & 240) >> 4); 105 $c .= self::charAt($str, ($g & 15) << 2); 106 $c .= '='; 107 break; 108 } 109 $h = self::charCodeAt($a, $b++); 110 $c .= self::charAt($str, $e >> 2); 111 $c .= self::charAt($str, ($e & 3) << 4 | ($g & 240) >> 4); 112 $c .= self::charAt($str, ($g & 15) << 2 | ($h & 192) >> 6); 113 $c .= self::charAt($str, $h & 63); 114 } 115 return $c; 116 } 117 private static function yk_na($a){ 118 if (!$a) { 119 return ''; 120 } 121 122 $h = explode(',', self::$sz); 123 $i = strlen($a); 124 $f = 0; 125 for ($e = ''; $f < $i;) { 126 do { 127 $c = $h[self::charCodeAt($a, $f++) & 255]; 128 } while ($f < $i && -1 == $c); 129 if (-1 == $c) { 130 break; 131 } 132 do { 133 $b = $h[self::charCodeAt($a, $f++) & 255]; 134 } while ($f < $i && -1 == $b); 135 if (-1 == $b) { 136 break; 137 } 138 $e .= self::fromCharCode($c << 2 | ($b & 48) >> 4); 139 do { 140 $c = self::charCodeAt($a, $f++) & 255; 141 if (61 == $c) { 142 return $e; 143 } 144 $c = $h[$c]; 145 } while ($f < $i && -1 == $c); 146 if (-1 == $c) { 147 break; 148 } 149 $e .= self::fromCharCode(($b & 15) << 4 | ($c & 60) >> 2); 150 do { 151 $b = self::charCodeAt($a, $f++) & 255; 152 if (61 == $b) { 153 return $e; 154 } 155 $b = $h[$b]; 156 } while ($f < $i && -1 == $b); 157 if (-1 == $b) { 158 break; 159 } 160 $e .= self::fromCharCode(($c & 3) << 6 | $b); 161 } 162 return $e; 163 } 164 private static function yk_e($a, $c){ 165 for ($f = 0, $i, $e = '', $h = 0; 256 > $h; $h++) { 166 $b[$h] = $h; 167 } 168 for ($h = 0; 256 > $h; $h++) { 169 $f = (($f + $b[$h]) + self::charCodeAt($a, $h % strlen($a))) % 256; 170 $i = $b[$h]; 171 $b[$h] = $b[$f]; 172 $b[$f] = $i; 173 } 174 for ($q = ($f = ($h = 0)); $q < strlen($c); $q++) { 175 $h = ($h + 1) % 256; 176 $f = ($f + $b[$h]) % 256; 177 $i = $b[$h]; 178 $b[$h] = $b[$f]; 179 $b[$f] = $i; 180 $e .= self::fromCharCode(self::charCodeAt($c, $q) ^ $b[($b[$h] + $b[$f]) % 256]); 181 } 182 return $e; 183 } 184 185 private static function fromCharCode($codes){ 186 if (is_scalar($codes)) { 187 $codes = func_get_args(); 188 } 189 $str = ''; 190 foreach ($codes as $code) { 191 $str .= chr($code); 192 } 193 return $str; 194 } 195 private static function charCodeAt($str, $index){ 196 static $charCode = array(); 197 $key = md5($str); 198 $index = $index + 1; 199 if (isset($charCode[$key])) { 200 return $charCode[$key][$index]; 201 } 202 $charCode[$key] = unpack('C*', $str); 203 return $charCode[$key][$index]; 204 } 205 206 private static function charAt($str, $index = 0){ 207 return substr($str, $index, 1); 208 } 209 210 211 /** 212 * [_getYouku description] 213 * @param [type] $vid [视频id] 214 * @return [type] [description] 215 */ 216 public static function _getYouku($vid){ 217 //$link = "http://v.youku.com/player/getPlayList/VideoIDS/{$vid}/Pf/4"; //获取视频信息json 有些视频获取不全(土豆网的 火影忍者) 218 $blink = self::$base.$vid; 219 $link = $blink."/Pf/4/ctype/12/ev/1"; 220 $retval = self::_cget($link); 221 $bretval = self::_cget($blink); 222 if ($retval) { 223 $rs = json_decode($retval, true); 224 $brs = json_decode($bretval, true); 225 if(!empty($rs['data'][0]['error'])){ 226 return false; //有错误返回false 227 } 228 $data = array(); 229 $streamtypes = $rs['data'][0]['streamtypes']; //可以输出的视频清晰度 230 $streamfileids = $rs['data'][0]['streamfileids']; 231 $seed = $rs['data'][0]['seed']; 232 $segs = $rs['data'][0]['segs']; 233 $ip = $rs['data'][0]['ip']; 234 $bsegs = $brs['data'][0]['segs']; 235 list($sid, $token) = explode('_', self::yk_e('becaf9be', self::yk_na($rs['data'][0]['ep']))); 236 foreach ($segs as $key=>$val) { 237 if(in_array($key,$streamtypes)){ 238 foreach($val as $k=> $v){ 239 $no = strtoupper(dechex($v['no'])); //转换为16进制 大写 240 if(strlen($no) == 1){ 241 $no ="0".$no; //no 为每段视频序号 242 } 243 //构建视频地址K值 244 $_k = $v['k']; 245 if ((!$_k || $_k == '') || $_k == '-1') { 246 $_k = $bsegs[$key][$k]['k']; 247 } 248 $fileId = self::getFileid($streamfileids[$key],$seed); 249 $fileId = substr($fileId,0,8).$no.substr($fileId,10); 250 $ep = urlencode(iconv('gbk', 'UTF-8', self::yk_d(self::yk_e('bf7e5f01', ((($sid . '_') . $fileId) . '_') . $token)))); 251 //判断后缀类型 、获得后缀 252 $typeArray = array("flv"=>"flv","mp4"=>"mp4","hd2"=>"flv","3gphd"=>"mp4","3gp"=>"flv","hd3"=>"flv"); 253 //判断视频清晰度 254 $sharpness = array("flv"=>"normal","flvhd"=>"normal","mp4"=>"high","hd2"=>"super","3gphd"=>"high","3gp"=>"normal","hd3"=>"original"); //清晰度 数组 255 $fileType = $typeArray[$key]; 256 $data[$sharpness[$key]][$k] = "http://k.youku.com/player/getFlvPath/sid/".$sid."_00/st/{$fileType}/fileid/".$fileId."?K=".$_k."&hd=1&myp=0&ts=".((((($v['seconds'].'&ypp=0&ctype=12&ev=1&token=').$token).'&oip=').$ip).'&ep=').$ep;; 257 } 258 } 259 } 260 //返回 图片 标题 链接 时长 视频地址 261 $data['img'] = $rs['data'][0]['logo']; 262 $data['title'] = $rs['data'][0]['title']; 263 $data['seconds'] = $rs['data'][0]['seconds']; 264 return $data; 265 } else { 266 return false; 267 } 268 } 269 //end 获得优酷视频需要用到的方法 270 }
引入这个类就可以使用: 输出一个带有各种清晰度的 视频url 的数组。
1 require "youku.class.php"; 2 $url = "http://v.youku.com/v_show/id_XNzM1NjQ0Mzgw.html"; 3 $data = Youku::parse($url); 4 print_r($data);