Nexchan commited on
Commit
ffb1bb5
β€’
1 Parent(s): d98dd1b

Update index.js

Browse files
Files changed (1) hide show
  1. index.js +375 -395
index.js CHANGED
@@ -9,60 +9,38 @@ import ffmpeg from 'fluent-ffmpeg';
9
  import nodeID3 from 'node-id3';
10
  import ytdl from 'ytdl-core';
11
  import FormData from 'form-data';
 
 
12
  const require = createRequire(import.meta.url);
13
  const fs = require('fs');
14
  const path = require('path');
15
  const { google } = require('googleapis');
 
 
16
  const PORT = process.env.PORT || 7860;
17
  const app = express();
18
  const readFileAsync = promisify(fs.readFile);
19
- import fetch from 'node-fetch';
20
-
21
-
22
-
23
- const puppeteer = require('puppeteer');
24
-
25
- // Function to ping a website
26
- async function pingWebsite() {
27
- const browser = await puppeteer.launch({
28
- headless: true,
29
- args: ['--no-sandbox', '--disable-setuid-sandbox']
30
- });
31
- const page = await browser.newPage();
32
- await page.setUserAgent("Mozilla/5.0 (Linux; Android 10; SM-G965U Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.141 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/420.0.0.32.61;]");
33
-
34
- await page.goto('https://huggingface.co/spaces/ArashiCode/api');
35
- console.log("ping")
36
- await browser.close();
37
- }
38
-
39
- // Ping website every 5 hours
40
- async function pingEvery5Hours() {
41
- await pingWebsite();
42
- setInterval(async () => {
43
- await pingWebsite();
44
- }, 5 * 60 * 60 * 1000); // 5 hours in milliseconds
45
- }
46
-
47
- // Start pinging
48
- pingEvery5Hours();
49
-
50
-
51
-
52
 
53
  const tempDir = path.join(os.tmpdir(), "temp");
54
  const fss = fs.promises;
55
-
 
56
  (async () => {
57
  if (!fs.existsSync(tempDir)) {
58
  await fss.mkdir(tempDir, { recursive: true });
59
  }
60
  })();
61
-
62
  const youtube = google.youtube({ version: 'v3', auth: 'AIzaSyBPkpdJEGtAHebbaP3_CcA1_urfMFfeLLg' });
63
 
 
 
 
 
 
64
  app.use('/temp', express.static(tempDir));
65
  app.use(express.json());
 
