diff --git a/client/src/components/adsense-ad.tsx b/client/src/components/adsense-ad.tsx index ca47b32..961ba7e 100644 --- a/client/src/components/adsense-ad.tsx +++ b/client/src/components/adsense-ad.tsx @@ -30,9 +30,7 @@ export default function AdSenseAd({ }, []); const adStyle: React.CSSProperties = { - display: 'block', - minWidth: '300px', - minHeight: '250px' + display: 'block' }; if (adFormat !== 'auto' && width && height) { diff --git a/client/src/components/netflix-grid.tsx b/client/src/components/netflix-grid.tsx index b9b0ac0..1295e59 100644 --- a/client/src/components/netflix-grid.tsx +++ b/client/src/components/netflix-grid.tsx @@ -230,7 +230,7 @@ function CategoryRow({ category, onVideoClick, hideScrollButtons = false }: Cate // Calculate card width with gap for precise scrolling const containerWidth = scrollRef.current.clientWidth; const isMobile = window.innerWidth < 768; - const cardWidth = isMobile ? containerWidth - 24 : 280; // 24px for mobile margins + const cardWidth = isMobile ? containerWidth - 24 : 330; // 24px for mobile margins (3rem - 1.5rem each side) const gap = 12; // 3 * 0.25rem = 12px gap const scrollAmount = cardWidth + gap; @@ -424,7 +424,7 @@ function CategoryRow({ category, onVideoClick, hideScrollButtons = false }: Cate {category.videos.map((video, index) => (
v.id.replace(/-/g, '').substring(0, 8) === id); } @@ -48,63 +48,24 @@ declare module "express-session" { } } -// Ensure upload directory exists -const uploadDir = './uploads/videos'; -if (!fs.existsSync(uploadDir)) { - fs.mkdirSync(uploadDir, { recursive: true }); - console.log('📁 Created upload directory:', uploadDir); -} - -// Configure multer for video uploads with better error handling +// Configure multer for video uploads const upload = multer({ storage: multer.diskStorage({ - destination: (req, file, cb) => { - try { - // Ensure directory exists - if (!fs.existsSync('./uploads/videos')) { - fs.mkdirSync('./uploads/videos', { recursive: true }); - } - cb(null, './uploads/videos'); - } catch (error) { - console.error('❌ Error creating upload directory:', error); - cb(error as Error, './uploads/videos'); - } - }, + destination: './uploads/videos', filename: (req, file, cb) => { - try { - const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); - const filename = file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname); - cb(null, filename); - } catch (error) { - console.error('❌ Error generating filename:', error); - cb(error as Error, file.originalname); - } + const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); + cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname)); } }), limits: { fileSize: 500 * 1024 * 1024, // 500MB max file size - fieldSize: 1024 * 1024, // 1MB field size - fields: 10, - files: 1 }, fileFilter: (req, file, cb) => { - try { - // Allow video files only with strict validation - const allowedMimes = [ - 'video/mp4', 'video/mpeg', 'video/quicktime', 'video/x-msvideo', - 'video/webm', 'video/ogg', 'video/3gpp', 'video/x-flv' - ]; - - if (file.mimetype.startsWith('video/') && allowedMimes.includes(file.mimetype)) { - cb(null, true); - } else { - const error = new Error(`Unsupported video format: ${file.mimetype}. Only video files are allowed.`); - console.error('❌ File filter error:', error.message); - cb(error); - } - } catch (error) { - console.error('❌ Error in file filter:', error); - cb(error as Error); + // Allow video files only + if (file.mimetype.startsWith('video/')) { + cb(null, true); + } else { + cb(new Error('Only video files are allowed')); } } }); @@ -540,104 +501,47 @@ export async function registerRoutes(app: Express): Promise { }); } - // Validate file size (max 500MB) - const parsedFileSize = parseInt(fileSize); - if (isNaN(parsedFileSize) || parsedFileSize <= 0 || parsedFileSize > 500 * 1024 * 1024) { - return res.status(400).json({ - message: "Invalid file size. Maximum file size is 500MB" - }); - } - - // Validate MIME type - const allowedMimes = [ - 'video/mp4', 'video/mpeg', 'video/quicktime', 'video/x-msvideo', - 'video/webm', 'video/ogg', 'video/3gpp', 'video/x-flv' - ]; - - if (!mimeType.startsWith('video/') || !allowedMimes.includes(mimeType)) { - return res.status(400).json({ - message: `Unsupported video format: ${mimeType}. Only video files are allowed.` - }); - } - const uploadData = { userId: req.session.userId!, originalFileName, - fileSize: parsedFileSize, + fileSize: parseInt(fileSize), mimeType, uploadStatus: "uploading" as const, uploadProgress: 0 }; const upload = await storage.createVideoUpload(uploadData); - console.log(`📤 Upload initialized: ${upload.id} for file: ${originalFileName}`); res.status(201).json(upload); } catch (error) { - console.error('❌ Failed to initialize upload:', error); - res.status(500).json({ - message: error instanceof Error ? error.message : "Failed to initialize upload" - }); + res.status(500).json({ message: "Failed to initialize upload" }); } }); app.post("/api/uploads/:id/video", authenticate, upload.single('video'), async (req, res) => { - let uploadId: string | null = null; try { - uploadId = req.params.id; + const uploadId = req.params.id; const file = req.file; - console.log(`📤 Processing video upload for ID: ${uploadId}`); - if (!file) { - console.error('❌ No video file provided in request'); return res.status(400).json({ message: "No video file provided" }); } - // Validate upload ID - if (!uploadId || uploadId.trim() === '') { - console.error('❌ Invalid upload ID provided'); - return res.status(400).json({ message: "Invalid upload ID" }); - } - - // Check if upload record exists - const existingUpload = await storage.getVideoUpload(uploadId); - if (!existingUpload) { - console.error(`❌ Upload record not found for ID: ${uploadId}`); - return res.status(404).json({ message: "Upload record not found" }); - } - - console.log(`📁 File uploaded: ${file.originalname}, size: ${file.size} bytes`); - // Update upload with file information await storage.updateVideoUpload(uploadId, { uploadStatus: "processing", uploadProgress: 1.0 }); - // Parse tags safely - let tags: string[] = []; - if (req.body.tags) { - try { - tags = JSON.parse(req.body.tags); - if (!Array.isArray(tags)) { - tags = []; - } - } catch (parseError) { - console.warn('⚠️ Failed to parse tags, using empty array:', parseError); - tags = []; - } - } - - // Create video record with safe data validation + // Create video record const videoData = { - title: req.body.title?.trim() || path.parse(file.originalname).name, - description: req.body.description?.trim() || "", - thumbnailUrl: req.body.thumbnailUrl?.trim() || "https://via.placeholder.com/800x450", + title: req.body.title || path.parse(file.originalname).name, + description: req.body.description || "", + thumbnailUrl: req.body.thumbnailUrl || "https://via.placeholder.com/800x450", videoUrl: `/uploads/videos/${file.filename}`, - duration: Math.max(0, parseInt(req.body.duration) || 0), + duration: parseInt(req.body.duration) || 0, views: 0, - category: req.body.category?.trim() || "", - tags: tags, + category: req.body.category || "", + tags: req.body.tags ? JSON.parse(req.body.tags) : [], isPublic: req.body.isPublic !== "false", uploadStatus: "completed", originalFileName: file.originalname, @@ -645,7 +549,6 @@ export async function registerRoutes(app: Express): Promise { format: path.extname(file.originalname).slice(1) }; - console.log(`🎬 Creating video record: ${videoData.title}`); const video = await storage.createVideo(videoData); // Link video to upload @@ -654,33 +557,19 @@ export async function registerRoutes(app: Express): Promise { uploadStatus: "completed" }); - console.log(`✅ Video upload completed successfully: ${video.id}`); - res.json({ - video, - upload: { id: uploadId, status: "completed" }, - message: "Video uploaded successfully" - }); + res.json({ video, upload: { id: uploadId, status: "completed" } }); } catch (error) { - console.error("❌ Upload error:", error); + console.error("Upload error:", error); - // Update upload status to failed if we have an uploadId - if (uploadId) { - try { - await storage.updateVideoUpload(uploadId, { - uploadStatus: "failed", - errorMessage: error instanceof Error ? error.message : "Upload failed" - }); - } catch (updateError) { - console.error("❌ Failed to update upload status:", updateError); - } + // Update upload status to failed + if (req.params.id) { + await storage.updateVideoUpload(req.params.id, { + uploadStatus: "failed", + errorMessage: error instanceof Error ? error.message : "Upload failed" + }); } - // Return appropriate error response - const errorMessage = error instanceof Error ? error.message : "Failed to upload video"; - res.status(500).json({ - message: errorMessage, - uploadId: uploadId - }); + res.status(500).json({ message: "Failed to upload video" }); } }); diff --git a/server/videoSync.ts b/server/videoSync.ts index dfc9ec4..806e1c1 100644 --- a/server/videoSync.ts +++ b/server/videoSync.ts @@ -166,21 +166,15 @@ class VideoSyncService { try { await this.syncVideos(); - // Add timeout protection for database sync with longer timeout + // Add timeout protection for database sync const syncTimeout = new Promise((_, reject) => - setTimeout(() => reject(new Error('Database sync timeout after 60 seconds')), 60000) + setTimeout(() => reject(new Error('Database sync timeout after 30 seconds')), 30000) ); - try { - await Promise.race([ - this.syncVideosToDatabase(), - syncTimeout - ]); - } catch (error) { - console.error('❌ Database sync failed with timeout:', error); - console.log('⚠️ Continuing with cached data only - videos will still be available'); - // Don't throw the error, just log it and continue - } + await Promise.race([ + this.syncVideosToDatabase(), + syncTimeout + ]); this.startPeriodicSync(); console.log('✅ Video sync service initialized successfully');