"""soundpiece.py — a small client for the Soundpiece API.
Copy this file into your project. Needs: pip install requests"""
import os
import time
import uuid
import requests
API_URL = "https://api.soundpiece.co/v1"
API_KEY = os.environ["SOUNDPIECE_API_KEY"]
class SoundpieceError(Exception):
"""A request was rejected, or an operation failed."""
def run(endpoint, body, idempotency_key=None, timeout=600):
"""Submit a request, wait for it to finish, and return the finished result."""
body = {**body, "idempotency_key": idempotency_key or str(uuid.uuid4())}
response = send("PUT", endpoint, json=body)
name = list(response)[0] # the result's name, e.g. "ducked_audio"
result = response[name]
give_up_at = time.monotonic() + timeout
while result["status"] == "processing":
if time.monotonic() > give_up_at:
raise TimeoutError(f"{endpoint} {result['id']} is still processing")
result = send("GET", endpoint, params={"id": result["id"], "wait": "true"})[name]
if result["status"] == "failed":
raise SoundpieceError(f"{result['error']['code']}: {result['error']['message']}")
return result
def download(url, path):
"""Save a result's audio to a file."""
with requests.get(url, stream=True, timeout=60) as response:
response.raise_for_status()
with open(path, "wb") as file:
for chunk in response.iter_content(chunk_size=1024 * 1024):
file.write(chunk)
def send(method, endpoint, **options):
"""Make one API call, retrying when that is safe: network errors, 429 and 5xx."""
for attempt in range(5):
wait = 2 ** attempt
try:
response = requests.request(
method,
f"{API_URL}/{endpoint}",
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=30, # longer than the 20-second long poll
**options,
)
except (requests.ConnectionError, requests.Timeout):
time.sleep(wait)
continue
if response.status_code == 429 or response.status_code >= 500:
time.sleep(float(response.headers.get("Retry-After", 0)) or wait)
continue
if response.status_code >= 400:
raise SoundpieceError(f"HTTP {response.status_code}: {response.json()['detail']}")
return response.json()
raise SoundpieceError(f"{method} {endpoint} kept failing; try again later")