99 lines
2.4 KiB
JavaScript
99 lines
2.4 KiB
JavaScript
import { mkdir, readdir, stat } from 'node:fs/promises'
|
|
import { extname, join } from 'node:path'
|
|
import sharp from 'sharp'
|
|
import {
|
|
POST_THUMBNAIL_HEIGHT,
|
|
POST_THUMBNAIL_QUALITY,
|
|
POST_THUMBNAIL_WIDTH,
|
|
getPostThumbnailDirectoryPath,
|
|
getPostThumbnailPathForFile,
|
|
isPostThumbnailSource
|
|
} from '../server/utils/post-thumbnail-image.js'
|
|
|
|
const uploadRoot = join(process.cwd(), 'public', 'uploads', 'posts')
|
|
|
|
/**
|
|
* 게시물 업로드 이미지 파일 목록을 수집한다.
|
|
* @param {string} directoryPath - 탐색 디렉터리
|
|
* @returns {Promise<string[]>} 이미지 파일 경로 목록
|
|
*/
|
|
const collectPostImages = async (directoryPath) => {
|
|
let entries = []
|
|
|
|
try {
|
|
entries = await readdir(directoryPath, { withFileTypes: true })
|
|
} catch {
|
|
return []
|
|
}
|
|
|
|
const files = []
|
|
|
|
for (const entry of entries) {
|
|
const entryPath = join(directoryPath, entry.name)
|
|
|
|
if (entry.isDirectory()) {
|
|
if (entry.name === 'thumbs') {
|
|
continue
|
|
}
|
|
|
|
files.push(...await collectPostImages(entryPath))
|
|
continue
|
|
}
|
|
|
|
if (entry.isFile() && isPostThumbnailSource('', entry.name)) {
|
|
files.push(entryPath)
|
|
}
|
|
}
|
|
|
|
return files
|
|
}
|
|
|
|
/**
|
|
* 게시물 이미지 카드 썸네일을 생성한다.
|
|
* @param {string} imagePath - 원본 이미지 경로
|
|
* @returns {Promise<boolean>} 새로 생성했는지 여부
|
|
*/
|
|
const createPostThumbnail = async (imagePath) => {
|
|
const thumbnailPath = getPostThumbnailPathForFile(imagePath)
|
|
|
|
try {
|
|
await stat(thumbnailPath)
|
|
return false
|
|
} catch {
|
|
await mkdir(getPostThumbnailDirectoryPath(imagePath), { recursive: true })
|
|
}
|
|
|
|
await sharp(imagePath)
|
|
.rotate()
|
|
.resize({
|
|
width: POST_THUMBNAIL_WIDTH,
|
|
height: POST_THUMBNAIL_HEIGHT,
|
|
fit: 'cover',
|
|
position: 'centre',
|
|
withoutEnlargement: true
|
|
})
|
|
.webp({ quality: POST_THUMBNAIL_QUALITY })
|
|
.toFile(thumbnailPath)
|
|
|
|
return true
|
|
}
|
|
|
|
const images = await collectPostImages(uploadRoot)
|
|
let createdCount = 0
|
|
let skippedCount = 0
|
|
|
|
for (const imagePath of images) {
|
|
if (!isPostThumbnailSource('', imagePath) || extname(imagePath).toLowerCase() === '.gif') {
|
|
skippedCount += 1
|
|
continue
|
|
}
|
|
|
|
if (await createPostThumbnail(imagePath)) {
|
|
createdCount += 1
|
|
} else {
|
|
skippedCount += 1
|
|
}
|
|
}
|
|
|
|
process.stdout.write(`게시물 카드 썸네일 생성 완료: 생성 ${createdCount}개, 건너뜀 ${skippedCount}개\n`)
|