Files
DndGamePlayer/app/main/project/assetPrune.ts
T

52 lines
1.8 KiB
TypeScript

import fs from 'node:fs/promises';
import path from 'node:path';
import type { MediaAsset, Project } from '../../shared/types';
import type { AssetId } from '../../shared/types/ids';
/** Все asset id, на которые есть ссылки из сцен (превью, видео, аудио). */
export function collectReferencedAssetIds(p: Project): Set<AssetId> {
const refs = new Set<AssetId>();
for (const sc of Object.values(p.scenes)) {
if (sc.previewAssetId) refs.add(sc.previewAssetId);
for (const vid of sc.media.videos) refs.add(vid);
for (const au of sc.media.audios) refs.add(au.assetId);
}
return refs;
}
/**
* Удаляет с диска файлы снятых материалов и записи в `assets`, на которые больше нет ссылок.
*/
export async function reconcileAssetFiles(prev: Project, next: Project, cacheDir: string): Promise<Project> {
const prevIds = new Set(Object.keys(prev.assets) as AssetId[]);
const nextIds = new Set(Object.keys(next.assets) as AssetId[]);
for (const id of prevIds) {
if (nextIds.has(id)) continue;
const a = prev.assets[id];
if (a) {
const abs = path.join(cacheDir, a.relPath);
await fs.unlink(abs).catch(() => undefined);
}
}
const refs = collectReferencedAssetIds(next);
const assets = next.assets;
const kept: Record<AssetId, MediaAsset> = {} as Record<AssetId, MediaAsset>;
let droppedOrphans = false;
for (const id of Object.keys(assets) as AssetId[]) {
const a = assets[id];
if (!a) continue;
if (refs.has(id)) {
kept[id] = a;
} else {
droppedOrphans = true;
const abs = path.join(cacheDir, a.relPath);
await fs.unlink(abs).catch(() => undefined);
}
}
return droppedOrphans ? { ...next, assets: kept } : next;
}