tonys-cli

module
v0.0.0-...-1e64fa5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 30, 2026 License: MIT

README

tonys

CI Release Go Reference Go Report Card

An agent-friendly command-line wrapper around the TonieCloud API for creative tonies. Send audio — local files, stdin, or YouTube links — to your kids' tonies from scripts and bots, with automatic format conversion and loudness normalization.

It's a Go port of the Python library alexhartm/tonie_api, extended with a real token-caching auth layer, structured errors, full chapter editing, audio conversion/normalization, a YouTube importer, and a machine-readable command schema.

[!NOTE] Unofficial project. Not affiliated with, endorsed by, or supported by Boxine GmbH / tonies. Use your own account, at your own risk. See Disclaimer.

Why "agent-friendly"?

  • Non-interactive auth via env vars, with a cached & auto-refreshed token.
  • --json everywhere — stable JSON on stdout, errors as JSON on stderr.
  • Meaningful exit codes (0 ok, 1 error, 2 usage, 3 auth, 4 not-found).
  • tonys schema emits a JSON description of every command/flag for tools to introspect — the authoritative, always-current command reference.
  • tonys raw can hit any endpoint, so nothing in the API is out of reach.
  • Tonies and households can be referenced by name or id.

Install

Homebrew (macOS / Linux)
brew install bernardo-cs/tap/tonys
Linux packages (.deb / .rpm / .apk)

Each release attaches native packages for amd64 and arm64. Download the one for your distro from the latest release and install it:

# Debian / Ubuntu
sudo apt install ./tonys_0.1.0_linux_amd64.deb

# Fedora / RHEL / openSUSE
sudo dnf install ./tonys_0.1.0_linux_amd64.rpm     # or: sudo rpm -i ...

# Alpine
sudo apk add --allow-untrusted ./tonys_0.1.0_linux_amd64.apk

The binary installs to /usr/bin/tonys. ffmpeg/yt-dlp are listed as optional (recommended/suggested) dependencies, so installation never pulls them in by force.

Prebuilt binary

Download the archive for your OS/arch from the latest release, verify it against checksums.txt, then put tonys on your PATH:

# macOS arm64 example — adjust the version/OS/arch to taste
VER=0.1.0
curl -fsSLO https://github.com/bernardo-cs/tonys-cli/releases/download/v$VER/tonys_${VER}_darwin_arm64.tar.gz
curl -fsSLO https://github.com/bernardo-cs/tonys-cli/releases/download/v$VER/checksums.txt
shasum -a 256 -c checksums.txt --ignore-missing      # verify
tar xzf tonys_${VER}_darwin_arm64.tar.gz
sudo mv tonys /usr/local/bin/
With Go
go install github.com/bernardo-cs/tonys-cli/cmd/tonys@latest
From source
git clone https://github.com/bernardo-cs/tonys-cli
cd tonys-cli
make build        # produces ./tonys
# or: make install   # installs to $GOBIN
Optional external tools

The binary is self-contained Go, but two optional external tools are NOT bundled — they must be on PATH for the features that use them:

  • ffmpeg — audio conversion & loudness normalization (brew install ffmpeg, apt-get install ffmpeg, …)
  • yt-dlp — the yt YouTube importer (brew install yt-dlp, pipx install yt-dlp, …)

Plain uploads of already-accepted formats and every API command work without either. Run tonys doctor to check what's present (it prints OS-specific install hints); for provisioning a box, gate on tonys doctor --strict:

tonys doctor            # human/JSON report of deps, creds, writable paths
tonys doctor --online   # also verify login + API reachability
tonys doctor --strict   # exit non-zero unless every check is ok

Override the discovered binaries with $TONYS_FFMPEG / $TONYS_YTDLP.

Authentication

The recommended path for bots is environment variables; the token is exchanged once and cached (mode 0600) under ~/.cache/tonys/token.json, then auto-refreshed.

export TONIE_USERNAME="you@example.com"
export TONIE_PASSWORD="secret"

tonys auth login      # optional: pre-warm & verify the cache
tonys auth status     # show cached token validity
tonys auth logout     # clear the cache (add --all to clear every account)

Credential precedence: --username/--password flags → env → optional ~/.config/tonys/config.json. Override endpoints with --api-url / --token-url or $TONIE_API_URL / $TONIE_TOKEN_URL. Passwords are never logged or printed, and only ever sent to the OpenID token endpoint over HTTPS — see SECURITY.md.

Quick start

# Tables by default; add --json for machine output.
tonys household list
tonys tonie list
tonys tonie get "Erna-Tonie"

