Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

mlikesit

macrumors newbie
Original poster
Nov 3, 2008
2
0
I am writing an app in Xcode and need to create a new playlist in iTUnes, and add specific tracks to it.

Here is the code I have so far:

Code:
-(ITunesError *) savePlaylist:(Playlist *) playlist {
	ITunesError * rv = [[ITunesError new]retain];

        //Get name of playlist we want to create
	NSString * name = [playlist name];
	ITReference *playlistRef = [[itunes playlists] byName:name];
	
        /* Find a unique name for playlist we are creating */
	if([[[playlistRef exists]send] boolValue]){
		int i = 1;
		NSString * n = @"";
		do{
			n = [NSString stringWithFormat:@"%@_%d", name, i];
			playlistRef = 	[[itunes playlists] byName:n];
			i++;
		}while([[[playlistRef exists]send] boolValue]);
		name = n;
	}
	
        //Create new playlist in itunes
	[[[[itunes make] new_: [ITConstant playlist]] withProperties: [NSDictionary dictionaryWithObject: name forKey: [ITConstant name]]] send];
	
	
        //Returns the main music library for iTunes
	ITReference *main = [self getRefToMainPlaylist];
	ITReference * tracks = [main tracks];
	
	//NSArray * trackNames = [[tracks name] getItem];
	//NSArray * artistNames = [[tracks artist] getItem];
	NSArray * locations = [[tracks location ] getItem];
	
	NSMutableArray *files = [NSMutableArray array];
	for(int j = 0; j < 10; j++){
		ASAlias * alias = (ASAlias *) [locations objectAtIndex:j];
		NSLog([alias path]);
		NSURL *file = [NSURL fileURLWithPath: [alias path]];
		[files addObject: file];
	}

        /* At this point here is what we have:
           playlistRef - Reference to playlist we just created in itunes
           files - Array of NSURL objects pointing to files on disk.
        */
	NSError * error = 0;
	[[[itunes add: files] to: playlistRef] sendWithError:&error];	
	if(error){
		[rv setErrorObject:error];
		NSLog([rv getErrorMessage]);	
	}
        return nil;
}

In the code above, i'm not too picky about what tracks I add (I'll worry about that after I can add a track to the playlist) so for now it is just adding the first N tracks from the iTUnes main track library.

When I run the code above the error message contained in NSError is:
Application error: the AppleEvent was not handled by any handler
which comes from when I try to add the files to the playlist.

Any help here would be greatly appreciated.
 

hhas

macrumors regular
Oct 15, 2007
126
0
I am writing an app in Xcode and need to create a new playlist in iTUnes, and add specific tracks to it.

Here is the code I have so far:

Code:
[...]
	NSMutableArray *files = [NSMutableArray array];
	for(int j = 0; j < 10; j++){
		ASAlias * alias = (ASAlias *) [locations objectAtIndex:j];
		NSLog([alias path]);
		NSURL *file = [NSURL fileURLWithPath: [alias path]];
		[files addObject: file];
	}

        /* At this point here is what we have:
           playlistRef - Reference to playlist we just created in itunes
           files - Array of NSURL objects pointing to files on disk.
        */
	NSError * error = 0;
	[[[itunes add: files] to: playlistRef] sendWithError:&error];

Couple of things:

1. Take care when casting values returned by application commands, in this case, the (ASAlias*) cast. Some application commands may return different types at different times, e.g. getting a track's location property will return an alias if the file exists, or a 'missing value' constant if it doesn't. If you want to specify the return type, do it when you're constructing the application command, e.g. in this case use [[tracks location] getListOfType: typeAlias error:&error], which will return a coercion error if it encounters a missing track. Alternatively, use -[NSObject isKindOfClass:] to check the returned value's class before using it.

2. You shouldn't need to re-pack ASAliases as NSURLs if you're only passing them back to iTunes; iTunes should accept either. OTOH, using ASAliases in other Cocoa APIs is a pain as they're not API-compatible with NSURLs (I need to work on this), but you can at least get an NSURL directly via -[ASAlias url], or else you can tell appscript to coerce the values returned by the application command to the desired type for you (in this case typeFileURL).

