class SunoAPI {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = 'https://api.kie.ai/api/v1';
}
async generateMusic(prompt, options = {}) {
const response = await fetch(`${this.baseUrl}/generate`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
prompt,
customMode: options.customMode || false,
instrumental: options.instrumental || false,
model: options.model || 'V3_5',
style: options.style,
title: options.title,
negativeTags: options.negativeTags,
callBackUrl: options.callBackUrl || 'https://your-app.com/callback'
})
});
const result = await response.json();
if (!response.ok || result.code !== 200) {
throw new Error(`生成失败: ${result.msg || '未知错误'}`);
}
return result.data.taskId;
}
async extendMusic(audioId, options = {}) {
const response = await fetch(`${this.baseUrl}/generate/extend`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
audioId,
defaultParamFlag: options.defaultParamFlag || false,
model: options.model || 'V3_5',
prompt: options.prompt,
style: options.style,
title: options.title,
continueAt: options.continueAt,
callBackUrl: options.callBackUrl || 'https://your-app.com/callback'
})
});
const result = await response.json();
if (!response.ok || result.code !== 200) {
throw new Error(`延长失败: ${result.msg || '未知错误'}`);
}
return result.data.taskId;
}
async generateLyrics(prompt, callBackUrl) {
const response = await fetch(`${this.baseUrl}/lyrics`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
prompt,
callBackUrl
})
});
const result = await response.json();
if (!response.ok || result.code !== 200) {
throw new Error(`歌词生成失败: ${result.msg || '未知错误'}`);
}
return result.data.taskId;
}
async waitForCompletion(taskId, maxWaitTime = 600000) { // 最长等待10分钟
const startTime = Date.now();
while (Date.now() - startTime < maxWaitTime) {
const status = await this.getTaskStatus(taskId);
switch (status.status) {
case 'SUCCESS':
console.log('所有音轨生成成功!');
return status.response;
case 'FIRST_SUCCESS':
console.log('第一个音轨生成完成!');
return status.response;
case 'TEXT_SUCCESS':
console.log('歌词/文本生成成功!');
return status.response;
case 'PENDING':
console.log('任务等待处理中...');
break;
case 'CREATE_TASK_FAILED':
const createError = status.errorMessage || '创建任务失败';
console.error('错误信息:', createError);
throw new Error(createError);
case 'GENERATE_AUDIO_FAILED':
const audioError = status.errorMessage || '音频生成失败';
console.error('错误信息:', audioError);
throw new Error(audioError);
case 'CALLBACK_EXCEPTION':
const callbackError = status.errorMessage || '回调过程中出错';
console.error('错误信息:', callbackError);
throw new Error(callbackError);
case 'SENSITIVE_WORD_ERROR':
const sensitiveError = status.errorMessage || '内容因敏感词被过滤';
console.error('错误信息:', sensitiveError);
throw new Error(sensitiveError);
default:
console.log(`未知状态: ${status.status}`);
if (status.errorMessage) {
console.error('错误信息:', status.errorMessage);
}
break;
}
// 等待10秒后再次检查
await new Promise(resolve => setTimeout(resolve, 10000));
}
throw new Error('生成超时');
}
async getTaskStatus(taskId) {
const response = await fetch(`${this.baseUrl}/generate/record-info?taskId=${taskId}`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${this.apiKey}`
}
});
const result = await response.json();
if (!response.ok || result.code !== 200) {
throw new Error(`查询状态失败: ${result.msg || '未知错误'}`);
}
return result.data;
}
}
// 使用示例
async function main() {
const api = new SunoAPI('YOUR_API_KEY');
try {
// 生成带歌词的音乐
console.log('开始生成音乐...');
const taskId = await api.generateMusic(
'一首关于童年回忆的怀旧民谣',
{
customMode: true,
instrumental: false,
model: 'V4_5',
style: '民谣, 原声吉他, 怀旧',
title: '童年梦想'
}
);
// 等待完成
console.log(`任务ID: ${taskId}。等待完成...`);
const result = await api.waitForCompletion(taskId);
console.log('音乐生成成功!');
console.log('生成的曲目:');
result.sunoData.forEach((track, index) => {
console.log(`曲目 ${index + 1}:`);
console.log(` 标题: ${track.title}`);
console.log(` 音频URL: ${track.audioUrl}`);
console.log(` 时长: ${track.duration}秒`);
console.log(` 标签: ${track.tags}`);
});
// 延长第一个曲目
const firstTrack = result.sunoData[0];
console.log('\n延长第一个曲目...');
const extendTaskId = await api.extendMusic(firstTrack.id, {
defaultParamFlag: true,
prompt: '继续一个充满希望的副歌',
style: '民谣, 振奋',
title: '童年梦想延长版',
continueAt: 60,
model: 'V4_5'
});
const extendResult = await api.waitForCompletion(extendTaskId);
console.log('音乐延长成功!');
console.log('延长曲目URL:', extendResult.sunoData[0].audioUrl);
} catch (error) {
console.error('错误:', error.message);
}
}
main();