- 地址動態生成,每次請求返回的地址都不一樣。
- 有效時間短,得到的下載地址大約只有1小時的有效時間。
- 視頻地址經過加密,需在客戶(用戶)端進行解密。
- 長視頻會被分割成多段短視頻。
- 對視頻下載沒有限制,即用戶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是一個隨機數,我們可以這樣獲得
private String genSid() {
int i1 = (int)(1000+Math.floor(Math.random()*999));
int i2 = (int)(1000+Math.floor(Math.random()*9000));
return System.currentTimeMillis()+"" + i1+"" + i2;
}
假設返回”130086939328910582812″。生成fileid
優酷返回的fileid已經做了加密工作,好在並不難解,利用上面得到的fileid和seed
private String getFileID(String fileid,double seed){
String mixed = getFileIDMixString(seed);
String[] ids= fileid.split("\\*");
StringBuilder realId = new StringBuilder();
int idx;
for (int i=0; i< ids.length; i++){
idx = Integer.parseInt(ids[i]);
realId.append(mixed.charAt(idx));
}
return realId.toString();
}
private String getFileIDMixString(double seed){
StringBuilder mixed = new StringBuilder();
StringBuilder source = new StringBuilder(
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/\\:._-1234567890");
int index, len = source.length();
for (int i=0; i< len;++i){
seed = (seed * 211 + 30031) % 65536;
index = (int)Math.floor(seed/65536 * source.length());
mixed.append(source.charAt(index));
source.deleteCharAt(index);
}
return mixed.toString();
}
假設返回”03000201004D8858360BD1047C4F5FF471CDD7-C742-8D74-3EED-90A9EF54EEC1″。生成key
利用上面得到的key1和key2
private String genKey(String key1,String key2){
int key = Long.valueOf(key1,16).intValue();
key ^= 0xA55AA5A5;
return key2 + Long.toHexString(key);
}
假設返回”de1515a31372faac182698bc”。好了,把sid,fileid,key合併起來,就可以得到一開始的下載地址了。
接下來就是分段視頻的問題了,如果一個視頻分成幾段,在返回的json對象中可以找到類似的內容:
"segs":{
"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"}],
"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版代碼如下:
function getSid(){
$sid = time().(rand(0,9000)+10000);
return $sid;
}
function getkey($key1,$key2){
$a = hexdec($key1);
$b = $a ^0xA55AA5A5;
$b = dechex($b);
return $key2.$b;
}
function getfileid($fileId,$seed){
$mixed = getMixString($seed);
$ids = explode("*",$fileId);
unset($ids[count($ids)-1]);
$realId = "";
for ($i=0;$i<count($ids);++$i){
$idx = $ids[$i];
$realId .= substr($mixed,$idx,1);
}
return $realId;
}
function getMixString($seed){
$mixed = "";
$source = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/\\:._-1234567890";
$len = strlen($source);
for($i=0;$i<$len;++$i){
$seed = ($seed * 211 + 30031)%65536;
$index = ($seed / 65536 * strlen($source));
$c = substr($source,$index,1);
$mixed .= $c;
$source = str_replace($c,"",$source);
}
return $mixed;
}
另一個要注意的地方,上面提到的分段視頻,從第11段開始是16進制的0A,注意是大寫的,後面依次類推。update2:
c#版代碼如下:
private String genSid()
{
int i1 = (int)(1000+ Math.Floor((double)(new Random().Next(999))));
int i2 = (int)(1000+ Math.Floor((double)(new Random().Next(9000))));
TimeSpan ts = new TimeSpan(System.DateTime.UtcNow.Ticks-new DateTime(1970,1,1, 0,0,0).Ticks);
return Convert.ToInt64(ts.TotalMilliseconds).ToString()+"" + i1+"" + i2;
}
private String getFileID(String fileid,double seed)
{
String mixed = getFileIDMixString(seed);
String[] ids= fileid.Split('*');
StringBuilder realId = new StringBuilder();
int idx;
for (int i=0; i< ids.Length-1; i++)
{
idx = int.Parse(ids[i]);
realId.Append(mixed[idx]);
}
return realId.ToString();
}
private String getFileIDMixString(double seed)
{
StringBuilder mixed = new StringBuilder();
StringBuilder source = new StringBuilder("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/\\:._-1234567890");
int index, len = source.Length;
for (int i=0; i< len;++i)
{
seed = (seed * 211 + 30031) % 65536;
index = (int)Math.Floor(seed/65536 * source.Length);
mixed.Append(source[index]);
source.Remove(index,1);
}
return mixed.ToString();
}
private String genKey(String key1,String key2)
{
int key = Convert.ToInt32(key1,16);
var tempkey = key ^ 0xA55AA5A5;
return key2 + Convert.ToString(tempkey,16).Substring(8);
}
update3:python版代碼如下:
import time
import random
import math
def createSid():
nowTime = int(time.time() *1000)
random1 = random.randint(1000,1998)
random2 = random.randint(1000,9999)
return "%d%d%d" %(nowTime,random1,random2)
def getFileIDMixString(seed):
mixed=[]
source=list("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/\:._-1234567890")
seed=float(seed)
for i in range(len(source)):
seed = (seed * 211 + 30031 ) % 65536
index = math.floor(seed /65536 *len(source))
mixed.append(source[int(index)])
source.remove(source[int(index)])
#return ''.join(mixed)
return mixed
def getFileId(fileId,seed):
mixed=getFileIDMixString(seed)
ids=fileId.split('*')
realId=[]
for ch in ids:
realId.append(mixed[int(ch)])
return ''.join(realId)
if __name__ == '__main__':
#print createSid()
#print getFileIDMixString(4528)
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'
seed=4528
print getFileId(fileId,seed)