/** * 花瓣网 Pin 收集器 —— 自动提取页面中所有 Pin 卡片信息 */ export interface PinData { /** 相对路径,如 "/pins/7118240882" */ url: string; /** 图片源地址 */ imgSrc: string; /** 图片原始宽度 */ imgWidth: number; /** 图片原始高度 */ imgHeight: number; /** alt 原始文本 */ alt: string; /** title 原始文本 */ title: string; /** 提取的作者名 */ author?: string; /** 提取的发布时间,如 "15小时" */ time?: string; /** 提取的标签,不含 # 号 */ tags: string[]; } export class PinCollector { private pins: PinData[] = []; /** * 扫描当前文档,收集所有 Pin 数据 * @returns 收集到的 Pin 数组 */ collect(): PinData[] { this.pins = []; const anchors = document.querySelectorAll( 'a.__7D5D_BHJ' ); anchors.forEach((anchor) => { try { const pin = this.parsePin(anchor); if (pin) this.pins.push(pin); } catch (e) { console.warn('解析 Pin 失败', e); } }); return this.pins; } /** * 获取最近一次收集的结果(不重新扫描) */ getPins(): PinData[] { return this.pins; } /** * 将收集到的 Pin 保存到 browser.storage.local * @param key 存储键名,默认 "collectedPins" */ async saveToStorage(key = 'collectedPins'): Promise { await browser.storage.local.set({ [key]: this.pins }); console.log(`已保存 ${this.pins.length} 个 Pin 到存储`); } /** * 从 browser.storage.local 加载之前保存的 Pin * @param key 存储键名 * @returns 加载到的 Pin 数组 */ async loadFromStorage(key = 'collectedPins'): Promise { const result = await browser.storage.local.get(key); if (result[key]) { this.pins = result[key] as PinData[]; } return this.pins; } /** * 解析单个 a 元素为 PinData */ private parsePin(anchor: HTMLAnchorElement): PinData | null { const href = anchor.getAttribute('href'); if (!href) return null; const img = anchor.querySelector('img.hb-image'); if (!img) return null; const altText = img.getAttribute('alt') || ''; const titleText = img.getAttribute('title') || ''; const fullText = altText || titleText; const parts = fullText .split('\n') .map((p) => p.trim()) .filter(Boolean); // 解析作者和发布时间 let author: string | undefined; let time: string | undefined; if (parts.length >= 1) { // 第一行格式通常为:作者名 @username const authorMatch = parts[0].match(/^(.+?)\s*@/); author = authorMatch ? authorMatch[1].trim() : parts[0]; } if (parts.length >= 2 && parts[1].startsWith('·')) { time = parts[1].replace(/^·\s*/, '').trim(); } // 提取标签 #xxx (支持中文) const tags: string[] = []; const tagRegex = /#([\w\u4e00-\u9fff]+)/g; let match; while ((match = tagRegex.exec(fullText)) !== null) { tags.push(match[1]); } const width = parseFloat(img.getAttribute('width') || '0'); const height = parseFloat(img.getAttribute('height') || '0'); return { url: href, imgSrc: img.src, imgWidth: width, imgHeight: height, alt: altText, title: titleText, author, time, tags, }; } } /** * 快捷函数:直接返回当前页面的所有 Pin 数据 */ export function collectPins(): PinData[] { const collector = new PinCollector(); return collector.collect(); }