Files
KiraTV-APP-NodeCast-Modific…/server/index.js
T

115 lines
3.8 KiB
JavaScript

const express = require('express');
const path = require('path');
const passport = require('passport');
const syncService = require('./services/syncService');
// Initialize database
require('./db');
const app = express();
const PORT = process.env.PORT || 3000;
// Trust proxy headers (X-Forwarded-Proto, X-Forwarded-For, etc.)
// Required for correct protocol detection behind reverse proxies (nginx, Caddy, etc.)
app.set('trust proxy', true);
// Middleware
app.use(express.json({ limit: '50mb' }));
// Initialize Passport
app.use(passport.initialize());
app.use(express.static(path.join(__dirname, '..', 'public')));
// FFMPEG Configuration (optional - for transcoding support)
// Priority: 1. System FFmpeg (better Docker DNS support), 2. ffmpeg-static npm package
const { execSync } = require('child_process');
function findFFmpeg() {
// Try system FFmpeg first (better Docker compatibility)
try {
execSync('ffmpeg -version', { stdio: 'ignore' });
console.log('FFmpeg binary configured at: ffmpeg (system)');
return 'ffmpeg';
} catch (e) {
// System FFmpeg not found, try ffmpeg-static
}
// Try ffmpeg-static npm package
try {
let ffmpegPath = require('ffmpeg-static');
// In packaged Electron apps, ffmpeg-static returns path inside .asar archive
// but the binary is actually unpacked to app.asar.unpacked
if (ffmpegPath && ffmpegPath.includes('app.asar')) {
ffmpegPath = ffmpegPath.replace('app.asar', 'app.asar.unpacked');
}
console.log('FFmpeg binary configured at:', ffmpegPath);
return ffmpegPath;
} catch (err) {
console.warn('FFmpeg not available - transcoding/remuxing will be disabled.');
console.warn('Install FFmpeg via your package manager or npm install ffmpeg-static');
return null;
}
}
function findFFprobe() {
// Try system ffprobe first
try {
execSync('ffprobe -version', { stdio: 'ignore' });
console.log('FFprobe binary configured at: ffprobe (system)');
return 'ffprobe';
} catch (e) {
// Not found in system
}
// Try @ffprobe-installer/ffprobe package
try {
const ffprobePath = require('@ffprobe-installer/ffprobe').path;
if (ffprobePath) {
console.log('FFprobe binary configured at:', ffprobePath);
return ffprobePath;
}
} catch (err) {
// Package not available
}
console.warn('FFprobe not available - auto transcode will fallback to always transcode');
return null;
}
app.locals.ffmpegPath = findFFmpeg();
app.locals.ffprobePath = findFFprobe();
// API Routes
app.use('/api/auth', require('./routes/auth'));
app.use('/api/sources', require('./routes/sources'));
app.use('/api/proxy', require('./routes/proxy'));
app.use('/api/channels', require('./routes/channels'));
app.use('/api/favorites', require('./routes/favorites'));
app.use('/api/transcode', require('./routes/transcode'));
app.use('/api/remux', require('./routes/remux'));
app.use('/api/probe', require('./routes/probe'));
app.use('/api/settings', require('./routes/settings'));
// SPA fallback - serve index.html for all non-API routes
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '..', 'public', 'index.html'));
});
// Error handling
app.use((err, req, res, next) => {
console.error('Server error:', err);
res.status(500).json({ error: 'Internal server error' });
});
app.listen(PORT, async () => {
console.log(`NodeCast TV server running on http://localhost:${PORT}`);
// Trigger background sync with delay to allow server to settle
setTimeout(async () => {
await syncService.syncAll().catch(console.error);
// Start the server-side sync timer after initial sync
await syncService.startSyncTimer().catch(console.error);
}, 5000);
});