defmodule ElixirAiWeb.VoiceLive do
use ElixirAiWeb, :live_view
require Logger
def mount(_params, _session, socket) do
{:ok, assign(socket, state: :idle, transcription: nil), layout: false}
end
def render(assigns) do
~H"""
<%= if @state == :idle do %>
Voice Input
<% end %>
<%= if @state == :recording do %>
Recording
<% end %>
<%= if @state == :processing do %>
Processing…
<% end %>
<%= if @state == :transcribed do %>
Transcription
<% end %>
<%= if @state in [:recording, :processing] do %>
<% end %>
<%= if @state == :transcribed do %>
<.transcription_display transcription={@transcription} />
<% end %>
<%= if @state == :idle do %>
<% end %>
<%= if @state == :recording do %>
<% end %>
<%= if @state == :transcribed do %>
<% end %>
"""
end
defp transcription_display(assigns) do
~H"""
{@transcription}
"""
end
def handle_event("recording_started", _params, socket) do
{:noreply, assign(socket, state: :recording)}
end
def handle_event("audio_recorded", %{"data" => base64, "mime_type" => mime_type}, socket) do
case Base.decode64(base64) do
{:ok, audio_binary} ->
Logger.info(
"VoiceLive: received #{byte_size(audio_binary)} bytes of audio (#{mime_type})"
)
ElixirAi.AudioProcessing.submit(audio_binary, mime_type, self())
{:noreply, assign(socket, state: :processing)}
:error ->
Logger.error("VoiceLive: failed to decode base64 audio data")
{:noreply, assign(socket, state: :idle)}
end
end
def handle_event("recording_error", %{"reason" => reason}, socket) do
Logger.warning("VoiceLive: recording error: #{reason}")
{:noreply, assign(socket, state: :idle)}
end
def handle_event("dismiss_transcription", _params, socket) do
{:noreply, assign(socket, state: :idle, transcription: nil)}
end
def handle_info({:transcription_result, {:ok, text}}, socket) do
{:noreply, assign(socket, state: :transcribed, transcription: text)}
end
def handle_info({:transcription_result, {:error, reason}}, socket) do
Logger.error("VoiceLive: transcription failed: #{inspect(reason)}")
{:noreply, assign(socket, state: :idle)}
end
end