Sleazy Fork is available in English.

Pornhub 增强

《也许同类型中最好用?》系列 - Pornhub 视频替换 Artplayer,不显示视频广告,支持进度条热力图,显示视频多分辨率 m3u8/mp4 下载链接,可直接下载 免费/付费/禁下 的视频

// ==UserScript==
// @name         Pornhub 增强
// @icon         data:image/x-icon;base64,AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAEAAAAAAAAAAAAAAAEAAAAAAAAAAAAAABorAAACAwAAwP8AAG+5AAA0VgAAUYkAAInkAAA/aQAAMFEAAJf/AABZlAAAmv8AAGSnAAAaLAAAERwAAKP/AABHZAAABAcAAC0xAACp/wAArP8AALL/AABDcAAAbowAAI/uAAAKEAAAht4AALv/AAAtSwAAiOEAAI3sAACV/wAAmP8AAJv/AAABAQAAnv8AAHrHAABlpwAA//8AAND/AACh/wAAiOIAAC9PAABqsgAApP8AAEVkAABJegAAp/8AADFSAACq/wAAGSoAAAECAACt/wAAM1UAAIjjAAA+aAAAfcgAALb/AAAdMAAAT4MAADpjAABAawAAuf8AAAsQAAC//wAAAAYAAML/AAADBgAACQ4AAMX/AACW/wAAmf8AADdcAACc/wAAn/8AABAcAABRhwAAov8AAIXdAADX/wAAqP8AAKv/AACQ8AAAWI0AAC5NAACx/wAAIEMAALT/AACF3gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISAxISEhISCIiSEgMIkhICkpWLSBIIUg1MEcgEDUiCk4ZCAQUSCQiMTwUNQ0xHxBSTwAvQ0sQSgAzWBw4AFkyUSoABjpOPykANihQCwA3UVEHAjRCRVQyD0wuETs0B1FRNyNVGAkAJh0CGkASAgdRUR4ALCdOAD0BVUFGTQA3UVIbAEQTAAA5AA4/Az4AWTJOUxcFV0klNSs8FDUNMR8QCkoWFhZYNSEVMEcgEDUiCkhIDAwMSEhIIiJISAwiSEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
// @namespace    github.com/hmjz100
// @version      0.1.7
// @description  《也许同类型中最好用?》系列 - Pornhub 视频替换 Artplayer,不显示视频广告,支持进度条热力图,显示视频多分辨率 m3u8/mp4 下载链接,可直接下载 免费/付费/禁下 的视频
// @author       hmjz100,liuwanlin
// @license      MIT
// @match        *://*.pornhub.org/*
// @match        *://*.pornhub.com/*
// @match        *://*.pornhubpremium.com/*
// @match        *://*/*/hvtrs8%2F-cl.ropnju%60.aoo%2Ftigw%5Dvkdgo%2Cpjp%3Dvkeukgy%3F*
// @grant        unsafeWindow
// @grant        GM_addStyle
// @grant        GM_setClipboard
// @grant        GM_openInTab
// @grant        GM_xmlHttpRequest
// @grant        GM.xmlHttpRequest
// @connect      pornhub.org
// @connect      pornhub.com
// @connect      pornhubpremium.com
// @connect      phncdn.com
// @require      https://unpkg.com/jquery@3.6.3/dist/jquery.min.js
// @require      https://unpkg.com/hls.js@1.5.10/dist/hls.min.js
// @require      https://unpkg.com/artplayer@5.1.6/dist/artplayer.legacy.js
// ==/UserScript==

