Metadata-Version: 2.4
Name: storylane-cli
Version: 0.1.0
Summary: Bulk-edit Storylane demos (text and AI voiceovers) from the command line via an Excel round-trip.
Project-URL: Homepage, https://github.com/vashek/slcli
Project-URL: Source, https://github.com/vashek/slcli
Project-URL: Issues, https://github.com/vashek/slcli/issues
Author-email: Václav Dvořák <vashek@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: automation,cli,demo,storylane,tts
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Office/Business
Classifier: Topic :: Utilities
Requires-Python: >=3.11
Requires-Dist: html-to-markdown>=2.29.0
Requires-Dist: keyring>=25.7.0
Requires-Dist: markdown-it-py>=4.0.0
Requires-Dist: mutagen>=1.47.0
Requires-Dist: openpyxl>=3.1.5
Requires-Dist: requests>=2.33.0
Requires-Dist: save-as-xlsx>=0.4.0
Requires-Dist: typer>=0.24.1
Description-Content-Type: text/markdown

# slcli — Storylane CLI

Bulk-edit [Storylane](https://www.storylane.io/) demos from the command line.

`slcli` lets you export a demo's text and AI voiceovers to an Excel spreadsheet, edit them offline, and push the changes back. It's significantly faster than clicking through every widget in the web UI when a demo has more than a handful of steps.

> **Unofficial.** This tool talks to Storylane's private API — the same one their web app uses — and is not endorsed by Storylane. Behaviour may break if their API changes.

## Install

```
pipx install storylane-cli
```

Or with `uv`:

```
uv tool install storylane-cli
```

The installed command is `slcli` (the PyPI package name is namespaced, but the binary stays short).

## Quick start

```
# 1. Log in once (the auth token is cached in your OS keyring).
slcli login

# 2. Export a demo's text content to an Excel file.
slcli export <project-id>

# 3. Open output/texts.xlsx, fill in the `new text` and/or `new voiceover`
#    columns on the rows you want to change. The other columns are protected.

# 4. Push the edits back.
slcli update
```

The project ID is the UUID in the demo's URL inside the Storylane web app — e.g. `https://app.storylane.io/project/78f4ee34-51fd-11f1-84bd-e41fd5b9abcb`.

## Commands

### `slcli login`

Prompts for your Storylane email and password, calls `POST /api/v1/login`, and stores the returned bearer token in your OS keyring (macOS Keychain, Windows Credential Manager, or Secret Service on Linux). All subsequent commands use the cached token silently.

```
slcli login                       # interactive
slcli login --email you@corp.com  # skip the email prompt
```

### `slcli logout`

Clears the cached token from the keyring.

### `slcli export <project-id>`

Fetches the project and writes `output/texts.xlsx` with one row per widget. Columns include the widget's HTML and markdown text, the page URL, the CTA label, and two empty editable columns: `new text` and `new voiceover`. The sheet is locked except for those two columns, so you can't accidentally damage the metadata columns that `slcli update` uses to identify widgets.

```
slcli export <project-id>
slcli export -x demo-edits.xlsx <project-id>   # custom output path
slcli export -o ./dump <project-id>            # also save the raw API JSON
```

### `slcli update`

Reads an edited xlsx and PATCHes each widget that has a non-empty `new text` or `new voiceover` cell. Markdown in `new text` is rendered to HTML before being sent. `new voiceover` cells trigger a TTS regeneration: the text goes to Storylane's TTS endpoint, the resulting mp3 is uploaded through their S3 bucket, and the widget's `audio_url`, `media_duration`, and `transcription_options.prompt` are updated together.

The project ID is read from the xlsx — no need to repeat it.

```
slcli update                                        # read output/texts.xlsx
slcli update -x demo-edits.xlsx                     # custom input
slcli update --yes                                  # skip the confirmation prompt
slcli update --voice-name brian                     # voice for widgets that have none
slcli update --voice-name matilda --voice-override  # force this voice everywhere
slcli update --voice-id nPczCjzI2devNBz1zQrb        # by id if you prefer
```

By default, when a widget already has an assigned voice, `slcli update` keeps it and `--voice-name` / `--voice-id` only apply as a fallback for widgets that have no voice yet. Pass `--voice-override` to force the CLI-provided voice everywhere.

### `slcli voices`

Lists the TTS voices available in your Storylane account (typically over 100). Useful for finding a `voice_id` or a `voice_name`.

```
slcli voices                       # pretty table on stdout
slcli voices -x voices.xlsx        # browsable spreadsheet, includes preview_url
slcli voices --all                 # include legacy/deprecated voices
```

## How it works

`slcli` authenticates by `POST /api/v1/login` and reuses the returned `auth_token` as a bearer token for the rest of Storylane's private API. The token lives in your OS keyring; `--key` overrides it for one-off invocations without touching the cache.

- **Export** pulls `GET /api/v1/company/projects/<id>` plus the `pages` and `themes` endpoints, then writes the xlsx using [save-as-xlsx](https://pypi.org/project/save-as-xlsx/).
- **Update** reads the xlsx with `openpyxl`, converts markdown with `markdown-it-py`, regenerates audio via `POST /api/v1/ai/transcriptions` → S3 upload → `POST /api/v1/uploads`, computes `media_duration` locally with `mutagen`, and PATCHes each widget at `/api/v1/company/projects/<pid>/flows/<fid>/widgets/<wid>`.
- **Voices** is a thin wrapper around `GET /api/v1/ai/transcriptions/voices`.

If a request returns 401, slcli prints a hint telling you to run `slcli logout` (which clears the stale token so the next command re-prompts via `slcli login`).

## License

MIT — see [LICENSE](LICENSE).
