// ==UserScript==
// @name 视频网HTML5播放小工具
//@description 需要配合油猴插件使用
// @homepage https://bbs.kafan.cn/thread-2093014-1-1.html
// @include https://*.qq.com/*
// @exclude https://user.qzone.qq.com/*
// @include https://www.weiyun.com/video_*
// @include https://www.youku.com/
// @include https://v.youku.com/v_show/id_*
// @include https://vku.youku.com/live/*
// @include https://video.tudou.com/v/*
// @include https://www.bilibili.com/*
// @include https://www.acfun.cn/*
// @include http://v.pptv.com/show/*
// @include https://tv.sohu.com/v/*
// @include https://film.sohu.com/album/*
// @include https://www.mgtv.com/*
// @include *://www.fun.tv/vplay/*
// @include *://m.fun.tv/*
// @include *://*.mtime.com/*
// @include *://www.miaopai.com/*
// @version 1.5.3
// @include *://*.163.com/*
// @include *://*.icourse163.org/*
// @include *://*.sina.com.cn/*
// @include *://video.sina.cn/*
// @include *://weibo.com/*
// @include *://*.weibo.com/*
// @include https://pan.baidu.com/*
// @include https://yun.baidu.com/*
// @include *://v.yinyuetai.com/video/h5/*
// @include *://v.yinyuetai.com/playlist/h5/*
// @include *://www.365yg.com/*
// @include *://v.ifeng.com/*
// @GM_info
// @include https://www.youtube.com/watch?v=*
// @include https://www.ted.com/talks/*
// @noframes
// @include *://www.yy.com/*
// @include *://v.huya.com/play/*
// @include https://www.huya.com/*
// @include https://v.douyu.com/*
// @include https://www.douyu.com/*
// @include *://star.longzhu.com/*
// @include https://www.zhanqi.tv/*
// @include https://www.yunbtv.com/vodplay/*
// @include http://www.dililitv.com/vplay/*
// @include http://www.dililitv.com/gresource/*?Play=*
// @include http://www.vtuapp.com/tv-play-*
// @include https://www.66s.cc/*/play/*
// @grant unsafeWindow
// @grant GM_addStyle
// @grant GM_registerMenuCommand
// @grant GM_setValue
// @grant GM_getValue
// @run-at document-start
// @namespace https://greasyfork.org/users/7036
// ==/UserScript==
‘use strict’;
const isEdge = navigator.userAgent.includes(‘Edge’);
const w = isEdge ? window : unsafeWindow;
const observeOpt = {childList : true, subtree : true};
const q = (css, p = document) => p.querySelector(css);
const delElem = e => e.remove();
const $$ = (c, cb = delElem, doc = document) => {
if (!c || !c.length) return;
if (typeof c === ‘string’) c = doc.querySelectorAll(c);
if (!cb) return c;
for (let e of c) {
if (e && cb(e)===false) break;
}
};
const gmFuncOfCheckMenu = (title, saveName, defaultVal = true) => {
const r = GM_getValue(saveName, defaultVal);
if (r) title += ‘ √’;
GM_registerMenuCommand(title, () => {
GM_setValue(saveName, !r);
location.reload();
});
return r;
};
const r1 = (regp, s) => regp.test(s) && RegExp.$1;
const log = console.log.bind(console, ‘%c脚本[%s] 反馈:%sn%s’, ‘color:#c3c;font-size:1.5em’,
GM_info.script.name, GM_info.script.homepage);
const sleep = ms => new Promise(resolve => {
setTimeout(resolve, ms);
});
const getStyle = (o, s) => {
if (o.style[s]) return o.style[s];
if (getComputedStyle) {//DOM
var x = getComputedStyle(o, ”);
//s = s.replace(/([A-Z])/g,’-$1′).toLowerCase();
return x && x.getPropertyValue(s);
}
};
const doClick = e => {
if (typeof e === ‘string’) e = q(e);
if (e) { e.click ? e.click() : e.dispatchEvent(new MouseEvent(‘click’)) };
};
const firefoxVer = r1(/Firefox/(d+)/, navigator.userAgent);
const fakeUA = ua => Object.defineProperty(navigator, ‘userAgent’, {
value: ua,
writable: false,
configurable: false,
enumerable: true
});
const getMainDomain = host => {
const re = /com|tv|net|org|gov|edu/;
const a = host.split(‘.’);
let i = a.length – 2;
if (re.test(a[i])) i–;
return a[i];
};
const ua_chrome = ‘Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3626.121 Safari/537.36’;
const ua_ipad2 = ‘Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3’;
class FullScreen {
constructor(video) {
this._video = video;
const d = document, e = video;
this._exitFn = d.exitFullscreen || d.webkitExitFullscreen || d.mozCancelFullScreen || d.msExitFullscreen;
this._enterFn = e.requestFullscreen || e.webkitRequestFullScreen || e.mozRequestFullScreen || e.msRequestFullScreen;
}
static isFull() {
const d = document;
return !!(d.fullscreen || d.webkitIsFullScreen || d.mozFullScreen ||
d.fullscreenElement || d.webkitFullscreenElement || d.mozFullScreenElement);
}
enter() {
this._enterFn && this._enterFn.call(this._video);
}
exit() {
this._exitFn && this._exitFn.call(document);
}
toggle() {
FullScreen.isFull() ? this.exit() : this.enter();
}
}
//万能网页全屏, CSS代码参考了:https://github.com/gooyie/ykh5p
class FullPage {
constructor(video, isFixView, onSwitch) {
this._video = video;
this._isFixView = isFixView;
this._isFull = !1;
this._onSwitch = onSwitch;
this._rects = new Map();
this._checkContainer();
GM_addStyle(
`.z-top {
position: relative !important;
z-index: 23333333 !important;
}
.webfullscreen {
display: block !important;
position: fixed !important;
width: 100% !important;
height: 100% !important;
top: 0 !important;
left: 0 !important;
background: #000 !important;
z-index: 23333333 !important;
}`
);
}
getPlayerContainer(video) {
let e = video, p = e.parentNode;
const { clientWidth: wid, clientHeight: h } = e;
for (;;) {
this._rects.set(e, {
width: e.clientWidth + ‘px’,
height: e.clientHeight + ‘px’
});
if (e.style.maxWidth) e.style.maxWidth = ‘100%’;
if (e.style.maxHeight) e.style.maxHeight = ‘100%’;
if (p==document.body || p.clientWidth-wid >3 || p.clientHeight-h >3) return e;
e = p;
p = e.parentNode;
}
}
_checkContainer() {
const c = this._container, e = this._video;
if (!c || c === e) this._container = this.getPlayerContainer(e);
}
get container() {
this._checkContainer();
return this._container;
}
fixView() {
if (!this._isFixView && !this._isFull) return;
if (this._video === this._container) return;
for (let [e, v] of this._rects) {
if (this._isFull) {
e.style.width = e.style.height = ‘100%’;
} else {
e.style.width = v.width;
e.style.height = v.height;
}
}
}
static isFull(e) {
return w.innerWidth – e.clientWidth <5 && w.innerHeight – e.clientHeight <5;
}
toggle() {
const cb = this._onSwitch;
if (!this._isFull && cb) cb(true);
const d = document.body;
d.style.overflow = this._isFull ? ” : ‘hidden’;
this.container.classList.toggle(‘webfullscreen’);
let p = this.container.parentNode;
while (p !== d) {
p.classList.toggle(‘z-top’);
p = p.parentNode;
}
this._isFull = !this._isFull;
setTimeout(this.fixView.bind(this), 9);
if (!this._isFull && cb) setTimeout(cb, 199, !1);
}
}
let v, _fp, _fs;
const { host, pathname: path } = location;
const u = getMainDomain(host);
const events = {
on(name, fn) {
this[name] = fn;
}
};
const app = {
isLive: !1,
disableSpace: !1,
multipleV: !1, //单页面多视频
isFixFPView: !1, //退出网页全屏时是否修正DOM视图
checkMVVisible() {
if (this.vList.length < 2 || v.offsetWidth>1) return v;
let e = this._findList(k => k.offsetWidth>1);
if (e) {
e.playbackRate = v.playbackRate;
e.volume = v.volume;
}
v = e;
return v;
},
checkDPlayer() {
if (v && !this.DPlayer_el && v.closest(‘.dplayer-video-wrap’)) {
this.DPlayer_el = v.closest(‘.dplayer’);
_fp = new FullPage(v, this.isFixFPView, this.switchFP);
_fs = null;
this.btnFS = q(‘.dplayer-full-icon’, this.DPlayer_el);
this.fullScreen = () => this._convertButton(this.btnFS);
this.btnWFS = null;
this.btnPlay = null;
this.btnNext = null;
}
return this.DPlayer_el;
},
_convertButton(btn) {
(!btn.nextSibling || btn.clientWidth >1 || getStyle(btn, ‘display’) !== ‘none’) ?
doClick(btn) : doClick(btn.nextSibling);
},
hotKey(e) {
if (e.ctrlKey || e.altKey || e.target.contentEditable==’true’ ||
/INPUT|TEXTAREA|SELECT/.test(e.target.nodeName)) return;
if (e.shiftKey && ![13,37,39].includes(e.keyCode)) return;
if (this.isLive && [37,39,78,88,67,90].includes(e.keyCode)) return;
if (this.extPlayer && this.extPlayer.contains(e.target) &&
[32,37,39].includes(e.keyCode)) return;
if (!this.checkMVVisible()) return;
!this.checkDPlayer() && this.checkUI();
if (events.keydown && events.keydown(e)) return;
let n;
switch (e.keyCode) {
case 32: //space
if (this.disableSpace) return;
if (e.shiftKey) {
v.scrollIntoView();
v.focus();
} else {
if (this.btnPlay) this.play();
else v.paused ? v.play() : v.pause();
}
e.preventDefault();
break;
case 37: n = e.shiftKey ? -20 : -5; //left 快退5秒,shift加速
case 39: //right
n = n || (e.shiftKey ? 20 : 5); //快进5秒,shift加速
v.currentTime += n;
break;
//case 80: // P 上一首
case 78: // N 下一首
this.btnNext && getStyle(this.btnNext, ‘display’) !== ‘none’ && doClick(this.btnNext);
break;
case 38: n = 0.1; //加音量
case 40: //降音量
n = n || -0.1;
n += v.volume;
if (0 <= n && n <= 1) v.volume = n;
e.preventDefault();
break;
case 13: //回车键。 全屏
if (e.shiftKey) {
_fp ? _fp.toggle() : this.fullPage();
} else {
_fs ? _fs.toggle() : this.fullScreen();
}
break;
case 27: //esc
if (FullScreen.isFull()) {
_fs ? _fs.exit() : this.fullScreen();
} else if (FullPage.isFull(v)) {
_fp ? _fp.toggle() : this.fullPage();
}
break;
case 67: n = 0.1; //按键C:加速播放 +0.1
case 88: //按键X:减速播放 -0.1
n = n || -0.1;
n += v.playbackRate;
if (0 < n && n <= 16) v.playbackRate = n;
break;
case 90: //按键Z:正常速度播放
v.playbackRate = 1;
break;
case 70: n = 0.03; //按键F:下一帧
case 68: //按键D:上一帧
n = n || -0.03;
if (!v.paused) v.pause();
v.currentTime += n;
break;
default: return;
}
e.stopPropagation();
},
checkUI() {
if (this.webfullCSS && !this.btnWFS) this.btnWFS = q(this.webfullCSS);
if (this.btnWFS) {
this.fullPage = () => this._convertButton(this.btnWFS);
if (_fp) _fp = null;
} else {
_fp = _fp || new FullPage(v, this.isFixFPView, this.switchFP);
}
if (this.fullCSS && !this.btnFS) this.btnFS = q(this.fullCSS);
if (!this.btnFS) {
_fs = _fs || new FullScreen(v);
} else {
this.fullScreen = () => this._convertButton(this.btnFS);
if (_fs) _fs = null;
}
if (this.nextCSS && !this.btnNext) this.btnNext = q(this.nextCSS);
if (this.playCSS && !this.btnPlay) this.btnPlay = q(this.playCSS);
if (this.btnPlay) this.play = () => this._convertButton(this.btnPlay);
},
switchFP(toFull) {
if (toFull) {
for (let e of this.vSet) this.viewObserver.unobserve(e);
} else {
for (let e of this.vList) this.viewObserver.observe(e);
}
},
onGrowVList() {
if (this.vList.length > this.vCount) {
if (this.viewObserver) {
for (let e of this.vList) {
if (!this.vSet.has(e)) this.viewObserver.observe(e);
}
} else {
const config = {
rootMargin: ‘0px’,
threshold: 0.9
};
this.viewObserver = new IntersectionObserver(this.onIntersection.bind(this), config);
for (let e of this.vList) this.viewObserver.observe(e);
}
this.vSet = new Set(this.vList);
this.vCount = this.vList.length;
}
},
onIntersection(entries) {
for (let entry of entries) {
if (entry.isIntersecting && v != entry.target) {//intersectionRatio
v = entry.target;
_fs = new FullScreen(v);
_fp = new FullPage(v, this.isFixFPView, this.switchFP);
events.switchMV && events.switchMV();
break;
}
}
},
bindEvent() {
log(‘bind eventn’, v);
events.foundMV && events.foundMV();
const fn = ev => {
events.canplay && events.canplay();
v.removeEventListener(‘canplaythrough’, fn);
};
if (v.readyState > 3) fn();
else v.addEventListener(‘canplaythrough’, fn);
document.body.addEventListener(‘keydown’, this.hotKey.bind(this));
if (this.extPlayerCSS) {
this.extPlayer = v.closest(this.extPlayerCSS);
this.extPlayer && this.extPlayer.addEventListener(‘keydown’, this.hotKey.bind(this));
}
if (this.multipleV) {
new MutationObserver(this.onGrowVList.bind(this))
.observe(document.body, observeOpt);
this.vCount = 0;
this.onGrowVList();
}
},
findMV() {
return !this.cssMV ? this.vList[0] : this._findList(e => e.matches(this.cssMV));
},
init() {
this.switchFP = this.multipleV ? this.switchFP.bind(this) : null;//多视频页面
this.vList = document.getElementsByTagName(‘video’);
this._findList = [].find.bind(this.vList);
const t = setInterval(() => {
if (v = this.findMV()) {
clearInterval(t);
$$(this.adsCSS);
this.bindEvent();
}
}, 300);
}
};
let router = {
youtube() {
app.fullCSS = ‘.ytp-fullscreen-button’;
app.webfullCSS = ‘.ytp-size-button’;
app.nextCSS = ‘.ytp-next-button’;
app.extPlayerCSS = ‘#ytp-player’;
},
ted() {
app.fullCSS = ‘button[title=”Enter Fullscreen”]’;
app.playCSS = ‘button[title=”play video”]’;
const forceHD = gmFuncOfCheckMenu(‘TED强制高清’, ‘ted_forceHD’);
const getHDSource = async () => {
const pn = r1(/^(/talks/w+)/, path);
const resp = await fetch(`https://www.ted.com${pn}/metadata.json`);
const data = await resp.json();
return data.talks[0].downloads.nativeDownloads.high;
};
const check = async (rs) => {
if (!v.src || v.src.startsWith(‘http’)) return;
$$(app.vList, e => { e.removeAttribute(‘src’) }); // 取消多余的媒体资源请求
try {
v.src = await getHDSource();
} catch(ex) {
console.error(ex);
}
};
forceHD && events.on(‘foundMV’, () => {
new MutationObserver(check).observe(v, {
attributes: true,
attributeFilter: [‘src’]
});
check();
});
},
qq() {
Object.assign(app, {
nextCSS: ‘.txp_btn_next’,
webfullCSS: ‘.txp_btn_fake’,
fullCSS: ‘.txp_btn_fullscreen’,
extPlayerCSS: ‘#mod_player’
});
},
youku() {
if (host.startsWith(‘vku.’)) {
events.on(‘canplay’, () => {
app.isLive = !q(‘.spv_progress’);
});
app.fullCSS = ‘.live_icon_full’;
} else {
events.on(‘foundMV’, () => {
app.btnFS = q(app.fullCSS);
if (!app.btnFS) {//使用了优酷播放器YAPfY扩展
app.webfullCSS = ‘.ABP-Web-FullScreen’;
app.fullCSS = ‘.ABP-FullScreen’;
app.nextCSS = ‘.ABP-Next’;
} else {
w.$(‘.settings-item.disable’).replaceWith(‘<div data-val=1080p class=settings-item data-eventlog=xsl>1080p</div>’);
}
});
app.fullCSS = ‘.control-fullscreen-icon’;
app.nextCSS = ‘.control-next-video’;
app.extPlayerCSS = ‘#player’;
}
},
bilibili() {
app.nextCSS = ‘.bilibili-player-video-btn-next’;
app.webfullCSS = ‘.bilibili-player-video-web-fullscreen’;
app.fullCSS = ‘.bilibili-player-video-btn-fullscreen’;
app.extPlayerCSS = ‘#playerWrap’;
const danmu = gmFuncOfCheckMenu(‘弹幕’, ‘bili_danmu’);
const autoPlay = gmFuncOfCheckMenu(‘自动播放’, ‘bili_autoPlay’);
const _setPlayer = () => {
const x = q(‘.bilibili-player-video-danmaku-switch input’);
if (!x) return setTimeout(_setPlayer, 300);
if (x.checked != danmu) x.click();
if (v.paused == autoPlay) autoPlay ? v.play() : v.pause();
v.focus();
doClick(‘i.bilibili-player-iconfont-repeat.icon-24repeaton’); //关循环播放
};
const setPlayer = () => {
const x = app.findMV();
if (!x || x == v || x.readyState < 4) return; //等待视频加载完成
v = x;
app.btnNext = app.btnWFS = app.btnFS = null;
_setPlayer();
};
events.on(‘canplay’, () => {
_setPlayer();
setInterval(setPlayer, 500);
});
},
pptv() {
if (!w.chrome) fakeUA(ua_chrome);
app.fullCSS = ‘.w-zoomIn’;
app.nextCSS = ‘.w-next’;
app.playCSS = ‘.w-play’;
//app.extPlayerCSS = ‘#playerWrap’;
},
sina() {
fakeUA(ua_ipad2);
},
weibo() {
app.isFixFPView = true;
app.multipleV = path.startsWith(‘/u/’);
},
miaopai() {
app.multipleV = path.startsWith(‘/u/’);
},
baidu() {
events.on(‘keydown’, e => {
if (!videojs) return;
let n, p = videojs.getPlayers(“video-player”).html5player;
switch (e.keyCode) {
case 67: n = 0.1;
case 88:
n = n || -0.1;
n += + p.tech_.playbackRate().toFixed(1);
if (0 < n && n <= 16) p.tech_.setPlaybackRate(n);
return true;
case 90:
p.tech_.setPlaybackRate(1);
return true;
}
});
app.extPlayerCSS = ‘.video-content’;
},
mgtv() {
app.nextCSS = ‘mango-control-playnext’;
app.webfullCSS = ‘mango-webscreen’;
app.fullCSS = ‘mango-screen.control-item’;
app.extPlayerCSS = ‘#mgtv-player-wrap’;
},
acfun() {
app.webfullCSS = ‘.fullscreen-web’;
app.fullCSS = ‘.fullscreen-screen’;
},
[‘163’]() {
app.multipleV = host.startsWith(‘news.’);
if (‘open.163.com’ == host) {
app.extPlayerCSS = ‘#playerWrap’;
}
return host.split(‘.’).length > 3;
},
sohu() {
app.nextCSS = ‘li.on[data-vid]+li a’;
app.fullCSS = ‘.x-fullscreen-btn’;
},
mtime() {
fakeUA(ua_ipad2);
events.on(‘foundMV’, () => {
v.preload = ‘auto’;
v.autoplay = true;
});
},
fun() {
if (host.startsWith(‘m.’)) {
if (!path.includes(‘play’)) return true;//非播放页,不执行init()
/^/[mv]/.test(path) && location.assign(path.replace(‘/’, ‘/i’) + location.search);
app.nextCSS = ‘a.btn.next-btn’;
app.fullCSS = ‘a.btn.full-btn’;
GM_addStyle(‘div.p-ip-wrap{overflow-y: auto !important;}’);
return;
}
let vid = r1(/bv-(d+)/, path);
let mid = r1(/bg-(d+)/, path);
//剧集path: /implay/,单视频path: /ivplay/
if (vid) {
mid && location.assign(`//m.fun.tv/implay/?mid=${mid}&vid=${vid}`);
location.assign(‘//m.fun.tv/ivplay/?vid=’+vid);
}
mid && setTimeout(() => {
vid = w.vplay.videoid;
vid && location.assign(`//m.fun.tv/implay/?mid=${mid}&vid=${vid}`);
}, 99);
r1(/bt-(d+)/, path) && setTimeout(() => {
vid = w.vplay.videoid;
location.assign(‘//m.fun.tv/ivplay/?vid=’+vid);
}, 99);
return true;
}
};
app.disableSpace = /youtube|qq|pptv|365yg/.test(u);
if (!router[u]) { //直播站点
router = {
douyu() {
const inRoom = host.startsWith(‘www.’);
events.on(‘canplay’, () => {
setTimeout(doClick, 2e3, ‘.roomSmallPlayerFloatLayout-closeBtn’);
$$(app.adsCSS);
if (inRoom) q(‘#js-player-aside-state’).checked = true;
else w.$(document).unbind(‘keydown’);
});
app.cssMV = ‘[src^=blob]’;
app.playCSS = inRoom ? ‘div[title=”播放”]’ : ‘input[title=”播放”]’;
app.webfullCSS = inRoom ? ‘div[title=”网页全屏”]’ : ‘input[title=”进入网页全屏”]’;
app.fullCSS = inRoom ? ‘div[title=”窗口全屏”]’ : ‘input[title=”进入全屏”]’;
app.adsCSS = ‘[data-dysign],a[href*=”wan.douyu.com”]’;
},
yy() {
app.isLive = !path.startsWith(‘/x/’);
if (app.isLive) {
app.fullCSS = ‘.yc__fullscreen-btn’;
app.webfullCSS = ‘.yc__cinema-mode-btn’;
app.playCSS = ‘.yc__play-btn’;
}
},
huya() {
if (firefoxVer && firefoxVer < 57) return true;
app.webfullCSS = ‘.player-fullpage-btn’;
app.fullCSS = ‘.player-fullscreen-btn’;
app.playCSS = ‘#player-btn’;
app.adsCSS = ‘#player-subscribe-wap,#wrap-income,#hy-ad’;
},
longzhu() {
app.fullCSS = ‘a.ya-screen-btn’;
},
zhanqi() {
localStorage.lastPlayer = ‘h5’;
app.fullCSS = ‘.video-fullscreen’;
}
};
if (router[u]) {
app.isLive = app.isLive || !host.startsWith(‘v.’);
!w.chrome && fakeUA(ua_chrome);
}
}
!/pptv|douyu/.test(u) && Object.defineProperty(navigator, ‘plugins’, {
get() { return { length: 0 } }
});
if (!router[u] || !router[u]()) app.init();
作者:衔玉教教主 https://www.bilibili.com/read/cv3961572 出处:bilibili
HyakuAkiLV4
音游DD
。
所幸LV1
...
苍木LV3
.
emmLV2
凡人无法理解。。。。。。
绅士LV3
眼