const express = require('express'); const { createServer } = require('http'); const { Server } = require('socket.io'); const cors = require('cors'); const helmet = require('helmet'); const compression = require('compression'); const dotenv = require('dotenv'); const path = require('path'); const fs = require('fs').promises; // Load environment variables dotenv.config(); class TikVibeServer { constructor() { this.app = express(); this.httpServer = createServer(this.app); // Setup Socket.IO for real-time features this.io = new Server(this.httpServer, { cors: { origin: '*', credentials: true, methods: ['GET', 'POST'] }, maxHttpBufferSize: 1e8 // 100MB for video streaming }); this.PORT = process.env.PORT || 7860; this.connectedUsers = new Map(); this.projectRooms = new Map(); this.activeRenders = new Map(); } async initialize() { console.log('๐Ÿš€ Initializing TikVibe Backend Server...'); // Create required directories await this.createDirectories(); this.setupMiddleware(); this.setupRoutes(); this.setupWebSocket(); this.setupErrorHandling(); this.setupCleanupJobs(); console.log('โœ… Server initialization complete'); } async createDirectories() { const dirs = ['uploads', 'renders', 'temp']; for (const dir of dirs) { const dirPath = path.join(__dirname, '..', dir); await fs.mkdir(dirPath, { recursive: true }); console.log(`โœ… Directory ready: ${dir}`); } } setupMiddleware() { // Security middleware this.app.use(helmet({ contentSecurityPolicy: false, crossOriginEmbedderPolicy: false, crossOriginResourcePolicy: { policy: "cross-origin" } })); // CORS configuration this.app.use(cors({ origin: '*', credentials: true, methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'] })); // Compression for faster responses this.app.use(compression({ level: 6, threshold: 1024, filter: (req, res) => { if (req.headers['x-no-compression']) return false; return compression.filter(req, res); } })); // Body parsing with increased limits for video this.app.use(express.json({ limit: '500mb' })); this.app.use(express.urlencoded({ extended: true, limit: '500mb' })); // Static file serving this.app.use('/uploads', express.static('uploads')); this.app.use('/renders', express.static('renders')); // Request logging with timing this.app.use((req, res, next) => { req.startTime = Date.now(); console.log(`๐Ÿ“ฅ ${new Date().toISOString()} - ${req.method} ${req.path}`); res.on('finish', () => { const duration = Date.now() - req.startTime; console.log(`๐Ÿ“ค ${req.method} ${req.path} - ${res.statusCode} - ${duration}ms`); }); next(); }); } setupRoutes() { // Health check with system info this.app.get('/health', (req, res) => { res.json({ status: 'healthy', timestamp: new Date().toISOString(), uptime: process.uptime(), environment: process.env.NODE_ENV || 'production', server: 'TikVibe Backend', version: '2.0.0', system: { memory: process.memoryUsage(), cpu: process.cpuUsage(), node: process.version, platform: process.platform } }); }); // Root endpoint with full API documentation this.app.get('/', (req, res) => { res.json({ name: '๐ŸŽฅ TikVibe Creator Pro API', version: '2.0.0', description: 'Advanced AI-Powered Video Editing Platform', documentation: '/api/docs', endpoints: { health: { path: '/health', method: 'GET', description: 'System health check' }, test: { path: '/api/test', method: 'GET', description: 'Test API connection' }, ai: { velocity: { path: '/api/ai/velocity-edit', method: 'POST', description: 'Create velocity/fancam style edit with beat sync' }, beatMontage: { path: '/api/ai/beat-montage', method: 'POST', description: 'Create beat-synced montage with perfect timing' }, aesthetic: { path: '/api/ai/aesthetic-edit', method: 'POST', description: 'Apply aesthetic filters and color grading' }, smooth: { path: '/api/ai/smooth-transitions', method: 'POST', description: 'Create smooth transitions between clips' }, vlog: { path: '/api/ai/vlog-edit', method: 'POST', description: 'Create vlog-style edit with text overlays' }, imitate: { path: '/api/ai/imitate-style', method: 'POST', description: 'Imitate editing style from inspiration video' } }, analytics: { trends: '/api/trends', predict: '/api/ai/predict-trends' }, auth: { login: '/api/auth/login', register: '/api/auth/register' } } }); }); // API Documentation this.app.get('/api/docs', (req, res) => { res.send(` TikVibe API Documentation

๐ŸŽฅ TikVibe Creator Pro API

Base URL: https://andevs-realeditz.hf.space

AI Editing Endpoints

POST /api/ai/velocity-edit

Create velocity/fancam style edit with beat sync

FormData: {
  clips: File[] (video files),
  audio: File (music file),
  intensity: "low" | "medium" | "high"
}
POST /api/ai/beat-montage

Create beat-synced montage

FormData: {
  clips: File[] (video files),
  audio: File (music file),
  montageType: "fancam" | "cinematic"
}
POST /api/ai/aesthetic-edit

