✨ feat: Model selection.
1. Use the model number as an optional parameter, defaulting to 3.5; 2. Improve documentation.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { NextResponse, NextRequest } from "next/server";
|
||||
import { sunoApi } from "@/lib/SunoApi";
|
||||
import { DEFAULT_MODEL, sunoApi } from "@/lib/SunoApi";
|
||||
import { corsHeaders } from "@/lib/utils";
|
||||
|
||||
export const maxDuration = 60; // allow longer timeout for wait_audio == true
|
||||
@@ -9,7 +9,7 @@ export async function POST(req: NextRequest) {
|
||||
if (req.method === 'POST') {
|
||||
try {
|
||||
const body = await req.json();
|
||||
const { prompt, tags, title, make_instrumental, wait_audio } = body;
|
||||
const { prompt, tags, title, make_instrumental, model, wait_audio } = body;
|
||||
if (!prompt || !tags || !title) {
|
||||
return new NextResponse(JSON.stringify({ error: 'Prompt, tags, and title are required' }), {
|
||||
status: 400,
|
||||
@@ -22,6 +22,7 @@ export async function POST(req: NextRequest) {
|
||||
const audioInfo = await (await sunoApi).custom_generate(
|
||||
prompt, tags, title,
|
||||
make_instrumental == true,
|
||||
model || DEFAULT_MODEL,
|
||||
wait_audio == true
|
||||
);
|
||||
return new NextResponse(JSON.stringify(audioInfo), {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NextResponse, NextRequest } from "next/server";
|
||||
import { sunoApi } from "@/lib/SunoApi";
|
||||
import { DEFAULT_MODEL, sunoApi } from "@/lib/SunoApi";
|
||||
import { corsHeaders } from "@/lib/utils";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
@@ -8,8 +8,7 @@ export async function POST(req: NextRequest) {
|
||||
if (req.method === 'POST') {
|
||||
try {
|
||||
const body = await req.json();
|
||||
const { audio_id, prompt, continue_at, tags, title } = body;
|
||||
console.log(body)
|
||||
const { audio_id, prompt, continue_at, tags, title, model } = body;
|
||||
|
||||
if (!audio_id) {
|
||||
return new NextResponse(JSON.stringify({ error: 'Audio ID is required' }), {
|
||||
@@ -22,7 +21,7 @@ export async function POST(req: NextRequest) {
|
||||
}
|
||||
|
||||
const audioInfo = await (await sunoApi)
|
||||
.extendAudio(audio_id, prompt, continue_at, tags, title);
|
||||
.extendAudio(audio_id, prompt, continue_at, tags, title, model || DEFAULT_MODEL);
|
||||
|
||||
return new NextResponse(JSON.stringify(audioInfo), {
|
||||
status: 200,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NextResponse, NextRequest } from "next/server";
|
||||
import { sunoApi } from "@/lib/SunoApi";
|
||||
import { DEFAULT_MODEL, sunoApi } from "@/lib/SunoApi";
|
||||
import { corsHeaders } from "@/lib/utils";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
@@ -8,7 +8,7 @@ export async function POST(req: NextRequest) {
|
||||
if (req.method === 'POST') {
|
||||
try {
|
||||
const body = await req.json();
|
||||
const { prompt, make_instrumental, wait_audio } = body;
|
||||
const { prompt, make_instrumental, model, wait_audio } = body;
|
||||
|
||||
if (!prompt) {
|
||||
return new NextResponse(JSON.stringify({ error: 'Prompt is required' }), {
|
||||
@@ -20,7 +20,12 @@ export async function POST(req: NextRequest) {
|
||||
});
|
||||
}
|
||||
|
||||
const audioInfo = await (await sunoApi).generate(prompt, make_instrumental == true, wait_audio == true);
|
||||
const audioInfo = await (await sunoApi).generate(
|
||||
prompt,
|
||||
make_instrumental == true,
|
||||
model || DEFAULT_MODEL,
|
||||
wait_audio == true
|
||||
);
|
||||
|
||||
return new NextResponse(JSON.stringify(audioInfo), {
|
||||
status: 200,
|
||||
|
||||
@@ -33,6 +33,11 @@
|
||||
"description": "Whether to generate instrumental music",
|
||||
"example": "false"
|
||||
},
|
||||
"model": {
|
||||
"type": "string",
|
||||
"description": "Model name ,default is chirp-v3-5",
|
||||
"example": "chirp-v3-5|chirp-v3-0"
|
||||
},
|
||||
"wait_audio": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to wait for music generation, default is false, directly return audio task information; set to true, will wait for up to 100s until the audio is generated.",
|
||||
@@ -79,7 +84,7 @@
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["prompt", "make_instrumental", "wait_audio"],
|
||||
"required": ["prompt"],
|
||||
"properties": {
|
||||
"prompt": {
|
||||
"type": "string",
|
||||
@@ -143,6 +148,11 @@
|
||||
"description": "Whether to generate instrumental music",
|
||||
"example": "false"
|
||||
},
|
||||
"model": {
|
||||
"type": "string",
|
||||
"description": "Model name ,default is chirp-v3-5",
|
||||
"example": "chirp-v3-5|chirp-v3-0"
|
||||
},
|
||||
"wait_audio": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to wait for music generation, default is false, directly return audio task information; set to true, will wait for up to 100s until the audio is generated.",
|
||||
@@ -199,7 +209,7 @@
|
||||
"prompt": {
|
||||
"type": "string",
|
||||
"description": "Detailed prompt, including information such as music lyrics.",
|
||||
"example": ""
|
||||
"example": "[lrc]Silent battlegrounds, no birds' song\nShadows of war, where we don't belong\nMay flowers of peace bloom in this place\nLet's guard this precious dream with grace\n[endlrc]"
|
||||
},
|
||||
"continue_at": {
|
||||
"type": "string",
|
||||
@@ -215,6 +225,11 @@
|
||||
"type": "string",
|
||||
"description": "Music genre",
|
||||
"example": ""
|
||||
},
|
||||
"model": {
|
||||
"type": "string",
|
||||
"description": "Model name ,default is chirp-v3-5",
|
||||
"example": "chirp-v3-5|chirp-v3-0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -357,6 +372,151 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/clip": {
|
||||
"get": {
|
||||
"summary": "Get clip information based on ID.",
|
||||
"description": "Retrieve specific clip information using the provided clip ID as a query parameter.",
|
||||
"tags": ["default"],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "query",
|
||||
"required": true,
|
||||
"description": "Clip ID",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "success",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/audio_info"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Missing parameter id",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string",
|
||||
"example": "Missing parameter id"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string",
|
||||
"example": "Internal server error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/concat": {
|
||||
"post": {
|
||||
"summary": "Generate the whole song from extensions.",
|
||||
"description": "Concatenate audio clips to generate a complete song using the provided clip ID.",
|
||||
"tags": ["default"],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": ["clip_id"],
|
||||
"properties": {
|
||||
"clip_id": {
|
||||
"type": "string",
|
||||
"description": "Clip ID"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "success",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/audio_info"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Clip id is required",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string",
|
||||
"example": "Clip id is required"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"402": {
|
||||
"description": "Payment required",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string",
|
||||
"example": "Payment required"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string",
|
||||
"example": "Internal server error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
|
||||
+14
-9
@@ -6,6 +6,7 @@ import { CookieJar } from "tough-cookie";
|
||||
import { sleep } from "@/lib/utils";
|
||||
|
||||
const logger = pino();
|
||||
export const DEFAULT_MODEL = "chirp-v3-5";
|
||||
|
||||
|
||||
export interface AudioInfo {
|
||||
@@ -64,7 +65,7 @@ class SunoApi {
|
||||
*/
|
||||
private async getAuthToken() {
|
||||
// URL to get session ID
|
||||
const getSessionUrl = `${SunoApi.CLERK_BASE_URL}/v1/client?_clerk_js_version=4.73.2`;
|
||||
const getSessionUrl = `${SunoApi.CLERK_BASE_URL}/v1/client?_clerk_js_version=4.73.2`;
|
||||
// Get session ID
|
||||
const sessionResponse = await this.client.get(getSessionUrl);
|
||||
if (!sessionResponse?.data?.response?.['last_active_session_id']) {
|
||||
@@ -83,7 +84,7 @@ class SunoApi {
|
||||
throw new Error("Session ID is not set. Cannot renew token.");
|
||||
}
|
||||
// URL to renew session token
|
||||
const renewUrl = `${SunoApi.CLERK_BASE_URL}/v1/client/sessions/${this.sid}/tokens?_clerk_js_version==4.73.2`;
|
||||
const renewUrl = `${SunoApi.CLERK_BASE_URL}/v1/client/sessions/${this.sid}/tokens?_clerk_js_version==4.73.2`;
|
||||
// Renew session token
|
||||
const renewResponse = await this.client.post(renewUrl);
|
||||
logger.info("KeepAlive...\n");
|
||||
@@ -91,7 +92,6 @@ class SunoApi {
|
||||
await sleep(1, 2);
|
||||
}
|
||||
const newToken = renewResponse.data['jwt'];
|
||||
console.log("newToken:===\n\n", newToken);
|
||||
// Update Authorization field in request header with the new JWT token
|
||||
this.currentToken = newToken;
|
||||
}
|
||||
@@ -106,11 +106,13 @@ class SunoApi {
|
||||
public async generate(
|
||||
prompt: string,
|
||||
make_instrumental: boolean = false,
|
||||
model?: string,
|
||||
wait_audio: boolean = false,
|
||||
|
||||
): Promise<AudioInfo[]> {
|
||||
await this.keepAlive(false);
|
||||
const startTime = Date.now();
|
||||
const audios = this.generateSongs(prompt, false, undefined, undefined, make_instrumental, wait_audio);
|
||||
const audios = this.generateSongs(prompt, false, undefined, undefined, make_instrumental, model, wait_audio);
|
||||
const costTime = Date.now() - startTime;
|
||||
logger.info("Generate Response:\n" + JSON.stringify(audios, null, 2));
|
||||
logger.info("Cost time: " + costTime);
|
||||
@@ -155,10 +157,11 @@ class SunoApi {
|
||||
tags: string,
|
||||
title: string,
|
||||
make_instrumental: boolean = false,
|
||||
model?: string,
|
||||
wait_audio: boolean = false,
|
||||
): Promise<AudioInfo[]> {
|
||||
const startTime = Date.now();
|
||||
const audios = await this.generateSongs(prompt, true, tags, title, make_instrumental, wait_audio);
|
||||
const audios = await this.generateSongs(prompt, true, tags, title, make_instrumental, model, wait_audio);
|
||||
const costTime = Date.now() - startTime;
|
||||
logger.info("Custom Generate Response:\n" + JSON.stringify(audios, null, 2));
|
||||
logger.info("Cost time: " + costTime);
|
||||
@@ -182,12 +185,13 @@ class SunoApi {
|
||||
tags?: string,
|
||||
title?: string,
|
||||
make_instrumental?: boolean,
|
||||
model?: string,
|
||||
wait_audio: boolean = false
|
||||
): Promise<AudioInfo[]> {
|
||||
await this.keepAlive(false);
|
||||
const payload: any = {
|
||||
make_instrumental: make_instrumental == true,
|
||||
mv: "chirp-v3-5",
|
||||
mv: model || DEFAULT_MODEL,
|
||||
prompt: "",
|
||||
};
|
||||
if (isCustom) {
|
||||
@@ -297,15 +301,16 @@ class SunoApi {
|
||||
prompt: string = "",
|
||||
continueAt: string = "0",
|
||||
tags: string = "",
|
||||
title: string = ""
|
||||
title: string = "",
|
||||
model?: string,
|
||||
): Promise<AudioInfo> {
|
||||
const response = await this.client.post(`${SunoApi.BASE_URL}/api/generate/v2/`, {
|
||||
continue_clip_id: audioId,
|
||||
continue_at: continueAt,
|
||||
mv: "chirp-v3-0",
|
||||
mv: model || DEFAULT_MODEL,
|
||||
prompt: prompt,
|
||||
tags: tags,
|
||||
title: ""
|
||||
title: title
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user