解除B站视频上传体积限制

解除 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
        }
    })

})();