|
|
const fs = require('fs') |
|
|
const os = require('os') |
|
|
const util = require('util') |
|
|
const axios = require('axios') |
|
|
const bytes = require('bytes') |
|
|
const sharp = require('sharp') |
|
|
const morgan = require('morgan') |
|
|
const express = require('express') |
|
|
const cp = require('child_process') |
|
|
const PDFDocument = require('pdfkit') |
|
|
const playwright = require('playwright-extra') |
|
|
const cookieParser = require('cookie-parser'); |
|
|
const { Buffer } = require('buffer'); |
|
|
const querystring = require('querystring'); |
|
|
|
|
|
|
|
|
const { NinexbuddySource, NinexbuddyScraper } = require("./lib/buddy.js") |
|
|
const kyou = new NinexbuddyScraper() |
|
|
|
|
|
const userSessions = {}; |
|
|
|
|
|
const app = express() |
|
|
app.set('json spaces', 4) |
|
|
app.use(morgan('dev')) |
|
|
app.use(cookieParser()); |
|
|
|
|
|
const CLIENT_ID = process.env.CLIENT_ID || 'your_client_id'; |
|
|
const CLIENT_SECRET = process.env.CLIENT_SECRET || 'your_client_secret'; |
|
|
|
|
|
const limitSize = '500mb' |
|
|
app.use(express.json({ limit: limitSize })) |
|
|
app.use(express.urlencoded({ extended: true, limit: limitSize })) |
|
|
|
|
|
const tmpFolder = os.tmpdir() |
|
|
app.use((req, res, next) => { |
|
|
fs.readdirSync(tmpFolder).map(file => { |
|
|
file = `${tmpFolder}/${file}` |
|
|
let stats = fs.statSync(file) |
|
|
if (!stats.isFile()) return |
|
|
if (Date.now() - stats.mtimeMs >= 1000 * 60 * 30) { |
|
|
fs.unlinkSync(file) |
|
|
console.log('Deleted file', file) |
|
|
} |
|
|
}) |
|
|
next() |
|
|
}) |
|
|
|
|
|
|
|
|
function getFrontendUri(req) { |
|
|
return `https://${req.get('host')}`; |
|
|
} |
|
|
|
|
|
async function getUserData(accessToken) { |
|
|
const response = await axios.get('https://api.spotify.com/v1/me', { |
|
|
headers: { 'Authorization': `Bearer ${accessToken}` } |
|
|
}); |
|
|
return response.data; |
|
|
} |
|
|
|
|
|
|
|
|
app.use('/file', express.static(tmpFolder)) |
|
|
|
|
|
app.all('/', (req, res) => { |
|
|
const status = {} |
|
|
const used = process.memoryUsage() |
|
|
for (let key in used) status[key] = formatSize(used[key]) |
|
|
|
|
|
const disk = cp.execSync('du -sh').toString().split('M')[0] |
|
|
status.diskUsage = `${disk} MB` |
|
|
|
|
|
const totalmem = os.totalmem() |
|
|
const freemem = os.freemem() |
|
|
status.memoryUsage = `${formatSize(totalmem - freemem)} / ${formatSize(totalmem)}` |
|
|
|
|
|
res.json({ |
|
|
creator: '@rippanteq7', |
|
|
message: 'Hello World', |
|
|
uptime: new Date(process.uptime() * 1000).toUTCString().split(' ')[4], |
|
|
status |
|
|
}) |
|
|
}) |
|
|
|
|
|
app.get('/proxy', async (req, res) => { |
|
|
const { url } = req.query; |
|
|
if (!url) return res.json({ success: false, message: 'Required parameter url' }); |
|
|
|
|
|
try { |
|
|
const response = await axios({ |
|
|
method: 'get', |
|
|
url, |
|
|
responseType: 'stream', |
|
|
headers: { |
|
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', |
|
|
'Referer': url |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
res.set(response.headers); |
|
|
|
|
|
|
|
|
response.data.pipe(res); |
|
|
} catch (error) { |
|
|
console.error(error.message); |
|
|
res.status(500).json({ success: false, message: 'Error fetching video' }); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
async function refreshAccessToken(refreshToken) { |
|
|
const response = await axios.post('https://accounts.spotify.com/api/token', |
|
|
querystring.stringify({ |
|
|
grant_type: 'refresh_token', |
|
|
refresh_token: refreshToken, |
|
|
}), { |
|
|
headers: { |
|
|
'Content-Type': 'application/x-www-form-urlencoded', |
|
|
'Authorization': 'Basic ' + Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64'), |
|
|
}, |
|
|
}); |
|
|
return response.data; |
|
|
} |
|
|
|
|
|
|
|
|
app.get('/api/current-playback', async (req, res) => { |
|
|
try { |
|
|
const userId = req.query.user; |
|
|
|
|
|
if (!userId || !userSessions[userId]) { |
|
|
return res.status(401).json({ |
|
|
error: 'Not authenticated', |
|
|
solution: 'Please login at /login first' |
|
|
}); |
|
|
} |
|
|
|
|
|
const session = userSessions[userId]; |
|
|
|
|
|
|
|
|
if (Date.now() >= session.expiresAt) { |
|
|
try { |
|
|
const newTokens = await refreshAccessToken(session.refreshToken); |
|
|
session.accessToken = newTokens.access_token; |
|
|
session.expiresAt = Date.now() + newTokens.expires_in * 1000; |
|
|
} catch (error) { |
|
|
delete userSessions[userId]; |
|
|
return res.status(401).json({ error: 'Session expired' }); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const params = {}; |
|
|
if (session.country) { |
|
|
params.market = session.country; |
|
|
} |
|
|
|
|
|
|
|
|
const response = await axios.get('https://api.spotify.com/v1/me/player', { |
|
|
params, |
|
|
headers: { |
|
|
'Authorization': `Bearer ${session.accessToken}`, |
|
|
'Content-Type': 'application/json' |
|
|
}, |
|
|
}); |
|
|
|
|
|
|
|
|
if (response.status === 204) { |
|
|
return res.json({ |
|
|
status: 'success', |
|
|
data: null, |
|
|
message: 'No active playback detected' |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
const playbackData = { |
|
|
device: { |
|
|
id: response.data.device.id, |
|
|
name: response.data.device.name, |
|
|
type: response.data.device.type, |
|
|
is_active: response.data.device.is_active, |
|
|
volume_percent: response.data.device.volume_percent |
|
|
}, |
|
|
track: { |
|
|
id: response.data.item.id, |
|
|
name: response.data.item.name, |
|
|
duration_ms: response.data.item.duration_ms, |
|
|
progress_ms: response.data.progress_ms, |
|
|
is_playing: response.data.is_playing, |
|
|
artists: response.data.item.artists.map(artist => ({ |
|
|
id: artist.id, |
|
|
name: artist.name |
|
|
})), |
|
|
album: { |
|
|
id: response.data.item.album.id, |
|
|
name: response.data.item.album.name, |
|
|
images: response.data.item.album.images, |
|
|
release_date: response.data.item.album.release_date |
|
|
} |
|
|
}, |
|
|
context: response.data.context ? { |
|
|
type: response.data.context.type, |
|
|
href: response.data.context.href, |
|
|
uri: response.data.context.uri |
|
|
} : null, |
|
|
shuffle_state: response.data.shuffle_state, |
|
|
repeat_state: response.data.repeat_state, |
|
|
timestamp: response.data.timestamp |
|
|
}; |
|
|
|
|
|
res.json({ |
|
|
status: 'success', |
|
|
user: { |
|
|
id: userId, |
|
|
display_name: session.display_name, |
|
|
country: session.country |
|
|
}, |
|
|
data: playbackData |
|
|
}); |
|
|
|
|
|
} catch (error) { |
|
|
console.error('Playback error:', error.response?.data || error.message); |
|
|
|
|
|
if (error.response?.status === 401) { |
|
|
return res.status(401).json({ error: 'Invalid or expired token' }); |
|
|
} |
|
|
|
|
|
res.status(500).json({ |
|
|
error: 'Failed to get playback state', |
|
|
details: error.response?.data || error.message |
|
|
}); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
app.get('/spotify', (req, res) => { |
|
|
const FRONTEND_URI = getFrontendUri(req); |
|
|
const REDIRECT_URI = `${FRONTEND_URI}/callback`; |
|
|
|
|
|
const scopes = [ |
|
|
'user-read-playback-state', |
|
|
'user-read-currently-playing', |
|
|
'user-read-private', |
|
|
'user-read-email' |
|
|
]; |
|
|
|
|
|
const queryParams = querystring.stringify({ |
|
|
response_type: 'code', |
|
|
client_id: CLIENT_ID, |
|
|
scope: scopes.join(' '), |
|
|
redirect_uri: REDIRECT_URI, |
|
|
show_dialog: true, |
|
|
}); |
|
|
|
|
|
res.redirect(`https://accounts.spotify.com/authorize?${queryParams}`); |
|
|
}); |
|
|
|
|
|
|
|
|
app.get('/callback', async (req, res) => { |
|
|
const FRONTEND_URI = getFrontendUri(req); |
|
|
const REDIRECT_URI = `${FRONTEND_URI}/callback`; |
|
|
|
|
|
const { code, error } = req.query; |
|
|
|
|
|
if (error) { |
|
|
return res.redirect(`${FRONTEND_URI}/error?message=${encodeURIComponent(error)}`); |
|
|
} |
|
|
|
|
|
try { |
|
|
|
|
|
const authResponse = await axios.post('https://accounts.spotify.com/api/token', |
|
|
querystring.stringify({ |
|
|
grant_type: 'authorization_code', |
|
|
code, |
|
|
redirect_uri: REDIRECT_URI, |
|
|
}), { |
|
|
headers: { |
|
|
'Content-Type': 'application/x-www-form-urlencoded', |
|
|
'Authorization': 'Basic ' + Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64'), |
|
|
}, |
|
|
}); |
|
|
|
|
|
const { access_token, refresh_token, expires_in } = authResponse.data; |
|
|
|
|
|
|
|
|
const userResponse = await axios.get('https://api.spotify.com/v1/me', { |
|
|
headers: { 'Authorization': `Bearer ${access_token}` }, |
|
|
}); |
|
|
|
|
|
|
|
|
const userId = userResponse.data.id; |
|
|
userSessions[userId] = { |
|
|
accessToken: access_token, |
|
|
refreshToken: refresh_token, |
|
|
expiresAt: Date.now() + expires_in * 1000, |
|
|
display_name: userResponse.data.display_name, |
|
|
country: userResponse.data.country, |
|
|
email: userResponse.data.email |
|
|
}; |
|
|
|
|
|
|
|
|
res.redirect(`${FRONTEND_URI}/api/current-playback?user=${userId}`); |
|
|
|
|
|
} catch (err) { |
|
|
console.error('Callback error:', err); |
|
|
res.redirect(`${FRONTEND_URI}/error?message=${encodeURIComponent('Authentication failed')}`); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
app.get('/pages', async (req, res) => { |
|
|
const targetUrl = req.query.url; |
|
|
|
|
|
if (!targetUrl) { |
|
|
return res.status(400).json({ error: 'Parameter "url" dibutuhkan' }); |
|
|
} |
|
|
|
|
|
const browser = await playwright.chromium.launch({ |
|
|
headless: true, |
|
|
executablePath: '/usr/bin/chromium', |
|
|
args: ['--no-sandbox'] |
|
|
}); |
|
|
|
|
|
const context = await browser.newContext({ |
|
|
extraHTTPHeaders: { |
|
|
'accept': '*/*', |
|
|
'accept-language': 'en-US,en;q=0.9,id;q=0.8', |
|
|
'cache-control': 'no-cache', |
|
|
'origin': 'https://doujindesu.tv', |
|
|
'pragma': 'no-cache', |
|
|
'referer': 'https://doujindesu.tv', |
|
|
'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="126", "Google Chrome";v="126"', |
|
|
'sec-ch-ua-mobile': '?0', |
|
|
'sec-ch-ua-platform': '"Windows"', |
|
|
'sec-fetch-dest': 'script', |
|
|
'sec-fetch-mode': 'cors', |
|
|
'sec-fetch-site': 'cross-site', |
|
|
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36' |
|
|
} |
|
|
}); |
|
|
|
|
|
try { |
|
|
const page = await context.newPage(); |
|
|
await page.goto(targetUrl, { waitUntil: 'domcontentloaded', timeout: 60000 }); |
|
|
|
|
|
|
|
|
await page.waitForTimeout(8000); |
|
|
|
|
|
const content = await page.content(); |
|
|
res.send(content); |
|
|
} catch (err) { |
|
|
res.status(500).json({ error: 'Gagal fetch halaman', detail: err.message }); |
|
|
} finally { |
|
|
await browser.close(); |
|
|
} |
|
|
}); |
|
|
|
|
|
app.all('/imagetopdf', async (req, res) => { |
|
|
if (!['POST'].includes(req.method)) return res.status(405).json({ success: false, message: 'Method Not Allowed' }) |
|
|
|
|
|
try { |
|
|
console.log(new Date().toLocaleString('id', { timeZone: 'Asia/Jakarta' }), '\n', req.body) |
|
|
const { images } = req.body |
|
|
if (!(images && Array.isArray(images))) return res.json({ success: false, message: 'Required an array image url' }) |
|
|
|
|
|
const buffer = await toPDF(images) |
|
|
res.setHeader('Content-Disposition', `attachment; filename=${Math.random().toString(36).slice(2)}.pdf`) |
|
|
res.setHeader('Content-Type', 'application/pdf') |
|
|
res.setHeader('Content-Length', buffer.byteLength) |
|
|
res.send(buffer) |
|
|
} catch (e) { |
|
|
console.log(e) |
|
|
e = String(e) |
|
|
res.status(500).json({ error: true, message: e === '[object Object]' ? 'Internal Server Error' : e }) |
|
|
} |
|
|
}) |
|
|
|
|
|
app.all('/webp2png', async (req, res) => { |
|
|
if (!['POST'].includes(req.method)) return res.status(405).json({ success: false, message: 'Method Not Allowed' }) |
|
|
|
|
|
try { |
|
|
const { file } = req.body |
|
|
if (!(file && isBase64(file))) return res.json({ success: false, message: 'Payload body file must be filled in base64 format' }) |
|
|
|
|
|
const fileBuffer = Buffer.from(file, 'base64') |
|
|
const fileName = `${Math.random().toString(36).slice(2)}.png` |
|
|
const convertData = await sharp(fileBuffer).png().toBuffer() |
|
|
|
|
|
await fs.promises.writeFile(`${tmpFolder}/${fileName}`, convertData) |
|
|
res.send(`https://${req.get('host')}/file/${fileName}`) |
|
|
} catch (e) { |
|
|
console.log(e) |
|
|
e = String(e) |
|
|
res.status(500).json({ error: true, message: e === '[object Object]' ? 'Internal Server Error' : e }) |
|
|
} |
|
|
}) |
|
|
|
|
|
app.all(['/webp2gif', '/webp2mp4'], async (req, res) => { |
|
|
if (!['POST'].includes(req.method)) return res.status(405).json({ success: false, message: 'Method Not Allowed' }) |
|
|
|
|
|
try { |
|
|
const { file } = req.body |
|
|
if (!(file && isBase64(file))) return res.json({ success: false, message: 'Payload body file must be filled in base64 format' }) |
|
|
|
|
|
const fileBuffer = Buffer.from(file, 'base64') |
|
|
const fileName = `${Math.random().toString(36).slice(2)}.webp` |
|
|
const filePath = `${tmpFolder}/${fileName}` |
|
|
await fs.promises.writeFile(filePath, fileBuffer) |
|
|
|
|
|
const exec = util.promisify(cp.exec).bind(cp) |
|
|
await exec(`convert ${filePath} ${filePath.replace('.webp', '.gif')}`) |
|
|
if (/gif/.test(req.path)) return res.send(`https://${req.get('host')}/file/${fileName.replace('.webp', '.gif')}`) |
|
|
|
|
|
await exec(`ffmpeg -i ${filePath.replace('.webp', '.gif')} -movflags faststart -pix_fmt yuv420p -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" ${filePath.replace(/.webp|.gif/g, '')}.mp4`) |
|
|
res.send(`https://${req.get('host')}/file/${fileName.replace('.webp', '.mp4')}`) |
|
|
} catch (e) { |
|
|
console.log(e) |
|
|
e = String(e) |
|
|
res.status(500).json({ error: true, message: e === '[object Object]' ? 'Internal Server Error' : e }) |
|
|
} |
|
|
}) |
|
|
|
|
|
app.get('/fetch', async (req, res) => { |
|
|
try { |
|
|
if (!req.query.url) return res.json({ message: 'Required an url' }) |
|
|
let json = await axios.get(req.query.url) |
|
|
res.json(json.data) |
|
|
} catch (e) { |
|
|
res.send(e) |
|
|
} |
|
|
}) |
|
|
|
|
|
app.all(['/enhance', '/hd', '/upscale'], async (req, res) => { |
|
|
if (!['GET', 'POST'].includes(req.method)) return res.status(405).json({ success: false, message: 'Method Not Allowed' }) |
|
|
|
|
|
try { |
|
|
const { url } = req.method !== 'GET' ? req.body : req.query |
|
|
if (!url) return res.json({ success: false, message: 'Required parameter url' }) |
|
|
|
|
|
const result = await enhanceImage(url) |
|
|
res.json({ success: true, result }) |
|
|
} catch (e) { |
|
|
console.log(e) |
|
|
e = String(e) |
|
|
res.status(500).json({ error: true, message: e === '[object Object]' ? 'Internal Server Error' : e }) |
|
|
} |
|
|
}) |
|
|
|
|
|
app.all('/animelast', async (req, res) => { |
|
|
const result = await scrapeAnimeInfo(); |
|
|
res.json({ |
|
|
creator: '@Kyouka', |
|
|
message: 'Success', |
|
|
uptime: new Date(process.uptime() * 1000).toUTCString().split(' ')[4], |
|
|
result |
|
|
}); |
|
|
}); |
|
|
|
|
|
app.all('/dood', async (req, res) => { |
|
|
const { url } = req.query |
|
|
if (!url) return res.json({ success: false, message: 'Required parameter url' }) |
|
|
const result = await kyou.execWithArgs(url) |
|
|
res.json({ |
|
|
creator: '@Kyouka', |
|
|
message: 'Success', |
|
|
uptime: new Date(process.uptime() * 1000).toUTCString().split(' ')[4], |
|
|
result |
|
|
}); |
|
|
}); |
|
|
|
|
|
app.get('/igstalk', async (req, res) => { |
|
|
try { |
|
|
if (!req.query.user) return res.json({ message: 'Required an username' }) |
|
|
let result = await igStalk(req.query.user) |
|
|
res.json({ result }) |
|
|
} catch (e) { |
|
|
res.send(e) |
|
|
} |
|
|
}) |
|
|
|
|
|
app.all('/stablediff/illusion', async (req, res) => { |
|
|
if (!['GET', 'POST'].includes(req.method)) return res.status(405).json({ success: false, message: 'Method Not Allowed' }) |
|
|
|
|
|
try { |
|
|
const { |
|
|
prompt, |
|
|
negative_prompt = 'low quality', |
|
|
image_url, |
|
|
num_inference_steps = 25, |
|
|
controlnet_conditioning_scale = 1 |
|
|
} = req.method !== 'GET' ? req.body : req.query |
|
|
|
|
|
if (!(prompt && image_url)) return res.json({ success: false, message: 'Required parameter prompt & image_url' }) |
|
|
|
|
|
const headers = { |
|
|
'Content-Type': 'application/json', |
|
|
Authorization: `Key ${process.env.falAIKey}` |
|
|
} |
|
|
|
|
|
const { detail, response_url } = await (await fetch('https://54285744-illusion-diffusion.gateway.alpha.fal.ai/fal/queue/submit', { |
|
|
method: 'POST', |
|
|
body: JSON.stringify({ |
|
|
prompt, negative_prompt, image_url, |
|
|
num_inference_steps, controlnet_conditioning_scale |
|
|
}), |
|
|
headers |
|
|
})).json() |
|
|
if (detail) return res.json({ success: false, message: detail }) |
|
|
|
|
|
let retry = 0 |
|
|
while (true) { |
|
|
await new Promise(resolve => setTimeout(resolve, 2500)) |
|
|
const prediction = await (await fetch(response_url, { headers })).json() |
|
|
console.log(prediction) |
|
|
if (retry > 10) return res.json({ success: false, message: prediction.detail || 'Max retry has reached' }) |
|
|
if (prediction.image) return res.json({ success: true, result: prediction }) |
|
|
retry += 1 |
|
|
} |
|
|
|
|
|
} catch (e) { |
|
|
console.log(e) |
|
|
e = String(e) |
|
|
res.status(500).json({ error: true, message: e === '[object Object]' ? 'Internal Server Error' : e }) |
|
|
} |
|
|
}) |
|
|
|
|
|
app.get('/dongo', async (req, res) => { |
|
|
const { url } = req.query |
|
|
if (!url) return res.json({ success: false, message: 'Required parameter url' }) |
|
|
const browser = await playwright.chromium.launch({ |
|
|
headless: true, |
|
|
executablePath: '/usr/bin/chromium', |
|
|
args: ['--no-sandbox'] |
|
|
}) |
|
|
const context = await browser.newContext({ |
|
|
extraHTTPHeaders: { |
|
|
'accept': '*/*', |
|
|
'accept-language': 'en-US,en;q=0.9,id;q=0.8', |
|
|
'cache-control': 'no-cache', |
|
|
'origin': url, |
|
|
'pragma': 'no-cache', |
|
|
'referer': url, |
|
|
'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="126", "Google Chrome";v="126"', |
|
|
'sec-ch-ua-mobile': '?0', |
|
|
'sec-ch-ua-platform': '"Windows"', |
|
|
'sec-fetch-dest': 'script', |
|
|
'sec-fetch-mode': 'cors', |
|
|
'sec-fetch-site': 'cross-site', |
|
|
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36' |
|
|
} |
|
|
}); |
|
|
|
|
|
const page = await context.newPage(); |
|
|
await page.goto(url); |
|
|
|
|
|
const content = await page.content(); |
|
|
|
|
|
await browser.close(); |
|
|
|
|
|
res.send(content); |
|
|
}); |
|
|
|
|
|
app.get('/fetch-page', async (req, res) => { |
|
|
const targetUrl = req.query.url; |
|
|
|
|
|
if (!targetUrl) { |
|
|
return res.status(400).json({ |
|
|
developer: "KyoukaDev", |
|
|
status: false, |
|
|
message: 'Parameter "url" dibutuhkan', |
|
|
}); |
|
|
} |
|
|
|
|
|
const baseUrl = new URL(targetUrl).origin; |
|
|
|
|
|
const browser = await playwright.chromium.launch({ |
|
|
headless: true, |
|
|
executablePath: '/usr/bin/chromium', |
|
|
args: ['--no-sandbox'] |
|
|
}); |
|
|
|
|
|
const context = await browser.newContext({ |
|
|
extraHTTPHeaders: { |
|
|
'accept': '*/*', |
|
|
'accept-language': 'en-US,en;q=0.9,id;q=0.8', |
|
|
'cache-control': 'no-cache', |
|
|
'origin': baseUrl, |
|
|
'pragma': 'no-cache', |
|
|
'referer': baseUrl, |
|
|
'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="126", "Google Chrome";v="126"', |
|
|
'sec-ch-ua-mobile': '?0', |
|
|
'sec-ch-ua-platform': '"Windows"', |
|
|
'sec-fetch-dest': 'script', |
|
|
'sec-fetch-mode': 'cors', |
|
|
'sec-fetch-site': 'cross-site', |
|
|
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36' |
|
|
} |
|
|
}); |
|
|
|
|
|
try { |
|
|
const page = await context.newPage(); |
|
|
await page.goto(targetUrl, { waitUntil: 'domcontentloaded', timeout: 60000 }); |
|
|
await page.waitForTimeout(8000); |
|
|
|
|
|
const content = await page.content(); |
|
|
|
|
|
res.json({ |
|
|
developer: "KyoukaDev", |
|
|
status: true, |
|
|
data: { |
|
|
results: [ |
|
|
{ content } |
|
|
] |
|
|
} |
|
|
}); |
|
|
|
|
|
} catch (err) { |
|
|
res.status(500).json({ |
|
|
developer: "KyoukaDev", |
|
|
status: false, |
|
|
message: 'Gagal fetch halaman', |
|
|
error: err.message |
|
|
}); |
|
|
} finally { |
|
|
await browser.close(); |
|
|
} |
|
|
}); |
|
|
|
|
|
const PORT = process.env.PORT || 7860 |
|
|
app.listen(PORT, () => console.log('App running on port', PORT)) |
|
|
|
|
|
function formatSize(num) { |
|
|
return bytes(+num || 0, { unitSeparator: ' ' }) |
|
|
} |
|
|
|
|
|
function isBase64(str) { |
|
|
try { |
|
|
return btoa(atob(str)) === str |
|
|
} catch { |
|
|
return false |
|
|
} |
|
|
} |
|
|
|
|
|
function toPDF(urls) { |
|
|
return new Promise(async (resolve, reject) => { |
|
|
try { |
|
|
if (!Array.isArray(urls)) urls = [urls] |
|
|
const doc = new PDFDocument({ margin: 0, size: 'A4' }) |
|
|
const buffers = [] |
|
|
|
|
|
for (let i = 0; i < urls.length; i++) { |
|
|
const response = await fetch(urls[i], { headers: { referer: urls[i] }}) |
|
|
if (!response.ok) continue |
|
|
|
|
|
const type = response.headers.get('content-type') |
|
|
if (!/image/.test(type)) continue |
|
|
|
|
|
let buffer = Buffer.from(await response.arrayBuffer()) |
|
|
if (/gif|webp/.test(type)) buffer = await sharp(buffer).png().toBuffer() |
|
|
|
|
|
doc.image(buffer, 0, 0, { fit: [595.28, 841.89], align: 'center', valign: 'center' }) |
|
|
if (urls.length !== i + 1) doc.addPage() |
|
|
} |
|
|
|
|
|
doc.on('data', (chunk) => buffers.push(chunk)) |
|
|
doc.on('end', () => resolve(Buffer.concat(buffers))) |
|
|
doc.on('error', reject) |
|
|
doc.end() |
|
|
} catch (e) { |
|
|
console.log(e) |
|
|
reject(e) |
|
|
} |
|
|
}) |
|
|
} |
|
|
|
|
|
async function scrapeAnimeInfo() { |
|
|
try { |
|
|
const browser = await playwright.chromium.launch({ |
|
|
headless: true, |
|
|
executablePath: '/usr/bin/chromium', |
|
|
args: ['--no-sandbox'] |
|
|
}); |
|
|
const page = await browser.newPage(); |
|
|
await page.goto('http://66.29.129.161/?filter=latest&cat=1'); |
|
|
const pageContent = await page.content(); |
|
|
console.log(pageContent); |
|
|
|
|
|
await page.waitForSelector('.videos-list'); |
|
|
|
|
|
|
|
|
const videoData = await page.$$eval('.videos-list article', articles => { |
|
|
return articles.map(article => { |
|
|
const thumbnail = article.querySelector('.post-thumbnail img').src; |
|
|
const title = article.querySelector('header.entry-header span').textContent; |
|
|
const url = article.querySelector('a').href; |
|
|
const duration = article.querySelector('.duration').textContent.trim(); |
|
|
return { thumbnail, title, url, duration }; |
|
|
}); |
|
|
}); |
|
|
|
|
|
await browser.close(); |
|
|
return videoData; |
|
|
} catch (error) { |
|
|
console.error('An error occurred during scraping:', error); |
|
|
return []; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
async function enhanceImage(url) { |
|
|
const browser = await playwright.chromium.launch({ |
|
|
headless: true, |
|
|
executablePath: '/usr/bin/chromium', |
|
|
args: ['--no-sandbox'] |
|
|
}) |
|
|
|
|
|
const page = await browser.newPage() |
|
|
await page.goto('https://snapedit.app/id/enhance/upload') |
|
|
await page.waitForLoadState('domcontentloaded') |
|
|
|
|
|
const arrayBuffer = await (await fetch(url)).arrayBuffer() |
|
|
page.on('filechooser', (fileChooser) => { |
|
|
const fileObject = { |
|
|
name: 'file.jpg', |
|
|
mimeType: 'image/jpg', |
|
|
buffer: Buffer.from(arrayBuffer) |
|
|
} |
|
|
fileChooser.setFiles([fileObject]) |
|
|
}) |
|
|
|
|
|
await page.getByRole('button', { name: 'Unggah gambar', exact: true }).click() |
|
|
const response = await page.waitForResponse(res => { |
|
|
console.log(res.url()) |
|
|
return res.url().includes('api/enhance/v1') |
|
|
}, { timeout: 60 * 1000 }) |
|
|
const json = await response.json() |
|
|
|
|
|
await browser.close() |
|
|
return json |
|
|
} |
|
|
|
|
|
|
|
|
async function igStalk(user) { |
|
|
const getDetailPost = async url => { |
|
|
let html = (await axios.get(url, { headers: { 'Referer': 'https://www.picuki.com/', 'User-Agent': fakeua.mobile() }})).data |
|
|
let $ = cheerio.load(html), obj = {} |
|
|
obj.caption = $('title').text().trim().split(' Instagram post ')[1].split(' - Picuki.com')[0] |
|
|
obj.ago = $('div.single-photo-info').find('div.single-photo-time').text() |
|
|
obj.likes = $('div.info-bottom').find('span.icon-thumbs-up-alt').text() |
|
|
obj.comments = $('div.info-bottom').find('span.icon-chat').text() |
|
|
obj.url = $('div.single-photo.owl-carousel.owl-theme > div.item').get().map((x) => $(x).find('img').attr('src') || $(x).find('video').attr('src')) |
|
|
if (!obj.url.length) obj.url = [$('div.single-photo').find('img').attr('src') || $('div.single-photo').find('video').attr('src')] |
|
|
return obj |
|
|
} |
|
|
let html = (await axios.get('https://www.picuki.com/profile/' + user, { headers: { 'Referer': 'https://www.picuki.com/', 'User-Agent': fakeua.mobile() }})).data |
|
|
let $ = cheerio.load(html), obj = {}, arr = [] |
|
|
let urlPost = $('div.content > ul > li').get().map((x) => $(x).find('a').attr('href')) |
|
|
for (let x of urlPost) { |
|
|
if (x) arr.push(await getDetailPost(x)) |
|
|
} |
|
|
obj.avatar = $('div.profile-avatar').find('a').attr('href') |
|
|
obj.username = $('div.profile-name > h1').text() |
|
|
obj.fullname = $('div.profile-name > h2').text() |
|
|
obj.description = $('div.profile-description').text().trim() |
|
|
obj.followers = $('div.content-title').find('span.followed_by').text() |
|
|
obj.following = $('div.content-title').find('span.follows').text() |
|
|
obj.post = arr |
|
|
return obj |
|
|
} |