上一篇博客(这里)寻找视频地址的时候,有些细节没有弄很清楚,这两天继续弄,发现原来路径里边的sid是个随机生成数,不需要找它。对于k值,现在的是服务器直接返回来的,而里边也有通过返回的json里边的key1计算获得:
PlayListProxy.as文件里边:
param1.key1 = (Number("0x" + param2.key1) ^ Number("0xA55AA5A5")).toString(16);
而关键之处是就剩下了那个fileid,这是我从它的播放器里边找到的源码:
private function parseMainData(param1:PlayListData, param2:Object) : void
{
var _loc_5:String = null;//临时变量
var _loc_9:String = null;//临时变量
var _loc_10:String = null;//flv\mp4分段视频信息
var _loc_11:Object = null;//streamfileids
var _loc_12:String = null;
var _loc_13:Array = null;
var _loc_14:RandomProxy = null;
var _loc_15:Dictionary = null;//segs里边几种视频类型的数据
var _loc_16:String = null;//视频类型
var _loc_17:int = 0;//临时变量,长度计数用
var _loc_18:String = null;
var _loc_19:Array = null;//存放视频数据
var _loc_20:String = null;
var _loc_21:Object = null;//临时变量
var _loc_22:VideoSegmentData = null;//视频数据:no,size.type,seconds,k值
if (param1 == null || param2 == null)
{
return;
}
param1.drm = param2.drm;
param1.rtmp = param2.rtmp;
param1.ct = param2.ct;
param1.show = this.parseShowData(param2.show);
param1.programList = this.parseProgramListData(param2.list, param2.list_pre, param2.list_next);
param1.dvdinfo = this.parseDVDData(param2.dvd);
param1.trial = this.pareseTrialData(param2.trial);
param1.caption = this.parseCaptionData(param2.dvd);
param1.threeD = this.parseThreeDData(param2.dvd);
param1.tt = param2.tt;
param1.ct = param2.ct;
param1.cs = param2.cs;
param1.logo = param2.logo;
param1.seed = Number(param2.seed);
param1.tags = param2.tags;
param1.categories = param2.categories;
param1.videoId = String(param2.videoid);
param1.vidEncoded = param2.vidEncoded;
param1.userName = param2.username;
param1.userId = param2.userid;
param1.title = unescape(param2.title);
param1.key1 = (Number("0x" + param2.key1) ^ Number("0xA55AA5A5")).toString(16);
param1.key2 = param2.key2;
param1.seconds = Number(param2.seconds);
if (param2.sourceVid != null)
{
param1.sourceVid = Number(param2.sourceVid);
}
param1.streamFileIds = param2.streamfileids;
param1.segs = param2.segs;
param1.streamSizes = param2.streamsizes;
param1.streamTypes = param2.streamtypes;
param1.error = param2.error;
param1.errorlink = param2.link;
param1.up = param2.up;
param1.down = param2.down;
if (param2.lastpoint)
{
param1.lastpoint = Number(param2.lastpoint);
}
param1.token = param2.ts ? (param2.ts) : ("");
var _loc_3:* = new HashCash();
param1.hashcash = _loc_3.getHashCash(param1.token + "o");
param1.tokenup = param2.tsup ? (param2.tsup) : ("");
param1.hashcashup = _loc_3.getHashCash(param1.tokenup);
param1.currentLang = "";
if (param1.dvdinfo && param1.dvdinfo.audioLangs.length > 1)
{
_loc_17 = 0;
while (_loc_17 < param1.dvdinfo.audioLangs.length)
{
if (param1.vidEncoded == param1.dvdinfo.audioLangs[_loc_17].vid || param1.videoId == param1.dvdinfo.audioLangs[_loc_17].vid)
{
param1.currentLang = param1.dvdinfo.audioLangs[_loc_17].lang;
param1.currentLangID = param1.dvdinfo.audioLangs[_loc_17].langid;
}
_loc_17++;
}
}
var _loc_4:Array = [];//视频类型
for (_loc_5 in param2.streamfileids)
{
if (!this.isValidType(_loc_5))
{
continue;
}
_loc_4.push(_loc_5);
}
param1.typeArr = _loc_4;
this._playListData.fileType = _loc_4[0];
if (_loc_4.length > 1)
{
}
var _loc_6:Number = 0;
var _loc_7:* = Number.MAX_VALUE;
var _loc_8:* = param2.segs;
for (_loc_9 in _loc_8)
{
if (_loc_9 == "flv" || _loc_9 == "mp4" || _loc_9 == "flvhd" || _loc_9 == "hd2" || _loc_9 == "hd3")
{
_loc_6 = 0;
for (_loc_18 in _loc_8[_loc_9])
{
_loc_6 = _loc_6 + Number(_loc_8[_loc_9][_loc_18].seconds);
}
if (_loc_6 < _loc_7)
{
_loc_7 = _loc_6;
}
}
}
param1.totalTime = _loc_7;
_loc_10 = "";
_loc_11 = param2.streamfileids;
for (_loc_12 in _loc_11)
{
if (_loc_12 == param1.fileType)
{
_loc_10 = _loc_11[_loc_12];
break;
}
}
_loc_13 = [];
_loc_14 = new RandomProxy(param1.seed);
_loc_15 = new Dictionary();
for (_loc_16 in param2.segs)
{
if (!this.isValidType(_loc_16))
{
continue;
}
_loc_19 = [];
//_loc_16 -- flv
//_loc_16 -- mp4
//_loc_20 -- 0,1,2,3
for (_loc_20 in param2.segs[_loc_16])
{
_loc_21 = param2.segs[_loc_16][_loc_20];
_loc_22 = new VideoSegmentData();
_loc_22.no = _loc_21.no;
_loc_22.size = _loc_21.size;
_loc_22.seconds = _loc_21.seconds;
if (_loc_21.k)
{
_loc_22.key = _loc_21.k;
}
//streamfileids , mp4/flv , i , RandomProxy(param1.seed)
_loc_22.fileId = this.getFileId(_loc_11, _loc_16, int(_loc_20), _loc_14);
_loc_22.type = _loc_16;
_loc_19.push(_loc_22);
}
_loc_15[_loc_16] = _loc_19;
}
param1.videoSegmentsDic = _loc_15;
param1.watermarks = param2.streamlogos;
if (param2.hasOwnProperty("paike"))
{
param1.paike = param2.paike as Boolean;
}
else
{
param1.paike = false;
}
if (param2.hasOwnProperty("share"))
{
param1.share = param2.share as Boolean;
}
else
{
param1.share = false;
}
param1.previews = this.parsePreviews(param2.preview);
return;
}// end function
里边的注释是我理解的备注,目前很疑惑的是这个:
_loc_22.fileId = this.getFileId(_loc_11, _loc_16, int(_loc_20), _loc_14);
知道它是计算fileId的关键代码,但是RandomProxy是什么结构的有人知道吗?为什么怎么搜索都找不到呢?
getFileId函数代码:
//streamfileids , mp4/flv , i , RandomProxy(param1.seed)
private function getFileId(param1:Object, param2:String, param3:int, param4:RandomProxy) : String
{
var _loc_6:String = null;//mp4/flv
var _loc_5:String = "";//"66*59*66*66*66*9*66*30*66*66*57*66*53*47*47*67*11*3..........
for (_loc_6 in param1)
{
if (_loc_6 == param2)
{
_loc_5 = param1[_loc_6];
break;
}
}
if (_loc_5 == "")
{
return "";
}
var _loc_7:String = "";
var _loc_8:* = param4.cg_fun(_loc_5);
var _loc_9:* = param4.cg_fun(_loc_5).slice(0, 8);
var _loc_10:* = int(param3);
var _loc_11:* = int(param3).toString(16);
if (int(param3).toString(16).length == 1)
{
_loc_11 = "0" + _loc_11;
}
_loc_11 = _loc_11.toUpperCase();
var _loc_12:* = _loc_8.slice(10, _loc_8.length);
_loc_7 = _loc_9 + _loc_11 + _loc_12;
return _loc_7;
}// end function
关键部分就是这个随机的函数cg_fun()是干什么的。
比如fileid是:0300080400510C15B61CF004ED56DCBD905FF1-B527-63DA-87D3-6C0D90F804E1
很显然,_loc_9得到的是前边8位,_loc_11得到的是中间两位,而_loc_12是通过函数cg_fun()得到的那个字符串出去前边被替换的10位的剩余字符串。
所有的核心就剩下这个函数了,一时间找不到办法解决,之后查了一下其他人做的方法,那些几乎都是以前的早起版本,但是现在依然可以用:
最新的优酷真实下载地址解析 http://blog.csdn.net/feige2008/article/details/8198434
优酷视频真实地址解析 http://blog.csdn.net/amor2006/article/details/7055902
记录一个获取fileid的脚本代码:
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='49*48*49*49*49*12*49*5*49*49*1*42*49*16*42*1*32*11*42*16*55*49*49*5*63*38*1*11*38*16*32*38*19*49*1*55*55*42*64*32*1*2*23*64*11*48*38*15*64*12*23*38*48*64*11*16*49*38*19*49*55*12*49*5*63*42'
seed=2034
print getFileId(fileId,seed)
整体的源码:
test.html 使用的时候使用IE浏览器,其他的还在研究中
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>JSON</title>
</head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript">
var xmlhttp;
var message="";
//本源码只能在IE浏览器或者IE模式下可以使用,因为ActiveXObject有足够的权限,而XMLHttpRequest不能跨域请求json
//创建http 请求的对象
function createxmlhttpRequest() {
try {
xmlhttp = new ActiveXObject("Msxml2.xmlhttp");
} catch (e) {
try {
xmlhttp = new ActiveXObject("Microsoft.xmlhttp");
} catch (e2) {
xmlhttp = false;
}
}
if (!xmlhttp && typeof XMLHttpRequest != "udefined") {
try {
xmlhttp = new XMLHttpRequest ();
} catch (e2) {
xmlhttp = false;
}
}
}
//工具函数,删除一个字符串指定位置的字符
function remove(str , index)
{
if(str == null)
return null;
if(index < 0)
return str;
if(index < str.length)
{
str=str.substring(0,index)+str.substring(index+1);
return str;
}
else
{
return null;
}
}
//通过seed计算得到解密的“种子”
function getFileIDMixString(_seed)
{
var mixed = [];
var source = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/\\:._-1234567890";
var seed = parseFloat(_seed)+0.00;
var source_length = source.length;
for(var i = 0 ; i < source_length ; i ++)
{
seed = (seed * 211 + 30031) % 65536;
var index = Math.floor(seed /65536 * (source.length));
index = parseInt(index);
mixed[i] = source.charAt(index);
source = remove(source,index);
}
return mixed;
}
//通过上边的解密种子,将fileid解密
function getFileId(fileId,seed)
{
var mixed = getFileIDMixString(seed);
var ids = fileId.split('*');
var realId = [];
for(var i = 0 ; i < ids.length ; i ++)
{
if(ids[i] == "")break
realId[i] = mixed[ids[i]];
}
return realId;
}
function getInfo()
{
createxmlhttpRequest();
try {
//实验方法:
//找到一个优酷视频的链接: http://v.youku.com/v_show/id_XMjQwOTM4NDQ4.html
//将id后边的XMjQwOTM4NDQ4取出,连接成字符串: http://v.youku.com/player/getPlayList/VideoIDS/XMjQwOTM4NDQ4
xmlhttp.open("get", "http://v.youku.com/player/getPlayList/VideoIDS/XMjQwOTM4NDQ4", false);
xmlhttp.onreadystatechange = returnInfo;
xmlhttp.send(null);
}
catch(e)
{
}
}
//回调函数,处理传回来的json
function returnInfo()
{
if(xmlhttp.readyState == 4)
{
var info = xmlhttp.responseText;
eval("var json= " + info);
var seed = "";
var title = "";
var streamtypes = [];
var streamfileids = [];
var segs = [];
var mixed = [];
var FileId = [];
var video = [];
var videorealurl = [];
seed = json.data[0].seed;
document.getElementById("seed").innerHTML = "随机种子: "+seed;//显示此时的随机种子
mixed = getFileIDMixString(seed);
title += json.data[0].title;
document.getElementById("title").innerHTML = "视频名字: "+title+"<br/>";//显示当前播放视频的名字
streamtypes = json.data[0].streamtypes;
document.getElementById("streamtypes").innerHTML = "视频格式: ";
document.getElementById("streamfileids").innerHTML = "加密的stream id: <br/>";
document.getElementById("FileId").innerHTML = "解密的stream id: <br/>";
document.getElementById("video").innerHTML = "视频加密的K值:<br/>";
for(var i = 0 ; i < streamtypes.length ; i ++)
{
streamfileids[i] = json.data[0].streamfileids[""+streamtypes[i]];
document.getElementById("streamtypes").innerHTML +=" "+streamtypes[i];//显示当前视频可以选择的种类
document.getElementById("streamfileids").innerHTML+=streamtypes[i]+": "+streamfileids[i]+"<br/>";//显示当前视频加密后的stream id
FileId[i] = getFileId(streamfileids[i],seed);
var temp_fileid = "";
for(var j = 0 ; j < FileId[i].length ; j ++)
{
temp_fileid += FileId[i][j];
}
FileId[i] = temp_fileid;
document.getElementById("FileId").innerHTML += streamtypes[i]+": FileId: "+FileId[i]+"<br/>";//显示当前播放视频的名字
document.getElementById("video").innerHTML += streamtypes[i]+" :<br/>";
segs = json.data[0].segs[""+streamtypes[i]];
var url = [];
for(var j = 0 ; j < segs.length ; j ++)
{
document.getElementById("video").innerHTML += segs[j].k+"<br/>";//显示当前播放视频的名字
//http://f.youku.com/player/getFlvPath/sid/138846963736713671615_00/st/flv/fileid/
url[j] = "http://f.youku.com/player/getFlvPath/sid/00_00/st/";//基本路径
url[j]+= streamtypes[i];//视频类型
temp_fileid = FileId[i];
if(j < 16)
temp_fileid = temp_fileid.substring(0,8)+"0"+parseInt(j).toString(16)+temp_fileid.substring(10);
else
temp_fileid = temp_fileid.substring(0,8)+parseInt(j).toString(16)+temp_fileid.substring(10);
url[j]+= "/fileid/"+temp_fileid;//增加file的id
url[j]+= "?K="+segs[j].k;//增加K值验证
document.getElementById("videorealurl").innerHTML +=
j+": <a href=\""+url[j]+"\">"+url[j]+"</a><br/>";//显示当前播放视频的名字
}
videorealurl[i] = url;
}
}
}
</script>
<body>
<br/><br/>
<h2 style="color: red;">JSON</h2>
<br/>
<input type="button" value="JSON" οnclick="getInfo()" />
<table>
<tr><td><div id="test"></div></td></tr>
<tr><td><div id="seed"></div></td></tr>
<tr><td><span id="title"><span></td></tr>
<tr><td><span id="streamtypes"><span></td></tr>
<tr><td><span id="streamfileids"><span></td></tr>
<tr><td><span id="FileIDMixString"><span></td></tr>
<tr><td><span id="FileId"><span></td></tr>
<tr><td><span id="video"><span></td></tr>
<tr><td><span id="videorealurl"><span></td></tr>
</table>
</body>
</html>
实验结果: