Spaces:
Paused
Paused
File size: 4,194 Bytes
7497a2c 5959f43 7497a2c 6cf3361 5959f43 6cf3361 7497a2c 6cf3361 5959f43 6cf3361 7497a2c 5959f43 7497a2c 5959f43 7497a2c 5959f43 7497a2c 2056762 7497a2c 5959f43 7497a2c 5959f43 7497a2c 5959f43 7497a2c 5959f43 7497a2c 5959f43 7497a2c 5959f43 7497a2c 2056762 5959f43 7497a2c 5959f43 7497a2c 5959f43 7497a2c 5959f43 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | const express = require('express');
const yts = require('yt-search');
const { spawn, exec } = require('child_process');
const util = require('util');
const path = require('path');
const dns = require('dns');
// Fixes the ENOTFOUND error for yt-search inside Docker
dns.setDefaultResultOrder('ipv4first');
const execPromise = util.promisify(exec);
const app = express();
app.use(express.json());
app.get('/', (req, res, next) => {
if (req.accepts('html')) next();
else res.json({ status: "Online", creator: "Silent Tech API" });
});
app.use(express.static(path.join(__dirname, 'public')));
// ββββββββββββββββββββββββββββββββββββββββββββ
// π ENDPOINT 1: YT SEARCH (Using yts)
// ββββββββββββββββββββββββββββββββββββββββββββ
app.get('/api/ytsearch', async (req, res) => {
try {
const query = req.query.q;
if (!query) return res.status(400).json({ success: false, error: 'Missing parameter: q' });
const search = await yts(query);
const videos = search.videos.slice(0, 10).map(v => ({
title: v.title,
url: v.url,
duration: v.timestamp,
views: v.views.toLocaleString(),
thumbnail: v.thumbnail,
author: v.author.name
}));
res.json({ creator: 'Silent Tech', success: true, results: videos });
} catch (e) {
res.status(500).json({ success: false, error: "Search failed: " + e.message });
}
});
// ββββββββββββββββββββββββββββββββββββββββββββ
// π΅ ENDPOINT 2: METADATA & STREAM LINKS
// ββββββββββββββββββββββββββββββββββββββββββββ
async function getVideoMeta(url) {
// Removed the broken IP flag, standard yt-dlp only
const { stdout } = await execPromise(`yt-dlp -J "${url.replace(/(["'$`\\])/g, '\\$1')}"`);
return JSON.parse(stdout);
}
app.get('/api/ytmp3', async (req, res) => {
try {
const url = req.query.url;
const meta = await getVideoMeta(url);
const host = req.headers['x-forwarded-proto'] ? `${req.headers['x-forwarded-proto']}://${req.get('host')}` : `http://${req.get('host')}`;
res.json({ success: true, title: meta.title, thumbnail: meta.thumbnail, download_url: `${host}/api/stream?url=${encodeURIComponent(url)}&type=mp3` });
} catch (e) { res.status(500).json({ success: false, error: e.message }); }
});
app.get('/api/ytmp4', async (req, res) => {
try {
const url = req.query.url;
const meta = await getVideoMeta(url);
const host = req.headers['x-forwarded-proto'] ? `${req.headers['x-forwarded-proto']}://${req.get('host')}` : `http://${req.get('host')}`;
res.json({ success: true, title: meta.title, thumbnail: meta.thumbnail, download_url: `${host}/api/stream?url=${encodeURIComponent(url)}&type=mp4` });
} catch (e) { res.status(500).json({ success: false, error: e.message }); }
});
// ββββββββββββββββββββββββββββββββββββββββββββ
// π‘οΈ INTERNAL STREAMER (Bypasses Bot IP Bans)
// ββββββββββββββββββββββββββββββββββββββββββββ
app.get('/api/stream', (req, res) => {
const { url, type } = req.query;
if (!url || !type) return res.status(400).send('Missing parameters');
const format = type === 'mp4' ? 'best[ext=mp4]' : 'bestaudio';
res.header('Content-Disposition', `attachment; filename="SilentTech.${type}"`);
if (type === 'mp4') res.header('Content-Type', 'video/mp4');
if (type === 'mp3') res.header('Content-Type', 'audio/mpeg');
const ytdlp = spawn('yt-dlp', ['-f', format, '-o', '-', url]);
ytdlp.stdout.pipe(res);
});
app.listen(7860, "0.0.0.0", () => console.log('Silent Tech Multi-API running on port 7860')); |