Add accent color, email subjects, MD+DOCX outputs, update README
Mirror and run GitLab CI / build (push) Has been cancelled
Ruff / ruff (push) Has been cancelled

This commit is contained in:
admin
2026-06-14 15:49:11 +00:00
parent dc20e9cff0
commit 63cd620b79
11 changed files with 205 additions and 54 deletions
+41 -2
View File
@@ -29,11 +29,16 @@ For more information: https://apstrom.ca
- Jobs are queued and processed in the background (Celery + Redis). - Jobs are queued and processed in the background (Celery + Redis).
- Emails: - Emails:
- Immediate confirmation with queue position. - Immediate confirmation with queue position.
- Final transcript (TXT + JSON) when ready. - Final transcript (MD + JSON) when ready.
- Summary as MD file (if requested). - Summary as MD file (if requested).
- Error notification if processing fails. - Error notification if processing fails.
- File formats:
- Transcript: .md and .docx
- Summary (if requested): .md and .docx
- Full structured output: .json
- Customizable branding: - Customizable branding:
- Web GUI title, logo, and email logo via environment variables. - Web GUI title, logo, and accent color via environment variables.
- Email logo, accent color, and subject lines via environment variables.
- CLI and Python API: - CLI and Python API:
- Simple command-line interface. - Simple command-line interface.
- Drop-in Scraibe class for integration into other tools. - Drop-in Scraibe class for integration into other tools.
@@ -74,6 +79,7 @@ Run the container with your LocalAI and summarizer endpoints:
-e WEBUI_TITLE="Your Transcription Service" \ -e WEBUI_TITLE="Your Transcription Service" \
-e WEBUI_LOGO_URL="https://your-domain.com/logo.png" \ -e WEBUI_LOGO_URL="https://your-domain.com/logo.png" \
-e EMAIL_LOGO_URL="https://your-domain.com/logo.png" \ -e EMAIL_LOGO_URL="https://your-domain.com/logo.png" \
-e EMAIL_ACCENT_COLOR="#7C6DA0" \
scraibe:latest scraibe:latest
Then open: http://<host>:7860 Then open: http://<host>:7860
@@ -195,6 +201,14 @@ Web GUI and branding:
- URL of the logo displayed in the web GUI header. - URL of the logo displayed in the web GUI header.
- Example: https://your-domain.com/logo.png - Example: https://your-domain.com/logo.png
Accent color (UI and emails):
- EMAIL_ACCENT_COLOR:
- Accent color used in:
- Web GUI buttons and accents
- Email headings, links, and email addresses
- Default: #7C6DA0
Async processing (Celery + Redis): Async processing (Celery + Redis):
- CELERY_BROKER_URL: - CELERY_BROKER_URL:
@@ -227,6 +241,30 @@ Email configuration:
- EMAIL_CSS_PATH: - EMAIL_CSS_PATH:
- Path to the CSS used in emails (default: /app/src/misc/mail_style.css). - Path to the CSS used in emails (default: /app/src/misc/mail_style.css).
Email subject lines (customizable):
- EMAIL_SUBJECT_UPLOAD:
- Subject for upload confirmation email.
- Default: "ScrAIbe: Your transcription request has been received"
- EMAIL_SUBJECT_SUCCESS:
- Subject for transcript-ready email.
- Default: "ScrAIbe: Your transcript is ready"
- EMAIL_SUBJECT_ERROR:
- Subject for error notification email.
- Default: "ScrAIbe: Error with your transcription request"
Output files (async web GUI):
When a job completes, the user receives:
- Transcript:
- .md file
- .docx file
- Summary (if requested):
- .md file
- .docx file
- JSON:
- Structured transcript with diarization and metadata
All of these can also be overridden from the CLI when needed (e.g., --localai-api-url, --summarizer-api-url). All of these can also be overridden from the CLI when needed (e.g., --localai-api-url, --summarizer-api-url).
## Dependencies ## Dependencies
@@ -240,6 +278,7 @@ Core runtime dependencies:
- gradio - gradio
- celery[redis] - celery[redis]
- redis - redis
- python-docx
- ffmpeg (for audio preprocessing) - ffmpeg (for audio preprocessing)
No local Whisper, PyTorch, or Pyannote models are required. No local Whisper, PyTorch, or Pyannote models are required.
+2 -2
View File
@@ -11,13 +11,13 @@
<body> <body>
<div class="container"> <div class="container">
{email_logo} {email_logo}
<h1>Error Notification</h1> <h1 style="color:{accent_color};">Error Notification</h1>
<p>Dear user,</p> <p>Dear user,</p>
<p>An error occurred while processing your audio file. This means that something went wrong during the processing of your file, and it could not be completed successfully.</p> <p>An error occurred while processing your audio file. This means that something went wrong during the processing of your file, and it could not be completed successfully.</p>
<p class="error-message">Error details: {exception}</p> <p class="error-message">Error details: {exception}</p>
<p>Please check the file and try again. If the problem persists, our support team is here to help.</p> <p>Please check the file and try again. If the problem persists, our support team is here to help.</p>
<div class="contact"> <div class="contact">
<p>You can contact our support team at <a href="mailto:{contact_email}">{contact_email}</a>. They are available to assist with any questions or issues you may have.</p> <p>You can contact our support team at <a href="mailto:{contact_email}" style="color:{accent_color};">{contact_email}</a>. They are available to assist with any questions or issues you may have.</p>
</div> </div>
<div class="disclaimer"> <div class="disclaimer">
<p>Please note that our support team does not have the ability to fix processing errors directly or access the files you have uploaded. They can provide guidance and help troubleshoot any issues you may encounter.</p> <p>Please note that our support team does not have the ability to fix processing errors directly or access the files you have uploaded. They can provide guidance and help troubleshoot any issues you may encounter.</p>
+4 -4
View File
@@ -43,7 +43,7 @@
text-align: left; text-align: left;
}} }}
.footer a {{ .footer a {{
color: #333; color: {accent_color};
transition: color 0.3s ease; transition: color 0.3s ease;
}} }}
.footer a:hover {{ .footer a:hover {{
@@ -79,7 +79,7 @@
font-size: 24px; font-size: 24px;
}} }}
.brand-icon a:hover, .brand-icon a:focus {{ .brand-icon a:hover, .brand-icon a:focus {{
background-color: #50AF31; background-color: {accent_color};
transform: scale(1.1); transform: scale(1.1);
text-decoration: none; text-decoration: none;
}} }}
@@ -98,11 +98,11 @@
<div class="footer"> <div class="footer">
<div class="foot-text"> <div class="foot-text">
<h2 style="font-weight: bold; color: #7C6DA0;">Disclaimer</h2> <h2 style="font-weight: bold; color: {accent_color};">Disclaimer</h2>
<p>The transcription completed by this application may contain errors.</p> <p>The transcription completed by this application may contain errors.</p>
<p>Users must take care to review transcripts before circulating to ensure that they are error-free and complete.</p> <p>Users must take care to review transcripts before circulating to ensure that they are error-free and complete.</p>
<p>The transcripts produced by this application do not replace a court reporter's transcription. The transcripts completed by this application are for the user's convenience only.</p> <p>The transcripts produced by this application do not replace a court reporter's transcription. The transcripts completed by this application are for the user's convenience only.</p>
<h2 style="font-weight: bold; color: #7C6DA0;">Data retention</h2> <h2 style="font-weight: bold; color: {accent_color};">Data retention</h2>
<p>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.</p> <p>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.</p>
</div> </div>
<div class="brand-section"> <div class="brand-section">
+3 -3
View File
@@ -34,7 +34,7 @@
font-family: 'Cormorant Garamond', serif; font-family: 'Cormorant Garamond', serif;
font-size: 50px !important; font-size: 50px !important;
font-weight: bold; font-weight: bold;
color: #7C6DA0; color: {accent_color};
margin: 0; margin: 0;
position: relative; position: relative;
padding: 0.5em 0; padding: 0.5em 0;
@@ -45,7 +45,7 @@
position: absolute; position: absolute;
height: 2px; height: 2px;
width: 80%; width: 80%;
background-color: #7C6DA0; background-color: {accent_color};
left: 10%; left: 10%;
}} }}
@@ -77,7 +77,7 @@
<p> <p>
Upload, record, or provide a video with audio for transcription. Our toolkit is designed to transcribe content from multiple languages accurately. The integrated speaker diarisation feature identifies different speakers, ensuring a smooth transcription experience. For optimal results, indicate the number of speakers and the original language of the content. Upload, record, or provide a video with audio for transcription. Our toolkit is designed to transcribe content from multiple languages accurately. The integrated speaker diarisation feature identifies different speakers, ensuring a smooth transcription experience. For optimal results, indicate the number of speakers and the original language of the content.
</p> </p>
<h2 style="font-weight: bold; color: #7C6DA0;">Start your transcription below.</h2> <h2 style="font-weight: bold; color: {accent_color};">Start your transcription below.</h2>
</div> </div>
</body> </body>
</html> </html>
+6 -2
View File
@@ -14,9 +14,10 @@ body {
border: 1px solid #ddd; border: 1px solid #ddd;
border-radius: 5px; border-radius: 5px;
} }
h1 { h1, h2, h3 {
font-size: 1.5em; font-size: 1.5em;
margin-top: 0; margin-top: 0;
color: {accent_color};
} }
p { p {
margin: 10px 0; margin: 10px 0;
@@ -39,12 +40,15 @@ p {
color: #555; color: #555;
} }
.contact a { .contact a {
color: #0056b3; color: {accent_color};
text-decoration: none; text-decoration: none;
} }
.contact a:hover { .contact a:hover {
text-decoration: underline; text-decoration: underline;
} }
a {
color: {accent_color};
}
.disclaimer { .disclaimer {
margin-top: 20px; margin-top: 20px;
font-size: 0.8em; font-size: 0.8em;
+2 -2
View File
@@ -11,12 +11,12 @@
<body> <body>
<div class="container"> <div class="container">
{email_logo} {email_logo}
<h1>Transcript Ready</h1> <h1 style="color:{accent_color};">Transcript Ready</h1>
<p>Dear user,</p> <p>Dear user,</p>
<p>Your file has been successfully processed, and the transcript is now ready. The transcript of your audio or video file is attached to this email.</p> <p>Your file has been successfully processed, and the transcript is now ready. The transcript of your audio or video file is attached to this email.</p>
<p>We hope you find the transcript useful. If you have any questions or need further assistance, please do not hesitate to contact our support team.</p> <p>We hope you find the transcript useful. If you have any questions or need further assistance, please do not hesitate to contact our support team.</p>
<div class="contact"> <div class="contact">
<p>You can reach our support team at <a href="mailto:{contact_email}">{contact_email}</a>. They are available to help with any questions or issues you may have.</p> <p>You can reach our support team at <a href="mailto:{contact_email}" style="color:{accent_color};">{contact_email}</a>. They are available to help with any questions or issues you may have.</p>
</div> </div>
<div class="disclaimer"> <div class="disclaimer">
<p>Please note that our support team cannot modify the content of the transcript. They can assist with any other questions or concerns you may have.</p> <p>Please note that our support team cannot modify the content of the transcript. They can assist with any other questions or concerns you may have.</p>
+2 -2
View File
@@ -11,13 +11,13 @@
<body> <body>
<div class="container"> <div class="container">
{email_logo} {email_logo}
<h1>Upload Successful</h1> <h1 style="color:{accent_color};">Upload Successful</h1>
<p>Dear user,</p> <p>Dear user,</p>
<p>Your file has been successfully uploaded and is now in our processing queue. This means that our system has received your file, and it is waiting to be processed. We will handle your file as soon as possible.</p> <p>Your file has been successfully uploaded and is now in our processing queue. This means that our system has received your file, and it is waiting to be processed. We will handle your file as soon as possible.</p>
<p class="success-message">Your current position in the queue is: {queue_position}. This is the order in which your file will be processed. We appreciate your patience as we work through the queue.</p> <p class="success-message">Your current position in the queue is: {queue_position}. This is the order in which your file will be processed. We appreciate your patience as we work through the queue.</p>
<p>We will notify you once your file has been processed. If you have any urgent needs or further questions, feel free to reach out to our support team.</p> <p>We will notify you once your file has been processed. If you have any urgent needs or further questions, feel free to reach out to our support team.</p>
<div class="contact"> <div class="contact">
<p>You can contact our support team at <a href="mailto:{contact_email}">{contact_email}</a>. Please note that our support team is here to help with any questions or issues you might have.</p> <p>You can contact our support team at <a href="mailto:{contact_email}" style="color:{accent_color};">{contact_email}</a>. Please note that our support team is here to help with any questions or issues you might have.</p>
</div> </div>
<div class="disclaimer"> <div class="disclaimer">
<p>Please note that our support team does not have the ability to change your position in the queue or access the files you have uploaded. They are here to provide assistance and answer any questions you might have about the process.</p> <p>Please note that our support team does not have the ability to change your position in the queue or access the files you have uploaded. They are here to provide assistance and answer any questions you might have about the process.</p>
+1
View File
@@ -5,3 +5,4 @@ gradio>=5.0.0
PyYAML>=6.0 PyYAML>=6.0
celery[redis]>=5.3.0 celery[redis]>=5.3.0
redis>=5.0.0 redis>=5.0.0
python-docx>=1.1.0
+68 -14
View File
@@ -68,25 +68,40 @@ def _email_logo_html() -> str:
""" """
Return logo HTML for emails. Return logo HTML for emails.
Priority: - Priority:
1) EMAIL_LOGO_URL (direct URL to logo image) 1) EMAIL_LOGO_URL (direct URL)
2) EMAIL_LOGO_PATH (local file; embedded as base64) 2) EMAIL_LOGO_PATH (local file as base64)
3) empty string if neither is set. - Size: max 200% of line height (small).
""" """
logo_url = os.getenv("EMAIL_LOGO_URL") logo_url = os.getenv("EMAIL_LOGO_URL")
if logo_url: src = logo_url
return f'<img src="{logo_url}" alt="Logo" style="max-width:180px; display:block; margin:0 auto 10px auto;"/>'
logo_path = os.getenv("EMAIL_LOGO_PATH", "/app/src/misc/logo1.png") if not logo_url:
if not os.path.exists(logo_path): 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 "" return ""
try: # max-height limited to 200% of line height (approx 2em)
with open(logo_path, "rb") as f: return (
b64 = base64.b64encode(f.read()).decode("utf-8") f'<img src="{src}" alt="Logo" '
return f'<img src="data:image/png;base64,{b64}" alt="Logo" style="max-width:180px; display:block; margin:0 auto 10px auto;"/>' f'style="max-height:2em; width:auto; display:block; margin:0 auto 0.5em auto;" />'
except Exception: )
return ""
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]: 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_CSS_PATH: path to mail_style.css (optional; we inline it)
- EMAIL_LOGO_URL: URL for email logo (preferred) - EMAIL_LOGO_URL: URL for email logo (preferred)
- EMAIL_LOGO_PATH: fallback local path for email logo - 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 # Load and inline mail_style.css for consistent email styling
css_path = os.getenv("EMAIL_CSS_PATH", "/app/src/misc/mail_style.css") 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) # Build logo HTML (URL or local fallback)
logo_html = _email_logo_html() logo_html = _email_logo_html()
# Accent color
accent = _accent_color()
ctx: Dict[str, Any] = { ctx: Dict[str, Any] = {
"contact_email": os.getenv("EMAIL_CONTACT_ADDRESS", "support@example.com"), "contact_email": os.getenv("EMAIL_CONTACT_ADDRESS", "support@example.com"),
"email_css": css_text, "email_css": css_text,
"email_logo": logo_html, "email_logo": logo_html,
"accent_color": accent,
} }
# Runtime values override env if provided # Runtime values override env if provided
@@ -255,3 +275,37 @@ def send_email(
except Exception as e: except Exception as e:
logger.error("Failed to send email: %s", e) logger.error("Failed to send email: %s", e)
raise EmailError(f"Failed to send email: {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
View File
@@ -12,6 +12,7 @@ from .celery_app import celery_app
from .autotranscript import Scraibe from .autotranscript import Scraibe
from .misc import setup_logging from .misc import setup_logging
from .email_sender import send_email, EmailError, load_template from .email_sender import send_email, EmailError, load_template
from .email_sender import create_transcript_docx, create_summary_docx
logger = logging.getLogger("scraibe.tasks") logger = logging.getLogger("scraibe.tasks")
@@ -19,7 +20,6 @@ logger = logging.getLogger("scraibe.tasks")
def get_queue_position(task_id: str) -> int: def get_queue_position(task_id: str) -> int:
""" """
Estimate the job's position in the queue. Estimate the job's position in the queue.
This is a simple count of ready/started tasks.
""" """
try: try:
inspect = celery_app.control.inspect() 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): def send_initial_email(to: str, queue_pos: int):
""" """
Send initial confirmation email with queue position using HTML template. Send initial confirmation email with queue position.
Static placeholders (contact_email, css, logo) come from env via load_template. 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 # Build plain-text fallback
body = ( body = (
@@ -88,10 +91,13 @@ def send_success_email(
task_id: str, task_id: str,
): ):
""" """
Send final email with transcript and attachments using HTML template. Send final email with transcript and attachments.
Static placeholders (contact_email, css, logo) come from env via load_template. 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 # Build plain-text fallback
body = ( body = (
@@ -140,10 +146,13 @@ def send_success_email(
def send_error_email(to: str, error_message: str, task_id: str): def send_error_email(to: str, error_message: str, task_id: str):
""" """
Send error notification email using HTML template. Send error notification email.
Static placeholders (contact_email, css, logo) come from env via load_template. 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 # Build plain-text fallback
body = ( body = (
@@ -243,11 +252,17 @@ def process_transcription_task(
# 4) Prepare files for email # 4) Prepare files for email
attachments = [] attachments = []
# TXT transcript # Transcript as .md
txt_path = tempfile.mktemp(suffix=".txt") md_transcript_path = tempfile.mktemp(suffix=".md")
with open(txt_path, "w", encoding="utf-8") as f: with open(md_transcript_path, "w", encoding="utf-8") as f:
f.write("# Transcript\n\n")
f.write(transcript_text) 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 with diarization
json_data = { json_data = {
@@ -271,13 +286,18 @@ def process_transcription_task(
json.dump(json_data, f, indent=2, ensure_ascii=False) json.dump(json_data, f, indent=2, ensure_ascii=False)
attachments.append(json_path) attachments.append(json_path)
# MD summary (only when summary is available) # Summary as .md (only when summary is available)
if summary_text: if summary_text:
md_path = tempfile.mktemp(suffix=".md") md_summary_path = tempfile.mktemp(suffix=".md")
with open(md_path, "w", encoding="utf-8") as f: with open(md_summary_path, "w", encoding="utf-8") as f:
f.write("# Summary\n\n") f.write("# Summary\n\n")
f.write(summary_text) 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 # 5) Send success email
send_success_email( send_success_email(
+38 -5
View File
@@ -83,11 +83,12 @@ def create_app():
header_path = layout_cfg.get("header", "/app/src/misc/header.html") header_path = layout_cfg.get("header", "/app/src/misc/header.html")
footer_path = layout_cfg.get("footer", "/app/src/misc/footer.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") webui_title = os.getenv("WEBUI_TITLE", "A.P.Strom Transcription")
logo_url = os.getenv("WEBUI_LOGO_URL", "https://apstrom.ca") 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 = "" header_html = ""
if os.path.exists(header_path): if os.path.exists(header_path):
header_html = load_html_template( header_html = load_html_template(
@@ -95,15 +96,17 @@ def create_app():
webui_title=webui_title, webui_title=webui_title,
header_logo_url=logo_url, header_logo_url=logo_url,
header_logo_src=logo_url, header_logo_src=logo_url,
accent_color=accent_color,
) )
# Prepare footer HTML # Prepare footer HTML with accent color
footer_html = "" footer_html = ""
if os.path.exists(footer_path): if os.path.exists(footer_path):
version = os.getenv("SCRABIE_VERSION", "0.1.1.dev") version = os.getenv("SCRABIE_VERSION", "0.1.1.dev")
footer_html = load_html_template( footer_html = load_html_template(
footer_path, footer_path,
footer_scraibe_webui_version=version, footer_scraibe_webui_version=version,
accent_color=accent_color,
) )
# Build Gradio interface # Build Gradio interface
@@ -248,14 +251,44 @@ def create_app():
outputs=[status_text], 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_name = launch_cfg.get("server_name", os.getenv("GRADIO_SERVER_NAME", "0.0.0.0"))
server_port = launch_cfg.get("server_port", 7860) 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( app.launch(
server_name=str(server_name), server_name=str(server_name),
server_port=int(server_port), server_port=int(server_port),
css="body { font-family: Arial, sans-serif; }", css=accent_css,
) )