2023-06-03 02:18:29 -07:00
|
|
|
|
// ==UserScript==
|
|
|
|
|
// @name Bilibili Archive Checker
|
2023-08-06 04:52:38 -07:00
|
|
|
|
// @version 1.4
|
2023-07-20 09:26:14 -07:00
|
|
|
|
// @description 检查 BiliBili 视频是否已经存档到 Internet Archive。
|
2023-06-03 02:18:29 -07:00
|
|
|
|
// @author yzqzss
|
|
|
|
|
// @match https://www.bilibili.com/video/*
|
2023-08-06 07:36:44 -07:00
|
|
|
|
// @run-at document-start
|
2023-06-03 02:18:29 -07:00
|
|
|
|
// @grant GM_xmlhttpRequest
|
2023-08-06 04:52:38 -07:00
|
|
|
|
// @grant unsafeWindow
|
2023-06-03 02:18:29 -07:00
|
|
|
|
// ==/UserScript==
|
|
|
|
|
|
2023-07-06 01:08:45 -07:00
|
|
|
|
(function () {
|
2023-06-03 02:18:29 -07:00
|
|
|
|
'use strict';
|
|
|
|
|
|
2023-08-06 07:36:44 -07:00
|
|
|
|
const initialState = unsafeWindow.__INITIAL_STATE__;
|
2024-01-26 23:13:40 -08:00
|
|
|
|
const BASEURL = ""; // 运行API的地址,不包括 /archive/ 部分
|
2023-08-06 07:36:44 -07:00
|
|
|
|
|
2023-07-22 09:05:19 -07:00
|
|
|
|
function sleep(ms) {
|
|
|
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isVideo() {
|
2023-07-20 09:26:14 -07:00
|
|
|
|
var url = window.location.href;
|
|
|
|
|
var avRegex = /\/video\/av(\d+)/;
|
|
|
|
|
var matches = url.match(avRegex);
|
|
|
|
|
if (matches && matches.length > 1) {
|
|
|
|
|
console.log("AV:", matches[1]);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2023-07-22 09:05:19 -07:00
|
|
|
|
var bvRegex = /\/video\/(BV\w+)/;
|
|
|
|
|
matches = url.match(bvRegex);
|
|
|
|
|
if (matches && matches.length > 1) {
|
|
|
|
|
console.log("BV:", matches[1]);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2023-07-20 09:26:14 -07:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-03 02:18:29 -07:00
|
|
|
|
// 从 URL 获取当前视频的 BV 号
|
2023-08-06 04:52:38 -07:00
|
|
|
|
function getBVNumber() {
|
2023-08-06 09:16:42 -07:00
|
|
|
|
return unsafeWindow.__INITIAL_STATE__?.bvid ?? initialState?.bvid ?? unsafeWindow.vd?.bvid;
|
2023-06-03 02:18:29 -07:00
|
|
|
|
}
|
|
|
|
|
|
2023-07-20 09:09:45 -07:00
|
|
|
|
function getPageNumber() {
|
2023-08-13 08:57:54 -07:00
|
|
|
|
return unsafeWindow.__INITIAL_STATE__?.p ?? initialState?.p ?? unsafeWindow.vd?.embedPlayer?.p;
|
2023-07-20 09:09:45 -07:00
|
|
|
|
}
|
|
|
|
|
|
2023-07-06 01:08:45 -07:00
|
|
|
|
function humanReadableUpperPartMap(string, backward) {
|
|
|
|
|
// 找到字符串中所有的 ASCII 大写字母,并返回一个能表示他们绝对位置的字符串。
|
|
|
|
|
// 其中每个非相邻的大写字母之间用数字表示相隔的字符数。
|
|
|
|
|
//
|
|
|
|
|
// params: backward - 可以表示是正着看还是倒着看。
|
|
|
|
|
//
|
|
|
|
|
// NOTE: 在我们的用例中,我们 backward = true ,这样产生的 upperPart 就不太像 BV 号或者类似的编号,以免 upperPart 污染全文搜索。
|
|
|
|
|
//
|
|
|
|
|
// 例如:
|
|
|
|
|
// backward = false
|
|
|
|
|
// BV1HP411D7Rj -> BV1HP3D1R (长得像 bvid )
|
|
|
|
|
// backward = true
|
|
|
|
|
// BV1HP411D7Rj -> 1R1D3PH1VB
|
|
|
|
|
|
|
|
|
|
if (backward) {
|
|
|
|
|
string = string.split('').reverse().join('');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let result = '';
|
|
|
|
|
let steps = 0;
|
|
|
|
|
let char_ = '';
|
|
|
|
|
for (let i = 0; i < string.length; i++) {
|
|
|
|
|
char_ = string[i];
|
2023-07-20 09:09:45 -07:00
|
|
|
|
// console.log('char_:', char_);
|
2023-07-06 01:08:45 -07:00
|
|
|
|
if (char_ >= 'A' && char_ <= 'Z') {
|
|
|
|
|
if (steps === 0) {
|
|
|
|
|
result += char_;
|
|
|
|
|
} else {
|
|
|
|
|
// steps to string
|
|
|
|
|
result += steps.toString() + char_;
|
|
|
|
|
}
|
|
|
|
|
steps = 0;
|
|
|
|
|
} else {
|
|
|
|
|
steps++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-07-20 09:09:45 -07:00
|
|
|
|
console.log("upperPart:", result);
|
2023-07-06 01:08:45 -07:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-03 02:18:29 -07:00
|
|
|
|
// 查 archive.org
|
2023-07-20 09:09:45 -07:00
|
|
|
|
function queryInternetArchive(bvNumber, pageNumber) {
|
2023-07-22 09:05:19 -07:00
|
|
|
|
var identifier = "BiliBili-" + bvNumber + "_p" + pageNumber + "-" + humanReadableUpperPartMap(bvNumber, true);
|
|
|
|
|
var iaUrl = "https://archive.org/services/check_identifier.php?output=json&identifier=" + identifier;
|
2023-07-20 09:09:45 -07:00
|
|
|
|
console.log("Querying:", iaUrl);
|
2023-08-06 04:52:38 -07:00
|
|
|
|
showPopup("正在查询 IA (BV: " + bvNumber + ", P:" + pageNumber + ")", "#f3d9a6")
|
2023-06-03 02:18:29 -07:00
|
|
|
|
GM_xmlhttpRequest({
|
|
|
|
|
method: "GET",
|
|
|
|
|
url: iaUrl,
|
2023-07-06 01:08:45 -07:00
|
|
|
|
onload: function (response) {
|
2023-06-03 02:18:29 -07:00
|
|
|
|
var data = JSON.parse(response.responseText);
|
|
|
|
|
if (data.code === "available") {
|
2023-08-06 04:52:38 -07:00
|
|
|
|
showPopup("未存档", "#fac7c7");
|
2023-06-03 02:18:29 -07:00
|
|
|
|
} else {
|
2023-08-06 04:52:38 -07:00
|
|
|
|
showPopup("本视频已存档", "#bdf8bd", "https://archive.org/details/" + identifier);
|
2023-06-03 02:18:29 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 创建悬浮窗
|
|
|
|
|
function createPopup() {
|
|
|
|
|
var popup = document.createElement("div");
|
|
|
|
|
popup.id = "archive-popup";
|
|
|
|
|
popup.style.position = "fixed";
|
2023-08-06 04:52:38 -07:00
|
|
|
|
popup.style.userSelect = "none"; // 否则选中会出现横向滚动条
|
2023-06-03 02:18:29 -07:00
|
|
|
|
popup.style.top = "50%";
|
|
|
|
|
popup.style.right = "10px";
|
|
|
|
|
popup.style.transform = "translateY(-50%)";
|
|
|
|
|
popup.style.padding = "10px";
|
|
|
|
|
popup.style.backgroundColor = "#f0f0f0";
|
|
|
|
|
popup.style.border = "1px solid #ccc";
|
|
|
|
|
popup.style.borderRadius = "4px";
|
|
|
|
|
popup.style.zIndex = "9999";
|
|
|
|
|
popup.style.opacity = "0.9";
|
2023-08-06 04:52:38 -07:00
|
|
|
|
popup.style.transition = "background-color 0.25s";
|
2023-06-03 02:18:29 -07:00
|
|
|
|
|
|
|
|
|
var text = document.createElement("span");
|
|
|
|
|
text.style.marginRight = "10px";
|
|
|
|
|
text.style.color = "#333";
|
2023-07-22 09:05:19 -07:00
|
|
|
|
text.textContent = "";
|
2023-06-03 02:18:29 -07:00
|
|
|
|
|
|
|
|
|
var status = document.createElement("span");
|
|
|
|
|
status.id = "item-status";
|
|
|
|
|
|
|
|
|
|
var copyButton = document.createElement("button");
|
|
|
|
|
copyButton.textContent = "Copy BV";
|
|
|
|
|
copyButton.style.marginTop = "10px";
|
2023-08-06 04:52:38 -07:00
|
|
|
|
copyButton.style.padding = "5px";
|
|
|
|
|
copyButton.style.margin = "auto";
|
2023-06-03 02:18:29 -07:00
|
|
|
|
copyButton.style.display = "block";
|
2023-07-06 01:08:45 -07:00
|
|
|
|
copyButton.addEventListener("click", function () {
|
2023-06-03 02:18:29 -07:00
|
|
|
|
copyBV();
|
|
|
|
|
});
|
|
|
|
|
|
2024-01-26 23:13:40 -08:00
|
|
|
|
var archiveButton = document.createElement("button");
|
|
|
|
|
archiveButton.textContent = "Archive Video";
|
|
|
|
|
archiveButton.style.marginTop = "10px";
|
|
|
|
|
archiveButton.style.padding = "5px";
|
|
|
|
|
archiveButton.style.margin = "auto";
|
|
|
|
|
archiveButton.style.display = "block"; // initially hidden
|
|
|
|
|
archiveButton.addEventListener("click", function () {
|
|
|
|
|
archiveVideo();
|
|
|
|
|
});
|
|
|
|
|
|
2023-06-03 02:18:29 -07:00
|
|
|
|
popup.appendChild(text);
|
|
|
|
|
popup.appendChild(status);
|
|
|
|
|
popup.appendChild(copyButton);
|
2024-01-26 23:13:40 -08:00
|
|
|
|
popup.appendChild(archiveButton);
|
2023-06-03 02:18:29 -07:00
|
|
|
|
|
|
|
|
|
document.body.appendChild(popup);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 显示悬浮窗
|
2023-08-06 04:52:38 -07:00
|
|
|
|
function showPopup(message, color, href = null) {
|
2023-06-03 02:18:29 -07:00
|
|
|
|
var popup = document.getElementById("archive-popup");
|
|
|
|
|
if (!popup) {
|
|
|
|
|
createPopup();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var status = document.getElementById("item-status");
|
|
|
|
|
if (status) {
|
|
|
|
|
status.textContent = message;
|
2023-07-22 09:05:19 -07:00
|
|
|
|
if (href) {
|
2023-08-06 04:52:38 -07:00
|
|
|
|
status.style.color = "#b6e9fe";
|
2023-07-22 09:05:19 -07:00
|
|
|
|
status.style.textDecoration = "underline";
|
2023-08-06 04:52:38 -07:00
|
|
|
|
status.innerHTML = "<a href=\"" + href + "\" target=\"_blank\">" + message + "</a>";
|
2023-07-22 09:05:19 -07:00
|
|
|
|
}
|
2023-06-03 02:18:29 -07:00
|
|
|
|
}
|
|
|
|
|
popup = document.getElementById("archive-popup");
|
|
|
|
|
popup.style.backgroundColor = color;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 复制 BV 号
|
2023-07-22 09:05:19 -07:00
|
|
|
|
async function copyBV() {
|
2023-08-06 04:52:38 -07:00
|
|
|
|
var bvNumber = getBVNumber();
|
2023-06-03 02:18:29 -07:00
|
|
|
|
if (bvNumber) {
|
|
|
|
|
navigator.clipboard.writeText(bvNumber)
|
2023-07-06 01:08:45 -07:00
|
|
|
|
.then(function () {
|
2023-06-03 02:18:29 -07:00
|
|
|
|
alert("BV号已复制到剪贴板: " + bvNumber);
|
|
|
|
|
})
|
2023-07-06 01:08:45 -07:00
|
|
|
|
.catch(function (error) {
|
2023-06-03 02:18:29 -07:00
|
|
|
|
console.error("无法复制BV号:", error);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-07-22 09:05:19 -07:00
|
|
|
|
async function check() {
|
|
|
|
|
if (!isVideo()) {
|
|
|
|
|
console.log("Not a video page, skip.");
|
2023-07-20 09:26:14 -07:00
|
|
|
|
return;
|
|
|
|
|
}
|
2023-08-06 04:52:38 -07:00
|
|
|
|
var bvNumber = getBVNumber();
|
2023-07-20 09:09:45 -07:00
|
|
|
|
var pageNumber = getPageNumber();
|
2023-06-03 02:18:29 -07:00
|
|
|
|
if (bvNumber) {
|
2023-07-20 09:09:45 -07:00
|
|
|
|
queryInternetArchive(bvNumber, pageNumber);
|
2023-06-03 02:18:29 -07:00
|
|
|
|
}
|
2023-07-22 09:05:19 -07:00
|
|
|
|
else {
|
2023-08-06 04:52:38 -07:00
|
|
|
|
showPopup("无法获取 BV 号", "#f3d9a6");
|
2023-07-22 09:05:19 -07:00
|
|
|
|
}
|
2023-06-03 02:18:29 -07:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-26 23:13:40 -08:00
|
|
|
|
async function archiveVideo() {
|
|
|
|
|
var bvNumber = getBVNumber();
|
|
|
|
|
var url = `${BASEURL}/archive/${bvNumber}`;
|
|
|
|
|
console.log("Archive URL:", url);
|
|
|
|
|
showPopup("正在发送存档请求", "#f3d9a6");
|
|
|
|
|
GM_xmlhttpRequest({
|
|
|
|
|
method: "PUT",
|
|
|
|
|
url: url,
|
|
|
|
|
onload: function (response) {
|
|
|
|
|
if (response.status === 200) {
|
|
|
|
|
showPopup("存档请求已发送", "#bdf8bd");
|
|
|
|
|
} else {
|
|
|
|
|
showPopup("存档请求失败", "#fac7c7");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-03 02:18:29 -07:00
|
|
|
|
|
2023-07-22 09:05:19 -07:00
|
|
|
|
async function main() {
|
|
|
|
|
var url = null;
|
|
|
|
|
while (true) {
|
2023-08-06 04:52:38 -07:00
|
|
|
|
url = window.location.href.split('&')[0];
|
2023-07-22 09:05:19 -07:00
|
|
|
|
console.log("url:", url);
|
|
|
|
|
check();
|
2023-08-06 04:52:38 -07:00
|
|
|
|
while (url === window.location.href.split('&')[0]) {
|
2023-07-22 09:05:19 -07:00
|
|
|
|
await sleep(5000);
|
|
|
|
|
}
|
2023-08-06 04:52:38 -07:00
|
|
|
|
showPopup("URL 变化...", "#b6e9fe");
|
2023-07-22 09:05:19 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-06 07:36:44 -07:00
|
|
|
|
document.addEventListener("DOMContentLoaded", main);
|
2023-06-03 02:18:29 -07:00
|
|
|
|
})();
|