// ==UserScript==
// @name 虎牙免登录观看
// @description 优化未登录时的观看体验,若需要登录请勿使用!!
// @author (σ`д′)σ
// @version 1.8.1
// @namespace https://greasyfork.org/zh-CN/scripts/477947
// @license GPL-3.0-or-later
// @match https://www.huya.com/*
// @exclude https://www.huya.com/
// @run-at document-start
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @grant GM_addStyle
// @supportURL https://greasyfork.org/zh-CN/scripts/477947
// @homepageURL https://greasyfork.org/zh-CN/scripts/477947
// @downloadURL https://update.greasyfork.icu/scripts/477947/%E8%99%8E%E7%89%99%E5%85%8D%E7%99%BB%E5%BD%95%E8%A7%82%E7%9C%8B.user.js
// @updateURL https://update.greasyfork.icu/scripts/477947/%E8%99%8E%E7%89%99%E5%85%8D%E7%99%BB%E5%BD%95%E8%A7%82%E7%9C%8B.meta.js
// ==/UserScript==
/*
* 在vm中,未使用@grant或者@grant none,则sandbox不生效,此时window与unsafeWindow均指向页面window
* 若指定了@grant,则sandbox生效,此时window是页面window的代理,可访问自带属性但无法获取三方属性,unsafeWindow指向页面window
* 在vm中,@inject-into默认值为 auto,优先尝试注入到 page,再尝试 content
* 若@inject-into content,则依旧可访问页面DOM,但unsafeWindow指向content script的全局对象,故找不到页面window上的三方属性,如 HUYA_*
*/
/*
* 在tampermonkey中,若未设置@sandbox,则sandbox默认值为 raw,表示脚本优先注入到页面环境中
* 当设置@sandbox DOM时,则只能访问页面DOM
* 或许需要设置@grant unsafeWindow以访问unsafeWindow。若设置@grant none,则sandbox不生效
*/
// 目前脚本在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 = `
`;
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('', '', 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,
// });
// }
});