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
+69
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
});
}
+1
View File
@@ -29,6 +29,7 @@ export default function Docs() {
ids. If no IDs are provided, all music will be returned.
- \`/api/get_limit\`: Get quota Info
- \`/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/concat\`: Generate the whole song from extensions
\`\`\`
+29
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": {
"post": {
"summary": "Generate lyrics based on Prompt.",
+1
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_limit\`: Get quota Info
- \`/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
\`\`\`
+21
View File
@@ -383,6 +383,27 @@ class SunoApi {
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.
* @param prompt The original lyrics text.