import { join, extname, basename } from 'node:path' import type { CaptureDate } from '@shared/types' import { UNMATCHED_FOLDER } from '@shared/constants' /** * 인물/미정 + 연/월 기준의 대상 디렉터리 경로를 생성한다. * 실제 파일명 충돌 해소는 fileOps에서 수행 (여기서는 디렉터리 + 원본 파일명까지). * * @param who 인물 폴더명, 또는 미검출이면 null → [미정] */ export function buildTargetPath( outputRoot: string, who: string | null, date: CaptureDate, sourceFile: string ): string { const folder = who ?? UNMATCHED_FOLDER const filename = basename(sourceFile) return join(outputRoot, folder, date.year, date.month, filename) } /** 폴더명으로 안전하지 않은 문자 제거 (라벨에 / : * 등이 들어오는 경우 대비) */ function safeFolder(name: string): string { return name.replace(/[\\/:*?"<>|]/g, '_').trim() || '_' } /** * 컬렉션(생일/기념일) 폴더 경로: <출력>/<카테고리>/<하위>/<연도>/<파일명> * 예: 출력/Birthdays/Alex/2024/IMG_0001.jpg, 출력/Anniversaries/결혼기념일/2024/IMG_0001.jpg */ export function buildCollectionPath( outputRoot: string, category: string, sub: string, year: string, sourceFile: string ): string { return join(outputRoot, category, safeFolder(sub), year, basename(sourceFile)) } /** * 파일명 충돌 시 사용할 후보 경로를 생성 (name_1.ext, name_2.ext ...). * @param index 1부터 시작하는 충돌 회피 인덱스 */ export function withCollisionSuffix(targetPath: string, index: number): string { const dir = targetPath.slice(0, targetPath.length - basename(targetPath).length) const ext = extname(targetPath) const stem = basename(targetPath, ext) return join(dir, `${stem}_${index}${ext}`) }