futaba WebM inline player

WebMをページ内で再生しちゃう

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name        futaba WebM inline player
// @namespace   https://github.com/himuro-majika
// @description WebMをページ内で再生しちゃう
// @author      himuro_majika
// @include     http://*.2chan.net/*/*
// @include     https://*.2chan.net/*/*
// @exclude     http://*.2chan.net/*/futaba.php?mode=cat*
// @exclude     https://*.2chan.net/*/futaba.php?mode=cat*
// @exclude     http://*.2chan.net/bin/*
// @exclude     https://*.2chan.net/bin/*
// @require     https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js
// @require     https://greasyfork.org/scripts/1884-gm-config/code/GM_config.js?version=4836
// @version     1.10.1
// @grant       none
// @run-at      document-idle
// @license     MIT
// @icon         data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAAPUExURYv4i2PQYy2aLUe0R////zorx9oAAAAFdFJOU/////8A+7YOUwAAAElJREFUeNqUj1EOwDAIQoHn/c88bX+2fq0kRsAoUXVAfwzCttWsDWzw0kNVWd2tZ5K9gqmMZB8libt4pSg6YlO3RnTzyxePAAMAzqMDgTX8hYYAAAAASUVORK5CYII=
// ==/UserScript==
this.$ = this.jQuery = jQuery.noConflict(true);

