Greasy Fork

来自缓存

Greasy Fork is available in English.

视频站启用html5播放器

html5工具箱。添加快捷键:快进、快退、暂停/播放、音量、下一集、切换[万能网页]全屏、上下帧、播放速度。支持视频站点:优.土、QQ、新浪、微博、网易视频[娱乐、云课堂、新闻]、搜狐、乐视、央视、风行、百度云视频等;直播:斗鱼、熊猫、YY、虎牙、火猫、龙珠、战旗。可自定义站点

当前为 2018-03-05 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name             视频站启用html5播放器
// @description      html5工具箱。添加快捷键:快进、快退、暂停/播放、音量、下一集、切换[万能网页]全屏、上下帧、播放速度。支持视频站点:优.土、QQ、新浪、微博、网易视频[娱乐、云课堂、新闻]、搜狐、乐视、央视、风行、百度云视频等;直播:斗鱼、熊猫、YY、虎牙、火猫、龙珠、战旗。可自定义站点
// @version          0.71
// @homepage         http://bbs.kafan.cn/thread-2093014-1-1.html
// @include          *://pan.baidu.com/*
// @include          *://v.qq.com/*
// @include          *://v.sports.qq.com/*
// @include          *://film.qq.com/*
// @include          *://view.inews.qq.com/*
// @include          *://news.qq.com/*
// @include          *://v.youku.com/v_show/id_*
// @include          *://*.tudou.com/v/*
// @include          *://v.163.com/*.html*
// @include          *://ent.163.com/*.html*
// @include          *://news.163.com/*.html*
// @include          *://news.163.com/special/*
// @include          *://study.163.com/course/*.htm?courseId=*
// @include          *://news.sina.com.cn/*
// @include          *://video.sina.com.cn/*
// @include          *://video.sina.cn/*
// @include          *://weibo.com/*
// @include          *://*.weibo.com/*
// @include          *://*.le.com/*.html*
// @include          *://*.lesports.com/*.html*
// @include          *://tv.sohu.com/*.shtml*
// @include          *://*.tv.sohu.com/*.shtml*
// @include          *://film.sohu.com/album/*
// @include          *://www.fun.tv/vplay/*
// @include          *://m.fun.tv/*
// @include          *://www.yy.com/*
// @include          *://www.huya.com/*
// @include          https://www.douyu.com/*
// include          https://www.huomao.com/*
// @include          https://www.panda.tv/*
// include          https://*.zhanqi.tv/*
// @include          *://*.longzhu.com/*
// @grant            unsafeWindow
// @grant            GM_addStyle
// @require          https://cdn.jsdelivr.net/hls.js/latest/hls.min.js
// @run-at           document-start
// @namespace  http://greasyfork.icu/users/7036
// ==/UserScript==
'use strict';
if (top !== self) return;
if (window.chrome)
	NodeList.prototype[Symbol.iterator] = HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];

const q = css => document.querySelector(css),
doClick = e => {
	if (e) e.click ? e.click() : e.dispatchEvent(new MouseEvent('click'));
},
r1 = (regp, s) => regp.test(s) && RegExp.$1,
fakeUA = ua => Object.defineProperty(navigator, 'userAgent', {
	value: ua,
	writable: false,
	configurable: false,
	enumerable: true
}),
//判断是否为Firefox,且低于量子版
underFirefox57 = () => {
	const x = r1(/Firefox\/(\d+)/, navigator.userAgent);
	return x && x < 57;
},
getMainDomain = host => {
	let a = host.split('.'),
	i = a.length -2;
	if (['com','tv','net','org','gov','edu'].includes(a[i])) i--;
	return a[i];
},
ua_samsung = 'Mozilla/5.0 (Linux; U; Android 4.0.4; GT-I9300 Build/IMM76D) AppleWebKit/534.30 Version/4.0 Mobile Safari/534.30',
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;
		this._exitFn = d.exitFullscreen || d.webkitExitFullscreen || d.mozCancelFullScreen || d.msExitFullscreen;

		const e = video;
		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();
	}
}

