Niconico My Theater

自分のPC内の動画ファイルと差し替えてニコニコできます。

目前为 2018-10-23 提交的版本。查看 最新版本

// ==UserScript==
// @name        Niconico My Theater
// @namespace   knoa.jp
// @description 自分のPC内の動画ファイルと差し替えてニコニコできます。
// @include     https://www.nicovideo.jp/watch/*
// @version     0.1
// @grant       none
// ==/UserScript==

(function(){
  const SCRIPTNAME = 'NiconicoMyTheater';
  const DEBUG = false;/*
[to do]
ZenzaWatch対応。
ローディング表示
置き換え完了時に何かリアクションを。
元の動画に戻すボタンで確認してから戻せるように。
ニコニコの時刻表示に時間単位追加

[bug]
Firefoxのblob処理が重いのかどうか?
*/
  if(window === top && console.time) console.time(SCRIPTNAME);
  const SHIFTARROW = 1;// Shift + 左右キーで移動する(秒)
  let site = {
    get: {
      playerOptionButton: () => $('button[data-title="設定"]'),
      originalVideo: () => $('#VideoPlayer video[src]'),
      videoStartButton: () => $('.VideoStartButton'),
      footerContainerLinks: () => $('.FooterContainer-links'),
    },
  };
  let originalVideo, replacedVideo;
  let core = {
    initialize: function(){
      core.addFileButton();
      core.linkVideos();
      core.listenEvents();
      core.addFooter();
      core.addStyle();
    },
    addFileButton: function(){
      let playerOptionButton = site.get.playerOptionButton();
      if(!playerOptionButton) return setTimeout(core.addFileButton, 1000);
      let fileButton = createElement(core.html.fileButton());
      let input = fileButton.querySelector('input[type="file"]');
      fileButton.addEventListener('click', function(e){
        input.click();
      });
      input.addEventListener('change', function(e){
        log('changed!');
        let object = URL.createObjectURL(input.files[0]);
        replacedVideo.src = object;
        replacedVideo.classList.add('loaded');
        // オリジナルビデオの状態をコピー
        replacedVideo.currentTime = originalVideo.currentTime;
        replacedVideo.playbackRate = originalVideo.playbackRate;
        replacedVideo.volume = originalVideo.volume;
        // オリジナルビデオは影の存在となる
        originalVideo.style.visibility = 'hidden';//displayは上書きされる
        originalVideo.muted = true;
        // ローディング表示
        //
      });
      playerOptionButton.parentNode.insertBefore(fileButton, playerOptionButton);
    },
    linkVideos: function(){
      // リプレイスビデオ要素の準備
      originalVideo = site.get.originalVideo();
      if(!originalVideo) return setTimeout(core.linkVideos, 1000);
      replacedVideo = createElement(core.html.replacedVideo());
      originalVideo.parentNode.insertBefore(replacedVideo, originalVideo);
      // 連動
      originalVideo.addEventListener('play', function(e){
        log('originalVideo: play!');
        replacedVideo.currentTime = originalVideo.currentTime;
        replacedVideo.playbackRate = originalVideo.playbackRate;
        replacedVideo.play();
      });
      originalVideo.addEventListener('pause', function(e){
        log('originalVideo: pause!');
        replacedVideo.currentTime = originalVideo.currentTime;
        replacedVideo.pause();        
      });
      originalVideo.addEventListener('seeking', function(e){
        log('originalVideo: seeking!');
        replacedVideo.currentTime = originalVideo.currentTime;
      });
      originalVideo.addEventListener('canplay', function(e){
        log('originalVideo: canplay!');
        replacedVideo.currentTime = originalVideo.currentTime;
      });
      originalVideo.addEventListener('volumechange', function(e){
        replacedVideo.volume = originalVideo.volume;
      });
      // 連動(?)
      replacedVideo.addEventListener('seeking', function(e){
        log('replacedVideo: seeking!');
      });
      replacedVideo.addEventListener('canplay', function(e){
        log('replacedVideo: canplay!');
      });
    },
    listenEvents: function(){
      window.addEventListener('keydown', function(e){
        switch(true){
          case(e.key === 'ArrowLeft' && e.shiftKey):
            replacedVideo.currentTime = originalVideo.currentTime = originalVideo.currentTime - SHIFTARROW;
            break;
          case(e.key === 'ArrowRight' && e.shiftKey):
            replacedVideo.currentTime = originalVideo.currentTime = originalVideo.currentTime + SHIFTARROW;
            break;
        }
      }, true);
    },
    addFooter: function(){
      let footerContainerLinks = site.get.footerContainerLinks();
      if(!footerContainerLinks) return setTimeout(core.addFooter, 1000);
      footerContainerLinks.appendChild(createElement(core.html.footer()));
    },
    addStyle: function(){
      let style = createElement(core.html.style());
      document.head.appendChild(style);
    },
    html: {
      fileButton: () => `
        <button class="ActionButton ControllerButton FileButton" type="button" data-title="ファイルに差し替える">
          <div class="ControllerButton-inner">
            <!-- https://www.onlinewebfonts.com/icon/112309 -->
            <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" enable-background="new 0 0 1000 1000" viewBox="0 0 1000 1000" xml:space="preserve">
              <metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>
              <g><path d="M581.7,10H132.5v980h735V295.8L581.7,10z M785.8,908.3H214.2V91.7h374.5l197.1,197.1V908.3z"></path><path d="M540.8,10v326.7h326.7L540.8,10z"></path><path d="M377.5,418.3V745l285.8-163.3L377.5,418.3z"></path></g>
            </svg>
          </div>
          <input type="file" id="${SCRIPTNAME}-file">
        </button>
      `,
      replacedVideo: () => `
        <video preload="auto" id="${SCRIPTNAME}-replaced" ${DEBUG ? 'controls' : ''}>
      `,
      footer: () => `
        <li><a href="http://www.onlinewebfonts.com">oNline Web Fonts</a></li>
      `,
      style: () => `
        <style type="text/css">
          input#${SCRIPTNAME}-file{
            display: none;
          }
          video#${SCRIPTNAME}-replaced{
            transition: opacity .5s;
            opacity: 0;
            z-index: 100;
            position: absolute;
            width: 100%;
            height: 100%;
            top: 0px;
            left: 0px;
            bottom: 0px;
            right: 0px;
            display: block;
          }
          video#${SCRIPTNAME}-replaced.loaded{
            opacity: 1;
          }
        </style>
      `,
    },
  };
  let $ = function(s){return document.querySelector(s)};
  let $$ = function(s){return document.querySelectorAll(s)};
  let createElement = function(html){
    let outer = document.createElement('div');
    outer.innerHTML = html;
    return outer.firstElementChild;
  };
  let log = function(){
    if(!DEBUG) return;
    let l = log.last = log.now || new Date(), n = log.now = new Date();
    let stack = new Error().stack, callers = stack.match(/^([^/<]+(?=<?@))/gm) || stack.match(/[^. ]+(?= \(<anonymous)/gm) || [];
    console.log(
      SCRIPTNAME + ':',
      /* 00:00:00.000  */ n.toLocaleTimeString() + '.' + n.getTime().toString().slice(-3),
      /* +0.000s       */ '+' + ((n-l)/1000).toFixed(3) + 's',
      /* :00           */ ':' + stack.match(/:[0-9]+:[0-9]+/g)[1].split(':')[1],/*LINE*/
      /* caller.caller */ (callers[2] ? callers[2] + '() => ' : '') +
      /* caller        */ (callers[1] || '')  + '()',
      ...arguments
    );
  };
  core.initialize();
  if(window === top && console.timeEnd) console.timeEnd(SCRIPTNAME);
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址