File size: 4,722 Bytes
af4851b cd0ff97 af4851b cd0ff97 af4851b cd0ff97 af4851b cd0ff97 af4851b cd0ff97 af4851b cd0ff97 af4851b cd0ff97 af4851b cd0ff97 af4851b cd0ff97 af4851b cd0ff97 af4851b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | from pathlib import Path
from typing import Dict, Iterable, List, Optional
from backend.types import RenderArtifact, RenderJob, VoiceConfig
class RenderPipeline:
def __init__(self, session_root: Path, synthesizer=None) -> None:
self.session_root = Path(session_root)
self.session_root.mkdir(parents=True, exist_ok=True)
self.synthesizer = synthesizer
self._jobs: Dict[str, RenderJob] = {}
self._cancelled: set[str] = set()
self._paused: set[str] = set()
def get_job(self, session_id: str) -> RenderJob:
return self._jobs.setdefault(session_id, RenderJob(session_id=session_id))
def cancel(self, session_id: str) -> None:
self._cancelled.add(session_id)
self.get_job(session_id).status = "cancelled"
def pause(self, session_id: str) -> Dict[str, object]:
self._paused.add(session_id)
self.get_job(session_id).status = "paused"
return {"type": "paused", "session_id": session_id}
def resume(self, session_id: str) -> Dict[str, object]:
self._paused.discard(session_id)
self.get_job(session_id).status = "running"
return {"type": "resumed", "session_id": session_id}
def render(
self,
*,
session_id: str,
book: Dict[str, object],
chapters: List[Dict[str, object]],
voice_config: Dict[str, object],
diffusion_steps: int,
speed: float,
synthesizer=None,
) -> Iterable[Dict[str, object]]:
selected = [chapter for chapter in chapters if chapter.get("included", True)]
job = self.get_job(session_id)
job.status = "running"
job.outputs.clear()
synth = synthesizer or self.synthesizer
if synth is None:
raise ValueError("No synthesizer provided for render pipeline")
yield {
"type": "started",
"session_id": session_id,
"total_chapters": len(selected),
"book_title": book.get("title"),
"backend": "local",
"model": VoiceConfig.from_dict(voice_config).model,
}
voice = VoiceConfig.from_dict(voice_config)
render_dir = self.session_root / session_id / "renders"
render_dir.mkdir(parents=True, exist_ok=True)
for index, chapter in enumerate(selected):
if session_id in self._cancelled:
yield {"type": "cancelled", "session_id": session_id, "backend": "local", "model": voice.model}
self._cancelled.discard(session_id)
job.status = "cancelled"
return
chapter_id = str(chapter["id"])
job.current_chapter_id = chapter_id
yield {
"type": "chapter_started",
"session_id": session_id,
"chapter_id": chapter_id,
"chapter_title": chapter["title"],
"chapter_index": index,
"overall_progress": index / max(1, len(selected)),
"backend": "local",
"model": voice.model,
}
output_path = render_dir / f"{index + 1:03d}-{chapter_id}.wav"
result = synth.synthesize(
text=str(chapter["text"]),
output_path=output_path,
voice_config=voice,
diffusion_steps=diffusion_steps,
speed=speed,
)
yield {
"type": "chapter_progress",
"session_id": session_id,
"chapter_id": chapter_id,
"percent": 100,
"backend": result.get("backend", "local"),
"model": result.get("model", voice.model),
}
artifact = RenderArtifact(
path=output_path,
duration_seconds=int(result.get("duration_seconds", 0)),
chapter_id=chapter_id,
)
job.outputs.append(artifact)
yield {
"type": "chapter_done",
"session_id": session_id,
"chapter_id": chapter_id,
"duration_seconds": artifact.duration_seconds,
"overall_progress": (index + 1) / max(1, len(selected)),
"output_path": str(output_path),
"backend": result.get("backend", "local"),
"model": result.get("model", voice.model),
}
job.status = "completed"
yield {
"type": "completed",
"session_id": session_id,
"outputs": [str(artifact.path) for artifact in job.outputs],
"backend": "local",
"model": voice.model,
}
|