Add route for generating stems

This commit is contained in:
tmirkovic 2024-11-09 22:44:30 -05:00
parent 74d34fb5de
commit 96b675a039
7 changed files with 123 additions and 0 deletions

View File

@ -126,6 +126,7 @@ Suno API currently mainly implements the following APIs:
If no IDs are provided, all music will be returned. If no IDs are provided, all music will be returned.
- `/api/get_limit`: Get quota Info - `/api/get_limit`: Get quota Info
- `/api/extend_audio`: Extend audio length - `/api/extend_audio`: Extend audio length
- `/api/generate_stems`: Make stem tracks (separate audio and music track)
- `/api/clip`: Get clip information based on ID passed as query parameter `id` - `/api/clip`: Get clip information based on ID passed as query parameter `id`
- `/api/concat`: Generate the whole song from extensions - `/api/concat`: Generate the whole song from extensions
``` ```

View File

@ -123,6 +123,7 @@ Suno API 目前主要实现了以下 API:
- `/api/get`: 根据id获取音乐信息。获取多个请用","分隔不传ids则返回所有音乐 - `/api/get`: 根据id获取音乐信息。获取多个请用","分隔不传ids则返回所有音乐
- `/api/get_limit`: 获取配额信息 - `/api/get_limit`: 获取配额信息
- `/api/extend_audio`: 在一首音乐的基础上,扩展音乐长度 - `/api/extend_audio`: 在一首音乐的基础上,扩展音乐长度
- `/api/generate_stems`: 制作主干轨道(单独的音频和音乐轨道
- `/api/clip`: 检索特定音乐的信息 - `/api/clip`: 检索特定音乐的信息
- `/api/concat`: 合并音乐,将扩展后的音乐和原始音乐合并 - `/api/concat`: 合并音乐,将扩展后的音乐和原始音乐合并
``` ```

View File

@ -0,0 +1,69 @@
import { NextResponse, NextRequest } from "next/server";
import { DEFAULT_MODEL, sunoApi } from "@/lib/SunoApi";
import { corsHeaders } from "@/lib/utils";
export const dynamic = "force-dynamic";
export async function POST(req: NextRequest) {
if (req.method === 'POST') {
try {
const body = await req.json();
const { audio_id } = body;
if (!audio_id) {
return new NextResponse(JSON.stringify({ error: 'Audio ID is required' }), {
status: 400,
headers: {
'Content-Type': 'application/json',
...corsHeaders
}
});
}
const audioInfo = await (await sunoApi)
.generateStems(audio_id);
return new NextResponse(JSON.stringify(audioInfo), {
status: 200,
headers: {
'Content-Type': 'application/json',
...corsHeaders
}
});
} catch (error: any) {
console.error('Error generating stems:', 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',
...corsHeaders
}
});
}
return new NextResponse(JSON.stringify({ error: 'Internal server error: ' + JSON.stringify(error.response.data.detail) }), {
status: 500,
headers: {
'Content-Type': 'application/json',
...corsHeaders
}
});
}
} else {
return new NextResponse('Method Not Allowed', {
headers: {
Allow: 'POST',
...corsHeaders
},
status: 405
});
}
}
export async function OPTIONS(request: Request) {
return new Response(null, {
status: 200,
headers: corsHeaders
});
}

View File

@ -29,6 +29,7 @@ export default function Docs() {
ids. If no IDs are provided, all music will be returned. ids. If no IDs are provided, all music will be returned.
- \`/api/get_limit\`: Get quota Info - \`/api/get_limit\`: Get quota Info
- \`/api/extend_audio\`: Extend audio length - \`/api/extend_audio\`: Extend audio length
- \`/api/generate_stems\`: Make stem tracks (separate audio and music track)
- \`/api/clip\`: Get clip information based on ID passed as query parameter \`id\` - \`/api/clip\`: Get clip information based on ID passed as query parameter \`id\`
- \`/api/concat\`: Generate the whole song from extensions - \`/api/concat\`: Generate the whole song from extensions
\`\`\` \`\`\`

View File

@ -243,6 +243,35 @@
} }
} }
}, },
"/api/generate_stems": {
"post": {
"summary": "Make stem tracks (separate audio and music track).",
"description": "Make stem tracks (separate audio and music track).",
"tags": ["default"],
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["audio_id"],
"properties": {
"audio_id": {
"type": "string",
"description": "The ID of the song to generate stems for.",
"example": "e76498dc-6ab4-4a10-a19f-8a095790e28d"
}
}
}
}
}
},
"responses": {
"200": {
"$ref": "#/components/schemas/audio_info"
}
}
}
},
"/api/generate_lyrics": { "/api/generate_lyrics": {
"post": { "post": {
"summary": "Generate lyrics based on Prompt.", "summary": "Generate lyrics based on Prompt.",

View File

@ -106,6 +106,7 @@ Suno API currently mainly implements the following APIs:
- \`/api/get?ids=\`: Get music Info by id, separate multiple id with ",". - \`/api/get?ids=\`: Get music Info by id, separate multiple id with ",".
- \`/api/get_limit\`: Get quota Info - \`/api/get_limit\`: Get quota Info
- \`/api/extend_audio\`: Extend audio length - \`/api/extend_audio\`: Extend audio length
- \`/api/generate_stems\`: Make stem tracks (separate audio and music track)
- \`/api/concat\`: Generate the whole song from extensions - \`/api/concat\`: Generate the whole song from extensions
\`\`\` \`\`\`

View File

@ -383,6 +383,27 @@ class SunoApi {
return response.data; return response.data;
} }
/**
* Generate stems for a song.
* @param song_id The ID of the song to generate stems for.
* @returns A promise that resolves to an AudioInfo object representing the generated stems.
*/
public async generateStems(song_id: string): Promise<AudioInfo[]> {
const response = await this.client.post(
`${SunoApi.BASE_URL}/api/edit/stems/${song_id}`, {}
);
console.log('generateStems response:\n', response?.data);
return response.data.clips.map((clip: any) => ({
id: clip.id,
status: clip.status,
metadata: clip.metadata,
created_at: clip.created_at,
title: clip.title,
stem_from_id: clip.metadata.stem_from_id,
duration: clip.metadata.duration
}));
}
/** /**
* Processes the lyrics (prompt) from the audio metadata into a more readable format. * Processes the lyrics (prompt) from the audio metadata into a more readable format.
* @param prompt The original lyrics text. * @param prompt The original lyrics text.