Skip to content

Commit

Permalink
Merge branch 'buffers'
Browse files Browse the repository at this point in the history
  • Loading branch information
nikhilm committed May 29, 2012
2 parents c1dc443 + e99682e commit 8e9fdb5
Show file tree
Hide file tree
Showing 10 changed files with 589 additions and 77 deletions.
24 changes: 21 additions & 3 deletions README.md
Expand Up @@ -58,6 +58,7 @@ The `examples` show usage.
## API ## API


### read(path, callback) ### read(path, callback)
### read(buffer, format, callback)


The function you will most likely want to use. `callback` should have signature The function you will most likely want to use. `callback` should have signature
`callback(err, tag, audioProperties)` where `tag` and `audioProperties` are `callback(err, tag, audioProperties)` where `tag` and `audioProperties` are
Expand Down Expand Up @@ -90,7 +91,11 @@ integers:


Writing audio properties is not supported. Writing audio properties is not supported.


In the second variant, which can read from a buffer, `format` should be
a string as specified in [Formats](#formats).

### tag(path, callback) ### tag(path, callback)
### tag(buffer, format, callback)


Read the tag from the file at `path` _asynchronously_. The callback should have Read the tag from the file at `path` _asynchronously_. The callback should have
signature `(err, tag)`. On success, `err` will be `null` and `tag` will be signature `(err, tag)`. On success, `err` will be `null` and `tag` will be
Expand All @@ -99,11 +104,18 @@ a `Tag`. If errors occurred, `err` will contain the error and
integer error code (`errno.h`) and field `message` will have a string integer error code (`errno.h`) and field `message` will have a string
representation. representation.


In the second variant, which can read from a buffer, `format` should be
a string as specified in [Formats](#formats).

### tagSync(path) ### tagSync(path)
### tagSync(buffer, format)


Read the tag from the file at `path` _synchronously_. Returns a `Tag`. If Read the tags from the file at `path` _synchronously_. Returns a `Tag`. If
errors occurred, throws an exception. errors occurred, throws an exception.


Read the tags from `buffer` assuming that it is a `format` file. See
[Formats](#formats)

### Tag ### Tag


**NOTE: A Tag object should *NOT* be created using `new`. Instead use `tag()` **NOTE: A Tag object should *NOT* be created using `new`. Instead use `tag()`
Expand Down Expand Up @@ -135,7 +147,8 @@ will be `null` if the save was successful, otherwise it will be an object with


### Tag.saveSync() ### Tag.saveSync()


Save any changes in the Tag meta-data to disk _synchronously_. Save any changes in the Tag meta-data to disk _synchronously_. Throws an
exception if the save failed.


### Tag.isEmpty() ### Tag.isEmpty()


Expand All @@ -149,7 +162,12 @@ last resolver will be called first. Multiple calls to `addResolvers` are
allowed. allowed.


Each resolver must be a JavaScript function which takes a `filename` parameter Each resolver must be a JavaScript function which takes a `filename` parameter
and returns a format `string`. The string must be one of (case-insensitive): and returns a format `string`. List of [formats](#formats).

### Formats {#formats}

Any place where `node-taglib` expects a format can be passed on of these
(case-insensitive):


"MPEG" "MPEG"
"OGG" - Ogg Vorbis "OGG" - Ogg Vorbis
Expand Down
259 changes: 259 additions & 0 deletions spec/buffersSpec.js
@@ -0,0 +1,259 @@
var assert = require('assert'),
vows = require('vows'),
fs = require('fs'),
Taglib = require(__dirname + '/../taglib');

vows.describe('taglib bindings: Buffers')
.addBatch({
'reading metadata from mp3 buffer': {
topic: function() {
var buf = fs.readFileSync(__dirname+'/sample.mp3');
Taglib.read(buf, 'mpeg', 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() {
var buf = fs.readFileSync(__dirname+'/sample.mp3');
Taglib.read(buf, 'mpeg', 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() {
var buf = fs.readFileSync(__dirname+'/blip.mp3');
Taglib.read(buf, 'mpeg', 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);
}
},
},

'reading data from a buffer with unknown format': {
topic: function() {
var buf = fs.readFileSync(__dirname+'/sample.mp3');
Taglib.read(buf, '', this.callback);
},

'should raise an error': function(err, _, _) {
assert.isNotNull(err);
assert.match(err.message, /Unknown file format/);
}
},

'reading data from a buffer with wrong format': {
topic: function() {
var buf = fs.readFileSync(__dirname+'/sample.mp3');
Taglib.read(buf, 'ogg', this.callback);
},

'should raise an error': function(err, _, _) {
assert.isNotNull(err);
assert.match(err.message, /Failed to extract tags/);
}
},

'reading data from empty buffer': {
topic: function() {
var buf = new Buffer(0);
Taglib.read(buf, 'mpeg', this.callback);
},

'should lead to empty tags and properties': function(err, tag, prop) {
assert.isNull(err);
assert.isEmpty(tag);
assert.isObject(prop);
}
},

/* * T A G * */
'tag metadata from mp3 buffer': {
topic: function() {
var buf = fs.readFileSync(__dirname+'/sample.mp3');
Taglib.tag(buf, 'mpeg', this.callback);
},

'should be called with two arguments': function (err, tag) {
assert.equal(arguments.length, 2);
assert.isNull(err);
assert.isObject(tag);
},

'reading tags': {
topic: function() {
var buf = fs.readFileSync(__dirname+'/sample.mp3');
Taglib.tag(buf, 'mpeg', 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.");
}
}
},

'tag data from a buffer with unknown format': {
topic: function() {
var buf = fs.readFileSync(__dirname+'/sample.mp3');
Taglib.tag(buf, '', this.callback);
},

'should raise an error': function(err, _) {
assert.isNotNull(err);
assert.match(err.message, /Unknown file format/);
}
},

'tag data from a buffer with wrong format': {
topic: function() {
var buf = fs.readFileSync(__dirname+'/sample.mp3');
Taglib.tag(buf, 'ogg', this.callback);
},

'should raise an error': function(err, _) {
assert.isNotNull(err);
assert.match(err.message, /Failed to extract tags/);
}
},

'tag data from empty buffer': {
topic: function() {
var buf = new Buffer(0);
Taglib.tag(buf, 'mpeg', this.callback);
},

'should lead to empty tags and properties': function(err, tag) {
assert.isNull(err);
assert.isObject(tag);
}
},


/* * T A G S Y N C * */
'tagSync metadata from mp3 buffer': {
topic: function() {
var buf = fs.readFileSync(__dirname+'/sample.mp3');
return Taglib.tagSync(buf, 'mpeg');
},

'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.");
}
},

'tagSync data from a buffer with unknown format': {
topic: function() {
return function() {
var buf = fs.readFileSync(__dirname+'/sample.mp3');
return Taglib.tagSync(buf, '', this.callback);
}
},

'should raise an error': function(topic) {
assert.throws(topic, /Unknown file format/);
}
},

'tagSync data from a buffer with wrong format': {
topic: function() {
return function() {
var buf = fs.readFileSync(__dirname+'/sample.mp3');
return Taglib.tagSync(buf, 'ogg');
}
},

'should raise an error': function(topic) {
assert.throws(topic, /Failed to extract tags/);
}
},

'tagSync data from empty buffer': {
topic: function() {
var buf = new Buffer(0);
return Taglib.tagSync(buf, 'mpeg');
},

'should lead to empty tags': function(tag) {
assert.isObject(tag);
}
},

'writing to a tag from a buffer': {
topic: function() {
return function() {
var buf = fs.readFileSync(__dirname+'/sample.mp3');
var tag = Taglib.tagSync(buf, 'mpeg');
tag.artist = 'nsm';
tag.saveSync();
}
},

'should fail': function(topic) {
assert.throws(topic);
}
}
}).export(module);
72 changes: 72 additions & 0 deletions src/bufferstream.cc
@@ -0,0 +1,72 @@
#include "bufferstream.h"

#include <node_buffer.h>

#include "taglib.h"

using namespace v8;
using namespace node;
using namespace node_taglib;

namespace node_taglib {
BufferStream::BufferStream(Handle<Object> buffer)
: TagLib::IOStream()
, m_data(Buffer::Data(buffer))
, m_length(Buffer::Length(buffer))
, m_offset(0)
{
}

BufferStream::~BufferStream()
{
}

TagLib::ByteVector BufferStream::readBlock(TagLib::ulong length) {
long start = m_offset;
m_offset += length;
return TagLib::ByteVector(m_data, m_length).mid(start, length);
}

void BufferStream::writeBlock(const TagLib::ByteVector &data) {
fprintf(stderr, "writeBlock called aborting\n");
abort();
}

void BufferStream::insert(const TagLib::ByteVector &data, TagLib::ulong start, TagLib::ulong replace) {
fprintf(stderr, "insert called aborting\n");
abort();
}

void BufferStream::removeBlock(TagLib::ulong start, TagLib::ulong length) {
fprintf(stderr, "removeBlock called aborting\n");
abort();
}

void BufferStream::seek(long offset, TagLib::IOStream::Position p) {
if (p == TagLib::IOStream::Beginning) {
m_offset = offset;
}
else if (p == TagLib::IOStream::Current) {
m_offset += offset;
}
else if (p == TagLib::IOStream::End) {
m_offset = length() + offset;
}
}

void BufferStream::clear() {
}

long BufferStream::tell() const {
return m_offset;
}

long BufferStream::length() {
return m_length;
}

void BufferStream::truncate(long length) {
fprintf(stderr, "truncate called aborting\n");
abort();
}
}

0 comments on commit 8e9fdb5

Please sign in to comment.