(function ($) {
	/**
	 * 設定
	 */
	// フルサイズプレーヤーを有効にする(4chanライクな表示)
	var USE_FULLPLAYER = true;
	// ループ再生を有効にする
	var USE_LOOP = true;
	// 自動再生を有効にする(ミニサイズプレーヤー使用時)
	var USE_AUTOPLAY = false;
	// コントロールを表示する(ミニサイズプレーヤー使用時)
	var USE_CONTROLS = true;
	// 動画のサイズをサムネ画像と同サイズに制限する
	var USE_LIMIT_SIZE = false;
	// 動画の外側をクリックして動画を閉じる
	var USE_CLOSE_ON_CLICK_OUTSIDE = true;
	// デフォルトの音量
	var DEFAULT_VOLUME = 50;
	// ミュート状態で再生する
	var USE_MUTED = false;
	// フルサイズプレーヤーに時間を表示する
	var USE_TIME_DISPLAY = true;
	// 再生速度変更を有効にする
	var USE_PLAYBACK_RATE_CONTROL = true;
	// 赤福のオートリンクにも反応する
	var USE_AUTOLINK = true;

	init();
	function init() {
		config();
		getImgNodeThread();
		getImgNodeRes();
		if (USE_CLOSE_ON_CLICK_OUTSIDE) {
			closeOnClick();
		}
		if ((isAkahukuEnabled() || isFutakuroEnabled()) && USE_AUTOLINK) {
			getAutoLinkURL();
		}
		observeInserted();
		getResPopup();
	}
	// 赤福が有効か
	function isAkahukuEnabled() {
		return $("#akahuku_thumbnail").length > 0;
	}
	// ふたクロが有効か
	function isFutakuroEnabled() {
		return $("#master").length > 0;
	}
	// スレ画
	function getImgNodeThread() {
		var $sure_a = $(".thre").length ?
			$(".thre > a > img") :
			$("body > form > a > img");
		if (isFutakuroEnabled()) { // ふたクロ
			$sure_a = $("#master > a > img");
		}
		$sure_a.each(function() {
			replaceNode($(this));
		});
	}
	// レス画像
	function getImgNodeRes() {
		var $res_a = $(".rtd > a > img");
		$res_a.each(function() {
			replaceNode($(this));
		});
	}
	// オートリンクURL
	function getAutoLinkURL() {
		var $link = $("blockquote > a");
		$link.each(function() {
			replaceNode($(this));
		});
	}
	// 続きを読むで挿入される要素を監視
	function observeInserted() {
		var target = $(".thre").length ?
			$(".thre").get(0) :
			$("html > body > form[action]:not([enctype])").get(0);
		var observer = new MutationObserver(function(mutations) {
			mutations.forEach(function(mutation) {
				var $nodes = $(mutation.addedNodes);
				replaceNodeInserted($nodes);
			});
		});
		observer.observe(target, { childList: true });
	}
	// 引用ポップアップ //TODO:wip
	function getResPopup() {
		// $(document).click(function(event) {
		// 	var target = event.target;
		// 	console.log(target);
		// 	webmopen(target);
		// });
	}
	// 挿入されたレス
	function replaceNodeInserted($nodes) {
		var $res_inserted = $nodes.find("td > a > img");
		if (isAkahukuEnabled()) {
			if ($res_inserted.length) {
				replaceNode($res_inserted);
			}
		} else if (isFutakuroEnabled()) {
			$res_inserted.each(function(){
				replaceNode($(this));
			});
		}
		// オートリンク
		var $autolink_inserted = $nodes.find("blockquote > a");
		if (USE_AUTOLINK && $autolink_inserted.length) {
			replaceNode($autolink_inserted);
		}
	}
	// 外側をクリックしてプレーヤーを閉じる
	function closeOnClick() {
		$(document).click(function(event) {
			if (event.target.className != "extendWebm") {
				if ($(event.target).parents(".akahuku_reply_popup").length > 0) {
					// akahuku_reply_popup
					// replaceNode($(event.target));
					// return false;
				} else {
					$("div.cancelbk").each(function() {
						$(this).get(0).click();
					});
				}
			}
		});
	}
	// ノードの書き換え
	function replaceNode(node) {
		var href = node.parent().attr("href");
		if (node.attr("dummyhref")) {
			// オートリンク
			href = node.attr("dummyhref");
		} else if (node.attr("href")) {
			href = node.attr("href");
		}
		if (!href.match(/\.(webm|mp4)$/)) {
			// 拡張子.webm, .mp4以外
			return;
		}
		var width = node.attr("width");
		if (!width) {
			// オートリンク
			width = node.get(0).offsetWidth;
		}
		var height = node.attr("height");
		var timer_show, timer_hide, timer_rate_hide;
		
		
		//クリックイベント
		// document.removeEventListener('click', thumbonclick, false);
		node.click(function(event) {
			addMiniPlayer(node);
			return false;
		});

		
		// マウスオーバーで読み込み
		node.hover(function(){
			if (USE_FULLPLAYER) {
				clearTimeout(timer_rate_hide);
				timer_show = setTimeout(function(){
					showFullPlayer();
					if (USE_PLAYBACK_RATE_CONTROL) {
						showplaybackRateControl();
					}
				}, 300);
			} else {
				if (node.attr("dummyhref") || node.attr("href")) {
					addMiniPlayerAutoLink();
				} else if (USE_AUTOPLAY) {
					addMiniPlayer(node);
				}
			}
		},function(){
			if (USE_FULLPLAYER) {
				clearTimeout(timer_show);
				timer_hide = setTimeout(function(){
					hideFullPlayer();
					if (USE_PLAYBACK_RATE_CONTROL) {
						hideplaybackRateControl();
					}
				}, 300);
			}
		});
		// 再生速度変更
		function showplaybackRateControl() {
			if ($("#GM_fwip_Rate_container").length) {
				return;
			}
			var $rateContainer = $("<div>", {
					id: "GM_fwip_Rate_container",
					css: {
						position: "absolute",
						// "margin-top": "5px",
						"margin-left": "20px",
						"background-color": "rgba(0,0,0,0.3)",
						"z-index": "100",
						color: "#fff",
					}
			}).hover(function(){
				clearTimeout(timer_hide);
			}, function() {
				timer_rate_hide = setTimeout(function() {
					hideFullPlayer();
					hideplaybackRateControl();
				}, 300);
			}).append(
				$("<label>", {
					text: "再生速度x",
					for: "GM_fwip_Rate",
				})
			).append(
				$("<input>", {
					id: "GM_fwip_Rate",
					type: "number",
					step: "0.25",
					max: "5.0",
					min: "0.25",
					value: "1.0",
					css: {
						width: "3em",
						opacity: "0.7"
					}
				})
			);
			if (node.attr("dummyhref") || node.attr("href")) {
				// オートリンク
				node.after($rateContainer);
			} else {
				node.parent().after($rateContainer);
			}
		}
		// 再生速度
		function hideplaybackRateControl() {
			$("#GM_fwip_Rate_container").remove();
		}
		// ミニプレイヤー
		function addMiniPlayer(node) {
			var thumb = node.get(0);
			webmopen(thumb);
			var video = node.parent().parent().find(".extendWebm");
			var videoDiv = video.parent();
			videoDiv.css({
				"margin": "0 20px",
				"float": "left",
				"clear": "left",
			});
			if (USE_LIMIT_SIZE) {
				video.css({
					"width": width,
					"height": height,
				});
			}
			video.prop({
				controls: USE_CONTROLS,
				// autoplay: USE_AUTOPLAY,
				loop: USE_LOOP,
				muted: USE_MUTED,
				volume: DEFAULT_VOLUME / 100,
			}).click(function(event) {
				//動画クリックでplay/pauseトグル(Chrome用)
				if (navigator.userAgent.indexOf("Firefox") == -1) {
					if (this.paused) {
						this.play();
					} else {
						this.pause();
					}
				}
			}).hover(function() {
				if (USE_AUTOPLAY && !USE_LOOP) {
					this.play();
				}
			}, function() {
			});
		}
		
		function addMiniPlayerAutoLink() {
			if (
				( node.attr("dummyhref") || node.attr("href") ) &&
				node.parent().parent().get(0).tagName == "FORM" &&
				width < 250
			) {
				// スレ本文のオートリンク
				width = 250;
			}
			var $videoContainer = $("<div>", {
				class: "GM_fwip_container_mini",
				css: {
					"margin": "0 20px",
					"float": "left",
					"clear": "left",
				}
			}).hover(function(){
				if (USE_AUTOPLAY) {
					$(this).find(".GM_fwip_player").get(0).play();
				}
			},function(){
			}).append(
				$("<video>", {
					class: "GM_fwip_player",
					css: {
						"width": USE_LIMIT_SIZE ? width : "",
						"height": USE_LIMIT_SIZE ? height : "",
					},
				}).prop({
					controls: USE_CONTROLS,
					autoplay: USE_AUTOPLAY,
					loop: USE_LOOP,
					muted: USE_MUTED,
					volume: DEFAULT_VOLUME / 100,
				}).click(function(event) {
					//動画クリックでplay/pauseトグル(Chrome用)
					if (navigator.userAgent.indexOf("Firefox") == -1) {
						if (this.paused) {
							this.play();
						} else {
							this.pause();
						}
					}
				})
				// .on("timeupdate", function(){
				// 	// 再生速度変更
				// 	$(this).prop("playbackRate", $("#GM_fwip_Rate").val());
				// })
				.append(
					$("<source>", {
						src: href,
						type: "video/webm",
					})
				)
			);
			// サムネイル画像を隠す
			if (node.attr("dummyhref") || node.attr("href")) {
				// オートリンク
				if (!node.parent().parent().children(".GM_fwip_container_mini").length) {
					node.parent().before($videoContainer);
				}
			} else {
				node.hide();
				node.parent().before($videoContainer);
			}
		}
		// フルプレイヤーを表示する
		function showFullPlayer() {
			if ($("#GM_fwip_Rate_container").length) {
				return;
			}
			hideFullPlayer();
			// サムネ右端のオフセット
			var offset = parseInt(node.offset().left) + parseInt(width);
			var $videoContainer = $("<div>", {
				class: "GM_fwip_container_full",
				css: {
					"background-color": "#000",
					"position": "fixed",
					"top": "20px",
					"right": "20px",
					// "border": "5px solid #333",
					// "border-radius": "5px",
					"box-shadow": "0 0 10px 5px rgba(0,0,0,0.5)",
					"z-index": "2000000013",
				}
			});
			var $videoPlayer = $("<video>", {
				class: "GM_fwip_player",
				css: {
					"width": "auto",
					"height": "auto",
					"max-width": $(window).width() - offset - 40,
					"max-height": $(window).height() - 50,
				},
			}).prop({
				autoplay: true,
				loop: USE_LOOP,
				muted: USE_MUTED,
				preload: true,
				volume: DEFAULT_VOLUME / 100,
				// playbackRate: "1.0",
			}).append(
				$("<source>", {
					src: href,
					type: "video/webm",
					error: function() {
						// ソースの読み込み失敗イベント
						onerror();
					},
				})
			);
			$videoContainer.append($videoPlayer);
			if (USE_PLAYBACK_RATE_CONTROL) {
				$videoPlayer.on("timeupdate", function() {
					// 再生速度変更
					$(this).prop("playbackRate", $("#GM_fwip_Rate").val());
				});
			}
			if (USE_TIME_DISPLAY) {
				$videoPlayer.on("loadedmetadata", function(){
					// メタデータ読み込み完了イベント
					showDuration($(this).get(0));
				}).on("timeupdate", function() {
					// 再生位置変更イベント
					showCurrentTime($(this).get(0));
				});
				$videoContainer.append(
					$("<div>", {
						class: "GM_fwip_time_container",
						css: {
							"font-size": "6pt",
							"font-family": "arial,helvetica,sans-serif",
							postion: "relative",
							"text-align": "right",
							color: "#fff",
						}
					}).append(
						$("<span>", {
							class: "GM_fwip_time_current"
						})
					).append(
						$("<span>").text("/")
					).append(
						$("<span>", {
							class: "GM_fwip_time_duration",
						})
					)
				);
			}
			$("body").append($videoContainer);
			// 動画の長さを表示する
			function showDuration(video) {
				var webm_duration = parseTime(video.duration);
				$(".GM_fwip_time_duration").text(webm_duration);
			}
			// 再生時間を表示する
			function showCurrentTime(video) {
				var currenttime = parseTime(video.currentTime);
				$(".GM_fwip_time_current").text(currenttime);
			}
			// エラー表示
			function onerror() {
				$videoContainer.children().remove();
				$videoContainer.append(
					$("<p>", {
						text: "動画が読み込めませんでした",
						class: "GM_fwip_error",
						css: {
							"text-align": "center",
							"background-color": "#fff",
							"color": "#c00"
						}
					})
				);
			}
		}
		// フルプレーヤーを消す
		function hideFullPlayer() {
			var $container = $(".GM_fwip_container_full");
			if ($container.length) {
				$container.remove();
			}
		}
	}

	// 設定
	function config() {
		// 設定画面
		GM_config.init("futaba WebM inline playerオプション<br>" +
		"(設定反映には[Save]ボタン押下後にページの再読み込みが必要です)", {
			"USE_LOOP" : {
				"section": ["共通"],
				"label" : "ループ再生を有効にする",
				"type" : "checkbox",
				"default" : USE_LOOP
			},
			"DEFAULT_VOLUME" : {
				"label" : "デフォルトの音量(範囲は0~100。ミュートの設定が優先されます。)",
				"type" : "int",
				"default" : DEFAULT_VOLUME
			},
			"USE_MUTED" : {
				"label" : "ミュート状態で再生する",
				"type" : "checkbox",
				"default" : USE_MUTED
			},
			"USE_AUTOLINK" : {
				"label" : "赤福・ふたクロのオートリンク文字列に反応する",
				"type" : "checkbox",
				"default" : USE_AUTOLINK
			},
			"USE_FULLPLAYER" : {
				"section": ["フルサイズプレーヤー(画面右上のスペースに表示される大きいサイズのプレーヤー)"],
				"label" : "フルサイズプレーヤーを使用する(オフにするとミニサイズプレーヤーが有効になります。)",
				"type" : "checkbox",
				"default" : USE_FULLPLAYER
			},
			"USE_TIME_DISPLAY" : {
				"label" : "動画の下に再生時間を表示する",
				"type" : "checkbox",
				"default" : USE_TIME_DISPLAY
			},
			"USE_PLAYBACK_RATE_CONTROL" : {
				"label" : "再生速度コントロールを有効にする(実験的)",
				"type" : "checkbox",
				"default" : USE_PLAYBACK_RATE_CONTROL
			},
			"USE_AUTOPLAY" : {
				"section": ["ミニサイズプレーヤー(サムネ画像と置き換わるプレーヤー)"],
				"label" : "マウスオーバーで再生開始する",
				"type" : "checkbox",
				"default" : USE_AUTOPLAY
			},
			"USE_CONTROLS" : {
				"label" : "コントロールを表示する",
				"type" : "checkbox",
				"default" : USE_CONTROLS
			},
			"USE_CLOSE_ON_CLICK_OUTSIDE" : {
				"label" : "動画の外側をクリックして動画を閉じる",
				"type" : "checkbox",
				"default" : USE_CLOSE_ON_CLICK_OUTSIDE
			},
			"USE_LIMIT_SIZE" : {
				"label" : "動画のサイズをサムネ画像と同サイズに制限する",
				"type" : "checkbox",
				"default" : USE_LIMIT_SIZE
			}
		});
		// 設定値読み込み
		USE_FULLPLAYER = GM_config.get("USE_FULLPLAYER");
		USE_LOOP = GM_config.get("USE_LOOP");
		USE_AUTOPLAY = GM_config.get("USE_AUTOPLAY");
		USE_CONTROLS = GM_config.get("USE_CONTROLS");
		if (!GM_config.get("DEFAULT_VOLUME") || GM_config.get("DEFAULT_VOLUME") > 100) {
			DEFAULT_VOLUME = 100;
			GM_config.set("DEFAULT_VOLUME", 100);
		} else {
			DEFAULT_VOLUME = GM_config.get("DEFAULT_VOLUME");
		}
		USE_MUTED = GM_config.get("USE_MUTED");
		USE_TIME_DISPLAY = GM_config.get("USE_TIME_DISPLAY");
		USE_PLAYBACK_RATE_CONTROL = GM_config.get("USE_PLAYBACK_RATE_CONTROL");
		USE_AUTOLINK = GM_config.get("USE_AUTOLINK");
		USE_LIMIT_SIZE = GM_config.get("USE_LIMIT_SIZE");
		USE_CLOSE_ON_CLICK_OUTSIDE = GM_config.get("USE_CLOSE_ON_CLICK_OUTSIDE");
		// 設定ボタンの表示
		$("body > table:not([class])").before(
			$("<span>", {
				id: "GM_fwip_configButton",
			}).append(
				$("<a>", {
					text: "[WebM設定]",
					css: {
						cursor: "pointer",
					},
					click : function(){
						GM_config.open();
					}
				})
			)
		);
	}
	// 秒をhh:mm:ss形式で返す
	function parseTime(sec) {
		var date = new Date(0,0,0,0,0,sec);
		var time =
		("0" + date.getHours()).slice( -2 ) + ":" +
		("0" + date.getMinutes()).slice( -2 ) + ":" +
		("0" + date.getSeconds()).slice( -2 );
		return time;
	}

})(jQuery);