Center responsive layout and add PWA install support for mobile, tablet, and desktop.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -179,13 +179,132 @@ def ui_full_pipeline(
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Gradio 界面
|
# Gradio 界面
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
APP_ROOT = Path(__file__).resolve().parent
|
||||||
|
PWA_DIR = APP_ROOT / "pwa"
|
||||||
|
|
||||||
|
PWA_HEAD = """
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||||
|
<meta name="theme-color" content="#2563eb">
|
||||||
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||||
|
<meta name="apple-mobile-web-app-title" content="Trading Studio">
|
||||||
|
<link rel="manifest" href="/manifest.webmanifest">
|
||||||
|
<link rel="icon" href="/pwa/icons/icon.svg" type="image/svg+xml">
|
||||||
|
<link rel="apple-touch-icon" href="/pwa/icons/icon.svg">
|
||||||
|
<script>
|
||||||
|
if ("serviceWorker" in navigator) {
|
||||||
|
window.addEventListener("load", function () {
|
||||||
|
navigator.serviceWorker.register("/sw.js", { scope: "/" }).catch(function () {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
"""
|
||||||
|
|
||||||
CUSTOM_CSS = """
|
CUSTOM_CSS = """
|
||||||
/* ========== 高对比度暗色主题(确保文字清晰可读) ========== */
|
/* ========== 居中布局 + 响应式 + 高对比度 ========== */
|
||||||
|
html, body {
|
||||||
|
width: 100% !important;
|
||||||
|
min-height: 100vh !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
background: #0f1419 !important;
|
||||||
|
overflow-x: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 外层全宽背景,内容区水平居中 */
|
||||||
|
gradio-app,
|
||||||
|
.gradio-app,
|
||||||
|
#main,
|
||||||
|
.app,
|
||||||
|
.fillable,
|
||||||
|
.contain {
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: flex-start !important;
|
||||||
|
background: #0f1419 !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 核心内容容器居中 */
|
||||||
.gradio-container {
|
.gradio-container {
|
||||||
background: #0f1419 !important;
|
background: #0f1419 !important;
|
||||||
color: #eef2f7 !important;
|
color: #eef2f7 !important;
|
||||||
font-size: 15px !important;
|
font-size: 15px !important;
|
||||||
max-width: 1400px !important;
|
width: 100% !important;
|
||||||
|
max-width: min(1200px, 94vw) !important;
|
||||||
|
margin-left: auto !important;
|
||||||
|
margin-right: auto !important;
|
||||||
|
padding: 16px clamp(12px, 3vw, 32px) !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
float: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 带鱼屏 / 超宽屏 */
|
||||||
|
@media (min-width: 1920px) {
|
||||||
|
.gradio-container { max-width: min(1280px, 82vw) !important; }
|
||||||
|
}
|
||||||
|
@media (min-width: 2560px) {
|
||||||
|
.gradio-container { max-width: min(1360px, 68vw) !important; }
|
||||||
|
}
|
||||||
|
@media (min-width: 3440px) {
|
||||||
|
.gradio-container { max-width: 1440px !important; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 平板 */
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.gradio-container {
|
||||||
|
max-width: 100% !important;
|
||||||
|
padding: 14px 18px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 手机 */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.gradio-container {
|
||||||
|
max-width: 100% !important;
|
||||||
|
padding: 10px 12px !important;
|
||||||
|
font-size: 14px !important;
|
||||||
|
}
|
||||||
|
.gradio-container h1 { font-size: 1.35rem !important; }
|
||||||
|
.gradio-container .tab-nav {
|
||||||
|
flex-wrap: wrap !important;
|
||||||
|
gap: 6px !important;
|
||||||
|
}
|
||||||
|
.gradio-container .tab-nav button {
|
||||||
|
flex: 1 1 28% !important;
|
||||||
|
min-width: 88px !important;
|
||||||
|
padding: 8px 8px !important;
|
||||||
|
font-size: 0.8rem !important;
|
||||||
|
}
|
||||||
|
.status-row,
|
||||||
|
.status-row > div,
|
||||||
|
.pipeline-steps,
|
||||||
|
.pipeline-steps > div,
|
||||||
|
.pipeline-output-row,
|
||||||
|
.pipeline-output-row > div {
|
||||||
|
flex-direction: column !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
.status-row button,
|
||||||
|
.pipeline-steps button,
|
||||||
|
.pipeline-output-row button {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
.dark-panel table {
|
||||||
|
font-size: 0.85rem !important;
|
||||||
|
display: block !important;
|
||||||
|
overflow-x: auto !important;
|
||||||
|
}
|
||||||
|
.install-hint { font-size: 0.85rem !important; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 触摸设备:按钮最小点击区域 44px */
|
||||||
|
@media (hover: none) and (pointer: coarse) {
|
||||||
|
.gradio-container button { min-height: 44px !important; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 全局文字 */
|
/* 全局文字 */
|
||||||
@@ -198,7 +317,6 @@ CUSTOM_CSS = """
|
|||||||
color: #eef2f7 !important;
|
color: #eef2f7 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 标题 */
|
|
||||||
.gradio-container h1 {
|
.gradio-container h1 {
|
||||||
color: #ffffff !important;
|
color: #ffffff !important;
|
||||||
font-size: 1.75rem !important;
|
font-size: 1.75rem !important;
|
||||||
@@ -211,7 +329,6 @@ CUSTOM_CSS = """
|
|||||||
font-weight: 600 !important;
|
font-weight: 600 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 标签 */
|
|
||||||
.gradio-container .block-label,
|
.gradio-container .block-label,
|
||||||
.gradio-container label,
|
.gradio-container label,
|
||||||
.gradio-container .label-wrap span {
|
.gradio-container .label-wrap span {
|
||||||
@@ -220,7 +337,6 @@ CUSTOM_CSS = """
|
|||||||
font-size: 0.95rem !important;
|
font-size: 0.95rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 输入框 / 文本域 */
|
|
||||||
.gradio-container textarea,
|
.gradio-container textarea,
|
||||||
.gradio-container input[type="text"],
|
.gradio-container input[type="text"],
|
||||||
.gradio-container .wrap textarea,
|
.gradio-container .wrap textarea,
|
||||||
@@ -238,14 +354,12 @@ CUSTOM_CSS = """
|
|||||||
opacity: 1 !important;
|
opacity: 1 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 只读日志框 */
|
|
||||||
.gradio-container .wrap .readonly textarea {
|
.gradio-container .wrap .readonly textarea {
|
||||||
background: #111827 !important;
|
background: #111827 !important;
|
||||||
color: #e5e7eb !important;
|
color: #e5e7eb !important;
|
||||||
border-color: #374151 !important;
|
border-color: #374151 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tab 标签页 */
|
|
||||||
.gradio-container .tab-nav button {
|
.gradio-container .tab-nav button {
|
||||||
color: #9ca3af !important;
|
color: #9ca3af !important;
|
||||||
font-weight: 600 !important;
|
font-weight: 600 !important;
|
||||||
@@ -258,7 +372,6 @@ CUSTOM_CSS = """
|
|||||||
border-bottom: 3px solid #3b82f6 !important;
|
border-bottom: 3px solid #3b82f6 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 按钮 */
|
|
||||||
.gradio-container button.primary,
|
.gradio-container button.primary,
|
||||||
.gradio-container .primary {
|
.gradio-container .primary {
|
||||||
background: #2563eb !important;
|
background: #2563eb !important;
|
||||||
@@ -267,9 +380,7 @@ CUSTOM_CSS = """
|
|||||||
font-size: 0.95rem !important;
|
font-size: 0.95rem !important;
|
||||||
border: 1px solid #3b82f6 !important;
|
border: 1px solid #3b82f6 !important;
|
||||||
}
|
}
|
||||||
.gradio-container button.primary:hover {
|
.gradio-container button.primary:hover { background: #1d4ed8 !important; }
|
||||||
background: #1d4ed8 !important;
|
|
||||||
}
|
|
||||||
.gradio-container button.secondary,
|
.gradio-container button.secondary,
|
||||||
.gradio-container button:not(.primary) {
|
.gradio-container button:not(.primary) {
|
||||||
color: #e5e7eb !important;
|
color: #e5e7eb !important;
|
||||||
@@ -278,13 +389,14 @@ CUSTOM_CSS = """
|
|||||||
font-weight: 600 !important;
|
font-weight: 600 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 顶部信息面板 */
|
|
||||||
.dark-panel {
|
.dark-panel {
|
||||||
border: 1px solid #3b82f6 !important;
|
border: 1px solid #3b82f6 !important;
|
||||||
border-radius: 10px !important;
|
border-radius: 10px !important;
|
||||||
padding: 18px 20px !important;
|
padding: 18px 20px !important;
|
||||||
background: #1a2332 !important;
|
background: #1a2332 !important;
|
||||||
margin-bottom: 16px !important;
|
margin-bottom: 16px !important;
|
||||||
|
width: 100% !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
}
|
}
|
||||||
.dark-panel code {
|
.dark-panel code {
|
||||||
color: #93c5fd !important;
|
color: #93c5fd !important;
|
||||||
@@ -293,7 +405,16 @@ CUSTOM_CSS = """
|
|||||||
border-radius: 4px !important;
|
border-radius: 4px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 状态卡片 */
|
.install-hint {
|
||||||
|
margin-top: 12px !important;
|
||||||
|
padding: 10px 14px !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
background: #1e3a5f !important;
|
||||||
|
border: 1px dashed #3b82f6 !important;
|
||||||
|
color: #bfdbfe !important;
|
||||||
|
font-size: 0.9rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
.status-card {
|
.status-card {
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
padding: 14px 16px;
|
padding: 14px 16px;
|
||||||
@@ -301,13 +422,14 @@ CUSTOM_CSS = """
|
|||||||
border-left: 5px solid #6b7280;
|
border-left: 5px solid #6b7280;
|
||||||
background: #1f2937;
|
background: #1f2937;
|
||||||
min-height: 72px;
|
min-height: 72px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.status-card .status-title {
|
.status-card .status-title {
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #93c5fd !important;
|
color: #93c5fd !important;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
letter-spacing: 0.03em;
|
|
||||||
}
|
}
|
||||||
.status-card .status-body {
|
.status-card .status-body {
|
||||||
font-size: 0.92rem;
|
font-size: 0.92rem;
|
||||||
@@ -315,23 +437,13 @@ CUSTOM_CSS = """
|
|||||||
color: #f3f4f6 !important;
|
color: #f3f4f6 !important;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
.status-ok {
|
.status-ok { border-left-color: #22c55e !important; background: #14291a !important; }
|
||||||
border-left-color: #22c55e !important;
|
|
||||||
background: #14291a !important;
|
|
||||||
}
|
|
||||||
.status-ok .status-body { color: #bbf7d0 !important; }
|
.status-ok .status-body { color: #bbf7d0 !important; }
|
||||||
.status-warn {
|
.status-warn { border-left-color: #f59e0b !important; background: #2a2010 !important; }
|
||||||
border-left-color: #f59e0b !important;
|
|
||||||
background: #2a2010 !important;
|
|
||||||
}
|
|
||||||
.status-warn .status-body { color: #fde68a !important; }
|
.status-warn .status-body { color: #fde68a !important; }
|
||||||
.status-err {
|
.status-err { border-left-color: #ef4444 !important; background: #2a1515 !important; }
|
||||||
border-left-color: #ef4444 !important;
|
|
||||||
background: #2a1515 !important;
|
|
||||||
}
|
|
||||||
.status-err .status-body { color: #fecaca !important; }
|
.status-err .status-body { color: #fecaca !important; }
|
||||||
|
|
||||||
/* 音频上传区 */
|
|
||||||
.gradio-container .audio-container,
|
.gradio-container .audio-container,
|
||||||
.gradio-container .upload-container {
|
.gradio-container .upload-container {
|
||||||
border: 2px dashed #4b5563 !important;
|
border: 2px dashed #4b5563 !important;
|
||||||
@@ -413,14 +525,16 @@ def build_app() -> gr.Blocks:
|
|||||||
| ChatTTS | 本地 GPU 固定音色合成 |
|
| ChatTTS | 本地 GPU 固定音色合成 |
|
||||||
|
|
||||||
> 仓库: [{GIT_REPO_URL}]({GIT_REPO_URL})
|
> 仓库: [{GIT_REPO_URL}]({GIT_REPO_URL})
|
||||||
|
|
||||||
|
<div class="install-hint">📱 <strong>安装为 App:</strong>手机/平板用浏览器菜单「添加到主屏幕」;电脑 Chrome/Edge 地址栏点击「安装 Trading Studio」图标即可。</div>
|
||||||
""",
|
""",
|
||||||
elem_classes=["dark-panel"],
|
elem_classes=["dark-panel"],
|
||||||
)
|
)
|
||||||
|
|
||||||
with gr.Row():
|
with gr.Row(elem_classes=["status-row"]):
|
||||||
ollama_status = gr.HTML(value=_status_html("Ollama 节点", "正在检测...", "warn"))
|
ollama_status = gr.HTML(value=_status_html("Ollama 节点", "正在检测...", "warn"))
|
||||||
speaker_status = gr.HTML(value=_status_html("音色状态", "正在检测...", "warn"))
|
speaker_status = gr.HTML(value=_status_html("音色状态", "正在检测...", "warn"))
|
||||||
refresh_btn = gr.Button("🔄 刷新状态", variant="secondary", scale=0)
|
refresh_btn = gr.Button("🔄 刷新状态", variant="secondary", scale=0, min_width=120)
|
||||||
|
|
||||||
refresh_btn.click(
|
refresh_btn.click(
|
||||||
fn=lambda: (ui_check_ollama_html(), ui_speaker_status_html()),
|
fn=lambda: (ui_check_ollama_html(), ui_speaker_status_html()),
|
||||||
@@ -455,7 +569,7 @@ def build_app() -> gr.Blocks:
|
|||||||
|
|
||||||
# ---- Tab 2: 分步操作 ----
|
# ---- Tab 2: 分步操作 ----
|
||||||
with gr.Tab("🔧 分步流水线"):
|
with gr.Tab("🔧 分步流水线"):
|
||||||
with gr.Row():
|
with gr.Row(elem_classes=["pipeline-steps"]):
|
||||||
with gr.Column(scale=1):
|
with gr.Column(scale=1):
|
||||||
gr.Markdown("### Step 1 · 音频极速识别")
|
gr.Markdown("### Step 1 · 音频极速识别")
|
||||||
rec_audio = gr.Audio(
|
rec_audio = gr.Audio(
|
||||||
@@ -513,7 +627,7 @@ def build_app() -> gr.Blocks:
|
|||||||
)
|
)
|
||||||
pipeline_btn = gr.Button("▶ 启动全流程", variant="primary", size="lg")
|
pipeline_btn = gr.Button("▶ 启动全流程", variant="primary", size="lg")
|
||||||
pipeline_log = gr.Textbox(label="流水线日志", lines=6, interactive=False)
|
pipeline_log = gr.Textbox(label="流水线日志", lines=6, interactive=False)
|
||||||
with gr.Row():
|
with gr.Row(elem_classes=["pipeline-output-row"]):
|
||||||
pipe_raw = gr.Textbox(label="转写原文", lines=6)
|
pipe_raw = gr.Textbox(label="转写原文", lines=6)
|
||||||
pipe_polished = gr.Textbox(label="润色稿", lines=6)
|
pipe_polished = gr.Textbox(label="润色稿", lines=6)
|
||||||
pipe_output = gr.Audio(label="成品配音", type="filepath")
|
pipe_output = gr.Audio(label="成品配音", type="filepath")
|
||||||
@@ -532,18 +646,57 @@ def build_app() -> gr.Blocks:
|
|||||||
return demo
|
return demo
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def create_fastapi_app():
|
||||||
"""主入口:启动 Gradio 服务。"""
|
"""创建 FastAPI 应用:Gradio 中控 + PWA 静态资源。"""
|
||||||
logger.info("Trading Studio 启动中... HOST=%s PORT=%s", HOST, PORT)
|
from fastapi import FastAPI
|
||||||
app = build_app()
|
from fastapi.responses import FileResponse
|
||||||
app.launch(
|
from fastapi.staticfiles import StaticFiles
|
||||||
server_name=HOST,
|
|
||||||
server_port=PORT,
|
fastapi_app = FastAPI(title="Trading Studio", docs_url=None, redoc_url=None)
|
||||||
share=False,
|
|
||||||
show_error=True,
|
if PWA_DIR.is_dir():
|
||||||
theme=build_theme(),
|
fastapi_app.mount("/pwa", StaticFiles(directory=str(PWA_DIR)), name="pwa")
|
||||||
|
|
||||||
|
@fastapi_app.get("/manifest.webmanifest")
|
||||||
|
async def manifest():
|
||||||
|
return FileResponse(
|
||||||
|
PWA_DIR / "manifest.webmanifest",
|
||||||
|
media_type="application/manifest+json",
|
||||||
|
)
|
||||||
|
|
||||||
|
@fastapi_app.get("/sw.js")
|
||||||
|
async def service_worker():
|
||||||
|
return FileResponse(
|
||||||
|
PWA_DIR / "sw.js",
|
||||||
|
media_type="application/javascript",
|
||||||
|
headers={"Service-Worker-Allowed": "/"},
|
||||||
|
)
|
||||||
|
|
||||||
|
blocks = build_app()
|
||||||
|
gr.mount_gradio_app(
|
||||||
|
fastapi_app,
|
||||||
|
blocks,
|
||||||
|
path="/",
|
||||||
css=CUSTOM_CSS,
|
css=CUSTOM_CSS,
|
||||||
allowed_paths=[str(Path(__file__).resolve().parent / "outputs")],
|
theme=build_theme(),
|
||||||
|
head=PWA_HEAD,
|
||||||
|
allowed_paths=[str(APP_ROOT / "outputs")],
|
||||||
|
)
|
||||||
|
return fastapi_app
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
"""主入口:FastAPI + Gradio + PWA。"""
|
||||||
|
import uvicorn
|
||||||
|
|
||||||
|
logger.info("Trading Studio 启动中... HOST=%s PORT=%s", HOST, PORT)
|
||||||
|
app = create_fastapi_app()
|
||||||
|
uvicorn.run(
|
||||||
|
app,
|
||||||
|
host=HOST,
|
||||||
|
port=PORT,
|
||||||
|
log_level="info",
|
||||||
|
access_log=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||||
|
<rect width="512" height="512" rx="108" fill="#0f1419"/>
|
||||||
|
<rect x="32" y="32" width="448" height="448" rx="88" fill="none" stroke="#2563eb" stroke-width="12"/>
|
||||||
|
<path d="M280 96 L168 288 H240 L232 416 L344 224 H272 Z" fill="#3b82f6"/>
|
||||||
|
<text x="256" y="472" text-anchor="middle" fill="#93c5fd" font-family="system-ui,sans-serif" font-size="56" font-weight="700">TS</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 447 B |
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "Trading Studio 交易复盘配音",
|
||||||
|
"short_name": "TradingStudio",
|
||||||
|
"description": "本地量化交易复盘 → B 站配音生产流水线",
|
||||||
|
"start_url": "/",
|
||||||
|
"scope": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"orientation": "any",
|
||||||
|
"background_color": "#0f1419",
|
||||||
|
"theme_color": "#2563eb",
|
||||||
|
"lang": "zh-CN",
|
||||||
|
"categories": ["productivity", "utilities"],
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/pwa/icons/icon.svg",
|
||||||
|
"sizes": "any",
|
||||||
|
"type": "image/svg+xml",
|
||||||
|
"purpose": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/pwa/icons/icon.svg",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/svg+xml",
|
||||||
|
"purpose": "maskable"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* Trading Studio PWA Service Worker
|
||||||
|
* 缓存应用壳,支持离线打开已访问页面;API 请求始终走网络。
|
||||||
|
*/
|
||||||
|
const CACHE_NAME = "trading-studio-v1";
|
||||||
|
const SHELL = ["/", "/manifest.webmanifest"];
|
||||||
|
|
||||||
|
self.addEventListener("install", (event) => {
|
||||||
|
event.waitUntil(
|
||||||
|
caches.open(CACHE_NAME).then((cache) => cache.addAll(SHELL)).catch(() => {})
|
||||||
|
);
|
||||||
|
self.skipWaiting();
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener("activate", (event) => {
|
||||||
|
event.waitUntil(
|
||||||
|
caches.keys().then((keys) =>
|
||||||
|
Promise.all(keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k)))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
self.clients.claim();
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener("fetch", (event) => {
|
||||||
|
const { request } = event;
|
||||||
|
const url = new URL(request.url);
|
||||||
|
|
||||||
|
// API / 文件上传 / Gradio 动态接口不走缓存
|
||||||
|
if (
|
||||||
|
request.method !== "GET" ||
|
||||||
|
url.pathname.startsWith("/gradio_api") ||
|
||||||
|
url.pathname.startsWith("/file=") ||
|
||||||
|
url.pathname.startsWith("/upload") ||
|
||||||
|
url.pathname.includes("call")
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.respondWith(
|
||||||
|
fetch(request)
|
||||||
|
.then((response) => {
|
||||||
|
if (response.ok && url.origin === self.location.origin) {
|
||||||
|
const clone = response.clone();
|
||||||
|
caches.open(CACHE_NAME).then((cache) => cache.put(request, clone));
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
})
|
||||||
|
.catch(() => caches.match(request).then((r) => r || caches.match("/")))
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
# Web 中控
|
# Web 中控
|
||||||
gradio>=4.44.0
|
gradio>=4.44.0
|
||||||
|
fastapi>=0.100.0
|
||||||
|
uvicorn[standard]>=0.23.0
|
||||||
|
|
||||||
# 语音识别(CUDA 加速)
|
# 语音识别(CUDA 加速)
|
||||||
faster-whisper>=1.0.0
|
faster-whisper>=1.0.0
|
||||||
|
|||||||
Reference in New Issue
Block a user