web2apk

JavaScriptPublic
by zx-apiAug 23, 2026, 02:44 AMExpires: Never60 views
web2apk
Raw
1/**
2 * Name Scrape: Web2Apk 
3 * Credit By Zx 
4 * Description: Website To Apk
5 * Sumber Kode: https://whatsapp.com/channel/0029VbDLqe7EquiSF4STU13o
6 * Note: Kalau Mau di sher lagi harap jangan hapus credit atau sumber saluran 
7 *
8**/
9
10import fs from 'fs'
11import path from 'path'
12import os from 'os'
13import axios from 'axios'
14
15class Web2Apk {
16  constructor({ apiUrl = 'https://webappcreator.amethystlab.org/api/build-apk', baseUrl = 'https://webappcreator.amethystlab.org' } = {}) {
17    this.apiUrl = apiUrl
18    this.baseUrl = baseUrl
19  }
20
21  isValidUrl(url) {
22    return /^https?:\/\//i.test(url)
23  }
24
25  buildPackageName(appName) {
26    const cleaned = appName.toLowerCase().replace(/[^a-z0-9]/g, '')
27    return `com.${cleaned || 'app'}.web2apk`
28  }
29
30  saveIconBuffer(buffer) {
31    const tempDir = path.join(os.tmpdir(), 'web2apk')
32    if (!fs.existsSync(tempDir)) fs.mkdirSync(tempDir, { recursive: true })
33
34    const iconPath = path.join(tempDir, `icon_${Date.now()}.png`)
35    fs.writeFileSync(iconPath, buffer)
36    return iconPath
37  }
38
39  async build({ url, appName, iconBuffer, versionName = '1.0.0', versionCode = 1 }) {
40    if (!this.isValidUrl(url)) throw new Error('URL harus diawali dengan http:// atau https://')
41    if (!appName) throw new Error('Nama aplikasi tidak boleh kosong.')
42    if (!iconBuffer) throw new Error('Icon aplikasi wajib disertakan.')
43
44    const packageName = this.buildPackageName(appName)
45    const iconPath = this.saveIconBuffer(iconBuffer)
46    let projectId = null
47
48    try {
49      const FormDataNode = (await import('form-data')).default
50      const form = new FormDataNode()
51
52      form.append('websiteUrl', url)
53      form.append('appName', appName)
54      form.append('icon', fs.createReadStream(iconPath), {
55        filename: 'icon.png',
56        contentType: 'image/png'
57      })
58      form.append('packageName', packageName)
59      form.append('versionName', versionName)
60      form.append('versionCode', String(versionCode)) // Pastikan string
61
62      console.log('๐Ÿ“ค Mengirim file ke server (Server lambat, mohon tunggu 1-5 menit)...')
63      
64      const response = await axios.post(this.apiUrl, form, {
65        headers: {
66          'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36',
67          'Accept': 'application/json, text/plain, */*',
68          'Origin': this.baseUrl,
69          'Referer': `${this.baseUrl}/`,
70          ...form.getHeaders()
71        },
72        maxContentLength: Infinity,
73        maxBodyLength: Infinity,
74        timeout: 600000, 
75        validateStatus: (status) => status >= 200 && status < 600 
76      })
77
78      const data = response.data
79      console.log(`โœ… Server merespons dengan status HTTP ${response.status}`)
80
81      if (response.status >= 400) {
82        const errorMsg = (data && (data.error || data.message)) || `Server mengembalikan error HTTP ${response.status}`
83        throw new Error(errorMsg)
84      }
85
86      if (data.success && data.downloadUrl) {
87        return {
88          success: true,
89          appName,
90          packageName,
91          downloadUrl: data.downloadUrl.startsWith('http') ? data.downloadUrl : `${this.baseUrl}${data.downloadUrl}`
92        }
93      }
94
95      if (data.queued || data.projectId) {
96        projectId = data.projectId
97        console.log(`โœ… Request diterima! Project ID: ${projectId}`)
98      } else {
99        throw new Error('Respon tidak dikenali dari server: ' + JSON.stringify(data))
100      }
101
102      console.log(`โณ Build masuk antrian. Memulai polling status...`)
103      const statusUrl = `${this.baseUrl}/api/build-status/${projectId}`
104      const maxAttempts = 120 // Maksimal polling 10 menit
105      let attempt = 0
106
107      while (attempt < maxAttempts) {
108        await new Promise(resolve => setTimeout(resolve, 5000)) // Tunggu 5 detik
109        attempt++
110
111        try {
112          const statusResponse = await axios.get(statusUrl, {
113            headers: {
114              'Accept': 'application/json',
115              'Origin': this.baseUrl,
116              'Referer': `${this.baseUrl}/`,
117              'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36'
118            },
119            timeout: 15000
120          })
121
122          const status = statusResponse.data
123
124          if (status.status === 'completed') {
125            console.log('\nโœ… Build Selesai!')
126            const downloadPath = status.downloadUrl || `/download/${projectId}.apk`
127            return {
128              success: true,
129              appName,
130              packageName,
131              downloadUrl: downloadPath.startsWith('http') ? downloadPath : `${this.baseUrl}${downloadPath}`
132            }
133          } 
134          else if (status.status === 'failed') {
135            throw new Error(status.error || 'Build gagal di server.')
136          } 
137          else {
138            const queueInfo = status.queueInfo ? `(${status.queueInfo.currentBuilds}/${status.queueInfo.maxConcurrent} slot)` : ''
139            const pos = status.position ? `Posisi: ${status.position}` : 'Sedang diproses'
140            
141            process.stdout.write(
142              `\rโณ Status: ${status.status.toUpperCase().padEnd(10)} | ` +
143              `${pos.padEnd(15)} | ${queueInfo.padEnd(20)} | Cek ke-${attempt}   `
144            )
145          }
146        } catch (err) {
147          console.warn(`\nโš ๏ธ Gagal cek status (percobaan ${attempt}): ${err.message}`)
148        }
149      }
150
151      throw new Error('Timeout: Build memakan waktu lebih dari 10 menit.')
152
153    } finally {
154      if (fs.existsSync(iconPath)) fs.unlinkSync(iconPath)
155    }
156  }
157}
158
159export default Web2Apk
160
161async function run() {
162  console.log('โณ Memulai proses pembuatan APK...\n');
163
164  try {
165    const web2apk = new Web2Apk();
166
167    console.log('๐Ÿ“ฅ Mengunduh icon aplikasi');
168    const iconResponse = await axios.get(
169      'https://placehold.co/512x512/4285F4/FFFFFF/png?text=GoogleKu',
170      { responseType: 'arraybuffer', timeout: 30000 }
171    );
172    const iconBuffer = Buffer.from(iconResponse.data);
173
174    const appConfig = {
175      url: 'https://google.com',
176      appName: 'GoogleKu',
177      iconBuffer: iconBuffer,
178      versionName: '1.0.0',
179      versionCode: 1
180    };
181
182    console.log('๐Ÿš€ Mengirim request build ke server AmethystLab...');
183    const result = await web2apk.build(appConfig);
184
185    console.log('\n\nโœ… Build Berhasil!');
186    console.log('-------------------');
187    console.log(`๐Ÿ“ฑ Nama Aplikasi : ${result.appName}`);
188    console.log(`๐Ÿ“ฆ Package Name  : ${result.packageName}`);
189    console.log(`๐Ÿ”— Link Download : ${result.downloadUrl}`);
190    console.log('-------------------');
191
192  } catch (error) {
193    console.error('\n\nโŒ Terjadi kesalahan saat mem-build APK:');
194    console.error(error.message);
195    if (error.response) {
196      console.error('Response status:', error.response.status);
197      console.error('Response data:', error.response.data);
198    }
199  }
200}
201
202run();
202 linesยท6,906 charsยท6.8 KB