// ==UserScript==
// @name Arkrec Wiki Assistant
// @name:zh-CN 明日方舟少人WIKI辅助插件
// @namespace http://tampermonkey.net/
// @version 2.1
// @description Automatically query if this video has been in the arkrec wiki database on video loading
// @description:zh-CN 在打开视频时自动判断是否已上传至少人WIKI数据库,并提供上传捷径链接
// @author philimao
// @match https://www.bilibili.com/video/BV*
// @icon https://www.google.com/s2/favicons?domain=arkrec.com
// @grant unsafeWindow
// @grant GM_registerMenuCommand
// @grant GM_getValue
// @grant GM_setValue
// @license MIT
// ==/UserScript==
const maxRetries = 100;
async function delay(timeout) {
return new Promise(r => setTimeout(r, timeout));
}
async function waitForDom(selector, retries) {
// console.log("[Arkrec]",selector);
let dom, r;
r = retries || maxRetries;
console.log("retries", r);
for (let i = 0; i < r; i++) {
dom = document.querySelector(selector);
if (dom) return dom;
await delay(500);
}
console.log(`[Arkrec] Dom element ${selector} not loaded in given time`);
}
async function waitForVar(v, retries, path) {
let res;
let r = retries || maxRetries;
for (let i = 0; i < r; i++) {
res = v;
if (path) {
for (let key of path) {
res = res[key];
}
}
if (res) return res;
console.log("[Arkrec]", res);
await delay(500);
}
console.log("[Arkrec] variable not loaded in given time");
}
(async function () {
GM_registerMenuCommand(
"Global Mode",
() => GM_setValue("mode", "global"),
"g"
);
GM_registerMenuCommand(
"Exclusive Mode",
() => GM_setValue("mode", "exclusive"),
"e"
);
const wikiBtn = document.createElement("button");
// https://greasyfork.org/en/scripts/398655-%E5%93%94%E5%93%A9%E5%93%94%E5%93%A9%E8%A7%86%E9%A2%91%E9%A1%B5%E9%9D%A2%E5%B8%B8%E9%A9%BB%E6%98%BE%E7%A4%BAav-bv%E5%8F%B7-%E5%B7%B2%E5%AE%8C%E5%85%A8%E9%87%8D%E6%9E%84-%E6%94%AF%E6%8C%81%E6%98%BE%E7%A4%BA%E5%88%86p%E6%A0%87%E9%A2%98
async function playerReady() {
let counter = 0;
return new Promise((resolve) => {
function wait() {
if (
unsafeWindow.player &&
unsafeWindow.player.isInitialized &&
unsafeWindow.player.isInitialized()
) {
return resolve();
}
if (counter === 1000) {
console.log("[Arkrec] Player Timeout, Increase Interval");
}
counter++;
const timeout = counter < 1000 ? 100 : 1000;
if (counter % 10000 === 0)
console.log("[Arkrec] Waiting", counter, timeout);
setTimeout(wait, timeout);
}
setTimeout(wait, 2000);
});
}
async function injection() {
const elem = document.querySelector("[data-inject='arkrec']");
if (elem) elem.remove();
const tags = unsafeWindow.__INITIAL_STATE__.tags.map((el) => el.tag_name);
console.log("[Arkrec]", tags);
if (
["明日方舟", "危机合约", /arknights/i].every(
(re) =>
tags.every((tag) => tag.search(re) < 0) ||
(GM_getValue("mode") === "global" && !tags.includes("手机游戏"))
)
)
return console.log("[Arkrec] No Arknights Tag Detected, Abort Injection");
const username = await waitForVar(unsafeWindow, 100, ["__INITIAL_STATE__","user","uname"]);
if (username === "philimao") {
const playerStyle = document.querySelector(".bpx-player-container") ? 0 : 1;
let x2 = playerStyle ?
await waitForDom(".bilibili-player-video-btn-speed-menu-list[data-value='2']") :
await waitForDom(".bpx-player-ctrl-playbackrate-menu-item[data-value='2']");
if (x2) x2.click();
else console.log("[Arkrec] Speed Icon Not Detected");
}
let host = "https://arkrec.com";
// host = "http://127.0.0.1:3001";
const { bvid, p } = unsafeWindow.__INITIAL_STATE__;
const url =
"https://www.bilibili.com/video/" + bvid + (p === 1 ? "" : "?p=" + p);
try {
const resRaw = await fetch(host + "/api/query-url", {
method: "POST",
mode: "cors",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ url }),
});
let toolbar = document.querySelector(".video-toolbar");
if (toolbar) toolbar.appendChild(wikiBtn);
else {
wikiBtn.style.transform = "none";
toolbar = await waitForDom(".video-toolbar-right");
toolbar.insertBefore(wikiBtn, toolbar.firstChild);
}
if (resRaw.ok) {
console.log("[Arkrec] Record Exists.");
const wikiBtn = document.querySelector("[data-inject='arkrec']");
wikiBtn.style.filter = "invert(0.8)";
wikiBtn.style.pointerEvents = "none";
wikiBtn.setAttribute("disabled", "true");
} else {
console.log("[Arkrec] Wiki Icon Injecting", bvid, "Page", p);
}
} catch (err) {
console.log(err);
document.querySelector(".video-toolbar").appendChild(wikiBtn);
console.log(
"[Arkrec] Fail to Connect to Arkrec Server. Wiki Icon Injecting",
bvid,
"Page",
p
);
}
}
// https://greasyfork.org/en/scripts/398542-bilibili-%E6%98%BE%E7%A4%BA-av-%E5%8F%B7
function registerObserver() {
console.log("[Arkrec] Register Re-Injection Listener");
const target = unsafeWindow.__INITIAL_STATE__;
let videoData = target.videoData;
const desc = Object.getOwnPropertyDescriptor(target, "videoData");
const vueHook = desc.set;
Object.defineProperty(target, "videoData", {
get: desc.get || (() => videoData),
set(data) {
videoData = data;
injection();
if (vueHook) vueHook.call(this, data);
},
enumerable: true,
configurable: true,
});
}
const wikiSvg =
"data:image/svg+xml,%3Csvg%20id%3D%22svg%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20width%3D%22400%22%20height%3D%22176.24521072796932%22%20viewBox%3D%220%2C%200%2C%20400%2C176.24521072796932%22%20version%3D%221.1%22%3E%3Cg%20id%3D%22svgg%22%3E%3Cpath%20id%3D%22path0%22%20d%3D%22M0.000%2081.302%20C%200.000%20178.729%2C-0.539%20176.253%2C20.648%20176.239%20C%2035.296%20176.230%2C36.588%20174.889%2C57.999%20137.469%20C%2068.404%20119.283%2C77.715%20103.341%2C78.689%20102.042%20C%2080.149%20100.093%2C83.915%20105.722%2C100.158%20134.127%20C%20123.163%20174.358%2C124.902%20176.245%2C138.968%20176.245%20C%20148.294%20176.245%2C149.315%20175.895%2C154.176%20171.034%20L%20159.387%20165.824%20159.387%2082.912%20L%20159.387%200.000%20141.762%200.000%20L%20124.138%200.000%20124.138%2052.487%20L%20124.138%20104.974%20107.142%2075.475%20L%2090.146%2045.977%2080.303%2045.977%20L%2070.460%2045.977%2065.395%2054.023%20C%2062.610%2058.448%2C54.860%2071.635%2C48.173%2083.326%20L%2036.015%20104.583%2035.612%2052.292%20L%2035.209%200.000%2017.604%200.000%20L%200.000%200.000%200.000%2081.302%20M163.985%2088.123%20L%20163.985%20176.245%20180.843%20176.245%20L%20197.701%20176.245%20197.701%2088.123%20L%20197.701%200.000%20180.843%200.000%20L%20163.985%200.000%20163.985%2088.123%20M202.299%2088.123%20L%20202.299%20176.245%20213.374%20176.245%20L%20224.450%20176.245%20292.685%20108.065%20L%20360.920%2039.884%20361.424%2019.942%20L%20361.929%200.000%20344.183%200.000%20L%20326.437%200.000%20326.437%2012.654%20L%20326.437%2025.307%20281.992%2069.732%20L%20237.548%20114.156%20237.548%2057.078%20L%20237.548%200.000%20219.923%200.000%20L%20202.299%200.000%20202.299%2088.123%20M364.751%2088.123%20L%20364.751%20176.245%20382.375%20176.245%20L%20400.000%20176.245%20400.000%2088.123%20L%20400.000%200.000%20382.375%200.000%20L%20364.751%200.000%20364.751%2088.123%20M301.143%20102.688%20L%20288.948%20114.956%20307.692%20133.745%20L%20326.437%20152.535%20326.437%20164.390%20L%20326.437%20176.245%20344.061%20176.245%20L%20361.686%20176.245%20361.686%20159.487%20C%20361.686%20150.270%2C360.962%20140.787%2C360.078%20138.414%20C%20358.678%20134.659%2C316.353%2090.421%2C314.160%2090.421%20C%20313.708%2090.421%2C307.850%2095.942%2C301.143%20102.688%20%22%20stroke%3D%22none%22%20fill%3D%22%23000000%22%20fill-rule%3D%22evenodd%22%3E%3C%2Fpath%3E%3C%2Fg%3E%3C%2Fsvg%3E";
wikiBtn.setAttribute("data-inject", "arkrec");
wikiBtn.style.width = "5rem";
wikiBtn.style.height = "1.8rem";
wikiBtn.style.marginRight = "2rem";
wikiBtn.style.float = "right";
wikiBtn.style.backgroundColor = "initial";
wikiBtn.style.border = "none";
wikiBtn.style.backgroundImage = `url(${wikiSvg})`;
wikiBtn.style.backgroundSize = "contain";
wikiBtn.style.backgroundRepeat = "no-repeat";
wikiBtn.style.backgroundPosition = "center";
wikiBtn.style.filter = "invert(0.4)";
wikiBtn.style.transform = "translateY(2px)";
wikiBtn.style.cursor = "pointer";
const toast = document.querySelector("div");
toast.id = "arkrec_toast";
toast.style.width = "80px";
toast.style.height = "30px";
toast.style.position = "absolute";
toast.style.left = 0;
toast.style.top = 0;
toast.style.backgroundColor = "black";
toast.style.color = "white";
toast.style.fontSize = "13px";
toast.style.textAlign = "center";
toast.style.lineHeight = "30px";
toast.style.opacity = 0;
toast.style.display = "none";
toast.style.borderRadius = "4px";
toast.style.transition = "all 0.3s ease-in-out";
toast.style.zIndex = 3000;
async function getScreenshot() {
return new Promise((resolve, reject) => {
const video = document.querySelector("video");
if (!video) {
alert(
"检测到播放器不支持截图,请在设置-更多设置-播放策略中选择非HEVC的选项并刷新页面"
);
reject();
}
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
const width = video.videoWidth;
const height = video.videoHeight;
canvas.width = width;
canvas.height = height;
context.drawImage(video, 0, 0, width, height);
const { ClipboardItem } = window;
canvas.toBlob((blob) => {
const clipboardItem = new ClipboardItem({
"image/png": blob,
});
navigator.clipboard
.write([clipboardItem])
.then(() => resolve(console.log("复制成功")));
}, "image/png");
});
}
async function toastInfo(evt, text) {
toast.innerText = text;
toast.style.display = "block";
toast.style.opacity = 1;
toast.style.transform = `translate(${evt.x - 40}px, ${evt.y - 40}px)`;
setTimeout(() => {
toast.style.opacity = 0;
setTimeout(() => {
toast.style.display = "none";
}, 300);
}, 800);
}
wikiBtn.addEventListener("click", (evt) => {
evt.preventDefault();
getScreenshot().then(() => toastInfo(evt, "复制成功"));
});
wikiBtn.addEventListener("contextmenu", (evt) => {
evt.preventDefault();
toastInfo(evt, "截图中...")
.then(getScreenshot)
.then(() => {
const { bvid, p } = unsafeWindow.__INITIAL_STATE__;
window.open(
"https://arkrec.com/submit?bvid=" + bvid + (p === 1 ? "" : "&p=" + p)
);
});
});
wikiBtn.addEventListener("mouseenter", () => {
wikiBtn.style.filter =
"invert(1) brightness(0.5) sepia(1) hue-rotate(-194deg) saturate(3)";
});
wikiBtn.addEventListener("mouseleave", () => {
wikiBtn.style.filter = "invert(0.4)";
});
await playerReady();
await injection();
registerObserver();
document.querySelector("body").append(toast);
})();