動画のAPリピート機能 ZenzaWatch用
当前为
// ==UserScript==
// @name Video AB Repeater
// @namespace https://github.com/segabito/
// @description 動画のAPリピート機能 ZenzaWatch用
// @match *://www.nicovideo.jp/*
// @match *://ext.nicovideo.jp/
// @match *://ext.nicovideo.jp/#*
// @match *://ch.nicovideo.jp/*
// @match *://com.nicovideo.jp/*
// @match *://commons.nicovideo.jp/*
// @match *://dic.nicovideo.jp/*
// @exclude *://ads*.nicovideo.jp/*
// @exclude *://www.upload.nicovideo.jp/*
// @exclude *://www.nicovideo.jp/watch/*?edit=*
// @exclude *://ch.nicovideo.jp/tool/*
// @exclude *://flapi.nicovideo.jp/*
// @exclude *://dic.nicovideo.jp/p/*
// @include *://www.youtube.com/*
// @version 0.0.1
// @grant none
// @author segabito macmoto
// @license public domain
// @noframes
// @require https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js
// @require https://cdnjs.cloudflare.com/ajax/libs/fetch/2.0.1/fetch.js
// ==/UserScript==
(() => {
const PRODUCT = 'Repeater';
const monkey = function(PRODUCT) {
const console = window.console;
let ZenzaWatch = null;
console.log(`exec ${PRODUCT}..`);
const CONSTANT = {
BASE_Z_INDEX: 150000
};
const product = {debug: {_const: CONSTANT}};
window[PRODUCT] = product;
const {util, Emitter} = (function() {
const util = {};
class Emitter {
on(name, callback) {
if (!this._events) { this._events = {}; }
name = name.toLowerCase();
if (!this._events[name]) {
this._events[name] = [];
}
this._events[name].push(callback);
}
clear(name) {
if (!this._events) { this._events = {}; }
if (name) {
this._events[name] = [];
} else {
this._events = {};
}
}
emit(name) {
if (!this._events) { this._events = {}; }
name = name.toLowerCase();
if (!this._events.hasOwnProperty(name)) { return; }
const e = this._events[name];
const arg = Array.prototype.slice.call(arguments, 1);
for (let i =0, len = e.length; i < len; i++) {
e[i].apply(null, arg);
}
}
emitAsync(...args) {
window.setTimeout(() => {
this.emit(...args);
}, 0);
}
}
util.emitter = new Emitter();
return {util, Emitter};
})(PRODUCT);
product.util = util;
const ZenzaDetector = (function() {
let isReady = false;
const emitter = new Emitter();
const onZenzaReady = () => {
isReady = true;
ZenzaWatch = window.ZenzaWatch;
emitter.emit('ready', window.ZenzaWatch);
};
if (window.ZenzaWatch && window.ZenzaWatch.ready) {
window.console.log('ZenzaWatch is Ready');
isReady = true;
} else {
document.body.addEventListener('ZenzaWatchInitialize', () => {
window.console.log('ZenzaWatchInitialize');
onZenzaReady();
});
}
const detect = function() {
return new Promise(res => {
if (isReady) {
return res(window.ZenzaWatch);
}
emitter.on('ready', () => {
res(window.ZenzaWatch);
});
});
};
return {detect};
})();
class Repeater extends Emitter {
constructor(params) {
super();
this._timer = null;
this._currentTime = params.currentTime;
this._notifier = params.notifier;
}
setA() {
this._a = this.currentTime;
if (this._b < this._a) {
this._b = this._currentTime.duration();
}
this.notify('set "["');
this.enable();
}
setB() {
this._b = this.currentTime;
if (this._b < this._a || this._a < 0) {
this._a = Math.max(this._b - 5, 0);
}
this.notify('set "]"');
this.enable();
}
resetA() {
this._a = -1;
this.notify('reset "["');
}
resetB() {
this._b = -1;
this.notify('reset "]"');
}
jumpToA() {
this.currentTime = Math.max(this._a, 0);
}
_reset() {
this._a = -1;
this._b = -1;
}
get currentTime() {
return this._currentTime.get();
}
set currentTime(v) {
this._currentTime.set(v);
}
notify(msg) {
this._notifier.notify(msg);
}
enable() {
if (this._timer) { return; }
console.info('start timer', this._timer, this._rate);
this._timer = setInterval(this._onTimer.bind(this), 100);
}
disable() {
clearInterval(this._timer);
this._reset();
this._timer = null;
}
onVideoChange() {
this._reset();
}
_onTimer() {
if (this._b < 0) { return; }
if (this.currentTime > this._b) {
this.currentTime = Math.max(this._a, 0);
}
}
}
const KeyEmitter = (() => {
const emitter = new Emitter();
const onKeyDown = e => {
if (e.target.tagName === 'SELECT' ||
e.target.tagName === 'INPUT' ||
e.target.tagName === 'TEXTAREA') {
return;
}
let keyCode = e.keyCode +
(e.metaKey ? 0x1000000 : 0) +
(e.altKey ? 0x100000 : 0) +
(e.ctrlKey ? 0x10000 : 0) +
(e.shiftKey ? 0x1000 : 0);
emitter.emit('keydown', keyCode);
};
const onKeyUp = e => {
if (e.target.tagName === 'SELECT' ||
e.target.tagName === 'INPUT' ||
e.target.tagName === 'TEXTAREA') {
return;
}
let keyCode = e.keyCode +
(e.metaKey ? 0x1000000 : 0) +
(e.altKey ? 0x100000 : 0) +
(e.ctrlKey ? 0x10000 : 0) +
(e.shiftKey ? 0x1000 : 0);
switch (keyCode) {
}
emitter.emit('keyup', keyCode);
};
let initialize = () => {
initialize = () => {};
document.body.addEventListener('keydown', onKeyDown);
document.body.addEventListener('keyup', onKeyUp);
};
return {
on: (...args) => { emitter.on(...args); },
off: (...args) => { emitter.off(...args); },
initialize
};
})();
const initExternal = (repeater) => {
product.external = {
repeater
};
product.isReady = true;
const ev = new CustomEvent(`${PRODUCT}Initialized`, { detail: { product } });
document.body.dispatchEvent(ev);
};
const initKey = repeater => {
KeyEmitter.initialize();
KeyEmitter.on('keydown', code => {
switch (code) {
case 219: // [
repeater.setA();
break;
case 221: // ]
repeater.setB();
break;
case 219 + 0x1000:
repeater.jumpToA();
break;
case 221 + 0x1000:
repeater.resetB();
break;
}
});
};
const initNico = () => {
let repeater;
ZenzaDetector.detect().then(() => {
const ZenzaWatch = window.ZenzaWatch;
const currentTime = {
get: () => {
return ZenzaWatch.external.getVideoElement().currentTime;
},
set: (v) => {
ZenzaWatch.external.getVideoElement().currentTime = v;
},
duration: () => {
return ZenzaWatch.external.getVideoElement().duration;
}
};
const notifier = {
notify: msg => {
ZenzaWatch.external.execCommand('notify', msg);
}
};
let dialog;
ZenzaWatch.emitter.on('DialogPlayerOpen', () => {
if (!dialog) {
dialog = ZenzaWatch.debug.dialog;
dialog.on('loadVideoInfo', () => {
repeater.onVideoChange();
});
}
});
ZenzaWatch.emitter.on('DialogPlayerClose', () => {
repeater.disable();
});
repeater = new Repeater({currentTime, notifier});
initKey(repeater);
initExternal(repeater);
});
};
const initTube = () => {
let video = document.querySelector('video.html5-main-video');
const getVideoElement = () => {
if (!video) {
video = document.querySelector('video.html5-main-video');
}
return video;
};
const currentTime = {
get: () => {
return getVideoElement().currentTime;
},
set: (v) => {
getVideoElement().currentTime = v;
},
duration: () => {
return getVideoElement().duration;
}
};
const notifier = {
notify: msg => {
console.log('%c%s', 'background: lightgreen;', msg);
}
};
let lastPath = location.pathname + location.search;
let repeater = new Repeater({currentTime, notifier});
initKey(repeater);
const onUpdatePage = () => {
if (lastPath !== location.pathname + location.search) {
lastPath = location.pathname + location.search;
repeater.onVideoChange();
}
};
window.setInterval(onUpdatePage, 1000);
};
if (location.host.match(/^[a-z0-9_-]+\.nicovideo\.jp$/)) {
initNico();
} else if (location.host.match(/youtube\.com$/)) {
initTube();
}
};
(() => {
const script = document.createElement('script');
script.id = `${PRODUCT}Loader`;
script.setAttribute('type', 'text/javascript');
script.setAttribute('charset', 'UTF-8');
script.appendChild(document.createTextNode(
`(${monkey})("${PRODUCT}");`
));
document.body.appendChild(script);
})();
})();