//万能网页全屏,代码参考了:https://github.com/gooyie/ykh5p
class WebFullscreen {
	constructor(video) {
		this._video = video;
		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;
			}
		`);
	}

	_checkContainer() {
		const e = this._video;
		if (!this._container || this._container === e)
			this._container = WebFullscreen.getPlayerContainer(e);
	}

	get container() {
		this._checkContainer();
		return this._container;
	}

	static getPlayerContainer(video) {
		let d = document.body,
		e = video,
		p = e.parentNode;
		if (p === d) return e;
		const w = p.clientWidth,
		h = p.clientHeight;
		do {
			e = p;
			p = e.parentNode;
		} while (p !== d && p.clientWidth - w < 5 && p.clientHeight - h < 5);
		//console.dirxml(e);
		return e;
	}

	fixView() {
		let e = this._video, c = this._container;
		if (e === c) return;
		if (e.clientWidth < c.clientWidth || e.clientHeight < c.clientHeight) {
			let a = [];
			while (e !== c) {
				a.push(e);
				e = e.parentNode;
			}
			while (e = a.pop()) e.style.width = e.style.height = '100%';
		}
	}

	static isFull(video) {
		return window.innerWidth -video.clientWidth < 5 && window.innerHeight - video.clientHeight < 5;
	}

	toggle() {
		const d = document.body,
		state = WebFullscreen.isFull(this.container);
		d.style.overflow = state ? '' : 'hidden';
		this.container.classList.toggle('webfullscreen');

		let p = this.container.parentNode;
		while (p !== d) {
			p.classList.toggle('z-top');
			p = p.parentNode;
		}

		!state && setTimeout(this.fixView.bind(this), 9);
	}
}

let webFull, fullScreen;

const { host, pathname: path } = location,
u = getMainDomain(host),//主域名
//容器,登记事件处理方法中的回调
events = {
	on(name, fn) {
		this[name] = fn;
	}
},
app = {
	el: null,//video
	isLive: !1,
	vList: null,
	getVideos() {
		this.vList = this.vList || document.getElementsByTagName('video');
		if (this.el.offsetWidth>1) return;
		for (let e of this.vList)
			if (e.offsetWidth>1) {
				e.playbackRate = this.el.playbackRate;
				e.volume = this.el.volume;
				this.el = e;
				break;
			}
	},
	_convertView(btn) {
		const s = btn.style.display || getComputedStyle(btn, '').getPropertyValue('display');
		s === 'none' ? doClick(btn.nextElementSibling) : doClick(btn);
	},
	onCanplay(e) {
		this.el.oncanplay = null;
		console.log('脚本[启用html5播放器],事件oncanplay');
		events.canplay && events.canplay();
		this.oldCanplay && this.oldCanplay(e);
	},
	hotKey(e) {
		//判断ctrl,alt,shift三键状态,防止浏览器快捷键被占用
		if (e.ctrlKey || e.altKey || /INPUT|TEXTAREA/.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;
		this.getVideos();
		this.checkElement();
		const v = this.el;
		let n;
		switch (e.keyCode) {
		case 32: //space
			if (this.disableSpace) return;
			v.paused ? v.play() : v.pause();
			e.preventDefault();
			e.stopPropagation();
			break;
		case 37: //left
			n = e.shiftKey ? -27 : -5; //快退5秒,shift加速
		case 39: //right
			n = n || (e.shiftKey ? 27 : 5); //快进5秒,shift加速
			v.currentTime += n;
			break;
		case 78: // N 下一首
			doClick(this.btnNext);
			break;
		//case 80: // P 上一首
		case 38: //加音量
			n = .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)
				webFull ? webFull.toggle() : this.webFullScreen();
			else
				fullScreen ? fullScreen.toggle() : this.fullScreen();
			break;
		case 27: //esc
			if (Fullscreen.isFull())
				fullScreen ? fullScreen.exit() : this.fullScreen();
			else if (WebFullscreen.isFull(v))
				webFull ? webFull.toggle() : this.webFullScreen();
			break;
		case 67: //按键C:加速播放 +0.1
			n = .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: //按键F:下一帧
			n = .03;
		case 68: //按键D:上一帧
			n = n || -0.03;
			if (!v.paused) v.pause();
			v.currentTime += n;
		}
	},
	doHls() {
		const v = this.el;
		if (!v || !v.src.includes('.m3u8') || !Hls.isSupported()) return;
		const hls = new Hls();
		hls.loadSource(v.src);
		hls.attachMedia(v);
		hls.on(Hls.Events.MANIFEST_PARSED, () => v.play());
	},
	checkElement() {
		if (!this.webfullCSS)
			webFull = webFull || new WebFullscreen(this.el);
		else if (!this.btnWFS){
			this.btnWFS = q(this.webfullCSS);
			this.webFullScreen = () => this._convertView(this.btnWFS);
		}
		if (this.nextCSS && !this.btnNext) this.btnNext = q(this.nextCSS);
		if (!this.fullCSS)
			fullScreen = fullScreen ||  new Fullscreen(this.el);
		else if (!this.btnFS){
			this.btnFS = q(this.fullCSS);
			//进入、退出、切换全屏:三合一
			this.fullScreen = () => this._convertView(this.btnFS);
		}
	},
	init() {
		if (u !== 'zhanqi') Object.defineProperty(navigator, 'plugins', {
			get: function() {
				return { length: 0 };
			}
		});
		const t = setInterval(() => {
			if (events.loop) {
				if (events.loop()) delete events.loop;
				return;
			}
			const v = q('video');
			if (!v) return;
			clearInterval(t);
			this.el = v;
			if (v.readyState<4) {
				this.oldCanplay = v.oncanplay;
				v.oncanplay = this.onCanplay.bind(this);
			}
			else this.onCanplay(this);

			document.addEventListener('keydown', this.hotKey.bind(this), !1);
			this.checkElement();
			setTimeout(() => {
				v.focus();
			}, 300);
			events.init && events.init();
		}, 300);
	}
};

let router = {
	qq() {
		Object.assign(app, {
			disableSpace: true,
			nextCSS: '.txp_btn_next',
			webfullCSS: '.txp_btn_fake',
			fullCSS: '.txp_btn_fullscreen',
		});
		events.on('canplay', app.getVideos);
		fakeUA('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10; rv:48.0) Gecko/20100101 Firefox/48.0');
	},
	youku() {
		events.on('canplay', () => {
			q('.youku-layer-logo').remove();
			if (app.el.src.startsWith('http')) app.getVideos();//初始化vList,旧版用flv.js~地址以blob:开头
			//修正下一个按钮无效 yk-trigger-layer
			const btn = q('button.control-next-video');
			if (btn && btn.offsetWidth>1) {
				let e = q('.program.current');
				e = e && e.closest('.item') || q('.item.current');
				e = e.nextSibling;
				if (!e) return;
				e = e.querySelector('a');//下一个视频链接
				btn.addEventListener('click', ev => e.click());
				app.btnNext = e;
			}
		});
		app.fullCSS = '.control-fullscreen-icon';
	},
	cctv() {
		fakeUA(ua_samsung);
	},
	sina() {
		fakeUA(ua_ipad2);
	},
	le() {
		//firefox 56以下 黑屏
		if (underFirefox57()) return true;
		fakeUA('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 Version/7.0.3 Safari/7046A194A');
		Object.assign(app, {
			nextCSS: 'div.hv_ico_next',
			webfullCSS: 'span.hv_ico_webfullscreen',
			fullCSS: 'span.hv_ico_screen'
		});
	},
	sohu() {
		fakeUA(ua_samsung);
		app.nextCSS = 'li.on[data-vid]+li a';
		app.fullCSS = 'div.x-fs-btn';
	},
	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 = unsafeWindow.vplay.videoid;
			vid && location.assign(`//m.fun.tv/implay/?mid=${mid}&vid=${vid}`);
		}, 99);
		return true;
	}
};
router.cntv = router.cctv;
router.lesports = router.le;
router['163'] = router.sina;

