Skip to content

Commit

Permalink
now using handlebars for filename templating, updated demos and added…
Browse files Browse the repository at this point in the history
… docs
  • Loading branch information
Dylan Smith committed Feb 20, 2014
1 parent 3f9b800 commit c53a1bd
Show file tree
Hide file tree
Showing 10 changed files with 281 additions and 107 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -1 +1,2 @@
node_modules
npm-debug.log
6 changes: 5 additions & 1 deletion Gruntfile.js
Expand Up @@ -14,6 +14,9 @@ module.exports = function(grunt) {
gruntfile: {
src: 'Gruntfile.js'
},
demo: {
src: ['demo/**/*.js']
},
lib: {
src: ['lib/**/*.js']
},
Expand Down Expand Up @@ -43,6 +46,7 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-watch');

// Default task.
grunt.registerTask('default', ['jshint', 'nodeunit']);
//grunt.registerTask('default', ['jshint', 'nodeunit']);
grunt.registerTask('default', ['jshint']);

};
171 changes: 166 additions & 5 deletions README.md
@@ -1,6 +1,6 @@
# exif-renamer [![Build Status](https://secure.travis-ci.org/dylansmith/node-exif-renamer.png?branch=master)](http://travis-ci.org/dylansmith/node-exif-renamer)

A NodeJS service to rename photos using their EXIF data.
A NodeJS service to rename images using their EXIF data.

## Installation
Install the module with: `npm install exif-renamer`
Expand All @@ -15,7 +15,7 @@ var exifRenamer = require('exif-renamer');
exif-renamer supports node-style callbacks:

```javascript
exifRenamer.rename('path/to/image.file', '%yyyy-%mm-%dd_%n', function(error, filename) {
exifRenamer.rename('path/to/image.file', '{{date "yyyy-mm-dd"}}_{{file}}', function(error, filename) {
if (!error) {
console.log('the file was renamed: ', filename);
} else {
Expand All @@ -24,12 +24,12 @@ exifRenamer.rename('path/to/image.file', '%yyyy-%mm-%dd_%n', function(error, fil
});
```

