new ui?!
This commit is contained in:
@@ -10,6 +10,7 @@ ffmpeg.setFfprobePath('/usr/bin/ffprobe');
|
||||
import { Database } from './database';
|
||||
import { SoundCloudService } from './soundcloud';
|
||||
import { QuixoticBot } from './bot';
|
||||
import { logger } from './logger';
|
||||
|
||||
const app = express();
|
||||
const port = process.env.PORT || 3000;
|
||||
@@ -70,7 +71,10 @@ if (!fs.existsSync(downloadsDir)) {
|
||||
|
||||
// Routes
|
||||
app.get('/', (req: Request, res: Response) => {
|
||||
const indexPath = path.join(__dirname, '../public/index.html');
|
||||
// Use minified HTML in production
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
const htmlFile = isProduction ? 'index.min.html' : 'index.html';
|
||||
const indexPath = path.join(__dirname, '../public', htmlFile);
|
||||
|
||||
// Set cache headers for HTML (short cache)
|
||||
res.set({
|
||||
@@ -97,7 +101,7 @@ app.post('/api/search', async (req: Request, res: Response) => {
|
||||
await db.addSearchHistory(user.id, query);
|
||||
}
|
||||
} catch (dbError) {
|
||||
console.error('Database error:', dbError);
|
||||
logger.error('Database error:', dbError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +109,7 @@ app.post('/api/search', async (req: Request, res: Response) => {
|
||||
res.json({ videos });
|
||||
|
||||
} catch (error) {
|
||||
console.error('Search error:', error);
|
||||
logger.error('Search error:', error);
|
||||
res.status(500).json({ error: 'Failed to search videos' });
|
||||
}
|
||||
});
|
||||
@@ -114,7 +118,7 @@ app.post('/api/search', async (req: Request, res: Response) => {
|
||||
app.post('/api/convert', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { videoId, title, userId, url }: { videoId?: string; title?: string; userId?: string; url?: string } = req.body;
|
||||
console.log('Convert request received:', { videoId, title, userId });
|
||||
logger.info(`Convert request received: ${title} (ID: ${videoId})`);
|
||||
|
||||
if (!videoId) {
|
||||
return res.status(400).json({ error: 'Video ID is required' });
|
||||
@@ -127,18 +131,18 @@ app.post('/api/convert', async (req: Request, res: Response) => {
|
||||
|
||||
// Check if file already exists
|
||||
if (fs.existsSync(outputPath)) {
|
||||
console.log('File already exists, serving cached version');
|
||||
logger.info('File already exists, serving cached version');
|
||||
const audioUrl = `${req.protocol}://${req.get('host')}/downloads/${filename}`;
|
||||
return res.json({ audioUrl, title });
|
||||
}
|
||||
|
||||
console.log(`Starting MP3 conversion for: ${title}`);
|
||||
logger.info(`Starting MP3 conversion: ${title}`);
|
||||
|
||||
try {
|
||||
// Get audio stream from YouTube
|
||||
console.log(`Attempting to get audio stream for: ${videoId}`);
|
||||
logger.debug(`Attempting to get audio stream for: ${videoId}`);
|
||||
const audioStream = await soundcloud.getAudioStream(videoId, url);
|
||||
console.log('Audio stream obtained, starting FFmpeg conversion...');
|
||||
logger.info('Audio stream obtained, starting FFmpeg conversion...');
|
||||
|
||||
// Download to temporary file first, then convert
|
||||
const tempInputPath = path.join(downloadsDir, `temp_${videoId}.tmp`);
|
||||
@@ -152,19 +156,19 @@ app.post('/api/convert', async (req: Request, res: Response) => {
|
||||
writeStream.on('error', reject);
|
||||
});
|
||||
|
||||
console.log('Temporary file saved, starting FFmpeg conversion...');
|
||||
logger.info('Temporary file saved, starting FFmpeg conversion...');
|
||||
|
||||
// Debug: check temp file
|
||||
const stats = fs.statSync(tempInputPath);
|
||||
console.log(`Temp file size: ${stats.size} bytes`);
|
||||
logger.debug(`Temp file size: ${stats.size} bytes`);
|
||||
|
||||
// Test ffmpeg with simple command first
|
||||
try {
|
||||
const { execSync } = require('child_process');
|
||||
execSync(`ffmpeg -i "${tempInputPath}" -t 1 -f null -`, { encoding: 'utf8', stdio: 'pipe' });
|
||||
console.log('FFmpeg file test passed');
|
||||
logger.debug('FFmpeg file test passed');
|
||||
} catch (e: any) {
|
||||
console.error('FFmpeg file test failed:', e.stderr || e.message);
|
||||
logger.error('FFmpeg file test failed:', e.stderr || e.message);
|
||||
}
|
||||
|
||||
// Convert temporary file to MP3 using ffmpeg
|
||||
@@ -177,23 +181,23 @@ app.post('/api/convert', async (req: Request, res: Response) => {
|
||||
.format('mp3')
|
||||
.output(outputPath)
|
||||
.on('start', (command: string) => {
|
||||
console.log('FFmpeg started:', command);
|
||||
logger.ffmpeg('Started', command);
|
||||
})
|
||||
.on('progress', (progress: any) => {
|
||||
if (progress.percent) {
|
||||
console.log(`Conversion progress: ${Math.round(progress.percent)}%`);
|
||||
logger.ffmpeg('Progress', `${Math.round(progress.percent)}%`);
|
||||
}
|
||||
})
|
||||
.on('end', () => {
|
||||
console.log('MP3 conversion completed successfully');
|
||||
logger.success('MP3 conversion completed successfully');
|
||||
// Clean up temporary file
|
||||
fs.unlink(tempInputPath, (err) => {
|
||||
if (err) console.error('Failed to delete temp file:', err);
|
||||
if (err) logger.error('Failed to delete temp file:', err);
|
||||
});
|
||||
resolve();
|
||||
})
|
||||
.on('error', (err: Error) => {
|
||||
console.error('FFmpeg error:', err.message);
|
||||
logger.error('FFmpeg error:', err.message);
|
||||
// Clean up temporary file on error
|
||||
fs.unlink(tempInputPath, () => {});
|
||||
reject(err);
|
||||
@@ -210,18 +214,18 @@ app.post('/api/convert', async (req: Request, res: Response) => {
|
||||
await db.addDownload(user.id, videoId, title || '', outputPath);
|
||||
}
|
||||
} catch (dbError) {
|
||||
console.error('Database error:', dbError);
|
||||
logger.error('Database error:', dbError);
|
||||
}
|
||||
}
|
||||
|
||||
const audioUrl = `${req.protocol}://${req.get('host')}/downloads/${filename}`;
|
||||
console.log('Conversion successful, file available at:', audioUrl);
|
||||
logger.success(`Conversion successful: ${audioUrl}`);
|
||||
res.json({ audioUrl, title });
|
||||
|
||||
} catch (conversionError: any) {
|
||||
console.error('Conversion failed for video:', videoId);
|
||||
console.error('Error details:', conversionError.message);
|
||||
console.error('Full error:', conversionError);
|
||||
logger.error(`Conversion failed for video: ${videoId}`);
|
||||
logger.error('Error details:', conversionError.message);
|
||||
logger.error('Full error:', conversionError);
|
||||
|
||||
// Return error - no fallbacks for Telegram bot
|
||||
return res.status(503).json({
|
||||
@@ -232,18 +236,18 @@ app.post('/api/convert', async (req: Request, res: Response) => {
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Server error:', error);
|
||||
logger.error('Server error:', error);
|
||||
res.status(500).json({ error: 'Failed to process request' });
|
||||
}
|
||||
});
|
||||
|
||||
// Direct Telegram API for sending audio
|
||||
app.post('/api/telegram-send', async (req: Request, res: Response) => {
|
||||
console.log('🚀 Telegram send request received');
|
||||
logger.telegram('Send request received');
|
||||
|
||||
try {
|
||||
const { userId, audioUrl, title, performer, thumbnail }: { userId?: string; audioUrl?: string; title?: string; performer?: string; thumbnail?: string } = req.body;
|
||||
console.log(`📤 Sending to user ${userId}: ${title}`);
|
||||
logger.telegram('Sending to user', `${userId}: ${title}`);
|
||||
|
||||
if (!userId || !audioUrl || !title) {
|
||||
return res.status(400).json({ error: 'Missing required fields' });
|
||||
@@ -251,18 +255,18 @@ app.post('/api/telegram-send', async (req: Request, res: Response) => {
|
||||
|
||||
const botInstance = (global as any).quixoticBot;
|
||||
if (!botInstance) {
|
||||
console.log('❌ Bot not available');
|
||||
logger.error('Bot not available');
|
||||
return res.status(500).json({ error: 'Bot not available' });
|
||||
}
|
||||
|
||||
const chatId = parseInt(userId);
|
||||
await botInstance.sendAudioFile(chatId, audioUrl, title, performer, thumbnail);
|
||||
console.log('✅ Audio sent successfully');
|
||||
logger.success('Audio sent successfully');
|
||||
|
||||
res.json({ success: true, message: 'Audio sent successfully' });
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('❌ Send failed:', error.message);
|
||||
logger.error('Send failed:', error.message);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
@@ -277,7 +281,7 @@ app.get('/health', (req: Request, res: Response) => {
|
||||
|
||||
// Error handler
|
||||
app.use((err: Error, _req: Request, res: Response, _next: any) => {
|
||||
console.error(err.stack);
|
||||
logger.error(err.stack || err.message);
|
||||
res.status(500).json({ error: 'Something went wrong!' });
|
||||
});
|
||||
|
||||
@@ -297,7 +301,7 @@ setInterval(() => {
|
||||
if (now - stats.mtime.getTime() > maxAge) {
|
||||
fs.unlink(filePath, (err) => {
|
||||
if (!err) {
|
||||
console.log('Deleted old file:', file);
|
||||
logger.info('Deleted old file:', file);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -307,9 +311,9 @@ setInterval(() => {
|
||||
}, 60 * 60 * 1000); // Run every hour
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Quixotic server running on port ${port}`);
|
||||
console.log(`Downloads directory: ${downloadsDir}`);
|
||||
console.log(`Open in browser: http://localhost:${port}`);
|
||||
logger.success(`Quixotic server running on port ${port}`);
|
||||
logger.info(`Downloads directory: ${downloadsDir}`);
|
||||
logger.info(`Open in browser: http://localhost:${port}`);
|
||||
});
|
||||
|
||||
// Initialize Telegram bot
|
||||
@@ -321,33 +325,33 @@ if (botToken && botToken.length > 10 && botToken !== 'your_telegram_bot_token_he
|
||||
const botInstance = new QuixoticBot(botToken, webAppUrl);
|
||||
// Store bot instance globally for API access
|
||||
(global as any).quixoticBot = botInstance;
|
||||
console.log('🤖 Telegram bot started and stored globally');
|
||||
logger.telegram('Bot started and stored globally');
|
||||
} catch (error: any) {
|
||||
console.error('❌ Bot initialization failed:', error.message);
|
||||
console.warn('⚠️ Bot disabled due to error');
|
||||
console.warn('⚠️ Telegram integration will not be available');
|
||||
logger.error('Bot initialization failed:', error.message);
|
||||
logger.warn('Bot disabled due to error');
|
||||
logger.warn('Telegram integration will not be available');
|
||||
// Don't crash the server, continue without bot
|
||||
}
|
||||
} else {
|
||||
console.warn('⚠️ TELEGRAM_BOT_TOKEN not configured properly');
|
||||
console.warn('⚠️ Bot will not start - only web interface will be available');
|
||||
console.warn('ℹ️ To enable Telegram bot, set a valid TELEGRAM_BOT_TOKEN');
|
||||
logger.warn('TELEGRAM_BOT_TOKEN not configured properly');
|
||||
logger.warn('Bot will not start - only web interface will be available');
|
||||
logger.info('To enable Telegram bot, set a valid TELEGRAM_BOT_TOKEN');
|
||||
}
|
||||
|
||||
// Handle unhandled promise rejections
|
||||
process.on('unhandledRejection', (reason: any, promise: Promise<any>) => {
|
||||
console.error('🚨 Unhandled Rejection at:', promise);
|
||||
console.error('Reason:', reason);
|
||||
logger.error('Unhandled Rejection at:', promise);
|
||||
logger.error('Reason:', reason);
|
||||
|
||||
// Log but don't crash the server
|
||||
if (reason?.code === 'ETELEGRAM') {
|
||||
console.warn('⚠️ Telegram API error - continuing operation');
|
||||
logger.warn('Telegram API error - continuing operation');
|
||||
}
|
||||
});
|
||||
|
||||
// Handle uncaught exceptions
|
||||
process.on('uncaughtException', (error: Error) => {
|
||||
console.error('🚨 Uncaught Exception:', error);
|
||||
logger.error('Uncaught Exception:', error);
|
||||
// Log but try to continue
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user