const express = require('express'); const cors = require('cors'); const swaggerUi = require('swagger-ui-express'); const fs = require('fs'); const path = require('path'); const { exec } = require('child_process'); const { trackUsage, getUsageStats } = require('./utils/usageTracker'); const app = express(); app.use(cors()); app.use(express.json()); // Config const DOMAIN = process.env.DOMAIN || 'https://rezaharis-hai.hf.space'; const AUTHOR = 'Fourstore'; const RELEASE_DATE = new Date().toISOString().split('T')[0]; // Ensure directories exist const cookiesDir = path.join(__dirname, 'cookies'); const downloadsDir = path.join(__dirname, 'downloads'); const cookieFile = path.join(cookiesDir, 'youtube_cookie.txt'); [downloadsDir, cookiesDir].forEach(dir => { if (!fs.existsSync(dir)) fs.mkdirSync(dir); }); // Fix for static files with special characters app.use('/downloads', express.static(downloadsDir, { setHeaders: (res, filePath) => { const ext = path.extname(filePath); if (['.mp3', '.webm', '.mp4'].includes(ext)) { res.setHeader('Content-Disposition', 'inline'); } } })); // Swagger setup const swaggerDocument = require('./swagger.json'); app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); // Homepage app.get('/', (req, res) => { res.json({ service: "YouTube Downloader API", author: AUTHOR, release_date: RELEASE_DATE, endpoints: { download: `${DOMAIN}/download`, downloads: `${DOMAIN}/downloads/{filename}`, docs: `${DOMAIN}/api-docs` } }); }); // Download endpoint app.post('/download', (req, res) => { const { url, format = 'audio' } = req.body; if (!url) return res.status(400).json({ error: "URL is required" }); trackUsage('/download'); const outputTemplate = `${downloadsDir}/%(title)s.%(ext)s`; let command = `yt-dlp --cookies ${cookieFile} -o "${outputTemplate}" "${url}"`; if (format === 'audio') { command += ' -x --audio-format mp3'; } console.log(`[EXEC] Running: ${command}`); exec(command, (error, stdout, stderr) => { if (error) { console.error(`[ERROR] ${error.message}`); return res.status(500).json({ error: "Download failed", details: stderr }); } // Parse filename from yt-dlp output const filenameMatch = stdout.match(/Destination: (.+)/) || stdout.match(/\[ExtractAudio\] Destination: (.+)/); if (!filenameMatch) { return res.status(500).json({ error: "Cannot determine output file" }); } const localPath = filenameMatch[1]; const filename = path.basename(localPath); const encodedFilename = encodeURIComponent(filename); // Encode untuk URL const downloadUrl = `${DOMAIN}/downloads/${encodedFilename}`; res.json({ success: true, filename: filename, download_url: downloadUrl, direct_access: `${DOMAIN}/downloads/${filename.replace(/ /g, '%20')}`, // Alternatif encode format: format }); }); }); const PORT = process.env.PORT || 7860; app.listen(PORT, () => { console.log(`Server ready at ${DOMAIN}`); console.log(`Downloads directory: ${downloadsDir}`); });