Greasy Fork

AbemaTV Auto Reload

AbemaTV(HTML5版)を閲覧中に動画が止まったとき、動画を再読み込みします。

当前为 2016-12-18 提交的版本,查看 最新版本

// ==UserScript==
// @name         AbemaTV Auto Reload
// @namespace    https://greasyfork.org/ja/scripts/25598
// @version      3
// @description  AbemaTV(HTML5版)を閲覧中に動画が止まったとき、動画を再読み込みします。
// @include      https://abema.tv/now-on-air/*
// @grant        none
// ==/UserScript==

(function() {
  'use strict';


  /* ---------- Settings ---------- */

  // 変更した値はWebブラウザのローカルストレージに保存するので
  // スクリプトをバージョンアップするたびに書き換える必要はありません。
  // (値が0のとき、以前に変更した値か初期値を使用します)

  // 動画が止まったとき、チャンネルを切り替えるまでの待ち時間(ミリ秒)
  // 初期値:1500
  // 有効値:1000~30000
  var waitingReloadChannel = 0;

  /* ------------------------------ */


  function log(s, t) {
    if (ls.debug) {
      if (t) console[t](sid, s);
      else console.log(sid, s);
    }
  }

  function logV(s, t) {
    if (ls.debug) {
      try {
        var v = returnVideo('logV');
        if (v) {
          var a = [
            v.readyState, v.networkState, v.played.length, v.currentTime, v.id.slice(v.id.indexOf('-') + 1),
            { 'duration': v.duration, 'seeking': v.seeking, 'preload': v.preload, 'ended': v.ended, 'srcLength': v.src.length },
            Object.assign({}, flag)
          ];
          if (Array.isArray(s)) {
            for (var i = s.length; i > 0; i--) {
              a.unshift(s[i - 1]);
            }
          } else a.unshift(s);
          if (v.paused) a.push('paused');
          if (v.error) a.push('error', v.error.code);
          log(a, t);
        } else log(['logV not found Video', s], 'warn');
      } catch (error) { log(['logV error', s, error], 'error'); }
    }
  }

  //デスクトップ通知
  function notify(f, m, t, s) {
    var title = 'AbemaTV Auto Reload',
      message = m,
      notifi;
    log(['notify', f, m], t);
    if ('Notification' in window) {
      if (Notification.permission === 'granted') {
        notifi = new Notification(title, { body: message, tag: f });
      } else if (Notification.permission !== 'denied') {
        Notification.requestPermission(function(permission) {
          if (permission === 'granted') {
            notifi = new Notification(title, { body: message, tag: f });
          }
        });
      }
      if (notifi) setTimeout(notifi.close.bind(notifi), s ? s : 3000);
    } else console.log(title, message);
  }

  //ローカルストレージに設定を保存する
  function saveLocalStorage() {
    localStorage.setItem(sid, JSON.stringify(ls));
  }

  //HTML5動画の要素を返す
  function returnVideo(s) {
    var e = document.getElementsByTagName('video') || null,
      v;
    if (!e) log(['returnVideo error', s], 'debug');
    else {
      var len = e.length;
      v = e[0];
      if (len > 1) {
        for (var i = 0; i < len; i++) {
          if (e[i].id && /^videoplayer/.test(e[i].id)) {
            v = e[i];
            break;
          }
        }
      }
    }
    return v;
  }

  //左下のチャンネルロゴ画像の要素を返す
  function returnChLogo(s) {
    var e = document.querySelector('div[class*="styles__channel-logo"] > img');
    if (s) {
      if (e) {
        chId = e.getAttribute('alt');
        log(['chId', chId], 'debug');
      } else log('returnChLogo error', 'error');
    }
    return e;
  }

  //動画を再読み込みする
  function reloadVideo(s) {
    logV(['reloadVideo', s]);
    if (!document.hidden && !flag.changeChannel && !flag.undoChannel) {
      if (theoplayer.player(0) && (!flag.reloadTime || (flag.reloadTime && Date.now() - flag.reloadTime < waitingReloadChannel))) {
        if (!flag.reloadTime) flag.reloadTime = Date.now();
        theoplayer.player(0).load();
        setTimeout(function() {
          checkPlayingVideo(eV.currentTime, 'reloadVideo');
        }, 500);
      } else {
        if (!flag.changeChannel && !flag.undoChannel) changeChannel('reloadVideo');
        else if (!flag.reload) {
          flag.reload = true;
          reloadPage(s);
        }
      }
    }
  }

  //チャンネルを切り替える
  function changeChannel(s) {
    log(['changeChannel', s], 'debug');
    flag.reloadTime = 0;
    flag.changeChannel = true;
    eNext.click();
  }

  //ページを再読み込みする
  function reloadPage(s) {
    if (flag.reload) {
      log(['reloadPage', s], 'debug');
      notify('reloadPage', 'ページを再読み込みします!');
      setTimeout(function() {
        location.reload();
      }, 500);
    }
  }

  //動画が切り替わったかを調べる
  function checkChangeVideo(a) {
    logV('checkChangeVideo');
    var e = returnVideo('checkChangeVideo');
    if (!flag.change1 && !e) {
      log(['checkChangeVideo1', a]);
      flag.change1 = true;
      setTimeout(function() {
        flag.change1 = false;
        checkChangeVideo('checkChangeVideo');
      }, 550);
    } else if (!flag.change2 && e && (!eV || e.id !== eV.id)) {
      log(['checkChangeVideo2', a]);
      flag.change2 = true;
      setTimeout(function() {
        flag.change2 = false;
        addEventVideo('checkChangeVideo');
      }, 1000);
    } else {
      log(['checkChangeVideo3', a]);
      checkPlayingVideo(e.currentTime, 'checkChangeVideo3');
    }
  }

  //動画が実際に再生中なのかを調べる
  function checkPlayingVideo(o, s) {
    setTimeout(function() {
      if (!eV || !eV.currentTime) {
        setTimeout(function() {
          checkChangeVideo('checkPlayingVideo');
        }, 500);
        return;
      }
      var now = eV.currentTime;
      log(['checkPlayingVideo', s, 'time', o, now]);
      if (!now) {
        setTimeout(function() {
          var old = eV.currentTime;
          setTimeout(function() {
            now = eV.currentTime;
            if (!now || now === old) reloadVideo(s);
          }, 100);
        }, 2000);
      } else if (now === o) reloadVideo(s);
    }, 100);
  }

  //動画の取得中にエラーが発生したとき
  function videoError() {
    logV('videoError');
    checkPlayingVideo(eV.currentTime, 'videoError');
  }

  //動画を再生し始めたときや再生中にソースが切り替わったとき
  function videoPlaying() {
    logV('videoPlaying');
    if (flag.reloadTime) {
      flag.reloadTime = 0;
    }
    if (flag.undoChannel) flag.undoChannel = false;
  }

  //動画を取得できなかったときや取得し終えたとき
  function videoStalled() {
    logV('videoStalled');
    if (eV && (eV.readyState < 3 || eV.networkState !== 2)) checkPlayingVideo(eV.currentTime, 'videoStalled');
  }

  //動画の読み込みを待っているとき
  function videoWaiting() {
    logV('videoWaiting');
    var old = eV.currentTime;
    if (!old) reloadVideo('videoWaiting');
    else checkPlayingVideo(old, 'videoWaiting');
  }

  //動画を再生できるがキャッシュが足りないとき
  function videoCanplay() { logV('videoCanplay'); }
  //動画を再生できてキャッシュも足りるとき
  function videoCanplaythrough() { logV('videoCanplaythrough'); }
  //動画の長さが変更されたとき
  function videoDurationchange() {
    if (eV.readyState < 3 || eV.networkState !== 2) logV('videoDurationchange');
  }
  //動画が空になったとき
  function videoEmpied() { logV('videoEmpied'); }
  //動画を最後まで再生したとき
  function videoEnded() { logV('videoEnded'); }
  //動画のメタ情報を読み込んだとき
  function videoLoadedmetadata() { logV('videoLoadedmetadata'); }
  //動画を再生できる状態になったとき
  function videoLoadeddata() { logV('videoLoadeddata'); }
  //動画をこれから読み込むとき
  function videoLoadstart() { logV('videoLoadstart'); }
  //動画を一時停止・解除したとき
  function videoPause() { logV('videoPause'); }
  //動画を(手動操作で)再生し始めたとき
  function videoPlay() { logV('videoPlay'); }
  //動画を取得しているとき
  function videoProgress() { logV('videoProgress'); }
  //動画をシークし終えたとき
  function videoSeeked() { logV('videoSeeked'); }
  //動画をシークし始めたとき
  function videoSeeking() { logV('videoSeeking'); }
  //動画の取得を取りやめたとき
  function videoSuspend() { logV('videoSuspend'); }
  //動画の再生位置が更新されたとき
  function videoTimeupdate() {
    if (eV.readyState < 3 || eV.networkState !== 2) logV('videoTimeupdate');
  }

  //ページが見えている状態かを調べる
  function checkPageVisibility() {
    var h = document.hidden;
    log(['checkPageVisibility', h]);
    if (flag.hidden && !h && !eV.paused) checkPlayingVideo(eV.currentTime, 'checkPageVisibility');
    flag.hidden = h;
  }

  //ページにイベントリスナーを追加
  function addEventPage() {
    log('addEventPage');
    document.addEventListener('visibilitychange', checkPageVisibility, false);
  }

  //動画にイベントリスナーを追加
  function addEventVideo(s) {
    logV(['addEventVideo', s]);
    if (!flag.reload) {
      eV = returnVideo('addEventVideo2');
      if (!eV) {
        checkChangeVideo('addEventVideo2');
        return;
      }
      if (!eV.classList.contains(sid)) {
        eV.classList.add(sid);
        eV.addEventListener('error', videoError, false);
        eV.addEventListener('playing', videoPlaying, false);
        eV.addEventListener('stalled', videoStalled, false);
        eV.addEventListener('waiting', videoWaiting, false);
        if (ls.debug) {
          eV.addEventListener('canplay', videoCanplay, false);
          eV.addEventListener('canplaythrough', videoCanplaythrough, false);
          eV.addEventListener('durationchange', videoDurationchange, false);
          eV.addEventListener('emptied', videoEmpied, false);
          eV.addEventListener('ended', videoEnded, false);
          eV.addEventListener('loadeddata', videoLoadeddata, false);
          eV.addEventListener('loadedmetadata', videoLoadedmetadata, false);
          eV.addEventListener('loadstart', videoLoadstart, false);
          eV.addEventListener('pause', videoPause, false);
          eV.addEventListener('play', videoPlay, false);
          eV.addEventListener('progress', videoProgress, false);
          eV.addEventListener('seeked', videoSeeked, false);
          eV.addEventListener('seeking', videoSeeking, false);
          eV.addEventListener('suspend', videoSuspend, false);
          eV.addEventListener('timeupdate', videoTimeupdate, false);
        }
      }
    }
  }

  //動画が表示されるのを待つ
  function waitShowVideo(s) {
    log(['waitShowVideo', s, flag.show]);
    if (!flag.show) {
      flag.show = true;
      clearInterval(interval);
      setTimeout(function() {
        if (theoplayer.player(0) && !isNaN(theoplayer.player(0).duration)) {
          eV = returnVideo('waitShowVideo');
          if (s === 'init') startObserve();
          else addEventVideo('waitShowVideo1');
          flag.show = false;
        } else {
          clearInterval(interval);
          interval = setInterval(function() {
            if (theoplayer.player(0) && !isNaN(theoplayer.player(0).duration)) {
              clearInterval(interval);
              flag.countWaitShowVideo = 0;
              eV = returnVideo('waitShowVideo');
              if (s === 'init') startObserve();
              else addEventVideo('waitShowVideo2');
              flag.show = false;
            } else if (flag.countWaitShowVideo > 25) {
              clearInterval(interval);
              flag.countWaitShowVideo = 0;
              changeChannel('waitShowVideo');
            }
          }, 200);
        }
      }, 400);
    }
    if (s === 'observerC') {
      returnChLogo('waitShowVideo-observerC');
      if (flag.changeChannel && !flag.undoChannel) {
        log('undoChannel', 'debug');
        flag.changeChannel = false;
        flag.undoChannel = true;
        ePrev.click();
        setTimeout(function() {
          flag.undoChannel = false;
        }, 100);
      }
    }
  }

  function waitShowVideoC() { waitShowVideo('observerC'); }

  function waitShowVideoV() { waitShowVideo('observerV'); }

  //ページを開いて動画が表示されたら1度だけ実行
  function startObserve() {
    log('startObserve');
    ePrev = document.querySelector('div[class^="style__box"] > button:first-child');
    eNext = document.querySelector('div[class^="style__box"] > button:last-child');
    observerC.observe(returnChLogo(), moConfig);
    observerV.observe(eV.parentNode, moConfig2);
    addEventPage();
    addEventVideo('startObserve');
  }

  //ページを開いたときに1度だけ実行
  function init() {
    log('init');
    var rc = (!Number.isInteger(waitingReloadChannel)) ? 0 : waitingReloadChannel;
    rc = (rc === 0) ? 0 : (rc > 30000) ? 30000 : (rc < 1000) ? 1000 : rc;
    waitingReloadChannel = (rc) ? rc : 1500;
    if (ls.waitingReloadChannel && rc && ls.waitingReloadChannel !== rc) {
      ls.waitingReloadChannel = rc;
      saveLocalStorage();
    }
    waitShowVideo('init');
  }

  var sid = 'AutoReload',
    ls = JSON.parse(localStorage.getItem(sid)) || {},
    observerC = new MutationObserver(waitShowVideoC),
    observerV = new MutationObserver(waitShowVideoV),
    moConfig = { attributes: true, characterData: true },
    moConfig2 = { childList: true },
    flag = {
      change1: false,
      change2: false,
      changeChannel: false,
      countWaitShowVideo: 0,
      hidden: false,
      loadstart: false,
      reload: false,
      reloadTime: 0,
      show: false,
      undoChannel: false
    },
    eV, ePrev, eNext, chId, interval;
  init();

})();