Merge pull request #9 from Rodribm10/fix-media-audio-v2

Fix media audio v2
This commit is contained in:
Rodribm10 2026-02-28 18:46:37 -03:00 committed by GitHub
commit ff52cf23e7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 40 additions and 6 deletions

View File

@ -114,6 +114,7 @@ class Attachment < ApplicationRecord
end
def audio_metadata
normalize_opus_blob_content_type!
audio_file_data = base_data.merge(file_metadata)
audio_file_data.merge(
{
@ -205,12 +206,12 @@ class Attachment < ApplicationRecord
{ host: host, protocol: uri.scheme }
end
# Marcel gem may detect OGG/Opus files as audio/opus instead of audio/ogg.
# Lazily normalize existing blobs so presigned URLs serve the correct Content-Type.
# Only applies to .ogg files — .opus files legitimately use audio/opus.
# WhatsApp audio arrives as OGG/Opus container. Some paths save it as audio/opus
# (raw Opus MIME type) which browsers cannot play via <audio> tag.
# Lazily normalise to audio/ogg on first access, regardless of filename extension.
def normalize_opus_blob_content_type!
blob = file.blob
return unless blob.content_type == 'audio/opus' && blob.filename.to_s.end_with?('.ogg')
return unless blob.content_type == 'audio/opus'
blob.update_column(:content_type, 'audio/ogg') # rubocop:disable Rails/SkipsModelValidations
end

View File

@ -105,6 +105,29 @@ class Whatsapp::Providers::Wuzapi::PayloadParser
params.dig(:event, :Info, :IsGroup) || params.dig(:event, :IsGroup)
end
def attachment_params
media_key = case message_type
when :image then :imageMessage
when :audio then :audioMessage
when :video then :videoMessage
when :document then :documentMessage
when :sticker then :stickerMessage
end
return nil unless media_key
msg = unwrap_ephemeral_message(params.dig(:event, :Message))
data = msg[media_key]
return nil unless data.is_a?(Hash)
{
external_url: data['URL'],
file_name: data['fileName'] || "file_#{external_id}",
mimetype: data['mimetype'],
thumbnail: data['JPEGThumbnail'],
media_key: data['mediaKey']
}
end
def text_content
# Direct text field (some WuzAPI versions)
return params.dig(:event, :Text) if params.dig(:event, :Text).present?

View File

@ -14,7 +14,7 @@ class Whatsapp::Wuzapi::MediaHandler
file: {
io: file_io,
filename: final_filename(attachment_data),
content_type: attachment_data[:mimetype] || 'application/octet-stream'
content_type: sanitize_content_type(attachment_data[:mimetype], @parser.message_type)
}
)
end
@ -68,12 +68,22 @@ class Whatsapp::Wuzapi::MediaHandler
name = data[:file_name] || "wuzapi_#{Time.now.to_i}"
ext = File.extname(name)
ext = detect_extension(data[:mimetype], @parser.message_type) if ext.blank?
# Normalise audio extension: WhatsApp always sends OGG/Opus, never MP3
ext = '.ogg' if @parser.message_type == :audio && ext == '.mp3'
"#{File.basename(name, '.*')}#{ext}"
end
def sanitize_content_type(mimetype, type)
# WhatsApp audio is OGG-wrapped Opus. audio/opus is raw Opus (wrong container header).
# Saving as audio/ogg ensures browsers can play via <audio> without issues.
return 'audio/ogg' if type == :audio && mimetype.to_s.include?('opus')
mimetype || 'application/octet-stream'
end
def detect_extension(mimetype, type)
return '.jpg' if type == :image || type == :sticker
return '.mp3' if type == :audio
return '.ogg' if type == :audio
return '.mp4' if type == :video
case mimetype