diff --git a/binding.gyp b/binding.gyp index 3211dd9..48c4875 100644 --- a/binding.gyp +++ b/binding.gyp @@ -4,6 +4,7 @@ "target_name": "taglib", "sources": ["src/bufferstream.c", "src/tag.cc", "src/taglib.cc"], "libraries": ["=0.6.0", - "async" : ">=0.1.0", - "match-files" : "latest" + "vows": ">=0.6.0", + "async": ">=0.1.0", + "match-files": "latest" }, "scripts": { "test": "vows --spec" diff --git a/src/tag.cc b/src/tag.cc index 4c14333..3240346 100644 --- a/src/tag.cc +++ b/src/tag.cc @@ -14,32 +14,27 @@ using namespace node; namespace node_taglib { -static Persistent TagTemplate; - -void Tag::Initialize(Handle target) -{ - HandleScope scope; - - TagTemplate = Persistent::New(FunctionTemplate::New()); - - TagTemplate->InstanceTemplate()->SetInternalFieldCount(1); - TagTemplate->SetClassName(String::NewSymbol("Tag")); - - NODE_SET_PROTOTYPE_METHOD(TagTemplate, "save", AsyncSaveTag); - NODE_SET_PROTOTYPE_METHOD(TagTemplate, "saveSync", SyncSaveTag); - NODE_SET_PROTOTYPE_METHOD(TagTemplate, "isEmpty", IsEmpty); - - TagTemplate->InstanceTemplate()->SetAccessor(String::New("title"), GetTitle, SetTitle); - TagTemplate->InstanceTemplate()->SetAccessor(String::New("album"), GetAlbum, SetAlbum); - TagTemplate->InstanceTemplate()->SetAccessor(String::New("comment"), GetComment, SetComment); - TagTemplate->InstanceTemplate()->SetAccessor(String::New("artist"), GetArtist, SetArtist); - TagTemplate->InstanceTemplate()->SetAccessor(String::New("track"), GetTrack, SetTrack); - TagTemplate->InstanceTemplate()->SetAccessor(String::New("year"), GetYear, SetYear); - TagTemplate->InstanceTemplate()->SetAccessor(String::New("genre"), GetGenre, SetGenre); - - target->Set(String::NewSymbol("Tag"), TagTemplate->GetFunction()); - NODE_SET_METHOD(target, "tag", AsyncTag); - NODE_SET_METHOD(target, "tagSync", SyncTag); +NAN_MODULE_INIT(Tag::Init) { + v8::Local tpl = Nan::New(); + tpl->SetClassName(Nan::New("Tag").ToLocalChecked()); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + + Nan::SetPrototypeMethod(tpl, "save", AsyncSaveTag); + Nan::SetPrototypeMethod(tpl, "saveSync", SyncSaveTag); + Nan::SetPrototypeMethod(tpl, "isEmpty", IsEmpty); + + Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("title").ToLocalChecked(), GetTitle, SetTitle); + Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("album").ToLocalChecked(), GetAlbum, SetAlbum); + Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("comment").ToLocalChecked(), GetComment, SetComment); + Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("artist").ToLocalChecked(), GetArtist, SetArtist); + Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("track").ToLocalChecked(), GetTrack, SetTrack); + Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("year").ToLocalChecked(), GetYear, SetYear); + Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("genre").ToLocalChecked(), GetGenre, SetGenre); + + constructor().Reset(Nan::GetFunction(tpl).ToLocalChecked()); + Nan::Set(target, Nan::New("Tag").ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked()); + Nan::SetMethod(target, "tag", AsyncTag); + Nan::SetMethod(target, "tagSync", SyncTag); } Tag::Tag(TagLib::FileRef * ffileRef) : tag(ffileRef->tag()), fileRef(ffileRef) { } @@ -51,152 +46,185 @@ Tag::~Tag() { tag = NULL; } -inline Tag * unwrapTag(const AccessorInfo& info) { - return ObjectWrap::Unwrap(info.Holder()); +inline Tag * unwrapTag(const Nan::PropertyCallbackInfo& info) { + return Nan::ObjectWrap::Unwrap(info.Holder()); } - -Handle Tag::GetTitle(Local property, const AccessorInfo& info) { - HandleScope scope; - return scope.Close(TagLibStringToString(unwrapTag(info)->tag->title())); +inline Tag * unwrapTag(const Nan::PropertyCallbackInfo& info) { + return Nan::ObjectWrap::Unwrap(info.Holder()); } -void Tag::SetTitle(Local property, Local value, const AccessorInfo& info) { - HandleScope scope; - unwrapTag(info)->tag->setTitle(NodeStringToTagLibString(value)); -} -Handle Tag::GetArtist(Local property, const AccessorInfo& info) { - HandleScope scope; - return scope.Close(TagLibStringToString(unwrapTag(info)->tag->artist())); +NAN_GETTER(Tag::GetTitle) { + info.GetReturnValue().Set(TagLibStringToString(unwrapTag(info)->tag->title())); } - -void Tag::SetArtist(Local property, Local value, const AccessorInfo& info) { - HandleScope scope; - unwrapTag(info)->tag->setArtist(NodeStringToTagLibString(value)); +NAN_SETTER(Tag::SetTitle) { + unwrapTag(info)->tag->setTitle(NodeStringToTagLibString(value)); } -Handle Tag::GetAlbum(Local property, const AccessorInfo& info) { - HandleScope scope; - return scope.Close(TagLibStringToString(unwrapTag(info)->tag->album())); +NAN_GETTER(Tag::GetArtist) { + info.GetReturnValue().Set(TagLibStringToString(unwrapTag(info)->tag->artist())); } - -void Tag::SetAlbum(Local property, Local value, const AccessorInfo& info) { - HandleScope scope; - unwrapTag(info)->tag->setAlbum(NodeStringToTagLibString(value)); +NAN_SETTER(Tag::SetArtist) { + unwrapTag(info)->tag->setArtist(NodeStringToTagLibString(value)); } -Handle Tag::GetComment(Local property, const AccessorInfo& info) { - HandleScope scope; - return scope.Close(TagLibStringToString(unwrapTag(info)->tag->comment())); +NAN_GETTER(Tag::GetAlbum) { + info.GetReturnValue().Set(TagLibStringToString(unwrapTag(info)->tag->album())); } - -void Tag::SetComment(Local property, Local value, const AccessorInfo& info) { - HandleScope scope; - unwrapTag(info)->tag->setComment(NodeStringToTagLibString(value)); +NAN_SETTER(Tag::SetAlbum) { + unwrapTag(info)->tag->setAlbum(NodeStringToTagLibString(value)); } -Handle Tag::GetTrack(Local property, const AccessorInfo& info) { - HandleScope scope; - return scope.Close(Integer::New(unwrapTag(info)->tag->track())); +NAN_GETTER(Tag::GetComment) { + info.GetReturnValue().Set(TagLibStringToString(unwrapTag(info)->tag->comment())); +} +NAN_SETTER(Tag::SetComment) { + unwrapTag(info)->tag->setComment(NodeStringToTagLibString(value)); } -void Tag::SetTrack(Local property, Local value, const AccessorInfo& info) { - HandleScope scope; - unwrapTag(info)->tag->setTrack(value->IntegerValue()); +NAN_GETTER(Tag::GetTrack) { + info.GetReturnValue().Set(unwrapTag(info)->tag->track()); +} +NAN_SETTER(Tag::SetTrack) { + unwrapTag(info)->tag->setTrack(value->IntegerValue()); } -Handle Tag::GetYear(Local property, const AccessorInfo& info) { - HandleScope scope; - return scope.Close(Integer::New(unwrapTag(info)->tag->year())); +NAN_GETTER(Tag::GetYear) { + info.GetReturnValue().Set(unwrapTag(info)->tag->year()); +} +NAN_SETTER(Tag::SetYear) { + unwrapTag(info)->tag->setYear(value->IntegerValue()); } -void Tag::SetYear(Local property, Local value, const AccessorInfo& info) { - HandleScope scope; - unwrapTag(info)->tag->setYear(value->IntegerValue()); +NAN_GETTER(Tag::GetGenre) { + info.GetReturnValue().Set(TagLibStringToString(unwrapTag(info)->tag->genre())); +} +NAN_SETTER(Tag::SetGenre) { + unwrapTag(info)->tag->setGenre(NodeStringToTagLibString(value)); } -Handle Tag::GetGenre(Local property, const AccessorInfo& info) { - HandleScope scope; - return scope.Close(TagLibStringToString(unwrapTag(info)->tag->genre())); +NAN_METHOD(Tag::IsEmpty) { + Tag *t = ObjectWrap::Unwrap(info.Holder()); + info.GetReturnValue().Set(t->tag->isEmpty()); } -void Tag::SetGenre(Local property, Local value, const AccessorInfo& info) { - HandleScope scope; - unwrapTag(info)->tag->setGenre(NodeStringToTagLibString(value)); +NAN_METHOD(Tag::SyncSaveTag) { + Tag *t = ObjectWrap::Unwrap(info.Holder()); + assert(t->fileRef); + bool success = t->fileRef->save(); + if (success) + info.GetReturnValue().SetUndefined(); + else + Nan::ThrowError("Failed to save file."); + //TODO: filename } -Handle Tag::IsEmpty(const Arguments &args) { - HandleScope scope; - Tag *t = ObjectWrap::Unwrap(args.This()); - return Boolean::New(t->tag->isEmpty()); +NAN_METHOD(Tag::AsyncSaveTag) { + if (info.Length() >= 1 && !info[0]->IsFunction()) { + Nan::ThrowError("Expected callback function as first argument"); + return; + } + + Local callback = Local::Cast(info[0]); + + Tag *t = ObjectWrap::Unwrap(info.Holder()); + + AsyncBaton *baton = new AsyncBaton; + baton->request.data = baton; + baton->tag = t; + baton->callback.Reset(callback); + baton->error = 1; + + uv_queue_work(uv_default_loop(), &baton->request, Tag::AsyncSaveTagDo, (uv_after_work_cb)Tag::AsyncSaveTagAfter); + + info.GetReturnValue().SetUndefined(); } -Handle Tag::SyncSaveTag(const Arguments &args) { - HandleScope scope; - Tag *t = ObjectWrap::Unwrap(args.This()); - assert(t->fileRef); - bool success = t->fileRef->save(); - if (success) - return Undefined(); - else - return ThrowException(String::Concat( - String::New("Failed to save file: "), - String::New(t->fileRef->file()->name()) - )); +void Tag::AsyncSaveTagDo(uv_work_t *req) { + AsyncBaton *baton = static_cast(req->data); + + assert(baton->tag->fileRef); + baton->error = !baton->tag->fileRef->save(); } -Handle Tag::SyncTag(const Arguments &args) { - HandleScope scope; +void Tag::AsyncSaveTagAfter(uv_work_t *req) { + Isolate* isolate = Isolate::GetCurrent(); + HandleScope scope(isolate); + + AsyncBaton *baton = static_cast(req->data); + + if (baton->error) { + Local error = Nan::New(); + error->Set(Nan::New("message").ToLocalChecked(), Nan::New("Failed to save file").ToLocalChecked()); + error->Set(Nan::New("path").ToLocalChecked(), Nan::New(baton->tag->fileRef->file()->name()).ToLocalChecked()); + Handle argv[] = { error }; + Nan::Call(Nan::New(baton->callback), Nan::GetCurrentContext()->Global(), 1, argv); + } + else { + Handle argv[] = { Nan::Null() }; + Nan::Call(Nan::New(baton->callback), Nan::GetCurrentContext()->Global(), 1, argv); + } + baton->callback.Reset(); + delete baton; +} + +NAN_METHOD(Tag::SyncTag) { TagLib::FileRef *f = 0; int error = 0; - if (args.Length() >= 1 && args[0]->IsString()) { - String::Utf8Value path(args[0]->ToString()); + if (info.Length() >= 1 && info[0]->IsString()) { + String::Utf8Value path(info[0]->ToString()); if ((error = CreateFileRefPath(*path, &f))) { - Local fn = String::Concat(args[0]->ToString(), Local::Cast(String::New(": ", -1))); - return ThrowException(String::Concat(fn, ErrorToString(error))); + Local fn = String::Concat(info[0]->ToString(), Nan::New(": ", -1).ToLocalChecked()); + Nan::ThrowError(String::Concat(fn, ErrorToString(error))); + return; + } + } else if (info.Length() >= 1 && Buffer::HasInstance(info[0])) { + if (info.Length() < 2 || !info[1]->IsString()) { + Nan::ThrowError("Expected string 'format' as second argument"); + return; } - } - else if (args.Length() >= 1 && Buffer::HasInstance(args[0])) { - if (args.Length() < 2 || !args[1]->IsString()) - return ThrowException(String::New("Expected string 'format' as second argument")); - if ((error = CreateFileRef(new BufferStream(args[0]->ToObject()), NodeStringToTagLibString(args[1]->ToString()), &f))) { - return ThrowException(ErrorToString(error)); + if ((error = CreateFileRef(new BufferStream(info[0]->ToObject()), NodeStringToTagLibString(info[1]->ToString()), &f))) { + Nan::ThrowError(ErrorToString(error)); + return; } - } - else { - return ThrowException(String::New("Expected string or buffer as first argument")); + } else { + Nan::ThrowError("Expected string or buffer as first argument"); + return; } Tag * tag = new Tag(f); - Handle inst = TagTemplate->InstanceTemplate()->NewInstance(); + Local inst = Nan::NewInstance(Nan::New(constructor())).ToLocalChecked(); tag->Wrap(inst); - return scope.Close(inst); + info.GetReturnValue().Set(inst); } -v8::Handle Tag::AsyncTag(const v8::Arguments &args) { - HandleScope scope; - - if (args.Length() < 1) { - return ThrowException(String::New("Expected string or buffer as first argument")); +NAN_METHOD(Tag::AsyncTag) { + if (info.Length() < 1) { + Nan::ThrowError("Expected string or buffer as first argument"); + return; } - if (args[0]->IsString()) { - if (args.Length() < 2 || !args[1]->IsFunction()) - return ThrowException(String::New("Expected callback function as second argument")); - - } - else if (Buffer::HasInstance(args[0])) { - if (args.Length() < 2 || !args[1]->IsString()) - return ThrowException(String::New("Expected string 'format' as second argument")); - if (args.Length() < 3 || !args[2]->IsFunction()) - return ThrowException(String::New("Expected callback function as third argument")); - } - else { - return ThrowException(String::New("Expected string or buffer as first argument")); + if (info[0]->IsString()) { + if (info.Length() < 2 || !info[1]->IsFunction()) { + Nan::ThrowError("Expected callback function as second argument"); + return; + } + } else if (Buffer::HasInstance(info[0])) { + if (info.Length() < 2 || !info[1]->IsString()) { + Nan::ThrowError("Expected string 'format' as second argument"); + return; + } + if (info.Length() < 3 || !info[2]->IsFunction()) { + Nan::ThrowError("Expected callback function as third argument"); + return; + } + } else { + Nan::ThrowError("Expected string or buffer as first argument"); + return; } @@ -206,21 +234,21 @@ v8::Handle Tag::AsyncTag(const v8::Arguments &args) { baton->tag = NULL; baton->error = 0; - if (args[0]->IsString()) { - String::Utf8Value path(args[0]->ToString()); + if (info[0]->IsString()) { + String::Utf8Value path(info[0]->ToString()); baton->path = strdup(*path); - baton->callback = Persistent::New(Local::Cast(args[1])); + baton->callback.Reset(Local::Cast(info[1])); } else { - baton->format = NodeStringToTagLibString(args[1]->ToString()); - baton->stream = new BufferStream(args[0]->ToObject()); - baton->callback = Persistent::New(Local::Cast(args[2])); + baton->format = NodeStringToTagLibString(info[1]->ToString()); + baton->stream = new BufferStream(info[0]->ToObject()); + baton->callback.Reset(Local::Cast(info[2])); } uv_queue_work(uv_default_loop(), &baton->request, Tag::AsyncTagReadDo, (uv_after_work_cb)Tag::AsyncTagReadAfter); - return Undefined(); + info.GetReturnValue().SetUndefined(); } void Tag::AsyncTagReadDo(uv_work_t *req) { @@ -242,76 +270,27 @@ void Tag::AsyncTagReadDo(uv_work_t *req) { } void Tag::AsyncTagReadAfter(uv_work_t *req) { - HandleScope scope; + Isolate* isolate = Isolate::GetCurrent(); + HandleScope scope(isolate); AsyncBaton *baton = static_cast(req->data); if (baton->error) { - Local error = Object::New(); - error->Set(String::New("code"), Integer::New(baton->error)); - error->Set(String::New("message"), ErrorToString(baton->error)); - Handle argv[] = { error, Null() }; - baton->callback->Call(Context::GetCurrent()->Global(), 2, argv); + Local error = Nan::New(); + error->Set(Nan::New("code").ToLocalChecked(), Nan::New(baton->error)); + error->Set(Nan::New("message").ToLocalChecked(), ErrorToString(baton->error)); + Handle argv[] = { error, Nan::Null() }; + Nan::Call(Nan::New(baton->callback), Nan::GetCurrentContext()->Global(), 2, argv); } else { - Persistent inst = Persistent::New(TagTemplate->InstanceTemplate()->NewInstance()); + Local inst = Nan::NewInstance(Nan::New(constructor())).ToLocalChecked(); baton->tag->Wrap(inst); - Handle argv[] = { Null(), inst }; - baton->callback->Call(Context::GetCurrent()->Global(), 2, argv); + Handle argv[] = { Nan::Null(), inst }; + Nan::Call(Nan::New(baton->callback), Nan::GetCurrentContext()->Global(), 2, argv); } - baton->callback.Dispose(); + baton->callback.Reset(); delete baton->path; delete baton; } - -v8::Handle Tag::AsyncSaveTag(const v8::Arguments &args) { - HandleScope scope; - - if (args.Length() >= 1 && !args[0]->IsFunction()) - return ThrowException(String::New("Expected callback function as first argument")); - - Local callback = Local::Cast(args[0]); - - Tag *t = ObjectWrap::Unwrap(args.This()); - - AsyncBaton *baton = new AsyncBaton; - baton->request.data = baton; - baton->tag = t; - baton->callback = Persistent::New(callback); - baton->error = 1; - - uv_queue_work(uv_default_loop(), &baton->request, Tag::AsyncSaveTagDo, (uv_after_work_cb)Tag::AsyncSaveTagAfter); - - return Undefined(); -} - -void Tag::AsyncSaveTagDo(uv_work_t *req) { - AsyncBaton *baton = static_cast(req->data); - - assert(baton->tag->fileRef); - baton->error = !baton->tag->fileRef->save(); -} - -void Tag::AsyncSaveTagAfter(uv_work_t *req) { - HandleScope scope; - - AsyncBaton *baton = static_cast(req->data); - - if (baton->error) { - Local error = Object::New(); - error->Set(String::New("message"), String::New("Failed to save file")); - error->Set(String::New("path"), String::New(baton->tag->fileRef->file()->name())); - Handle argv[] = { error }; - baton->callback->Call(Context::GetCurrent()->Global(), 1, argv); - } - else { - Handle argv[] = { Null() }; - baton->callback->Call(Context::GetCurrent()->Global(), 1, argv); - } - - baton->callback.Dispose(); - delete baton; -} - } diff --git a/src/tag.h b/src/tag.h index f327b21..1c890d8 100644 --- a/src/tag.h +++ b/src/tag.h @@ -1,48 +1,61 @@ #ifndef NODE_TAGLIB_TAG_H #define NODE_TAGLIB_TAG_H +#include #include #include +#include #include +#include +#include + namespace node_taglib { -class Tag : public node::ObjectWrap { - TagLib::Tag * tag; - TagLib::FileRef * fileRef; +class Tag : public Nan::ObjectWrap { + +private: + TagLib::Tag * tag; + TagLib::FileRef *fileRef; //static v8::Persistent pft; - public: - static void Initialize(v8::Handle target); +public: + static NAN_MODULE_INIT(Init); Tag(TagLib::FileRef * fileRef); ~Tag(); + + static inline Nan::Persistent & constructor() { + static Nan::Persistent my_constructor; + return my_constructor; + } - static v8::Handle GetTitle(v8::Local property, const v8::AccessorInfo& info); - static void SetTitle(v8::Local property, v8::Local value, const v8::AccessorInfo& info); - - static v8::Handle GetArtist(v8::Local property, const v8::AccessorInfo& info); - static void SetArtist(v8::Local property, v8::Local value, const v8::AccessorInfo& info); - - static v8::Handle GetAlbum(v8::Local property, const v8::AccessorInfo& info); - static void SetAlbum(v8::Local property, v8::Local value, const v8::AccessorInfo& info); - - static v8::Handle GetYear(v8::Local property, const v8::AccessorInfo& info); - static void SetYear(v8::Local property, v8::Local value, const v8::AccessorInfo& info); - - static v8::Handle GetComment(v8::Local property, const v8::AccessorInfo& info); - static void SetComment(v8::Local property, v8::Local value, const v8::AccessorInfo& info); - - static v8::Handle GetTrack(v8::Local property, const v8::AccessorInfo& info); - static void SetTrack(v8::Local property, v8::Local value, const v8::AccessorInfo& info); - - static v8::Handle GetGenre(v8::Local property, const v8::AccessorInfo& info); - static void SetGenre(v8::Local property, v8::Local value, const v8::AccessorInfo& info); - - static v8::Handle IsEmpty(const v8::Arguments &args); - static v8::Handle AsyncSaveTag(const v8::Arguments &args); - static v8::Handle SyncSaveTag(const v8::Arguments &args); - static v8::Handle SyncTag(const v8::Arguments &args); - static v8::Handle AsyncTag(const v8::Arguments &args); + static NAN_GETTER(GetTitle); + static NAN_SETTER(SetTitle); + + static NAN_GETTER(GetArtist); + static NAN_SETTER(SetArtist); + + static NAN_GETTER(GetAlbum); + static NAN_SETTER(SetAlbum); + + static NAN_GETTER(GetYear); + static NAN_SETTER(SetYear); + + static NAN_GETTER(GetComment); + static NAN_SETTER(SetComment); + + static NAN_GETTER(GetTrack); + static NAN_SETTER(SetTrack); + + static NAN_GETTER(GetGenre); + static NAN_SETTER(SetGenre); + + static NAN_METHOD(IsEmpty); + static NAN_METHOD(AsyncSaveTag); + static NAN_METHOD(SyncSaveTag); + static NAN_METHOD(SyncTag); + static NAN_METHOD(AsyncTag); + static void AsyncTagReadDo(uv_work_t *req); static void AsyncTagReadAfter(uv_work_t *req); static void AsyncSaveTagDo(uv_work_t *req); diff --git a/src/taglib.cc b/src/taglib.cc index b22dd6b..5f7bd14 100644 --- a/src/taglib.cc +++ b/src/taglib.cc @@ -130,8 +130,7 @@ TagLib::File *createFile(TagLib::IOStream *stream, TagLib::String format) { return file; } -Handle ErrorToString(int error) { - HandleScope scope; +Local< String > ErrorToString(int error) { std::string err; switch (error) { @@ -152,29 +151,37 @@ Handle ErrorToString(int error) { break; } - return scope.Close(String::New(err.c_str(), err.length())); + return Nan::New(err).ToLocalChecked(); } -v8::Handle AsyncReadFile(const v8::Arguments &args) { - HandleScope scope; +void AsyncReadFile(const Nan::FunctionCallbackInfo< v8::Value >& args) { + Isolate* isolate = Isolate::GetCurrent(); + HandleScope scope(isolate); if (args.Length() < 1) { - return ThrowException(String::New("Expected string or buffer as first argument")); + Nan::ThrowError("Expected string or buffer as first argument"); + return; } if (args[0]->IsString()) { - if (args.Length() < 2 || !args[1]->IsFunction()) - return ThrowException(String::New("Expected callback function as second argument")); - + if (args.Length() < 2 || !args[1]->IsFunction()) { + Nan::ThrowError("Expected callback function as second argument"); + return; + } } else if (Buffer::HasInstance(args[0])) { - if (args.Length() < 2 || !args[1]->IsString()) - return ThrowException(String::New("Expected string 'format' as second argument")); - if (args.Length() < 3 || !args[2]->IsFunction()) - return ThrowException(String::New("Expected callback function as third argument")); + if (args.Length() < 2 || !args[1]->IsString()) { + Nan::ThrowError("Expected string 'format' as second argument"); + return; + } + if (args.Length() < 3 || !args[2]->IsFunction()) { + Nan::ThrowError("Expected callback function as third argument"); + return; + } } else { - return ThrowException(String::New("Expected string or buffer as first argument")); + Nan::ThrowError("Expected string or buffer as first argument"); + return; } AsyncBaton *baton = new AsyncBaton; @@ -187,18 +194,18 @@ v8::Handle AsyncReadFile(const v8::Arguments &args) { if (args[0]->IsString()) { String::Utf8Value path(args[0]->ToString()); baton->path = strdup(*path); - baton->callback = Persistent::New(Local::Cast(args[1])); + baton->callback.Reset(Local::Cast(args[1])); } else { baton->format = NodeStringToTagLibString(args[1]->ToString()); baton->stream = new BufferStream(args[0]->ToObject()); - baton->callback = Persistent::New(Local::Cast(args[2])); + baton->callback.Reset(Local::Cast(args[2])); } uv_queue_work(uv_default_loop(), &baton->request, AsyncReadFileDo, (uv_after_work_cb)AsyncReadFileAfter); - return Undefined(); + args.GetReturnValue().SetUndefined(); } void AsyncReadFileDo(uv_work_t *req) { @@ -222,37 +229,37 @@ void AsyncReadFileDo(uv_work_t *req) { void AsyncReadFileAfter(uv_work_t *req) { AsyncBaton *baton = static_cast(req->data); if (baton->error) { - Local error = Object::New(); - error->Set(String::New("code"), Integer::New(baton->error)); - error->Set(String::New("message"), ErrorToString(baton->error)); - Handle argv[] = { error, Null(), Null() }; - baton->callback->Call(Context::GetCurrent()->Global(), 3, argv); + Local error = Nan::New(); + error->Set(Nan::New("code").ToLocalChecked(), Nan::New(baton->error)); + error->Set(Nan::New("message").ToLocalChecked(), ErrorToString(baton->error)); + Handle argv[] = { error, Nan::Null(), Nan::Null() }; + Nan::Call(Nan::New(baton->callback), Nan::GetCurrentContext()->Global(), 3, argv); } else { // read the data, put it in objects and delete the fileref TagLib::Tag *tag = baton->fileRef->tag(); - Local tagObj = Object::New(); + Local tagObj = Nan::New(); if (!tag->isEmpty()) { - tagObj->Set(String::New("album"), TagLibStringToString(tag->album())); - tagObj->Set(String::New("artist"), TagLibStringToString(tag->artist())); - tagObj->Set(String::New("comment"), TagLibStringToString(tag->comment())); - tagObj->Set(String::New("genre"), TagLibStringToString(tag->genre())); - tagObj->Set(String::New("title"), TagLibStringToString(tag->title())); - tagObj->Set(String::New("track"), Integer::New(tag->track())); - tagObj->Set(String::New("year"), Integer::New(tag->year())); + tagObj->Set(Nan::New("album").ToLocalChecked(), TagLibStringToString(tag->album())); + tagObj->Set(Nan::New("artist").ToLocalChecked(), TagLibStringToString(tag->artist())); + tagObj->Set(Nan::New("comment").ToLocalChecked(), TagLibStringToString(tag->comment())); + tagObj->Set(Nan::New("genre").ToLocalChecked(), TagLibStringToString(tag->genre())); + tagObj->Set(Nan::New("title").ToLocalChecked(), TagLibStringToString(tag->title())); + tagObj->Set(Nan::New("track").ToLocalChecked(), Nan::New(tag->track())); + tagObj->Set(Nan::New("year").ToLocalChecked(), Nan::New(tag->year())); } TagLib::AudioProperties *props = baton->fileRef->audioProperties(); - Local propsObj = Object::New(); + Local propsObj = Nan::New(); if (props) { - propsObj->Set(String::New("length"), Integer::New(props->length())); - propsObj->Set(String::New("bitrate"), Integer::New(props->bitrate())); - propsObj->Set(String::New("sampleRate"), Integer::New(props->sampleRate())); - propsObj->Set(String::New("channels"), Integer::New(props->channels())); + propsObj->Set(Nan::New("length").ToLocalChecked(), Nan::New(props->length())); + propsObj->Set(Nan::New("bitrate").ToLocalChecked(), Nan::New(props->bitrate())); + propsObj->Set(Nan::New("sampleRate").ToLocalChecked(), Nan::New(props->sampleRate())); + propsObj->Set(Nan::New("channels").ToLocalChecked(), Nan::New(props->channels())); } - Handle argv[] = { Null(), tagObj, propsObj }; - baton->callback->Call(Context::GetCurrent()->Global(), 3, argv); + Handle argv[] = { Nan::Null(), tagObj, propsObj }; + Nan::Call(Nan::New(baton->callback), Nan::GetCurrentContext()->Global(), 3, argv); delete baton->fileRef; delete baton; @@ -260,16 +267,16 @@ void AsyncReadFileAfter(uv_work_t *req) { } } -Handle TagLibStringToString( TagLib::String s ) +Local< Value > TagLibStringToString( TagLib::String s ) { if(s.isEmpty()) { - return Null(); + return Nan::Null(); } else { TagLib::ByteVector str = s.data(TagLib::String::UTF16); // Strip the Byte Order Mark of the input to avoid node adding a UTF-8 // Byte Order Mark - return String::New((uint16_t *)str.mid(2,str.size()-2).data(), s.size()); + return Nan::New((uint16_t *)str.mid(2,str.size()-2).data(), s.size()).ToLocalChecked(); } } @@ -284,19 +291,19 @@ TagLib::String NodeStringToTagLibString( Local s ) } } -Handle AddResolvers(const Arguments &args) +void AddResolvers(const Nan::FunctionCallbackInfo &args) { for (int i = 0; i < args.Length(); i++) { Local arg = args[i]; if (arg->IsFunction()) { - Persistent resolver = Persistent::New(Local::Cast(arg)); + Local resolver = Local::Cast(arg); TagLib::FileRef::addFileTypeResolver(new CallbackResolver(resolver)); } } - return Undefined(); + args.GetReturnValue().SetUndefined(); } -CallbackResolver::CallbackResolver(Persistent func) +CallbackResolver::CallbackResolver(Local< Function > func) : TagLib::FileRef::FileTypeResolver() , resolverFunc(func) // the constructor is always called in the v8 thread @@ -308,7 +315,7 @@ CallbackResolver::CallbackResolver(Persistent func) { } -void CallbackResolver::invokeResolverCb(uv_async_t *handle, int status) +void CallbackResolver::invokeResolverCb(uv_async_t *handle) { AsyncResolverBaton *baton = (AsyncResolverBaton *) handle->data; invokeResolver(baton); @@ -316,16 +323,16 @@ void CallbackResolver::invokeResolverCb(uv_async_t *handle, int status) uv_close((uv_handle_t*)&baton->request, 0); } -void CallbackResolver::stopIdling(uv_async_t *handle, int status) +void CallbackResolver::stopIdling(uv_async_t *handle) { uv_close((uv_handle_t*) handle, 0); } void CallbackResolver::invokeResolver(AsyncResolverBaton *baton) { - HandleScope scope; + Nan::HandleScope scope; Handle argv[] = { TagLibStringToString(baton->fileName) }; - Local ret = baton->resolver->resolverFunc->Call(Context::GetCurrent()->Global(), 1, argv); + Local ret = Nan::Call(Nan::New(baton->resolver->resolverFunc), Nan::GetCurrentContext()->Global(), 1, argv).ToLocalChecked(); if (!ret->IsString()) { baton->type = TagLib::String::null; } @@ -369,25 +376,25 @@ extern "C" { static void init (Handle target) { - HandleScope scope; + Nan::HandleScope scope; #ifdef TAGLIB_WITH_ASF - target->Set(String::NewSymbol("WITH_ASF"), v8::True()); + Nan::Set(target, Nan::New("WITH_ASF").ToLocalChecked(), Nan::True()); #else - target->Set(String::NewSymbol("WITH_ASF"), v8::False()); + Nan::Set(target, Nan::New("WITH_ASF").ToLocalChecked(), Nan::False()); #endif #ifdef TAGLIB_WITH_MP4 - target->Set(String::NewSymbol("WITH_MP4"), v8::True()); + Nan::Set(target, Nan::New("WITH_MP4").ToLocalChecked(), Nan::True()); #else - target->Set(String::NewSymbol("WITH_MP4"), v8::False()); + Nan::Set(target, Nan::New("WITH_MP4").ToLocalChecked(), Nan::False()); #endif - NODE_SET_METHOD(target, "read", AsyncReadFile); + Nan::SetMethod(target, "read", AsyncReadFile); #ifdef ENABLE_RESOLVERS - NODE_SET_METHOD(target, "addResolvers", AddResolvers); + Nan::SetMethod(target, "addResolvers", AddResolvers); #endif - Tag::Initialize(target); + Tag::Init(target); } NODE_MODULE(taglib, init) diff --git a/src/taglib.h b/src/taglib.h index 1fa7b45..1fc523a 100644 --- a/src/taglib.h +++ b/src/taglib.h @@ -9,6 +9,8 @@ #include +#include + namespace node_taglib { class Tag; class BufferStream; @@ -20,16 +22,16 @@ class BufferStream; int CreateFileRefPath(TagLib::FileName path, TagLib::FileRef **ref); int CreateFileRef(TagLib::IOStream *stream, TagLib::String format, TagLib::FileRef **ref); TagLib::File *createFile(TagLib::IOStream *stream, TagLib::String format); -v8::Handle ErrorToString(int error); -v8::Handle TagLibStringToString( TagLib::String s ); +v8::Local ErrorToString(int error); +v8::Local TagLibStringToString( TagLib::String s ); TagLib::String NodeStringToTagLibString( v8::Local s ); -v8::Handle AsyncReadFile(const v8::Arguments &args); +void AsyncReadFile(const Nan::FunctionCallbackInfo< v8::Value > &args); void AsyncReadFileDo(uv_work_t *req); void AsyncReadFileAfter(uv_work_t *req); struct AsyncBaton { uv_work_t request; - v8::Persistent callback; + Nan::Persistent callback; int error; TagLib::FileName path; /* only used by read/tag, not save */ @@ -43,7 +45,7 @@ struct AsyncBaton { Tag *tag; /* only used by taglib.tag */ }; -v8::Handle AddResolvers(const v8::Arguments &args); +void AddResolvers(const Nan::FunctionCallbackInfo< v8::Value >& args); class CallbackResolver; @@ -56,14 +58,14 @@ struct AsyncResolverBaton { }; class CallbackResolver : public TagLib::FileRef::FileTypeResolver { - v8::Persistent resolverFunc; + Nan::Persistent resolverFunc; const uv_thread_t created_in; public: - CallbackResolver(v8::Persistent func); + CallbackResolver(v8::Local func); TagLib::File *createFile(TagLib::FileName fileName, bool readAudioProperties, TagLib::AudioProperties::ReadStyle audioPropertiesStyle) const; - static void invokeResolverCb(uv_async_t *handle, int status); - static void stopIdling(uv_async_t *handle, int status); + static void invokeResolverCb(uv_async_t *handle); + static void stopIdling(uv_async_t *handle); static void invokeResolver(AsyncResolverBaton *baton); };