proxy / index.js
Kyouka's picture
Update index.js
a748dd9 verified
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 stealth = require('puppeteer-extra-plugin-stealth')
// playwright.chromium.use(stealth())
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
}
});
// Mengatur header agar tetap sesuai dengan respons asli
res.set(response.headers);
// Streaming langsung ke response
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;
}
// 1. Current Playback Endpoint with User ID
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];
// Refresh token if needed
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' });
}
}
// Prepare request parameters
const params = {};
if (session.country) {
params.market = session.country;
}
// Get current playback state
const response = await axios.get('https://api.spotify.com/v1/me/player', {
params,
headers: {
'Authorization': `Bearer ${session.accessToken}`,
'Content-Type': 'application/json'
},
});
// Handle no content response
if (response.status === 204) {
return res.json({
status: 'success',
data: null,
message: 'No active playback detected'
});
}
// Format playback data
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
});
}
});
// 2. Login Endpoint
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}`);
});
// 3. Callback Endpoint
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 {
// Get tokens
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;
// Get user data
const userResponse = await axios.get('https://api.spotify.com/v1/me', {
headers: { 'Authorization': `Bearer ${access_token}` },
});
// Store user session
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
};
// Redirect to current playback with user ID
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', // pastikan path ini benar di server kamu
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 });
// Tunggu ekstra jika perlu ngelewatin challenge
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(); // Get the page content as HTML
await browser.close();
res.send(content); // Send the page content as response
});
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', // pastikan path ini valid
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);
// Wait for the list of videos to load
await page.waitForSelector('.videos-list');
// Extract data from all articles
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 []; // Return an empty array in case of error
}
}
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
}