7bbe55129ea15161247a2fafd422807d3a02f54b
[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: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: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: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         ITDebugLog(@"No sources.");
172         return nil;
173     }
174     
175     for (k = 1; k <= numSources ; k++) {
176         SInt32 numPlaylists = [ITSendAEWithString([NSString stringWithFormat:@"kocl:type('cPly'), '----':obj { form:'indx', want:type('cSrc'), seld:long(%u), from:() }",k], 'core', 'cnte', &savedPSN) int32Value];
177         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];
178         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];
179         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];
180         unsigned long class;
181         if (sourceName) {
182             NSMutableArray *aSource = [[NSMutableArray alloc] init];
183             [aSource addObject:sourceName];
184             switch (fourcc) {
185                 case 'kTun':
186                     class = ITMTRemoteRadioSource;
187                     break;
188                 case 'kDev':
189                     class = ITMTRemoteGenericDeviceSource;
190                     break;
191                 case 'kPod':
192                     class = ITMTRemoteiPodSource;
193                     break;
194                 case 'kMCD':
195                 case 'kACD':
196                     class = ITMTRemoteCDSource;
197                     break;
198                 case 'kShd':
199                     class = ITMTRemoteSharedLibrarySource;
200                     break;
201                 case 'kUnk':
202                 case 'kLib':
203                 default:
204                     class = ITMTRemoteLibrarySource;
205                     break;
206             }
207             ITDebugLog(@"Adding source %@ of type %i at index %i", sourceName, class, index);
208             [aSource addObject:[NSNumber numberWithInt:class]];
209             [aSource addObject:[NSNumber numberWithInt:index]];
210             for (i = 1; i <= numPlaylists; i++) {
211                 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];
212                 NSString *theObj = [ITSendAEWithString(sendStr, 'core', 'getd', &savedPSN) stringValue];
213                 ITDebugLog(@" - Adding playlist %@", theObj);
214                 if (theObj) {
215                     [aSource addObject:theObj];
216                 }
217             }
218             [allSources addObject:[aSource autorelease]];
219         } else {
220             ITDebugLog(@"Source at index %i disappeared.", k);
221         }
222     }
223     ITDebugLog(@"Finished getting playlists.");
224     return [allSources autorelease];
225 }
226
227 - (NSArray *)artists
228 {
229     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);
230     int i;
231     NSMutableArray *array = [[NSMutableArray alloc] init];
232     NSArray *returnArray;
233     for (i = 1; i <= [rawr numberOfItems]; i++) {
234         NSString *artist = [[rawr descriptorAtIndex:i] stringValue];
235         if (artist && [artist length] && ![array containsObject:artist]) {
236             [array addObject:artist];
237         }
238     }
239     [array sortUsingSelector:@selector(caseInsensitiveCompare:)];
240     returnArray = [NSArray arrayWithArray:array];
241     [array release];
242     return returnArray;
243 }
244
245 - (NSArray *)albums
246 {
247     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);
248     int i;
249     NSMutableArray *array = [[NSMutableArray alloc] init];
250     NSArray *returnArray;
251     for (i = 1; i <= [rawr numberOfItems]; i++) {
252         NSString *album = [[rawr descriptorAtIndex:i] stringValue];
253         if (album && [album length] && ![array containsObject:album]) {
254             [array addObject:album];
255         }
256     }
257     [array sortUsingSelector:@selector(caseInsensitiveCompare:)];
258     returnArray = [NSArray arrayWithArray:array];
259     [array release];
260     return returnArray;
261 }
262
263 - (int)numberOfSongsInPlaylistAtIndex:(int)index
264 {
265     int temp1;
266     ITDebugLog(@"Getting number of songs in playlist at index %i", index);
267     temp1 = [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', 'getd', &savedPSN) int32Value];
268     ITDebugLog(@"Getting number of songs in playlist at index %i done", index);
269     return temp1;
270 }
271
272 - (ITMTRemotePlayerSource)currentSource
273 {
274     SInt32 fourcc;
275
276     ITDebugLog(@"Getting current source.");   
277     
278     fourcc = [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];
279     
280     switch (fourcc) {
281         case 'kTun':
282             ITDebugLog(@"Getting current source done. Source: Radio.");
283             return ITMTRemoteRadioSource;
284             break;
285         case 'kDev':
286             ITDebugLog(@"Getting current source done. Source: Generic Device.");
287             return ITMTRemoteGenericDeviceSource;
288         case 'kPod':
289             ITDebugLog(@"Getting current source done. Source: iPod.");
290             return ITMTRemoteiPodSource; //this is stupid
291             break;
292         case 'kMCD':
293         case 'kACD':
294             ITDebugLog(@"Getting current source done. Source: CD.");
295             return ITMTRemoteCDSource;
296             break;
297         case 'kShd':
298             ITDebugLog(@"Getting current source done. Source: Shared Library.");
299             return ITMTRemoteSharedLibrarySource;
300             break;
301         case 'kUnk':
302         case 'kLib':
303         default:
304             ITDebugLog(@"Getting current source done. Source: Library.");
305             return ITMTRemoteLibrarySource;
306             break;
307     }
308 }
309
310 - (int)currentSourceIndex
311 {
312     ITDebugLog(@"Getting current source.");
313     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];
314 }
315
316 - (ITMTRemotePlayerPlaylistClass)currentPlaylistClass
317 {
318     SInt32 realResult;
319     ITDebugLog(@"Getting current playlist class");
320     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];
321     switch (realResult)
322            {
323            case 'cLiP':
324                ITDebugLog(@"Getting current playlist class done. Class: Library.");
325                return ITMTRemotePlayerLibraryPlaylist;
326                break;
327            case 'cRTP':
328                ITDebugLog(@"Getting current playlist class done. Class: Radio.");
329                return ITMTRemotePlayerRadioPlaylist;
330                break;
331            default:
332                ITDebugLog(@"Getting current playlist class done. Class: Standard playlist.");
333                return ITMTRemotePlayerPlaylist;
334            }
335 }
336
337 - (int)currentPlaylistIndex
338 {  
339     int temp1;
340     ITDebugLog(@"Getting current playlist index.");
341     temp1 = [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];
342     ITDebugLog(@"Getting current playlist index done.");
343     return temp1;
344 }
345
346 - (NSString *)songTitleAtIndex:(int)index
347 {
348     NSString *temp1;
349     ITDebugLog(@"Getting song title at index %i.", index);
350     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];
351     ITDebugLog(@"Getting song title at index %i done.", index);
352     return ( ([temp1 length]) ? temp1 : nil ) ;
353 }
354
355 - (int)currentAlbumTrackCount
356 {
357     int temp1;
358     ITDebugLog(@"Getting current album track count.");
359     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];
360     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { temp1 = 0; }
361     ITDebugLog(@"Getting current album track count done.");
362     return temp1;
363 }
364
365 - (int)currentSongTrack
366 {
367     int temp1;
368     ITDebugLog(@"Getting current song track.");
369     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];
370     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { temp1 = 0; }
371     ITDebugLog(@"Getting current song track done.");
372     return temp1;
373 }
374
375 - (NSString *)playerStateUniqueIdentifier
376 {
377     NSString *temp1;
378     ITDebugLog(@"Getting current unique identifier.");
379     SInt32 cls = [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];
380     if ( ([self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist) || (cls == 'cURT') ) {
381         temp1 = [ITSendAEWithKey('pStT', 'core', 'getd', &savedPSN) stringValue];
382                 NSLog(@"%@", temp1);
383     } else {
384         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]];
385     }
386     ITDebugLog(@"Getting current unique identifier done.");
387     return ( ([temp1 length]) ? temp1 : nil ) ;
388 }
389
390 - (int)currentSongIndex
391 {
392     int temp1;
393     ITDebugLog(@"Getting current song index.");
394     temp1 = [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];
395     ITDebugLog(@"Getting current song index done.");
396     return temp1;
397 }
398
399 - (NSString *)currentSongTitle
400 {
401     NSString *temp1;
402     ITDebugLog(@"Getting current song title.");
403     
404     //If we're listening to the radio.
405     if ([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] == 'cURT') {
406         NSString *bad = [NSString stringWithUTF8String:"浳湧"];
407         temp1 = [ITSendAEWithKey('pStT', 'core', 'getd', &savedPSN) stringValue];
408         if ([temp1 isEqualToString:bad]) {
409                         NSLog(@"arrrr");
410             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];
411         }
412         temp1 = [temp1 stringByAppendingString:@" (Stream)"];
413     } else {
414         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];
415     }
416     ITDebugLog(@"Getting current song title done.");
417     return ( ([temp1 length]) ? temp1 : nil ) ;
418 }
419
420 - (NSString *)currentSongArtist
421 {
422     NSString *temp1;
423     ITDebugLog(@"Getting current song artist.");
424     if ( [self currentPlaylistClass] != ITMTRemotePlayerRadioPlaylist ) {
425         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];
426     } else {
427         temp1 = @"";
428     }
429     ITDebugLog(@"Getting current song artist done.");
430     return ( ([temp1 length]) ? temp1 : nil ) ;
431 }
432
433 - (NSString *)currentSongComposer
434 {
435     NSString *temp1;
436     ITDebugLog(@"Getting current song artist.");
437     if ( [self currentPlaylistClass] != ITMTRemotePlayerRadioPlaylist ) {
438         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];
439     } else {
440         temp1 = @"";
441     }
442     ITDebugLog(@"Getting current song artist done.");
443     return ( ([temp1 length]) ? temp1 : nil ) ;
444 }
445
446 - (NSString *)currentSongAlbum
447 {
448     NSString *temp1;
449     ITDebugLog(@"Getting current song album.");
450     if ( [self currentPlaylistClass] != ITMTRemotePlayerRadioPlaylist ) {
451         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];
452     } else {
453         temp1 = @"";
454     }
455     ITDebugLog(@"Getting current song album done.");
456     return ( ([temp1 length]) ? temp1 : nil ) ;
457 }
458
459 - (NSString *)currentSongGenre
460 {
461     NSString *temp1;
462     ITDebugLog(@"Getting current song genre.");
463     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];
464     ITDebugLog(@"Getting current song genre done.");
465     return ( ([temp1 length]) ? temp1 : nil ) ;
466 }
467
468 - (NSString *)currentSongLength
469 {
470     SInt32 temp1;
471     NSString *temp2;
472     ITDebugLog(@"Getting current song length.");
473     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];
474     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];
475     if ( ([self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist) || (temp1 == 'cURT') ) { temp2 = @"Continuous"; }
476     ITDebugLog(@"Getting current song length done.");
477     return temp2;
478 }
479
480 - (NSString *)currentSongRemaining
481 {
482     SInt32 duration, current, final;
483     NSString *finalString;
484     
485     ITDebugLog(@"Getting current song remaining time.");
486     
487     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];
488     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];
489     final = duration - current;
490     finalString = [self formatTimeInSeconds:final];
491     
492     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { finalString = nil; }
493     
494     ITDebugLog(@"Getting current song remaining time done.");
495     
496     return finalString;
497 }
498
499 - (NSString *)currentSongElapsed
500 {
501     long final;
502     NSString *finalString;
503     
504     ITDebugLog(@"Getting current song elapsed time.");
505     final = (long)[ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pPos'), from:'null'() }", 'core', 'getd', &savedPSN) int32Value];
506     finalString = [self formatTimeInSeconds:final];
507     ITDebugLog(@"Getting current song elapsed time done.");
508     return finalString;
509 }
510
511 - (NSImage *)currentSongAlbumArt
512 {
513     ITDebugLog(@"Getting current song album art.");
514     NSData *data = [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];
515     ITDebugLog(@"Getting current song album art done.");    
516     if (data) {
517         return [[[NSImage alloc] initWithData:data] autorelease];
518     } else {
519         return nil;
520     }
521 }
522
523 - (int)currentSongPlayCount
524 {
525     int count;
526     ITDebugLog(@"Getting current song play count.");
527     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];
528     ITDebugLog(@"Getting current song play count done.");
529     return count;
530 }
531
532 - (float)currentSongRating
533 {
534     float temp1;
535     ITDebugLog(@"Getting current song rating.");
536     temp1 = ((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);
537     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { temp1 = -1.0; }
538     ITDebugLog(@"Getting current song rating done.");
539     return temp1;
540 }
541
542 - (BOOL)setCurrentSongRating:(float)rating
543 {
544     ITDebugLog(@"Setting current song rating to %f.", rating);
545     if ( [self currentPlaylistClass] == ITMTRemotePlayerRadioPlaylist ) { return NO; }
546         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);
547     ITDebugLog(@"Setting current song rating to %f done.", rating);
548     return YES;
549 }
550
551 - (BOOL)equalizerEnabled
552 {
553     ITDebugLog(@"Getting equalizer enabled status.");
554     int thingy = (int)[ITSendAEWithKey('pEQ ', 'core', 'getd', &savedPSN) int32Value];
555     ITDebugLog(@"Done getting equalizer enabled status.");
556     return (thingy != 0) ? YES : NO;
557 }
558
559 - (BOOL)setEqualizerEnabled:(BOOL)enabled
560 {
561     ITDebugLog(@"Setting equalizer enabled to %i.", enabled);
562         ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pEQ '), from:'null'() }", enabled], 'core', 'setd', &savedPSN);
563     ITDebugLog(@"Done setting equalizer enabled to %i.", enabled);
564     return YES;
565 }
566
567 - (NSArray *)eqPresets
568 {
569     int i;
570     SInt32 numPresets = [ITSendAEWithString(@"kocl:type('cEQP'), '----':(), &subj:()", 'core', 'cnte', &savedPSN) int32Value];
571     NSMutableArray *presets = [[NSMutableArray alloc] initWithCapacity:numPresets];
572     ITDebugLog(@"Getting EQ presets");
573     for (i = 1; i <= numPresets; i++) {
574         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];
575         if (theObj) {
576             ITDebugLog(@"Adding preset %@", theObj);
577             [presets addObject:theObj];
578         }
579     }
580     ITDebugLog(@"Done getting EQ presets");
581     return [presets autorelease];
582 }
583
584 - (int)currentEQPresetIndex
585 {
586     int result;
587     ITDebugLog(@"Getting current EQ preset index.");
588     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];
589     ITDebugLog(@"Getting current EQ preset index done.");
590     return result;
591 }
592
593 - (float)volume
594 {
595     ITDebugLog(@"Getting volume.");
596     ITDebugLog(@"Getting volume done.");
597     return (float)[ITSendAEWithKey('pVol', 'core', 'getd', &savedPSN) int32Value] / 100;
598 }
599
600 - (BOOL)setVolume:(float)volume
601 {
602     ITDebugLog(@"Setting volume to %f.", volume);
603         ITSendAEWithString([NSString stringWithFormat:@"data:long(%lu), '----':obj { form:'prop', want:type('prop'), seld:type('pVol'), from:'null'() }", (long)(volume * 100)], 'core', 'setd', &savedPSN);
604     ITDebugLog(@"Setting volume to %f done.", volume);
605     return YES;
606 }
607
608 - (BOOL)shuffleEnabled
609 {
610     ITDebugLog(@"Getting shuffle enabled status.");
611     BOOL final;
612     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];
613     if (result != 0) {
614         final = YES;
615     } else {
616         final = NO;
617     }
618     ITDebugLog(@"Getting shuffle enabled status done.");
619     return final;
620 }
621
622 - (BOOL)setShuffleEnabled:(BOOL)enabled
623 {
624     ITDebugLog(@"Set shuffle enabled to %i", enabled);
625         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);
626     ITDebugLog(@"Set shuffle enabled to %i done", enabled);
627     return YES;
628 }
629
630 - (ITMTRemotePlayerRepeatMode)repeatMode
631 {
632     FourCharCode m00f = 0;
633     int result = 0;
634     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];
635     ITDebugLog(@"Getting repeat mode.");
636     switch (m00f)
637     {
638         //case 'kRp0':
639         case 1800564815:
640             ITDebugLog(@"Repeat off");
641             result = ITMTRemotePlayerRepeatOff;
642             break;
643         case 'kRp1':
644             ITDebugLog(@"Repeat one");
645             result = ITMTRemotePlayerRepeatOne;
646             break;
647         case 'kRpA':
648             ITDebugLog(@"Repeat all");
649             result = ITMTRemotePlayerRepeatAll;
650             break;
651     }
652     ITDebugLog(@"Getting repeat mode done.");
653     return result;
654 }
655
656 - (BOOL)setRepeatMode:(ITMTRemotePlayerRepeatMode)repeatMode
657 {
658     char *m00f;
659     ITDebugLog(@"Setting repeat mode to %i", repeatMode);
660     switch (repeatMode)
661     {
662         case ITMTRemotePlayerRepeatOne:
663             m00f = "kRp1";
664             break;
665         case ITMTRemotePlayerRepeatAll:
666             m00f = "kRpA";
667             break;
668         case ITMTRemotePlayerRepeatOff:
669         default:
670             m00f = "kRp0";
671             break;
672     }
673         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);
674     ITDebugLog(@"Setting repeat mode to %c done", m00f);
675     return YES;
676 }
677
678 - (BOOL)play
679 {
680     ITDebugLog(@"Play");
681         ITSendAE('hook', 'Play', &savedPSN);
682     ITDebugLog(@"Play done");
683     return YES;
684 }
685
686 - (BOOL)pause
687 {
688     ITDebugLog(@"Pause");
689     ITSendAE('hook', 'Paus', &savedPSN);
690     ITDebugLog(@"Pause done");
691     return YES;
692 }
693
694 - (BOOL)goToNextSong
695 {
696     ITDebugLog(@"Go to next track");
697     ITSendAE('hook', 'Next', &savedPSN);
698     ITDebugLog(@"Go to next track done");
699     return YES;
700 }
701
702 - (BOOL)goToPreviousSong
703 {
704     ITDebugLog(@"Go to previous track");
705     ITSendAE('hook', 'Prev', &savedPSN);
706     ITDebugLog(@"Go to previous track done");
707     return YES;
708 }
709
710 - (BOOL)forward
711 {
712     ITDebugLog(@"Fast forward action");
713     ITSendAE('hook', 'Fast', &savedPSN);
714     ITDebugLog(@"Fast forward action done");
715     return YES;
716 }
717
718 - (BOOL)rewind
719 {
720     ITDebugLog(@"Rewind action");
721     ITSendAE('hook', 'Rwnd', &savedPSN);
722     ITDebugLog(@"Rewind action done");
723     return YES;
724 }
725
726 - (BOOL)switchToPlaylistAtIndex:(int)index
727 {
728     ITDebugLog(@"Switching to playlist at index %i", index);
729         ITSendAEWithString([NSString stringWithFormat:@"'----':obj { form:'indx', want:type('cPly'), seld:long(%lu), from:() }", index], 'hook', 'Play', &savedPSN);
730     ITDebugLog(@"Done switching to playlist at index %i", index);
731     return YES;
732 }
733
734 - (BOOL)switchToPlaylistAtIndex:(int)index ofSourceAtIndex:(int)index2
735 {
736     ITDebugLog(@"Switching to playlist at index %i of source %i", index, index2);
737         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);
738     ITDebugLog(@"Done switching to playlist at index %i of source %i", index, index2);
739     return YES;
740 }
741
742 - (BOOL)switchToSongAtIndex:(int)index
743 {
744     ITDebugLog(@"Switching to track at index %i", index);
745         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);
746     ITDebugLog(@"Done switching to track at index %i", index);
747     return YES;
748 }
749
750 - (BOOL)switchToEQAtIndex:(int)index
751 {
752     ITDebugLog(@"Switching to EQ preset at index %i", index);
753     // index should count from 0, but itunes counts from 1, so let's add 1.
754     [self setEqualizerEnabled:YES];
755         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);
756     ITDebugLog(@"Done switching to EQ preset at index %i", index);
757     return YES;
758 }
759
760 - (BOOL)makePlaylistWithTerm:(NSString *)term ofType:(int)type
761 {
762     int i;
763     
764     //Get fixed indexing status
765     BOOL fixed = [ITSendAEWithString(@"'----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", 'core', 'getd', &savedPSN) booleanValue];
766     
767     //Enabled fixed indexing
768     ITSendAEWithString(@"data:long(1), '----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", 'core', 'setd', &savedPSN);
769     
770     //Search for the term
771     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);
772     
773     //If MenuTunes playlist exists
774     if ([ITSendAEWithString(@"'----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'core', 'doex', &savedPSN) booleanValue]) {
775         //Clear old MenuTunes playlist
776         int numSongs = [ITSendAEWithString(@"kocl:type('cTrk'), '----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:obj { form:'prop', want:type('prop'), seld:type('ctnr'), from:obj { form:'prop', want:type('prop'), seld:type('pPla'), from:'null'() } } }", 'core', 'cnte', &savedPSN) int32Value];
777         for (i = 1; i <= numSongs; i++) {
778             ITSendAEWithString(@"'----':obj { form:'indx', want:type('cTrk'), seld:long(1), from:obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() } }", 'core', 'delo', &savedPSN);
779         }
780     } else {
781         //Create MenuTunes playlist
782         ITSendAEWithString(@"prdt:{ pnam:\"MenuTunes\" }, kocl:type('cPly'), &subj:()", 'core', 'crel', &savedPSN);
783     }
784     
785     //Duplicate search results to playlist
786     for (i = 1; i <= [searchResults numberOfItems]; i++) {
787         ITSendAEWithStringAndObject(@"insh:obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", [[searchResults descriptorAtIndex:i] aeDesc], 'core', 'clon', &savedPSN);
788     }
789     //Reset fixed indexing
790     ITSendAEWithString([NSString stringWithFormat:@"data:long(%i), '----':obj { form:'prop', want:type('prop'), seld:type('pFix'), from:'null'() }", fixed], 'core', 'setd', &savedPSN);
791     
792     //Play MenuTunes playlist
793     ITSendAEWithString(@"'----':obj { form:'name', want:type('cPly'), seld:\"MenuTunes\", from:'null'() }", 'hook', 'Play', &savedPSN);
794     
795     return YES;
796 }
797
798 - (ProcessSerialNumber)iTunesPSN
799 {
800     /*NSArray *apps = [[NSWorkspace sharedWorkspace] launchedApplications];
801     ProcessSerialNumber number;
802     int i;
803     int count = [apps count];
804     
805     number.highLongOfPSN = kNoProcess;
806     
807     for (i = 0; i < count; i++)
808     {
809         NSDictionary *curApp = [apps objectAtIndex:i];
810         
811         if ([[curApp objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"])
812         {
813             number.highLongOfPSN = [[curApp objectForKey:
814                 @"NSApplicationProcessSerialNumberHigh"] intValue];
815             number.lowLongOfPSN = [[curApp objectForKey:
816                 @"NSApplicationProcessSerialNumberLow"] intValue];
817         }
818     }
819     return number;*/
820     ProcessSerialNumber number;
821     number.highLongOfPSN = kNoProcess;
822     number.lowLongOfPSN = 0;
823     ITDebugLog(@"Getting iTunes' PSN.");
824     while ( (GetNextProcess(&number) == noErr) ) 
825     {
826         CFStringRef name;
827         if ( (CopyProcessName(&number, &name) == noErr) )
828         {
829             if ([(NSString *)name isEqualToString:@"iTunes"])
830             {
831                 ITDebugLog(@"iTunes' highLPongOfPSN: %lu.", number.highLongOfPSN);
832                 ITDebugLog(@"iTunes' lowLongOfPSN: %lu.", number.lowLongOfPSN);
833                 ITDebugLog(@"Done getting iTunes' PSN.");
834                 return number;
835             }
836             [(NSString *)name release];
837         }
838     }
839     ITDebugLog(@"Failed getting iTunes' PSN.");
840     return number;
841 }
842
843 - (NSString*)formatTimeInSeconds:(long)seconds {
844     long final = seconds;
845     NSString *finalString;
846     if (final >= 60) {
847         if (final > 3600) {
848             finalString = [NSString stringWithFormat:@"%i:%@:%@",(final / 3600),[self zeroSixty:(int)((final % 3600) / 60)],[self zeroSixty:(int)((final % 3600) % 60)]];
849         } else {
850             finalString = [NSString stringWithFormat:@"%i:%@",(final / 60),[self zeroSixty:(int)(final % 60)]];
851         }
852     } else {
853         finalString = [NSString stringWithFormat:@"0:%@",[self zeroSixty:(int)final]];
854     }
855     return finalString;
856 }
857 - (NSString*)zeroSixty:(int)seconds {
858     if ( (seconds < 10) && (seconds > 0) ) {
859         return [NSString stringWithFormat:@"0%i",seconds];
860     } else if ( (seconds == 0) ) {
861         return [NSString stringWithFormat:@"00"];
862     } else {
863         return [NSString stringWithFormat:@"%i",seconds];
864     }
865 }
866
867 @end