// ==UserScript==
// @name 91pinse-优化
// @namespace https://sleazyfork.org/zh-CN/users/1461640-%E6%98%9F%E5%AE%BF%E8%80%81%E9%AD%94
// @version 0.1.1
// @description 91pinse 视频宽屏
// @match https://91pinse.com/*
// @match https://fplayer.cc/*
// @match https://*.fplayer.cc/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=91pinse.com
// @license MIT
// @grant none
// @run-at document-start
// ==/UserScript==
(function () {
'use strict';
// ======= 广告/弹窗拦截 =======
const blockedAdHosts = ['tsyndicate.com', 'rmhfrtnd.com', 'pemsrv.com'];
// 点击视频区域时短时间内屏蔽弹窗
let suppressPopupUntil = 0;
function armPopupShield(durationMs) {
const duration = typeof durationMs === 'number' ? durationMs : 1500;
const until = Date.now() + duration;
suppressPopupUntil = Math.max(suppressPopupUntil, until);
}
function isShieldActive() {
return Date.now() < suppressPopupUntil;
}
function isInsideVideoArea(target) {
try {
if (!target) return false;
if (target.closest && target.closest('video, #videoIframe, .video-container')) return true;
return false;
} catch (_) {
return false;
}
}
// 在护盾生效期间,禁止脚本触发强制导航(assign/replace/reload)
(function patchLocationNavigation() {
try {
const LocProto = window.Location && window.Location.prototype;
if (!LocProto) return;
const originalAssign = LocProto.assign;
const originalReplace = LocProto.replace;
const originalReload = LocProto.reload;
if (originalAssign) {
LocProto.assign = function (url) {
if (isShieldActive()) {
try { console.log('[91pinse-优化] 护盾生效,拦截 assign:', url); } catch (_) {}
return; }
return originalAssign.call(this, url);
};
}
if (originalReplace) {
LocProto.replace = function (url) {
if (isShieldActive()) {
try { console.log('[91pinse-优化] 护盾生效,拦截 replace:', url); } catch (_) {}
return; }
return originalReplace.call(this, url);
};
}
if (originalReload) {
LocProto.reload = function () {
if (isShieldActive()) {
try { console.log('[91pinse-优化] 护盾生效,拦截 reload'); } catch (_) {}
return; }
return originalReload.call(this);
};
}
} catch (_) {}
})();
// 强制启用原生右键菜单(视频区域)
function stripContextMenuAttribute(root) {
try {
const scope = (root || document).querySelectorAll('[oncontextmenu]');
scope.forEach(function (el) {
try { el.oncontextmenu = null; el.removeAttribute('oncontextmenu'); } catch (_) {}
});
} catch (_) {}
}
function enableContextMenuInDoc(doc) {
try {
doc.addEventListener('contextmenu', function (e) {
try {
// 仅在视频区域或包含视频的容器中放开右键
const target = e.target;
const inVideo = target && (target.tagName === 'VIDEO' || (target.closest && target.closest('video, .video-container, .plyr, .plyr__video-wrapper')));
if (inVideo) {
e.stopImmediatePropagation();
e.stopPropagation();
}
} catch (_) {}
}, true);
const removeInline = function (root) {
try {
const scope = (root || doc).querySelectorAll('[oncontextmenu], .plyr__poster');
scope.forEach(function (el) { try { el.oncontextmenu = null; el.removeAttribute('oncontextmenu'); } catch (_) {} });
} catch (_) {}
};
removeInline(doc);
// 观察 iframe 内部变化,持续移除 oncontextmenu
const obs = new doc.defaultView.MutationObserver(function (mutations) {
mutations.forEach(function (m) {
if (m.type === 'childList') {
m.addedNodes.forEach(function (n) { if (n.nodeType === 1) removeInline(n); });
} else if (m.type === 'attributes' && (m.attributeName === 'oncontextmenu' || m.attributeName === 'style')) {
try { m.target.oncontextmenu = null; m.target.removeAttribute('oncontextmenu'); } catch (_) {}
}
});
});
obs.observe(doc.documentElement || doc, { childList: true, subtree: true, attributes: true, attributeFilter: ['oncontextmenu','style'] });
} catch (_) {}
}
function tryHookSameOriginIframe(iframe) {
try {
const cw = iframe && iframe.contentWindow;
const cd = cw && cw.document;
if (!cd) return;
// 在同源或明确为 fplayer 域时尝试放开右键
const origin = cw.location.origin;
if (origin === window.location.origin || /(^https?:\/\/)?([\w-]+\.)?fplayer\.cc$/i.test(origin)) {
enableContextMenuInDoc(cd);
}
} catch (_) {
// 跨域无法处理
}
}
// 在捕获阶段阻止站点脚本接管右键,但不阻止默认行为,从而保留浏览器菜单
document.addEventListener('contextmenu', function (e) {
try {
// 全局右键:启动护盾,阻断冒泡,减少站点监听器触发的弹窗/刷新
armPopupShield(1600);
e.stopImmediatePropagation();
e.stopPropagation();
// 不调用 preventDefault,确保浏览器显示自带菜单
} catch (_) {}
}, true);
function isBlockedAdUrl(url) {
try {
const u = new URL(url, window.location.href);
return blockedAdHosts.some(function (host) {
return u.hostname === host || u.hostname.endsWith('.' + host);
});
} catch (e) {
return false;
}
}
// ======= 91pinse: 优先使用 HD =======
function isPinseDomain() {
try { return /(^|\.)91pinse\.com$/i.test(window.location.hostname); } catch (_) { return false; }
}
function extractVideoIdFromPath(pathname) {
try {
const mHD = pathname.match(/^\/vhd\/(\d+)/);
if (mHD) return { id: mHD[1], isHD: true };
const mSD = pathname.match(/^\/v\/(\d+)/);
if (mSD) return { id: mSD[1], isHD: false };
return null;
} catch (_) { return null; }
}
function buildPinseUrl(isHD, id) {
return isHD ? `/vhd/${id}` : `/v/${id}`;
}
// 基于页面“是否存在 HD 标记/链接”来判断是否应走 HD,而不是发请求探测
function elementHasHdText(el) {
try {
if (!el) return false;
const text = (el.textContent || '').trim();
return text === 'HD' || text === 'Hd' || text === 'hd' || /\bHD\b/i.test(text);
} catch (_) { return false; }
}
function hasHdMarkerIn(container) {
try {
if (!container) return false;
// 仅识别右上角的 HD 徽标,避免把时长等误判
const hdBadges = container.querySelectorAll('.absolute.top-1.right-1, .absolute[class*="top-1"][class*="right-1"], .bg-primary-600');
for (let i = 0; i < hdBadges.length; i++) { if (elementHasHdText(hdBadges[i])) return true; }
// 也可能作为按钮或链接出现
const anchors = container.querySelectorAll('a');
for (let i = 0; i < anchors.length; i++) { if (elementHasHdText(anchors[i]) || /Switch\s+to\s+HD/i.test((anchors[i].textContent||''))) return true; }
return false;
} catch (_) { return false; }
}
function hasHdForId(id, scope) {
try {
const root = scope || document;
if (root.querySelector(`a[href="/vhd/${id}"]`)) return true;
return hasHdMarkerIn(root);
} catch (_) { return false; }
}
async function preferHDOnPage() {
if (!isPinseDomain()) return;
const info = extractVideoIdFromPath(window.location.pathname);
if (!info || info.isHD) return;
// 仅在页面存在明确的 HD 入口/标记时才切换
const pageHasHd = hasHdForId(info.id, document) || !!document.querySelector('.absolute.top-1.right-1, .absolute[class*="top-1"][class*="right-1"], a:contains("Switch to HD")');
if (!pageHasHd) return;
const hdUrl = buildPinseUrl(true, info.id);
console.log('[91pinse-优化] 自动切换到 HD:', hdUrl);
window.location.replace(hdUrl);
}
function upgradeLinksToHD(root) {
if (!isPinseDomain()) return;
const scope = (root || document).querySelectorAll('a[href^="/v/"]:not([data-hd-upgraded])');
scope.forEach(function (a) {
try {
const m = a.getAttribute('href').match(/^\/v\/(\d+)/);
if (!m) return;
const id = m[1];
const sdHref = a.getAttribute('href');
const hdHref = `/vhd/${id}`;
// 仅当“该卡片”内存在 HD 标记/链接时才升级;否则如已被误升级则回退
const card = a.closest('.group, [id^="preview_"], .rounded, .shadow, .card, .overflow-hidden, .relative') || a.parentElement;
const shouldUpgrade = hasHdForId(id, card);
if (shouldUpgrade) {
a.dataset.hdUpgraded = '1';
a.dataset.sdHref = sdHref;
a.setAttribute('href', hdHref);
} else {
// 若之前被误升级,恢复 sd
const prevSd = a.dataset.sdHref;
if (prevSd && a.getAttribute('href') === hdHref) {
a.setAttribute('href', prevSd);
}
delete a.dataset.hdUpgraded;
}
} catch (_) {}
});
}
function installPreferHdClickHandler() {
if (!isPinseDomain()) return;
document.addEventListener('click', async function (event) {
try {
if (event.defaultPrevented) return;
if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
let anchor = event.target && event.target.closest ? event.target.closest('a') : null;
if (!anchor) return;
const href = anchor.getAttribute('href') || '';
const m = href.match(/^\/v\/(\d+)/);
if (!m) return;
const id = m[1];
// 仅当附近确实有 HD 标记/链接时,才替换为 HD
const card = anchor.closest('.group, [id^="preview_"], .rounded, .shadow, .card, .overflow-hidden, .relative') || anchor.parentElement;
if (!hasHdForId(id, card)) return;
const hdUrl = `/vhd/${id}`;
if (href === hdUrl) return;
event.preventDefault();
window.location.href = hdUrl;
} catch (_) {}
}, true);
}
// 移除/禁用指向广告域名的 iframe,以防止其在自身上下文内弹窗
function shouldRemoveIframe(iframe) {
try {
const src = iframe.getAttribute('src') || '';
if (!src) return false;
return isBlockedAdUrl(src);
} catch (_) {
return false;
}
}
function sanitizeIframes(root) {
const frames = (root || document).querySelectorAll('iframe');
frames.forEach(function (frame) {
try {
if (shouldRemoveIframe(frame)) {
console.log('[91pinse-优化] 已移除广告 iframe:', frame.src);
frame.remove();
return;
}
// 降权可能触发弹窗的 iframe:尽量禁止其响应点击
if (!frame.__pinseLocked) {
frame.style.pointerEvents = 'auto';
// 对可疑域名关闭指针事件,避免在其内部触发“基于用户手势”的弹窗
if (frame.src && isBlockedAdUrl(frame.src)) {
frame.style.pointerEvents = 'none';
frame.__pinseLocked = true;
}
}
} catch (_) {}
});
}
(function patchWindowOpen() {
const originalOpen = window.open;
window.open = function () {
try {
let url = arguments[0];
if (url && typeof url !== 'string') {
try { url = String(url); } catch (_) {}
}
// 直接拦截已知广告域名
if (typeof url === 'string' && isBlockedAdUrl(url)) {
console.log('[91pinse-优化] 已拦截弹窗:', url);
return null;
}
// 某些站点先打开 about:blank 再在新窗口内跳转,这里在 91pinse 域名内一并拦截
if ((url === '' || url === 'about:blank' || url === undefined) && window.location.hostname.endsWith('91pinse.com')) {
const target = arguments[1];
if (target === '_blank' || !target) {
console.log('[91pinse-优化] 已拦截空白弹窗');
return null;
}
}
// 若当前处于“弹窗护盾”时间窗内,仅允许同源窗口
if (isShieldActive()) {
try {
const u = new URL(typeof url === 'string' ? url : '', window.location.href);
const sameOrigin = u.origin === window.location.origin;
if (!sameOrigin) {
console.log('[91pinse-优化] 护盾生效,已拦截跨域弹窗:', url || '(empty)');
return null;
}
} catch (_) {
console.log('[91pinse-优化] 护盾生效,已拦截无法解析的弹窗');
return null;
}
}
} catch (e) {}
return originalOpen.apply(this, arguments);
};
try { if (window.top && window.top !== window) window.top.open = window.open; } catch (_) {}
try { if (window.parent && window.parent !== window) window.parent.open = window.open; } catch (_) {}
})();
// 捕获阶段拦截到指向广告域名的 <a> 点击
function interceptAnchorEvent(event) {
try {
let anchor = null;
const path = event.composedPath ? event.composedPath() : [];
for (let i = 0; i < path.length; i++) {
const node = path[i];
if (node && node.tagName === 'A') {
anchor = node;
break;
}
}
if (!anchor && event.target && event.target.closest) {
anchor = event.target.closest('a');
}
// 列表页点击/右键同源链接时,先开启护盾,防止随后触发的跨域弹窗
if (anchor && anchor.href) {
try {
const u = new URL(anchor.href, window.location.href);
if (u.origin === window.location.origin) {
armPopupShield(1600);
}
} catch (_) {}
}
if (anchor && anchor.href && isBlockedAdUrl(anchor.href)) {
event.preventDefault();
event.stopImmediatePropagation();
console.log('[91pinse-优化] 已阻止跳转到广告域名:', anchor.href);
}
} catch (e) {}
}
['click', 'mousedown', 'mouseup', 'pointerdown', 'auxclick', 'contextmenu', 'touchstart'].forEach(function (evt) {
document.addEventListener(evt, interceptAnchorEvent, true);
});
// 在视频区域的任意按键或点击都会激活护盾(包括右键)
['mousedown', 'pointerdown', 'click', 'auxclick', 'contextmenu'].forEach(function (evt) {
document.addEventListener(
evt,
function (e) {
try {
// 在视频区域或链接点击前,启动护盾
if (isInsideVideoArea(e.target)) armPopupShield(1600);
// 右键/中键在任何位置也启动护盾
if (evt === 'contextmenu' || (e.button && e.button !== 0)) armPopupShield(1600);
} catch (_) {}
},
true
);
});
const currentDomain = window.location.hostname;
const config = {
selectors: {
videoContainer: '.video-container',
videoIframe: '#videoIframe',
mainGrid: '.grid.grid-cols-1.lg\\:grid-cols-3.gap-6',
mainCol: '.lg\\:col-span-2'
},
applyStyles: function () {
const mainGrid = document.querySelector(
'.grid.grid-cols-1.lg\\:grid-cols-3.gap-6'
);
const mainCol = document.querySelector('.lg\\:col-span-2');
if (mainGrid) {
mainGrid.classList.remove('grid-cols-1', 'lg:grid-cols-3');
mainGrid.classList.add('grid-cols-1');
mainGrid.style.maxWidth = '100%';
}
if (mainCol) {
mainCol.classList.remove('lg:col-span-2');
mainCol.style.width = '100%';
mainCol.style.maxWidth = '100%';
}
},
cssRules: `
.video-container {
width: 100% !important;
max-width: 100% !important;
margin: 0 auto !important;
background-color: #000 !important;
}
/* 去除 plyr 覆盖层影响右键 */
.plyr__poster { pointer-events: none !important; }
.plyr__controls[hidden], .plyr__controls[style*="display: none"] { display: none !important; }
.pinse-open-player-btn {
position: absolute;
top: 10px;
right: 10px;
z-index: 9999;
padding: 6px 10px;
font-size: 12px;
border-radius: 6px;
background: rgba(0,0,0,0.6);
color: #fff;
border: 1px solid rgba(255,255,255,0.2);
cursor: pointer;
user-select: none;
text-decoration: none;
}
.pinse-open-player-btn:hover { background: rgba(0,0,0,0.75); }
#videoIframe, .aspect-video {
display: block !important;
width: 100% !important;
height: auto !important;
}
.grid.grid-cols-1 {
width: 100% !important;
max-width: 100% !important;
}
.text-xl.md\\:text-2xl.font-bold.p-4 {
text-align: center !important;
}
.grid.grid-cols-2.md\\:grid-cols-3.gap-4 {
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)) !important;
}
@media (min-width: 1400px) {
.video-container {
max-width: 85% !important;
}
.lg\\:col-span-2 {
margin: 0 auto !important;
max-width: 85% !important;
}
}
body {
background-color: #121212 !important;
}
.bg-base-100 {
background-color: #1a1a1a !important;
}
.shadow-lg {
box-shadow: 0 4px 6px rgba(0,0,0,0.2) !important;
}
`
};
function setWideScreenMode() {
const videoContainer = document.querySelector(config.selectors.videoContainer);
const videoElement =
document.querySelector(config.selectors.videoElement) ||
document.querySelector(config.selectors.videoIframe);
if (!videoContainer && !videoElement) {
console.log(`${currentDomain}宽屏模式: 未找到视频元素`);
return;
}
if (config.applyStyles) {
config.applyStyles();
}
if (videoContainer) {
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
const targetAspect = 16 / 9;
let containerWidth = viewportWidth;
let containerMaxWidth = viewportWidth;
let containerMaxHeight = Math.floor(viewportHeight * 0.9);
if (viewportWidth >= 1400) {
containerMaxWidth = Math.floor(viewportWidth * 0.9);
}
// 计算在可用高度内能容纳的最大宽度
const widthByHeight = Math.floor(containerMaxHeight * targetAspect);
containerWidth = Math.min(containerMaxWidth, widthByHeight);
videoContainer.style.width = containerWidth + 'px';
videoContainer.style.maxWidth = containerWidth + 'px';
videoContainer.style.margin = '0 auto';
videoContainer.style.position = 'relative';
}
if (videoElement) {
const aspectRatio = 16 / 9;
videoElement.style.width = '100%';
videoElement.style.height = 'auto';
videoElement.style.maxHeight = '90vh';
videoElement.removeAttribute('style');
// 重新设置关键样式,避免行内样式与 CSS 冲突
videoElement.style.width = '100%';
videoElement.style.height = 'auto';
videoElement.style.maxHeight = '90vh';
}
ensureOpenPlayerButton();
console.log(`${currentDomain}宽屏模式: 已启用宽屏模式`);
}
function setupResizeListener() {
let resizeRaf = null;
window.addEventListener('resize', function () {
if (resizeRaf) cancelAnimationFrame(resizeRaf);
resizeRaf = requestAnimationFrame(function () {
setWideScreenMode();
});
});
}
function addStyles() {
const styleSheet = document.createElement('style');
styleSheet.textContent = config.cssRules;
document.head.appendChild(styleSheet);
}
// 去除内联 style 中的强制宽高/比例,避免与适配冲突
function normalizeInlineStyles() {
try {
const iframe = document.querySelector('#videoIframe');
if (iframe) {
iframe.style.removeProperty('max-height');
iframe.style.removeProperty('aspect-ratio');
iframe.style.removeProperty('height');
iframe.style.removeProperty('width');
}
const container = document.querySelector('.video-container');
if (container) {
container.style.removeProperty('max-width');
container.style.removeProperty('width');
}
} catch (_) {}
}
// 在同域播放器中提供“在新标签页打开播放器”按钮,方便用户用原生界面右键
function ensureOpenPlayerButton() {
try {
const container = document.querySelector(config.selectors.videoContainer);
const iframe = document.querySelector(config.selectors.videoIframe);
if (!container || !iframe) return;
if (iframe.src && new URL(iframe.src, location.href).origin !== location.origin) return; // 仅处理同源
let btn = container.querySelector('.pinse-open-player-btn');
if (!btn) {
btn = document.createElement('a');
btn.className = 'pinse-open-player-btn';
btn.textContent = '在新标签页打开播放器';
btn.target = '_blank';
container.appendChild(btn);
}
btn.href = iframe.src || location.href;
} catch (_) {}
}
function init() {
addStyles();
sanitizeIframes(document);
stripContextMenuAttribute(document);
// 尝试对同源 iframe 也启用右键
try {
document.querySelectorAll('iframe').forEach(function (f) { tryHookSameOriginIframe(f); });
} catch (_) {}
// 列表链接与详情页优先 HD
upgradeLinksToHD(document);
preferHDOnPage();
installPreferHdClickHandler();
const delay = 200;
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function () {
setTimeout(function () {
setWideScreenMode();
setupResizeListener();
}, delay);
});
} else {
setTimeout(function () {
setWideScreenMode();
setupResizeListener();
}, delay);
}
document.addEventListener(
'load',
function (e) {
if (e.target.tagName === 'VIDEO' || e.target.tagName === 'IFRAME') {
setWideScreenMode();
}
},
true
);
window.addEventListener('load', function () {
setWideScreenMode();
});
const observer = new MutationObserver(function (mutations) {
let shouldUpdate = false;
mutations.forEach(function (mutation) {
if (mutation.addedNodes.length > 0) {
shouldUpdate = true;
mutation.addedNodes.forEach(function (node) {
try {
if (node.nodeType === 1) {
if (node.tagName === 'IFRAME') {
sanitizeIframes(node.parentNode || document);
tryHookSameOriginIframe(node);
} else if (node.querySelectorAll) {
sanitizeIframes(node);
stripContextMenuAttribute(node);
// 针对新增的同源 iframe
try { node.querySelectorAll('iframe').forEach(function (f) { tryHookSameOriginIframe(f); }); } catch (_) {}
// 新增节点里同样升级视频链接
upgradeLinksToHD(node);
}
}
} catch (_) {}
});
}
});
if (shouldUpdate) {
setWideScreenMode();
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['src','style']
});
console.log(`${currentDomain}宽屏模式: 已启用`);
}
init();
})();