(function () {
	Artplayer.LOG_VERSION = false;
	'use strict';
	let url = new URL(unsafeWindow.location.href)

	// 空白间距
	waitForKeyElements("li.emptyBlockSpace", function (element) {
		if (element.attr('removed')) return;
		element.attr('removed', true);
		element.fadeOut()
	})

	// 顶栏广告
	waitForKeyElements("#headerUpgradePremiumBtn", function (element) {
		if (element.parent().attr('removed')) return;
		element.parent().attr('removed', true);
		element.parent().fadeOut()
	})

	// 年龄认证
	waitForKeyElements('[id^="age-verification"]', function (element) {
		if (element.attr('removed')) return;
		element.attr('removed', true);
		element.fadeOut()
	})

	// 删除页面 iframe 广告
	waitForKeyElements('iframe[allowtransparency][data-embeddedads][data-spot-id], ins[class*="adsby"]', function (element) {
		function findParentWithClass(el) {
			if (el.length > 0) {
				if (el.parent().attr("class")) {
					if (el.parent().attr('removed') || el.parent().parent().parent().attr('removed')) return;
					if (el.parent().parent().parent().prop('tagName') === "LI") {
						el.parent().parent().parent().attr('removed', true);
						console.log(`【Pornhub增强】AD\n`, el.parent().parent().parent().attr("class"), "有class且有li,隐藏!")
						el.parent().parent().parent().fadeOut();
					} else {
						el.parent().attr('removed', true);
						console.log(`【Pornhub增强】AD\n`, el.parent().attr("class"), "有class,隐藏!")
						el.parent().fadeOut();
					}
				} else {
					console.log(`【Pornhub增强】AD\n`, "没有class,继续查找...")
					findParentWithClass(el.parent());
				}
			}
		}
		findParentWithClass(element);
	})

	waitForKeyElements('.bg-spice-badge', function (element) {
		function findParentWithClass(el) {
			if (el.length > 0) {
				if (el.parent().prop('tagName') === "LI") {
					if (el.parent().attr('removed')) return;
					el.parent().attr('removed', true);
					console.log(`【Pornhub增强】AD\n`, el.parent().attr("class"), "有li,隐藏!")
					el.parent().fadeOut();
				} else {
					console.log(`【Pornhub增强】AD\n`, "没有class,继续查找...")
					findParentWithClass(el.parent());
				}
			}
		}
		findParentWithClass(element);
	})

	waitForKeyElements("body div.bottomNav, div#js-abContainterMain", function (element) {
		element.fadeOut()
	})

	if (!url.pathname.includes("view_video.php") && !url.searchParams.get("viewkey") && !url.pathname.includes("vkeukgy")) return;

	// 暂停原始播放器的播放,避免替换后没法暂停
	waitForKeyElements("div.mgp_videoWrapper video", function (element) {
		setInterval(() => {
			element[0].pause()
		}, 1)
	})

	// 等待页面信息出现
	let flashvarsRequireTimer = setInterval(async () => {
		let flashvars = {};
		for (let prop in unsafeWindow) {
			if (prop.startsWith('flashvars_')) {
				clearInterval(flashvarsRequireTimer)
				flashvars = unsafeWindow[prop];
			} else continue
		}
		if (!flashvars.length && !flashvars.mediaDefinitions) return;

		GM_addStyle(`
			a, button, ul li, ui, label, input, select {
				transition: all 0.25s !important;
				-webkit-transition: all 0.25s !important;
			}
			.no-scroll {
				overflow: hidden !important;
			}
			.download-urls {
				margin-top: 10px !important;
			}
			.download-urls ul {
				padding: 0 10px 15px;
				font-weight: bold;
				line-height: 1.5;
			}
			.download-urls ul li {
				display: flex;
				align-items: center;
				height: auto;
			}
			.download-url.label {
				width: 10%;
				text-align: center;
			}
			.download-url {
				width: 5%;
				text-align: center;
			}
			.download-url.download {
				border: none;
				border-radius: 5px;
				font-size: 12px;
				background: #ff9000;
				color: #fff;
			}
			.download-url.download:hover, .download-url.download:disabled {
				background: #ff9000D0;
			}
			.download-url.input {
				width: 80%;
				font-size: 12px;
				padding: 0 5px;
				border: 1px solid #fff;
				margin: 0 5px;
			}
			.art-contextmenu svg, .art-contextmenu img {
				vertical-align: top;
				margin-right: 5px
			}
			header {
				z-index: 999999999999 !important;
			}
		`);

		console.log(`【Pornhub增强】info\n`, flashvars);

		let data = {
			id: flashvars.link_url,
			title: flashvars.video_title,
			url: null,
			hls: [],
			mp4: [],
			title: flashvars.video_title,
			cover: flashvars.image_url,
			hotspots: flashvars.hotspots
		}
		let maxQuality = null;
		let promises = [];

		flashvars.mediaDefinitions.forEach(item => {
			if (item.format === 'hls') {
				let quality = Number(item.quality);
				if (maxQuality === null || quality < maxQuality) {
					maxQuality = quality;
				}
				let video = {
					"url": item.videoUrl,
					"html": quality <= 1080 ? `${quality}P` : quality <= 1440 ? `2K` : quality <= 2160 ? `4K` : `${quality}P`,
					"quality": quality
				};
				data.hls.push(video);
			} else if (item.format === 'mp4') {
				let url = new URL(item.videoUrl)
				if (location.host.includes("pornhub")) url.host = location.host
				let promise = unsafeWindow.fetch(url.href)
					.then(response => response.json())
					.then(res => {
						res.forEach(item => {
							let quality = Number(item.quality);
							if (maxQuality === null || quality < maxQuality) {
								maxQuality = quality;
							}
							let video = {
								"url": item.videoUrl,
								"html": quality <= 1080 ? `${quality}P` : quality <= 1440 ? `2K` : quality <= 2160 ? `4K` : `${quality}P`,
								"quality": quality
							};
							data.mp4.push(video);
						});
					})
					.catch(error => {
						console.error('Fetch 发生错误:', error);
					});

				promises.push(promise); // 将 Promise 存入数组
			}
		});
		data.hls.sort((a, b) => (b.quality || 0) - (a.quality || 0));
		data.hls.forEach(url => {
			if (url.quality === maxQuality) {
				url.default = true;
				data.url = url.url
			}
		});

		waitForKeyElements("div.video-wrapper div#player", function (element) {
			let player = $('<div class="index-module_art-player" style="overflow: hidden; aspect-ratio: 16/9; border-radius: 10px;"></div>')
			element.after(player);
			element.fadeOut();
			play(data.id, data.url, data.hls, data.cover, (art) => {
				art.controls.add({
					name: 'hideList',
					index: 50,
					position: 'right',
					html: '<i class="art-icon"><svg width="18" height="18" viewBox="0 0 1152 1024"><path fill="#fff" d="M1075.2 0H76.8A76.8 76.8 0 0 0 0 76.8v870.4a76.8 76.8 0 0 0 76.8 76.8h998.4a76.8 76.8 0 0 0 76.8-76.8V76.8A76.8 76.8 0 0 0 1075.2 0zM1024 128v768H128V128h896zm-576"></path></svg></i>',
					tooltip: '宽屏模式',
					click: function () {
						$('#hd-rightColVideoPage').fadeToggle();
						if ($('#vpContentContainer').attr("style")) {
							$('#vpContentContainer').removeAttr("style");
						} else {
							$('#vpContentContainer').css({ "grid-template-columns": "auto" });
						}
					}
				})
				art.on('fullscreen', () => {
					$('header').fadeToggle();
					$(art.controls.fullscreenWeb).toggle();
					$(art.controls.hideList).toggle();
				});
				art.on('fullscreenWeb', () => {
					$(art.controls.fullscreen).toggle();
					$(art.controls.hideList).toggle();
				});
				if (data.hotspots) {
					// 平滑函数,使用简单的移动平均
					function smoothData(data, windowSize) {
						var smoothed = [];
						for (let i = 0; i < data.length; i++) {
							let sum = 0;
							let count = 0;
							for (let j = Math.max(0, i - windowSize); j <= Math.min(data.length - 1, i + windowSize); j++) {
								sum += data[j];
								count++;
							}
							smoothed.push(sum / count);
						}
						return smoothed;
					}

					let hotspots = smoothData(data.hotspots.slice(5), 2);

					var svgWidth = 1000;
					var svgHeight = 100;
					var max = Math.max(...hotspots);

					let points = `0,${svgHeight} `; // 从左下角开始
					hotspots.forEach((value, index) => {
						var x = (index / (hotspots.length - 1)) * svgWidth;
						var y = svgHeight - (value / max) * svgHeight;
						points += `${x},${y} `;
					});
					points += `${svgWidth},${svgHeight}`; // 到右下角
					$(art.template.$layer).css({ "z-index": "61" })
					$(art.template.$progress).prepend(`
						<svg viewBox="0 0 1000 100" preserveAspectRatio="none" style="height: 30px; width: 100%; transform: translateY(5.6px); pointer-events: none;">
							<polygon id="heatmap" points="${points}" fill="var(--art-progress-color)" stroke="var(--art-progress-color)" />
						</svg>
					`)
				}
			});
		})
		waitForKeyElements("div.playerWrapper", function (element) {
			let player = $('<div class="index-module_art-player" style="overflow: hidden; aspect-ratio: 16/9; margin-top: 60px;"></div>')
			element.after(player);
			element.fadeOut();
			play(data.id, data.url, data.hls, data.cover, (art) => {
				art.on('fullscreen', () => {
					$('header').fadeToggle();
					$(art.controls.fullscreenWeb).toggle();
					$(art.controls.hideList).toggle();
				});
				art.on('fullscreenWeb', () => {
					$(art.controls.fullscreen).toggle();
					$(art.controls.hideList).toggle();
				});
				if (data.hotspots) {
					// 平滑函数,使用简单的移动平均
					function smoothData(data, windowSize) {
						var smoothed = [];
						for (let i = 0; i < data.length; i++) {
							let sum = 0;
							let count = 0;
							for (let j = Math.max(0, i - windowSize); j <= Math.min(data.length - 1, i + windowSize); j++) {
								sum += data[j];
								count++;
							}
							smoothed.push(sum / count);
						}
						return smoothed;
					}

					let hotspots = smoothData(data.hotspots.slice(5), 2);

					var svgWidth = 1000;
					var svgHeight = 100;
					var max = Math.max(...hotspots);

					let points = `0,${svgHeight} `; // 从左下角开始
					hotspots.forEach((value, index) => {
						var x = (index / (hotspots.length - 1)) * svgWidth;
						var y = svgHeight - (value / max) * svgHeight;
						points += `${x},${y} `;
					});
					points += `${svgWidth},${svgHeight}`; // 到右下角
					$(art.template.$progress).prepend(`
						<svg viewBox="0 0 1000 100" preserveAspectRatio="none" style="height: 30px; width: 100%; transform: translateY(5.1px);">
							<polygon id="heatmap" points="${points}" fill="var(--art-progress-color)" stroke="var(--art-progress-color)" />
						</svg>
					`)
				}
			});
		})

		waitForKeyElements("div.video-wrapper div.video-actions-menu,div.categoryTags div.tooltipWrapper", function (element) {
			let downloadContent = $(`<div class="download-urls"><ul></ul><li><span style="width:100%; text-align: center;">小贴士:MP4 下载总是失败?复制 HLS 链接到猫抓插件(需单独安装)的 M3U8 解析器里再下载吧</span></li></div>`);
			element.after(downloadContent);

			data.hls.forEach(item => {
				let downloadItem = $(`
					<li>
						<span class="download-url label">HLS ${item.html}</span>
						<input class="download-url input" value="${item.url}" />
						<a class="download-url copy" data-href="${item.url}" href="javascript: void(0);" style="width: 10%;">复制</a>
					</li>
				`);
				downloadItem.find('.download-url.copy').on('click', function (element) {
					element.preventDefault();
					element = $(this)
					GM_setClipboard(element.data('href'));
					element.text("成功")
					setTimeout(() => { element.text("复制") }, 1000)
				})
				element.parent().find(".download-urls ul").append(downloadItem);
			});
			Promise.all(promises).then(() => {
				data.mp4.sort((a, b) => (b.quality || 0) - (a.quality || 0));
				data.mp4.forEach(item => {
					let downloadItem
					// 对于原始站点显示下载按钮
					if (location.host.includes("pornhub")) {
						downloadItem = $(`
							<li>
								<span class="download-url label">MP4 ${item.html}</span>
								<input class="download-url input" value="${item.url}" />
								<button class="download-url download" data-href="${item.url}" data-name="${data.title} ${item.html}">下载</button>
								<a class="download-url copy" data-href="${item.url}" href="javascript: void(0);">复制</a>
							</li>
						`)
						downloadItem.find('.download-url.download').on('click', async function (event) {
							if (!$(this).data('href')) return;
							event.preventDefault();

							let element = $(this);
							let index = element.data('index');
							let ins = {};
							let progress = {};
							let totalSize = 0;

							element.prop('disabled', true);

							ins[index] = setInterval(() => {
								let prog = +progress[index] || 0;
								element.text(prog.toFixed(1) + "%");  // 显示下载进度,保留一位小数
							}, 100);

							const httpRequest = typeof GM_xmlhttpRequest !== "undefined" ? GM_xmlhttpRequest : GM.xmlHttpRequest;

							const handleProgress = (event) => {
								if (event.lengthComputable) {
									totalSize = event.total;
									let currentLoaded = event.loaded;
									progress[index] = ((currentLoaded / totalSize) * 100).toFixed(1);  // 计算百分比
								}
							};

							const handleLoad = (response) => {
								clearInterval(ins[index]);

								if (response.status !== 200) {
									handleError(new Error(`请求失败,状态码:${response.status}`));
									return;
								}

								const arrayBuffer = response.response;
								progress[index] = 100;
								element.text("完成");

								// 创建 Blob
								const blob = new Blob([new Uint8Array(arrayBuffer)]);
								const url = URL.createObjectURL(blob);

								// 创建下载链接
								const a = document.createElement('a');
								a.href = url;
								a.download = `${element.data('name')}.mp4`;
								document.body.appendChild(a);
								a.click();

								setTimeout(() => {
									URL.revokeObjectURL(url);
									document.body.removeChild(a);
									element.text("下载");
									element.prop('disabled', false);
								}, 1000);
							};

							const handleError = (error) => {
								clearInterval(ins[index]);
								console.error("下载失败:", error);
								element.text("重试?");
								element.prop('disabled', false).one('click', () => {
									element.trigger('click');
								});
							};

							try {
								httpRequest({
									method: 'GET',
									url: element.data('href'),
									headers: {
										"referer": "https://www.pornhub.com/",
										"user-agent": navigator.userAgent
									},
									responseType: 'arraybuffer',
									onprogress: handleProgress,
									onload: handleLoad,
									onerror: handleError
								});
							} catch (error) {
								handleError(error);
							}
						});
					} else {
						// 对于镜像站点显示直链按钮
						downloadItem = $(`
							<li>
								<span class="download-url label">MP4 ${item.html}</span>
								<input class="download-url input" value="${item.url}" />
								<a class="download-url download" href="${item.url}" target="_blank">直链</a>
								<a class="download-url copy" data-href="${item.url}" href="javascript: void(0);">复制</a>
							</li>
						`)
					}
					downloadItem.find('.download-url.copy').on('click', function (element) {
						if (!$(this).data('href')) return;
						element.preventDefault();
						element = $(this)
						GM_setClipboard(element.data('href'));
						element.text("完成")
						setTimeout(() => { element.text("复制") }, 1000)
					})
					element.parent().find(".download-urls ul").append(downloadItem);
				});
			})
		}, true);
	}, 1)

	function play(id, url, urls, cover, actionFunction) {
		var art = new Artplayer({
			id: id,
			container: '.index-module_art-player',
			url: url,
			volume: 1,
			autoPlayback: true,
			theme: '#ff9000',
			customType: {
				m3u8: function (video, url, art) {
					if (Hls.isSupported()) {
						if (art.hls) art.hls.destroy();
						let hls = new Hls();
						hls.loadSource(url);
						hls.attachMedia(video);
						art.hls = hls;
						art.on('destroy', () => hls.destroy());
					} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
						video.src = url;
					} else {
						art.notice.show = 'Unsupported playback format: m3u8';
					}
				},
			},
			controls: [
				{
					name: 'goodRing',
					index: 21,
					position: 'left',
					html: '<i class="art-icon ph-icon-thumb-up"></i>',
					tooltip: '赏个好评',
					click: function () {
						GM_openInTab('https://greasyfork.org/scripts/501537/feedback', { active: true });
					},
				}
			],
			icons: {
				state: '<svg width="100" height="100" fill="none"><g opacity=".9"><path opacity=".5" d="M50 4.167C24.687 4.167 4.167 24.687 4.167 50c0 25.314 20.52 45.834 45.833 45.834 25.314 0 45.834-20.52 45.834-45.834 0-25.313-20.52-45.833-45.834-45.833Z" fill="#000"/><path d="M69.194 53.043 43.153 70.231a3.646 3.646 0 0 1-5.653-3.043V32.816a3.646 3.646 0 0 1 5.654-3.043l26.042 17.188c2.183 1.44 2.183 4.645 0 6.086-2.184 1.44 6.51-4.3-.002-.004Z" fill="#fff"/></g></svg>',
				indicator: '<img width="16" heigth="16" style="border-radius:50px" src="/favicon.ico">',
				play: '<svg width="13" height="13" viewBox="0 0 87.5 100"><path d="m0 0v100l87.5-50z"></path></svg>',
				pause: '<svg width="13" height="13" viewBox="0 0 81.75 100"><path d="m56.596 100v-100h25.154v100zm-56.596-100h25.154v100h-25.154z"></path></svg>',
				volume: '<svg width="18" height="18" viewBox="0 0 120 100" fill="none"><path d="m30 25 39.999-25v100l-39.999-25zm-30 50h20v-50h-20zm101.32-66.32-7.4851 7.485c9.2351 8.915 14.915 20.785 14.915 33.835s-5.6752 24.92-14.915 33.83l7.4899 7.49c11.525-10.765 18.676-25.275 18.676-41.32 0-16.046-7.1498-30.55-18.68-41.32zm-2.4453 41.32c0-10.42-4.5747-19.835-11.919-26.954l-7.5146 7.515c5.0547 5.245 8.1845 12.005 8.1845 19.44 0 7.44-3.1146 14.205-8.1697 19.456l7.515 7.5147c7.34-7.12 11.904-16.544 11.904-26.97z" stroke-width="1.1995"></path></svg>',
				volumeClose: '<svg width="18" height="18" viewBox="0 0 120 100" fill="none"><path d="m83.949 30.656v65.176l-42.579-20.834v-3.2712zm7.9837-25.393-7.1156-5.2669-15.84 17.697-27.608 15.637v15.192l-9.1933 10.267v-25.459h-18.387v41.668h3.8749l-17.664 19.734 7.1156 5.267z" stroke-width="1.0504"></path></svg>',
				setting: '<svg width="18" height="18" viewBox="0 0 97.3 100"><path d="m85.822 54.9c0.19964-1.6004 0.35-3.2002 0.35-4.9002s-0.15036-3.3-0.35-4.9l10.553-8.2497c0.95018-0.75 1.2004-2.1 0.60018-3.2l-10.003-17.3c-0.60018-1.1-1.9509-1.5-3.0515-1.1l-12.453 5c-2.601-2-5.4016-3.65-8.4525-4.9l-1.901-13.25c-0.14973-1.2-1.2004-2.1-2.4506-2.1h-20.006c-1.2504 0-2.3007 0.9-2.4508 2.1l-1.9006 13.25c-3.051 1.25-5.8518 2.95-8.4525 4.9l-12.453-5c-1.1503-0.45-2.4508 0-3.051 1.1l-10.003 17.3c-0.65022 1.1-0.35013 2.45 0.60018 3.2l10.553 8.2497c-0.20008 1.6-0.35013 3.25-0.35013 4.9 0 1.65 0.15005 3.2998 0.35013 4.9002l-10.553 8.2497c-0.95031 0.75032-1.2004 2.1-0.60018 3.2002l10.003 17.3c0.60018 1.0996 1.9506 1.5 3.051 1.0996l12.453-4.9996c2.6008 1.9996 5.4016 3.6499 8.4525 4.8998l1.9006 13.25c0.15004 1.2 1.2004 2.1 2.4508 2.1h20.006c1.2503 0 2.3009-0.9 2.4506-2.1l1.901-13.25c3.0508-1.2499 5.8515-2.9501 8.4525-4.8998l12.453 4.9996c1.1505 0.45032 2.4513 0 3.0515-1.0996l10.003-17.3c0.60018-1.1002 0.35-2.4499-0.60018-3.2002zm-37.161 12.6c-9.6528 0-17.505-7.8499-17.505-17.5 0-9.6499 7.8523-17.5 17.505-17.5 9.6528 0 17.505 7.8499 17.505 17.5 0 9.6506-7.8523 17.5-17.505 17.5z"></path></svg>',
			},
			setting: true,
			hotkey: true,
			flip: true,
			playbackRate: true,
			aspectRatio: true,
			miniProgressBar: true,
			fullscreen: true,
			fullscreenWeb: true,
			fastForward: true,
			autoOrientation: true,
			lock: true,
			moreVideoAttr: {
				'preload': 'none'
			},
		})
		art.on('fullscreen', () => {
			$('header').toggle();
			$(art.controls.goodRing).toggle();
			if (!$('body').hasClass('no-scroll')) {
				$('body').addClass('no-scroll');
			} else {
				$('body').removeClass('no-scroll');
			}
		});
		art.on('fullscreenWeb', () => {
			$('header').toggle();
			$(art.controls.goodRing).toggle();
			if (!$('body').hasClass('no-scroll')) {
				$('body').addClass('no-scroll');
			} else {
				$('body').removeClass('no-scroll');
			}
		});
		var contextmenuStyle = {
			"display": "flex",
			"justify-content": "center",
			"align-items": "center",
			"border-bottom": "none"
		}
		art.contextmenu.add({
			name: 'appTitle',
			index: 1,
			html: `<img width="16" heigth="16" style="border-radius:50px" src="/favicon.ico">Pronhub 增强`,
			style: contextmenuStyle,
			click: function () {
				GM_openInTab('https://greasyfork.org/scripts/501537/feedback', { active: true });
			},
		})
		art.contextmenu.update({
			name: 'version',
			index: 2,
			html: `<img width="15" heigth="15" src="https://artplayer.org/assets/img/logo.png"/>Artplayer Ultra ${Artplayer.version}`,
			click: function () {
				GM_openInTab('https://artplayer.org/', { active: true });
			},
			style: contextmenuStyle
		})
		art.contextmenu.update({
			name: 'info',
			index: 40,
			html: `${art.i18n.language["Video Info"]}`,
			style: contextmenuStyle
		})
		art.contextmenu.update({
			name: 'close',
			index: 50,
			html: `${art.i18n.language["Close"]}`,
			style: contextmenuStyle
		})
		unsafeWindow.art = art
		cover ? (art.poster = cover) : ""
		urls ? (art.quality = [...urls]) : ""
		$(art.template.$container).find(".icon").removeClass("icon")
		actionFunction ? actionFunction(art) : ""
	}

	function waitForKeyElements(selectorTxt, actionFunction, bWaitOnce, iframeSelector) {
		var targetbadges, btargetsFound;

		if (typeof iframeSelector == "undefined")
			targetbadges = $(selectorTxt);
		else
			targetbadges = $(iframeSelector).contents().find(selectorTxt);

		if (targetbadges && targetbadges.length > 0) {
			btargetsFound = true;
			targetbadges.each(function () {
				var jThis = $(this);
				var alreadyFound = jThis.data('alreadyFound') || false;
				if (!alreadyFound) {
					var cancelFound = actionFunction(jThis);
					if (cancelFound) {
						btargetsFound = false;
					} else {
						jThis.data('alreadyFound', true);
					}
				}
			});
		} else {
			btargetsFound = false;
		}

		var controlObj = waitForKeyElements.controlObj || {};
		var controlKey = selectorTxt.replace(/[^\w]/g, "_");
		var timeControl = controlObj[controlKey];

		if (btargetsFound && bWaitOnce && timeControl) {
			clearInterval(timeControl);
			delete controlObj[controlKey];
		} else {
			if (!timeControl) {
				timeControl = setInterval(function () {
					waitForKeyElements(selectorTxt, actionFunction, bWaitOnce, iframeSelector);
				}, 1);
				controlObj[controlKey] = timeControl;
			}
		}

		waitForKeyElements.controlObj = controlObj;
	}
})();