9

我正在尝试使用可可脚本桥创建一个新的用户播放列表,但似乎无法使其正常工作。我到目前为止:

iTunesApplication *iTunes = [SBApplication 
                            applicationWithBundleIdentifier:@"com.apple.iTunes"];
SBElementArray *iSources = [iTunes sources];
iTunesSource *library = nil;
for (iTunesSource *source in iSources) {
    if ([[source name] isEqualToString:@"Library"]) {
        library = source;
        break;
    }
}

// could not find the itunes library
if (!library) {
    NSLog(@"Could not connect to the iTunes library");
    return;
}

// now look for our playlist
NSString *playlistName = @"new playlist";
SBElementArray *playlists = [library userPlaylists];
iTunesUserPlaylist *playlist = nil;
for (iTunesUserPlaylist *thisList in playlists) {
    if ([[thisList name] isEqualToString:playlistName]) {
        playlist = thisList;
        break;
    }
}

// if the playlist was not found, create it
if (!playlist) {
    playlist = [[[iTunes classForScriptingClass:@"playlist"] alloc] init];
    [playlist setName:playlistName];
    [[library userPlaylists] insertObject:playlist atIndex:0];
}

当我尝试为播放列表添加名称时,我收到错误消息:

iTunesBridge[630:80f] *** -[SBProxyByClass setName:]:对象尚未添加到容器中;选择器无法识别

谁能指出我正确的方向?

4

5 回答 5

10

错误消息告诉您,像播放列表这样的脚本桥对象在添加到相关的 SBElementArray 之前无法接收消息,因此您尝试在播放列表上设置属性之前将其添加到数组失败。

最简单的解决方案就是重新排列最后两行代码,如下所示:

// if the playlist was not found, create it
if (!playlist) {
    playlist = [[[iTunes classForScriptingClass:@"playlist"] alloc] init];
    [[library userPlaylists] insertObject:playlist atIndex:0];
    [playlist setName:playlistName];
}

另一种选择是使用initWithProperties:which 根据您对另一个答案的评论是您最终所做的。

于 2011-04-25T22:08:50.190 回答
5

在 SB 中创建新的应用程序对象是非常模糊的。伪 Cocoa 风格的 alloc-init-insert 过程与下面实际发生的事情没有任何相似之处。虽然 alloc-init 似乎创建了一个常规对象,您可以通过后续方法调用对其进行操作,但结果实际上是一个 shim,其唯一功能是“插入”到“数组”中,此时 SB 将实际make事件发送到目标进程。(另请参阅此处此处了解 SB 批评。)

IIRC,您可以实际指定初始属性的唯一一点是在-initWithProperties:. 您可以在“插入”对象后设置它们,但这是完全不同的操作(操作已经存在的对象而不是为正在创建的对象指定初始状态),因此如果您不小心,很容易产生意想不到的后果.

无论如何,如果一个新播放列表不存在,您通常会按照以下方式创建一个新播放列表:

set playlistName to "new playlist"
tell application "iTunes"
    if not (exists playlist playlistName) then
        make new playlist with properties {name:playlistName}
    end if
end tell

而且,FWIW,这是我在 ObjC 中使用objc-appscript 的方法(我写了它,所以我不必使用 SB,natch):

#import "ITGlue/ITGlue.h"

NSString *playlistName = @"new playlist";

ITApplication *itunes = [ITApplication applicationWithName: @"iTunes"];
ITReference *playlist = [[itunes playlists] byName: playlistName];

if ([[[playlist exists] send] boolValue])
    playlist = [playlist getItem];
else
    playlist = [[[[itunes make] new_: [ITConstant playlist]] 
                      withProperties: [NSDictionary dictionaryWithObject: playlistName
                                                                  forKey: [ITConstant name]]] send];

(objc-appscript 的缺点是你必须在你的应用程序包中构建和嵌入框架的副本。好处是它更强大,更不容易出现应用程序兼容性问题,而且更不容易混淆。另外你可以使用 appscript 的ASTranslate工具将上述 AppleScript 发送的 Apple 事件转换为 ObjC 语法 - 在弄清楚如何构造引用和命令时非常方便。)

于 2009-12-29T16:54:42.273 回答
2

只是一个简短的说明,[[source name] isEqualToString:@"Library"]绝对不适用于非英语系统。简单地使用可能会更好,iTunesSource *library = [[_iTunes sources] objectAtIndex: 0];因为第一个源项是顶部的源项,例如主库。

于 2010-07-29T05:06:51.503 回答
2

这是我为可靠地识别库所做的。我可能做错了。

- (iTunesSource *)iTunesLibrary
{
  NSArray *librarySource = [[[self iTunes] sources] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"kind == %@", [NSAppleEventDescriptor descriptorWithTypeCode:iTunesESrcLibrary]]];
  if ([[librarySource lastObject] exists]) {
    return [librarySource lastObject];
  }
  return nil;
}
于 2011-11-19T05:36:39.090 回答
1

你应该看看 EyeTunes。它是一个使用 Objective-C 与 iTunes 交互的开源框架。如果您通过 EyeTunes 编写代码,您的代码看起来会简单得多。

http://www.liquidx.net/eyetunes/

于 2009-12-29T16:57:02.817 回答