Restored to 'e1314807be287b395cffa7ada2c736a4a7831e11'
Replit-Restored-To: e1314807be
This commit is contained in:
parent
d71f6d7f28
commit
72295f7fe8
@ -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) {
|
||||
|
||||
@ -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) => (
|
||||
<div
|
||||
key={video.id}
|
||||
className="flex-shrink-0 w-[calc(100vw-1.5rem)] sm:w-[280px] relative hover:z-50"
|
||||
className="flex-shrink-0 w-[calc(100vw-2.5rem)] sm:w-[330px] relative hover:z-50"
|
||||
style={{
|
||||
scrollSnapAlign: window.innerWidth < 768 ? 'start' : 'none'
|
||||
}}
|
||||
|
||||
169
server/routes.ts
169
server/routes.ts
@ -30,7 +30,7 @@ export async function findVideoByAnyId(id: string) {
|
||||
|
||||
// If it's an 8-character short ID, find by short ID
|
||||
if (id.length === 8) {
|
||||
const allVideos = await storage.getVideos(1000, 0);
|
||||
const allVideos = await storage.getVideos(200, 0);
|
||||
return allVideos.find(v => 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<Server> {
|
||||
});
|
||||
}
|
||||
|
||||
// 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<Server> {
|
||||
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<Server> {
|
||||
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" });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -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');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user