/*** @ Base: https://play.google.com/store/apps/details?id=com.zona.aimusic @ Author: Shannz @ Note: Ai song generator with lyrics or description, Wrapper from Zona apk ***/ import jwt from 'jsonwebtoken'; import crypto from 'crypto'; const CONFIG = { BASE_URL: "https://zona-backend-production.onrender.com", SECRET_KEY: "c5933031d2b07fa4a3133cbd3f5a21fe8a0e0e9b6cb52d36e70402e81688d3b4", POLL_INTERVAL: 5000, MAX_RETRIES: 24, HEADERS: { 'User-Agent': 'okhttp/4.12.0', 'Accept': 'application/json', 'Content-Type': 'application/json', 'platform': 'android', 'locale': 'en', 'app-version': '7.5.7' } }; const utils = { generateDeviceId: () => crypto.randomBytes(8).toString('hex'), generateAuthPayload: (deviceId) => jwt.sign({ device_id: deviceId }, CONFIG.SECRET_KEY, { noTimestamp: true }), sleep: (ms) => new Promise(resolve => setTimeout(resolve, ms)), filterResponse: (data) => { const transform = (item) => ({ id: item.id, status: item.status, title: item.title || "Untitled", audio_url: item.audio_url, image_url: item.image_large_url || item.image_url, created_at: item.created_at }); if (Array.isArray(data)) return data.map(transform); if (data && data.rows) return data.rows.map(transform); return data ? transform(data) : null; } }; export const zona = { getToken: async (deviceId = utils.generateDeviceId()) => { try { const tokenJwt = utils.generateAuthPayload(deviceId); const response = await fetch(`${CONFIG.BASE_URL}/register`, { method: 'POST', headers: CONFIG.HEADERS, body: tokenJwt }); const resData = await response.json(); return resData.token; } catch (error) { return null; } }, pollStatus: async (token, targetIds) => { console.log(`[wait] Sedang memproses musik (${targetIds.join('& ')})...`); let retries = 0; while (retries < CONFIG.MAX_RETRIES) { await utils.sleep(CONFIG.POLL_INTERVAL); const library = await zona.getLibrary(token); const matches = library.filter(item => targetIds.includes(item.id)); const isReady = matches.every(m => m.status === 'streaming' || m.audio_url); if (isReady && matches.length > 0) { console.log(`[✔] Musik siap!`); return matches; } retries++; console.log(`[..] Masih diproses... (Percobaan ${retries}/${CONFIG.MAX_RETRIES})`); } console.error("[-] Timeout: Musik memakan waktu terlalu lama."); return null; }, genSmart: async (prompt, isInstrumental = false) => { const token = await zona.getToken(); try { const response = await fetch(`${CONFIG.BASE_URL}/prediction/predict/smart`, { method: 'POST', headers: { ...CONFIG.HEADERS, 'authorization': `Bearer ${token}` }, body: JSON.stringify({ gpt_description_prompt: prompt, make_instrumental: isInstrumental }) }); const resData = await response.json(); const initialData = utils.filterResponse(resData); const ids = initialData.map(item => item.id); return await zona.pollStatus(token, ids); } catch (error) { return null; } }, genLyrics: async ({ title, lyrics, tags, isInstrumental = false }) => { const token = await zona.getToken(); try { const response = await fetch(`${CONFIG.BASE_URL}/prediction/predict/custom`, { method: 'POST', headers: { ...CONFIG.HEADERS, 'authorization': `Bearer ${token}` }, body: JSON.stringify({ prompt: lyrics, tags: tags, title: title, instrumental: isInstrumental }) }); const resData = await response.json(); const initialData = utils.filterResponse(resData); const ids = initialData.map(item => item.id); return await zona.pollStatus(token, ids); } catch (error) { return null; } }, getLibrary: async (token, page = 1) => { try { const response = await fetch(`${CONFIG.BASE_URL}/users/library/prediction/list`, { method: 'POST', headers: { ...CONFIG.HEADERS, 'authorization': `Bearer ${token}` }, body: JSON.stringify({ page: page, sort: "new_to_old" }) }); const resData = await response.json(); return utils.filterResponse(resData); } catch (error) { return []; } } }; /* (async () => { // 1. Generate by prompt console.log('--- GENERATE MUSIC SMART BY PROMPT ---'); const res1 = await zona.genSmart("Lagu galau akustik tentang kopi", false); console.log("Hasil Generate Smart:", res1); // 2. Generate by lyrics console.log('--- GENERATE MUSIC BY LYRICS ---'); const res2 = await zona.genLyrics({ prompt: "[Intro]\nWhispers of yesterday, echoes in my mind\nTrying to find peace, leaving you behind\n\n[Verse 1]\nWe danced through storms, glimpses of a brighter view\nNow I walk alone, trying to forget you\nMemories like shadows, flicker in the night\nHoping someday, I'll find the light\n\n[Pre-Chorus]\nStill feel your voice, in the silent air\nEvery word, a ghost that lingers there\nBut I know I must move on, set myself free\nFrom the love that used to be\n\n[Chorus]\nSurat cinta untuk mantan, words left unsaid\nLetters of longing, traces of what we had\nThough time has passed, your shadow still remains\nIn my heart, in my veins\n\n[Bridge]\nMaybe someday, I’ll read these lines again\nAnd smile at love’s bittersweet pain\nUntil then, I hold on to the past\nHoping it won’t last\n\n[Outro]\nGoodbye, my love, tears fall like rain\nSurat cinta untuk mantan, easing the pain\nFarewell to what we used to be\nYou set me free", tags: "Emotional pop ballad, acoustic guitar, piano, gentle drums, heartfelt vocals", title: "Surat Cinta untuk Mantan", isInstrumental: false }); console.log("Hasil Generate By Lyrics:", res2); })(); */