| const axios = require('axios'); | |
| const { v4: uuidv4 } = require('uuid'); | |
| const readline = require('readline'); | |
| const sonu = { | |
| api: { | |
| base: 'https://musicai.apihub.today/api/v1', | |
| endpoints: { | |
| register: '/users', | |
| create: '/song/create', | |
| checkStatus: '/song/user' | |
| } | |
| }, | |
| headers: { | |
| 'user-agent': 'NB Android/1.0.0', | |
| 'content-type': 'application/json', | |
| 'accept': 'application/json', | |
| 'x-platform': 'android', | |
| 'x-app-version': '1.0.0', | |
| 'x-country': 'ID', | |
| 'accept-language': 'id-ID', | |
| 'x-client-timezone': 'Asia/Jakarta' | |
| }, | |
| deviceId: uuidv4(), | |
| userId: null, | |
| fcmToken: 'eqnTqlxMTSKQL5NQz6r5aP:APA91bHa3CvL5Nlcqx2yzqTDAeqxm_L_vIYxXqehkgmTsCXrV29eAak6_jqXv5v1mQrdw4BGMLXl_BFNrJ67Em0vmdr3hQPVAYF8kR7RDtTRHQ08F3jLRRI', | |
| register: async () => { | |
| const msgId = uuidv4(); | |
| const time = Date.now().toString(); | |
| const header = { | |
| ...sonu.headers, | |
| 'x-device-id': sonu.deviceId, | |
| 'x-request-id': msgId, | |
| 'x-message-id': msgId, | |
| 'x-request-time': time | |
| }; | |
| try { | |
| const response = await axios.put( | |
| `${sonu.api.base}${sonu.api.endpoints.register}`, | |
| { | |
| deviceId: sonu.deviceId, | |
| fcmToken: sonu.fcmToken | |
| }, | |
| { headers: header } | |
| ); | |
| sonu.userId = response.data.id; | |
| return { | |
| success: true, | |
| code: 200, | |
| result: { userId: sonu.userId } | |
| }; | |
| } catch (err) { | |
| return { | |
| success: false, | |
| code: err.response?.status || 500, | |
| result: { error: err.message } | |
| }; | |
| } | |
| }, | |
| create: async ({ title, mood, genre, lyrics, gender }) => { | |
| if (!title || title.trim() === '') { | |
| return { | |
| success: false, | |
| code: 400, | |
| result: { error: "Judul lagunya kagak boleh kosong bree π" } | |
| }; | |
| } | |
| if (!lyrics || lyrics.trim() === '') { | |
| return { | |
| success: false, | |
| code: 400, | |
| result: { error: "Lirik lagunya mana? Mau generate lagu kan? Yaa mana liriknya π" } | |
| }; | |
| } | |
| if (lyrics.length > 1500) { | |
| return { | |
| success: false, | |
| code: 400, | |
| result: { error: "Lirik lagu kagak boleh lebih dari 1500 karakter yak bree πΏ"} | |
| }; | |
| } | |
| const msgId = uuidv4(); | |
| const time = Date.now().toString(); | |
| const header = { | |
| ...sonu.headers, | |
| 'x-device-id': sonu.deviceId, | |
| 'x-client-id': sonu.userId, | |
| 'x-request-id': msgId, | |
| 'x-message-id': msgId, | |
| 'x-request-time': time | |
| }; | |
| const body = { | |
| type: 'lyrics', | |
| name: title, | |
| lyrics | |
| }; | |
| if (mood && mood.trim() !== '') body.mood = mood; | |
| if (genre && genre.trim() !== '') body.genre = genre; | |
| if (gender && gender.trim() !== '') body.gender = gender; | |
| try { | |
| const response = await axios.post( | |
| `${sonu.api.base}${sonu.api.endpoints.create}`, | |
| body, | |
| { headers: header } | |
| ); | |
| return { | |
| success: true, | |
| code: 200, | |
| result: { songId: response.data.id } | |
| }; | |
| } catch (err) { | |
| return { | |
| success: false, | |
| code: err.response?.status || 500, | |
| result: { error: err.message } | |
| }; | |
| } | |
| }, | |
| task: async (songId) => { | |
| const header = { | |
| ...sonu.headers, | |
| 'x-client-id': sonu.userId | |
| }; | |
| const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); | |
| try { | |
| let attempt = 0; | |
| let found = null; | |
| while (true) { | |
| const response = await axios.get( | |
| `${sonu.api.base}${sonu.api.endpoints.checkStatus}`, | |
| { | |
| params: { | |
| userId: sonu.userId, | |
| isFavorite: false, | |
| page: 1, | |
| searchText: '' | |
| }, | |
| headers: header | |
| } | |
| ); | |
| found = response.data.datas.find(song => song.id === songId); | |
| if (!found) { | |
| return { | |
| success: false, | |
| code: 404, | |
| result: { error: "Lagunya belum jadi keknya bree, soalnya kagak ada π" } | |
| }; | |
| } | |
| attempt++; | |
| if (found.url) { | |
| return { | |
| success: true, | |
| code: 200, | |
| result: { | |
| status: found.status, | |
| songId: found.id, | |
| title: found.name, | |
| username: found.username, | |
| url: found.url, | |
| thumbnail: found.thumbnail_url | |
| } | |
| }; | |
| } | |
| await delay(3000); | |
| } | |
| } catch (err) { | |
| return { | |
| success: false, | |
| code: err.response?.status || 500, | |
| result: { error: err.message } | |
| }; | |
| } | |
| } | |
| }; | |
| const handler = async (req, res) => { | |
| try { | |
| const { title, mood, genre, lyrics, gender } = req.query; | |
| if (!title) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'Missing required parameter: title' | |
| }); | |
| } | |
| if (!lyrics) { | |
| return res.status(400).json({ | |
| success: false, | |
| error: 'Missing required parameter: lyrics' | |
| }); | |
| } | |
| const registerResult = await sonu.register(); | |
| if (!registerResult.success) { | |
| return res.status(registerResult.code).json({ | |
| author: "Herza", | |
| success: false, | |
| error: registerResult.result.error | |
| }); | |
| } | |
| const createResult = await sonu.create({ title, mood, genre, lyrics, gender }); | |
| if (!createResult.success) { | |
| return res.status(createResult.code).json({ | |
| author: "Herza", | |
| success: false, | |
| error: createResult.result.error | |
| }); | |
| } | |
| const taskResult = await sonu.task(createResult.result.songId); | |
| if (!taskResult.success) { | |
| return res.status(taskResult.code).json({ | |
| author: "Herza", | |
| success: false, | |
| error: taskResult.result.error | |
| }); | |
| } | |
| res.json({ | |
| author: "Herza", | |
| success: true, | |
| result: taskResult.result | |
| }); | |
| } catch (error) { | |
| res.status(500).json({ | |
| success: false, | |
| error: error.message | |
| }); | |
| } | |
| }; | |
| module.exports = { | |
| name: 'Suno AI Music Generator', | |
| description: 'Generate music using Suno AI with custom lyrics', | |
| type: 'GET', | |
| routes: ['api/AI/sunov1'], | |
| tags: ['ai', 'music', 'suno', 'generator'], | |
| main: ['AI'], | |
| limit: 6, | |
| parameters: ['title', 'lyrics', 'mood', 'genre', 'gender', 'key'], | |
| enabled: true, | |
| handler | |
| }; |