使用脚本桥创建iTunes播放列表

时间:2009-12-28 10:12:46

标签: cocoa itunes scripting-bridge

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

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:]:对象尚未添加到容器中;选择器未被识别

有人能指出我正确的方向吗?

5 个答案:

答案 0 :(得分: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:,根据您对另一个答案的评论,你最终会做什么。

答案 1 :(得分:5)

在SB中对新的应用程序对象进行了大量的混淆。伪Cocoa-ish alloc-init-insert程序与下面的实际内容没有任何相似之处。虽然alloc-init似乎创建了一个可以通过后续方法调用操作的常规对象,但结果实际上是一个垫片,其唯一的功能是“插入”'数组',此时SB发送一个实际的{{ 1}}事件到目标进程。 (另请参阅herehere了解SB的批评。)

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

无论如何,如果一个新的播放列表尚不存在,通常会创建一个新的播放列表:

-initWithProperties:

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

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

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

答案 2 :(得分:2)

请注意,[[source name] isEqualToString:@"Library"]肯定不适用于非英语系统。简单地使用iTunesSource *library = [[_iTunes sources] objectAtIndex: 0];可能更好,因为第一个源项是顶部的项,例如主图书馆。

答案 3 :(得分:2)

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

- (iTunesSource *)iTunesLibrary
{
  NSArray *librarySource = [[[self iTunes] sources] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"kind == %@", [NSAppleEventDescriptor descriptorWithTypeCode:iTunesESrcLibrary]]];
  if ([[librarySource lastObject] exists]) {
    return [librarySource lastObject];
  }
  return nil;
}

答案 4 :(得分:1)

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

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