c537975f190c35f6b0a7a097b59a9ee8f41a7fa6
[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 setParent:newParent];
355                                 [[newParent children] addObject:nextNode];
356                                 [newParent setType:ITMTFolderNode];
357                                 [nested addObject:nextNode];
358                         }
359                 }
360                 
361                 NSEnumerator *nestEnumerator = [nested objectEnumerator];
362                 while ( (nextNode = [nestEnumerator nextObject]) ) {
363                         [[sourceNode children] removeObject:nextNode];
364                 }
365                 [nested release];
366                 
367                 //Move all the folders to the beginning of the list
368                 //Move the podcasts playlist to the top
369                 BOOL movedPodcasts = NO;
370                 enumerator = [[sourceNode children] reverseObjectEnumerator];
371                 while ( (nextNode = [enumerator nextObject]) ) {
372                         if ([nextNode type] == ITMTPodcastsNode) {
373                                 [nextNode retain];
374                                 [[sourceNode children] removeObject:nextNode];
375                                 [[sourceNode children] insertObject:nextNode atIndex:1];
376                                 movedPodcasts = YES;
377                         } else if ([nextNode type] == ITMTFolderNode) {
378                                 [nextNode retain];
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 = ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pArt'), from:obj { form:'indx', want:type('cTrk'), seld:abso($616C6C20$), from:obj { form:'indx', want:type('cPly'), seld:long(1), from:obj { form:'indx', want:type('cSrc'), seld:long(1), from:() } } } }", 'core', 'getd', &savedPSN);
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 = ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pAlb'), from:obj { form:'indx', want:type('cTrk'), seld:abso($616C6C20$), from:obj { form:'indx', want:type('cPly'), seld:long(1), from:obj { form:'indx', want:type('cSrc'), seld:long(1), from:() } } } }", 'core', 'getd', &savedPSN);
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 - (int)currentAlbumTrackCount
529 {
530     int temp1;
531     ITDebugLog(@"Getting current album track count.");
532     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];
533     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { temp1 = 0; }
534     ITDebugLog(@"Getting current album track count done.");
535     return temp1;
536 }
537
538 - (int)currentSongTrack
539 {
540     int temp1;
541     ITDebugLog(@"Getting current song track.");
542     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];
543     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { temp1 = 0; }
544     ITDebugLog(@"Getting current song track done.");
545     return temp1;
546 }
547
548 - (NSString *)playerStateUniqueIdentifier
549 {
550     NSString *temp1;
551     ITDebugLog(@"Getting current unique identifier.");
552         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);
553         if ([descriptor typeCodeValue] == 'prop') {
554                 return @"0-0";
555         } else if (descriptor == nil) {
556                 return nil;
557         }
558     FourCharCode cls = [descriptor typeCodeValue];
559     if ( ([self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist) || (cls == 'cURT') ) {
560                 NSString *bad = [NSString stringWithUTF8String:"浳湧"];
561         temp1 = [ITSendAEWithKey('pStT', 'core', 'getd', &savedPSN) stringValue];
562         if ([temp1 isEqualToString:bad]) {
563             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];
564         }
565     } else {
566         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]];
567     }
568     ITDebugLog(@"Getting current unique identifier done.");
569     return ( ([temp1 length]) ? temp1 : nil ) ;
570 }
571
572 - (int)currentSongIndex
573 {
574     int temp1;
575     ITDebugLog(@"Getting current song index.");
576         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);
577     ITDebugLog(@"Getting current song index done.");
578     return temp1;
579 }
580
581 - (NSString *)currentSongTitle
582 {
583     NSString *temp1;
584     ITDebugLog(@"Getting current song title.");
585     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];
586         
587     //If we're listening to the radio.
588     if (result == 'cURT') {
589         NSString *bad = [NSString stringWithUTF8String:"浳湧"];
590         temp1 = [ITSendAEWithKey('pStT', 'core', 'getd', &savedPSN) stringValue];
591         if ([temp1 isEqualToString:bad]) {
592             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];
593         }
594         temp1 = [temp1 stringByAppendingString:@" (Stream)"];
595     } else if (result == 'prop') {
596                 temp1 = nil;
597         } else {
598         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];
599     }
600     ITDebugLog(@"Getting current song title done.");
601     return ( ([temp1 length]) ? temp1 : nil ) ;
602 }
603
604 - (NSString *)currentSongArtist
605 {
606     NSString *temp1;
607     ITDebugLog(@"Getting current song artist.");
608     if ( [self currentPlaylistClass] != ITMTRemotePlayerRadioPlaylist ) {
609         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];
610     } else {
611         temp1 = @"";
612     }
613     ITDebugLog(@"Getting current song artist done.");
614     return ( ([temp1 length]) ? temp1 : nil ) ;
615 }
616
617 - (NSString *)currentSongComposer
618 {
619     NSString *temp1;
620     ITDebugLog(@"Getting current song artist.");
621     if ( [self currentPlaylistClass] != ITMTRemotePlayerRadioPlaylist ) {
622         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];
623     } else {
624         temp1 = @"";
625     }
626     ITDebugLog(@"Getting current song artist done.");
627     return ( ([temp1 length]) ? temp1 : nil ) ;
628 }
629
630 - (NSString *)currentSongAlbum
631 {
632     NSString *temp1;
633     ITDebugLog(@"Getting current song album.");
634     if ( [self currentPlaylistClass] != ITMTRemotePlayerRadioPlaylist ) {
635         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];
636     } else {
637         temp1 = @"";
638     }
639     ITDebugLog(@"Getting current song album done.");
640     return ( ([temp1 length]) ? temp1 : nil ) ;
641 }
642
643 - (NSString *)currentSongGenre
644 {
645     NSString *temp1;
646     ITDebugLog(@"Getting current song genre.");
647     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];
648     ITDebugLog(@"Getting current song genre done.");
649     return ( ([temp1 length]) ? temp1 : nil ) ;
650 }
651
652 - (NSString *)currentSongLength
653 {
654     SInt32 temp1;
655     NSString *temp2;
656     ITDebugLog(@"Getting current song length.");
657     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];
658     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];
659     if ( ([self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist) || (temp1 == 'cURT') ) { temp2 = @"Continuous"; }
660     ITDebugLog(@"Getting current song length done.");
661     return temp2;
662 }
663
664 - (NSString *)currentSongRemaining
665 {
666     SInt32 duration, current, final;
667     NSString *finalString;
668     
669     ITDebugLog(@"Getting current song remaining time.");
670     
671     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];
672     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];
673     final = duration - current;
674     finalString = [self formatTimeInSeconds:final];
675     
676     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { finalString = nil; }
677     
678     ITDebugLog(@"Getting current song remaining time done.");
679     
680     return finalString;
681 }
682
683 - (NSString *)currentSongElapsed
684 {
685     long final;
686     NSString *finalString;
687     
688     ITDebugLog(@"Getting current song elapsed time.");
689         final = (long)[ITSendAEWithKey('pPos', 'core', 'getd', &savedPSN) int32Value];
690     finalString = [self formatTimeInSeconds:final];
691     ITDebugLog(@"Getting current song elapsed time done.");
692     return finalString;
693 }
694
695 - (NSImage *)currentSongAlbumArt
696 {
697     ITDebugLog(@"Getting current song album art.");
698     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;
699     ITDebugLog(@"Getting current song album art done.");    
700     if (data) {
701         return [[[NSImage alloc] initWithData:data] autorelease];
702     } else {
703         return nil;
704     }
705 }
706
707 - (int)currentSongPlayCount
708 {
709     int count;
710     ITDebugLog(@"Getting current song play count.");
711     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];
712     ITDebugLog(@"Getting current song play count done.");
713     return count;
714 }
715
716 - (float)currentSongRating
717 {
718     float temp1;
719     ITDebugLog(@"Getting current song rating.");
720     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);
721     ITDebugLog(@"Getting current song rating done.");
722     return temp1;
723 }
724
725 - (BOOL)setCurrentSongRating:(float)rating
726 {
727     ITDebugLog(@"Setting current song rating to %f.", rating);
728     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { return NO; }
729         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);
730     ITDebugLog(@"Setting current song rating to %f done.", rating);
731     return YES;
732 }
733
734 - (BOOL)currentSongShufflable
735 {
736         BOOL temp1;
737         ITDebugLog(@"Getting current song shufflable status.");
738     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];
739     ITDebugLog(@"Getting current song shufflable status done.");
740     return temp1;
741 }
742
743 - (BOOL)setCurrentSongShufflable:(BOOL)shufflable
744 {
745         ITDebugLog(@"Setting current song shufflable status to %i.", shufflable);
746     if ([self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist) {
747                 ITDebugLog(@"Not a valid track to set status to, returning.");
748                 return NO;
749         }
750         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);
751     ITDebugLog(@"Setting current song shufflable status to %i done.", shufflable);
752     return YES;
753 }
754
755 - (BOOL)equalizerEnabled
756 {
757     ITDebugLog(@"Getting equalizer enabled status.");
758     int thingy = (int)[ITSendAEWithKey('pEQ ', 'core', 'getd', &savedPSN) int32Value];
759     ITDebugLog(@"Done getting equalizer enabled status.");
760     return (thingy != 0) ? YES : NO;
761 }
762
763 - (BOOL)setEqualizerEnabled:(BOOL)enabled
764 {
765     ITDebugLog(@"Setting equalizer enabled to %i.", enabled);
766         ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pEQ '), from:'null'() }", enabled], 'core', 'setd', &savedPSN);
767     ITDebugLog(@"Done setting equalizer enabled to %i.", enabled);
768     return YES;
769 }
770
771 - (NSArray *)eqPresets
772 {
773     int i;
774     SInt32 numPresets = [ITSendAEWithString(@"kocl:type('cEQP'), '----':(), &subj:()", 'core', 'cnte', &savedPSN) int32Value];
775     NSMutableArray *presets = [[NSMutableArray alloc] initWithCapacity:numPresets];
776     ITDebugLog(@"Getting EQ presets");
777     for (i = 1; i <= numPresets; i++) {
778         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];
779         if (theObj) {
780             ITDebugLog(@"Adding preset %@", theObj);
781             [presets addObject:theObj];
782         }
783     }
784     ITDebugLog(@"Done getting EQ presets");
785     return [presets autorelease];
786 }
787
788 - (int)currentEQPresetIndex
789 {
790     int result;
791     ITDebugLog(@"Getting current EQ preset index.");
792     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];
793     ITDebugLog(@"Getting current EQ preset index done.");
794     return result;
795 }
796
797 - (float)volume
798 {
799     ITDebugLog(@"Getting volume.");
800     ITDebugLog(@"Getting volume done.");
801     return (float)[ITSendAEWithKey('pVol', 'core', 'getd', &savedPSN) int32Value] / 100;
802 }
803
804 - (BOOL)setVolume:(float)volume
805 {
806     ITDebugLog(@"Setting volume to %f.", volume);
807         ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pVol'), from:'null'() }", (long)(volume * 100)], 'core', 'setd', &savedPSN);
808     ITDebugLog(@"Setting volume to %f done.", volume);
809     return YES;
810 }
811
812 - (BOOL)shuffleEnabled
813 {
814         int result;
815     ITDebugLog(@"Getting shuffle enabled status.");
816         if (![self isPlaying]) {
817                 ITDebugLog(@"No current playlist, getting shuffle status from visible playlist.");
818                 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];
819         } else {
820                 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];
821         }
822     ITDebugLog(@"Getting shuffle enabled status done.");
823     return (result != 0);
824 }
825
826 - (BOOL)setShuffleEnabled:(BOOL)enabled
827 {
828     ITDebugLog(@"Set shuffle enabled to %i", enabled);
829         if (![self isPlaying]) {
830                 ITDebugLog(@"No current playlist, setting shuffle status on visible playlist.");
831                 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);
832         } else {
833                 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);
834         }
835     ITDebugLog(@"Set shuffle enabled to %i done", enabled);
836     return YES;
837 }
838
839 - (ITMTRemotePlayerRepeatMode)repeatMode
840 {
841     FourCharCode m00f = 0;
842     int result = 0;
843         ITDebugLog(@"Getting repeat mode.");
844     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];
845         
846         if (m00f == 0) {
847                 ITDebugLog(@"No current playlist, getting repeat mode from visible playlist.");
848                 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];
849         }
850         
851     switch (m00f)
852     {
853         //case 'kRp0':
854         case 1800564815:
855             ITDebugLog(@"Repeat off");
856             result = ITMTRemotePlayerRepeatOff;
857             break;
858         case 'kRp1':
859             ITDebugLog(@"Repeat one");
860             result = ITMTRemotePlayerRepeatOne;
861             break;
862         case 'kRpA':
863             ITDebugLog(@"Repeat all");
864             result = ITMTRemotePlayerRepeatAll;
865             break;
866     }
867     ITDebugLog(@"Getting repeat mode done.");
868     return result;
869 }
870
871 - (BOOL)setRepeatMode:(ITMTRemotePlayerRepeatMode)repeatMode
872 {
873     char *m00f;
874     ITDebugLog(@"Setting repeat mode to %i", repeatMode);
875     switch (repeatMode)
876     {
877         case ITMTRemotePlayerRepeatOne:
878             m00f = "kRp1";
879             break;
880         case ITMTRemotePlayerRepeatAll:
881             m00f = "kRpA";
882             break;
883         case ITMTRemotePlayerRepeatOff:
884         default:
885             m00f = "kRp0";
886             break;
887     }
888         if (![self isPlaying]) {
889                 ITDebugLog(@"No current playlist, setting repeat mode on visible playlist.");
890                 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);
891         } else {
892                 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);
893         }
894     ITDebugLog(@"Setting repeat mode to %c done", m00f);
895     return YES;
896 }
897
898 - (BOOL)play
899 {
900     ITDebugLog(@"Play");
901         ITSendAE('hook', 'Play', &savedPSN);
902     ITDebugLog(@"Play done");
903     return YES;
904 }
905
906 - (BOOL)pause
907 {
908     ITDebugLog(@"Pause");
909     ITSendAE('hook', 'Paus', &savedPSN);
910     ITDebugLog(@"Pause done");
911     return YES;
912 }
913
914 - (BOOL)goToNextSong
915 {
916     ITDebugLog(@"Go to next track");
917     ITSendAE('hook', 'Next', &savedPSN);
918     ITDebugLog(@"Go to next track done");
919     return YES;
920 }
921
922 - (BOOL)goToPreviousSong
923 {
924     ITDebugLog(@"Go to previous track");
925     ITSendAE('hook', 'Back', &savedPSN);
926     ITDebugLog(@"Go to previous track done");
927     return YES;
928 }
929
930 - (BOOL)forward
931 {
932     ITDebugLog(@"Fast forward action");
933     ITSendAE('hook', 'Fast', &savedPSN);
934     ITDebugLog(@"Fast forward action done");
935     return YES;
936 }
937
938 - (BOOL)rewind
939 {
940     ITDebugLog(@"Rewind action");
941     ITSendAE('hook', 'Rwnd', &savedPSN);
942     ITDebugLog(@"Rewind action done");
943     return YES;
944 }
945
946 - (BOOL)switchToPlaylistAtIndex:(int)index
947 {
948     ITDebugLog(@"Switching to playlist at index %i", index);
949         ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'indx', want:type('cPly'), seld:long(%lu), from:() }", index], 'hook', 'Play', &savedPSN);
950     ITDebugLog(@"Done switching to playlist at index %i", index);
951     return YES;
952 }
953
954 - (BOOL)switchToPlaylistAtIndex:(int)index ofSourceAtIndex:(int)index2
955 {
956     ITDebugLog(@"Switching to playlist at index %i of source %i", index, index2);
957         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);
958     ITDebugLog(@"Done switching to playlist at index %i of source %i", index, index2);
959     return YES;
960 }
961
962 - (BOOL)switchToSongAtIndex:(int)index
963 {
964     ITDebugLog(@"Switching to track at index %i", index);
965         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);
966     ITDebugLog(@"Done switching to track at index %i", index);
967     return YES;
968 }
969
970 - (BOOL)switchToEQAtIndex:(int)index
971 {
972     ITDebugLog(@"Switching to EQ preset at index %i", index);
973     // index should count from 0, but itunes counts from 1, so let's add 1.
974     [self setEqualizerEnabled:YES];
975         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);
976     ITDebugLog(@"Done switching to EQ preset at index %i", index);
977     return YES;
978 }
979
980 - (BOOL)makePlaylistWithTerm:(NSString *)term ofType:(int)type
981 {
982     int i;
983         
984     //Get fixed indexing status
985     BOOL fixed = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", 'core', 'getd', &savedPSN) booleanValue];
986     
987     //Enabled fixed indexing
988     ITSendAEWithString(@"data:long(1), '----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", 'core', 'setd', &savedPSN);
989     
990     //Search for the term
991     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);
992     
993     //If MenuTunes playlist exists
994     if ([ITSendAEWithString(@"'----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'core', 'doex', &savedPSN) booleanValue]) {
995         //Clear old MenuTunes playlist
996                 int numSongs = [ITSendAEWithString(@"kocl:type('cTrk'), '----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'core', 'cnte', &savedPSN) int32Value];
997         for (i = 1; i <= numSongs; i++) {
998             ITSendAEWithString(@"'----':obj { form:'indx', want:type('cTrk'), seld:long(1), from:obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() } }", 'core', 'delo', &savedPSN);
999         }
1000     } else {
1001         //Create MenuTunes playlist
1002         ITSendAEWithString(@"prdt:{ pnam:\"MenuTunes\" }, kocl:type('cPly'), &subj:()", 'core', 'crel', &savedPSN);
1003     }
1004     
1005     //Duplicate search results to playlist
1006     for (i = 1; i <= [searchResults numberOfItems]; i++) {
1007                 //NSLog(@"%@", ITSendAEWithStringAndParameter(@"'----':obj { form:'prop', want:type('prop'), seld:prop('pnam'), from:aevt(@) }", *[[searchResults descriptorAtIndex:i] aeDesc], 'core', 'getd', &savedPSN));
1008                 
1009         ITSendAEWithStringAndObject(@"insh:obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", [[searchResults descriptorAtIndex:i] aeDesc], 'core', 'clon', &savedPSN);
1010     }
1011     //Reset fixed indexing
1012     ITSendAEWithString([NSString stringWithFormat:@"data:long(%i), '----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", fixed], 'core', 'setd', &savedPSN);
1013     
1014     //Play MenuTunes playlist
1015     ITSendAEWithString(@"'----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'hook', 'Play', &savedPSN);
1016     
1017     return YES;
1018 }
1019
1020 - (BOOL)isPlaying
1021 {
1022         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');
1023 }
1024
1025 - (void)notificationHandler:(NSNotification *)note
1026 {
1027         ITDebugLog(@"Received notification: %@", note);
1028         [[NSNotificationCenter defaultCenter] postNotificationName:@"ITMTTrackChanged" object:self userInfo:[note userInfo]];
1029         ITDebugLog(@"Handled notification.");
1030 }
1031
1032 - (ProcessSerialNumber)iTunesPSN
1033 {
1034     /*NSArray *apps = [[NSWorkspace sharedWorkspace] launchedApplications];
1035     ProcessSerialNumber number;
1036     int i;
1037     int count = [apps count];
1038     
1039     number.highLongOfPSN = kNoProcess;
1040     
1041     for (i = 0; i < count; i++)
1042     {
1043         NSDictionary *curApp = [apps objectAtIndex:i];
1044         
1045         if ([[curApp objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"])
1046         {
1047             number.highLongOfPSN = [[curApp objectForKey:
1048                 @"NSApplicationProcessSerialNumberHigh"] intValue];
1049             number.lowLongOfPSN = [[curApp objectForKey:
1050                 @"NSApplicationProcessSerialNumberLow"] intValue];
1051         }
1052     }
1053     return number;*/
1054     ProcessSerialNumber number;
1055     number.highLongOfPSN = kNoProcess;
1056     number.lowLongOfPSN = 0;
1057     ITDebugLog(@"Getting iTunes' PSN.");
1058     while ( (GetNextProcess(&number) == noErr) ) 
1059     {
1060         CFStringRef name;
1061         if ( (CopyProcessName(&number, &name) == noErr) )
1062         {
1063             if ([(NSString *)name isEqualToString:@"iTunes"])
1064             {
1065                 ITDebugLog(@"iTunes' highLPongOfPSN: %lu.", number.highLongOfPSN);
1066                 ITDebugLog(@"iTunes' lowLongOfPSN: %lu.", number.lowLongOfPSN);
1067                 ITDebugLog(@"Done getting iTunes' PSN.");
1068                 return number;
1069             }
1070             [(NSString *)name release];
1071         }
1072     }
1073     ITDebugLog(@"Failed getting iTunes' PSN.");
1074     return number;
1075 }
1076
1077 - (NSString*)formatTimeInSeconds:(long)seconds {
1078     long final = seconds;
1079     NSString *finalString;
1080     if (final >= 60) {
1081         if (final > 3600) {
1082             finalString = [NSString stringWithFormat:@"%i:%@:%@",(final / 3600),[self zeroSixty:(int)((final % 3600) / 60)],[self zeroSixty:(int)((final % 3600) % 60)]];
1083         } else {
1084             finalString = [NSString stringWithFormat:@"%i:%@",(final / 60),[self zeroSixty:(int)(final % 60)]];
1085         }
1086     } else {
1087         finalString = [NSString stringWithFormat:@"0:%@",[self zeroSixty:(int)final]];
1088     }
1089     return finalString;
1090 }
1091 - (NSString*)zeroSixty:(int)seconds {
1092     if ( (seconds < 10) && (seconds > 0) ) {
1093         return [NSString stringWithFormat:@"0%i",seconds];
1094     } else if ( (seconds == 0) ) {
1095         return [NSString stringWithFormat:@"00"];
1096     } else {
1097         return [NSString stringWithFormat:@"%i",seconds];
1098     }
1099 }
1100
1101 @end