* feat: add audio transcoding support for WhatsApp Cloud API - Introduced `Audio::TranscodeService` to handle audio transcoding to OGG/Opus format. - Updated `Messages::MessageBuilder` to transcode audio attachments based on `transcode_audio` parameter. - Enhanced `WhatsappCloudService` to normalize audio content types and send voice flag for recorded audio in OGG format. - Added utility functions for audio conversion in JavaScript. - Updated Dockerfile to include FFmpeg for audio processing. - Added tests for audio transcoding and WhatsApp Cloud service interactions. * feat: enhance audio handling with transcoding support and error management * feat: improve audio transcoding error handling and enhance audio recording features * feat: enhance audio transcoding process and error handling for better reliability * feat: update recorded audio handling to support boolean and array formats
107 lines
3.9 KiB
Ruby
107 lines
3.9 KiB
Ruby
require 'rails_helper'
|
|
|
|
RSpec.describe Audio::TranscodeService do
|
|
let(:message) { create(:message) }
|
|
let(:attachment) do
|
|
attachment = message.attachments.new(account_id: message.account_id, file_type: :audio)
|
|
attachment.file.attach(io: Rails.root.join('spec/assets/sample.mp3').open, filename: 'sample.mp3', content_type: 'audio/mpeg')
|
|
attachment.save!
|
|
attachment
|
|
end
|
|
|
|
describe '#perform' do
|
|
context 'with unsupported format' do
|
|
it 'raises UnsupportedFormatError' do
|
|
error = nil
|
|
begin
|
|
described_class.new(attachment, 'aac').perform
|
|
rescue StandardError => e
|
|
error = e
|
|
end
|
|
|
|
expect(error).not_to be_nil
|
|
expect(error.class.name).to eq('CustomExceptions::Audio::UnsupportedFormatError')
|
|
expect(error.message).to match(/Unsupported transcode format: aac/)
|
|
end
|
|
end
|
|
|
|
context 'with opus format' do
|
|
it 'skips transcoding when file is already in target format' do
|
|
ogg_attachment = message.attachments.new(account_id: message.account_id, file_type: :audio)
|
|
ogg_attachment.file.attach(io: StringIO.new('ogg_data'), filename: 'recording.ogg', content_type: 'audio/ogg')
|
|
ogg_attachment.save!
|
|
|
|
allow(FFMPEG::Movie).to receive(:new)
|
|
|
|
described_class.new(ogg_attachment, 'opus').perform
|
|
|
|
expect(FFMPEG::Movie).not_to have_received(:new)
|
|
expect(ogg_attachment.file.content_type).to eq('audio/ogg')
|
|
end
|
|
|
|
it 'transcodes audio to ogg/opus format' do
|
|
mock_movie = instance_double(FFMPEG::Movie, valid?: true)
|
|
allow(FFMPEG::Movie).to receive(:new).and_return(mock_movie)
|
|
allow(mock_movie).to receive(:transcode) do |output_path, _options|
|
|
File.write(output_path, 'fake_opus_data')
|
|
end
|
|
|
|
described_class.new(attachment, 'opus').perform
|
|
|
|
expect(attachment.file.filename.to_s).to eq('sample.ogg')
|
|
expect(attachment.file.content_type).to eq('audio/ogg')
|
|
expect(attachment.file_type).to eq('audio')
|
|
end
|
|
|
|
it 'transcodes using source_file when provided' do
|
|
uploaded_file = Rack::Test::UploadedFile.new(Rails.root.join('spec/assets/sample.mp3').to_s, 'audio/mpeg')
|
|
|
|
mock_movie = instance_double(FFMPEG::Movie, valid?: true)
|
|
allow(FFMPEG::Movie).to receive(:new).and_return(mock_movie)
|
|
allow(mock_movie).to receive(:transcode) do |output_path, _options|
|
|
File.write(output_path, 'fake_opus_data')
|
|
end
|
|
|
|
described_class.new(attachment, 'opus', source_file: uploaded_file).perform
|
|
|
|
expect(attachment.file.filename.to_s).to eq('sample.ogg')
|
|
expect(attachment.file.content_type).to eq('audio/ogg')
|
|
expect(attachment.file_type).to eq('audio')
|
|
end
|
|
|
|
it 'raises TranscodingError when the audio file is invalid' do
|
|
mock_movie = instance_double(FFMPEG::Movie, valid?: false)
|
|
allow(FFMPEG::Movie).to receive(:new).and_return(mock_movie)
|
|
|
|
error = nil
|
|
begin
|
|
described_class.new(attachment, 'opus').perform
|
|
rescue StandardError => e
|
|
error = e
|
|
end
|
|
|
|
expect(error).not_to be_nil
|
|
expect(error.class.name).to eq('CustomExceptions::Audio::TranscodingError')
|
|
expect(error.message).to match(/Invalid or unreadable audio file/)
|
|
end
|
|
|
|
it 'raises TranscodingError when FFmpeg fails' do
|
|
mock_movie = instance_double(FFMPEG::Movie, valid?: true)
|
|
allow(FFMPEG::Movie).to receive(:new).and_return(mock_movie)
|
|
allow(mock_movie).to receive(:transcode).and_raise(FFMPEG::Error, 'encoding failed')
|
|
|
|
error = nil
|
|
begin
|
|
described_class.new(attachment, 'opus').perform
|
|
rescue StandardError => e
|
|
error = e
|
|
end
|
|
|
|
expect(error).not_to be_nil
|
|
expect(error.class.name).to eq('CustomExceptions::Audio::TranscodingError')
|
|
expect(error.message).to match(/FFmpeg transcoding failed/)
|
|
end
|
|
end
|
|
end
|
|
end
|