NicoSpeedMaster

プレミアム会員向けに、ニコ動プレーヤーにx2.0以上の再生速度を追加します。 一般会員向けには公式と同じ速度制限があります。速度を出すには動画右クリックのメニューから「視聴方法の切替」で「http」を選んでおくとhlsより速いとされています。

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

You will need to install an extension such as Tampermonkey to install this script.

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         NicoSpeedMaster
// @namespace    https://toogiri.buhoho.net/
// @version      0.1.0.1
// @description  プレミアム会員向けに、ニコ動プレーヤーにx2.0以上の再生速度を追加します。 一般会員向けには公式と同じ速度制限があります。速度を出すには動画右クリックのメニューから「視聴方法の切替」で「http」を選んでおくとhlsより速いとされています。
// @author       buhoho
// @match        https://*.nicovideo.jp/watch/*
// @grant        none
// @license      MIT
// ==/UserScript==



(function () {

'use strict';

// スクリプトが管理している再生速度
let customPlaybackRate = parseFloat(localStorage.customPlaybackRate ?? 1.0);

// 変更前のvideo要素
let prevVideo, prevVideoSrc;

// 多分Premiumじゃないとろくに再生速度出ないので、公式仕様通り制限
let isPremium = JSON.parse(document.querySelector('#CommonHeader').dataset.commonHeader).initConfig.user.isPremium;


function createPlaybackRateMenuItem(rate) {
	const menuItem = document.createElement('div');
	menuItem.classList.add('PlaybackRateMenuItem');
	menuItem.innerHTML = rate === customPlaybackRate?
		`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36" class="CheckIcon PlaybackRateMenuItem-checkIcon"><path d="M13.7 29.9 2.9 19.1l4.2-4.3 6.6 6.6L28.9 6.2l4.2 4.3-19.4 19.4z"></path></svg><div>x${rate.toFixed(1)}</div>`:
		`<div class="PlaybackRateMenuItem-iconSpace"></div><div>x${rate.toFixed(1)}</div>`;
	menuItem.onclick = function () {
		localStorage.customPlaybackRate = prevVideo.playbackRate = customPlaybackRate = rate;
		togglePlaybackRateMenu();
	};
	return menuItem;
}

function createPlaybackRateMenu() {
	const menu = document.createElement('div');
	menu.classList.add('PlaybackRateMenu');
	menu.innerHTML = '<div class="PlaybackRateMenu-title">再生速度</div>';

	const menuContents = document.createElement('div');
	menuContents.classList.add('PlaybackRateMenu-contents');
	menu.appendChild(menuContents);

	const rates = isPremium ? [0.5, 1.0, 1.5, 1.8, 2.3, 2.7, 3.4, 4.2]: [0.5, 1.0, 1.25];
	for (const rate of rates) {
		const menuItem = createPlaybackRateMenuItem(rate);
		menuContents.appendChild(menuItem);
	}

	return menu;
}

function togglePlaybackRateMenu(e) {
	e && e.stopPropagation();
	const existingMenu = document.querySelector('.PlaybackRateMenu');
	if (existingMenu) {
		existingMenu.remove();
	} else {
		const menu = createPlaybackRateMenu();
		const container = document.querySelector('.VideoOverlayContainer');
		if (container) {
			container.appendChild(menu);
		}
	}
}

function buttonShowRate(rate) {
	let btn = document.querySelector('.ActionButton.PlaybackRateButton');

	const btnItem = document.createElement('div');
	//btnItem.style.border = '2px solid white';
	btnItem.style.setProperty('border', '2px solid white', 'important');
	// btnItem.style.background = '#113';
	btnItem.style.padding = '2px';
	btnItem.style.borderRadius = '12px';
	// btnItem.style.backgroundColor = 'rgb(20, 13, 55)';
	btnItem.innerText = `x${rate.toFixed(1)}`;

	// 文字色を白に設定
	btn.style.color = 'white';
	// btn.style.backgroundColor = 'rgb(20, 13, 55)';

	if (rate > 1.0 && rate <= 2.0) {
		btnItem.style.backgroundColor = '#002176';
		btnItem.style.setProperty('border', '2px solid #aaeeff', 'important');
		btnItem.style.setProperty('color', '#aaeeff', 'important');
		// btnItem.style.backgroundColor = '#003386'; // 濃い青色
	} else if (rate > 2.0) {
		btnItem.style.backgroundColor = '#660005';
		btnItem.style.setProperty('border', '2px solid #ffaacf', 'important');
		btnItem.style.setProperty('color', '#ffaacf', 'important');
		// btnItem.style.backgroundColor = '#a60012'; // 濃い赤色
	}

	btn.innerHTML = '';
	btn.appendChild(btnItem);
}

function setPlaybackRateEventListener(v) {
	v.addEventListener('ratechange', () => {
		// console.log("レートが変更されました。");
		let rate = v.playbackRate;
		if (rate !== customPlaybackRate) {
			// 再生速度がスクリプトが管理しているものと異なる場合、管理している再生速度に戻す
			v.playbackRate = customPlaybackRate;
			return;
		}
		buttonShowRate(rate);
	});
}

new MutationObserver(mutations => {
	const v = document.querySelector('#MainVideoPlayer video');
	if (prevVideo === v && prevVideoSrc === v.src)
		return;
	// video 要素が変更されたときの処理を記述
	setPlaybackRateEventListener(v);
	// メニュー表示処理を上書き
	document.querySelector('button.ActionButton.PlaybackRateButton').onclick = togglePlaybackRateMenu;
	// 速度更新
	v.playbackRate = customPlaybackRate;
	// 現在のvideo要素を保存(変更を検知するため)
	prevVideo = v;
	prevVideoSrc = v.src;
}).observe(document.querySelector('#js-app'), {childList: true, subtree: true});


})();