Создать плейлист iTunes с помощью скриптового моста
Я пытаюсь создать новый пользовательский плейлист с использованием моста сценариев какао, но не могу заставить его работать. У меня так далеко
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 ответов
Сообщение об ошибке говорит вам, что объекты Scripting Bridge, такие как ваш список воспроизведения, не могут получать сообщения, пока они не будут добавлены в соответствующий 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:
что в соответствии с вашим комментарием на другой ответ, что вы в конечном итоге делать.
Создание новых объектов приложения ужасно запутано в SB. Процедура псевдо-Cocoa-ish 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 заключается в том, что вам необходимо создать и встроить копию инфраструктуры в свой пакет приложений. Преимущества заключаются в том, что она более способна, менее подвержена проблемам совместимости приложений и намного менее запутана. Плюс вы можете использовать приложения Инструмент ASTranslate для преобразования событий Apple, отправленных вышеприведенным AppleScript, в синтаксис ObjC - очень удобно при выяснении того, как создавать ссылки и команды.)
Просто быстрое замечание, что [[source name] isEqualToString:@"Library"]
определенно не работает на неанглийских системах. Может быть, лучше просто использовать iTunesSource *library = [[_iTunes sources] objectAtIndex: 0];
поскольку первый исходный элемент находится сверху, например, в главной библиотеке.
Это то, что я сделал, чтобы надежно идентифицировать библиотеку. Я мог бы сделать это неправильно.
- (iTunesSource *)iTunesLibrary
{
NSArray *librarySource = [[[self iTunes] sources] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"kind == %@", [NSAppleEventDescriptor descriptorWithTypeCode:iTunesESrcLibrary]]];
if ([[librarySource lastObject] exists]) {
return [librarySource lastObject];
}
return nil;
}
Вы должны посмотреть в EyeTunes. Это платформа с открытым исходным кодом для взаимодействия с iTunes с помощью Objective-C. Ваш код выглядел бы намного проще, если бы вы делали это через EyeTunes.