This repository has been archived by the owner on Apr 14, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
BiliBili-TextSearchList.user.js
248 lines (232 loc) · 13.1 KB
/
BiliBili-TextSearchList.user.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
// ==UserScript==
// @name BiliBili-TextSearchList
// @name:zh-CN BiliBili-文字搜索列表
// @namespace https://github.com/Mehver
// @version 1.0
// @description (Thanks to ZEP's paid customization) Display Bilibili search results in a text list, which is convenient for sorting by each column.
// @description:zh-CN (感谢闲鱼买家ZEP的有偿定制) 用文字列表的方式展示B站搜索结果,方便按各列排序。
// @sponsor ZEP
// @author https://github.com/Mehver
// @icon data:image/png;base64,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
// @match http*://search.bilibili.com/*
// @license MPL-2.0
// @license^ Mozilla Public License 2.0
// @charset UTF-8
// @homepageURL https://github.com/SynRGB/BiliBili-TextSearchList
// @contributionURL https://github.com/SynRGB/BiliBili-TextSearchList
// @copyright Copyright © 2022-PRESENT, Mehver (https://github.com/Mehver)
// @grant GM_addStyle
// @grant GM_getResourceText
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @resource DataTablesCSS https://cdn.datatables.net/1.11.3/css/jquery.dataTables.min.css
// ==/UserScript==
let table_font_size = GM_getValue('table_font_size', 16);
GM_registerMenuCommand('设置表格字体大小', async () => {
let newFontSize = prompt('请输入新的字体大小(单位px):', table_font_size);
if (newFontSize) {
table_font_size = newFontSize;
GM_setValue('table_font_size', table_font_size);
alert('字体大小已更新!请刷新页面以查看更改。');
}
});
//////////////////////////////////////
//////////// DataTables //////////////
let cssTxt = GM_getResourceText("DataTablesCSS");
GM_addStyle(cssTxt);
let head = document.head || document.getElementsByTagName('head')[0];
let link = document.createElement('link');
link.type = 'text/css';
link.rel = 'stylesheet';
link.href = 'https://cdn.datatables.net/1.11.3/css/jquery.dataTables.min.css';
head.appendChild(link);
(function() {
let jQueryScript = document.createElement("script");
jQueryScript.src = "https://code.jquery.com/jquery-3.6.0.min.js";
jQueryScript.onload = () => {
let dtScript = document.createElement("script");
dtScript.src = "https://cdn.datatables.net/1.11.3/js/jquery.dataTables.min.js";
// 加载完成后首次运行
dtScript.onload = () => {
main();
};
document.body.appendChild(dtScript);
};
document.body.appendChild(jQueryScript);
})();
//////////// DataTables //////////////
//////////////////////////////////////
//////////////////////////////////////
/////////////// 触发器 ////////////////
// 延时避免在 dtScript 和 jQueryScript 加载完成前就运行
setTimeout(function() {
// if DOM changed, re-run the script
let observer = new window.MutationObserver(function (mutations) {
mutations.forEach(function () {
const biliResultsTable = document.querySelector('#biliResultsTable');
if (biliResultsTable === null) {
main();
}
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}, 1000);
/////////////// 触发器 ////////////////
//////////////////////////////////////
/////////////////////////////////////
/////////////// main ////////////////
function main() {
// Create table with thead for DataTables
let table = document.createElement('table');
table.id = "biliResultsTable";
let thead = document.createElement('thead');
let tbody = document.createElement('tbody');
let header = ["发布日期", "时长", "标题", "播放量", "UP主"];
let trHead = document.createElement('tr');
header.forEach(text => {
let th = document.createElement('th');
th.textContent = text;
trHead.appendChild(th);
});
thead.appendChild(trHead);
setTimeout(function() {
let videoCards = document.querySelectorAll('.bili-video-card');
videoCards.forEach(videoCard => {
let title = videoCard.querySelector('.bili-video-card__info--tit')?.textContent.trim();
let up = videoCard.querySelector('.bili-video-card__info--author')?.textContent.trim();
let playCount = videoCard.querySelector('.bili-video-card__stats--item > span')?.textContent.trim();
let danmakuCount = videoCard.querySelectorAll('.bili-video-card__stats--item > span')[1]?.textContent.trim();
let duration = videoCard.querySelector('.bili-video-card__stats__duration')?.textContent.trim();
let date = videoCard.querySelector('.bili-video-card__info--date')?.textContent.trim().replace('· ', '');
let link_video = videoCard.querySelectorAll('a')[0].getAttribute('href');
// UP主的link在targetDiv1页面有概率玄学报错,应该是异步逻辑的问题,用try之后基本正常
let link_up;
try { link_up = videoCard.querySelectorAll('a')[2].getAttribute('href'); } catch (e) {}
let tr = document.createElement('tr');
// 确保没有为空的数据
if (
(title !== undefined) &&
(up !== undefined) &&
(playCount !== undefined) &&
(danmakuCount !== undefined) &&
(duration !== undefined) &&
(date !== undefined) &&
(link_video !== undefined)
) {
[date, duration, title, playCount, up].forEach(text => {
let td = document.createElement('td');
td.textContent = text;
tr.appendChild(td);
});
let tdTitle = tr.querySelector('td:nth-child(3)');
tdTitle.innerHTML = `<a href="${link_video}" target="_blank">${title}</a>`;
let tdUp = tr.querySelector('td:nth-child(5)');
tdUp.innerHTML = `<a href="${link_up}" target="_blank">${up}</a>`;
// b230815.02 时长加粗
tr.querySelector('td:nth-child(2)').style.fontWeight = 'bold';
// b230815.02 标题用 `#00AEEC` 颜色
tr.querySelector('td:nth-child(3)').style.color = '#00AEEC';
tbody.appendChild(tr);
}
});
// console.log(videoCards);
table.appendChild(thead);
table.appendChild(tbody);
// 回调获取异步数据,适用于下方 targetDiv2 的异步加载
if (tbody.childElementCount === 0) {
main();
return;
}
// 搜索第一页的 DOM 结构与其他页不同,直接用这种方式即可匹配
let targetDiv1 = document.querySelector("#i_cecream > div > div:nth-child(2) > div.search-content.search-content--gray > div > div > div > div.video.i_wrapper.search-all-list");
if (targetDiv1 !== null) {
targetDiv1.innerHTML = '';
targetDiv1.appendChild(table);
} else {
// 匹配除第一页外的其他 DOM 结构,第一页返回的结果是一次性全部加载的,而其他页是异步加载的
let targetDiv2_rm = document.querySelector("#i_cecream > div > div:nth-child(2) > div.search-content--gray.search-content > div > div > div.video-list.row");
targetDiv2_rm.innerHTML = '';
let targetDiv2_bott = document.querySelector("#i_cecream > div > div:nth-child(2) > div.search-content--gray.search-content > div > div > div.flex_center.mt_x50.mb_lg");
targetDiv2_bott.parentNode.insertBefore(table, targetDiv2_bott);
// 每次翻页时,上一页的表格不会自动被覆盖而是叠加在一起,所以需要手动删除
let tables = document.querySelectorAll('#biliResultsTable');
if (tables.length > 1) {
tables.forEach(table => {
if (table !== tables[0]) {
table.remove();
}
});
}
}
// DataTables 的自定义排序算法
$.fn.dataTable.ext.type.order['duration-sort-pre'] = function (d) {return convertDurationToSeconds(d);};
$.fn.dataTable.ext.type.order['playcount-sort-pre'] = function (d) {return convertPlayCount(d);};
$.fn.dataTable.ext.type.order['date-sort-pre'] = function (d) {return convertDate(d);};
// Initialize DataTables
$(table).DataTable({
"paging": false,
"searching": false,
"info": false,
"columnDefs": [
{ "type": "playcount-sort", "targets": 3 },
{ "type": "duration-sort", "targets": 1 },
{ "type": "date-sort", "targets": 0 }
]
});
// b230815.02 去掉底边横线
GM_addStyle("table.dataTable.no-footer { border-bottom: 0px none !important; }");
// b230815.02 去掉表头横线 (因CSS复杂,所以创建白色色块覆盖)
GM_addStyle(".dataTable thead th { border-bottom: 0px none !important; }");
// b230815.02 调大字号
GM_addStyle(`.dataTable { font-size: ${table_font_size}px !important; }`);
}, 100);
}
/////////////// main ////////////////
/////////////////////////////////////
///////////////////////////////////
/////// DataTable 的排序算法 ////////
function convertDurationToSeconds(duration) {
let parts = duration.split(':').map(part => parseInt(part, 10));
if (parts.length === 3) {
return parts[0] * 3600 + parts[1] * 60 + parts[2];
} else if (parts.length === 2) {
return parts[0] * 60 + parts[1];
} else {
return NaN;
}
}
function convertPlayCount(playCount) {
if (playCount.includes('万')) {
return parseFloat(playCount.replace('万', '')) * 10000;
} else {
return parseInt(playCount, 10);
}
}
function convertDate(date) {
const now = new Date();
if (date.includes('小时前')) {
const hoursAgo = parseFloat(date.replace('小时前', ''));
return now - hoursAgo * 3600 * 1000; // Convert hours to milliseconds
}
if (date === "昨天") {
return now - 24 * 3600 * 1000; // 24 hours in milliseconds
}
if (date.includes('-')) {
const parts = date.split('-').map(part => {
return part.padStart(2, '0');
});
// If only month and day are given, use the current year.
if (parts.length === 2) {
parts.unshift(now.getFullYear().toString());
}
// Create a new Date object and return its time value in milliseconds
return new Date(parts.join('-')).getTime();
}
}
/////// DataTable 的排序算法 ////////
///////////////////////////////////
console.log("JS script BiliBili-TextSearchList (BiliBili-文字搜索列表) loaded. See more details at https://github.com/SynRGB/BiliBili-TextSearchList");