爱奇艺vf算法播放直连解析

本文介绍如何获取爱奇艺手机版http://m.iqiyi.com/中视频的播放源链接

前言

想编写一个 可以获取爱奇艺直播源的解析工具,遇到如下问题并抓包记录下来

问题

寻找解析接口爱奇艺播放链接直连源

XXX平台接口百度搜索一下vip接口很多,

解析接口:XXX平台/?url=http://m.iqiyi.com/v_19rqpl2i8w.html


电脑版:User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36

分析抓包

通过浏览器抓包分析,F12 ,首先访问XXX平台/player.php?url= GET方式提交后,得到api.php 的7个参数如下,url,referer,ref,time,md5,type,key,然后在通过post 方式提交这五个参数即可得到播放源的解析参数,post 提交地址 XXX平台/api.php

XXX平台 /player.php?url= GET方式提交后得到如下内容


把上面7个参数 组合 post 方式 提交 XXX平台 /api.php 得到直连解析参数

Request URL: XXX平台/api.phpurl=http%3A%2F%2Fm.iqiyi.com%2Fv_19rqpl2i8w.html&referer=aHR0cDovL2p4Lml0YW9qdS50b3AvP3VybD1odHRwOi8vbS5pcWl5aS5jb20vdl8xOXJxcGwyaTh3Lmh0bWw%3D&ref=0&time=1547937959&md5=2acbc4c7a5a0911db0d40328a5e918d7&type=&key=U6m7yVJvtLgXZpFimrdLL17fTn_j%2FG7aJ_yV_IJvJGQqxbJZ

{

    "code": "200",

    "url": "\/jp\/dash?tvid=1745487500&vid=785c82b2b8949a49c6be38876fede723&bid=300&abid=300&src=02020031010000000000&uid=&ut=1&ori=h5&ps=0&messageId=dash_9b0f71b7c544b76d7b2d2b0341158ce0&ost=0&preIdAll=&locale=zh_cn&dfp=&k_tag=1&k_ft1=18141941858304&k_err_retries=3&k_uid=6466b50c2a41c9e46a950a6436880dca&pt=0&lid=&cf=&ct=&qd_v=1&qdy=a&qds=0&tm=1547937960000&callback=Z1547937960000",

    "type": "iqiyi",

    "play": "ajax"

}




把直连解析参数组合GTE方式提交到 http://cache.video.iqiyi.com/jp/dash 会得到我们需要的直连视频地址

http://cache.video.iqiyi.com/jp/dash?tvid=1745487500&vid=785c82b2b8949a49c6be38876fede723&bid=300&abid=300&src=02020031010000000000&uid=&ut=1&ori=h5&ps=0&messageId=dash_9b0f71b7c544b76d7b2d2b0341158ce0&ost=0&preIdAll=&locale=zh_cn&dfp=&k_tag=1&k_ft1=18141941858304&k_err_retries=3&k_uid=6466b50c2a41c9e46a950a6436880dca&pt=0&lid=&cf=&ct=&qd_v=1&qdy=a&qds=0&tm=1547937960000&vf=82973155af32b6ed1efd4dfc5e33833c&callback=Z1547937960000




