import { eq } from 'drizzle-orm'; import { db } from '../../src/db'; import { media } from '../../src/db/schema/media'; import { resources } from '../../src/db/schema/resource'; import { mediaBucketName } from '../../src/lib/config'; import minioClient from '../../src/lib/minio'; type MediaMetadata = { hlsPath?: string; }; async function removeObjectIfExists(objectName: string) { try { await minioClient.removeObject(mediaBucketName, objectName); } catch (error: any) { if (error?.code !== 'NoSuchKey' && error?.code !== 'NotFound') { throw error; } } } async function removePrefix(prefix: string) { const objectNames: string[] = []; const stream = minioClient.listObjectsV2(mediaBucketName, prefix, true); await new Promise((resolve, reject) => { stream.on('data', (object) => { if (object.name) objectNames.push(object.name); }); stream.on('error', reject); stream.on('end', resolve); }); if (objectNames.length > 0) { await minioClient.removeObjects(mediaBucketName, objectNames); } } export async function cleanupMediaRecord(mediaId: string) { const [mediaRecord] = await db.select().from(media).where(eq(media.id, mediaId)).limit(1); if (!mediaRecord) { return; } const metadata = mediaRecord.metadata as MediaMetadata | null; const hlsPath = metadata?.hlsPath; const hlsPrefix = hlsPath?.includes('/') ? hlsPath.slice(0, hlsPath.lastIndexOf('/') + 1) : null; await removeObjectIfExists(mediaRecord.storageKey); if (hlsPrefix) { await removePrefix(hlsPrefix); } await db.transaction(async (tx) => { await tx.delete(media).where(eq(media.id, mediaId)); if (mediaRecord.resourceId) { await tx.delete(resources).where(eq(resources.id, mediaRecord.resourceId)); } }); }