Skip to content

Instantly share code, notes, and snippets.

@lijunle
Last active May 6, 2024 09:08
Show Gist options
  • Save lijunle/d714c8f4b2554c3e4acfa866605645e8 to your computer and use it in GitHub Desktop.
Save lijunle/d714c8f4b2554c3e4acfa866605645e8 to your computer and use it in GitHub Desktop.
Rename DJI and iOS video files to normalized format.
import fs from 'node:fs';
import path from "node:path";
import process from "node:process";
import url from "node:url";
if (!process.argv[2]) {
console.error(`Usage: node ${process.argv[1]} <media-folder> [shift-hours]`);
process.exit(-1);
}
const dirname = path.dirname(url.fileURLToPath(import.meta.url));
const mediaFolder = path.resolve(dirname, process.argv[2]);
console.info("Media folder: " + mediaFolder);
const shiftHours = isNaN(parseInt(process.argv[3])) ? 0 : parseInt(process.argv[3]);
console.info("Shift Hours: " + shiftHours);
const files = fs.readdirSync(mediaFolder, "utf-8");
for (let filename of files) {
processSonyPhotoFiles(filename, mediaFolder);
processSonyVideoFiles(filename, mediaFolder);
processIPhoneMediaFiles(filename, mediaFolder);
processDjiVideoFiles(filename, mediaFolder);
}
/**
* Process Sony photo files.
* @param {string} filename File name.
* @param {string} mediaFolder Media folder.
*/
function processSonyPhotoFiles(filename, mediaFolder) {
if (filename.startsWith("_DSC") && filename.endsWith(".ARW")) {
console.log(`[${filename}] Identified as Sony photo file`);
const extname = path.extname(filename).toLowerCase();
const filePath = path.resolve(mediaFolder, filename);
const fileStat = fs.statSync(filePath);
const targetFilename = formatDate(addHours(fileStat.mtime, shiftHours)) + extname;
console.log(`[${filename}] Rename to ${targetFilename}`);
renameSafely(filename, targetFilename, mediaFolder);
// If XMP file exists, rename it as well.
// BUG here: rename safely will end up with .arw.1.xmp which is incorrect.
const xmpFile = `${filename}.xmp`;
if (fs.existsSync(path.resolve(mediaFolder, xmpFile))) {
console.log(`[${filename}] Rename corresponding XMP file: ${xmpFile}`);
renameSafely(xmpFile, `${targetFilename}.xmp`, mediaFolder);
}
}
}
/**
* Process Sony video files.
* @param {string} filename File name.
* @param {string} mediaFolder Media folder.
*/
function processSonyVideoFiles(filename, mediaFolder) {
if (filename.startsWith("C") && filename.endsWith(".MP4")) {
console.log(`[${filename}] Identified as Sony video file`);
const extname = path.extname(filename).toLowerCase();
const filePath = path.resolve(mediaFolder, filename);
const fileStat = fs.statSync(filePath);
const targetFilename = formatDate(addHours(fileStat.mtime, shiftHours)) + extname;
console.log(`[${filename}] Rename to ${targetFilename}`);
fs.renameSync(
path.resolve(mediaFolder, filename),
path.resolve(mediaFolder, targetFilename)
);
// If XML file exists, rename it as well.
const xmlFile = filename.replace(/\.MP4$/, "M01.XML");
if (fs.existsSync(path.resolve(mediaFolder, xmlFile))) {
console.log(`[${filename}] Rename corresponding XML file: ${xmlFile}`);
renameSafely(xmlFile, targetFilename.replace(/\.mp4$/, ".xml"), mediaFolder);
}
}
}
/**
* Process iPhone photo and video files.
* @param {string} filename File name.
* @param {string} mediaFolder Media folder.
*/
function processIPhoneMediaFiles(filename, mediaFolder) {
const extname = path.extname(filename).toLowerCase();
if (
filename.startsWith("IMG_") &&
['.mov', '.heic', '.jpg'].includes(extname)
) {
console.log(`[${filename}] Identified as iPhone media file`);
const filePath = path.resolve(mediaFolder, filename);
const fileStat = fs.statSync(filePath);
const targetFilename = formatDate(fileStat.mtime) + extname;
console.log(`[${filename} Rename to ${targetFilename}`);
renameSafely(filename, targetFilename, mediaFolder);
}
}
/**
* Process DJI video files.
* @param {string} filename File name.
* @param {string} mediaFolder Media folder.
*/
function processDjiVideoFiles(filename, mediaFolder) {
if (filename.startsWith("DJI_")) {
console.log(`[${filename}] Identified as DJI video file`);
const { name, ext } = path.parse(filename);
const targetName = name.replace(
/DJI_(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})_\d{3}_video/,
"$1-$2-$3 $4.$5.$6"
);
const targetedFilename = targetName + ext.toLowerCase();
console.log(`[${filename}] Rename to ${targetedFilename}`);
renameSafely(filename, targetedFilename, mediaFolder);
}
}
/**
* Add hours to a date time
* @param {Date} date The date time to add hours
* @param {number} hour The number of hours to add
* @returns The new date time
*/
function addHours(date, hour) {
const newDate = new Date(date);
newDate.setHours(date.getHours() + hour);
return newDate;
}
/**
* Format the date time to file name format.
* @param {Date} date The date time to format
* @returns The formated date time string
*/
function formatDate(date) {
const year = date.getFullYear();
const month = padZero(date.getMonth() + 1);
const day = padZero(date.getDate());
const hour = padZero(date.getHours());
const minute = padZero(date.getMinutes());
const second = padZero(date.getSeconds());
return `${year}-${month}-${day} ${hour}.${minute}.${second}`;
}
/**
* Pad zero to a number to be two digits.
* @param {number} number The number to pad zero
* @returns The padded number
*/
function padZero(number) {
return number.toString().padStart(2, "0");
}
/**
* Rename the old file name to new one. Stop if same name exists.
* @param {string} oldFilename Old file name.
* @param {string} newFilename New file name.
* @param {string} mediaFolder Media folder.
* @param {number} counterSuffix Count suffix to resolve file name conflict issue.
*/
function renameSafely(oldFilename, newFilename, mediaFolder, counterSuffix) {
if (counterSuffix) {
const extname = path.extname(newFilename);
const basename = path.basename(newFilename, extname);
newFilename = `${basename} ${counterSuffix}${extname}`;
}
const oldPath = path.resolve(mediaFolder, oldFilename);
const newPath = path.resolve(mediaFolder, newFilename);
if (fs.existsSync(newPath)) {
const suffix = (counterSuffix || 0) + 1;
console.warn(`[${oldFilename}] New file name exist: ${newFilename}, try suffix: ${suffix}`);
renameSafely(oldFilename, newFilename, mediaFolder, suffix);
} else {
fs.renameSync(oldPath, newPath);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment