Add face detection and thumbnail centering for videos

This commit introduces face detection capabilities to the video platform, enabling automatic identification of faces in video thumbnails. It integrates face-api.js and sharp for image analysis, allowing for face-centered thumbnail crops and dynamic object-positioning. New API endpoints are added to process thumbnails individually and in batches. The database schema is updated to store face detection data, and the storage layer is modified to support these updates and cache face data. The project's dependencies are also updated to include necessary libraries for these new features.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 2eb1084e-b728-4449-9231-f1665924c8d5
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8cc42625-c1f5-4e43-99bd-77f2c4dedee2/2eb1084e-b728-4449-9231-f1665924c8d5/xF0EUqR
This commit is contained in:
sebastjanartic
2025-08-29 07:34:08 +00:00
parent 8a4ea10fc4
commit d321b4f384
14 changed files with 3319 additions and 9 deletions
+101
View File
@@ -222,6 +222,107 @@ export async function registerRoutes(app: Express): Promise<Server> {
}
});
// Face detection endpoint for thumbnails
app.post("/api/videos/:id/analyze-face", async (req, res) => {
try {
const { id } = req.params;
const video = await storage.getVideo(id);
if (!video) {
return res.status(404).json({ message: "Video not found" });
}
if (!video.thumbnailUrl) {
return res.status(400).json({ message: "Video has no thumbnail" });
}
// Import smart thumbnail service (no external dependencies)
const { smartThumbnailService } = await import("./smart-thumbnail-service");
const result = await smartThumbnailService.getOptimizedThumbnailInfo(video.thumbnailUrl);
// Update video with face position data
await storage.updateVideo(id, {
faceCenterPosition: result.faceCenterPosition,
facesDetected: result.facesDetected,
faceConfidence: result.confidence
});
res.json({
videoId: id,
...result
});
} catch (error) {
console.error("Error analyzing face in thumbnail:", error);
res.status(500).json({ message: "Failed to analyze face" });
}
});
// Batch face analysis for all videos
app.post("/api/videos/batch-analyze-faces", async (req, res) => {
try {
// Get all videos that don't have face analysis yet
const allVideos = await storage.getVideos(1000, 0); // Get up to 1000 videos
const videosToProcess = allVideos.filter(video => !video.faceCenterPosition && video.thumbnailUrl);
if (videosToProcess.length === 0) {
return res.json({
message: "All videos already processed or no thumbnails available",
processed: 0,
total: allVideos.length
});
}
console.log(`Starting batch face analysis for ${videosToProcess.length} videos...`);
// Import smart thumbnail service (no external dependencies)
const { smartThumbnailService } = await import("./smart-thumbnail-service");
let processed = 0;
let failed = 0;
// Process videos in batches to avoid overwhelming the system
const batchSize = 5;
for (let i = 0; i < videosToProcess.length; i += batchSize) {
const batch = videosToProcess.slice(i, i + batchSize);
await Promise.all(batch.map(async (video) => {
try {
console.log(`Processing face detection for video: ${video.title} (${video.id})`);
const result = await smartThumbnailService.getOptimizedThumbnailInfo(video.thumbnailUrl);
await storage.updateVideo(video.id, {
faceCenterPosition: result.faceCenterPosition,
facesDetected: result.facesDetected,
faceConfidence: result.confidence
});
processed++;
console.log(`✅ Face analysis completed for ${video.title} - Faces detected: ${result.facesDetected}`);
} catch (error) {
console.error(`❌ Face analysis failed for ${video.title}:`, error);
failed++;
}
}));
// Small delay between batches to be respectful
if (i + batchSize < videosToProcess.length) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
console.log(`Batch face analysis completed: ${processed} processed, ${failed} failed`);
res.json({
message: "Batch face analysis completed",
processed,
failed,
total: videosToProcess.length
});
} catch (error) {
console.error("Error in batch face analysis:", error);
res.status(500).json({ message: "Failed to perform batch face analysis" });
}
});
// Get single video by ID
app.get("/api/videos/:id", async (req, res) => {
try {