diff --git a/README.md b/README.md index b89799d..42fc9d8 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,42 @@ # ScrAIbe – LocalAI-Backed Transcription and Summarization -ScrAIbe is a lightweight transcription and summarization client that: +ScrAIbe is a transcription and summarization service that: - Sends audio to a LocalAI server running vibevoice.cpp for transcription and speaker diarization. -- Optionally uses a second LLM to generate a detailed, structured summary of the conversation. +- Optionally uses a second LLM to generate a detailed, structured summary. +- Provides: + - A web GUI for uploading audio and receiving transcripts via email. + - A CLI and Python API for direct integration. -No local speech models or heavy dependencies are required. ScrAIbe is designed to be run as a thin client in front of your own AI services. +No local speech models or heavy dependencies are required. ScrAIbe is designed as a thin client in front of your own AI services. + +For more information: https://apstrom.ca ## Features - Transcription with speaker diarization via LocalAI: - - Uses the `/v1/audio/diarization` endpoint. + - Uses the /v1/audio/diarization endpoint. - Compatible with vibevoice.cpp and other diarization-capable backends. - Optional AI-powered summarization: - - Task: `transcript_and_summarize` - - Chunks long transcripts, summarizes each chunk, then generates a final comprehensive summary. - - Summary highlights: + - Task: transcript_and_summarize + - Highlights: - Main topics and discussion points - Key decisions and outcomes - Action items and responsibilities - Open issues and risks +- Async web GUI: + - Upload audio via browser. + - Jobs are queued and processed in the background (Celery + Redis). + - Emails: + - Immediate confirmation with queue position. + - Final transcript (TXT + JSON) when ready. + - Summary as MD file (if requested). + - Error notification if processing fails. +- Customizable branding: + - Web GUI title, logo, and email logo via environment variables. - CLI and Python API: - Simple command-line interface. - - Drop-in `Scraibe` class for integration into other tools. + - Drop-in Scraibe class for integration into other tools. - Docker-ready: - Lightweight container, configured via environment variables. @@ -38,6 +52,31 @@ No local speech models or heavy dependencies are required. ScrAIbe is designed t - Transcript assembly - Chunked summarization - Output formatting (e.g., .md with transcript + summary) + - Runs: + - Web GUI (Gradio) + - Celery worker (async processing) + - Redis (in-container by default) + +## Quick Start (Web GUI in Docker) + +Run the container with your LocalAI and summarizer endpoints: + +- docker run -d \ + -p 7860:7860 \ + -e LOCALAI_API_URL=http://localai:8080 \ + -e SUMMARIZER_API_URL=http://llm:8080 \ + -e EMAIL_SMTP_HOST=smtp.your-domain.com \ + -e EMAIL_SMTP_PORT=587 \ + -e EMAIL_SMTP_USER=transcribe@your-domain.com \ + -e EMAIL_SMTP_PASSWORD=your_password \ + -e EMAIL_FROM_ADDRESS="ScrAIbe " \ + -e EMAIL_CONTACT_ADDRESS=support@your-domain.com \ + -e WEBUI_TITLE="Your Transcription Service" \ + -e WEBUI_LOGO_URL="https://your-domain.com/logo.png" \ + -e EMAIL_LOGO_URL="https://your-domain.com/logo.png" \ + scraibe:latest + +Then open: http://:7860 ## Quick Start (CLI) @@ -101,7 +140,7 @@ Other options (e.g., --language, --num-speakers) are accepted and forwarded wher ScrAIbe is designed to run in Docker as a client to your LocalAI and summarizer LLM. -### Basic run (transcribe) +### Basic run (transcribe via CLI) - docker run -it \ -e LOCALAI_API_URL=http://localai:8080 \ @@ -109,7 +148,7 @@ ScrAIbe is designed to run in Docker as a client to your LocalAI and summarizer scraibe:latest \ -f /audio/meeting.wav -o /audio/output -of txt -### Basic run (transcribe + summarize) +### Basic run (transcribe + summarize via CLI) - docker run -it \ -e LOCALAI_API_URL=http://localai:8080 \ @@ -148,6 +187,46 @@ Summarization LLM: - Optional (default: llama-3.1-8b-instruct). - Model name used for summarization. +Web GUI and branding: + +- WEBUI_TITLE: + - Title shown in the web GUI (default: A.P.Strom Transcription). +- WEBUI_LOGO_URL: + - URL of the logo displayed in the web GUI header. + - Example: https://your-domain.com/logo.png + +Async processing (Celery + Redis): + +- CELERY_BROKER_URL: + - Redis broker URL (default: redis://localhost:6379/0). +- CELERY_RESULT_BACKEND: + - Redis backend URL (default: redis://localhost:6379/0). +- SCRAIBE_UPLOAD_DIR: + - Directory where uploaded audio is stored (default: /tmp/scraibe_uploads). + +Email configuration: + +- EMAIL_SMTP_HOST: + - SMTP server host. +- EMAIL_SMTP_PORT: + - SMTP server port (e.g., 587). +- EMAIL_SMTP_USER: + - SMTP username. +- EMAIL_SMTP_PASSWORD: + - SMTP password. +- EMAIL_SMTP_USE_TLS: + - Use TLS (true/false; default: true). +- EMAIL_FROM_ADDRESS: + - Sender address (e.g., "ScrAIbe "). +- EMAIL_CONTACT_ADDRESS: + - Support contact address shown in email templates. +- EMAIL_LOGO_URL: + - URL of the logo used in emails (preferred). +- EMAIL_LOGO_PATH: + - Fallback local path for email logo (default: /app/src/misc/logo1.png). +- EMAIL_CSS_PATH: + - Path to the CSS used in emails (default: /app/src/misc/mail_style.css). + All of these can also be overridden from the CLI when needed (e.g., --localai-api-url, --summarizer-api-url). ## Dependencies @@ -158,6 +237,9 @@ Core runtime dependencies: - httpx - numpy - tqdm +- gradio +- celery[redis] +- redis - ffmpeg (for audio preprocessing) No local Whisper, PyTorch, or Pyannote models are required. diff --git a/misc/footer.html b/misc/footer.html index 7a7328b..c21c7cb 100644 --- a/misc/footer.html +++ b/misc/footer.html @@ -56,14 +56,14 @@ font-size: 14px; color: #333; }} - .github-section {{ + .brand-section {{ display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; }} - .github-icon a {{ + .brand-icon a {{ display: flex; align-items: center; justify-content: center; @@ -75,15 +75,15 @@ color: white; transition: background-color 0.3s ease, transform 0.3s ease; }} - .github-icon i {{ + .brand-icon i {{ font-size: 24px; }} - .github-icon a:hover, .github-icon a:focus {{ + .brand-icon a:hover, .brand-icon a:focus {{ background-color: #50AF31; transform: scale(1.1); text-decoration: none; }} - .github-icon a, .github-icon a:hover, .github-icon a:active, .github-icon a:visited {{ + .brand-icon a, .brand-icon a:hover, .brand-icon a:active, .brand-icon a:visited {{ text-decoration: none; }} .build-version {{ @@ -105,10 +105,10 @@

