// ==UserScript== // @name AbemaTV Auto Reload // @namespace https://greasyfork.org/ja/scripts/25598 // @version 2 // @description AbemaTV(HTML5版)を閲覧中に動画が止まったとき、チャンネルを切り替えて動画を再読み込みします。 // @include https://abema.tv/now-on-air/* // @grant none // @downloadURL none // ==/UserScript== (function() { 'use strict'; function log(s, t) { if (ls.debug) { if (t) console[t]('AutoReload', s); else console.log('AutoReload', 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 = 0, j = s.length; i < j; i++) { a.unshift(s[i]); } } 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(a, t, s) { var title = 'AbemaTV Auto Reload', message = a, notifi; log(['notify', a], t); if ('Notification' in window) { if (Notification.permission === 'granted') { notifi = new Notification(title, { body: message }); } else if (Notification.permission !== 'denied') { Notification.requestPermission(function(permission) { if (permission === 'granted') { notifi = new Notification(title, { body: message }); } }); } if (notifi) setTimeout(notifi.close.bind(notifi), s ? s : 3000); } else alert(title + '\n\n' + message); } //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]); } else log('returnChLogo error', 'error'); } return e; } //動画のチャンネルを切り替えて読み直す function reloadVideo(s) { logV(['reloadVideo', s], 'warn'); if (!flag.reload && !document.hidden) { flag.reload = true; flag.loadstart = false; if (flag.countReload > 3) { flag.countReload = 0; reloadPage(); } else { flag.countReload++; eNext.click(); } } } //ページを再読み込みする function reloadPage() { notify('ページを再読み込みします!'); if (flag.reload) { setTimeout(function() { location.reload(); }, 500); } else location.reload(); } //動画が切り替わったかを調べる function checkChangeV(a) { logV('checkChangeV'); var e = returnVideo('checkChangeV'); if (!flag.change1 && !e) { log(['checkChangeV1', a]); flag.change1 = true; setTimeout(function() { flag.change1 = false; checkChangeV('checkChangeV'); }, 550); } else if (!flag.change2 && e && (!eV || e.id !== eV.id)) { log(['checkChangeV2', a]); flag.change2 = true; setTimeout(function() { addEventVideo('checkChangeV'); flag.change2 = false; }, 1000); } else { log(['checkChangeV3', a]); checkPlayingVideo(e.currentTime, 'checkChangeV3'); } } //動画の停止&エラープロパティがtrueになっているかを調べる function checkPausedErrorVideo(s) { if (!flag.checkPausedErrorVideo) logV(['checkPausedErrorVideo', s]); if (!flag.reload && eV.paused && eV.error) reloadVideo('checkPausedErrorVideo'); else if (!flag.checkPausedErrorVideo) { flag.checkPausedErrorVideo = true; checkChangeV('checkPausedErrorVideo'); setTimeout(function() { flag.checkPausedErrorVideo = false; }, 1500); } } //動画が実際に再生中なのかを調べる function checkPlayingVideo(o, s) { setTimeout(function() { var now = eV.currentTime; log([s, 'time', o, now]); if (!now || now === o) reloadVideo(s); }, 500); } //動画の取得中にエラーが発生したとき function videoError() { logV('videoError'); if (flag.reload !== undefined && flag.reload === false && eV.error) { var old = eV.currentTime; if (!old) reloadVideo('videoError'); else checkPlayingVideo(old, 'videoError'); } else checkPausedErrorVideo('videoError'); } //動画をこれから読み込むとき function videoLoadstart() { logV('videoLoadstart'); if (!flag.loadstart && eV.readyState < 3 || eV.networkState !== 2) { flag.loadstart = true; checkPausedErrorVideo('videoLoadstart'); setTimeout(function() { try { if (!eV) checkChangeV('videoLoadstart'); else if (!eV.paused && !eV.played.length) reloadVideo('videoLoadstart'); else if (flag.loadstart && eV.readyState === 2 && eV.networkState === 2) reloadVideo('videoLoadstart'); else checkPausedErrorVideo('videoLoadstart'); } catch (error) { logV(['videoLoadstart error', error, eV], 'error'); } flag.loadstart = false; }, 10000); } } //動画を一時停止したとき function videoPause() { checkPausedErrorVideo('videoPause'); } //動画を再生し始めたときや再生中にソースが切り替わったとき function videoPlaying() { logV('videoPlaying'); if (flag.countReload) flag.countReload = 0; } //動画を取得できなかったときや取得し終えたとき function videoStalled() { logV('videoStalled'); if (eV && (eV.readyState < 3 || eV.networkState !== 2)) { logV('videoStalled'); if (eV.readyState <= 1) { if (!eV || !eV.hasOwnProperty('readyState') || !eV.hasOwnProperty('networkState')) { reloadVideo('videoStalled'); return; } var old = eV.currentTime; if (!old) reloadVideo('videoStalled'); else checkPlayingVideo(old, 'videoStalled'); } else checkPausedErrorVideo('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 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) 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) { var e = returnVideo('addEventVideo1'); if (!e) { checkChangeV('addEventVideo1'); return; } setTimeout(function() { eV = returnVideo('addEventVideo2'); if (!eV) { checkChangeV('addEventVideo2'); return; } eV.addEventListener('error', videoError, false); eV.addEventListener('loadstart', videoLoadstart, false); eV.addEventListener('pause', videoPause, 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('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); } }, 100); } } //動画が表示されるのを待つ function waitShowVideo(s) { log(['waitShowVideo', s, flag.show]); if (!flag.show) { flag.show = true; clearInterval(interval); setTimeout(function() { eV = returnVideo('waitShowVideo'); if (eV) { if (s === 'init') startObserve(); else addEventVideo('waitShowVideo1'); flag.show = false; } else { clearInterval(interval); interval = setInterval(function() { eV = returnVideo('waitShowVideo'); if (eV) { clearInterval(interval); if (s === 'init') startObserve(); else addEventVideo('waitShowVideo2'); flag.show = false; } else if (flag.countWaitShowVideo > 100) clearInterval(interval); flag.countWaitShowVideo++; }, 100); } }, 400); } if (s === 'observerC') { if (flag.reload) { flag.reload = false; ePrev.click(); } returnChLogo('waitShowVideo-observerC'); } } 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'); waitShowVideo('init'); } var ls = JSON.parse(localStorage.getItem('AutoReload')) || {}, observerC = new MutationObserver(waitShowVideoC), observerV = new MutationObserver(waitShowVideoV), moConfig = { attributes: true, characterData: true }, moConfig2 = { childList: true }, flag = { change1: false, change2: false, checkPausedErrorVideo: false, countReload: 0, countWaitShowVideo: 0, hidden: false, loadstart: false, reload: false, show: false }, eV, ePrev, eNext, chId, interval; init(); })();