Updated to version 1.6.5
[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 - (int)currentSongPlayed
674 {
675         UInt32 final;
676     ITDebugLog(@"Getting current song played time.");
677         final = [ITSendAEWithKey('pPos', 'core', 'getd', &savedPSN) int32Value];
678     ITDebugLog(@"Getting current song played time done.");
679     return final;
680 }
681
682 - (int)currentSongDuration
683 {
684     SInt32 temp1;
685     ITDebugLog(@"Getting current song duration.");
686     temp1 = [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];
687     ITDebugLog(@"Getting current song duration done.");
688     return temp1;
689 }
690
691 - (NSString *)currentSongRemaining
692 {
693     SInt32 duration, current, final;
694     NSString *finalString;
695     
696     ITDebugLog(@"Getting current song remaining time.");
697     
698     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];
699     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];
700     final = duration - current;
701     finalString = [self formatTimeInSeconds:final];
702     
703     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { finalString = nil; }
704     
705     ITDebugLog(@"Getting current song remaining time done.");
706     
707     return finalString;
708 }
709
710 - (NSString *)currentSongElapsed
711 {
712     long final;
713     NSString *finalString;
714     
715     ITDebugLog(@"Getting current song elapsed time.");
716         final = (long)[ITSendAEWithKey('pPos', 'core', 'getd', &savedPSN) int32Value];
717     finalString = [self formatTimeInSeconds:final];
718     ITDebugLog(@"Getting current song elapsed time done.");
719     return finalString;
720 }
721
722 - (NSImage *)currentSongAlbumArt
723 {
724     ITDebugLog(@"Getting current song album art.");
725     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;
726     ITDebugLog(@"Getting current song album art done.");
727     if (data) {
728         return [[[NSImage alloc] initWithData:data] autorelease];
729     } else {
730         return nil;
731     }
732 }
733
734 - (int)currentSongPlayCount
735 {
736     int count;
737     ITDebugLog(@"Getting current song play count.");
738     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];
739     ITDebugLog(@"Getting current song play count done.");
740     return count;
741 }
742
743 - (float)currentSongRating
744 {
745     float temp1;
746     ITDebugLog(@"Getting current song rating.");
747     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);
748     ITDebugLog(@"Getting current song rating done.");
749     return temp1;
750 }
751
752 - (BOOL)setCurrentSongRating:(float)rating
753 {
754     ITDebugLog(@"Setting current song rating to %f.", rating);
755     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { return NO; }
756         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);
757     ITDebugLog(@"Setting current song rating to %f done.", rating);
758     return YES;
759 }
760
761 - (BOOL)currentSongShufflable
762 {
763         BOOL temp1;
764         ITDebugLog(@"Getting current song shufflable status.");
765     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];
766     ITDebugLog(@"Getting current song shufflable status done.");
767     return temp1;
768 }
769
770 - (BOOL)setCurrentSongShufflable:(BOOL)shufflable
771 {
772         ITDebugLog(@"Setting current song shufflable status to %i.", shufflable);
773     if ([self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist) {
774                 ITDebugLog(@"Not a valid track to set status to, returning.");
775                 return NO;
776         }
777         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);
778     ITDebugLog(@"Setting current song shufflable status to %i done.", shufflable);
779     return YES;
780 }
781
782 - (BOOL)equalizerEnabled
783 {
784     ITDebugLog(@"Getting equalizer enabled status.");
785     int thingy = (int)[ITSendAEWithKey('pEQ ', 'core', 'getd', &savedPSN) int32Value];
786     ITDebugLog(@"Done getting equalizer enabled status.");
787     return (thingy != 0) ? YES : NO;
788 }
789
790 - (BOOL)setEqualizerEnabled:(BOOL)enabled
791 {
792     ITDebugLog(@"Setting equalizer enabled to %i.", enabled);
793         ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pEQ '), from:'null'() }", enabled], 'core', 'setd', &savedPSN);
794     ITDebugLog(@"Done setting equalizer enabled to %i.", enabled);
795     return YES;
796 }
797
798 - (NSArray *)eqPresets
799 {
800     int i;
801     SInt32 numPresets = [ITSendAEWithString(@"kocl:type('cEQP'), '----':(), &subj:()", 'core', 'cnte', &savedPSN) int32Value];
802     NSMutableArray *presets = [[NSMutableArray alloc] initWithCapacity:numPresets];
803     ITDebugLog(@"Getting EQ presets");
804     for (i = 1; i <= numPresets; i++) {
805         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];
806         if (theObj) {
807             ITDebugLog(@"Adding preset %@", theObj);
808             [presets addObject:theObj];
809         }
810     }
811     ITDebugLog(@"Done getting EQ presets");
812     return [presets autorelease];
813 }
814
815 - (int)currentEQPresetIndex
816 {
817     int result;
818     ITDebugLog(@"Getting current EQ preset index.");
819     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];
820     ITDebugLog(@"Getting current EQ preset index done.");
821     return result;
822 }
823
824 - (float)volume
825 {
826     ITDebugLog(@"Getting volume.");
827     ITDebugLog(@"Getting volume done.");
828     return (float)[ITSendAEWithKey('pVol', 'core', 'getd', &savedPSN) int32Value] / 100;
829 }
830
831 - (BOOL)setVolume:(float)volume
832 {
833     ITDebugLog(@"Setting volume to %f.", volume);
834         ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pVol'), from:'null'() }", (long)(volume * 100)], 'core', 'setd', &savedPSN);
835     ITDebugLog(@"Setting volume to %f done.", volume);
836     return YES;
837 }
838
839 - (BOOL)shuffleEnabled
840 {
841         int result;
842     ITDebugLog(@"Getting shuffle enabled status.");
843         if (![self isPlaying]) {
844                 ITDebugLog(@"No current playlist, getting shuffle status from visible playlist.");
845                 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];
846         } else {
847                 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];
848         }
849     ITDebugLog(@"Getting shuffle enabled status done.");
850     return (result != 0);
851 }
852
853 - (BOOL)setShuffleEnabled:(BOOL)enabled
854 {
855     ITDebugLog(@"Set shuffle enabled to %i", enabled);
856         if (![self isPlaying]) {
857                 ITDebugLog(@"No current playlist, setting shuffle status on visible playlist.");
858                 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);
859         } else {
860                 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);
861         }
862     ITDebugLog(@"Set shuffle enabled to %i done", enabled);
863     return YES;
864 }
865
866 - (ITMTRemotePlayerRepeatMode)repeatMode
867 {
868     FourCharCode m00f = 0;
869     int result = 0;
870         ITDebugLog(@"Getting repeat mode.");
871     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];
872         
873         if (m00f == 0) {
874                 ITDebugLog(@"No current playlist, getting repeat mode from visible playlist.");
875                 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];
876         }
877         
878     switch (m00f)
879     {
880         //case 'kRp0':
881         case 1800564815:
882             ITDebugLog(@"Repeat off");
883             result = ITMTRemotePlayerRepeatOff;
884             break;
885         case 'kRp1':
886             ITDebugLog(@"Repeat one");
887             result = ITMTRemotePlayerRepeatOne;
888             break;
889         case 'kRpA':
890             ITDebugLog(@"Repeat all");
891             result = ITMTRemotePlayerRepeatAll;
892             break;
893     }
894     ITDebugLog(@"Getting repeat mode done.");
895     return result;
896 }
897
898 - (BOOL)setRepeatMode:(ITMTRemotePlayerRepeatMode)repeatMode
899 {
900     char *m00f;
901     ITDebugLog(@"Setting repeat mode to %i", repeatMode);
902     switch (repeatMode)
903     {
904         case ITMTRemotePlayerRepeatOne:
905             m00f = "kRp1";
906             break;
907         case ITMTRemotePlayerRepeatAll:
908             m00f = "kRpA";
909             break;
910         case ITMTRemotePlayerRepeatOff:
911         default:
912             m00f = "kRp0";
913             break;
914     }
915         if (![self isPlaying]) {
916                 ITDebugLog(@"No current playlist, setting repeat mode on visible playlist.");
917                 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);
918         } else {
919                 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);
920         }
921     ITDebugLog(@"Setting repeat mode to %c done", m00f);
922     return YES;
923 }
924
925 - (BOOL)play
926 {
927     ITDebugLog(@"Play");
928         ITSendAE('hook', 'Play', &savedPSN);
929     ITDebugLog(@"Play done");
930     return YES;
931 }
932
933 - (BOOL)pause
934 {
935     ITDebugLog(@"Pause");
936     ITSendAE('hook', 'Paus', &savedPSN);
937     ITDebugLog(@"Pause done");
938     return YES;
939 }
940
941 - (BOOL)goToNextSong
942 {
943     ITDebugLog(@"Go to next track");
944     ITSendAE('hook', 'Next', &savedPSN);
945     ITDebugLog(@"Go to next track done");
946     return YES;
947 }
948
949 - (BOOL)goToPreviousSong
950 {
951     ITDebugLog(@"Go to previous track");
952     ITSendAE('hook', 'Back', &savedPSN);
953     ITDebugLog(@"Go to previous track done");
954     return YES;
955 }
956
957 - (BOOL)forward
958 {
959     ITDebugLog(@"Fast forward action");
960     ITSendAE('hook', 'Fast', &savedPSN);
961     ITDebugLog(@"Fast forward action done");
962     return YES;
963 }
964
965 - (BOOL)rewind
966 {
967     ITDebugLog(@"Rewind action");
968     ITSendAE('hook', 'Rwnd', &savedPSN);
969     ITDebugLog(@"Rewind action done");
970     return YES;
971 }
972
973 - (BOOL)switchToPlaylistAtIndex:(int)index
974 {
975     ITDebugLog(@"Switching to playlist at index %i", index);
976         ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'indx', want:type('cPly'), seld:long(%lu), from:() }", index], 'hook', 'Play', &savedPSN);
977     ITDebugLog(@"Done switching to playlist at index %i", index);
978     return YES;
979 }
980
981 - (BOOL)switchToPlaylistAtIndex:(int)index ofSourceAtIndex:(int)index2
982 {
983     ITDebugLog(@"Switching to playlist at index %i of source %i", index, index2);
984         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);
985     ITDebugLog(@"Done switching to playlist at index %i of source %i", index, index2);
986     return YES;
987 }
988
989 - (BOOL)switchToSongAtIndex:(int)index
990 {
991     ITDebugLog(@"Switching to track at index %i", index);
992         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);
993     ITDebugLog(@"Done switching to track at index %i", index);
994     return YES;
995 }
996
997 - (BOOL)switchToEQAtIndex:(int)index
998 {
999     ITDebugLog(@"Switching to EQ preset at index %i", index);
1000     // index should count from 0, but itunes counts from 1, so let's add 1.
1001     [self setEqualizerEnabled:YES];
1002         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);
1003     ITDebugLog(@"Done switching to EQ preset at index %i", index);
1004     return YES;
1005 }
1006
1007 - (BOOL)makePlaylistWithTerm:(NSString *)term ofType:(int)type
1008 {
1009     int i;
1010         
1011     //Get fixed indexing status
1012     BOOL fixed = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", 'core', 'getd', &savedPSN) booleanValue];
1013     
1014     //Enabled fixed indexing
1015     ITSendAEWithString(@"data:long(1), '----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", 'core', 'setd', &savedPSN);
1016     
1017     //Search for the term
1018     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);
1019     
1020     //If MenuTunes playlist exists
1021     if ([ITSendAEWithString(@"'----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'core', 'doex', &savedPSN) booleanValue]) {
1022         //Clear old MenuTunes playlist
1023                 int numSongs = [ITSendAEWithString(@"kocl:type('cTrk'), '----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'core', 'cnte', &savedPSN) int32Value];
1024         for (i = 1; i <= numSongs; i++) {
1025             ITSendAEWithString(@"'----':obj { form:'indx', want:type('cTrk'), seld:long(1), from:obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() } }", 'core', 'delo', &savedPSN);
1026         }
1027     } else {
1028         //Create MenuTunes playlist
1029         ITSendAEWithString(@"prdt:{ pnam:\"MenuTunes\" }, kocl:type('cPly'), &subj:()", 'core', 'crel', &savedPSN);
1030     }
1031     
1032     //Duplicate search results to playlist
1033     for (i = 1; i <= [searchResults numberOfItems]; i++) {
1034                 //NSLog(@"%@", ITSendAEWithStringAndParameter(@"'----':obj { form:'prop', want:type('prop'), seld:prop('pnam'), from:aevt(@) }", *[[searchResults descriptorAtIndex:i] aeDesc], 'core', 'getd', &savedPSN));
1035                 
1036         ITSendAEWithStringAndObject(@"insh:obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", [[searchResults descriptorAtIndex:i] aeDesc], 'core', 'clon', &savedPSN);
1037     }
1038     //Reset fixed indexing
1039     ITSendAEWithString([NSString stringWithFormat:@"data:long(%i), '----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", fixed], 'core', 'setd', &savedPSN);
1040     
1041     //Play MenuTunes playlist
1042     ITSendAEWithString(@"'----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'hook', 'Play', &savedPSN);
1043     
1044     return YES;
1045 }
1046
1047 - (BOOL)isPlaying
1048 {
1049         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');
1050 }
1051
1052 - (void)notificationHandler:(NSNotification *)note
1053 {
1054         ITDebugLog(@"Received notification: %@", note);
1055         [[NSNotificationCenter defaultCenter] postNotificationName:@"ITMTTrackChanged" object:self userInfo:[note userInfo]];
1056         ITDebugLog(@"Handled notification.");
1057 }
1058
1059 - (ProcessSerialNumber)iTunesPSN
1060 {
1061     /*NSArray *apps = [[NSWorkspace sharedWorkspace] launchedApplications];
1062     ProcessSerialNumber number;
1063     int i;
1064     int count = [apps count];
1065     
1066     number.highLongOfPSN = kNoProcess;
1067     
1068     for (i = 0; i < count; i++)
1069     {
1070         NSDictionary *curApp = [apps objectAtIndex:i];
1071         
1072         if ([[curApp objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"])
1073         {
1074             number.highLongOfPSN = [[curApp objectForKey:
1075                 @"NSApplicationProcessSerialNumberHigh"] intValue];
1076             number.lowLongOfPSN = [[curApp objectForKey:
1077                 @"NSApplicationProcessSerialNumberLow"] intValue];
1078         }
1079     }
1080     return number;*/
1081     ProcessSerialNumber number;
1082     number.highLongOfPSN = kNoProcess;
1083     number.lowLongOfPSN = 0;
1084     ITDebugLog(@"Getting iTunes' PSN.");
1085     while ( (GetNextProcess(&number) == noErr) ) 
1086     {
1087         CFStringRef name;
1088         if ( (CopyProcessName(&number, &name) == noErr) )
1089         {
1090             if ([(NSString *)name isEqualToString:@"iTunes"])
1091             {
1092                 ITDebugLog(@"iTunes' highLPongOfPSN: %lu.", number.highLongOfPSN);
1093                 ITDebugLog(@"iTunes' lowLongOfPSN: %lu.", number.lowLongOfPSN);
1094                 ITDebugLog(@"Done getting iTunes' PSN.");
1095                                 [(NSString *)name release];
1096                 return number;
1097             }
1098             [(NSString *)name release];
1099         }
1100     }
1101     ITDebugLog(@"Failed getting iTunes' PSN.");
1102     return number;
1103 }
1104
1105 - (NSString*)formatTimeInSeconds:(long)seconds {
1106     long final = seconds;
1107     NSString *finalString;
1108     if (final >= 60) {
1109         if (final > 3600) {
1110             finalString = [NSString stringWithFormat:@"%i:%@:%@",(final / 3600),[self zeroSixty:(int)((final % 3600) / 60)],[self zeroSixty:(int)((final % 3600) % 60)]];
1111         } else {
1112             finalString = [NSString stringWithFormat:@"%i:%@",(final / 60),[self zeroSixty:(int)(final % 60)]];
1113         }
1114     } else {
1115         finalString = [NSString stringWithFormat:@"0:%@",[self zeroSixty:(int)final]];
1116     }
1117     return finalString;
1118 }
1119 - (NSString*)zeroSixty:(int)seconds {
1120     if ( (seconds < 10) && (seconds > 0) ) {
1121         return [NSString stringWithFormat:@"0%i",seconds];
1122     } else if ( (seconds == 0) ) {
1123         return [NSString stringWithFormat:@"00"];
1124     } else {
1125         return [NSString stringWithFormat:@"%i",seconds];
1126     }
1127 }
1128
1129 @end