Add accent color, email subjects, MD+DOCX outputs, update README
This commit is contained in:
+68
-14
@@ -68,25 +68,40 @@ def _email_logo_html() -> str:
|
||||
"""
|
||||
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.
|
||||
- Priority:
|
||||
1) EMAIL_LOGO_URL (direct URL)
|
||||
2) EMAIL_LOGO_PATH (local file as base64)
|
||||
- Size: max 200% of line height (small).
|
||||
"""
|
||||
logo_url = os.getenv("EMAIL_LOGO_URL")
|
||||
if logo_url:
|
||||
return f'<img src="{logo_url}" alt="Logo" style="max-width:180px; display:block; margin:0 auto 10px auto;"/>'
|
||||
src = logo_url
|
||||
|
||||
logo_path = os.getenv("EMAIL_LOGO_PATH", "/app/src/misc/logo1.png")
|
||||
if not os.path.exists(logo_path):
|
||||
if not logo_url:
|
||||
logo_path = os.getenv("EMAIL_LOGO_PATH", "/app/src/misc/logo1.png")
|
||||
if os.path.exists(logo_path):
|
||||
try:
|
||||
with open(logo_path, "rb") as f:
|
||||
b64 = base64.b64encode(f.read()).decode("utf-8")
|
||||
src = f"data:image/png;base64,{b64}"
|
||||
except Exception:
|
||||
src = None
|
||||
|
||||
if not src:
|
||||
return ""
|
||||
|
||||
try:
|
||||
with open(logo_path, "rb") as f:
|
||||
b64 = base64.b64encode(f.read()).decode("utf-8")
|
||||
return f'<img src="data:image/png;base64,{b64}" alt="Logo" style="max-width:180px; display:block; margin:0 auto 10px auto;"/>'
|
||||
except Exception:
|
||||
return ""
|
||||
# max-height limited to 200% of line height (approx 2em)
|
||||
return (
|
||||
f'<img src="{src}" alt="Logo" '
|
||||
f'style="max-height:2em; width:auto; display:block; margin:0 auto 0.5em auto;" />'
|
||||
)
|
||||
|
||||
|
||||
def _accent_color() -> str:
|
||||
"""
|
||||
Accent color for UI and emails.
|
||||
Default: #7C6DA0
|
||||
"""
|
||||
return os.getenv("EMAIL_ACCENT_COLOR", "#7C6DA0")
|
||||
|
||||
|
||||
def build_template_context(**runtime_kwargs: Any) -> Dict[str, Any]:
|
||||
@@ -100,6 +115,7 @@ def build_template_context(**runtime_kwargs: Any) -> Dict[str, Any]:
|
||||
- EMAIL_CSS_PATH: path to mail_style.css (optional; we inline it)
|
||||
- EMAIL_LOGO_URL: URL for email logo (preferred)
|
||||
- EMAIL_LOGO_PATH: fallback local path for email logo
|
||||
- EMAIL_ACCENT_COLOR: accent color (default #7C6DA0)
|
||||
"""
|
||||
# Load and inline mail_style.css for consistent email styling
|
||||
css_path = os.getenv("EMAIL_CSS_PATH", "/app/src/misc/mail_style.css")
|
||||
@@ -108,10 +124,14 @@ def build_template_context(**runtime_kwargs: Any) -> Dict[str, Any]:
|
||||
# Build logo HTML (URL or local fallback)
|
||||
logo_html = _email_logo_html()
|
||||
|
||||
# Accent color
|
||||
accent = _accent_color()
|
||||
|
||||
ctx: Dict[str, Any] = {
|
||||
"contact_email": os.getenv("EMAIL_CONTACT_ADDRESS", "support@example.com"),
|
||||
"email_css": css_text,
|
||||
"email_logo": logo_html,
|
||||
"accent_color": accent,
|
||||
}
|
||||
|
||||
# Runtime values override env if provided
|
||||
@@ -255,3 +275,37 @@ def send_email(
|
||||
except Exception as e:
|
||||
logger.error("Failed to send email: %s", e)
|
||||
raise EmailError(f"Failed to send email: {e}")
|
||||
|
||||
|
||||
def create_transcript_docx(text: str, filename: str):
|
||||
"""
|
||||
Create a .docx file from plain/markdown transcript text.
|
||||
"""
|
||||
from docx import Document
|
||||
from docx.shared import Pt
|
||||
|
||||
doc = Document()
|
||||
doc.add_heading("Transcript", level=1)
|
||||
|
||||
for line in text.splitlines():
|
||||
p = doc.add_paragraph(line)
|
||||
p.paragraph_format.space_after = Pt(4)
|
||||
|
||||
doc.save(filename)
|
||||
|
||||
|
||||
def create_summary_docx(text: str, filename: str):
|
||||
"""
|
||||
Create a .docx file from summary text.
|
||||
"""
|
||||
from docx import Document
|
||||
from docx.shared import Pt
|
||||
|
||||
doc = Document()
|
||||
doc.add_heading("Summary", level=1)
|
||||
|
||||
for line in text.splitlines():
|
||||
p = doc.add_paragraph(line)
|
||||
p.paragraph_format.space_after = Pt(4)
|
||||
|
||||
doc.save(filename)
|
||||
|
||||
+38
-18
@@ -12,6 +12,7 @@ from .celery_app import celery_app
|
||||
from .autotranscript import Scraibe
|
||||
from .misc import setup_logging
|
||||
from .email_sender import send_email, EmailError, load_template
|
||||
from .email_sender import create_transcript_docx, create_summary_docx
|
||||
|
||||
logger = logging.getLogger("scraibe.tasks")
|
||||
|
||||
@@ -19,7 +20,6 @@ logger = logging.getLogger("scraibe.tasks")
|
||||
def get_queue_position(task_id: str) -> int:
|
||||
"""
|
||||
Estimate the job's position in the queue.
|
||||
This is a simple count of ready/started tasks.
|
||||
"""
|
||||
try:
|
||||
inspect = celery_app.control.inspect()
|
||||
@@ -38,10 +38,13 @@ def get_queue_position(task_id: str) -> int:
|
||||
|
||||
def send_initial_email(to: str, queue_pos: int):
|
||||
"""
|
||||
Send initial confirmation email with queue position using HTML template.
|
||||
Static placeholders (contact_email, css, logo) come from env via load_template.
|
||||
Send initial confirmation email with queue position.
|
||||
Subject is customizable via EMAIL_SUBJECT_UPLOAD.
|
||||
"""
|
||||
subject = "ScrAIbe: Your transcription request has been received"
|
||||
subject = os.getenv(
|
||||
"EMAIL_SUBJECT_UPLOAD",
|
||||
"ScrAIbe: Your transcription request has been received",
|
||||
)
|
||||
|
||||
# Build plain-text fallback
|
||||
body = (
|
||||
@@ -88,10 +91,13 @@ def send_success_email(
|
||||
task_id: str,
|
||||
):
|
||||
"""
|
||||
Send final email with transcript and attachments using HTML template.
|
||||
Static placeholders (contact_email, css, logo) come from env via load_template.
|
||||
Send final email with transcript and attachments.
|
||||
Subject is customizable via EMAIL_SUBJECT_SUCCESS.
|
||||
"""
|
||||
subject = "ScrAIbe: Your transcript is ready"
|
||||
subject = os.getenv(
|
||||
"EMAIL_SUBJECT_SUCCESS",
|
||||
"ScrAIbe: Your transcript is ready",
|
||||
)
|
||||
|
||||
# Build plain-text fallback
|
||||
body = (
|
||||
@@ -140,10 +146,13 @@ def send_success_email(
|
||||
|
||||
def send_error_email(to: str, error_message: str, task_id: str):
|
||||
"""
|
||||
Send error notification email using HTML template.
|
||||
Static placeholders (contact_email, css, logo) come from env via load_template.
|
||||
Send error notification email.
|
||||
Subject is customizable via EMAIL_SUBJECT_ERROR.
|
||||
"""
|
||||
subject = "ScrAIbe: Error with your transcription request"
|
||||
subject = os.getenv(
|
||||
"EMAIL_SUBJECT_ERROR",
|
||||
"ScrAIbe: Error with your transcription request",
|
||||
)
|
||||
|
||||
# Build plain-text fallback
|
||||
body = (
|
||||
@@ -243,11 +252,17 @@ def process_transcription_task(
|
||||
# 4) Prepare files for email
|
||||
attachments = []
|
||||
|
||||
# TXT transcript
|
||||
txt_path = tempfile.mktemp(suffix=".txt")
|
||||
with open(txt_path, "w", encoding="utf-8") as f:
|
||||
# Transcript as .md
|
||||
md_transcript_path = tempfile.mktemp(suffix=".md")
|
||||
with open(md_transcript_path, "w", encoding="utf-8") as f:
|
||||
f.write("# Transcript\n\n")
|
||||
f.write(transcript_text)
|
||||
attachments.append(txt_path)
|
||||
attachments.append(md_transcript_path)
|
||||
|
||||
# Transcript as .docx
|
||||
docx_transcript_path = tempfile.mktemp(suffix=".docx")
|
||||
create_transcript_docx(transcript_text, docx_transcript_path)
|
||||
attachments.append(docx_transcript_path)
|
||||
|
||||
# JSON with diarization
|
||||
json_data = {
|
||||
@@ -271,13 +286,18 @@ def process_transcription_task(
|
||||
json.dump(json_data, f, indent=2, ensure_ascii=False)
|
||||
attachments.append(json_path)
|
||||
|
||||
# MD summary (only when summary is available)
|
||||
# Summary as .md (only when summary is available)
|
||||
if summary_text:
|
||||
md_path = tempfile.mktemp(suffix=".md")
|
||||
with open(md_path, "w", encoding="utf-8") as f:
|
||||
md_summary_path = tempfile.mktemp(suffix=".md")
|
||||
with open(md_summary_path, "w", encoding="utf-8") as f:
|
||||
f.write("# Summary\n\n")
|
||||
f.write(summary_text)
|
||||
attachments.append(md_path)
|
||||
attachments.append(md_summary_path)
|
||||
|
||||
# Summary as .docx
|
||||
docx_summary_path = tempfile.mktemp(suffix=".docx")
|
||||
create_summary_docx(summary_text, docx_summary_path)
|
||||
attachments.append(docx_summary_path)
|
||||
|
||||
# 5) Send success email
|
||||
send_success_email(
|
||||
|
||||
+38
-5
@@ -83,11 +83,12 @@ def create_app():
|
||||
header_path = layout_cfg.get("header", "/app/src/misc/header.html")
|
||||
footer_path = layout_cfg.get("footer", "/app/src/misc/footer.html")
|
||||
|
||||
# Configurable title and logo URL via environment
|
||||
# Configurable title, logo URL, and accent color via environment
|
||||
webui_title = os.getenv("WEBUI_TITLE", "A.P.Strom Transcription")
|
||||
logo_url = os.getenv("WEBUI_LOGO_URL", "https://apstrom.ca")
|
||||
accent_color = os.getenv("EMAIL_ACCENT_COLOR", "#7C6DA0")
|
||||
|
||||
# Prepare header HTML with logo URL
|
||||
# Prepare header HTML with logo URL and accent color
|
||||
header_html = ""
|
||||
if os.path.exists(header_path):
|
||||
header_html = load_html_template(
|
||||
@@ -95,15 +96,17 @@ def create_app():
|
||||
webui_title=webui_title,
|
||||
header_logo_url=logo_url,
|
||||
header_logo_src=logo_url,
|
||||
accent_color=accent_color,
|
||||
)
|
||||
|
||||
# Prepare footer HTML
|
||||
# Prepare footer HTML with accent color
|
||||
footer_html = ""
|
||||
if os.path.exists(footer_path):
|
||||
version = os.getenv("SCRABIE_VERSION", "0.1.1.dev")
|
||||
footer_html = load_html_template(
|
||||
footer_path,
|
||||
footer_scraibe_webui_version=version,
|
||||
accent_color=accent_color,
|
||||
)
|
||||
|
||||
# Build Gradio interface
|
||||
@@ -248,14 +251,44 @@ def create_app():
|
||||
outputs=[status_text],
|
||||
)
|
||||
|
||||
# Launch options
|
||||
# Launch options with accent color applied via CSS
|
||||
server_name = launch_cfg.get("server_name", os.getenv("GRADIO_SERVER_NAME", "0.0.0.0"))
|
||||
server_port = launch_cfg.get("server_port", 7860)
|
||||
|
||||
accent_css = f"""
|
||||
:root {{
|
||||
--primary-accent: {accent_color};
|
||||
}}
|
||||
button.primary,
|
||||
.primary,
|
||||
.gradio-button-primary,
|
||||
.gradio-container button.primary {{
|
||||
background-color: var(--primary-accent) !important;
|
||||
border-color: var(--primary-accent) !important;
|
||||
}}
|
||||
button.primary:hover,
|
||||
.primary:hover,
|
||||
.gradio-button-primary:hover {{
|
||||
background-color: var(--primary-accent) !important;
|
||||
opacity: 0.95;
|
||||
}}
|
||||
.radio-item.selected,
|
||||
.radio-item.selected label {{
|
||||
color: var(--primary-accent) !important;
|
||||
}}
|
||||
a,
|
||||
.gradio-container a {{
|
||||
color: var(--primary-accent) !important;
|
||||
}}
|
||||
body {{
|
||||
font-family: Arial, sans-serif;
|
||||
}}
|
||||
"""
|
||||
|
||||
app.launch(
|
||||
server_name=str(server_name),
|
||||
server_port=int(server_port),
|
||||
css="body { font-family: Arial, sans-serif; }",
|
||||
css=accent_css,
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user