Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

エンジン側でいろいろ設定できるGUIの追加 #531

Merged
merged 17 commits into from
Dec 31, 2022
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ idna==3.3
# via
# anyio
# requests
jinja2==3.1.2
# via -r requirements.in
markupsafe==2.1.1
# via jinja2
nodeenv==1.6.0
# via pre-commit
numpy==1.20.0
Expand Down
4 changes: 4 additions & 0 deletions requirements-test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ iniconfig==1.1.1
# via pytest
isort==5.1.4
# via pysen
jinja2==3.1.2
# via -r requirements.in
markupsafe==2.1.1
# via jinja2
mccabe==0.6.1
# via flake8
mypy==0.790
Expand Down
1 change: 1 addition & 0 deletions requirements.in
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
numpy
fastapi
python-multipart
jinja2
uvicorn
aiofiles
soundfile
Expand Down
4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ idna==3.3
# via
# anyio
# requests
jinja2==3.1.2
# via -r requirements.in
markupsafe==2.1.1
# via jinja2
numpy==1.20.0
# via
# -r requirements.in
Expand Down
76 changes: 66 additions & 10 deletions run.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import traceback
import zipfile
from distutils.version import LooseVersion
from enum import Enum
from functools import lru_cache
from io import TextIOWrapper
from pathlib import Path
Expand All @@ -20,10 +19,11 @@
import requests
import soundfile
import uvicorn
from fastapi import FastAPI, HTTPException, Request, Response
from fastapi import FastAPI, Form, HTTPException, Request, Response
from fastapi.middleware.cors import CORSMiddleware
from fastapi.params import Query
from fastapi.responses import JSONResponse
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.templating import Jinja2Templates
from pydantic import ValidationError, conint
from starlette.background import BackgroundTask
from starlette.responses import FileResponse
Expand Down Expand Up @@ -51,6 +51,7 @@
)
from voicevox_engine.part_of_speech_data import MAX_PRIORITY, MIN_PRIORITY
from voicevox_engine.preset import Preset, PresetLoader
from voicevox_engine.setting import CorsPolicyMode, Setting, SettingLoader
from voicevox_engine.synthesis_engine import SynthesisEngineBase, make_synthesis_engines
from voicevox_engine.user_dict import (
apply_word,
Expand All @@ -68,11 +69,6 @@
)


class CorsPolicyMode(str, Enum):
all = "all"
localapps = "localapps"


def b64encode_str(s):
return base64.b64encode(s).decode("utf-8")

Expand Down Expand Up @@ -173,6 +169,10 @@ async def block_origin_middleware(request: Request, call_next):
root_dir / "engine_manifest.json", root_dir
)

setting_loader = SettingLoader(root_dir / "setting.yml")
My-MC marked this conversation as resolved.
Show resolved Hide resolved

setting_ui_template = Jinja2Templates(directory=root_dir / "ui_template")

