| const express = require('express'); |
| const fileUpload = require('express-fileupload'); |
| const ffmpeg = require('fluent-ffmpeg'); |
| const fs = require('fs'); |
| const path = require('path'); |
| const { execSync } = require('child_process'); |
|
|
| const app = express(); |
| const PORT = process.env.PORT || 7860; |
|
|
| |
| try { |
| const ffmpegPath = execSync('which ffmpeg').toString().trim(); |
| ffmpeg.setFfmpegPath(ffmpegPath); |
| console.log('[INFO] FFmpeg path:', ffmpegPath); |
| } catch (err) { |
| console.error('[ERROR] FFmpeg not found'); |
| } |
|
|
| app.use(fileUpload()); |
| app.use(express.json()); |
|
|
| |
| app.get('/', (req, res) => { |
| res.send('🟢 FFmpeg API is online'); |
| }); |
|
|
| |
| app.post('/api/clip', async (req, res) => { |
| const { start, duration, format } = req.body; |
| if (!req.files || !req.files.video) return res.status(400).send('No video uploaded'); |
|
|
| const inputPath = path.join('/tmp', 'input.mp4'); |
| const outputExt = format || 'mp4'; |
| const outputPath = path.join('/tmp', `output.${outputExt}`); |
|
|
| try { |
| await req.files.video.mv(inputPath); |
| ffmpeg(inputPath) |
| .setStartTime(start || 0) |
| .setDuration(duration || 5) |
| .toFormat(outputExt) |
| .output(outputPath) |
| .on('start', cmd => console.log('[FFmpeg]', cmd)) |
| .on('stderr', line => console.log('[FFmpeg]', line)) |
| .on('end', () => { |
| res.setHeader('Content-Type', 'video/mp4'); |
| const readStream = fs.createReadStream(outputPath); |
| readStream.pipe(res); |
| readStream.on('close', () => { |
| fs.unlinkSync(inputPath); |
| fs.unlinkSync(outputPath); |
| }); |
| }) |
| .on('error', err => { |
| console.error('[FFmpeg Error]', err); |
| res.status(500).send('Video processing failed'); |
| }) |
| .run(); |
| } catch (err) { |
| console.error('[Server Error]', err); |
| res.status(500).send('Internal error'); |
| } |
| }); |
|
|
| |
| app.post('/api/mpd-to-mp4', async (req, res) => { |
| const { url } = req.body; |
| const outputPath = path.join('/tmp', 'output.mp4'); |
|
|
| if (!url) return res.status(400).send('Missing URL'); |
|
|
| try { |
| ffmpeg(url) |
| .outputOptions('-c copy') |
| .on('start', cmd => console.log('[FFmpeg]', cmd)) |
| .on('stderr', line => console.log('[FFmpeg]', line)) |
| .on('end', () => { |
| const readStream = fs.createReadStream(outputPath); |
| res.setHeader('Content-Type', 'video/mp4'); |
| readStream.pipe(res); |
| readStream.on('close', () => { |
| fs.unlinkSync(outputPath); |
| }); |
| }) |
| .on('error', err => { |
| console.error('[FFmpeg Error]', err); |
| res.status(500).send('Failed to fetch video'); |
| }) |
| .save(outputPath); |
| } catch (err) { |
| console.error('[Server Error]', err); |
| res.status(500).send('Internal server error'); |
| } |
| }); |
|
|
| |
| |
|
|
| app.post('/api/add-text-on-top', async (req, res) => { |
| if ( |
| !req.files || |
| !req.files.video || |
| !req.body.textcolor || |
| !req.body.height || |
| !req.body.drawtextFilters |
| ) { |
| return res.status(400).send('Missing Params'); |
| } |
|
|
| const uploadedVideo = req.files.video; |
| const drawtextFilters = req.body.drawtextFilters; |
| const textColor = req.body.textcolor; |
| const height = req.body.height; |
| const inputPath = path.join('/tmp', 'input.mp4'); |
| const outputPath = path.join('/tmp', 'output.mp4'); |
|
|
| await uploadedVideo.mv(inputPath); |
|
|
| let ffmpegLogs = ''; |
| ffmpeg(inputPath) |
| .videoFilters([ |
| `drawbox=x=0:y=0:w=iw:h=${height}:color=${textColor}:t=fill`, |
| `${drawtextFilters}` |
| ]) |
| .on('stderr', line => { |
| console.log('[FFmpeg]', line); |
| ffmpegLogs += line + '\n'; |
| }) |
| .on('end', () => { |
| res.download(outputPath, 'video_with_text.mp4', err => { |
| if (err) console.error('Download error:', err); |
| fs.unlinkSync(inputPath); |
| fs.unlinkSync(outputPath); |
| }); |
| }) |
| .on('error', err => { |
| console.error('FFmpeg error:', err); |
| if (fs.existsSync(inputPath)) fs.unlinkSync(inputPath); |
| if (fs.existsSync(outputPath)) fs.unlinkSync(outputPath); |
| res.status(500).send(`Failed to add text to video:\n${ffmpegLogs.slice(0, 500)}`); |
| }) |
| .save(outputPath); |
| }); |
|
|
|
|
|
|
| |
| setInterval(() => { |
| for (const file of ['input.mp4', 'output.mp4']) { |
| const filePath = path.join('/tmp', file); |
| if (fs.existsSync(filePath)) fs.unlinkSync(filePath); |
| } |
| }, 15 * 60 * 1000); |
|
|
| app.listen(PORT, () => console.log(`✅ Server running at http://localhost:${PORT}`)); |