Files
Socialflow/server/services/media-purge.ts
T
Claude ac8b494f24 fix: vignettes vidéo persistantes et remise à zéro des erreurs de types
Deux corrections demandées ensemble.

1. Vignettes vidéo

Le planificateur supprime la vidéo locale une fois publiée, alors que la
ligne `media` survit pour l'historique. L'interface tentait donc de lire
un fichier disparu pour en extraire une image : d'où les avertissements
« Video thumbnail generation failed » et les tuiles vides.

Une vignette JPEG est désormais extraite à la création du reel, dans les
deux flux de génération, et stockée à part : elle reste disponible quand
la vidéo ne l'est plus. La purge quotidienne la nettoie en même temps que
la ligne qu'elle illustre. L'extraction est tolérante à l'échec — une
vignette manquante dégrade l'affichage mais ne fait jamais échouer une
publication — et le composant retombe sur son icône de remplacement pour
les médias créés avant ce changement. La fonction d'extraction, jusque-là
privée à la route Remotion, devient un service partagé.

2. Erreurs de compilation préexistantes (27 → 0)

- remotion-video.tsx (bureau et mobile) : les composants Select étaient
  utilisés sans être importés, ce qui faisait planter le sélecteur de voix
  à l'exécution.
- external.ts : fileSize et aiGenerated étaient transmis dans un type que
  le schéma ne déclare pas, et ne fonctionnaient que par conversion
  implicite de PostgreSQL.
- reels.ts : l'interface MusicTrack, référencée mais jamais déclarée dans
  ce module.
- remotion.ts : chromiumOptions.args retiré. Remotion ne lit jamais cette
  clé, les drapeaux Chrome qu'elle contenait n'atteignaient donc pas le
  navigateur.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018uUPZ1GNpEkYB1dpXHYPek
2026-08-12 08:00:48 +00:00

65 lines
2.2 KiB
TypeScript

import { db } from '../db';
import { media } from '@shared/schema';
import { eq, lt, and } from 'drizzle-orm';
import { minioService as storageService } from './minio';
/**
* Purge old local media to save disk space:
* - Images older than 30 days
* - Videos older than 7 days
*/
export async function purgeOldMedia() {
console.log('[Media Purge] Starting automatic purge of old media...');
try {
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
const sevenDaysAgo = new Date();
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
const oldImages = await db.query.media.findMany({
where: (m, { and, lt, eq }) => and(eq(m.type, 'image'), lt(m.createdAt, thirtyDaysAgo))
});
const oldVideos = await db.query.media.findMany({
where: (m, { and, lt, eq }) => and(eq(m.type, 'video'), lt(m.createdAt, sevenDaysAgo))
});
const toDelete = [...oldImages, ...oldVideos];
if (toDelete.length === 0) {
console.log('[Media Purge] No old media found to purge.');
return;
}
console.log(`[Media Purge] Found ${oldImages.length} old images (>30 days) and ${oldVideos.length} old videos (>7 days) to purge.`);
let successCount = 0;
let errorCount = 0;
for (const item of toDelete) {
try {
if (item.cloudinaryPublicId) {
await storageService.deleteMedia(item.cloudinaryPublicId, item.userId, item.type === 'video' ? 'video' : 'image');
}
// La vignette n'a plus de raison d'être une fois la ligne supprimée
if (item.thumbnailUrl?.startsWith('/uploads/')) {
await storageService
.deleteMedia(item.thumbnailUrl.replace('/uploads/', ''), item.userId, 'image')
.catch(err => console.warn(`[Media Purge] Vignette non supprimée pour ${item.id}:`, err));
}
await db.delete(media).where(eq(media.id, item.id));
successCount++;
} catch (err) {
console.error(`[Media Purge] Failed to delete media ${item.id}:`, err);
errorCount++;
}
}
console.log(`[Media Purge] Purge completed. Deleted: ${successCount}, Failed: ${errorCount}.`);
} catch (error) {
console.error('[Media Purge] Fatal error during media purge process:', error);
}
}