0) {
timeArray.push(timeUnitNumber + timeUnits[i]);
seconds -= timeUnitNumber * timeUnitSeconds[i];
}
}
time = timeArray.join('');
return time;
}
// 爬取数据
function spider() {
// 爬取数据
let items = seedList.querySelectorAll(siteData[siteIndex].seedItemsSelector);
items.forEach((item) => {
let title = item.querySelector(siteData[siteIndex].seedTitleSelector).title;
let size = item.querySelector(siteData[siteIndex].seedSizeSelector).innerText.replace(',', '').split('\n');
let seeders = Number(item.querySelector(siteData[siteIndex].seedersNumberSelector).innerText.replace(',', ''));
let uploadSize = item.querySelector(siteData[siteIndex].seedUploadSizeSelector).innerText.replace(',', '').split('\n');;
let downloadSize = item.querySelector(siteData[siteIndex].seedDownloadSizeSelector).innerText.replace(',', '').split('\n');;
let time = item.querySelector(siteData[siteIndex].seedTimeSelector).innerText.replace('天', ':').replace('小时', ':').replace('分', ':').replace('秒', '').replace(',', ''); // D:HH:MM:SS
// 将日志更新到 result 中
appendResult('正在解析:' + title + '
');
// 遍历 siteData 中的所有站点,查找 siteGroup 中的元素包含于 title 中的首个元素
let finded = false;
for (let i = 0; i < siteData.length; i++) {
for (let j = 0; j < siteData[i].siteGroups.length; j++) {
// 不区分大小写
if (title.toLowerCase().indexOf(siteData[i].siteGroups[j].toLowerCase()) !== -1) {
siteData[i].seedSize += sizeToBytes(size);
siteData[i].seedersNumber += seeders;
siteData[i].seedUploadSize += sizeToBytes(uploadSize);
siteData[i].seedDownloadSize += sizeToBytes(downloadSize);
siteData[i].seedTime += timeToSeconds(time);
siteData[i].seedItemsNumber++;
finded = true;
break;
}
}
if (finded) {
break;
}
}
// 如果没找到,更新 Others 站点的数据
if (!finded) {
// 在 siteData 中找到 Others 站点的索引
let othersIndex = siteData.findIndex((site) => {
return site.siteName === 'Others';
});
siteData[othersIndex].seedSize += sizeToBytes(size);
siteData[othersIndex].seedersNumber += seeders;
siteData[othersIndex].seedUploadSize += sizeToBytes(uploadSize);
siteData[othersIndex].seedDownloadSize += sizeToBytes(downloadSize);
siteData[othersIndex].seedTime += timeToSeconds(time);
siteData[othersIndex].seedItemsNumber++;
}
showResult('解析完成:' + title + '
');
});
}
// 输出数据到表格
function outputData() {
// 将站点名称、用户名称和用户的 UID 输出到 result 中
// 使用谷歌 icon 缓存获取站点 icon 地址
let siteIcon = 'https://www.google.com/s2/favicons?domain=' + window.location.hostname;
showResult('站点名称:
' + siteData[siteIndex].siteName + '
');
appendResult('用户名称:' + userNameHTML + '
');
appendResult('用户 ID/UID:' + userID + '
');
// 将 siteData 中所有站点的 做种数量 和 做种体积 输出到 result 中
let totalSeedItemsNumber = 0;
let totalSeedSize = 0;
for (let i = 0; i < siteData.length; i++) {
totalSeedItemsNumber += siteData[i].seedItemsNumber;
totalSeedSize += siteData[i].seedSize;
}
appendResult('做种总量:' + totalSeedItemsNumber + '
');
appendResult('做种总大小:' + bytesToSize(totalSeedSize, 3) + '
');
// 输出数据到表格(优先输出当前站点,然后依次输出其他站点)
// 在同一行输出站点名称、做种数量、做种体积、平均做种人数、做种上传总量、做种下载总量、平均做种时间,并将其原始值写入 data-value 属性中
let table = document.createElement('table');
table.innerHTML = '站点名称 | 做种数量 | 做种体积 | 平均做种人数 | 做种上传总量 | 做种下载总量 | 平均做种时间 |
';
// 输出当前站点的数据(仅当 seedItemsNumber 不为 0 时)
if (siteData[siteIndex].seedItemsNumber !== 0) {
let tr = document.createElement('tr');
tr.innerHTML = '' + siteData[siteIndex].siteName
+ ' | ' + siteData[siteIndex].seedItemsNumber
+ ' | ' + bytesToSize(siteData[siteIndex].seedSize, 3)
+ ' | ' + (siteData[siteIndex].seedersNumber / siteData[siteIndex].seedItemsNumber).toFixed(2)
+ ' | ' + bytesToSize(siteData[siteIndex].seedUploadSize, 2)
+ ' | ' + bytesToSize(siteData[siteIndex].seedDownloadSize, 2)
+ ' | ' + secondsToTime((siteData[siteIndex].seedTime / siteData[siteIndex].seedItemsNumber))
+ ' | ';
table.appendChild(tr);
}
// 输出其他站点的数据(仅当 seedItemsNumber 不为 0 时)
for (let i = 0; i < siteData.length; i++) {
if (i !== siteIndex && siteData[i].seedItemsNumber !== 0) {
let tr = document.createElement('tr');
tr.innerHTML = '' + siteData[i].siteName
+ ' | ' + siteData[i].seedItemsNumber
+ ' | ' + bytesToSize(siteData[i].seedSize, 3)
+ ' | ' + (siteData[i].seedersNumber / siteData[i].seedItemsNumber).toFixed(2)
+ ' | ' + bytesToSize(siteData[i].seedUploadSize, 2)
+ ' | ' + bytesToSize(siteData[i].seedDownloadSize, 2)
+ ' | ' + secondsToTime((siteData[i].seedTime / siteData[i].seedItemsNumber))
+ ' | ';
table.appendChild(tr);
}
}
// 将表格输出到 result 中
appendResult(table.outerHTML);
notice.style.backgroundColor = 'rgba(31,177,65,1)';
notice.style.color = 'white';
// #ss-container 的最小宽度设置为其当前值,防止变形
ssContainer.style.minWidth = ssContainer.offsetWidth + 'px';
// 表格排序功能
// 鼠标悬停显示手型
let tableHead = document.querySelectorAll('#ss-container table tr th');
for (let i = 0; i < tableHead.length; i++) {
tableHead[i].addEventListener('mouseover', function() {
this.style.cursor = 'pointer';
});
}
// 获取表格元素
let tableElement = document.querySelector('#ss-container table');
// 当 #ss-container 中的表格表头项被点击时,使用 sortTable 函数进行排序
for(let i = 0; i < tableHead.length; i++) {
tableHead[i].addEventListener('click', function() {
sortTable(tableElement, i);
});
}
}
// sortTable 函数,根据对应列 col 对 table 进行排序
function sortTable(table, col) {
// 清除所有其他列后的 ' ▲' ' ▼'
let tableHead = document.querySelectorAll('#ss-container table tr th');
for (let i = 0; i < tableHead.length; i++) {
if (i !== col) {
tableHead[i].innerHTML = tableHead[i].innerHTML.replace(' ▲', '').replace(' ▼', '');
}
}
// 如果列后面有 ' ▲',则将其改为 ' ▼',并将 table 进行降序排序
if (tableHead[col].innerHTML.indexOf(' ▲') !== -1) {
tableHead[col].innerHTML = tableHead[col].innerHTML.replace(' ▲', ' ▼');
sortTableDesc(table, col);
}
// 如果列后面有 ' ▼',则将其改为 ' ▲',并将 table 进行升序排序
else if (tableHead[col].innerHTML.indexOf(' ▼') !== -1) {
tableHead[col].innerHTML = tableHead[col].innerHTML.replace(' ▼', ' ▲');
sortTableAsc(table, col);
}
// 如果列后面没有 ' ▲' ' ▼',则将其改为 ' ▲',并将 table 进行升序排序
else {
tableHead[col].innerHTML = tableHead[col].innerHTML + ' ▲';
sortTableAsc(table, col);
}
}
// sortTableAsc 函数,根据对应列 col 对 table 进行升序排序
function sortTableAsc(table, col) {
let rows = table.rows;
let arr = [];
for (let i = 1; i < rows.length; i++) {
arr.push(rows[i]);
}
arr.sort(function(a, b) {
let aVal = a.cells[col].getAttribute('data-value');
let bVal = b.cells[col].getAttribute('data-value');
if (isNaN(aVal) || isNaN(bVal)) {
return aVal.localeCompare(bVal);
}
else {
return aVal - bVal;
}
});
for (let i = 0; i < arr.length; i++) {
table.appendChild(arr[i]);
}
}
// sortTableDesc 函数,根据对应列 col 对 table 进行降序排序
function sortTableDesc(table, col) {
let rows = table.rows;
let arr = [];
for (let i = 1; i < rows.length; i++) {
arr.push(rows[i]);
}
arr.sort(function(a, b) {
let aVal = a.cells[col].getAttribute('data-value');
let bVal = b.cells[col].getAttribute('data-value');
if (isNaN(aVal) || isNaN(bVal)) {
return bVal.localeCompare(aVal);
}
else {
return bVal - aVal;
}
});
for (let i = 0; i < arr.length; i++) {
table.appendChild(arr[i]);
}
}
/* UI 相关 */
// 向页面注入 UI
function injectUI() {
let ssHTML = document.createElement('div');
ssHTML.id = 'ss-container';
ssHTML.innerHTML = `
`;
let ssCSS = document.createElement('style');
ssCSS.innerHTML = `
#ss-high-friction-bar {
display: flex;
flex-direction: column;
justify-content: start;
align-items: center;
margin-bottom: 6px;
cursor: move;
}
.ss-graph {
height: 1px;
width: 60px;
background-color: rgba(0, 0, 0, 0.1);
margin-bottom: 2px;
}
#ss-container {
font-family: "Helvetica Neue", Helvetica, Arial;
font-size: 0.8rem;
line-height: 1.45;
color: rgba(0, 0, 0, 0.76);
box-sizing: border-box;
min-width: 20vw;
max-width: 80vw;
width: auto;
height: auto;
min-height: 15vh;
max-height: 90vh;
background-color: rgba(255, 255, 255, 0.96);
position: fixed;
left: 0;
top: 0;
border-radius: 6px;
margin: 12px;
padding: 12px;
box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.1);
transition: box-shadow 0.1s ease-in-out;
overflow: auto;
z-index: 9999;
}
#ss-container:hover {
box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.25);
background-color: white;
}
#ss-notice {
background-color: rgba(0, 0, 0, 0.05);
width: fit-content;
padding: 0.2rem;
border-radius: 3px;
}
#ss-result {
margin-top: 0.4rem;
}
#ss-result table {
border-collapse: collapse;
margin-top: 0.4rem;
}
#ss-result table, #ss-result th, #ss-result td {
border: 0;
/* background-color: rgba(4,150,255,1); */
}
#ss-result th {
background-color: rgba(4, 142, 255, 0.96);
color: rgba(255, 255, 255, 0.96);
}
#ss-result th, #ss-result td {
text-align: left;
padding: 0.2rem 1rem;
}
#ss-result tr:nth-child(odd) {
background-color: rgba(0, 0, 0, 0.05);
}
#ss-result tr:nth-child(even) {
background-color: rgba(0, 0, 0, 0.1);
}
#ss-result td {
font-size: inherit;
}
`;
document.body.appendChild(ssCSS);
document.body.appendChild(ssHTML);
notice = document.querySelector('#ss-notice');
result = document.querySelector('#ss-result');
}
// 显示提示信息
function showNotice(text) {
notice.style.display = 'block';
notice.querySelector('#ss-notice-text').innerText = text;
}
// 隐藏提示信息
function hideNotice() {
notice.style.display = 'none';
}
// 显示结果
function showResult(text) {
result.style.display = 'block';
result.innerHTML = text;
}
// 追加结果
function appendResult(text) {
result.style.display = 'block';
result.innerHTML += text;
}
// 隐藏结果
function hideResult() {
result.style.display = 'none';
}
// UI 事件绑定
function bindUIActions() {
ssContainer = document.querySelector("#ss-container");
ssBar = document.querySelector("#ss-high-friction-bar");
// 鼠标在 ssBar 上按下时,使 ssContainer 跟随鼠标移动
ssBar.onmousedown = function (e) {
let x = e.clientX - ssContainer.offsetLeft;
let y = e.clientY - ssContainer.offsetTop;
// 增大 ssContainer 的阴影效果
ssContainer.style.boxShadow = "0 0 10px 5px rgba(0, 0, 0, 0.1)";
document.onmousemove = function (e) {
ssContainer.style.left = e.clientX - x - 12 + "px";
ssContainer.style.top = e.clientY - y - 12 + "px";
// 防止鼠标移出浏览器窗口
if (e.clientX - x - 12 < 0) {
ssContainer.style.left = 0;
}
if (e.clientY - y - 12 < 0) {
ssContainer.style.top = 0;
}
if (e.clientX - x - 12 > document.documentElement.clientWidth - ssContainer.offsetWidth) {
ssContainer.style.left = document.documentElement.clientWidth - ssContainer.offsetWidth + "px";
}
if (e.clientY - y - 12 > document.documentElement.clientHeight - ssContainer.offsetHeight) {
ssContainer.style.top = document.documentElement.clientHeight - ssContainer.offsetHeight + "px";
}
}
}
// 鼠标松开时,停止移动
document.onmouseup = function () {
// 恢复 ssContainer 的阴影效果
ssContainer.style.boxShadow = "0 0 5px 2px rgba(0, 0, 0, 0.1)";
document.onmousemove = null;
}
}
/* 主函数 */
(function() {
'use strict';
// 调试
// printSiteData();
// 初始化
init();
// 持续判断页面是否包含做种列表
let timer = setInterval(() => {
console.log('Running...');
if (checkPage()) {
if (!uiReady) {
injectUI();
bindUIActions();
uiReady = true;
}
showNotice('正在统计当前页数据,请稍候...');
// 爬取数据
spider();
if(hasNextPage()) {
showNotice('当前页面统计完成,稍后自动统计下一页');
} else {
showNotice('统计完成,若对结果不满意,可刷新页面后重新尝试');
outputData();
clearInterval(timer);
}
} else {
if (uiReady) {
showNotice('当前页面数据已统计过,有可能是没有数据,或需手动切换至下一页');
}
}
}, 1000);
})();