Spaces:
Sleeping
Sleeping
Update index.js
Browse files
index.js
CHANGED
@@ -1,247 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import axios from 'axios';
|
2 |
import cheerio from 'cheerio';
|
3 |
-
import { createRequire } from 'module';
|
4 |
-
import os from 'os';
|
5 |
-
import express from 'express';
|
6 |
import { promisify } from 'util';
|
7 |
-
|
8 |
-
import ffmpeg from 'fluent-ffmpeg';
|
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 |
import { fileURLToPath } from 'url';
|
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 { exec } = require('child_process');
|
34 |
const writeFileAsync = promisify(fs.writeFile);
|
35 |
-
const
|
36 |
-
const youtube = google.youtube({ version: 'v3', auth: 'AIzaSyBPkpdJEGtAHebbaP3_CcA1_urfMFfeLLg' });
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
const tempDirBase = tempDir
|
41 |
-
|
42 |
-
|
43 |
-
app.use('/temp', express.static(tempDir));
|
44 |
-
app.use(express.json());
|
45 |
-
|
46 |
-
app.get("/", (req, res) => {
|
47 |
-
res.type("json");
|
48 |
-
const keluaran = {
|
49 |
-
success: true,
|
50 |
-
author: "Nex",
|
51 |
-
data: {
|
52 |
-
igdl: "/igdl",
|
53 |
-
twdl: "/twdl"
|
54 |
-
},
|
55 |
-
};
|
56 |
-
res.send(keluaran);
|
57 |
-
});
|
58 |
-
|
59 |
-
// Fungsi untuk menghasilkan IP acak
|
60 |
-
const generateRandomIP = () => {
|
61 |
-
const octet = () => Math.floor(Math.random() * 256);
|
62 |
-
return `${octet()}.${octet()}.${octet()}.${octet()}`;
|
63 |
-
};
|
64 |
-
|
65 |
-
// Fungsi untuk upload file
|
66 |
-
async function uploader(buffer) {
|
67 |
-
const { ext } = await fileTypeFromBuffer(buffer);
|
68 |
-
const bodyForm = new FormData();
|
69 |
-
bodyForm.append('file', buffer, `file.${ext}`);
|
70 |
-
|
71 |
-
const response = await fetch('https://aemt.me/api/upload.php', {
|
72 |
-
method: 'POST',
|
73 |
-
body: bodyForm,
|
74 |
-
});
|
75 |
-
|
76 |
-
return {
|
77 |
-
status: response.status,
|
78 |
-
creator: 'Nex',
|
79 |
-
result: await response.json(),
|
80 |
-
};
|
81 |
-
}
|
82 |
-
|
83 |
-
// Fungsi untuk mendapatkan URL thumbnail HD
|
84 |
-
async function getHDThumbnailUrl(videoId) {
|
85 |
-
try {
|
86 |
-
const response = await youtube.videos.list({ part: 'snippet', id: videoId });
|
87 |
-
return response.data.items[0].snippet.thumbnails.maxres.url;
|
88 |
-
} catch (error) {
|
89 |
-
console.error('Error fetching HD thumbnail URL:', error.message);
|
90 |
-
return null;
|
91 |
-
}
|
92 |
-
}
|
93 |
-
|
94 |
-
// Fungsi untuk mendapatkan ID video dari URL
|
95 |
-
async function GetId(data) {
|
96 |
-
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})/;
|
97 |
-
const res = regex.exec(data);
|
98 |
-
if (res && res[1]) return res[1];
|
99 |
-
throw new Error("Please check the URL you have entered");
|
100 |
-
}
|
101 |
-
|
102 |
-
// Fungsi untuk menambahkan tag ID3 ke file audio
|
103 |
-
async function addAudioTags(media, title, artist, year, imagecover) {
|
104 |
-
try {
|
105 |
-
let audioBuffer;
|
106 |
-
if (typeof media === 'string') {
|
107 |
-
const response = await axios.get(media, { responseType: 'arraybuffer', maxContentLength: -1 });
|
108 |
-
audioBuffer = Buffer.from(response.data);
|
109 |
-
} else if (media instanceof Buffer) {
|
110 |
-
audioBuffer = media;
|
111 |
-
} else {
|
112 |
-
throw new Error('Media harus berupa URL string atau Buffer.');
|
113 |
-
}
|
114 |
-
|
115 |
-
const randomFilename = generateRandomName(10) + '.mp3';
|
116 |
-
const tmpFilePath = path.join(tempDir, randomFilename);
|
117 |
-
fs.writeFileSync(tmpFilePath, audioBuffer);
|
118 |
-
|
119 |
-
const tags = { title, artist, year };
|
120 |
-
if (typeof imagecover === 'string') {
|
121 |
-
const response = await axios.get(imagecover, { responseType: 'arraybuffer' });
|
122 |
-
const coverBuffer = Buffer.from(response.data);
|
123 |
-
tags.image = {
|
124 |
-
mime: 'image/jpeg',
|
125 |
-
type: { id: 3, name: 'Front Cover' },
|
126 |
-
description: 'Cover',
|
127 |
-
imageBuffer: coverBuffer
|
128 |
-
};
|
129 |
-
} else if (imagecover instanceof Buffer) {
|
130 |
-
tags.image = {
|
131 |
-
mime: 'image/jpeg',
|
132 |
-
type: { id: 3, name: 'Front Cover' },
|
133 |
-
description: 'Cover',
|
134 |
-
imageBuffer: imagecover
|
135 |
-
};
|
136 |
-
}
|
137 |
-
|
138 |
-
const success = nodeID3.write(tags, tmpFilePath);
|
139 |
-
console[success ? 'log' : 'error'](success ? 'Tag ID3 berhasil diubah!' : 'Gagal mengubah tag ID3.');
|
140 |
-
|
141 |
-
return { msg: `Audio berhasil diubah.`, path: `${tmpFilePath}` };
|
142 |
-
} catch (error) {
|
143 |
-
console.error('Terjadi kesalahan:', error);
|
144 |
-
throw new Error('Terjadi kesalahan saat mengubah audio.');
|
145 |
-
}
|
146 |
-
}
|
147 |
-
|
148 |
-
// Fungsi untuk menghasilkan nama acak
|
149 |
-
function generateRandomName(length) {
|
150 |
-
const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
151 |
-
let randomName = '';
|
152 |
-
for (let i = 0; i < length; i++) {
|
153 |
-
randomName += characters.charAt(Math.floor(Math.random() * characters.length));
|
154 |
-
}
|
155 |
-
return randomName;
|
156 |
-
}
|
157 |
-
|
158 |
-
// Fungsi untuk mendapatkan URL audio MP3 dari video YouTube
|
159 |
-
async function getAudioMP3Url(videoUrl) {
|
160 |
-
try {
|
161 |
-
const info = await ytdl.getInfo(videoUrl);
|
162 |
-
const audioFormat = ytdl.chooseFormat(info.formats, { filter: 'audioonly', quality: 'highestaudio' });
|
163 |
-
const path_audio = path.join(tempDir, generateRandomName(10) + '.mp3');
|
164 |
-
let uploadResult;
|
165 |
-
let convert;
|
166 |
-
|
167 |
-
await new Promise((resolve, reject) => {
|
168 |
-
ffmpeg()
|
169 |
-
.input(audioFormat.url)
|
170 |
-
.outputOptions('-f mp3')
|
171 |
-
.outputOptions('-acodec libmp3lame')
|
172 |
-
.outputOptions('-ab 128k')
|
173 |
-
.outputOptions('-ar 44100')
|
174 |
-
.on('end', async () => {
|
175 |
-
try {
|
176 |
-
const buffer = fs.readFileSync(path_audio);
|
177 |
-
const id_video = await GetId(videoUrl);
|
178 |
-
const hd_thumbnail = await getHDThumbnailUrl(id_video);
|
179 |
-
convert = await addAudioTags(buffer, info.videoDetails.title, info.videoDetails.ownerChannelName, 2024, hd_thumbnail);
|
180 |
-
const buffer2 = fs.readFileSync(convert.path);
|
181 |
-
|
182 |
-
fs.unlinkSync(path_audio);
|
183 |
-
|
184 |
-
resolve();
|
185 |
-
} catch (error) {
|
186 |
-
reject(error);
|
187 |
-
}
|
188 |
-
})
|
189 |
-
.on('error', (err) => {
|
190 |
-
console.error('FFmpeg conversion error:', err);
|
191 |
-
reject(err);
|
192 |
-
})
|
193 |
-
.save(path_audio);
|
194 |
-
});
|
195 |
-
|
196 |
-
return {
|
197 |
-
status: 200,
|
198 |
-
title: info.videoDetails.title,
|
199 |
-
result: {
|
200 |
-
url_path: `https://${process.env.SPACE_HOST}/temp/${path.basename(convert.path)}`,
|
201 |
-
curl_path: `https://${process.env.SPACE_HOST}/temp/${path.basename(convert.path)}?download=1&filename=${info.videoDetails.title}`,
|
202 |
-
path: convert.path
|
203 |
-
}
|
204 |
-
};
|
205 |
-
} catch (error) {
|
206 |
-
console.error('Error:', error);
|
207 |
-
throw new Error('Failed to process audio URL');
|
208 |
-
}
|
209 |
-
}
|
210 |
-
|
211 |
-
// Endpoint untuk mengunduh audio MP3 dari YouTube
|
212 |
-
app.get('/ytmp3', async (req, res) => {
|
213 |
-
try {
|
214 |
-
const { url } = req.query;
|
215 |
-
if (!url) return res.status(400).json({ error: 'Parameter url is required' });
|
216 |
-
|
217 |
-
let result = await getAudioMP3Url(url);
|
218 |
-
res.json(result);
|
219 |
-
|
220 |
-
// Menghapus file setelah 10 menit
|
221 |
-
try {
|
222 |
-
await new Promise(resolve => setTimeout(resolve, 10 * 60 * 1000)); // 10 minutes
|
223 |
-
await fss.unlink(result.result.path);
|
224 |
-
console.log(`File ${result.result.path} deleted.`);
|
225 |
-
} catch (error) {
|
226 |
-
console.error(`Error deleting file ${result.result.path}:`, error);
|
227 |
-
}
|
228 |
-
} catch (error) {
|
229 |
-
console.error('Error processing request:', error);
|
230 |
-
res.status(500).json({
|
231 |
-
error: 'Failed to process request\n' + error
|
232 |
-
});
|
233 |
-
}
|
234 |
-
});
|
235 |
|
236 |
|
237 |
-
|
238 |
-
┏┓┏━┓┏━━━┓┏━┓┏━┓┏━━┓┏┓┏━┓┏┓╋┏┓
|
239 |
-
┃┃┃┏┛┃┏━┓┃┃┃┗┛┃┃┗┫┣┛┃┃┃┏┛┃┃╋┃┃
|
240 |
-
┃┗┛┛╋┃┃╋┃┃┃┏┓┏┓┃╋┃┃╋┃┗┛┛╋┃┃╋┃┃
|
241 |
-
┃┏┓┃╋┃┃╋┃┃┃┃┃┃┃┃╋┃┃╋┃┏┓┃╋┃┃╋┃┃
|
242 |
-
┃┃┃┗┓┃┗━┛┃┃┃┃┃┃┃┏┫┣┓┃┃┃┗┓┃┗━┛┃
|
243 |
-
┗┛┗━┛┗━━━┛┗┛┗┛┗┛┗━━┛┗┛┗━┛┗━━━┛
|
244 |
-
*/
|
245 |
|
246 |
function generateRandomID(length = 8) {
|
247 |
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
@@ -254,14 +30,18 @@ function generateRandomID(length = 8) {
|
|
254 |
|
255 |
async function komiku_download(url) {
|
256 |
const instanceID = generateRandomID();
|
257 |
-
const tempDir = path.join(
|
258 |
await fss.mkdir(tempDir);
|
259 |
|
260 |
-
// Extracting the title from the URL
|
261 |
const title = url.split('/').filter(part => part).pop();
|
262 |
|
263 |
try {
|
264 |
-
const response = await axios.get(url
|
|
|
|
|
|
|
|
|
|
|
265 |
const html = response.data;
|
266 |
const $ = cheerio.load(html);
|
267 |
const imgList = [];
|
@@ -271,11 +51,10 @@ async function komiku_download(url) {
|
|
271 |
imgList.push({ path: src });
|
272 |
});
|
273 |
|
274 |
-
await
|
275 |
-
const pdfPath = await createPDF(instanceID, tempDir);
|
276 |
|
277 |
-
|
278 |
-
return { path: pdfPath, title: title, url: `https://${process.env.SPACE_HOST}/temp/${path.basename(pdfPathq)}` };
|
279 |
} catch (error) {
|
280 |
console.log(error);
|
281 |
throw error;
|
@@ -285,220 +64,65 @@ async function komiku_download(url) {
|
|
285 |
}
|
286 |
|
287 |
async function downloadImage(image, tempDir, instanceID) {
|
288 |
-
const response = await axios.get(image.path, {
|
|
|
|
|
|
|
|
|
|
|
|
|
289 |
const imagePath = path.join(tempDir, `image_${instanceID}_${Date.now()}_${Math.floor(Math.random() * 1000)}.jpg`);
|
290 |
await writeFileAsync(imagePath, response.data);
|
291 |
|
292 |
-
const imageHeight = await getImageHeight(imagePath);
|
293 |
-
const newHeight = Math.floor(imageHeight * 0.7);
|
294 |
-
const command = `convert ${imagePath} -resize 720x${newHeight}! -quality 75 -background white -gravity center -extent 720x${newHeight} ${imagePath}`;
|
295 |
-
await execPromise(command);
|
296 |
-
|
297 |
return imagePath;
|
298 |
}
|
299 |
|
300 |
-
async function
|
301 |
-
const
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
async function processImages(imgList, tempDir, instanceID) {
|
306 |
-
const maxImagesPerPage = 10; // Maksimal 10 gambar per halaman
|
307 |
-
let partIndex = 0;
|
308 |
-
let partImages = [];
|
309 |
-
|
310 |
-
for (let i = 0; i < imgList.length; i++) {
|
311 |
-
const imagePath = await downloadImage(imgList[i], tempDir, instanceID);
|
312 |
-
partImages.push(imagePath);
|
313 |
-
|
314 |
-
if (partImages.length >= maxImagesPerPage) {
|
315 |
-
await combineAndSave(partImages, partIndex, tempDir, instanceID);
|
316 |
-
partImages = [];
|
317 |
-
partIndex++;
|
318 |
-
}
|
319 |
-
}
|
320 |
-
|
321 |
-
// Jika masih ada gambar yang belum diproses
|
322 |
-
if (partImages.length > 0) {
|
323 |
-
await combineAndSave(partImages, partIndex, tempDir, instanceID);
|
324 |
}
|
|
|
325 |
}
|
326 |
|
327 |
-
async function
|
328 |
-
const
|
329 |
-
const
|
330 |
-
await execPromise(command);
|
331 |
|
332 |
-
|
333 |
|
334 |
-
|
335 |
-
}
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
const combinedImagesPath = combinedParts.map(file => path.join(tempDir, file)).join(' ');
|
340 |
|
341 |
-
|
342 |
-
const createPDFCommand = `convert ${combinedImagesPath} ${pdfPath}`;
|
343 |
-
await execPromise(createPDFCommand);
|
344 |
|
345 |
return pdfPath;
|
346 |
}
|
347 |
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
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({
|
367 |
-
error: 'Failed to process request\n' + error
|
368 |
-
});
|
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 |
-
|
410 |
-
}
|
411 |
-
|
412 |
-
|
413 |
}
|
414 |
-
}
|
415 |
|
416 |
-
async function GetKomik(url) {
|
417 |
try {
|
418 |
-
const
|
419 |
-
|
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 |
-
|
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({
|
@@ -507,7 +131,7 @@ async function pingWebsite() {
|
|
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/
|
511 |
console.log("Ping");
|
512 |
await browser.close();
|
513 |
}
|
|
|
1 |
+
import express from 'express';
|
2 |
+
import path from 'path';
|
3 |
+
import os from 'os';
|
4 |
+
import PDFDocument from 'pdfkit';
|
5 |
+
import fs from 'fs';
|
6 |
import axios from 'axios';
|
7 |
import cheerio from 'cheerio';
|
|
|
|
|
|
|
8 |
import { promisify } from 'util';
|
9 |
+
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
const require = createRequire(import.meta.url);
|
|
|
|
|
|
|
12 |
const puppeteer = require('puppeteer');
|
13 |
import { fileURLToPath } from 'url';
|
14 |
const PORT = process.env.PORT || 7860;
|
15 |
const app = express();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
const writeFileAsync = promisify(fs.writeFile);
|
17 |
+
const fss = fs.promises;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
|
19 |
|
20 |
+
app.use('/static', express.static(os.tmpdir()));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
|
22 |
function generateRandomID(length = 8) {
|
23 |
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
|
30 |
|
31 |
async function komiku_download(url) {
|
32 |
const instanceID = generateRandomID();
|
33 |
+
const tempDir = path.join(os.tmpdir(), instanceID);
|
34 |
await fss.mkdir(tempDir);
|
35 |
|
|
|
36 |
const title = url.split('/').filter(part => part).pop();
|
37 |
|
38 |
try {
|
39 |
+
const response = await axios.get(url, {
|
40 |
+
headers: {
|
41 |
+
'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0.1; SM-N916S Build/MMB29K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/106.0.5249.126 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/389.0.0.42.111;]',
|
42 |
+
'Referer': 'https://komiku.id/'
|
43 |
+
}
|
44 |
+
});
|
45 |
const html = response.data;
|
46 |
const $ = cheerio.load(html);
|
47 |
const imgList = [];
|
|
|
51 |
imgList.push({ path: src });
|
52 |
});
|
53 |
|
54 |
+
const imagePaths = await downloadImages(imgList, tempDir, instanceID);
|
55 |
+
const pdfPath = await createPDF(imagePaths, instanceID, tempDir);
|
56 |
|
57 |
+
return { path: `/static/${instanceID}.pdf`, title: title };
|
|
|
58 |
} catch (error) {
|
59 |
console.log(error);
|
60 |
throw error;
|
|
|
64 |
}
|
65 |
|
66 |
async function downloadImage(image, tempDir, instanceID) {
|
67 |
+
const response = await axios.get(image.path, {
|
68 |
+
responseType: 'arraybuffer',
|
69 |
+
headers: {
|
70 |
+
'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0.1; SM-N916S Build/MMB29K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/106.0.5249.126 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/389.0.0.42.111;]',
|
71 |
+
'Referer': 'https://komiku.id/'
|
72 |
+
}
|
73 |
+
});
|
74 |
const imagePath = path.join(tempDir, `image_${instanceID}_${Date.now()}_${Math.floor(Math.random() * 1000)}.jpg`);
|
75 |
await writeFileAsync(imagePath, response.data);
|
76 |
|
|
|
|
|
|
|
|
|
|
|
77 |
return imagePath;
|
78 |
}
|
79 |
|
80 |
+
async function downloadImages(imgList, tempDir, instanceID) {
|
81 |
+
const imagePaths = [];
|
82 |
+
for (const img of imgList) {
|
83 |
+
const imagePath = await downloadImage(img, tempDir, instanceID);
|
84 |
+
imagePaths.push(imagePath);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
}
|
86 |
+
return imagePaths;
|
87 |
}
|
88 |
|
89 |
+
async function createPDF(imagePaths, instanceID, tempDir) {
|
90 |
+
const pdfPath = path.join(os.tmpdir(), `${instanceID}.pdf`);
|
91 |
+
const doc = new PDFDocument({ autoFirstPage: false });
|
|
|
92 |
|
93 |
+
doc.pipe(fs.createWriteStream(pdfPath));
|
94 |
|
95 |
+
for (const imagePath of imagePaths) {
|
96 |
+
const { width, height } = await getImageDimensions(imagePath);
|
97 |
+
doc.addPage({ size: [width, height] });
|
98 |
+
doc.image(imagePath, 0, 0, { width: width, height: height });
|
99 |
+
}
|
|
|
100 |
|
101 |
+
doc.end();
|
|
|
|
|
102 |
|
103 |
return pdfPath;
|
104 |
}
|
105 |
|
106 |
+
async function getImageDimensions(imagePath) {
|
107 |
+
const sizeOf = promisify(require('image-size'));
|
108 |
+
const dimensions = await sizeOf(imagePath);
|
109 |
+
return dimensions;
|
110 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
111 |
|
112 |
+
app.get('/download', async (req, res) => {
|
113 |
+
const { url } = req.query;
|
114 |
+
if (!url) {
|
115 |
+
return res.status(400).send('URL is required');
|
116 |
}
|
|
|
117 |
|
|
|
118 |
try {
|
119 |
+
const result = await komiku_download(url);
|
120 |
+
res.json(result);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
} catch (error) {
|
122 |
+
res.status(500).send('Error processing request');
|
|
|
123 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
124 |
});
|
125 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
126 |
// Fungsi untuk ping website
|
127 |
async function pingWebsite() {
|
128 |
const browser = await puppeteer.launch({
|
|
|
131 |
});
|
132 |
const page = await browser.newPage();
|
133 |
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;]");
|
134 |
+
await page.goto('https://huggingface.co/spaces/ArashiCode/komik/');
|
135 |
console.log("Ping");
|
136 |
await browser.close();
|
137 |
}
|