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.6
// @description  《也许同类型中最好用?》系列 - Pornhub 视频替换 Artplayer,不显示视频广告,支持进度条热力图,显示视频多分辨率 m3u8/mp4 下载链接,可直接下载 免费/付费/禁下 的视频
// @author       hmjz100,liuwanlin
// @license      MIT
// @match        *://*.pornhub.org/view_video.php?viewkey=*
// @match        *://*.pornhub.com/view_video.php?viewkey=*
// @match        *://*.pornhubpremium.com/view_video.php?viewkey=*
// @match        *://*/*/hvtrs8%2F-cl.ropnju%60.aoo%2Ftigw%5Dvkdgo%2Cpjp%3Dvkeukgy%3F*
// @grant        unsafeWindow
// @grant        GM_addStyle
// @grant        GM_setClipboard
// @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 () {
	'use strict';

	waitForKeyElements("body div.bottomNav", function (element) {
		console.log(element)
		element.fadeToggle()
	})

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

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

	// 等待页面信息出现
	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 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.toString())
					.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.fadeToggle();
			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.fadeToggle();
			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></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 (element) {
							if (!$(this).data('href')) return;
							element.preventDefault();
							element = $(this)
							let ins = {};
							let progress = {};
							let index = element.data('index');

							element.prop('disabled', true);

							ins[index] = setInterval(function () {
								let prog = +progress[index] || 0;
								element.text(prog + "%");
							}, 10);

							try {
								let response = await unsafeWindow.fetch(element.data('href'));
								if (!response.ok) throw new Error('网络响应失败');

								let totalSize = response.headers.get('Content-Length') || 0;
								let reader = response.body.getReader();
								let receivedLength = 0;
								let chunks = [];

								while (true) {
									const { done, value } = await reader.read();
									if (done) break;
									chunks.push(value);
									receivedLength += value.length;
									progress[index] = ((receivedLength / totalSize) * 100).toFixed(1);
								}

								const blob = new Blob(chunks);

								clearInterval(ins[index]);
								progress[index] = 100;
								element.text("完成")

								const url = URL.createObjectURL(blob);
								const a = document.createElement('a');
								a.href = url;
								a.download = `${element.data('name')}.mp4`;
								a.style.display = 'none';
								document.body.appendChild(a);
								a.click();

								setTimeout(() => {
									URL.revokeObjectURL(url);
									document.body.removeChild(a);
								}, 1000);

								setTimeout(function () {
									element.text("下载");
									$(this).prop('disabled', false);
								}.bind(this), 3000);
							} catch (error) {
								clearInterval(ins[index]);
								console.error("下载失败:", error);
								element.text("重试?");
								$(this).prop('disabled', false).one('click', async function () {
									$(this).trigger('click');
								});
							}
						})
					} 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 () {
						window.open('https://greasyfork.org/scripts/501537/feedback', '_blank')
					},
				}
			],
			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,
		})
		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 () {
				window.open('https://greasyfork.org/scripts/501537/feedback', '_blank')
			},
		})
		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 () {
				window.open('https://artplayer.org/', '_blank')
			},
			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;
	}
})();