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 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(
source: string
): Promise<{ extension: string; buffer: Buffer } | null> {
@@ -941,10 +928,7 @@ async function runSingleExport(
request.format === 'html'
? options.outputFolderName || safeFilePart(request.outputName)
: `${safeFilePart(request.outputName)}_${exportStamp()}`
const root =
options.outputRoot ||
request.outputDirectory ||
(await resolveDefaultExportRoot(outputFolder))
const root = options.outputRoot || request.outputDirectory || (await resolveDefaultExportRoot(outputFolder))
await fs.mkdir(root, { recursive: true })
const outputDir = join(root, outputFolder)
const outputPath =
@@ -1236,7 +1220,16 @@ async function runSingleExport(
markResourceExists(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) {
keepMediaError(
request,
@@ -1345,28 +1338,12 @@ async function runSingleExport(
const reusableImageQuality =
previous?.exportMediaQuality === 'original' ||
(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 (
reusableMediaType &&
previous?.exportMediaUrl &&
(!previous.exportMediaType || previous.exportMediaType === reusableMediaType) &&
(reusableMediaType !== 'image' || reusableImageQuality) &&
reusableMediaExists
(await resourceExists(previous.exportMediaUrl))
) {
message.exportMediaUrl = previous.exportMediaUrl
message.exportMediaType = reusableMediaType
@@ -1478,10 +1455,13 @@ async function runSingleExport(
} else if (message.contentData.type === 'sticker' && stickerService) {
const stickerSource = message.contentData.url || message.contentData.thumbUrl
const result = await stickerService.resolveSticker(stickerSource, message.contentData.md5)
const decoded = result.data ? decodeDataUrl(result.data) : null
const stickerExtension = decoded ? detectAssetExtension(decoded.buffer) : null
if (decoded && stickerExtension) {
const name = `sticker_${bufferHashPart(decoded.buffer)}.${stickerExtension}`
const decoded = result.data
? decodeDataUrl(result.data)
: stickerSource
? await readAvatarAsset(stickerSource)
: null
if (decoded) {
const name = `sticker_${bufferHashPart(decoded.buffer)}.${decoded.extension}`
const mediaUrl = `media/${name}`
if (!(await resourceExists(mediaUrl))) {
await fs.writeFile(join(outputDir, 'media', name), decoded.buffer)
+6 -2
View File
@@ -125,7 +125,11 @@ export class VoiceService {
codec: 'silk',
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 cacheKey = this.buildCacheKey(
reference.sessionId,
@@ -239,7 +243,7 @@ export class VoiceService {
private createWavBuffer(
pcmData: Buffer,
sampleRate: number = 24000,
sampleRate: number = 16000,
channels: number = 1
): Buffer {
const pcmLength = pcmData.length