try {

    Z1547937960000({

        "code": "A00000",

        "data": {

            "dm3u8": "http:\/\/cache.m.iqiyi.com\/dc\/dt\/",

            "st": 101,

            "dstl": "http:\/\/meta.video.iqiyi.com",

            "ctl": {

                "uip": "39.177.55.180",

                "vf": "82973155af32b6ed1efd4dfc5e33833c",

                "cached": true,

                "bid": 300,

                "uid": "",

                "ut": [1],

                "qd_vipdyn": 0,

                "num": "148"

            },

            "aid": 216266201,

            "dm": "http:\/\/meta.video.iqiyi.com",

            "drm": "http:\/\/drm.video.iqiyi.com\/drm\/",

            "parts": {},

            "bmsg": {

                "t": "20190120064402",

                "f": "web",

                "mid": "",

                "sp": "9051021601"

            },

            "svp": [],

            "cid": 2,

            "content": {

                "vipTypes": [0],

                "thdt": 1,

                "hdcp": 0,

                "isRs": 0,

                "exclusive": 0,

                "bossStatus": 0,

                "isProduced": 0

            },

            "dd": "http:\/\/data.video.iqiyi.com\/videos",

            "tvid": 1745487500,

            "program": {

                "audio": [],

                "video": [{

                    "drmType": 1,

                    "isdol": 0,

                    "ff": "mp4",

                    "mu": "",

                    "dr": -1,

                    "vid": "b2bae5e7a9f6cd54d831a1d772410827",

                    "isPreview": 0,

                    "ispre": 1,

                    "lgt": 0,

                    "code": 2,

                    "lid": 1,

                    "ists": 0,

                    "bid": 200,

                    "_selected": false,

                    "m3u8Url": "",

                    "duration": 2754,

                    "vsize": 81759320,

                    "name": "国语",

                    "rp": 2

                }, {

                    "drmType": 1,

                    "isdol": 0,

                    "ff": "mp4",

                    "mu": "",

                    "mp4Url": "http:\/\/data.video.iqiyi.com\/videos\/v0\/20190108\/56\/42\/ff3c988aa04e0aeb55d2dd0368be8521.mp4?m=v&qd_uri=dash&qd_sc=89a6fa342f6eca907c8f501274a0bc4c&pv=0.2&qd_tm=1547937960000&qd_p=27b137b4&qdv=1&dfp=&ssl=0&qd_vip=0&qd_src=02020031010000000000&dis_src=vrs&qd_uid=&qd_k=82973155af32b6ed1efd4dfc5e33833c&qd_ip=27b137b4",

                    "duration": 2754,

                    "dr": -1,

                    "vid": "4cf0ec53a7e8c620202a35f6c06bbb85",

                    "isPreview": 0,

                    "ispre": 1,

                    "unencryptedDuration": 0,

                    "lgt": 0,

                    "code": 2,

                    "lid": 1,

                    "fs": [],

                    "scrsz": "640x360",

                    "tag": {

                        "vt": "",

                        "at": ""

                    },

                    "p2p": "",

                    "ists": 0,

                    "bid": 300,

                    "_selected": true,

                    "ps": 0,

                    "rp": 2,

                    "vsize": 122022919,

                    "name": "国语",

                    "m3u8Url": ""

                }],

                "stl": []

            },

            "p": {

                "lgh": [],

                "lgp": 0,

                "wmarkPos": 0,

                "bt": -1,

                "raudio": false,

                "et": -1,

                "pano": {

                    "type": 1

                },

                "ca": 0,

                "tsl": [{

                    "stm": 969,

                    "etm": 0

                }, {

                    "stm": 1875,

                    "etm": 0

                }]

            }

        }

    });

} catch (e) {}

;

获取到的播放直连地址如下

"mp4Url": "http://data.video.iqiyi.com/videos/v0/20190108/56/42/ff3c988aa04e0aeb55d2dd0368be8521.mp4?m=v&qd_uri=dash&qd_sc=89a6fa342f6eca907c8f501274a0bc4c&pv=0.2&qd_tm=1547937960000&qd_p=27b137b4&qdv=1&dfp=&ssl=0&qd_vip=0&qd_src=02020031010000000000&dis_src=vrs&qd_uid=&qd_k=82973155af32b6ed1efd4dfc5e33833c&qd_ip=27b137b4",

vf 算法 本文引用了,某大佬,纳豆app视频播放链解析教程,下面将原文贴在下面提供大家学习。

分析测试前期抓包。请求网站:http://m.toutiao.iqiyi.com/top_126hd0mujy.html

在app端抓包,两次的不同的请求:

http://cache.m.iqiyi.com/jp/tmts/961149300/961149300/?uid=&cupid=&platForm=h5&qyid=&agenttype=13&type=mp4&rate=1&k_ft1=8&qdv=1&qdx=n&qdy=x&qds=0&__jsT=sgve&t=1521166692860&src=02020031010000000000&vf=0b53f9287e1b0a970ed1343392af05c2&callback=tmtsCallback

http://cache.m.iqiyi.com/jp/tmts/961149300/961149300/?uid=&cupid=&platForm=h5&qyid=&agenttype=13&type=mp4&rate=1&k_ft1=8&qdv=1&qdx=n&qdy=x&qds=0&__jsT=sgve&t=1521105711069&src=02020031010000000000&vf=1461673fa3ce9cc1cdef6308c9691171&callback=tmtsCallback

