Znote (recipes)
  Get Znote  

Organize your photos

Automation: This note will help you create a Note script to organize your photos and videos.

 

Organize all your photos

This note will help you organize your photos and videos.

The code below scans the folders recursively and copies the photos into separate folders by year. The script supports incremental import and gives a unique name to each photo using the last counter of each folder.

The input folder is not updated. Try updating the input and output folders to import photos into the output folder.

  • inPathdir
  • outPathdir

Copy files and sort by year

//exec: node
const { resolve } = require('path');
const { readdir } = require('fs').promises;
const path = require('path');
var fs = require('fs')

const inPathdir = "/Users/alagrede/Desktop/inputPhotos";
const outPathdir = "/Users/alagrede/Desktop/photos/";

// recursively read folders
async function* getFiles(dir) {
  const dirents = await readdir(dir, { withFileTypes: true });
  for (const dirent of dirents) {
    const res = resolve(dir, dirent.name);
    if (dirent.isDirectory()) {
      yield* getFiles(res);
    } else {
      yield res;
    }
  }
}

(async () => {
  
  const sortByNameFunc = function(a,b) {
    const nameA = path.parse(a).name;
    const nameB = path.parse(b).name;
    if (nameA < nameB) return 1;
    if (nameA > nameB) return -1;
    return 0;
  }

  const sortByBirthtimeFunc = function(a,b) {
    return fs.statSync(a).birthtime - fs.statSync(b).birthtime;
  }

  async function getFilesSortedBy(dir, sortFunction) {
    let files = []
      for await (const f of getFiles(dir)) {
        if (!path.parse(f).name.startsWith("."))
          files.push(f);
      }
      return files.sort(sortFunction);
  }

  async function getMaxCounterInDir(dir) {
    const files = await getFilesSortedBy(dir, sortByNameFunc);
    if (files.length === 0) return 1;
    return path.parse(files[0]).name.split(".")[0].split("_").slice(-1);
  }

  const inFiles = await getFilesSortedBy(inPathdir, sortByBirthtimeFunc);

  const currentCounterByYear = new Map();
  
  let totalCount = 0;
  // Start to copy files to dest
  for await (const f of inFiles) {
    //var name = path.basename(f);
    const name = path.parse(f).name;
    const dirname = path.dirname(f);
    const extname = path.extname(f);
    const stats = fs.statSync(f);
    const time = stats.birthtime;
    const year = time.getFullYear();
    const yearDir = outPathdir + year + "/";

    // find the current max counter for year directory
    if (!currentCounterByYear.has(year)) {
      if (!fs.existsSync(yearDir)) {
        fs.mkdirSync(yearDir);
        currentCounterByYear.set(year, 1);
      } else {
        currentCounterByYear.set(year, await getMaxCounterInDir(yearDir));
      }
    }

    // copy file to dest
    const count = currentCounterByYear.get(year);

    const dateformatted = time.toISOString().split('T')[0]; // 2012-10-25
    const outFilename = `${yearDir}IMG-${dateformatted}_${count}${extname}`;
    
    currentCounterByYear.set(year, ++count);
    fs.copyFileSync(f, outFilename);
    fs.utimesSync(outFilename, time, time);
    totalCount++;
  }

  console.log(`Successfully imported ${totalCount} files`);
})()

Remove duplicate files

Remove duplicate files based on size and updated time

//exec: node
const { resolve } = require('path');
const { readdir } = require('fs').promises;
const path = require('path');
var fs = require('fs')

const pathdir = "/Users/alagrede/Desktop/out/";

async function* getFiles(dir) {
  const dirents = await readdir(dir, { withFileTypes: true });
  for (const dirent of dirents) {
    const res = resolve(dir, dirent.name);
    if (dirent.isDirectory()) {
      yield* getFiles(res);
    } else {
      yield res;
    }
  }
}

(async () => {
  let m = new Map();

  // count files with same updated time and size
  for await (const f of getFiles(pathdir)) {
    const name = path.parse(f).name;
    const dirname = path.dirname(f);
    const extname = path.extname(f);
      if (!name.startsWith("."))
        const stats = fs.statSync(f);
        const length = stats.size;
        const mtime = stats.mtime;
        const timestamp = mtime.getTime();
        const key = `${timestamp}-${length}`
        if (!m.has(key)) {
          m.set(key, []);
        }
        m.get(key).push(f);
      }
  }

  // remove files appearing once
  for (let k of m.keys()) {
    if (m.get(k).length === 1)
      m.delete(k);
  }

  let totalCount = 0;
  for (let k of m.keys()) {
    let files = m.get(k);
    files.shift(); // remove the first file
    // Delete the others
    for (let f of files) {
      fs.unlinkSync(f);
      totalCount++;
    }
  }

  console.log(`Successfully deleted ${totalCount} duplicate files!`)
})()

Related recipes