nainowalene / server.js
vikarshana's picture
Update server.js
b9fc96c verified
const express = require('express');
const path = require('path');
const fs = require('fs');
const puppeteer = require('puppeteer');
const app = express();
const PORT = 7860;
// 中间件
app.use(express.json({ limit: '40mb' }));
app.use(express.static('.'));
// CORS支持
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
if (req.method === 'OPTIONS') {
res.sendStatus(200);
} else {
next();
}
});
async function redr(url, method = "GET", postData = null) {
const browser = await puppeteer.launch({
executablePath: "/opt/google/chrome/chrome",
headless: true,
});
const page = await browser.newPage();
await page.setExtraHTTPHeaders({
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"accept-encoding": "gzip, deflate, br, zstd",
"accept-language": "en-US,en;q=0.9",
"user-agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36"
});
let response;
try {
if (method === "POST" && postData) {
response = await page.goto(url, {
waitUntil: "load",
timeout: 0,
method: "POST",
postData: JSON.stringify(postData),
headers: {
"Content-Type": "application/json",
},
});
} else {
response = await page.goto(url, { waitUntil: "load", timeout: 10000 });
}
if (!response) throw new Error("No response received");
const cookiesArr = await page.cookies();
const cookies = cookiesArr.map(c => `${c.name}=${c.value}`).join("; ");
return {
finalUrl: page.url(),
status: response.status(),
headers: response.headers(),
cookies: cookies,
};
} catch (error) {
console.error("Error scraping page:", error);
return { error: error.message };
} finally {
await page.close();
}
}
app.get('/location', async (req, res) => {
const url = req.query.url;
const method = req.query.method || 'GET';
if (!url) {
return res.status(400).send('URL query parameter is required');
}
try {
const pageContent = await redr(url, method);
res.json(pageContent);
} catch (error) {
res.status(500).send('Failed to scrape the page');
}
});
async function fzdl(url) {
const browser = await puppeteer.launch({
executablePath: "/opt/google/chrome/chrome",
headless: true,
});
const page = await browser.newPage();
await page.setExtraHTTPHeaders({
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
"accept-language": "en-US,en;q=0.9",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36"
});
try {
let dllink;
if (url.includes("/generate-link")) {
const parsedUrl = new URL("https://fzmovie.co.za" + url);
dllink = parsedUrl.searchParams.get("link");
if (!dllink) throw new Error("No 'link' query parameter found");
} else {
await page.goto(url, { waitUntil: "networkidle2" });
await page.waitForSelector("#downloadBtn", { visible: true, timeout: 15000 });
dllink = await page.$eval("#downloadBtn", el => el.href);
}
const cookiesArr = await page.cookies();
const cookies = cookiesArr.map(c => `${c.name}=${c.value}`).join("; ");
return {
url,
dllink,
cookies
};
} catch (err) {
console.error("Error fetching download link or cookies:", err.message);
return { error: err.message };
} finally {
await browser.close();
}
}
app.get('/fzdl', async (req, res) => {
const url = req.query.url;
if (!url) {
return res.status(400).send('URL query parameter is required');
}
try {
const pakiii = await fzdl(url)
res.json(pakiii);
} catch (error) {
res.status(500).send('Failed to scrape the page');
}
});
async function captureDirectDownloadLink(url, opts = {}) {
const {
chromePath = '/opt/google/chrome/chrome',
headless = 'new',
waitForButtonMs = 60000
} = opts;
let browser = null;
let page = null;
try {
browser = await puppeteer.launch({
executablePath: chromePath,
headless,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-web-security',
'--disable-gpu',
'--disable-blink-features=AutomationControlled',
'--disable-features=IsolateOrigins,site-per-process'
],
});
page = await browser.newPage();
await page.setViewport({ width: 1280, height: 900 });
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36');
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5] });
Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] });
window.chrome = { runtime: {} };
window.DisableDevtool = function() {};
window.qajblusk = false;
});
await page.setRequestInterception(true);
page.on('request', req => {
const rt = req.resourceType();
if (['image', 'font'].includes(rt)) {
req.abort();
} else {
req.continue();
}
});
page.on('console', msg => console.log('PAGE LOG:', msg.text()));
page.on('pageerror', err => console.error('PAGE ERROR:', err));
page.on('response', async response => {
if (response.url() === page.url() && response.request().method() === 'POST') {
console.log('📡 API Response Status:', response.status());
try {
const text = await response.text().catch(() => '[BINARY]');
console.log('📡 API Response Body:', text.substring(0, 500));
} catch (e) {
console.log('📡 API Response: Could not read body');
}
}
});
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 });
await page.waitForFunction(() => document.readyState === 'complete', { timeout: 30000 }).catch(() => {});
await new Promise(resolve => setTimeout(resolve, 2000));
const directBtnId = await page.evaluate(() => {
const buttons = Array.from(document.querySelectorAll('button'));
const targetButton = buttons.find(btn =>
btn.textContent.trim() === 'Direct Download 2'
);
return targetButton ? targetButton.id : null;
});
if (!directBtnId) {
throw new Error('No "Direct Download 2" button found on page!');
}
console.log('✅ Found "Direct Download 2" button with ID:', directBtnId);
console.log(`🖱️ Clicking "Direct Download 2" button (ID: ${directBtnId})...`);
const button = await page.$(`#${directBtnId}`);
if (button) {
await button.click();
} else {
throw new Error('Button disappeared before click');
}
console.log('⏳ Waiting for API request to complete...');
await page.waitForFunction(
id => {
const el = document.getElementById(id);
if (!el) return true;
const span = el.querySelector('.download-text');
return !span || span.textContent.trim() !== 'Processing...';
},
{ timeout: 45000, polling: 500 },
directBtnId
);
console.log('✅ Processing finished. Listening for download...');
const newPagePromise = new Promise(resolve => {
const timeout = setTimeout(() => {
resolve(null);
}, 25000);
browser.once('targetcreated', async target => {
clearTimeout(timeout);
if (target.type() !== 'page') {
resolve(null);
return;
}
try {
const popupPage = await target.page();
if (!popupPage) {
resolve(null);
return;
}
await popupPage.waitForNavigation({ waitUntil: 'networkidle0', timeout: 30000 }).catch(() => {});
resolve(popupPage);
} catch (err) {
console.error('Error handling new tab:', err);
resolve(null);
}
});
});
const newPage = await newPagePromise;
let finalUrl;
if (newPage) {
finalUrl = newPage.url();
console.log('🎉 Captured Download URL from NEW TAB:', finalUrl);
await newPage.close();
} else {
console.log('⚠️ No popup detected. Checking main tab navigation...');
await new Promise(resolve => setTimeout(resolve, 3000));
const currentUrl = page.url();
if (currentUrl !== url && !currentUrl.includes('/images/fordev.jpg') && !currentUrl.startsWith('about:')) {
finalUrl = currentUrl;
console.log('✅ Fallback success: Download URL from MAIN TAB:', finalUrl);
} else {
throw new Error('❌ No new tab OR main tab navigation detected after click.');
}
}
await browser.close();
if (!finalUrl || finalUrl === 'about:blank' || finalUrl.includes('fordev.jpg')) {
throw new Error('❌ Download URL is invalid or blocked.');
}
return { success: true, url: finalUrl };
} catch (err) {
console.error('❌ captureDirectDownloadLink error:', err);
if (page) try { await page.close(); } catch {}
if (browser) try { await browser.close(); } catch {}
return { success: false, error: String(err.message || err) };
}
}
app.get('/api/bypass', async (req, res) => {
const { url } = req.query;
if (!url) return res.status(400).json({ error: 'URL is required' });
try {
const result = await captureDirectDownloadLink(url, {
chromePath: '/opt/google/chrome/chrome',
headless: 'new',
buttonSelector: '#gdriveButton',
fallbackSelector: '.google-download',
waitForButtonMs: 60000,
waitForPostMs: 20000,
});
if (!result.success) {
return res.status(500).json({ error: result.error || 'failed' });
}
// Return cookie header and payload
return res.json({
url: result
});
} catch (err) {
console.error('/api/bypass error:', err);
return res.status(500).json({ error: 'Internal error' });
}
});
const PRESETS = {
"Mobile (375x667)": { width: 375, height: 667 },
"Tablet (768x1024)": { width: 768, height: 1024 },
"PC (1366x768)": { width: 1366, height: 768 },
"Full Screenshot": { width: 1920, height: 1080 }
};
async function takeScreenshot(url, device) {
let browser;
const preset = PRESETS[device] || PRESETS["PC (1366x768)"];
try {
browser = await puppeteer.launch({
executablePath: "/opt/google/chrome/chrome",
args: ['--no-sandbox', '--disable-setuid-sandbox'],
headless: 'new'
});
const page = await browser.newPage();
await page.setViewport({
width: preset.width,
height: preset.height,
deviceScaleFactor: 1
});
await page.goto(url, { waitUntil: 'networkidle0', timeout: 30000 });
let screenshotBuffer;
if (device === "Full Screenshot") {
const bodyHandle = await page.$('body');
const boundingBox = await bodyHandle.boundingBox();
await bodyHandle.dispose();
const fullHeight = boundingBox ? boundingBox.height : preset.height;
await page.setViewport({
width: preset.width,
height: fullHeight,
deviceScaleFactor: 1
});
screenshotBuffer = await page.screenshot({
fullPage: true,
type: 'png'
});
} else {
screenshotBuffer = await page.screenshot({ type: 'png' });
}
return screenshotBuffer;
} catch (error) {
console.error('Puppeteer operation failed:', error);
throw new Error('Screenshot failed: ' + error.message);
} finally {
if (browser) {
await page.close();
}
}
}
app.get('/api/screenshot', async (req, res) => {
const { url, device } = req.query;
if (!url) {
return res.status(400).json({ error: 'URL query parameter is required' });
}
const targetUrl = url.startsWith('http') ? url : `https://${url}`;
try {
const screenshotBuffer = await takeScreenshot(targetUrl, device);
const base64Image = screenshotBuffer.toString('base64');
return res.json({
success: true,
device: device || "PC (1366x768)",
screenshotBase64: base64Image
});
} catch (err) {
console.error('/api/screenshot error:', err.message);
return res.status(500).json({ error: err.message || 'Internal server error during screenshot capture' });
}
});
// 健康检查端点
app.get('/', (req, res) => {
res.json({ status: 'hii', timestamp: new Date().toISOString() });
});
// 启动服务器
app.listen(PORT, () => {
console.log(`Puppeteer服务器运行在 http://localhost:${PORT}`);
console.log('API端点:');
console.log(' POST /api/generate-pdf - 生成PDF');
console.log(' POST /api/generate-images - 生成图片');
console.log(' GET /api/health - 健康检查');
});
module.exports = app;