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:
+50
-4
@@ -640,6 +640,7 @@ export class MemStorage implements IStorage {
|
||||
class BunnyStorage implements IStorage {
|
||||
private bunnyService: BunnyService;
|
||||
private viewsCache: Map<string, number> = new Map();
|
||||
private faceDataCache: Map<string, { faceCenterPosition?: string; facesDetected?: number; faceConfidence?: number }> = new Map();
|
||||
|
||||
constructor() {
|
||||
this.bunnyService = new BunnyService();
|
||||
@@ -649,7 +650,12 @@ class BunnyStorage implements IStorage {
|
||||
console.log(`Fetching videos from cache: limit=${limit}, offset=${offset}, search=${search}`);
|
||||
const result = videoSyncService.getVideos(limit, offset, search);
|
||||
console.log(`Returning ${result.videos.length} videos from cache (age: ${result.cacheAge}ms)`);
|
||||
return result.videos;
|
||||
|
||||
// Apply face detection data from cache
|
||||
return result.videos.map(video => {
|
||||
const faceData = this.faceDataCache.get(video.id);
|
||||
return faceData ? { ...video, ...faceData } : video;
|
||||
});
|
||||
}
|
||||
|
||||
async getVideo(id: string): Promise<Video | undefined> {
|
||||
@@ -660,7 +666,10 @@ class BunnyStorage implements IStorage {
|
||||
if (this.viewsCache.has(cachedVideo.id)) {
|
||||
cachedVideo.views += this.viewsCache.get(cachedVideo.id)!;
|
||||
}
|
||||
return cachedVideo;
|
||||
|
||||
// Apply face detection data from cache
|
||||
const faceData = this.faceDataCache.get(cachedVideo.id);
|
||||
return faceData ? { ...cachedVideo, ...faceData } : cachedVideo;
|
||||
}
|
||||
|
||||
// Fallback to direct API call
|
||||
@@ -673,7 +682,9 @@ class BunnyStorage implements IStorage {
|
||||
video.views += this.viewsCache.get(video.id)!;
|
||||
}
|
||||
|
||||
return video;
|
||||
// Apply face detection data from cache
|
||||
const faceData = this.faceDataCache.get(video.id);
|
||||
return faceData ? { ...video, ...faceData } : video;
|
||||
} catch (error) {
|
||||
console.error(`Error fetching video ${id} from Bunny:`, error);
|
||||
return undefined;
|
||||
@@ -685,7 +696,42 @@ class BunnyStorage implements IStorage {
|
||||
}
|
||||
|
||||
async updateVideo(id: string, updates: UpdateVideo): Promise<Video | undefined> {
|
||||
throw new Error("Updating videos is not supported with Bunny.net integration");
|
||||
// Allow updating face detection data and views even with Bunny.net integration
|
||||
const allowedUpdates = ['faceCenterPosition', 'facesDetected', 'faceConfidence', 'views'];
|
||||
const filteredUpdates = Object.fromEntries(
|
||||
Object.entries(updates).filter(([key]) => allowedUpdates.includes(key))
|
||||
);
|
||||
|
||||
if (Object.keys(filteredUpdates).length === 0) {
|
||||
throw new Error("Only face detection and view updates are supported with Bunny.net integration");
|
||||
}
|
||||
|
||||
try {
|
||||
// Try to update in database first
|
||||
try {
|
||||
await db.update(videos).set(filteredUpdates as UpdateVideo).where(eq(videos.id, id));
|
||||
} catch (dbError) {
|
||||
console.warn(`Database update failed for video ${id}, using cache:`, dbError);
|
||||
}
|
||||
|
||||
// Update face detection data in cache (always reliable)
|
||||
const { views, ...faceData } = filteredUpdates;
|
||||
if (Object.keys(faceData).length > 0) {
|
||||
const existing = this.faceDataCache.get(id) || {};
|
||||
this.faceDataCache.set(id, { ...existing, ...faceData });
|
||||
}
|
||||
|
||||
// Update views cache
|
||||
if (views !== undefined) {
|
||||
this.viewsCache.set(id, (this.viewsCache.get(id) || 0) + (views - (this.viewsCache.get(id) || 0)));
|
||||
}
|
||||
|
||||
// Return updated video
|
||||
return await this.getVideo(id);
|
||||
} catch (error) {
|
||||
console.error(`Error updating video ${id}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async deleteVideo(id: string): Promise<boolean> {
|
||||
|
||||
Reference in New Issue
Block a user