sunoai-api / suno-api Goto Github PK
View Code? Open in Web Editor NEWThis is an unofficial API based on Python and FastAPI. It currently supports generating songs, lyrics, etc. 👇
License: MIT License
This is an unofficial API based on Python and FastAPI. It currently supports generating songs, lyrics, etc. 👇
License: MIT License
I've been working with the API code to have it run locally within an app of mine, but I'm running into the {detail: Unauthorized} bug that I can't get around when I call generate(). Here's my full code -- no issues before the generate call.
Is there a reason this code can't work locally or on its own in a single py file? would appreciate if you could pass in your session and cookie info and see if this works for you.
from datetime import datetime
from typing import Any, List, Optional, Union
from pydantic import BaseModel, Field
import asyncio
class schemas:
class Response(BaseModel):
code: Optional[int] = 0
msg: Optional[str] = "success"
data: Optional[Any] = None
class CustomModeGenerateParam(BaseModel):
"""Generate with Custom Mode"""
prompt: str = Field(..., description="lyrics")
mv: str = Field(..., description="model version, default: chirp-v3-0", examples=["chirp-v3-0"])
title: str = Field(..., description="song title")
tags: str = Field(..., description="style of music")
continue_at: Optional[int] = Field(default=None, description="continue a new clip from a previous song, format number", examples=[120])
continue_clip_id: Optional[str] = None
class DescriptionModeGenerateParam(BaseModel):
"""Generate with Song Description"""
gpt_description_prompt: str
make_instrumental: bool = False
mv: str = Field(default='chirp-v3-0', description="model version, default: chirp-v3-0", examples=["chirp-v3-0"])
prompt: str = Field(default="", description="Placeholder, keep it as an empty string, do not modify it")
## UTILS
import json
import os
import time
import aiohttp
# from dotenv import load_dotenv
# load_dotenv()
# BASE_URL = os.getenv("BASE_URL")
BASE_URL = "https://studio-api.suno.ai"
COMMON_HEADERS = {
"Content-Type": "text/plain;charset=UTF-8",
"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",
"Referer": "https://suno.com",
"Origin": "https://suno.com",
}
async def fetch(url, headers=None, data=None, method="POST"):
if headers is None:
headers = {}
headers.update(COMMON_HEADERS)
if data is not None:
data = json.dumps(data)
print(data, method, headers, url)
async with aiohttp.ClientSession() as session:
try:
async with session.request(
method=method, url=url, data=data, headers=headers
) as resp:
return await resp.json()
except Exception as e:
return f"An error occurred: {e}"
async def get_feed(ids, token):
headers = {"Authorization": f"Bearer {token}"}
api_url = f"{BASE_URL}/api/feed/?ids={ids}"
response = await fetch(api_url, headers, method="GET")
return response
async def generate_music(data, token):
headers = {"Authorization": f"Bearer {token}"}
api_url = f"{BASE_URL}/api/generate/v2/"
print(api_url, headers)
response = await fetch(api_url, headers, data)
return response
async def generate_lyrics(prompt, token):
headers = {"Authorization": f"Bearer {token}"}
api_url = f"{BASE_URL}/api/generate/lyrics/"
data = {"prompt": prompt}
return await fetch(api_url, headers, data)
async def get_lyrics(lid, token):
headers = {"Authorization": f"Bearer {token}"}
api_url = f"{BASE_URL}/api/generate/lyrics/{lid}"
return await fetch(api_url, headers, method="GET")
## cookie
import os
import time
from http.cookies import SimpleCookie
from threading import Thread
import requests
# from utils import COMMON_HEADERS
class SunoCookie:
def __init__(self):
self.cookie = SimpleCookie()
self.session_id = None
self.token = None
def load_cookie(self, cookie_str):
self.cookie.load(cookie_str)
def get_cookie(self):
return ";".join([f"{i}={self.cookie.get(i).value}" for i in self.cookie.keys()])
def set_session_id(self, session_id):
self.session_id = session_id
def get_session_id(self):
return self.session_id
def get_token(self):
return self.token
def set_token(self, token: str):
self.token = token
def update_token(suno_cookie: SunoCookie):
headers = {"cookie": suno_cookie.get_cookie()}
headers.update(COMMON_HEADERS)
session_id = suno_cookie.get_session_id()
resp = requests.post(
url=f"https://clerk.suno.com/v1/client/sessions/{session_id}/tokens?_clerk_js_version=4.72.0-snapshot.vc141245",
headers=headers,
)
resp_headers = dict(resp.headers)
set_cookie = resp_headers.get("Set-Cookie")
suno_cookie.load_cookie(set_cookie)
token = resp.json().get("jwt")
suno_cookie.set_token(token)
# print(set_cookie)
# print(f"*** token -> {token} ***")
def keep_alive(suno_cookie: SunoCookie):
while True:
try:
update_token(suno_cookie)
except Exception as e:
print(e)
finally:
time.sleep(5)
def start_keep_alive(suno_cookie: SunoCookie):
t = Thread(target=keep_alive, args=(suno_cookie,))
t.start()
import json
from fastapi import Depends, HTTPException, status
async def get_root():
return schemas.Response()
async def generate(data: schemas.CustomModeGenerateParam, token: str = Depends(get_token)):
try:
resp = await generate_music(data.dict(), token)
return resp
except Exception as e:
raise HTTPException(
detail=str(e),
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR
)
async def generate_with_song_description(data: schemas.DescriptionModeGenerateParam, token: str = Depends(get_token)):
try:
resp = await generate_music(data.dict(), token)
return resp
except Exception as e:
raise HTTPException(
detail=str(e),
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR
)
async def fetch_feed(aid: str, token: str = Depends(get_token)):
try:
resp = await get_feed(aid, token)
return resp
except Exception as e:
raise HTTPException(
detail=str(e),
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR
)
async def generate_lyrics_post(prompt: str, token: str = Depends(get_token)):
if prompt is None:
raise HTTPException(
detail="prompt is required",
status_code=status.HTTP_400_BAD_REQUEST
)
try:
resp = await generate_lyrics(prompt, token)
return resp
except Exception as e:
raise HTTPException(
detail=str(e),
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR
)
async def fetch_lyrics(lid: str, token: str = Depends(get_token)):
try:
resp = await get_lyrics(lid, token)
return resp
except Exception as e:
raise HTTPException(
detail=str(e),
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR
)
cookie_new = "replace this"
session = "replace this"
suno_auth = SunoCookie()
suno_auth.set_session_id(session)
suno_auth.load_cookie(cookie_new)
start_keep_alive(suno_auth)
def get_token():
token = suno_auth.get_token()
try:
yield token
finally:
pass
tags = "fast, chill, happy"
prompt = """\[Verse 1\] Walking down the street The city's shining bright Neon signs are glowing Colors catching my sight (ooh-yeah) People passing by In a hurry Out of time Caught up in the chaos of this city's daily grind \[Verse 2\] Taxis honking horns Sirens fill the air Skyscrapers reaching high A bustling affair (ooh-ooh) Nightclubs pumping beats Dance floors packed tight Everyone's searching for a taste of the nightlife \[Chorus\] Lost in the city lights I feel the energy ignite Moving to the rhythm Partying into the night Living for the moment Taking in the sights Lost in the city lights We're catching dreams in flight"""
data_test = schemas.CustomModeGenerateParam(
title="City Lights",
tags=tags,
prompt=prompt,
mv="chirp-v3-0"
)
# Run the coroutine and get the result
async def main():
result = await generate(data=data_test)
print(result)
# Create an event loop and run the main function
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()```
Collecting dotenv (from -r requirements.txt (line 2))
Using cached dotenv-0.0.5.tar.gz (2.4 kB)
Installing build dependencies ... done
Getting requirements to build wheel ... done
Installing backend dependencies ... error
error: subprocess-exited-with-error
× pip subprocess to install backend dependencies did not run successfully.
│ exit code: 1
╰─> [34 lines of output]
Collecting distribute
Using cached distribute-0.7.3.zip (145 kB)
Installing build dependencies: started
Installing build dependencies: finished with status 'done'
Getting requirements to build wheel: started
Getting requirements to build wheel: finished with status 'done'
Preparing metadata (pyproject.toml): started
Preparing metadata (pyproject.toml): finished with status 'error'
error: subprocess-exited-with-error
Preparing metadata (pyproject.toml) did not run successfully.
exit code: 1
[6 lines of output]
usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
or: setup.py --help [cmd1 cmd2 ...]
or: setup.py --help-commands
or: setup.py cmd --help
error: invalid command 'dist_info'
[end of output]
note: This error originates from a subprocess, and is likely not a problem with pip.
error: metadata-generation-failed
Encountered error while generating package metadata.
See above for output.
note: This is an issue with the package mentioned above, not pip.
hint: See above for details.
[notice] A new release of pip is available: 23.1.2 -> 24.0
[notice] To update, run: python.exe -m pip install --upgrade pip
[end of output]
note: This error originates from a subprocess, and is likely not a problem with pip.
error: subprocess-exited-with-error
请问一下作者,兼容oneapi或者newapi吗?
All of the sudden my code stopped working today and I am getting "unauthorized" as an error message. I re-added the session ID and cookie, but it is still not working.
Can you try your code and try to hit the Suno AI API and let me know if something changed?
I deployed using Docker and found it outputs a large volume of debug log information.
Setting the "DEBUG" environment variable to false had no effect.
Is there a way to prevent the output of debug logs?
curl -X 'POST'
'http://127.0.0.1:8000/generate'
-H 'accept: application/json'
-H 'Content-Type: application/json'
-d '{
"prompt": "",
"mv": "chirp-v3-0",
"title": "hello world",
"tags": "Pop, folk, easy listening, anime, blues, country, classical"
}'
It will still produce songs with lyrics, are there any parameters that need to be added?
Recently suno disabled the service with, quote
Making songs is currently disabled while we upgrade our infrastructure. You can still listen to your favorite tunes.
This can be detected via {'detail': "Service unavailable"} response from /generate.
Is it possible to detect this without actually calling /generate ?
🎉 Congratulations! Your project has been featured and recommended by the HelloGitHub community. We invite you to join the HelloGitHub Badge Program. Joining will grant you the following privileges:
📌 Click here to wear the badge and join the HelloGitHub Badge Program, allowing your open-source project to shine even brighter.
HelloGitHub is a community focused on discovering, sharing, and promoting open-source projects. Since its inception in 2016, it has grown from a monthly newsletter into a dynamic community with over 10,000 users. Our footprint extends across multiple content platforms, earning the trust and support of 500,000 fans worldwide.
Hello:
My code works and the song is generated in Suno AI's website. However, the program is unable to download the song because the Audio URL is not being generated. Only the song ID is outputted, which is different from the URL to download the song.
import os
import requests
from dotenv import load_dotenv
import time
load_dotenv()
def get_song_status(song_id):
api_url = f"http://127.0.0.1:8000/api/feed/?ids={song_id}"
response = requests.get(api_url)
print("Attempting to retrieve song status with ID:", song_id)
print("API URL being called:", api_url)
if response.status_code == 200:
print("Full response data:", response.json())
return response.json()
else:
print("Failed to retrieve song status. Status Code:", response.status_code)
print("Response:", response.text)
return None
def wait_for_song(song_id, timeout=300):
start_time = time.time()
while time.time() - start_time < timeout:
song_data = get_song_status(song_id)
if song_data and 'audio_url' in song_data:
print("Audio URL found:", song_data['audio_url'])
return song_data['audio_url']
time.sleep(10) # Wait before checking again
print("Timed out waiting for the song to be available.")
return None
def initiate_song_generation(description):
url = "http://127.0.0.1:8000/generate/description-mode"
data = {
"gpt_description_prompt": description,
"make_instrumental": False,
"mv": "chirp-v3-0"
}
response = requests.post(url, json=data)
if response.status_code == 200:
response_data = response.json()
print("Response Data from Generation:", response_data)
song_id = response_data.get('id')
print(f"Song generation initiated successfully. Song ID: {song_id}")
return song_id
else:
print("Failed to initiate song generation:", response.text)
return None
def download_song(audio_url):
response = requests.get(audio_url)
if response.status_code == 200:
filename = audio_url.split('/')[-1]
with open(filename, 'wb') as f:
f.write(response.content)
print(f"Song downloaded successfully: {filename}")
else:
print("Failed to download the song:", response.text)
def main():
description = input("Enter a description for the song you want to generate: ")
song_id = initiate_song_generation(description)
if song_id:
print("Waiting for song to be processed...")
audio_url = wait_for_song(song_id)
if audio_url:
print(f"Song ready! Downloading from: {audio_url}")
download_song(audio_url)
else:
print("Song processing failed or took too long.")
if name == "main":
main()
Sadly, i can't concatenate at the moment.
Concatenating songs with V1 ID: 186fa21c-f5c7-4af5-8fd4-5112f4486e31 and V2 ID: 5ed9894f-2208-47ed-8210-a35c8545927b
Failed to concatenate songs: 500 Server Error: Internal Server Error for url: https://api.sunoaiapi.com/api/v1/gateway/generate/concat
Here is a snippet:
def concatenate_songs(v1_id, v2_id):
url = 'https://api.sunoaiapi.com/api/v1/gateway/generate/concat'
headers = {'Content-Type': 'application/json', 'api-key': API_KEY}
payload = {"clip_id": v2_id}
try:
print(f"Concatenating songs with V1 ID: {v1_id} and V2 ID: {v2_id}")
response = requests.post(url, json=payload, headers=headers)
response.raise_for_status()
data = response.json()
print(f"Response from concatenation API: {data}")
if 'data' in data and 'song_id' in data['data']:
concat_id = data['data']['song_id']
print(f"Successfully concatenated V1 and V2: {v1_id} + {v2_id} = {concat_id}")
return concat_id
else:
print(f"Unexpected response structure: {data}")
return None
except requests.exceptions.RequestException as e:
print(f"Failed to concatenate songs: {e}")
return None
Thanks in advance
how to read form this pipe? write to file or whatever https://audiopipe.suno.ai/?item_id=
Hello,
and thanks for your hard work on this!
The readme says:
Automatic token maintenance and keep-alive
But I find this not to be the case for me, as every exactly 7 days the API starts returning Unauthorized and I have to go add new cookies manually.
Is anyone else experiencing this? Anything I can do?
Thanks!
Running some tests from tests.py:
INFO: Started server process [1813280]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: 127.0.0.1:55394 - "POST /generate/description-mode HTTP/1.1" 422 Unprocessable Entity
INFO: 127.0.0.1:38478 - "POST /generate/description-mode HTTP/1.1" 422 Unprocessable Entity
INFO: 127.0.0.1:46720 - "POST /generate HTTP/1.1" 422 Unprocessable Entity
HTTPSConnectionPool(host='clerk.suno.ai', port=443): Max retries exceeded with url: /v1/client/sessions/sess_2eIknoT0bT7smQLtUQiTZ9dTePi/tokens?_clerk_js_version=4.70.5 (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x7f15c1444828>: Failed to establish a new connection: [Errno -2] Name or service not known',))
Hi,
"Prompt" in API3 seems to be both for generating songs based on prompting and lyrics generation, but also for lyrics directly sent to the API. I'm on the right track?
Aniway, in my case, with 3.5 version from Suno.ai, we can handle up to 4 min generation but "Prompt" key can't handle that much characters.
Is that supposed to evolve or i'm misleading myself with the "prompt" key and there is another way to sent "large lyrics" ?
Thanks in advance
I am running the Suno API properly, however i notice every 7 days, i have to update my session id and cookie value.
In the readme it says: "Automatic token maintenance and keep-alive"
How can i make sure my token gets properly refreshed, without me having to manually update this information?
like {"credits": 140}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.