优酷视频真实地址解析

来源:互联网 发布:移除数组中的指定元素 编辑:程序博客网 时间:2024/04/29 13:57

原文地址:http://blog.csdn.net/amor2006/article/details/7055902

优酷的视频下载有以下几个特点:

  1. 地址动态生成,每次请求返回的地址都不一样。
  2. 有效时间短,得到的下载地址大约只有1小时的有效时间。
  3. 视频地址经过加密,需在客户(用户)端进行解密。
  4. 长视频会被分割成多段短视频。
  5. 对视频下载没有限制,即用户A得到的下载地址,用户B也可以下载。

先来看一下解析后的视频地址:

http://f.youku.com/player/getFlvPath/sid/130086939328910582812_00/st/flv/fileid/03000201004D8858360BD1047C4F5FF471CDD7-C742-8D74-3EED-90A9EF54EEC1?K=de1515a31372faac182698bc

以上三段红色部分分别代表sid、fileid和key。

我们来分析一下这个地址,除了固定的部分以外,整个地址由sid、fileid和key三部分组成,下面我们逐一来分析如何解析这三个值。

以普通的优酷视频播放地址为例,

http://v.youku.com/v_show/id_XMjUyODAzNDg0.html

把其中的红色部分复制出来,拼在

http://v.youku.com/player/getPlayList/VideoIDS/

后面,得到

http://v.youku.com/player/getPlayList/VideoIDS/XMjUyODAzNDg0

访问该地址得到json格式的字符串,其中我们感兴趣的内容是:

"seed":6302,"key1":"bd7c3d19","key2":"de1515a31372faac","fileid":"13*18*13*13*13*11*13*42*13*13*39*44*41*41*47*41*18*29*13*60*44*42*13*39*17*33*39*56*47*56*56*39*17*42*33*44*44*17*54*33*17*39*11*54*41*44*17*39*54*18*55*55*44*54*38*13*57*38*55*56*47*39*55*55*33*42*", 

我们的解析工作需要用到上面的这些内容。这里要说明一下,因为地址是动态生成的,每次请求返回的结果都不一样,所以你看到的和上面是不一样的,但是不影响解析的过程。

生成sid

sid是一个随机数,我们可以这样获得

[csharp] view plaincopy
  1. private String genSid() {  
  2.   int i1 = (int)(1000+Math.floor(Math.random()*999));  
  3.   int i2 = (int)(1000+Math.floor(Math.random()*9000));  
  4.   return System.currentTimeMillis()+"" + i1+"" + i2;  
  5. }  
假设返回”130086939328910582812″。

生成fileid

优酷返回的fileid已经做了加密工作,好在并不难解,利用上面得到的fileid和seed

[java] view plaincopy
  1. private String getFileID(String fileid,double seed){  
  2.   String mixed = getFileIDMixString(seed);  
  3.   String[] ids= fileid.split("\\*");  
  4.   StringBuilder realId = new StringBuilder();  
  5.   int idx;  
  6.   for (int i=0; i< ids.length; i++){  
  7.     idx = Integer.parseInt(ids[i]);  
  8.     realId.append(mixed.charAt(idx));  
  9.   }  
  10.   return realId.toString();  
  11. }  
  12.   
  13. private String getFileIDMixString(double seed){  
  14.   StringBuilder mixed = new StringBuilder();  
  15.   StringBuilder source = new StringBuilder(  
  16.     "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/\\:._-1234567890");  
  17.   int index, len = source.length();  
  18.   for (int i=0; i< len;++i){  
  19.     seed = (seed * 211 + 30031) % 65536;  
  20.     index = (int)Math.floor(seed/65536 * source.length());  
  21.     mixed.append(source.charAt(index));  
  22.     source.deleteCharAt(index);  
  23.   }  
  24.   return mixed.toString();  
  25. }  
假设返回”03000201004D8858360BD1047C4F5FF471CDD7-C742-8D74-3EED-90A9EF54EEC1″。

生成key

利用上面得到的key1和key2

[java] view plaincopy
  1. private String genKey(String key1,String key2){  
  2.   int key = Long.valueOf(key1,16).intValue();  
  3.   key ^= 0xA55AA5A5;  
  4.   return key2 + Long.toHexString(key);  
  5. }  
假设返回”de1515a31372faac182698bc”。

好了,把sid,fileid,key合并起来,就可以得到一开始的下载地址了。

接下来就是分段视频的问题了,如果一个视频分成几段,在返回的json对象中可以找到类似的内容:

[plain] view plaincopy
  1. "segs":{  
  2. "mp4":[{"no":"0","size":"39095085","seconds":"426"},{"no":"1","size":"22114342","seconds":"426"},{"no":"2","size":"23296715","seconds":"424"},{"no":"3","size":"18003234","seconds":"426"},{"no":"4","size":"31867294","seconds":"423"},{"no":"5","size":"14818514","seconds":"248"}],  
  3. "flv":[{"no":"0","size":"19739080","seconds":"425"},{"no":"1","size":"11506385","seconds":"426"},{"no":"2","size":"11821267","seconds":"426"},{"no":"3","size":"8988612","seconds":"426"},{"no":"4","size":"16078739","seconds":"425"},{"no":"5","size":"7634043","seconds":"245"}]}  
很明显,该视频分成了6段,而且有mp4和flv两种格式的视频。还记得一开始的视频地址中的蓝色部分吗,我们只要修改那一部分的数字就可以了,比如第二段,就把蓝色部分换成01(两个都要换),注意这是16进制的。

