1.购买服务器阿里云:服务器购买地址https://t.aliyun.com/U/E8o0aM若失效,可用地址
阿里云:
服务器购买地址
https://t.aliyun.com/U/E8o0aM
若失效,可用地址
https://www.aliyun.com/daily-act/ecs/activity_selection?source=5176.29345612&userCode=49hts92d
腾讯云:
https://curl.qcloud.com/wJpWmSfU
若失效,可用地址
https://cloud.tencent.com/act/cps/redirect?redirect=2446&cps_key=ad201ee2ef3b771157f72ee5464b1fea&from=console
华为云
https://activity.huaweicloud.com/cps.html?fromacct=64b5cf7cc11b4840bb4ed2ea0b2f4468&utm_source=V1g3MDY4NTY=&utm_medium=cps&utm_campaign=201905
2.部署教程
3.代码如下
// 目前脚本在vm和tm中均测试通过,且unsafeWindow都指向页面window,而window都是包装过的Proxy,无法获取页面window的三方属性如window.jQuery、window.HUYA_*等
// 此时要么使用unsafeWindow.jQuery,要么直接访问全局属性(如脚本中直接使用 $ )
window.addEventListener('DOMContentLoaded', () => {
'use strict';
// 禁止篡改console
// Object.freeze(console);
if (GM_getValue('autoBestRESDelay') !== undefined) GM_deleteValue('autoBestRESDelay'); // 在vm上与GM_setValue(key)作用相同
const getById = (id) => document.getElementById(id);
if (!getById('liveRoomObj')) return;
// 更改菜单选项
function changeMenuItem(id, onCaption, offCaption, onClick) {
const inplace = id === GM_registerMenuCommand(offCaption, null, { id });
if (inplace) {
// 插件如VM2.15.9起支持根据id注册选项
GM_registerMenuCommand(onCaption, onClick, { id });
} else {
// 不支持需先卸载原选项,再注册新选项
GM_unregisterMenuCommand(offCaption);
GM_registerMenuCommand(onCaption, onClick);
}
}
// 注册可变菜单项
function registerToggle(id, onCaption, offCaption, onClick) {
changeMenuItem(id, onCaption, offCaption, () => {
onClick();
registerToggle(id, offCaption, onCaption, onClick);
});
}
// 注册常规菜单项
/* function addMenuItem(item) {
if (item.id !== GM_registerMenuCommand(item.title, () => item.click(), { id: item.id })) {
GM_unregisterMenuCommand(item.title);
GM_registerMenuCommand(item.title, () => item.click());
}
} */
/* function addModal(title, body, persist) {
const dialog = document.createElement('dialog');
dialog.innerHTML = `<div><p>${title}</p>${body}<div style='text-align:right'><button>确定</button> <button>取消</button></div></div>`;
const btns = dialog.querySelectorAll('button');
dialog.children[0].onclick = (e) => e.stopPropagation();
dialog.onclick = btns[1].onclick = () => {
dialog.oncancel?.();
dialog.close();
};
btns[0].onclick = () => {
dialog.onconfirm?.();
dialog.close();
};
Object.defineProperty(dialog, 'title', {
get() {
return this.children[0].children[0].textContent;
},
set(text) {
this.children[0].children[0].innerText = text;
}
});
if (!persist) {
dialog.onclose = () => dialog.remove();
}
document.body.append(dialog);
return dialog;
} */
/* const Modal = addModal('', '<input type=number min=0 step=.5 />', true);
Modal.onclose = () => {
Modal.querySelector('input').value = '';
}; */
// 待注册
const toggles = [
{
id: 1,
title: '自动最高画质',
gmKey: 'autoBestRES',
gmValue: GM_getValue('autoBestRES')
},
{
id: 2,
title: '单击/空格控制播放/暂停',
gmKey: 'clickToPlay',
gmValue: GM_getValue('clickToPlay', true)
},
{
id: 3,
title: '中键/回车切换全屏(火狐限制左键触发全屏',
gmKey: 'midClickToFullscreen',
gmValue: GM_getValue('midClickToFullscreen', true)
},
{
id: 4,
title: '自动剧场模式',
gmKey: 'autoFullPage',
gmValue: GM_getValue('autoFullPage')
},
{
id: 5,
title: '屏蔽视频下方礼物栏',
gmKey: 'hideGiftBar',
gmValue: GM_getValue('hideGiftBar'),
click() {
if (this.gmValue) {
this.css = GM_addStyle(
'#player-ctrl-wrap:not(.showup){opacity:0}#player-gift-wrap{visibility:hidden;}#player-wrap{min-height:100%}#player-ctrl-wrap{bottom:0!important}'
);
getById('player-ctrl-wrap').classList.add('showup');
return;
}
getById('player-ctrl-wrap').classList.remove('showup');
this.css?.remove();
}
}
];
toggles.forEach((e) => {
registerToggle(
e.id,
(e.gmValue ? '✔️' : '✖️') + e.title,
(!e.gmValue ? '✔️' : '✖️') + e.title,
() => {
e.gmValue = !e.gmValue;
e.click?.(e.gmValue);
GM_setValue(e.gmKey, e.gmValue);
}
);
});
/* const menu = [
// {
// id: 6,
// title: '自动切换画质延迟',
// gmKey: 'autoBestRESDelay',
// gmValue: GM_getValue('autoBestRESDelay', 0),
// click() {
// Modal.title = '调整自动切换画质的延迟,单位:秒(s),留空无延迟\n当前延迟:' + this.gmValue;
// Modal.showModal();
// Modal.onconfirm = () => {
// const delay = +Modal.querySelector('input').value;
// this.gmValue = isNaN(delay) ? 0 : delay;
// GM_setValue(this.gmKey, this.gmValue);
// };
// }
// }
];
menu.forEach((e) => {
addMenuItem(e);
}); */
if (toggles[3].gmValue) {
document.body.classList.add('mode-page-theater');
}
// 隐藏进入页面后的登录弹窗。 ← 貌似不需要了,虎牙不会立马显示登录框了
new MutationObserver((mutations, ob) => {
// const mask = getById('HUYA-UDBSdkLgn');
// if (!mask) return;
const video = getById('hy-video');
if (!video) return;
if (toggles[4].gmValue) {
toggles[4].click();
}
const $vtList = $('#player-ctrl-wrap .player-videotype-list'),
unlockRES = () => {
const $highRes = $vtList.children(':has(.bitrate-right-btn.common-enjoy-btn)');
$highRes.length
? $highRes.each((i, e) => {
$(e).data('data').status = 0;
// autoBestRES
i === 0 && toggles[0].gmValue && e.click(); // setTimeout(() => e.click(), menu[0].gmValue * 1000);
})
: toggles[0].gmValue && $vtList.children().length > 1 && $vtList.children()[0].click();
};
// 插入登录框后则只监听该元素的变更。
/* new MutationObserver((records, mob) => {
if (mask?.style.display !== 'none') {
mask.style.display = 'none';
mob.disconnect();
}
}).observe(mask, {
attributes: true
}); */
// 无限制播放,避免严格模式下对getter属性赋值导致异常中断
// try {
if (toggles[3].gmValue) {
const pfBtn = getById('player-fullpage-btn');
const tid = setInterval(() => {
pfBtn.classList.contains('player-narrowpage') ? clearInterval(tid) : pfBtn.click();
}, 500);
// getById('player-fullpage-btn').className="player-narrowpage"
// getById('player-fullpage-btn').title="退出剧场"
}
// getById('hy-video').srcObject.active = false;
// } catch (e) {
// // alert('尝试无限制播放失败,可能需要刷新页面或切换线路。异常:\n' + e)
// }
ob.disconnect();
// unlock res
new MutationObserver(unlockRES).observe($vtList[0], {
attributes: false,
childList: true,
subtree: false
});
unlockRES();
// 添加部分播放器事件
setTimeout(() => {
const vid = mutations[0].target; // 此处观察的节点即getById('hy-video')
let flag = null,
tid = null;
getById('player-mouse-event-wrap').onmousemove = function () {
if (flag) return;
flag = true;
clearTimeout(tid);
this.style.cursor = '';
if (toggles[4].gmValue) {
getById('player-ctrl-wrap').classList.add('showup');
}
tid = setTimeout(() => {
this.style.cursor = 'none';
if (toggles[4].gmValue) {
getById('player-ctrl-wrap').classList.remove('showup');
}
}, 5000);
setTimeout(() => {
flag = null;
}, 1000);
};
// 单击/空格控制播放/暂停
if (toggles[1].gmValue) {
let isOneClick, tmp, tid;
// 判断是否触发虎牙播放器单击模拟的双击
vid.addEventListener('click', () => {
isOneClick = !tmp;
if (isOneClick) {
tmp = setTimeout(() => {
tmp = null;
}, 301);
}
});
vid.onclick = () => {
clearTimeout(tid);
const arr = ['smartMenu_videoMenu', 'player-danmu-report'];
if (arr.every((e) => !(getById(e)?.style.display === 'block'))) {
tid = setTimeout(() => {
isOneClick && getById('player-btn').click();
}, 300);
}
};
document.addEventListener('keyup', (e) => {
if (e.code === 'Space' && !'INPUT TEXTAREA'.includes(e.target.nodeName)) {
e.preventDefault();
getById('player-btn').click();
}
});
}
// 中键/回车切换全屏
if (toggles[2].gmValue) {
vid.onmousedown = (e) => e.preventDefault();
vid.onauxclick = (e) => {
e.button === 1 && getById('player-fullscreen-btn').click();
};
document.addEventListener('keyup', (e) => {
e.key === 'Enter' &&
!'INPUT TEXTAREA'.includes(e.target.nodeName) &&
getById('player-fullscreen-btn').click();
});
}
});
}).observe(getById('player-video'), {
attributes: false,
childList: true,
subtree: false
});
// 观察节点自动点击播放模式
// function autoPlay() {
// GM_addStyle("div#UDBSdkLgn{z-index: -1;}");
// const targetNode =
// getById("player-ctrl-wrap").querySelector(".player-play-big");
// new MutationObserver((mutationsList, ob) => {
// // console.log(mutationsList)
// if (
// mutationsList[0].type !== "attributes" ||
// targetNode.style.display === "none"
// )
// return;
// const mask = getById("UDBSdkLgn");
// if (mask.style.display === "block") {
// targetNode.click();
// mask.style.display = "none";
// // console.log('自动续播成功')
// }
// }).observe(targetNode, {
// attributes: true,
// childList: false,
// subtree: false,
// });
// }
});
解析
这是一款面向虎牙直播页面(huya.com/*
)的脚本,用来在未登录情况下改善观看体验。核心改动包括:
自动切换最高画质(尽量解锁被登录限制的清晰度选项)。
支持单击/空格控制播放暂停、中键/回车切换全屏(兼容火狐限制)。
自动剧场模式(页面内大屏)。
隐藏礼物栏/控制条自动显隐以提升观感。
提供油猴菜单里的开关项,可随时启用/停用以上功能,状态持久化到
GM_*
存储。
注意:脚本专门优化"未登录观看"。如果你需要登录互动,不建议使用。
主要方法
环境/注入说明(顶部注释)
解释@grant
/ sandbox /unsafeWindow
在不同管理器(VM/TM)下的差异;本脚本依赖直接访问页面全局对象(如$
、HUYA 变量)时的注意事项。入口:
window.addEventListener('DOMContentLoaded', …)
DOM 就绪后开始执行;若未找到播放器容器#liveRoomObj
则直接返回。菜单注册工具
autoBestRES
:自动最高画质。clickToPlay
:单击/空格 控制播放暂停(默认开)。midClickToFullscreen
:中键/回车 全屏(默认开)。autoFullPage
:自动剧场模式。hideGiftBar
:隐藏视频下方礼物栏(附带插入 CSS/恢复逻辑)。changeMenuItem(id, onCaption, offCaption, onClick)
/registerToggle(...)
动态创建"可切换"的菜单项(✔/✖ 图标切换),并在点击时反转开关值、调用回调、写入GM_setValue
。toggles
数组(功能开关清单):初始化时为每个开关调用
registerToggle
,把当前状态展示到油猴菜单。剧场模式初始化
若
autoFullPage
为真,给body
添加mode-page-theater
,进入剧场布局。主 MutationObserver(监听播放器装载)
观察#player-video
子节点变化,等#hy-video
(video 标签)出现后:控制条显隐与鼠标指针自动隐藏(5s 无操作隐藏,移动时显示)。
clickToPlay
:midClickToFullscreen
:处理虎牙自身的单击/双击识别冲突(300ms 内判定);
单击空白(无菜单/举报框显示时)触发
#player-btn
播放/暂停;监听键盘
Space
(非输入框下)触发播放/暂停。禁止默认中键行为,
auxclick
中键触发#player-fullscreen-btn
;键盘
Enter
(非输入框下)也触发全屏。若有"享受按钮"项,优先点击第一项;
若列表长度>1 且找不到标记项,退化为点击第一项。
该函数既在初次执行,也通过新的MutationObserver
持续监听清晰度列表变化自动执行。若启用"隐藏礼物栏",注入/移除对应 CSS 并控制底部条显隐。
解锁画质
unlockRES()
:
寻找清晰度列表#player-ctrl-wrap .player-videotype-list
,把高码率项的状态改为可选;自动剧场模式点击:循环点击"剧场模式"按钮
#player-fullpage-btn
直到生效。播放器交互事件:
样式注入(可选)
GM_addStyle(...)
在开启"隐藏礼物栏"时注入:隐藏礼物区、控制条透明、播放器高度等。
总结:
脚本通过 DOM 观察 + 菜单化开关,把虎牙未登录的观看障碍(画质、控制方式、弹条干扰)"一网打尽",默认就能用。
注意:
本文部分变量已做脱敏处理,仅用于测试和学习研究,禁止用于商业用途,不能保证其合法性,准确性,完整性和有效性,请根据情况自行判断。技术层面需要提供帮助,可以通过打赏的方式进行探讨。
没有评论:
发表评论