Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
40 changes: 40 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: CI

on:
push:
branches: [master, main]
pull_request:

jobs:
python:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install
run: python -m pip install -e '.[dev]'
- name: Test
run: pytest -q
- name: Lint
run: ruff check .

frontend:
runs-on: ubuntu-latest
defaults:
run:
working-directory: frontend
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
cache-dependency-path: frontend/package-lock.json
- name: Install
run: npm ci
- name: Lint
run: npm run lint
- name: Build
run: npm run build
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ Input: Output:
- **CLI + Web UI** — Use from terminal or browser
- **Cloud GPU Ready** — Runs on Google Colab (free T4) or any CUDA server

## Choose Your Run Mode

| Mode | Best For | Command |
| --- | --- | --- |
| Gradio local | Quick single-process demo | `python app.py` |
| Gradio remote TTS | Using Kaggle/Colab GPU TTS | `TTS_BASE_URL=https://your-ngrok-url.ngrok-free.dev python app_remote.py` |
| FastAPI backend | API server for the Next.js frontend | `python -m backend.main` |
| Next.js frontend | Full web UI | `cd frontend && npm run dev` |
| CLI | Script-to-WAV generation | `podforge --script examples/demo_drama.txt --output output.wav --tts-url http://localhost:8809` |

## Quick Start

### Deploy Your Own Space
Expand Down Expand Up @@ -153,6 +163,39 @@ cd frontend && npm install && npm run dev

Open `http://localhost:3000` → load a demo script → pick voices → click **生成播客**.

## Verification

Run the Python checks from the repository root:

```bash
pip install -e '.[dev]'
pytest -q
ruff check .
```

Run the frontend checks from `frontend/`:

```bash
npm ci
npm run lint
npm run build
```

### Dependency Audit Note

`npm audit` may report a moderate `postcss` advisory through Next.js. Do not run `npm audit fix --force` unless Next.js provides a safe non-breaking upgrade path; the current forced fix proposes a destructive downgrade to `next@9.3.3`. Treat this as a monitored dependency risk, not an immediate force-fix task.

### Manual UI Regression Check

This check does not require a live TTS server:

1. Start the frontend with `cd frontend && npm run dev`.
2. Open `http://localhost:3000`.
3. Click **加载示例**.
4. Confirm the editor footer and voice panel show parsed script content.
5. Select all script text and delete it.
6. Confirm the editor footer shows `0` roles and `0` lines, and the voice panel returns to its empty state.

## Script Format

```text
Expand Down
4 changes: 2 additions & 2 deletions backend/tts_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ class VoxCPMClient:
def __init__(
self,
base_url: str | None = None,
timeout: float = 120.0,
timeout: float | None = None,
):
self.base_url = (
base_url
or os.environ.get("PODFORGE_TTS_URL")
or "http://localhost:8809"
).rstrip("/")
self.timeout = timeout
self.timeout = timeout if timeout is not None else float(os.environ.get("PODFORGE_TTS_TIMEOUT", "300"))
self._client: httpx.AsyncClient | None = None

@property
Expand Down
4 changes: 2 additions & 2 deletions frontend/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ export default function Home() {
<div className="flex-1 min-h-0 overflow-auto mb-4">
<VoicePanel
presets={presets}
lines={parsedLines}
lines={lines}
voiceOverrides={voiceOverrides}
onOverride={(char, desc) =>
setVoiceOverrides((prev) => ({ ...prev, [char]: desc }))
Expand All @@ -209,7 +209,7 @@ export default function Home() {
{error}
</div>
)}
{generating ? (
{generating || (error && progress.current > 0) ? (
<ProgressTracker
current={progress.current}
total={progress.total}
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ dependencies = [
"fastapi>=0.115.0",
"uvicorn[standard]>=0.30.0",
"httpx>=0.27.0",
"socksio>=1.0.0",
"pydantic>=2.0",
"soundfile>=0.12.0",
"numpy>=1.26.0",
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
fastapi>=0.115.0
uvicorn[standard]>=0.30.0
httpx>=0.27.0
socksio>=1.0.0
pydantic>=2.0
soundfile>=0.12.0
numpy>=1.26.0
Loading