I made a few calls and I want to access using pyth...
# ask-ai
b
I made a few calls and I want to access using python all the audios and listen them? I also want to add filters to detect if there was voice mail or actually answered
t
Thanks for reaching out, Wesley. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I can help with both parts: (1) retrieving and listening to your call recordings in Python, and (2) adding filters to detect whether a call was answered by a person or went to voicemail. Before I give a concrete example, a couple quick clarifying questions so I tailor the code to your setup: • Where are your recordings stored? (Are you using LiveKit Composite egress URLs, an S3 bucket, or another storage location?) • Do you already have API credentials and access to call the EgressService from Python? • Do you want batch post-call analysis (download files and analyze) or real-time detection during calls? If you want a quick working approach now, here's a clear, practical workflow you can follow in Python for batch processing (download → listen → basic voicemail detection). You can adapt this to real-time later if you prefer. 1) List recordings (get URLs) from LiveKit Composite egress • Use the EgressService to list room composite egress. Each egress entry contains a
url
when the recording is ready. • Example pattern (async):
Copy code
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.
Copy code
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.
Copy code
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:
Copy code
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 calls
b
create the full python code
t
I don't have the answer you're looking for. You could also try asking your question: • in one of the other Slack channels or • to https://deepwiki.com/livekit/livekit_composite which is trained on all LiveKit source code If you find the answer, please post it here to help others!
b
What is the method to call? This is the current code
Copy code
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


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
    }
so?