Files
photoai/scripts/download-models.mjs
T
koriweb 8a8c10248c Initial commit: AI Photo Organizer (Electron + face-api)
Local-first photo organizer that auto-sorts images by face recognition
and EXIF capture date.

- Electron app with 3-process split: Main (Node) / UI Renderer (React) /
  hidden Inference Renderer (face-api + WebGL)
- Core pipeline: scan -> face match + EXIF -> path build -> atomic move/copy
- Move = copy -> verify -> delete; auto-rename on filename collision
- 1st-registered profile = move, others = copy; unmatched -> [미정]/YYYY/MM
- EXIF date with mtime fallback
- Vitest unit tests (pathBuilder / fileOps / concurrency) all green
- electron-builder config for Windows (nsis) + macOS (dmg)
- Docs: PRD / DECISIONS / ARCHITECTURE

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 13:36:40 +09:00

63 lines
1.9 KiB
JavaScript

// face-api 모델 가중치를 ./models 로 내려받는 스크립트.
// 출처: @vladmandic/face-api 모델 저장소 (오프라인 동작을 위해 앱에 동봉).
// node scripts/download-models.mjs
import { mkdir, writeFile, access } from 'node:fs/promises'
import { constants } from 'node:fs'
import { join, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = dirname(fileURLToPath(import.meta.url))
const MODELS_DIR = join(__dirname, '..', 'models')
const BASE = 'https://raw.githubusercontent.com/vladmandic/face-api/master/model'
// 필요한 모델: SSD MobileNet v1, Tiny Face Detector, Landmark68, Recognition
const FILES = [
'ssd_mobilenetv1_model-weights_manifest.json',
'ssd_mobilenetv1_model.bin',
'tiny_face_detector_model-weights_manifest.json',
'tiny_face_detector_model.bin',
'face_landmark_68_model-weights_manifest.json',
'face_landmark_68_model.bin',
'face_recognition_model-weights_manifest.json',
'face_recognition_model.bin'
]
async function exists(p) {
try {
await access(p, constants.F_OK)
return true
} catch {
return false
}
}
async function download(file) {
const dest = join(MODELS_DIR, file)
if (await exists(dest)) {
console.log(` skip ${file} (이미 존재)`)
return
}
const url = `${BASE}/${file}`
const res = await fetch(url)
if (!res.ok) throw new Error(`다운로드 실패 ${res.status}: ${url}`)
const buf = Buffer.from(await res.arrayBuffer())
await writeFile(dest, buf)
console.log(` ok ${file} (${(buf.length / 1024).toFixed(0)} KB)`)
}
async function main() {
await mkdir(MODELS_DIR, { recursive: true })
console.log(`모델 다운로드 → ${MODELS_DIR}`)
for (const f of FILES) {
await download(f)
}
console.log('완료. 모델 준비됨.')
}
main().catch((err) => {
console.error('오류:', err.message)
process.exit(1)
})