AbemaTV Shortcut Key Controller

AbemaTVでショートカットキーやマウスホイールによる操作を可能にします。キーアサインはYouTube準拠。

目前为 2017-10-17 提交的版本。查看 最新版本

// ==UserScript==
// @name        AbemaTV Shortcut Key Controller
// @namespace   knoa.jp
// @description AbemaTVでショートカットキーやマウスホイールによる操作を可能にします。キーアサインはYouTube準拠。
// @include     https://abema.tv/*
// @version     2.0.2
// @grant       none
// ==/UserScript==
/*
共通:
[F]: フルスクリーン
[M]: ミュート
[マウスホイール]: 音量調整

リアルタイム放送:
[K], [Space], [Enter]: コメント入力欄フォーカス
[C]: コメント表示

タイムシフト放送:
[K], [Space], [Enter]: 再生・停止
[J], [←]: 10秒戻る
[L], [→]: 10秒進む
*/

// console.log('AbemaTV? => hireMe()');
(function(){
  const SCRIPTNAME = 'ShortcutKeyController';
  const DEBUG = false;/**/
  if(window === top) console.time(SCRIPTNAME);
  let site = {
    elements: {
      /* 共通 */
      fullscreenButton: function(){return $('use[*|href*="_screen.svg"]'/*タイムシフトのbuttonにaria-labelがないので*/).parentNode.parentNode},
      volumeSlider: function(){return $('button[aria-label="音声オンオフ切り替え"]').previousSibling.firstElementChild.firstElementChild},
      muteButton: function(){return $('button[aria-label="音声オンオフ切り替え"]')},
      /* リアルタイム */
      commentButton: function(){return $('use[*|href^="/images/icons/comment.svg"]').parentNode.parentNode},
      commentTextarea: function(){return $('textarea[placeholder="コメントを入力"]')},
      footer: function(){return $('button[aria-label^="フルスクリーン"]').parentNode.parentNode},
      closer: function(){return $('form:not([role="search"])').parentNode.parentNode.nextElementSibling;},
      /* タイムシフト */
      playButton: function(){return $((getComputedStyle($('use[*|href^="/images/icons/pause.svg"]')).cursor === 'pointer') ? 'use[*|href^="/images/icons/pause.svg"]' : 'use[*|href^="/images/icons/playback.svg"]').parentNode.parentNode},
      rewindButton: function(){return $('use[*|href^="/images/icons/rewind_10.svg"]').parentNode.parentNode},
      advancesButton: function(){return $('use[*|href^="/images/icons/advances_10.svg"]').parentNode.parentNode},
    },
    isCommentPaneHidden: function(){
      let form = $('form:not([role="search"])');
      return (form) ? (form.parentNode.parentNode.getAttribute('aria-hidden') === 'true') : false;
    },
    modifyVolume: function(e){
      let slider = site.elements.volumeSlider(), rect = slider.getBoundingClientRect();
      let volume = parseInt(slider.firstElementChild.style.height) / rect.height;
      switch(e.deltaMode){
        case(WheelEvent.DOM_DELTA_PIXEL):
          volume += -(e.deltaY/1000);
          break;
        case(WheelEvent.DOM_DELTA_LINE):
        default:
          volume += (0 < e.deltaY) ? -(1/10) : (1/10);
          break;
      }
      slider.dispatchEvent(new MouseEvent('mousedown', {
        clientX: rect.x + (rect.width/2),
        clientY: rect.y + (rect.height * (1 - volume)),
        bubbles: true,
      }));
    },
    assign: function(e){
      switch(true){
        case(location.href.startsWith('https://abema.tv/now-on-air/')):
          return core.realtime(e);
        case(location.href.startsWith('https://abema.tv/channels/')):
        case(location.href.startsWith('https://abema.tv/video/watch/')):
          return core.timeshift(e);
      }
    },
  };
  let core = {
    initialize: function(){
      window.addEventListener('keydown', site.assign, true);
      window.addEventListener('wheel', site.assign, true);
    },
    /* リアルタイム */
    realtime: function(e){
      switch(true){
        /* テキスト入力中は反応しない */
        case(['input', 'textarea', 'button'].includes(document.activeElement.localName)):
          break;
        /* Alt/Shift/Ctrl/Meta */
        case(e.altKey || e.shiftKey || e.ctrlKey || e.metaKey):
          break;
        /* コメント入力欄フォーカス */
        case(e.key == 'k'):
        case(e.key == ' '):
        case(e.key == 'Enter'):
          /* コメント欄が表示されていなければあらかじめ表示しておく */
          if(site.isCommentPaneHidden()) site.elements.commentButton().click();
          site.elements.commentTextarea().focus();
          return e.preventDefault();
        /* コメント */
        case(e.key == 'c'):
          if(site.isCommentPaneHidden()) site.elements.commentButton().click();
          else site.elements.closer().click();
          return e.preventDefault();
        /* フルスクリーン */
        case(e.key == 'f'):
          site.elements.fullscreenButton().click();
          return e.preventDefault();
        /* ミュート */
        case(e.key == 'm'):
          site.elements.muteButton().click();
          return e.preventDefault();
        /* 音量 */
        case(e.type === 'wheel'):
          /* あらゆる場所でのイベントを拾ってwindow.addEventListenerで一括処理する代償をここで支払う */
          for(let target = e.target; target; target = target.parentNode){
            if([site.elements.closer(), site.elements.footer()].includes(target)){
              site.modifyVolume(e);
              return e.preventDefault();
            }
          }
      }
    },
    /* タイムシフト */
    timeshift: function(e){
      switch(true){
        /* テキスト入力中は反応しない */
        case(['input', 'textarea', 'button'].includes(document.activeElement.localName)):
          break;
        /* Alt/Shift/Ctrl/Meta */
        case(e.altKey || e.shiftKey || e.ctrlKey || e.metaKey):
          break;
        /* 再生・停止トグル */
        case(e.key == 'k'):
        case(e.key == ' '):
        case(e.key == 'Enter'):
          site.elements.playButton().click();
          return e.preventDefault();
        /* 10秒戻る */
        case(e.key == 'j'):
        case(e.key == 'ArrowLeft'):
          site.elements.rewindButton().click();
          return e.preventDefault();
        /* 10秒進む */
        case(e.key == 'l'):
        case(e.key == 'ArrowRight'):
          site.elements.advancesButton().click();
          return e.preventDefault();
        /* フルスクリーン */
        case(e.key == 'f'):
          site.elements.fullscreenButton().click();
          return e.preventDefault();
        /* ミュート */
        case(e.key == 'm'):
          site.elements.muteButton().click();
          return e.preventDefault();
        /* 音量 */
        case(e.type === 'wheel'):
          site.modifyVolume(e);
          return e.preventDefault();
      }
    },
  };
  let $ = function(s){return document.querySelector(s)};
  let log = (DEBUG) ? console.log.bind(null, SCRIPTNAME + ':') : function(){};
  core.initialize();
  if(window === top) console.timeEnd(SCRIPTNAME);
})();

QingJ © 2025

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