Upped the timeout for the albums and artist method to 600.
[MenuTunes.git] / iTunesRemote.m
1 #import "iTunesRemote.h"
2 #import "PlaylistNode.h"
3
4 @implementation iTunesRemote
5
6 + (id)remote
7 {
8     return [[[iTunesRemote alloc] init] autorelease];
9 }
10
11 - (NSString *)remoteTitle
12 {
13     return @"iTunes Remote";
14 }
15
16 - (NSString *)remoteInformation
17 {
18     return @"Default MenuTunes plugin to control iTunes, by iThink Software.";
19 }
20
21 - (NSImage *)remoteIcon
22 {
23     return nil;
24 }
25
26 - (BOOL)begin
27 {
28     ITDebugLog(@"iTunesRemote begun");
29     savedPSN = [self iTunesPSN];
30         [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationHandler:) name:@"com.apple.iTunes.playerInfo" object:@"com.apple.iTunes.player"];
31         
32         NSString *iTunesPath = [[NSUserDefaults standardUserDefaults] stringForKey:@"CustomPlayerPath"];
33         NSDictionary *iTunesInfoPlist;
34         float iTunesVersion;
35
36         //Check if iTunes 5.0 or later is installed     
37         if (!iTunesPath) {
38                 iTunesPath = [[NSWorkspace sharedWorkspace] fullPathForApplication:@"iTunes.app"];
39         }
40         iTunesInfoPlist = [[NSBundle bundleWithPath:iTunesPath] infoDictionary];
41         iTunesVersion = [[iTunesInfoPlist objectForKey:@"CFBundleVersion"] floatValue];
42         ITDebugLog(@"iTunes version found: %f.", iTunesVersion);
43         _iTunesVersion = iTunesVersion;
44         
45     return YES;
46 }
47
48 - (BOOL)halt
49 {
50     ITDebugLog(@"iTunesRemote halted");
51         [[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
52     return YES;
53 }
54
55 - (NSString *)playerFullName
56 {
57     return @"iTunes";
58 }
59
60 - (NSString *)playerSimpleName
61 {
62     return @"iTunes";
63 }
64
65 - (NSDictionary *)capabilities
66 {
67     return [NSDictionary dictionaryWithObjectsAndKeys:
68                 [NSNumber numberWithBool: YES], @"Remote",
69                 [NSNumber numberWithBool: YES], @"Basic Track Control",
70                 [NSNumber numberWithBool: YES], @"Track Information",
71                 [NSNumber numberWithBool: YES], @"Track Navigation",
72                 [NSNumber numberWithBool: YES], @"Upcoming Songs",
73                 [NSNumber numberWithBool: YES], @"Playlists",
74                 [NSNumber numberWithBool: YES], @"Volume",
75                 [NSNumber numberWithBool: YES], @"Shuffle",
76                 [NSNumber numberWithBool: YES], @"Repeat Modes",
77                 [NSNumber numberWithBool: YES], @"Equalizer",
78                 [NSNumber numberWithBool: YES], @"Track Rating",
79                 nil];
80 }
81
82 - (BOOL)showPrimaryInterface
83 {
84     ITDebugLog(@"Showing player primary interface.");
85     
86     if ([self playerRunningState] == ITMTRemotePlayerRunning) {
87         ITDebugLog(@"Showing player interface.");
88         //If not minimized and visible
89         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) &&
90                          ([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) &&
91              [[[[NSWorkspace sharedWorkspace] activeApplication] objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"] ) {
92             //set minimized of browser window 1 to true
93                         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);
94         } else {
95             //set minimized of browser window 1 to false
96                         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);
97         }
98         //set visible of browser window 1 to true
99                 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);
100         //active iTunes
101                 ITSendAEWithString(@"data:long(1), '----':obj { form:'prop', want:type('prop'), seld:type('pisf'), from:'null'() }", 'core', 'setd', &savedPSN);
102         ITDebugLog(@"Done showing player primary interface.");
103         return YES;
104     } else {
105         NSString *path;
106         ITDebugLog(@"Launching player.");
107         if ( (path = [[NSUserDefaults standardUserDefaults] stringForKey:@"CustomPlayerPath"]) ) {
108         } else {
109             path = [self playerFullName];
110         }
111         if (![[NSWorkspace sharedWorkspace] launchApplication:path]) {
112             ITDebugLog(@"Error Launching Player");
113             return NO;
114         }
115         return YES;
116     }
117 }
118
119 - (ITMTRemotePlayerRunningState)playerRunningState
120 {
121     NSArray *apps = [[NSWorkspace sharedWorkspace] launchedApplications];
122     int i;
123     int count = [apps count];
124     
125     for (i = 0; i < count; i++) {
126         if ([[[apps objectAtIndex:i] objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"]) {
127             ITDebugLog(@"Player running state: 1");
128             return ITMTRemotePlayerRunning;
129         }
130     }
131     ITDebugLog(@"Player running state: 0");
132     return ITMTRemotePlayerNotRunning;
133 }
134
135 - (ITMTRemotePlayerPlayingState)playerPlayingState
136 {
137     SInt32 result;
138     
139     ITDebugLog(@"Getting player playing state");
140     result = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pPlS'), from:'null'() }", 'core', 'getd', &savedPSN) typeCodeValue];
141     switch (result)
142     {
143         case 'kPSP':
144             ITDebugLog(@"Getting player playing state done. Player state: Playing");
145             return ITMTRemotePlayerPlaying;
146         case 'kPSp':
147             ITDebugLog(@"Getting player playing state done. Player state: Paused");
148             return ITMTRemotePlayerPaused;
149         case 'kPSR':
150             ITDebugLog(@"Getting player playing state done. Player state: Rewinding");
151             return ITMTRemotePlayerRewinding;
152         case 'kPSF':
153             ITDebugLog(@"Getting player playing state done. Player state: Forwarding");
154             return ITMTRemotePlayerForwarding;
155         case 'kPSS':
156         default:
157             ITDebugLog(@"Getting player playing state done. Player state: Stopped");
158             return ITMTRemotePlayerStopped;
159     }
160     ITDebugLog(@"Getting player playing state done. Player state: Stopped");
161     return ITMTRemotePlayerStopped;
162 }
163
164 /*- (NSArray *)playlists
165 {
166     long i = 0;
167     const signed long numPlaylists = [[ITAppleEventCenter sharedCenter] sendAEWithSendStringForNumber:@"kocl:type('cPly'), '----':()" eventClass:@"core" eventID:@"cnte" appPSN:savedPSN];
168     NSMutableArray *playlists = [[NSMutableArray alloc] initWithCapacity:numPlaylists];
169     
170     for (i = 1; i <= numPlaylists; i++) {
171         const long j = i;
172         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];
173         NSString *theObj = [[ITAppleEventCenter sharedCenter] sendAEWithSendString:sendStr eventClass:@"core" eventID:@"getd" appPSN:savedPSN];
174         [playlists addObject:theObj];
175     }
176     return [playlists autorelease];
177 }*/
178
179 //Full source awareness
180 /*- (NSArray *)playlists
181 {
182     unsigned long i, k;
183     SInt32 numSources = [ITSendAEWithString(@"kocl:type('cSrc'), '----':()", 'core', 'cnte', &savedPSN) int32Value];
184     NSMutableArray *allSources = [[NSMutableArray alloc] init];
185     
186     ITDebugLog(@"Getting playlists.");
187     if (numSources == 0) {
188                 [allSources release];
189         ITDebugLog(@"No sources.");
190         return nil;
191     }
192     
193     for (k = 1; k <= numSources ; k++) {
194         SInt32 numPlaylists = [ITSendAEWithString([NSString stringWithFormat:@"kocl:type('cPly'), '----':obj { form:'indx', want:type('cSrc'), seld:long(%u), from:() }",k], 'core', 'cnte', &savedPSN) int32Value];
195         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];
196         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];
197         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];
198         unsigned long class;
199         if (sourceName) {
200             NSMutableArray *aSource = [[NSMutableArray alloc] init];
201             [aSource addObject:sourceName];
202             switch (fourcc) {
203                 case 'kTun':
204                     class = ITMTRemoteRadioSource;
205                     break;
206                 case 'kDev':
207                     class = ITMTRemoteGenericDeviceSource;
208                     break;
209                 case 'kPod':
210                     class = ITMTRemoteiPodSource;
211                     break;
212                 case 'kMCD':
213                 case 'kACD':
214                     class = ITMTRemoteCDSource;
215                     break;
216                 case 'kShd':
217                     class = ITMTRemoteSharedLibrarySource;
218                     break;
219                 case 'kUnk':
220                 case 'kLib':
221                 default:
222                     class = ITMTRemoteLibrarySource;
223                     break;
224             }
225             ITDebugLog(@"Adding source %@ of type %i at index %i", sourceName, class, index);
226             [aSource addObject:[NSNumber numberWithInt:class]];
227             [aSource addObject:[NSNumber numberWithInt:index]];
228             for (i = 1; i <= numPlaylists; i++) {
229                 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];
230                 NSString *theObj = [ITSendAEWithString(sendStr, 'core', 'getd', &savedPSN) stringValue];
231                 ITDebugLog(@" - Adding playlist %@", theObj);
232                 if (theObj) {
233                     [aSource addObject:theObj];
234                 }
235             }
236             [allSources addObject:[aSource autorelease]];
237         } else {
238             ITDebugLog(@"Source at index %i disappeared.", k);
239         }
240     }
241         NSLog(@"playlists: %@", allSources);
242     ITDebugLog(@"Finished getting playlists.");
243     return [allSources autorelease];
244 }*/
245
246 - (NSArray *)playlists
247 {
248         SInt32 numSources = [ITSendAEWithString(@"kocl:type('cSrc'), '----':()", 'core', 'cnte', &savedPSN) int32Value];
249         NSMutableArray *sources = [[NSMutableArray alloc] init];
250         int i;
251         
252         ITDebugLog(@"Getting playlists.");
253     if (numSources == 0) {
254                 [sources release];
255         ITDebugLog(@"No sources.");
256         return nil;
257     }
258         
259         //Loop through each source
260         for (i = 1; i <= numSources; i++) {
261         FourCharCode fourcc = [ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pKnd'), from:obj { form:'indx', want:type('cSrc'), seld:long(%i), from:() } }", i], 'core', 'getd', &savedPSN) typeCodeValue]; //Type of the current source
262         NSString *sourceName = [ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'indx', want:type('cSrc'), seld:long(%i), from:() } }", i], 'core', 'getd', &savedPSN) stringValue]; //Name of the current source
263         SInt32 index = [ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pidx'), from:obj { form:'indx', want:type('cSrc'), seld:long(%i), from:() } }", i], 'core', 'getd', &savedPSN) int32Value]; //Index of the current source
264         ITMTRemotePlayerSource class; //The class of the current source
265                 
266                 //Make a new PlaylistNode for this source
267                 PlaylistNode *sourceNode = [PlaylistNode playlistNodeWithName:sourceName type:ITMTSourceNode index:index];
268                 
269                 switch (fourcc) {
270                         case 'kTun':
271                                 class = ITMTRemoteRadioSource;
272                                 break;
273                         case 'kDev':
274                                 class = ITMTRemoteGenericDeviceSource;
275                                 break;
276                         case 'kPod':
277                                 class = ITMTRemoteiPodSource;
278                                 break;
279                         case 'kMCD':
280                         case 'kACD':
281                                 class = ITMTRemoteCDSource;
282                                 break;
283                         case 'kShd':
284                                 class = ITMTRemoteSharedLibrarySource;
285                                 break;
286                         case 'kUnk':
287                         case 'kLib':
288                         default:
289                                 class = ITMTRemoteLibrarySource;
290                                 break;
291                 }
292                 [sourceNode setSourceType:class];
293                 ITDebugLog(@"New source %@ of type %i at index %i", sourceName, class, index);
294                 
295                 int j;
296                 SInt32 numPlaylists = [ITSendAEWithString([NSString stringWithFormat:@"kocl:type('cPly'), '----':obj { form:'indx', want:type('cSrc'), seld:long(%i), from:() }", i], 'core', 'cnte', &savedPSN) int32Value]; //Number of playlists in the current source
297                 
298                 //Pass 1, add all the playlists into the main array
299                 for (j = 1; j <= numPlaylists; j++) {
300                         NSString *sendStr = [NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'indx', want:type('cPly'), seld:long(%i), from:obj { form:'indx', want:type('cSrc'), seld:long(%i), from:() } } }", j, (_iTunesVersion >= 5) ? i : index];
301                         NSString *parentSendStr = [NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pnam'), from:obj { form:'prop', want:type('prop'), seld:type('pPlP'), from:obj { form:'indx', want:type('cPly'), seld:long(%i), from:obj { form:'indx', want:type('cSrc'), seld:long(%i), from:() } } } }", j, i];
302                         NSString *theObj = [ITSendAEWithString(sendStr, 'core', 'getd', &savedPSN) stringValue], *parent = [ITSendAEWithString(parentSendStr, 'core', 'getd', &savedPSN) stringValue];
303                         ITDebugLog(@" - Adding playlist %@", theObj);
304                         if (theObj) {
305                                 FourCharCode code = [ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pSpK'), from:obj { form:'indx', want:type('cPly'), seld:long(%i), from:obj { form:'indx', want:type('cSrc'), seld:long(%i), from:() } } }", j, i], 'core', 'getd', &savedPSN) typeCodeValue];
306                                 ITMTNodeType type;
307                                 switch (code) {
308                                         case 'kSpN':
309                                                 type = ITMTPlaylistNode;
310                                                 break;
311                                         case 'kSpF':
312                                                 type = ITMTFolderNode;
313                                                 break;
314                                         case 'kSpS':
315                                                 type = ITMTPartyShuffleNode;
316                                                 break;
317                                         case 'kSpP':
318                                                 type = ITMTPodcastsNode;
319                                                 break;
320                                         case 'kSpM':
321                                                 type = ITMTPurchasedMusicNode;
322                                                 break;
323                                         case 'kSpV':
324                                                 type = ITMTVideosNode;
325                                                 break;
326                                 }
327                                 PlaylistNode *node = [PlaylistNode playlistNodeWithName:theObj type:type index:j];
328                                 [[sourceNode children] addObject:node];
329                                 if (parent) {
330                                         int parentIndex = [ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('pidx'), from:obj { form:'prop', want:type('prop'), seld:type('pPlP'), from:obj { form:'indx', want:type('cPly'), seld:long(%i), from:obj { form:'indx', want:type('cSrc'), seld:long(%i), from:() } } } }", j, i], 'core', 'getd', &savedPSN) int32Value];
331                                         [node setParent:[PlaylistNode playlistNodeWithName:parent type:ITMTFolderNode index:parentIndex]];
332                                 } else {
333                                         [node setParent:sourceNode];
334                                 }
335                         }
336                 }
337                 
338                 //Pass 2, nest each item under its proper parent. Once everything has been nested, delete the original from the main array.
339                 NSEnumerator *enumerator = [[sourceNode children] objectEnumerator];
340                 PlaylistNode *nextNode;
341                 NSMutableArray *nested = [[NSMutableArray alloc] init];
342                 
343                 while ( (nextNode = [enumerator nextObject]) ) {
344                         PlaylistNode *pNode = [nextNode parent];
345                         if ([pNode type] == ITMTFolderNode) {
346                                 PlaylistNode *newParent = nil;
347                                 int k;
348                                 for (k = 0; !newParent; k++) {
349                                         PlaylistNode *test = [[sourceNode children] objectAtIndex:k];
350                                         if ([test index] == [pNode index]) {
351                                                 newParent = test;
352                                         }
353                                 }
354                                 [[[nextNode parent] children] removeObject:nextNode];
355                                 [nextNode setParent:newParent];
356                                 [[newParent children] addObject:nextNode];
357                                 [newParent setType:ITMTFolderNode];
358                                 [nested addObject:nextNode];
359                         }
360                 }
361                 
362                 NSEnumerator *nestEnumerator = [nested objectEnumerator];
363                 while ( (nextNode = [nestEnumerator nextObject]) ) {
364                         [[sourceNode children] removeObject:nextNode];
365                         [nested removeObject:nextNode];
366                 }
367                 [nested release];
368                 
369                 //Move all the folders to the beginning of the list
370                 //Move the podcasts playlist to the top
371                 BOOL movedPodcasts = NO;
372                 enumerator = [[sourceNode children] reverseObjectEnumerator];
373                 while ( (nextNode = [enumerator nextObject]) ) {
374                         if ([nextNode type] == ITMTPodcastsNode) {
375                                 [[sourceNode children] removeObject:nextNode];
376                                 [[sourceNode children] insertObject:nextNode atIndex:1];
377                                 movedPodcasts = YES;
378                         } else if ([nextNode type] == ITMTFolderNode) {
379                                 [[sourceNode children] removeObject:nextNode];
380                                 [[sourceNode children] insertObject:nextNode atIndex:1 + movedPodcasts];
381                         }
382                 }
383                 
384                 [sources addObject:sourceNode];
385         }
386         
387         return [sources autorelease];
388 }
389
390 - (NSArray *)artists
391 {
392     NSAppleEventDescriptor *rawr = ITSendAEWithStringAndTimeout(@"'----':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, 600);
393     int i;
394     NSMutableArray *array = [[NSMutableArray alloc] init];
395     NSArray *returnArray;
396     for (i = 1; i <= [rawr numberOfItems]; i++) {
397         NSString *artist = [[rawr descriptorAtIndex:i] stringValue];
398         if (artist && [artist length] && ![array containsObject:artist]) {
399             [array addObject:artist];
400         }
401     }
402     [array sortUsingSelector:@selector(caseInsensitiveCompare:)];
403     returnArray = [NSArray arrayWithArray:array];
404     [array release];
405     return returnArray;
406 }
407
408 - (NSArray *)albums
409 {
410     NSAppleEventDescriptor *rawr = ITSendAEWithStringAndTimeout(@"'----':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, 600);
411     int i;
412     NSMutableArray *array = [[NSMutableArray alloc] init];
413     NSArray *returnArray;
414     for (i = 1; i <= [rawr numberOfItems]; i++) {
415         NSString *album = [[rawr descriptorAtIndex:i] stringValue];
416         if (album && [album length] && ![array containsObject:album]) {
417             [array addObject:album];
418         }
419     }
420     [array sortUsingSelector:@selector(caseInsensitiveCompare:)];
421     returnArray = [NSArray arrayWithArray:array];
422     [array release];
423     return returnArray;
424 }
425
426 - (int)numberOfSongsInPlaylistAtIndex:(int)index
427 {
428         /*
429                 This method only returns the proper number if there's something playing.
430                 This is because it gets the container of the current playlist so that it
431                 gets the playlist index from the current source. Operating this way is fine,
432                 since MT only ever calls this method when there is something playlist.
433                 A working version of this that works in just the main source is in the
434                 makePlaylistWithTerm:ofType: method.
435         */
436     int temp1;
437         NSAppleEventDescriptor *result;
438     ITDebugLog(@"Getting number of songs in playlist at index %i", index);
439         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);
440         temp1 = (result == nil) ? -1 : (int)[result int32Value];
441     ITDebugLog(@"Getting number of songs in playlist at index %i done", index);
442     return temp1;
443 }
444
445 - (ITMTRemotePlayerSource)currentSource
446 {
447     SInt32 fourcc;
448
449     ITDebugLog(@"Getting current source.");   
450     
451     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) typeCodeValue] : 'kLib';
452     
453     switch (fourcc) {
454         case 'kTun':
455             ITDebugLog(@"Getting current source done. Source: Radio.");
456             return ITMTRemoteRadioSource;
457             break;
458         case 'kDev':
459             ITDebugLog(@"Getting current source done. Source: Generic Device.");
460             return ITMTRemoteGenericDeviceSource;
461         case 'kPod':
462             ITDebugLog(@"Getting current source done. Source: iPod.");
463             return ITMTRemoteiPodSource; //this is stupid
464             break;
465         case 'kMCD':
466         case 'kACD':
467             ITDebugLog(@"Getting current source done. Source: CD.");
468             return ITMTRemoteCDSource;
469             break;
470         case 'kShd':
471             ITDebugLog(@"Getting current source done. Source: Shared Library.");
472             return ITMTRemoteSharedLibrarySource;
473             break;
474         case 'kUnk':
475         case 'kLib':
476         default:
477             ITDebugLog(@"Getting current source done. Source: Library.");
478             return ITMTRemoteLibrarySource;
479             break;
480     }
481 }
482
483 - (int)currentSourceIndex
484 {
485     ITDebugLog(@"Getting current source.");
486     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];
487 }
488
489 - (ITMTRemotePlayerPlaylistClass)currentPlaylistClass
490 {
491     FourCharCode realResult;
492     ITDebugLog(@"Getting current playlist class");
493     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) typeCodeValue];
494     switch (realResult)
495            {
496            case 'cLiP':
497                ITDebugLog(@"Getting current playlist class done. Class: Library.");
498                return ITMTRemotePlayerLibraryPlaylist;
499                break;
500            case 'cRTP':
501                ITDebugLog(@"Getting current playlist class done. Class: Radio.");
502                return ITMTRemotePlayerRadioPlaylist;
503                break;
504            default:
505                ITDebugLog(@"Getting current playlist class done. Class: Standard playlist.");
506                return ITMTRemotePlayerPlaylist;
507            }
508 }
509
510 - (int)currentPlaylistIndex
511 {  
512     int temp1;
513     ITDebugLog(@"Getting current playlist index.");
514     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);
515     ITDebugLog(@"Getting current playlist index done.");
516     return temp1;
517 }
518
519 - (NSString *)songTitleAtIndex:(int)index
520 {
521     NSString *temp1;
522     ITDebugLog(@"Getting song title at index %i.", index);
523     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];
524     ITDebugLog(@"Getting song title at index %i done.", index);
525     return ( ([temp1 length]) ? temp1 : nil ) ;
526 }
527
528 - (BOOL)songEnabledAtIndex:(int)index
529 {
530     BOOL temp1;
531     ITDebugLog(@"Getting song enabled at index %i.", index);
532     temp1 = [ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'prop', want:type('prop'), seld:type('enbl'), 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) booleanValue];
533     ITDebugLog(@"Getting song enabled at index %i done.", index);
534     return temp1;
535 }
536
537 - (int)currentAlbumTrackCount
538 {
539     int temp1;
540     ITDebugLog(@"Getting current album track count.");
541     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];
542     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { temp1 = 0; }
543     ITDebugLog(@"Getting current album track count done.");
544     return temp1;
545 }
546
547 - (int)currentSongTrack
548 {
549     int temp1;
550     ITDebugLog(@"Getting current song track.");
551     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];
552     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { temp1 = 0; }
553     ITDebugLog(@"Getting current song track done.");
554     return temp1;
555 }
556
557 - (NSString *)playerStateUniqueIdentifier
558 {
559     NSString *temp1;
560     ITDebugLog(@"Getting current unique identifier.");
561         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);
562         if ([descriptor typeCodeValue] == 'prop') {
563                 return @"0-0";
564         } else if (descriptor == nil) {
565                 return nil;
566         }
567     FourCharCode cls = [descriptor typeCodeValue];
568     if ( ([self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist) || (cls == 'cURT') ) {
569                 NSString *bad = [NSString stringWithUTF8String:"浳湧"];
570         temp1 = [ITSendAEWithKey('pStT', 'core', 'getd', &savedPSN) stringValue];
571         if ([temp1 isEqualToString:bad]) {
572             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];
573         }
574     } else {
575         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]];
576     }
577     ITDebugLog(@"Getting current unique identifier done.");
578     return ( ([temp1 length]) ? temp1 : nil ) ;
579 }
580
581 - (int)currentSongIndex
582 {
583     int temp1;
584     ITDebugLog(@"Getting current song index.");
585         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);
586     ITDebugLog(@"Getting current song index done.");
587     return temp1;
588 }
589
590 - (NSString *)currentSongTitle
591 {
592     NSString *temp1;
593     ITDebugLog(@"Getting current song title.");
594     FourCharCode 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) typeCodeValue];
595         
596     //If we're listening to the radio.
597     if (result == 'cURT') {
598         NSString *bad = [NSString stringWithUTF8String:"浳湧"];
599         temp1 = [ITSendAEWithKey('pStT', 'core', 'getd', &savedPSN) stringValue];
600         if ([temp1 isEqualToString:bad]) {
601             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];
602         }
603         temp1 = [temp1 stringByAppendingString:@" (Stream)"];
604     } else if (result == 'prop') {
605                 temp1 = nil;
606         } else {
607         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];
608     }
609     ITDebugLog(@"Getting current song title done.");
610     return ( ([temp1 length]) ? temp1 : nil ) ;
611 }
612
613 - (NSString *)currentSongArtist
614 {
615     NSString *temp1;
616     ITDebugLog(@"Getting current song artist.");
617     if ( [self currentPlaylistClass] != ITMTRemotePlayerRadioPlaylist ) {
618         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];
619     } else {
620         temp1 = @"";
621     }
622     ITDebugLog(@"Getting current song artist done.");
623     return ( ([temp1 length]) ? temp1 : nil ) ;
624 }
625
626 - (NSString *)currentSongComposer
627 {
628     NSString *temp1;
629     ITDebugLog(@"Getting current song artist.");
630     if ( [self currentPlaylistClass] != ITMTRemotePlayerRadioPlaylist ) {
631         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];
632     } else {
633         temp1 = @"";
634     }
635     ITDebugLog(@"Getting current song artist done.");
636     return ( ([temp1 length]) ? temp1 : nil ) ;
637 }
638
639 - (NSString *)currentSongAlbum
640 {
641     NSString *temp1;
642     ITDebugLog(@"Getting current song album.");
643     if ( [self currentPlaylistClass] != ITMTRemotePlayerRadioPlaylist ) {
644         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];
645     } else {
646         temp1 = @"";
647     }
648     ITDebugLog(@"Getting current song album done.");
649     return ( ([temp1 length]) ? temp1 : nil ) ;
650 }
651
652 - (NSString *)currentSongGenre
653 {
654     NSString *temp1;
655     ITDebugLog(@"Getting current song genre.");
656     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];
657     ITDebugLog(@"Getting current song genre done.");
658     return ( ([temp1 length]) ? temp1 : nil ) ;
659 }
660
661 - (NSString *)currentSongLength
662 {
663     SInt32 temp1;
664     NSString *temp2;
665     ITDebugLog(@"Getting current song length.");
666     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];
667     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];
668     if ( ([self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist) || (temp1 == 'cURT') ) { temp2 = @"Continuous"; }
669     ITDebugLog(@"Getting current song length done.");
670     return temp2;
671 }
672
673 - (NSString *)currentSongRemaining
674 {
675     SInt32 duration, current, final;
676     NSString *finalString;
677     
678     ITDebugLog(@"Getting current song remaining time.");
679     
680     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];
681     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];
682     final = duration - current;
683     finalString = [self formatTimeInSeconds:final];
684     
685     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { finalString = nil; }
686     
687     ITDebugLog(@"Getting current song remaining time done.");
688     
689     return finalString;
690 }
691
692 - (NSString *)currentSongElapsed
693 {
694     long final;
695     NSString *finalString;
696     
697     ITDebugLog(@"Getting current song elapsed time.");
698         final = (long)[ITSendAEWithKey('pPos', 'core', 'getd', &savedPSN) int32Value];
699     finalString = [self formatTimeInSeconds:final];
700     ITDebugLog(@"Getting current song elapsed time done.");
701     return finalString;
702 }
703
704 - (NSImage *)currentSongAlbumArt
705 {
706     ITDebugLog(@"Getting current song album art.");
707     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;
708     ITDebugLog(@"Getting current song album art done.");    
709     if (data) {
710         return [[[NSImage alloc] initWithData:data] autorelease];
711     } else {
712         return nil;
713     }
714 }
715
716 - (int)currentSongPlayCount
717 {
718     int count;
719     ITDebugLog(@"Getting current song play count.");
720     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];
721     ITDebugLog(@"Getting current song play count done.");
722     return count;
723 }
724
725 - (float)currentSongRating
726 {
727     float temp1;
728     ITDebugLog(@"Getting current song rating.");
729     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);
730     ITDebugLog(@"Getting current song rating done.");
731     return temp1;
732 }
733
734 - (BOOL)setCurrentSongRating:(float)rating
735 {
736     ITDebugLog(@"Setting current song rating to %f.", rating);
737     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { return NO; }
738         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);
739     ITDebugLog(@"Setting current song rating to %f done.", rating);
740     return YES;
741 }
742
743 - (BOOL)currentSongShufflable
744 {
745         BOOL temp1;
746         ITDebugLog(@"Getting current song shufflable status.");
747     temp1 = (![self isPlaying] || ([self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist)) ? NO : [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pSfa'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) booleanValue];
748     ITDebugLog(@"Getting current song shufflable status done.");
749     return temp1;
750 }
751
752 - (BOOL)setCurrentSongShufflable:(BOOL)shufflable
753 {
754         ITDebugLog(@"Setting current song shufflable status to %i.", shufflable);
755     if ([self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist) {
756                 ITDebugLog(@"Not a valid track to set status to, returning.");
757                 return NO;
758         }
759         ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pSfa'), from:obj { form:'indx', want:type('cTrk'), seld:long(%lu), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } } }", shufflable, [self currentSongIndex]], 'core', 'setd', &savedPSN);
760     ITDebugLog(@"Setting current song shufflable status to %i done.", shufflable);
761     return YES;
762 }
763
764 - (BOOL)equalizerEnabled
765 {
766     ITDebugLog(@"Getting equalizer enabled status.");
767     int thingy = (int)[ITSendAEWithKey('pEQ ', 'core', 'getd', &savedPSN) int32Value];
768     ITDebugLog(@"Done getting equalizer enabled status.");
769     return (thingy != 0) ? YES : NO;
770 }
771
772 - (BOOL)setEqualizerEnabled:(BOOL)enabled
773 {
774     ITDebugLog(@"Setting equalizer enabled to %i.", enabled);
775         ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pEQ '), from:'null'() }", enabled], 'core', 'setd', &savedPSN);
776     ITDebugLog(@"Done setting equalizer enabled to %i.", enabled);
777     return YES;
778 }
779
780 - (NSArray *)eqPresets
781 {
782     int i;
783     SInt32 numPresets = [ITSendAEWithString(@"kocl:type('cEQP'), '----':(), &subj:()", 'core', 'cnte', &savedPSN) int32Value];
784     NSMutableArray *presets = [[NSMutableArray alloc] initWithCapacity:numPresets];
785     ITDebugLog(@"Getting EQ presets");
786     for (i = 1; i <= numPresets; i++) {
787         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];
788         if (theObj) {
789             ITDebugLog(@"Adding preset %@", theObj);
790             [presets addObject:theObj];
791         }
792     }
793     ITDebugLog(@"Done getting EQ presets");
794     return [presets autorelease];
795 }
796
797 - (int)currentEQPresetIndex
798 {
799     int result;
800     ITDebugLog(@"Getting current EQ preset index.");
801     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];
802     ITDebugLog(@"Getting current EQ preset index done.");
803     return result;
804 }
805
806 - (float)volume
807 {
808     ITDebugLog(@"Getting volume.");
809     ITDebugLog(@"Getting volume done.");
810     return (float)[ITSendAEWithKey('pVol', 'core', 'getd', &savedPSN) int32Value] / 100;
811 }
812
813 - (BOOL)setVolume:(float)volume
814 {
815     ITDebugLog(@"Setting volume to %f.", volume);
816         ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pVol'), from:'null'() }", (long)(volume * 100)], 'core', 'setd', &savedPSN);
817     ITDebugLog(@"Setting volume to %f done.", volume);
818     return YES;
819 }
820
821 - (BOOL)shuffleEnabled
822 {
823         int result;
824     ITDebugLog(@"Getting shuffle enabled status.");
825         if (![self isPlaying]) {
826                 ITDebugLog(@"No current playlist, getting shuffle status from visible playlist.");
827                 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];
828         } else {
829                 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];
830         }
831     ITDebugLog(@"Getting shuffle enabled status done.");
832     return (result != 0);
833 }
834
835 - (BOOL)setShuffleEnabled:(BOOL)enabled
836 {
837     ITDebugLog(@"Set shuffle enabled to %i", enabled);
838         if (![self isPlaying]) {
839                 ITDebugLog(@"No current playlist, setting shuffle status on visible playlist.");
840                 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);
841         } else {
842                 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);
843         }
844     ITDebugLog(@"Set shuffle enabled to %i done", enabled);
845     return YES;
846 }
847
848 - (ITMTRemotePlayerRepeatMode)repeatMode
849 {
850     FourCharCode m00f = 0;
851     int result = 0;
852         ITDebugLog(@"Getting repeat mode.");
853     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];
854         
855         if (m00f == 0) {
856                 ITDebugLog(@"No current playlist, getting repeat mode from visible playlist.");
857                 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];
858         }
859         
860     switch (m00f)
861     {
862         //case 'kRp0':
863         case 1800564815:
864             ITDebugLog(@"Repeat off");
865             result = ITMTRemotePlayerRepeatOff;
866             break;
867         case 'kRp1':
868             ITDebugLog(@"Repeat one");
869             result = ITMTRemotePlayerRepeatOne;
870             break;
871         case 'kRpA':
872             ITDebugLog(@"Repeat all");
873             result = ITMTRemotePlayerRepeatAll;
874             break;
875     }
876     ITDebugLog(@"Getting repeat mode done.");
877     return result;
878 }
879
880 - (BOOL)setRepeatMode:(ITMTRemotePlayerRepeatMode)repeatMode
881 {
882     char *m00f;
883     ITDebugLog(@"Setting repeat mode to %i", repeatMode);
884     switch (repeatMode)
885     {
886         case ITMTRemotePlayerRepeatOne:
887             m00f = "kRp1";
888             break;
889         case ITMTRemotePlayerRepeatAll:
890             m00f = "kRpA";
891             break;
892         case ITMTRemotePlayerRepeatOff:
893         default:
894             m00f = "kRp0";
895             break;
896     }
897         if (![self isPlaying]) {
898                 ITDebugLog(@"No current playlist, setting repeat mode on visible playlist.");
899                 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);
900         } else {
901                 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);
902         }
903     ITDebugLog(@"Setting repeat mode to %c done", m00f);
904     return YES;
905 }
906
907 - (BOOL)play
908 {
909     ITDebugLog(@"Play");
910         ITSendAE('hook', 'Play', &savedPSN);
911     ITDebugLog(@"Play done");
912     return YES;
913 }
914
915 - (BOOL)pause
916 {
917     ITDebugLog(@"Pause");
918     ITSendAE('hook', 'Paus', &savedPSN);
919     ITDebugLog(@"Pause done");
920     return YES;
921 }
922
923 - (BOOL)goToNextSong
924 {
925     ITDebugLog(@"Go to next track");
926     ITSendAE('hook', 'Next', &savedPSN);
927     ITDebugLog(@"Go to next track done");
928     return YES;
929 }
930
931 - (BOOL)goToPreviousSong
932 {
933     ITDebugLog(@"Go to previous track");
934     ITSendAE('hook', 'Back', &savedPSN);
935     ITDebugLog(@"Go to previous track done");
936     return YES;
937 }
938
939 - (BOOL)forward
940 {
941     ITDebugLog(@"Fast forward action");
942     ITSendAE('hook', 'Fast', &savedPSN);
943     ITDebugLog(@"Fast forward action done");
944     return YES;
945 }
946
947 - (BOOL)rewind
948 {
949     ITDebugLog(@"Rewind action");
950     ITSendAE('hook', 'Rwnd', &savedPSN);
951     ITDebugLog(@"Rewind action done");
952     return YES;
953 }
954
955 - (BOOL)switchToPlaylistAtIndex:(int)index
956 {
957     ITDebugLog(@"Switching to playlist at index %i", index);
958         ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'indx', want:type('cPly'), seld:long(%lu), from:() }", index], 'hook', 'Play', &savedPSN);
959     ITDebugLog(@"Done switching to playlist at index %i", index);
960     return YES;
961 }
962
963 - (BOOL)switchToPlaylistAtIndex:(int)index ofSourceAtIndex:(int)index2
964 {
965     ITDebugLog(@"Switching to playlist at index %i of source %i", index, index2);
966         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);
967     ITDebugLog(@"Done switching to playlist at index %i of source %i", index, index2);
968     return YES;
969 }
970
971 - (BOOL)switchToSongAtIndex:(int)index
972 {
973     ITDebugLog(@"Switching to track at index %i", index);
974         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);
975     ITDebugLog(@"Done switching to track at index %i", index);
976     return YES;
977 }
978
979 - (BOOL)switchToEQAtIndex:(int)index
980 {
981     ITDebugLog(@"Switching to EQ preset at index %i", index);
982     // index should count from 0, but itunes counts from 1, so let's add 1.
983     [self setEqualizerEnabled:YES];
984         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);
985     ITDebugLog(@"Done switching to EQ preset at index %i", index);
986     return YES;
987 }
988
989 - (BOOL)makePlaylistWithTerm:(NSString *)term ofType:(int)type
990 {
991     int i;
992         
993     //Get fixed indexing status
994     BOOL fixed = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", 'core', 'getd', &savedPSN) booleanValue];
995     
996     //Enabled fixed indexing
997     ITSendAEWithString(@"data:long(1), '----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", 'core', 'setd', &savedPSN);
998     
999     //Search for the term
1000     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);
1001     
1002     //If MenuTunes playlist exists
1003     if ([ITSendAEWithString(@"'----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'core', 'doex', &savedPSN) booleanValue]) {
1004         //Clear old MenuTunes playlist
1005                 int numSongs = [ITSendAEWithString(@"kocl:type('cTrk'), '----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'core', 'cnte', &savedPSN) int32Value];
1006         for (i = 1; i <= numSongs; i++) {
1007             ITSendAEWithString(@"'----':obj { form:'indx', want:type('cTrk'), seld:long(1), from:obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() } }", 'core', 'delo', &savedPSN);
1008         }
1009     } else {
1010         //Create MenuTunes playlist
1011         ITSendAEWithString(@"prdt:{ pnam:\"MenuTunes\" }, kocl:type('cPly'), &subj:()", 'core', 'crel', &savedPSN);
1012     }
1013     
1014     //Duplicate search results to playlist
1015     for (i = 1; i <= [searchResults numberOfItems]; i++) {
1016                 //NSLog(@"%@", ITSendAEWithStringAndParameter(@"'----':obj { form:'prop', want:type('prop'), seld:prop('pnam'), from:aevt(@) }", *[[searchResults descriptorAtIndex:i] aeDesc], 'core', 'getd', &savedPSN));
1017                 
1018         ITSendAEWithStringAndObject(@"insh:obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", [[searchResults descriptorAtIndex:i] aeDesc], 'core', 'clon', &savedPSN);
1019     }
1020     //Reset fixed indexing
1021     ITSendAEWithString([NSString stringWithFormat:@"data:long(%i), '----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", fixed], 'core', 'setd', &savedPSN);
1022     
1023     //Play MenuTunes playlist
1024     ITSendAEWithString(@"'----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'hook', 'Play', &savedPSN);
1025     
1026     return YES;
1027 }
1028
1029 - (BOOL)isPlaying
1030 {
1031         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) typeCodeValue] != 'prop');
1032 }
1033
1034 - (void)notificationHandler:(NSNotification *)note
1035 {
1036         ITDebugLog(@"Received notification: %@", note);
1037         [[NSNotificationCenter defaultCenter] postNotificationName:@"ITMTTrackChanged" object:self userInfo:[note userInfo]];
1038         ITDebugLog(@"Handled notification.");
1039 }
1040
1041 - (ProcessSerialNumber)iTunesPSN
1042 {
1043     /*NSArray *apps = [[NSWorkspace sharedWorkspace] launchedApplications];
1044     ProcessSerialNumber number;
1045     int i;
1046     int count = [apps count];
1047     
1048     number.highLongOfPSN = kNoProcess;
1049     
1050     for (i = 0; i < count; i++)
1051     {
1052         NSDictionary *curApp = [apps objectAtIndex:i];
1053         
1054         if ([[curApp objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"])
1055         {
1056             number.highLongOfPSN = [[curApp objectForKey:
1057                 @"NSApplicationProcessSerialNumberHigh"] intValue];
1058             number.lowLongOfPSN = [[curApp objectForKey:
1059                 @"NSApplicationProcessSerialNumberLow"] intValue];
1060         }
1061     }
1062     return number;*/
1063     ProcessSerialNumber number;
1064     number.highLongOfPSN = kNoProcess;
1065     number.lowLongOfPSN = 0;
1066     ITDebugLog(@"Getting iTunes' PSN.");
1067     while ( (GetNextProcess(&number) == noErr) ) 
1068     {
1069         CFStringRef name;
1070         if ( (CopyProcessName(&number, &name) == noErr) )
1071         {
1072             if ([(NSString *)name isEqualToString:@"iTunes"])
1073             {
1074                 ITDebugLog(@"iTunes' highLPongOfPSN: %lu.", number.highLongOfPSN);
1075                 ITDebugLog(@"iTunes' lowLongOfPSN: %lu.", number.lowLongOfPSN);
1076                 ITDebugLog(@"Done getting iTunes' PSN.");
1077                                 [(NSString *)name release];
1078                 return number;
1079             }
1080             [(NSString *)name release];
1081         }
1082     }
1083     ITDebugLog(@"Failed getting iTunes' PSN.");
1084     return number;
1085 }
1086
1087 - (NSString*)formatTimeInSeconds:(long)seconds {
1088     long final = seconds;
1089     NSString *finalString;
1090     if (final >= 60) {
1091         if (final > 3600) {
1092             finalString = [NSString stringWithFormat:@"%i:%@:%@",(final / 3600),[self zeroSixty:(int)((final % 3600) / 60)],[self zeroSixty:(int)((final % 3600) % 60)]];
1093         } else {
1094             finalString = [NSString stringWithFormat:@"%i:%@",(final / 60),[self zeroSixty:(int)(final % 60)]];
1095         }
1096     } else {
1097         finalString = [NSString stringWithFormat:@"0:%@",[self zeroSixty:(int)final]];
1098     }
1099     return finalString;
1100 }
1101 - (NSString*)zeroSixty:(int)seconds {
1102     if ( (seconds < 10) && (seconds > 0) ) {
1103         return [NSString stringWithFormat:@"0%i",seconds];
1104     } else if ( (seconds == 0) ) {
1105         return [NSString stringWithFormat:@"00"];
1106     } else {
1107         return [NSString stringWithFormat:@"%i",seconds];
1108     }
1109 }
1110
1111 @end