[JavaScript] 纯文本查看 复制代码
// ==UserScript==
// @name 哔哩哔哩 - beta
// @namespace Violentmonkey Scripts
// @match *://*.bilibili.com/*
// @version 1.0
// @noframes -
// @author moxi
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addStyle
// @require [url=https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.5.1.min.js]https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.5.1.min.js[/url]
// ==/UserScript==
const Match = {
Init: function () {
var link = window.location.href
var rules = this.Rules
for (keyNmae in rules) {
var focus = rules[keyNmae] //==[正则,[函数]]
var result = RegExp(focus[0], "i").exec(link)
if (result != null) {
console.log("页面类型", keyNmae, " | ", "使用函数", focus[1]);
for (let i = 0; i < focus[1].length; i++) {
focus[1][i].call(null, { result: result.slice(1,), rules: focus[0] })
}
}
}
},
Rules: {//正则
"Home": [".*://www.bilibili.com(?:$|/$|/\\?.*)", [DeleteSundries]],//主页
"Video": [".*://www.bilibili.com/video/([ab][v][a-z0-9]*).*", [RecordLastPlayTime, DanmuAirborne]],//视频地址
"Space": [".*://space.bilibili.com/.*", [AddPlayProgressUI, MarkingForRead]],//空间主页
},
config: {
"HideHaveRead": true,//隐藏已经阅读过的视频
}
};
(function () { Match.Init() }());
function RecordLastPlayTime() {
setInterval(function () {
var Rresult = RegExp(".*://www.bilibili.com/video/([ab][v][a-z0-9]*).*", "i").exec(window.location.href)
if (Rresult != null) {
var node = []
var element = document.querySelector('.bpx-player-ctrl-time-current')
if (!!element) {
node.push(element)
}//播放进度
var element = document.querySelector('.bpx-player-ctrl-time-duration')
if (!!element) {
node.push(element)
}//视频时长
if (node.length == 2) {
var id = Rresult[1]
//------------------------------------------------------
var minute = node[0].innerHTML.split(":")
var progress = (minute[0] * 60) + Math.trunc(minute[1])
//document.querySelector("bwp-video,video").currentTime
//------------------------------------------------------
var minute = node[1].innerHTML.split(":")
var duration = (minute[0] * 60) + Math.trunc(minute[1])
//document.querySelector("bwp-video,video").duration
//------------------------------------------------------
var json = GM_getValue_local("config", id, {})
if (!json.progress && !json.duration && !json.interaction) {
var type = id[0].toLowerCase == "a" ? "avid" : "bvid";
var httpRequest = new XMLHttpRequest();
httpRequest.open('GET', `[url=https://api.bilibili.com/x/web-interface/view?]https://api.bilibili.com/x/web-interface/view?[/url]${type}=${id}`, false);
httpRequest.send(null);
var json = JSON.parse(httpRequest.responseText);
var duration = json.data.duration
var interaction = json.data.rights['is_stein_gate']
if (interaction == 1) {
//console.log("该视频为互动视频", id, progress, duration);
GM_setValue_local("config", id, {
interaction: interaction
})
} else {
//console.log("首次记录", id, progress, duration);
GM_setValue_local("config", id, {
progress: progress,
duration: duration
})
}
} else {
if (!json.interaction) {//不是互动视频才进行记录
var disparity = json.duration - duration
if (disparity > -5 && disparity < 5) {//这是哔哩哔哩的BUG//同一个视频差距时间不得超过5秒
if (progress > json.progress) {//避免被小数覆盖
GM_setValue_local("config", id, {
progress: progress,
duration: duration
})
}
}
} else {
//console.log(id,"该视频为互动视频,无法记录");
}
}
}
//console.log(id, { progress: progress, duration: duration });
}
}, 1000)
}
function AddPlayProgressUI() {
ObserveNewElements(".small-item.fakeDanmu-item", function (node) {
var id = $(node).attr('data-aid')
var json = GM_getValue_local("config", id, {})
//console.log("config", id, json.progress, json.duration);
if (!$.isEmptyObject(json)) {//判断JSON是否为空
var progress_ui = document.createElement('div');
var Percent = GetPercent(json.progress, json.duration)
$(progress_ui).attr('class', 'progress').attr('style', 'top: 0px;position: absolute;width: 100%;height: 4px;background-color: #909090;').html(`<div id="content" style="width: ${Percent}%;height: 100%;background-color: #f00;"></div>`);
$(node).find('.cover').append(progress_ui);
if (Match.config["HideHaveRead"]) {
if (Percent > 95) {
$(node).hide();
}
}
}
})
}
function DanmuAirborne() {//弹幕空降
const BarrageAirborne = {
init: function () {
this.IntervalID = setInterval((function (execution, This) {
return function () {
execution.call(This)
}
}(function () {
var danmaku = this.hBarrage
var danmaNode = $(".b-danmaku")
for (let index = 0; index < danmaNode.length; index++) {
if (!null) {//danmaku.includes(this.innerText)
var result = RegExp("(?:空降|跳伞|伞兵|空)\\D*(\\d{1,}[::]\\d{1,})", "i").exec(danmaNode[index].innerText)//弹幕是否预设的过滤规则
if (result != null && this.status == 0) {//符合则执行
console.log(danmaNode[index].innerText);
this.status = 1 //许可
danmaku.push(danmaNode[index].innerText)
var minute = result[1].split(":")//分割
if (minute.length == 1) {
var minute = result[1].split(":")
}
var position = (Math.trunc(minute[0]) * 60) + Math.trunc(minute[1])//取秒
if (position > document.querySelector("bwp-video,video").currentTime) {
var node = $('.bpx-player-video-area').append(`<div class="div dtms" style="position: absolute;z-index: 1;left: 50%;top: 50%;transform: translate(-50%, -50%);"><button class="datumoshi" style="padding: 6px 6px;line-height: 18px;border: none;background: #ffffff00;font-size: 18px;color: #fff;text-shadow: 2px 1px 1px #000;">点击空降到${minute[0]}分${minute[1]}秒</button></div>`).find('.datumoshi')[0];
$(node).click(CFunction(function (position, node, This) {
document.querySelector("bwp-video,video").currentTime = position
$('.datumoshi').remove()
This.status = 0
}, position, node, this))
setTimeout(CFunction(function (position, node, This) {
$(node).remove()
if (This.status != 0) {
This.status = 0
}
}, null, node, this), 7500)
function CFunction(execution, position, node, This) {
return function () {
execution.call(null, position, node, This)
}
}
}
}
}
}
}, this)), 1000)
},
hBarrage: [],
IntervalID: undefined,
status: 0,
}
BarrageAirborne.init()
}
function MarkingForRead() {
ObserveNewElements(".play-all-btn:not(.all,.check,.save)", function (node) {
setTimeout(
function () {
$(node).parent().prepend(`<button class="play-all-btn all" style="width: auto;background-color: #ffffff00;padding: 4px;">反选</button><button class="play-all-btn save" style="width: auto;background-color: #ffffff00;padding: 4px;">保存</button>`)
$(".play-all-btn.all").click(//全选
function () {
var checkbox = $('.select')
if (checkbox.length == 0) {
var item = $('.small-item.fakeDanmu-item')
for (let index = 0; index < item.length; index++) {
$(item[index]).prepend(`<input type="checkbox" class="select" aid="${$(item[index]).attr("data-aid")}" index="${index}" style="position: absolute;z-index: 10;">`)
}
} else {
checkbox.click()
}
}
)
$(".play-all-btn.save").click(//保存
function () {
var select = []
$('.select').each(function () {
if (this.checked) {
select.push(this.getAttribute('aid'))
}
})
for (let index = 0; index < select.length; index++) {
SetForRead(select[index])
}
}
)
}, 300
)
})
}//在UP的投稿列表添加一个全选和保存按钮,选中后按保存会标记为已阅视频
function DeleteSundries() {//删除节点
var remove = {
"推荐": ".bili-grid .eva-extension-area",
"直播": ".bili-grid .live-card-list",
"动画": ".bili-grid .video-card-list.is-main",
"番剧": ".bili-grid .bangumi-area",
"国创": ".bili-grid .guo-chuang-area",
"综艺": ".bili-grid .variety-area",
"课堂": ".bili-grid .cheese-card-list",
"资讯": ".bili-grid .information-area",
"专栏": ".bili-grid .article-card-list",
//"跳转": ".banner-link",
"电影,电视,纪录片": ".bili-grid .movie-card-list",
}
for (key in remove) {
WaitNodeLoad(null, remove[key], 100, 100, function () {
if (!this.status) {
this.parent().remove();
}
})
}
WaitNodeLoad(null, '.recommended-swipe', 100, 100, function () {
if (typeof (this.status) == "undefined") {
console.log("this", this);
this[0].innerHTML = `<img style="object-fit: cover;position: absolute;top: 50%;left: 50%;display: block;min-width: 100%;min-height: 100%;transform:translate(-50%,-50%);border: 1px solid #000000;background: url([url=https://s1.ax1x.com/2022/11/10/zpbUds.jpg]https://s1.ax1x.com/2022/11/10/zpbUds.jpg[/url]) no-repeat center center;background-size: cover;">`
}
})
}
function GM_getValue_local(id, key, defaultValue) {
var data = GM_getValue(id, {});
return data[key] || defaultValue;
}
function GM_setValue_local(id, key, value) {
var data = GM_getValue(id, {});
data[key] = value;
return GM_setValue(id, data);
}
function ObserveNewElements(selector, response, filter) {//selector,(node,mutation)=>{},filterRepetition=true)
'use strict';
if (!!selector) {
let config = {
childList: true,
subtree: true
};
const mutationCallback = (mutationsList) => {
for (let mutation of mutationsList) {
let type = mutation.type;
if (type == "childList") {
if (mutation.addedNodes.length != 0) {
var New_node = []
for (let index = 0; index < mutation.addedNodes.length; index++) {
var Nodes = $(mutation.addedNodes[index])
for (let Tindex = 0; Tindex < Nodes.length; Tindex++) {
if ($(Nodes[Tindex]).is(selector)) {
New_node.push(Nodes[Tindex])
}
}
Nodes.find(selector).each(
function () {
New_node.push(this)
}
)
}
if (New_node.length > 0) {
setTimeout((function (List) {
for (let Tindex = 0; Tindex < List.length; Tindex++) {
if (filter) {
if (List[Tindex]['OpenFilter']) {
console.log("重复", List[Tindex]);
continue;
} else {
List[Tindex]['OpenFilter'] = true
}
}
response.call(mutation, List[Tindex], mutation)
}
}(New_node)), 300)
}
}
}
}
};
var observe = new MutationObserver(mutationCallback);
observe.observe(document, config)
return observe
}
}
function GetPercent(num, total) {//取百分比
num = parseFloat(num);
total = parseFloat(total);
if (isNaN(num) || isNaN(total)) {
return 0;
}
return total <= 0 ? "0%" : (Math.round(num / total * 10000) / 100.00);
}
function SetForRead(id) {//将视频标记为已阅
return new Promise(
function (response) {
var type = id.toLowerCase == "a" ? "avid" : "bvid";
var httpRequest = new XMLHttpRequest();
httpRequest.open('GET', `[url=https://api.bilibili.com/x/web-interface/view?]https://api.bilibili.com/x/web-interface/view?[/url]${type}=${id}`, false);
httpRequest.send(null);
var json = JSON.parse(httpRequest.responseText);
var duration = json.data.duration
var interaction = json.data.rights['is_stein_gate']
if (interaction == 1) {
console.log("ForRead >>> 互动视频", id, duration, duration);
GM_setValue_local("config", id, {
interaction: interaction
})
} else {
console.log("ForRead >>> 完成记录", id, duration, duration);
GM_setValue_local("config", id, {
progress: duration,//标记为已阅
duration: duration
})
}
return response(id)
}
)
}
function WaitNodeLoad(parent, selector, interval, times, callback) {//父节点,选择器,间隔,超时次数,回调
var _selector = selector, _interval = interval, _times = times
var _node = !parent ? $(_selector) : $(parent).find(_selector)
if (_node.length == 0) {//匹配失败
var number = setInterval(
function () {
if (_times <= 0) {
callback.call({ "status": false })
console.log("关闭", number);
return clearInterval(number)
} else {
_times--
var node = !parent ? $(_selector) : $(parent).find(_selector)
if (node.length != 0) {
callback.call(node)
return clearInterval(number)
}
}
}, _interval
)
} else {
return callback.call(_node)
}
}