It also supports promises (using the `q` library):
It also supports promises (using the [Q library](https://www.npmjs.org/package/q)):

```javascript
exifRenamer
.rename('path/to/image.file', '%yyyy-%mm-%dd_%n')
.then(function(filename)) {
.rename('path/to/image.file', '{{date "yyyy-mm-dd"}}_{{file}}')
.then(function(filename) {
console.log('the file was renamed: ', filename);
})
.catch(function(error) {
Expand All @@ -39,8 +39,169 @@ exifRenamer
```

## Documentation

### Configuration
Coming soon.

### Renaming templates

The #process and #rename methods accept a `template` argument which is used to determine the new
filename for the renamed image. As the name might suggest, the template is a way for you to format
the filename using values present in the EXIF data.

`exif-renamer` uses [Handlebars](http://handlebarsjs.com/) for templating, which allows you to
easily access the image file metadata to construct just about any filename you could imagine, e.g.:

> Prefix the filename with the date (defaults to YYYY-MM-DD format):<br>
> `{{date}}_{{file}}`
> Prefix the filename with a custom date format (see [dateformat](https://www.npmjs.org/package/dateformat)):<br>
> `{{date "yy-mm"}}_{{file}}'`
> Move the image to a YYYY-MM directory:<br>
> `{{date "yyyy-mm"}}/{{file}}`
> Prefix the parent directory with the year:<br>
> `{{date "yyyy"}}-{{dir}}/{{file}}`
> Prefix the filename with the file extension and camera model:<br>
> `{{EXT}}-{{image.Model}}-{{file}}`
> Prefix the filename with the F-number:<br>
> `F{{exif.FNumber}}-{{file}}`
`date` (*datetime*, really) is currently the only metadata that supports additional formatting, via
the [dateformat module](https://www.npmjs.org/package/dateformat) as mentioned above.

#### Custom renaming

It is possible to pass your own custom function rather than a handlebars template, giving you total
control over the renaming process. Here is an example:

```javascript
doge_prefixer = function(fileinfo, metadata) {
var dogeisms = ['very', 'wow', 'so', 'much'];
console.log(arguments);
return [dogeisms[Math.floor(Math.random() * dogeisms.length)], fileinfo.basename].join('_');
}

exifRenamer.process('path/to/image.file', doge_prefixer, function(err, result) {
//...
});
```

#### Metadata

The metadata available to a handlebar template is a combination of the exif data generated by the
[exif module](https://www.npmjs.org/package/exif), path information, and some other useful stuff:

```json
{
// EXIF data
<see: https://www.npmjs.org/package/exif>,

// path information
path: <the absolute path to the image>,
basename: <the image filename>
dirname: <the image's parent directory>,
extname: <the image extension>,

// other useful stuff
'date': <EXIF date in YYYY-MM-DD format>,
'time': <EXIF time in HH:MM:SS format>,
'file': <the image filename, alias for basename>,
'dir': <the image's parent directory, alias for dirname>,
'ext': <the image extension, lowercased>,
'EXT': <the image extension, uppercased>,
}
```

### Methods

#### #exif

Returns the EXIF data for an image.

** arguments **

- `filepath` the path to the image file
- `callback` the node-style callback that will receive the response

** usage **

```javascript
exifRenamer.exif('path/to/image.file', function(err, exifdata) {
//...
});
```

#### #process

Takes an image and renaming template or callback and returns an object containing the renamed
image path, but does not actually rename the image (see #rename for this).

** arguments **

- `filepath` the path to the image file
- `template` the renaming template or a custom callback function
- `callback` the node-style callback that will receive the response

** usage **

```javascript
// using a handlebars template
exifRenamer.process('path/to/image.file', 'renaming-template', function(err, result) {
//...
});

// using a custom function
exifRenamer.process('path/to/image.file', customRenamer, function(err, result) {
//...
});
```

#### #rename

Takes an image and renaming template or callback and renames/moves the image.

** arguments **

- `filepath` the path to the image file
- `template` the renaming template or a custom callback function
- `callback` the node-style callback that will receive the response

** usage **

```javascript
// using a handlebars template
exifRenamer.rename('path/to/image.file', 'renaming-template', function(err, result) {
//...
});

// using a custom function
exifRenamer.rename('path/to/image.file', customRenamer, function(err, result) {
//...
});
```

#### #watch

Watches a specified directory, renaming all images that are added to that directory.

** arguments **

- `dirpath` the path to the watch directory
- `template` the renaming template or a custom callback function
- `callback` the node-style callback that will receive the response

** usage **

```javascript
exifRenamer.watch('path/to/watch/dir', 'renaming-template', function(err, result) {
//...
});
```

## Contributing
In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [Grunt](http://gruntjs.com/).

Expand Down
11 changes: 11 additions & 0 deletions demo/demo-exif.js
@@ -0,0 +1,11 @@
var exifRenamer = require('../lib/exif-renamer'),
path = require('path'),
helpers = require('./helpers'),
img = path.resolve(__dirname, 'test.jpg');

helpers.ul('DEMO: exif-renamer#process', '=', '\n');
console.log('Getting EXIF data for', img, ':\n');
exifRenamer.exif(img).then(function(exifdata) {
console.log(exifdata);
console.log('');
});
49 changes: 28 additions & 21 deletions demo/demo-process.js
@@ -1,35 +1,42 @@
var path = require('path'),
_ = require('lodash'),
Q = require('q'),
helpers = require('./helpers'),
log = console.log,
exifRenamer = require('../lib/exif-renamer'),
img = path.resolve(__dirname, 'test.jpg'),
examples = {
'Prefix with a default datetime': '%date_%file',
'Prefix with "YY-MM" datetime': '%yy-%mm-%file',
'Move to YYYY-MMM directory': '%yyyy-%mm/%file',
'Prefix parent directory with year': '%yyyy-%dir/%file',
'Prefix with the file extension and camera model': '%EXT-%{image.Model}-%file',
'Prefix with F-number': 'F%{exif.FNumber}-%file',
'Rename using a custom function': function(filepath, data) { return 'CUSTOM-' + path.basename(filepath); }
};
examples, doge_prefixer;

function ul(text, chr) {
chr = chr || '=';
return text + '\n' + _.times(text.length, function() { return chr; }).join('');
doge_prefixer = function(fileinfo, metadata) {
var dogeisms = ['very', 'wow', 'so', 'much'];
console.log(arguments);
return [dogeisms[Math.floor(Math.random() * dogeisms.length)], fileinfo.basename].join('_');
}

function render(description, result) {
console.log(ul('EXAMPLE: ' + description));
console.log('template :', result.template);
console.log('original :', result.original.path);
console.log('processed:', result.processed.path);
console.log('');
examples = {
'Prefix the filename with the date': '{{date}}_{{file}}',
'Prefix the filename with a custom date format': '{{date "yy-mm"}}_{{file}}',
'Move the image to a YYYY-MM directory': '{{date "yyyy-mm"}}/{{file}}',
'Prefix the parent directory with the year': '{{date "yyyy"}}-{{dir}}/{{file}}',
'Prefix the filename with the extension & camera model': '{{EXT}}-{{image.Model}}-{{file}}',
'Prefix the filename with the F-number': 'F{{exif.FNumber}}-{{file}}',
'Rename using a custom function': doge_prefixer
};

helpers.ul('DEMO: exif-renamer#process', '=', '\n');

function render(title, result) {
helpers.ul('EXAMPLE: ' + title, '-', '\n');
log('template :', result.template);
log('original :', result.original.path);
log('processed:', result.processed.path);
}

// rename using string-based patterns
console.log('');
_.forEach(examples, function(template, description) {
exifRenamer.process(img, template).then(function(result) {
Q.all(_.map(examples, function(template, description) {
return exifRenamer.process(img, template).then(function(result) {
render(description, result);
});
})).done(function() {
log('');
});
31 changes: 11 additions & 20 deletions demo/demo-watch.js
@@ -1,34 +1,25 @@
var path = require('path'),
fs = require('fs'),
_ = require('lodash'),
helpers = require('./helpers'),
log = console.log,
exifRenamer = require('../lib/exif-renamer'),
watch_dir = path.resolve(__dirname, 'watch_target');
watch_dir = path.resolve(__dirname, 'watch_target'),
stdin = process.openStdin(),
src_file = path.resolve(__dirname, 'test.jpg');

function ul(text, chr) {
chr = chr || '=';
return text + '\n' + _.times(text.length, function() { return chr; }).join('');
}

log('\n' + ul('DEMO: watching the filesystem for changes'));
helpers.ul('DEMO: exif-renamer#watch', '=', '\n', '\n');
log('Press <Enter> to trigger file creation in the watch directory...');

// watch a target dir for new image and rename them
exifRenamer.watch(watch_dir, 'processed/%date_%file', function(err, result) {
var u = '';
description = 'CHANGE DETECTED';
_.times(description.length, function() { u += '='; });
log([,description,u,].join('\n'));
// watch the target dir for new images and rename them
exifRenamer.watch(watch_dir, 'processed/{{date}}_{{file}}', function(err, result) {
helpers.ul('CHANGE DETECTED', '=', '\n');
log('template :', result.template);
log('original :', result.original.path);
log('processed:', result.processed.path);
log('');
});

// create file everytime a key is pressed
log('Press <Enter> to trigger file creation in the watch directory...');
var src_file = path.resolve(__dirname, 'test.jpg');
var stdin = process.openStdin();
stdin.on('data', function(chunk) {
// create file every time the Enter key is pressed
stdin.on('data', function() {
var target_file = path.join(watch_dir, Date.now() + '_test.jpg');
log('creating: ' + target_file);
fs.createReadStream(src_file).pipe(fs.createWriteStream(target_file));
Expand Down
10 changes: 10 additions & 0 deletions demo/helpers.js
@@ -0,0 +1,10 @@
var S = require('string');

module.exports = {
ul: function(text, ul, prefix, suffix) {
ul = ul || '=';
prefix = prefix || '';
suffix = suffix || '';
console.log(prefix + text + '\n' + S(ul).repeat(text.length) + suffix);
}
};

0 comments on commit c53a1bd

Please sign in to comment.