观察其中的参数,只有t和vf不同,这个t很明显能看出来是一个时间戳(13位的),而这个vf就得继续研究了

找寻加密方法

猜想是不是由某个js文件加密生成的

于是我按下f12,刷新了下页面,发现在这个播放链出来之前加载了如下几个js文件



注:最下面的那个请求是获取详细播放链的url

在图中的js文件中,很轻易便能看到有一个特别显眼的js文件:app_detail_video.18f1b586.js

http://static.qiyi.com/assets/js/page/detail/app_detail_video.18f1b586.js

我们进入到以上的js文件中,检索 vf= 这个关键词,找到相关的代码如下

function(e, t, a) {

    var i = window.cmd5xtmts ? window.cmd5xtmts() : {};

    $.extend(a || {}, i, {

        src: "02020031010000000000"

    });

    var n = "/jp/tmts/" + e + "/" + t + "/?" + $.param(a) + "&callback=tmtsCallback";

    return a.vf = window.cmd5x ? window.cmd5x(n) : "", a

}(e, t, a));

这里的 a.vf = window.cmd5x ? window.cmd5x(n) : “”, a

意思是:若window这个对象有cmd5x这个方法(属性),就返回window.cmd5x(n);否则返回””空字符串, 后面的a是第二个返回值

到这里就出现两个问题:

1.cmd5x这个函数在哪儿?

2.这个n是怎么得到的?

找寻cmd5x这个加密函数

在所有加载的js文件中都找了一遍,但是还没有明确地看到cmd5x这个function,但是在网上查到的一份有关爱奇艺的vf算法js文件看到如下一段代码

