Metadata-Version: 2.4
Name: sakuraspeech
Version: 0.1.0
Summary: Official Python SDK for SakuraSpeech TTS API
Project-URL: Homepage, https://sakuraspeech.jp
Project-URL: Documentation, https://sakuraspeech.jp/developer
Author-email: "CrystalMethod Co., Ltd." <info@crystal-method.com>
License-Expression: MIT
License-File: LICENSE
Keywords: ai,sakuraspeech,text-to-speech,tts,voice
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx>=0.25.0
Description-Content-Type: text/markdown

# SakuraSpeech Python SDK

Official Python SDK for the [SakuraSpeech](https://sakuraspeech.jp) TTS API.

## Installation

```bash
pip install sakuraspeech
```

## Quick Start

```python
from sakuraspeech import SakuraSpeech

client = SakuraSpeech(api_key="sk_live_...")

# Synthesize text to speech
audio = client.tts.synthesize(text="こんにちは、世界！")
audio.save("output.mp3")
print(f"Duration: {audio.duration}s, Size: {audio.size} bytes")
```

## Configuration

```python
client = SakuraSpeech(
    api_key="sk_live_...",
    base_url="https://api.sakuraspeech.jp",  # default
    timeout=60.0,            # seconds
    max_retries=2,           # retry on 429/503/network errors
    default_voice="kanon-sakura",
    default_format="mp3",
)
```

## TTS (Text-to-Speech)

### Synthesize

```python
audio = client.tts.synthesize(
    text="こんにちは",
    voice="kanon-sakura",
    emotion="happy",
    speed=1.0,        # 0.5–2.0
    pitch=1.0,        # 0.5–2.0
    volume=1.0,       # 0.0–1.0
    format="mp3",     # mp3, wav, ogg
    sample_rate=44100, # 44100 のみ対応
)
audio.save("output.mp3")
print(audio.content)         # bytes
print(audio.duration)        # float (seconds)
print(audio.characters_used) # int
print(audio.request_id)      # str
```

### Batch Synthesis

```python
# Create a batch job
job = client.tts.batch_create(
    items=[
        {"id": "1", "text": "おはようございます"},
        {"id": "2", "text": "こんばんは", "voice": "kanon-sakura", "emotion": "happy"},
    ],
    format="mp3",
    webhook_url="https://example.com/webhook",
)
print(f"Batch ID: {job.batch_id}, Status: {job.status}")

# Poll until complete
completed = client.tts.batch_poll(job.batch_id, interval=2.0, timeout=300.0)
for result in completed.results:
    print(f"  {result.id}: {result.status}")
```

## Voices

### List Preset Voices

```python
voice_list = client.voices.list()
for v in voice_list.voices:
    print(f"{v.id}: {v.name} ({v.gender}) - emotions: {v.emotions}")
```

### Preview a Voice

```python
preview = client.voices.preview("kanon-sakura")
preview.save("preview.wav")
```

### Voice Cloning

```python
# Clone from a file path
result = client.voices.clone(
    audio="reference.wav",
    name="My Custom Voice",
    description="A friendly female voice",
)
print(f"Custom voice ID: {result.custom_voice_id}")

# Use the cloned voice
audio = client.tts.synthesize(
    text="カスタムボイスのテスト",
    voice=f"custom:{result.custom_voice_id}",
)
```

### Manage Custom Voices

```python
# List custom voices
custom_voices = client.voices.custom_list()

# Get specific custom voice
voice = client.voices.custom_get("voice-id")

# Delete a custom voice
client.voices.custom_delete("voice-id")
```

## User Dictionary

```python
# List entries
entries = client.dictionary.list()

# Add entries
result = client.dictionary.add([
    {"word": "東京", "reading": "トウキョウ"},
    {"word": "SakuraSpeech", "reading": "サクラスピーチ", "accent": "4"},
])
print(f"Added {result.added} entries")

# Delete entry
client.dictionary.delete("entry-id")
```

## Usage

```python
usage = client.usage.get()
print(f"Plan: {usage.plan}")
print(f"Characters: {usage.characters.used}/{usage.characters.limit}")
print(f"Requests today: {usage.requests.today}")
```

## Webhooks

```python
# Create a webhook
webhook = client.webhooks.create(
    url="https://example.com/webhook",
    events=["batch.completed"],
)
print(f"Webhook ID: {webhook.id}, Secret: {webhook.secret}")

# List webhooks
webhooks = client.webhooks.list()

# Delete a webhook
client.webhooks.delete("webhook-id")
```

## Async Usage

```python
import asyncio
from sakuraspeech import AsyncSakuraSpeech

async def main():
    async with AsyncSakuraSpeech(api_key="sk_live_...") as client:
        audio = await client.tts.synthesize(text="非同期でこんにちは")
        audio.save("async_output.mp3")

        # Parallel requests
        results = await asyncio.gather(
            client.tts.synthesize(text="文章1"),
            client.tts.synthesize(text="文章2"),
            client.tts.synthesize(text="文章3"),
        )
        for i, r in enumerate(results):
            r.save(f"parallel_{i}.mp3")

asyncio.run(main())
```

## Error Handling

```python
from sakuraspeech import (
    SakuraSpeech,
    AuthenticationError,
    RateLimitError,
    NotFoundError,
    APIError,
)

client = SakuraSpeech(api_key="sk_live_...")

try:
    audio = client.tts.synthesize(text="テスト")
except AuthenticationError:
    print("Invalid API key")
except RateLimitError as e:
    print(f"Rate limited. Retry after {e.retry_after_seconds}s")
except NotFoundError:
    print("Resource not found")
except APIError as e:
    print(f"API error: {e.code} ({e.status_code}): {e}")
```

### Exception Hierarchy

```
SakuraSpeechError (base)
├── APIError (4xx/5xx, has code, status_code, request_id)
│   ├── AuthenticationError (401)
│   ├── PermissionError (403)
│   ├── NotFoundError (404)
│   ├── RateLimitError (429, retry_after_seconds)
│   ├── InternalServerError (500)
│   └── ServiceUnavailableError (503)
├── ConnectionError
└── TimeoutError
```

## Requirements

- Python >= 3.9
- httpx >= 0.25.0

## License

MIT
