// ==UserScript==
// @name AbemaTV Auto Reload
// @namespace http://greasyfork.icu/ja/scripts/25598
// @version 2
// @description AbemaTV(HTML5版)を閲覧中に動画が止まったとき、チャンネルを切り替えて動画を再読み込みします。
// @include https://abema.tv/now-on-air/*
// @grant 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();
})();