【问题标题】:Use iTunes Library framework in Node.js/Electron app?在 Node.js/Electron 应用程序中使用 iTunes 库框架?
【发布时间】:2020-12-16 21:25:43
【问题描述】:

我正在开发一个访问 iTunes 数据库的 Electron (Node.js) 应用程序。在 Catalina 之前,人们可以使用数据库 iTunes Music Library.xml 的导出 XML 版本。这在 Catalina 中被删除,应该使用 iTunes 库框架来代替 https://developer.apple.com/documentation/ituneslibrary

是否可以将此框架包含在我的 Electron 中或通常包含在 Node.js 项目中并与之交互,如果可以,如何?

我在 GitHub 上扫描了模块或可能的解决方案,但它们都依赖于旧的 XML 文件。

【问题讨论】:

    标签: node.js macos electron itunes


    【解决方案1】:

    我看到的唯一方法是使用节点本机模块。 Apple 使用 Objectiv-C,而其他所有语言(javascript、python 等)都使用 C 或 C++ 绑定。幸运的是,clang 支持 Objective-C++,它允许混合 C++ 和 Objective-C。一个可以工作的基本 sn-p 如下所示(将其命名为 readMusic.mmmm 是 Objectiv-C++ 的扩展)

    #import <Foundation/Foundation.h>
    #import <iTunesLibrary/ITLibrary.h>
    #import <iTunesLibrary/ITLibMediaItem.h>
    #import <iTunesLibrary/ITLibArtist.h>
    #include <node.h>
     
    
    namespace demo {
    
    using v8::FunctionCallbackInfo;
    using v8::Isolate;
    using v8::Local;
    using v8::Array;
    using v8::Object;
    using v8::String;
    using v8::Value;
    using v8::Integer;
    
    char const *emptyString = "";
    
    void Method(const FunctionCallbackInfo<Value>& args) {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Set this up so Cocoa works
    
        Isolate* isolate = args.GetIsolate(); // Setup for Javascript Connection
        Local<v8::Context> context = isolate->GetCurrentContext();
        Local<String> keyTitle = String::NewFromUtf8(isolate, "title").ToLocalChecked();
        Local<String> keyArtist = String::NewFromUtf8(isolate, "artist").ToLocalChecked();
        Local<String> keyFilePath = String::NewFromUtf8(isolate, "filePath").ToLocalChecked();
        Local<String> keyBpm = String::NewFromUtf8(isolate, "bpm").ToLocalChecked();
    
    
        NSError *error = nil;
        ITLibrary *library = [ITLibrary libraryWithAPIVersion:@"1.0" error:&error]; // Connect to iTunes / Music Library
        if (library)
        {
            NSArray *tracks = library.allMediaItems; // Load all Songs with Cocoa / Objective-C
            int size = [tracks count];
    
            Local<Array> jsSongsArr = Array::New(isolate, size); // Create Array for Javascript v8 engine
    
            for (int i = 0; i < size; i++) { // Copy elements
                // Reading Data from Cocoa
                ITLibMediaItem *song = tracks[i];
                NSString *title = [song title];
                ITLibArtist *artist = [song artist];
                NSURL *location = [song location];
    
                // Convert it to c
                const char *titleInC = emptyString;
                const char *artistInC = emptyString;
                const char *filePathInC = emptyString;
                const long bpmInC = [song beatsPerMinute];
    
                if (title) {
                    titleInC = [title UTF8String];
                }
                if (artist) {
                    NSString *artistNSString = [artist name];
                    if (artistNSString) {
                        artistInC = [artistNSString UTF8String];
                    }
                }
                if (location) {
                    NSString *locationNSString = [location absoluteString];
                    if (locationNSString) {
                        filePathInC = [locationNSString UTF8String];
                    }
                }
    
                Local<Object> jsSong = Object::New(isolate); // Create Javascript Object
                jsSong->Set(context, keyTitle, String::NewFromUtf8(isolate, titleInC).ToLocalChecked()).FromJust(); // Copy data in Javascript Object
                jsSong->Set(context, keyArtist, String::NewFromUtf8(isolate, artistInC).ToLocalChecked()).FromJust();
                jsSong->Set(context, keyFilePath, String::NewFromUtf8(isolate, filePathInC).ToLocalChecked()).FromJust();
                jsSong->Set(context, keyBpm, Integer::New(isolate, bpmInC)).FromJust();
                
                jsSongsArr->Set(context, i, jsSong).FromJust(); // Add the Object to Javascript Array
            }
            args.GetReturnValue().Set(jsSongsArr); // Set the return value of the function
        } else { // If error occurs
            args.GetReturnValue().Set(String::NewFromUtf8(isolate, [[error localizedDescription] UTF8String]).ToLocalChecked());
        }
    }
    
    void Initialize(Local<Object> exports) {
      NODE_SET_METHOD(exports, "readMusic", Method); // Tells node which function to use
    }
    
    NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) // Inits a native node module
    
    }
    

    在做这样的事情时不要忘记 NSAutoReleasePool。对于绑定,您需要在 binding.gyp 中设置 -ObjC++ 标志。

    {
      "targets": [
        {
          "target_name": "addon",
          "sources": [ "readMusic.mm" ],
          "cflags!": [ "-ObjC++" ],
          "cflags_cc!": [ "-ObjC++" ],
          "libraries": [
              "/System/Library/Frameworks/iTunesLibrary.framework/Versions/Current/iTunesLibrary"
          ]
        }
      ]
    }
    

    然后通过

    编译并对其进行代码设计(对于分发非常重要)
    HOME=~/.electron-gyp node-gyp rebuild --target=10.1.0 --arch=x64 --dist-url=https://electronjs.org/headers
    codesign -s "YOUR DEVELOPER ID" build/Release/addon.node
    

    并通过

    require("./build/Release/addon.node").readMusic();
    

    由于对本地模块的依赖,它只能在 Mac 上编译 (https://www.electron.build/multi-platform-build)。确保在 Windows 平台上排除该代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-12-02
      • 2015-10-23
      • 2015-11-29
      • 2016-06-11
      • 1970-01-01
      • 1970-01-01
      • 2018-02-05
      相关资源
      最近更新 更多