mirror of
https://github.com/langgenius/dify.git
synced 2026-10-04 13:47:55 +08:00
+41![gemini-code-assist[bot]](/avatar/56998622369826d13ab151bc815833de5bcfacd06c957772b394602b82b65bda?size=40)

![autofix-ci[bot]](/avatar/90c1f68f2dec38933926f82cc3639064fb96506a5738419500ebeb5085083409?size=40)







Saumya Talwani
Poojan
sahil-infocusp
非法操作
Pandaaaa906
Asuka Minato
heyszt
autofix-ci[bot]
Ijas
gemini-code-assist[bot]
木之本澪
KinomotoMio
不做了睡大觉
User
Copilot
edvatar
-LAN-
Leilei
HaKu
dependabot[bot]
wangxiaolei
Varun Chawla
Stephen Zhou
yyh
yyh
tda
root
Sisyphus
Niels Kaspers
hj24
Tyson Cung
Stephen Zhou
FFXN
slegarraga
99
Br1an
L1nSn0w
Yunlu Wen
akkoaya
盐粒 Yanli
lif
weiguang li
Copilot
crazywoola
HanWenbo
Coding On Star
CodingOnStar
Stable Genius
Stable Genius
ふるい
Xiyuan Chen
f50e44b24a
Signed-off-by: edvatar <88481784+toroleapinc@users.noreply.github.com> Signed-off-by: -LAN- <laipz8200@outlook.com> Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: majiayu000 <1835304752@qq.com> Co-authored-by: Poojan <poojan@infocusp.com> Co-authored-by: sahil-infocusp <73810410+sahil-infocusp@users.noreply.github.com> Co-authored-by: 非法操作 <hjlarry@163.com> Co-authored-by: Pandaaaa906 <ye.pandaaaa906@gmail.com> Co-authored-by: Asuka Minato <i@asukaminato.eu.org> Co-authored-by: heyszt <270985384@qq.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Ijas <ijas.ahmd.ap@gmail.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: 木之本澪 <kinomotomiovo@gmail.com> Co-authored-by: KinomotoMio <200703522+KinomotoMio@users.noreply.github.com> Co-authored-by: 不做了睡大觉 <64798754+stakeswky@users.noreply.github.com> Co-authored-by: User <user@example.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: edvatar <88481784+toroleapinc@users.noreply.github.com> Co-authored-by: -LAN- <laipz8200@outlook.com> Co-authored-by: Leilei <138381132+Inlei@users.noreply.github.com> Co-authored-by: HaKu <104669497+haku-ink@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: wangxiaolei <fatelei@gmail.com> Co-authored-by: Varun Chawla <34209028+veeceey@users.noreply.github.com> Co-authored-by: Stephen Zhou <38493346+hyoban@users.noreply.github.com> Co-authored-by: yyh <yuanyouhuilyz@gmail.com> Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com> Co-authored-by: tda <95275462+tda1017@users.noreply.github.com> Co-authored-by: root <root@DESKTOP-KQLO90N> Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> Co-authored-by: Niels Kaspers <153818647+nielskaspers@users.noreply.github.com> Co-authored-by: hj24 <mambahj24@gmail.com> Co-authored-by: Tyson Cung <45380903+tysoncung@users.noreply.github.com> Co-authored-by: Stephen Zhou <hi@hyoban.cc> Co-authored-by: FFXN <31929997+FFXN@users.noreply.github.com> Co-authored-by: slegarraga <64795732+slegarraga@users.noreply.github.com> Co-authored-by: 99 <wh2099@pm.me> Co-authored-by: Br1an <932039080@qq.com> Co-authored-by: L1nSn0w <l1nsn0w@qq.com> Co-authored-by: Yunlu Wen <yunlu.wen@dify.ai> Co-authored-by: akkoaya <151345394+akkoaya@users.noreply.github.com> Co-authored-by: 盐粒 Yanli <yanli@dify.ai> Co-authored-by: lif <1835304752@qq.com> Co-authored-by: weiguang li <codingpunk@gmail.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com> Co-authored-by: HanWenbo <124024253+hwb96@users.noreply.github.com> Co-authored-by: Coding On Star <447357187@qq.com> Co-authored-by: CodingOnStar <hanxujiang@dify.com> Co-authored-by: Stable Genius <stablegenius043@gmail.com> Co-authored-by: Stable Genius <259448942+stablegenius49@users.noreply.github.com> Co-authored-by: ふるい <46769295+Echo0ff@users.noreply.github.com> Co-authored-by: Xiyuan Chen <52963600+GareArc@users.noreply.github.com>
197 lines
5.3 KiB
TypeScript
197 lines
5.3 KiB
TypeScript
import { convertToMp3 } from '../utils'
|
|
|
|
// ── Hoisted mocks ──
|
|
|
|
const mocks = vi.hoisted(() => {
|
|
const readHeader = vi.fn()
|
|
const encodeBuffer = vi.fn()
|
|
const flush = vi.fn()
|
|
|
|
return { readHeader, encodeBuffer, flush }
|
|
})
|
|
|
|
vi.mock('lamejs', () => ({
|
|
default: {
|
|
WavHeader: {
|
|
readHeader: mocks.readHeader,
|
|
},
|
|
Mp3Encoder: class MockMp3Encoder {
|
|
encodeBuffer = mocks.encodeBuffer
|
|
flush = mocks.flush
|
|
},
|
|
},
|
|
}))
|
|
|
|
vi.mock('lamejs/src/js/BitStream', () => ({ default: {} }))
|
|
vi.mock('lamejs/src/js/Lame', () => ({ default: {} }))
|
|
vi.mock('lamejs/src/js/MPEGMode', () => ({ default: {} }))
|
|
|
|
// ── helpers ──
|
|
|
|
/** Build a fake recorder whose getChannelData returns DataView-like objects with .buffer and .byteLength. */
|
|
function createMockRecorder(opts: {
|
|
channels: number
|
|
sampleRate: number
|
|
leftSamples: number[]
|
|
rightSamples?: number[]
|
|
}) {
|
|
const toDataView = (samples: number[]) => {
|
|
const buf = new ArrayBuffer(samples.length * 2)
|
|
const view = new DataView(buf)
|
|
samples.forEach((v, i) => {
|
|
view.setInt16(i * 2, v, true)
|
|
})
|
|
return view
|
|
}
|
|
|
|
const leftView = toDataView(opts.leftSamples)
|
|
const rightView = opts.rightSamples ? toDataView(opts.rightSamples) : null
|
|
|
|
mocks.readHeader.mockReturnValue({
|
|
channels: opts.channels,
|
|
sampleRate: opts.sampleRate,
|
|
})
|
|
|
|
return {
|
|
getWAV: vi.fn(() => new ArrayBuffer(44)),
|
|
getChannelData: vi.fn(() => ({
|
|
left: leftView,
|
|
right: rightView,
|
|
})),
|
|
}
|
|
}
|
|
|
|
describe('convertToMp3', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
it('should convert mono WAV data to an MP3 blob', () => {
|
|
const recorder = createMockRecorder({
|
|
channels: 1,
|
|
sampleRate: 44100,
|
|
leftSamples: [100, 200, 300, 400],
|
|
})
|
|
|
|
mocks.encodeBuffer.mockReturnValue(new Int8Array([1, 2, 3]))
|
|
mocks.flush.mockReturnValue(new Int8Array([4, 5]))
|
|
|
|
const result = convertToMp3(recorder)
|
|
|
|
expect(result).toBeInstanceOf(Blob)
|
|
expect(result.type).toBe('audio/mp3')
|
|
expect(mocks.encodeBuffer).toHaveBeenCalled()
|
|
// Mono: encodeBuffer called with only left data
|
|
const firstCall = mocks.encodeBuffer.mock.calls[0]
|
|
expect(firstCall).toHaveLength(1)
|
|
expect(mocks.flush).toHaveBeenCalled()
|
|
})
|
|
|
|
it('should convert stereo WAV data to an MP3 blob', () => {
|
|
const recorder = createMockRecorder({
|
|
channels: 2,
|
|
sampleRate: 48000,
|
|
leftSamples: [100, 200],
|
|
rightSamples: [300, 400],
|
|
})
|
|
|
|
mocks.encodeBuffer.mockReturnValue(new Int8Array([10, 20]))
|
|
mocks.flush.mockReturnValue(new Int8Array([30]))
|
|
|
|
const result = convertToMp3(recorder)
|
|
|
|
expect(result).toBeInstanceOf(Blob)
|
|
expect(result.type).toBe('audio/mp3')
|
|
// Stereo: encodeBuffer called with left AND right
|
|
const firstCall = mocks.encodeBuffer.mock.calls[0]
|
|
expect(firstCall).toHaveLength(2)
|
|
})
|
|
|
|
it('should skip empty encoded buffers', () => {
|
|
const recorder = createMockRecorder({
|
|
channels: 1,
|
|
sampleRate: 44100,
|
|
leftSamples: [100, 200],
|
|
})
|
|
|
|
mocks.encodeBuffer.mockReturnValue(new Int8Array(0))
|
|
mocks.flush.mockReturnValue(new Int8Array(0))
|
|
|
|
const result = convertToMp3(recorder)
|
|
|
|
expect(result).toBeInstanceOf(Blob)
|
|
expect(result.type).toBe('audio/mp3')
|
|
expect(result.size).toBe(0)
|
|
})
|
|
|
|
it('should include flush data when flush returns non-empty buffer', () => {
|
|
const recorder = createMockRecorder({
|
|
channels: 1,
|
|
sampleRate: 22050,
|
|
leftSamples: [1],
|
|
})
|
|
|
|
mocks.encodeBuffer.mockReturnValue(new Int8Array(0))
|
|
mocks.flush.mockReturnValue(new Int8Array([99, 98, 97]))
|
|
|
|
const result = convertToMp3(recorder)
|
|
|
|
expect(result).toBeInstanceOf(Blob)
|
|
expect(result.size).toBe(3)
|
|
})
|
|
|
|
it('should omit flush data when flush returns empty buffer', () => {
|
|
const recorder = createMockRecorder({
|
|
channels: 1,
|
|
sampleRate: 44100,
|
|
leftSamples: [10, 20],
|
|
})
|
|
|
|
mocks.encodeBuffer.mockReturnValue(new Int8Array([1, 2]))
|
|
mocks.flush.mockReturnValue(new Int8Array(0))
|
|
|
|
const result = convertToMp3(recorder)
|
|
|
|
expect(result).toBeInstanceOf(Blob)
|
|
expect(result.size).toBe(2)
|
|
})
|
|
|
|
it('should process multiple chunks when sample count exceeds maxSamples (1152)', () => {
|
|
const samples = Array.from({ length: 2400 }, (_, i) => i % 32767)
|
|
const recorder = createMockRecorder({
|
|
channels: 1,
|
|
sampleRate: 44100,
|
|
leftSamples: samples,
|
|
})
|
|
|
|
mocks.encodeBuffer.mockReturnValue(new Int8Array([1]))
|
|
mocks.flush.mockReturnValue(new Int8Array(0))
|
|
|
|
const result = convertToMp3(recorder)
|
|
|
|
expect(mocks.encodeBuffer.mock.calls.length).toBeGreaterThan(1)
|
|
expect(result).toBeInstanceOf(Blob)
|
|
})
|
|
|
|
it('should encode stereo with right channel subarray', () => {
|
|
const recorder = createMockRecorder({
|
|
channels: 2,
|
|
sampleRate: 44100,
|
|
leftSamples: [100, 200, 300],
|
|
rightSamples: [400, 500, 600],
|
|
})
|
|
|
|
mocks.encodeBuffer.mockReturnValue(new Int8Array([5, 6, 7]))
|
|
mocks.flush.mockReturnValue(new Int8Array([8]))
|
|
|
|
const result = convertToMp3(recorder)
|
|
|
|
expect(result).toBeInstanceOf(Blob)
|
|
for (const call of mocks.encodeBuffer.mock.calls) {
|
|
expect(call).toHaveLength(2)
|
|
expect(call[0]).toBeInstanceOf(Int16Array)
|
|
expect(call[1]).toBeInstanceOf(Int16Array)
|
|
}
|
|
})
|
|
})
|