75lb / renamer

Rename files in bulk.
MIT License
529 stars 30 forks source link

Moving files fails if target folder does not exist (yet) #37

Closed SynerG closed 6 years ago

SynerG commented 6 years ago

Great tool. It would awesome if it supported operations where "renaming" the file involves moving the file to a new folder:

Consider the following scenario:

Running the following command "works":

renamer --regex --find '^(\d{4})(\d{2})(\d{2})(.)' --replace '$1-$2-$3/$1$2$3$4' -v

But it only works if all the destination folders already exist beforehand. Otherwise, renamer fails with the following error: "ENOENT: no such file or directory"

Manually creating the destination folders for each file beforehand would be cumbersome/time consuming, better to offload that task to renamer itself :-D

I think that renamer should create the destination folder before moving the file, in order to prevent this error. This behaviour could be enabled/disabled by a new command line parameter (e.g. --createfolder)

For this purpose, the 'mkdirp' npm package could be used to create all the required subfolders before moving the file.

75lb commented 6 years ago

hi, take a look at the renamer v1 prerelease on the next branch.. then have a look at the wiki and read about plugins, i think you'll be able to solve this quite easily.. renamer v1 will land in the next day or two.

On Tue, 17 Jul 2018, 20:15 SynerG, notifications@github.com wrote:

Great tool. It would awesome if it supported operations where "renaming" the file involves moving the file to a new folder:

Consider the following scenario:

  • A directory contains a lot of pictures with a naming format like this: /20180716_180000.jpg /20180717_210000.jpg ...
  • The user (me :) wants to move the files to a folder hierarchy based on the date included in the filename of each file. Desired result: /2018-07-16/20180716_180000.jpg /2018-07-17/20180717_210000.jpg

Running the following command "works":

renamer --regex --find '^(\d{4})(\d{2})(\d{2})(.)' --replace '$1-$2-$3/$1$2$3$4' -v

But it only works if all the destination folders already exist beforehand. Otherwise, renamer fails with the following error: "ENOENT: no such file or directory"

Manually creating the destination folders for each file beforehand would be cumbersome/time consuming, better to offload that task to renamer itself :-D

I think that renamer should create the destination folder before moving the file, in order to prevent this error. This behaviour could be enabled/disabled by a new command line parameter (e.g. --createfolder)

For this purpose, the 'mkdirp' npm package could be used to create all the required subfolders before moving the file.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/75lb/renamer/issues/37, or mute the thread https://github.com/notifications/unsubscribe-auth/ABMK7A25YCQCi7JpOJPlX1HmJLl8opN2ks5uHjflgaJpZM4VTaNr .

75lb commented 6 years ago

Here's a sample plugin using the template from the writing plugins page. Save it as image-sub-dir.js or similar.

module.exports = PluginBase => class ImageSubDir extends PluginBase {
  /**
   * An optional description which will be printed in the "replace chain" section of `renamer --help`.
   * @returns {string}
   */
  description () {
    return 'Plugin description.'
  }

  /**
   * Zero or more custom option definitions.
   * @see https://github.com/75lb/command-line-args/blob/master/doc/option-definition.md
   * @returns {OptionDefinition[]}
   */
  optionDefinitions () {
    return []
  }

  /**
   * This method is mandatory and should modify the incoming file path as required, returning the new path.
   * @param {string} filePath - The current file path being processed
   * @param {object} options - The current options (in camel-case), e.g. `options.find`, `options.replace` plus any custom options.
   * @param {number} index - The index of the current filePath within the full list of input files.
   * @param {string[]} files - The full list of input files to process (after glob expressions have been expanded).
   * @returns {string} - The modified or unmodified file path.
   */
  replace (filePath, options, index, files) {
    const path = require('path')
    const file = path.parse(filePath)
    const matches = file.name.match(/^(\d{4})(\d{2})(\d{2})(.*)/)
    if (matches) {
      const subDirName = `${matches[1]}-${matches[2]}-${matches[3]}`
      if (!options.dryRun) {
        const fs = require('fs')
        try {
          fs.mkdirSync(subDirName)
        } catch (err) {
          // dir already exists
        }
      }
      return path.join(file.dir, subDirName, file.base)
    } else {
      return filePath
    }
  }
}

Invoke it like so (your plugin path may differ):

$ renamer -p ./image-sub-dir.js  * --dry-run

Remove the --dry-run flag once ready. Let me know if there's anything else. 👍