// ==UserScript==
// @name 花瓣网用户采集页信息增强
// @namespace https://github.com/yourname
// @version 2.0
// @description 在用户采集页面显示图片分辨率、来源和画板信息,支持滚动加载
// @match https://huaban.com/*/pins
// @match https://huaban.com/user/*/pins
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @connect huaban.com
// @license GNU GPLv3
// @downloadURL https://update.greasyfork.icu/scripts/534891/%E8%8A%B1%E7%93%A3%E7%BD%91%E7%94%A8%E6%88%B7%E9%87%87%E9%9B%86%E9%A1%B5%E4%BF%A1%E6%81%AF%E5%A2%9E%E5%BC%BA.user.js
// @updateURL https://update.greasyfork.icu/scripts/534891/%E8%8A%B1%E7%93%A3%E7%BD%91%E7%94%A8%E6%88%B7%E9%87%87%E9%9B%86%E9%A1%B5%E4%BF%A1%E6%81%AF%E5%A2%9E%E5%BC%BA.meta.js
// ==/UserScript==
(function() {
'use strict';
// 添加自定义样式
GM_addStyle(`
.huaban-pin-info {
font-size: 14px;
color: #333;
background: rgba(255,255,255,0.8);
padding: 2px 2px;
border-radius: 2px;
margin-top: 2px;
display: block;
line-height: 1.2;
}
.fki7oGar:hover .huaban-pin-info {
background: rgba(0,0,0,0.7);
color: #fff;
}
.pin-size {
color: #e74c3c;
font-weight: bold;
}
.pin-source {
color: #3498db;
}
.pin-board {
color: #2ecc71;
}
`);
// 1. 获取用户名
const getUsername = () => {
const path = window.location.pathname;
const match = path.match(/\/(?:user\/)?([^\/]+)\/pins/);
return match ? match[1] : null;
};
const username = getUsername();
if (!username) return;
// 2. 数据存储和状态
const pinDataMap = new Map();
let currentMax = null;
let isLoading = false;
let isLastPage = false;
// 3. 获取单页图片数据
async function fetchPinPage() {
if (isLoading || isLastPage) return;
isLoading = true;
let url = `https://huaban.com/v3/${username}/pins?limit=30&fields=pins:PIN|total,page_num,page_size`;
if (currentMax) {
url += `&max=${currentMax}`;
}
try {
const response = await new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: resolve,
onerror: reject
});
});
const data = JSON.parse(response.responseText);
if (data.pins && data.pins.length > 0) {
// 更新分页参数
currentMax = data.pins[data.pins.length - 1].pin_id;
// 存储新获取的pin数据
data.pins.forEach(pin => {
pinDataMap.set(pin.pin_id.toString(), pin);
});
// 检查是否是最后一页
if (data.pins.length < 30) {
isLastPage = true;
}
return data.pins;
} else {
isLastPage = true;
return [];
}
} catch (error) {
console.error('获取数据失败:', error);
return [];
} finally {
isLoading = false;
}
}
// 4. 注入图片信息
function injectPinInfo() {
const pinContainers = document.querySelectorAll('.BR9Lhzqi:not(.info-enhanced)');
pinContainers.forEach(container => {
const pinId = container.getAttribute('data-pin-id');
if (!pinId) return;
const pinInfo = pinDataMap.get(pinId);
if (!pinInfo || !pinInfo.file) {
// 如果数据未加载,且不是正在加载中,则触发加载
if (!isLoading && !isLastPage) {
fetchPinPage().then(injectPinInfo);
}
return;
}
// 标记已处理
container.classList.add('info-enhanced');
// 创建信息元素
const infoElement = document.createElement('div');
infoElement.className = 'huaban-pin-info';
// 构建信息HTML
let infoHTML = `
${pinInfo.file.width} × ${pinInfo.file.height}
`;
// 修改这里:无论是否有来源都显示来源信息
infoHTML += ` | 来源: ${pinInfo.source || '无'}`;
if (pinInfo.board?.title) {
infoHTML += ` | 画板: ${pinInfo.board.title}`;
}
infoElement.innerHTML = infoHTML;
// 插入到图片下方
const targetElement = container.querySelector('.hUfxKZoq') ||
container.querySelector('.__0ionfaOd');
if (targetElement) {
targetElement.insertAdjacentElement('afterend', infoElement);
}
});
}
// 5. 滚动加载处理
const scrollHandler = debounce(() => {
// 当距离底部500px时加载更多
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight - 500) {
fetchPinPage().then(injectPinInfo);
}
}, 300);
// 6. DOM变化观察器
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
if (mutation.addedNodes.length > 0) {
injectPinInfo();
}
});
});
// 7. 初始化
async function initialize() {
// 初始加载第一页
await fetchPinPage();
injectPinInfo();
// 开始观察DOM变化
observer.observe(document.body, {
childList: true,
subtree: true
});
// 添加滚动监听
window.addEventListener('scroll', scrollHandler);
// 定期检查(防止某些情况下遗漏)
setInterval(() => {
if (!isLoading) {
injectPinInfo();
}
}, 3000);
}
// 防抖函数
function debounce(func, wait) {
let timeout;
return function() {
clearTimeout(timeout);
timeout = setTimeout(func, wait);
};
}
initialize();
})();