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

解除 BiliBili 网页端视频上传限制

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==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
        }
    })

})();