86 lines
2.7 KiB
Python
86 lines
2.7 KiB
Python
# Copyright 2026 Umbra Tools (umbratools.com)
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
"""Reusable library entry point for creating a new, isolated project.
|
|
|
|
Extracted from the ``umbraaudio init`` CLI path so the web UI can bootstrap a
|
|
brand-new project (its own directory + SQLite DB + parsed chapters) without
|
|
touching the currently-active project. Mirrors ``cli.init`` exactly.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from umbraaudio.config import load_config, slugify_project_name
|
|
from umbraaudio.database import Database
|
|
from umbraaudio.parsers import detect_format, parse_manuscript
|
|
from umbraaudio.voices.registry import VoiceRegistry
|
|
|
|
|
|
async def _bootstrap(
|
|
manuscript_path: Path,
|
|
project_name: str | None = None,
|
|
) -> dict[str, Any]:
|
|
cfg = load_config(project_name or slugify_project_name(manuscript_path.name))
|
|
project_dir: Path = cfg.project_dir
|
|
|
|
name = project_name or slugify_project_name(manuscript_path.name)
|
|
source_format = detect_format(manuscript_path)
|
|
chapters = parse_manuscript(manuscript_path, source_format)
|
|
|
|
db = Database(cfg.db_path)
|
|
await db.connect()
|
|
project_id: int = await db.create_project(
|
|
name=name,
|
|
source_path=str(manuscript_path.resolve()),
|
|
source_format=source_format,
|
|
)
|
|
registry = VoiceRegistry(cfg.voice_registry_path)
|
|
await registry.connect()
|
|
|
|
try:
|
|
for i, ch in enumerate(chapters):
|
|
await db.insert_chapter(
|
|
project_id=project_id,
|
|
index=i,
|
|
title=ch.get("title"),
|
|
source_text=ch["text"],
|
|
word_count=len(ch["text"].split()),
|
|
)
|
|
await db.update_project_status(project_id, "parsed")
|
|
finally:
|
|
await registry.close()
|
|
await db.close()
|
|
|
|
return {
|
|
"project_id": project_id,
|
|
"slug": project_dir.name,
|
|
"name": name,
|
|
"project_dir": project_dir,
|
|
"data_dir": cfg.data_dir,
|
|
"db_path": cfg.db_path,
|
|
"chapter_count": len(chapters),
|
|
}
|
|
|
|
|
|
def create_project_from_manuscript(
|
|
manuscript_path: Path,
|
|
project_name: str | None = None,
|
|
) -> dict[str, Any]:
|
|
"""Create a fresh isolated project from a manuscript path (synchronous).
|
|
|
|
Returns a dict with ``slug``, ``project_id``, ``name``, paths, and
|
|
``chapter_count``.
|
|
"""
|
|
return asyncio.run(_bootstrap(manuscript_path, project_name))
|
|
|
|
|
|
async def create_project_from_manuscript_async(
|
|
manuscript_path: Path,
|
|
project_name: str | None = None,
|
|
) -> dict[str, Any]:
|
|
"""Async variant for use inside a running event loop (e.g. web routes)."""
|
|
return await _bootstrap(manuscript_path, project_name)
|