Data retention

Audio or video files uploaded to this application are only retained for the time that it takes to complete the transcription. All transcripts are deleted after they are transmitted to the user.

-
-
- - + diff --git a/misc/header.html b/misc/header.html index e42b46c..a25bfcb 100644 --- a/misc/header.html +++ b/misc/header.html @@ -3,7 +3,7 @@ - A.P.Strom Transcription + {webui_title} @@ -66,10 +66,10 @@
-

A.P.Strom Transcription

+

{webui_title}

diff --git a/scraibe/email_sender.py b/scraibe/email_sender.py index 33a6be3..bd3e02e 100644 --- a/scraibe/email_sender.py +++ b/scraibe/email_sender.py @@ -64,11 +64,19 @@ def _load_css(path: str) -> str: return f.read() -def _logo1_inline_html() -> str: +def _email_logo_html() -> str: """ - Return an inline tag for logo1.png as base64. - Falls back to empty string if not found. + Return logo HTML for emails. + + Priority: + 1) EMAIL_LOGO_URL (direct URL to logo image) + 2) EMAIL_LOGO_PATH (local file; embedded as base64) + 3) empty string if neither is set. """ + logo_url = os.getenv("EMAIL_LOGO_URL") + if logo_url: + return f'Logo' + logo_path = os.getenv("EMAIL_LOGO_PATH", "/app/src/misc/logo1.png") if not os.path.exists(logo_path): return "" @@ -76,7 +84,7 @@ def _logo1_inline_html() -> str: try: with open(logo_path, "rb") as f: b64 = base64.b64encode(f.read()).decode("utf-8") - return f'A.P.Strom' + return f'Logo' except Exception: return "" @@ -90,14 +98,15 @@ def build_template_context(**runtime_kwargs: Any) -> Dict[str, Any]: Environment variables: - EMAIL_CONTACT_ADDRESS: value for {contact_email} - EMAIL_CSS_PATH: path to mail_style.css (optional; we inline it) - - EMAIL_LOGO_PATH: path to logo1.png (default: /app/src/misc/logo1.png) + - EMAIL_LOGO_URL: URL for email logo (preferred) + - EMAIL_LOGO_PATH: fallback local path for email logo """ # Load and inline mail_style.css for consistent email styling css_path = os.getenv("EMAIL_CSS_PATH", "/app/src/misc/mail_style.css") css_text = _load_css(css_path) - # Build inline logo HTML - logo_html = _logo1_inline_html() + # Build logo HTML (URL or local fallback) + logo_html = _email_logo_html() ctx: Dict[str, Any] = { "contact_email": os.getenv("EMAIL_CONTACT_ADDRESS", "support@example.com"), diff --git a/scraibe/webui.py b/scraibe/webui.py index 684f6bb..e47e2b8 100644 --- a/scraibe/webui.py +++ b/scraibe/webui.py @@ -82,15 +82,19 @@ def create_app(): # Paths for assets header_path = layout_cfg.get("header", "/app/src/misc/header.html") footer_path = layout_cfg.get("footer", "/app/src/misc/footer.html") - logo_path = layout_cfg.get("logo", "/app/src/misc/logo.png") - # Prepare header HTML with logo + # Configurable title and logo URL via environment + webui_title = os.getenv("WEBUI_TITLE", "A.P.Strom Transcription") + logo_url = os.getenv("WEBUI_LOGO_URL", "https://apstrom.ca") + + # Prepare header HTML with logo URL header_html = "" if os.path.exists(header_path): header_html = load_html_template( header_path, - header_logo_url="https://www.apstrom.de/", - header_logo_src=logo_path if os.path.exists(logo_path) else "", + webui_title=webui_title, + header_logo_url=logo_url, + header_logo_src=logo_url, ) # Prepare footer HTML @@ -104,7 +108,7 @@ def create_app(): # Build Gradio interface with gr.Blocks( - title="A.P.Strom Transcription", + title=webui_title, ) as app: # Header @@ -247,12 +251,10 @@ def create_app(): # Launch options server_name = launch_cfg.get("server_name", os.getenv("GRADIO_SERVER_NAME", "0.0.0.0")) server_port = launch_cfg.get("server_port", 7860) - favicon_path = logo_path if os.path.exists(logo_path) else None app.launch( server_name=str(server_name), server_port=int(server_port), - favicon_path=favicon_path, css="body { font-family: Arial, sans-serif; }", )