반응형
사진을 정리하다보니 exif가 없거나 생성날짜가 잘못된 파일이 있었습니다.
그래서 촬영일이 잘못된 사진의 메타데이터를 일괄 변경하기 위해서 JS스크립트를 작성했습니다.
아래 스크립트 코드는 윈도우, 맥OS에서 테스트하였습니다.
파일명에서 날짜 파싱하기
안드로이드폰으로 촬영한 사진은 파일명이 대부분 촬영날짜로 되어있습니다.
그래서 정규식을 사용하여 파일명에서 날짜를 추출하는 함수를 작성했습니다.
// 파일명에서 날짜 추출하기
function parseFileDate(filename) {
const matched = filename.match(/(?<year>20(?:0|1|2)\d{1})(?:-|\.|_)?(?<month>\d{2})(?:-|\.|_)?(?<day>\d{2})(?:-|_|\.|\s)?(?<hour>\d{2})(?:-|\.|_)?(?<minute>\d{2})(?:-|\.|_)?(?<second>\d{2})/);
return matched?.groups;
}
만약 파일명이 20210715_194008.jpeg 라면 다음과 같이 날짜가 추출됩니다.
사진 exif 메타데이터에 촬영날짜 업데이트하기
exif 메타데이터 업데이트에는 node-exiftool 패키지를 사용했습니다.
그리고 exif 메타데이터 속성은 exiftool.org 사이트를 참고 했습니다.
exif의 날짜 데이터 형식은 "YYYY:MM:DD HH:MM:SS"+0x00, 총 20바이트입니다
async function updateExif(filePath) {
const date = parseFileDate(filePath);
if (!date) return;
const { year, month, day, hour, minute, second } = date;
await ep.open();
const rs = fs.createReadStream(filePath);
const { data, error } = await ep.readMetadata(rs, ["-File:all"]);
console.log({ data, error });
const { DateTimeOriginal } = data?.[0] ?? {};
if (!DateTimeOriginal) {
const dateString = `${year}:${month}:${day} ${hour}:${minute}:${second}`;
await ep.writeMetadata(
filePath,
{
DateTimeOriginal: dateString,
CreateDate: dateString,
ModifyDate: dateString,
},
["overwrite_original"]
);
}
await ep.close();
}
파일 생성일 변경하기
동영상 파일은 exif 메타데이터가 없기 때문에 파일생성일을 변경해야 합니다.
파일 타임스탬프 변경에는 utimes 패키지를 사용했습니다.
const moment = require('moment');
const { utimes } = require('utimes');
async function updateCreateTime(filePath, newDate) {
const time = moment(newDate, 'YYYY:MM:DD HH:mm:ss').toDate().getTime();
await utimes(filePath, {
btime: time, // creation time
mtime: time, // modified time
});
}
전체 코드
마지막으로 제가 사용한 전체 코드는 아래와 같습니다.
const fs = require("fs");
const path = require("path");
const moment = require("moment");
const { utimes } = require("utimes");
const exiftool = require("node-exiftool");
const exiftoolBin = require("dist-exiftool");
const ep = new exiftool.ExiftoolProcess(exiftoolBin);
function parseFileDate(filename) {
const matched = filename.match(
/(?<year>20(?:0|1|2)\d{1})(?:-|\.|_)?(?<month>\d{2})(?:-|\.|_)?(?<day>\d{2})(?:-|_|\.|\s)?(?<hour>\d{2})(?:-|\.|_)?(?<minute>\d{2})(?:-|\.|_)?(?<second>\d{2})/
);
return matched?.groups;
}
async function updateExif(filePath, newDate) {
await ep.open();
const rs = fs.createReadStream(filePath);
const { data, error } = await ep.readMetadata(rs, ["-File:all"]);
const { DateTimeOriginal } = data?.[0] ?? {};
if (!DateTimeOriginal) {
await ep.writeMetadata(
filePath,
{
DateTimeOriginal: newDate,
CreateDate: newDate,
ModifyDate: newDate,
},
["overwrite_original"]
);
}
await ep.close();
}
async function updateCreateTime(filePath, newDate) {
const time = moment(newDate, "YYYY:MM:DD HH:mm:ss").toDate().getTime();
await utimes(filePath, {
btime: time,
mtime: time,
});
}
async function process(dir) {
try {
const files = fs.readdirSync(dir);
for (const file of files) {
const filePath = path.join(dir, file);
if (fs.lstatSync(filePath).isFile()) {
const date = parseFileDate(filePath);
if (!date) continue;
const isImage = /\.(jpeg|jpg|png|gif)$/i.test(filePath);
const isVideo = /\.(mp4|mov)$/i.test(filePath);
if (!isImage || !isVideo) continue;
console.log(filePath);
const { year, month, day, hour, minute, second } = date;
const dateString = `${year}:${month}:${day} ${hour}:${minute}:${second}`;
if (isImage) {
await updateExif(filePath, dateString);
}
await updateCreateTime(filePath, dateString);
}
}
} catch (e) {
console.error(e);
}
}
// run
process("C:\\Users\\anpigon\\Photos");
END.
이 글은 스팀잇에서 작성되었습니다.
https://steemit.com/hive-137029/@anpigon/202206132011z
반응형
'개발' 카테고리의 다른 글
맥OS 보안 설정하기 (0) | 2022.07.25 |
---|---|
Buy Me a Coffee 후원 버튼: payoneer 연동하여 국내에서 사용하기 (2) | 2022.07.25 |
깃헙(Github) 계정을 개인용&업무용 분리하여 사용하기 (0) | 2022.06.05 |
최근에 아이맥이 너무 느려져서 외장 SSD에 MacOS를 설치했습니다. (0) | 2022.06.05 |
Metaplex 캔디머신v2을 사용하여 솔라나Solana에 NFT 발행하기 (Solana, NFT, Metaplex, Candy Machine v2) (0) | 2022.05.05 |