From 8cf877f657072735a492402ad6a4dc9ceba3e15b Mon Sep 17 00:00:00 2001 From: Alistair Hughes Date: Wed, 15 May 2024 19:25:09 +0100 Subject: [PATCH 1/3] add concat endpoint --- src/app/api/concat/route.ts | 64 ++++++++++++++++++++++++++++ src/app/api/custom_generate/route.ts | 2 +- src/lib/SunoApi.ts | 23 ++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 src/app/api/concat/route.ts diff --git a/src/app/api/concat/route.ts b/src/app/api/concat/route.ts new file mode 100644 index 0000000..6bf6e4b --- /dev/null +++ b/src/app/api/concat/route.ts @@ -0,0 +1,64 @@ +import { NextResponse, NextRequest } from "next/server"; +import { 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 { clip_id } = body; + if (!clip_id) { + return new NextResponse(JSON.stringify({ error: 'Clip id is required' }), { + status: 400, + headers: { + 'Content-Type': 'application/json', + ...corsHeaders + } + }); + } + const audioInfo = await (await sunoApi).concatenate(clip_id); + return new NextResponse(JSON.stringify(audioInfo), { + status: 200, + headers: { + 'Content-Type': 'application/json', + ...corsHeaders + } + }); + } catch (error: any) { + console.error('Error generating concatenating audio:', 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' }), { + 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 + }); +} diff --git a/src/app/api/custom_generate/route.ts b/src/app/api/custom_generate/route.ts index 12e9964..c613803 100644 --- a/src/app/api/custom_generate/route.ts +++ b/src/app/api/custom_generate/route.ts @@ -65,4 +65,4 @@ export async function OPTIONS(request: Request) { status: 200, headers: corsHeaders }); -} \ No newline at end of file +} diff --git a/src/lib/SunoApi.ts b/src/lib/SunoApi.ts index f332b0a..2caeeed 100644 --- a/src/lib/SunoApi.ts +++ b/src/lib/SunoApi.ts @@ -116,6 +116,29 @@ class SunoApi { return audios; } + /** + * Calls the concatenate endpoint for a clip to generate the whole song. + * @param clip_id The ID of the audio clip to concatenate. + * @returns A promise that resolves to an AudioInfo object representing the concatenated audio. + * @throws Error if the response status is not 200. + */ + public async concatenate(clip_id: string): Promise { + await this.keepAlive(false); + const payload: any = { clip_id: clip_id }; + + const response = await this.client.post( + `${SunoApi.BASE_URL}/api/generate/concat/v2/`, + payload, + { + timeout: 10000, // 10 seconds timeout + }, + ); + if (response.status !== 200) { + throw new Error("Error response:" + response.statusText); + } + return response.data; + } + /** * Generates custom audio based on provided parameters. * From 95b7a773f02fb733e3ce07f9d8afcfb937e67b66 Mon Sep 17 00:00:00 2001 From: Alistair Hughes Date: Thu, 16 May 2024 22:55:51 +0100 Subject: [PATCH 2/3] added docs --- README.md | 8 ++++++++ src/app/docs/page.tsx | 1 + src/app/page.tsx | 1 + 3 files changed, 10 insertions(+) diff --git a/README.md b/README.md index 03f52ea..fe5494d 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,7 @@ Suno API currently mainly implements the following APIs: If no IDs are provided, all music will be returned. - `/api/get_limit`: Get quota Info - `/api/extend_audio`: Extend audio length +- `/api/concat`: Generate the whole song from extensions ``` For more detailed documentation, please check out the demo site: @@ -170,6 +171,13 @@ def get_quota_information(): return response.json() +def generate_whole_song(clip_id): + payloyd = {"clip_id": clip_id} + url = f"{base_url}/api/concat" + response = requests.post(url, json=payload) + return response.json() + + if __name__ == '__main__': data = generate_audio_by_prompt({ "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.", diff --git a/src/app/docs/page.tsx b/src/app/docs/page.tsx index 2a53426..2f0f77d 100644 --- a/src/app/docs/page.tsx +++ b/src/app/docs/page.tsx @@ -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/concat\`: Generate the whole song from extensions \`\`\` Feel free to explore the detailed API parameters and conduct tests on this page. diff --git a/src/app/page.tsx b/src/app/page.tsx index 2d87bda..0d39d36 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -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/concat\`: Generate the whole song from extensions \`\`\` For more detailed documentation, please check out the demo site: From d2acd138e4e452fef5d2af644b318fe7593ee5da Mon Sep 17 00:00:00 2001 From: swumagic <128472639+swumagic@users.noreply.github.com> Date: Sun, 19 May 2024 00:30:43 +0800 Subject: [PATCH 3/3] Update SunoApi.ts 4.72.2 up4.72.4 --- src/lib/SunoApi.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/SunoApi.ts b/src/lib/SunoApi.ts index f332b0a..1139097 100644 --- a/src/lib/SunoApi.ts +++ b/src/lib/SunoApi.ts @@ -63,7 +63,7 @@ class SunoApi { */ private async getAuthToken() { // URL to get session ID - const getSessionUrl = `${SunoApi.CLERK_BASE_URL}/v1/client?_clerk_js_version=4.72.1`; + const getSessionUrl = `${SunoApi.CLERK_BASE_URL}/v1/client?_clerk_js_version=4.72.4`; // Get session ID const sessionResponse = await this.client.get(getSessionUrl); if (!sessionResponse?.data?.response?.['last_active_session_id']) { @@ -82,7 +82,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.72.0-snapshot.vc141245`; + const renewUrl = `${SunoApi.CLERK_BASE_URL}/v1/client/sessions/${this.sid}/tokens?_clerk_js_version==4.72.4`; // Renew session token const renewResponse = await this.client.post(renewUrl); logger.info("KeepAlive...\n");