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);
});