mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-08-17 19:47:08 +08:00
修复bug,针对部分没有MD5名字的视频也能支持导出
This commit is contained in:
@@ -1152,11 +1152,10 @@ export async function runExport(
|
||||
].filter((value): value is string => Boolean(value))
|
||||
if (!videoService) {
|
||||
keepMediaError(request, message, '数据库未连接,无法定位本地视频')
|
||||
} else if (hashes.length === 0) {
|
||||
keepMediaError(request, message, '视频标识不完整,无法定位本地视频')
|
||||
} else {
|
||||
const resolved = await videoService.resolve(hashes, {
|
||||
createTime: message.createTime,
|
||||
byteLength: message.contentData.byteLength,
|
||||
duration: message.contentData.duration,
|
||||
width: message.contentData.width,
|
||||
height: message.contentData.height
|
||||
|
||||
+7
-1
@@ -1339,7 +1339,13 @@ app.whenReady().then(async () => {
|
||||
async (
|
||||
_,
|
||||
hashes: string[],
|
||||
options?: { createTime?: number; duration?: number; width?: number; height?: number }
|
||||
options?: {
|
||||
createTime?: number
|
||||
byteLength?: number
|
||||
duration?: number
|
||||
width?: number
|
||||
height?: number
|
||||
}
|
||||
) => {
|
||||
if (!videoAssetService) {
|
||||
const client = chat.getChatDb()?.getWcdb4Client()
|
||||
|
||||
@@ -65,6 +65,7 @@ type VideoContent = {
|
||||
md5?: string
|
||||
newMd5?: string
|
||||
rawMd5?: string
|
||||
byteLength?: number
|
||||
duration?: number
|
||||
width?: number
|
||||
height?: number
|
||||
@@ -160,12 +161,11 @@ function parseVideoMessage(content: string): ParsedContent {
|
||||
const md5 = normalizeMd5(extractXmlAttribute(decoded, 'videomsg', 'md5'))
|
||||
const newMd5 = normalizeMd5(extractXmlAttribute(decoded, 'videomsg', 'newmd5'))
|
||||
const rawMd5 = normalizeMd5(extractXmlAttribute(decoded, 'videomsg', 'rawmd5'))
|
||||
if (!md5 && !newMd5 && !rawMd5) return { type: 'unknown', raw: content }
|
||||
|
||||
const byteLength = Number(extractXmlAttribute(decoded, 'videomsg', 'length')) || undefined
|
||||
const duration = Number(extractXmlAttribute(decoded, 'videomsg', 'playlength')) || undefined
|
||||
const width = Number(extractXmlAttribute(decoded, 'videomsg', 'cdnthumbwidth')) || undefined
|
||||
const height = Number(extractXmlAttribute(decoded, 'videomsg', 'cdnthumbheight')) || undefined
|
||||
return { type: 'video', md5, newMd5, rawMd5, duration, width, height }
|
||||
return { type: 'video', md5, newMd5, rawMd5, byteLength, duration, width, height }
|
||||
}
|
||||
|
||||
function parseSystemMessage(content: string): ParsedContent {
|
||||
|
||||
@@ -10,6 +10,7 @@ type VideoAsset = {
|
||||
|
||||
export type VideoResolveOptions = {
|
||||
createTime?: number
|
||||
byteLength?: number
|
||||
duration?: number
|
||||
width?: number
|
||||
height?: number
|
||||
@@ -52,7 +53,13 @@ export class VideoAssetService {
|
||||
.filter((value) => /^[a-f0-9]{32}$/.test(value))
|
||||
)
|
||||
)
|
||||
if (candidates.length === 0) return { success: false, error: '视频标识为空' }
|
||||
const hasMetadata =
|
||||
Number(options.byteLength) > 0 ||
|
||||
Number(options.duration) > 0 ||
|
||||
(Number(options.width) > 0 && Number(options.height) > 0)
|
||||
if (candidates.length === 0 && !hasMetadata) {
|
||||
return { success: false, error: '视频标识为空' }
|
||||
}
|
||||
|
||||
const hardlinkDb = path.join(
|
||||
this.client.getAccountRoot(),
|
||||
@@ -178,6 +185,21 @@ export class VideoAssetService {
|
||||
|
||||
let narrowed = assets
|
||||
let appliedCriteria = 0
|
||||
const byteLength = Number(options.byteLength)
|
||||
if (byteLength > 0) {
|
||||
const matches = narrowed.filter((asset) => {
|
||||
try {
|
||||
return fs.statSync(asset.filePath).size === byteLength
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
})
|
||||
if (matches.length > 0) {
|
||||
narrowed = matches
|
||||
appliedCriteria += 1
|
||||
}
|
||||
}
|
||||
|
||||
const width = Number(options.width)
|
||||
const height = Number(options.height)
|
||||
if (width > 0 && height > 0) {
|
||||
|
||||
Vendored
+8
-1
@@ -102,6 +102,7 @@ export type ParsedContent =
|
||||
md5?: string
|
||||
newMd5?: string
|
||||
rawMd5?: string
|
||||
byteLength?: number
|
||||
duration?: number
|
||||
width?: number
|
||||
height?: number
|
||||
@@ -278,7 +279,13 @@ declare global {
|
||||
}>
|
||||
getVideo: (
|
||||
hashes: string[],
|
||||
options?: { createTime?: number; duration?: number; width?: number; height?: number }
|
||||
options?: {
|
||||
createTime?: number
|
||||
byteLength?: number
|
||||
duration?: number
|
||||
width?: number
|
||||
height?: number
|
||||
}
|
||||
) => Promise<{ success: boolean; url?: string; poster?: string; error?: string }>
|
||||
getSticker: (
|
||||
cdnUrl?: string,
|
||||
|
||||
@@ -179,7 +179,13 @@ const api = {
|
||||
) => ipcRenderer.invoke('db:getImage', imageMd5, imageDatNameOrThumb, sessionId, options),
|
||||
getVideo: (
|
||||
hashes: string[],
|
||||
options?: { createTime?: number; duration?: number; width?: number; height?: number }
|
||||
options?: {
|
||||
createTime?: number
|
||||
byteLength?: number
|
||||
duration?: number
|
||||
width?: number
|
||||
height?: number
|
||||
}
|
||||
) => ipcRenderer.invoke('db:getVideo', hashes, options),
|
||||
getSticker: (cdnUrl?: string, md5?: string) => ipcRenderer.invoke('db:getSticker', cdnUrl, md5),
|
||||
startExport: (request: ExportRequest) => ipcRenderer.invoke('export:start', request),
|
||||
|
||||
@@ -5,6 +5,7 @@ interface VideoBubbleProps {
|
||||
newMd5?: string
|
||||
rawMd5?: string
|
||||
createTime?: number
|
||||
byteLength?: number
|
||||
duration?: number
|
||||
width?: number
|
||||
height?: number
|
||||
@@ -15,6 +16,7 @@ export function VideoBubble({
|
||||
newMd5,
|
||||
rawMd5,
|
||||
createTime,
|
||||
byteLength,
|
||||
duration,
|
||||
width,
|
||||
height
|
||||
@@ -28,7 +30,7 @@ export function VideoBubble({
|
||||
useEffect(() => {
|
||||
let cancelled = false
|
||||
window.api
|
||||
.getVideo(hashes, { createTime, duration, width, height })
|
||||
.getVideo(hashes, { createTime, byteLength, duration, width, height })
|
||||
.then((result) => {
|
||||
if (!cancelled) setMedia(result.success ? result : { error: result.error })
|
||||
})
|
||||
@@ -38,7 +40,7 @@ export function VideoBubble({
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [createTime, duration, hashes, height, width])
|
||||
}, [byteLength, createTime, duration, hashes, height, width])
|
||||
|
||||
if (!media.url) {
|
||||
return <div className="video-placeholder">{media.error || '视频加载中…'}</div>
|
||||
|
||||
@@ -82,6 +82,7 @@ export function MessageBubble({
|
||||
newMd5={message.contentData.newMd5}
|
||||
rawMd5={message.contentData.rawMd5}
|
||||
createTime={message.createTime}
|
||||
byteLength={message.contentData.byteLength}
|
||||
duration={message.contentData.duration}
|
||||
width={message.contentData.width}
|
||||
height={message.contentData.height}
|
||||
|
||||
@@ -111,6 +111,7 @@ type VideoContent = {
|
||||
md5?: string
|
||||
newMd5?: string
|
||||
rawMd5?: string
|
||||
byteLength?: number
|
||||
duration?: number
|
||||
width?: number
|
||||
height?: number
|
||||
|
||||
Reference in New Issue
Block a user