├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── lib ├── anotheriqiyimp4.js ├── iqiyi.js ├── iqiyimp4.js ├── souhu.js └── youku.js ├── package.json ├── test.js └── test ├── index.test.js └── lib ├── anotheriqiyimp4.test.js ├── iqiyimp4.test.js ├── iqyi.test.js ├── souhu.test.js └── youku.test.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Commenting this out is preferred by some people, see 24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 25 | node_modules 26 | 27 | # Users Environment Variables 28 | .lock-wscript 29 | .idea/.name 30 | .idea/encodings.xml 31 | .idea/jsLibraryMappings.xml 32 | .idea/libraries/nodeParseVideo_node_modules.xml 33 | .idea/misc.xml 34 | .idea/modules.xml 35 | .idea/nodeParseVideo.iml 36 | .idea/scopes/scope_settings.xml 37 | .idea/vcs.xml 38 | .idea/workspace.xml 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 jiangli 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | nodeParseVideo 2 | ============== 3 | 4 | 这个项目通过一个优酷或者爱奇艺的视频地址,最终可以得到视频的下载地址 5 | 6 | 这个项目的想法是来源:https://github.com/keygle/parseVideo 7 | 8 | 9 | 因为自己要用到优酷和奇艺,所以就测试了这两个,测试结果是优酷的可以使用,爱奇艺的已经失效。 10 | 11 | 经过研究爱奇艺的视频页面的http请求,以及反编译视频播放器,找到它的下载地址,自己做了一个pr,现在该项目作者已经merge了。 12 | 13 | 14 | 这是一个php工程,所以自己用node把这个工程里面解析优酷和爱奇艺的方法重写了下 15 | 16 | 说明: 17 | 18 | 最终取到的爱奇艺的地址 19 | 20 | http://data.video.qiyi.com/f3060e76a71fedac773d327734e723f2/videos/v0/20141023/7b/b5/96c5fa7650168e97c445f976b0b5b68f.f4v?ran=1000&qyid=08ca8cb480c0384cb5d3db068161f44f&qypid=321778500_11&retry=1 21 | 22 | 是这样的,下载地址需要请求上面这个地址得到: 23 | 24 | { 25 | "t": "CT|ShangHai-101.81.48.14", 26 | "s": "1", 27 | "z": "hengyang3_ct", 28 | "h": "0", 29 | "l": "http://220.170.79.37/videos/v0/20141221/38/e6/4be4e1cad4374d3447be144397366ff8.f4v?key=8f5bdcd17a8765c&ran=1000&qyid=08ca8cb480c0384cb5d3db068161f44f&qypid=335764500_11&retry=1&uuid=6551300e-5497c256-36", 30 | "e": "0" 31 | } 32 | 33 | 返回结果中的l是真正的下载地址,而这个地址失效时间非常短,故返回的是上面的地址,如果最终取到的下载地址失效,再次请求返回的这个地址即可. 34 | 35 | 36 | 这个项目相当于把那个项目用node实现了一把。 37 | 38 | 基本用法:参考test.js 39 | 40 | 41 | 如果有问题,欢迎大家做pr 42 | 43 | 44 | 45 | 分析优酷的下载地址可以参考[这篇文章](http://www.cnblogs.com/keygle/p/3829653.html) 46 | 47 | 48 | 49 | ##2015 03 16 更新 50 | 51 | #### 增加了解析爱奇艺视频的mp4地址的方法 52 | 53 | #### 修改了调用方式 54 | 55 | var video = require('./index'); 56 | 57 | video('http://www.iqiyi.com/v_19rrnbyreg.html','MP4',function(err,data){ 58 | console.log(data); 59 | }); 60 | 61 | 62 | 第一个参数是视频地址(必填),第二参数是返回的视频格式(选填,只针对爱奇艺),第三个参数回掉函数(必填) 63 | 64 | 65 | ps:爱奇艺的mp4结果不是很稳定,有时解析出来的地址无法播放,解决办法只能多解析几次,这个是爱奇艺的问题,他的移动版就是这样,经常无法播放 66 | 67 | 68 | 69 | ##2015 10 08 70 | 71 | #### 修改下获取爱奇艺flv格式的问题 72 | #### 爱奇艺获取mp4问题,现在爱奇艺已经改为m3u8格式了 73 | 74 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jiangli on 15/1/6. 3 | */ 4 | 5 | 6 | "use strict"; 7 | 8 | var youku = require('./lib/youku.js'); 9 | var iqiyi = require('./lib/iqiyi.js'); 10 | //var iqiyimp4 = require('./lib/iqiyimp4.js'); 11 | 12 | var iqiyimp4 = require('./lib/anotheriqiyimp4.js'); 13 | 14 | var souhu = require('./lib/souhu.js'); 15 | 16 | /** 17 | * @param url 视频地址,,运行时需要提供视频地址 必填 18 | * @param option 选填,这个参数目前只针对爱奇艺的mp4格式 19 | * @param callback 回掉函数,必填 20 | * 21 | */ 22 | 23 | 24 | module.exports = function(url,option,callback){ 25 | 26 | 27 | 28 | if(arguments.length<2){ 29 | throw "参数不正确"; 30 | } 31 | 32 | var isMp4 = false; 33 | 34 | var _url = url; 35 | 36 | if(typeof url !== 'string'|| !isUrl(url)){ 37 | throw "第一个参数需要是正确的地址"; 38 | } 39 | 40 | if(typeof option === 'string'&&option==='MP4'){ 41 | isMp4 = true; 42 | } 43 | 44 | if(typeof option === 'function'){ 45 | callback = option; 46 | } 47 | 48 | 49 | 50 | if(_url.indexOf('youku.com')>0){ 51 | 52 | youku(_url,callback); 53 | 54 | }else if(_url.indexOf('iqiyi.com')>0){ 55 | 56 | if(isMp4){ 57 | iqiyimp4(_url,callback); 58 | }else{ 59 | iqiyi(_url,callback); 60 | } 61 | 62 | }else if(_url.indexOf('sohu.com')>0){ 63 | 64 | souhu(_url,callback); 65 | 66 | }else{ 67 | callback('暂时只支持优酷和爱奇艺的视频地址'); 68 | } 69 | } 70 | 71 | function isUrl(url){ 72 | 73 | return /^((http|https|ftp):\/\/)?(\w(\:\w)?@)?([0-9a-z_-]+\.)*?([a-z0-9-]+\.[a-z]{2,6}(\.[a-z]{2})?(\:[0-9]{2,6})?)((\/[^?#<>\/\\*":]*)+(\?[^#]*)?(#.*)?)?$/i.test(url); 74 | } -------------------------------------------------------------------------------- /lib/anotheriqiyimp4.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jiangli on 15/3/16. 3 | */ 4 | 5 | 6 | "use strict"; 7 | var request = require('request'); 8 | var btoa = require('btoa'); 9 | var atob = require('atob'); 10 | var url = require('url'); 11 | var cheerio = require('cheerio'); 12 | /** 13 | * [_parseIqiyi 解析爱奇艺视频]mp4格式 14 | * @param [type] $url [description] 15 | * @return [type] [description] 16 | */ 17 | module.exports = function($url,callback){ 18 | 19 | var options = { 20 | url: $url 21 | }; 22 | request(options, function(er, response,body) { 23 | 24 | if (er){ 25 | return callback(er); 26 | } 27 | 28 | var $ = cheerio.load(body); 29 | 30 | var videoArea = $('#flashbox'); 31 | var vid = videoArea.attr('data-player-videoid'); 32 | var tvid = videoArea.attr('data-player-tvid'); 33 | var tvname = ''; 34 | if(vid&&tvid){ 35 | iqiyiParseFlv2(tvid,vid, callback); 36 | }else{ 37 | callback('解析错误'); 38 | } 39 | 40 | 41 | 42 | 43 | }); 44 | } 45 | 46 | 47 | 48 | function iqiyiParseFlv2($tvid,$vid,callback){ 49 | 50 | var static_ip = ['114.80.196.201']; //目前测试通过的可以播放的静态ip地址 51 | var api_url = 'http://cache.m.iqiyi.com/jp/tmts'; 52 | api_url = api_url+'/'+$tvid+'/'+$vid+'/?uid=&cupid=qc_100001_100102&platForm=h5&qyid=80f752d14113d33b394d1390e615e9ba&type=mp4'; 53 | var options = { 54 | url: api_url, 55 | headers: { 56 | 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3', 57 | 'Referer':'http://m.iqiyi.com/' 58 | } 59 | }; 60 | 61 | 62 | var return_data = { 63 | 'mp4':[], 64 | 'title':'', 65 | 'coverImg':'' 66 | }; 67 | request(options, function(er, response,body) { 68 | if (er) 69 | return callback(er); 70 | 71 | var body = body; 72 | 73 | var $video_datas = JSON.parse(body.replace(/var tvInfoJs=/,'')); 74 | if($video_datas.data&&$video_datas.data.m3u){ 75 | var m3u = $video_datas.data.m3u; 76 | 77 | if(m3u.indexOf(static_ip[0])<0){ //初步怀疑爱奇艺这个服务器有问题,如果是这个ip的话,切换到一个新的ip上去 78 | var parse_m3u = url.parse(m3u); 79 | parse_m3u.host = static_ip[0]; 80 | parse_m3u.hostname = static_ip[0]; 81 | m3u = url.format(parse_m3u); 82 | } 83 | return_data['mp4'].push(m3u); 84 | return_data['title'] = $video_datas.data.playInfo.an; 85 | return_data['coverImg'] = $video_datas.data.playInfo.apic; 86 | } 87 | return callback(null,return_data); 88 | }); 89 | } -------------------------------------------------------------------------------- /lib/iqiyi.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jiangli on 15/1/6. 3 | */ 4 | 5 | "use strict"; 6 | var request = require('request'); 7 | var crypto = require('crypto'); 8 | var cheerio = require('cheerio'); 9 | /** 10 | * [_parseIqiyi 解析爱奇艺视频] 11 | * @param [type] $url [description] 12 | * @return [type] [description] 13 | */ 14 | module.exports = function($url,callback){ 15 | 16 | var options = { 17 | url: $url 18 | }; 19 | request(options, function(er, response,body) { 20 | 21 | if (er){ 22 | throw er; 23 | } 24 | 25 | var $ = cheerio.load(body); 26 | 27 | var videoArea = $('#flashbox'); 28 | var vid = videoArea.attr('data-player-videoid'); 29 | var tvid = videoArea.attr('data-player-tvid'); 30 | var tvname = ''; 31 | iqiyiParseFlv2(tvid,vid,tvname,callback); 32 | 33 | }); 34 | } 35 | 36 | 37 | 38 | function md5(str){ 39 | var shasum = crypto.createHash('md5'); 40 | shasum.update(str); 41 | return shasum.digest('hex'); 42 | } 43 | 44 | function utf8_encode(argString) { 45 | if (argString === null || typeof argString === 'undefined') { 46 | return ''; 47 | } 48 | 49 | var string = (argString + ''); // .replace(/\r\n/g, "\n").replace(/\r/g, "\n"); 50 | var utftext = '', 51 | start, end, stringl = 0; 52 | 53 | start = end = 0; 54 | stringl = string.length; 55 | for (var n = 0; n < stringl; n++) { 56 | var c1 = string.charCodeAt(n); 57 | var enc = null; 58 | 59 | if (c1 < 128) { 60 | end++; 61 | } else if (c1 > 127 && c1 < 2048) { 62 | enc = String.fromCharCode( 63 | (c1 >> 6) | 192, (c1 & 63) | 128 64 | ); 65 | } else if ((c1 & 0xF800) != 0xD800) { 66 | enc = String.fromCharCode( 67 | (c1 >> 12) | 224, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128 68 | ); 69 | } else { // surrogate pairs 70 | if ((c1 & 0xFC00) != 0xD800) { 71 | throw new RangeError('Unmatched trail surrogate at ' + n); 72 | } 73 | var c2 = string.charCodeAt(++n); 74 | if ((c2 & 0xFC00) != 0xDC00) { 75 | throw new RangeError('Unmatched lead surrogate at ' + (n - 1)); 76 | } 77 | c1 = ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000; 78 | enc = String.fromCharCode( 79 | (c1 >> 18) | 240, ((c1 >> 12) & 63) | 128, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128 80 | ); 81 | } 82 | if (enc !== null) { 83 | if (end > start) { 84 | utftext += string.slice(start, end); 85 | } 86 | utftext += enc; 87 | start = end = n + 1; 88 | } 89 | } 90 | 91 | if (end > start) { 92 | utftext += string.slice(start, stringl); 93 | } 94 | 95 | return utftext; 96 | }; 97 | 98 | 99 | function calenc($tvId,$enc_key,$deadpara){ 100 | return md5($enc_key+$deadpara+$tvId); 101 | } 102 | 103 | function calauthKey($tvId,$deadpara){ 104 | return md5(""+$deadpara+$tvId); 105 | } 106 | 107 | 108 | function calmd($t,$fileId){ 109 | 110 | var $local3 = ")(*&^flash@#$%a"; 111 | var $local4 = Math.floor(($t / (600))); 112 | return md5($local4+$local3+$fileId); 113 | } 114 | 115 | function getVrsEncodeCode($_arg1){ 116 | var $_local6; 117 | var $_local2 = ""; 118 | var $_local3 = $_arg1.split('-'); 119 | var $_local4 = $_local3.length; 120 | 121 | var $_local5 = ($_local4 - 1); 122 | 123 | while ($_local5 >= 0) { 124 | $_local6 = getVRSXORCode(parseInt($_local3[(($_local4 - $_local5) - 1)], 16), $_local5); 125 | $_local2 = String.fromCharCode($_local6)+$_local2; 126 | $_local5--; 127 | }; 128 | return $_local2; 129 | } 130 | function getVRSXORCode($_arg1, $_arg2){ 131 | 132 | var $_local3 = ($_arg2 % 3); 133 | if ($_local3 == 1){ 134 | return (($_arg1 ^ 121)); 135 | }; 136 | if ($_local3 == 2){ 137 | return (($_arg1 ^ 72)); 138 | }; 139 | return (($_arg1 ^ 103)); 140 | } 141 | 142 | 143 | /** 144 | * [parseFlv2 解析网站flv格式的视频,第二种方法] 145 | * @param [type] $tvid [description] 146 | * @param [type] $vid [description] 147 | * @return [type] [description] 148 | */ 149 | function iqiyiParseFlv2($tvid,$vid,$tvname,callback){ 150 | 151 | var $deadpara = 832; 152 | var $enc_key = "a6f2a01ab9ad4510be0449fab528b82c"; 153 | 154 | var $api_url = "http://cache.video.qiyi.com/vms?key=fvip&src=1702633101b340d8917a69cf8a4b8c7c"; 155 | $api_url = $api_url+"&tvId="+$tvid+"&vid="+$vid+"&vinfo=1&tm="+$deadpara+"&enc="+calenc($tvid,$enc_key,$deadpara)+"&qyid=08ca8cb480c0384cb5d3db068161f44f&&puid=&authKey="+calauthKey($tvid,$deadpara)+"&tn="+Math.random(); 156 | 157 | 158 | var return_data = ""; 159 | request($api_url, function(er, response,body) { 160 | if (er) 161 | return callback(er); 162 | 163 | var body = response.body; 164 | 165 | var $video_datas = JSON.parse(body); 166 | 167 | var $vs = $video_datas.data.vp.tkl[0].vs; //.data.vp.tkl[0].vs 168 | 169 | var coverImg = $video_datas.data.apic; 170 | var $time_url = "http://data.video.qiyi.com/t?tn="+Math.random(); 171 | request($time_url, function(er, response,body) { 172 | if (er) 173 | return callback(er); 174 | 175 | var $time_datas = JSON.parse(body); 176 | var $server_time = $time_datas.t; 177 | 178 | var $urls_data = { 179 | '极速':[], 180 | '流畅':[], 181 | '高清':[], 182 | '超清':[], 183 | '720P':[], 184 | '1080P':[], 185 | '4K':[] 186 | },$data = {}; 187 | 188 | if($vs.length>0){ 189 | for(var i=0;i<$vs.length;i++){ 190 | var $vsi = $vs[i]; 191 | var $fs = $vsi.fs; 192 | 193 | $urls_data['seconds'] = $vsi.duration; 194 | 195 | for(var j=0;j<$fs.length;j++){ 196 | var $val = $fs[j]; 197 | 198 | var $this_link = $val.l; 199 | var $bid = $vsi.bid; 200 | if($bid == 4 || $bid == 5 || $bid == 10){ 201 | $this_link = getVrsEncodeCode($this_link); 202 | } 203 | var $sp = $this_link.split('/'); 204 | var $sp_length = $sp.length; 205 | 206 | var $fileId = $sp[$sp_length-1].split('.')[0]; 207 | 208 | var $this_key = calmd($server_time,$fileId); 209 | 210 | $this_link = $this_link+'?ran='+$deadpara+'&qyid=08ca8cb480c0384cb5d3db068161f44f&qypid='+$tvid+'_11&retry=1'; 211 | 212 | var $final_url = "http://data.video.qiyi.com/"+$this_key+"/videos"+$this_link; 213 | if($bid == 96)$urls_data['极速'].push($final_url); 214 | if($bid == 1)$urls_data['流畅'].push($final_url); 215 | if($bid == 2)$urls_data['高清'].push($final_url); 216 | if($bid == 3)$urls_data['超清'].push($final_url); 217 | if($bid == 4)$urls_data['720P'].push($final_url); 218 | if($bid == 5)$urls_data['1080P'].push($final_url); 219 | if($bid == 10)$urls_data['4K'].push($final_url); 220 | 221 | } 222 | } 223 | $urls_data['title'] = $tvname; 224 | $urls_data['coverImg'] = coverImg; 225 | return callback(null,$urls_data); 226 | }else{ 227 | return callback(null,null); 228 | } 229 | }); 230 | }); 231 | } -------------------------------------------------------------------------------- /lib/iqiyimp4.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jiangli on 15/3/16. 3 | */ 4 | 5 | 6 | "use strict"; 7 | var request = require('request'); 8 | var btoa = require('btoa'); 9 | var atob = require('atob'); 10 | var url = require('url'); 11 | /** 12 | * [_parseIqiyi 解析爱奇艺视频]mp4格式 13 | * @param [type] $url [description] 14 | * @return [type] [description] 15 | */ 16 | module.exports = function($url,callback){ 17 | 18 | $url = $url.replace(/www/,'m'); 19 | var return_data = ""; 20 | 21 | var options = { 22 | url: $url, 23 | headers: { 24 | 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3' 25 | } 26 | }; 27 | request(options, function(er, response,body) { 28 | 29 | if (er){ 30 | return callback(er); 31 | } 32 | 33 | var body = body; 34 | var result = body.match(/"vid":"(.*?)".*?"vn":(.*?),/g); 35 | if(result.length>0){ 36 | var r1 = result[0]; 37 | r1 = '{'+r1+'\"blank\"\:0}'; 38 | r1 = JSON.parse(r1); 39 | 40 | var $vid = r1.vid?r1.vid:''; 41 | var $tvid = r1.tvid?r1.tvid:''; 42 | 43 | if($vid!==''&&$tvid!==''){ 44 | 45 | iqiyiParseFlv2($tvid,$vid, callback); 46 | 47 | }else{ 48 | return callback(null,null); 49 | } 50 | } 51 | }); 52 | } 53 | 54 | 55 | 56 | 57 | 58 | var Q = {}; 59 | Q.PageInfo = Q.PageInfo || {}; 60 | Q.PageInfo.cname = ""; 61 | Q.PageInfo.page = "play"; 62 | Q.PageInfo.playInfo = { 63 | 64 | }; 65 | var _0 = ["fromCharCode", "", "length", "charCodeAt", "random", "floor", "cache", "undefined", "PageInfo", "substr", 66 | // 67 | "height", "orientation", "width", "devicePixelRatio", "round", "screenTop", "outerHeight", "_", 68 | 69 | "localStorage", "iqiyi", "getItem", "startTime", "removeItem", "setItem", "referrer", "baidu.com", 70 | 71 | "indexOf", "UCW", "_boluoWebView", "BOL", 72 | 73 | "TURVek1EYzJOZz09TWpnMk1qYzFPREppTWpGak5UZzVNVFJtWmpGa1lUWmtNZz09", 74 | 75 | "push", "getTime", "sin", "abs", "function%20javaEnabled%28%29%20%7B%20%5Bnative%20code%5D%20%7D", 76 | 77 | "null", "WebkitAppearance", "style", "documentElement", "javaEnabled", "sgve", "sijsc", "d", "jc", 78 | 79 | "md", "join", "URL", ";", ";&tim=", "__jsT", "t", "__refI", "sc", "src", "d846d0c32d664d32b6b54ea48997a589", 80 | 81 | "qd_jsin", "qd_wsz", "jfakmkafklw23321f4ea32459", "__ctmM", "__sigC", "__cliT", "h5"]; 82 | 83 | function weorjjigh(e, n, t, a, r, o) { 84 | function i(e, n) { 85 | return ((e >> 1) + (n >> 1) << 1) + (1 & e) + (1 & n) 86 | } 87 | function c(e, n, t, a) { 88 | e > 0 && 5 > e ? t >= 0 ? p << 6 > n ? (a = [a[3], i(a[1], (x = i(i(a[0], [a[1] & a[2] | ~a[1] & a[3], a[3] & a[1] | ~a[3] & a[2], a[1] ^ a[2] ^ a[3], a[2] ^ (a[1] | ~a[3])][y = t >> 4]), i(j(m(t + 1)) * _ | 0, B[[t, 5 * t + 1, 3 * t + 5, 7 * t][y] % 16 + (n++ >>> 6)]))) << (y = [7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21][4 * y + t % 4]) | x >>> 32 - y), a[1], a[2]], 63 & n ? c(e, n, 63 & n, a) : (A = [i(a[0], A[0]), i(a[1], A[1]), i(a[2], A[2]), i(a[3], A[3])], c(e, n + 960, 63 & n, A))) : (B = [], p = "") : C ? c(12, n, 0, "1330848328159996040014666388890612187513113019274921193194001265062984") : c(13, n, 0, "03603482862038069294069554231005389970290678182205184924421910467011111731869494") : e > 6 && 10 > e ? (e = atob(unescape(a)), t < e.length ? (B[n >> 2] |= e.charCodeAt(t++) << 8 * (n % 4), c(9, ++n, t, M)) : (d(String.fromCharCode(81)) && (B[n >> 2] |= 1 << (n % 4 << 3) + 7), B[p = (n + 8 >> 6 << 4) + 14] = n << 3, c(3, 0, 0, A))) : e > 11 && 15 > e && (t < (a.length << 1) / 5 ? (B[n >> 2] |= (parseInt(a.substr(10 * (t >> 2), 10), 10) >> (3 - (3 & t) << 3) & 255 ^ (a.length << 1) / 5 - t++) << ((3 & n++) << 3), c(14, n, t, a)) : c(7, n, 0, M)) 89 | } 90 | function d(e) { 91 | return "undefined" != typeof eval(e) 92 | } 93 | var u = 667, 94 | h = 320, 95 | s = 0; 96 | (90 === s || -90 === s) && (u = u > h ? h : u); 97 | var f = 1; 98 | u = Math.round(u / f); 99 | var w = Math.round(0 / f), 100 | b = Math.round(667 / f), 101 | v = u - b - w, 102 | g = btoa(w + "_" + v), 103 | l = "", 104 | _ = 4294967296, 105 | j = Math.abs, 106 | m = Math.sin, 107 | p = ""; 108 | //d("ucweb") && (p = null, p && p.indexOf("baidu.com") >= 0 || (l += btoa("UCW"))), 109 | l += d("_0") ? (l ? "_" : "") + btoa("BOL") : ""; 110 | var C = n, 111 | M = e, 112 | T = 7, 113 | k = {}, 114 | R = (new Date).getTime(); 115 | k.cache = (n ? o + "" + r : R - T) + "", 116 | M = escape(btoa(M)), 117 | k.cache = escape(btoa(C ? k.cache + a + "" + t : k.cache)); 118 | var x, y, A = [x = 1732584193, y = -271733879, ~x, ~y], 119 | B = [], 120 | D = function () { 121 | for (p = atob(unescape(k.cache)), y = 0; y < p.length;) B[y >> 2] |= p.charCodeAt(y) << 8 * (y++ % 4); 122 | for (c(1, y, -1, B), x = 0; 32 > x;) p += (A[x >> 3] >> 4 * (1 ^ 7 & x++) & 15).toString(16); 123 | return p 124 | }, 125 | E = function () { 126 | var e = "function%20javaEnabled%28%29%20%7B%20%5Bnative%20code%5D%20%7D", 127 | n = "null"; 128 | return n = "sijsc", 129 | n 130 | }; 131 | if (n) { 132 | var I = {};c 133 | return I.md = D, 134 | I.jc = E, 135 | I.d = R, 136 | I 137 | } 138 | var S = D(); 139 | if (S.length > 4) { 140 | var U = ""; 141 | U += "http://www.iqiyi.com" + ";1;&tim=" + R, 142 | U = encodeURIComponent(U); 143 | var W = {}; 144 | return W.src = "d846d0c32d664d32b6b54ea48997a589", 145 | W.sc = S, 146 | W.__refI = U, 147 | l && (W.qd_jsin = l), 148 | g && (W.qd_wsz = g), 149 | W.t = R - 7, 150 | W.__jsT = E(), 151 | W 152 | } 153 | } 154 | 155 | 156 | function iqiyiParseFlv2($tvid,$vid,callback){ 157 | 158 | 159 | var static_ip = ['114.80.196.201']; //目前测试通过的可以播放的静态ip地址 160 | var h = weorjjigh($tvid); 161 | var $api_url = 'http://cache.m.iqiyi.com/jp/tmts'; 162 | $api_url = $api_url+'/'+$tvid+'/'+$vid+'/?uid=&cupid=qc_100001_100102&platForm=h5&qyid=80f752d14113d33b394d1390e615e9ba&type=mp4&rate=1&src='+h.src+'&sc='+ h.sc+'&t='+ h.t+ 163 | '&__refI='+ h.__refI+'&qd_wsz='+ h.qd_wsz+'&__jsT='+ h.__jsT; 164 | 165 | var return_data = { 166 | 'mp4':[], 167 | 'title':'', 168 | 'coverImg':'' 169 | }; 170 | request($api_url, function(er, response,body) { 171 | if (er) 172 | return callback(er); 173 | 174 | var body = body; 175 | 176 | var $video_datas = JSON.parse(body.replace(/var tvInfoJs=/,'')); 177 | 178 | if($video_datas.data&&$video_datas.data.m3u){ 179 | var m3u = $video_datas.data.m3u; 180 | 181 | if(m3u.indexOf(static_ip[0])<0){ //初步怀疑爱奇艺这个服务器有问题,如果是这个ip的话,切换到一个新的ip上去 182 | var parse_m3u = url.parse(m3u); 183 | parse_m3u.host = static_ip[0]; 184 | parse_m3u.hostname = static_ip[0]; 185 | m3u = url.format(parse_m3u); 186 | } 187 | return_data['mp4'].push(m3u); 188 | return_data['title'] = $video_datas.data.playInfo.an; 189 | return_data['coverImg'] = $video_datas.data.playInfo.apic; 190 | } 191 | return callback(null,return_data); 192 | }); 193 | } -------------------------------------------------------------------------------- /lib/souhu.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jiangli on 15/4/10. 3 | */ 4 | 5 | "use strict"; 6 | var request = require('request'); 7 | var async = require('async'); 8 | 9 | module.exports = function($url,callback){ 10 | 11 | 12 | request($url, function(err, response,body) { 13 | if(err){ 14 | return callback(err); 15 | }else{ 16 | 17 | 18 | if(body){ 19 | var result = body.match(/\s*vid=\s*"([^"]+)"/); 20 | 21 | if(result&&result[1]){ 22 | 23 | var $sub_ip ='220.181.118.53'; 24 | var $vid = result[1]-0; 25 | var $api_url = "http://"+$sub_ip+"/vrs_flash.action?vid="+$vid+"&af=1&out=-1&g=8&r=2&t="+Math.random(); 26 | 27 | request($api_url, function(err, response,body) { 28 | if(err){ 29 | return callback(err); 30 | }else{ 31 | if(body){ 32 | var $video_datas = JSON.parse(body); 33 | 34 | if($video_datas['data']){ 35 | var video_data = $video_datas['data']; 36 | var title = video_data['tvName']; 37 | var coverImg = video_data['coverImg']; 38 | parseSoHuVideoUrl(video_data['relativeId'],$sub_ip, function (err,data) { 39 | if(err) return callback(err); 40 | if(data){ 41 | data.title = title; 42 | data.coverImg = coverImg; 43 | return callback(null,data); 44 | } 45 | }); 46 | 47 | }else{ 48 | return callback(null,null); 49 | } 50 | } 51 | } 52 | }); 53 | } 54 | }else{ 55 | return callback(null,null); 56 | } 57 | } 58 | 59 | }); 60 | 61 | } 62 | 63 | 64 | //拼接参数解析视频的方法 65 | function parseSoHuVideoUrl($vid,$sub_ip,callback){ 66 | 67 | var $api_url = "http://"+$sub_ip+"/vrs_flash.action?vid="+$vid+"&out=0&g=8&r=2&t="+Math.random(); 68 | 69 | var data; 70 | request($api_url, function(err, response,body) { 71 | if(err){ 72 | return callback(err); 73 | } 74 | if(body){ 75 | 76 | var $video_data = JSON.parse(body); 77 | 78 | var $rands = Math.floor(Math.random()*22)+1; 79 | 80 | var $params = []; 81 | if($video_data['data']){ 82 | for(var i=0;i< $video_data['data'].clipsURL.length;i++ ){ 83 | var $su_key = $video_data['data']['su'][i]; 84 | var $url = "http://"+$video_data['allot']+"/?prot="+$video_data['prot']+"&file="+$video_data['data'].clipsURL[i].replace(/http:\/\/data.vod.itc.cn/,'')+"&new="+$su_key; 85 | $params[i]= { 86 | 'su_key':'', 87 | 'rands':'', 88 | 'url':'' 89 | }; 90 | $params[i].su_key= $su_key; 91 | $params[i].rands = $rands; 92 | $params[i].url = $url; 93 | } 94 | soHu_rolling_curl_url($params,callback); 95 | 96 | }else{ 97 | return callback("请求失败",null); 98 | } 99 | 100 | }else{ 101 | return callback("请求失败",null); 102 | } 103 | }); 104 | } 105 | 106 | 107 | function soHu_rolling_curl_url(params,callback){ 108 | 109 | var return_result = {'url':[]}; 110 | 111 | 112 | async.each(params, function (param,callback) { 113 | 114 | request(param.url, function (err,response,body) { 115 | 116 | if(err){ 117 | return callback(err); 118 | } 119 | 120 | if(body){ 121 | var param_array = body.split('|'); 122 | 123 | 124 | var su_key = param.su_key; 125 | var rands = param.rands; 126 | 127 | 128 | var realurl = (param_array[0].slice(0,-1)+ su_key + "?start=&key=" + param_array[3] + "&n="+rands+"&a=4019"); 129 | param.realurl = realurl; 130 | return callback(); 131 | 132 | }else{ 133 | 134 | return callback('请求失败'); 135 | } 136 | 137 | }); 138 | 139 | 140 | }, function(err){ 141 | 142 | if(err) return callback(err); 143 | 144 | for(var i=0;i1){ 21 | 22 | return _getYouku($matches[1].trim(),callback); 23 | 24 | }else{ 25 | return null; 26 | } 27 | } 28 | 29 | 30 | function _getYouku($vid,callback){ 31 | 32 | var $base = "http://v.youku.com/player/getPlaylist/VideoIDS/"; 33 | var $blink = $base+$vid; 34 | var $link = $blink+"/Pf/4/ctype/12/ev/1"; 35 | request($link, function(er, response,body) { 36 | if (er) 37 | return callback(er); 38 | 39 | var $retval = body; 40 | 41 | if($retval){ 42 | 43 | var $rs = JSON.parse($retval); 44 | 45 | request($blink, function(er, response,body) { 46 | if (er) 47 | return callback(er); 48 | var $data = { 49 | '1080Phd3':[], 50 | '超清hd2':[], 51 | '高清mp4':[], 52 | '高清flvhd':[], 53 | '标清flv':[], 54 | '高清3gphd':[], 55 | '3gp':[] 56 | }; 57 | 58 | var $bretval = body; 59 | var $brs = JSON.parse($bretval); 60 | 61 | var $rs_data = $rs.data[0]; 62 | var $brs_data = $brs.data[0]; 63 | 64 | if($rs_data.error){ 65 | return callback(null, $data['error'] = $rs_data.error); 66 | } 67 | 68 | 69 | var $streamtypes = $rs_data.streamtypes; //可以输出的视频清晰度 70 | 71 | var $streamfileids = $rs_data.streamfileids; 72 | var $seed = $rs_data.seed; 73 | var $segs = $rs_data.segs; 74 | var $ip = $rs_data.ip; 75 | var $bsegs = $brs_data.segs; 76 | 77 | var yk_e_result = yk_e('becaf9be', yk_na($rs_data.ep)).split('_'); 78 | var $sid = yk_e_result[0], $token = yk_e_result[1]; 79 | for(var $key in $segs){ 80 | 81 | if(in_array($key,$streamtypes)){ 82 | var $segs_key_val = $segs[$key]; 83 | 84 | for(var kk=0;kk<$segs_key_val.length;kk++){ 85 | var $v = $segs_key_val[kk]; 86 | var $no = $v.no.toString(16).toUpperCase(); //转换为16进制 大写 87 | 88 | if($no.length == 1){ 89 | $no ="0"+$no; //no 为每段视频序号 90 | } 91 | //构建视频地址K值 92 | var $_k = $v.k; 93 | if ((!$_k || $_k == '') || $_k == '-1') { 94 | $_k = $bsegs[$key][kk].k; 95 | } 96 | var $fileId = getFileid($streamfileids[$key],$seed); 97 | $fileId = $fileId.substr(0,8)+$no+$fileId.substr(10); 98 | var m0 = yk_e('bf7e5f01', $sid + '_' + $fileId + '_' + $token); 99 | var m1 = yk_d(m0); 100 | var iconv_result = iconv.decode(new Buffer(m1), 'UTF-8'); 101 | 102 | if(iconv_result!=""){ 103 | var $ep = urlencode(iconv_result); 104 | 105 | var $typeArray = []; 106 | $typeArray['flv']= 'flv'; 107 | $typeArray['mp4']= 'mp4'; 108 | $typeArray['hd2']= 'flv'; 109 | $typeArray['3gphd']= 'mp4'; 110 | $typeArray['3gp']= 'flv'; 111 | $typeArray['hd3']= 'flv'; 112 | 113 | //判断视频清晰度 114 | var $sharpness = []; //清晰度 数组 115 | $sharpness['flv']= '标清flv'; 116 | $sharpness['flvhd']= '高清flvhd'; 117 | $sharpness['mp4']= '高清mp4'; 118 | $sharpness['hd2']= '超清hd2'; 119 | $sharpness['3gphd']= '高清3gphd'; 120 | $sharpness['3gp']= '3gp'; 121 | $sharpness['hd3']= '1080Phd3'; 122 | var $fileType = $typeArray[$key]; 123 | $data[$sharpness[$key]][kk] = "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; 124 | } 125 | } 126 | } 127 | } 128 | //返回 图片 标题 链接 时长 视频地址 129 | $data['coverImg'] = $rs['data'][0]['logo']; 130 | $data['title'] = $rs['data'][0]['title']; 131 | $data['seconds'] = $rs['data'][0]['seconds']; 132 | return callback(null,$data); 133 | }); 134 | }else{ 135 | return callback(null,null); 136 | } 137 | }) 138 | 139 | } 140 | function urlencode(str) { 141 | str = (str + '').toString(); 142 | 143 | return encodeURIComponent(str) 144 | .replace(/!/g, '%21') 145 | .replace(/'/g, '%27') 146 | .replace(/\(/g, '%28') 147 | .replace(/\)/g, '%29') 148 | .replace(/\*/g, '%2A') 149 | .replace(/%20/g, '+'); 150 | }; 151 | 152 | function in_array(needle, haystack, argStrict) { 153 | var key = '', 154 | strict = !! argStrict; 155 | if (strict) { 156 | for (key in haystack) { 157 | if (haystack[key] === needle) { 158 | return true; 159 | } 160 | } 161 | } else { 162 | for (key in haystack) { 163 | if (haystack[key] == needle) { 164 | return true; 165 | } 166 | } 167 | } 168 | 169 | return false; 170 | }; 171 | 172 | 173 | //start 获得优酷视频需要用到的方法 174 | function getSid(){ 175 | 176 | var $sid = new Date().getTime()+(Math.random() * 9001+10000); 177 | return $sid; 178 | } 179 | 180 | function getKey($key1,$key2){ 181 | var $a = parseInt($key1,16); 182 | var $b = $a ^0xA55AA5A5; 183 | var $b = $b.toString(16); 184 | return $key2+$b; 185 | } 186 | 187 | function getFileid($fileId,$seed){ 188 | var $mixed = getMixString($seed); 189 | var $ids = $fileId.replace(/(\**$)/g, "").split('*'); //去掉末尾的*号分割为数组 190 | var $realId = ""; 191 | for (var $i=0;$i<$ids.length;$i++){ 192 | var $idx = $ids[$i]; 193 | $realId += $mixed.substr($idx,1); 194 | } 195 | return $realId; 196 | } 197 | 198 | function getMixString($seed){ 199 | var $mixed = ""; 200 | var $source = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/\\:._-1234567890"; 201 | var $len = $source.length; 202 | for(var $i=0;$i<$len;$i++){ 203 | $seed = ($seed * 211 + 30031)%65536; 204 | var $index = ($seed / 65536 * $source.length); 205 | var $c = $source.substr($index,1); 206 | $mixed += $c; 207 | $source = $source.replace($c,""); 208 | } 209 | return $mixed; 210 | } 211 | function yk_d($a){ 212 | 213 | if (!$a) { 214 | return ''; 215 | } 216 | var $f = $a.length; 217 | var $b = 0; 218 | var $str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; 219 | for (var $c = ''; $b < $f;) { 220 | var $e = charCodeAt($a, $b++) & 255; 221 | if ($b == $f) { 222 | $c += charAt($str, $e >> 2); 223 | $c += charAt($str, ($e & 3) << 4); 224 | $c += '=='; 225 | break; 226 | } 227 | var $g = charCodeAt($a, $b++); 228 | if ($b == $f) { 229 | $c += charAt($str, $e >> 2); 230 | $c += charAt($str, ($e & 3) << 4 | ($g & 240) >> 4); 231 | $c += charAt($str, ($g & 15) << 2); 232 | $c += '='; 233 | break; 234 | } 235 | var $h = charCodeAt($a, $b++); 236 | $c += charAt($str, $e >> 2); 237 | $c += charAt($str, ($e & 3) << 4 | ($g & 240) >> 4); 238 | $c += charAt($str, ($g & 15) << 2 | ($h & 192) >> 6); 239 | $c += charAt($str, $h & 63); 240 | } 241 | return $c; 242 | } 243 | function yk_na($a){ 244 | if (!$a) { 245 | return ''; 246 | } 247 | var $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'; 248 | var $h = $sz.split(','); 249 | var $i = $a.length; 250 | var $f = 0; 251 | for (var $e = ''; $f < $i;) { 252 | var $c; 253 | do { 254 | $c = $h[charCodeAt($a, $f++) & 255]; 255 | } while ($f < $i && -1 == $c); 256 | 257 | if (-1 == $c) { 258 | break; 259 | } 260 | var $b; 261 | do { 262 | $b = $h[charCodeAt($a, $f++) & 255]; 263 | } while ($f < $i && -1 == $b); 264 | if (-1 == $b) { 265 | break; 266 | } 267 | $e += String.fromCharCode($c << 2 | ($b & 48) >> 4); 268 | do { 269 | $c = charCodeAt($a, $f++) & 255; 270 | if (61 == $c) { 271 | return $e; 272 | } 273 | $c = $h[$c]; 274 | } while ($f < $i && -1 == $c); 275 | if (-1 == $c) { 276 | break; 277 | } 278 | $e += String.fromCharCode(($b & 15) << 4 | ($c & 60) >> 2); 279 | do { 280 | $b = charCodeAt($a, $f++) & 255; 281 | if (61 == $b) { 282 | return $e; 283 | } 284 | $b = $h[$b]; 285 | } while ($f < $i && -1 == $b); 286 | if (-1 == $b) { 287 | break; 288 | } 289 | $e += String.fromCharCode(($c & 3) << 6 | $b); 290 | } 291 | return $e; 292 | } 293 | function yk_e($a, $c){ 294 | 295 | var $b = []; 296 | for (var $f = 0, $i, $e = '', $h = 0; 256 > $h; $h++) { 297 | $b[$h] = $h; 298 | } 299 | for ($h = 0; 256 > $h; $h++) { 300 | $f = (($f + $b[$h]) + charCodeAt($a, $h % $a.length)) % 256; 301 | $i = $b[$h]; 302 | $b[$h] = $b[$f]; 303 | $b[$f] = $i; 304 | } 305 | for (var $q = ($f = ($h = 0)); $q < $c.length; $q++) { 306 | $h = ($h + 1) % 256; 307 | $f = ($f + $b[$h]) % 256; 308 | $i = $b[$h]; 309 | $b[$h] = $b[$f]; 310 | $b[$f] = $i; 311 | $e += String.fromCharCode(charCodeAt($c, $q) ^ $b[($b[$h] + $b[$f]) % 256]); 312 | } 313 | return $e; 314 | } 315 | 316 | 317 | function md5(str){ 318 | var shasum = crypto.createHash('md5'); 319 | shasum.update(str); 320 | return shasum.digest('hex'); 321 | 322 | } 323 | 324 | function charCodeAt($str, $index){ 325 | var $charCode = []; 326 | var $key = md5($str); 327 | $index = $index + 1; 328 | if ($charCode[$key]) { 329 | return $charCode[$key][$index]; 330 | } 331 | $charCode[$key] = unpack('C*', $str); 332 | 333 | return $charCode[$key][$index]; 334 | } 335 | 336 | function charAt($str, $index){ 337 | return $str.substr($index, 1); 338 | } 339 | 340 | function unpack(format, data) { 341 | var formatPointer = 0, dataPointer = 0, result = {}, instruction = '', 342 | quantifier = '', label = '', currentData = '', i = 0, j = 0, 343 | word = '', fbits = 0, ebits = 0, dataByteLength = 0; 344 | 345 | var fromIEEE754 = function(bytes, ebits, fbits) { 346 | // Bytes to bits 347 | var bits = []; 348 | for (var i = bytes.length; i; i -= 1) { 349 | var m_byte = bytes[i - 1]; 350 | for (var j = 8; j; j -= 1) { 351 | bits.push(m_byte % 2 ? 1 : 0); m_byte = m_byte >> 1; 352 | } 353 | } 354 | bits.reverse(); 355 | var str = bits.join(''); 356 | 357 | // Unpack sign, exponent, fraction 358 | var bias = (1 << (ebits - 1)) - 1; 359 | var s = parseInt(str.substring(0, 1), 2) ? -1 : 1; 360 | var e = parseInt(str.substring(1, 1 + ebits), 2); 361 | var f = parseInt(str.substring(1 + ebits), 2); 362 | 363 | // Produce number 364 | if (e === (1 << ebits) - 1) { 365 | return f !== 0 ? NaN : s * Infinity; 366 | } 367 | else if (e > 0) { 368 | return s * Math.pow(2, e - bias) * (1 + f / Math.pow(2, fbits)); 369 | } 370 | else if (f !== 0) { 371 | return s * Math.pow(2, -(bias-1)) * (f / Math.pow(2, fbits)); 372 | } 373 | else { 374 | return s * 0; 375 | } 376 | } 377 | 378 | while (formatPointer < format.length) { 379 | instruction = format.charAt(formatPointer); 380 | 381 | // Start reading 'quantifier' 382 | quantifier = ''; 383 | formatPointer++; 384 | while ((formatPointer < format.length) && 385 | (format.charAt(formatPointer).match(/[\d\*]/) !== null)) { 386 | quantifier += format.charAt(formatPointer); 387 | formatPointer++; 388 | } 389 | if (quantifier === '') { 390 | quantifier = '1'; 391 | } 392 | 393 | 394 | // Start reading label 395 | label = ''; 396 | while ((formatPointer < format.length) && 397 | (format.charAt(formatPointer) !== '/')) { 398 | label += format.charAt(formatPointer); 399 | formatPointer++; 400 | } 401 | if (format.charAt(formatPointer) === '/') { 402 | formatPointer++; 403 | } 404 | 405 | // Process given instruction 406 | switch (instruction) { 407 | case 'a': // NUL-padded string 408 | case 'A': // SPACE-padded string 409 | if (quantifier === '*') { 410 | quantifier = data.length - dataPointer; 411 | } else { 412 | quantifier = parseInt(quantifier, 10); 413 | } 414 | currentData = data.substr(dataPointer, quantifier); 415 | dataPointer += quantifier; 416 | var currentResult; 417 | if (instruction === 'a') { 418 | currentResult = currentData.replace(/\0+$/, ''); 419 | } else { 420 | currentResult = currentData.replace(/ +$/, ''); 421 | } 422 | result[label] = currentResult; 423 | break; 424 | 425 | case 'h': // Hex string, low nibble first 426 | case 'H': // Hex string, high nibble first 427 | if (quantifier === '*') { 428 | quantifier = data.length - dataPointer; 429 | } else { 430 | quantifier = parseInt(quantifier, 10); 431 | } 432 | currentData = data.substr(dataPointer, quantifier); 433 | dataPointer += quantifier; 434 | 435 | if (quantifier > currentData.length) { 436 | throw new Error('Warning: unpack(): Type ' + instruction + 437 | ': not enough input, need ' + quantifier); 438 | } 439 | 440 | currentResult = ''; 441 | for (i = 0; i < currentData.length; i++) { 442 | word = currentData.charCodeAt(i).toString(16); 443 | if (instruction === 'h') { 444 | word = word[1] + word[0]; 445 | } 446 | currentResult += word; 447 | } 448 | result[label] = currentResult; 449 | break; 450 | 451 | case 'c': // signed char 452 | case 'C': // unsigned c 453 | if (quantifier === '*') { 454 | quantifier = data.length - dataPointer; 455 | } else { 456 | quantifier = parseInt(quantifier, 10); 457 | } 458 | 459 | currentData = data.substr(dataPointer, quantifier); 460 | dataPointer += quantifier; 461 | 462 | for (i = 0; i < currentData.length; i++) { 463 | currentResult = currentData.charCodeAt(i); 464 | if ((instruction === 'c') && (currentResult >= 128)) { 465 | currentResult -= 256; 466 | } 467 | result[label + (quantifier > 1 ? 468 | (i + 1) : 469 | '')] = currentResult; 470 | } 471 | break; 472 | 473 | case 'S': // unsigned short (always 16 bit, machine byte order) 474 | case 's': // signed short (always 16 bit, machine byte order) 475 | case 'v': // unsigned short (always 16 bit, little endian byte order) 476 | if (quantifier === '*') { 477 | quantifier = (data.length - dataPointer) / 2; 478 | } else { 479 | quantifier = parseInt(quantifier, 10); 480 | } 481 | 482 | currentData = data.substr(dataPointer, quantifier * 2); 483 | dataPointer += quantifier * 2; 484 | 485 | for (i = 0; i < currentData.length; i += 2) { 486 | // sum per word; 487 | currentResult = ((currentData.charCodeAt(i + 1) & 0xFF) << 8) + 488 | (currentData.charCodeAt(i) & 0xFF); 489 | if ((instruction === 's') && (currentResult >= 32768)) { 490 | currentResult -= 65536; 491 | } 492 | result[label + (quantifier > 1 ? 493 | ((i / 2) + 1) : 494 | '')] = currentResult; 495 | } 496 | break; 497 | 498 | case 'n': // unsigned short (always 16 bit, big endian byte order) 499 | if (quantifier === '*') { 500 | quantifier = (data.length - dataPointer) / 2; 501 | } else { 502 | quantifier = parseInt(quantifier, 10); 503 | } 504 | 505 | currentData = data.substr(dataPointer, quantifier * 2); 506 | dataPointer += quantifier * 2; 507 | 508 | for (i = 0; i < currentData.length; i += 2) { 509 | // sum per word; 510 | currentResult = ((currentData.charCodeAt(i) & 0xFF) << 8) + 511 | (currentData.charCodeAt(i + 1) & 0xFF); 512 | result[label + (quantifier > 1 ? 513 | ((i / 2) + 1) : 514 | '')] = currentResult; 515 | } 516 | break; 517 | 518 | case 'i': // signed integer (machine dependent size and byte order) 519 | case 'I': // unsigned integer (machine dependent size & byte order) 520 | case 'l': // signed long (always 32 bit, machine byte order) 521 | case 'L': // unsigned long (always 32 bit, machine byte order) 522 | case 'V': // unsigned long (always 32 bit, little endian byte order) 523 | if (quantifier === '*') { 524 | quantifier = (data.length - dataPointer) / 4; 525 | } else { 526 | quantifier = parseInt(quantifier, 10); 527 | } 528 | 529 | currentData = data.substr(dataPointer, quantifier * 4); 530 | dataPointer += quantifier * 4; 531 | 532 | for (i = 0; i < currentData.length; i += 4) { 533 | currentResult = 534 | ((currentData.charCodeAt(i + 3) & 0xFF) << 24) + 535 | ((currentData.charCodeAt(i + 2) & 0xFF) << 16) + 536 | ((currentData.charCodeAt(i + 1) & 0xFF) << 8) + 537 | ((currentData.charCodeAt(i) & 0xFF)); 538 | result[label + (quantifier > 1 ? 539 | ((i / 4) + 1) : 540 | '')] = currentResult; 541 | } 542 | 543 | break; 544 | 545 | case 'N': // unsigned long (always 32 bit, little endian byte order) 546 | if (quantifier === '*') { 547 | quantifier = (data.length - dataPointer) / 4; 548 | } else { 549 | quantifier = parseInt(quantifier, 10); 550 | } 551 | 552 | currentData = data.substr(dataPointer, quantifier * 4); 553 | dataPointer += quantifier * 4; 554 | 555 | for (i = 0; i < currentData.length; i += 4) { 556 | currentResult = 557 | ((currentData.charCodeAt(i) & 0xFF) << 24) + 558 | ((currentData.charCodeAt(i + 1) & 0xFF) << 16) + 559 | ((currentData.charCodeAt(i + 2) & 0xFF) << 8) + 560 | ((currentData.charCodeAt(i + 3) & 0xFF)); 561 | result[label + (quantifier > 1 ? 562 | ((i / 4) + 1) : 563 | '')] = currentResult; 564 | } 565 | 566 | break; 567 | 568 | case 'f': //float 569 | case 'd': //double 570 | ebits = 8; 571 | fbits = (instruction === 'f') ? 23 : 52; 572 | dataByteLength = 4; 573 | if (instruction === 'd') { 574 | ebits = 11; 575 | dataByteLength = 8; 576 | } 577 | 578 | if (quantifier === '*') { 579 | quantifier = (data.length - dataPointer) / dataByteLength; 580 | } else { 581 | quantifier = parseInt(quantifier, 10); 582 | } 583 | 584 | currentData = data.substr(dataPointer, quantifier * dataByteLength); 585 | dataPointer += quantifier * dataByteLength; 586 | 587 | for (i = 0; i < currentData.length; i += dataByteLength) { 588 | data = currentData.substr(i, dataByteLength); 589 | 590 | var bytes = []; 591 | for (j = data.length - 1; j >= 0; --j) { 592 | bytes.push(data.charCodeAt(j)); 593 | } 594 | result[label + (quantifier > 1 ? 595 | ((i / 4) + 1) : 596 | '')] = fromIEEE754(bytes, ebits, fbits); 597 | } 598 | 599 | break; 600 | 601 | case 'x': // NUL byte 602 | case 'X': // Back up one byte 603 | case '@': // NUL byte 604 | if (quantifier === '*') { 605 | quantifier = data.length - dataPointer; 606 | } else { 607 | quantifier = parseInt(quantifier, 10); 608 | } 609 | 610 | if (quantifier > 0) { 611 | if (instruction === 'X') { 612 | dataPointer -= quantifier; 613 | } else { 614 | if (instruction === 'x') { 615 | dataPointer += quantifier; 616 | } else { 617 | dataPointer = quantifier; 618 | } 619 | } 620 | } 621 | break; 622 | 623 | default: 624 | throw new Error('Warning: unpack() Type ' + instruction + 625 | ': unknown format code'); 626 | } 627 | } 628 | return result; 629 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "parseVideo", 3 | "version": "1.0.0", 4 | "description": "获取优酷和奇艺的视频下载地址", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "github.com" 12 | }, 13 | "keywords": [ 14 | "video", 15 | "youku", 16 | "iqiyi" 17 | ], 18 | "author": "jiangli", 19 | "license": "MIT", 20 | "dependencies": { 21 | "async": "^0.9.0", 22 | "atob": "^1.1.2", 23 | "btoa": "^1.1.2", 24 | "cheerio": "^0.19.0", 25 | "iconv-lite": "^0.4.5", 26 | "request": "^2.51.0" 27 | }, 28 | "devDependencies": { 29 | "mm": "*", 30 | "mocha": "*", 31 | "pedding": "*", 32 | "rewire": "*", 33 | "should": "*", 34 | "supertest": "*" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jiangli on 15/1/6. 3 | */ 4 | 5 | var index = require('./index'); 6 | 7 | 8 | 9 | //提供了url,直接运行node test.js 10 | //index('http://www.iqiyi.com/v_19rrnphkz4.html','MP4',function(err,data){ 11 | // console.log(data); 12 | //}); 13 | 14 | 15 | index('http://tv.sohu.com/20150410/n411059863.shtml','MP4',function(err,data){ 16 | if(err){ 17 | throw err; 18 | } 19 | console.log(data); 20 | }); -------------------------------------------------------------------------------- /test/index.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jiangli on 15/4/10. 3 | */ 4 | 5 | 6 | 7 | "use strict"; 8 | 9 | var should = require('should'); 10 | var index = require('../index'); 11 | 12 | describe('test/index.test.js', function () { 13 | 14 | describe('/getIndexInfo', function () { 15 | 16 | it('should getIndexInfo successful', function (done) { 17 | 18 | 19 | index('http://tv.sohu.com/20150410/n411059863.shtml',function(err,data){ 20 | 21 | data.should.have.property('title'); 22 | data.should.have.property('coverImg'); 23 | data.should.have.property('url'); 24 | done(err); 25 | 26 | }); 27 | }); 28 | }); 29 | 30 | }); -------------------------------------------------------------------------------- /test/lib/anotheriqiyimp4.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jiangli on 15/4/10. 3 | */ 4 | 5 | "use strict"; 6 | 7 | var should = require('should'); 8 | var anotheriqiyimp4 = require('../../lib/anotheriqiyimp4'); 9 | 10 | describe('test/lib/anotheriqiyimp4.test.js', function () { 11 | 12 | describe('/getAnotherIQiYiMp4VideoInfo', function () { 13 | 14 | it('should getAnotherIQiYiMp4VideoInfo successful', function (done) { 15 | anotheriqiyimp4('http://www.iqiyi.com/v_19rrnphkz4.html',function(err,data){ 16 | 17 | data.should.have.property('title'); 18 | data.should.have.property('coverImg'); 19 | data.should.have.property('mp4'); 20 | done(err); 21 | 22 | }); 23 | }); 24 | }); 25 | 26 | }); -------------------------------------------------------------------------------- /test/lib/iqiyimp4.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jiangli on 15/4/10. 3 | */ 4 | 5 | "use strict"; 6 | 7 | var should = require('should'); 8 | var iqiyimp4 = require('../../lib/iqiyimp4'); 9 | 10 | describe('test/lib/iqiyimp4.test.js', function () { 11 | 12 | describe('/getIQiYiMp4VideoInfo', function () { 13 | 14 | it('should getIQiYiMp4VideoInfo successful', function (done) { 15 | iqiyimp4('http://www.iqiyi.com/v_19rrnphkz4.html',function(err,data){ 16 | 17 | data.should.have.property('title'); 18 | data.should.have.property('coverImg'); 19 | data.should.have.property('mp4'); 20 | done(err); 21 | 22 | }); 23 | }); 24 | }); 25 | 26 | }); -------------------------------------------------------------------------------- /test/lib/iqyi.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jiangli on 15/4/10. 3 | */ 4 | 5 | "use strict"; 6 | 7 | var should = require('should'); 8 | var iqiyi = require('../../lib/iqiyi'); 9 | 10 | describe('test/lib/iqiyi.test.js', function () { 11 | 12 | describe('/getIQiYiVideoInfo', function () { 13 | 14 | it('should getIQiYiVideoInfo successful', function (done) { 15 | iqiyi('http://www.iqiyi.com/v_19rrnphkz4.html',function(err,data){ 16 | 17 | data.should.have.property('title'); 18 | data.should.have.property('coverImg'); 19 | data.should.have.property('极速'); 20 | done(err); 21 | 22 | }); 23 | }); 24 | }); 25 | 26 | }); -------------------------------------------------------------------------------- /test/lib/souhu.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jiangli on 15/4/10. 3 | */ 4 | 5 | "use strict"; 6 | 7 | var should = require('should'); 8 | var souhu = require('../../lib/souhu'); 9 | 10 | describe('test/lib/souhu.test.js', function () { 11 | 12 | describe('/getSouHuVideoInfo', function () { 13 | 14 | it('should getSouHuVideoInfo successful', function (done) { 15 | souhu('http://tv.sohu.com/20150410/n411059863.shtml',function(err,data){ 16 | 17 | data.should.have.property('title'); 18 | data.should.have.property('coverImg'); 19 | data.should.have.property('url'); 20 | done(err); 21 | 22 | }); 23 | }); 24 | }); 25 | 26 | }); -------------------------------------------------------------------------------- /test/lib/youku.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jiangli on 15/4/10. 3 | */ 4 | 5 | "use strict"; 6 | 7 | var should = require('should'); 8 | var youku = require('../../lib/youku'); 9 | 10 | describe('test/lib/youku.test.js', function () { 11 | 12 | describe('/getYouKuVideoInfo', function () { 13 | 14 | it('should getYouKuVideoInfo successful', function (done) { 15 | youku('http://v.youku.com/v_show/id_XODI5ODQxMzEy.html',function(err,data){ 16 | 17 | data.should.have.property('title'); 18 | data.should.have.property('coverImg'); 19 | data.should.have.property('高清mp4'); 20 | done(err); 21 | 22 | }); 23 | }); 24 | }); 25 | 26 | }); --------------------------------------------------------------------------------