// ==UserScript==
// @name 115优化大师
// @author zxf10608
// @version 2.2.4
// @namespace https://greasyfork.org/zh-CN/scripts/408466
// @icon https://115.com/favicon.ico
// @description 任意浏览器自由登录、一键离线下载、调用Dplayer或Potplayer播放视频、文件快捷下载等功能
// @require https://cdn.bootcss.com/jquery/3.5.1/jquery.min.js
// @require https://greasyfork.org/scripts/398240-gm-config-zh-cn/code/GM_config_zh-CN.js
// @require https://greasyfork.org/scripts/408457-fec115/code/fec115.js
// @require https://greasyfork.org/scripts/408462-js-xxhash/code/js-xxhash.js
// @require https://greasyfork.org/scripts/408794-elliptic/code/elliptic.js
// @require https://cdn.bootcdn.net/ajax/libs/toastr.js/2.1.4/toastr.min.js
// @resource toastrCss https://cdn.bootcdn.net/ajax/libs/toastr.js/2.1.4/toastr.min.css
// @require https://cdn.bootcdn.net/ajax/libs/crc-32/1.2.0/crc32.min.js
// @require https://cdn.bootcdn.net/ajax/libs/blueimp-md5/2.16.0/js/md5.min.js
// @require https://cdn.bootcdn.net/ajax/libs/aes-js/3.1.2/index.min.js
// @require https://cdn.bootcdn.net/ajax/libs/jsSHA/2.4.2/sha1.js
// @require https://cdn.jsdelivr.net/npm/dplayer/dist/DPlayer.min.js
// @resource Dplayercss https://cdn.jsdelivr.net/npm/dplayer/dist/DPlayer.min.css
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/hls.min.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/build/lz4.min.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/src/md4.min.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/index.js
// @include *
// @exclude http*://*.baidu.com/*
// @exclude http*://*.jb51.*
// @exclude http*://www.iqiyi.com/*
// @exclude http*://*.qq.com/*
// @exclude http*://*.youku.com/*
// @exclude http*://*.bilibili.com/
// @exclude http*://*.pptv.com/*
// @exclude http*://*.fun.tv/*
// @exclude http*://*.sohu.com/*
// @exclude http*://*.le.com/*
// @exclude http*://*.tudou.com/*
// @grant GM_log
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addStyle
// @grant GM_openInTab
// @grant GM_getResourceText
// @grant GM_xmlhttpRequest
// @grant unsafeWindow
// @grant GM_registerMenuCommand
// @grant GM_xmlhttpRequest
// @connect 115.com
// @grant window.open
// @grant window.close
// @run-at document-start
// @compatible chrome
// @license GPL-3.0-or-later
// ==/UserScript==
(function() {
'use strict';
if ( typeof (GM_config) == 'undefined') {
alert('115优化大师:相关库文件加载失败,脚本无法使用,请刷新网页重新加载!');
return;
} else {
console.log('115优化大师:相关库文件加载成功');
};
function config(){
var windowcss = ' #Cfg .config_var {margin-left: 10%;margin-right: 10%;} #Cfg input[type="checkbox"] {margin: 3px 3px 3px 0px;} #Cfg input[type="text"] {width: 50px;} #Cfg {background-color: lightblue;} #Cfg .reset_holder {float: left; position: relative; bottom: -1em;} #Cfg .saveclose_buttons {margin: .7em;} #Cfg .section_desc {font-size: 10pt;}';
GM_registerMenuCommand('设置', opencfg);
function opencfg(){
GM_config.open();
};
GM_config.init(
{
id: 'Cfg',
title: GM_config.create('a', {
href: 'https://greasyfork.org/zh-CN/scripts/408466',
target: '_blank',
textContent: '115优化大师',
title: '作者:zxf10608 版本:v2.2 点击访问主页'
}),
isTabs: true,
skin: 'tab',
css: windowcss,
frameStyle:
{
height: '420px',
width: '425px',
zIndex:'2147483648',
},
fields:
{
account_Login:
{
section: ['登录管理', '修改登录模式,实现自由登录'],
label: '启用帐号登录',
labelPos: 'right',
type: 'checkbox',
default: true,
},
hide_qrcodeLogin:
{
label: '隐藏二维码登录',
labelPos: 'right',
type: 'checkbox',
default: false,
},
hide_officeLogin:
{
label: '隐藏115组织登录',
labelPos: 'right',
type: 'checkbox',
default: false,
},
login_Alidity:
{
label: '登录有效期',
labelPos: 'left',
type: 'select',
options: ['1天', '7天', '30天'],
default: '7天',
},
show_Alidity:
{
label: '显示登录有效截止时间',
labelPos: 'right',
type: 'checkbox',
default: false,
},
reminder:
{
label: '温馨提示',
labelPos: 'right',
type: 'button',
click: function() {
alert('登录有效期仅对以“帐号登录”生效。');
}
},
offline_Down:
{
section: ['离线升级', '升级离线下载功能'],
label: '启用一键离线下载',
labelPos: 'right',
type: 'checkbox',
default: true,
},
offline_result:
{
label: '任务添加后显示离线下载结果',
labelPos: 'right',
type: 'checkbox',
default: true,
},
open_List:
{
label: '离线后自动打开链接任务列表',
labelPos: 'right',
type: 'checkbox',
default: false,
},
open_search:
{
label: '离线成功后开启视频搜索',
labelPos: 'right',
type: 'checkbox',
default: true,
line: 'start',
},
search_result:
{
label: '显示视频搜索结果',
labelPos: 'right',
type: 'checkbox',
default: true,
},
open_Popup:
{
label: '搜到视频自动播放',
labelPos: 'right',
type: 'checkbox',
default: false,
line: 'end',
},
reminder2:
{
label: '温馨提示',
labelPos: 'right',
type: 'button',
click: function() {
alert('1、显示离线下载结果有10s延时,用于服务器响应时间。\n2、为避免通知过多,最多只显示2个视频结果,更多请自行到115查看。');
}
},
player:
{
section: ['播放优化', '调用第三方播放器,优化播放体验'],
label: '视频播放器',
labelPos: 'left',
type: 'select',
options: ['Dplayer', 'Potplayer', '官方Flash'],
default: 'Dplayer',
},
play_Number:
{
label: '播放记录最大保存数量',
labelPos: 'left',
type: 'select',
'options': ['0', '15', '30'],
default: '15',
},
skip_titles:
{
label: '跳过片头秒数',
type: 'unsigned int',
default: '0',
},
skip_credits:
{
label: '跳过片尾秒数',
type: 'unsigned int',
default: '0',
},
reminder3:
{
label: '温馨提示',
labelPos: 'right',
type: 'button',
click: function() {
alert('1、单击视频文件名调用指定播放器,双击时始终调用官方Flash。\n2、Potplayer默认播放最高清晰度,暂不支持切换。\n3、除第一项外,其他仅在启用Dplayer时有效。\n4、播放记录最大保存数量设置为0,可关闭记忆播放或清空播放记录。\n5、播放界面右键可显示更多功能菜单,谨慎使用“删除”操作。');
}
},
hide_sidebar:
{
section: ['更多设置', '优化浏览体验'],
label: '隐藏网盘侧边栏',
labelPos: 'right',
type: 'checkbox',
default: false,
},
file_Down:
{
label: '启用文件快捷下载',
labelPos: 'right',
type: 'checkbox',
default: true,
},
},
events:
{
save: function() {
GM_config.close();
}
},
});
};
config();
var login = function(){
var Buffer, LZ4, LoginEncrypt_, browserInterface, bytesToHex, bytesToString, cloneInto, dictToForm, dictToQuery, ec115_compress_decode, ec115_decode, ec115_decode_aes, ec115_encode_data, ec115_encode_token, ec115_init, elliptic, g_ver, get_key, md4, md4_init, preLoginEncrypt, ref, sig_calc, sig_init, stringToBytes;
g_ver = '8.3.0.25';
Buffer = require('buffer').Buffer;
LZ4 = require('lz4');
elliptic = window.elliptic;
md4 = window.md4;
stringToBytes = function(s) {
var i, l, ref, ret;
ret = [];
for (i = l = 0, ref = s.length; (0 <= ref ? l < ref : l > ref); i = 0 <= ref ? ++l : --l) {
ret.push(s.charCodeAt(i));
}
return ret;
};
bytesToString = function(b) {
var i, l, len1, ret;
ret = '';
for (l = 0, len1 = b.length; l < len1; l++) {
i = b[l];
ret += String.fromCharCode(i);
}
return ret;
};
bytesToHex = function(b) {
var l, len1, ret, t;
ret = '';
for (l = 0, len1 = b.length; l < len1; l++) {
t = b[l];
ret += (t >> 4).toString(16);
ret += (t & 0xf).toString(16);
}
return ret;
};
ec115_init = function() {
var Q, c, key, keys, pub;
c = new elliptic.ec('p224');
keys = c.genKeyPair();
pub = [0x1d].concat(keys.getPublic(true, true));
Q = c.keyFromPublic('0457A29257CD2320E5D6D143322FA4BB8A3CF9D3CC623EF5EDAC62B7678A89C91A83BA800D6129F522D034C895DD2465243ADDC250953BEEBA'.toLowerCase(), 'hex');
key = (keys.derive(Q.getPublic())).toArray();
return {pub, key};
};
ec115_encode_token = function(pub, tm, cnt) {
var i, l, m, o, q, r20, r21, tmp, tmp2;
r20 = Math.floor(Math.random() * 256);
r21 = Math.floor(Math.random() * 256);
tmp = Buffer.alloc(48);
for (i = l = 0; l < 15; i = ++l) {
tmp[i] = pub[i] ^ r20;
}
tmp[15] = r20;
tmp.writeInt32LE(115, 16);
tmp.writeInt32LE(tm, 20);
for (i = m = 16; m < 24; i = ++m) {
tmp[i] ^= r20;
}
for (i = o = 24; o < 39; i = ++o) {
tmp[i] = pub[i - 9] ^ r21;
}
tmp[39] = r21;
tmp.writeInt32LE(cnt, 40);
for (i = q = 40; q < 44; i = ++q) {
tmp[i] ^= r21;
}
tmp2 = Buffer.concat([Buffer.from('^j>WD3Kr?J2gLFjD4W2y@'), tmp.slice(0, 44)]);
tmp.writeInt32LE(CRC32.buf(tmp2), 44);
return tmp.toString('base64');
};
ec115_encode_data = function(data, key) {
var aesEcb, i, j, k, key1, key2, l, n, part, rets, tmp;
key1 = key.slice(0, 16);
key2 = key.slice(-16);
aesEcb = new aesjs.ModeOfOperation.ecb(key1);
tmp = stringToBytes(data);
n = tmp.length;
j = 0;
rets = [];
while (n > 0) {
part = Buffer.alloc(16);
for (i = l = 0; l < 16; i = ++l) {
k = n <= 0 ? 0 : tmp[i + j];
part[i] = key2[i] ^ k;
--n;
}
key2 = aesEcb.encrypt(part);
rets.push(Buffer.from(key2));
j += 16;
}
return Buffer.concat(rets);
};
ec115_decode_aes = function(data, key) {
var aesCbc, i, iv, key1, ret;
key1 = key.slice(0, 16);
iv = key.slice(-16);
aesCbc = new aesjs.ModeOfOperation.cbc(key1, iv);
ret = aesCbc.decrypt(data);
i = ret.length;
while (i > 0 && ret[i - 1] === 0) {
--i;
}
return Buffer.from(ret.buffer, ret.byteOffset, i);
};
ec115_compress_decode = function(data) {
var len, p, r, rets, tmp;
p = 0;
rets = [];
while (p < data.length) {
len = data.readInt16LE(p) + 2;
if (p + len > data.length) {
return null;
}
tmp = Buffer.alloc(0x2000);
r = LZ4.decodeBlock(data.slice(p + 2, p + len), tmp);
if (r < 0) {
return null;
}
rets.push(tmp.slice(0, r));
p += len;
}
return Buffer.concat(rets);
};
get_key = function(data_buf) {
var i, l, p, ret, t;
p = 0;
ret = Buffer.alloc(40);
for (i = l = 0; l < 40; i = ++l) {
t = data_buf.readInt32LE(p);
p = t + 1;
ret[i] = data_buf[t];
}
return ret;
};
md4_init = function(pSig) {
var ret;
ret = md4.create();
ret.h0 = pSig.readInt32LE(4);
ret.h1 = pSig.readInt32LE(8);
ret.h2 = pSig.readInt32LE(12);
ret.h3 = pSig.readInt32LE(16);
ret.first = false;
return ret;
};
sig_init = function(body) {
var data_buf, data_buf_p, dhash, md4h, ori_data_p, pSig, sz;
ori_data_p = Module._malloc(body.length);
Module.HEAPU8.set(body, ori_data_p);
data_buf_p = Module._malloc(body.length);
sz = Module.ccall('calc_out', 'number', ['number', 'number', 'number'], [ori_data_p, body.length, data_buf_p]);
Module._free(ori_data_p);
data_buf = Buffer.from(Module.buffer, data_buf_p, sz);
pSig = get_key(data_buf);
md4h = md4_init(pSig);
md4h.update(data_buf);
dhash = md4h.digest();
return {data_buf, data_buf_p, pSig, dhash};
};
sig_calc = function({data_buf, data_buf_p, pSig, dhash}, src) {
var h1, h1_p, h2, h2b, i, l, md4h, out_data, out_data_p, pad, ret, sz, xxh;
xxh = XXH.h64();
xxh.init(pSig.readUInt32LE(8));
xxh.update(src);
h2 = xxh.digest().toString(16);
pad = '0000000000000000';
h2b = Buffer.from(pad.slice(0, 16 - h2.length) + h2, 'hex').swap64();
md4h = md4_init(pSig);
md4h.update(dhash);
md4h.update(src);
md4h.update(h2b);
md4h.update(pSig);
h1 = new Uint8Array(md4h.buffer());
h1_p = Module._malloc(16);
Module.HEAPU8.set(h1, h1_p);
out_data_p = Module._malloc(0x10000);
sz = Module.ccall('encode', 'number', ['number', 'number', 'number', 'number', 'number', 'number', 'number'], [data_buf_p, data_buf.length / 2, h1_p, 16, out_data_p, 8, 10]);
Module._free(data_buf_p);
Module._free(h1_p);
out_data = new Uint8Array(Module.buffer, out_data_p, sz);
md4h = md4_init(pSig);
md4h.update(out_data);
ret = md4h.digest();
Module._free(out_data_p);
ret.push(pSig[0]);
for (i = l = 36; l < 40; i = ++l) {
ret.push(pSig[i]);
}
return bytesToHex(ret);
};
ec115_decode = function(data, key) {
var dec, unzip;
dec = data[data.length - 12 + 5];
unzip = data[data.length - 12 + 4];
data = data.slice(0, -12);
if (dec === 1) {
data = ec115_decode_aes(data, key);
}
if ((data != null) && unzip === 1) {
data = ec115_compress_decode(data);
}
return data;
};
dictToQuery = function(dict) {
var k, tmp, v;
tmp = [];
for (k in dict) {
v = dict[k];
tmp.push(`${encodeURIComponent(k)}=${encodeURIComponent(v)}`);
}
return tmp.join('&');
};
dictToForm = function(dict) {
var k, tmp, v;
tmp = [];
for (k in dict) {
v = dict[k];
tmp.push(`${k}=${v}`);
}
return tmp.join('&');
};
LoginEncrypt_ = function({account, passwd, environment, goto, login_type}, g, {pub, key}, sig) {
var data, fake, tm, tmus, token;
tmus = (new Date()).getTime();
tm = Math.floor(tmus / 1000);
fake = md5(account);
token = ec115_encode_token(pub, tm, 1);
data = ec115_encode_data(dictToForm({
GUID: fake.slice(0, 20),
account: account,
device: 'GhostXP',
device_id: fake.slice(2, 14).toUpperCase(),
device_type: 'windows',
disk_serial: fake.slice(0, 8).toUpperCase(),
dk: '',
environment: environment,
goto: goto,
login_source: '115chrome',
network: '5',
passwd: passwd,
sign: md5(`${account}${tm}`),
system_info: ` ${fake[1]}${fake[0]}${fake[3]}${fake[2]}${fake[5]}${fake[4]}${fake[7]}${fake[6]}`.toUpperCase(),
time: tm,
login_type: login_type,
signew: 1,
sign115: sig_calc(sig, md5(`${account}${tm}`))
}), key);
return GM_xmlhttpRequest({
method: 'POST',
url: `http://passport.115.com/?ct=encrypt&ac=login&k_ec=${token}`,
data: GM_info.scriptHandler === 'Violentmonkey' && compareVersions.compare(GM_info.version, 'v2.12.2', '<') ? new Blob([data.buffer], {
type: 'application/octet-binary'
}) : data.toString('latin1'),
binary: true,
responseType: 'arraybuffer',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
},
onerror: function(response) {
return GM_log(`response.status = ${response.status}, response.statusText = ${response.statusText}`);
},
onload: function(response) {
var date, datestr, json;
if (response.status === 200) {
data = Buffer.from(response.response);
data = ec115_decode(data, key);
if (data != null) {
json = JSON.parse(data.toString('latin1'));
if (json.state && (json.data != null)) {
date = new Date();
date.setTime(date.getTime() + day * 32 * 3600 * 1000);
datestr = date.toUTCString();
var time = date.Format("yyyy-MM-dd HH:mm:ss");
GM_setValue('login_Last',time);
document.cookie = `UID=${json.data.cookie.UID}; expires=${datestr}; path=/; domain=115.com`;
document.cookie = `CID=${json.data.cookie.CID}; expires=${datestr}; path=/; domain=115.com`;
document.cookie = `SEID=${json.data.cookie.SEID}; expires=${datestr}; path=/; domain=115.com`;
document.cookie = `OOFL=${json.data.user_id}; expires=${datestr}; path=/; domain=115.com`;
delete json.data;
}
return unsafeWindow[g](JSON.stringify(json));
} else {
return GM_log('data is null');
}
} else {
return GM_log(`response.status = ${response.status}`);
}
}
});
};
preLoginEncrypt = function(n, g) {
var key, pub, tm, tmus, token;
tmus = (new Date()).getTime();
tm = Math.floor(tmus / 1000);
({pub, key} = ec115_init());
token = ec115_encode_token(pub, tm, 0);
return GM_xmlhttpRequest({
method: 'GET',
url: `https://passportapi.115.com/app/2.0/web/${g_ver}/login/sign?k_ec=${token}`,
responseType: 'arraybuffer',
anonymous: true,
onerror: function(response) {
return GM_log(`response.status = ${response.status}, response.statusText = ${response.statusText}`);
},
onload: function(response) {
var body, data, error, json, sig;
if (response.status === 200) {
data = Buffer.from(response.response);
data = ec115_decode(data, key);
if (data != null) {
json = JSON.parse(data.toString('latin1'));
if (json.state) {
body = Buffer.from(json.sign, 'base64');
try {
sig = sig_init(body);
return LoginEncrypt_(JSON.parse(n), g, {pub, key}, sig);
} catch (error1) {
error = error1;
return GM_log(`${error.message}\n${error.stack}`);
}
} else {
return GM_log(JSON.stringify(json));
}
} else {
return GM_log('data is null');
}
} else {
return GM_log(`response.status = ${response.status}`);
}
}
});
};
browserInterface = (ref = unsafeWindow.browserInterface) != null ? ref : {};
browserInterface.LoginEncrypt = function(n, g) {
var error;
try {
return preLoginEncrypt(n, g);
} catch (error1) {
error = error1;
return GM_log(`${error.message}\n${error.stack}`);
}
};
browserInterface.GetBrowserVersion = function() {
return new String(g_ver);
};
browserInterface.ChromeGetIncognitoState = function() {
return false;
};
if (typeof cloneInto !== 'function') {
cloneInto = function(x) {
return x;
};
}
unsafeWindow.browserInterface = cloneInto(browserInterface, unsafeWindow, {
cloneFunctions: true
});
};
var G = GM_config;
var localHref = window.location.href;
var sign_url = 'http://115.com/?ct=offline&ac=space';
var add_url = 'http://115.com/web/lixian/?ct=lixian&ac=add_task_url';
var lists_url = 'http://115.com/web/lixian/?ct=lixian&ac=task_lists';
GM_addStyle(GM_getResourceText("toastrCss"));
toastr.options = {
"closeButton": true,
"debug": false,
"progressBar": true,
"timeOut": "3000",
"extendedTimeOut": "2000",
"allowHtml": true,
"newestOnTop" : false,
};
function AjaxCall(href,callback) {
GM_xmlhttpRequest({
method: "GET",
url: href,
onload: function(data,status) {
if(data.readyState==4 && data.status==200){
var htmlTxt = data.responseText;
callback(null,htmlTxt);
};
},
onerror: function (error) {
callback(error);
},
ontimeout: function (error) {
callback(error);
},
});
};
function download(key){
var href = 'https://webapi.115.com/files/download?pickcode='+key;
AjaxCall(href,function(error,htmlTxt) {
var json = JSON.parse(htmlTxt);
console.log('文件下载信息:');
console.log(json);
if(json.state) {
var link = json.file_url.replace(/\\/g,'');
GM_openInTab(link,false);
console.log('下载地址:'+link);
} else {
toastr.warning(json.msg,'下载失败!');
};
});
};
function palyData(video){
if (G.get('player') =='官方Flash'){
GM_openInTab('http://120.78.32.31/play.html?pickcode='+video.pid,false);
return;
};
GM_setValue('videoInfo',video);//保存视频属性
var herfLink = 'https://115.com/api/video/m3u8/'+video.pid+'.m3u8';
AjaxCall(herfLink,function(error,htmlTxt) {
if (typeof (htmlTxt) == 'undefined') {
toastr.options.onclick = function(){
GM_openInTab('http://120.78.32.31/play.html?pickcode='+video.pid,false);
};
toastr.warning('视频转码中,请稍后再试。点击此处查看转码进度。','播放失败!',{timeOut:5000});
return
};
var dataList = htmlTxt.split('\n');
var m3u8 = [];
var temp = '"YH"|原画|"BD"|4K|"UD"|蓝光|"HD"|超清|"SD"|高清|"3G"|标清';
var txt = temp.split('|');
for (var i=0; i<6; i++){
dataList.forEach(function (e,j,arr) {
if (e.indexOf(txt[i*2])!= -1) {
m3u8.push({name: txt[i*2+1], url: arr[j+1].replace(/\r/,''), type: 'hls'});
};
});
};
if (G.get('player') =='Potplayer'){//调用potplayer播放
var m3u8Link = m3u8[0].url;
window.location.href = 'potplayer://'+m3u8Link;
return;
};
GM_setValue('m3u8List',m3u8);
GM_openInTab('http://115.com/web/lixian',false);
});
};
function change(number){
var size = "";
if(number < 1024 * 1024 * 1024){
size = (number/(1024 * 1024)).toFixed(2) + "MB";
}else{
size = (number/(1024 * 1024 * 1024)).toFixed(2) + "GB";
};
var sizeStr = size + "";
var index = sizeStr.indexOf(".");
var dou = sizeStr.substr(index + 1 ,2)
if(dou == "00"){
return sizeStr.substring(0, index) + sizeStr.substr(index + 3, 2)
};
return size;
};
function tranTime(num){
var showTime = '';
if (num > 3600) {showTime += ' '+parseInt(num/3600)+' 小时'; num = num%3600;}
if (num > 60) {showTime += ' '+parseInt(num/60)+' 分'; num = num%60;}
return showTime += ' '+parseInt(num)+' 秒';
};
function enterPiP(videoEl){
if(document.pictureInPictureEnabled && !videoEl.disablePictureInPicture) {
if (!document.pictureInPictureElement) {
videoEl.requestPictureInPicture();
}else{
document.exitPictureInPicture();
};
}else{
alert('浏览器不支持或已关闭画中画功能!');
};
};
if(localHref.indexOf('https://115.com/') != -1) {
if (G.get('account_Login')){
if (G.get('login_Alidity') =='1天'){
var day = 1;
} else if(G.get('login_Alidity') =='7天'){
var day = 7;
} else {
var day = 30;
};
if ( typeof (require) == 'undefined') {
alert('115优化大师:帐号登录库文件加载失败,该功能无法使用,请刷新网页重新加载!');
return false;
};
Date.prototype.Format = function (fmt) {
var o = {
"M+": this.getMonth() + 1,
"d+": this.getDate(),
"H+": this.getHours() + 8,
"m+": this.getMinutes(),
"s+": this.getSeconds(),
"q+": Math.floor((this.getMonth() + 3) / 3),
"S": this.getMilliseconds()
};
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return fmt;
};
login();
};
$(document).ready(function(){
if (typeof (unsafeWindow.USER_ID) != 'undefined') {
GM_setValue('115ID', unsafeWindow.USER_ID);
var UserID = GM_getValue('115ID');
console.log('115账号已登录,账号ID获取成功!');
} else {
if (G.get('hide_officeLogin')){
$('.ltab-office').hide();
};
if (G.get('hide_qrcodeLogin')){
$('.login-scene,.login-footer span:eq(0),.login-footer i:eq(0)').hide();
$('[lg_rel="login"]').show();
} else if ($('[lgb-nav="download"]').length > 0) {
$('[lgb-nav="download"]').attr('lgb-nav','login');
};
console.log('115账号未登录,账号ID获取失败!');
};
if (G.get('hide_sidebar')){
$('.sub-core').hide();
setTimeout(function(){
$('.main-core').css({'left':'16px','top':'16px'});
},50);
};
if (G.get('file_Down')){
var herfd = 'li[rel="item"][file_type="1"]:not([down_button="1"])';
$('body').on('mouseenter',herfd,function(){
var tempEl = $(this).attr('down_button', '1');
tempEl.find('.file-opr [menu="download_one"]').hide();
tempEl.find('.file-opr').prepend('<a href="javascript:;" class="Down" style="cursor:pointer;" title="快捷下载文件"><i class="icon-operate ifo-download"></i><span>快捷下载</span></a>');
});
$('body').on('click','.Down',function(){
var El = $(this).parents('li');
var pickcode = El.attr('pick_code');
download(pickcode);
return false;
});
};
if (G.get('player') !='官方Flash'){
var herfv = 'li[rel="item"][file_type="1"][file_mode="9"]:not([paly_button="1"])';
$('body').on('mouseenter',herfv,function(){
var tempEl = $(this).attr('paly_button','1');
if (G.get('player') =='Dplayer'){
var txt1 = 'Dp播放';
var txt2 = '使用Dplayer在线播放';
}else{
var txt1 = 'Pot播放';
var txt2 = '使用Potplayer本地播放';
};
tempEl.find('.name').addClass('115play').removeAttr('menu');
tempEl.find('.file-opr').prepend('<a class="115play" href="javascript:;" title='+txt2+'><span>'+txt1+'</span></a>');
return false;
});
};
});
};
if(localHref.indexOf('https://115.com/?cid=0&offset=0&mode=wangpan') != -1) {
//var rsc = setInterval(function(){
window.onload=function(){
//clearInterval(rsc);
var errorLink = GM_getValue('error');
if (typeof (errorLink) != 'undefined'){
/* GM_setValue('error','');
unsafeWindow.Core.OFFL5Plug.OpenLink();
setTimeout(function() {
$('#js_offline_new_add').val(errorLink);
setTimeout(function() {
console.log('已点击!');
}, 500);
}, 50); */
}else if (G.get('show_Alidity')){
var loginTime= GM_getValue('login_Last');
if (typeof (unsafeWindow.USER_ID) != 'undefined') {
toastr.success(loginTime, '登录有效期为');
} else {
toastr.warning('其他方式登录,未知有效期!', '登录有效期为');
};
};
};
//}, 3000);
};
if(localHref.indexOf('http://115.com/web/lixian') != -1 ) {
var m3u8 = GM_getValue('m3u8List');
var video = GM_getValue('videoInfo');
var titleTxt = video.name;
var pickID = video.pid;
var folderID = video.fid1;
var videoID = video.fid2;
var size = video.size;
var jsonList = GM_getValue('palyList');
if (!jsonList){
var jsonList = {};
};
var curTime = jsonList[pickID];
if (curTime){
delete jsonList[pickID];
};
var skipTime = G.get('skip_titles');
var skipTime2 = G.get('skip_credits');
$('pre').remove();
$('head').html(`<meta http-equiv="Content-Type" content="text/html; charset=GBK"><title>${titleTxt} ${size}</title>`);
GM_addStyle(`html,body,div{margin:0;padding:0;border:0;outline:0;background:transparent}`);
GM_addStyle(GM_getResourceText('Dplayercss'));
$('body').append('<div id="Dplayer"></div>');
function playVideo(m3u8) {
if(m3u8.length >1){
var z = 1;
}else{
var z = 0;
};
var dp = new DPlayer({
container: $('#Dplayer')[0],
screenshot: true,
volume: 1,
video:
{
quality: m3u8,
defaultQuality: z,
},
contextmenu:
[{
text: '下载视频',
click: function(t) {
download(pickID);
}
},{
text: '删除视频',
click: function(t) {
offline.del(videoID);
}
},{
text: '打开文件夹',
click: function(t) {
GM_openInTab(`https://115.com/?cid=${folderID}&offset=0&mode=wangpan`,false);
}
},{
text: '删除文件夹',
click: function(t) {
if (folderID==0){
alert('根目录,不可删除!');
return ;
};
offline.del(folderID);
}
},{
text: '开启画中画',
click: function(t) {
enterPiP(t.video)
}
}],
});
unsafeWindow.dp = dp;
$('.dplayer-setting-loop,.dplayer-mobile-play,.dplayer-menu-item:gt(-3)').hide();
$('.dplayer-menu').css('width','98px');
dp.on('loadstart', function () {
dp.notice('视频加载中,请稍等。', 1000);
});
var a = 0;
dp.on('loadeddata', function () {
dp.notice('视频加载完成,即将播放。', 1000);
a = a+1;
if( a==1){
setTimeout(function(){
if (curTime && curTime > skipTime){
dp.seek(curTime);
dp.notice('已跳转到上次观看进度'+tranTime(curTime), 2500);
}else if(skipTime>0){
dp.seek(skipTime);
dp.notice('已跳过片头'+skipTime+'秒', 2500);
};
if(m3u8.length >1){
setTimeout(function(){
dp.notice('本视频支持切换清晰度', 2500);
}, 3000);
};
dp.play();
}, 1000);
};
});
dp.on('timeupdate', function () {
if ((dp.video.duration - dp.video.currentTime > 30) && dp.video.currentTime > 30) {
jsonList[pickID] = dp.video.currentTime;
var setNum = G.get('play_Number');
var saveNum = Object.keys(jsonList).length;
if (saveNum > parseInt(setNum)){
var palyOld = JSON.stringify(jsonList).split('"')[1];
delete jsonList[palyOld];
};
}else{
delete jsonList[pickID];
};
GM_setValue('palyList',jsonList);
if(0< skipTime2 && (dp.video.duration - dp.video.currentTime) <= skipTime2){
dp.pause();
dp.notice('已跳过片尾'+skipTime2+'秒', 2500);
setTimeout(function(){
alert('视频已播放结束!');
}, 2000);
};
});
dp.on('error', function () {
alert('视频加载失败!');
return
});
dp.on('ended', function () {
alert('视频已播放结束!');
});
};
playVideo(m3u8);
};
var offline = function(){
return {
getSign:function () {
return new Promise(function(resolve, reject){
var time = Date.now();
GM_xmlhttpRequest({
method: 'GET',
url: sign_url,
onload: function(response) {
if (response.responseText.indexOf('html')!= -1) {
toastr.error('请先登录115网盘账号!','离线任务添加失败!');
setTimeout(function(){
GM_openInTab('https://115.com/?mode=login',false);
}, 3000);
return;
};
var json = JSON.parse(response.responseText);
resolve(json);
},
onerror: function(error) {
reject(error);
},
});
});
},
param2:function (data,e) {
if (typeof e != 'undefined'){
return e;
};
var res = '';
for(var key in data){
var value = data[key];
res += '&' + encodeURIComponent( key ) + "=" + encodeURIComponent( value );
};
return res.slice(1);
},
getData:function (url,herf,e) {
var UserID = GM_getValue('115ID');
return offline.getSign().then(function(json){
return new Promise(function(resolve, reject){
var data = {
uid: UserID,
sign: json.sign,
time: json.time,
url: url
};
GM_xmlhttpRequest({
method: 'POST',
data: offline.param2(data,e),
url: herf,
headers: {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Accept": "application/json, text/javascript, */*; q=0.01",
Origin: "http://115.com",
"X-Requested-With": "XMLHttpRequest"
},
onload: function(response) {
var json = JSON.parse(response.responseText);
resolve(json);
},
onerror: function(error) {
reject(error);
},
});
});
});
},
del:function (key) {
var url = '';
var del_url ='https://webapi.115.com/rb/delete';
var fidInfo = 'fid='+key;
offline.getData(url,del_url,fidInfo).then(function(json){
console.log('文件删除结果:');
console.log(json);
if(json.state) {
alert('删除成功,可从回收站还原。\n本页面将在2s后自动关闭!');
setTimeout(function(){
window.opener=null;
window.open('','_self');
window.close();
}, 2000);
} else {
alert('删除失败:'+json.error);
};
});
},
search:function (name,fid1,offlined,callback){
var title = new Array();
title[0] = name.replace(/(\.|-|_)?(f?hd|sd|720p|1080p|mp4|avi|mkv|wmv|rmvb|rm|flv|f4v)/gi,' ');
title[1] = title[0].replace(/\.|-|_|@/g,' ');
title[2] = 'mp4 avi mkv wmv rmvb rm flv f4v vob mov 3GP';
var a = 0;//递归循环
function to_for(){
if(a == 3 ){
console.log('该文件夹无视频文件。');
callback(false);
return;
};
var searchLink = 'https://webapi.115.com/files/search?cid='+fid1+'&search_value='+encodeURIComponent(title[a])+'&type=4';
AjaxCall(searchLink,function(error,htmlTxt) {
console.log('第'+a+'次搜索链接:'+searchLink);
if (typeof htmlTxt == 'undefined'){
console.log('第'+a+'次搜索失败,关键词异常,无返回数据。');
a++;
to_for();
}else{
var json = JSON.parse(htmlTxt);
console.log('第'+a+'次搜索数据:');
console.log(json);
if(json.count > 0) {
var num= json.data.length;
for(var i=0; i<num; i++){
var dataEh = json.data[i];
var pickcode = dataEh.pc;
var fileName = dataEh.n.replace(/\s*/g, '')
var fid2 = dataEh.fid;
var size = change(dataEh.s);
var time = dataEh.play_long;
callback(true,pickcode,fileName,fid2,size,time,i+1,num);
console.log('第'+a+'次搜索结果'+i+':'+fileName+' '+size);
if(i == 1){
console.log('第'+a+'次搜索到2个视频,停止搜索。');
return;
};
};
console.log('已搜索到视频。');
return;
}else{
console.log('第'+a+'次搜索失败,无视频结果。');
a++;
to_for();
};
};
});
};
to_for();
},
check:function (offlined,link) {
var url = '';
var show_result = G.get('offline_result');
offline.getData(url,lists_url).then(function(json){
console.log('离线下载结果:');
console.log(json);
if(json.state){
for (var k=0; k<json.tasks.length; k++){
var dataEl = json.tasks[k];
if (dataEl.url == link){
if (dataEl.status != -1){
if (dataEl.move == -1){
toastr.warning('空间不足,请到115扩容', '离线下载异常!',{timeOut:5000});
return;
};
var down_result = dataEl.percentDone.toFixed(0);
if (down_result >= 99){
var name = dataEl.name;
var fid1 = dataEl.file_id;
if (show_result) {
var txtTemp = `文件(夹)名:${dataEl.name},大小:${change(dataEl.size)},点击此处打开任务列表。`;
var txt = `<a target="_blank" class="openList" href="javascript: void(0);" style="cursor:pointer;" title="打开链接任务列表">${txtTemp}</a>`;
toastr.success(txt, '离线下载已完成',{timeOut:5000});
};
if (G.get('open_search')) {
offline.search(name,fid1,offlined,function(search_result,pickcode,fileName,fid2,size,time,j,num) {
if (search_result) {
var video = {'name':fileName,'pid':pickcode,'fid1':fid1,'fid2':fid2,'size':size};
if (G.get('search_result')) {//显示视频搜索结果
var videoTXT = JSON.stringify(video);
var txtTemp = `视频:${fileName},大小:${size},时间:${tranTime(time)},点击此处播放视频。`;
var note = `<a target="_blank" class="115play" PID=${videoTXT} href="javascript: void(0);" style="cursor:pointer;" title="点击播放">${txtTemp}</a>`;
var noteTitle = `发现第${j}个视频(共${num}个)`;
toastr.success(note,noteTitle,{timeOut:8000});
};
if (G.get('open_Popup') && j==1){
setTimeout(function(){
palyData(video);
}, 500);
};
}else {
if(offlined){
var txtTemp = '视频可能已被重命名或删除,请自行到115查看或删除离线任务重试!点击此处打开文件列表。';
}else{
var txtTemp = '视频名称不规则或无视频文件,请自行到115查看!点击此处打开文件列表。';
};
var txt = `<a target="_blank" class="openFolder" data=${fid1} href="javascript: void(0);" style="cursor:pointer;" title="打开所属文件列表">${txtTemp}</a>`;
toastr.warning(txt, '未发现任何视频!',{timeOut:8000});
console.log(name+' 无搜索结果!');
return;
};
});
};
}else if(show_result) {
var txtTemp = `文件名:${dataEl.name},下载进度为:${down_result}%,点击此处打开任务列表。`;
var txt = `<a target="_blank" class="openList" href="javascript: void(0);" style="cursor:pointer;" title="打开链接任务列表">${txtTemp}</a>`;
toastr.warning(txt, '离线下载中。',{timeOut:8000});
console.log(`文件名:${dataEl.name},下载进度为:${down_result}%`);
};
}else if(show_result) {
var txt = `<a target="_blank" class="openList" href="javascript: void(0);" style="cursor:pointer;" title="打开链接任务列表">未知原因,请到115查看。点击此处打开任务列表。</a>`;
toastr.error(txt,'离线下载失败',{timeOut:8000});
return;
};
};
};
}else{
toastr.error('查询离线结果失败。','服务器错误!',{timeOut:5000});
return;
};
});
},
addButton:function(){
var supportHref = 'a[href^="magnet:"],[href^="ed2k:"],a[href^="thunder:"],[href^="ftp:"],[href$=".torrent"],[href$=".rar"],[href$=".7z"],[href$=".zip"],[href$=".mp4"],[href$=".rmvb"],[href$=".mkv"],[href$=".avi"],[href$=".iso"]';
$(supportHref).each(function(){
if ($(this).attr('offline_button')){
console.log('已添加离线按钮');
return;
};
$(this).attr('offline_button', true);
var link = $(this).attr('href');
$(this).attr('style','display:inline-block;');
$(this).after('<img src="https://115.com/favicon.ico" class="offline_logo" data-href='+link+' style="display:inline-block;cursor:pointer; margin-left:5px;border-radius:50%;border:0px;vertical-align:middle;outline:none!important;padding:0px!important;height:20px!important;width:20px!important;left:0px!important;top:0px!important;" title="使用115网盘离线下载">');
});
},
};
}();
$(document).ready(function(){
$('body').on('click','.115play',function () {
if ( typeof ($(this).attr('PID')) != 'undefined'){
var video = JSON.parse($(this).attr('PID'));
}else{
var El = $(this).parents('li');
var video = {};
video['name'] = El.attr('title').replace(/\s*/g, '');
video['pid'] = El.attr('pick_code');
video['fid1'] = El.attr('cid');
video['fid2'] = El.attr('file_id');
video['size'] = change(El.attr('file_size'));
};
palyData(video);
return false;
});
$('body').on('click','.openList:not([click="1"])',function(){
$(this).attr('click', '1');
GM_openInTab('https://115.com/?tab=offline&mode=wangpan',false);
return false;
});
$('body').on('click','.openFolder:not([click="1"])',function(){
$(this).attr('click', '1');
var fID=$(this).attr('data');
GM_openInTab(`https://115.com/?cid=${fID}&offset=0&mode=wangpan`,false);
return false;
});
$('body').on('click','.offline_logo',function(){
var link = $(this).data('href');
offline.getData(link,add_url).then(function(json){
console.log('离线任务添加结果:');
console.log(json);
if(json.state) {
if (G.get('offline_result')) {
var txt = '10秒后显示离线结果。';
}else{
var txt = link
if (G.get('open_List')){
setTimeout(function(){
GM_openInTab('https://115.com/?tab=offline&mode=wangpan',false);
}, 2000);
};
};
toastr.info(txt, '离线任务添加成功。');
setTimeout(function(){
var offlined = false;
offline.check(offlined,link)
}, 10000);
} else if (json.errcode == 10008){
toastr.warning('任务已存在,无需重复添加。','离线任务添加无效!');
if (G.get('open_List')){
setTimeout(function(){
GM_openInTab('https://115.com/?tab=offline&mode=wangpan',false);
}, 2000);
};
var offlined = true;
offline.check(offlined,link);
} else if (json.errcode == 911){
toastr.warning('账号异常,请手动添加一次离线任务用于验证帐号。','离线失败,请验证账号!');
//GM_setValue('error',link);
setTimeout(function(){
GM_openInTab('https://115.com/?cid=0&offset=0&mode=wangpan',false);
}, 2000);
} else {
toastr.warning(json.error_msg,'离线任务添加失败!');
};
console.log('离线链接:'+link+ ' 添加结果:' +json.state+ ' 原因:' +json.error_msg);
return
}, function(error) {
toastr.error('服务器繁忙,请稍后再试。','离线任务添加异常!');
console.log(error);
});
return false;
});
if (G.get('offline_Down')){
if (localHref.match(/[0-9]mag.net/) != null) {
var trigger;
$('body').on('mouseenter mouseleave','a:not([offline_button="true"])',function(event){
if(event.type == 'mouseenter'){
trigger = setTimeout(function(){
offline.addButton();
}, 200);
}else if(event.type == 'mouseleave'){
clearTimeout(trigger);
};
});
} else if(localHref.match(/pianku/) != null) {
$('body').on('click','.torrent',function(){
setTimeout(function(){
offline.addButton();
},50);
});
} else {
setTimeout(function(){
offline.addButton();
},100);
};
};
});
})();