| const axios = require('axios'); | |
| const crypto = require('crypto'); | |
| const HttpProxyAgent = require('http-proxy-agent'); | |
| const HttpsProxyAgent = require('https-proxy-agent'); | |
| async function getRandomProxy() { | |
| try { | |
| const response = await axios.get('https://raw.githubusercontent.com/proxifly/free-proxy-list/refs/heads/main/proxies/protocols/https/data.json'); | |
| const proxies = response.data; | |
| if (proxies.length === 0) return null; | |
| return proxies[Math.floor(Math.random() * proxies.length)].proxy; | |
| } catch (err) { | |
| return null; | |
| } | |
| } | |
| const aiMu = { | |
| api: { | |
| base: 'https://aimu.1010diy.com', | |
| endpoint: { | |
| credits: '/and/getUserActualCredits', | |
| musicAI: '/music/MusicAI', | |
| taskStatus: '/music/getTaskStatus', | |
| generateList: '/and/data/generateList', | |
| options: '/and/home/index', | |
| }, | |
| }, | |
| headers: { | |
| 'user-agent': 'NB Android/1.0.0', | |
| 'accept-encoding': 'gzip', | |
| 'x-version-code': '27', | |
| 'x-token': '', | |
| }, | |
| sid: null, | |
| setDeviceId: (id) => { | |
| aiMu.sid = id; | |
| }, | |
| getDeviceId: () => { | |
| if (!aiMu.sid) { | |
| aiMu.sid = crypto.randomBytes(8).toString('hex'); | |
| } | |
| return aiMu.sid; | |
| }, | |
| opts: async () => { | |
| try { | |
| const proxyUrl = await getRandomProxy(); | |
| const axiosConfig = { headers: { ...aiMu.headers, 'x-device-id': aiMu.getDeviceId() } }; | |
| if (proxyUrl) { | |
| axiosConfig.httpAgent = new HttpProxyAgent(proxyUrl); | |
| axiosConfig.httpsAgent = new HttpsProxyAgent(proxyUrl); | |
| } | |
| const res = await axios.get( | |
| `${aiMu.api.base}${aiMu.api.endpoint.options}`, | |
| axiosConfig | |
| ); | |
| const { code, msg, data } = res.data; | |
| if (code !== 200) { | |
| return { | |
| success: false, | |
| code, | |
| author: 'Daffa ~', | |
| team: 'NB Team', | |
| result: { | |
| error: { msg } | |
| } | |
| }; | |
| } | |
| const moods = [...(data.mood?.default || []), ...(data.mood?.more || [])].map(m => m.text); | |
| const genres = [...(data.genre?.default || []), ...(data.genre?.more || [])].map(g => g.text); | |
| const voices = ['female', 'male', 'random']; | |
| return { | |
| success: true, | |
| code: 200, | |
| author: 'Daffa ~', | |
| team: 'NB Team', | |
| result: { | |
| moods, | |
| genres, | |
| voices | |
| } | |
| }; | |
| } catch (err) { | |
| return { | |
| success: false, | |
| code: err.response?.status || 500, | |
| author: 'Daffa ~', | |
| team: 'NB Team', | |
| result: { | |
| error: err.message | |
| } | |
| }; | |
| } | |
| }, | |
| getCredits: async () => { | |
| try { | |
| const proxyUrl = await getRandomProxy(); | |
| const axiosConfig = { headers: { ...aiMu.headers, 'x-device-id': aiMu.getDeviceId() } }; | |
| if (proxyUrl) { | |
| axiosConfig.httpAgent = new HttpProxyAgent(proxyUrl); | |
| axiosConfig.httpsAgent = new HttpsProxyAgent(proxyUrl); | |
| } | |
| const res = await axios.get( | |
| `${aiMu.api.base}${aiMu.api.endpoint.credits}`, | |
| axiosConfig | |
| ); | |
| const { code, msg, data } = res.data; | |
| if (code === 200) { | |
| return { | |
| success: true, | |
| code: 200, | |
| author: 'Daffa ~', | |
| team: 'NB Team', | |
| result: { | |
| ...data | |
| } | |
| }; | |
| } | |
| return { | |
| success: false, | |
| code, | |
| author: 'Daffa ~', | |
| team: 'NB Team', | |
| result: { | |
| error: msg | |
| } | |
| }; | |
| } catch (err) { | |
| return { | |
| success: false, | |
| code: err.response?.status || 500, | |
| author: 'Daffa ~', | |
| team: 'NB Team', | |
| result: { | |
| error: err.message | |
| } | |
| }; | |
| } | |
| }, | |
| generate: async (params) => { | |
| let make_instrumental = params.make_instrumental ? 1 : 0; | |
| let vocal_only = params.vocal_only ? 1 : 0; | |
| if (make_instrumental === 1) vocal_only = 0; | |
| const { lyrics = '', prompt = '' } = params; | |
| if (!lyrics.trim() && !prompt.trim()) { | |
| return { | |
| success: false, | |
| code: 400, | |
| author: 'Daffa ~', | |
| team: 'NB Team', | |
| result: { | |
| error: 'Lu bisa pilih Lyric atau Prompt bree' | |
| } | |
| }; | |
| } | |
| if (prompt && prompt.length > 1000) { | |
| return { | |
| success: false, | |
| code: 400, | |
| author: 'Daffa ~', | |
| team: 'NB Team', | |
| result: { | |
| error: 'Promptnya kepanjangan yak, max 1k karakter aja.. ' | |
| } | |
| }; | |
| } | |
| const payload = { make_instrumental, vocal_only, lyrics, prompt, ...params }; | |
| try { | |
| const proxyUrl = await getRandomProxy(); | |
| const axiosConfig = { | |
| headers: { | |
| ...aiMu.headers, | |
| 'content-type': 'application/json', | |
| 'x-device-id': aiMu.getDeviceId(), | |
| }, | |
| }; | |
| if (proxyUrl) { | |
| axiosConfig.httpAgent = new HttpProxyAgent(proxyUrl); | |
| axiosConfig.httpsAgent = new HttpsProxyAgent(proxyUrl); | |
| } | |
| const res = await axios.post( | |
| `${aiMu.api.base}${aiMu.api.endpoint.musicAI}`, | |
| payload, | |
| axiosConfig | |
| ); | |
| const { code, msg, data } = res.data; | |
| if (code === 200) { | |
| return { | |
| success: true, | |
| code: 200, | |
| author: 'Daffa ~', | |
| team: 'NB Team', | |
| result: { | |
| taskId: data?.task_id, | |
| inputType: prompt ? 'prompt' : 'lyrics', | |
| inputValue: prompt || lyrics | |
| } | |
| }; | |
| } | |
| return { | |
| success: false, | |
| code, | |
| author: 'Daffa ~', | |
| team: 'NB Team', | |
| result: { | |
| error: msg | |
| } | |
| }; | |
| } catch (err) { | |
| return { | |
| success: false, | |
| code: err.response?.status || 500, | |
| author: 'Daffa ~', | |
| team: 'NB Team', | |
| result: { | |
| error: err.message | |
| } | |
| }; | |
| } | |
| }, | |
| polling: async (taskId) => { | |
| if (!String(taskId)?.trim()) { | |
| return { | |
| success: false, | |
| code: 400, | |
| author: 'Daffa ~', | |
| team: 'NB Team', | |
| result: { | |
| error: 'Task IDnya kagak boleh kosong bree... ' | |
| } | |
| }; | |
| } | |
| try { | |
| const proxyUrl = await getRandomProxy(); | |
| const axiosConfig = { | |
| params: { task_id: taskId }, | |
| headers: { ...aiMu.headers, 'x-device-id': aiMu.getDeviceId() }, | |
| }; | |
| if (proxyUrl) { | |
| axiosConfig.httpAgent = new HttpProxyAgent(proxyUrl); | |
| axiosConfig.httpsAgent = new HttpsProxyAgent(proxyUrl); | |
| } | |
| const res = await axios.get( | |
| `${aiMu.api.base}${aiMu.api.endpoint.taskStatus}`, | |
| axiosConfig | |
| ); | |
| const { code, msg, data } = res.data; | |
| if (code === 200) { | |
| return { | |
| success: true, | |
| code: 200, | |
| author: 'Daffa ~', | |
| team: 'NB Team', | |
| result: { | |
| status: data?.status, | |
| } | |
| }; | |
| } | |
| return { | |
| success: false, | |
| code, | |
| author: 'Daffa ~', | |
| team: 'NB Team', | |
| result: { | |
| error: msg | |
| } | |
| }; | |
| } catch (err) { | |
| return { | |
| success: false, | |
| code: err.response?.status || 500, | |
| author: 'Daffa ~', | |
| team: 'NB Team', | |
| result: { | |
| error: err.message | |
| } | |
| }; | |
| } | |
| }, | |
| getGenerateList: async () => { | |
| try { | |
| const proxyUrl = await getRandomProxy(); | |
| const axiosConfig = { headers: { ...aiMu.headers, 'x-device-id': aiMu.getDeviceId() } }; | |
| if (proxyUrl) { | |
| axiosConfig.httpAgent = new HttpProxyAgent(proxyUrl); | |
| axiosConfig.httpsAgent = new HttpsProxyAgent(proxyUrl); | |
| } | |
| const res = await axios.get( | |
| `${aiMu.api.base}${aiMu.api.endpoint.generateList}`, | |
| axiosConfig | |
| ); | |
| const { code, msg, data } = res.data; | |
| if (code === 200) { | |
| const results = (data || []).map((item) => ({ | |
| taskId: item.task_id, | |
| title: item.title, | |
| duration: item.duration, | |
| mp3: item.conversion_path, | |
| cover: item.album_cover_path, | |
| input: item.lyrics, | |
| created_at: item.created_at, | |
| updated_at: item.updated_at, | |
| })); | |
| return { | |
| success: true, | |
| code: 200, | |
| author: 'Daffa ~', | |
| team: 'NB Team', | |
| result: { | |
| results | |
| } | |
| }; | |
| } | |
| return { | |
| success: false, | |
| code, | |
| author: 'Daffa ~', | |
| team: 'NB Team', | |
| result: { | |
| error: msg | |
| } | |
| }; | |
| } catch (err) { | |
| return { | |
| success: false, | |
| code: err.response?.status || 500, | |
| author: 'Daffa ~', | |
| team: 'NB Team', | |
| result: { | |
| error: err.message | |
| } | |
| }; | |
| } | |
| }, | |
| task: async (taskId, inputType, inputValue, intervalMs = 5000) => { | |
| if (!String(taskId)?.trim()) { | |
| return { | |
| success: false, | |
| code: 400, | |
| author: 'Daffa ~', | |
| team: 'NB Team', | |
| result: { | |
| error: 'Task IDnya kagak boleh kosong bree... ' | |
| } | |
| }; | |
| } | |
| while (true) { | |
| const resx = await aiMu.polling(taskId); | |
| if (!resx.success) return resx; | |
| if (resx.result.status === 'complete') { | |
| const re = await aiMu.getGenerateList(); | |
| if (!re.success) return re; | |
| const tracks = re.result.results.filter((r) => String(r.taskId) === String(taskId)); | |
| const avr = tracks.every((t) => t.mp3.includes('vocal-remover.s3.us-west-2.amazonaws.com')); | |
| if (tracks.length > 0 && avr) { | |
| return { | |
| success: true, | |
| code: 200, | |
| author: 'Daffa ~', | |
| team: 'NB Team', | |
| result: { | |
| taskId: String(taskId), | |
| count: tracks.length, | |
| tracks: tracks.map((t) => ({ | |
| title: t.title, | |
| duration: t.duration, | |
| audio: t.mp3, | |
| cover: t.cover, | |
| [inputType]: t.input, | |
| created_at: t.created_at, | |
| updated_at: t.updated_at, | |
| })), | |
| inputValue | |
| } | |
| }; | |
| } | |
| } | |
| await new Promise((r) => setTimeout(r, intervalMs)); | |
| } | |
| }, | |
| }; | |
| const handler = async (req, res) => { | |
| try { | |
| const { prompt, lyrics, mood, genre, voice, key } = req.query; | |
| if (!prompt && !lyrics) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'Missing required parameter: prompt or lyrics' | |
| }); | |
| } | |
| const params = { | |
| prompt: prompt || '', | |
| lyrics: lyrics || '', | |
| mood: mood || '', | |
| genre: genre || '', | |
| voice: voice || 'random', | |
| make_instrumental: 0, | |
| vocal_only: 0 | |
| }; | |
| const genResult = await aiMu.generate(params); | |
| if (!genResult.success) { | |
| return res.status(400).json(genResult); | |
| } | |
| const taskId = genResult.result.taskId; | |
| const inputType = genResult.result.inputType; | |
| const inputValue = genResult.result.inputValue; | |
| const taskResult = await aiMu.task(taskId, inputType, inputValue); | |
| res.json(taskResult); | |
| } catch (error) { | |
| res.status(500).json({ | |
| success: false, | |
| error: error.message | |
| }); | |
| } | |
| }; | |
| module.exports = { | |
| name: 'Sunov4 Music Generator', | |
| description: 'Generate music using AiMu AI model', | |
| type: 'GET', | |
| routes: ['api/AI/suno4'], | |
| tags: ['ai', 'music', 'aimu', 'suno'], | |
| main: ['AI'], | |
| parameters: ['prompt', 'lyrics', 'mood', 'genre', 'voice', 'key'], | |
| enabled: true, | |
| limit: 18, | |
| handler | |
| }; |