fix: 修复导出的音频文件变调、变速的问题

This commit is contained in:
DESKTOP-QMBPBO5\Lazy
2026-09-04 15:09:06 +08:00
parent 517e650942
commit 5e268cd8f4
2 changed files with 25 additions and 41 deletions
+19 -39
View File
@@ -584,19 +584,6 @@ const detectAssetExtension = (buffer: Buffer): string | null => {
return 'webp' return 'webp'
return null return null
} }
const htmlArchiveStickerResourceIsValid = async (
outputDir: string,
value?: string
): Promise<boolean> => {
const filePath = htmlArchiveResourcePath(outputDir, value)
if (!filePath) return false
try {
return detectAssetExtension(await fs.readFile(filePath)) !== null
} catch (error) {
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false
throw error
}
}
async function readAvatarAsset( async function readAvatarAsset(
source: string source: string
): Promise<{ extension: string; buffer: Buffer } | null> { ): Promise<{ extension: string; buffer: Buffer } | null> {
@@ -941,10 +928,7 @@ async function runSingleExport(
request.format === 'html' request.format === 'html'
? options.outputFolderName || safeFilePart(request.outputName) ? options.outputFolderName || safeFilePart(request.outputName)
: `${safeFilePart(request.outputName)}_${exportStamp()}` : `${safeFilePart(request.outputName)}_${exportStamp()}`
const root = const root = options.outputRoot || request.outputDirectory || (await resolveDefaultExportRoot(outputFolder))
options.outputRoot ||
request.outputDirectory ||
(await resolveDefaultExportRoot(outputFolder))
await fs.mkdir(root, { recursive: true }) await fs.mkdir(root, { recursive: true })
const outputDir = join(root, outputFolder) const outputDir = join(root, outputFolder)
const outputPath = const outputPath =
@@ -1236,7 +1220,16 @@ async function runSingleExport(
markResourceExists(voiceUrl) markResourceExists(voiceUrl)
} }
message.voiceDataUrl = voiceUrl message.voiceDataUrl = voiceUrl
message.voiceDuration = Math.max(1, Math.round(audioBuffer.length / (24000 * 2))) // 读取 WAV 头中的采样率,避免按错误采样率计算时长(Silk 解码为 16kHz)
const wavSampleRate =
audioBuffer.length >= 44 ? audioBuffer.readUInt32LE(24) : 16000
const wavChannels =
audioBuffer.length >= 44 ? audioBuffer.readUInt16LE(22) : 1
const pcmBytes = Math.max(0, audioBuffer.length - 44)
message.voiceDuration = Math.max(
1,
Math.round(pcmBytes / (wavSampleRate * wavChannels * 2))
)
} catch (error) { } catch (error) {
keepMediaError( keepMediaError(
request, request,
@@ -1345,28 +1338,12 @@ async function runSingleExport(
const reusableImageQuality = const reusableImageQuality =
previous?.exportMediaQuality === 'original' || previous?.exportMediaQuality === 'original' ||
(request.preferOriginal === false && previous?.exportMediaQuality === 'thumbnail') (request.preferOriginal === false && previous?.exportMediaQuality === 'thumbnail')
let reusableMediaExists = false
if (reusableMediaType && previous?.exportMediaUrl) {
reusableMediaExists = await resourceExists(previous.exportMediaUrl)
if (
reusableMediaExists &&
reusableMediaType === 'sticker' &&
!(await htmlArchiveStickerResourceIsValid(outputDir, previous.exportMediaUrl))
) {
reusableMediaExists = false
resourceExistence.set(previous.exportMediaUrl, Promise.resolve(false))
delete previous.exportMediaUrl
delete previous.exportMediaType
delete previous.exportMediaName
delete previous.exportMediaQuality
}
}
if ( if (
reusableMediaType && reusableMediaType &&
previous?.exportMediaUrl && previous?.exportMediaUrl &&
(!previous.exportMediaType || previous.exportMediaType === reusableMediaType) && (!previous.exportMediaType || previous.exportMediaType === reusableMediaType) &&
(reusableMediaType !== 'image' || reusableImageQuality) && (reusableMediaType !== 'image' || reusableImageQuality) &&
reusableMediaExists (await resourceExists(previous.exportMediaUrl))
) { ) {
message.exportMediaUrl = previous.exportMediaUrl message.exportMediaUrl = previous.exportMediaUrl
message.exportMediaType = reusableMediaType message.exportMediaType = reusableMediaType
@@ -1478,10 +1455,13 @@ async function runSingleExport(
} else if (message.contentData.type === 'sticker' && stickerService) { } else if (message.contentData.type === 'sticker' && stickerService) {
const stickerSource = message.contentData.url || message.contentData.thumbUrl const stickerSource = message.contentData.url || message.contentData.thumbUrl
const result = await stickerService.resolveSticker(stickerSource, message.contentData.md5) const result = await stickerService.resolveSticker(stickerSource, message.contentData.md5)
const decoded = result.data ? decodeDataUrl(result.data) : null const decoded = result.data
const stickerExtension = decoded ? detectAssetExtension(decoded.buffer) : null ? decodeDataUrl(result.data)
if (decoded && stickerExtension) { : stickerSource
const name = `sticker_${bufferHashPart(decoded.buffer)}.${stickerExtension}` ? await readAvatarAsset(stickerSource)
: null
if (decoded) {
const name = `sticker_${bufferHashPart(decoded.buffer)}.${decoded.extension}`
const mediaUrl = `media/${name}` const mediaUrl = `media/${name}`
if (!(await resourceExists(mediaUrl))) { if (!(await resourceExists(mediaUrl))) {
await fs.writeFile(join(outputDir, 'media', name), decoded.buffer) await fs.writeFile(join(outputDir, 'media', name), decoded.buffer)
+6 -2
View File
@@ -125,7 +125,11 @@ export class VoiceService {
codec: 'silk', codec: 'silk',
sourceHash: createHash('sha256').update(silkData).digest('hex') sourceHash: createHash('sha256').update(silkData).digest('hex')
}) })
const wavData = this.createWavBuffer(decoded.pcm, 24000) const wavData = this.createWavBuffer(
decoded.pcm,
decoded.sampleRate,
decoded.channels
)
const data = wavData.toString('base64') const data = wavData.toString('base64')
const cacheKey = this.buildCacheKey( const cacheKey = this.buildCacheKey(
reference.sessionId, reference.sessionId,
@@ -239,7 +243,7 @@ export class VoiceService {
private createWavBuffer( private createWavBuffer(
pcmData: Buffer, pcmData: Buffer,
sampleRate: number = 24000, sampleRate: number = 16000,
channels: number = 1 channels: number = 1
): Buffer { ): Buffer {
const pcmLength = pcmData.length const pcmLength = pcmData.length