Skip to content

Commit

Permalink
整理: generate_app() を分離 (#1217)
Browse files Browse the repository at this point in the history
refactor: `generate_app()` を分離
  • Loading branch information
tarepan authored May 9, 2024
1 parent 3df49b9 commit 477cdb6
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 134 deletions.
5 changes: 2 additions & 3 deletions build_util/make_docs.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
from pathlib import Path

from voicevox_engine.app.application import generate_app
from voicevox_engine.dev.core.mock import MockCoreWrapper
from voicevox_engine.dev.tts_engine.mock import MockTTSEngine
from voicevox_engine.preset.PresetManager import PresetManager
Expand Down Expand Up @@ -33,11 +34,9 @@ def generate_api_docs_html(schema: str) -> str:


if __name__ == "__main__":
import run

mock_core = MockCoreWrapper()
# FastAPI の機能を用いて OpenAPI schema を生成する
app = run.generate_app(
app = generate_app(
tts_engines={"mock": MockTTSEngine()},
cores={"mock": CoreAdapter(mock_core)},
latest_core_version="mock",
Expand Down
137 changes: 8 additions & 129 deletions run.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,24 @@
"""VOICEVOX ENGINE の実行"""

import argparse
import multiprocessing
import os
import sys
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from io import TextIOWrapper
from pathlib import Path
from typing import TypeVar

import uvicorn
from fastapi import FastAPI, HTTPException
from fastapi.responses import HTMLResponse

from voicevox_engine import __version__
from voicevox_engine.app.dependencies import deprecated_mutable_api
from voicevox_engine.app.middlewares import configure_middlewares
from voicevox_engine.app.openapi_schema import configure_openapi_schema
from voicevox_engine.app.routers.engine_info import generate_engine_info_router
from voicevox_engine.app.routers.library import generate_library_router
from voicevox_engine.app.routers.morphing import generate_morphing_router
from voicevox_engine.app.routers.preset import generate_preset_router
from voicevox_engine.app.routers.setting import generate_setting_router
from voicevox_engine.app.routers.speaker import generate_speaker_router
from voicevox_engine.app.routers.tts_pipeline import generate_tts_pipeline_router
from voicevox_engine.app.routers.user_dict import generate_user_dict_router

from voicevox_engine.app.application import generate_app
from voicevox_engine.cancellable_engine import CancellableEngine
from voicevox_engine.core.core_adapter import CoreAdapter
from voicevox_engine.core.core_initializer import initialize_cores
from voicevox_engine.engine_manifest.EngineManifestLoader import EngineManifestLoader
from voicevox_engine.library_manager import LibraryManager
from voicevox_engine.metas.MetasStore import MetasStore
from voicevox_engine.preset.PresetManager import PresetManager
from voicevox_engine.setting.Setting import CorsPolicyMode
from voicevox_engine.setting.SettingLoader import USER_SETTING_PATH, SettingHandler
from voicevox_engine.tts_pipeline.tts_engine import (
TTSEngine,
make_tts_engines_from_cores,
)
from voicevox_engine.user_dict.user_dict import update_dict
from voicevox_engine.tts_pipeline.tts_engine import make_tts_engines_from_cores
from voicevox_engine.utility.core_version_utility import get_latest_version
from voicevox_engine.utility.path_utility import engine_root, get_save_dir
from voicevox_engine.utility.path_utility import engine_root
from voicevox_engine.utility.run_utility import decide_boolean_from_env


Expand Down Expand Up @@ -75,108 +54,6 @@ def set_output_log_utf8() -> None:
pass


def generate_app(
tts_engines: dict[str, TTSEngine],
cores: dict[str, CoreAdapter],
latest_core_version: str,
setting_loader: SettingHandler,
preset_manager: PresetManager,
cancellable_engine: CancellableEngine | None = None,
root_dir: Path | None = None,
cors_policy_mode: CorsPolicyMode = CorsPolicyMode.localapps,
allow_origin: list[str] | None = None,
disable_mutable_api: bool = False,
) -> FastAPI:
"""ASGI 'application' 仕様に準拠した VOICEVOX ENGINE アプリケーションインスタンスを生成する。"""
if root_dir is None:
root_dir = engine_root()

@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncIterator[None]:
update_dict()
yield

app = FastAPI(
title="VOICEVOX Engine",
description="VOICEVOXの音声合成エンジンです。",
version=__version__,
lifespan=lifespan,
)
app = configure_middlewares(app, cors_policy_mode, allow_origin)

if disable_mutable_api:
deprecated_mutable_api.enable = False

engine_manifest_data = EngineManifestLoader(
engine_root() / "engine_manifest.json", engine_root()
).load_manifest()
library_manager = LibraryManager(
get_save_dir() / "installed_libraries",
engine_manifest_data.supported_vvlib_manifest_version,
engine_manifest_data.brand_name,
engine_manifest_data.name,
engine_manifest_data.uuid,
)

metas_store = MetasStore(root_dir / "speaker_info")

def get_engine(core_version: str | None) -> TTSEngine:
if core_version is None:
return tts_engines[latest_core_version]
if core_version in tts_engines:
return tts_engines[core_version]
raise HTTPException(status_code=422, detail="不明なバージョンです")

def get_core(core_version: str | None) -> CoreAdapter:
"""指定したバージョンのコアを取得する"""
if core_version is None:
return cores[latest_core_version]
if core_version in cores:
return cores[core_version]
raise HTTPException(status_code=422, detail="不明なバージョンです")

app.include_router(
generate_tts_pipeline_router(
get_engine, get_core, preset_manager, cancellable_engine
)
)
app.include_router(generate_morphing_router(get_engine, get_core, metas_store))
app.include_router(generate_preset_router(preset_manager))
app.include_router(generate_speaker_router(get_core, metas_store, root_dir))
if engine_manifest_data.supported_features.manage_library:
app.include_router(
generate_library_router(engine_manifest_data, library_manager)
)
app.include_router(generate_user_dict_router())
app.include_router(
generate_engine_info_router(get_core, cores, engine_manifest_data)
)
app.include_router(generate_setting_router(setting_loader, engine_manifest_data))

@app.get("/", response_class=HTMLResponse, tags=["その他"])
async def get_portal() -> str:
"""ポータルページを返します。"""
engine_name = engine_manifest_data.name

return f"""
<html>
<head>
<title>{engine_name}</title>
</head>
<body>
<h1>{engine_name}</h1>
{engine_name} へようこそ!
<ul>
<li><a href='/setting'>設定</a></li>
<li><a href='/docs'>API ドキュメント</a></li>
</ul></body></html>
"""

app = configure_openapi_schema(app)

return app


T = TypeVar("T")


Expand All @@ -200,6 +77,8 @@ def select_first_not_none_or_none(candidates: list[S | None]) -> S | None:


def main() -> None:
"""VOICEVOX ENGINE を実行する"""

multiprocessing.freeze_support()

output_log_utf8 = decide_boolean_from_env("VV_OUTPUT_LOG_UTF8")
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from run import generate_app

from voicevox_engine.app.application import generate_app
from voicevox_engine.core.core_initializer import initialize_cores
from voicevox_engine.preset.PresetManager import PresetManager
from voicevox_engine.setting.SettingLoader import SettingHandler
Expand Down
3 changes: 2 additions & 1 deletion test/e2e/test_disable_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
from typing import Literal

from fastapi.testclient import TestClient
from run import generate_app

from voicevox_engine.app.application import generate_app


# clientとschemaとパスを受け取ってリクエストを送信し、レスポンスが403であることを確認する
Expand Down
132 changes: 132 additions & 0 deletions voicevox_engine/app/application.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from pathlib import Path

from fastapi import FastAPI, HTTPException
from fastapi.responses import HTMLResponse

from voicevox_engine import __version__
from voicevox_engine.app.dependencies import deprecated_mutable_api
from voicevox_engine.app.middlewares import configure_middlewares
from voicevox_engine.app.openapi_schema import configure_openapi_schema
from voicevox_engine.app.routers.engine_info import generate_engine_info_router
from voicevox_engine.app.routers.library import generate_library_router
from voicevox_engine.app.routers.morphing import generate_morphing_router
from voicevox_engine.app.routers.preset import generate_preset_router
from voicevox_engine.app.routers.setting import generate_setting_router
from voicevox_engine.app.routers.speaker import generate_speaker_router
from voicevox_engine.app.routers.tts_pipeline import generate_tts_pipeline_router
from voicevox_engine.app.routers.user_dict import generate_user_dict_router
from voicevox_engine.cancellable_engine import CancellableEngine
from voicevox_engine.core.core_adapter import CoreAdapter
from voicevox_engine.engine_manifest.EngineManifestLoader import EngineManifestLoader
from voicevox_engine.library_manager import LibraryManager
from voicevox_engine.metas.MetasStore import MetasStore
from voicevox_engine.preset.PresetManager import PresetManager
from voicevox_engine.setting.Setting import CorsPolicyMode
from voicevox_engine.setting.SettingLoader import SettingHandler
from voicevox_engine.tts_pipeline.tts_engine import TTSEngine
from voicevox_engine.user_dict.user_dict import update_dict
from voicevox_engine.utility.path_utility import engine_root, get_save_dir


def generate_app(
tts_engines: dict[str, TTSEngine],
cores: dict[str, CoreAdapter],
latest_core_version: str,
setting_loader: SettingHandler,
preset_manager: PresetManager,
cancellable_engine: CancellableEngine | None = None,
root_dir: Path | None = None,
cors_policy_mode: CorsPolicyMode = CorsPolicyMode.localapps,
allow_origin: list[str] | None = None,
disable_mutable_api: bool = False,
) -> FastAPI:
"""ASGI 'application' 仕様に準拠した VOICEVOX ENGINE アプリケーションインスタンスを生成する。"""
if root_dir is None:
root_dir = engine_root()

@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncIterator[None]:
update_dict()
yield

app = FastAPI(
title="VOICEVOX Engine",
description="VOICEVOXの音声合成エンジンです。",
version=__version__,
lifespan=lifespan,
)
app = configure_middlewares(app, cors_policy_mode, allow_origin)

if disable_mutable_api:
deprecated_mutable_api.enable = False

engine_manifest_data = EngineManifestLoader(
engine_root() / "engine_manifest.json", engine_root()
).load_manifest()
library_manager = LibraryManager(
get_save_dir() / "installed_libraries",
engine_manifest_data.supported_vvlib_manifest_version,
engine_manifest_data.brand_name,
engine_manifest_data.name,
engine_manifest_data.uuid,
)

metas_store = MetasStore(root_dir / "speaker_info")

def get_engine(core_version: str | None) -> TTSEngine:
if core_version is None:
return tts_engines[latest_core_version]
if core_version in tts_engines:
return tts_engines[core_version]
raise HTTPException(status_code=422, detail="不明なバージョンです")

def get_core(core_version: str | None) -> CoreAdapter:
"""指定したバージョンのコアを取得する"""
if core_version is None:
return cores[latest_core_version]
if core_version in cores:
return cores[core_version]
raise HTTPException(status_code=422, detail="不明なバージョンです")

app.include_router(
generate_tts_pipeline_router(
get_engine, get_core, preset_manager, cancellable_engine
)
)
app.include_router(generate_morphing_router(get_engine, get_core, metas_store))
app.include_router(generate_preset_router(preset_manager))
app.include_router(generate_speaker_router(get_core, metas_store, root_dir))
if engine_manifest_data.supported_features.manage_library:
app.include_router(
generate_library_router(engine_manifest_data, library_manager)
)
app.include_router(generate_user_dict_router())
app.include_router(
generate_engine_info_router(get_core, cores, engine_manifest_data)
)
app.include_router(generate_setting_router(setting_loader, engine_manifest_data))

@app.get("/", response_class=HTMLResponse, tags=["その他"])
async def get_portal() -> str:
"""ポータルページを返します。"""
engine_name = engine_manifest_data.name

return f"""
<html>
<head>
<title>{engine_name}</title>
</head>
<body>
<h1>{engine_name}</h1>
{engine_name} へようこそ!
<ul>
<li><a href='/setting'>設定</a></li>
<li><a href='/docs'>API ドキュメント</a></li>
</ul></body></html>
"""

app = configure_openapi_schema(app)

return app

0 comments on commit 477cdb6

Please sign in to comment.