add embedded web interface HTML (single-file version with inline CSS/JS/SVG)
### Added - `get_inline_ui_html()`: generates a self-contained version of the web interface, with CSS, JS, and SVG assets inlined directly into the HTML. useful for environments where serving static files is inconvenient or when a single-call UI delivery is preferred. (cherry picked from commit aa44a92a6775bcc42b3518e78acd16eda275935a)
This commit is contained in:
parent
4a5d5e1f3b
commit
1ba171a58d
3 changed files with 68 additions and 5 deletions
|
|
@ -1,12 +1,13 @@
|
||||||
from .audio_processor import AudioProcessor
|
from .audio_processor import AudioProcessor
|
||||||
from .core import TranscriptionEngine
|
from .core import TranscriptionEngine
|
||||||
from .parse_args import parse_args
|
from .parse_args import parse_args
|
||||||
from .web.web_interface import get_web_interface_html
|
from .web.web_interface import get_web_interface_html, get_inline_ui_html
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"TranscriptionEngine",
|
"TranscriptionEngine",
|
||||||
"AudioProcessor",
|
"AudioProcessor",
|
||||||
"parse_args",
|
"parse_args",
|
||||||
"get_web_interface_html",
|
"get_web_interface_html",
|
||||||
|
"get_inline_ui_html",
|
||||||
"download_simulstreaming_backend",
|
"download_simulstreaming_backend",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from contextlib import asynccontextmanager
|
||||||
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
|
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
|
||||||
from fastapi.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from whisperlivekit import TranscriptionEngine, AudioProcessor, get_web_interface_html, parse_args
|
from whisperlivekit import TranscriptionEngine, AudioProcessor, get_inline_ui_html, parse_args
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from starlette.staticfiles import StaticFiles
|
from starlette.staticfiles import StaticFiles
|
||||||
|
|
@ -38,7 +38,7 @@ app.mount("/web", StaticFiles(directory=str(web_dir)), name="web")
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
async def get():
|
async def get():
|
||||||
return HTMLResponse(get_web_interface_html())
|
return HTMLResponse(get_inline_ui_html())
|
||||||
|
|
||||||
|
|
||||||
async def handle_websocket_results(websocket, results_generator):
|
async def handle_websocket_results(websocket, results_generator):
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
import importlib.resources as resources
|
import importlib.resources as resources
|
||||||
|
import base64
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -12,6 +13,67 @@ def get_web_interface_html():
|
||||||
logger.error(f"Error loading web interface HTML: {e}")
|
logger.error(f"Error loading web interface HTML: {e}")
|
||||||
return "<html><body><h1>Error loading interface</h1></body></html>"
|
return "<html><body><h1>Error loading interface</h1></body></html>"
|
||||||
|
|
||||||
|
def get_inline_ui_html():
|
||||||
|
"""Returns the complete web interface HTML with all assets embedded in a single call."""
|
||||||
|
try:
|
||||||
|
# Load HTML template
|
||||||
|
with resources.files('whisperlivekit.web').joinpath('live_transcription.html').open('r', encoding='utf-8') as f:
|
||||||
|
html_content = f.read()
|
||||||
|
|
||||||
|
# Load CSS and embed it
|
||||||
|
with resources.files('whisperlivekit.web').joinpath('live_transcription.css').open('r', encoding='utf-8') as f:
|
||||||
|
css_content = f.read()
|
||||||
|
|
||||||
|
# Load JS and embed it
|
||||||
|
with resources.files('whisperlivekit.web').joinpath('live_transcription.js').open('r', encoding='utf-8') as f:
|
||||||
|
js_content = f.read()
|
||||||
|
|
||||||
|
# Load SVG files and convert to data URIs
|
||||||
|
with resources.files('whisperlivekit.web').joinpath('src', 'system_mode.svg').open('r', encoding='utf-8') as f:
|
||||||
|
system_svg = f.read()
|
||||||
|
system_data_uri = f"data:image/svg+xml;base64,{base64.b64encode(system_svg.encode('utf-8')).decode('utf-8')}"
|
||||||
|
|
||||||
|
with resources.files('whisperlivekit.web').joinpath('src', 'light_mode.svg').open('r', encoding='utf-8') as f:
|
||||||
|
light_svg = f.read()
|
||||||
|
light_data_uri = f"data:image/svg+xml;base64,{base64.b64encode(light_svg.encode('utf-8')).decode('utf-8')}"
|
||||||
|
|
||||||
|
with resources.files('whisperlivekit.web').joinpath('src', 'dark_mode.svg').open('r', encoding='utf-8') as f:
|
||||||
|
dark_svg = f.read()
|
||||||
|
dark_data_uri = f"data:image/svg+xml;base64,{base64.b64encode(dark_svg.encode('utf-8')).decode('utf-8')}"
|
||||||
|
|
||||||
|
# Replace external references with embedded content
|
||||||
|
html_content = html_content.replace(
|
||||||
|
'<link rel="stylesheet" href="/web/live_transcription.css" />',
|
||||||
|
f'<style>\n{css_content}\n</style>'
|
||||||
|
)
|
||||||
|
|
||||||
|
html_content = html_content.replace(
|
||||||
|
'<script src="/web/live_transcription.js"></script>',
|
||||||
|
f'<script>\n{js_content}\n</script>'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Replace SVG references with data URIs
|
||||||
|
html_content = html_content.replace(
|
||||||
|
'<img src="/web/src/system_mode.svg" alt="" />',
|
||||||
|
f'<img src="{system_data_uri}" alt="" />'
|
||||||
|
)
|
||||||
|
|
||||||
|
html_content = html_content.replace(
|
||||||
|
'<img src="/web/src/light_mode.svg" alt="" />',
|
||||||
|
f'<img src="{light_data_uri}" alt="" />'
|
||||||
|
)
|
||||||
|
|
||||||
|
html_content = html_content.replace(
|
||||||
|
'<img src="/web/src/dark_mode.svg" alt="" />',
|
||||||
|
f'<img src="{dark_data_uri}" alt="" />'
|
||||||
|
)
|
||||||
|
|
||||||
|
return html_content
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error creating embedded web interface: {e}")
|
||||||
|
return "<html><body><h1>Error loading embedded interface</h1></body></html>"
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
|
@ -28,6 +90,6 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
async def get():
|
async def get():
|
||||||
return HTMLResponse(get_web_interface_html())
|
return HTMLResponse(get_inline_ui_html())
|
||||||
|
|
||||||
uvicorn.run(app=app)
|
uvicorn.run(app=app)
|
||||||
Loading…
Reference in a new issue