Skip to content

Commit

Permalink
1.2.0 (#13)
Browse files Browse the repository at this point in the history
* fixed #9 by adding sequential filename generation for conflicting filepaths; reduced test/demo images

* #9: added support for globs in rename_dir and --glob flag

* improve detection of file creation time (#10)

* fix file creation calc, add better test

* updated deps, removed Grunt for tooling

* remove grunt dep, updated exif-parser

* updated README

* updated README
  • Loading branch information
dylansmith authored Jan 15, 2018
1 parent 418530e commit 47da224
Show file tree
Hide file tree
Showing 14 changed files with 206 additions and 151 deletions.
19 changes: 19 additions & 0 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
parserOptions:
ecmaVersion: 6
env:
node: true
extends: 'eslint:recommended'
rules:
indent:
- error
- 4
linebreak-style:
- error
- unix
no-console: off
quotes:
- error
- single
semi:
- error
- always
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.nyc_output
coverage
node_modules
npm-debug.log
npm-debug.log
25 changes: 25 additions & 0 deletions .nycrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"check-coverage": true,
"per-file": true,
"lines": 86,
"statements": 86,
"functions": 83,
"branches": 75,
"include": [
"lib/**/*.js"
],
"exclude": [
],
"reporter": [
"lcov",
"html",
"text"
],
"require": [
],
"extension": [
],
"cache": true,
"all": true,
"report-dir": "./coverage"
}
62 changes: 0 additions & 62 deletions Gruntfile.js

This file was deleted.

33 changes: 24 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,15 @@ Usage:
exif-renamer [OPTIONS] [ARGS]

Options:
-c, --no_ctime do not use the ctime fallback if no EXIF data is present
(also sets require_exif=true)
-c, --no_ctime do not use the filesystem creation time fallback if
no EXIF data is present (also sets require_exif=true)
-d, --dryrun run without performing filesystem changes
-e, --exif get the exif data for the specified image
-f, --filetypes STRING comma-separated list of file extensions to process
(jpg and jpeg are default)
-g, --glob STRING glob pattern to filter files for processing within a
target directory (overrides --recursive)
-l, --list list available template variables
-o, --overwrite overwrite existing files
-r, --recursive recursively process the specified directory
-t, --template [STRING]renaming template (Default is {{datetime}}_{{file}})
-w, --watch watch the specified directory for changes and
Expand All @@ -63,8 +64,7 @@ The following configuration options are available when using _exif-renamer_ as a
```javascript
{
dryrun: false, // simulate processing without modifying the filesystem
fallback_ctime: true, // fallback to filesystem ctime if no EXIF DateTimeOriginal
overwrite: false, // overwrite existing files?
fallback_ctime: true, // fallback to filesystem creation time if no EXIF DateTimeOriginal
require_exif: false, // fail if EXIF data is not found?
path_separator: '/', // the character used to separate paths in templates
formats: {
Expand Down Expand Up @@ -163,7 +163,7 @@ path information, and some other useful stuff:
'stat': <see: http://nodejs.org/api/fs.html#fs_class_fs_stats>,
// other useful stuff
'datetime': <EXIF date or ctime>,
'datetime': <EXIF date or filesystem creation time>,
'date': <EXIF date formatted using the value of config.formats.date>,
'time': <EXIF time formatted using the value of config.formats.time>
}
Expand Down Expand Up @@ -247,14 +247,17 @@ exifRenamer.rename('path/to/image.file', customRenamer, function(err, result) {
#### #rename_dir
Renames/moves all applicable images in the specified directory, using the provided
template/callback.
Renames/moves all applicable images in the specified directory, using the provided template/callback.
##### arguments
- `dirpath` the path to the directory
- `template` the renaming template or a custom callback function
- `[recursive=false]` boolean switch to enable recursive processing, defaults to false
- `[recursiveOrGlob=false]` boolean switch to enable recursive processing, defaults to false. Since 1.2.0, it can also accept a glob pattern which is applied to the target directory. Some examples:
- `'**'` is the equivalent of `true`
- `'*'` is the equivalent of `false`
- `**/*.jpg` would recursively select only `.jpg` files
- `*.tiff` would non-recursively select only `.tiff` files
- `[callback]` the node-style callback called once all files have been processed
- `[itemCallback]` the node-style callback called after each file is processed
Expand Down Expand Up @@ -310,6 +313,18 @@ your enhancements or bugfix.
* Swap out Grunt for Gulp
## Release History
* 1.2.0
* Introduced filename conflict resolution via sequential filenaming
in response to [#9](https://github.com/dylansmith/node-exif-renamer/issues/9)
* Fixed incorrect calculation of modified time on some systems [#10](https://github.com/dylansmith/node-exif-renamer/issues/10)
* Deprecated the `--overwrite` flag
* Added support for passing globs to `rename_dir`, exposed via the
`--glob` cli flag. This option will override `--recursive` and allow
for greater control of file processing (thanks to @TotallyInformation
for the suggestion).
* Reduced size of test/demo images
* Upgraded most dependencies
* Swapped out Grunt-based tooling for npm scripts
* 1.1.2
* switched back to fixed `exif-parser@0.1.9` dependency
* added test for alternate date parsing
Expand Down
9 changes: 3 additions & 6 deletions bin/exif-renamer
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ cli.parse({
dryrun: ['d', 'run without performing filesystem changes'],
exif: ['e', 'get the exif data for the specified image'],
filetypes: ['f', 'comma-separated list of file extensions to process (jpg and jpeg are default)', 'string'],
glob: ['g', 'glob pattern to filter files for processing within a target directory (overrides --recursive)', 'string'],
list: ['l', 'list available template variables'],
overwrite: ['o', 'overwrite existing files'],
recursive: ['r', 'recursively process the specified directory'],
template: ['t', 'renaming template', 'string', '{{datetime}}_{{file}}'],
watch: ['w', 'watch the specified directory for changes and process automatically']
Expand Down Expand Up @@ -44,9 +44,6 @@ cli.main(function(args, options) {
// set dryrun
exifRenamer.config.dryrun = options.dryrun;

// set overwrite
exifRenamer.config.overwrite = options.overwrite;

// set valid_extensions
if (options.filetypes) {
exifRenamer.config.valid_extensions = options.filetypes.split(/\s*\,\s*/);
Expand Down Expand Up @@ -84,7 +81,7 @@ cli.main(function(args, options) {
exifRenamer.watch(p, options.template, reporter);
}

else if (options.recursive) {
else if (options.recursive && !options.glob) {
if (!is_dir) {
return cli.fatal('the --recursive option requires a valid directory');
}
Expand Down Expand Up @@ -116,7 +113,7 @@ cli.main(function(args, options) {
try {
if (is_dir) {
cli.info('Processing directory: ' + p);
exifRenamer.rename_dir(p, options.template, _.noop, reporter);
exifRenamer.rename_dir(p, options.template, options.glob || null, _.noop, reporter);
}
else if (is_file) {
exifRenamer.rename(p, options.template, reporter);
Expand Down
Binary file modified demo/img/exif.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/img/no_exif.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
44 changes: 29 additions & 15 deletions lib/exif-renamer.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,18 @@ var exifRenamer,
defaults = {
dryrun: false, // simulate processing without modifying the filesystem
fallback_ctime: true, // fallback to filesystem ctime if no EXIF DateTimeOriginal
overwrite: false, // overwrite existing files?
overwrite: false, // deprecated: uses sequential filenames to resolve conflicts since 1.2.0
require_exif: false, // fail if EXIF data is not found?
path_separator: '/', // the character used to separate paths in templates
formats: {
datetime: 'yyyymmdd-HHMMss', // default formatting for {{datetime}}
date: 'yyyymmdd', // default formatting for {{date}}
time: 'HHMMss' // default formatting for {{time}}
},
valid_extensions: [ // supported file extensions for processing
valid_extensions: [ // supported file extensions for processing
'jpg','jpeg','tif','tiff'
]
],
sequential_template: '{{dir}}/{{name}}({{index}}).{{ext}}' // template used to generate sequential file paths
};

Handlebars.registerHelper('datetime', function(format) {
Expand Down Expand Up @@ -166,12 +167,12 @@ exifRenamer = {
}

// determine datetime
datetime = exif_data.exif && (exif_data.exif.DateTimeOriginal || exif_data.exif.CreateDate);
datetime = exif_data && exif_data.exif && (exif_data.exif.DateTimeOriginal || exif_data.exif.CreateDate);
if (datetime) {
datetime = datetime * 1000;
}
else if (this.config.fallback_ctime === true && src.stat && src.stat.ctime) {
datetime = src.stat.ctime;
else if (this.config.fallback_ctime === true && src.stat) {
datetime = new Date(Math.min(src.stat.birthtime, src.stat.ctime, src.stat.mtime));
}
else if (this.config.fallback_ctime === false) {
return callback(this.error('can\'t resolve datetime: no EXIF datetimes and fallback_ctime=false', metadata));
Expand All @@ -190,6 +191,19 @@ exifRenamer = {
}.bind(this));
},

/**
* Returns the next available sequential filename for a given conflicting filepath
* @param {String} filepath
* @return {String}
*/
get_sequential_filepath: function(filepath, index) {
index = index || 1;
var params = this.get_path_info(filepath);
params.index = index;
var newpath = Handlebars.compile(this.config.sequential_template)(params);
return (fs.existsSync(newpath)) ? this.get_sequential_filepath(filepath, index + 1) : newpath;
},

/**
* @method #is_supported_file(filepath)
* @arg {String} filepath
Expand Down Expand Up @@ -269,8 +283,8 @@ exifRenamer = {
return callback(this.error('rename target "' + result.processed.path + '"" is a directory', result));
}

if (result.processed.stat.isFile() && !this.config.overwrite) {
return callback(this.error('rename target "' + result.processed.path + '"" already exists', result));
if (result.processed.stat.isFile()) {
result.processed.path = this.get_sequential_filepath(result.processed.path);
}
}

Expand All @@ -290,22 +304,22 @@ exifRenamer = {
},

/**
* @method #rename_dir(dirpath, template, [recursive=true], callback)
* @method #rename_dir(dirpath, template, [recursive=false], callback, itemCallback)
* @arg {String} filepath
* @arg {(String|Function)} template
* @arg {Boolean} [recursive=false]
* @arg {Boolean|String} [recursiveOrGlob=false]
* @arg {Function} [callback]
* @arg {Function} [itemCallback]
*/
rename_dir: function(dirpath, template, recursive, callback, itemCallback) {
rename_dir: function(dirpath, template, recursiveOrGlob, callback, itemCallback) {
var p = path.resolve(dirpath),
pattern = '*',
pattern,
promises = [],
dfd = Q.defer();

// recursive is optional
if (_.isBoolean(arguments[2]) === false) {
recursive = false;
if (!_.isBoolean(arguments[2]) && !_.isString(arguments[2])) {
recursiveOrGlob = false;
callback = arguments[2];
itemCallback = arguments[3];
}
Expand All @@ -318,7 +332,7 @@ exifRenamer = {
return callback(this.error(dirpath + ' is not a valid directory'));
}

pattern = (recursive === true) ? '**' : '*';
pattern = (recursiveOrGlob === true) ? '**' : (_.isString(recursiveOrGlob) ? recursiveOrGlob : '*');
glob.sync(pattern, {cwd: p}).forEach(function(file) {
var f;
f = path.join(p, file);
Expand Down
Loading

0 comments on commit 47da224

Please sign in to comment.