wella / app.js
Reaperxxxx's picture
Update app.js
7658a6b verified
Raw
History Blame Contribute Delete
13.1 kB
import express from 'express';
import axios from 'axios';
import * as cheerio from 'cheerio';
const app = express();
app.use(express.json());
const VIDEO_EXTENSIONS = ['.mkv', '.mp4', '.mov', '.avi', '.webm', '.flv', '.wmv', '.m4v', '.ts'];
/**
* Extract final download link from downloadwella/wetafiles pages
*/
async function extractDownloadLink(url) {
const capturedLinks = new Set();
const navigationLog = [];
let finalDownloadUrl = null;
// Create axios client with standard headers
const client = axios.create({
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1'
},
maxRedirects: 0,
validateStatus: (status) => status < 500,
timeout: 30000
});
try {
console.log(`\nπŸ” Fetching: ${url}`);
navigationLog.push({ time: new Date().toISOString(), url, step: 'initial' });
// FIRST REQUEST - Get the initial page
let response = await client.get(url);
console.log(`βœ… Initial page status: ${response.status}`);
let $ = cheerio.load(response.data);
const pageUrl = response.request.res.responseUrl || url;
// Extract form data from the page
const form = $('form[name="F1"]');
if (form.length === 0) {
throw new Error('Could not find form[name="F1"] on page');
}
const formData = {};
form.find('input[type="hidden"]').each((i, elem) => {
const name = $(elem).attr('name');
const value = $(elem).attr('value') || '';
if (name) {
formData[name] = value;
}
});
console.log('\nπŸ“ Extracted form data:');
Object.keys(formData).forEach(key => {
console.log(` ${key}: ${formData[key]}`);
});
// Wait a bit to simulate human behavior
await new Promise(resolve => setTimeout(resolve, 2000));
// FIRST FORM SUBMISSION
console.log('\nπŸ–±οΈ FIRST FORM SUBMIT...');
navigationLog.push({ time: new Date().toISOString(), url: pageUrl, step: 'first_submit' });
const submitUrl = pageUrl;
const postData = new URLSearchParams(formData).toString();
let submitResponse;
try {
submitResponse = await client.post(submitUrl, postData, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Origin': new URL(url).origin,
'Referer': pageUrl
},
maxRedirects: 0,
validateStatus: (status) => status < 500
});
} catch (error) {
if (error.response) {
submitResponse = error.response;
} else {
throw error;
}
}
console.log(`βœ… First submit status: ${submitResponse.status}`);
// Handle redirect
let currentUrl = submitResponse.request.res.responseUrl || submitUrl;
if (submitResponse.status === 302 || submitResponse.status === 301) {
const redirectUrl = submitResponse.headers.location;
currentUrl = redirectUrl.startsWith('http') ? redirectUrl : new URL(redirectUrl, submitUrl).href;
console.log(`πŸ”„ Redirected to: ${currentUrl}`);
navigationLog.push({ time: new Date().toISOString(), url: currentUrl, step: 'after_first_redirect' });
// Check if this redirect URL is already a download link
if (currentUrl.includes('/d/') && VIDEO_EXTENSIONS.some(ext => currentUrl.includes(ext))) {
finalDownloadUrl = currentUrl;
capturedLinks.add(currentUrl);
console.log(`βœ… βœ… βœ… DOWNLOAD LINK FOUND IN FIRST REDIRECT: ${currentUrl}`);
// Return immediately - no need to continue
return {
success: true,
downloadUrl: finalDownloadUrl,
alternativeLinks: [],
totalFound: 1,
navigationLog: navigationLog,
allCapturedLinks: Array.from(capturedLinks)
};
}
// Follow the redirect only if we didn't find the download link
submitResponse = await client.get(currentUrl, {
headers: { 'Referer': submitUrl }
});
}
$ = cheerio.load(submitResponse.data);
// Check if we need second submission (look for download button)
const downloadBtn = $('#downloadbtn');
const secondForm = $('form[name="F1"]');
if (downloadBtn.length > 0 && secondForm.length > 0) {
console.log('\nπŸ–±οΈ SECOND FORM SUBMIT (triggering download)...');
// Wait before second submit
await new Promise(resolve => setTimeout(resolve, 3000));
// Extract updated form data
const secondFormData = {};
secondForm.find('input[type="hidden"]').each((i, elem) => {
const name = $(elem).attr('name');
const value = $(elem).attr('value') || '';
if (name) {
secondFormData[name] = value;
}
});
console.log(' Second form data:', secondFormData);
const secondSubmitUrl = currentUrl;
const secondPostData = new URLSearchParams(secondFormData).toString();
navigationLog.push({ time: new Date().toISOString(), url: secondSubmitUrl, step: 'second_submit' });
try {
const secondSubmit = await client.post(secondSubmitUrl, secondPostData, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Origin': new URL(url).origin,
'Referer': currentUrl
},
maxRedirects: 0,
validateStatus: (status) => status < 500
});
console.log(`βœ… Second submit status: ${secondSubmit.status}`);
// Check for download URL in redirect
if (secondSubmit.status === 302 || secondSubmit.status === 301) {
const downloadRedirect = secondSubmit.headers.location;
const fullDownloadUrl = downloadRedirect.startsWith('http')
? downloadRedirect
: new URL(downloadRedirect, secondSubmitUrl).href;
console.log(`πŸ”„ Download redirect: ${fullDownloadUrl}`);
navigationLog.push({ time: new Date().toISOString(), url: fullDownloadUrl, step: 'download_redirect' });
if (fullDownloadUrl.includes('/d/') && VIDEO_EXTENSIONS.some(ext => fullDownloadUrl.includes(ext))) {
finalDownloadUrl = fullDownloadUrl;
capturedLinks.add(fullDownloadUrl);
console.log(`βœ… βœ… βœ… DOWNLOAD LINK FOUND: ${fullDownloadUrl}`);
}
} else {
// Check response body for download links
$ = cheerio.load(secondSubmit.data);
$('a').each((i, elem) => {
const href = $(elem).attr('href');
if (href && href.includes('/d/')) {
const fullHref = href.startsWith('http') ? href : new URL(href, secondSubmitUrl).href;
if (VIDEO_EXTENSIONS.some(ext => fullHref.includes(ext))) {
capturedLinks.add(fullHref);
if (!finalDownloadUrl) finalDownloadUrl = fullHref;
console.log(` Found in HTML: ${fullHref}`);
}
}
});
}
} catch (error) {
if (error.response && (error.response.status === 302 || error.response.status === 301)) {
const downloadRedirect = error.response.headers.location;
const fullDownloadUrl = downloadRedirect.startsWith('http')
? downloadRedirect
: new URL(downloadRedirect, secondSubmitUrl).href;
if (fullDownloadUrl.includes('/d/') && VIDEO_EXTENSIONS.some(ext => fullDownloadUrl.includes(ext))) {
finalDownloadUrl = fullDownloadUrl;
capturedLinks.add(fullDownloadUrl);
console.log(`βœ… βœ… βœ… DOWNLOAD LINK FOUND: ${fullDownloadUrl}`);
}
navigationLog.push({ time: new Date().toISOString(), url: fullDownloadUrl, step: 'download_redirect' });
} else {
console.error('Error in second submit:', error.message);
}
}
}
// Final scan for any missed download links
if (!finalDownloadUrl) {
console.log('\nπŸ” Scanning page for download links...');
$('a').each((i, elem) => {
const href = $(elem).attr('href');
if (href && href.includes('/d/')) {
const fullHref = href.startsWith('http') ? href : new URL(href, currentUrl).href;
if (VIDEO_EXTENSIONS.some(ext => fullHref.includes(ext))) {
capturedLinks.add(fullHref);
if (!finalDownloadUrl) finalDownloadUrl = fullHref;
console.log(` Found: ${fullHref}`);
}
}
});
}
} catch (error) {
console.error('❌ Error:', error.message);
if (error.response) {
console.error(` Status: ${error.response.status}`);
}
}
const allLinks = Array.from(capturedLinks);
return {
success: finalDownloadUrl !== null || allLinks.length > 0,
downloadUrl: finalDownloadUrl || allLinks[0],
alternativeLinks: allLinks.slice(1, 5),
totalFound: allLinks.length,
navigationLog: navigationLog,
allCapturedLinks: allLinks
};
}
/**
* API Endpoint
*/
app.get('/extract', async (req, res) => {
const { url } = req.query;
if (!url) {
return res.status(400).json({
error: 'URL parameter is required',
usage: '/extract?url=YOUR_DOWNLOAD_PAGE_URL'
});
}
try {
console.log('\nπŸš€ Starting download link extraction...');
console.log(`πŸ“ Target: ${url}\n`);
const result = await extractDownloadLink(url);
if (!result.success || !result.downloadUrl) {
return res.status(404).json({
error: 'No download link found',
navigationLog: result.navigationLog,
capturedLinks: result.allCapturedLinks,
message: 'Could not extract download link. Check logs for navigation history.'
});
}
// Extract movie title
const urlPath = result.downloadUrl.split('/').pop().split('?')[0];
const movieTitle = urlPath
.replace(/\.(mkv|mp4|mov|avi|webm|flv|wmv|m4v|ts)$/i, '')
.replace(/[\.\-_\(\)]/g, ' ')
.replace(/\s+/g, ' ')
.trim();
console.log('\nβœ… EXTRACTION SUCCESSFUL!');
console.log(`🎬 Title: ${movieTitle}`);
console.log(`πŸ”— Download URL: ${result.downloadUrl}\n`);
res.json({
success: true,
movie: movieTitle || 'Unknown',
downloadUrl: result.downloadUrl,
alternativeLinks: result.alternativeLinks,
totalLinksFound: result.totalFound,
navigationLog: result.navigationLog
});
} catch (error) {
console.error('\n❌ Fatal Error:', error);
res.status(500).json({
error: 'Internal server error',
message: error.message
});
}
});
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
const PORT = process.env.PORT || 7860;
app.listen(PORT, () => {
console.log('\n' + '='.repeat(50));
console.log('πŸš€ Downloadwella Axios-Based Extractor');
console.log('='.repeat(50));
console.log(`πŸ“‘ Server: http://localhost:${PORT}`);
console.log(`πŸ“ Usage: http://localhost:${PORT}/extract?url=YOUR_URL`);
console.log(`πŸ’š Health: http://localhost:${PORT}/health`);
console.log('='.repeat(50) + '\n');
});
export default app;