| import express from 'express'; |
| import fs from 'fs'; |
| import path from 'path'; |
| import cors from 'cors'; |
| import { fileURLToPath } from 'url'; |
| import axios from 'axios'; |
| import YTDown from './scrape/ytdown.js'; |
| import extractAndDownloadTikTok from './scrape/tiktokdl.js'; |
|
|
| const __filename = fileURLToPath(import.meta.url); |
| const __dirname = path.dirname(__filename); |
|
|
| const app = express(); |
| const PORT = process.env.PORT || 7860; |
| const domain = process.env.DOMAIN || `https://fourstore-ytdl-api.hf.space`; |
| const LIB_FOLDER = path.join(__dirname, 'downloads'); |
|
|
| let visitCount = 0; |
| let successDownloads = 0; |
| let failedDownloads = 0; |
|
|
| if (!fs.existsSync(LIB_FOLDER)) { |
| fs.mkdirSync(LIB_FOLDER, { recursive: true }); |
| } |
|
|
| app.use(cors({ |
| origin: '*', |
| methods: ['GET', 'POST', 'OPTIONS'], |
| allowedHeaders: ['Content-Type', 'Authorization'] |
| })); |
|
|
| app.use(express.json()); |
| app.use('/image', express.static(path.join(__dirname, 'image'))); |
|
|
| |
| app.use('/download', (req, res, next) => { |
| const filePath = path.join(LIB_FOLDER, req.path); |
| if (fs.existsSync(filePath)) { |
| const ext = path.extname(filePath).toLowerCase(); |
| if (ext === '.mp3' || ext === '.mp4') { |
| res.setHeader('Content-Disposition', `attachment; filename="${path.basename(filePath)}"`); |
| res.setHeader('Content-Type', ext === '.mp3' ? 'audio/mpeg' : 'video/mp4'); |
| return res.sendFile(filePath); |
| } |
| } |
| next(); |
| }, express.static(LIB_FOLDER)); |
|
|
| const ytdown = new YTDown(); |
|
|
| app.get('/api/download/audio', async (req, res) => { |
| const { url, quality = '128K' } = req.query; |
| if (!url) return res.status(400).json({ error: "YouTube URL required" }); |
| |
| try { |
| const info = await ytdown.getVideoInfo(url); |
| |
| if (info?.api?.status !== 'ok') { |
| throw new Error(info?.api?.message || 'Gagal mendapatkan info video'); |
| } |
| |
| let previewUrl = null; |
| let mediaInfo = null; |
| |
| for (const item of info.api.mediaItems) { |
| if (item.type === 'Audio') { |
| if (item.mediaQuality === quality) { |
| previewUrl = item.mediaPreviewUrl; |
| mediaInfo = item; |
| break; |
| } |
| if (!previewUrl) { |
| previewUrl = item.mediaPreviewUrl; |
| mediaInfo = item; |
| } |
| } |
| } |
| |
| if (!previewUrl) { |
| throw new Error('Audio tidak ditemukan'); |
| } |
| |
| const fileRes = await axios({ |
| method: 'GET', |
| url: previewUrl, |
| responseType: 'arraybuffer', |
| headers: { |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', |
| 'Referer': 'https://www.youtube.com/' |
| } |
| }); |
| |
| const fileId = Math.random().toString(36).substring(2, 8); |
| const safeTitle = info.api.title.replace(/[^a-z0-9]/gi, '_').substring(0, 50); |
| const filename = `${safeTitle}_${fileId}.mp3`; |
| const filePath = path.join(LIB_FOLDER, filename); |
| fs.writeFileSync(filePath, Buffer.from(fileRes.data)); |
| |
| successDownloads++; |
| |
| res.json({ |
| success: true, |
| type: 'AUDIO', |
| title: info.api.title, |
| channel: info.api.userInfo?.name, |
| duration: mediaInfo?.mediaDuration, |
| thumbnail: info.api.imagePreviewUrl, |
| quality: mediaInfo?.mediaQuality, |
| size: mediaInfo?.mediaFileSize, |
| download_url: `${domain}/download/${filename}` |
| }); |
| |
| } catch (error) { |
| failedDownloads++; |
| res.status(500).json({ success: false, error: error.message }); |
| } |
| }); |
|
|
| app.get('/api/download/video', async (req, res) => { |
| const { url, quality = 'HD' } = req.query; |
| if (!url) return res.status(400).json({ error: "YouTube URL required" }); |
| |
| try { |
| const info = await ytdown.getVideoInfo(url); |
| |
| if (info?.api?.status !== 'ok') { |
| throw new Error(info?.api?.message || 'Gagal mendapatkan info video'); |
| } |
| |
| let previewUrl = null; |
| let mediaInfo = null; |
| |
| const qualityMap = { |
| '1080': 'FHD', 'fhd': 'FHD', |
| '720': 'HD', 'hd': 'HD', |
| '480': 'SD', '360': 'SD', '240': 'SD', '144': 'SD' |
| }; |
| |
| const targetQuality = qualityMap[String(quality).toLowerCase()] || quality; |
| |
| for (const item of info.api.mediaItems) { |
| if (item.type === 'Video') { |
| if (item.mediaQuality === targetQuality) { |
| previewUrl = item.mediaPreviewUrl; |
| mediaInfo = item; |
| break; |
| } |
| if (!previewUrl) { |
| previewUrl = item.mediaPreviewUrl; |
| mediaInfo = item; |
| } |
| } |
| } |
| |
| if (!previewUrl) { |
| throw new Error('Video tidak ditemukan'); |
| } |
| |
| const fileRes = await axios({ |
| method: 'GET', |
| url: previewUrl, |
| responseType: 'arraybuffer', |
| headers: { |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', |
| 'Referer': 'https://www.youtube.com/' |
| } |
| }); |
| |
| const fileId = Math.random().toString(36).substring(2, 8); |
| const safeTitle = info.api.title.replace(/[^a-z0-9]/gi, '_').substring(0, 50); |
| const filename = `${safeTitle}_${fileId}.mp4`; |
| const filePath = path.join(LIB_FOLDER, filename); |
| fs.writeFileSync(filePath, Buffer.from(fileRes.data)); |
| |
| successDownloads++; |
| |
| res.json({ |
| success: true, |
| type: 'VIDEO', |
| title: info.api.title, |
| channel: info.api.userInfo?.name, |
| duration: mediaInfo?.mediaDuration, |
| thumbnail: info.api.imagePreviewUrl, |
| quality: mediaInfo?.mediaQuality, |
| resolution: mediaInfo?.mediaRes, |
| size: mediaInfo?.mediaFileSize, |
| download_url: `${domain}/download/${filename}` |
| }); |
| |
| } catch (error) { |
| failedDownloads++; |
| res.status(500).json({ success: false, error: error.message }); |
| } |
| }); |
|
|
| app.get('/api/download/tiktok', async (req, res) => { |
| try { |
| const { url } = req.query; |
| if (!url) return res.status(400).json({ error: "TikTok URL required" }); |
| const result = await extractAndDownloadTikTok(url, domain); |
| successDownloads++; |
| res.json(result); |
| } catch (error) { |
| failedDownloads++; |
| res.status(500).json({ success: false, error: error.message }); |
| } |
| }); |
|
|
| app.get('/', (req, res) => { |
| visitCount++; |
| res.send(`<!DOCTYPE html> |
| <html lang="id"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>FourStore Downloader | YouTube & TikTok</title> |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet"> |
| <style> |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| } |
| |
| body { |
| font-family: 'Inter', sans-serif; |
| background: linear-gradient(135deg, #0f0c29, #302b63, #24243e); |
| min-height: 100vh; |
| color: #fff; |
| } |
| |
| .bg-animation { |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| z-index: -1; |
| overflow: hidden; |
| } |
| |
| .bg-animation span { |
| position: absolute; |
| width: 4px; |
| height: 4px; |
| background: rgba(255,255,255,0.1); |
| border-radius: 50%; |
| animation: float 15s infinite linear; |
| } |
| |
| @keyframes float { |
| 0% { transform: translateY(100vh) scale(0); opacity: 0; } |
| 100% { transform: translateY(-100vh) scale(1); opacity: 0.5; } |
| } |
| |
| .navbar { |
| position: fixed; |
| top: 0; |
| left: 0; |
| right: 0; |
| background: rgba(15, 12, 41, 0.95); |
| backdrop-filter: blur(20px); |
| padding: 16px 32px; |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| z-index: 1000; |
| border-bottom: 1px solid rgba(255,255,255,0.1); |
| } |
| |
| .logo { |
| display: flex; |
| align-items: center; |
| gap: 12px; |
| } |
| |
| .logo-icon { |
| width: 40px; |
| height: 40px; |
| background: linear-gradient(135deg, #ff4b2b, #ff416c); |
| border-radius: 12px; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| font-size: 22px; |
| } |
| |
| .logo-text { |
| font-size: 20px; |
| font-weight: 700; |
| background: linear-gradient(135deg, #fff, #ffd89b); |
| -webkit-background-clip: text; |
| -webkit-text-fill-color: transparent; |
| } |
| |
| .menu-icon { |
| font-size: 24px; |
| cursor: pointer; |
| display: none; |
| } |
| |
| .sidebar { |
| position: fixed; |
| top: 0; |
| left: -280px; |
| width: 280px; |
| height: 100%; |
| background: rgba(15, 12, 41, 0.98); |
| backdrop-filter: blur(20px); |
| z-index: 1100; |
| transition: 0.3s ease; |
| padding: 80px 24px 24px; |
| border-right: 1px solid rgba(255,255,255,0.1); |
| } |
| |
| .sidebar.open { |
| left: 0; |
| } |
| |
| .close-btn { |
| position: absolute; |
| top: 20px; |
| right: 20px; |
| font-size: 28px; |
| cursor: pointer; |
| } |
| |
| .sidebar a { |
| display: block; |
| padding: 14px 16px; |
| color: #fff; |
| text-decoration: none; |
| border-radius: 12px; |
| margin-bottom: 8px; |
| transition: 0.3s; |
| } |
| |
| .sidebar a:hover { |
| background: rgba(255,75,43,0.2); |
| transform: translateX(8px); |
| } |
| |
| .container { |
| max-width: 600px; |
| margin: 0 auto; |
| padding: 100px 24px 60px; |
| } |
| |
| .hero { |
| text-align: center; |
| margin-bottom: 48px; |
| } |
| |
| .hero h1 { |
| font-size: 48px; |
| font-weight: 800; |
| background: linear-gradient(135deg, #fff, #ffd89b, #ff4b2b); |
| -webkit-background-clip: text; |
| -webkit-text-fill-color: transparent; |
| margin-bottom: 16px; |
| } |
| |
| .hero p { |
| font-size: 18px; |
| color: rgba(255,255,255,0.7); |
| } |
| |
| .card { |
| background: rgba(255,255,255,0.05); |
| backdrop-filter: blur(10px); |
| border-radius: 28px; |
| padding: 32px; |
| border: 1px solid rgba(255,255,255,0.1); |
| box-shadow: 0 20px 40px rgba(0,0,0,0.3); |
| } |
| |
| .input-wrapper { |
| margin-bottom: 24px; |
| } |
| |
| .input-wrapper input { |
| width: 100%; |
| padding: 18px 20px; |
| background: rgba(255,255,255,0.1); |
| border: 1px solid rgba(255,255,255,0.2); |
| border-radius: 20px; |
| font-size: 16px; |
| color: #fff; |
| outline: none; |
| transition: 0.3s; |
| } |
| |
| .input-wrapper input:focus { |
| border-color: #ff4b2b; |
| background: rgba(255,255,255,0.15); |
| } |
| |
| .input-wrapper input::placeholder { |
| color: rgba(255,255,255,0.5); |
| } |
| |
| .btn-group { |
| display: grid; |
| grid-template-columns: repeat(3, 1fr); |
| gap: 12px; |
| margin-bottom: 32px; |
| } |
| |
| .btn { |
| padding: 14px; |
| border: none; |
| border-radius: 16px; |
| font-size: 14px; |
| font-weight: 600; |
| cursor: pointer; |
| transition: 0.3s; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| gap: 8px; |
| } |
| |
| .btn-youtube-audio { |
| background: linear-gradient(135deg, #ff4b2b, #ff416c); |
| color: white; |
| } |
| |
| .btn-youtube-video { |
| background: linear-gradient(135deg, #667eea, #764ba2); |
| color: white; |
| } |
| |
| .btn-tiktok { |
| background: linear-gradient(135deg, #25F4EE, #000000); |
| color: white; |
| } |
| |
| .btn:hover { |
| transform: translateY(-3px); |
| filter: brightness(1.1); |
| } |
| |
| .loading { |
| display: none; |
| text-align: center; |
| margin: 24px 0; |
| } |
| |
| .spinner { |
| width: 48px; |
| height: 48px; |
| border: 3px solid rgba(255,255,255,0.2); |
| border-top: 3px solid #ff4b2b; |
| border-radius: 50%; |
| animation: spin 1s linear infinite; |
| margin: 0 auto 12px; |
| } |
| |
| @keyframes spin { |
| 0% { transform: rotate(0deg); } |
| 100% { transform: rotate(360deg); } |
| } |
| |
| .result { |
| margin-top: 32px; |
| } |
| |
| .result-card { |
| background: rgba(255,255,255,0.1); |
| border-radius: 20px; |
| padding: 24px; |
| animation: fadeIn 0.5s ease; |
| } |
| |
| @keyframes fadeIn { |
| from { opacity: 0; transform: translateY(20px); } |
| to { opacity: 1; transform: translateY(0); } |
| } |
| |
| .result-card h3 { |
| font-size: 18px; |
| margin-bottom: 12px; |
| } |
| |
| .result-card img { |
| width: 100%; |
| max-height: 200px; |
| object-fit: cover; |
| border-radius: 16px; |
| margin: 12px 0; |
| } |
| |
| .result-info { |
| margin: 12px 0; |
| font-size: 14px; |
| color: rgba(255,255,255,0.8); |
| } |
| |
| .result-info p { |
| margin: 6px 0; |
| } |
| |
| .download-btn { |
| display: inline-block; |
| width: 100%; |
| padding: 14px; |
| background: linear-gradient(135deg, #00d4ff, #667eea); |
| border: none; |
| border-radius: 16px; |
| color: white; |
| font-weight: 700; |
| text-align: center; |
| text-decoration: none; |
| cursor: pointer; |
| transition: 0.3s; |
| margin-top: 16px; |
| } |
| |
| .download-btn:hover { |
| transform: scale(1.02); |
| filter: brightness(1.1); |
| } |
| |
| .stats { |
| display: flex; |
| gap: 16px; |
| margin-top: 32px; |
| padding-top: 24px; |
| border-top: 1px solid rgba(255,255,255,0.1); |
| } |
| |
| .stat-box { |
| flex: 1; |
| text-align: center; |
| background: rgba(255,255,255,0.05); |
| padding: 16px; |
| border-radius: 20px; |
| } |
| |
| .stat-box span { |
| font-size: 28px; |
| font-weight: 800; |
| display: block; |
| margin-bottom: 6px; |
| background: linear-gradient(135deg, #ffd89b, #ff4b2b); |
| -webkit-background-clip: text; |
| -webkit-text-fill-color: transparent; |
| } |
| |
| .stat-box p { |
| font-size: 12px; |
| color: rgba(255,255,255,0.6); |
| } |
| |
| footer { |
| text-align: center; |
| padding: 32px; |
| color: rgba(255,255,255,0.5); |
| font-size: 13px; |
| } |
| |
| footer a { |
| color: #ff4b2b; |
| text-decoration: none; |
| } |
| |
| @media (max-width: 600px) { |
| .container { padding: 80px 16px 40px; } |
| .hero h1 { font-size: 32px; } |
| .btn-group { grid-template-columns: 1fr; } |
| .menu-icon { display: block; } |
| .stats { flex-direction: column; } |
| .card { padding: 24px; } |
| } |
| </style> |
| </head> |
| <body> |
| <div class="bg-animation" id="bgAnimation"></div> |
| |
| <div class="navbar"> |
| <div class="logo"> |
| <div class="logo-icon">π¬</div> |
| <div class="logo-text">FourStore Downloader</div> |
| </div> |
| <div class="menu-icon" onclick="toggleSidebar()">β°</div> |
| </div> |
| |
| <div class="sidebar" id="sidebar"> |
| <div class="close-btn" onclick="toggleSidebar()">Γ</div> |
| <a href="/">π Beranda</a> |
| <a href="/docs">π Dokumentasi</a> |
| <a href="https://wa.me/6283163784116" target="_blank">π¬ WhatsApp</a> |
| <a href="https://github.com/" target="_blank">π GitHub</a> |
| </div> |
| |
| <div class="container"> |
| <div class="hero"> |
| <h1>Download Anything</h1> |
| <p>YouTube Audio, Video & TikTok β Fast & Free</p> |
| </div> |
| |
| <div class="card"> |
| <div class="input-wrapper"> |
| <input type="text" id="url" placeholder="Paste URL YouTube or TikTok..."> |
| </div> |
| |
| <div class="btn-group"> |
| <button class="btn btn-youtube-audio" onclick="unduh('audio')">π΅ YouTube Audio</button> |
| <button class="btn btn-youtube-video" onclick="unduh('video')">π¬ YouTube Video</button> |
| <button class="btn btn-tiktok" onclick="unduh('tiktok')">π± TikTok</button> |
| </div> |
| |
| <div class="loading" id="loading"> |
| <div class="spinner"></div> |
| <p>Processing your request...</p> |
| </div> |
| |
| <div id="hasil" class="result"></div> |
| |
| <div class="stats"> |
| <div class="stat-box"> |
| <span id="jumlahKunjungan">${visitCount}</span> |
| <p>Total Visits</p> |
| </div> |
| <div class="stat-box"> |
| <span id="berhasilDiunduh">${successDownloads}</span> |
| <p>Successful</p> |
| </div> |
| <div class="stat-box"> |
| <span id="gagalDiunduh">${failedDownloads}</span> |
| <p>Failed</p> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| <footer> |
| <p>Made with β€οΈ by <strong>Fourstore</strong> | Owner: RezaHaris</p> |
| <p>π¬ <a href="https://wa.me/6283163784116" target="_blank">Contact Support</a></p> |
| </footer> |
| |
| <script> |
| const domain = window.location.origin; |
| |
| for (let i = 0; i < 100; i++) { |
| const span = document.createElement('span'); |
| span.style.left = Math.random() * 100 + '%'; |
| span.style.animationDelay = Math.random() * 15 + 's'; |
| span.style.animationDuration = 10 + Math.random() * 20 + 's'; |
| document.getElementById('bgAnimation').appendChild(span); |
| } |
| |
| function toggleSidebar() { |
| document.getElementById('sidebar').classList.toggle('open'); |
| } |
| |
| async function unduh(jenis) { |
| const url = document.getElementById('url').value.trim(); |
| if (!url) { |
| alert("β οΈ Harap masukkan URL!"); |
| return; |
| } |
| |
| document.getElementById('loading').style.display = 'block'; |
| document.getElementById('hasil').innerHTML = ''; |
| |
| try { |
| let endpoint = jenis === 'tiktok' ? 'tiktok' : (jenis === 'audio' ? 'audio' : 'video'); |
| const response = await fetch(domain + '/api/download/' + endpoint + '?url=' + encodeURIComponent(url)); |
| const hasil = await response.json(); |
| |
| document.getElementById('loading').style.display = 'none'; |
| |
| if (hasil.success) { |
| if (jenis === 'tiktok') { |
| let html = '<div class="result-card">'; |
| if (hasil.type === 'video') { |
| html += '<h3>π¬ TikTok Video</h3>'; |
| html += '<p>' + (hasil.description || 'No description') + '</p>'; |
| html += '<video controls style="width:100%; border-radius:16px; margin:12px 0;"><source src="' + hasil.downloadInfo?.download_url + '" type="video/mp4"></video>'; |
| html += '<a href="' + hasil.downloadInfo?.download_url + '" class="download-btn" download>β¬οΈ Download Video</a>'; |
| } else if (hasil.images) { |
| html += '<h3>πΌοΈ TikTok Slides</h3><div style="display:grid; grid-template-columns:repeat(auto-fill,minmax(120px,1fr)); gap:12px; margin:16px 0;">'; |
| for (let img of hasil.images) { |
| html += '<div><img src="' + img.download_url + '" style="width:100%; border-radius:12px;"><a href="' + img.download_url + '" download style="display:block;text-align:center;margin-top:8px;font-size:12px;">β¬οΈ Save</a></div>'; |
| } |
| html += '</div><a href="' + hasil.folder_url + '" class="download-btn">π¦ Download All Images</a>'; |
| } |
| if (hasil.musicInfo?.download_url) { |
| html += '<a href="' + hasil.musicInfo.download_url + '" class="download-btn" style="margin-top:12px;">π΅ Download Audio</a>'; |
| } |
| html += '</div>'; |
| document.getElementById('hasil').innerHTML = html; |
| } else { |
| let html = '<div class="result-card">'; |
| html += '<h3>β¨ ' + hasil.title + '</h3>'; |
| if (hasil.thumbnail) html += '<img src="' + hasil.thumbnail + '" alt="Thumbnail">'; |
| html += '<div class="result-info">'; |
| html += '<p>πΊ Channel: ' + (hasil.channel || '-') + '</p>'; |
| html += '<p>β±οΈ Duration: ' + (hasil.duration || '-') + '</p>'; |
| html += '<p>ποΈ Quality: ' + (hasil.quality || '-') + '</p>'; |
| html += '<p>π¦ Size: ' + (hasil.size || '-') + '</p>'; |
| html += '</div>'; |
| html += '<a href="' + hasil.download_url + '" class="download-btn" download>β¬οΈ Download ' + hasil.type + '</a>'; |
| html += '</div>'; |
| document.getElementById('hasil').innerHTML = html; |
| } |
| updateStats(true); |
| } else { |
| document.getElementById('hasil').innerHTML = '<div class="result-card" style="background:rgba(255,75,43,0.2);"><p>β ' + (hasil.error || 'Download failed') + '</p></div>'; |
| updateStats(false); |
| } |
| } catch (error) { |
| document.getElementById('loading').style.display = 'none'; |
| document.getElementById('hasil').innerHTML = '<div class="result-card" style="background:rgba(255,75,43,0.2);"><p>β Error: ' + error.message + '</p></div>'; |
| updateStats(false); |
| } |
| } |
| |
| function updateStats(success) { |
| let visits = parseInt(document.getElementById('jumlahKunjungan').textContent); |
| document.getElementById('jumlahKunjungan').textContent = visits + 1; |
| |
| if (success) { |
| let successCount = parseInt(document.getElementById('berhasilDiunduh').textContent); |
| document.getElementById('berhasilDiunduh').textContent = successCount + 1; |
| } else { |
| let failCount = parseInt(document.getElementById('gagalDiunduh').textContent); |
| document.getElementById('gagalDiunduh').textContent = failCount + 1; |
| } |
| } |
| </script> |
| </body> |
| </html> |
| `); |
| }); |
|
|
| app.get('/docs', (req, res) => { |
| res.send(`<!DOCTYPE html> |
| <html lang="id"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>API Documentation - FourStore Downloader</title> |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet"> |
| <style> |
| * { margin: 0; padding: 0; box-sizing: border-box; } |
| body { |
| font-family: 'Inter', sans-serif; |
| background: linear-gradient(135deg, #0f0c29, #302b63, #24243e); |
| color: #fff; |
| min-height: 100vh; |
| } |
| .navbar { |
| position: fixed; |
| top: 0; |
| left: 0; |
| right: 0; |
| background: rgba(15,12,41,0.95); |
| backdrop-filter: blur(20px); |
| padding: 16px 32px; |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| z-index: 1000; |
| border-bottom: 1px solid rgba(255,255,255,0.1); |
| } |
| .logo { |
| display: flex; |
| align-items: center; |
| gap: 12px; |
| } |
| .logo-icon { |
| width: 40px; |
| height: 40px; |
| background: linear-gradient(135deg, #ff4b2b, #ff416c); |
| border-radius: 12px; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| font-size: 22px; |
| } |
| .logo-text { |
| font-size: 20px; |
| font-weight: 700; |
| background: linear-gradient(135deg, #fff, #ffd89b); |
| -webkit-background-clip: text; |
| -webkit-text-fill-color: transparent; |
| } |
| .menu-icon { font-size: 24px; cursor: pointer; display: none; } |
| .sidebar { |
| position: fixed; |
| top: 0; |
| left: -280px; |
| width: 280px; |
| height: 100%; |
| background: rgba(15,12,41,0.98); |
| backdrop-filter: blur(20px); |
| z-index: 1100; |
| transition: 0.3s ease; |
| padding: 80px 24px 24px; |
| border-right: 1px solid rgba(255,255,255,0.1); |
| } |
| .sidebar.open { left: 0; } |
| .close-btn { position: absolute; top: 20px; right: 20px; font-size: 28px; cursor: pointer; } |
| .sidebar a { |
| display: block; |
| padding: 14px 16px; |
| color: #fff; |
| text-decoration: none; |
| border-radius: 12px; |
| margin-bottom: 8px; |
| transition: 0.3s; |
| } |
| .sidebar a:hover { background: rgba(255,75,43,0.2); transform: translateX(8px); } |
| .container { max-width: 900px; margin: 0 auto; padding: 100px 24px 60px; } |
| .card { |
| background: rgba(255,255,255,0.05); |
| backdrop-filter: blur(10px); |
| border-radius: 28px; |
| padding: 40px; |
| border: 1px solid rgba(255,255,255,0.1); |
| } |
| h1 { font-size: 36px; margin-bottom: 8px; background: linear-gradient(135deg, #fff, #ffd89b); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } |
| .endpoint { |
| background: rgba(0,0,0,0.3); |
| border-radius: 20px; |
| padding: 24px; |
| margin: 24px 0; |
| border-left: 4px solid #ff4b2b; |
| } |
| .method { |
| display: inline-block; |
| padding: 4px 12px; |
| background: #ff4b2b; |
| border-radius: 20px; |
| font-size: 12px; |
| font-weight: bold; |
| margin-right: 12px; |
| } |
| .url { font-family: monospace; font-size: 14px; color: #ffd89b; } |
| pre { |
| background: rgba(0,0,0,0.5); |
| padding: 16px; |
| border-radius: 12px; |
| overflow-x: auto; |
| margin: 16px 0; |
| font-size: 13px; |
| } |
| table { width: 100%; border-collapse: collapse; margin: 16px 0; } |
| th, td { padding: 12px; text-align: left; border-bottom: 1px solid rgba(255,255,255,0.1); } |
| th { color: #ffd89b; } |
| footer { text-align: center; padding: 32px; color: rgba(255,255,255,0.5); font-size: 13px; } |
| a { color: #ff4b2b; text-decoration: none; } |
| @media (max-width: 600px) { .menu-icon { display: block; } .container { padding: 80px 16px 40px; } .card { padding: 24px; } } |
| </style> |
| </head> |
| <body> |
| <div class="navbar"> |
| <div class="logo"><div class="logo-icon">π</div><div class="logo-text">API Docs</div></div> |
| <div class="menu-icon" onclick="toggleSidebar()">β°</div> |
| </div> |
| <div class="sidebar" id="sidebar"> |
| <div class="close-btn" onclick="toggleSidebar()">Γ</div> |
| <a href="/">π Home</a> |
| <a href="/docs">π Documentation</a> |
| <a href="https://wa.me/6283163784116" target="_blank">π¬ WhatsApp</a> |
| </div> |
| <div class="container"> |
| <div class="card"> |
| <h1>π API Documentation</h1> |
| <p>REST API for downloading YouTube and TikTok content</p> |
| |
| <div class="endpoint"> |
| <h3><span class="method">GET</span> <span class="url">/api/download/audio</span></h3> |
| <p>Download YouTube audio as MP3</p> |
| <h4>Parameters:</h4> |
| <table> |
| <tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr> |
| <tr><td>url</d><d>string</d><d>β</d><d>YouTube video URL</d></tr> |
| <tr><td>quality</d><d>string</d><d>β</d><d>48K or 128K (default: 128K)</d></tr> |
| </table> |
| <h4>Example:</h4> |
| <pre>GET ${domain}/api/download/audio?url=https://youtube.com/watch?v=VIDEO_ID</pre> |
| </div> |
| |
| <div class="endpoint"> |
| <h3><span class="method">GET</span> <span class="url">/api/download/video</span></h3> |
| <p>Download YouTube video as MP4</p> |
| <h4>Parameters:</h4> |
| <table> |
| <tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr> |
| <tr><td>url</d><d>string</d><d>β</d><d>YouTube video URL</d></tr> |
| <tr><td>quality</d><d>string</d><d>β</d><d>144/240/360/480/720/1080 or SD/HD/FHD (default: HD)</d></tr> |
| </table> |
| <h4>Example:</h4> |
| <pre>GET ${domain}/api/download/video?url=https://youtube.com/watch?v=VIDEO_ID&quality=720</pre> |
| </div> |
| |
| <div class="endpoint"> |
| <h3><span class="method">GET</span> <span class="url">/api/download/tiktok</span></h3> |
| <p>Download TikTok video or slides</p> |
| <h4>Parameters:</h4> |
| <table> |
| <tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr> |
| <tr><td>url</d><d>string</d><d>β</d><d>TikTok video/photo URL</d></tr> |
| </table> |
| <h4>Example:</h4> |
| <pre>GET ${domain}/api/download/tiktok?url=https://tiktok.com/@user/video/123456789</pre> |
| </div> |
| |
| <h3>π¦ Response Format</h3> |
| <pre>{ |
| "success": true, |
| "type": "AUDIO", |
| "title": "Video Title", |
| "channel": "Channel Name", |
| "duration": "03:15", |
| "thumbnail": "https://...", |
| "quality": "128K", |
| "size": "3.31 MB", |
| "download_url": "${domain}/download/audio_abc123.mp3" |
| }</pre> |
| </div> |
| </div> |
| <footer>Made with β€οΈ by Fourstore | Owner: RezaHaris | <a href="https://wa.me/6283163784116">Contact</a></footer> |
| <script> |
| function toggleSidebar() { document.getElementById('sidebar').classList.toggle('open'); } |
| </script> |
| </body> |
| </html> |
| `); |
| }); |
|
|
| app.listen(PORT, () => { |
| console.log(`Server running at ${domain}`); |
| console.log(`Documentation available at ${domain}/docs`); |
| }); |