// ==UserScript==
// @name 動畫瘋-自動觀看廣告
// @namespace https://shinoharahare.github.io/
// @version 0.4
// @description 自動觀看動畫瘋廣告
// @author Hare
// @match https://ani.gamer.com.tw/animeVideo.php?sn=*
// @match https://imasdk.googleapis.com/js/core/bridge*
// @grant GM.getValue
// @grant GM.setValue
// @grant GM.addValueChangeListener
// @grant GM.registerMenuCommand
// @grant GM.unregisterMenuCommand
// @require https://cdn.jsdelivr.net/npm/sweetalert2@9
// ==/UserScript==
(async () => {
'use strict';
window.config = new Proxy({
skipTime: 30,
pauseOnSkipped: true,
muteAd: true
}, {
async get(target, key) {
if (key in target) {
let type = target[key].constructor
let val = await GM.getValue(key, target[key])
return type(val)
} else {
throw 'Unknown Config'
}
},
async set(target, key, value) {
if (key in target) {
await GM.setValue(key, value)
} else {
throw 'Unknown Config'
}
return true
}
})
if (location.href.includes('ani.gamer.com.tw')) {
if (await hasAd()) {
let btn = await wait(() => document.querySelector('#adult'))
btn.click()
if (hasImaAd()) {
registerMessageHandlerTop()
let video = document.querySelector('video[title=Advertisement]')
if (await config.muteAd) {
video.addEventListener('play', ({ target }) => target.muted = true)
}
await sleep(await config.skipTime * 1000)
await postMessage2Ima({ type: 'skip' })
}
let player = videojs.getPlayer('ani_video')
if (!skipped()) {
if (await config.muteAd) {
player.muted(true)
}
let skipBtn = await wait(() => document.querySelector('div[class*="skip"]'), skipped)
await wait(() => skipBtn.innerText.includes('跳過'), skipped)
skipBtn.click()
}
if (await config.pauseOnSkipped) {
const video = document.querySelector('#ani_video_html5_api')
video.addEventListener('loadeddata', () => {
const t = () => video.pause() || video.removeEventListener('play', t)
video.addEventListener('play', t)
})
}
if (await config.muteAd) {
player.muted(false)
}
}
} else if (location.href.includes('imasdk.googleapis.com')) {
if (window.self != window.top) {
registerMessageHandlerIma()
}
}
})();
(async () => {
if (window.self === window.top) {
let ids = []
let names = ['pauseOnSkipped', 'muteAd']
async function loadMenu() {
for (let id of ids) {
GM.unregisterMenuCommand(id)
}
ids = []
registerMenuCommand('修改跳過秒數', async () => {
const { isConfirmed, value } = await swal.fire({
title: '修改跳過秒數',
input: 'number',
inputValue: await config.skipTime,
showCancelButton: true,
})
if (isConfirmed) {
config.skipTime = value
}
}, 'D')
registerSwitch('跳過後暫停', 'pauseOnSkipped', 'P')
registerSwitch('靜音廣告', 'muteAd', 'M')
}
async function registerMenuCommand(...args) {
const id = await GM.registerMenuCommand(...args)
ids.push(id)
}
async function registerSwitch(title, name, accessKey) {
registerMenuCommand(`[${await config[name] ? '停用' : '啟用'}] ${title}`, async() => config[name] = !await config[name], accessKey)
}
for (let name of names) {
GM.addValueChangeListener(name, () => loadMenu())
}
loadMenu()
}
})();
function hasImaAd() {
return document.querySelector('video[title=Advertisement]') != null
}
function skipped() {
return videojs.getPlayer('ani_video').adFinished
}
function registerMessageHandlerTop() {
window.resolves = {}
window.addEventListener('message', ({ data }) => {
const id = data._id
if (id in window.resolves) {
window.resolves[id]()
delete window.resolves[id]
}
})
}
function registerMessageHandlerIma() {
window.addEventListener('message', ({ data, source }) => {
if (data._id) {
switch (data.type) {
case 'skip': {
document.querySelector('.videoAdUiSkipButton').click()
break
}
}
source.postMessage(data, 'https://ani.gamer.com.tw')
}
})
}
function postMessage2Ima(msg) {
const win = document.querySelector('iframe[src^="https://imasdk.googleapis.com/js/core/"]').contentWindow
return new Promise(resolve => {
const id = performance.now()
window.resolves[id] = resolve
win.postMessage({
_id: id,
...msg
}, 'https://imasdk.googleapis.com')
})
}
async function hasAd() {
const res = await fetch(`/ajax/token.php?sn=${animefun.videoSn}`)
const json = await res.json()
return json.time == 0
}
function wait(condition, canceler, responser, tick=100) {
return new Promise((resolve) => {
let i = setInterval(() => {
let c = condition()
if (canceler && canceler()) {
clearInterval(i)
} else if (c) {
clearInterval(i)
resolve(responser ? responser() : c)
}
}, tick);
})
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}