1 #import "iTunesRemote.h"
3 @implementation iTunesRemote
7 return [[[iTunesRemote alloc] init] autorelease];
10 - (NSString *)remoteTitle
12 return @"iTunes Remote";
15 - (NSString *)remoteInformation
17 return @"Default MenuTunes plugin to control iTunes, by iThink Software.";
20 - (NSImage *)remoteIcon
27 ITDebugLog(@"iTunesRemote begun");
28 savedPSN = [self iTunesPSN];
29 [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationHandler:) name:@"com.apple.iTunes.playerInfo" object:@"com.apple.iTunes.player"];
35 ITDebugLog(@"iTunesRemote halted");
36 [[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
40 - (NSString *)playerFullName
45 - (NSString *)playerSimpleName
50 - (NSDictionary *)capabilities
52 return [NSDictionary dictionaryWithObjectsAndKeys:
53 [NSNumber numberWithBool: YES], @"Remote",
54 [NSNumber numberWithBool: YES], @"Basic Track Control",
55 [NSNumber numberWithBool: YES], @"Track Information",
56 [NSNumber numberWithBool: YES], @"Track Navigation",
57 [NSNumber numberWithBool: YES], @"Upcoming Songs",
58 [NSNumber numberWithBool: YES], @"Playlists",
59 [NSNumber numberWithBool: YES], @"Volume",
60 [NSNumber numberWithBool: YES], @"Shuffle",
61 [NSNumber numberWithBool: YES], @"Repeat Modes",
62 [NSNumber numberWithBool: YES], @"Equalizer",
63 [NSNumber numberWithBool: YES], @"Track Rating",
67 - (BOOL)showPrimaryInterface
69 ITDebugLog(@"Showing player primary interface.");
71 if ([self playerRunningState] == ITMTRemotePlayerRunning) {
72 ITDebugLog(@"Showing player interface.");
73 //If not minimized and visible
74 if ( ([ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pMin'), from:obj { form:'indx', want:type('cBrW'), seld:1, from:'null'() } }", 'core', 'getd', &savedPSN) booleanValue] == 0) &&
75 ([ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pvis'), from:obj { form:'indx', want:type('cBrW'), seld:1, from:'null'() } }", 'core', 'getd', &savedPSN) booleanValue] != 0) &&
76 [[[[NSWorkspace sharedWorkspace] activeApplication] objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"] ) {
77 //set minimized of browser window 1 to true
78 ITSendAEWithString(@"data:long(1), '----':obj { form:'prop', want:type('prop'), seld:type('pMin'), from:obj { form:'indx', want:type('cBrW'), seld:long(1), from:'null'() } }", 'core', 'setd', &savedPSN);
80 //set minimized of browser window 1 to false
81 ITSendAEWithString(@"data:long(0), '----':obj { form:'prop', want:type('prop'), seld:type('pMin'), from:obj { form:'indx', want:type('cBrW'), seld:long(1), from:'null'() } }", 'core', 'setd', &savedPSN);
83 //set visible of browser window 1 to true
84 ITSendAEWithString(@"data:long(1), '----':obj { form:'prop', want:type('prop'), seld:type('pvis'), from:obj { form:'indx', want:type('cBrW'), seld:long(1), from:'null'() } }", 'core', 'setd', &savedPSN);
86 ITSendAEWithString(@"data:long(1), '----':obj { form:'prop', want:type('prop'), seld:type('pisf'), from:'null'() }", 'core', 'setd', &savedPSN);
87 ITDebugLog(@"Done showing player primary interface.");
91 ITDebugLog(@"Launching player.");
92 if ( (path = [[NSUserDefaults standardUserDefaults] stringForKey:@"CustomPlayerPath"]) ) {
94 path = [self playerFullName];
96 if (![[NSWorkspace sharedWorkspace] launchApplication:path]) {
97 ITDebugLog(@"Error Launching Player");
104 - (ITMTRemotePlayerRunningState)playerRunningState
106 NSArray *apps = [[NSWorkspace sharedWorkspace] launchedApplications];
108 int count = [apps count];
110 for (i = 0; i < count; i++) {
111 if ([[[apps objectAtIndex:i] objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"]) {
112 ITDebugLog(@"Player running state: 1");
113 return ITMTRemotePlayerRunning;
116 ITDebugLog(@"Player running state: 0");
117 return ITMTRemotePlayerNotRunning;
120 - (ITMTRemotePlayerPlayingState)playerPlayingState
124 ITDebugLog(@"Getting player playing state");
125 result = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pPlS'), from:'null'() }", 'core', 'getd', &savedPSN) typeCodeValue];
129 ITDebugLog(@"Getting player playing state done. Player state: Playing");
130 return ITMTRemotePlayerPlaying;
132 ITDebugLog(@"Getting player playing state done. Player state: Paused");
133 return ITMTRemotePlayerPaused;
135 ITDebugLog(@"Getting player playing state done. Player state: Rewinding");
136 return ITMTRemotePlayerRewinding;
138 ITDebugLog(@"Getting player playing state done. Player state: Forwarding");
139 return ITMTRemotePlayerForwarding;
142 ITDebugLog(@"Getting player playing state done. Player state: Stopped");
143 return ITMTRemotePlayerStopped;
145 ITDebugLog(@"Getting player playing state done. Player state: Stopped");
146 return ITMTRemotePlayerStopped;
149 /*- (NSArray *)playlists
152 const signed long numPlaylists = [[ITAppleEventCenter sharedCenter] sendAEWithSendStringForNumber:@"kocl:type('cPly'), '----':()" eventClass:@"core" eventID:@"cnte" appPSN:savedPSN];
153 NSMutableArray *playlists = [[NSMutableArray alloc] initWithCapacity:numPlaylists];
155 for (i = 1; i <= numPlaylists; i++) {
157 NSString *sendStr = [NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'indx', want:type('cPly'), seld:long(%lu), from:'null'() } }",(unsigned long)j];
158 NSString *theObj = [[ITAppleEventCenter sharedCenter] sendAEWithSendString:sendStr eventClass:@"core" eventID:@"getd" appPSN:savedPSN];
159 [playlists addObject:theObj];
161 return [playlists autorelease];
164 //Full source awareness
165 - (NSArray *)playlists
168 SInt32 numSources = [ITSendAEWithString(@"kocl:type('cSrc'), '----':()", 'core', 'cnte', &savedPSN) int32Value];
169 NSMutableArray *allSources = [[NSMutableArray alloc] init];
171 ITDebugLog(@"Getting playlists.");
172 if (numSources == 0) {
173 [allSources release];
174 ITDebugLog(@"No sources.");
178 for (k = 1; k <= numSources ; k++) {
179 SInt32 numPlaylists = [ITSendAEWithString([NSString stringWithFormat:@"kocl:type('cPly'), '----':obj { form:'indx', want:type('cSrc'), seld:long(%u), from:() }",k], 'core', 'cnte', &savedPSN) int32Value];
180 SInt32 fourcc = [ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pKnd'), from:obj { form:'indx', want:type('cSrc'), seld:long(%u), from:() } }",k], 'core', 'getd', &savedPSN) int32Value];
181 NSString *sourceName = [ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'indx', want:type('cSrc'), seld:long(%u), from:() } }",k], 'core', 'getd', &savedPSN) stringValue];
182 SInt32 index = [ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pidx'), from:obj { form:'indx', want:type('cSrc'), seld:long(%u), from:() } }",k], 'core', 'getd', &savedPSN) int32Value];
185 NSMutableArray *aSource = [[NSMutableArray alloc] init];
186 [aSource addObject:sourceName];
189 class = ITMTRemoteRadioSource;
192 class = ITMTRemoteGenericDeviceSource;
195 class = ITMTRemoteiPodSource;
199 class = ITMTRemoteCDSource;
202 class = ITMTRemoteSharedLibrarySource;
207 class = ITMTRemoteLibrarySource;
210 ITDebugLog(@"Adding source %@ of type %i at index %i", sourceName, class, index);
211 [aSource addObject:[NSNumber numberWithInt:class]];
212 [aSource addObject:[NSNumber numberWithInt:index]];
213 for (i = 1; i <= numPlaylists; i++) {
214 NSString *sendStr = [NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'indx', want:type('cPly'), seld:long(%u), from:obj { form:'indx', want:type('cSrc'), seld:long(%u), from:() } } }",i,k];
215 NSString *theObj = [ITSendAEWithString(sendStr, 'core', 'getd', &savedPSN) stringValue];
216 ITDebugLog(@" - Adding playlist %@", theObj);
218 [aSource addObject:theObj];
221 [allSources addObject:[aSource autorelease]];
223 ITDebugLog(@"Source at index %i disappeared.", k);
226 ITDebugLog(@"Finished getting playlists.");
227 return [allSources autorelease];
232 NSAppleEventDescriptor *rawr = ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pArt'), from:obj { form:'indx', want:type('cTrk'), seld:abso($616C6C20$), from:obj { form:'indx', want:type('cPly'), seld:long(1), from:obj { form:'indx', want:type('cSrc'), seld:long(1), from:() } } } }", 'core', 'getd', &savedPSN);
234 NSMutableArray *array = [[NSMutableArray alloc] init];
235 NSArray *returnArray;
236 for (i = 1; i <= [rawr numberOfItems]; i++) {
237 NSString *artist = [[rawr descriptorAtIndex:i] stringValue];
238 if (artist && [artist length] && ![array containsObject:artist]) {
239 [array addObject:artist];
242 [array sortUsingSelector:@selector(caseInsensitiveCompare:)];
243 returnArray = [NSArray arrayWithArray:array];
250 NSAppleEventDescriptor *rawr = ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pAlb'), from:obj { form:'indx', want:type('cTrk'), seld:abso($616C6C20$), from:obj { form:'indx', want:type('cPly'), seld:long(1), from:obj { form:'indx', want:type('cSrc'), seld:long(1), from:() } } } }", 'core', 'getd', &savedPSN);
252 NSMutableArray *array = [[NSMutableArray alloc] init];
253 NSArray *returnArray;
254 for (i = 1; i <= [rawr numberOfItems]; i++) {
255 NSString *album = [[rawr descriptorAtIndex:i] stringValue];
256 if (album && [album length] && ![array containsObject:album]) {
257 [array addObject:album];
260 [array sortUsingSelector:@selector(caseInsensitiveCompare:)];
261 returnArray = [NSArray arrayWithArray:array];
266 - (int)numberOfSongsInPlaylistAtIndex:(int)index
269 This method only returns the proper number if there's something playing.
270 This is because it gets the container of the current playlist so that it
271 gets the playlist index from the current source. Operating this way is fine,
272 since MT only ever calls this method when there is something playlist.
273 A working version of this that works in just the main source is in the
274 makePlaylistWithTerm:ofType: method.
277 NSAppleEventDescriptor *result;
278 ITDebugLog(@"Getting number of songs in playlist at index %i", index);
279 result = ITSendAEWithString([NSString stringWithFormat:@"kocl:type('cTrk'), '----':obj { form:'indx', want:type('cPly'), seld:long(%lu), from:obj { form:'prop', want:type('prop'), seld:type('ctnr'), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } } }", index], 'core', 'cnte', &savedPSN);
280 temp1 = (result == nil) ? -1 : (int)[result int32Value];
281 ITDebugLog(@"Getting number of songs in playlist at index %i done", index);
285 - (ITMTRemotePlayerSource)currentSource
289 ITDebugLog(@"Getting current source.");
291 fourcc = ([self isPlaying]) ? [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pKnd'), from:obj { form:'prop', want:type('prop'), seld:type('ctnr'), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } } }", 'core', 'getd', &savedPSN) int32Value] : 'kLib';
295 ITDebugLog(@"Getting current source done. Source: Radio.");
296 return ITMTRemoteRadioSource;
299 ITDebugLog(@"Getting current source done. Source: Generic Device.");
300 return ITMTRemoteGenericDeviceSource;
302 ITDebugLog(@"Getting current source done. Source: iPod.");
303 return ITMTRemoteiPodSource; //this is stupid
307 ITDebugLog(@"Getting current source done. Source: CD.");
308 return ITMTRemoteCDSource;
311 ITDebugLog(@"Getting current source done. Source: Shared Library.");
312 return ITMTRemoteSharedLibrarySource;
317 ITDebugLog(@"Getting current source done. Source: Library.");
318 return ITMTRemoteLibrarySource;
323 - (int)currentSourceIndex
325 ITDebugLog(@"Getting current source.");
326 return [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pidx'), from:obj { form:'prop', want:type('prop'), seld:type('ctnr'), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } } }", 'core', 'getd', &savedPSN) int32Value];
329 - (ITMTRemotePlayerPlaylistClass)currentPlaylistClass
332 ITDebugLog(@"Getting current playlist class");
333 realResult = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pcls'), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
337 ITDebugLog(@"Getting current playlist class done. Class: Library.");
338 return ITMTRemotePlayerLibraryPlaylist;
341 ITDebugLog(@"Getting current playlist class done. Class: Radio.");
342 return ITMTRemotePlayerRadioPlaylist;
345 ITDebugLog(@"Getting current playlist class done. Class: Standard playlist.");
346 return ITMTRemotePlayerPlaylist;
350 - (int)currentPlaylistIndex
353 ITDebugLog(@"Getting current playlist index.");
354 temp1 = ([self isPlaying] ? [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pidx'), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value] : -1);
355 ITDebugLog(@"Getting current playlist index done.");
359 - (NSString *)songTitleAtIndex:(int)index
362 ITDebugLog(@"Getting song title at index %i.", index);
363 temp1 = [ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'indx', want:type('cTrk'), seld:long(%lu), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } } }",index], 'core', 'getd', &savedPSN) stringValue];
364 ITDebugLog(@"Getting song title at index %i done.", index);
365 return ( ([temp1 length]) ? temp1 : nil ) ;
368 - (int)currentAlbumTrackCount
371 ITDebugLog(@"Getting current album track count.");
372 temp1 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pTrC'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
373 if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { temp1 = 0; }
374 ITDebugLog(@"Getting current album track count done.");
378 - (int)currentSongTrack
381 ITDebugLog(@"Getting current song track.");
382 temp1 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pTrN'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
383 if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { temp1 = 0; }
384 ITDebugLog(@"Getting current song track done.");
388 - (NSString *)playerStateUniqueIdentifier
391 ITDebugLog(@"Getting current unique identifier.");
392 NSAppleEventDescriptor *descriptor = ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pcls'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN);
393 if ([descriptor int32Value] == 'prop') {
395 } else if (descriptor == nil) {
398 SInt32 cls = [descriptor int32Value];
399 if ( ([self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist) || (cls == 'cURT') ) {
400 NSString *bad = [NSString stringWithUTF8String:"浳湧"];
401 temp1 = [ITSendAEWithKey('pStT', 'core', 'getd', &savedPSN) stringValue];
402 if ([temp1 isEqualToString:bad]) {
403 temp1 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) stringValue];
406 temp1 = [NSString stringWithFormat:@"%i-%i", [self currentPlaylistIndex], [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pDID'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value]];
408 ITDebugLog(@"Getting current unique identifier done.");
409 return ( ([temp1 length]) ? temp1 : nil ) ;
412 - (int)currentSongIndex
415 ITDebugLog(@"Getting current song index.");
416 temp1 = ([self isPlaying] ? [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pidx'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value] : -1);
417 ITDebugLog(@"Getting current song index done.");
421 - (NSString *)currentSongTitle
424 ITDebugLog(@"Getting current song title.");
425 SInt32 result = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pcls'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
427 //If we're listening to the radio.
428 if (result == 'cURT') {
429 NSString *bad = [NSString stringWithUTF8String:"浳湧"];
430 temp1 = [ITSendAEWithKey('pStT', 'core', 'getd', &savedPSN) stringValue];
431 if ([temp1 isEqualToString:bad]) {
432 temp1 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) stringValue];
434 temp1 = [temp1 stringByAppendingString:@" (Stream)"];
435 } else if (result == 'prop') {
438 temp1 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) stringValue];
440 ITDebugLog(@"Getting current song title done.");
441 return ( ([temp1 length]) ? temp1 : nil ) ;
444 - (NSString *)currentSongArtist
447 ITDebugLog(@"Getting current song artist.");
448 if ( [self currentPlaylistClass] != ITMTRemotePlayerRadioPlaylist ) {
449 temp1 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pArt'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) stringValue];
453 ITDebugLog(@"Getting current song artist done.");
454 return ( ([temp1 length]) ? temp1 : nil ) ;
457 - (NSString *)currentSongComposer
460 ITDebugLog(@"Getting current song artist.");
461 if ( [self currentPlaylistClass] != ITMTRemotePlayerRadioPlaylist ) {
462 temp1 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pCmp'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) stringValue];
466 ITDebugLog(@"Getting current song artist done.");
467 return ( ([temp1 length]) ? temp1 : nil ) ;
470 - (NSString *)currentSongAlbum
473 ITDebugLog(@"Getting current song album.");
474 if ( [self currentPlaylistClass] != ITMTRemotePlayerRadioPlaylist ) {
475 temp1 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pAlb'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) stringValue];
479 ITDebugLog(@"Getting current song album done.");
480 return ( ([temp1 length]) ? temp1 : nil ) ;
483 - (NSString *)currentSongGenre
486 ITDebugLog(@"Getting current song genre.");
487 temp1 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pGen'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) stringValue];
488 ITDebugLog(@"Getting current song genre done.");
489 return ( ([temp1 length]) ? temp1 : nil ) ;
492 - (NSString *)currentSongLength
496 ITDebugLog(@"Getting current song length.");
497 temp1 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pcls'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
498 temp2 = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pTim'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) stringValue];
499 if ( ([self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist) || (temp1 == 'cURT') ) { temp2 = @"Continuous"; }
500 ITDebugLog(@"Getting current song length done.");
504 - (NSString *)currentSongRemaining
506 SInt32 duration, current, final;
507 NSString *finalString;
509 ITDebugLog(@"Getting current song remaining time.");
511 duration = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pDur'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
512 current = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pPos'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
513 final = duration - current;
514 finalString = [self formatTimeInSeconds:final];
516 if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { finalString = nil; }
518 ITDebugLog(@"Getting current song remaining time done.");
523 - (NSString *)currentSongElapsed
526 NSString *finalString;
528 ITDebugLog(@"Getting current song elapsed time.");
529 final = (long)[ITSendAEWithKey('pPos', 'core', 'getd', &savedPSN) int32Value];
530 finalString = [self formatTimeInSeconds:final];
531 ITDebugLog(@"Getting current song elapsed time done.");
535 - (NSImage *)currentSongAlbumArt
537 ITDebugLog(@"Getting current song album art.");
538 NSData *data = ([self isPlaying]) ? [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pPCT'), from:obj { form:'indx', want:type('cArt'), seld:long(1), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } } }", 'core', 'getd', &savedPSN) data] : nil;
539 ITDebugLog(@"Getting current song album art done.");
541 return [[[NSImage alloc] initWithData:data] autorelease];
547 - (int)currentSongPlayCount
550 ITDebugLog(@"Getting current song play count.");
551 count = (int)[ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pPlC'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
552 ITDebugLog(@"Getting current song play count done.");
556 - (float)currentSongRating
559 ITDebugLog(@"Getting current song rating.");
560 temp1 = (![self isPlaying] || ([self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist)) ? -1.0 : ((float)[ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pRte'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value] / 100.0);
561 ITDebugLog(@"Getting current song rating done.");
565 - (BOOL)setCurrentSongRating:(float)rating
567 ITDebugLog(@"Setting current song rating to %f.", rating);
568 if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { return NO; }
569 ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pRte'), from:obj { form:'indx', want:type('cTrk'), seld:long(%lu), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } } }",(long)(rating*100), [self currentSongIndex]], 'core', 'setd', &savedPSN);
570 ITDebugLog(@"Setting current song rating to %f done.", rating);
574 - (BOOL)equalizerEnabled
576 ITDebugLog(@"Getting equalizer enabled status.");
577 int thingy = (int)[ITSendAEWithKey('pEQ ', 'core', 'getd', &savedPSN) int32Value];
578 ITDebugLog(@"Done getting equalizer enabled status.");
579 return (thingy != 0) ? YES : NO;
582 - (BOOL)setEqualizerEnabled:(BOOL)enabled
584 ITDebugLog(@"Setting equalizer enabled to %i.", enabled);
585 ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pEQ '), from:'null'() }", enabled], 'core', 'setd', &savedPSN);
586 ITDebugLog(@"Done setting equalizer enabled to %i.", enabled);
590 - (NSArray *)eqPresets
593 SInt32 numPresets = [ITSendAEWithString(@"kocl:type('cEQP'), '----':(), &subj:()", 'core', 'cnte', &savedPSN) int32Value];
594 NSMutableArray *presets = [[NSMutableArray alloc] initWithCapacity:numPresets];
595 ITDebugLog(@"Getting EQ presets");
596 for (i = 1; i <= numPresets; i++) {
597 NSString *theObj = [ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'indx', want:type('cEQP'), seld:long(%lu), from:'null'() } }", i], 'core', 'getd', &savedPSN) stringValue];
599 ITDebugLog(@"Adding preset %@", theObj);
600 [presets addObject:theObj];
603 ITDebugLog(@"Done getting EQ presets");
604 return [presets autorelease];
607 - (int)currentEQPresetIndex
610 ITDebugLog(@"Getting current EQ preset index.");
611 result = (int)[ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pidx'), from:obj { form:'prop', want:type('prop'), seld:type('pEQP'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
612 ITDebugLog(@"Getting current EQ preset index done.");
618 ITDebugLog(@"Getting volume.");
619 ITDebugLog(@"Getting volume done.");
620 return (float)[ITSendAEWithKey('pVol', 'core', 'getd', &savedPSN) int32Value] / 100;
623 - (BOOL)setVolume:(float)volume
625 ITDebugLog(@"Setting volume to %f.", volume);
626 ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pVol'), from:'null'() }", (long)(volume * 100)], 'core', 'setd', &savedPSN);
627 ITDebugLog(@"Setting volume to %f done.", volume);
631 - (BOOL)shuffleEnabled
633 ITDebugLog(@"Getting shuffle enabled status.");
634 int result = (int)[ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pShf'), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
635 ITDebugLog(@"Getting shuffle enabled status done.");
636 return (result != 0);
639 - (BOOL)setShuffleEnabled:(BOOL)enabled
641 ITDebugLog(@"Set shuffle enabled to %i", enabled);
642 ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pShf'), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } }", (unsigned long)enabled], 'core', 'setd', &savedPSN);
643 ITDebugLog(@"Set shuffle enabled to %i done", enabled);
647 - (ITMTRemotePlayerRepeatMode)repeatMode
649 FourCharCode m00f = 0;
651 m00f = (FourCharCode)[ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pRpt'), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
652 ITDebugLog(@"Getting repeat mode.");
657 ITDebugLog(@"Repeat off");
658 result = ITMTRemotePlayerRepeatOff;
661 ITDebugLog(@"Repeat one");
662 result = ITMTRemotePlayerRepeatOne;
665 ITDebugLog(@"Repeat all");
666 result = ITMTRemotePlayerRepeatAll;
669 ITDebugLog(@"Getting repeat mode done.");
673 - (BOOL)setRepeatMode:(ITMTRemotePlayerRepeatMode)repeatMode
676 ITDebugLog(@"Setting repeat mode to %i", repeatMode);
679 case ITMTRemotePlayerRepeatOne:
682 case ITMTRemotePlayerRepeatAll:
685 case ITMTRemotePlayerRepeatOff:
690 ITSendAEWithString([NSString stringWithFormat:@"data:'%s', '----':obj { form:'prop', want:type('prop'), seld:type('pRpt'), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:() } }", m00f], 'core', 'setd', &savedPSN);
691 ITDebugLog(@"Setting repeat mode to %c done", m00f);
698 ITSendAE('hook', 'Play', &savedPSN);
699 ITDebugLog(@"Play done");
705 ITDebugLog(@"Pause");
706 ITSendAE('hook', 'Paus', &savedPSN);
707 ITDebugLog(@"Pause done");
713 ITDebugLog(@"Go to next track");
714 ITSendAE('hook', 'Next', &savedPSN);
715 ITDebugLog(@"Go to next track done");
719 - (BOOL)goToPreviousSong
721 ITDebugLog(@"Go to previous track");
722 ITSendAE('hook', 'Back', &savedPSN);
723 ITDebugLog(@"Go to previous track done");
729 ITDebugLog(@"Fast forward action");
730 ITSendAE('hook', 'Fast', &savedPSN);
731 ITDebugLog(@"Fast forward action done");
737 ITDebugLog(@"Rewind action");
738 ITSendAE('hook', 'Rwnd', &savedPSN);
739 ITDebugLog(@"Rewind action done");
743 - (BOOL)switchToPlaylistAtIndex:(int)index
745 ITDebugLog(@"Switching to playlist at index %i", index);
746 ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'indx', want:type('cPly'), seld:long(%lu), from:() }", index], 'hook', 'Play', &savedPSN);
747 ITDebugLog(@"Done switching to playlist at index %i", index);
751 - (BOOL)switchToPlaylistAtIndex:(int)index ofSourceAtIndex:(int)index2
753 ITDebugLog(@"Switching to playlist at index %i of source %i", index, index2);
754 ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'indx', want:type('cPly'), seld:long(%lu), from: obj { form:'indx', want:type('cSrc'), seld:long(%lu), from:'null'() } }", index - 1, index2 + 1], 'hook', 'Play', &savedPSN);
755 ITDebugLog(@"Done switching to playlist at index %i of source %i", index, index2);
759 - (BOOL)switchToSongAtIndex:(int)index
761 ITDebugLog(@"Switching to track at index %i", index);
762 ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'indx', want:type('cTrk'), seld:long(%lu), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:() } }", index], 'hook' ,'Play', &savedPSN);
763 ITDebugLog(@"Done switching to track at index %i", index);
767 - (BOOL)switchToEQAtIndex:(int)index
769 ITDebugLog(@"Switching to EQ preset at index %i", index);
770 // index should count from 0, but itunes counts from 1, so let's add 1.
771 [self setEqualizerEnabled:YES];
772 ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pEQP'), from:'null'() }, data:obj { form:'indx', want:type('cEQP'), seld:long(%lu), from:'null'() }", (index+1)], 'core', 'setd', &savedPSN);
773 ITDebugLog(@"Done switching to EQ preset at index %i", index);
777 - (BOOL)makePlaylistWithTerm:(NSString *)term ofType:(int)type
781 //Get fixed indexing status
782 BOOL fixed = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", 'core', 'getd', &savedPSN) booleanValue];
784 //Enabled fixed indexing
785 ITSendAEWithString(@"data:long(1), '----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", 'core', 'setd', &savedPSN);
787 //Search for the term
788 NSAppleEventDescriptor *searchResults = ITSendAEWithString([NSString stringWithFormat:@"pTrm:\"%@\", pAre:'%@', '----':obj { form:'indx', want:type('cPly'), seld:long(1), from:obj { form:'indx', want:type('cSrc'), seld:long(1), from:'null'() } }", term, ((type == 1) ? @"kSrR" : @"kSrL")], 'hook', 'Srch', &savedPSN);
790 //If MenuTunes playlist exists
791 if ([ITSendAEWithString(@"'----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'core', 'doex', &savedPSN) booleanValue]) {
792 //Clear old MenuTunes playlist
793 int numSongs = [ITSendAEWithString(@"kocl:type('cTrk'), '----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'core', 'cnte', &savedPSN) int32Value];
794 for (i = 1; i <= numSongs; i++) {
795 ITSendAEWithString(@"'----':obj { form:'indx', want:type('cTrk'), seld:long(1), from:obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() } }", 'core', 'delo', &savedPSN);
798 //Create MenuTunes playlist
799 ITSendAEWithString(@"prdt:{ pnam:\"MenuTunes\" }, kocl:type('cPly'), &subj:()", 'core', 'crel', &savedPSN);
802 //Duplicate search results to playlist
803 for (i = 1; i <= [searchResults numberOfItems]; i++) {
804 //NSLog(@"%@", ITSendAEWithStringAndParameter(@"'----':obj { form:'prop', want:type('prop'), seld:prop('pnam'), from:aevt(@) }", *[[searchResults descriptorAtIndex:i] aeDesc], 'core', 'getd', &savedPSN));
805 ITSendAEWithStringAndObject(@"insh:obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", [[searchResults descriptorAtIndex:i] aeDesc], 'core', 'clon', &savedPSN);
807 //Reset fixed indexing
808 ITSendAEWithString([NSString stringWithFormat:@"data:long(%i), '----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", fixed], 'core', 'setd', &savedPSN);
810 //Play MenuTunes playlist
811 ITSendAEWithString(@"'----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'hook', 'Play', &savedPSN);
818 return ([ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pcls'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value] != 'prop');
821 - (void)notificationHandler:(NSNotification *)note
823 ITDebugLog(@"Received notification: %@", note);
824 [[NSNotificationCenter defaultCenter] postNotificationName:@"ITMTTrackChanged" object:self userInfo:[note userInfo]];
825 ITDebugLog(@"Handled notification.");
828 - (ProcessSerialNumber)iTunesPSN
830 /*NSArray *apps = [[NSWorkspace sharedWorkspace] launchedApplications];
831 ProcessSerialNumber number;
833 int count = [apps count];
835 number.highLongOfPSN = kNoProcess;
837 for (i = 0; i < count; i++)
839 NSDictionary *curApp = [apps objectAtIndex:i];
841 if ([[curApp objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"])
843 number.highLongOfPSN = [[curApp objectForKey:
844 @"NSApplicationProcessSerialNumberHigh"] intValue];
845 number.lowLongOfPSN = [[curApp objectForKey:
846 @"NSApplicationProcessSerialNumberLow"] intValue];
850 ProcessSerialNumber number;
851 number.highLongOfPSN = kNoProcess;
852 number.lowLongOfPSN = 0;
853 ITDebugLog(@"Getting iTunes' PSN.");
854 while ( (GetNextProcess(&number) == noErr) )
857 if ( (CopyProcessName(&number, &name) == noErr) )
859 if ([(NSString *)name isEqualToString:@"iTunes"])
861 ITDebugLog(@"iTunes' highLPongOfPSN: %lu.", number.highLongOfPSN);
862 ITDebugLog(@"iTunes' lowLongOfPSN: %lu.", number.lowLongOfPSN);
863 ITDebugLog(@"Done getting iTunes' PSN.");
866 [(NSString *)name release];
869 ITDebugLog(@"Failed getting iTunes' PSN.");
873 - (NSString*)formatTimeInSeconds:(long)seconds {
874 long final = seconds;
875 NSString *finalString;
878 finalString = [NSString stringWithFormat:@"%i:%@:%@",(final / 3600),[self zeroSixty:(int)((final % 3600) / 60)],[self zeroSixty:(int)((final % 3600) % 60)]];
880 finalString = [NSString stringWithFormat:@"%i:%@",(final / 60),[self zeroSixty:(int)(final % 60)]];
883 finalString = [NSString stringWithFormat:@"0:%@",[self zeroSixty:(int)final]];
887 - (NSString*)zeroSixty:(int)seconds {
888 if ( (seconds < 10) && (seconds > 0) ) {
889 return [NSString stringWithFormat:@"0%i",seconds];
890 } else if ( (seconds == 0) ) {
891 return [NSString stringWithFormat:@"00"];
893 return [NSString stringWithFormat:@"%i",seconds];