Permalink
Browse files

Added read(), removed AudioProperties.

The read() call asynchronously fetches read-only tags and audio properties and
discards the FileRef. This makes AudioProperties a redundant and synchronous
way to fetch audio properties. Since they are read only by default, unlike tags,
it is getter to just extract them and stuff them into an object rather than
have a backing C++ object.
  • Loading branch information...
1 parent da8ac19 commit d31ffc4819814c57e19643d2d8904825156f8c5c @nikhilm committed Apr 16, 2012
Showing with 184 additions and 183 deletions.
  1. +32 −33 README.md
  2. +1 −1 async.js
  3. +2 −2 examples/simple.js
  4. +64 −43 spec/taglibSpec.js
  5. +0 −76 src/audioproperties.cc
  6. +0 −26 src/audioproperties.h
  7. +80 −1 src/taglib.cc
  8. +4 −0 src/taglib.h
  9. +1 −1 wscript
View
@@ -56,6 +56,33 @@ The `examples` show usage.
## API
+### read(path, callback)
+
+The function you will most likely want to use. `callback` should have signature
+`callback(err, tag, audioProperties)` where `tag` and `audioProperties` are
+plain-old JavaScript objects. For the distinction between these and `Tag`, see
+`Tag` below.
+
+tag can have the following fields. node-taglib currently supports only the
+fields common to all formats:
+
+* title (string)
+* album (string)
+* comment (string)
+* artist (string)
+* track (string)
+* year (integer)
+* genre (string)
+
+The following fields are available in audioProperties, all are integers:
+
+* length
+* bitrate
+* sampleRate
+* channels
+
+Writing audio properties is not supported.
+
### tag(path, callback)
Read the tag from the file at `path` _asynchronously_. The callback should have
@@ -75,16 +102,8 @@ errors occurred, throws an exception.
**NOTE: A Tag object should *NOT* be created using `new`. Instead use `tag()`
or `tagSync()`**
-A Tag object allows access to all the meta-data fields. node-taglib currently
-supports only the fields common to all formats:
-
-* title (string)
-* album (string)
-* comment (string)
-* artist (string)
-* track (string)
-* year (integer)
-* genre (string)
+A Tag object allows _read-write_ access to all the meta-data fields. For valid
+field names see `read()` above.
To get a value, simply access the field -- `tag.artist`.
@@ -96,10 +115,9 @@ have to call `saveSync()`** to actually save the changes to the file on disc.
Due to TagLib's design, every `Tag` object in memory has to keep its backing
file descriptor open. If you are dealing with a large number of files, you will
soon run into problems because operating systems impose limits on how many
-files a process can have open simultaneously. If you only want to read
-meta-data and not write it immediately, then **deep copy** the fields over to
-a plain JS object, then dispose the `Tag` object so that you can operate on
-more files.
+files a process can have open simultaneously. If you want to only read tags,
+use `read()` instead as it will immediately close the file after the tag is
+read.
### Tag.save(callback)
@@ -116,25 +134,6 @@ Save any changes in the Tag meta-data to disk _synchronously_.
Returns whether the tag is empty or not.
-### AudioProperties(path)
-
-**NOTE: This will be replaced by a more functional API, similar to the tags API.**
-
-Object to get the audio properties of file at `path`. Throws an exception on
-errors.
-
- var ap = new taglib.AudioProperties('path');
- console.log("Bitrate", ap.bitrate);
-
-The following fields are available:
-
-* length
-* bitrate
-* sampleRate
-* channels
-
-Writing audio properties is not supported.
-
### taglib.WITH_ASF
A boolean representing whether node-taglib supports ASF files. Depends on
View
@@ -11,7 +11,7 @@ match.find(process.argv[2], {fileFilters: [isMp3]}, function(err, files) {
var count = 0;
console.log(files.length, "files");
async.forEach(files, function(fn, cb) {
- taglib.tag(fn, function(err, tag) {
+ taglib.read(fn, function(err, tag) {
if (err) {
console.log("ERROR");
return cb(false);
View
@@ -2,7 +2,7 @@ var Taglib = require(__dirname+"/../taglib")
var util = require("util")
for (var i = 2; i < process.argv.length; i++) {
- Taglib.tag(process.argv[i], function(err, tag) {
- console.dir(err ? err : tag);
+ Taglib.read(process.argv[i], function(err, tag, props) {
+ console.dir(err ? err : {'tag': tag, 'audioProperties': props});
});
}
View
@@ -80,30 +80,6 @@ vows.describe('taglib bindings')
}
},
- 'reading AudioProperties from non-existent file': {
- topic: function() {
- return function() {
- return new Taglib.AudioProperties('thisfileobviouslyshouldnot.exist');
- }
- },
-
- 'should throw an exception': function(topic) {
- assert.throws(topic, /readable/);
- }
- },
-
- 'reading AudioProperties from a non-audio file': {
- topic: function() {
- return function() {
- return new Taglib.AudioProperties(__filename);
- }
- },
-
- 'should throw an exception': function(topic) {
- assert.throws(topic, /extract audio properties/);
- }
- },
-
'writing Tags to File': {
topic: function() {
var filename = __dirname+'/sample-write.mp3';
@@ -142,25 +118,6 @@ vows.describe('taglib bindings')
}
},
- 'reading Properties from File': {
- topic: new Taglib.AudioProperties(__dirname+'/blip.mp3'),
- 'should be a `AudioProperties`': function (prop) {
- assert.equal(Taglib.AudioProperties, prop.constructor);
- },
- 'should have length 1 second': function(prop) {
- assert.equal(1, prop.length);
- },
- 'should have bitrate 128kbps': function(prop) {
- assert.equal(128, prop.bitrate);
- },
- 'should have sampleRate 44100Hz': function(prop) {
- assert.equal(44100, prop.sampleRate);
- },
- 'should have 2 channels': function(prop) {
- assert.equal(2, prop.channels);
- },
- },
-
'reading Tag from a file asynchronously': {
topic: function() {
Taglib.tag(__dirname+'/sample.mp3', this.callback);
@@ -210,5 +167,69 @@ vows.describe('taglib bindings')
var tag = Taglib.tagSync(filename);
assert.equal(tag.title, "Something completely different…");
}
+ },
+
+ 'reading file metadata asynchronously': {
+ topic: function() {
+ Taglib.read(__dirname+'/sample.mp3', this.callback);
+ },
+
+ 'should be called with three arguments': function (err, tag, props) {
+ assert.isNull(err);
+ assert.isObject(tag);
+ assert.isObject(props);
+ },
+
+ 'reading tags': {
+ topic: function() {
+ Taglib.read(__dirname+'/sample.mp3', this.callback);
+ },
+
+ 'title should be `A bit-bucket full of tags`': function (tag) {
+ assert.equal(tag.title, 'A bit-bucket full of tags');
+ },
+ 'artist should be by `gitzer\'s`': function (tag) {
+ assert.equal(tag.artist, 'gitzer\'s');
+ },
+ 'album should be on `Waffles for free!`': function (tag) {
+ assert.equal(tag.album, "Waffles for free!");
+ },
+ 'track should be the first': function (tag) {
+ assert.equal(tag.track, 1);
+ },
+ 'should be from 2011': function (tag) {
+ assert.equal(tag.year, 2011);
+ },
+ 'should have a silly comment': function(tag) {
+ assert.equal(tag.comment, "Salami Wiglet.");
+ }
+ },
+
+ 'reading audioProperties': {
+ topic: function() {
+ Taglib.read(__dirname+'/blip.mp3', this.callback);
+ },
+
+ 'should have length 1 second': function(err, _, prop) {
+ assert.equal(prop.length, 1);
+ },
+ 'should have bitrate 128kbps': function(err, _, prop) {
+ assert.equal(prop.bitrate, 128);
+ },
+ 'should have sampleRate 44100Hz': function(err, _, prop) {
+ assert.equal(prop.sampleRate, 44100);
+ },
+ 'should have 2 channels': function(err, _, prop) {
+ assert.equal(prop.channels, 2);
+ }
+ },
+ },
+
+ 'read() on non-existent file': {
+ topic: function() {
+ return function() {
+ Taglib.read('thisfileobviouslyshouldnot.exist', this.callback);
+ }
+ },
}
}).export(module);
@@ -1,76 +0,0 @@
-#include "audioproperties.h"
-
-using namespace node_taglib;
-using namespace v8;
-using namespace node;
-
-static Persistent<FunctionTemplate> pft;
-
-void AudioProperties::Initialize(Handle<Object> target)
-{
- HandleScope scope;
-
- Local<FunctionTemplate> t = FunctionTemplate::New(New);
- pft = Persistent<FunctionTemplate>::New(t);
- pft->InstanceTemplate()->SetInternalFieldCount(1);
- pft->SetClassName(String::NewSymbol("AudioProperties"));
-
- pft->InstanceTemplate()->SetAccessor(String::New("length"), GetLength);
- pft->InstanceTemplate()->SetAccessor(String::New("bitrate"), GetBitrate);
- pft->InstanceTemplate()->SetAccessor(String::New("sampleRate"), GetSampleRate);
- pft->InstanceTemplate()->SetAccessor(String::New("channels"), GetChannels);
- target->Set(String::NewSymbol("AudioProperties"), pft->GetFunction());
-}
-
-AudioProperties::AudioProperties(TagLib::FileRef * ffileRef) : properties(ffileRef->audioProperties()), fileRef(ffileRef) { }
-AudioProperties::~AudioProperties() { delete fileRef; }
-
-inline AudioProperties * unwrapAudioProperties(const AccessorInfo& info) {
- return ObjectWrap::Unwrap<AudioProperties>(info.Holder());
-}
-
-Handle<Value> AudioProperties::GetLength(Local<String> property, const AccessorInfo& info) {
- HandleScope scope;
- return scope.Close(Integer::New(unwrapAudioProperties(info)->properties->length()));
-}
-
-Handle<Value> AudioProperties::GetBitrate(Local<String> property, const AccessorInfo& info) {
- HandleScope scope;
- return scope.Close(Integer::New(unwrapAudioProperties(info)->properties->bitrate()));
-}
-
-Handle<Value> AudioProperties::GetSampleRate(Local<String> property, const AccessorInfo& info) {
- HandleScope scope;
- return scope.Close(Integer::New(unwrapAudioProperties(info)->properties->sampleRate()));
-}
-
-Handle<Value> AudioProperties::GetChannels(Local<String> property, const AccessorInfo& info) {
- HandleScope scope;
- return scope.Close(Integer::New(unwrapAudioProperties(info)->properties->channels()));
-}
-
-Handle<Value> AudioProperties::New(const Arguments &args) {
- HandleScope scope;
-
- if (args.Length() < 1 || !args[0]->IsString())
- return ThrowException(String::New("Expected string 'path' as first argument"));
-
- String::Utf8Value path(args[0]->ToString());
-
- if (!TagLib::File::isReadable(*path)) {
- std::string err = "File " + std::string(*path) + " is not readable";
- return ThrowException(String::New(err.c_str(), err.length()));
- }
-
- TagLib::FileRef * f = new TagLib::FileRef(*path);
- if ( f->isNull() || !f->audioProperties() )
- {
- std::string err = "Failed to extract audio properties from " + std::string(*path);
- return ThrowException(String::New(err.c_str(),err.length()));
- }
-
- AudioProperties * properties = new AudioProperties(f);
- properties->Wrap(args.This());
-
- return args.This();
-}
View
@@ -1,26 +0,0 @@
-#ifndef NODE_TAGLIB_AUDIOPROPERTIES_H
-#define NODE_TAGLIB_AUDIOPROPERTIES_H
-
-#include <fileref.h>
-#include <audioproperties.h>
-#include <node.h>
-
-namespace node_taglib {
-class AudioProperties : public node::ObjectWrap {
- TagLib::AudioProperties * properties;
- TagLib::FileRef * fileRef;
-
- public:
- static void Initialize(v8::Handle<v8::Object> target);
- AudioProperties(TagLib::FileRef * fileRef);
- ~AudioProperties();
-
- static v8::Handle<v8::Value> New(const v8::Arguments &args);
-
- static v8::Handle<v8::Value> GetLength(v8::Local<v8::String> property, const v8::AccessorInfo& info);
- static v8::Handle<v8::Value> GetBitrate(v8::Local<v8::String> property, const v8::AccessorInfo& info);
- static v8::Handle<v8::Value> GetSampleRate(v8::Local<v8::String> property, const v8::AccessorInfo& info);
- static v8::Handle<v8::Value> GetChannels(v8::Local<v8::String> property, const v8::AccessorInfo& info);
-};
-}
-#endif
Oops, something went wrong.

0 comments on commit d31ffc4

Please sign in to comment.