I’m trying to convert an audio file that has been (successfully) uploaded to my data table to mp3 format. I’d like to convert “call_audio.mp3” to an mp3. Despite the name, it is not being recognized as a .mp3 file upstream. I believe it is still being treated as a wav file.
What I’ve tried and what’s not working:
I’m using pydub, and so far I’ve tried:
You would need to search by something other than the media field. You have a field named audio_uuid that looks like a unique id. You could search by the audio_uuid for the file you want to convert.
You should also be using get instead of search, since you know you’re only going to get one result out of it.
Your larger issue, though, is that you’re calling AudioSegment.from_wav, which expects a file name, and passing it a data table row.
I’ve never had to deal with getting media from a data table into pydub, so can’t help you with that, but a lot of other folks on the forum have done similar things. Do some searching around about how to create a temporary file from a media object, or maybe see if pydub has a function that will take the actual bytes of the file instead of the file name.
A quick check on StackOverflow about a similar question about pydub says you should be able to pass a file like object to the .from_wav() method. This means you could use BytesIO
So putting everything so far all together:
import io
row = app_tables.recorded_audio.get(audio_uuid="c2023cc2-ab9e-4516-84cc-789e49aff4b6")
if row is not None:
f_stream = io.BytesIO( row['audio'].get_bytes() )
call_recording = AudioSegment.from_wav( f_stream )
#etc etc
...
at /usr/local/lib/python3.7/site-packages/pydub/audio_segment.py, line 725
called from /usr/local/lib/python3.7/site-packages/pydub/audio_segment.py, line 750
called from AudioServer, line 44
called from AudioRecorder, line 24
So it looks like you are having an error with whatever you are passing to pydub, I think earlier in this thread you mentioned something about not knowing if it was a wav or not.
It looks like the Anvil part is working ok.
Maybe check the pydub docs? I’m not sure why a library is trying to decode audio data using ffmpeg, but I don’t know that much about what pydub does.
The error mentions [wav @ 0x61cbcc0] invalid start code [26]E[223][163] in RIFF header so Im guessing your assumption that you are passing it a full .wav file and not just the audio data from that file may be incorrect?
I think you are going to have to revisit your code that leads you here.
I think I see the issue. When I print the audio type, I get “audio/webm;codecs=opus”. So, it doesn’t appear to be a .wav file like I thought. So now, I (think?) I just need to figure out how to convert from “opus” to mp3. I’ll post again when I figure it out.
A couple of simple tweaks got me past that issue. For example, it needed to be “AudioSegment.from_file”, instead of “AudioSegment.from_wav”. I was incorrectly assuming it was a wav file.
And I needed to change it from:
AudioSegment.from_file(f_stream, “webm”)
Instead of:
AudioSegment.from_file(f_stream)
However, when I try to save the newly converted mp3 file to the data table, I get the below error:
anvil.server.SerializationError: Cannot serialize arguments to function. Cannot serialize <class ‘_io.BufferedRandom’> object at msg[‘kwargs’][‘audio’]
Any idea how I should save the mp3 file to the data table if “add_row” won’t work?
add_row works fine, you’re just passing it something it isn’t prepared to deal with (whatever the return from call_recording.export is).
You need to take that return value and build a Media object from it, and put the Media object into the table. Look at the BlobMedia example in the docs: Anvil Docs | Files, Media and Binary Data
If the return from call_recording.export is a byte stream, then you should be good just following the BlobMedia example. If it’s something else, you’ll need to convert it to a byte stream.
Im guessing this would also work (keeping everything in memory, why not keep the same theme all the way through):
@anvil.server.callable
def get_blobmedia(b64str,mediatype):
binary_content = base64.standard_b64decode(b64str)
my_media = anvil.BlobMedia(content_type="audio/mp3", content=binary_content, name="call_audio.mp3")
row = app_tables.recorded_audio.get(audio_uuid="19126b6c-30d7-4118-8488-aad316666f57")
if row is not None:
f_stream = io.BytesIO(row['audio'].get_bytes())
call_recording = AudioSegment.from_file(f_stream, "webm")
with io.BytesIO(b"") as f_out_stream:
call_recording.export( f_out_stream, format="mp3")
print(call_recording.name) # Does this have a name? Are we ever passing it one using .get_bytes? (no) should it be ""call_audio.mp3" ?
call_recording = anvil.BlobMedia('mp3', f_out_stream.read(), name=call_recording.name) # See question above about the name
app_tables.recorded_audio.add_row(timestamp=datetime.datetime.now(), audio_uuid=str(uuid.uuid4()), audio=call_recording)
return my_media