// ==UserScript==
// @name 💖 VIP视频解析
// @namespace https://greasyfork.org/zh-CN/users/1409010-i-breathe
// @version 5.1
// @description 自用视频解析、多源切换、简洁易用、UI美观(支持"爱优腾芒"等多平台多解析源切换)双击可能更好用哦!
// @author I-Breathe
// @include http*://*.iqiyi.*/*
// @include http*://*.qq.*/*
// @include http*://*.youku.*/*
// @include http*://*.bilibili.*/*
// @include http*://*.mgtv.*/*
// @include http*://*.sohu.*/*
// @include http*://*.pptv.*/*
// @include http*://*.le.*/*
// @include http*://*.1905.*/*
// @include http*://*.acfun.*/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @run-at document-start
// ==/UserScript==
(() => {
'use strict';
const CONFIG = {
buttonSize: 50,
buttonRight: '25px',
buttonBottom: '35px',
imageUrl: GM_getValue('imageUrl', 'https://img13.360buyimg.com/ddimg/jfs/t1/121241/11/19612/181715/5fbac680E636138b5/267dd280e727aff4.jpg'),
opacity: 1,
buttonRadius: '50%',
listBlur: '10px',
listBgColor: 'rgba(20,20,20,0.5)',
listColor: '#00000099',
listFontSize: '14px',
itemHoverBg: 'rgba(255,255,255,0.2)',
selectedColor: '#FFFFFA',
breatheColors: ['#FF00FF95', '#00FAFF95', '#FFFF0095', '#00FFFF95', '#00FF0095'],
breatheDuration: 12,
glowSize: 7,
parseUrl: GM_getValue('selectedParseUrl', 'https://jx.2s0.cn/player/?url='),
parseUrls: GM_getValue('storedParseUrls', [
["https://bd.jx.cn/?url=", "冰豆弹幕"],
["https://am1907.top/?jx=", "1907解析"],
["https://jx.xmflv.cc/?url=", "虾米解析"],
["https://jx.xymp4.cc/?url=", "咸鱼解析"],
["https://www.yemu.xyz/?url=", "夜幕解析"],
["https://jx.77flv.cc/?url=", "77云解析"],
["https://www.8090g.cn/jiexi/?url=", "8090g"],
["https://jx.playerjy.com/?url=", "PlayerJy"],
["https://www.ckplayer.vip/jiexi/?url=", "CkPlay"],
["https://www.pangujiexi.com/jiexi/?url=", "盘古解析"],
["https://jx.hls.one/?url=", "hls解析"],
["https://jx.973973.xyz/?url=", "973播放"],
["https://jx.nnxv.cn/tv.php?url=", "七哥解析"],
["https://jx.2s0.cn/player/?url=", "极速高清"],
["https://rdfplayer.mrgaocloud.com/player/?url=", "红狐解析"],
["https://jx.m3u8.tv/jiexi/?url=", "M3U8"],
["https://www.pouyun.com/?url=", "剖云解析"],
["https://www.playm3u8.cn/jiexi.php?url=", "playm3u8"],
["https://yparse.ik9.cc/?url=", "ik9云解析"],
["https://xiaoapi.cn/API/jx_txsp.php?url=", "腾讯API解析"],
["https://xiaoapi.cn/API/jx_yk.php?url=", "优酷API解析"],
["https://xiaoapi.cn/API/zs_ewm.php?msg=", "网址生成二维码"],
["https://bd.jx.cn/?url=", "+"],
["set", "• • •"]
])
};
let floatingButton, clickTimer, idleTimer;
const createStyle = (id, content) => {
const style = document.createElement('style');
if (id) style.id = id;
style.textContent = content;
document.head.appendChild(style);
};
createStyle('base-style', `.floating-button{position:fixed;z-index:999999;cursor:pointer;transition:all 0.3s ease;box-shadow:0 0 ${CONFIG.glowSize}px ${CONFIG.glowSize}px ${CONFIG.breatheColors[0]}}.source-list{position:fixed;border-radius:16px;border:1px solid rgba(255,255,255,0.1);box-shadow:0 2px 10px rgba(0,0,0,0.2);z-index:999999;padding:1px;min-width:150px;font-family:"Microsoft YaHei",sans-serif}.source-item{padding:4px 20px;border-radius:50px;cursor:pointer;white-space:nowrap;transition:all 0.2s;text-align:center}@keyframes breathe{0%{box-shadow:0 0 ${CONFIG.glowSize}px ${CONFIG.glowSize}px ${CONFIG.breatheColors[0]}}25%{box-shadow:0 0 ${CONFIG.glowSize}px ${CONFIG.glowSize}px ${CONFIG.breatheColors[1]}}50%{box-shadow:0 0 ${CONFIG.glowSize}px ${CONFIG.glowSize}px ${CONFIG.breatheColors[2]}}75%{box-shadow:0 0 ${CONFIG.glowSize}px ${CONFIG.glowSize}px ${CONFIG.breatheColors[1]}}100%{box-shadow:0 0 ${CONFIG.glowSize}px ${CONFIG.glowSize}px ${CONFIG.breatheColors[0]}}}`);
const updateStyles = () => createStyle('dynamic-styles', `.floating-button{border-radius:${GM_getValue('buttonRadius',CONFIG.buttonRadius)}}.source-list{backdrop-filter:blur(${GM_getValue('listBlur',CONFIG.listBlur)});background:${GM_getValue('listBgColor',CONFIG.listBgColor)};color:${GM_getValue('listColor',CONFIG.listColor)};font-size:${GM_getValue('listFontSize',CONFIG.listFontSize)}}.source-item:hover{background:${GM_getValue('itemHoverBg',CONFIG.itemHoverBg)};color:#FFFFFA;font-weight:bold}.selected{color:${GM_getValue('selectedColor',CONFIG.selectedColor)}!important;font-weight:bold}`);
updateStyles();
const createElement = (tag, props) => {
const el = document.createElement(tag);
Object.entries(props).forEach(([k, v]) => el[k] = v);
return el;
};
const createFloatingButton = () => {
return createElement('img', {
className: 'floating-button',
src: GM_getValue('imageUrl', CONFIG.imageUrl),
style: `right:${GM_getValue('buttonRight',CONFIG.buttonRight)};bottom:${GM_getValue('buttonBottom',CONFIG.buttonBottom)};width:${GM_getValue('buttonSize',CONFIG.buttonSize)}px;height:${GM_getValue('buttonSize',CONFIG.buttonSize)}px;opacity:${GM_getValue('opacity',CONFIG.opacity)};animation:breathe ${GM_getValue('breatheDuration',CONFIG.breatheDuration)}s infinite`
});
};
const createListItem = ([url, name], isSetting) => {
const item = createElement('div', {
className: `source-item${url === CONFIG.parseUrl && !isSetting ? ' selected' : ''}`,
textContent: name
});
if (url === 'set') {
let clickTimer;
item.addEventListener('click', (e) => {
e.stopPropagation();
clearTimeout(clickTimer);
clickTimer = setTimeout(() => {
const input = prompt('输入格式:名称,URL丨示例:解析1,https://jx.hls.one/?url=');
if (input) {
const [n, u] = input.split(',').map(s => s.trim());
if (n && u) {
CONFIG.parseUrls.splice(-2, 0, [u, n]);
GM_setValue('storedParseUrls', CONFIG.parseUrls);
}
}
}, 200);
});
item.addEventListener('dblclick', (e) => {
clearTimeout(clickTimer);
showSettingsPanel();
});
return item;
}
if (!isSetting && url !== 'help') {
item.addEventListener('dblclick', (e) => {
e.stopPropagation();
if (confirm(`确认删除 ${name} 解析源?`)) {
CONFIG.parseUrls = CONFIG.parseUrls.filter(v => v[0] !== url);
GM_setValue('storedParseUrls', CONFIG.parseUrls);
item.remove();
}
});
}
item.addEventListener('click', (e) => {
if (item.dataset.func) return;
e.stopPropagation();
if (!isSetting) {
document.querySelectorAll('.source-item').forEach(i => i.classList.remove('selected'));
item.classList.add('selected');
GM_setValue('selectedParseUrl', CONFIG.parseUrl = url);
}
document.getElementById('parse-source-list')?.remove();
});
return item;
};
const showSettingsPanel = () => {
document.querySelectorAll('#parse-source-list').forEach(p => p.remove());
const panel = createElement('div', {
id: 'parse-source-list',
className: 'source-list',
style: `right:${CONFIG.buttonRight};bottom:calc(${CONFIG.buttonBottom} + ${CONFIG.buttonSize + 10}px)`
});
const settings = [
['parseList', '解析列表'],
['listBgColor', '背景颜色'],
['listBlur', '模糊强度'],
['listFontSize', '字体大小'],
['listColor', '字体颜色'],
['selectedColor', '选中颜色'],
['itemHoverBg', '悬停背景'],
['imageUrl', '更换图标'],
['buttonRadius', '图标圆角'],
['reset', '恢复默认']
];
settings.forEach(([key, label]) => {
const item = createListItem([key, label], true);
item.addEventListener('click', () => {
if (key === 'reset') {
if (confirm('即将清除所有配置!确认重置吗?')) {
GM_deleteValue('storedParseUrls');
GM_deleteValue('selectedParseUrl');
Object.keys(CONFIG).forEach(k => GM_deleteValue(k));
location.reload(true);
}
return;
}
if (key === 'parseList') {
showParseListPanel();
return;
}
const val = prompt(`设置${key} (当前:${GM_getValue(key, CONFIG[key])})`, GM_getValue(key, CONFIG[key]));
if (val !== null) {
GM_setValue(key, val);
updateStyles();
if (key === 'imageUrl') floatingButton.src = val;
}
panel.remove();
});
panel.appendChild(item);
});
const closeHandler = (e) => {
if (!panel.contains(e.target) && e.target !== floatingButton) {
panel.remove();
document.removeEventListener('click', closeHandler);
}
};
document.addEventListener('click', closeHandler);
document.body.appendChild(panel);
};
const showParseListPanel = () => {
document.querySelectorAll('#parse-source-list').forEach(p => p.remove());
const panel = createElement('div', {
id: 'parse-source-list',
className: 'source-list',
style: `
right: ${CONFIG.buttonRight};
bottom: calc(${CONFIG.buttonBottom} + ${CONFIG.buttonSize + 10}px);
max-width: 366px;
max-height: 60vh;
overflow-y: auto;
`
});
const title = createElement('div', {
className: 'source-item',
textContent: '当前解析源(双击删除)',
style: 'font-weight:bold;color: #000;'
});
panel.appendChild(title);
CONFIG.parseUrls
.filter(([url]) => url !== 'set')
.forEach(([url, name]) => {
const item = createElement('div', {
className: 'source-item',
textContent: `${name} : ${url}`,
title: '双击删除此解析源'
});
item.addEventListener('dblclick', () => {
CONFIG.parseUrls = CONFIG.parseUrls.filter(([u]) => u !== url);
GM_setValue('storedParseUrls', CONFIG.parseUrls);
item.remove();
showParseListPanel();
});
panel.appendChild(item);
});
const addForm = createElement('div', {
className: 'source-item',
style: 'display:flex;gap:5px;padding:1px 8px;'
});
const input = createElement('input', {
type: 'text',
placeholder: '格式:名称,URL',
style: 'flex:1;text-align:center;border-radius:18px;border:none;'
});
const addBtn = createElement('button', {
textContent: '+ 添加',
style: 'text-align:center;padding:3px 8px;border-radius:12px;background:#2196F3;color:white;border:none;',
onclick: () => {
const [name, url] = input.value.split(',').map(s => s.trim());
if (name && url) {
CONFIG.parseUrls.unshift([url, name]);
GM_setValue('storedParseUrls', CONFIG.parseUrls);
input.value = '';
showParseListPanel();
}
}
});
addForm.appendChild(input);
addForm.appendChild(addBtn);
panel.appendChild(addForm);
const closeHandler = (e) => {
if (!panel.contains(e.target) && e.target !== floatingButton) {
panel.remove();
document.removeEventListener('click', closeHandler);
}
};
document.addEventListener('click', closeHandler);
document.body.appendChild(panel);
};
const initButtonEvents = () => {
floatingButton.addEventListener('mouseenter', () => {
floatingButton.style.opacity = '1';
floatingButton.style.transform = 'scale(1.1)';
});
floatingButton.addEventListener('mouseleave', () => {
floatingButton.style.opacity = GM_getValue('opacity', CONFIG.opacity);
floatingButton.style.transform = 'none';
});
floatingButton.addEventListener('click', () => {
clearTimeout(clickTimer);
clickTimer = setTimeout(() => window.open(GM_getValue('parseUrl', CONFIG.parseUrl) + location.href), 250);
});
floatingButton.addEventListener('dblclick', () => {
clearTimeout(clickTimer);
document.querySelectorAll('#parse-source-list').forEach(p => p.remove());
const list = createElement('div', {
id: 'parse-source-list',
className: 'source-list',
style: `
right: ${CONFIG.buttonRight};
bottom: calc(${CONFIG.buttonBottom} + ${CONFIG.buttonSize + 10}px);
`
});
CONFIG.parseUrls.forEach(item => list.appendChild(createListItem(item)));
const closeHandler = (e) => {
if (!list.contains(e.target) && e.target !== floatingButton) {
list.remove();
document.removeEventListener('click', closeHandler);
}
};
document.addEventListener('click', closeHandler);
document.body.appendChild(list);
});
const initIdleMode = () => {
const enterIdle = () => {
floatingButton.style.opacity = '0.8';
floatingButton.style.animationPlayState = 'paused';
};
const exitIdle = () => {
floatingButton.style.opacity = GM_getValue('opacity', CONFIG.opacity);
floatingButton.style.animationPlayState = 'running';
};
const resetTimer = () => {
clearTimeout(idleTimer);
exitIdle();
idleTimer = setTimeout(enterIdle, 10000);
};
['mouseenter', 'click', 'dblclick'].forEach(evt =>
floatingButton.addEventListener(evt, resetTimer)
);
resetTimer();
};
initIdleMode();
};
const init = () => {
floatingButton = createFloatingButton();
document.body.appendChild(floatingButton);
initButtonEvents();
};
document.readyState === 'loading' ? document.addEventListener('DOMContentLoaded', init) : init();
})();