如果想下载mp4格式的,只要把下载地址中的/flv/换成/mp4/,当然你要确定该视频有mp4格式。

update:

php版代码如下:

[php] view plaincopy
  1. function getSid(){  
  2.     $sid = time().(rand(0,9000)+10000);  
  3.     return $sid;  
  4. }  
  5.   
  6. function getkey($key1,$key2){  
  7.     $a = hexdec($key1);  
  8.     $b = $a ^0xA55AA5A5;  
  9.     $b = dechex($b);  
  10.     return $key2.$b;  
  11. }  
  12.   
  13. function getfileid($fileId,$seed){  
  14.     $mixed = getMixString($seed);  
  15.     $ids = explode("*",$fileId);  
  16.     unset($ids[count($ids)-1]);  
  17.     $realId = "";  
  18.     for ($i=0;$i<count($ids);++$i){  
  19.         $idx = $ids[$i];  
  20.         $realId .= substr($mixed,$idx,1);  
  21.     }  
  22.     return $realId;  
  23. }  
  24.   
  25. function getMixString($seed){  
  26.     $mixed = "";  
  27.     $source = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/\\:._-1234567890";  
  28.     $len = strlen($source);  
  29.     for($i=0;$i<$len;++$i){  
  30.         $seed = ($seed * 211 + 30031)%65536;  
  31.         $index = ($seed / 65536 * strlen($source));  
  32.         $c = substr($source,$index,1);  
  33.         $mixed .= $c;  
  34.         $source = str_replace($c,"",$source);  
  35.     }  
  36.     return $mixed;  
  37. }  
另一个要注意的地方,上面提到的分段视频,从第11段开始是16进制的0A,注意是大写的,后面依次类推。

update2:

c#版代码如下:

[csharp] view plaincopy
  1. private String genSid()  
  2. {  
  3.     int i1 = (int)(1000+ Math.Floor((double)(new Random().Next(999))));  
  4.     int i2 = (int)(1000+ Math.Floor((double)(new Random().Next(9000))));  
  5.     TimeSpan ts = new TimeSpan(System.DateTime.UtcNow.Ticks-new DateTime(1970,1,1, 0,0,0).Ticks);  
  6.     return Convert.ToInt64(ts.TotalMilliseconds).ToString()+"" + i1+"" + i2;  
  7. }  
  8.   
  9. private String getFileID(String fileid,double seed)  
  10. {  
  11.     String mixed = getFileIDMixString(seed);  
  12.     String[] ids= fileid.Split('*');  
  13.     StringBuilder realId = new StringBuilder();  
  14.     int idx;  
  15.     for (int i=0; i< ids.Length-1; i++)  
  16.     {  
  17.         idx = int.Parse(ids[i]);  
  18.         realId.Append(mixed[idx]);  
  19.     }  
  20.     return realId.ToString();  
  21. }  
  22.   
  23. private String getFileIDMixString(double seed)  
  24. {  
  25.     StringBuilder mixed = new StringBuilder();  
  26.     StringBuilder source = new StringBuilder("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/\\:._-1234567890");  
  27.     int index, len = source.Length;  
  28.     for (int i=0; i< len;++i)  
  29.     {  
  30.         seed = (seed * 211 + 30031) % 65536;  
  31.         index = (int)Math.Floor(seed/65536 * source.Length);  
  32.         mixed.Append(source[index]);  
  33.         source.Remove(index,1);  
  34.     }  
  35.     return mixed.ToString();  
  36. }  
  37.   
  38. private String genKey(String key1,String key2)  
  39. {  
  40.     int key = Convert.ToInt32(key1,16);  
  41.     var tempkey = key ^ 0xA55AA5A5;  
  42.     return key2 + Convert.ToString(tempkey,16).Substring(8);  
  43. }  
update3:

python版代码如下:

[python] view plaincopy
  1. import time  
  2. import random  
  3. import math  
  4. def createSid():  
  5.     nowTime = int(time.time() *1000)  
  6.     random1 = random.randint(1000,1998)  
  7.     random2 = random.randint(1000,9999)  
  8.     return "%d%d%d" %(nowTime,random1,random2)  
  9.   
  10. def getFileIDMixString(seed):  
  11.     mixed=[]  
  12.     source=list("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/\:._-1234567890")  
  13.     seed=float(seed)  
  14.     for i in range(len(source)):  
  15.         seed = (seed * 211 + 30031 ) % 65536  
  16.         index = math.floor(seed /65536 *len(source))  
  17.         mixed.append(source[int(index)])  
  18.         source.remove(source[int(index)])  
  19.     #return ''.join(mixed)  
  20.     return mixed  
  21.   
  22. def getFileId(fileId,seed):  
  23.     mixed=getFileIDMixString(seed)  
  24.     ids=fileId.split('*')  
  25.     realId=[]  
  26.     for ch in ids:  
  27.         realId.append(mixed[int(ch)])  
  28.     return ''.join(realId)  
  29. if __name__ == '__main__':  
  30.     #print createSid()  
  31.     #print getFileIDMixString(4528)  
  32.     fileId='3*31*3*3*3*61*3*13*3*3*36*17*48*21*17*55*31*17*61*31*14*14*3*3*36*13*67*31*31*10*21*32*58*31*13*14*3*48*15*13*10*48*55*15*55*10*36*31*15*31*61*10*67*15*3*61*17*13*13*14*11*36*48*21*36*10'  
  33.     seed=4528  
  34.     print getFileId(fileId,seed)  

原创粉丝点击