diff --git a/src/app/api/custom_generate/route.ts b/src/app/api/custom_generate/route.ts index 61d17a0..dcd2104 100644 --- a/src/app/api/custom_generate/route.ts +++ b/src/app/api/custom_generate/route.ts @@ -6,23 +6,17 @@ export async function POST(req: NextRequest) { try { const body = await req.json(); const { prompt, tags, title, make_instrumental, wait_audio } = body; - - // 校验输入参数 if (!prompt || !tags || !title) { return new NextResponse(JSON.stringify({ error: 'Prompt, tags, and title are required' }), { status: 400, headers: { 'Content-Type': 'application/json' } }); } - - // 调用 SunoApi.custom_generate 方法生成定制音频 const audioInfo = await (await sunoApi).custom_generate( prompt, tags, title, make_instrumental == true, wait_audio == true ); - - // 使用 NextResponse 构建成功响应 return new NextResponse(JSON.stringify(audioInfo), { status: 200, headers: { 'Content-Type': 'application/json' } @@ -35,7 +29,6 @@ export async function POST(req: NextRequest) { headers: { 'Content-Type': 'application/json' } }); } - // 使用 NextResponse 构建错误响应 return new NextResponse(JSON.stringify({ error: 'Internal server error' }), { status: 500, headers: { 'Content-Type': 'application/json' } diff --git a/src/app/api/generate/route.ts b/src/app/api/generate/route.ts index af95b78..ea8d06a 100644 --- a/src/app/api/generate/route.ts +++ b/src/app/api/generate/route.ts @@ -7,7 +7,6 @@ export async function POST(req: NextRequest) { const body = await req.json(); const { prompt, make_instrumental, wait_audio } = body; - // 校验输入参数 if (!prompt) { return new NextResponse(JSON.stringify({ error: 'Prompt is required' }), { status: 400, @@ -15,10 +14,8 @@ export async function POST(req: NextRequest) { }); } - // 调用 SunoApi.generate 方法生成音频 const audioInfo = await (await sunoApi).generate(prompt, make_instrumental == true, wait_audio == true); - // 使用 NextResponse 构建成功响应 return new NextResponse(JSON.stringify(audioInfo), { status: 200, headers: { 'Content-Type': 'application/json' } @@ -31,7 +28,6 @@ export async function POST(req: NextRequest) { headers: { 'Content-Type': 'application/json' } }); } - // 使用 NextResponse 构建错误响应 return new NextResponse(JSON.stringify({ error: 'Internal server error: ' + JSON.stringify(error.response.data.detail) }), { status: 500, headers: { 'Content-Type': 'application/json' } diff --git a/src/app/api/get/route.ts b/src/app/api/get/route.ts index 9de1b56..991e0be 100644 --- a/src/app/api/get/route.ts +++ b/src/app/api/get/route.ts @@ -5,26 +5,23 @@ export const dynamic = "force-dynamic"; export async function GET(req: NextRequest) { if (req.method === 'GET') { try { - // 修复了获取查询参数的方式 const url = new URL(req.url); const songIds = url.searchParams.get('ids'); let audioInfo = []; if (songIds && songIds.length > 0) { const idsArray = songIds.split(','); - // 调用 SunoApi.get 方法获取音频信息 audioInfo = await (await sunoApi).get(idsArray); } else { audioInfo = await (await sunoApi).get(); } - // 使用 NextResponse 构建成功响应 return new NextResponse(JSON.stringify(audioInfo), { status: 200, headers: { 'Content-Type': 'application/json' } }); } catch (error) { console.error('Error fetching audio:', error); - // 使用 NextResponse 构建错误响应 + return new NextResponse(JSON.stringify({ error: 'Internal server error' }), { status: 500, headers: { 'Content-Type': 'application/json' } diff --git a/src/app/api/get_limit/route.ts b/src/app/api/get_limit/route.ts index d498cb3..8094694 100644 --- a/src/app/api/get_limit/route.ts +++ b/src/app/api/get_limit/route.ts @@ -4,17 +4,17 @@ export const dynamic = "force-dynamic"; export async function GET(req: NextRequest) { if (req.method === 'GET') { try { - // 调用 SunoApi.get_limit 方法获取剩余的信用额度 + const limit = await (await sunoApi).get_credits(); - // 使用 NextResponse 构建成功响应 + return new NextResponse(JSON.stringify(limit), { status: 200, headers: { 'Content-Type': 'application/json' } }); } catch (error) { console.error('Error fetching limit:', error); - // 使用 NextResponse 构建错误响应 + return new NextResponse(JSON.stringify({ error: 'Internal server error' }), { status: 500, headers: { 'Content-Type': 'application/json' } diff --git a/src/lib/SunoApi.ts b/src/lib/SunoApi.ts index adc24fc..0934336 100644 --- a/src/lib/SunoApi.ts +++ b/src/lib/SunoApi.ts @@ -9,20 +9,20 @@ const logger = pino(); export interface AudioInfo { - id: string; - title?: string; - image_url?: string; - lyric?: string; - audio_url?: string; - video_url?: string; - created_at: string; - model_name: string; - gpt_description_prompt?: string; - prompt?: string; - status: string; - type?: string; - tags?: string; - duration?: string; + id: string; // Unique identifier for the audio + title?: string; // Title of the audio + image_url?: string; // URL of the image associated with the audio + lyric?: string; // Lyrics of the audio + audio_url?: string; // URL of the audio file + video_url?: string; // URL of the video associated with the audio + created_at: string; // Date and time when the audio was created + model_name: string; // Name of the model used for audio generation + gpt_description_prompt?: string; // Prompt for GPT description + prompt?: string; // Prompt for audio generation + status: string; // Status + type?: string; + tags?: string; // Genre of music. + duration?: string; // Duration of the audio } class SunoApi { @@ -45,7 +45,7 @@ class SunoApi { } })) this.client.interceptors.request.use((config) => { - if (this.currentToken) { // 使用当前token的状态 + if (this.currentToken) { // Use the current token status config.headers['Authorization'] = `Bearer ${this.currentToken}`; } return config; @@ -58,27 +58,33 @@ class SunoApi { return this; } - + /** + * Get the session ID and save it for later use. + */ private async getAuthToken() { - // 获取会话ID的URL + // URL to get session ID const getSessionUrl = `${SunoApi.CLERK_BASE_URL}/v1/client?_clerk_js_version=4.70.5`; - // 获取会话ID + // 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"); } - // 保存会话ID以备后用 + // Save session ID for later use this.sid = sid; } + /** + * Keep the session alive. + * @param isWait Indicates if the method should wait for the session to be fully renewed before returning. + */ public async keepAlive(isWait?: boolean): Promise { if (!this.sid) { throw new Error("Session ID is not set. Cannot renew token."); } - // 续订会话令牌的URL + // URL to renew session token const renewUrl = `${SunoApi.CLERK_BASE_URL}/v1/client/sessions/${this.sid}/tokens/api?_clerk_js_version=4.70.0`; - // 续订会话令牌 + // Renew session token const renewResponse = await this.client.post(renewUrl); logger.info("KeepAlive...\n"); if (isWait) { @@ -86,10 +92,17 @@ class SunoApi { } const newToken = renewResponse.data['jwt']; console.log("newToken:===\n\n", newToken); - // 更新请求头中的Authorization字段,使用新的JWT令牌 + // Update Authorization field in request header with the new JWT token this.currentToken = newToken; } + /** + * Generate a song based on the prompt. + * @param prompt The text prompt to generate audio from. + * @param make_instrumental Indicates if the generated audio should be instrumental. + * @param wait_audio Indicates if the method should wait for the audio file to be fully generated before returning. + * @returns + */ public async generate( prompt: string, make_instrumental: boolean = false, diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 942b948..ddabf9d 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -3,9 +3,9 @@ import pino from "pino"; const logger = pino(); /** - * 暂停指定的秒数。 - * @param x 最小秒数。 - * @param y 最大秒数(可选)。 + * Pause for a specified number of seconds. + * @param x Minimum number of seconds. + * @param y Maximum number of seconds (optional). */ export const sleep = (x: number, y?: number): Promise => { let timeout = x * 1000;