Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,31 @@ pip install git+https://github.com/flowese/UdioWrapper.git
2. Once registered, open your browser's inspector:
- In Chrome: `Ctrl+Shift+I` or `F12` on Windows, `Cmd+Option+I` on Mac.
3. Go to the `Application` tab.
4. On the left panel, locate and click on `Cookies`, then select the Ideogram website.
4. On the left panel, locate and click on `Cookies`, then select the Udio website.
5. Find the cookie named `sb-api-auth-token`.
6. Click on `sb-api-auth-token` and copy the value in the `Value` field.

![Udio Wrapper](screen_cookies.jpeg)

### hCaptcha, 500 responses, and stale sessions

Udio can require a fresh browser session or an hCaptcha/anti-bot check before accepting
requests to `https://www.udio.com/api/generate-proxy`. When that happens, Udio may
return `500 Internal Server Error` even though the problem is authentication/session
state rather than a normal server outage.

If generation fails with a 401, 403, 429, or 500 response:

1. Open [Udio](https://www.udio.com/) in your browser.
2. Sign in and complete any hCaptcha or anti-bot challenge shown by Udio.
3. Refresh the page.
4. Copy either the refreshed `sb-api-auth-token` cookie value or the full `Cookie`
request header from your browser's network inspector.
5. Recreate `UdioWrapper` with the refreshed value.

The wrapper does not bypass or automatically solve hCaptcha. It reuses the valid
browser session that you provide after completing Udio's challenge yourself.

### Usage

To use `udio_wrapper`, import the `UdioWrapper` class and provide the necessary parameters.
Expand All @@ -75,6 +94,14 @@ auth_token = "your-auth-token-here" # Replace this with your actual authenticat
udio_wrapper = UdioWrapper(auth_token)
```

If Udio rejects token-only requests, pass the full browser Cookie header instead
with or without the leading `Cookie:` label:

```python
cookies = "sb-api-auth-token=your-auth-token-here; other-cookie=value"
udio_wrapper = UdioWrapper(cookies=cookies)
```

1. Creating a Short Song
You can specify the prompt, seed, custom lyrics.
```python
Expand Down
64 changes: 57 additions & 7 deletions udio_wrapper/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,81 @@
import os
import time


class UdioWrapperError(Exception):
"""Raised when Udio returns an error response."""


class UdioWrapper:
API_BASE_URL = "https://www.udio.com/api"

def __init__(self, auth_token):
def __init__(self, auth_token=None, cookies=None, timeout=30):
if not auth_token and not cookies:
raise ValueError("auth_token or cookies is required")

self.auth_token = auth_token
self.cookies = cookies
self.timeout = timeout
self.session = requests.Session()
self.all_track_ids = []

if isinstance(cookies, dict):
self.session.cookies.update(cookies)

def _get_cookie_header(self):
if isinstance(self.cookies, str):
cookie_header = self.cookies.strip()
if cookie_header.lower().startswith("cookie:"):
return cookie_header.split(":", 1)[1].strip()
return cookie_header

if isinstance(self.cookies, dict):
cookie_header = "; ".join(f"{key}={value}" for key, value in self.cookies.items())
if "sb-api-auth-token=" in cookie_header or not self.auth_token:
return cookie_header
return f"{cookie_header}; sb-api-auth-token={self.auth_token}"

return f"sb-api-auth-token={self.auth_token}"

def _format_error_message(self, response, method, url):
body = response.text.strip()
if len(body) > 1000:
body = f"{body[:1000]}..."

message = (
f"Error making {method} request to {url}: "
f"{response.status_code} {response.reason}"
)
if body:
message = f"{message}\nResponse body: {body}"

if url.endswith("/generate-proxy") and response.status_code in (401, 403, 429, 500):
message = (
f"{message}\n\n"
"Udio may require a fresh browser session or an hCaptcha/anti-bot check. "
"Open udio.com in your browser, complete any challenge, then pass either "
"the refreshed sb-api-auth-token or the full Cookie header to UdioWrapper."
)
return message

def make_request(self, url, method, data=None, headers=None):
try:
if method == 'POST':
response = requests.post(url, headers=headers, json=data)
response = self.session.post(url, headers=headers, json=data, timeout=self.timeout)
else:
response = requests.get(url, headers=headers)
response = self.session.get(url, headers=headers, timeout=self.timeout)
response.raise_for_status()
return response
except requests.exceptions.HTTPError as e:
raise UdioWrapperError(self._format_error_message(e.response, method, url)) from e
except requests.exceptions.RequestException as e:
print(f"Error making {method} request to {url}: {e}")
return None
raise UdioWrapperError(f"Error making {method} request to {url}: {e}") from e

def get_headers(self, get_request=False):
headers = {
"Accept": "application/json, text/plain, */*" if get_request else "application/json",
"Content-Type": "application/json",
"Cookie": f"; sb-api-auth-token={self.auth_token}",
"Cookie": self._get_cookie_header(),
"Origin": "https://www.udio.com",
"Referer": "https://www.udio.com/my-creations",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
Expand Down Expand Up @@ -219,4 +270,3 @@ def download_song(self, song_url, song_title, folder="downloaded_songs"):
print(f"Downloaded {song_title} with url {song_url} to {file_path}")
except requests.exceptions.RequestException as e:
print(f"Failed to download the song. Error: {e}")