# Upload an audio file as a new chapter (the main command):
tonys upload "Erna-Tonie" bedtime.mp3
tonys upload "Erna-Tonie" story.m4a --title "Bedtime story" --wait
tonys upload "Erna-Tonie" intro.mp3 --position beginning

# Pipe from stdin:
cat song.mp3 | tonys upload "Erna-Tonie" - --title "Song"

Commands

tonys schema is the authoritative, machine-readable reference for every command and flag. A human summary:

Command Purpose
me the logged-in user (GET /me)
config backend config & upload limits (GET /config)
auth login|status|logout manage the token cache
household list list households
tonie list|get|rename list / inspect / rename creative tonies
upload <tonie> <file> upload a file (or -) as a new chapter
chapter list|add|add-existing|rm|rename|move|sort|clear edit chapters
yt <tonie> <url> import a YouTube video/playlist (needs yt-dlp)
loudness measure|show|measure-tonie loudness tooling (see below)
file create low-level: request a presigned upload slot
raw <METHOD> <path> call any endpoint directly
doctor check dependencies, credentials and writable paths
schema machine-readable description of all commands
version print version

Run tonys help <command> for full per-command help.

Global flags

Available on every command:

Flag Meaning
--json shorthand for --output json
-o, --output table|json|jsonl output format (default table)
--username, --password credentials (override env)
--api-url, --token-url endpoint overrides
--no-cache ignore the token cache for this call
--timeout 3m per-request timeout
--quiet suppress stderr status lines
--verbose extra stderr diagnostics
Chapters

Chapters can be referenced by id, 1-based number, or exact title:

tonys chapter list "Erna-Tonie"
tonys chapter rename "Erna-Tonie" 3 "New title"
tonys chapter move "Erna-Tonie" "Old intro" 1
tonys chapter rm "Erna-Tonie" 4 5            # remove chapters 4 and 5
tonys chapter sort "Erna-Tonie" 3 1 2        # reorder
tonys chapter clear "Erna-Tonie"

New chapters are added to the end by default. Pass --position beginning on upload, chapter add, chapter add-existing, or yt to put new audio at the front instead.

file create plus chapter add-existing is the low-level split of an upload: file create asks TonieCloud for a presigned upload slot (returning a fileId), and chapter add-existing <tonie> <fileId> attaches an already-uploaded file as a chapter. Most users just want upload, which does both.

Audio conversion

TonieCloud accepts aac aif aiff flac mp3 m4a m4b wav oga ogg opus wma. Anything else is auto-converted with ffmpeg before upload. Controls (on upload, chapter add, yt):

--convert auto|always|never          # default auto (convert only unsupported inputs)
--format mp3|opus|ogg|m4a|flac|wav   # target codec when converting (default mp3)
--bitrate 128k
tonys upload "Erna-Tonie" voice-memo.webm   # webm → mp3 automatically

--format only takes effect when a conversion actually happens. With --convert auto (the default), an already-accepted input is uploaded as-is and keeps its original format; with --normalize on, a re-encode is required, so the file is encoded to --format (default mp3).

Loudness normalization

Normalize perceived volume (EBU R128 two-pass loudnorm) so chapters don't jump between quiet and loud. It runs in the same ffmpeg pass as conversion and is independent of the input bitrate/codec.

--normalize off|target|match   # default off
--target-lufs -16              # integrated loudness target
  • target brings the file to a fixed loudness (default −16 LUFS).
  • match brings the file to the average loudness of the chapters already on the tonie (as known to the local loudness ledger; see below).
tonys upload "Erna-Tonie" loud.wav  --normalize target --target-lufs -16
tonys upload "Erna-Tonie" quiet.mp3 --normalize match
The loudness ledger and match

TonieCloud does not offer a supported way to download a chapter's audio (the official API returns only metadata, and neither the website nor the iOS app expose downloads). So tonys keeps a local loudness ledger at ~/.local/share/tonys/loudness.json: every file you upload through tonys is measured and recorded, and --normalize match averages those records. When a tonie has no known chapter loudness yet, match falls back to --target-lufs. If you upload everything through tonys, your whole tonie stays at a consistent level.

tonys loudness measure song.mp3        # measure a local file's LUFS/true-peak/LRA
tonys loudness show "Erna-Tonie"       # what the ledger already knows for a tonie
tonys loudness measure-tonie "Erna-Tonie"

loudness measure-tonie additionally makes a best-effort attempt to fetch and measure the tonie's existing cloud chapters through an undocumented endpoint, caching any it can. This is not guaranteed: tonies-published content can never be downloaded, and many accounts/plans don't expose the endpoint at all — in those cases chapters are reported as not downloadable and the local ledger remains the reliable source for match.

