UNPKG

anydownload

Version:

A powerful website downloader with GUI support

164 lines (143 loc) 5.4 kB
const express = require('express'); const http = require('http'); const socketIo = require('socket.io'); const path = require('path'); const fs = require('fs-extra'); const { Downloader } = require('./src/downloader'); // Create Express app const app = express(); const server = http.createServer(app); const io = socketIo(server); // Middleware app.use(express.json()); app.use(express.urlencoded({ extended: true })); // View engine setup app.set('view engine', 'ejs'); app.set('views', path.join(__dirname, 'views')); // Static files app.use(express.static(path.join(__dirname, 'public'))); // Routes app.get('/', (req, res) => { res.render('index'); }); // API download route app.post('/api/download', async (req, res) => { const opts = req.body; if (!opts.url) { return res.json({ success: false, error: 'Please provide a website URL' }); } try { const outputDir = opts.output || 'downloaded_site'; const host = new URL(opts.url).host.replace(/[:\/\\]/g, '_'); const folder = path.join(outputDir, host); // Remove old folder if exists if (fs.existsSync(folder)) { await fs.remove(folder); } // Create downloader instance with options const downloader = new Downloader({ ...opts, outputDir, recursive: opts.recursive === 'true', maxDepth: Number(opts.maxDepth) || 1, delay: Number(opts.delay) || 1000, concurrency: Number(opts.concurrency) || 5, retry: Number(opts.retry) || 3, headless: opts.headless === 'true', dynamic: opts.dynamic === 'true', type: opts.type || 'all', onResource: (resourceUrl, idx, total, speed, eta) => { // Emit progress updates to connected clients io.emit('download-progress', { current: idx, total: total, file: resourceUrl, speed: speed ? `${speed} KB/s` : 'Calculating...', eta: eta ? `${eta}s` : 'Calculating...' }); } }); // Start download await downloader.downloadWebsite(opts.url); // Notify completion io.emit('download-complete'); res.json({ success: true, folder, message: 'Download completed successfully' }); } catch (error) { console.error('Download error:', error); res.json({ success: false, error: error.message || 'An error occurred during download' }); } }); // API open website route app.post('/api/open-website', async (req, res) => { const { folder } = req.body; if (!folder) { return res.json({ success: false, error: '請提供網站資料夾路徑' }); } try { // 使用絕對路徑 const absoluteFolder = path.resolve(folder); const indexFile = path.join(absoluteFolder, 'index.html'); console.log('嘗試開啟檔案:', indexFile); if (!fs.existsSync(indexFile)) { console.log('找不到檔案:', indexFile); return res.json({ success: false, error: '找不到網站首頁檔案' }); } // 使用系統預設瀏覽器開啟網站 const { exec } = require('child_process'); const platform = process.platform; let command; if (platform === 'win32') { // Windows 使用 file:// 協議 const fileUrl = `file:///${indexFile.replace(/\\/g, '/')}`; command = `start "" "${fileUrl}"`; } else if (platform === 'darwin') { command = `open "${indexFile}"`; } else { command = `xdg-open "${indexFile}"`; } console.log('執行命令:', command); exec(command, (error, stdout, stderr) => { if (error) { console.error('開啟網站錯誤:', error); console.error('錯誤輸出:', stderr); return res.json({ success: false, error: error.message }); } console.log('命令輸出:', stdout); res.json({ success: true }); }); } catch (error) { console.error('開啟網站錯誤:', error); res.json({ success: false, error: error.message }); } }); // Error handling middleware app.use((err, req, res, next) => { console.error('Server error:', err.stack); res.status(500).json({ success: false, error: 'Internal server error' }); }); // Start server const PORT = process.env.PORT || 3000; server.listen(PORT, () => { console.log(`Web GUI running at http://localhost:${PORT}`); console.log('Press Ctrl+C to stop the server'); }).on('error', (err) => { if (err.code === 'EADDRINUSE') { console.log(`Port ${PORT} is in use, trying port ${PORT + 1}...`); server.listen(PORT + 1, () => { console.log(`Web GUI running at http://localhost:${PORT + 1}`); console.log('Press Ctrl+C to stop the server'); }); } else { console.error('Server error:', err); } });