Apply aesthetic filters

FormData: {
  clips: File[] (video files),
  audio: File (optional),
  aesthetic: "vintage" | "cinematic" | "vibrant" | "moody" | "dreamy"
}
POST /api/ai/smooth-transitions

Create smooth transitions

FormData: {
  clips: File[] (video files),
  audio: File (optional)
}
POST /api/ai/vlog-edit

Create vlog-style edit

FormData: {
  clips: File[] (video files),
  audio: File (optional)
}
POST /api/ai/imitate-style

Imitate style from inspiration video

FormData: {
  inspoVideo: File (inspiration video),
  clips: File[] (your clips),
  audio: File (optional)
}
`); }); // Test endpoint this.app.get('/api/test', (req, res) => { res.json({ success: true, message: 'โœ… TikVibe API is working correctly!', timestamp: new Date().toISOString(), features: [ 'velocity-edit', 'beat-montage', 'aesthetic-edit', 'smooth-transitions', 'vlog-edit', 'imitate-style' ] }); }); // Auth endpoints (simplified) this.app.post('/api/auth/login', (req, res) => { const { email, password } = req.body; if (!email || !password) { return res.status(400).json({ success: false, message: 'Email and password required' }); } res.json({ success: true, message: 'Login successful', token: 'mock-jwt-token-' + Date.now(), user: { id: 1, email: email, name: email.split('@')[0] } }); }); this.app.post('/api/auth/register', (req, res) => { const { email, password, name } = req.body; if (!email || !password) { return res.status(400).json({ success: false, message: 'Email and password required' }); } res.json({ success: true, message: 'Registration successful', user: { id: Date.now(), email: email, name: name || email.split('@')[0] } }); }); // Trends endpoint with mock data this.app.get('/api/trends', (req, res) => { const { category = 'all', timeframe = '24h' } = req.query; const trends = { all: [ { id: 1, name: '#dancechallenge', category: 'hashtag', growth: 25, views: 1000000 }, { id: 2, name: 'Summer Vibes', category: 'sound', growth: 15, views: 500000 }, { id: 3, name: 'Glitch Effect', category: 'effect', growth: -5, views: 250000 } ] }; res.json({ success: true, timeframe, category, trends: trends[category] || trends.all }); }); // AI Routes const aiRouter = require('./routes/ai'); this.app.use('/api/ai', aiRouter); // ============= VIDEO DOWNLOAD ENDPOINT ============= this.app.get('/api/video/download/:filename', async (req, res) => { try { const filename = req.params.filename; // Sanitize filename to prevent path traversal const safeFilename = path.basename(filename); const filepath = path.join(__dirname, '../renders', safeFilename); console.log('๐Ÿ“ฅ Download requested:', safeFilename); console.log('Full path:', filepath); // Check if file exists try { await fs.access(filepath); } catch (error) { console.error('File not found:', filepath); return res.status(404).json({ error: 'File not found', message: 'The video file may have been deleted or expired' }); } // Get file stats const stats = await fs.stat(filepath); console.log(`File size: ${stats.size} bytes`); // Set headers res.setHeader('Content-Disposition', `attachment; filename="${safeFilename}"`); res.setHeader('Content-Type', 'video/mp4'); res.setHeader('Content-Length', stats.size); // Send file res.sendFile(filepath, (err) => { if (err) { console.error('Error sending file:', err); if (!res.headersSent) { res.status(500).json({ error: 'Failed to send file' }); } } else { console.log('โœ… File sent successfully:', safeFilename); } }); } catch (error) { console.error('Download error:', error); res.status(500).json({ error: 'Download failed', message: error.message }); } }); // Progress tracking for renders this.app.get('/api/render/progress/:renderId', (req, res) => { const { renderId } = req.params; const progress = this.activeRenders.get(renderId) || 0; res.json({ renderId, progress }); }); // Catch-all for undefined routes this.app.use('*', (req, res) => { res.status(404).json({ success: false, message: 'Endpoint not found', path: req.originalUrl, availableEndpoints: '/api/docs' }); }); } setupWebSocket() { this.io.on('connection', (socket) => { console.log('๐Ÿ”Œ Client connected:', socket.id); socket.emit('connected', { id: socket.id, message: 'Connected to TikVibe server', timestamp: new Date().toISOString() }); this.connectedUsers.set(socket.id, { id: socket.id, joinedAt: new Date().toISOString(), lastActivity: new Date().toISOString() }); socket.on('project:join', (data) => { const { projectId, userId, userName } = data; socket.join(`project:${projectId}`); if (!this.projectRooms.has(projectId)) { this.projectRooms.set(projectId, new Set()); } this.projectRooms.get(projectId).add(socket.id); socket.emit('project:joined', { projectId, users: Array.from(this.projectRooms.get(projectId)).length }); socket.to(`project:${projectId}`).emit('user:joined', { userId: userId || socket.id, userName: userName || 'Anonymous', timestamp: new Date().toISOString() }); console.log(`๐Ÿ‘ค User ${userName || socket.id} joined project ${projectId}`); }); socket.on('project:message', (data) => { const { projectId, message, userId, userName } = data; const messageData = { id: Date.now(), userId: userId || socket.id, userName: userName || 'Anonymous', message: message, timestamp: new Date().toISOString() }; socket.emit('message:sent', messageData); socket.to(`project:${projectId}`).emit('project:message', messageData); }); socket.on('disconnect', () => { console.log('๐Ÿ”Œ Client disconnected:', socket.id); this.connectedUsers.delete(socket.id); this.projectRooms.forEach((users, projectId) => { if (users.has(socket.id)) { users.delete(socket.id); socket.to(`project:${projectId}`).emit('user:left', { userId: socket.id, timestamp: new Date().toISOString() }); if (users.size === 0) { this.projectRooms.delete(projectId); } } }); }); }); } setupErrorHandling() { this.app.use((err, req, res, next) => { console.error('โŒ Global error:', err); const status = err.status || 500; const message = err.message || 'Internal server error'; console.error({ timestamp: new Date().toISOString(), path: req.path, method: req.method, status, message, stack: err.stack }); res.status(status).json({ success: false, error: message, code: err.code, timestamp: new Date().toISOString() }); }); } setupCleanupJobs() { // Clean up old files every hour setInterval(async () => { try { const now = Date.now(); const dirs = ['uploads', 'renders', 'temp']; for (const dir of dirs) { const dirPath = path.join(__dirname, '..', dir); const files = await fs.readdir(dirPath); for (const file of files) { const filePath = path.join(dirPath, file); const stats = await fs.stat(filePath); // Delete files older than 1 hour (uploads/temp) or 24 hours (renders) const maxAge = dir === 'renders' ? 86400000 : 3600000; // 24 hours or 1 hour if (now - stats.mtimeMs > maxAge) { await fs.unlink(filePath); console.log(`๐Ÿงน Cleaned up old file: ${dir}/${file}`); } } } } catch (error) { console.error('Cleanup error:', error); } }, 3600000); // Every hour } async start() { await this.initialize(); this.httpServer.listen(this.PORT, '0.0.0.0', () => { console.log(` โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•— โ•‘ โ•‘ โ•‘ ๐ŸŽฅ TikVibe Creator Pro Backend โ•‘ โ•‘ ================================================== โ•‘ โ•‘ โ•‘ โ•‘ ๐Ÿš€ Server: https://andevs-realeditz.hf.space โ•‘ โ•‘ ๐Ÿ”Œ WebSocket: wss://andevs-realeditz.hf.space โ•‘ โ•‘ ๐ŸŒ Environment: ${process.env.NODE_ENV || 'production'} โ•‘ โ•‘ ๐Ÿ“Š Status: ONLINE โ•‘ โ•‘ โ•‘ โ•‘ ๐Ÿ“ก AI Endpoints: โ•‘ โ•‘ โ€ข โšก /api/ai/velocity-edit โ•‘ โ•‘ โ€ข ๐ŸŽต /api/ai/beat-montage โ•‘ โ•‘ โ€ข ๐ŸŽจ /api/ai/aesthetic-edit โ•‘ โ•‘ โ€ข ๐Ÿ”„ /api/ai/smooth-transitions โ•‘ โ•‘ โ€ข ๐ŸŽฅ /api/ai/vlog-edit โ•‘ โ•‘ โ€ข ๐ŸŽญ /api/ai/imitate-style โ•‘ โ•‘ โ•‘ โ•‘ ๐Ÿ“ฅ Download: /api/video/download/:filename โ•‘ โ•‘ ๐Ÿ“š Documentation: /api/docs โ•‘ โ•‘ ๐Ÿ’š Health Check: /health โ•‘ โ•‘ โ•‘ โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• `); }); } async shutdown() { console.log('\n๐Ÿ›‘ Shutting down gracefully...'); this.io.emit('server:shutdown', { message: 'Server is shutting down', timestamp: new Date().toISOString() }); this.io.close(() => { console.log('โœ… Socket.IO closed'); }); this.httpServer.close(() => { console.log('โœ… HTTP server closed'); process.exit(0); }); setTimeout(() => { console.error('โŒ Force shutdown after timeout'); process.exit(1); }, 10000); } } const server = new TikVibeServer(); process.on('SIGTERM', () => server.shutdown()); process.on('SIGINT', () => server.shutdown()); process.on('uncaughtException', (err) => { console.error('โŒ Uncaught Exception:', err); server.shutdown(); }); process.on('unhandledRejection', (err) => { console.error('โŒ Unhandled Rejection:', err); server.shutdown(); }); server.start().catch(err => { console.error('โŒ Failed to start server:', err); process.exit(1); });