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