Want to Contribute to us or want to have 15k+ Audience read your Article ? Or Just want to make a strong Backlink?

Organize the mess of your photo folders with Node

When you have hassle organizing your images unfold throughout a number of folders like me, this text may help you!

I just lately skilled a tough drive crash 💥 containing all of my images 😢. Fortunately, I discovered backups and recovered some images from family and friends. However I actually had a tough time organizing all these images. After 4 days of repetitive renaming and shifting duties, I made a decision to create a script to automate this horribly time-consuming activity and perhaps save another person.

As you may see it was a little bit of a multitude 😅



Defining an efficient group

The paragraph beneath is an opinionated thought. You might be free to implement another organizational system with the code given.

My first attempt was to maneuver all of the images and movies right into a single folder and let the “Final Up to date Date” type it. Merely scroll to the specified date and look at images from that interval. However a number of thousand images in a single folder took too lengthy to load and scroll via. Furthermore, I had too many title conflicts with images with the identical title from totally different smartphones.

Second attempt: Divided by yr and month. Few images in folders, few conflicts. After organizing a couple of years of images, I noticed that it was painful to learn. I used to be always altering folders to view my images. A extremely not nice expertise.

Third attempt: Simply divided by yr. Not too many images in folders, a major variety of title conflicts. However simple to learn with out altering folders to observe them. This appears higher however there are nonetheless fairly a couple of title conflicts to resolve…

👉 I selected the choice: divide by yr to simply navigate over time and restrict the loading time of every folder



Create a Node script to import as soon as

The code beneath recursively scan folders and replica images to an output folder divided by yr. Sufficient for a single run, however will override images on incremental import.

const { resolve } = require('path');
const { readdir } = require('fs').guarantees;
const path = require('path');
var fs = require('fs')

const inPathdir = "/Customers/alagrede/Desktop/inputPhotos";
const outPathdir = "/Customers/alagrede/Desktop/images/";


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

(async () => {
  let totalCount = 0;
  for await (const f of getFiles(inPathdir)) {
    const title = path.parse(f).title;
    const dirname = path.dirname(f);
    const extname = path.extname(f);
    const stats = fs.statSync(f);
    const time = stats.birthtime;
    const yr = time.getFullYear();
    const yearDir = outPathdir + yr + "/";

    if (!fs.existsSync(yearDir)) {
      fs.mkdirSync(yearDir);
    }

    const dateformatted = time.toISOString().break up('T')[0]; // 2012-10-25
    const outFilename = `${yearDir}IMG-${dateformatted}_${rely}${extname}`;

    fs.copyFileSync(f, outFilename);
    fs.utimesSync(outFilename, time, time);
    totalCount++;
  }

  console.log(`Efficiently imported ${totalCount} information`);
})()
Enter fullscreen mode

Exit fullscreen mode

Screenshot photos after



Enhance to import incrementally

Now we do the identical factor however this time looking for the final picture quantity in annually folder to not overwrite current images.

const { resolve } = require('path');
const { readdir } = require('fs').guarantees;
const path = require('path');
var fs = require('fs')

const inPathdir = "/Customers/alagrede/Desktop/inputPhotos";
const outPathdir = "/Customers/alagrede/Desktop/images/";

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

(async () => {

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

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

  async operate getFilesSortedBy(dir, sortFunction) {
    let information = []
      for await (const f of getFiles(dir)) {
        // keep away from contemplating hidden cache information
        if (!path.parse(f).title.startsWith(".")) 
          information.push(f);
      }
      return information.type(sortFunction);
  }

  async operate getMaxCounterInDir(dir) {
    const information = await getFilesSortedBy(dir, sortByNameFunc);
    if (information.size === 0) return 1;
    return path.parse(information[0]).title.break up(".")[0].break up("_").slice(-1);
  }

  const inFiles = await getFilesSortedBy(inPathdir, sortByBirthtimeFunc);

  const currentCounterByYear = new Map();

  let totalCount = 0;
  // Begin to copy information to dest
  for await (const f of inFiles) {
    const title = path.parse(f).title;
    const dirname = path.dirname(f);
    const extname = path.extname(f);
    const stats = fs.statSync(f);
    const time = stats.birthtime;
    const yr = time.getFullYear();
    const yearDir = outPathdir + yr + "/";

    // discover the present max counter for yr listing
    if (!currentCounterByYear.has(yr)) {
      if (!fs.existsSync(yearDir)) {
        fs.mkdirSync(yearDir);
        currentCounterByYear.set(yr, 1);
      } else {
        currentCounterByYear.set(yr, await getMaxCounterInDir(yearDir));
      }
    }

    // copy file to dest
    const rely = currentCounterByYear.get(yr);

    const dateformatted = time.toISOString().break up('T')[0]; // 2012-10-25
    const outFilename = `${yearDir}IMG-${dateformatted}_${rely}${extname}`;

    currentCounterByYear.set(yr, ++rely);
    fs.copyFileSync(f, outFilename);
    fs.utimesSync(outFilename, time, time);
    totalCount++;
  }

  console.log(`Efficiently imported ${totalCount} information`);
})()
Enter fullscreen mode

Exit fullscreen mode

Good for importing images over time! And I’m tremendous proud of this.
Perfect image



Only one other thing

I additionally need to present you how one can keep away from picture duplication, permitting you to import any folder with current images with out worrying about it.



Take away duplicates

To seek out duplicate images in bulk, I cross-reference the file dimension with the up to date time. If these 2 values are precisely the identical, I assume they’re the identical information.

const { resolve } = require('path');
const { readdir } = require('fs').guarantees;
const path = require('path');
var fs = require('fs')

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

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

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

  // rely information with identical up to date time and dimension
  for await (const f of getFiles(pathdir)) {
    const title = path.parse(f).title;
    const dirname = path.dirname(f);
    const extname = path.extname(f);
      if (!title.startsWith("."))
        const stats = fs.statSync(f);
        const size = stats.dimension;
        const mtime = stats.mtime;
        const timestamp = mtime.getTime();
        const key = `${timestamp}-${size}`
        if (!m.has(key)) {
          m.set(key, []);
        }
        m.get(key).push(f);
      }
  }

  // take away information showing as soon as
  for (let okay of m.keys()) {
    if (m.get(okay).size === 1)
      m.delete(okay);
  }

  let totalCount = 0;
  for (let okay of m.keys()) {
    let information = m.get(okay);
    information.shift(); // take away the primary file
    // Delete the others
    for (let f of information) {
      fs.unlinkSync(f);
      totalCount++;
    }
  }

  console.log(`Efficiently deleted ${totalCount} duplicate information!`)
})()
Enter fullscreen mode

Exit fullscreen mode



Work performed for immediately 👍

This method is sufficient for me to arrange all images and movies and luxuriate in viewing them with out losing time. I hope these scripts will allow you to.

You will discover them here instantly executable and editable via Znote. (A quite simple strategy to work on this sort of script)

In the event you wrestle with this tutorial, I additionally package deal a little bit app right here: photo-organizer.app with superior options (a bit lengthy to elucidate in a single article however you can find beneath the libraries used). Amongst these options, it is possible for you to to seek out comparable images (with pixel comparability) and resize pictures.

Librairies used:

Add a Comment

Your email address will not be published. Required fields are marked *

Want to Contribute to us or want to have 15k+ Audience read your Article ? Or Just want to make a strong Backlink?