// ==UserScript==
// @name 🌀VIP视频解析——fatcat
// @namespace [fatcat]
// @version 2.8.3
// @description 适配手机端与电脑端,可自主管理解析点
// @author 暴走的肥猫
// @license MIT, Sign after modification
// @include /^http[^http]*youku\.com\/.+$/
// @include /^http[^http]*iqiyi\.com\/.+$/
// @include /^http[^http]*v\.qq\.com\/.+$/
// @include /^http[^http]*le\.com\/.+$/
// @include /^http[^http]*mgtv\.com\/.+$/
// @include /^http[^http]*tv\.sohu\.com\/.+$/
// @compatible safari
// @compatible chrome
// @compatible edge
// @grant GM_getValue
// @grant GM_setValue
// @grant GM.getValue
// @grant GM.setValue
// @run-at document-idle
// @noframes
// @downloadURL none
// ==/UserScript==
/*jshint esversion: 11 */
(async function () {
const ACCESS_POINT = [
{ "name": "虾米-1", "url": "https://jx.xmflv.com/?url=" },
{ "name": "虾米-2", "url": "https://jx.xmflv.cc/?url=" },
{ "name": "M1907-1", "url": "https://im1907.top/?jx=" },
{ "name": "M1907-2", "url": "https://z1.m1907.top/?eps=0&jx=" },
{ "name": "M3U8TV", "url": "https://jx.m3u8.tv/jiexi/?url=" },
{ "name": "夜幕", "url": "https://www.yemu.xyz/?url=" },
{ "name": "777", "url": "https://jx.jsonplayer.com/player/?url=" },
{ "name": "CK", "url": "https://www.ckplayer.vip/jiexi/?url=" },
{ "name": "YT", "url": "https://jx.yangtu.top/?url=" },
{ "name": "Player-JY", "url": "https://jx.playerjy.com/?url=" },
{ "name": "Yparse", "url": "https://jx.yparse.com/index.php?url=" },
{ "name": "8090", "url": "https://www.8090g.cn/?url=" },
{ "name": "剖元", "url": "https://www.pouyun.com/?url=" },
{ "name": "全民", "url": "https://43.240.74.102:4433?url=" },
{ "name": "爱豆", "url": "https://jx.aidouer.net/?url=" },
{ "name": "冰豆", "url": "https://bd.jx.cn/?url=" },
{ "name": "Playm3u8", "url": "https://www.playm3u8.cn/jiexi.php?url=" },
];
var searchSelector = ['video'];
async function getUserConfig(key, defaultVal) {
return typeof GM_getValue === 'function' ? GM_getValue(key, defaultVal) : GM.getValue(key, defaultVal);
}
async function setUserConfig(key, val) {
return typeof GM_setValue === 'function' ? GM_setValue(key, val) : GM.setValue(key, val);
}
function seekSameSizeParentNode(node) {
let nodeSize = node.getBoundingClientRect();
let parents = [node];
while (node.parentNode != document.body) {
parents.push(node.parentNode);
node = node.parentNode;
};
let parentSize;
let sameSizeParent = parents.findLast(parent => {
parentSize = parent.getBoundingClientRect();
return Math.abs(parentSize.width - nodeSize.width) < 1 && Math.abs(parentSize.height - nodeSize.height) < 1
});
return sameSizeParent;
}
function useInterval(fn, intervalTime, maxTime) {
return new Promise((resolve) => {
let totalTime = 0;
let interval = setInterval(() => {
totalTime += intervalTime;
if (totalTime >= maxTime || fn()) { clearInterval(interval); resolve(); }
}, intervalTime)
})
}
async function findVideoWrapper() {
await useInterval(() => Array.from(document.querySelectorAll(searchSelector.join(','))).find(videoNode => {
videoNode.style.display = 'revert';
return videoNode.getBoundingClientRect().width > 100 && videoNode.getBoundingClientRect().height > 100
}), 200, Infinity);
let videoNodes = Array.from(document.querySelectorAll(searchSelector.join(',')));
return videoNodes.map(videoNode => seekSameSizeParentNode(videoNode)).reduce((pre, cur) => {
let preW = pre?.getBoundingClientRect().width || 0;
let curW = cur.getBoundingClientRect().width;
if (curW > preW) return cur;
return pre;
})
}
async function createVideoFrame(videoWrapper, curAccessPoint) {
videoWrapper.style.overflow = 'hidden';
if (window.getComputedStyle(videoWrapper).position == 'static') videoWrapper.style.position = 'relative';
let iframeWrapper = document.createElement('div');
iframeWrapper.innerHTML = ``;
iframeWrapper.style = `position:absolute; inset:0; z-index: 99999999;`
videoWrapper.appendChild(iframeWrapper);
return;
}
async function createMenu(accessPoints, curAccessPoint) {
if (document.querySelector('#fatcat_video_vip')) return;
let wrapper = document.createElement('div');
wrapper.innerHTML = `
`;
let popoverToggleBtn = wrapper.querySelector('#fatcat_video_vip_popover_toggle_btn');
let popoverCloseBtn = wrapper.querySelector('#fatcat_video_vip_popover_close_btn');
let popover = wrapper.querySelector('#fatcat_video_vip_popover');
popoverToggleBtn.onclick = () => popover.toggleAttribute('hidden');
popoverCloseBtn.onclick = () => popover.toggleAttribute('hidden', true);
window.addEventListener('click', (e) => { if (!e.target.matches('#fatcat_video_vip_popover,#fatcat_video_vip_popover *,#fatcat_video_vip_popover_toggle_btn')) popover.toggleAttribute('hidden', true); })
let popoverTitle = wrapper.querySelector('#fatcat_video_vip_popover_title');
let nameInput = wrapper.querySelector('#fatcat_video_vip_point_name');
let urlInput = wrapper.querySelector('#fatcat_video_vip_point_url');
let addBtn = wrapper.querySelector('#fatcat_video_vip_addpoint');
let delBtn = wrapper.querySelector('#fatcat_video_vip_delpoint');
let setWrapperBtn = wrapper.querySelector('#fatcat_video_vip_setwrapper');
let resetBtn = wrapper.querySelector('#fatcat_video_vip_resetpoints');
let selects = wrapper.querySelector('#fatcat_video_vip_select');
let aTag = wrapper.querySelector('#fatcat_video_vip_link');
function updateSelectView(accessPoints, curAccessPoint) {
selects.innerHTML = accessPoints.map(({ name, url }) => ``).join();
}
async function applyAccessPoint(accessPoints, curAccessPoint) {
await setUserConfig('accessPoints', accessPoints);
await setUserConfig('curAccessPoint', curAccessPoint);
updateSelectView(accessPoints, curAccessPoint);
updateView(curAccessPoint);
displayResult('✅ 完成');
return;
}
let resultToasting;
function displayResult(html) {
let rawTitle = '当前解析点';
popoverTitle.innerHTML = html;
clearTimeout(resultToasting);
resultToasting = setTimeout(() => popoverTitle.innerHTML = rawTitle, 1500);
}
addBtn.onclick = async () => {
if (!(nameInput.value.trim() && urlInput.value.trim())) {
displayResult('❌ 请完整输入');
return;
}
let accessPoints = await getUserConfig('accessPoints');
let sameUrlPoint = accessPoints.find(({ url }) => url == urlInput.value.trim());
if (sameUrlPoint) {
nameInput.value = sameUrlPoint.name;
displayResult('❌ 重复数据');
return;
}
let sameNamePoint = accessPoints.find(({ name }) => name == nameInput.value.trim());
if (sameNamePoint) {
urlInput.value = sameNamePoint.url
displayResult('❌ 重复数据');
return;
}
let newPoint = { name: nameInput.value.trim(), url: urlInput.value.trim() };
let newAccessPoints = accessPoints.concat([newPoint]);
applyAccessPoint(newAccessPoints, newPoint);
}
delBtn.onclick = async () => {
let accessPoints = await getUserConfig('accessPoints');
let curAccessPoint = await getUserConfig('curAccessPoint');
if (accessPoints.length == 0) {
displayResult('❌ 数据已被清空');
return;
}
nameInput.value = curAccessPoint.name;
urlInput.value = curAccessPoint.url;
let curAccessPointIndex = accessPoints.findIndex(point => point.name == curAccessPoint.name);
let newAccessPoints = accessPoints.filter(point => point.name != curAccessPoint.name);
curAccessPoint = newAccessPoints[Math.min(curAccessPointIndex, newAccessPoints.length - 1)];
applyAccessPoint(newAccessPoints, curAccessPoint);
}
resetBtn.onclick = async () => {
applyAccessPoint(ACCESS_POINT, ACCESS_POINT[0]);
}
function setWrapper(e) {
if (e.target.matches('#fatcat_video_vip,#fatcat_video_vip *')) return;
e.target.classList.toggle('fatcat_video_vip_custom_wrapper', true);
searchSelector.push('.fatcat_video_vip_custom_wrapper');
window.removeEventListener('mousedown', setWrapper);
document.querySelector('#fatcat_video_vip_select_area_css').remove();
e.stopPropagation();
e.preventDefault();
}
setWrapperBtn.addEventListener('click', () => {
try { document.querySelector('.fatcat_video_vip_custom_wrapper').remove(); } catch (e) { }
let selectAreaCss = document.createElement('style');
selectAreaCss.id = 'fatcat_video_vip_select_area_css';
selectAreaCss.innerHTML = `
*:hover{
outline: 2px solid #13bf92!important;
outline-offset: -2px!important;
border-radius: 6px!important;
z-index: 99999999!important; }
*:has(*:hover){
outline: unset!important;
z-index: 99999999!important; }`;
document.body.append(selectAreaCss);
setWrapperBtn.innerHTML = '请点击需要插入视频的区域';
setTimeout(() => setWrapperBtn.innerHTML = '📍手动选择视频位置', 3000)
window.addEventListener('mousedown', setWrapper);
})
function updateView(curAccessPoint) {
let iframe = document.querySelector('#fatcat_video_vip_iframe');
if (!iframe) return true;
let src = curAccessPoint ? curAccessPoint.url + window.location.href : '';
aTag.href = src;
if (iframe.src != src) {
iframe.src = src;
return true;
}
}
window.addEventListener('mousedown', async (e) => {
if (!(e.target.matches("#fatcat_video_vip *") || e.target.matches("#fatcat_video_vip_iframe"))) {
let curAccessPoint = await getUserConfig('curAccessPoint');
useInterval(() => updateView(curAccessPoint), 100, 10000);
}
});
selects.onchange = async () => {
let accessPoints = await getUserConfig('accessPoints');
let curAccessPoint = accessPoints.find(({ url }) => url == selects.value)
await setUserConfig('curAccessPoint', curAccessPoint);
updateView(curAccessPoint);
};
document.body.appendChild(wrapper);
}
async function inject() {
let accessPoints = await getUserConfig('accessPoints');
if (!accessPoints) {
await setUserConfig('accessPoints', ACCESS_POINT);
await setUserConfig('curAccessPoint', ACCESS_POINT[0]);
accessPoints = ACCESS_POINT;
}
let curAccessPoint = await getUserConfig('curAccessPoint');
createMenu(accessPoints, curAccessPoint);
let videoWrapper = await findVideoWrapper();
try {
useInterval(() => {
document.querySelectorAll('video').forEach(video => {
video.muted = true; video.pause();
video.style.visibility = 'hidden';
video.removeAttribute('src');
})
}, 200, Infinity);
document.querySelector('#fatcat_video_vip_setwrapper').style.display = 'none';
} catch (e) { }
createVideoFrame(videoWrapper, curAccessPoint);
}
inject();
})();