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
634 ITDebugLog(@"Getting shuffle enabled status.");
635 if ([[self playerStateUniqueIdentifier] isEqualToString:@"0-0"]) {
636 ITDebugLog(@"No current playlist, getting shuffle status from visible playlist.");
637 result = (int)[ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pShf'), from:obj { form:'prop', want:type('prop'), seld:type('pPly'), from:obj { form:'indx', want:type('cBrW'), seld:1, from:'null'() } } }", 'core', 'getd', &savedPSN) int32Value];
639 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];
641 ITDebugLog(@"Getting shuffle enabled status done.");
642 return (result != 0);
645 - (BOOL)setShuffleEnabled:(BOOL)enabled
647 ITDebugLog(@"Set shuffle enabled to %i", enabled);
648 if ([[self playerStateUniqueIdentifier] isEqualToString:@"0-0"]) {
649 ITDebugLog(@"No current playlist, setting shuffle status on visible playlist.");
650 ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pShf'), from:obj { form:'prop', want:type('prop'), seld:type('pPly'), from:obj { form:'indx', want:type('cBrW'), seld:1, from:'null'() } } }", (unsigned long)enabled], 'core', 'setd', &savedPSN);
652 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);
654 ITDebugLog(@"Set shuffle enabled to %i done", enabled);
658 - (ITMTRemotePlayerRepeatMode)repeatMode
660 FourCharCode m00f = 0;
662 ITDebugLog(@"Getting repeat mode.");
663 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) typeCodeValue];
666 ITDebugLog(@"No current playlist, getting repeat mode from visible playlist.");
667 m00f = (FourCharCode)[ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pRpt'), from:obj { form:'prop', want:type('prop'), seld:type('pPly'), from:obj { form:'indx', want:type('cBrW'), seld:1, from:'null'() } } }", 'core', 'getd', &savedPSN) typeCodeValue];
674 ITDebugLog(@"Repeat off");
675 result = ITMTRemotePlayerRepeatOff;
678 ITDebugLog(@"Repeat one");
679 result = ITMTRemotePlayerRepeatOne;
682 ITDebugLog(@"Repeat all");
683 result = ITMTRemotePlayerRepeatAll;
686 ITDebugLog(@"Getting repeat mode done.");
690 - (BOOL)setRepeatMode:(ITMTRemotePlayerRepeatMode)repeatMode
693 ITDebugLog(@"Setting repeat mode to %i", repeatMode);
696 case ITMTRemotePlayerRepeatOne:
699 case ITMTRemotePlayerRepeatAll:
702 case ITMTRemotePlayerRepeatOff:
707 if ([[self playerStateUniqueIdentifier] isEqualToString:@"0-0"]) {
708 ITDebugLog(@"No current playlist, setting repeat mode on visible playlist.");
709 ITSendAEWithString([NSString stringWithFormat:@"data:'%s', '----':obj { form:'prop', want:type('prop'), seld:type('pRpt'), from:obj { form:'prop', want:type('prop'), seld:type('pPly'), from:obj { form:'indx', want:type('cBrW'), seld:1, from:'null'() } } }", m00f], 'core', 'setd', &savedPSN);
711 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);
713 ITDebugLog(@"Setting repeat mode to %c done", m00f);
720 ITSendAE('hook', 'Play', &savedPSN);
721 ITDebugLog(@"Play done");
727 ITDebugLog(@"Pause");
728 ITSendAE('hook', 'Paus', &savedPSN);
729 ITDebugLog(@"Pause done");
735 ITDebugLog(@"Go to next track");
736 ITSendAE('hook', 'Next', &savedPSN);
737 ITDebugLog(@"Go to next track done");
741 - (BOOL)goToPreviousSong
743 ITDebugLog(@"Go to previous track");
744 ITSendAE('hook', 'Back', &savedPSN);
745 ITDebugLog(@"Go to previous track done");
751 ITDebugLog(@"Fast forward action");
752 ITSendAE('hook', 'Fast', &savedPSN);
753 ITDebugLog(@"Fast forward action done");
759 ITDebugLog(@"Rewind action");
760 ITSendAE('hook', 'Rwnd', &savedPSN);
761 ITDebugLog(@"Rewind action done");
765 - (BOOL)switchToPlaylistAtIndex:(int)index
767 ITDebugLog(@"Switching to playlist at index %i", index);
768 ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'indx', want:type('cPly'), seld:long(%lu), from:() }", index], 'hook', 'Play', &savedPSN);
769 ITDebugLog(@"Done switching to playlist at index %i", index);
773 - (BOOL)switchToPlaylistAtIndex:(int)index ofSourceAtIndex:(int)index2
775 ITDebugLog(@"Switching to playlist at index %i of source %i", index, index2);
776 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);
777 ITDebugLog(@"Done switching to playlist at index %i of source %i", index, index2);
781 - (BOOL)switchToSongAtIndex:(int)index
783 ITDebugLog(@"Switching to track at index %i", index);
784 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);
785 ITDebugLog(@"Done switching to track at index %i", index);
789 - (BOOL)switchToEQAtIndex:(int)index
791 ITDebugLog(@"Switching to EQ preset at index %i", index);
792 // index should count from 0, but itunes counts from 1, so let's add 1.
793 [self setEqualizerEnabled:YES];
794 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);
795 ITDebugLog(@"Done switching to EQ preset at index %i", index);
799 - (BOOL)makePlaylistWithTerm:(NSString *)term ofType:(int)type
803 //Get fixed indexing status
804 BOOL fixed = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", 'core', 'getd', &savedPSN) booleanValue];
806 //Enabled fixed indexing
807 ITSendAEWithString(@"data:long(1), '----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", 'core', 'setd', &savedPSN);
809 //Search for the term
810 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);
812 //If MenuTunes playlist exists
813 if ([ITSendAEWithString(@"'----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'core', 'doex', &savedPSN) booleanValue]) {
814 //Clear old MenuTunes playlist
815 int numSongs = [ITSendAEWithString(@"kocl:type('cTrk'), '----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'core', 'cnte', &savedPSN) int32Value];
816 for (i = 1; i <= numSongs; i++) {
817 ITSendAEWithString(@"'----':obj { form:'indx', want:type('cTrk'), seld:long(1), from:obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() } }", 'core', 'delo', &savedPSN);
820 //Create MenuTunes playlist
821 ITSendAEWithString(@"prdt:{ pnam:\"MenuTunes\" }, kocl:type('cPly'), &subj:()", 'core', 'crel', &savedPSN);
824 //Duplicate search results to playlist
825 for (i = 1; i <= [searchResults numberOfItems]; i++) {
826 //NSLog(@"%@", ITSendAEWithStringAndParameter(@"'----':obj { form:'prop', want:type('prop'), seld:prop('pnam'), from:aevt(@) }", *[[searchResults descriptorAtIndex:i] aeDesc], 'core', 'getd', &savedPSN));
828 ITSendAEWithStringAndObject(@"insh:obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", [[searchResults descriptorAtIndex:i] aeDesc], 'core', 'clon', &savedPSN);
830 //Reset fixed indexing
831 ITSendAEWithString([NSString stringWithFormat:@"data:long(%i), '----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", fixed], 'core', 'setd', &savedPSN);
833 //Play MenuTunes playlist
834 ITSendAEWithString(@"'----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'hook', 'Play', &savedPSN);
841 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');
844 - (void)notificationHandler:(NSNotification *)note
846 ITDebugLog(@"Received notification: %@", note);
847 [[NSNotificationCenter defaultCenter] postNotificationName:@"ITMTTrackChanged" object:self userInfo:[note userInfo]];
848 ITDebugLog(@"Handled notification.");
851 - (ProcessSerialNumber)iTunesPSN
853 /*NSArray *apps = [[NSWorkspace sharedWorkspace] launchedApplications];
854 ProcessSerialNumber number;
856 int count = [apps count];
858 number.highLongOfPSN = kNoProcess;
860 for (i = 0; i < count; i++)
862 NSDictionary *curApp = [apps objectAtIndex:i];
864 if ([[curApp objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"])
866 number.highLongOfPSN = [[curApp objectForKey:
867 @"NSApplicationProcessSerialNumberHigh"] intValue];
868 number.lowLongOfPSN = [[curApp objectForKey:
869 @"NSApplicationProcessSerialNumberLow"] intValue];
873 ProcessSerialNumber number;
874 number.highLongOfPSN = kNoProcess;
875 number.lowLongOfPSN = 0;
876 ITDebugLog(@"Getting iTunes' PSN.");
877 while ( (GetNextProcess(&number) == noErr) )
880 if ( (CopyProcessName(&number, &name) == noErr) )
882 if ([(NSString *)name isEqualToString:@"iTunes"])
884 ITDebugLog(@"iTunes' highLPongOfPSN: %lu.", number.highLongOfPSN);
885 ITDebugLog(@"iTunes' lowLongOfPSN: %lu.", number.lowLongOfPSN);
886 ITDebugLog(@"Done getting iTunes' PSN.");
889 [(NSString *)name release];
892 ITDebugLog(@"Failed getting iTunes' PSN.");
896 - (NSString*)formatTimeInSeconds:(long)seconds {
897 long final = seconds;
898 NSString *finalString;
901 finalString = [NSString stringWithFormat:@"%i:%@:%@",(final / 3600),[self zeroSixty:(int)((final % 3600) / 60)],[self zeroSixty:(int)((final % 3600) % 60)]];
903 finalString = [NSString stringWithFormat:@"%i:%@",(final / 60),[self zeroSixty:(int)(final % 60)]];
906 finalString = [NSString stringWithFormat:@"0:%@",[self zeroSixty:(int)final]];
910 - (NSString*)zeroSixty:(int)seconds {
911 if ( (seconds < 10) && (seconds > 0) ) {
912 return [NSString stringWithFormat:@"0%i",seconds];
913 } else if ( (seconds == 0) ) {
914 return [NSString stringWithFormat:@"00"];
916 return [NSString stringWithFormat:@"%i",seconds];