您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
公式HTML5埋め込みプレイヤーのコメントの動きをなめらかにする(主にFirefox)
// ==UserScript== // @name Comment Render Smoother for embed player // @description 公式HTML5埋め込みプレイヤーのコメントの動きをなめらかにする(主にFirefox) // @match *://embed.nicovideo.jp/watch/* // @grant none // @author rinsuki (original author: guest https://greasyfork.org/ja/scripts/377400/code?version=668741 ) // @license public domain // @version 0.0.4.4 // @namespace https://rinsuki.net/ // ==/UserScript== const monkey = (() => { let fps = 60; let playbackRate = 1.0; const toFrameTime = (time) => { return time; // こっちは体感しづらいかも // const timeMs = time * 1000; // const MSEC_PER_FRAME = 1000 / fps * playbackRate; // const nextFrame = Math.ceil(timeMs / MSEC_PER_FRAME); // return nextFrame * MSEC_PER_FRAME / 1000; }; const init = () => { if (!window.__videoplayer) { return; } const player = window.__videoplayer; const _currentTime = player.currentTime.bind(player); const _playbackRate = player.playbackRate.bind(player); _playbackRate(); console.log('%cinitialize Comment Render Smoother playbackRate:%s', 'background: cyan;', playbackRate); let worldTime = performance.now(); let lastVideoTime = _currentTime(); let stallCount = 0; player.currentTime = (time) => { const now = performance.now(); if (typeof time === 'number') { lastVideoTime = time; worldTime = now; return _currentTime(time); } const isPlaying = !player.paused(); const videoTime = _currentTime(); const timeDiff = (now - worldTime) / 1000 * playbackRate; const predictionTime = lastVideoTime + timeDiff; if (isPlaying && lastVideoTime === videoTime) { stallCount ++; } else { stallCount = 0; } // stallCount = 0; // debug... if ( !isPlaying || // 再生してない or lastVideoTime > videoTime || // 時間が戻った ≒ シークした or //videoTime - lastVideoTime > playbackRate || // いきなり1秒以上も進んだ ≒ シークした or Math.abs(predictionTime - videoTime) > 1 || // 予測とn秒以上の誤差ができた stallCount > 5 // 詰まってんじゃねーの? の時 ) { lastVideoTime = videoTime; worldTime = now; _playbackRate(); return toFrameTime(videoTime); } else { return toFrameTime(predictionTime); } }; player.playbackRate = rate => { if (typeof rate !== 'number') { const currentRate = _playbackRate(); if (playbackRate !== currentRate) { console.log('%cupdate playbackRate %s -> %s', 'background: yellow;', playbackRate, currentRate); playbackRate = currentRate; worldTime = performance.now(); lastVideoTime = _currentTime(); } return currentRate; } if (rate === playbackRate || rate <= 0) { return; } console.log('%cset playbackRate %s -> %s', 'background: orange;', playbackRate, rate); playbackRate = rate; worldTime = performance.now(); lastVideoTime = _currentTime(); return _playbackRate(rate); }; // player.play(); }; // TODO: なんかプレイヤー初期化のタイミングでやる init(); }); if (document.querySelector('#ext-player')) { const script = document.createElement('script'); script.setAttribute('type', 'text/javascript'); script.setAttribute('charset', 'UTF-8'); script.appendChild(document.createTextNode(`(${monkey})();`)); document.body.appendChild(script); }