o.exports = {

    request: function(e, t, i) {

        var o = navigator.userAgent.match(/miuivideo\//i) || n.os.android && parseInt(n.os.version) > 4 && navigator.userAgent.match(/MiuiBrowser/i);

        u.sendVrsRequestPingback(),

        r.jsonp({

            url: c.h5tmtsUrl + e.tvid + "/" + e.vid + "/",

            params: function() {

                var t = {

                    platForm: "h5",

                    uid: d.getUid(),

                    dfp: s.get(),

                    cupid: e.cupid,

                    src: p.getSrc(),

                    codeflag: 1,

                    type: n.os.mac && n.browser.SAFARI || n.browser.iPad || n.browser.iPhone || n.os.android && parseFloat(n.os.version) > 4 || o ? "m3u8": "mp4"

                };

                f.isBoss() && (t.nolimit = 1);

                try {

                    l(t, p.cmd5xtmts())

                } catch(e) {}

                return t

            } (),

            beforeSend: function(e) {

                var t = a.parse(e.url).host;

                try {

                    p.cmd5x && (e.url += "&vf=" + p.cmd5x(e.url.replace(new RegExp("^https?://" + t, "ig"), "")))

                } catch(e) {}

                return e

            },

            success: function(e) {

                u.sendVrsReadyPingback(),

                e && e.hasOwnProperty("code") ? "A00000" === e.code ? t(e) : i(e) : i({

                    code: "P00001"

                })

            },

            failure: function() {

                i({

                    code: "P00001"

                })

            }

        })

    }

}

能看出来以上是部分关于发送验证请求的代码,主要看beforeSend这一部分,大概能够知道

url: c.h5tmtsUrl + e.tvid + "/" + e.vid + "/",

var t = a.parse(e.url).host;

e.url += "&vf=" + p.cmd5x(e.url.replace(new RegExp("^https?://" + t, "ig"), ""))

以上,我们还找到h5tmtsUrl这个未知的变量,又找到如下的代码

注:e.url.replace(new RegExp(“^https?://“ + t, “ig”), “”)

这是将”https://“+e.url的域名 , “ig”表示不区分大小写,统统替换成成空字符串,结合下面提到的来说,也就是将”https://cache.m.iqiyi.com/jp/tmts/" 这一段字符替换成空字符串

function(e, t, i) {

    var o;

    void 0 !== (o = function(e, t, i) {

        var o = window.location.protocol;

        i.exports = {

            vipauthUrl: "https://api.vip.iqiyi.com/services/cknsp.action",

            h5tmtsUrl: o + "//cache.m.iqiyi.com/jp/tmts/",

            vmsUrl: o + "//cache.video.iqiyi.com/jp/vms",

            vmsIPUrl1: "http://115.182.125.142/jp/vms",

            vmsIPUrl2: "http://124.250.53.164/jp/vms",

            pingbackUrl: "http://msg.71.am/core",

            isfanUrl: o + "//sns-api.iqiyi.com/apis/friend/follow.action"

        }

    }.call(t, i, t, e)) && (e.exports = o)

}

可以知道

var o = window.location.protocol;

h5tmtsUrl: o + "//cache.m.iqiyi.com/jp/tmts/"

h5tmtsUrl其实就是一个通信协议+指定字符串:”https” + “//cache.m.iqiyi.com/jp/tmts/“

所以,到这里我们需要解决的是什么呢?cmd5x这个函数我们依然不知道,且我们只能大概知道作为其参数的e.url是一个被替换掉了”https://cache.m.iqiyi.com/jp/tmts/" 的正常url字符串,其可能的参数有如下

url = c.h5tmtsUrl + e.tvid + "/" + e.vid + "/"

#以下这些拼接成字符串

{

    platForm: "h5",

    uid: d.getUid(),

    dfp: s.get(),

    cupid: e.cupid,

    src: p.getSrc(),

    codeflag: 1,

    type: n.os.mac && n.browser.SAFARI || n.browser.iPad || n.browser.iPhone || n.os.android && parseFloat(n.os.version) > 4 || o ? "m3u8": "mp4"

};

由于其js文件实在太混乱,所有参数都不知道怎么来的,没法推算出我们应该给这个cmd5x传一个什么样的值,更重要的是js中的cmd5x在哪儿,我该怎么用py去调用都不知道。最后还是在万能的google上扎到了答案,感谢贡献代码的大佬,一路带我前行

来自大佬的微笑

参考了几篇博客,自己也尝试着构造请求,也成功了,大概有以下这些参数是必要的

head = "/jp/tmts/tvid/vid/?"

param = {

        "uid":"",

        "cupid":"",

        "platForm":"h5",

        "qyid":"",

        "agenttype":"13",

        "type":"mp4",

        "nolimit":"",

        "k_ft1":"8",

        "rate":"2",

        "sgti":"",

        "qdv":"1",

        "qdx":"n",

        "qdy":"x",

        "qds":"0",

        "__jsT":"sgve",

        "t":t,

        "src":"02020031010000000000",

        "callback":"tmtsCallback"

    }

将param构造成一个字符串,再在头部拼接上head即是一个正常的cmd5x需要的参数,一个正常的出参数值为:

/jp/tmts/12476488409/12476488409/?src=02020031010000000000&callback=tmtsCallback&uid=&sgti=13_12e9e055c713deba092399c62d8cb2b2_1489561211612&agenttype=13&cupid=&t=1521388096000&qdv=1&platForm=h5&rate=2&__jsT=sgve&k_ft1=8&qds=0&nolimit=&qdy=x&type=mp4&qdx=n&qyid=&&vf=39f7f4c1b39089dbfe887ad15f1ffb7a

加上对应的头部文件:

“http://cache.m.iqiyi.com"

最后即是我们请求视频链接的地址

http://cache.m.iqiyi.com/jp/tmts/12476488409/12476488409/?src=02020031010000000000&callback=tmtsCallback&uid=&sgti=13_12e9e055c713deba092399c62d8cb2b2_1489561211612&agenttype=13&cupid=&t=1521388096000&qdv=1&platForm=h5&rate=2&__jsT=sgve&k_ft1=8&qds=0&nolimit=&qdy=x&type=mp4&qdx=n&qyid=&&vf=39f7f4c1b39089dbfe887ad15f1ffb7a

其返回的数据格式化后如下:

{

  "timestamp":"20180318234822",

  "ctl":{

    "area":1

  },

  "code":"A00000",

  "data":{

    "vipTypes":[

    ],

    "screenSize":"854x480",

    "vidl":[

      {

        "vd":1,

        "screenSize":"854x480",

        "vid":"e7eb1b37eeb029f6529bde6dc66a4815"

      }

    ],

    "aid":"",

    "pano":{

      "rType":0,

      "type":1

    },

    "adDuration":0,

    "messageId":"",

    "head":0,

    "previewType":"",

    "ugc":1,

    "clientIp":"114.248.64.91",

    "prv":"",

    "vd":1,

    "vid":"12476488409",

    "rTime":"",

    "ds":"A00012",

    "lgh":[

    ],

    "duration":272,

    "wmarkPos":0,

    "bossStatus":0,

    "cacheTime":1521388047491,

    "tail":0,

    "isProduced":0,

    "cid":5,

    "tipType":"",

    "m3u":"http://222.134.2.36/videos/v1/20180210/59/d2/82ae8f5cf70d7093c1b4dbcfbe667e6b.mp4?key=09bf99dfe8d1239f4fd1deb1192c91f75&dis_k=244569a7f99feb6f8fde7414223cfb756&dis_t=1521388102&dis_dz=CNC-BeiJing&dis_st=40&src=iqiyi.com&uuid=a6e5338-5aae8a46-e&m=v&qd_k=39f7f4c1b39089dbfe887ad15f1ffb7a&sgti=13_12e9e055c713deba092399c62d8cb2b2_1489561211612&qd_ip=72f8405b&qd_p=72f8405b&dfp=&qd_src=02020031010000000000&ssl=&ip=114.248.64.91&qd_vip=0&dis_src=vrs&qd_uid=0&qdv=1&qd_tm=1521388102146",

    "ad":1,

    "exclusive":0,

    "tvid":12476488409,

    "previewTime":"",

    "m3utx":"http://222.134.2.36/videos/v1/20180210/59/d2/82ae8f5cf70d7093c1b4dbcfbe667e6b.mp4?key=09bf99dfe8d1239f4fd1deb1192c91f75&dis_k=244569a7f99feb6f8fde7414223cfb756&dis_t=1521388102&dis_dz=CNC-BeiJing&dis_st=40&src=iqiyi.com&uuid=a6e5338-5aae8a46-e&m=v&qd_k=39f7f4c1b39089dbfe887ad15f1ffb7a&sgti=13_12e9e055c713deba092399c62d8cb2b2_1489561211612&qd_ip=72f8405b&qd_p=72f8405b&dfp=&qd_src=02020031010000000000&ssl=&ip=114.248.64.91&qd_vip=0&dis_src=vrs&qd_uid=0&qdv=1&qd_tm=1521388102146"

  }

}

其中的m3u就是视频真正的播放地址了

cmd5x也有破解好的对应几个版本,其实也叫做vf的算法,针对不同类型

import hashlib

#app端的vf算法

def cmd5x(str):

    vf = hashlib.new('md5', str + '3sj8xof48xof4tk9f4tk9ypgk9ypg5ul').hexdigest()

    return vf

#pc端的vf算法,针对爱奇艺的网页播放

def getPcVf(str):

    vf = hashlib.new('md5', str + 'u6fnp3eok0dpftcq9qbr4n9svk8tqh7u').hexdigest()

    return vf

#针对vip用户的vf算法

def getPcIbt(str):

    vf = hashlib.new('md5', str + 't6hrq6k0n6n6k6qdh6tje6wpb62v7654').hexdigest()

    return vf

这里我们使用第一个即可

代码

代码放在github了,有需要的可以看看

2018/07/12更新

看了下日志,爱奇艺大概是从6月27号更新了策略。不知道是不是被抓的多了….

以上app端的加盐md5算法失效了,花了点时间找到了cmd5x更新后的方法,说下思路:其关键函数藏在一个js文件中,参数也更新了,并且还需要注意某一个参数的先后顺序.

注:我没有使用新的cmd5x方法去加密原始的参数,所以不知道原始的参数是不是能使用。最好还是都替换成新的吧,因为app_detail_video.xxxx.js文件也换了新的

由于怕被封,暂时先不把方法写到这里,还是那句话,接口且用且珍惜.

发现更多好玩的乐趣,百度一下 【 软件队长 】

你可能感兴趣的:(爱奇艺vf算法播放直连解析)