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:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user