Greasy Fork is available in English.

通用视频搜索

视频匹配搜索:爱优腾、B站、芒果TV、搜狐、乐视、1905、PPTV、西瓜

// ==UserScript==
// @name         通用视频搜索
// @namespace    http://tampermonkey.net/
// @version      1.5.1
// @description  视频匹配搜索:爱优腾、B站、芒果TV、搜狐、乐视、1905、PPTV、西瓜
// @author       tutu辣么可爱
// @include      *://*.iqiyi.com/*
// @include      *://*.iq.com/*
// @include      *://*.youku.com/*
// @include      *://*v.qq.com/*
// @include      *://*.bilibili.com/*
// @include      *://*.acfun.cn/*
// @include      *://*.mgtv.com/*
// @include      *://*tv.sohu.com/*
// @include      *://*.le.com/*
// @include      *://*.1905.com/*
// @include      *://*.pptv.com/*
// @include      *://*.ixigua.com/*
// @license      MIT
// ==/UserScript==

(function() {
	/*
	1.manipulate用于点击/长按事件的设置
	2.set用于创建设置页、获取/修改/保存/应用设置数据。data保存设置数据,并通过localstorage的vipParseSettingsData长期保存,保存形式为JSON字符串。backupData保存初始设置数据用于恢复默认。set->createBody->setMenu保存设置页选项数据
	3.check用于检查浏览器UA,或通过list检查是否是可生效的网站。list保存脚本可生效的网站ID、名称、域名
	4.vip用于创建新播放器,指向解析线路。map保存不同网站不同ua情况下的播放器ID或类名
	5.btn用于创建主题按钮。interface保存解析接口,backupInterfa保存初始解析接口数据用于恢复默认
	*/
	var manipulate = {
		data: {
			"flag": false,
			"wait": false,
			"timeStart": 0,
			"timeEnd": 0
		},
		get: function(key) {
			if (key === "time") {
				return new Date().getTime();
			} else {
				return this.data[key];
			}
		},
		set: function(key, value) {
			this.data[key] = value;
		},
		start: function(clickEvent, longPressEvent) {
			if (!this.get("flag") && !this.get("wait")) {
				this.set("flag", true);
				this.set("wait", true);
				this.set("timeStart", this.get("time"));
				setTimeout(function() {
					manipulate.end(clickEvent, longPressEvent);
					manipulate.set("wait", false);
				}, 310);
			}
		},
		end: function(clickEvent, longPressEvent) {
			if (this.get("flag")) {
				this.set("flag", false);
				this.set("timeEnd", this.get("time"));
				var timeSpan = this.get("timeEnd") - this.get("timeStart");
				if (timeSpan < 300) {
					clickEvent();
				} else {
					longPressEvent();
				}
			}
		},
		on: function(target, clickEvent, longPressEvent) {
			target.addEventListener("mousedown", function() {
				manipulate.start(clickEvent, longPressEvent);
			});
			target.addEventListener("touchstart", function() {
				manipulate.start(clickEvent, longPressEvent);
			});
			target.addEventListener("mouseup", function() {
				manipulate.end(clickEvent, longPressEvent);
			});
			target.addEventListener("touchend", function() {
				manipulate.end(clickEvent, longPressEvent);
			});
		}
	};
	var set = {
		data: {
			"box-top": "100px",
			"box-right": "2px",
			"main-height": "30px",
			"main-width": "50px",
			"main-border-radius": "20px",
			"menu-height": "310px",
			"menu-width": "120px",
			"menu-border-radius": "20px",
			"item-font-size": "18px",
			"item-color": "white",
			"background": "rgb(217, 6, 9)",
			"user-interface": []
		},
		backupData: {},
		init: function() {
			this.backupData = this.data;
			var localData = JSON.parse(localStorage.getItem("vipParseSettingsData"));
			if (localData) {
				var newData = {
					...this.data,
					...localData
				};
				this.data = newData;
			}
			btn.backup();
			btn.init();
			this.store();
		},
		get: function(key) {
			return this.data[key];
		},
		edit: function(key, value) {
			if (key.search("user-interface") !== -1) {
				key = parseInt(key.replace(/[^0-9]/ig, ""));
				this.data["user-interface"][key] = value;
			}
			this.data[key] = value;
		},
		store: function() {
			localStorage.setItem("vipParseSettingsData", JSON.stringify(this.data));
		},
		save: function() {
			var setPage = document.getElementById("vipParseBtnSetting");
			var inputBox = setPage.getElementsByTagName("input");
			for (let i = 0; i < inputBox.length; i++) {
				this.edit(inputBox[i].name, inputBox[i].value);
			}
			btn.init();
		},
		apply: function() {
			var flag = false;
			if (document.getElementById("vipParseBtnMenu").style.display !== "none") {
				flag = true;
			}
			document.getElementById("vipParseBtnBox").remove();
			btn.create();
			if (flag) {
				btn.clickMain();
			}
			document.getElementById("vipParseBtnSetting").style.background = this.get("background");
		},
		revert: function() {
			var setPage = document.getElementById("vipParseBtnSetting-StyleSet");
			if (setPage.style.display === "none") {
				setPage = document.getElementById("vipParseBtnSetting-ParseSet");
			}
			var inputBox = setPage.getElementsByTagName("input");
			var interfaceBackup = btn.get("backup");
			for (let i in inputBox) {
				let key = inputBox[i].name;
				if (key.search("user-interface") !== -1) {
					key = parseInt(key.replace(/[^0-9]/ig, ""));
					inputBox[i].value = interfaceBackup[key];
				} else {
					inputBox[i].value = this.backupData[key];
				}
			}
		},
		switch: function() {
			var setTitle = document.getElementById("vipParseBtnSetting-head").children[0];
			var styleSet = document.getElementById("vipParseBtnSetting-StyleSet");
			var parseSet = document.getElementById("vipParseBtnSetting-ParseSet");
			if (styleSet.style.display === "none") {
				styleSet.style.display = "";
				parseSet.style.display = "none";
				setTitle.innerText = "脚本样式设置";
			} else {
				styleSet.style.display = "none";
				parseSet.style.display = "";
				setTitle.innerText = "脚本解析设置";
			}
		},
		create: function() {
			var setting = document.createElement("div");
			var wid = 400;
			var hei = 260;
			var screenW = document.documentElement.clientWidth;
			var screenH = document.documentElement.clientHeight;
			if ((wid + 10) > screenW) {
				wid = screenW - 10;
			}
			var settingH = (screenH - hei) / 2;
			var settingW = (screenW - wid) / 2;
			setting.style =
				"display:none;width:" + wid + "px;height:" + hei +
				"px;overflow:hidden;position:fixed;top:" + settingH + "px;right:" + settingW +
				"px;border-radius:15px;padding:20px;background:" + this.get("background") +
				";color:white;font-size:12px;overflow-y:auto;z-index: 99999;";
			setting.id = "vipParseBtnSetting";
			setting.appendChild(this.createHead());
			setting.appendChild(this.createBody());
			setting.appendChild(this.createFoot());
			document.body.insertBefore(setting, document.body.firstChild);
		},
		createHead: function() {
			var head = document.createElement("div");
			var title = document.createElement("span");
			var subtitle = document.createElement("span");
			head.id = "vipParseBtnSetting-head";
			head.style = "width:100%;height:30px;text-align:center;margin-bottom:20px;";
			title.style = "font-size:20px;";
			title.innerText = "脚本样式设置";
			subtitle.style = "font-size:12px;";
			subtitle.innerText = "(仅对" + (check.ua() === "pc" ? "桌面端" : "移动端") + check.site("name") + "生效)";
			head.appendChild(title);
			head.appendChild(subtitle);
			return head;
		},
		createBody: function() {
			var body = document.createElement("div");
			body.style = "width:115%;height:calc(100% - 80px);overflow: hidden scroll;";
			body.id = "vipParseBtnSetting-body";
			body.appendChild(this.createStyle());
			body.appendChild(this.createParse());
			return body;
		},
		createFoot: function() {
			var foot = document.createElement("div");
			foot.style = "width:100%;height:30px;position:absolute;bottom:10px;";
			foot.id = "vipParseBtnSetting-foot";
			foot.appendChild(this.createBtn("revert"));
			foot.appendChild(this.createBtn("save"));
			foot.appendChild(this.createBtn("close"));
			foot.appendChild(this.createBtn("switch"));
			return foot;
		},
		createOpt: function(data, titleWidth, inputWidth) {
			var optBox = document.createElement("div");
			var titleBox = document.createElement("span");
			var textBox = document.createElement("input");
			var textBoxMaxWidth = document.documentElement.clientWidth - 20 - titleWidth;
			optBox.style = "height:30px;float:left;"
			titleBox.innerText = data.name;
			titleBox.style = "width:" + titleWidth + "px;float:left;margin-bottom:8px;";
			textBox.name = data.key;
			textBox.style = "width:" + inputWidth +
				"px;max-width:" + textBoxMaxWidth +
				"px;float:left;color:black;margin-bottom:8px;margin-right:10px;";
			if (data.value) {
				textBox.value = data.value;
			} else {
				textBox.value = set.get(data.key);
			}
			optBox.appendChild(titleBox);
			optBox.appendChild(textBox);
			return optBox;
		},
		createStyle: function() {
			var page = document.createElement("div");
			page.style = "width:100%;height:100%;";
			page.id = "vipParseBtnSetting-StyleSet";
			var setMenu = [{
				"name": "按钮距离顶部",
				"key": "box-top"
			}, {
				"name": "按钮距离右侧",
				"key": "box-right"
			}, {
				"name": "按钮高度",
				"key": "main-height"
			}, {
				"name": "按钮宽度",
				"key": "main-width"
			}, {
				"name": "按钮圆角",
				"key": "main-border-radius"
			}, {
				"name": "菜单圆角",
				"key": "menu-border-radius"
			}, {
				"name": "菜单高度",
				"key": "menu-height"
			}, {
				"name": "菜单宽度",
				"key": "menu-width"
			}, {
				"name": "菜单字体大小",
				"key": "item-font-size"
			}, {
				"name": "菜单字体颜色",
				"key": "item-color"
			}, {
				"name": "主题背景色调",
				"key": "background"
			}];
			for (let i in setMenu) {
				page.appendChild(this.createOpt(setMenu[i], 90, 90));
			}
			return page;
		},
		createParse: function() {
			var page = document.createElement("div");
			page.style = "display:none;width:100%;height:100%;";
			page.id = "vipParseBtnSetting-ParseSet";
			for (let i = 0; i < btn.get("length"); i++) {
				var setdata = {
					"name": "播放线路" + ((i + 1) < 10 ? "0" : "") + (i + 1),
					"key": "user-interface-" + i,
					"value": btn.get(i)
				};
				page.appendChild(this.createOpt(setdata, 90, 180));
			}
			return page;
		},
		createBtn: function(name) {
			var btnBox = document.createElement("span");
			var btnObj = document.createElement("div");
			var text, func, id;
			btnObj.style =
				"margin-right:12px;padding:0 10px;cursor:pointer;border:white thin solid;border-radius:10px;float:left";
			switch (name) {
				case "revert":
					text = "重置";
					id = "vipParseBtnSetting-" + name + "Btn";
					func = function() {
						set.revert()
					}
					break;
				case "save":
					text = "保存并应用";
					id = "vipParseBtnSetting-" + name + "Btn";
					func = function() {
						if (confirm("是否确认保存并应用新设置?")) {
							set.save();
							set.store();
							set.apply();
						}
					}
					break;
				case "close":
					text = "关闭设置";
					id = "vipParseBtnSetting-" + name + "Btn";
					func = function() {
						var setting = document.getElementById("vipParseBtnSetting");
						setting.style.display = "none";
					}
					break;
				case "switch":
					text = "切换设置项";
					id = "vipParseBtnSetting-" + name + "Btn";
					func = function() {
						set.switch();
					}
					break;
				default:
					break;
			}
			btnObj.innerText = text;
			btnObj.id = id;
			btnObj.onclick = func;
			btnBox.appendChild(btnObj);
			return btnBox;
		}
	};
	var check = {
		list: {
			0: {
				"id": "iqiyi",
				"name": "爱奇艺",
				"host": "iqiyi.com|iq.com"
			},
			1: {
				"id": "youku",
				"name": "优酷",
				"host": "youku.com"
			},
			2: {
				"id": "tencentvideo",
				"name": "腾讯视频",
				"host": "v.qq.com"
			},
			3: {
				"id": "bilibili",
				"name": "B站",
				"host": "bilibili.com"
			},
			4: {
				"id": "acfun",
				"name": "A站",
				"host": "acfun.cn"
			},
			5: {
				"id": "mgtv",
				"name": "芒果TV",
				"host": "mgtv.com"
			},
			6: {
				"id": "sohu",
				"name": "搜狐视频",
				"host": "tv.sohu.com"
			},
			7: {
				"id": "le",
				"name": "乐视视频",
				"host": "le.com"
			},
			8: {
				"id": "1905",
				"name": "1905电影网",
				"host": "1905.com"
			},
			9: {
				"id": "pptv",
				"name": "PP视频",
				"host": "pptv.com"
			},
			10: {
				"id": "xigua",
				"name": "西瓜视频",
				"host": "ixigua.com"
			}
		},
		ua: function() {
			var uaStr = "pc";
			if (/Android|webOS|HarmonyOS|iPhone|iPod|BlackBerry|mobile/i.test(navigator.userAgent)) {
				uaStr = "mobile";
			}
			return uaStr;
		},
		site: function(type) {
			var site = "unknow";
			var host = location.hostname;
			for (let i in this.list) {
				if (host.match(new RegExp(this.list[i].host))) {
					site = this.list[i][type];
				}
			}
			return site;
		},
		url: function(urlStr) {
			var res = false;
			urlStr = urlStr.replace(/\s+/g, "");
			if (/^((http|https):\/\/)?(([A-Za-z0-9]+-[A-Za-z0-9]+|[A-Za-z0-9]+)\.)+([A-Za-z]+)[/\?\:]?.*$/i
				.test(urlStr)) {
				res = urlStr;
			}
			return res;
		}
	}
	var vip = {
		map: {
			"iqiyi": {
				"pc": "flashbox",
				"mobile": "m-box"
			},
			"youku": {
				"pc": "ykPlayer",
				"mobile": "h5-detail-player"
			},
			"tencentvideo": {
				"pc": "mod_player",
				"mobile": "mod_player"
			},
			"bilibili": {
				"pc": "bilibili-player-video-wrap|player-limit-mask",
				"mobile": "bilibiliPlayer|player-wrapper"
			},
			"acfun": {
				"pc": "ACPlayer",
				"mobile": "#ACPlayer"
			},
			"mgtv": {
				"pc": "mgtv-player-wrap",
				"mobile": "video-area"
			},
			"sohu": {
				"pc": "x-player",
				"mobile": "player-view"
			},
			"le": {
				"pc": "fla_box",
				"mobile": "playB"
			},
			"1905": {
				"pc": "player",
				"mobile": "player"
			},
			"pptv": {
				"pc": "pplive-player",
				"mobile": "pp-details-video"
			},
			"xigua": {
				"pc": "teleplayPage__playerSection__left",
				"mobile": "xigua-detailvideo-video-inner"
			}
		},
		get: function(site, ua) {
			var res = false;
			if (site && ua) {
				var key = this.map[site][ua];
				if (key) {
					key = key.split("|");
					for (let i = 0; i < key.length; i++) {
						res = this.search(key[i]);
						if (res) {
							break;
						}
					}
				}
			}
			return res;
		},
		search: function(key) {
			var player = document.getElementById(key);
			if (player) {
				return player;
			}
			player = document.getElementsByClassName(key)[0];
			if (player) {
				return player;
			}
			player = false;
			return player;
		},
		on: function() {
			var site = check.site("id");
			if (site !== "unknow") {
				set.init();
				set.create();
				btn.create();
			}
		}
	}
	var btn = {
		interface: ["https://z1.m1907.cn/?eps=0&jx=", "https://vip.parwix.com:4433/player/?url=",
			"https://vip.bljiex.com/?v=", "https://lecurl.cn/?url=", "https://jx.m3u8.tv/jiexi/?url=",
			"https://api.leduotv.com/wp-api/ifr.php?isDp=1&vid=", "https://okjx.cc/?url=",
			"https://m2090.com/?url=", "http://51wujin.net/?url=", "https://vip.2ktvb.com/player/?url=",
			"https://660e.com/?url=", "https://api.sigujx.com/?url=", "https://jiexi.janan.net/jiexi/?url=",
			"https://jx.618g.com/?url=", "https://jx.ergan.top/?url=", "https://api.147g.cc/m3u8.php?url=",
			"http://17kyun.com/api.php?url=", "", "", ""
		],
		backupInterface: [],
		get: function(key) {
			var res;
			if (/^\d+$/.test(key)) {
				res = this.interface[key];
			} else if (key === "length") {
				res = this.interface.length;
			} else if (key === "backup") {
				res = this.backupInterface;
			} else {
				res = this.interface;
			}
			return res;
		},
		edit: function(key, value) {
			if (/^\d+$/.test(key)) {
				this.interface[key] = value;
			}
		},
		backup: function() {
			for (let i in this.interface) {
				this.backupInterface[i] = this.interface[i];
			}
		},
		init: function() {
			var interfaceData = set.get("user-interface");
			for (let i in interfaceData) {
				if (this.get(i) === interfaceData[i]) {
					delete interfaceData[i];
				} else if (interfaceData[i]) {
					this.edit(i, interfaceData[i]);
				}
			}
			set.edit("user-interface", interfaceData);
		},
		create: function() {
			var box = document.createElement("div");
			box.style = "cursor: pointer; position: fixed; top: " + set.get("box-top") + ";right: " + set
				.get("box-right") + ";z-index: 99999;";
			box.id = "vipParseBtnBox";
			var main = this.createMain();
			var menu = this.createMenu();
			box.appendChild(main);
			box.appendChild(menu);
			document.body.insertBefore(box, document.body.firstChild);
		},
		createMain: function() {
			var main = document.createElement("div");
			var pH = set.get("main-height");
			pH = (pH.slice(0, pH.search("px")) - 30) / 2;
			var pW = set.get("main-width");
			pW = (pW.slice(0, pW.search("px")) - 50) / 2;
			main.style =
				"height:" + set.get("main-height") + ";width:" + set.get("main-width") + ";background:" +
				set.get("background") + ";border-radius:" + set.get("main-border-radius") +
				";box-sizing:border-box;padding:" + pH + "px " + pW + "px;";
			main.id = "vipParseBtnMain";
			main.innerHTML =
				"<svg height='30' width='50'><polygon points='20,6 35,15 20,24' style='fill:white;'/></svg>";
			manipulate.on(main, btn.clickMain, btn.longPressMain);
			return main;
		},
		createMenu: function() {
			var menu = document.createElement("div");
			var posTop = set.get("main-height");
			posTop = posTop.slice(0, posTop.search("px"));
			posTop = parseFloat(posTop) + 5;
			menu.style =
				"display:none;width:" + set.get("menu-width") + ";height:" + set.get("menu-height") +
				";overflow:hidden;position:absolute;top:" + posTop + "px;right:0px;border-radius:" + set
				.get(
					"menu-border-radius") + ";transition: all 0.2s";
			menu.id = "vipParseBtnMenu";
			var innerMenu = document.createElement("div");
			innerMenu.style = "width:115%;height:100%;overflow-y:scroll;overflow-x:hidden;";
			for (let i = 0; i < this.get("length"); i++) {
				innerMenu.appendChild(this.createItem(i, this.get(i)));
			}
			menu.appendChild(innerMenu);
			return menu;
		},
		createItem: function(i, url) {
			var item = document.createElement("span");
			item.style =
				"color:" + set.get("item-color") + ";font-size:" + set.get("item-font-size") +
				";display:block;padding:10px 6px 6px 10px;width:calc(100% + 30px);background:" + set.get(
					"background") + ";border-bottom:white solid;"
			item.setAttribute("url", url);
			item.innerText = "播放线路 " + ((i + 1) < 10 ? "0" : "") + (i + 1);
			item.onclick = function() {
				url = check.url(url);
				if (url) {
					btn.clickItem(url);
					console.log("当前解析接口:" + url);
				} else {
					var msg = "当前解析接口为空,或接口URL错误";
					console.log(msg);
					alert(msg);
				}
			}
			item.onmouseover = function() {
				this.style.background = set.get("item-color");
				this.style.color = set.get("background");
			}
			item.onmouseleave = function() {
				this.style.background = set.get("background");
				this.style.color = set.get("item-color");
			}
			return item;
		},
		clickMain: function() {
			var menu = document.getElementById("vipParseBtnMenu");
			if (menu.style.display !== "none") {
				menu.style.display = "none";
			} else {
				menu.style.display = "block";
			}
		},
		longPressMain: function() {
			var setting = document.getElementById("vipParseBtnSetting");
			if (setting.style.display !== "none") {
				setting.style.display = "none";
			} else {
				setting.style.display = "block";
			}
		},
		clickItem: function(url) {
			var newPlayer = document.createElement("iframe");
			newPlayer.frameBorder = "no";
			newPlayer.width = "100%";
			newPlayer.height = "100%";
			newPlayer.allowFullscreen = "true";
			newPlayer.allowTransparency = "true";
			newPlayer.scrolling = "no";
			newPlayer.id = "newPlayerParseIframe";
			newPlayer.src = url + location.href;
			var player = vip.get(check.site("id"), check.ua());
			if (player) {
				var originHeight = player.clientHeight;
				player.innerHTML = "";
				player.style.height = originHeight + "px";
				player.appendChild(newPlayer);
			}
		}
	}
	vip.on();
})();