YouTube import

tonys yt "Erna-Tonie" "https://youtu.be/XXXX"
tonys yt "Erna-Tonie" "https://youtube.com/playlist?list=YYYY" \
    --normalize target --limit 10 --title-prefix "Mix: " --wait

Each item is downloaded (yt-dlp), run through the same convert/normalize pipeline, and added as a chapter. Useful flags: --limit N, --reverse, --title-prefix, --position, --wait / --wait-timeout, and --continue-on-error (default on) which keeps going if one item fails and reports failures in the summary.

YouTube radio URLs such as watch?v=...&list=RD...&start_radio=1 can expand to large auto-generated playlists. tonys yt warns when it sees one and asks whether to import only the current video or the whole radio playlist. Choosing the current video cleans the URL before download; pass --allow-radio to skip the prompt when you intended to import the radio playlist.

Using it from an agent / script

export TONIE_USERNAME=... TONIE_PASSWORD=...

# Discover the interface:
tonys schema | jq .

# Machine output + robust error handling:
if out=$(tonys --json tonie get "Erna-Tonie"); then
    echo "$out" | jq '.chapters | length'
else
    code=$?            # 2 = usage, 3 = auth, 4 = not found, 1 = other
fi

Successful output goes to stdout as JSON; errors go to stderr as {"error": "...", "class": "...", "status": N}. stdout stays clean for parsing.

Exit code Class Meaning
0 success
1 error generic / unexpected error
2 usage bad flags or arguments (incl. ambiguous references)
3 auth authentication failure
4 not_found tonie / household / chapter not found

Environment variables

Var Meaning
TONIE_USERNAME, TONIE_PASSWORD credentials (aliases: TONIES_*, TONIE_CLOUD_*)
TONYS_OUTPUT default output format (table/json/jsonl)
TONYS_CONFIG, TONYS_CACHE, TONYS_LOUDNESS_DB explicit state-file locations
TONIE_API_URL, TONIE_TOKEN_URL endpoint overrides
TONYS_FFMPEG, TONYS_YTDLP binary overrides (aliases: TONIE_FFMPEG, TONIE_YTDLP)
XDG_CONFIG_HOME, XDG_CACHE_HOME, XDG_DATA_HOME base dirs for config / token cache / ledger

Development

make build    # build ./tonys
make test     # go test ./...
make vet      # go vet ./...
make fmt      # gofmt -w .
make cover    # coverage summary

See CONTRIBUTING.md for conventions, and SECURITY.md for the security model and how to report issues.

Disclaimer

tonys is an unofficial tool. It is not affiliated with, authorized, endorsed, or sponsored by Boxine GmbH or tonies. "tonies", "TonieCloud", "Toniebox", and "Creative-Tonie" are trademarks of their respective owners; they are used here only to describe what this tool interoperates with.

The tool talks to TonieCloud on your behalf using your own credentials. It relies on undocumented endpoints that can change or break at any time. Use it at your own risk and in accordance with tonies' Terms of Service. The software is provided "as is", without warranty of any kind — see LICENSE.

License

MIT © 2026 Bernardo Simoes. Ported from the MIT-licensed alexhartm/tonie_api.

Directories

Path Synopsis
cmd
tonys command
Command tonys is an agent-friendly CLI for the TonieCloud API: it sends audio (files, stdin, or YouTube links) to creative tonies, with automatic format conversion and loudness normalization.
Command tonys is an agent-friendly CLI for the TonieCloud API: it sends audio (files, stdin, or YouTube links) to creative tonies, with automatic format conversion and loudness normalization.
internal
audio
Package audio converts arbitrary audio files into a format accepted by TonieCloud, using ffmpeg when available.
Package audio converts arbitrary audio files into a format accepted by TonieCloud, using ffmpeg when available.
cli
Package cli implements the `tonys` command-line interface: an agent-friendly wrapper around the TonieCloud API (package toniecloud).
Package cli implements the `tonys` command-line interface: an agent-friendly wrapper around the TonieCloud API (package toniecloud).
toniecloud
Package toniecloud is a Go client for the TonieCloud REST API (https://api.tonie.cloud/v2).
Package toniecloud is a Go client for the TonieCloud REST API (https://api.tonie.cloud/v2).
ytdl
Package ytdl wraps the yt-dlp (or youtube-dl) binary to list and download audio from videos and playlists.
Package ytdl wraps the yt-dlp (or youtube-dl) binary to list and download audio from videos and playlists.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL