// ==UserScript== // @name 115转存助手ui优化版 // @name:zh 115转存助手ui优化版 // @description 2022.06.16 更新,115转存助手ui优化版 v3.6 (143.2022.0616.1)(based on Fake115Upload 1.4.3 @T3rry) // @author Never4Ever // @namespace Fake115Upload@Never4Ever // @version 143.2022.0616.1 // @match https://115.com/* // @exclude https://115.com/s/* // @grant GM_xmlhttpRequest // @grant GM_log // @grant GM_setValue // @grant GM_getValue // @grant GM_setClipboard // @grant unsafeWindow // @grant GM_registerMenuCommand // @grant GM_addStyle // @grant GM_info // @connect proapi.115.com // @connect webapi.115.com // @connect 115.com // @require https://unpkg.zhimg.com/underscore@1.12.0/underscore-min.js // @require https://unpkg.zhimg.com/sweetalert2@11.3.0 // @require https://unpkg.zhimg.com/node-forge@0.10.0/dist/forge.min.js // @require https://unpkg.zhimg.com/emoutils@2.0.0/dist/umd/emoutils.min.js // ==/UserScript== /********************************************* 请从以下获取最新版,或者遇到问题去此反馈,感谢 https://github.com/Nerver4Ever/SevenSha1UIAdvancedHelper **********************************************/ /*针对网络问题,只能将不稳定的依赖库放置于此*/ /*jssha@2.3.1/src/sha.js*/ /* A JavaScript implementation of the SHA family of hashes, as defined in FIPS PUB 180-4 and FIPS PUB 202, as well as the corresponding HMAC implementation as defined in FIPS PUB 198a Copyright Brian Turek 2008-2017 Distributed under the BSD License See http://caligatio.github.com/jsSHA/ for more information Several functions taken from Paul Johnston */ 'use strict'; (function (Y) { function C(c, a, b) { var e = 0, h = [], n = 0, g, l, d, f, m, q, u, r, I = !1, v = [], w = [], t, y = !1, z = !1, x = -1; b = b || {}; g = b.encoding || "UTF8"; t = b.numRounds || 1; if (t !== parseInt(t, 10) || 1 > t) throw Error("numRounds must a integer >= 1"); if ("SHA-1" === c) m = 512, q = K, u = Z, f = 160, r = function (a) { return a.slice() }; else if (0 === c.lastIndexOf("SHA-", 0)) if (q = function (a, b) { return L(a, b, c) }, u = function (a, b, h, e) { var k, f; if ("SHA-224" === c || "SHA-256" === c) k = (b + 65 >>> 9 << 4) + 15, f = 16; else if ("SHA-384" === c || "SHA-512" === c) k = (b + 129 >>> 10 << 5) + 31, f = 32; else throw Error("Unexpected error in SHA-2 implementation"); for (; a.length <= k;) a.push(0); a[b >>> 5] |= 128 << 24 - b % 32; b = b + h; a[k] = b & 4294967295; a[k - 1] = b / 4294967296 | 0; h = a.length; for (b = 0; b < h; b += f) e = L(a.slice(b, b + f), e, c); if ("SHA-224" === c) a = [e[0], e[1], e[2], e[3], e[4], e[5], e[6]]; else if ("SHA-256" === c) a = e; else if ("SHA-384" === c) a = [e[0].a, e[0].b, e[1].a, e[1].b, e[2].a, e[2].b, e[3].a, e[3].b, e[4].a, e[4].b, e[5].a, e[5].b]; else if ("SHA-512" === c) a = [e[0].a, e[0].b, e[1].a, e[1].b, e[2].a, e[2].b, e[3].a, e[3].b, e[4].a, e[4].b, e[5].a, e[5].b, e[6].a, e[6].b, e[7].a, e[7].b ]; else throw Error("Unexpected error in SHA-2 implementation"); return a }, r = function (a) { return a.slice() }, "SHA-224" === c) m = 512, f = 224; else if ("SHA-256" === c) m = 512, f = 256; else if ("SHA-384" === c) m = 1024, f = 384; else if ("SHA-512" === c) m = 1024, f = 512; else throw Error("Chosen SHA variant is not supported"); else if (0 === c.lastIndexOf("SHA3-", 0) || 0 === c.lastIndexOf("SHAKE", 0)) { var F = 6; q = D; r = function (a) { var c = [], e; for (e = 0; 5 > e; e += 1) c[e] = a[e].slice(); return c }; x = 1; if ("SHA3-224" === c) m = 1152, f = 224; else if ("SHA3-256" === c) m = 1088, f = 256; else if ("SHA3-384" === c) m = 832, f = 384; else if ("SHA3-512" === c) m = 576, f = 512; else if ("SHAKE128" === c) m = 1344, f = -1, F = 31, z = !0; else if ("SHAKE256" === c) m = 1088, f = -1, F = 31, z = !0; else throw Error("Chosen SHA variant is not supported"); u = function (a, c, e, b, h) { e = m; var k = F, f, g = [], n = e >>> 5, l = 0, d = c >>> 5; for (f = 0; f < d && c >= e; f += n) b = D(a.slice(f, f + n), b), c -= e; a = a.slice(f); for (c %= e; a.length < n;) a.push(0); f = c >>> 3; a[f >> 2] ^= k << f % 4 * 8; a[n - 1] ^= 2147483648; for (b = D(a, b); 32 * g.length < h;) { a = b[l % 5][l / 5 | 0]; g.push(a.b); if (32 * g.length >= h) break; g.push(a.a); l += 1; 0 === 64 * l % e && D(null, b) } return g } } else throw Error("Chosen SHA variant is not supported"); d = M(a, g, x); l = A(c); this.setHMACKey = function (a, b, h) { var k; if (!0 === I) throw Error("HMAC key already set"); if (!0 === y) throw Error("Cannot set HMAC key after calling update"); if (!0 === z) throw Error("SHAKE is not supported for HMAC"); g = (h || {}).encoding || "UTF8"; b = M(b, g, x)(a); a = b.binLen; b = b.value; k = m >>> 3; h = k / 4 - 1; if (k < a / 8) { for (b = u(b, a, 0, A(c), f); b.length <= h;) b.push(0); b[h] &= 4294967040 } else if (k > a / 8) { for (; b.length <= h;) b.push(0); b[h] &= 4294967040 } for (a = 0; a <= h; a += 1) v[a] = b[a] ^ 909522486, w[a] = b[a] ^ 1549556828; l = q(v, l); e = m; I = !0 }; this.update = function (a) { var c, b, k, f = 0, g = m >>> 5; c = d(a, h, n); a = c.binLen; b = c.value; c = a >>> 5; for (k = 0; k < c; k += g) f + m <= a && (l = q(b.slice(k, k + g), l), f += m); e += f; h = b.slice(f >>> 5); n = a % m; y = !0 }; this.getHash = function (a, b) { var k, g, d, m; if (!0 === I) throw Error("Cannot call getHash after setting HMAC key"); d = N(b); if (!0 === z) { if (-1 === d.shakeLen) throw Error("shakeLen must be specified in options"); f = d.shakeLen } switch (a) { case "HEX": k = function (a) { return O(a, f, x, d) }; break; case "B64": k = function (a) { return P(a, f, x, d) }; break; case "BYTES": k = function (a) { return Q(a, f, x) }; break; case "ARRAYBUFFER": try { g = new ArrayBuffer(0) } catch (p) { throw Error("ARRAYBUFFER not supported by this environment"); } k = function (a) { return R(a, f, x) }; break; default: throw Error("format must be HEX, B64, BYTES, or ARRAYBUFFER"); } m = u(h.slice(), n, e, r(l), f); for (g = 1; g < t; g += 1) !0 === z && 0 !== f % 32 && (m[m.length - 1] &= 16777215 >>> 24 - f % 32), m = u(m, f, 0, A(c), f); return k(m) }; this.getHMAC = function (a, b) { var k, g, d, p; if (!1 === I) throw Error("Cannot call getHMAC without first setting HMAC key"); d = N(b); switch (a) { case "HEX": k = function (a) { return O(a, f, x, d) }; break; case "B64": k = function (a) { return P(a, f, x, d) }; break; case "BYTES": k = function (a) { return Q(a, f, x) }; break; case "ARRAYBUFFER": try { k = new ArrayBuffer(0) } catch (v) { throw Error("ARRAYBUFFER not supported by this environment"); } k = function (a) { return R(a, f, x) }; break; default: throw Error("outputFormat must be HEX, B64, BYTES, or ARRAYBUFFER"); } g = u(h.slice(), n, e, r(l), f); p = q(w, A(c)); p = u(g, f, m, p, f); return k(p) } } function b(c, a) { this.a = c; this.b = a } function O(c, a, b, e) { var h = ""; a /= 8; var n, g, d; d = -1 === b ? 3 : 0; for (n = 0; n < a; n += 1) g = c[n >>> 2] >>> 8 * (d + n % 4 * b), h += "0123456789abcdef".charAt(g >>> 4 & 15) + "0123456789abcdef".charAt(g & 15); return e.outputUpper ? h.toUpperCase() : h } function P(c, a, b, e) { var h = "", n = a / 8, g, d, p, f; f = -1 === b ? 3 : 0; for (g = 0; g < n; g += 3) for (d = g + 1 < n ? c[g + 1 >>> 2] : 0, p = g + 2 < n ? c[g + 2 >>> 2] : 0, p = (c[g >>> 2] >>> 8 * (f + g % 4 * b) & 255) << 16 | (d >>> 8 * (f + (g + 1) % 4 * b) & 255) << 8 | p >>> 8 * (f + (g + 2) % 4 * b) & 255, d = 0; 4 > d; d += 1) 8 * g + 6 * d <= a ? h += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(p >>> 6 * (3 - d) & 63) : h += e.b64Pad; return h } function Q(c, a, b) { var e = ""; a /= 8; var h, d, g; g = -1 === b ? 3 : 0; for (h = 0; h < a; h += 1) d = c[h >>> 2] >>> 8 * (g + h % 4 * b) & 255, e += String.fromCharCode(d); return e } function R(c, a, b) { a /= 8; var e, h = new ArrayBuffer(a), d, g; g = new Uint8Array(h); d = -1 === b ? 3 : 0; for (e = 0; e < a; e += 1) g[e] = c[e >>> 2] >>> 8 * (d + e % 4 * b) & 255; return h } function N(c) { var a = { outputUpper: !1, b64Pad: "=", shakeLen: -1 }; c = c || {}; a.outputUpper = c.outputUpper || !1; !0 === c.hasOwnProperty("b64Pad") && (a.b64Pad = c.b64Pad); if (!0 === c.hasOwnProperty("shakeLen")) { if (0 !== c.shakeLen % 8) throw Error("shakeLen must be a multiple of 8"); a.shakeLen = c.shakeLen } if ("boolean" !== typeof a.outputUpper) throw Error("Invalid outputUpper formatting option"); if ("string" !== typeof a.b64Pad) throw Error("Invalid b64Pad formatting option"); return a } function M(c, a, b) { switch (a) { case "UTF8": case "UTF16BE": case "UTF16LE": break; default: throw Error("encoding must be UTF8, UTF16BE, or UTF16LE"); } switch (c) { case "HEX": c = function (a, c, d) { var g = a.length, l, p, f, m, q, u; if (0 !== g % 2) throw Error("String of HEX type must be in byte increments"); c = c || [0]; d = d || 0; q = d >>> 3; u = -1 === b ? 3 : 0; for (l = 0; l < g; l += 2) { p = parseInt(a.substr(l, 2), 16); if (isNaN(p)) throw Error("String of HEX type contains invalid characters"); m = (l >>> 1) + q; for (f = m >>> 2; c.length <= f;) c.push(0); c[f] |= p << 8 * (u + m % 4 * b) } return { value: c, binLen: 4 * g + d } }; break; case "TEXT": c = function (c, h, d) { var g, l, p = 0, f, m, q, u, r, t; h = h || [0]; d = d || 0; q = d >>> 3; if ("UTF8" === a) for (t = -1 === b ? 3 : 0, f = 0; f < c.length; f += 1) for (g = c.charCodeAt(f), l = [], 128 > g ? l.push(g) : 2048 > g ? (l.push(192 | g >>> 6), l.push(128 | g & 63)) : 55296 > g || 57344 <= g ? l.push(224 | g >>> 12, 128 | g >>> 6 & 63, 128 | g & 63) : (f += 1, g = 65536 + ((g & 1023) << 10 | c.charCodeAt(f) & 1023), l.push(240 | g >>> 18, 128 | g >>> 12 & 63, 128 | g >>> 6 & 63, 128 | g & 63)), m = 0; m < l.length; m += 1) { r = p + q; for (u = r >>> 2; h.length <= u;) h.push(0); h[u] |= l[m] << 8 * (t + r % 4 * b); p += 1 } else if ("UTF16BE" === a || "UTF16LE" === a) for (t = -1 === b ? 2 : 0, l = "UTF16LE" === a && 1 !== b || "UTF16LE" !== a && 1 === b, f = 0; f < c.length; f += 1) { g = c.charCodeAt(f); !0 === l && (m = g & 255, g = m << 8 | g >>> 8); r = p + q; for (u = r >>> 2; h.length <= u;) h.push(0); h[u] |= g << 8 * (t + r % 4 * b); p += 2 } return { value: h, binLen: 8 * p + d } }; break; case "B64": c = function (a, c, d) { var g = 0, l, p, f, m, q, u, r, t; if (-1 === a.search(/^[a-zA-Z0-9=+\/]+$/)) throw Error("Invalid character in base-64 string"); p = a.indexOf("="); a = a.replace(/\=/g, ""); if (-1 !== p && p < a.length) throw Error("Invalid '=' found in base-64 string"); c = c || [0]; d = d || 0; u = d >>> 3; t = -1 === b ? 3 : 0; for (p = 0; p < a.length; p += 4) { q = a.substr(p, 4); for (f = m = 0; f < q.length; f += 1) l = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(q[f]), m |= l << 18 - 6 * f; for (f = 0; f < q.length - 1; f += 1) { r = g + u; for (l = r >>> 2; c.length <= l;) c.push(0); c[l] |= (m >>> 16 - 8 * f & 255) << 8 * (t + r % 4 * b); g += 1 } } return { value: c, binLen: 8 * g + d } }; break; case "BYTES": c = function (a, c, d) { var g, l, p, f, m, q; c = c || [0]; d = d || 0; p = d >>> 3; q = -1 === b ? 3 : 0; for (l = 0; l < a.length; l += 1) g = a.charCodeAt(l), m = l + p, f = m >>> 2, c.length <= f && c.push(0), c[f] |= g << 8 * (q + m % 4 * b); return { value: c, binLen: 8 * a.length + d } }; break; case "ARRAYBUFFER": try { c = new ArrayBuffer(0) } catch (e) { throw Error("ARRAYBUFFER not supported by this environment"); } c = function (a, c, d) { var g, l, p, f, m, q; c = c || [0]; d = d || 0; l = d >>> 3; m = -1 === b ? 3 : 0; q = new Uint8Array(a); for (g = 0; g < a.byteLength; g += 1) f = g + l, p = f >>> 2, c.length <= p && c.push(0), c[p] |= q[g] << 8 * (m + f % 4 * b); return { value: c, binLen: 8 * a.byteLength + d } }; break; default: throw Error("format must be HEX, TEXT, B64, BYTES, or ARRAYBUFFER"); } return c } function y(c, a) { return c << a | c >>> 32 - a } function S(c, a) { return 32 < a ? (a -= 32, new b(c.b << a | c.a >>> 32 - a, c.a << a | c.b >>> 32 - a)) : 0 !== a ? new b(c.a << a | c.b >>> 32 - a, c.b << a | c.a >>> 32 - a) : c } function w(c, a) { return c >>> a | c << 32 - a } function t(c, a) { var k = null, k = new b(c.a, c.b); return k = 32 >= a ? new b(k.a >>> a | k.b << 32 - a & 4294967295, k.b >>> a | k.a << 32 - a & 4294967295) : new b(k.b >>> a - 32 | k.a << 64 - a & 4294967295, k.a >>> a - 32 | k.b << 64 - a & 4294967295) } function T(c, a) { var k = null; return k = 32 >= a ? new b(c.a >>> a, c.b >>> a | c.a << 32 - a & 4294967295) : new b(0, c.a >>> a - 32) } function aa(c, a, b) { return c & a ^ ~c & b } function ba(c, a, k) { return new b(c.a & a.a ^ ~c.a & k.a, c.b & a.b ^ ~c.b & k.b) } function U(c, a, b) { return c & a ^ c & b ^ a & b } function ca(c, a, k) { return new b(c.a & a.a ^ c.a & k.a ^ a.a & k.a, c.b & a.b ^ c.b & k.b ^ a.b & k.b) } function da(c) { return w(c, 2) ^ w(c, 13) ^ w(c, 22) } function ea(c) { var a = t(c, 28), k = t(c, 34); c = t(c, 39); return new b(a.a ^ k.a ^ c.a, a.b ^ k.b ^ c.b) } function fa(c) { return w(c, 6) ^ w(c, 11) ^ w(c, 25) } function ga(c) { var a = t(c, 14), k = t(c, 18); c = t(c, 41); return new b(a.a ^ k.a ^ c.a, a.b ^ k.b ^ c.b) } function ha(c) { return w(c, 7) ^ w(c, 18) ^ c >>> 3 } function ia(c) { var a = t(c, 1), k = t(c, 8); c = T(c, 7); return new b(a.a ^ k.a ^ c.a, a.b ^ k.b ^ c.b) } function ja(c) { return w(c, 17) ^ w(c, 19) ^ c >>> 10 } function ka(c) { var a = t(c, 19), k = t(c, 61); c = T(c, 6); return new b(a.a ^ k.a ^ c.a, a.b ^ k.b ^ c.b) } function G(c, a) { var b = (c & 65535) + (a & 65535); return ((c >>> 16) + (a >>> 16) + (b >>> 16) & 65535) << 16 | b & 65535 } function la(c, a, b, e) { var h = (c & 65535) + (a & 65535) + (b & 65535) + (e & 65535); return ((c >>> 16) + (a >>> 16) + (b >>> 16) + (e >>> 16) + (h >>> 16) & 65535) << 16 | h & 65535 } function H(c, a, b, e, h) { var d = (c & 65535) + (a & 65535) + (b & 65535) + (e & 65535) + (h & 65535); return ((c >>> 16) + (a >>> 16) + (b >>> 16) + (e >>> 16) + (h >>> 16) + (d >>> 16) & 65535) << 16 | d & 65535 } function ma(c, a) { var d, e, h; d = (c.b & 65535) + (a.b & 65535); e = (c.b >>> 16) + (a.b >>> 16) + (d >>> 16); h = (e & 65535) << 16 | d & 65535; d = (c.a & 65535) + (a.a & 65535) + (e >>> 16); e = (c.a >>> 16) + (a.a >>> 16) + (d >>> 16); return new b((e & 65535) << 16 | d & 65535, h) } function na(c, a, d, e) { var h, n, g; h = (c.b & 65535) + (a.b & 65535) + (d.b & 65535) + (e.b & 65535); n = (c.b >>> 16) + (a.b >>> 16) + (d.b >>> 16) + (e.b >>> 16) + (h >>> 16); g = (n & 65535) << 16 | h & 65535; h = (c.a & 65535) + (a.a & 65535) + (d.a & 65535) + (e.a & 65535) + (n >>> 16); n = (c.a >>> 16) + (a.a >>> 16) + (d.a >>> 16) + (e.a >>> 16) + (h >>> 16); return new b((n & 65535) << 16 | h & 65535, g) } function oa(c, a, d, e, h) { var n, g, l; n = (c.b & 65535) + (a.b & 65535) + (d.b & 65535) + (e.b & 65535) + (h.b & 65535); g = (c.b >>> 16) + (a.b >>> 16) + (d.b >>> 16) + (e.b >>> 16) + (h.b >>> 16) + (n >>> 16); l = (g & 65535) << 16 | n & 65535; n = (c.a & 65535) + (a.a & 65535) + (d.a & 65535) + (e.a & 65535) + (h.a & 65535) + (g >>> 16); g = (c.a >>> 16) + (a.a >>> 16) + (d.a >>> 16) + (e.a >>> 16) + (h.a >>> 16) + (n >>> 16); return new b((g & 65535) << 16 | n & 65535, l) } function B(c, a) { return new b(c.a ^ a.a, c.b ^ a.b) } function A(c) { var a = [], d; if ("SHA-1" === c) a = [1732584193, 4023233417, 2562383102, 271733878, 3285377520]; else if (0 === c.lastIndexOf("SHA-", 0)) switch (a = [3238371032, 914150663, 812702999, 4144912697, 4290775857, 1750603025, 1694076839, 3204075428], d = [1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225], c) { case "SHA-224": break; case "SHA-256": a = d; break; case "SHA-384": a = [new b(3418070365, a[0]), new b(1654270250, a[1]), new b(2438529370, a[2]), new b(355462360, a[3]), new b(1731405415, a[4]), new b(41048885895, a[5]), new b(3675008525, a[6]), new b(1203062813, a[7])]; break; case "SHA-512": a = [new b(d[0], 4089235720), new b(d[1], 2227873595), new b(d[2], 4271175723), new b(d[3], 1595750129), new b(d[4], 2917565137), new b(d[5], 725511199), new b(d[6], 4215389547), new b(d[7], 327033209) ]; break; default: throw Error("Unknown SHA variant"); } else if (0 === c.lastIndexOf("SHA3-", 0) || 0 === c.lastIndexOf("SHAKE", 0)) for (c = 0; 5 > c; c += 1) a[c] = [new b(0, 0), new b(0, 0), new b(0, 0), new b(0, 0), new b(0, 0)]; else throw Error("No SHA variants supported"); return a } function K(c, a) { var b = [], e, d, n, g, l, p, f; e = a[0]; d = a[1]; n = a[2]; g = a[3]; l = a[4]; for (f = 0; 80 > f; f += 1) b[f] = 16 > f ? c[f] : y(b[f - 3] ^ b[f - 8] ^ b[f - 14] ^ b[f - 16], 1), p = 20 > f ? H(y(e, 5), d & n ^ ~d & g, l, 1518500249, b[f]) : 40 > f ? H(y(e, 5), d ^ n ^ g, l, 1859775393, b[f]) : 60 > f ? H(y(e, 5), U(d, n, g), l, 2400959708, b[f]) : H(y(e, 5), d ^ n ^ g, l, 3395469782, b[f]), l = g, g = n, n = y(d, 30), d = e, e = p; a[0] = G(e, a[0]); a[1] = G(d, a[1]); a[2] = G(n, a[2]); a[3] = G(g, a[3]); a[4] = G(l, a[4]); return a } function Z(c, a, b, e) { var d; for (d = (a + 65 >>> 9 << 4) + 15; c.length <= d;) c.push(0); c[a >>> 5] |= 128 << 24 - a % 32; a += b; c[d] = a & 4294967295; c[d - 1] = a / 4294967296 | 0; a = c.length; for (d = 0; d < a; d += 16) e = K(c.slice(d, d + 16), e); return e } function L(c, a, k) { var e, h, n, g, l, p, f, m, q, u, r, t, v, w, y, A, z, x, F, B, C, D, E = [], J; if ("SHA-224" === k || "SHA-256" === k) u = 64, t = 1, D = Number, v = G, w = la, y = H, A = ha, z = ja, x = da, F = fa, C = U, B = aa, J = d; else if ("SHA-384" === k || "SHA-512" === k) u = 80, t = 2, D = b, v = ma, w = na, y = oa, A = ia, z = ka, x = ea, F = ga, C = ca, B = ba, J = V; else throw Error("Unexpected error in SHA-2 implementation"); k = a[0]; e = a[1]; h = a[2]; n = a[3]; g = a[4]; l = a[5]; p = a[6]; f = a[7]; for (r = 0; r < u; r += 1) 16 > r ? (q = r * t, m = c.length <= q ? 0 : c[q], q = c.length <= q + 1 ? 0 : c[q + 1], E[r] = new D(m, q)) : E[r] = w(z(E[r - 2]), E[r - 7], A(E[r - 15]), E[r - 16]), m = y(f, F(g), B(g, l, p), J[r], E[r]), q = v(x(k), C(k, e, h)), f = p, p = l, l = g, g = v(n, m), n = h, h = e, e = k, k = v(m, q); a[0] = v(k, a[0]); a[1] = v(e, a[1]); a[2] = v(h, a[2]); a[3] = v(n, a[3]); a[4] = v(g, a[4]); a[5] = v(l, a[5]); a[6] = v(p, a[6]); a[7] = v(f, a[7]); return a } function D(c, a) { var d, e, h, n, g = [], l = []; if (null !== c) for (e = 0; e < c.length; e += 2) a[(e >>> 1) % 5][(e >>> 1) / 5 | 0] = B(a[(e >>> 1) % 5][(e >>> 1) / 5 | 0], new b(c[e + 1], c[e])); for (d = 0; 24 > d; d += 1) { n = A("SHA3-"); for (e = 0; 5 > e; e += 1) { h = a[e][0]; var p = a[e][1], f = a[e][2], m = a[e][3], q = a[e][4]; g[e] = new b(h.a ^ p.a ^ f.a ^ m.a ^ q.a, h.b ^ p.b ^ f.b ^ m.b ^ q.b) } for (e = 0; 5 > e; e += 1) l[e] = B(g[(e + 4) % 5], S(g[(e + 1) % 5], 1)); for (e = 0; 5 > e; e += 1) for (h = 0; 5 > h; h += 1) a[e][h] = B(a[e][h], l[e]); for (e = 0; 5 > e; e += 1) for (h = 0; 5 > h; h += 1) n[h][(2 * e + 3 * h) % 5] = S(a[e][h], W[e][h]); for (e = 0; 5 > e; e += 1) for (h = 0; 5 > h; h += 1) a[e][h] = B(n[e][h], new b(~n[(e + 1) % 5][h].a & n[(e + 2) % 5][h].a, ~n[(e + 1) % 5][h].b & n[(e + 2) % 5][h].b)); a[0][0] = B(a[0][0], X[d]) } return a } var d, V, W, X; d = [1116352408, 1899447441, 3049323471, 3921009573, 961987163, 1508970993, 2453635748, 2870763221, 3624381080, 310598401, 607225278, 1426881987, 1925078388, 2162078206, 2614888103, 3248222580, 3835390401, 4022224774, 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, 2554220882, 2821834349, 2952996808, 3210313671, 3336571891, 3584528711, 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291, 1695183700, 1986661051, 2177026350, 2456956037, 2730485921, 2820302411, 3259730800, 3345764771, 3516065817, 3600352804, 4094571909, 275423344, 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218, 1537002063, 1747873779, 1955562222, 2024104815, 2227730452, 2361852424, 2428436474, 2756734187, 3204031479, 3329325298 ]; V = [new b(d[0], 3609767458), new b(d[1], 602891725), new b(d[2], 3964484399), new b(d[3], 2173295548), new b(d[4], 4081628472), new b(d[5], 3053834265), new b(d[6], 2937671579), new b(d[7], 3664609560), new b(d[8], 2734883394), new b(d[9], 1164996542), new b(d[10], 1323610764), new b(d[11], 3590304994), new b(d[12], 4068182383), new b(d[13], 991336113), new b(d[14], 633803317), new b(d[15], 3479774868), new b(d[16], 2666613458), new b(d[17], 944711139), new b(d[18], 2341262773), new b(d[19], 2007800933), new b(d[20], 1495990901), new b(d[21], 1856431235), new b(d[22], 3175218132), new b(d[23], 2198950837), new b(d[24], 3999719339), new b(d[25], 766784016), new b(d[26], 2566594879), new b(d[27], 3203337956), new b(d[28], 1034457026), new b(d[29], 2466948901), new b(d[30], 3758326383), new b(d[31], 168717936), new b(d[32], 1188179964), new b(d[33], 1546045734), new b(d[34], 1522805485), new b(d[35], 2643833823), new b(d[36], 2343527390), new b(d[37], 1014477480), new b(d[38], 1206759142), new b(d[39], 344077627), new b(d[40], 1290863460), new b(d[41], 3158454273), new b(d[42], 3505952657), new b(d[43], 106217008), new b(d[44], 3606008344), new b(d[45], 1432725776), new b(d[46], 1467031594), new b(d[47], 851169720), new b(d[48], 3100823752), new b(d[49], 1363258195), new b(d[50], 3750685593), new b(d[51], 3785050280), new b(d[52], 3318307427), new b(d[53], 3812723403), new b(d[54], 2003034995), new b(d[55], 3602036899), new b(d[56], 1575990012), new b(d[57], 1125592928), new b(d[58], 2716904306), new b(d[59], 442776044), new b(d[60], 593698344), new b(d[61], 3733110249), new b(d[62], 2999351573), new b(d[63], 3815920427), new b(3391569614, 3928383900), new b(3515267271, 566280711), new b(3940187606, 3454069534), new b(4118630271, 4000239992), new b(116418474, 1914138554), new b(174292421, 2731055270), new b(289380356, 3203993006), new b(460393269, 320620315), new b(685471733, 587496836), new b(852142971, 1086792851), new b(1017036298, 365543100), new b(1126000580, 2618297676), new b(1288033470, 3409855158), new b(1501505948, 4234509866), new b(1607167915, 987167468), new b(1816402316, 1246189591) ]; X = [new b(0, 1), new b(0, 32898), new b(2147483648, 32906), new b(2147483648, 2147516416), new b(0, 32907), new b(0, 2147483649), new b(2147483648, 2147516545), new b(2147483648, 32777), new b(0, 138), new b(0, 136), new b(0, 2147516425), new b(0, 2147483658), new b(0, 2147516555), new b(2147483648, 139), new b(2147483648, 32905), new b(2147483648, 32771), new b(2147483648, 32770), new b(2147483648, 128), new b(0, 32778), new b(2147483648, 2147483658), new b(2147483648, 2147516545), new b(2147483648, 32896), new b(0, 2147483649), new b(2147483648, 2147516424) ]; W = [ [0, 36, 3, 41, 18], [1, 44, 10, 45, 2], [62, 6, 43, 15, 61], [28, 55, 25, 21, 56], [27, 20, 39, 8, 14] ]; "function" === typeof define && define.amd ? define(function () { return C }) : "undefined" !== typeof exports ? ("undefined" !== typeof module && module.exports && (module.exports = C), exports = C) : Y.jsSHA = C })(this); /* 优化说明 1、改成中文 "确定"、"取消" 按钮。 2、select 新增了 textContents 数组。 3、新增了skin: 'tab'换页切换菜单样式 4、更新部分翻译 5、优化字体显示效果 6、优化同一行内CSS样式 */ // The GM_config constructor function GM_configStruct() { // call init() if settings were passed to constructor if (arguments.length) { GM_configInit(this, arguments); this.onInit(); } } // This is the initializer function function GM_configInit(config, args) { // Initialize instance variables if (typeof config.fields == "undefined") { config.fields = {}; config.onInit = config.onInit || function () {}; config.onOpen = config.onOpen || function () {}; config.onSave = config.onSave || function () {}; config.onClose = config.onClose || function () {}; config.onReset = config.onReset || function () {}; config.isOpen = false; config.title = '用户脚本设置'; config.css = { basic: [ "#GM_config * { font-family: arial,tahoma,myriad pro,sans-serif; }", "#GM_config { background: #FFF; }", "#GM_config input[type='radio'] { margin-right: 8px; }", "#GM_config .indent40 { margin-left: 40%; }", "#GM_config .field_label { font-size: 14px; font-weight: bold; margin-right: 6px; }", "#GM_config .radio_label { font-size: 14px; }", "#GM_config .block { display: block; }", "#GM_config .saveclose_buttons { margin: 16px 10px 10px; padding: 2px 12px; }", "#GM_config .reset, #GM_config .reset a," + " #GM_config_buttons_holder { color: #000; text-align: right; }", "#GM_config .config_header { font-size: 20pt; margin: 0; }", "#GM_config .config_desc, #GM_config .section_desc, #GM_config .reset { font-size: 9pt; }", "#GM_config .center { text-align: center; }", "#GM_config .section_header_holder { margin-top: 8px; }", "#GM_config .config_var { margin: 0 0 4px; }", "#GM_config .section_header { background: #414141; border: 1px solid #000; color: #FFF;" + " font-size: 12pt; margin: 0; }", "#GM_config .section_desc { background: #EFEFEF; border: 1px solid #CCC; color: #575757;" + " font-size: 10pt; margin: 0 0 6px; }", // newer "#GM_config input[type='number'] { width: 60px; }", "#GM_config .nav-tabs { margin: 10 0}", "#GM_config .nav-tabs > div { display: inline; padding: 3px 10px; }", "#pv-prefs .section_header_holder { padding-left: 10px; }", ].join('\n') + '\n', skin_tab: [ "#GM_config { background: #EEE; }", "#GM_config textarea { width: 98%; height: 45px; margin-top: 5px; }", "#GM_config .field_label { display: inline-block; font-weight: normal; }", // 在同一行内的设置 "#GM_config .inline input[type='checkbox'] {margin: 3px 3px 3px 0px;}", "#GM_config .inline .config_var { margin-left: 15px; }", // 内容样式 "#GM_config .config_var { font-size: 14px; padding: 5px; margin: 0; }", "#GM_config .config_header a { text-decoration: none; color: #000; }", "#GM_config .nav-tabs { margin: 20 0}", "#GM_config .nav-tabs > div { font-size: 15px; color: #999; cursor: pointer; padding: 10px 20px; }", "#GM_config .nav-tabs > .active { cursor: default; color: #FFF; }", "#GM_config .nav-tabs > div:hover { color: #FFF; }", ].join('\n') + '\n', skin_1: [ // 仿 Mouseover Popup Image Viewer 样式 "#GM_config { background: #EEE; }", "#GM_config textarea { width: 98%; height: 45px; margin-top: 5px; }", "#GM_config .config_var { font-size: 12px; }", "#GM_config .inline .config_var { margin-left: 15px; }", "#GM_config .field_label { display: inline-block; font-weight: normal; }", "#GM_config { padding: 20px 30px; margin: 0; }", "#GM_config .config_header { margin-bottom: 10px; }", "#GM_config div.config_var { padding: 7px 0; }", ].join('\n') + '\n', basicPrefix: "GM_config", stylish: "" }; } if (args.length == 1 && typeof args[0].id == "string" && typeof args[0].appendChild != "function") var settings = args[0]; else { // Provide backwards-compatibility with argument style intialization var settings = {}; // loop through GM_config.init() arguments for (var i = 0, l = args.length, arg; i < l; ++i) { arg = args[i]; // An element to use as the config window if (typeof arg.appendChild == "function") { settings.frame = arg; continue; } switch (typeof arg) { case 'object': for (var j in arg) { // could be a callback functions or settings object if (typeof arg[j] != "function") { // we are in the settings object if (typeof arg[j] == 'string') { settings.frameStyle = arg; } else { settings.fields = arg; // store settings object } break; // leave the loop } // otherwise it must be a callback function if (!settings.events) settings.events = {}; settings.events[j] = arg[j]; } break; case 'function': // passing a bare function is set to open callback settings.events = { open: arg }; break; case 'string': // could be custom CSS or the title string // if (/[\w\.]+\s*\{\s*[\w-]+\s*:\s*\w+[\s|\S]*\}/.test(arg)) if (/[\w\.]+\s*\{\s*[\w-]+\s*:[\s|\S]*\}/.test(arg)) settings.css = arg; else if (arg) settings.title = arg; break; } } } /* Initialize everything using the new settings object */ // Set the id if (settings.id) config.id = settings.id; else if (typeof config.id == "undefined") config.id = 'GM_config'; // Set the title if (settings.title) config.title = settings.title; // Set the custom css if (settings.css) config.css.stylish = settings.css; if (settings.skin) { var skin = config.css['skin_' + settings.skin]; if (skin) { config.css.basic += skin; } } // Set the frame if (settings.frame) config.frame = settings.frame; if (settings.frameStyle) config.frameStyle = settings.frameStyle; config.isTabs = settings.isTabs; // Set the event callbacks if (settings.events) { var events = settings.events; for (var e in events) config["on" + e.charAt(0).toUpperCase() + e.slice(1)] = events[e]; } // Create the fields if (settings.fields) { var stored = config.read(), // read the stored settings fields = settings.fields, customTypes = settings.types || {}; for (var id in fields) { var field = fields[id]; // for each field definition create a field object if (field) config.fields[id] = new GM_configField(field, stored[id], id, customTypes[field.type]); else if (config.fields[id]) delete config.fields[id]; } } // If the id has changed we must modify the default style if (config.id != config.css.basicPrefix) { config.css.basic = config.css.basic.replace( new RegExp('#' + config.css.basicPrefix, 'gm'), '#' + config.id); config.css.basicPrefix = config.id; } } GM_configStruct.prototype = { // Support old method of initalizing init: function () { GM_configInit(this, arguments); this.onInit(); }, // call GM_config.open() from your script to open the menu open: function () { // Die if the menu is already open on this page // You can have multiple instances but you can't open the same instance twice var match = document.getElementById(this.id); if (match && (match.tagName == "IFRAME" || match.childNodes.length > 0)) return; // Sometimes "this" gets overwritten so create an alias var config = this; // Function to build the mighty config window :) function buildConfigWin(body, head) { var create = config.create, fields = config.fields, configId = config.id, bodyWrapper = create('div', { id: configId + '_wrapper' }); // Append the style which is our default style plus the user style head.appendChild( create('style', { type: 'text/css', textContent: config.css.basic + config.css.stylish })); // Add header and title bodyWrapper.appendChild(create('div', { id: configId + '_header', className: 'config_header block center' }, config.title)); // Append elements var section = bodyWrapper, secNum = 0; // Section count var lastParentNode = null; // loop through fields for (var id in fields) { var field = fields[id], settings = field.settings; if (settings.section) { // the start of a new section section = bodyWrapper.appendChild(create('div', { className: 'section_header_holder', id: configId + '_section_' + secNum })); if (Object.prototype.toString.call(settings.section) !== '[object Array]') settings.section = [settings.section]; if (settings.section[0]) section.appendChild(create('div', { className: 'section_header center', id: configId + '_section_header_' + secNum }, settings.section[0])); if (settings.section[1]) section.appendChild(create('p', { className: 'section_desc center', id: configId + '_section_desc_' + secNum }, settings.section[1])); ++secNum; } if (settings.line == 'start' && lastParentNode) { // 切换到下一行 lastParentNode = null; } // Create field elements and append to current section (lastParentNode || section).appendChild((field.wrapper = field.toNode(configId, lastParentNode))); if (settings.line == 'start') { lastParentNode = field.wrapper; lastParentNode.classList.add('inline') } else if (settings.line == 'end') { lastParentNode = null; } } // Add save and close buttons bodyWrapper.appendChild(create('div', { id: configId + '_buttons_holder' }, create('button', { id: configId + '_saveBtn', textContent: '确定', title: '部分选项需要刷新页面才能生效', className: 'saveclose_buttons', onclick: function () { config.save(); config.close(); } }), create('button', { id: configId + '_closeBtn', textContent: '取消', title: '取消本次设置,所有选项还原', className: 'saveclose_buttons', onclick: function () { config.close() } }), create('div', { className: 'reset_holder block' }, // Reset link create('a', { id: configId + '_resetLink', textContent: '恢复默认设置', href: '#', title: '恢复所有设置的内容为默认值', className: 'reset', onclick: function (e) { e.preventDefault(); config.reset() } }) ))); body.appendChild(bodyWrapper); // Paint everything to window at once config.center(); // Show and center iframe window.addEventListener('resize', config.center, false); // Center frame on resize // Call the open() callback function config.onOpen(config.frame.contentDocument || config.frame.ownerDocument, config.frame.contentWindow || window, config.frame); if (config.isTabs) { config.toTabs(); } // Close frame on window close window.addEventListener('beforeunload', function () { config.close(); }, false); // Now that everything is loaded, make it visible config.frame.style.display = "block"; config.isOpen = true; } // Change this in the onOpen callback using this.frame.setAttribute('style', '') var defaultStyle = 'bottom: auto; border: 1px solid #000; display: none; height: 75%;' + ' left: 0; margin: 0; max-height: 95%; max-width: 95%; opacity: 0;' + ' overflow: auto; padding: 0; position: fixed; right: auto; top: 0;' + ' width: 75%; z-index: 999999999;'; // Either use the element passed to init() or create an iframe if (this.frame) { this.frame.id = this.id; // Allows for prefixing styles with the config id this.frame.setAttribute('style', defaultStyle); buildConfigWin(this.frame, this.frame.ownerDocument.getElementsByTagName('head')[0]); } else { // Create frame document.body.appendChild((this.frame = this.create('iframe', { id: this.id, style: defaultStyle }))); if (this.frameStyle) { Object.keys(this.frameStyle).forEach(function (key) { config.frame.style[key] = config.frameStyle[key]; }) } // In WebKit src can't be set until it is added to the page this.frame.src = 'about:blank'; // we wait for the iframe to load before we can modify it this.frame.addEventListener('load', function (e) { var frame = config.frame; var body = frame.contentDocument.getElementsByTagName('body')[0]; body.id = config.id; // Allows for prefixing styles with the config id buildConfigWin(body, frame.contentDocument.getElementsByTagName('head')[0]); }, false); } }, save: function () { var forgotten = this.write(); this.onSave(forgotten); // Call the save() callback function }, close: function () { if (!this.frame) return; // If frame is an iframe then remove it if (this.frame.contentDocument) { this.remove(this.frame); this.frame = null; } else { // else wipe its content this.frame.innerHTML = ""; this.frame.style.display = "none"; } // Null out all the fields so we don't leak memory var fields = this.fields; for (var id in fields) { var field = fields[id]; field.wrapper = null; field.node = null; } this.onClose(); // Call the close() callback function this.isOpen = false; }, set: function (name, val) { this.fields[name].value = val; if (this.fields[name].node) { this.fields[name].reload(); } }, get: function (name, getLive) { var field = this.fields[name], fieldVal = null; if (getLive && field.node) { fieldVal = field.toValue(); } return fieldVal != null ? fieldVal : field.value; }, write: function (store, obj) { if (!obj) { var values = {}, forgotten = {}, fields = this.fields; for (var id in fields) { var field = fields[id]; var value = field.toValue(); if (field.save) { if (value != null) { values[id] = value; field.value = value; } else values[id] = field.value; } else forgotten[id] = value; } } try { this.setValue(store || this.id, this.stringify(obj || values)); } catch (e) { this.log("GM_config failed to save settings!"); } return forgotten; }, read: function (store) { try { var rval = this.parser(this.getValue(store || this.id, '{}')); } catch (e) { this.log("GM_config failed to read saved settings!"); var rval = {}; } return rval; }, reset: function () { var fields = this.fields; // Reset all the fields for (var id in fields) fields[id].reset(); this.onReset(); // Call the reset() callback function }, create: function () { switch (arguments.length) { case 1: var A = document.createTextNode(arguments[0]); break; default: var A = document.createElement(arguments[0]), B = arguments[1]; for (var b in B) { if (b.indexOf("on") == 0) A.addEventListener(b.substring(2), B[b], false); else if (",style,accesskey,id,name,src,href,which,for".indexOf("," + b.toLowerCase()) != -1) A.setAttribute(b, B[b]); else if (typeof B[b] != 'undefined') A[b] = B[b]; } if (typeof arguments[2] == "string") A.innerHTML = arguments[2]; else for (var i = 2, len = arguments.length; i < len; ++i) A.appendChild(arguments[i]); } return A; }, center: function () { var node = this.frame; if (!node) return; var style = node.style, beforeOpacity = style.opacity; if (style.display == 'none') style.opacity = '0'; style.display = ''; style.top = Math.floor((window.innerHeight / 2) - (node.offsetHeight / 2)) + 'px'; style.left = Math.floor((window.innerWidth / 2) - (node.offsetWidth / 2)) + 'px'; style.opacity = '1'; }, remove: function (el) { if (el && el.parentNode) el.parentNode.removeChild(el); }, toTabs: function () { // 转为 tab 的形式 var body = this.frame.tagName == 'IFRAME' ? this.frame.contentWindow.document : this.frame, configId = this.id; var $ = function (id) { return body.getElementById(configId + '_' + id); }; var headers = body.querySelectorAll('.section_header'); if (!headers.length) return; var anch = this.create('div', { // id: configId + '_tab_holder', className: 'nav-tabs', }); for (var i = 0, header; i < headers.length; i++) { header = headers[i]; if (i == 0) { header.classList.add('active'); } anch.appendChild(header); } anch.addEventListener('click', this.toggleTab.bind(this), false); $('section_0').parentNode.insertBefore(anch, $('section_0')); var curTab = localStorage.getItem('picviewerCE.config.curTab') || 0; this.toggleTab(parseInt(curTab, 10)); }, toggleTab: function (e) { var body = this.frame.tagName == 'IFRAME' ? this.frame.contentWindow.document : this.frame, configId = this.id; var curTab = typeof e == 'number' ? e : /\_(\d+)/.exec(e.target.id)[1]; [].forEach.call(body.querySelectorAll('.section_header'), function (header, i) { if (i == curTab) { header.classList.add('active'); } else { header.classList.remove('active'); } }); [].forEach.call(body.querySelectorAll('.section_header_holder'), function (holder, i) { holder.style.display = (i == curTab) ? 'block' : 'none'; }); localStorage.setItem('picviewerCE.config.curTab', curTab) } }; // Define a bunch of API stuff (function () { var isGM = typeof GM_getValue != 'undefined' && typeof GM_getValue('a', 'b') != 'undefined', setValue, getValue, stringify, parser; // Define value storing and reading API if (!isGM) { setValue = function (name, value) { return localStorage.setItem(name, value); }; getValue = function (name, def) { var s = localStorage.getItem(name); return s == null ? def : s }; // We only support JSON parser outside GM stringify = JSON.stringify; parser = JSON.parse; } else { setValue = GM_setValue; getValue = GM_getValue; stringify = typeof JSON == "undefined" ? function (obj) { return obj.toSource(); } : JSON.stringify; parser = typeof JSON == "undefined" ? function (jsonData) { return (new Function('return ' + jsonData + ';'))(); } : JSON.parse; } GM_configStruct.prototype.isGM = isGM; GM_configStruct.prototype.setValue = setValue; GM_configStruct.prototype.getValue = getValue; GM_configStruct.prototype.stringify = stringify; GM_configStruct.prototype.parser = parser; GM_configStruct.prototype.log = window.console ? console.log : (isGM && typeof GM_log != 'undefined' ? GM_log : (window.opera ? opera.postError : function () { /* no logging */ } )); })(); function GM_configDefaultValue(type, options) { var value; if (type && type.indexOf('unsigned ') == 0) type = type.substring(9); switch (type) { case 'radio': case 'select': value = options[0]; break; case 'checkbox': value = false; break; case 'int': case 'integer': case 'float': case 'number': value = 0; break; default: value = ''; } return value; } function GM_configField(settings, stored, id, customType) { // Store the field's settings this.settings = settings; this.id = id; this.node = null; this.wrapper = null; this.save = typeof settings.save == "undefined" ? true : settings.save; // Buttons are static and don't have a stored value if (settings.type == "button") this.save = false; if (settings.type == "span") this.save = false; // if a default value wasn't passed through init() then // if the type is custom use its default value // else use default value for type // else use the default value passed through init() this['default'] = typeof settings['default'] == "undefined" ? customType ? customType['default'] : GM_configDefaultValue(settings.type, settings.options) : settings['default']; // Store the field's value this.value = typeof stored == "undefined" ? this['default'] : stored; // Setup methods for a custom type if (customType) { this.toNode = customType.toNode; this.toValue = customType.toValue; this.reset = customType.reset; } } GM_configField.prototype = { create: GM_configStruct.prototype.create, toNode: function (configId, lastParentNode) { var field = this.settings, value = this.value, options = field.options, type = field.type, id = this.id, labelPos = field.labelPos, create = this.create; function addLabel(pos, labelEl, parentNode, beforeEl) { if (!beforeEl) { beforeEl = lastParentNode ? parentNode.lastChild : parentNode.firstChild; // oneLine 的修正 } switch (pos) { case 'right': case 'below': if (pos == 'below') parentNode.appendChild(create('br', {})); parentNode.appendChild(labelEl); break; default: if (pos == 'above') parentNode.insertBefore(create('br', {}), beforeEl); parentNode.insertBefore(labelEl, beforeEl); } } var retNode = create('div', { className: 'config_var', id: configId + '_' + id + '_var', title: field.title || '' }), firstProp; // Retrieve the first prop for (var i in field) { firstProp = i; break; } var label = field.label && type != "button" ? create('label', { id: configId + '_' + id + '_field_label', for: configId + '_field_' + id, className: 'field_label' }, field.label) : null; switch (type) { case 'span': label = null; this.node = create('span', { innerHTML: field.label, className: 'field_label', title: field.title, style: field.style }); retNode = this.node; break; case 'textarea': retNode.appendChild((this.node = create('textarea', { innerHTML: value, id: configId + '_field_' + id, className: 'block' + (field.className ? (" " + field.className) : ''), cols: (field.cols ? field.cols : 20), rows: (field.rows ? field.rows : 2), placeholder: field.placeholder }))); break; case 'radio': var wrap = create('div', { id: configId + '_field_' + id, className: field.className }); this.node = wrap; for (var i = 0, len = options.length; i < len; ++i) { var radLabel = create('label', { className: 'radio_label' }, options[i]); var rad = wrap.appendChild(create('input', { value: options[i], type: 'radio', name: id, checked: options[i] == value })); var radLabelPos = labelPos && (labelPos == 'left' || labelPos == 'right') ? labelPos : firstProp == 'options' ? 'left' : 'right'; addLabel(radLabelPos, radLabel, wrap, rad); } retNode.appendChild(wrap); break; case 'select': var wrap = create('select', { id: configId + '_field_' + id }); this.node = wrap; for (var i = 0, len = options.length; i < len; ++i) { var option = options[i]; wrap.appendChild(create('option', { value: option, selected: option == value }, option)); } retNode.appendChild(wrap); break; default: // fields using input elements var props = { id: configId + '_field_' + id, type: type, value: type == 'button' ? field.label : value }; switch (type) { case 'checkbox': props.checked = value; break; case 'button': props.size = field.size ? field.size : 25; if (field.script) field.click = field.script; if (field.click) props.onclick = field.click; break; case 'hidden': break; default: // type = text, int, or float props.type = 'text'; props.size = field.size ? field.size : 25; } retNode.appendChild((this.node = create('input', props))); } if (label) { // If the label is passed first, insert it before the field // else insert it after if (!labelPos) labelPos = firstProp == "label" || type == "radio" ? "left" : "right"; addLabel(labelPos, label, retNode); } return retNode; }, toValue: function () { var node = this.node, field = this.settings, type = field.type, unsigned = false, rval = null; if (!node) return rval; if (type.indexOf('unsigned ') == 0) { type = type.substring(9); unsigned = true; } switch (type) { case 'checkbox': rval = node.checked; break; case 'select': rval = node[node.selectedIndex].value; break; case 'radio': var radios = node.getElementsByTagName('input'); for (var i = 0, len = radios.length; i < len; ++i) if (radios[i].checked) rval = radios[i].value; break; case 'button': break; case 'int': case 'integer': case 'float': case 'number': var num = Number(node.value); var warn = '输入字符 "' + field.label + '" 要求必须为' + (unsigned ? ' 正 ' : 'n ') + '整数值'; if (isNaN(num) || (type.substr(0, 3) == 'int' && Math.ceil(num) != Math.floor(num)) || (unsigned && num < 0)) { alert(warn + '.'); return null; } if (!this._checkNumberRange(num, warn)) return null; rval = num; break; default: rval = node.value; break; } return rval; // value read successfully }, reset: function () { var node = this.node, field = this.settings, type = field.type; if (!node) return; switch (type) { case 'checkbox': node.checked = this['default']; break; case 'select': for (var i = 0, len = node.options.length; i < len; ++i) if (node.options[i].value == this['default']) node.selectedIndex = i; break; case 'radio': var radios = node.getElementsByTagName('input'); for (var i = 0, len = radios.length; i < len; ++i) if (radios[i].value == this['default']) radios[i].checked = true; break; case 'button': break; default: node.value = this['default']; break; } }, remove: function (el) { GM_configStruct.prototype.remove(el || this.wrapper); this.wrapper = null; this.node = null; }, reload: function () { var wrapper = this.wrapper; if (wrapper) { var fieldParent = wrapper.parentNode; fieldParent.insertBefore((this.wrapper = this.toNode()), wrapper); this.remove(wrapper); } }, _checkNumberRange: function (num, warn) { var field = this.settings; if (typeof field.min == "number" && num < field.min) { alert(warn + ' greater than or equal to ' + field.min + '.'); return null; } if (typeof field.max == "number" && num > field.max) { alert(warn + ' less than or equal to ' + field.max + '.'); return null; } return true; } }; // Create default instance of GM_config var GM_config = new GM_configStruct(); /*waitForKeyElements*/ /*--- waitForKeyElements(): A utility function, for Greasemonkey scripts, that detects and handles AJAXed content. Usage example: waitForKeyElements ("div.comments", commentCallbackFunction); //--- Page-specific function to do what we want when the node is found. function commentCallbackFunction (jNode) { jNode.text ("This comment changed by waitForKeyElements()."); } IMPORTANT: This function requires your script to have loaded jQuery. */ function waitForKeyElements( selectorTxt, /* Required: The jQuery selector string that specifies the desired element(s). */ actionFunction, /* Required: The code to run when elements are found. It is passed a jNode to the matched element. */ bWaitOnce, /* Optional: If false, will continue to scan for new elements even after the first match is found. */ iframeSelector /* Optional: If set, identifies the iframe to search. */ ) { var targetNodes, btargetsFound; if (typeof iframeSelector == "undefined") targetNodes = $(selectorTxt); else targetNodes = $(iframeSelector).contents() .find(selectorTxt); if (targetNodes && targetNodes.length > 0) { btargetsFound = true; /*--- Found target node(s). Go through each and act if they are new. */ targetNodes.each(function () { var jThis = $(this); var alreadyFound = jThis.data('alreadyFound') || false; if (!alreadyFound) { //--- Call the payload function. var cancelFound = actionFunction(jThis); if (cancelFound) btargetsFound = false; else jThis.data('alreadyFound', true); } }); } else { btargetsFound = false; } //--- Get the timer-control variable for this selector. var controlObj = waitForKeyElements.controlObj || {}; var controlKey = selectorTxt.replace(/[^\w]/g, "_"); var timeControl = controlObj[controlKey]; //--- Now set or clear the timer as appropriate. if (btargetsFound && bWaitOnce && timeControl) { //--- The only condition where we need to clear the timer. clearInterval(timeControl); delete controlObj[controlKey]; } else { //--- Set a timer, if needed. if (!timeControl) { timeControl = setInterval(function () { waitForKeyElements(selectorTxt, actionFunction, bWaitOnce, iframeSelector ); }, 300 ); controlObj[controlKey] = timeControl; } } waitForKeyElements.controlObj = controlObj; } (function () { 'use strict'; //版本信息 const TIPS = { CurrentVersion: "143.2022.0616.1", LastUpdateDate: "2022.06.16", VersionTips: "115转存助手ui优化版 v3.6", UpdateUrl: "https://github.com/Nerver4Ever/SevenSha1UIAdvancedHelper", Sha1FileInputDetails: "", }; const WORKSETTINGS = { WorkingItemsNumber: 3, //同时执行任务数 SleepLittleTime: 1000, //短暂休眠,毫秒,暂时在转存中使用 SleepMoreTime: 1000, //长时休眠,毫秒,暂时在提取中使用 SleepMuchMoreTime: 8000, //超长休眠,暂时未使用 ANumber: 27, //随机数,暂时未使用 }; GM_addStyle(` @keyframes hue { from { filter: hue-rotate(0); } to { filter: hue-rotate(360deg); } } .rainbow-text { display: inline-block; color: red; animation: hue 6s linear infinite; background-image: linear-gradient(to right bottom, rgb(255,0,0), rgb(255,255,0),rgb(255,0,255)); -webkit-background-clip: text; } .my115Info{ color:red } .btnInGrid{ height:20px; width:20px; margin-left:-22px; margin-top:36px; border:0px; border-color:transparent; background-color:transparent; } .btnInGrid i{ margin:3px -3px } li:hover .btnInGrid{ background-color:#2777F8 !important } /* Style The Dropdown Button */ .my115Dropbtn { background-color: #2777F8; color: white; font-size: 16px; border: none; cursor: pointer; } /* The container
[${TIPS.CurrentVersion}]: 操作时,确保本页面置顶,防止脚本休眠!!
无115会员,提取速度受限,转存文件大小不超过5GB!!
' + message.msg + '
<\p><\div><\li>
${message.msg.onlineFile.name}
`, allowOutsideClick: false, allowEscapeKey: false, confirmButtonText: `完成`, showCancelButton: false, willOpen: function () { Swal.showLoading(Swal.getConfirmButton()); downloadAFile(message.msg.onlineFile, text => { $(Swal.getHtmlContainer()).find("#processInSwal").html(text); }).then(r => { if (r.state) { console.log(r.text) message.msg.config.text = r.text; UploadFilesBySha1Links(message.msg.config); } }); } }) } } else if (message.messageType == MessageType.MSGERROR) { Swal.fire({ icon: 'error', text: message.msg, }) } else if (message.messageType == MessageType.JSINFO) { Swal.fire({ icon: 'info', text: '脚本与环境信息:(如果报bug,请附带上如下信息)\r\n' + message.msg, }) } }) }); } function delay(ms) { if (ms == 0) { ms = 1000 * (Math.floor(Math.random() * (11 - 4)) + 4); } return new Promise(resolve => setTimeout(resolve, ms)) } //#region 115 api //get UploadInfo //return {state:false,user_id:0,userkey:'0',error:''} async function getUploadInfo() { const r = await $.ajax({ url: 'https://proapi.115.com/app/uploadinfo', dataType: 'json', xhrFields: { withCredentials: true } }); return r; } //add a folder //return {state: false, error: "该目录名称已存在。", errno: 20004, errtype: "war"} //return {state: true, error: "", errno: "", aid: 1, cid: "2020455078010511975", …} async function addFolder(pid, folderName) { const postData = PostData({ pid: pid, cname: encodeURIComponent(folderName) }); const r = await $.ajax({ type: 'POST', url: 'https://webapi.115.com/files/add', headers: { 'Content-Type': 'application/x-www-form-urlencoded', //'Origin': 'https://115.com' }, xhrFields: { withCredentials: true }, dataType: 'json', data: postData }); return r; } //return {data: Array(30), count: 53, data_source: "DB", sys_count: 0, offset: 0, page_size:115, …} //return Array type: // [folder]:{cid: "", aid: "1", pid: "", n: "", m: 0, …} // [file]: {fid: "", uid: 1447812, aid: 1, cid: "", n: "",pc:"",sha:"",s:0,t:"" …} async function getDirectChildItemsByOffset(cid, offset) { var tUrl = 'https://webapi.115.com/files?aid=1&cid=' + cid + '&o=file_name&asc=1&offset=' + offset + '&show_dir=1&limit=1150&code=&scid=&snap=0&natsort=1&record_open_time=1&source=&format=json&fc_mix=&type=&star=&is_share=&suffix=&custom_order='; // var tUrl = "https://aps.115.com/natsort/files.php?aid=1&cid=" + cid + "&o=file_name&asc=1&offset=" + offset + "&show_dir=1&limit=1150&code=&scid=&snap=0&natsort=1&record_open_time=1&source=&format=json&fc_mix=0&type=&star=&is_share=&suffix=&custom_order="; const result = await $.ajax({ type: 'GET', url: tUrl, dataType: "json", xhrFields: { withCredentials: true } }); return result; } //直接子项目少于1200 async function getDirectChildItemsByOffsetlt1200(cid, offset) { //var tUrl = 'https://webapi.115.com/files?aid=1&cid='+cid+'&o=file_name&asc=1&offset='+offset+'&show_dir=1&limit=1150&code=&scid=&snap=0&natsort=1&record_open_time=1&source=&format=json&fc_mix=&type=&star=&is_share=&suffix=&custom_order='; var tUrl = "https://aps.115.com/natsort/files.php?aid=1&cid=" + cid + "&o=file_name&asc=1&offset=" + offset + "&show_dir=1&limit=1150&code=&scid=&snap=0&natsort=1&record_open_time=1&source=&format=json&fc_mix=0&type=&star=&is_share=&suffix=&custom_order="; const result = await $.ajax({ type: 'GET', url: tUrl, dataType: "json", xhrFields: { withCredentials: true } }); return result; } //return AllDirect items :{id:"",parentID:cid,isFolder:false,name:"",size:0,pc:"",sha:"",paths[] }; async function getAllDirectItems(cid, folderProcessCallback) { var items = new Array(); var index = 0; var flag = true; var pageIndex = 1; var first = true; var isLT1200 = false; while (flag) { if (getTaskCancelFlag()) break; folderProcessCallback(pageIndex); var result = null; //1200数量,不同的api;这么写减少发包 if (first) { result = await getDirectChildItemsByOffset(cid, index); console.log(`first >1200 :${result.state},${result.count}`); if (!result.state) { result = await getDirectChildItemsByOffsetlt1200(cid, index); console.log(`first <1200 :${result.state},${result.count}`); isLT1200 = true; } first = false; } else { if (isLT1200) result = await getDirectChildItemsByOffsetlt1200(cid, index); else result = await getDirectChildItemsByOffset(cid, index); } var totalCount = parseInt(result.count); if (totalCount >= 1) { result.data.forEach(function (item) { var pItem = { id: "", parentID: cid, isFolder: false, name: "", size: "", pickCode: "", sha1: "", paths: new Array(), preid: "", needToRemoved: false }; if (item.fid) //文件 fid,cid { pItem.isFolder = false; pItem.id = item.fid; pItem.name = item.n; pItem.pickCode = item.pc; pItem.sha1 = item.sha; pItem.size = item.s; } else //目录 cid,pid { pItem.isFolder = true; pItem.id = item.cid; pItem.name = item.n; pItem.pickCode = item.pc; } var itemIndex = items.findIndex(q => q.name == pItem.name && q.pickCode == pItem.pickCode && q.sha1 == pItem.sha1 && (_.isEqual(q.paths, pItem.paths))); if (itemIndex == -1) items.push(pItem); else { //可能存在同一个目录下,两个文件一模一样, //相同文件处理:不然循环条件退不出 //fix:pickcode不一样,先保存着吧 pItem.needToRemoved = true; items.push(pItem) } }) } console.log("_______________totalCount " + totalCount); console.log(items.length) //当获取到比pagesize小时,获取结束,1200时有个坑。。。 if (totalCount <= items.length) { break; } else { await delay(500); index = items.length; pageIndex = pageIndex + 1; } } console.log("cid: {0}, count: {1}".format(cid, items.length)); var noNullItems = items.filter(q => !q.needToRemoved); console.log("cid: {0}, 除去完全重复count: {1}".format(cid, noNullItems.length)); return noNullItems; } //return {file_name:"",pick_code:"",sha1:"",count:"",size:"",folder_count:"",paths:[]} //return paths:[]层级目录 async function getFolderInfo(cid) { var pUrl = "https://webapi.115.com/category/get?aid=1&cid=" + cid; const result = await $.ajax({ type: 'GET', url: pUrl, dataType: "json", xhrFields: { withCredentials: true } }); console.log(result); var pItem = { fileCount: parseInt(result.count), folderCount: parseInt(result.folder_count), id: cid, parentID: "", isFolder: true, name: result.file_name, size: result.size, pickCode: result.pick_code, sha1: "", paths: result.paths, preid: "" }; return pItem; } // get fileArray:{id:"",parentID:cid,isFolder:false,name:"",size:0,pc:"",sha:"",paths[] }; async function getAllFiles(cid, fileArray, topCid, folderProcessCallback) { var thisFolder = await getFolderInfo(cid); folderProcessCallback(thisFolder.name, 0); //空目录,跳过遍历 if (getTaskCancelFlag()) return; if (thisFolder.fileCount == 0) return; folderProcessCallback(thisFolder.name) var directItems = await getAllDirectItems(thisFolder.id, pageIndex => { folderProcessCallback(thisFolder.name, pageIndex); }); //空目录,跳过遍历 if (directItems.length == 0) return; var files = directItems.filter(t => !t.isFolder); files.forEach(f => { var index = thisFolder.paths.findIndex(q => q.file_id.toString() == topCid); var paths = new Array(); if (index != -1) { paths = thisFolder.paths.slice(index).map(q => q.file_name); } paths.push(thisFolder.name); f.paths = paths.slice(1); fileArray.push(f); }); var folders = directItems.filter(t => t.isFolder); for (var folder of folders) { if (getTaskCancelFlag()) break; await getAllFiles(folder.id, fileArray, topCid, folderProcessCallback); await delay(200); } } //批量重命名 fileArray [{id:id,name:ddd}] //{"state":true,"error":"","errno":0,"data":{"2187365717527997108":"14214.mp4"}} async function renameFiles(fileArray) { console.log("renameFiles fileArray"); console.log(fileArray); let datas = fileArray.map((value, index, array) => { let dataKey = `files_new_name[${value.id}]`; let dataValue = value.name; return `${encodeURIComponent(dataKey)}=${encodeURIComponent(dataValue)}`; }).join("&"); let renameUrl = "https://webapi.115.com/files/batch_rename"; const result = await $.ajax({ type: 'POST', url: renameUrl, headers: { 'Content-Type': 'application/x-www-form-urlencoded', //'Origin': 'https://115.com' }, dataType: "json", xhrFields: { withCredentials: true }, data: datas }); return result; } async function getUploadHistory(offset, limit) { let historyFiles = []; let pUrl = `https://webapi.115.com/history/list?type=4&offset=${offset}&limit=${limit}`; const result = await $.ajax({ type: 'GET', url: pUrl, dataType: "json", xhrFields: { withCredentials: true } }); let files = result.data.list; for (const aFile of files) { historyFiles.push({ sha1: aFile.sha1, id: aFile.file_id, createTime: aFile.create_time, pickCode: aFile.pick_code, //可能不一样 name: aFile.file_name, parentID: aFile.parent_id }); } return historyFiles; } async function getLastUploadFiles(count, delayTime = 200, processCallback) { let files = []; let offset = 0; let limit = 115; let leftCount = count; while (leftCount >= limit) { processCallback(`正在获取第${offset+1}-${offset+limit+1}个文件数据...`) let result = await getUploadHistory(offset, limit); result.forEach(f => files.push(f)); offset = offset + limit; leftCount = leftCount - limit; await delay(delayTime); } if (leftCount > 0) { processCallback(`正在获取第${offset+1}-${offset+leftCount+1}个文件数据...`) let result = await getUploadHistory(offset, leftCount); result.forEach(f => files.push(f)); } return files; } //获取生成sha1需要preid //return: {state:,error:,fileItem:} function getFileItemPreid(fileItem) { console.log('getFileItemPreid') console.log(fileItem); const f = fileItem; let fileSize = parseInt(fileItem.size); if (fileSize == 0) { return new Promise((resolve, reject) => { const errorMsg = "{0} 文件大小为0,已经跳过!".format(f.filename); console.error("errorMsg"); resolve({ state: false, error: "文件大小为0,已经跳过!", fileItem: fileItem }); }); } const r = new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "POST", url: 'https://proapi.115.com/app/chrome/downurl', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 115Browser/23.9.3.6' }, responseType: 'json', data: PostData({ data: m115_encode('{"pickcode":"' + fileItem.pickCode + '"}') }), onload: function (r) { if (r.status == 200) { var download_info = r.response; if (download_info.state && download_info.data) { try { var json = m115_decode(download_info.data); //console.log(json) var url = JSON.parse(json)[fileItem.id]['url']['url']; //todo:不能下载的文件处理 if (!url.startsWith("http://cdnfhnfdfs.115.com") && url.startsWith('http:///')) { console.error(`error url:${url}`); url = url.replace("http:///", "http://cdnfhnfdfs.115.com/") } console.log(url); var resp = r.responseHeaders var setCookie = DeleteCookie(resp) var fileCookie = null; if (setCookie) { fileCookie = setCookie; } GM_xmlhttpRequest({ method: "GET", url: url, timeout: 12000, headers: { "Range": "bytes=0-131072", "Cookie": fileCookie, 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 115Browser/23.9.3.6' }, responseType: 'arraybuffer', onload: function (response) { if (response.status === 206) { var pre_buff = response.response; var data = new Uint8Array(pre_buff); var sha1 = new jsSHA('SHA-1', 'ARRAYBUFFER'); sha1.update(data.slice(0, 128 * 1024)); var preid = sha1.getHash('HEX', { outputUpper: true }); fileItem.preid = preid; resolve({ state: true, error: "", fileItem: fileItem }); } else if (response.status === 403) { console.error("Forbidden, 已经用40个0代替"); fileItem.preid = "0000000000000000000000000000000000000000"; resolve({ state: true, error: "", fileItem: fileItem }); } else { //fix v3.3: 修复404文件无法下载导致卡ui问题 @指环王 console.error("可能文件无法下载或者网络问题"); console.log(response); resolve({ state: false, error: "下载出错,可能文件无法下载或者网络问题", fileItem: fileItem }); } }, ontimeout: function (res) { console.error("下载超时,可能文件无法下载或者网络问题"); console.log(res); resolve({ state: false, error: "下载超时,可能文件无法下载或者网络问题", fileItem: fileItem }); } }); } catch (error) { console.error(error); resolve({ state: false, error: "在提取中发生错误...", fileItem: fileItem }); } } else { console.log(download_info); resolve({ state: false, error: download_info.msg, fileItem: fileItem }); } } else { console.error(response.response); resolve({ state: false, error: "在提取中发生错误...", fileItem: fileItem }); } } }); }); return r; } var utf8ArrayToStr = (function () { var charCache = new Array(128); // Preallocate the cache for the common single byte chars var charFromCodePt = String.fromCodePoint || String.fromCharCode; var result = []; return function (array) { var codePt, byte1; var buffLen = array.length; result.length = 0; for (var i = 0; i < buffLen;) { byte1 = array[i++]; if (byte1 <= 0x7F) { codePt = byte1; } else if (byte1 <= 0xDF) { codePt = ((byte1 & 0x1F) << 6) | (array[i++] & 0x3F); } else if (byte1 <= 0xEF) { codePt = ((byte1 & 0x0F) << 12) | ((array[i++] & 0x3F) << 6) | (array[i++] & 0x3F); } else if (String.fromCodePoint) { codePt = ((byte1 & 0x07) << 18) | ((array[i++] & 0x3F) << 12) | ((array[i++] & 0x3F) << 6) | (array[i++] & 0x3F); } else { codePt = 63; // Cannot convert four byte code points, so use "?" instead i += 3; } result.push(charCache[codePt] || (charCache[codePt] = charFromCodePt(codePt))); } return result.join(''); }; })(); function downloadAFile(fileItem, progressCallback = function (text) {}) { console.log("downloadAFile") console.log(fileItem) const r = new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "POST", url: 'https://proapi.115.com/app/chrome/downurl', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 115Browser/23.9.3.6' }, responseType: 'json', data: PostData({ data: m115_encode('{"pickcode":"' + fileItem.pickCode + '"}') }), onload: function (r) { if (r.status == 200) { var download_info = r.response; if (download_info.state && download_info.data) { try { var json = m115_decode(download_info.data); //console.log(json) var url = JSON.parse(json)[fileItem.id]['url']['url']; //todo:不能下载的文件处理 if (!url.startsWith("http://cdnfhnfdfs.115.com") && url.startsWith('http:///')) { console.error(`error url:${url}`); url = url.replace("http:///", "http://cdnfhnfdfs.115.com/") } console.log(url); var resp = r.responseHeaders var setCookie = DeleteCookie(resp) var fileCookie = null; if (setCookie) { fileCookie = setCookie; } GM_xmlhttpRequest({ method: "GET", url: url, headers: { "Content-Type": "application/octet-stream", "Cookie": fileCookie, 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 115Browser/23.9.3.6' }, responseType: 'arraybuffer', onload: function (response) { if (response.status === 200) { let str = utf8ArrayToStr(new Uint8Array(response.response)); resolve({ state: true, error: "", text: str }); } else { console.error(response); resolve({ state: false, error: `response.status:${response.status}`, text: "" }); } }, onprogress: function (event) { let textMsg = `${event.loaded} of ${event.total} bytes, ${(event.loaded/event.total).toFixed(4)*100}%`; console.log(textMsg); progressCallback(textMsg) } }); } catch (error) { console.error(error); resolve({ state: false, error: `${error}`, text: "" }); } } else { console.log(download_info); resolve({ state: false, error: download_info.msg, text: "" }); } } else { console.log("下载第一阶段") console.error(response.response); resolve({ state: false, error: `在下载中发生错误...${response.status}`, text: "" }); } } }); }); return r; } //windows平台上限制的字符:/\|":*?<> 其他平台比windows宽泛一些 function repalceValidatedName(name) { return name.replace(//g, ']') .replace(/\|/g, '_') .replace(/:/g, '_') .replace(/\//g, '_') .replace(/\\/g, '_') .replace(/\*/g, '_') .replace(/"/g, '\'') .replace(/\?/g, '_'); } //格式化sha1 链接 //return type: {state:succeed,msg:""} // false:msg->出错信息 //true: msg->sha1链接 function convertToSha1Link(fileItem, isSimpleFormat) { var succeed = false; var msg = "格式生成失败!"; if (fileItem.name && fileItem.size && fileItem.sha1 && fileItem.preid) { var sha1Link = "115://" + repalceValidatedName(fileItem.name) + "|" + fileItem.size + "|" + fileItem.sha1 + "|" + fileItem.preid; if (!isSimpleFormat) { if (fileItem.paths.length > 0) { //console.log(fileItem.paths); //fix: v3.3 目录中的‘|’或者‘#’替换为‘/’,防止脚本导出再导入时破坏目录结构 //fix: v3.4 windows平台上不能限制的字符:/\|":*?<> 替换;去掉了老版本的#分隔符 var paths = fileItem.paths.map(t => repalceValidatedName(t)).join('|'); msg = sha1Link + '|' + paths; } else { msg = sha1Link; } } else { msg = sha1Link; } succeed = true; } if(!succeed){ console.error(fileItem); } return { state: succeed, msg: msg }; } // 从sha1link 转换为 FileItem //return type:{state:succeed,fileItem:{}} //true: fileItem, false:null function convertFromSha1Link(sha1Link) { var succeed = false; var item = {}; if (sha1Link) { if (sha1Link.startsWith("115://")) { sha1Link = sha1Link.substring(6); } //v3.4 add 转存时,文件名、文件夹名替换非法字符 var infos = sha1Link.split('|'); if (infos.length >= 4) { item.id = ""; item.pickCode = ""; item.name = repalceValidatedName(infos[0]); item.size = infos[1]; item.sha1 = infos[2]; item.preid = infos[3]; item.parentID = ""; item.paths = new Array(); if (infos.length > 4) { //fix: v3.4 移除了此兼容,因为开放了#作为目录名 // if (infos.length == 5 && infos[4].includes('#')) { //兼容 #字符分割 // item.paths = infos[4].split('#'); //} else { item.paths = infos.slice(4).map(t => repalceValidatedName(t)); //} } item.extension = ""; item.formatedName = ""; item.formatedExtension="" succeed = true; } } return { state: succeed, fileItem: item }; } function createUploadFile(urlData, postData) { console.log("createUploadFile"); console.log(urlData) return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'POST', url: 'http://uplb.115.com/3.0/initupload.php?' + urlData, data: postData, responseType: 'json', headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', //'Origin': 'https://115.com' }, onload: function (response) { let data = { state: false, error: "", pickCode: "" }; if (response.status === 200 && response.response.status === 2) { data.state = true; data.pickCode = response.response.pickcode; } else { console.error(response); let error = "或许sha1链接不匹配(?)"; if (response.status === 405) { error = "频繁请求,被115限制 ([!]立即停止,尝试停止操作半小时或者重新登录):" + response.statusText; } else if (response.response && response.response.message) error = response.response.message; else if (response.response && response.response.statusmsg) error = "可能参数不正确(?):" + response.response.statusmsg; data.error = error; } resolve(data); } }) }); } //return:{state:false,error:"",fileItem:}; function uploadFile(targetFolder, fileItem, uploadInfo) { let fCid = `U_1_${targetFolder}`; let appVersion = "25.2.0"; let urlData = UrlData({ isp: 0, appid: 0, appversion: appVersion, format: 'json', sig: GetSig(uploadInfo.user_id, fileItem.sha1, fCid, uploadInfo.userkey) }); console.log("postData") console.log("fileItem.formatedName") let postData = PostData({ preid: fileItem.preid, fileid: fileItem.sha1, quickid: fileItem.sha1, app_ver: appVersion, filename: encodeURIComponent(fileItem.formatedName), filesize: fileItem.size, exif: '', target: fCid, userid: uploadInfo.user_id }); console.log(postData) const r = createUploadFile(urlData, postData); const x = r.then(t => { return new Promise((resole, reject) => { fileItem.state = t.state; fileItem.pickCode = t.pickCode; resole({ state: t.state, error: t.error, fileItem: fileItem }); }) }); return x; } function setListView() { GM_xmlhttpRequest({ method: "POST", url: 'https://115.com/?ct=user_setting&ac=set', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, data: PostData({ setting: '{"view_file":"list"}' }), responseType: 'json', onload: function (response) { if (response.status === 200) {} } }); } //#endregion async function updateParentID(cid, cname, thisLevel, maxLevel, items, sleepTime, createFolderCallback) { if (thisLevel == maxLevel) return; let files = new Array(); if (thisLevel == 0) { files = items; } else { files = items.filter(f => f.paths[thisLevel - 1] == cname); } let childFiles = files.filter(q => q.paths.length == thisLevel); let childFolderNames = files.map(q => q.paths[thisLevel]).filter(q => q).filter((x, i, a) => a.indexOf(x) == i) console.log(`childFiles :${childFiles.length}`) //upload file: for (let file of childFiles) { file.parentID = cid; //console.log(file.parentID); } //create folder: for (let folderName of childFolderNames) { let r = await createRootFolder(cid, folderName, 10, sleepTime, callbackMsg => { createFolderCallback && createFolderCallback({ state: true, folderName: folderName, error: callbackMsg }); }) //fix: v3.4 有同名文件夹,处理跟根目录相同处理。如果超过重试次数的逻辑未处理 //let r = await addFolder(cid, folderName); console.log(r); if (r.state) { await updateParentID(r.cid, folderName, thisLevel + 1, maxLevel, files, createFolderCallback); } else { //ui 目录创建失败 //todo: !!!尚未解决这个 console.error(`updateParentID 如果出现这个,说明重复名字的文件夹也太太太多了`); } await delay(sleepTime); } } function internelFormat(folder, files, folderParents) { var paths = folderParents.slice(0); paths.push(folder.dir_name); for (var file of folder.files) { var link = file + '|' + paths.slice(1).join('|'); files.push(link); } for (var childFolder of folder.dirs) { internelFormat(childFolder, files, paths) } } //{state:true,error:"",text:""} function formatJsonToCommon(text) { try { var root = JSON.parse(text); console.log(root); var files = new Array(); var paths = new Array(); internelFormat(root, files, paths); return { state: true, error: "", text: files.join('\r\n'), rootFolder: root.dir_name }; } catch (error) { return { state: false, error: error, text: "" }; } } function reverseString(str) { return str.split("").reverse().join(""); } //解析inline text sha1 links,并根据配置设置分隔符;返回FileArray function parseSha1LinksToFileArray(text, nameSeparator, errorCallback) { let textLines = text.split(/\r?\n/); let files = new Array(); for (let line of textLines) { let fLine = line.trim(); if (!fLine) continue; let r = convertFromSha1Link(fLine); if (r.state) { //let nameStrings = r.fileItem.name.split("."); //let extension = nameStrings.pop(); //r.fileItem.extension = extension; //let formatedExtension=reverseString(extension); //根据配置重新设置文件名 if (nameSeparator) { //使用emoutils.js库来分割,带有emoji的文件名 //let fileName = emojiUtils.toArray(nameStrings.join('.')).map(c => c + nameSeparator).join("").slice(0, -1); //r.fileItem.formatedName = fileName + "." + formatedExtension; r.fileItem.formatedName=emojiUtils.toArray(r.fileItem.name).map(c => c + nameSeparator).join("").slice(0, -1); } else { r.fileItem.formatedName = r.fileItem.name; } files.push(r.fileItem); } else { errorCallback && errorCallback(`${fLine} 格式错误?`); } } return files; } //fix: v3.4 时间日期中含有"/",":"导致目录或者文件下载失败 function getCurrentTimeString() { let time = new Date(); let timeString = `${time.toLocaleString()} (${time.getMilliseconds()})`; return timeString.replace(/\//g, ".").replace(/:/g, ".") } //在targetCid下创建目录,成功则返回新目录cid,否则返回原cid;返回’-1‘,target已经被移除或者删除 async function createRootFolder(targetCid, folderName, retryTimes, sleepTime, processCallback) { let cid = targetCid; let newFolderName = folderName; if (folderName == "") { newFolderName = `auto_create@${getCurrentTimeString()}`; } for (let i = 0; i < retryTimes; i++) { if (i != 0) { newFolderName = `${folderName==""?"auto_create":folderName}@${getCurrentTimeString()}`; } processCallback && processCallback(`正在自动创建目录${newFolderName}...`); let tr = await addFolder(targetCid, newFolderName); if (tr.state) { cid = tr.cid; processCallback && processCallback(`自动创建目录${newFolderName}成功!`); break; } else { processCallback && processCallback(`自动创建目录${newFolderName}失败!原因:${tr.error},将自动尝试新的名字...`); if (tr.error.includes('云端目录不存在') || tr.error.includes('文件不存在或已删除')) { cid = '-1'; //父目录不存在时的提示 break; } await delay(sleepTime); } } let state = cid != '-1'; let error = state ? "" : "云端目录之前已经删除,请重新选择保存位置!" //todo:父目录不存在时的提示 return { cid: cid, folderName: newFolderName, state: state, error: error }; } function uploadFileWithTimeOut(timeOut, fileParentID, file, uploadInfo) { console.log('uploadFileWithTimeOut') let to = delay(timeOut).then(t => { return { state: false, error: `等待上传结果超时,此乃警告!成功与否,看最后统计结果!`, fileItem: file } }); let up = uploadFile(fileParentID, file, uploadInfo); return Promise.race([to, up]); } async function processUpload(allFiles, workingNumber, sleepTime, resultCallback) { let fileArray = allFiles.filter(q => !q.state); let index = 1; let fileLength = allFiles.length; let completed = fileLength - fileArray.length; let promisArray = new Array(); let uploadInfo = await getUploadInfo(); console.log("uploadInfo") let msg; for (let file of fileArray) { if (getTaskCancelFlag()) { console.log("转存取消"); //postSha1Messgae(createMessage(MessageType.PROCESSING, "已取消,正在等待进行中的任务结束...")); break; } console.log(file); //let r = uploadFile(file.parentID, file, uploadInfo).then(t => { let r = uploadFileWithTimeOut(8000, file.parentID, file, uploadInfo).then(t => { completed = completed + 1; if (t.state) { msg = `转存也在此处选择位置。因115页面结构以及接口调整,暂时下线【默认保存至当前位置】