From a5c5b0b38cfed390ec24422de98b33bddc110b69 Mon Sep 17 00:00:00 2001 From: meishibiezb <750783119@qq.com> Date: Mon, 4 May 2026 22:36:37 +0800 Subject: [PATCH] =?UTF-8?q?fixed:=20=E7=8E=B0=E5=9C=A8=E8=83=BD=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E4=BD=9C=E8=80=85=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/pin-collector.ts | 64 +++++++++++++++++++++++++++++++++++++ entrypoints/content.ts | 20 +++--------- entrypoints/popup/main.ts | 9 ++++-- wxt.config.ts | 2 +- 4 files changed, 76 insertions(+), 19 deletions(-) diff --git a/components/pin-collector.ts b/components/pin-collector.ts index f33539c..38000f7 100644 --- a/components/pin-collector.ts +++ b/components/pin-collector.ts @@ -146,3 +146,67 @@ export function collectPins(): PinData[] { const collector = new PinCollector(); return collector.collect(); } + +/** + * 从花瓣 API 响应中的单个 pin 对象提取 PinData + * @param pin - /v3/boards/{id}/pins 返回的 pins 数组元素 + */ +export function extractPinFromApi(pin: any): PinData { + return { + url: `/pins/${pin.pin_id}/`, + imgSmallSrc: pin.file?.url ?? '', + imgSrc: (pin.file?.url ?? '').replace(/_fw\d+webp|_png/, ''), + imgWidth: pin.file?.width ?? 0, + imgHeight: pin.file?.height ?? 0, + alt: pin.raw_text ?? '', + title: pin.raw_text ?? '', + author: parseAuthorFromRawText(pin.raw_text) ?? '', + time: formatTimestamp(pin.created_at) ?? '', + tags: extractTagsFromText(pin.raw_text), + }; +} + +function formatTimestamp(ts: number): string { + if (!ts) return ''; + const date = new Date(ts * 1000); // API 给的是秒,JS 需要毫秒 + const now = Date.now(); + const diff = now - date.getTime(); + const hours = Math.floor(diff / 3600000); + if (hours < 24) return `${hours}小时`; + const days = Math.floor(hours / 24); + if (days < 30) return `${days}天`; + return date.toLocaleDateString('zh-CN'); // 超过30天直接显示日期 +} + +/** + * 从 raw_text 中解析作者名 + */ +function parseAuthorFromRawText(rawText: string): string { + if (!rawText) return ''; + const lines = rawText.split('\n').map(s => s.trim()).filter(Boolean); + if (lines.length === 0) return ''; + const firstLine = lines[0]; + // 格式: "北道(きたみち)かえる@Aiart" → 去掉 @xxx 后缀 + const atIndex = firstLine.indexOf('@'); + if (atIndex > 0) return firstLine.slice(0, atIndex).trim(); + // 格式: "@无硫火花 的个人主页 - 微博" → 取 @ 后到空格前的用户名 + if (atIndex === 0) { + const username = firstLine.slice(1).split(/\s/)[0]; + return username || firstLine; + } + // 格式: "Z3zz_的照片 - 微相册" + return firstLine; +} + +/** + * 从文本中提取 #标签 + */ +export function extractTagsFromText(text: string): string[] { + const tags: string[] = []; + const tagRegex = /#([\w\u4e00-\u9fff]+)/g; + let match; + while ((match = tagRegex.exec(text)) !== null) { + tags.push(match[1]); + } + return tags; +} \ No newline at end of file diff --git a/entrypoints/content.ts b/entrypoints/content.ts index b40bcd7..945df85 100644 --- a/entrypoints/content.ts +++ b/entrypoints/content.ts @@ -2,6 +2,7 @@ import { showPageMarker } from "@/components/page-marker"; import { PinCollector } from "#imports"; import { AutoLoader } from '@/components/autoLoader'; import type { PinData } from '#imports'; +import { extractPinFromApi } from "@/components/pin-collector"; let loader: AutoLoader | null = null; @@ -57,12 +58,12 @@ export default defineContentScript({ }).catch(() => { }); }, onData: async (items) => { - const newPins: PinData[] = items.map((pin: any) => extractPinData(pin)); + const newPins: PinData[] = items.map((pin: any) => extractPinFromApi(pin)); // 读取已有数据,按 pin_id 去重合并 const stored = await browser.storage.local.get('collectedPins') as { collectedPins?: PinData[] }; const existing: PinData[] = stored.collectedPins || []; - const existingIds = new Set(existing.map((p) => p.pinId)); - const merged = [...existing, ...newPins.filter((p) => !existingIds.has(p.pinId))]; + const existingUrls = new Set(existing.map((p) => p.url)); + const merged = [...existing, ...newPins.filter((p) => !existingUrls.has(p.url))]; await browser.storage.local.set({ collectedPins: merged }); console.log(`[content] 已写入 storage,累计 ${merged.length} 条`); }, @@ -91,17 +92,4 @@ function sayHello() { closable: true, // 用户可点击 × 关闭 autoRemoveSeconds: 10, }); -} - -function extractPinData(pin: any): PinData { - return { - pinId: pin.pin_id ?? '', - imgSmallSrc: pin.file?.url ?? '', - imgSrc: (pin.file?.url ?? '').replace(/_fw\d+webp|_png/, ''), - alt: pin.raw_text ?? '', - author: pin.user?.username ?? '', - time: pin.created_at ?? '', - tags: pin.tags?.map((t: any) => t.tag ?? t) ?? [], - url: `/pins/${pin.pin_id}/`, - }; } \ No newline at end of file diff --git a/entrypoints/popup/main.ts b/entrypoints/popup/main.ts index 85443cf..93a824d 100644 --- a/entrypoints/popup/main.ts +++ b/entrypoints/popup/main.ts @@ -7,8 +7,13 @@ document.querySelector('#app')!.innerHTML = `
- - + + +
+
+
diff --git a/wxt.config.ts b/wxt.config.ts index ddfe1d6..3b4619f 100644 --- a/wxt.config.ts +++ b/wxt.config.ts @@ -4,6 +4,6 @@ import { defineConfig } from 'wxt'; export default defineConfig({ browser: "firefox", manifest: { - permissions: ['storage'], + permissions: ['storage', 'downloads'], }, });