broad-boots-9735
02/26/2026, 1:15 PMtall-airline-16756
02/26/2026, 1:15 PMtall-airline-16756
02/26/2026, 1:15 PMurl when the recording is ready.
• Example pattern (async):
from livekit.api import LiveKitAPI
import asyncio
async def list_recordings():
lk_api = LiveKitAPI(url="<wss://your-instance.livekit.cloud>", api_key="your-key", api_secret="your-secret")
egress_list = await lk_api.egress.list_room_composite_egress()
urls = [e.url for e in egress_list.items if e.url]
return urls
2) Download recordings to local disk
• Use requests to download each file using its URL.
import requests
def download_file(url, out_path):
r = requests.get(url, stream=True)
r.raise_for_status()
with open(out_path, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
3) Play/listen to recordings locally (simple)
• You can use pydub to load files and simpleaudio (or other players) to play.
from pydub import AudioSegment
import simpleaudio as sa
def play_file(path):
audio = AudioSegment.from_file(path)
play_obj = sa.play_buffer(audio.raw_data, num_channels=audio.channels, bytes_per_sample=audio.sample_width, sample_rate=audio.frame_rate)
play_obj.wait_done()
4) Basic voicemail detection (post-call)
• Two straightforward heuristics that you can combine:
◦ Look for an initial greeting phrase common to voicemail systems like "leave a message after the tone" or "please leave a message".
◦ Measure long silent stretches (or high silence ratio) at the start or throughout the recording.
• Example using pydub:
from pydub.silence import detect_nonsilent
def analyze_voicemail(path, silence_thresh=-50, min_silence_len=700):
audio = AudioSegment.from_file(path)
duration_s = len(audio) / 1000.0
# detect non-silent segments (returns list of [start_ms, end_ms])
nonsilent = detect_nonsilent(audio, min_silence_len=min_silence_len, silence_thresh=silence_thresh)
# compute silence ratio
nonsilent_duration = sum((end - start) for start, end in nonsilent) / 1000.0
silence_ratio = 1.0 - (nonsilent_duration / duration_s)
# simple rule: voicemail likely if there is a long continuous silence at start or high silence ratio
starts_with_silence = len(nonsilent) == 0 or nonsilent[0][0] > 2000 # first nonsilent starts after 2s
likely_voicemail = (silence_ratio > 0.3) or starts_with_silence
return {
"duration_s": duration_s,
"silence_ratio": silence_ratio,
"starts_with_silence": starts_with_silence,
"likely_voicemail": likely_voicemail
}
5) Improve accuracy (recommended)
• Combine heuristics with speech-to-text transcription and keyword spotting:
◦ Transcribe the recording (e.g., Whisper, other STT).
◦ Search transcription for voicemail phrases: "please leave a message", "after the tone", "we're not available", etc.
◦ If transcription shows an answering-machine phrase OR long silence/gapped speech, classify as voicemail.
• For best results use both audio-based features (silence patterns, energy) and text features (phrases).
6) Optional: LLM-assisted or ML classifier
• If you want higher accuracy, you can:
◦ Create labeled examples (voicemail vs answered) and train a small classifier using features like silence ratio, number of speaker segments, average segment length, transcription phrases.
◦ Or use an LLM with a function that ingests the transcript plus simple audio stats and returns a classification.
Next steps I can do for you
• Give a ready-to-run script that:
a. Lists egress recordings,
b. Downloads each file,
c. Transcribes with Whisper (or another STT),
d. Applies combined rules (silence + phrases) and outputs a CSV of results.
• Help adapt this for real-time detection (monitor audio during a call and trigger a "likely voicemail" label).
• Provide a sample small ML pipeline (feature extraction + model training) if you want higher accuracy.
Which of these would you like next? Also please tell me where your recordings are stored (LiveKit egress URLs, S3, etc.) and whether you prefer batch or real-time analysis.
Sources: examples/voice_agents | livekit/livekit_composite | DeepWiki | How to detect and handle silence during callsbroad-boots-9735
02/26/2026, 1:17 PMtall-airline-16756
02/26/2026, 1:18 PMbroad-boots-9735
02/26/2026, 1:20 PMfrom livekit.api import LiveKitAPI
import asyncio
async def list_recordings():
lk_api = LiveKitAPI(url="<wss://your-instance.livekit.cloud>", api_key="your-key", api_secret="your-secret")
egress_list = await lk_api.egress.list_room_composite_egress()
urls = [e.url for e in egress_list.items if e.url]
return urls
import requests
def download_file(url, out_path):
r = requests.get(url, stream=True)
r.raise_for_status()
with open(out_path, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
from pydub import AudioSegment
import simpleaudio as sa
def play_file(path):
audio = AudioSegment.from_file(path)
play_obj = sa.play_buffer(audio.raw_data, num_channels=audio.channels, bytes_per_sample=audio.sample_width, sample_rate=audio.frame_rate)
play_obj.wait_done()
from pydub.silence import detect_nonsilent
def analyze_voicemail(path, silence_thresh=-50, min_silence_len=700):
audio = AudioSegment.from_file(path)
duration_s = len(audio) / 1000.0
# detect non-silent segments (returns list of [start_ms, end_ms])
nonsilent = detect_nonsilent(audio, min_silence_len=min_silence_len, silence_thresh=silence_thresh)
# compute silence ratio
nonsilent_duration = sum((end - start) for start, end in nonsilent) / 1000.0
silence_ratio = 1.0 - (nonsilent_duration / duration_s)
# simple rule: voicemail likely if there is a long continuous silence at start or high silence ratio
starts_with_silence = len(nonsilent) == 0 or nonsilent[0][0] > 2000 # first nonsilent starts after 2s
likely_voicemail = (silence_ratio > 0.3) or starts_with_silence
return {
"duration_s": duration_s,
"silence_ratio": silence_ratio,
"starts_with_silence": starts_with_silence,
"likely_voicemail": likely_voicemail
}broad-boots-9735
02/26/2026, 1:32 PM