mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-08-17 03:27:00 +08:00
feat: 支持按导出媒体文件名精确搜索
离线 HTML 搜索支持使用不含扩展名的完整图片或视频哈希文件名进行精确匹配,并保持其他消息字段的模糊搜索行为。\n\n补充图片和视频命中、大小写兼容、哈希片段及带扩展名不命中的 DOM 回归测试。
This commit is contained in:
@@ -995,6 +995,19 @@ const renderExportScript = (name: string): string => `
|
||||
message.contentData && forwardedSearchText(message.contentData.items),
|
||||
message.exportMediaName
|
||||
].filter(Boolean).join(' ').toLowerCase()
|
||||
const exportedImageOrVideoFileStem = (message) => {
|
||||
const mediaType = message.exportMediaType || (message.contentData && message.contentData.type)
|
||||
if (mediaType !== 'image' && mediaType !== 'video') return ''
|
||||
const mediaUrl = String(message.exportMediaUrl || '').split(/[?#]/, 1)[0]
|
||||
const fileName = mediaUrl.slice(mediaUrl.lastIndexOf('/') + 1)
|
||||
if (!fileName) return ''
|
||||
let decodedFileName = fileName
|
||||
try {
|
||||
decodedFileName = decodeURIComponent(fileName)
|
||||
} catch {}
|
||||
const extensionIndex = decodedFileName.lastIndexOf('.')
|
||||
return (extensionIndex > 0 ? decodedFileName.slice(0, extensionIndex) : decodedFileName).toLowerCase()
|
||||
}
|
||||
|
||||
const shareLabel = (typeVal) => {
|
||||
if (String(typeVal) === '5') return '公众号链接'
|
||||
@@ -1516,7 +1529,9 @@ const renderExportScript = (name: string): string => `
|
||||
return allMessages.filter((message) =>
|
||||
(activeConversation === 'all' || message.exportConversationId === activeConversation) &&
|
||||
(activeKind === 'all' || kindOf(message) === activeKind) &&
|
||||
(!term || searchText(message).includes(term))
|
||||
(!term ||
|
||||
searchText(message).includes(term) ||
|
||||
exportedImageOrVideoFileStem(message) === term)
|
||||
)
|
||||
}
|
||||
const applyFilters = (restorePosition = false) => {
|
||||
@@ -1779,7 +1794,7 @@ export function renderExportPage(name: string): string {
|
||||
</div>
|
||||
</div>
|
||||
<div class="controls">
|
||||
<input id="query" type="search" placeholder="搜索发送者或消息内容…" aria-label="搜索消息">
|
||||
<input id="query" type="search" placeholder="搜索发送者、消息内容或媒体文件名(不含后缀)…" aria-label="搜索消息">
|
||||
</div>
|
||||
<div class="filters" id="filters">
|
||||
<button class="filter-button active" type="button" data-kind="all">全部</button>
|
||||
|
||||
@@ -33,7 +33,7 @@ describe('export media', () => {
|
||||
expect(html).toContain('aria-expanded="')
|
||||
expect(html).toContain('setExpandedTimelineYear')
|
||||
expect(html).toContain('data-kind="media"')
|
||||
expect(html).toContain('placeholder="搜索发送者或消息内容…"')
|
||||
expect(html).toContain('placeholder="搜索发送者、消息内容或媒体文件名(不含后缀)…"')
|
||||
expect(html).toContain('font-size: 16px;')
|
||||
expect(html).toContain('filtered.slice(windowStart, windowEnd)')
|
||||
expect(html).toContain('windowStart = Math.max(0, windowEnd - PAGE_SIZE)')
|
||||
@@ -156,6 +156,61 @@ describe('export media', () => {
|
||||
dom.window.close()
|
||||
})
|
||||
|
||||
it('matches exported image and video filenames exactly without their extension', () => {
|
||||
const html = renderExportPage('媒体文件名搜索')
|
||||
const dom = new JSDOM(html, { runScripts: 'outside-only' })
|
||||
const imageFileName = 'image_0123456789abcdef.jpg'
|
||||
const videoFileName = 'video_fedcba9876543210.mp4'
|
||||
const messages: Message[] = [
|
||||
{
|
||||
...messageForArchive('image-name', 'fixture', '媒体文件名搜索', '', 1),
|
||||
type: '图片',
|
||||
exportMediaType: 'image',
|
||||
exportMediaUrl: `media/${imageFileName}`,
|
||||
contentData: { type: 'image' }
|
||||
},
|
||||
{
|
||||
...messageForArchive('video-name', 'fixture', '媒体文件名搜索', '', 2),
|
||||
type: '视频',
|
||||
exportMediaType: 'video',
|
||||
exportMediaUrl: `media/${videoFileName}`,
|
||||
contentData: { type: 'video' }
|
||||
}
|
||||
]
|
||||
Object.assign(dom.window, {
|
||||
__WECHAT_EXPORT__: {
|
||||
version: 1,
|
||||
sourceId: 'fixture',
|
||||
name: '媒体文件名搜索',
|
||||
messages
|
||||
}
|
||||
})
|
||||
|
||||
dom.window.eval(inlineScriptOf(html))
|
||||
const search = dom.window.document.querySelector('#query') as HTMLInputElement
|
||||
|
||||
search.value = 'IMAGE_0123456789ABCDEF'
|
||||
search.dispatchEvent(new dom.window.Event('input'))
|
||||
expect(dom.window.document.querySelectorAll('.message')).toHaveLength(1)
|
||||
expect(dom.window.document.querySelectorAll('img.media-image')).toHaveLength(1)
|
||||
expect(dom.window.document.querySelectorAll('video.media-image')).toHaveLength(0)
|
||||
|
||||
search.value = '0123456789abcdef'
|
||||
search.dispatchEvent(new dom.window.Event('input'))
|
||||
expect(dom.window.document.querySelectorAll('.message')).toHaveLength(0)
|
||||
|
||||
search.value = 'video_fedcba9876543210'
|
||||
search.dispatchEvent(new dom.window.Event('input'))
|
||||
expect(dom.window.document.querySelectorAll('.message')).toHaveLength(1)
|
||||
expect(dom.window.document.querySelectorAll('img.media-image')).toHaveLength(0)
|
||||
expect(dom.window.document.querySelectorAll('video.media-image')).toHaveLength(1)
|
||||
|
||||
search.value = videoFileName
|
||||
search.dispatchEvent(new dom.window.Event('input'))
|
||||
expect(dom.window.document.querySelectorAll('.message')).toHaveLength(0)
|
||||
dom.window.close()
|
||||
})
|
||||
|
||||
it('filters a v2 merged archive by conversation before search and month counts', () => {
|
||||
const html = renderExportPage('合并档案')
|
||||
const dom = new JSDOM(html, { runScripts: 'outside-only' })
|
||||
|
||||
Reference in New Issue
Block a user