3. If you're importing files that aren't already in iTunes, use its 'add' command. In this case though you're wanting to add tracks that already exist in your library playlist to a user playlist, and for that you should use iTunes' 'duplicate' command. Don't have sample code to hand (ask if you need some), but it should be pretty straightforward. iTunes' 'duplicate' command appears to accept either a single-track reference or a multi-track reference (most apps do), though not a list of references (most apps don't), e.g. these work:

Code:
tell application "iTunes"
	duplicate (track 1 of library playlist 1) to playlist "test"
end tell

tell application "iTunes"
	duplicate (tracks 1 thru 5 of library playlist 1) to playlist "test"
end tell

but this doesn't:

Code:
tell application "iTunes"
	set tracksList to (get tracks 1 thru 5 of library playlist 1)
	duplicate tracksList to playlist "test"
end tell

So if you need to add multiple tracks, either create a single reference that identifies all the tracks you want (e.g. by using a by-test specifier) and use that in the 'duplicate' command, or iterate over a list of references and duplicate them one at a time.

Tips:

- You can use ASTranslate (available from the appscript website) to convert the above iTunes commands from AppleScript to ObjC syntax, which is handy if you've got an AppleScript-based example and need some help with translating it into ObjC.

- Doug's iTunes (http://dougscripts.com/) has tons of iTunes-related AppleScripts, many of which can be viewed in Script Editor; also very handy when you're trying to work out how to perform a particular task. As you've probably noticed, iTunes' own scripting documentation (like that of most apps) is very thin and doesn't tell you half the stuff you actually need to know in order to script it effectively - so other folks' scripts can be a valuable source of hints and solutions.
 

mlikesit

macrumors newbie
Original poster
Nov 3, 2008
2
0
Can't create new Playlist

Thank you for mentioning ASTranslate, wish I would have found out about it when I started this app instead of when I was almost done.

Using it, I have figured out how to add existing tracks to an existing playlist, but I am still having problems creating a new playlist.

So i've created this method to simply create a playlist. I got the code from ASTranslate and the only modification I made was to sendWithError.

Applescript Code
Code:
tell application "iTunes"
	make new user playlist with properties {name:"DUH"}
end tell

Translates to the following Appscript code:
Code:
-(NSError *) createPlaylist:(NSString *) playlist{
	ITMakeCommand *cmd = [[[itunes make] new_: [ITConstant userPlaylist]] withProperties: [NSDictionary dictionaryWithObject: @"Playlist" forKey: [ITConstant name]]];
	NSError * rv = 0;
	[cmd sendWithError:&rv];
	return rv;
}

When I run the Applescript code, it creates a new playlist in iTunes. When I run the objective-c appscript code, I get the following error:

Error Domain=net.sourceforge.appscript.objc-appscript.ErrorDomain Code=-10001 UserInfo=0x1af030 "Application error: / invalid TELHandle or handle not found (-10001)"

I have now idea why that happens or what it means and I don't understand why it works when I run the Applescript but not when I run the Appscript.
 

hhas

macrumors regular
Oct 15, 2007
126
0
So i've created this method to simply create a playlist. I got the code from ASTranslate and the only modification I made was to sendWithError. [...] When I run the Applescript code, it creates a new playlist in iTunes. When I run the objective-c appscript code, I get the following error:

Hmm. Wonderfully helpful error message. That you get different behaviour with appscript to AppleScript is a concern, but so far I'm unable to reproduce the problem here (iTunes 8.0.1 on 10.5.5/i386), e.g.:

Code:
#import "ITGlue/ITGlue.h"

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

ITApplication *itunes = [ITApplication applicationWithName: @"iTunes"];

ITMakeCommand *cmd = [[[itunes make] new_: [ITConstant userPlaylist]] 
						   withProperties: [NSDictionary dictionaryWithObject: @"DUH"
																	   forKey: [ITConstant name]]];

NSError *error;
id result = [cmd sendWithError:&error];
NSLog(@"Result = %@", result);
NSLog(@"Error = %@", error);

    [pool drain];
    return 0;
}

produces:

Code:
2008-11-04 18:23:29.561 itunes-newplaylist[23202:10b] Result = [[[[[ITApplication applicationWithName: @"/Applications/iTunes.app"] sources] byID: [NSNumber numberWithInt: 41]] userPlaylists] byID: [NSNumber numberWithInt: 56496]]
2008-11-04 18:23:29.563 itunes-newplaylist[23202:10b] Error = (null)

which makes it hard to troubleshoot the problem at this end without more info.

A couple things to try:

- Build the Appscript framework using the latest objc-appscript trunk, regenerate your glue using ASDictionary 0.11.0 and do a clean build of your project to check there isn't a version mismatch between them.

- Run iTunes with AEDebug enabled and email me the trace for 'make' event sent by AppleScript and the trace for the 'make' event sent by appscript. Also include your OS version, iTunes version and hardware type.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.