Hopefully fixed a dumb error.
[MenuTunes.git] / iTunesRemote.m
1 #import "iTunesRemote.h"
2
3 @implementation iTunesRemote
4
5 + (id)remote
6 {
7     return [[[iTunesRemote alloc] init] autorelease];
8 }
9
10 - (NSString *)remoteTitle
11 {
12     return @"iTunes Remote";
13 }
14
15 - (NSString *)remoteInformation
16 {
17     return @"Default MenuTunes plugin to control iTunes, by iThink Software.";
18 }
19
20 - (NSImage *)remoteIcon
21 {
22     return nil;
23 }
24
25 - (BOOL)begin
26 {
27     ITDebugLog(@"iTunesRemote begun");
28     savedPSN = [self iTunesPSN];
29     return YES;
30 }
31
32 - (BOOL)halt
33 {
34     ITDebugLog(@"iTunesRemote halted");
35     return YES;
36 }
37
38 - (NSString *)playerFullName
39 {
40     return @"iTunes";
41 }
42
43 - (NSString *)playerSimpleName
44 {
45     return @"iTunes";
46 }
47
48 - (NSDictionary *)capabilities
49 {
50     return [NSDictionary dictionaryWithObjectsAndKeys:
51                 [NSNumber numberWithBool: YES], @"Remote",
52                 [NSNumber numberWithBool: YES], @"Basic Track Control",
53                 [NSNumber numberWithBool: YES], @"Track Information",
54                 [NSNumber numberWithBool: YES], @"Track Navigation",
55                 [NSNumber numberWithBool: YES], @"Upcoming Songs",
56                 [NSNumber numberWithBool: YES], @"Playlists",
57                 [NSNumber numberWithBool: YES], @"Volume",
58                 [NSNumber numberWithBool: YES], @"Shuffle",
59                 [NSNumber numberWithBool: YES], @"Repeat Modes",
60                 [NSNumber numberWithBool: YES], @"Equalizer",
61                 [NSNumber numberWithBool: YES], @"Track Rating",
62                 nil];
63 }
64
65 - (BOOL)showPrimaryInterface
66 {
67     ITDebugLog(@"Showing player primary interface.");
68     
69     if ([self playerRunningState] == ITMTRemotePlayerRunning) {
70         ITDebugLog(@"Showing player interface.");
71         //If not minimized and visible
72         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) &&
73                          ([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) &&
74              [[[[NSWorkspace sharedWorkspace] activeApplication] objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"] ) {
75             //set minimized of browser window 1 to true
76                         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);
77         } else {
78             //set minimized of browser window 1 to false
79                         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);
80         }
81         //set visible of browser window 1 to true
82                 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);
83         //active iTunes
84                 ITSendAEWithString(@"data:long(1), '----':obj { form:'prop', want:type('prop'), seld:type('pisf'), from:'null'() }", 'core', 'setd', &savedPSN);
85         ITDebugLog(@"Done showing player primary interface.");
86         return YES;
87     } else {
88         NSString *path;
89         ITDebugLog(@"Launching player.");
90         if ( (path = [[NSUserDefaults standardUserDefaults] stringForKey:@"CustomPlayerPath"]) ) {
91         } else {
92             path = [self playerFullName];
93         }
94         if (![[NSWorkspace sharedWorkspace] launchApplication:path]) {
95             ITDebugLog(@"Error Launching Player");
96             return NO;
97         }
98         return YES;
99     }
100 }
101
102 - (ITMTRemotePlayerRunningState)playerRunningState
103 {
104     NSArray *apps = [[NSWorkspace sharedWorkspace] launchedApplications];
105     int i;
106     int count = [apps count];
107     
108     for (i = 0; i < count; i++) {
109         if ([[[apps objectAtIndex:i] objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"]) {
110             ITDebugLog(@"Player running state: 1");
111             return ITMTRemotePlayerRunning;
112         }
113     }
114     ITDebugLog(@"Player running state: 0");
115     return ITMTRemotePlayerNotRunning;
116 }
117
118 - (ITMTRemotePlayerPlayingState)playerPlayingState
119 {
120     SInt32 result;
121     
122     ITDebugLog(@"Getting player playing state");
123     result = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pPlS'), from:'null'() }", 'core', 'getd', &savedPSN) int32Value];
124     switch (result)
125     {
126         case 'kPSP':
127             ITDebugLog(@"Getting player playing state done. Player state: Playing");
128             return ITMTRemotePlayerPlaying;
129         case 'kPSp':
130             ITDebugLog(@"Getting player playing state done. Player state: Paused");
131             return ITMTRemotePlayerPaused;
132         case 'kPSR':
133             ITDebugLog(@"Getting player playing state done. Player state: Rewinding");
134             return ITMTRemotePlayerRewinding;
135         case 'kPSF':
136             ITDebugLog(@"Getting player playing state done. Player state: Forwarding");
137             return ITMTRemotePlayerForwarding;
138         case 'kPSS':
139         default:
140             ITDebugLog(@"Getting player playing state done. Player state: Stopped");
141             return ITMTRemotePlayerStopped;
142     }
143     ITDebugLog(@"Getting player playing state done. Player state: Stopped");
144     return ITMTRemotePlayerStopped;
145 }
146
147 /*- (NSArray *)playlists
148 {
149     long i = 0;
150     const signed long numPlaylists = [[ITAppleEventCenter sharedCenter] sendAEWithSendStringForNumber:@"kocl:type('cPly'), '----':()" eventClass:@"core" eventID:@"cnte" appPSN:savedPSN];
151     NSMutableArray *playlists = [[NSMutableArray alloc] initWithCapacity:numPlaylists];
152     
153     for (i = 1; i <= numPlaylists; i++) {
154         const long j = i;
155         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];
156         NSString *theObj = [[ITAppleEventCenter sharedCenter] sendAEWithSendString:sendStr eventClass:@"core" eventID:@"getd" appPSN:savedPSN];
157         [playlists addObject:theObj];
158     }
159     return [playlists autorelease];
160 }*/
161
162 //Full source awareness
163 - (NSArray *)playlists
164 {
165     unsigned long i, k;
166     SInt32 numSources = [ITSendAEWithString(@"kocl:type('cSrc'), '----':()", 'core', 'cnte', &savedPSN) int32Value];
167     NSMutableArray *allSources = [[NSMutableArray alloc] init];
168     
169     ITDebugLog(@"Getting playlists.");
170     if (numSources == 0) {
171                 [allSources release];
172         ITDebugLog(@"No sources.");
173         return nil;
174     }
175     
176     for (k = 1; k <= numSources ; k++) {
177         SInt32 numPlaylists = [ITSendAEWithString([NSString stringWithFormat:@"kocl:type('cPly'), '----':obj { form:'indx', want:type('cSrc'), seld:long(%u), from:() }",k], 'core', 'cnte', &savedPSN) int32Value];
178         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];
179         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];
180         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];
181         unsigned long class;
182         if (sourceName) {
183             NSMutableArray *aSource = [[NSMutableArray alloc] init];
184             [aSource addObject:sourceName];
185             switch (fourcc) {
186                 case 'kTun':
187                     class = ITMTRemoteRadioSource;
188                     break;
189                 case 'kDev':
190                     class = ITMTRemoteGenericDeviceSource;
191                     break;
192                 case 'kPod':
193                     class = ITMTRemoteiPodSource;
194                     break;
195                 case 'kMCD':
196                 case 'kACD':
197                     class = ITMTRemoteCDSource;
198                     break;
199                 case 'kShd':
200                     class = ITMTRemoteSharedLibrarySource;
201                     break;
202                 case 'kUnk':
203                 case 'kLib':
204                 default:
205                     class = ITMTRemoteLibrarySource;
206                     break;
207             }
208             ITDebugLog(@"Adding source %@ of type %i at index %i", sourceName, class, index);
209             [aSource addObject:[NSNumber numberWithInt:class]];
210             [aSource addObject:[NSNumber numberWithInt:index]];
211             for (i = 1; i <= numPlaylists; i++) {
212                 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];
213                 NSString *theObj = [ITSendAEWithString(sendStr, 'core', 'getd', &savedPSN) stringValue];
214                 ITDebugLog(@" - Adding playlist %@", theObj);
215                 if (theObj) {
216                     [aSource addObject:theObj];
217                 }
218             }
219             [allSources addObject:[aSource autorelease]];
220         } else {
221             ITDebugLog(@"Source at index %i disappeared.", k);
222         }
223     }
224     ITDebugLog(@"Finished getting playlists.");
225     return [allSources autorelease];
226 }
227
228 - (NSArray *)artists
229 {
230     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);
231     int i;
232     NSMutableArray *array = [[NSMutableArray alloc] init];
233     NSArray *returnArray;
234     for (i = 1; i <= [rawr numberOfItems]; i++) {
235         NSString *artist = [[rawr descriptorAtIndex:i] stringValue];
236         if (artist && [artist length] && ![array containsObject:artist]) {
237             [array addObject:artist];
238         }
239     }
240     [array sortUsingSelector:@selector(caseInsensitiveCompare:)];
241     returnArray = [NSArray arrayWithArray:array];
242     [array release];
243     return returnArray;
244 }
245
246 - (NSArray *)albums
247 {
248     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);
249     int i;
250     NSMutableArray *array = [[NSMutableArray alloc] init];
251     NSArray *returnArray;
252     for (i = 1; i <= [rawr numberOfItems]; i++) {
253         NSString *album = [[rawr descriptorAtIndex:i] stringValue];
254         if (album && [album length] && ![array containsObject:album]) {
255             [array addObject:album];
256         }
257     }
258     [array sortUsingSelector:@selector(caseInsensitiveCompare:)];
259     returnArray = [NSArray arrayWithArray:array];
260     [array release];
261     return returnArray;
262 }
263
264 - (int)numberOfSongsInPlaylistAtIndex:(int)index
265 {
266         /*
267                 This method only returns the proper number if there's something playing.
268                 This is because it gets the container of the current playlist so that it
269                 gets the playlist index from the current source. Operating this way is fine,
270                 since MT only ever calls this method when there is something playlist.
271                 A working version of this that works in just the main source is in the
272                 makePlaylistWithTerm:ofType: method.
273         */
274     int temp1;
275         NSAppleEventDescriptor *result;
276     ITDebugLog(@"Getting number of songs in playlist at index %i", index);
277         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);
278         temp1 = (result == nil) ? -1 : (int)[result int32Value];
279     ITDebugLog(@"Getting number of songs in playlist at index %i done", index);
280     return temp1;
281 }
282
283 - (ITMTRemotePlayerSource)currentSource
284 {
285     SInt32 fourcc;
286
287     ITDebugLog(@"Getting current source.");   
288     
289     fourcc = ([self isPlaying]) ? [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pKnd'), from:obj { form:'prop', want:type('prop'), seld:type('ctnr'), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } } }", 'core', 'getd', &savedPSN) int32Value] : 'kLib';
290     
291     switch (fourcc) {
292         case 'kTun':
293             ITDebugLog(@"Getting current source done. Source: Radio.");
294             return ITMTRemoteRadioSource;
295             break;
296         case 'kDev':
297             ITDebugLog(@"Getting current source done. Source: Generic Device.");
298             return ITMTRemoteGenericDeviceSource;
299         case 'kPod':
300             ITDebugLog(@"Getting current source done. Source: iPod.");
301             return ITMTRemoteiPodSource; //this is stupid
302             break;
303         case 'kMCD':
304         case 'kACD':
305             ITDebugLog(@"Getting current source done. Source: CD.");
306             return ITMTRemoteCDSource;
307             break;
308         case 'kShd':
309             ITDebugLog(@"Getting current source done. Source: Shared Library.");
310             return ITMTRemoteSharedLibrarySource;
311             break;
312         case 'kUnk':
313         case 'kLib':
314         default:
315             ITDebugLog(@"Getting current source done. Source: Library.");
316             return ITMTRemoteLibrarySource;
317             break;
318     }
319 }
320
321 - (int)currentSourceIndex
322 {
323     ITDebugLog(@"Getting current source.");
324     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];
325 }
326
327 - (ITMTRemotePlayerPlaylistClass)currentPlaylistClass
328 {
329     SInt32 realResult;
330     ITDebugLog(@"Getting current playlist class");
331     realResult = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pcls'), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
332     switch (realResult)
333            {
334            case 'cLiP':
335                ITDebugLog(@"Getting current playlist class done. Class: Library.");
336                return ITMTRemotePlayerLibraryPlaylist;
337                break;
338            case 'cRTP':
339                ITDebugLog(@"Getting current playlist class done. Class: Radio.");
340                return ITMTRemotePlayerRadioPlaylist;
341                break;
342            default:
343                ITDebugLog(@"Getting current playlist class done. Class: Standard playlist.");
344                return ITMTRemotePlayerPlaylist;
345            }
346 }
347
348 - (int)currentPlaylistIndex
349 {  
350     int temp1;
351     ITDebugLog(@"Getting current playlist index.");
352     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);
353     ITDebugLog(@"Getting current playlist index done.");
354     return temp1;
355 }
356
357 - (NSString *)songTitleAtIndex:(int)index
358 {
359     NSString *temp1;
360     ITDebugLog(@"Getting song title at index %i.", index);
361     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];
362     ITDebugLog(@"Getting song title at index %i done.", index);
363     return ( ([temp1 length]) ? temp1 : nil ) ;
364 }
365
366 - (int)currentAlbumTrackCount
367 {
368     int temp1;
369     ITDebugLog(@"Getting current album track count.");
370     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];
371     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { temp1 = 0; }
372     ITDebugLog(@"Getting current album track count done.");
373     return temp1;
374 }
375
376 - (int)currentSongTrack
377 {
378     int temp1;
379     ITDebugLog(@"Getting current song track.");
380     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];
381     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { temp1 = 0; }
382     ITDebugLog(@"Getting current song track done.");
383     return temp1;
384 }
385
386 - (NSString *)playerStateUniqueIdentifier
387 {
388     NSString *temp1;
389     ITDebugLog(@"Getting current unique identifier.");
390         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);
391         if ((descriptor == nil) || ([descriptor int32Value] == 'prop')) {
392                 return @"0-0";
393         }
394     SInt32 cls = [descriptor int32Value];
395     if ( ([self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist) || (cls == 'cURT') ) {
396                 NSString *bad = [NSString stringWithUTF8String:"浳湧"];
397         temp1 = [ITSendAEWithKey('pStT', 'core', 'getd', &savedPSN) stringValue];
398         if ([temp1 isEqualToString:bad]) {
399             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];
400         }
401     } else {
402         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]];
403     }
404     ITDebugLog(@"Getting current unique identifier done.");
405     return ( ([temp1 length]) ? temp1 : nil ) ;
406 }
407
408 - (int)currentSongIndex
409 {
410     int temp1;
411     ITDebugLog(@"Getting current song index.");
412         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);
413     ITDebugLog(@"Getting current song index done.");
414     return temp1;
415 }
416
417 - (NSString *)currentSongTitle
418 {
419     NSString *temp1;
420     ITDebugLog(@"Getting current song title.");
421     SInt32 result = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pcls'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
422         
423     //If we're listening to the radio.
424     if (result == 'cURT') {
425         NSString *bad = [NSString stringWithUTF8String:"浳湧"];
426         temp1 = [ITSendAEWithKey('pStT', 'core', 'getd', &savedPSN) stringValue];
427         if ([temp1 isEqualToString:bad]) {
428             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];
429         }
430         temp1 = [temp1 stringByAppendingString:@" (Stream)"];
431     } else if (result == 'prop') {
432                 temp1 = nil;
433         } else {
434         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];
435     }
436     ITDebugLog(@"Getting current song title done.");
437     return ( ([temp1 length]) ? temp1 : nil ) ;
438 }
439
440 - (NSString *)currentSongArtist
441 {
442     NSString *temp1;
443     ITDebugLog(@"Getting current song artist.");
444     if ( [self currentPlaylistClass] != ITMTRemotePlayerRadioPlaylist ) {
445         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];
446     } else {
447         temp1 = @"";
448     }
449     ITDebugLog(@"Getting current song artist done.");
450     return ( ([temp1 length]) ? temp1 : nil ) ;
451 }
452
453 - (NSString *)currentSongComposer
454 {
455     NSString *temp1;
456     ITDebugLog(@"Getting current song artist.");
457     if ( [self currentPlaylistClass] != ITMTRemotePlayerRadioPlaylist ) {
458         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];
459     } else {
460         temp1 = @"";
461     }
462     ITDebugLog(@"Getting current song artist done.");
463     return ( ([temp1 length]) ? temp1 : nil ) ;
464 }
465
466 - (NSString *)currentSongAlbum
467 {
468     NSString *temp1;
469     ITDebugLog(@"Getting current song album.");
470     if ( [self currentPlaylistClass] != ITMTRemotePlayerRadioPlaylist ) {
471         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];
472     } else {
473         temp1 = @"";
474     }
475     ITDebugLog(@"Getting current song album done.");
476     return ( ([temp1 length]) ? temp1 : nil ) ;
477 }
478
479 - (NSString *)currentSongGenre
480 {
481     NSString *temp1;
482     ITDebugLog(@"Getting current song genre.");
483     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];
484     ITDebugLog(@"Getting current song genre done.");
485     return ( ([temp1 length]) ? temp1 : nil ) ;
486 }
487
488 - (NSString *)currentSongLength
489 {
490     SInt32 temp1;
491     NSString *temp2;
492     ITDebugLog(@"Getting current song length.");
493     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];
494     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];
495     if ( ([self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist) || (temp1 == 'cURT') ) { temp2 = @"Continuous"; }
496     ITDebugLog(@"Getting current song length done.");
497     return temp2;
498 }
499
500 - (NSString *)currentSongRemaining
501 {
502     SInt32 duration, current, final;
503     NSString *finalString;
504     
505     ITDebugLog(@"Getting current song remaining time.");
506     
507     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];
508     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];
509     final = duration - current;
510     finalString = [self formatTimeInSeconds:final];
511     
512     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { finalString = nil; }
513     
514     ITDebugLog(@"Getting current song remaining time done.");
515     
516     return finalString;
517 }
518
519 - (NSString *)currentSongElapsed
520 {
521     long final;
522     NSString *finalString;
523     
524     ITDebugLog(@"Getting current song elapsed time.");
525         final = (long)[ITSendAEWithKey('pPos', 'core', 'getd', &savedPSN) int32Value];
526     finalString = [self formatTimeInSeconds:final];
527     ITDebugLog(@"Getting current song elapsed time done.");
528     return finalString;
529 }
530
531 - (NSImage *)currentSongAlbumArt
532 {
533     ITDebugLog(@"Getting current song album art.");
534     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;
535     ITDebugLog(@"Getting current song album art done.");    
536     if (data) {
537         return [[[NSImage alloc] initWithData:data] autorelease];
538     } else {
539         return nil;
540     }
541 }
542
543 - (int)currentSongPlayCount
544 {
545     int count;
546     ITDebugLog(@"Getting current song play count.");
547     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];
548     ITDebugLog(@"Getting current song play count done.");
549     return count;
550 }
551
552 - (float)currentSongRating
553 {
554     float temp1;
555     ITDebugLog(@"Getting current song rating.");
556     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);
557     ITDebugLog(@"Getting current song rating done.");
558     return temp1;
559 }
560
561 - (BOOL)setCurrentSongRating:(float)rating
562 {
563     ITDebugLog(@"Setting current song rating to %f.", rating);
564     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { return NO; }
565         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);
566     ITDebugLog(@"Setting current song rating to %f done.", rating);
567     return YES;
568 }
569
570 - (BOOL)equalizerEnabled
571 {
572     ITDebugLog(@"Getting equalizer enabled status.");
573     int thingy = (int)[ITSendAEWithKey('pEQ ', 'core', 'getd', &savedPSN) int32Value];
574     ITDebugLog(@"Done getting equalizer enabled status.");
575     return (thingy != 0) ? YES : NO;
576 }
577
578 - (BOOL)setEqualizerEnabled:(BOOL)enabled
579 {
580     ITDebugLog(@"Setting equalizer enabled to %i.", enabled);
581         ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pEQ '), from:'null'() }", enabled], 'core', 'setd', &savedPSN);
582     ITDebugLog(@"Done setting equalizer enabled to %i.", enabled);
583     return YES;
584 }
585
586 - (NSArray *)eqPresets
587 {
588     int i;
589     SInt32 numPresets = [ITSendAEWithString(@"kocl:type('cEQP'), '----':(), &subj:()", 'core', 'cnte', &savedPSN) int32Value];
590     NSMutableArray *presets = [[NSMutableArray alloc] initWithCapacity:numPresets];
591     ITDebugLog(@"Getting EQ presets");
592     for (i = 1; i <= numPresets; i++) {
593         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];
594         if (theObj) {
595             ITDebugLog(@"Adding preset %@", theObj);
596             [presets addObject:theObj];
597         }
598     }
599     ITDebugLog(@"Done getting EQ presets");
600     return [presets autorelease];
601 }
602
603 - (int)currentEQPresetIndex
604 {
605     int result;
606     ITDebugLog(@"Getting current EQ preset index.");
607     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];
608     ITDebugLog(@"Getting current EQ preset index done.");
609     return result;
610 }
611
612 - (float)volume
613 {
614     ITDebugLog(@"Getting volume.");
615     ITDebugLog(@"Getting volume done.");
616     return (float)[ITSendAEWithKey('pVol', 'core', 'getd', &savedPSN) int32Value] / 100;
617 }
618
619 - (BOOL)setVolume:(float)volume
620 {
621     ITDebugLog(@"Setting volume to %f.", volume);
622         ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pVol'), from:'null'() }", (long)(volume * 100)], 'core', 'setd', &savedPSN);
623     ITDebugLog(@"Setting volume to %f done.", volume);
624     return YES;
625 }
626
627 - (BOOL)shuffleEnabled
628 {
629     ITDebugLog(@"Getting shuffle enabled status.");
630     BOOL final;
631     int result = (int)[ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pShf'), from:obj { form:'prop', want:type('pPla'), seld:type('pEQP'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
632     if (result != 0) {
633         final = YES;
634     } else {
635         final = NO;
636     }
637     ITDebugLog(@"Getting shuffle enabled status done.");
638     return final;
639 }
640
641 - (BOOL)setShuffleEnabled:(BOOL)enabled
642 {
643     ITDebugLog(@"Set shuffle enabled to %i", enabled);
644         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);
645     ITDebugLog(@"Set shuffle enabled to %i done", enabled);
646     return YES;
647 }
648
649 - (ITMTRemotePlayerRepeatMode)repeatMode
650 {
651     FourCharCode m00f = 0;
652     int result = 0;
653     m00f = (FourCharCode)[ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pRpt'), from:obj { form:'prop', want:type('pPla'), seld:type('pEQP'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value];
654     ITDebugLog(@"Getting repeat mode.");
655     switch (m00f)
656     {
657         //case 'kRp0':
658         case 1800564815:
659             ITDebugLog(@"Repeat off");
660             result = ITMTRemotePlayerRepeatOff;
661             break;
662         case 'kRp1':
663             ITDebugLog(@"Repeat one");
664             result = ITMTRemotePlayerRepeatOne;
665             break;
666         case 'kRpA':
667             ITDebugLog(@"Repeat all");
668             result = ITMTRemotePlayerRepeatAll;
669             break;
670     }
671     ITDebugLog(@"Getting repeat mode done.");
672     return result;
673 }
674
675 - (BOOL)setRepeatMode:(ITMTRemotePlayerRepeatMode)repeatMode
676 {
677     char *m00f;
678     ITDebugLog(@"Setting repeat mode to %i", repeatMode);
679     switch (repeatMode)
680     {
681         case ITMTRemotePlayerRepeatOne:
682             m00f = "kRp1";
683             break;
684         case ITMTRemotePlayerRepeatAll:
685             m00f = "kRpA";
686             break;
687         case ITMTRemotePlayerRepeatOff:
688         default:
689             m00f = "kRp0";
690             break;
691     }
692         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);
693     ITDebugLog(@"Setting repeat mode to %c done", m00f);
694     return YES;
695 }
696
697 - (BOOL)play
698 {
699     ITDebugLog(@"Play");
700         ITSendAE('hook', 'Play', &savedPSN);
701     ITDebugLog(@"Play done");
702     return YES;
703 }
704
705 - (BOOL)pause
706 {
707     ITDebugLog(@"Pause");
708     ITSendAE('hook', 'Paus', &savedPSN);
709     ITDebugLog(@"Pause done");
710     return YES;
711 }
712
713 - (BOOL)goToNextSong
714 {
715     ITDebugLog(@"Go to next track");
716     ITSendAE('hook', 'Next', &savedPSN);
717     ITDebugLog(@"Go to next track done");
718     return YES;
719 }
720
721 - (BOOL)goToPreviousSong
722 {
723     ITDebugLog(@"Go to previous track");
724     ITSendAE('hook', 'Back', &savedPSN);
725     ITDebugLog(@"Go to previous track done");
726     return YES;
727 }
728
729 - (BOOL)forward
730 {
731     ITDebugLog(@"Fast forward action");
732     ITSendAE('hook', 'Fast', &savedPSN);
733     ITDebugLog(@"Fast forward action done");
734     return YES;
735 }
736
737 - (BOOL)rewind
738 {
739     ITDebugLog(@"Rewind action");
740     ITSendAE('hook', 'Rwnd', &savedPSN);
741     ITDebugLog(@"Rewind action done");
742     return YES;
743 }
744
745 - (BOOL)switchToPlaylistAtIndex:(int)index
746 {
747     ITDebugLog(@"Switching to playlist at index %i", index);
748         ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'indx', want:type('cPly'), seld:long(%lu), from:() }", index], 'hook', 'Play', &savedPSN);
749     ITDebugLog(@"Done switching to playlist at index %i", index);
750     return YES;
751 }
752
753 - (BOOL)switchToPlaylistAtIndex:(int)index ofSourceAtIndex:(int)index2
754 {
755     ITDebugLog(@"Switching to playlist at index %i of source %i", index, index2);
756         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);
757     ITDebugLog(@"Done switching to playlist at index %i of source %i", index, index2);
758     return YES;
759 }
760
761 - (BOOL)switchToSongAtIndex:(int)index
762 {
763     ITDebugLog(@"Switching to track at index %i", index);
764         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);
765     ITDebugLog(@"Done switching to track at index %i", index);
766     return YES;
767 }
768
769 - (BOOL)switchToEQAtIndex:(int)index
770 {
771     ITDebugLog(@"Switching to EQ preset at index %i", index);
772     // index should count from 0, but itunes counts from 1, so let's add 1.
773     [self setEqualizerEnabled:YES];
774         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);
775     ITDebugLog(@"Done switching to EQ preset at index %i", index);
776     return YES;
777 }
778
779 - (BOOL)makePlaylistWithTerm:(NSString *)term ofType:(int)type
780 {
781     int i;
782         
783     //Get fixed indexing status
784     BOOL fixed = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", 'core', 'getd', &savedPSN) booleanValue];
785     
786     //Enabled fixed indexing
787     ITSendAEWithString(@"data:long(1), '----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", 'core', 'setd', &savedPSN);
788     
789     //Search for the term
790     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);
791     
792     //If MenuTunes playlist exists
793     if ([ITSendAEWithString(@"'----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'core', 'doex', &savedPSN) booleanValue]) {
794         //Clear old MenuTunes playlist
795                 int numSongs = [ITSendAEWithString(@"kocl:type('cTrk'), '----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'core', 'cnte', &savedPSN) int32Value];
796         for (i = 1; i <= numSongs; i++) {
797             ITSendAEWithString(@"'----':obj { form:'indx', want:type('cTrk'), seld:long(1), from:obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() } }", 'core', 'delo', &savedPSN);
798         }
799     } else {
800         //Create MenuTunes playlist
801         ITSendAEWithString(@"prdt:{ pnam:\"MenuTunes\" }, kocl:type('cPly'), &subj:()", 'core', 'crel', &savedPSN);
802     }
803     
804     //Duplicate search results to playlist
805     for (i = 1; i <= [searchResults numberOfItems]; i++) {
806         ITSendAEWithStringAndObject(@"insh:obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", [[searchResults descriptorAtIndex:i] aeDesc], 'core', 'clon', &savedPSN);
807     }
808     //Reset fixed indexing
809     ITSendAEWithString([NSString stringWithFormat:@"data:long(%i), '----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", fixed], 'core', 'setd', &savedPSN);
810     
811     //Play MenuTunes playlist
812     ITSendAEWithString(@"'----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'hook', 'Play', &savedPSN);
813     
814     return YES;
815 }
816
817 - (BOOL)isPlaying
818 {
819         return ([ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pcls'), from:obj { form:'prop', want:type('prop'), seld:type('pTrk'), from:'null'() } }", 'core', 'getd', &savedPSN) int32Value] != 'prop');
820 }
821
822 - (ProcessSerialNumber)iTunesPSN
823 {
824     /*NSArray *apps = [[NSWorkspace sharedWorkspace] launchedApplications];
825     ProcessSerialNumber number;
826     int i;
827     int count = [apps count];
828     
829     number.highLongOfPSN = kNoProcess;
830     
831     for (i = 0; i < count; i++)
832     {
833         NSDictionary *curApp = [apps objectAtIndex:i];
834         
835         if ([[curApp objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"])
836         {
837             number.highLongOfPSN = [[curApp objectForKey:
838                 @"NSApplicationProcessSerialNumberHigh"] intValue];
839             number.lowLongOfPSN = [[curApp objectForKey:
840                 @"NSApplicationProcessSerialNumberLow"] intValue];
841         }
842     }
843     return number;*/
844     ProcessSerialNumber number;
845     number.highLongOfPSN = kNoProcess;
846     number.lowLongOfPSN = 0;
847     ITDebugLog(@"Getting iTunes' PSN.");
848     while ( (GetNextProcess(&number) == noErr) ) 
849     {
850         CFStringRef name;
851         if ( (CopyProcessName(&number, &name) == noErr) )
852         {
853             if ([(NSString *)name isEqualToString:@"iTunes"])
854             {
855                 ITDebugLog(@"iTunes' highLPongOfPSN: %lu.", number.highLongOfPSN);
856                 ITDebugLog(@"iTunes' lowLongOfPSN: %lu.", number.lowLongOfPSN);
857                 ITDebugLog(@"Done getting iTunes' PSN.");
858                 return number;
859             }
860             [(NSString *)name release];
861         }
862     }
863     ITDebugLog(@"Failed getting iTunes' PSN.");
864     return number;
865 }
866
867 - (NSString*)formatTimeInSeconds:(long)seconds {
868     long final = seconds;
869     NSString *finalString;
870     if (final >= 60) {
871         if (final > 3600) {
872             finalString = [NSString stringWithFormat:@"%i:%@:%@",(final / 3600),[self zeroSixty:(int)((final % 3600) / 60)],[self zeroSixty:(int)((final % 3600) % 60)]];
873         } else {
874             finalString = [NSString stringWithFormat:@"%i:%@",(final / 60),[self zeroSixty:(int)(final % 60)]];
875         }
876     } else {
877         finalString = [NSString stringWithFormat:@"0:%@",[self zeroSixty:(int)final]];
878     }
879     return finalString;
880 }
881 - (NSString*)zeroSixty:(int)seconds {
882     if ( (seconds < 10) && (seconds > 0) ) {
883         return [NSString stringWithFormat:@"0%i",seconds];
884     } else if ( (seconds == 0) ) {
885         return [NSString stringWithFormat:@"00"];
886     } else {
887         return [NSString stringWithFormat:@"%i",seconds];
888     }
889 }
890
891 @end