Skip to content

Commit

Permalink
Render waveform in a different context (#10)
Browse files Browse the repository at this point in the history
* add firestore_sdk ad session_manager

* save user chats on firestore

* pass down session_id for a deterministic workflow

* handle conversion of chat object to/fro a dict

* remove references to langchain

* reuse a previously downloaded audiofile if it's processable

* render audiocast metdata on share page

* cleanup

* temp remove audio_enchancement

* sanitize audiocast transcript

* add elevenlabs client

* add __text_to_speech_elevenlabs; cleanup

* use dry in text_to_speech

* only lint on python versions 3.11 and 3.12

* add write permission to deploy job for marocchino/sticky-pull-request-comment

* use eleven_multilingual_v2 model for improved stability, accuracy and quality

* Refactor audiocast page to include waveform visualization

* put waveform viz in an expander

* cleanup

* move download_waveform_video internal to render_waveform

* allow toggling waveform visualizer

* save waveform to gcs

* reshuffle dependencies in requirements.txt

* add pycairo to deps

* add a server app

* add header and process_time middlewares

* make utils a shared folder

* use root level services in app dir

* write an improved init_project workflow

* move streamlit related packages into app folder

* convert app.src.utils into a module

* install setup tools and create a setup file for utils_pkg

* remove streamlit content from chat_utils

* cleanup remnants

* more cleanup

* update package imports and initialization logic for utils_pkg

* hit the backend in for chat workflow in generate_stream_response

* add generate_audiocast endpoint handler

* create a handler for get_audiocast by session_id

* abstract waveform_utils; save wave_form on_generate audiocast

* hit the server for generate_audiocast and get_audiocast

* abstract audiocast inteface definitions

* move audiocast_request to server

* remove unused artifacts

* save and update metadata.info

* Remove audiocast page and add docstrings to session manager methods

* revise github actions workflow files

* add deploy_server.yml

* rename deploy server workflow to reflect server deployment

* upgrade to the latest version of pip

* cleanup

* remove the underscore in the service name

* remove unused env variables

* cleanup

* add dockerfile for deploying the server

* populate a dockerignore file on the fly

* remove unused context from gcloudignore

* localize the .*ignore files

* move the dockerfile to root project pre-deploy step

* specify corret path to streamlit index

* Add debug output for Docker and gcloud ignore files; set no_traffic to false for deployment

* add a gunicorn config file

* rename server to api

* rename all footprints of server to api

* refactor: update workflow names and paths for cloudrun deployment

* bump version to 1.0.1 and update project description

* move chat_request to api utils

* move api only utils into api folder

* rename utils_pkg to shared_utils_pkg

* add pre-commit config

* refactor: clean up sidebar and improve metadata subscription handling

* feat: add Streamlit configuration for browser and client settings

* log the project_id in init_admin_sdk

* remove reference_code from .gitignore

* allow other ui elements to render before loading waveform video

* remove subscription to metadata.info
  • Loading branch information
nwaughachukwuma authored Nov 6, 2024
1 parent ca62856 commit 9c31b5c
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 42 deletions.
3 changes: 2 additions & 1 deletion app/pages/audiocast.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ async def render_audiocast_page():
if audiocast["created_at"]:
st.markdown(f"> Created: {audiocast["created_at"]}")

share_url = render_audiocast_handler(session_id, audiocast)
share_url, finalize_task = render_audiocast_handler(session_id, audiocast)

share_col, restart_row = st.columns(2, vertical_alignment="center")
with share_col:
Expand All @@ -42,6 +42,7 @@ async def render_audiocast_page():
if st.button("Create your Audiocast", use_container_width=True):
navigate_to_home()

await finalize_task
except Exception as e:
st.error(f"Error loading audiocast: {str(e)}")
else:
Expand Down
43 changes: 24 additions & 19 deletions app/src/utils/metadata_subscription.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,34 @@
from queue import Queue
import asyncio

import streamlit as st

from shared_utils_pkg.session_manager import SessionManager


def subscribe_to_audio_generation(session_id: str):
"""Subscribe to audio generation metadata"""
q = Queue()
class SubscribeToAudioGeneration:
def __init__(self, session_id: str):
self.session_id = session_id
self.info: str | None = None

def handler(info: str | None):
if info:
q.put(info, block=False)
def create(self):
"""Subscribe to audio generation metadata"""

db = SessionManager(session_id)
doc_watch = db.subscribe_to_metadata_info(handler)
def __handler(info: str):
self.info = info

with st.empty():
while True:
try:
info = q.get(timeout=2)
if not info:
break
st.info(info)
except Exception:
break
db = SessionManager(self.session_id)
return db.subscribe_to_metadata_info(__handler)

return doc_watch
async def _update_ui(self, read_timeout=10):
"""Update the UI with the current info attribute value."""
timeout = 0
container = st.empty()

with container:
while read_timeout > timeout:
if self.info:
container.info(self.info)
await asyncio.sleep(1)
timeout += 1

container.empty()
6 changes: 3 additions & 3 deletions app/src/utils/render_audiocast.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import streamlit as st

from src.utils.custom_components import copy_button
from src.utils.render_audiocast_utils import (
GenerateAudiocastDict,
Expand All @@ -8,15 +7,15 @@
from src.utils.session_state import reset_session


def render_audiocast(session_id: str):
async def render_audiocast(session_id: str):
"""
Render the audiocast based on the user's preferences
- Display current audiocast if available
"""
st.markdown("#### Your Audiocast")
current_audiocast: GenerateAudiocastDict = st.session_state.current_audiocast

share_url = render_audiocast_handler(session_id, current_audiocast)
share_url, finalize_task = render_audiocast_handler(session_id, current_audiocast)

share_col, restart_row = st.columns(2, vertical_alignment="center")

Expand All @@ -28,3 +27,4 @@ def render_audiocast(session_id: str):
if st.button("Generate New Audiocast", use_container_width=True):
reset_session()
st.rerun()
await finalize_task
26 changes: 15 additions & 11 deletions app/src/utils/render_audiocast_utils.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import asyncio
import re
from pathlib import Path
from typing import cast

import httpx
import streamlit as st
from src.utils.metadata_subscription import subscribe_to_audio_generation
from src.utils.render_waveform import render_waveform
from src.utils.render_waveform import load_waveform_video, render_waveform

from env_var import API_URL, APP_URL
from shared_utils_pkg.audiocast_utils import GenerateAudioCastRequest, GenerateAudiocastDict
Expand Down Expand Up @@ -33,8 +33,6 @@ async def generate_audiocast(
summary: str,
content_category: ContentCategory,
):
doc_watch = subscribe_to_audio_generation(session_id)

audiocast_req = GenerateAudioCastRequest(
sessionId=session_id,
summary=summary,
Expand All @@ -46,8 +44,6 @@ async def generate_audiocast(
timeout=None,
)
response.raise_for_status()
doc_watch.unsubscribe()

return cast(GenerateAudiocastDict, response.json())


Expand All @@ -57,10 +53,11 @@ def render_audiocast_handler(session_id: str, audiocast: GenerateAudiocastDict):

# Voice waveform
with st.expander("Show Audio Waveform"):
try:
render_waveform(session_id, audiocast["url"], False)
except Exception as e:
st.error(f"Error rendering waveform: {str(e)}")
waveform_video_path: Path | str = st.session_state.get("waveform_video_path", False)
if waveform_video_path:
render_waveform(waveform_video_path)
else:
st.info("Loading waveform video...")

# Transcript
with st.expander("Show Transcript"):
Expand All @@ -75,4 +72,11 @@ def render_audiocast_handler(session_id: str, audiocast: GenerateAudiocastDict):
share_url = f"{APP_URL}/audiocast?session_id={session_id}"
st.text_input("Share this audiocast:", share_url)

return share_url
async def finalize():
try:
load_waveform_video(session_id, audiocast["url"])
except Exception as e:
st.error(f"Error rendering waveform: {str(e)}")

finalize_task = asyncio.create_task(finalize())
return share_url, finalize_task
22 changes: 15 additions & 7 deletions app/src/utils/render_waveform.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
from pathlib import Path

import streamlit as st
from pydub import AudioSegment
Expand All @@ -7,8 +8,8 @@
from shared_utils_pkg.waveform_utils import WaveformUtils


def render_waveform(session_id: str, audio_path: str, autoplay=False):
"""Render waveform visualization from audio file."""
def load_waveform_video(session_id: str, audio_path: str):
"""Load waveform visualization from audio file."""
waveform_utils = WaveformUtils(session_id, audio_path)
tmp_vid_path = waveform_utils.get_tmp_video_path()

Expand All @@ -32,15 +33,22 @@ def render_waveform(session_id: str, audio_path: str, autoplay=False):
video_path = waveform_utils.generate_waveform_video(tmp_vid_path)
waveform_utils.save_waveform_video_to_gcs(str(video_path))

with open(video_path, "rb") as video_file:
video_bytes = video_file.read()
st.video(video_bytes, autoplay=autoplay)

download_waveform_video(str(video_path))
if not st.session_state.get("waveform_video_path"):
st.session_state.waveform_video_path = video_path
st.rerun()
except Exception as e:
st.error(f"Error generating visualization: {str(e)}")


def render_waveform(video_path: Path | str, autoplay=False):
"""Render waveform visualization from path"""
with open(video_path, "rb") as video_file:
video_bytes = video_file.read()
st.video(video_bytes, autoplay=autoplay)

download_waveform_video(str(video_path))


def download_waveform_video(video_path: str):
"""Download video with waveform"""
gen_video, _ = st.columns(2)
Expand Down
2 changes: 1 addition & 1 deletion app/uis/audioui.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ async def audioui(session_id: str, uichat: DeltaGenerator):
await use_audiocast_request(session_id, summary, content_category)
else:
st.info("Audiocast generation completed!")
render_audiocast(session_id)
await render_audiocast(session_id)

0 comments on commit 9c31b5c

Please sign in to comment.