Metadata-Version: 2.4
Name: flowspeech-cli
Version: 0.2.1
Summary: Command-line client for FlowSpeech public API v1 text to speech.
Project-URL: Homepage, https://flowspeech.io/
Project-URL: Documentation, https://flowspeech.io/docs/public-api
Project-URL: Repository, https://github.com/waeckerlinfederowicz66-sketch/flowspeech-cli
Author-email: FlowSpeech <support@flowspeech.io>
License-Expression: MIT
License-File: LICENSE
Keywords: flowspeech,text-to-speech,tts,voiceover
Requires-Python: >=3.9
Requires-Dist: httpx<1,>=0.27
Provides-Extra: test
Requires-Dist: pytest<9,>=8; extra == 'test'
Description-Content-Type: text/markdown

# flowspeech-cli

Command-line client for [FlowSpeech](https://flowspeech.io/) public API v1.

```bash
pip install flowspeech-cli==0.2.1
```

Set FLOWSPEECH_API_KEY in your server or local terminal environment. The CLI never
stores or prints the key and does not accept it as a command-line argument.

```bash
flowspeech doctor
flowspeech quota
flowspeech voices
flowspeech synthesize "Welcome.[pause:2s]Let's begin." --voice Kore --out speech.wav
flowspeech synthesize --file script.txt --out narration.wav --timeout 600
```

The CLI prints request_id to stderr before generation and job_id on completion.
For recovery, pass --request-id with the original UUID and unchanged input, or set
FLOWSPEECH_REQUEST_ID. Never use a new UUID just because a response was lost.

```bash
flowspeech status --request-id UUID_FROM_PREVIOUS_RUN
flowspeech status --job-id JOB_UUID
flowspeech download --job-id JOB_UUID --out recovered.wav
flowspeech cancel --job-id JOB_UUID
```

Replace the ID placeholders with actual UUIDs. download retrieves an already
successful job and never creates a recording.

## Multiple speakers

Create a UTF-8 JSON file containing an ordered array of turns:

```json
[
  {"speaker": "Alice", "text": "Welcome.", "voiceName": "Kore"},
  {"speaker": "Bob", "text": "Thank you.", "voiceName": "Puck"}
]
```

```bash
flowspeech synthesize --segments conversation.json --out conversation.wav
```

Use exactly one of inline text, --file or --segments. --api-url remains available
but must be an HTTPS origin followed by /api/v1/tts/jobs. Legacy endpoints are
rejected. Error exit status is 1; argument errors use argparse status 2.

The Python synthesize(...) function preserves the original result fields and
adds job_id, request_id, result_url and usage. audio_base64 is encoded locally
from downloaded WAV, not returned by the v1 API. quota is None; query quota
separately. Existing audio helper functions remain available.

## API v1 behavior

An active paid Basic, Pro or Scale subscription and a FlowSpeech API key are
required; trials do not include API generation. Keep FLOWSPEECH_API_KEY server-side.
See [public API documentation](https://flowspeech.io/docs/public-api).

Generation submits POST /api/v1/tts/jobs with {"input": ...} and one UUID
Idempotency-Key, waits through queued/running/merging/settling, then downloads
result.url after success. A 200 response alone does not mean generation succeeded.
Single input uses mode/text/voiceName. Multi input uses mode/segments, where each
turn contains speaker, text and voiceName. Model, source, speed and format options
are unsupported and must not be sent.

The default total deadline is 300 seconds, polling is at least five seconds, and
longer Retry-After values (seconds or HTTP dates) are honored. Transient transport
failures, 429, and explicitly retryable 5xx errors get at most two retries.
Create retries preserve the UUID and exact body. A deadline stops local waiting,
not the server job; use the saved IDs for recovery or explicit cancellation.

A deadline ends local waiting, not the remote job. A transport already in progress
may finish later; its worker performs no follow-up request. Each client permits
one active HTTP request, and this package permits at most eight HTTP workers per
process. An occupied client raises `request_in_progress`; exhausted capacity
raises `client_busy`, without queueing or sending another request. Do not use one
client concurrently, and recover the original request ID after a timeout instead
of submitting a new recording. Caller-owned HTTP clients are never closed.
Closing this client rejects future work; cleanup of owned HTTP clients is
deferred until any active request exits, rather than blocking local shutdown.

If reading a response body fails, known status codes and Retry-After are retained.
Authentication failures are not retried. Interrupted successful or server-error
responses use bounded transport retries with the original UUID and body.
Errors use a local HTTP-status message rather than echoing upstream text. Error
codes and trace IDs are bounded tokens, filtered for credentials and URLs;
request/job IDs remain available for recovery.

Errors expose status_code, code, retryable, trace_id, request_id, job_id and
result_url where available. Failed and cancelled jobs raise errors. Deleted
success results are reported as result_deleted, not as empty audio.

Audio is a separately downloaded, validated WAV (24000 Hz, mono, 16 bit).
Validation checks the entire RIFF size, chunk bounds/padding, exactly one PCM
format/data pair, byte rate 48000, block alignment 2 and nonempty complete samples.
Malformed audio raises invalid_audio with request/job IDs and the existing result
URL preserved; it does not trigger another recording.
Downloads use HTTPS without API Authorization, cookies, or default client
credentials, including across redirects. API redirects are rejected. Downloads
are bounded to 64 MiB by default. A download retry never submits another recording.
Anyone holding a result URL can access the recording; treat links as sensitive.

GET /api/v1/quota returns quota.remaining/reserved/available plus subscription
and limits. GET /api/v1/voices
returns voices. Recovery uses GET /api/v1/tts/jobs?requestId=UUID; status and cancel
use /api/v1/tts/jobs/{UUID} and /api/v1/tts/jobs/{UUID}/cancel.

## Development

```bash
python -m pip install -e ".[test]" build
python -m pytest -q
python -m build
```

See PROVENANCE.md for verified 0.1.0 wheel provenance and RELEASE.md for the
coordinator's release steps. No upstream source repository URL is assumed.

