OpenAI-backed TTS CLIs: aisay (short-form, like macOS `say`) and aitts (long-form → playlist).
Project description
aitts
OpenAI-backed text-to-speech CLIs for the terminal.
aisay— speak short text immediately, like macOSsaybut with OpenAI voices, tone instructions, and concurrency-safe playback.aitts— turn a long text file or web article into an mp3 playlist you can queue in any m3u player.
Platform: macOS only for now (
aisayplays viaafplay). Cross-platform playback is on the roadmap — see #1.-ooutput works everywhere.
Install
# Recommended: isolated install via pipx or uv
pipx install aisay
uv tool install aisay
# Or plain pip
pip install aisay
The PyPI distribution is
aisay(the headline command), but it ships bothaisayandaittsonto yourPATH. SetOPENAI_API_KEYin your environment or place it in a.envfile in the working directory.
aisay — short-form, plays immediately
aisay "Hello world"
aisay -v nova -i "calm, slow, British accent" "Welcome back."
echo "piped text works too" | aisay
aisay -o out.mp3 "Save instead of play"
aisay -s 1.2 "Faster, please"
aisay --list-voices
Flags
| Flag | Description |
|---|---|
-v, --voice |
Voice name (default: marin). See --list-voices. |
-i, --instructions |
Free-form tone/style prompt. gpt-4o-mini-tts and newer only. |
-s, --speed |
Speech speed, 0.25–4.0. |
-f, --format |
mp3, opus, aac, flac, wav, pcm. |
-o, --output |
Write to file instead of playing. |
-m, --model |
OpenAI TTS model (default: gpt-4o-mini-tts). |
--list-voices |
Print supported voice names and exit. |
--max-wait |
Drop the message if it waited longer than this many seconds in the playback queue (default: 10). |
--no-lock |
Skip the playback lock entirely. |
-q, --quiet |
Suppress informational messages on stderr. |
Concurrency
When many callers invoke aisay at once (say, twenty claude sessions each running a "speak the response" hook), playback is serialized via a per-user flock on $TMPDIR/aisay-<uid>.lock. Audio generation still happens in parallel — only afplay is serialized.
If a message has been waiting in the queue longer than --max-wait seconds by the time its turn comes, it's silently dropped (with a one-line note to stderr, unless --quiet) rather than played stale. Tune higher to hear more, lower to drop sooner, or pass --no-lock to bypass.
aitts — long-form, writes a playlist
aitts samples/english-sonnet.txt
aitts https://example.com/long-article --play
aitts https://example.com/long-article -v nova -i "audiobook narration"
curl -s https://example.com/raw.txt | aitts -
aitts long.txt --merge # also produces a single merged.mp3 via ffmpeg
Output lands in ${XDG_DATA_HOME:-~/.local/share}/aitts/<slug>/<timestamp>/, containing one partNN.mp3 per chunk plus a playlist.m3u. Override the base directory with AITTS_DATA_DIR=/some/path.
URLs go through trafilatura for clean article extraction, with a browser User-Agent fallback for sites that reject default library UAs. Paywalled or heavily JS-rendered pages (NYT, many Substack premium posts) will fail with a clean error — the workaround is to save the article text yourself and pipe it in:
pbpaste | aitts - # from clipboard
cat saved-article.txt | aitts -
Flags
| Flag | Description |
|---|---|
-v, --voice |
Voice name (default: marin). |
-m, --model |
OpenAI TTS model (default: gpt-4o-mini-tts). |
-i, --instructions |
Tone/style prompt. gpt-4o-mini-tts and newer only. |
-s, --speed |
Speech speed, 0.25–4.0. |
--concurrency |
Parallel chunk generation (default: 4). |
--merge |
Concatenate parts into merged.mp3 (needs ffmpeg on PATH). |
--play |
Open the playlist in VLC (PATH-first, then VLC.app on macOS). |
Pass - as the input to read text from stdin.
Environment
| Variable | Purpose |
|---|---|
OPENAI_API_KEY |
Required. Read from env or a .env file in the working directory. |
AITTS_DATA_DIR |
Override the output base directory for aitts. |
XDG_DATA_HOME |
Honored when AITTS_DATA_DIR is unset. |
Develop
git clone https://github.com/orlenko/aitts
cd aitts
uv sync
uv run aisay "hello"
uv run pytest
uv run ruff check .
To install your local checkout as the global tool:
uv tool install . --reinstall --no-cache
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file aisay-0.4.1.tar.gz.
File metadata
- Download URL: aisay-0.4.1.tar.gz
- Upload date:
- Size: 13.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9395b476bbe7f9ef6a05234fcc1ecb44d376ef7c0fea597c16ad7f6a7bee4a45
|
|
| MD5 |
5b159caad99e24cfdc1a3e11afd316ce
|
|
| BLAKE2b-256 |
ddc9e916ad2281667149c9bab8d3f195f78e29070f7bb1871003952f07268a2d
|
Provenance
The following attestation bundles were made for aisay-0.4.1.tar.gz:
Publisher:
publish.yml on orlenko/aitts
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aisay-0.4.1.tar.gz -
Subject digest:
9395b476bbe7f9ef6a05234fcc1ecb44d376ef7c0fea597c16ad7f6a7bee4a45 - Sigstore transparency entry: 1624514937
- Sigstore integration time:
-
Permalink:
orlenko/aitts@dd80c8df0bdd3abd41089b01d13d298b38552ba7 -
Branch / Tag:
refs/tags/v0.4.1 - Owner: https://github.com/orlenko
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@dd80c8df0bdd3abd41089b01d13d298b38552ba7 -
Trigger Event:
push
-
Statement type:
File details
Details for the file aisay-0.4.1-py3-none-any.whl.
File metadata
- Download URL: aisay-0.4.1-py3-none-any.whl
- Upload date:
- Size: 13.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
40b560f0f843d54c283180c98ffdab78e1ba445a858706d439234e6aef82c087
|
|
| MD5 |
4fd4a7d8aadec02781f641aec3bf8488
|
|
| BLAKE2b-256 |
ad0856b76466efecbef892c27859041e04b159686bb63dd8646585babfc2c9e2
|
Provenance
The following attestation bundles were made for aisay-0.4.1-py3-none-any.whl:
Publisher:
publish.yml on orlenko/aitts
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aisay-0.4.1-py3-none-any.whl -
Subject digest:
40b560f0f843d54c283180c98ffdab78e1ba445a858706d439234e6aef82c087 - Sigstore transparency entry: 1624514948
- Sigstore integration time:
-
Permalink:
orlenko/aitts@dd80c8df0bdd3abd41089b01d13d298b38552ba7 -
Branch / Tag:
refs/tags/v0.4.1 - Owner: https://github.com/orlenko
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@dd80c8df0bdd3abd41089b01d13d298b38552ba7 -
Trigger Event:
push
-
Statement type: