Merge pull request #7 from blueeon/main

feat: Add new API, fix session ID validation logic, and update documentation
This commit is contained in:
GitPusher99 2024-03-31 19:54:31 +08:00 committed by GitHub
commit 550d6478cc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 240 additions and 105 deletions

View File

@ -22,6 +22,7 @@ We have deployed an example bound to a free Suno account, so it has daily usage
## Features
- Perfectly implements the creation API from app.suno.ai
- Automatically keep the account active.
- Supports Custom Mode
- One-click deployment to Vercel
- In addition to the standard API, it also adapts to the API Schema of Agent platforms like GPTs and Coze, so you can use it as a tool/plugin/Action for LLMs and integrate it into any AI Agent.
@ -106,14 +107,16 @@ Suno API currently mainly implements the following APIs:
```bash
- `/api/generate`: Generate music
- `/api/custom_generate`: Generate music (Custom Mode, support setting lyrics, music style, title, etc.)
- `/api/get`: Get music Info
- `/api/generate_lyrics`: Generate lyrics based on prompt
- `/api/get`: Get music information based on the id. Use “,” to separate multiple ids.
If no IDs are provided, all music will be returned.
- `/api/get_limit`: Get quota Info
```
For more detailed documentation, please check out the demo site:
[suno.gcui.art/docs](https://suno.gcui.art/docs)
## Code example
## API Integration Code Example
### Python
@ -173,59 +176,63 @@ if __name__ == '__main__':
### Js
```js
const axios = require('axios');
const axios = require("axios");
// replace your vercel domain
const baseUrl = 'http://localhost:3000';
const baseUrl = "http://localhost:3000";
async function customGenerateAudio(payload) {
const url = `${baseUrl}/api/custom_generate`;
const response = await axios.post(url, payload, { headers: { 'Content-Type': 'application/json' } });
return response.data;
const url = `${baseUrl}/api/custom_generate`;
const response = await axios.post(url, payload, {
headers: { "Content-Type": "application/json" },
});
return response.data;
}
async function generateAudioByPrompt(payload) {
const url = `${baseUrl}/api/generate`;
const response = await axios.post(url, payload, { headers: { 'Content-Type': 'application/json' } });
return response.data;
const url = `${baseUrl}/api/generate`;
const response = await axios.post(url, payload, {
headers: { "Content-Type": "application/json" },
});
return response.data;
}
async function getAudioInformation(audioIds) {
const url = `${baseUrl}/api/get?ids=${audioIds}`;
const response = await axios.get(url);
return response.data;
const url = `${baseUrl}/api/get?ids=${audioIds}`;
const response = await axios.get(url);
return response.data;
}
async function getQuotaInformation() {
const url = `${baseUrl}/api/get_limit`;
const response = await axios.get(url);
return response.data;
const url = `${baseUrl}/api/get_limit`;
const response = await axios.get(url);
return response.data;
}
async function main() {
const data = await generateAudioByPrompt({
prompt: "A popular heavy metal song about war, sung by a deep-voiced male singer, slowly and melodiously. The lyrics depict the sorrow of people after the war.",
make_instrumental: false,
wait_audio: false
});
const data = await generateAudioByPrompt({
prompt:
"A popular heavy metal song about war, sung by a deep-voiced male singer, slowly and melodiously. The lyrics depict the sorrow of people after the war.",
make_instrumental: false,
wait_audio: false,
});
const ids = `${data[0].id},${data[1].id}`;
console.log(`ids: ${ids}`);
const ids = `${data[0].id},${data[1].id}`;
console.log(`ids: ${ids}`);
for (let i = 0; i < 60; i++) {
const data = await getAudioInformation(ids);
if (data[0].status === 'streaming') {
console.log(`${data[0].id} ==> ${data[0].audio_url}`);
console.log(`${data[1].id} ==> ${data[1].audio_url}`);
break;
}
// sleep 5s
await new Promise(resolve => setTimeout(resolve, 5000));
for (let i = 0; i < 60; i++) {
const data = await getAudioInformation(ids);
if (data[0].status === "streaming") {
console.log(`${data[0].id} ==> ${data[0].audio_url}`);
console.log(`${data[1].id} ==> ${data[1].audio_url}`);
break;
}
// sleep 5s
await new Promise((resolve) => setTimeout(resolve, 5000));
}
}
main();
```
## Integration with Custom Agents

View File

@ -4,7 +4,7 @@
[English](./README.md) | [简体中文](./README_CN.md)
用 API 调用 suno.ai 的音乐生成AI并且可以轻松集成到 GPTs 等 agent 中。
用 API 调用 suno.ai 的音乐生成 AI并且可以轻松集成到 GPTs 等 agent 中。
## 简介
@ -20,7 +20,8 @@ Suno.ai v3 是一个令人惊叹的 AI 音乐服务,虽然官方还没有开
## Features
- 完美的实现了 app.suno.ai 中的创作 API
- 完美的实现了 app.suno.ai 中的大部分 API
- 自动保持账号活跃
- 支持 Custom Mode
- 一键部署到 vercel
- 除了标准 API还适配了 GPTs、coze 等 Agent 平台的 API Schema所以你可以把它当做一个 LLM 的工具/插件/Action集成到任意 AI Agent 中。
@ -103,14 +104,15 @@ Suno API 目前主要实现了以下 API:
```bash
- `/api/generate`: 创建音乐
- `/api/custom_generate`: 创建音乐(自定义模式,支持设置歌词、音乐风格、设置标题等)
- `/api/get`: 获取音乐
- `/api/generate_lyrics`: 根据Prompt创建歌词
- `/api/get`: 根据id获取音乐信息。获取多个请用","分隔不传ids则返回所有音乐
- `/api/get_limit`: 获取配额信息
```
详细文档请查看演示站点:
[suno.gcui.art/docs](https://suno.gcui.art/docs)
## 代码示例
## API 集成代码示例
### Python
@ -170,62 +172,65 @@ if __name__ == '__main__':
### Js
```js
const axios = require('axios');
const axios = require("axios");
// replace your vercel domain
const baseUrl = 'http://localhost:3000';
const baseUrl = "http://localhost:3000";
async function customGenerateAudio(payload) {
const url = `${baseUrl}/api/custom_generate`;
const response = await axios.post(url, payload, { headers: { 'Content-Type': 'application/json' } });
return response.data;
const url = `${baseUrl}/api/custom_generate`;
const response = await axios.post(url, payload, {
headers: { "Content-Type": "application/json" },
});
return response.data;
}
async function generateAudioByPrompt(payload) {
const url = `${baseUrl}/api/generate`;
const response = await axios.post(url, payload, { headers: { 'Content-Type': 'application/json' } });
return response.data;
const url = `${baseUrl}/api/generate`;
const response = await axios.post(url, payload, {
headers: { "Content-Type": "application/json" },
});
return response.data;
}
async function getAudioInformation(audioIds) {
const url = `${baseUrl}/api/get?ids=${audioIds}`;
const response = await axios.get(url);
return response.data;
const url = `${baseUrl}/api/get?ids=${audioIds}`;
const response = await axios.get(url);
return response.data;
}
async function getQuotaInformation() {
const url = `${baseUrl}/api/get_limit`;
const response = await axios.get(url);
return response.data;
const url = `${baseUrl}/api/get_limit`;
const response = await axios.get(url);
return response.data;
}
async function main() {
const data = await generateAudioByPrompt({
prompt: "A popular heavy metal song about war, sung by a deep-voiced male singer, slowly and melodiously. The lyrics depict the sorrow of people after the war.",
make_instrumental: false,
wait_audio: false
});
const data = await generateAudioByPrompt({
prompt:
"A popular heavy metal song about war, sung by a deep-voiced male singer, slowly and melodiously. The lyrics depict the sorrow of people after the war.",
make_instrumental: false,
wait_audio: false,
});
const ids = `${data[0].id},${data[1].id}`;
console.log(`ids: ${ids}`);
const ids = `${data[0].id},${data[1].id}`;
console.log(`ids: ${ids}`);
for (let i = 0; i < 60; i++) {
const data = await getAudioInformation(ids);
if (data[0].status === 'streaming') {
console.log(`${data[0].id} ==> ${data[0].audio_url}`);
console.log(`${data[1].id} ==> ${data[1].audio_url}`);
break;
}
// sleep 5s
await new Promise(resolve => setTimeout(resolve, 5000));
for (let i = 0; i < 60; i++) {
const data = await getAudioInformation(ids);
if (data[0].status === "streaming") {
console.log(`${data[0].id} ==> ${data[0].audio_url}`);
console.log(`${data[1].id} ==> ${data[1].audio_url}`);
break;
}
// sleep 5s
await new Promise((resolve) => setTimeout(resolve, 5000));
}
}
main();
```
## 集成到到常见的自定义 Agent 中
你可以把 suno ai 当做一个 工具/插件/Action 集成到你的 AI Agent 中。

View File

@ -6,7 +6,7 @@
"url": "https://github.com/gcui-art/"
},
"license": "LGPL-3.0-or-later",
"version": "1.0.0",
"version": "1.1.0",
"private": true,
"scripts": {
"dev": "next dev",

View File

@ -0,0 +1,42 @@
import { NextResponse, NextRequest } from "next/server";
import { sunoApi } from "@/lib/SunoApi";
export const dynamic = "force-dynamic";
export async function POST(req: NextRequest) {
if (req.method === 'POST') {
try {
const body = await req.json();
const { prompt } = body;
if (!prompt) {
return new NextResponse(JSON.stringify({ error: 'Prompt is required' }), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}
const lyrics = await (await sunoApi).generateLyrics(prompt);
return new NextResponse(JSON.stringify(lyrics), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
} catch (error: any) {
console.error('Error generating lyrics:', JSON.stringify(error.response.data));
if (error.response.status === 402) {
return new NextResponse(JSON.stringify({ error: error.response.data.detail }), {
status: 402,
headers: { 'Content-Type': 'application/json' }
});
}
return new NextResponse(JSON.stringify({ error: 'Internal server error: ' + JSON.stringify(error.response.data.detail) }), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
} else {
return new NextResponse('Method Not Allowed', {
headers: { Allow: 'POST' },
status: 405
});
}
}

View File

@ -15,7 +15,7 @@ export async function GET(req: NextRequest) {
} catch (error) {
console.error('Error fetching limit:', error);
return new NextResponse(JSON.stringify({ error: 'Internal server error' }), {
return new NextResponse(JSON.stringify({ error: 'Internal server error. ' + error }), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});

View File

@ -16,26 +16,33 @@ export default function Docs() {
<Markdown>
{`
---
Suno API currently mainly implements the following APIs:
\`gcui-art/suno-api\` currently mainly implements the following APIs:
\`\`\`bash
- \`/api/generate\`: Generate music
- \`/api/custom_generate\`: Generate music (Custom Mode, support setting lyrics,
music style, title, etc.)
- \`/api/get\`: Get music Info
music style, title, etc.)
- \`/api/generate_lyrics\`: Generate lyrics based on prompt
- \`/api/get\`: Get music information based on the id. Use “,” to separate multiple
ids. If no IDs are provided, all music will be returned.
- \`/api/get_limit\`: Get quota Info
\`\`\`
Feel free to explore the detailed API parameters and conduct tests on this page.
> Please note:
>
> we have bound a free account with a daily usage limit.
> You can deploy and bind your own account to complete the testing.
Feel free to explore the detailed API parameters and conduct tests on this page.
`}
</Markdown>
</article>
</Section>
<Section className="my-10">
<article className='prose lg:prose-lg max-w-3xl py-10'>
<h2 className='text-center'>
Details of the API and testing it online
</h2>
<p className='text-red-800 italic'>
This is just a demo, bound to a test account. Please do not use it frequently, so that more people can test online.
</p>
</article>
<div className=' border p-4 rounded-2xl shadow-xl hover:shadow-none duration-200'>
<Swagger spec={spec} />
</div>

View File

@ -3,7 +3,7 @@
"info": {
"title": "suno-api",
"description": "Use API to call the music generation service of Suno.ai and easily integrate it into agents like GPTs.",
"version": "1.0.0"
"version": "1.1.0"
},
"tags": [
{
@ -137,6 +137,56 @@
}
}
},
"/api/generate_lyrics": {
"post": {
"summary": "Generate lyrics based on Prompt.",
"description": "Generate lyrics based on Prompt.",
"tags": ["default"],
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["prompt"],
"properties": {
"prompt": {
"type": "string",
"description": "Prompt",
"example": "A soothing lullaby"
}
}
}
}
}
},
"responses": {
"200": {
"description": "success",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"text": {
"type": "string",
"description": "Lyrics"
},
"title": {
"type": "string",
"description": "music title"
},
"status": {
"type": "string",
"description": "Status"
}
}
}
}
}
}
}
}
},
"/api/get": {
"get": {
"summary": "Get audio information",
@ -146,8 +196,8 @@
{
"in": "query",
"name": "ids",
"description": "Audio IDs, separated by commas.",
"required": true,
"description": "Audio IDs, separated by commas. Leave blank to return a list of all music.",
"required": false,
"schema": {
"type": "string"
}
@ -297,7 +347,7 @@
}
},
"title": "audio_info",
"description": "audio info"
"description": "Audio Info"
}
}
}

View File

@ -7,23 +7,24 @@ export default function Home() {
const markdown = `
---
## Introduction
## 👋 Introduction
Suno.ai v3 is an amazing AI music service. Although the official API is not yet available, we couldn't wait to integrate its capabilities somewhere.
We discovered that some users have similar needs, so we decided to open-source this project, hoping you'll like it.
👉 We update quickly, please Star us on Github: [github.com/gcui-art/suno-api](https://github.com/gcui-art/suno-api)
We update quickly, please star us on Github: [github.com/gcui-art/suno-api](https://github.com/gcui-art/suno-api)
## Features
## 🌟 Features
- Perfectly implements the creation API from \`app.suno.ai\`
- Automatically keep the account active.
- Supports \`Custom Mode\`
- One-click deployment to Vercel
- In addition to the standard API, it also adapts to the API Schema of Agent platforms like GPTs and Coze, so you can use it as a tool/plugin/Action for LLMs and integrate it into any AI Agent.
- Permissive open-source license, allowing you to freely integrate and modify.
## Getting Started
## 🚀 Getting Started
### 1. Obtain the cookie of your app.suno.ai account
@ -60,9 +61,9 @@ npm install
- If youre running this locally, be sure to add the following to your \`.env\` file:
\`\`\`bash
SUNO_COOKIE=<your-cookie>
\`\`\`
\`\`\`bash
SUNO_COOKIE=<your-cookie>
\`\`\`
### 4. Run suno-api
@ -74,14 +75,14 @@ SUNO_COOKIE=<your-cookie>
- Visit the \`http://localhost:3000/api/get_limit\` API for testing.
- If the following result is returned:
\`\`\`json
{
"credits_left": 50,
"period": "day",
"monthly_limit": 50,
"monthly_usage": 50
}
\`\`\`
\`\`\`json
{
"credits_left": 50,
"period": "day",
"monthly_limit": 50,
"monthly_usage": 50
}
\`\`\`
it means the program is running normally.
@ -89,7 +90,7 @@ it means the program is running normally.
You can check out the detailed API documentation at [suno.gcui.art/docs](https://suno.gcui.art/docs).
## API Reference
## 📚 API Reference
Suno API currently mainly implements the following APIs:
@ -97,7 +98,9 @@ Suno API currently mainly implements the following APIs:
- \`/api/generate\`: Generate music
- \`/api/custom_generate\`: Generate music (Custom Mode, support setting lyrics,
music style, title, etc.)
- \`/api/get\`: Get music Info
- \`/api/generate_lyrics\`: Generate lyrics based on prompt
- \`/api/get\`: Get music list
- \`/api/get?ids=\`: Get music Info by id, separate multiple id with ",".
- \`/api/get_limit\`: Get quota Info
\`\`\`

View File

@ -66,12 +66,11 @@ class SunoApi {
const getSessionUrl = `${SunoApi.CLERK_BASE_URL}/v1/client?_clerk_js_version=4.70.5`;
// Get session ID
const sessionResponse = await this.client.get(getSessionUrl);
const sid = sessionResponse.data.response['last_active_session_id'];
if (!sid) {
throw new Error("Failed to get session id");
if (!sessionResponse?.data?.response?.['last_active_session_id']) {
throw new Error("Failed to get session id, you may need to update the SUNO_COOKIE");
}
// Save session ID for later use
this.sid = sid;
this.sid = sessionResponse.data.response['last_active_session_id'];
}
/**
@ -234,6 +233,28 @@ class SunoApi {
}
}
/**
* Generates lyrics based on a given prompt.
* @param prompt The prompt for generating lyrics.
* @returns The generated lyrics text.
*/
public async generateLyrics(prompt: string): Promise<string> {
await this.keepAlive(false);
// Initiate lyrics generation
const generateResponse = await this.client.post(`${SunoApi.BASE_URL}/api/generate/lyrics/`, { prompt });
const generateId = generateResponse.data.id;
// Poll for lyrics completion
let lyricsResponse = await this.client.get(`${SunoApi.BASE_URL}/api/generate/lyrics/${generateId}`);
while (lyricsResponse?.data?.status !== 'complete') {
await sleep(2); // Wait for 2 seconds before polling again
lyricsResponse = await this.client.get(`${SunoApi.BASE_URL}/api/generate/lyrics/${generateId}`);
}
// Return the generated lyrics text
return lyricsResponse.data;
}
/**
* Processes the lyrics (prompt) from the audio metadata into a more readable format.
* @param prompt The original lyrics text.