66
  app.get("/", (req, res) => {
67
  res.type("json");
68
  const keluaran = {
@@ -76,355 +54,13 @@ app.get("/", (req, res) => {
76
  res.send(keluaran);
77
  });
78
 
 
79
  const generateRandomIP = () => {
80
  const octet = () => Math.floor(Math.random() * 256);
81
  return `${octet()}.${octet()}.${octet()}.${octet()}`;
82
  };
83
 
84
- async function igdl1(url) {
85
- const apiEndpoint = 'https://v3.igdownloader.app/api/ajaxSearch';
86
- const requestOptions = {
87
- headers: {
88
- 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
89
- 'Accept': '*/*',
90
- 'User-Agent': 'Mozilla/5.0 (Linux; Android 12; SM-S908B Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.58 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/357.0.0.23.115;]',
91
- 'Referer': 'https://igdownloader.app/en',
92
- 'X-Forwarded-For': generateRandomIP()
93
- },
94
- };
95
- const postData = `recaptchaToken=&q=${encodeURIComponent(url)}&t=media&lang=en`;
96
- try {
97
- const response = await axios.post(apiEndpoint, postData, requestOptions);
98
- const $ = cheerio.load(response.data.data);
99
- const downloadLinks = $('div.download-items__btn > a');
100
- return await Promise.all(downloadLinks.map(async (index, element) => $(element).attr('href')));
101
- } catch (error) {
102
- console.error('Instagram Downloader 1 - Error:', error.message);
103
- return null;
104
- }
105
- }
106
-
107
- async function igdl2(url) {
108
- try {
109
- const response = await axios.post('https://fastdl.app/c/', {
110
- url: url,
111
- lang_code: 'en',
112
- token: ''
113
- }, {
114
- headers: {
115
- 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
116
- 'Accept': '*/*',
117
- 'X-Requested-With': 'XMLHttpRequest',
118
- 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36',
119
- 'Referer': 'https://fastdl.app/'
120
- },
121
- responseType: 'arraybuffer'
122
- });
123
- const html = response.data.toString('utf-8');
124
- const $ = cheerio.load(html);
125
- $('img').remove();
126
- const links = [];
127
- $('a').each((index, element) => links.push($(element).attr('href')));
128
- return links;
129
- } catch (error) {
130
- console.error('Error downloading Instagram post:', error);
131
- return null;
132
- }
133
- }
134
-
135
- async function igdl3(url) {
136
- const apiEndpoint = 'https://co.wuk.sh/api/json';
137
- const requestData = { url, aFormat: 'mp3', filenamePattern: 'classic', dubLang: false, vQuality: '720' };
138
- const headers = {
139
- 'Accept': 'application/json',
140
- 'Content-Type': 'application/json',
141
- 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36',
142
- 'Referer': 'https://cobalt.tools/',
143
- };
144
- try {
145
- const response = await axios.post(apiEndpoint, requestData, { headers });
146
- const array_res = [];
147
- if (response.data.status === 'redirect') {
148
- array_res.push(response.data.url);
149
- } else if (response.data.status === 'picker') {
150
- response.data.picker.forEach(item => array_res.push(item.url));
151
- }
152
- return array_res;
153
- } catch (error) {
154
- console.error('Instagram Downloader 3 - Error:', error.message);
155
- return null;
156
- }
157
- }
158
-
159
- async function igdl4(url) {
160
- try {
161
- const apiEndpoint = 'https://v3.saveig.app/api/ajaxSearch';
162
- const requestOptions = {
163
- headers: {
164
- 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
165
- 'Accept': '*/*',
166
- 'User-Agent': 'Mozilla/5.0 (Linux; Android 12; SM-S908B Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.58 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/357.0.0.23.115;]',
167
- 'Referer': 'https://saveig.app/en',
168
- },
169
- };
170
- const postData = `recaptchaToken=&q=${encodeURIComponent(url)}&t=media&lang=en`;
171
- const response = await axios.post(apiEndpoint, postData, requestOptions);
172
- const $ = cheerio.load(response.data.data);
173
- const downloadLinks = $('div.download-items__btn > a');
174
- return await Promise.all(downloadLinks.map(async (index, element) => $(element).attr('href')));
175
- } catch (error) {
176
- console.error('Instagram Downloader 4 - Error:', error.message);
177
- return null;
178
- }
179
- }
180
-
181
- async function twdl1(url) {
182
- const apiUrl = 'https://savetwitter.net/api/ajaxSearch';
183
- const headers = {
184
- 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
185
- 'Accept': '*/*',
186
- 'X-Requested-With': 'XMLHttpRequest',
187
- 'User-Agent': 'Mozilla/5.0 (Linux; Android 12; SM-S908B Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.58 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/357.0.0.23.115;]',
188
- 'Referer': 'https://x2twitter.com/id',
189
- 'X-Forwarded-For': generateRandomIP(),
190
- };
191
- const data = `q=${encodeURIComponent(url)}&lang=id`;
192
-
193
- try {
194
- const response = await axios.post(apiUrl, data, {
195
- headers
196
- });
197
- if (!response.data.hasOwnProperty('data')) {
198
- throw new Error('Data tidak ditemukan di response');
199
- }
200
- const $ = cheerio.load(response.data.data);
201
-
202
- $('a[onclick="showAd()"][href="#"]').remove();
203
- $('a[href="/"]').remove();
204
- $('a[href="#"]').remove();
205
-
206
- const hrefs = [];
207
- $('.dl-action').each((index, element) => {
208
- const firstAnchor = $(element).find('a').first();
209
- hrefs.push(firstAnchor.attr('href'));
210
- });
211
-
212
- return hrefs;
213
-
214
- } catch (error) {
215
- throw new Error('Failed to fetch Twitter image: ' + error);
216
- }
217
- }
218
-
219
-
220
-
221
- async function twdl2(url) {
222
- const apiUrl = 'https://x2twitter.com/api/ajaxSearch';
223
- const headers = {
224
- 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
225
- 'Accept': '*/*',
226
- 'X-Requested-With': 'XMLHttpRequest',
227
- 'User-Agent': 'Mozilla/5.0 (Linux; Android 12; SM-S908B Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.58 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/357.0.0.23.115;]',
228
- 'Referer': 'https://x2twitter.com/id',
229
- 'X-Forwarded-For': generateRandomIP(),
230
- };
231
- const data = `q=${encodeURIComponent(url)}&lang=id`;
232
-
233
- try {
234
- const response = await axios.post(apiUrl, data, {
235
- headers
236
- });
237
- if (!response.data.hasOwnProperty('data')) {
238
- throw new Error('Data tidak ditemukan di response');
239
- }
240
- const $ = cheerio.load(response.data.data);
241
-
242
- $('a[onclick="showAd()"][href="#"]').remove();
243
- $('a[href="/"]').remove();
244
-
245
- const hrefs = [];
246
- $('.dl-action').each((index, element) => {
247
- const firstAnchor = $(element).find('a').first();
248
- hrefs.push(firstAnchor.attr('href'));
249
- });
250
-
251
- return hrefs;
252
-
253
- } catch (error) {
254
- throw new Error('Failed to fetch Twitter image: ' + error);
255
- }
256
- }
257
-
258
-
259
-
260
- async function twdl3(url) {
261
- const apiUrl = 'https://twtube.app/en/download?url=';
262
-
263
- const headers = {
264
- 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
265
- 'Accept': '*/*',
266
- 'X-Requested-With': 'XMLHttpRequest',
267
- 'User-Agent': 'Mozilla/5.0 (Linux; Android 12; SM-S908B Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.58 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/357.0.0.23.115;]',
268
- 'Referer': 'https://savetwitter.net/id/twitter-image-downloader',
269
- 'X-Forwarded-For': generateRandomIP(),
270
- };
271
- try {
272
- const response = await axios.get(apiUrl + url, {
273
- headers
274
- });
275
- const $ = cheerio.load(response.data);
276
- // Mendapatkan semua href dari elemen a dalam div dengan kelas 'square-box-btn'
277
- const allHrefs = [];
278
- // Menggunakan map untuk mengambil href dan mengembalikan array promise
279
- const promises = $('.square-box-btn a').map(async (index, element) => {
280
- let link = $(element).attr('href');
281
- return link;
282
- }).get();
283
- // Menunggu semua promise selesai dan mengumpulkan hasilnya
284
- return Promise.all(promises);
285
- } catch (error) {
286
- throw new Error('Failed to fetch Twitter image: ' + error);
287
- }
288
- }
289
-
290
- async function DownloadFile(url) {
291
- try {
292
- const response = await axios.get(url, { responseType: 'arraybuffer' });
293
- const mimeType = response.headers['content-type'];
294
- const buffer = response.data;
295
- const fileExtension = (await fileTypeFromBuffer(buffer)).ext;
296
- const filename = `downloaded_file_${Date.now()}.${fileExtension}`;
297
- const filePath = path.join(tempDir, filename);
298
- await fss.writeFile(filePath, buffer);
299
- console.log(`File berhasil diunduh dan disimpan sebagai: ${filePath}`);
300
- return { mimeType, filePath };
301
- } catch (error) {
302
- console.error('Error saat mengunduh file:', error);
303
- return null;
304
- }
305
- }
306
-
307
-
308
-
309
- const getInstagramDownloadLinks = async (url) => {
310
- let result = await igdl1(url);
311
- if (!result || result.length === 0) {
312
- result = await igdl2(url);
313
- }
314
- if (!result || result.length === 0) {
315
- result = await igdl3(url);
316
- }
317
- if (!result || result.length === 0) {
318
- result = await igdl4(url);
319
- }
320
- if (!result || result.length === 0) {
321
- result = {
322
- message: "all server error"
323
- };
324
- }
325
- return result;
326
- };
327
-
328
- const Twitter = async (url) => {
329
- let result = await twdl3(url);
330
- if (!result || result.length === 0) {
331
- result = await twdl2(url);
332
- }
333
- if (!result || result.length === 0) {
334
- result = await twdl1(url);
335
- }
336
- if (!result || result.length === 0) {
337
- result = {
338
- message: "all server error"
339
- };
340
- }
341
- return result;
342
- };
343
-
344
- app.get('/igdl', async (req, res) => {
345
- try {
346
- const { url } = req.query;
347
- if (!url) return res.status(400).json({ error: 'Parameter url is required' });
348
- if (!/https?:\/\/(www\.)?instagram\.com\/(p|reel|tv)/.test(url)) return res.status(400).json({ error: "Example: https://www.instagram.com/p/Cz1fTwMJFpx/?igsh=MXRrY2g4eWNucGoyZg==" });
349
- let result = await getInstagramDownloadLinks(url);
350
- let result_upload = {
351
- media: []
352
- }
353
-
354
- for (let item of result) {
355
- let unduh = await DownloadFile(item);
356
- result_upload.media.push({
357
- type: unduh.mimeType,
358
- path: unduh.filePath,
359
- url_path: `https://arashicode-api.hf.space/temp/${path.basename(unduh.filePath)}`
360
- });
361
- }
362
-
363
- res.json(result_upload);
364
-
365
- for (let item of result_upload.media) {
366
- try {
367
- await new Promise(resolve => setTimeout(resolve, 10 * 60 * 1000)); // 10 minutes
368
- await fss.unlink(item.path);
369
- console.log(`File ${item.path} deleted.`);
370
- } catch (error) {
371
- console.error(`Error deleting file ${item.path}:`, error);
372
- }
373
- }
374
- } catch (error) {
375
- console.error('Error processing request:', error);
376
- res.status(500).json({
377
- error: 'Failed to process request\n' + error
378
- });
379
- }
380
- });
381
-
382
- app.get('/twdl', async (req, res) => {
383
- try {
384
- const { url } = req.query;
385
- if (!url) return res.status(400).json({ error: 'Parameter url is required' });
386
- let result = await Twitter(url);
387
-
388
- let result_upload = {
389
- media: []
390
- }
391
-
392
- for (let item of result) {
393
- let unduh = await DownloadFile(item);
394
- result_upload.media.push({
395
- type: unduh.mimeType,
396
- path: unduh.filePath,
397
- url_path: `https://arashicode-api.hf.space/temp/${path.basename(unduh.filePath)}`,
398
- url_path2: `http://${os.hostname()}:${PORT}/temp/${path.basename(unduh.filePath)}`
399
- });
400
- }
401
-
402
- res.json(result_upload);
403
-
404
- for (let item of result_upload.media) {
405
- try {
406
- await new Promise(resolve => setTimeout(resolve, 10 * 60 * 1000)); // 10 minutes
407
- await fss.unlink(item.path);
408
- console.log(`File ${item.path} deleted.`);
409
- } catch (error) {
410
- console.error(`Error deleting file ${item.path}:`, error);
411
- }
412
- }
413
-
414
- } catch (error) {
415
- console.error('Error processing request:', error);
416
- res.status(500).json({
417
- error: 'Failed to process request\n' + error
418
- });
419
- }
420
- });
421
-
422
- /****
423
- YTMP3
424
- YTMP3
425
- YTMP3
426
- YTMP3
427
- *****/
428
  async function uploader(buffer) {
429
  const { ext } = await fileTypeFromBuffer(buffer);
430
  const bodyForm = new FormData();
@@ -442,6 +78,7 @@ async function uploader(buffer) {
442
  };
443
  }
444
 
 
445
  async function getHDThumbnailUrl(videoId) {
446
  try {
447
  const response = await youtube.videos.list({ part: 'snippet', id: videoId });
@@ -452,6 +89,7 @@ async function getHDThumbnailUrl(videoId) {
452
  }
453
  }
454
 
 
455
  async function GetId(data) {
456
  const regex = /(?:https?:\/\/)?(?:www\.)?(?:youtu(?:be\.com\/(?:watch\?(?:v=|vi=)|v\/|vi\/)|\.be\/|be\.com\/embed\/|be\.com\/shorts\/)|youtube\.com\/\?(?:v=|vi=))([\w-]{11})/;
457
  const res = regex.exec(data);
@@ -459,21 +97,45 @@ async function GetId(data) {
459
  throw new Error("Please check the URL you have entered");
460
  }
461
 
 
462
  async function addAudioTags(media, title, artist, year, imagecover) {
463
  try {
464
- let audioBuffer = (typeof media === 'string') ? Buffer.from((await axios.get(media, { responseType: 'arraybuffer', maxContentLength: -1 })).data) : (media instanceof Buffer) ? media : (() => { throw new Error('Media harus berupa URL string atau Buffer.'); })();
 
 
 
 
 
 
 
 
 
465
  const randomFilename = generateRandomName(10) + '.mp3';
466
  const tmpFilePath = path.join(tempDir, randomFilename);
467
  fs.writeFileSync(tmpFilePath, audioBuffer);
 
468
  const tags = { title, artist, year };
469
  if (typeof imagecover === 'string') {
470
- const coverBuffer = Buffer.from((await axios.get(imagecover, { responseType: 'arraybuffer' })).data);
471
- tags.image = { mime: 'image/jpeg', type: { id: 3, name: 'Front Cover' }, description: 'Cover', imageBuffer: coverBuffer };
 
 
 
 
 
 
472
  } else if (imagecover instanceof Buffer) {
473
- tags.image = { mime: 'image/jpeg', type: { id: 3, name: 'Front Cover' }, description: 'Cover', imageBuffer: imagecover };
 
 
 
 
 
474
  }
 
475
  const success = nodeID3.write(tags, tmpFilePath);
476
  console[success ? 'log' : 'error'](success ? 'Tag ID3 berhasil diubah!' : 'Gagal mengubah tag ID3.');
 
477
  return { msg: `Audio berhasil diubah.`, path: `${tmpFilePath}` };
478
  } catch (error) {
479
  console.error('Terjadi kesalahan:', error);
@@ -481,6 +143,7 @@ async function addAudioTags(media, title, artist, year, imagecover) {
481
  }
482
  }
483
 
 
484
  function generateRandomName(length) {
485
  const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
486
  let randomName = '';
@@ -490,12 +153,15 @@ function generateRandomName(length) {
490
  return randomName;
491
  }
492
 
 
493
  async function getAudioMP3Url(videoUrl) {
494
  try {
495
  const info = await ytdl.getInfo(videoUrl);
496
  const audioFormat = ytdl.chooseFormat(info.formats, { filter: 'audioonly', quality: 'highestaudio' });
497
  const path_audio = path.join(tempDir, generateRandomName(10) + '.mp3');
498
  let uploadResult;
 
 
499
  await new Promise((resolve, reject) => {
500
  ffmpeg()
501
  .input(audioFormat.url)
@@ -504,16 +170,19 @@ async function getAudioMP3Url(videoUrl) {
504
  .outputOptions('-ab 128k')
505
  .outputOptions('-ar 44100')
506
  .on('end', async () => {
507
- const buffer = fs.readFileSync(path_audio);
508
- const id_video = await GetId(videoUrl);
509
- const hd_thumbnail = await getHDThumbnailUrl(id_video);
510
- const convert = await addAudioTags(buffer, info.videoDetails.title, info.videoDetails.ownerChannelName, 2024, hd_thumbnail);
511
- const buffer2 = fs.readFileSync(convert.path);
512
- uploadResult = await uploader(buffer2);
513
- console.log('Upload result:', uploadResult);
514
- fs.unlinkSync(path_audio);
515
- fs.unlinkSync(convert.path);
516
- resolve(uploadResult);
 
 
 
517
  })
518
  .on('error', (err) => {
519
  console.error('FFmpeg conversion error:', err);
@@ -521,19 +190,177 @@ async function getAudioMP3Url(videoUrl) {
521
  })
522
  .save(path_audio);
523
  });
524
- return uploadResult;
 
 
 
 
 
 
 
 
 
525
  } catch (error) {
526
  console.error('Error:', error);
527
  throw new Error('Failed to process audio URL');
528
  }
529
  }
530
 
 
531
  app.get('/ytmp3', async (req, res) => {
532
  try {
533
  const { url } = req.query;
534
  if (!url) return res.status(400).json({ error: 'Parameter url is required' });
 
535
  let result = await getAudioMP3Url(url);
536
  res.json(result);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
537
  } catch (error) {
538
  console.error('Error processing request:', error);
539
  res.status(500).json({
@@ -542,7 +369,160 @@ app.get('/ytmp3', async (req, res) => {
542
  }
543
  });
544
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
545
 
546
  app.listen(PORT, () => {
547
- console.log(`Server is running on port https://localhost:${PORT}`);
548
  });
 
9
  import nodeID3 from 'node-id3';
10
  import ytdl from 'ytdl-core';
11
  import FormData from 'form-data';
12
+ import fetch from 'node-fetch';
13
+
14
  const require = createRequire(import.meta.url);
15
  const fs = require('fs');
16
  const path = require('path');
17
  const { google } = require('googleapis');
18
+ const puppeteer = require('puppeteer');
19
+
20
  const PORT = process.env.PORT || 7860;
21
  const app = express();
22
  const readFileAsync = promisify(fs.readFile);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
  const tempDir = path.join(os.tmpdir(), "temp");
25
  const fss = fs.promises;
26
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
27
+ // Membuat direktori sementara jika belum ada
28
  (async () => {
29
  if (!fs.existsSync(tempDir)) {
30
  await fss.mkdir(tempDir, { recursive: true });
31
  }
32
  })();
33
+ const execPromise = promisify(exec);
34
  const youtube = google.youtube({ version: 'v3', auth: 'AIzaSyBPkpdJEGtAHebbaP3_CcA1_urfMFfeLLg' });
35
 
36
+
37
+
38
+ const tempDirBase = tempDir
39
+
40
+
41
  app.use('/temp', express.static(tempDir));
42
  app.use(express.json());
43
+
44
  app.get("/", (req, res) => {
45
  res.type("json");
46
  const keluaran = {
 
54
  res.send(keluaran);
55
  });
56
 
57
+ // Fungsi untuk menghasilkan IP acak
58
  const generateRandomIP = () => {
59
  const octet = () => Math.floor(Math.random() * 256);
60
  return `${octet()}.${octet()}.${octet()}.${octet()}`;
61
  };
62
 
63
+ // Fungsi untuk upload file
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  async function uploader(buffer) {
65
  const { ext } = await fileTypeFromBuffer(buffer);
66
  const bodyForm = new FormData();
 
78
  };
79
  }
80
 
81
+ // Fungsi untuk mendapatkan URL thumbnail HD
82
  async function getHDThumbnailUrl(videoId) {
83
  try {
84
  const response = await youtube.videos.list({ part: 'snippet', id: videoId });
 
89
  }
90
  }
91
 
92
+ // Fungsi untuk mendapatkan ID video dari URL
93
  async function GetId(data) {
94
  const regex = /(?:https?:\/\/)?(?:www\.)?(?:youtu(?:be\.com\/(?:watch\?(?:v=|vi=)|v\/|vi\/)|\.be\/|be\.com\/embed\/|be\.com\/shorts\/)|youtube\.com\/\?(?:v=|vi=))([\w-]{11})/;
95
  const res = regex.exec(data);
 
97
  throw new Error("Please check the URL you have entered");
98
  }
99
 
100
+ // Fungsi untuk menambahkan tag ID3 ke file audio
101
  async function addAudioTags(media, title, artist, year, imagecover) {
102
  try {
103
+ let audioBuffer;
104
+ if (typeof media === 'string') {
105
+ const response = await axios.get(media, { responseType: 'arraybuffer', maxContentLength: -1 });
106
+ audioBuffer = Buffer.from(response.data);
107
+ } else if (media instanceof Buffer) {
108
+ audioBuffer = media;
109
+ } else {
110
+ throw new Error('Media harus berupa URL string atau Buffer.');
111
+ }
112
+
113
  const randomFilename = generateRandomName(10) + '.mp3';
114
  const tmpFilePath = path.join(tempDir, randomFilename);
115
  fs.writeFileSync(tmpFilePath, audioBuffer);
116
+
117
  const tags = { title, artist, year };
118
  if (typeof imagecover === 'string') {
119
+ const response = await axios.get(imagecover, { responseType: 'arraybuffer' });
120
+ const coverBuffer = Buffer.from(response.data);
121
+ tags.image = {
122
+ mime: 'image/jpeg',
123
+ type: { id: 3, name: 'Front Cover' },
124
+ description: 'Cover',
125
+ imageBuffer: coverBuffer
126
+ };
127
  } else if (imagecover instanceof Buffer) {
128
+ tags.image = {
129
+ mime: 'image/jpeg',
130
+ type: { id: 3, name: 'Front Cover' },
131
+ description: 'Cover',
132
+ imageBuffer: imagecover
133
+ };
134
  }
135
+
136
  const success = nodeID3.write(tags, tmpFilePath);
137
  console[success ? 'log' : 'error'](success ? 'Tag ID3 berhasil diubah!' : 'Gagal mengubah tag ID3.');
138
+
139
  return { msg: `Audio berhasil diubah.`, path: `${tmpFilePath}` };
140
  } catch (error) {
141
  console.error('Terjadi kesalahan:', error);
 
143
  }
144
  }
145
 
146
+ // Fungsi untuk menghasilkan nama acak
147
  function generateRandomName(length) {
148
  const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
149
  let randomName = '';
 
153
  return randomName;
154
  }
155
 
156
+ // Fungsi untuk mendapatkan URL audio MP3 dari video YouTube
157
  async function getAudioMP3Url(videoUrl) {
158
  try {
159
  const info = await ytdl.getInfo(videoUrl);
160
  const audioFormat = ytdl.chooseFormat(info.formats, { filter: 'audioonly', quality: 'highestaudio' });
161
  const path_audio = path.join(tempDir, generateRandomName(10) + '.mp3');
162
  let uploadResult;
163
+ let convert;
164
+
165
  await new Promise((resolve, reject) => {
166
  ffmpeg()
167
  .input(audioFormat.url)
 
170
  .outputOptions('-ab 128k')
171
  .outputOptions('-ar 44100')
172
  .on('end', async () => {
173
+ try {
174
+ const buffer = fs.readFileSync(path_audio);
175
+ const id_video = await GetId(videoUrl);
176
+ const hd_thumbnail = await getHDThumbnailUrl(id_video);
177
+ convert = await addAudioTags(buffer, info.videoDetails.title, info.videoDetails.ownerChannelName, 2024, hd_thumbnail);
178
+ const buffer2 = fs.readFileSync(convert.path);
179
+
180
+ fs.unlinkSync(path_audio);
181
+
182
+ resolve();
183
+ } catch (error) {
184
+ reject(error);
185
+ }
186
  })
187
  .on('error', (err) => {
188
  console.error('FFmpeg conversion error:', err);
 
190
  })
191
  .save(path_audio);
192
  });
193
+
194
+ return {
195
+ status: 200,
196
+ title: info.videoDetails.title,
197
+ result: {
198
+ url_path: `https://${process.env.SPACE_HOST}/temp/${path.basename(convert.path)}`,
199
+ curl_path: `https://${process.env.SPACE_HOST}/temp/${path.basename(convert.path)}?download=1&filename=${info.videoDetails.title}`,
200
+ path: convert.path
201
+ }
202
+ };
203
  } catch (error) {
204
  console.error('Error:', error);
205
  throw new Error('Failed to process audio URL');
206
  }
207
  }
208
 
209
+ // Endpoint untuk mengunduh audio MP3 dari YouTube
210
  app.get('/ytmp3', async (req, res) => {
211
  try {
212
  const { url } = req.query;
213
  if (!url) return res.status(400).json({ error: 'Parameter url is required' });
214
+
215
  let result = await getAudioMP3Url(url);
216
  res.json(result);
217
+
218
+ // Menghapus file setelah 10 menit
219
+ try {
220
+ await new Promise(resolve => setTimeout(resolve, 10 * 60 * 1000)); // 10 minutes
221
+ await fss.unlink(result.result.path);
222
+ console.log(`File ${result.result.path} deleted.`);
223
+ } catch (error) {
224
+ console.error(`Error deleting file ${result.result.path}:`, error);
225
+ }
226
+ } catch (error) {
227
+ console.error('Error processing request:', error);
228
+ res.status(500).json({
229
+ error: 'Failed to process request\n' + error
230
+ });
231
+ }
232
+ });
233
+
234
+
235
+ /*
236
+ ┏┓┏━┓┏━━━┓┏━┓┏━┓┏━━┓┏┓┏━┓┏┓╋┏┓
237
+ ┃┃┃┏┛┃┏━┓┃┃┃┗┛┃┃┗┫┣┛┃┃┃┏┛┃┃╋┃┃
238
+ ┃┗┛┛╋┃┃╋┃┃┃┏┓┏┓┃╋┃┃╋┃┗┛┛╋┃┃╋┃┃
239
+ ┃┏┓┃╋┃┃╋┃┃┃┃┃┃┃┃╋┃┃╋┃┏┓┃╋┃┃╋┃┃
240
+ ┃┃┃┗┓┃┗━┛┃┃┃┃┃┃┃┏┫┣┓┃┃┃┗┓┃┗━┛┃
241
+ ┗┛┗━┛┗━━━┛┗┛┗┛┗┛┗━━┛┗┛┗━┛┗━━━┛
242
+ */
243
+
244
+ function generateRandomID(length = 8) {
245
+ const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
246
+ let result = '';
247
+ for (let i = 0; i < length; i++) {
248
+ result += characters.charAt(Math.floor(Math.random() * characters.length));
249
+ }
250
+ return result;
251
+ }
252
+
253
+ async function komiku_download(url) {
254
+ const instanceID = generateRandomID();
255
+ const tempDir = path.join(tempDirBase, instanceID);
256
+ await fss.mkdir(tempDir);
257
+
258
+ // Extracting the title from the URL
259
+ const title = url.split('/').filter(part => part).pop();
260
+
261
+ try {
262
+ const response = await axios.get(url);
263
+ const html = response.data;
264
+ const $ = cheerio.load(html);
265
+ const imgList = [];
266
+
267
+ $('#Baca_Komik img').each((index, element) => {
268
+ const src = $(element).attr('src');
269
+ imgList.push({ path: src });
270
+ });
271
+
272
+ await processImages(imgList, tempDir, instanceID);
273
+ const pdfPath = await createPDF(instanceID, tempDir);
274
+
275
+ console.log(`PDF berhasil dibuat: ${pdfPath}`);
276
+ return { path: pdfPath, title: title, url: `https://${process.env.SPACE_HOST}/temp/${path.basename(pdfPathq)}` };
277
+ } catch (error) {
278
+ console.log(error);
279
+ throw error;
280
+ } finally {
281
+ await fss.rmdir(tempDir, { recursive: true });
282
+ }
283
+ }
284
+
285
+ async function downloadImage(image, tempDir, instanceID) {
286
+ const response = await axios.get(image.path, { responseType: 'arraybuffer' });
287
+ const imagePath = path.join(tempDir, `image_${instanceID}_${Date.now()}_${Math.floor(Math.random() * 1000)}.jpg`);
288
+ await writeFileAsync(imagePath, response.data);
289
+
290
+ const imageHeight = await getImageHeight(imagePath);
291
+ const command = `convert ${imagePath} -resize 720x -quality 75 -background white -gravity center -extent 720x${imageHeight} ${imagePath}`;
292
+ await execPromise(command);
293
+
294
+ return imagePath;
295
+ }
296
+
297
+ async function getImageHeight(imagePath) {
298
+ const { stdout } = await execPromise(`identify -format "%h" ${imagePath}`);
299
+ return parseInt(stdout.trim());
300
+ }
301
+
302
+ async function processImages(imgList, tempDir, instanceID) {
303
+ let partIndex = 0;
304
+ let partImages = [];
305
+
306
+ for (let i = 0; i < imgList.length; i++) {
307
+ const imagePath = await downloadImage(imgList[i], tempDir, instanceID);
308
+ partImages.push(imagePath);
309
+
310
+ if (await getTotalHeight(partImages) > 64000) {
311
+ await combineAndSave(partImages, partIndex, tempDir);
312
+ partImages = [];
313
+ partIndex++;
314
+ }
315
+ }
316
+
317
+ if (partImages.length > 0) {
318
+ await combineAndSave(partImages, partIndex, tempDir);
319
+ }
320
+ }
321
+
322
+ async function getTotalHeight(imagePaths) {
323
+ let totalHeight = 0;
324
+ for (const imagePath of imagePaths) {
325
+ totalHeight += await getImageHeight(imagePath);
326
+ }
327
+ return totalHeight;
328
+ }
329
+
330
+ async function combineAndSave(imagePaths, partIndex, tempDir) {
331
+ const combinedImagePath = path.join(tempDir, `combined_part_${partIndex}.jpg`);
332
+ const command = `convert ${imagePaths.join(' ')} -append -quality 75 ${combinedImagePath}`;
333
+ await execPromise(command);
334
+
335
+ imagePaths.forEach(fs.unlinkSync);
336
+
337
+ return combinedImagePath;
338
+ }
339
+
340
+ async function createPDF(instanceID, tempDir) {
341
+ const combinedParts = fs.readdirSync(tempDir).filter(file => file.startsWith('combined_part_'));
342
+ const pdfPath = path.join(tempDir, `${instanceID}.pdf`);
343
+ const createPDFCommand = `convert ${combinedParts.map(file => path.join(tempDir, file)).join(' ')} ${pdfPath}`;
344
+ await execPromise(createPDFCommand);
345
+ return pdfPath;
346
+ }
347
+
348
+ app.get('/komiku/download', async (req, res) => {
349
+ try {
350
+ const { url } = req.query;
351
+ if (!url) return res.status(400).json({ error: 'Parameter url is required' });
352
+
353
+ let result = await komiku_download(url);
354
+ res.json(result);
355
+
356
+ // Menghapus file setelah 10 menit
357
+ try {
358
+ await new Promise(resolve => setTimeout(resolve, 10 * 60 * 1000)); // 10 minutes
359
+ await fss.unlink(result.path);
360
+ console.log(`File ${result.path} deleted.`);
361
+ } catch (error) {
362
+ console.error(`Error deleting file ${result.path}:`, error);
363
+ }
364
  } catch (error) {
365
  console.error('Error processing request:', error);
366
  res.status(500).json({
 
369
  }
370
  });
371
 
372
+ /***********/
373
+
374
+ async function getLatestKomik(page) {
375
+ const url = `https://api.komiku.id/manga/page/${page}/`;
376
+ const headers = {
377
+ 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Mobile Safari/537.36',
378
+ 'Referer': 'https://komiku.id/pustaka/'
379
+ };
380
+
381
+ try {
382
+ const response = await axios.get(url, { headers });
383
+ const $ = cheerio.load(response.data);
384
+ const mangaArray = [];
385
+
386
+ // Scraping data
387
+ $('.bge').each((index, element) => {
388
+ const title = $(element).find('.kan h3').text().trim();
389
+ const link_komik = $(element).find('.bgei a').attr('href');
390
+ const imgSrc = $(element).find('.bgei img').attr('src');
391
+ const type = $(element).find('.tpe1_inf b').text().trim();
392
+ const type2 = $(element).find('.tpe1_inf').text().trim();
393
+ const description = $(element).find('.kan p').text().trim();
394
+ const readersInfo = $(element).find('.judul2').text().trim();
395
+ const latestChapter = "https://komiku.id" + $(element).find('.new1:last-child a').attr('href');
396
+
397
+ mangaArray.push({
398
+ title,
399
+ link_komik,
400
+ imgSrc,
401
+ type,
402
+ type2,
403
+ description,
404
+ readersInfo,
405
+ latestChapter
406
+ });
407
+ });
408
+
409
+ return mangaArray;
410
+ } catch (error) {
411
+ console.error('Error fetching the URL', error);
412
+ throw error;
413
+ }
414
+ }
415
+
416
+ async function GetKomik(url) {
417
+ try {
418
+ const response = await axios.get(url);
419
+ const $ = cheerio.load(response.data);
420
+
421
+ const cover = $('#Informasi > div > img').attr('src');
422
+ const judul = $('#Informasi > table > tbody > tr:nth-child(1) > td:nth-child(2)').text().trim();
423
+ const jenis = $('#Informasi > table > tbody > tr:nth-child(3) > td:nth-child(2) > b').text().trim();
424
+ const konsepCerita = $('#Informasi > table > tbody > tr:nth-child(4) > td:nth-child(2)').text().trim();
425
+ const author = $('#Informasi > table > tbody > tr:nth-child(5) > td:nth-child(2)').text().trim();
426
+ const status = $('#Informasi > table > tbody > tr:nth-child(6) > td:nth-child(2)').text().trim();
427
+ const sinopsis = $('#Judul > p.desc').text().trim();
428
+
429
+ const genreElements = $('#Informasi > ul > li').map((i, el) => $(el).text().trim()).get();
430
+
431
+ const chapterElements = $('#daftarChapter > tr').map((i, el) => {
432
+ if (i === 0) {
433
+ return null;
434
+ }
435
+ return {
436
+ judulSeries: $(el).find('td.judulseries > a').text().trim(),
437
+ tanggalUpdate: $(el).find('td.tanggalseries').text().trim(),
438
+ url: "https://komiku.id" + $(el).find('td.judulseries > a').attr('href')
439
+ };
440
+ }).get().filter(chapter => chapter !== null);
441
+
442
+ const mangaInfo = {
443
+ cover,
444
+ judul,
445
+ sinopsis,
446
+ jenis,
447
+ konsepCerita,
448
+ author,
449
+ status,
450
+ genres: genreElements,
451
+ chapters: chapterElements
452
+ };
453
+
454
+ return mangaInfo;
455
+ } catch (error) {
456
+ console.error('Error fetching the URL', error);
457
+ throw error;
458
+ }
459
+ }
460
+
461
+
462
+ app.get('/komiku/latest', async (req, res) => {
463
+ try {
464
+ const { page } = req.query;
465
+ if (!page) return res.status(400).json({ error: 'Parameter page is required' });
466
+
467
+ let result = await getLatestKomik(page);
468
+ res.json(result);
469
+ } catch (error) {
470
+ console.error('Error processing request:', error);
471
+ res.status(500).json({
472
+ error: 'Failed to process request\n' + error
473
+ });
474
+ }
475
+ });
476
+
477
+ app.get('/komiku', async (req, res) => {
478
+ try {
479
+ const { url } = req.query;
480
+ if (!url) return res.status(400).json({ error: 'Parameter url is required' });
481
+
482
+ let result = await GetKomik(url);
483
+ res.json(result);
484
+ } catch (error) {
485
+ console.error('Error processing request:', error);
486
+ res.status(500).json({
487
+ error: 'Failed to process request\n' + error
488
+ });
489
+ }
490
+ });
491
+
492
+
493
+ /*******************
494
+ ┏┓┏━┓┏━━━┓┏━┓┏━┓┏━━┓┏┓┏━┓┏┓╋┏┓
495
+ ┃┃┃┏┛┃┏━┓┃┃┃┗┛┃┃┗┫┣┛┃┃┃┏┛┃┃╋┃┃
496
+ ┃┗┛┛╋┃┃╋┃┃┃┏┓┏┓┃╋┃┃╋┃┗┛┛╋┃┃╋┃┃
497
+ ┃┏┓┃╋┃┃╋┃┃┃┃┃┃┃┃╋┃┃╋┃┏┓┃╋┃┃╋┃┃
498
+ ┃┃┃┗┓┃┗━┛┃┃┃┃┃┃┃┏┫┣┓┃┃┃┗┓┃┗━┛┃
499
+ ┗┛┗━┛┗━━━┛┗┛┗┛┗┛┗━━┛┗┛┗━┛┗━━━┛
500
+ *********************/
501
+
502
+ // Fungsi untuk ping website
503
+ async function pingWebsite() {
504
+ const browser = await puppeteer.launch({
505
+ headless: true,
506
+ args: ['--no-sandbox', '--disable-setuid-sandbox']
507
+ });
508
+ const page = await browser.newPage();
509
+ await page.setUserAgent("Mozilla/5.0 (Linux; Android 10; SM-G965U Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.141 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/420.0.0.32.61;]");
510
+ await page.goto('https://huggingface.co/spaces/ArashiCode/api');
511
+ console.log("Ping");
512
+ await browser.close();
513
+ }
514
+
515
+ // Ping website setiap 5 jam
516
+ async function pingEvery5Hours() {
517
+ await pingWebsite();
518
+ setInterval(async () => {
519
+ await pingWebsite();
520
+ }, 5 * 60 * 60 * 1000); // 5 hours in milliseconds
521
+ }
522
+
523
+ // Mulai ping
524
+ pingEvery5Hours();
525
 
526
  app.listen(PORT, () => {
527
+ console.log(`Server is running on port ${PORT}`);
528
  });