# キャッシュを有効化
# モジュール側でlru_cacheを指定するとキャッシュを制御しにくいため、HTTPサーバ側で指定する
# TODO: キャッシュを管理するモジュール側API・HTTP側APIを用意する
Expand Down Expand Up @@ -875,6 +875,49 @@ def supported_devices(
def engine_manifest():
return engine_manifest_loader.load_manifest()

@app.get("/setting", response_class=HTMLResponse)
def setting_get(request: Request):
settings = setting_loader.load_setting_file()

cors_policy_mode = settings.cors_policy_mode
allow_origin = settings.allow_origin

return setting_ui_template.TemplateResponse(
"ui.html",
{
"request": request,
"cors_policy_mode": cors_policy_mode,
"allow_origin": allow_origin,
},
)

@app.post("/setting", response_class=HTMLResponse)
def setting_post(
request: Request,
cors_policy_mode: Optional[str] = Form(None), # noqa: B008
allow_origin: Optional[str] = Form(None), # noqa: B008
):
settings = Setting(
cors_policy_mode=cors_policy_mode,
allow_origin=allow_origin,
)

# None文字列が返された場合上書きをする
if settings.allow_origin == "None":
settings.allow_origin = None
My-MC marked this conversation as resolved.
Show resolved Hide resolved

# 更新した設定へ上書き
setting_loader.dump_setting_file(settings)

return setting_ui_template.TemplateResponse(
"ui.html",
{
"request": request,
"cors_policy_mode": cors_policy_mode,
"allow_origin": allow_origin,
},
)

return app


Expand Down Expand Up @@ -986,13 +1029,26 @@ def engine_manifest():
cancellable_engine = CancellableEngine(args)

root_dir = args.voicevox_dir if args.voicevox_dir is not None else engine_root()

settings = SettingLoader(root_dir / "setting.yml").load_setting_file()
My-MC marked this conversation as resolved.
Show resolved Hide resolved
My-MC marked this conversation as resolved.
Show resolved Hide resolved

cors_policy_mode = (
args.cors_policy_mode
if args.cors_policy_mode is not CorsPolicyMode.localapps
else settings.cors_policy_mode
)
My-MC marked this conversation as resolved.
Show resolved Hide resolved

allow_origin = (
args.allow_origin if args.allow_origin is not None else settings.allow_origin
)

uvicorn.run(
generate_app(
synthesis_engines,
latest_core_version,
root_dir=root_dir,
cors_policy_mode=args.cors_policy_mode,
allow_origin=args.allow_origin,
cors_policy_mode=cors_policy_mode,
allow_origin=allow_origin,
),
host=args.host,
port=args.port,
Expand Down
2 changes: 2 additions & 0 deletions setting.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
allow_origin: null
cors_policy_mode: localapps
118 changes: 118 additions & 0 deletions ui_template/ui.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>VOICEVOX Engine 設定</title>
<link
rel="shortcut icon"
href="https://voicevox.hiroshiba.jp/favicon-32x32.png"
/>

<link
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
crossorigin="anonymous"
/>
<script
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
crossorigin="anonymous"
></script>
</head>

<body>
<div class="container p-3">
<form method="post">
<div class="alert alert-warning" role="alert">
設定の変更の更新にはエンジンの再起動が必要です。
</div>

<div class="mb-3">
<label class="form-label">CORS Policy Mode</label>
<select
class="form-select"
aria-label="cors_policy_mode"
name="cors_policy_mode"
>
<option selected>{{ cors_policy_mode }}</option>
My-MC marked this conversation as resolved.
Show resolved Hide resolved
<option value="localapps">localapps</option>
<option value="all">all</option>
</select>
<div class="form-text">
<p class="mb-1">
allまたはlocalappsを指定。allはすべてを許可します。
</p>
<p class="mb-1">
localappsはオリジン間リソース共有ポリシーを、app://.とlocalhost関連に限定します。
</p>
<p>
その他のオリジンはallow_originオプションで追加できます。デフォルトはlocalapps。
</p>
</div>
</div>

<div class="mb-3">
<label class="form-label">Allow Origin</label>
<input
class="form-control"
type="text"
name="allow_origin"
value="{{ allow_origin }}"
/>
<div class="form-text">
許可するオリジンを指定します。複数指定する場合は、直後にスペースで区切って追加できます。
</div>
</div>

<div
class="modal fade"
id="submitModal"
tabindex="-1"
aria-labelledby="submitModalLabel"
aria-hidden="true"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="submitModalLabel">
設定の保存
</h5>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<div class="modal-body">
設定をを保存します。よろしいですか?
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary"
data-bs-dismiss="modal"
>
キャンセル
</button>
<button type="submit" class="btn btn-primary">
保存
</button>
</div>
</div>
</div>
</div>

<button
type="button"
class="btn btn-primary"
data-bs-toggle="modal"
data-bs-target="#submitModal"
>
保存
</button>
</form>
</div>
</body>
</html>
21 changes: 21 additions & 0 deletions voicevox_engine/setting/Setting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from enum import Enum
from typing import Optional

from pydantic import BaseModel, Field


class CorsPolicyMode(str, Enum):
all = "all"
localapps = "localapps"


class Setting(BaseModel):
"""
エンジンの設定情報
"""

cors_policy_mode: CorsPolicyMode = Field(title="リソース共有ポリシー")
allow_origin: Optional[str] = Field(title="許可するオリジン")

class Config:
use_enum_values = True
26 changes: 26 additions & 0 deletions voicevox_engine/setting/SettingLoader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from pathlib import Path

import yaml

from .Setting import Setting


class SettingLoader:
def __init__(self, setting_file_path: Path) -> None:
self.setting_file_path = setting_file_path

def load_setting_file(self) -> Setting:
setting = yaml.safe_load(self.setting_file_path.read_text(encoding="utf-8"))

setting = Setting(
cors_policy_mode=setting["cors_policy_mode"],
allow_origin=setting["allow_origin"],
)

return setting

def dump_setting_file(self, settings: Setting) -> None:
settings_dict = settings.dict()

with open("setting.yml", mode="w", encoding="utf-8") as f:
yaml.dump(settings_dict, f)
4 changes: 4 additions & 0 deletions voicevox_engine/setting/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .Setting import CorsPolicyMode, Setting
from .SettingLoader import SettingLoader

__all__ = ["Setting", "SettingLoader", "CorsPolicyMode"]