|
|
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()); |
|
|
|
|
|
|
|
|
const DOMAIN = process.env.DOMAIN || 'https://rezaharis-hai.hf.space'; |
|
|
const AUTHOR = 'Fourstore'; |
|
|
const RELEASE_DATE = new Date().toISOString().split('T')[0]; |
|
|
|
|
|
|
|
|
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); |
|
|
}); |
|
|
|
|
|
|
|
|
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'); |
|
|
} |
|
|
} |
|
|
})); |
|
|
|
|
|
|
|
|
const swaggerDocument = require('./swagger.json'); |
|
|
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); |
|
|
|
|
|
|
|
|
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` |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
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 }); |
|
|
} |
|
|
|
|
|
|
|
|
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); |
|
|
const downloadUrl = `${DOMAIN}/downloads/${encodedFilename}`; |
|
|
|
|
|
res.json({ |
|
|
success: true, |
|
|
filename: filename, |
|
|
download_url: downloadUrl, |
|
|
direct_access: `${DOMAIN}/downloads/${filename.replace(/ /g, '%20')}`, |
|
|
format: format |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
const PORT = process.env.PORT || 7860; |
|
|
app.listen(PORT, () => { |
|
|
console.log(`Server ready at ${DOMAIN}`); |
|
|
console.log(`Downloads directory: ${downloadsDir}`); |
|
|
}); |