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 (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 (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 (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);
if (status.status === 'SUCCESS') {
return status.response;
} else if (status.status.includes('FAILED') || status.status === 'SENSITIVE_WORD_ERROR') {
throw new Error(`生成失败: ${status.errorMessage || status.status}`);
}
// 等待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}`, {
headers: {
'Authorization': `Bearer ${this.apiKey}`
}
});
const result = await response.json();
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();