// ==UserScript==
// @name 记住B站动态首页上次看的位置
// @namespace cordy
// @version 1.1
// @description 记住B站动态首页上次看的位置并自动滚动到该视频
// @author cordy
// @license MIT
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/minified/arrive.min.js
// @match https://t.bilibili.com/*
// @icon https://www.bilibili.com/favicon.ico
// ==/UserScript==
var docCookies = {
getItem: function (sKey) {
return (
decodeURIComponent(
document.cookie.replace(
new RegExp(
"(?:(?:^|.*;)\\s*" +
encodeURIComponent(sKey).replace(/[-.+*]/g, "\\$&") +
"\\s*\\=\\s*([^;]*).*$)|^.*$",
),
"$1",
),
) || null
);
},
setItem: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) {
if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) {
return false;
}
var sExpires = "";
if (vEnd) {
switch (vEnd.constructor) {
case Number:
sExpires =
vEnd === Infinity
? "; expires=Fri, 31 Dec 2099 23:59:59 GMT"
: "; max-age=" + vEnd;
break;
case String:
sExpires = "; expires=" + vEnd;
break;
case Date:
sExpires = "; expires=" + vEnd.toUTCString();
break;
}
}
document.cookie =
encodeURIComponent(sKey) +
"=" +
encodeURIComponent(sValue) +
sExpires +
(sDomain ? "; domain=" + sDomain : "") +
(sPath ? "; path=" + sPath : "") +
(bSecure ? "; secure" : "");
return true;
},
removeItem: function (sKey, sPath, sDomain) {
if (!sKey || !this.hasItem(sKey)) {
return false;
}
document.cookie =
encodeURIComponent(sKey) +
"=; expires=Thu, 01 Jan 1970 00:00:00 GMT" +
(sDomain ? "; domain=" + sDomain : "") +
(sPath ? "; path=" + sPath : "");
return true;
},
hasItem: function (sKey) {
return new RegExp(
"(?:^|;\\s*)" +
encodeURIComponent(sKey).replace(/[-.+*]/g, "\\$&") +
"\\s*\\=",
).test(document.cookie);
},
keys: /* optional method: you can safely remove it! */ function () {
var aKeys = document.cookie
.replace(/((?:^|\s*;)[^\=]+)(?=;|$)|^\s*|\s*(?:\=[^;]*)?(?:\1|$)/g, "")
.split(/\s*(?:\=[^;]*)?;\s*/);
for (var nIdx = 0; nIdx < aKeys.length; nIdx++) {
aKeys[nIdx] = decodeURIComponent(aKeys[nIdx]);
}
return aKeys;
},
};
(async () => {
'use strict';
var last_watch_video = getRecordInfo();
var has_new_item =false;
var delay_to_scroll_id;
var last_video_item;
document.body.arrive(
".bili-dyn-my-info",
{
fireOnAttributesModification: true,
onceOnly: true,
existing: true,
},
item => {
if(last_watch_video){
$(item).parent().parent().append(`<section><div style="background:#ffffff;padding:10px;text-align:center;font-size:12px;color:#6d757a;cursor:pointer" class="scroll-to-last-video">滚动到前次观看<br>最后视频:${last_watch_video.time}</div></section>`);
$('.scroll-to-last-video').click(function(){
scrollToLastVideo();
})
}else{
$(item).parent().parent().append('<section><div style="background:#ffffff;padding:10px;text-align:center;font-size:12px;color:#6d757a;cursor:pointer" class="scroll-to-last-video">还没有观看记录</div></section>');
}
}
);
document.body.arrive(
".bili-dyn-list__item",
{
fireOnAttributesModification: true,
onceOnly: false,
existing: true,
},
item => {
has_new_item = true;
$(item).find("a.bili-dyn-card-video").mousedown(function(e){
if(e.which == 1 || e.which == 2){
let time=timeToDate($(item).find(".bili-dyn-time").text().trim());
let is_reference=$(item).find(".bili-dyn-content__orig").hasClass("reference");
let id = $(this).attr("dyn-id");
let bvid = $(this).attr("href").match(/\/video\/(\w+)\//i)[1];
last_watch_video = {time:time,id:id,reference:is_reference,bvid:bvid};
setRecordInfo(last_watch_video);
identifyLastVideo(item);
}
});
}
);
function scrollToLastVideo(){
if(!last_watch_video){
alert("还没有看过视频");
return;
}
has_new_item =false;
let item = checkLastVideo();
if(item){
identifyLastVideo(item);
clearTimeout(delay_to_scroll_id);
$('html,body').animate({
scrollTop : last_video_item.offset().top,
}, 1000);
}else{
$('html,body').animate({
scrollTop : $(".bili-dyn-list__item:last").offset().top
}, 1000,delayToScrollToLastVideo);
}
}
function checkLastVideo(){
let items = $(".bili-dyn-list__item");
for(var i=0;i<items.length;i++){
let item = items[i];
let video = $(item).find("a.bili-dyn-card-video");
if(video.length){
let time=timeToDate($(item).find(".bili-dyn-time").text().trim());
let id = video.attr("dyn-id");
if(time<last_watch_video.time || (time == last_watch_video.time&& id==last_watch_video.id)){
return item;
}
}
}
return null;
}
function delayToScrollToLastVideo(){
if(has_new_item){
scrollToLastVideo();
}else{
delay_to_scroll_id = setTimeout(delayToScrollToLastVideo,1000);
}
}
function identifyLastVideo(item){
if(last_video_item)last_video_item.find(".bili-dyn-time").css('color','');
last_video_item = $(item);
last_video_item.find(".bili-dyn-time").css('color','#fb7299');
}
function timeToDate(time) {
let videoDate = new Date();
let matches;
if (time.match(/\d+年\d+月\d+日/)) {
matches = time.match(/(\d+)年(\d+)月(\d+)日/);
videoDate.setFullYear(matches[1]);
videoDate.setMonth(matches[2] - 1);
videoDate.setDate(matches[3]);
}else if (time.match(/\d+月\d+日/)) {
matches = time.match(/(\d+)月(\d+)日/);
videoDate.setMonth(matches[1] - 1);
videoDate.setDate(matches[2]);
}else{
let day=0;
if (time.match(/\d+小时前/)) {
}else if (time.match(/\昨天/)) {
day = 1;
}else if (time.match(/\d+天前/)) {
matches = time.match(/(\d+)天前/);
day = parseInt(matches[1]);
}
videoDate.setDate(videoDate.getDate()-day);
}
return formatTime('Y-M-D', videoDate);
}
function formatNum(num, dig = 2){
let str = num + "";
while (str.length < dig) {
str = "0" + str;
}
return str;
}
function formatTime(format,time){
//Y 4位年 M 2位月 D 2位日 y 2位年 m 1位月 d 1位日 H 2位时 I 2位分 S 2位秒 h 1位时 i 1位分 1 1位秒
var d = new Date();
if(typeof(time)=='number'){
d.setTime(time);
}else if(time instanceof Date){
d = time;
}
if(!format)format="Y-M-D H:I:S";
var str = format;//Y年M月D日
str = str.split("Y").join(d.getFullYear().toString());
str = str.split("y").join(d.getFullYear().toString().substr(2));
str = str.split("M").join(formatNum(d.getMonth() + 1));
str = str.split("m").join((d.getMonth() + 1).toString());
str = str.split("D").join(formatNum(d.getDate()));
str = str.split("d").join(d.getDate().toString());
str = str.split("H").join(formatNum(d.getHours()));
str = str.split("h").join(d.getHours().toString());
str = str.split("I").join(formatNum(d.getMinutes()));
str = str.split("i").join(d.getMinutes().toString());
str = str.split("S").join(formatNum(d.getSeconds()));
str = str.split("s").join(d.getSeconds().toString());
return str;
}
function getRecordInfo(){
return JSON.parse(docCookies.getItem("last_watch_video"));
}
function setRecordInfo(info){
docCookies.setItem("last_watch_video", JSON.stringify(info),Infinity);
}
})();