if (!router[u]) { //直播站点
	router = {
		douyu() {
			events.on('loop', () => {
				const p = unsafeWindow.__player;
				if (p && p.switchPlayer) {
					p.switchPlayer('h5');
					return true;
				}
			});
			app.webfullCSS = 'div[title="网页全屏"]';
			app.fullCSS = 'div[title="窗口全屏"]';
		},
		panda() {
			localStorage.setItem('panda.tv/user/player', '{"useH5player": true}');
			app.webfullCSS = '.h5player-control-bar-fullscreen';
			app.fullCSS = '.h5player-control-bar-allfullscreen';
		},
		yy() {
			if (!window.chrome) fakeUA('Mozilla/5.0 (Windows NT 10.0; WOW64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.9');
			app.fullCSS = '.liveplayerToolBar-fullScreenBtn';
		},
		huya() {
			if (underFirefox57()) return true;
			if (!window.chrome) fakeUA('Mozilla/5.0 (Windows NT 10.0; WOW64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.9');
			app.webfullCSS = '.player-fullpage-btn';
			app.fullCSS = '.player-fullscreen-btn';
		},
		huomao() {
			if (!window.chrome || !/^\/\d+/.test(path)) return true;
			localStorage.setItem('hm_cookie_$', '{"ready_fireH5": true}');
			app.webfullCSS = '.page-full-screen';
			app.fullCSS = '.full-screen';

		},
		longzhu() {
			fakeUA(ua_ipad2);
			events.on('init', () => {
				const v = app.el;
				if (v.src && v.src.includes('.m3u8'))
					app.doHls();
				else {
					q('#landscape_dialog').remove();
					new MutationObserver(function(records) {
						this.disconnect();
						app.doHls();
					})
					.observe(v, {
						attributes: true,
						attributeFilter: ['src']
					});
					setTimeout(() => q('.player.report-rbi-click').click(), 1200);
				}
			});
		},
		zhanqi() {
			events.on('loop', () => {
				const e = q('#BFPlayerID');//flash ID
				if (e) {
					let s = e.children.flashvars.value,
					url = r1(/PlayUrl=([^&]+)/, s);//视频
					app.isLive = !url;
					if (!url) {
						s = r1(/VideoLevels=([^&]+)/, s);//直播
						s = atob(s);
						url = JSON.parse(s).streamUrl;
					}
					e.parentNode.innerHTML = `<video width="100%" height="100%" autoplay controls src="${url}"/>`;
				}
				return !!e;
			});
			events.on('init', app.doHls.bind(app));
		}
	};

	if (router[u]) app.isLive = true;
}

if (!router[u] || !router[u].call(null)) app.init();