Skip to content

Commit

Permalink
fixed #9 by adding sequential filename generation for conflicting fil…
Browse files Browse the repository at this point in the history
…epaths; reduced test/demo images
  • Loading branch information
dylansmith committed Oct 20, 2015
1 parent 418530e commit 6eb2042
Show file tree
Hide file tree
Showing 9 changed files with 59 additions and 37 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ Options:
-f, --filetypes STRING comma-separated list of file extensions to process
(jpg and jpeg are default)
-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 @@ -64,7 +63,6 @@ The following configuration options are available when using _exif-renamer_ as a
{
dryrun: false, // simulate processing without modifying the filesystem
fallback_ctime: true, // fallback to filesystem ctime if no EXIF DateTimeOriginal
overwrite: false, // overwrite existing files?
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 @@ -310,6 +308,11 @@ 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)
* Deprecated the `overwrite` flag
* Reduced size of test/demo images
* 1.1.2
* switched back to fixed `[email protected]` dependency
* added test for alternate date parsing
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.
26 changes: 20 additions & 6 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,7 +167,7 @@ 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;
}
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 Down
17 changes: 9 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "exif-renamer",
"description": "A NodeJS library & shell command to rename photos using their EXIF data.",
"version": "1.1.2",
"version": "1.2.0",
"homepage": "https://github.com/dylansmith/node-exif-renamer",
"author": {
"name": "Dylan Smith",
Expand Down Expand Up @@ -31,6 +31,7 @@
"test": "grunt test"
},
"devDependencies": {
"async": "^1.4.2",
"grunt": "~0.4.2",
"grunt-contrib-jshint": "~0.6.4",
"grunt-contrib-watch": "~0.5.3",
Expand All @@ -41,16 +42,16 @@
},
"keywords": [],
"dependencies": {
"exif-parser": "~0.1.9",
"q": "~1.0.0",
"cli": "~0.4.5",
"cli-color": "~0.2.3",
"dateformat": "~1.0.7-1.2.3",
"exif-parser": "~0.1.9",
"glob": "~3.2.8",
"handlebars": "~2.0.0-alpha.1",
"lodash": "~3.1.0",
"watchr": "~2.4.11",
"mkdirp": "~0.3.5",
"q": "~1.0.0",
"string": "~1.8.0",
"handlebars": "~2.0.0-alpha.1",
"cli": "~0.4.5",
"cli-color": "~0.2.3",
"glob": "~3.2.8"
"watchr": "~2.4.11"
}
}
46 changes: 25 additions & 21 deletions test/exif-renamer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ var path = require('path'),
fs = require('fs'),
Q = require('q'),
_ = require('lodash'),
async = require('async'),
sinon = require('sinon'),
dateformat = require('dateformat'),
template = '{{datetime}}_{{file}}',
imgPath = path.resolve(__dirname, '../demo/img'),
imgPath = path.resolve(__dirname, 'img'),
imgExif = path.join(imgPath, 'exif.jpg'),
imgNoExif = path.join(imgPath, 'no_exif.jpg'),
notDir = path.join(imgPath, 'NOPE'),
notFile = path.join(imgPath, 'NOPE.jpg'),
unsupportedFile = path.join(imgPath, '../helpers.js'),
unsupportedFile = path.resolve(__dirname, '..', 'package.json'),
helpers, testExif, exifRenamer;

helpers = {
Expand Down Expand Up @@ -318,29 +319,13 @@ describe('exif-renamer', function() {

it('should not overwrite files', function(done) {
exifRenamer.config.overwrite.should.be.false;
exifRenamer.rename(tmpExif, '{{file}}', function(err) {
err.should.be.an.instanceOf(Error);
exifRenamer.rename(tmpExif, '{{file}}', function(err, result) {
err.should.be.false;
path.basename(result.processed.path).should.equal('exif(1).jpg')
done();
});
});

it('should overwrite files when config.overwrite=true', function(done) {
var targetPath = path.join(tmpDir, 'target.jpg');
// create the target file
helpers.cp(imgExif, targetPath, function() {
fs.existsSync(targetPath).should.be.true;
// rename to existing path
exifRenamer.config.overwrite = true;
exifRenamer.rename(tmpExif, '{{dir}}:target.jpg', function(err, result) {
err.should.be.false;
result.processed.path.should.equal(targetPath);
fs.existsSync(targetPath).should.be.true;
fs.existsSync(tmpExif).should.be.false;
done();
});
});
});

it('should not overwrite directories, regardless of configuration', function(done) {
exifRenamer.config.overwrite = true;
exifRenamer.rename(tmpExif, '{{dir}}:', function(err) {
Expand Down Expand Up @@ -370,6 +355,25 @@ describe('exif-renamer', function() {
});
});

it('should handle filename clashes via an auto-incrementing suffix', function(done) {
var results = [];
var renameTest = function(cb) {
helpers.cp(imgExif, tmpExif, function() {
exifRenamer.rename(tmpExif, template, function(err, result) {
err.should.be.false;
results.push(result);
cb();
});
});
}

async.series([renameTest, renameTest, renameTest], function() {
results[0].processed.path.should.match(/_exif\.jpg$/)
results[1].processed.path.should.match(/_exif\(1\)\.jpg$/)
results[2].processed.path.should.match(/_exif\(2\)\.jpg$/)
done();
});
});
});

/**
Expand Down
Binary file modified test/img/alt-date-format.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 added test/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 added test/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.

0 comments on commit 6eb2042

Please sign in to comment.