您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
解除 BiliBili 网页端视频上传限制
// ==UserScript== // @name 解除B站视频上传体积限制 // @namespace mscststs // @version 0.1 // @description 解除 BiliBili 网页端视频上传限制 // @author mscststs // @run-at document-start // @license ISC // @match *://member.bilibili.com/york/videoup* // @icon https://www.google.com/s2/favicons?sz=64&domain=bilibili.com // @grant none // ==/UserScript== (function() { 'use strict'; (function() { function isArray(a) { return "[object Array]" == ({}).toString.call(Object(a)); } function isRexExp(a) { return "[object RegExp]" == ({}).toString.call(Object(a)); } /** @callback overrideRequest * @param {Object} req 原有请求数据 * @property {string} req.url 原有url,字符串,不包含search参数, 支持通配符* * @property {string} req.method 原有method * @property {Object} req.headers 原有headers,键值对象,不会传入null * @property {Object} req.search 原有search参数,键值对象,不会传入null * @property {boolean} req.withCredentials 原有的withCredentials * @property {string|null} req.user 原有的open传入的user,默认为null * @property {string|password} req.password 原有的open传入的password, 默认为null * @property {*} req.data 原有的data * @property {boolean} req.async 原有的async * @property {string} req.mimeType 原有的mimeType, overrideMimeType设置的mimeType * @property {number} req.timeout 原有的超时时间 * @returns {req} 修改后的请求数据,在传入的req上修改后返回 * */ /** * * @callback overrideResponse * @param {Object} res 原有的res * @property {Object} res.headers 原有headers,键值对象,不会传入null * @property {number} res.status * @property {string} res.statusText * @property {string} res.responseType * @property {string} res.responseURL * @property {string} res.response * @property {string} res.responseText * @property {string} res.responseXML * @returns {*} 修改后的res,在传入的res上修改后返回 * */ /** * 劫持XMLHttpRequest * @global * @param {...Object} rule 劫持规则 * @property {string|RegExp|string[]} rule.url 原请求地址,若传入的参数未携带search参数,则匹配时也会忽略search参数;正则匹配时不会忽略search参数; 若域名和location域名相同,会忽略域名 * @property {overrideRequest=} rule.before 发送前的处理 * @property {overrideResponse=} rule.after 对返回值的处理, 默认情况下rule.after仅在readyState===4的时候调用,可通过rule.callAfterEveryState修改 * @property {boolean=} rule.callAfterEveryState 每次readystatechange变更时都调用rule.after去修改res * * @example * hijackAjax({ * url: '/article/list', * before: req => { * req.url = 'recommend/list'; * let data = JSON.parse(req.data); * data.name = "test"; * req.data = JSON.stringify(data) * return req; * }, * after: res => { * let data = JSON.parse(res.responseText); * data.name = 'test'; * res.responseText = JSON.stringify(data); * return res; * } * }) * * @todo 暂时不支持监听open之前的addEventListener * 可以劫持XMLHttpRequest.prototype.addEventListener,在open之前先放入队列,open之后根据url判断是否需要重新劫持 * 需要劫持的listener全部removeEventListener,通过队列的回调去触发 * */ window.hijackAjax = function(rule) { overrideRules.unshift.apply(overrideRules, arguments) } var overrideRules = []; // var originAddEventListener = XMLHttpRequest.prototype.addEventListener; // var originRemoveEventListener = XMLHttpRequest.prototype.removeEventListener; function hasOrigin(url, origin) { url = url.split('#').shift().split('?').shift() return url === origin || url.indexOf(origin + '/') === 0 } function matchRule(originUrl, ruleUrl) { if (isArray(ruleUrl)) { for (var i = 0; i < ruleUrl.length; i++) { if (matchRule(originUrl, ruleUrl)) { return true; } } } else if (isRexExp(ruleUrl)) { return ruleUrl.test(originUrl); } else if (typeof ruleUrl === 'string') { originUrl = ruleUrl.indexOf('?') > -1 ? originUrl : originUrl.split('?').shift(); if (hasOrigin(ruleUrl, location.origin)) { ruleUrl = ruleUrl.substr(location.origin.length) } if (ruleUrl.lastIndexOf('*') === ruleUrl.length - 1) { return originUrl.indexOf(ruleUrl.slice(0, -1)) > -1; } return originUrl === ruleUrl; } return false } function parseUrl(url) { if (hasOrigin(url, location.origin)) { url = url.substr(location.origin.length) } return url.split('?').shift() } function parseSearch(url) { var ret = {}; var querystring = url.split('#').shift().split('?').slice(1).join('?'); var arr = querystring.split('&'); for (var i = 0; i < arr.length; i++) { var pair = arr[i].split('='); ret[decodeURIComponent(pair[0])] = decodeURIComponent(pair.slice(1).join('=')); } return ret; } function stringifySearch(search) { var querystring = ''; if (search) { var arr = []; for (var i in search) { if (search.hasOwnProperty(i)) { arr.push([encodeURIComponent(i), encodeURIComponent(search[i])].join('=')) } } querystring = arr.join('&'); } return querystring } function parseHeaders(str) { var ret = {}; var arr = str.split('\r\n'); for (var i = 0; i < arr.length; i++) { var pair = arr[i].split(': '); ret[decodeURIComponent(pair[0])] = decodeURIComponent(pair.slice(1).join(': ')); } return ret; } function stringifyHeader(headers) { var arr = []; for (var i in headers) { if (headers.hasOwnProperty(i)) { arr.push([i, headers[i]].join(': ')) } } return arr.join('\r\n'); } function hijack(originXhr, rule, openArgs) { var hijacker = originXhr.__hijack_xhr___ = { req: { method: openArgs[0].toUpperCase(), url: parseUrl(openArgs[1]), search: parseSearch(openArgs[1]), async: openArgs[2] === false ? false : true, user: openArgs[3], password: openArgs[4], withCredentials: originXhr.withCredentials, headers: {}, mimeType: null, timeout: originXhr.timeout, data: null }, res: { status: originXhr.status, statusText: originXhr.statusText, headers: {}, responseType: originXhr.responseType, responseURL: originXhr.responseURL, response: originXhr.response, responseText: originXhr.responseText, responseXML: originXhr.responseXML }, xhr: originXhr, descriptors:Object.assign( Object.getOwnPropertyDescriptors(Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf(originXhr)))), Object.getOwnPropertyDescriptors(Object.getPrototypeOf(Object.getPrototypeOf(originXhr))), Object.getOwnPropertyDescriptors(Object.getPrototypeOf(originXhr)) ), event: { addEventListener: originXhr.addEventListener.bind(originXhr), removeEventListener: originXhr.removeEventListener.bind(originXhr), onreadystatechange: originXhr.onreadystatechange, onload: originXhr.onload, onloadend: originXhr.onloadend, listeners: [] } }; Object.defineProperties(originXhr, { withCredentials: { get: function() { return hijacker.req.withCredentials; }, set: function(value) { hijacker.req.withCredentials = value; }, configurable: true, enumerable: false }, timeout: { get: function() { return hijacker.req.timeout; }, set: function(value) { hijacker.req.timeout = value; }, configurable: true, enumerable: false }, setRequestHeader: { value: function(header, value) { hijacker.req.headers[header] = value; }, configurable: true, enumerable: false }, overrideMimeType: { value: function(value) { hijacker.req.mimeType = value }, configurable: true, enumerable: false }, send: { value: function(data) { var req = hijacker.req; req.data = data; if (rule.before) { req = hijacker.req = rule.before(req) || hijacker.req; } if (req.withCredentials) { hijacker.descriptors.withCredentials.set.call(originXhr, req.withCredentials); } if (req.timeout) { hijacker.descriptors.timeout.set.call(originXhr, req.timeout); } if (req.mimeType) { hijacker.descriptors.overrideMimeType.value.call(originXhr, req.mimeType); } originOpen.call(originXhr, req.method, req.url + '?' + stringifySearch(req.search), req.async, req.user, req.password); for (var header in req.headers) { if (req.headers.hasOwnProperty(header)) { hijacker.descriptors.setRequestHeader.value.call(originXhr, header, req.headers[header]); } } // 状态变更时,挂载response数据 async function readystatechangeFn(args) { var res = hijacker.res; if (this.readyState === this.HEADERS_RECEIVED) { var headerString = hijacker.descriptors.getAllResponseHeaders.value.call(this); res.headers = parseHeaders(headerString) } if (this.readyState === this.LOADING || this.readyState === this.DONE) { res.status = hijacker.descriptors.status.get.call(this); res.statusText = hijacker.descriptors.statusText.get.call(this); res.responseType = hijacker.descriptors.responseType.get.call(this); res.response = hijacker.descriptors.response.get.call(this); res.responseText = hijacker.descriptors.responseText.get.call(this); res.responseXML = hijacker.descriptors.responseXML.get.call(this); } if (rule.after && (rule.callAfterEveryState || this.readyState === this.DONE)) { //console.log("After>>>",await rule.after(res)) res = hijacker.res = (await rule.after(res)) || hijacker.res; } if (typeof hijacker.event.onreadystatechange === 'function') { hijacker.event.onreadystatechange.apply(this, args); } triggerListeners.call(this, 'readystatechange', args); if (this.readyState === this.DONE) { if (typeof hijacker.event.onload === 'function') { hijacker.event.onload.apply(this, args); } triggerListeners.call(this, 'load', args); } if (this.readyState === this.DONE || this.readyState === this.UNSET) { if (typeof hijacker.event.onloadend === 'function') { hijacker.event.onloadend.apply(this, args); } triggerListeners.call(this, 'loadend', args); } } // 触发hijacker.event.listeners function triggerListeners(type, args) { var listeners = hijacker.event.listeners; for (var i = 0; i < listeners.length; i++) { var item = listeners[i]; if (item[0] === type) { item[1].apply(this, args); } } if (type === 'load' || type === 'readystatechange' || type === 'loadend') { } else { hijacker.event.removeEventListener.apply(this, arguments); } } hijacker.descriptors.onreadystatechange.set.call(originXhr, readystatechangeFn.bind(this)); hijacker.descriptors.send.value.call(originXhr, req.data); }, configurable: true, enumerable: false }, onreadystatechange: { get: function() { return hijacker.event.onreadystatechange }, set: function(value) { hijacker.event.onreadystatechange = value }, configurable: true, enumerable: false }, onload: { get: function() { return hijacker.event.onload }, set: function(value) { hijacker.event.onload = value }, configurable: true, enumerable: false }, onloadend: { get: function() { return hijacker.event.onloadend }, set: function(value) { hijacker.event.onloadend = value }, configurable: true, enumerable: false }, // open之前的addEventListener是无法劫持的 addEventListener: { value: function(type, listener) { if (type === 'load' || type === 'readystatechange' || type === 'loadend') { hijacker.event.listeners.push([type, listener]) } else { hijacker.event.addEventListener.apply(this, arguments); } }, configurable: true, enumerable: false }, removeEventListener: { value: function(type, listener) { if (type === 'load' || type === 'readystatechange' || type === 'loadend') { for (var i = 0; i < hijacker.event.listeners.length; i++) { var item = hijacker.event.listeners[i]; if (item[0] === type && item[1] === listener) { hijacker.event.listeners.splice(i--, 1); } } } else { hijacker.event.removeEventListener.apply(this, arguments); } }, configurable: true, enumerable: false }, getAllResponseHeaders: { value: function() { return stringifyHeader(hijacker.res.headers); }, configurable: true, enumerable: false }, getResponseHeader: { value: function(header) { return hijacker.res.headers[header]; }, configurable: true, enumerable: false }, status: { get:function() { return hijacker.res.status; }, configurable: true, enumerable: false }, statusText: { get:function() { return hijacker.res.statusText; }, configurable: true, enumerable: false }, responseType: { get:function() { return hijacker.res.responseType; }, configurable: true, enumerable: false }, response: { get:function() { return hijacker.res.response; }, configurable: true, enumerable: false }, responseText: { get:function() { return hijacker.res.responseText; }, configurable: true, enumerable: false }, responseXML: { get:function() { return hijacker.res.responseXML; }, configurable: true, enumerable: false } }); } var originOpen = XMLHttpRequest.prototype.open XMLHttpRequest.prototype.open = function(method, url, async) { var hitRule; for (var i = 0; i < overrideRules.length; i++) { var rule = overrideRules[i]; if (matchRule(url, rule.url)) { hitRule = rule break } } if (hitRule) { var __hijack_xhr___ = this.__hijack_xhr___; hijack(this, hitRule, arguments); } else { originOpen.apply(this, arguments); } } })(); hijackAjax({ url: '/x/vupre/web/archive/pre', before: req => { // console.log(req) return req; }, after: async res => { console.log(res); let rs = JSON.parse(res.response); console.log(rs); Object.keys(rs.data.myinfo.uploadsize).forEach(item=>{ rs.data.myinfo.uploadsize[item] = true; }) //let rm = await rebuild(rs); //console.log(rm); res.response = res.responseText = JSON.stringify(rs) return res } }) })();