Converted one bit of AppleScripting to AEs
[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 *)title
11 {
12     return @"iTunes Plug-in";
13 }
14
15 - (NSString *)information;
16 {
17     return @"Default MenuTunes plugin to control iTunes. Written by iThink Software.";
18 }
19
20 - (NSImage *)icon
21 {
22     return nil;
23 }
24
25 - (BOOL)begin
26 {
27     iTunesPSN = [self iTunesPSN];
28
29     //We won't need this once we're pure AEs
30     asComponent = OpenDefaultComponent(kOSAComponentType, kAppleScriptSubtype);
31
32     //Register for application termination in NSWorkspace
33     [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(applicationLaunched:) name:NSWorkspaceDidLaunchApplicationNotification object:nil];
34     [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(applicationTerminated:) name:NSWorkspaceDidTerminateApplicationNotification object:nil];
35
36     return YES;
37 }
38
39 - (BOOL)halt
40 {
41     iTunesPSN.highLongOfPSN = kNoProcess;
42
43     //We won't need this once we're pure AEs
44     CloseComponent(asComponent);
45
46     //Unregister for application termination in NSWorkspace
47     [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
48
49     return YES;
50 }
51
52 - (BOOL)isAppRunning
53 {
54     NSArray *apps = [[NSWorkspace sharedWorkspace] launchedApplications];
55     int i,count = [apps count];
56
57     for (i = 0; i < count; i++) {
58         if ([[[apps objectAtIndex:i] objectForKey:@"NSApplicationName"]
59                 isEqualToString:@"iTunes"]) {
60             return YES;
61         }
62     }
63     return NO;
64 }
65
66 - (PlayerState)playerState
67 {
68     NSString *result = [self runScriptAndReturnResult:@"get player state"];
69
70     if ([result isEqualToString:@"playing"]) {
71         return playing;
72     } else if ([result isEqualToString:@"paused"]) {
73         return paused;
74     } else if ([result isEqualToString:@"stopped"]) {
75         return stopped;
76     } else if ([result isEqualToString:@"rewinding"]) {
77         return rewinding;
78     } else if ([result isEqualToString:@"fast forwarding"]) {
79         return forwarding;
80     }
81
82     return stopped;
83 }
84
85 - (NSArray *)playlists
86 {
87     int i;
88     int numPresets = [[self runScriptAndReturnResult:@"get number of playlists"] intValue];
89     NSMutableArray *presets = [[NSMutableArray alloc] init];
90     for (i = 1; i <= numPresets; i++) {
91         [presets addObject:[self runScriptAndReturnResult:[NSString stringWithFormat:@"get name of playlist %i", i]]];
92     }
93     return [presets autorelease];
94 }
95
96 - (int)numberOfSongsInPlaylistAtIndex:(int)index
97 {
98     NSString *result = [self runScriptAndReturnResult:[NSString stringWithFormat:@"get number of tracks in playlist %i", index]];
99     return [result intValue];
100 }
101
102 - (NSString *)classOfPlaylistAtIndex:(int)index
103 {
104     //Not working yet. It returns the 4 character code instead of a name.
105     int realResult = [[ITAppleEventCenter sharedCenter]
106                 sendTwoTierAEWithRequestedKeyForNumber:@"pcls"
107                                                 fromObjectByKey:@"pPla" eventClass:@"core" eventID:@"getd"
108                                                                                                           appPSN:[self iTunesPSN]];
109
110     //result = [self runScriptAndReturnResult:[NSString stringWithFormat:@"get class of playlist %i", index]];
111     if (realResult == 'cRTP') return @"radio tuner playlist";
112     else return @"playlist";
113 }
114
115 - (int)currentPlaylistIndex
116 {
117     int result;
118     result = [[ITAppleEventCenter sharedCenter]
119                 sendTwoTierAEWithRequestedKeyForNumber:@"pidx"
120                                                             fromObjectByKey:@"pPla" eventClass:@"core" eventID:@"getd"
121                                                                            appPSN:[self iTunesPSN]];
122     return result;
123 }
124
125 - (NSString *)songTitleAtIndex:(int)index
126 {
127     NSString *result = [self runScriptAndReturnResult:[NSString stringWithFormat:@"get name of track %i of current playlist", index]];
128     return result;
129 }
130
131 - (int)currentSongIndex
132 {
133     int result;
134     result = [[ITAppleEventCenter sharedCenter]
135                 sendTwoTierAEWithRequestedKeyForNumber:@"pidx"
136                                                             fromObjectByKey:@"pTrk" eventClass:@"core" eventID:@"getd"
137                                                                            appPSN:[self iTunesPSN]];
138     return result;
139 }
140
141 - (NSString *)currentSongTitle
142 {
143     return [[ITAppleEventCenter sharedCenter] sendTwoTierAEWithRequestedKey:@"pnam"
144                                                                                                 fromObjectByKey:@"pTrk" eventClass:@"core" eventID:@"getd"
145                                                                                                             appPSN:[self iTunesPSN]];
146 }
147
148 - (NSString *)currentSongArtist
149 {
150     return [[ITAppleEventCenter sharedCenter] sendTwoTierAEWithRequestedKey:@"pArt"
151                                                                                                 fromObjectByKey:@"pTrk" eventClass:@"core" eventID:@"getd"
152                                                                                                             appPSN:[self iTunesPSN]];
153 }
154
155 - (NSString *)currentSongAlbum
156 {
157     return [[ITAppleEventCenter sharedCenter] sendTwoTierAEWithRequestedKey:@"pAlb"
158                                                                                                 fromObjectByKey:@"pTrk" eventClass:@"core" eventID:@"getd"
159                                                                                                             appPSN:[self iTunesPSN]];
160 }
161
162 - (NSString *)currentSongGenre
163 {
164     return [[ITAppleEventCenter sharedCenter] sendTwoTierAEWithRequestedKey:@"pGen"
165                                                                                                 fromObjectByKey:@"pTrk" eventClass:@"core" eventID:@"getd"
166                                                                                                             appPSN:[self iTunesPSN]];
167 }
168
169 - (NSString *)currentSongLength
170 {
171     return [[ITAppleEventCenter sharedCenter] sendTwoTierAEWithRequestedKey:@"pTim"
172                                                                                                 fromObjectByKey:@"pTrk" eventClass:@"core" eventID:@"getd"
173                                                                                                             appPSN:[self iTunesPSN]];
174 }
175
176 - (NSString *)currentSongRemaining
177 {
178     long duration = [[ITAppleEventCenter sharedCenter]
179                         sendTwoTierAEWithRequestedKeyForNumber:@"pDur"
180                                                                           fromObjectByKey:@"pTrk" eventClass:@"core" eventID:@"getd"
181                                                                                          appPSN:[self iTunesPSN]];
182     long current = [[ITAppleEventCenter sharedCenter]
183                         sendAEWithRequestedKeyForNumber:@"pPos"
184                                                                         eventClass:@"core" eventID:@"getd"
185                                                                             appPSN:[self iTunesPSN]];
186
187     return [[NSNumber numberWithLong:duration - current] stringValue];
188 }
189
190 - (NSArray *)eqPresets;
191 {
192     int i;
193     int numPresets = [[self runScriptAndReturnResult:@"get number of EQ presets"] intValue];
194     NSMutableArray *presets = [[NSMutableArray alloc] init];
195
196     for (i = 1; i <= numPresets; i++) {
197         [presets addObject:[self runScriptAndReturnResult:[NSString stringWithFormat:@"get name of EQ preset %i", i]]];
198     }
199
200     return [presets autorelease];
201 }
202
203 - (int)currentEQPresetIndex
204 {
205     int result;
206     result = [[ITAppleEventCenter sharedCenter]
207                 sendTwoTierAEWithRequestedKeyForNumber:@"pidx"
208                                                             fromObjectByKey:@"pEQP" eventClass:@"core" eventID:@"getd"
209                                                                            appPSN:[self iTunesPSN]];
210     return result;
211 }
212
213 - (BOOL)play
214 {
215     [[ITAppleEventCenter sharedCenter] sendAEWithEventClass:@"hook" eventID:@"Play"
216                                                                                    appPSN:[self iTunesPSN]];
217     return YES;
218 }
219
220 - (BOOL)pause
221 {
222     [[ITAppleEventCenter sharedCenter] sendAEWithEventClass:@"hook" eventID:@"Paus"
223                                                                                    appPSN:[self iTunesPSN]];
224     return YES;
225 }
226
227 - (BOOL)goToNextSong
228 {
229     [[ITAppleEventCenter sharedCenter] sendAEWithEventClass:@"hook" eventID:@"Next"
230                                                                                    appPSN:[self iTunesPSN]];
231     return YES;
232 }
233
234 - (BOOL)goToPreviousSong
235 {
236     [[ITAppleEventCenter sharedCenter] sendAEWithEventClass:@"hook" eventID:@"Prev"
237                                                                                    appPSN:[self iTunesPSN]];
238     return YES;
239 }
240
241 - (BOOL)fastForward
242 {
243     [[ITAppleEventCenter sharedCenter] sendAEWithEventClass:@"hook" eventID:@"Fast"
244                                                                                    appPSN:[self iTunesPSN]];
245     return YES;
246 }
247
248 - (BOOL)rewind
249 {
250     [[ITAppleEventCenter sharedCenter] sendAEWithEventClass:@"hook" eventID:@"Rwnd"
251                                                                                    appPSN:[self iTunesPSN]];
252     return YES;
253 }
254
255
256 - (BOOL)switchToPlaylistAtIndex:(int)index
257 {
258     [self runScriptAndReturnResult:[NSString stringWithFormat:
259         @"play playlist %i", index]];
260     return NO;
261 }
262
263 - (BOOL)switchToSongAtIndex:(int)index
264 {
265     [self runScriptAndReturnResult:[NSString stringWithFormat:
266         @"play track %i of current playlist", index]];
267     return NO;
268 }
269
270 - (BOOL)switchToEQAtIndex:(int)index
271 {
272     [self runScriptAndReturnResult:[NSString stringWithFormat:
273         @"set current EQ preset to EQ preset %i", index]];
274     [self runScriptAndReturnResult:@"set EQ enabled to 1"];
275     return NO;
276 }
277
278 - (ProcessSerialNumber)iTunesPSN
279 {
280     NSArray *apps = [[NSWorkspace sharedWorkspace] launchedApplications];
281     ProcessSerialNumber number;
282     int i, count = [apps count];
283
284     number.highLongOfPSN = kNoProcess;
285
286     for (i = 0; i < count; i++)
287     {
288         NSDictionary *curApp = [apps objectAtIndex:i];
289
290         if ([[curApp objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"])
291         {
292             number.highLongOfPSN = [[curApp objectForKey:@"NSApplicationProcessSerialNumberHigh"] intValue];
293             number.lowLongOfPSN = [[curApp objectForKey:@"NSApplicationProcessSerialNumberLow"] intValue];
294         }
295     }
296     return number;
297 }
298
299 - (void)applicationLaunched:(NSNotification *)note
300 {
301     NSDictionary *info = [note userInfo];
302
303     if ([[info objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"]) {
304         iTunesPSN.highLongOfPSN = [[info objectForKey:@"NSApplicationProcessSerialNumberHigh"] longValue];
305         iTunesPSN.lowLongOfPSN = [[info objectForKey:@"NSApplicationProcessSerialNumberLow"] longValue];
306
307         [[NSNotificationCenter defaultCenter] postNotificationName:@"ITMTRemoteAppDidLaunchNotification" object:nil];
308     }
309 }
310
311 - (void)applicationTerminated:(NSNotification *)note
312 {
313     NSDictionary *info = [note userInfo];
314
315     if ([[info objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"]) {
316         iTunesPSN.highLongOfPSN = kNoProcess;
317         [[NSNotificationCenter defaultCenter] postNotificationName:@"ITMTRemoteAppDidTerminateNotification" object:nil];
318     }
319 }
320
321 //This is just temporary
322 - (NSString *)runScriptAndReturnResult:(NSString *)script
323 {
324     AEDesc scriptDesc, resultDesc;
325     Size length;
326     NSString *result;
327     Ptr buffer;
328
329     script = [NSString stringWithFormat:@"tell application \"iTunes\"\n%@\nend tell", script];
330
331     //NSLog(@"I say \"%@\"",script);
332
333     AECreateDesc(typeChar, [script cString], [script cStringLength],
334                           &scriptDesc);
335
336     OSADoScript(asComponent, &scriptDesc, kOSANullScript, typeChar, kOSAModeCanInteract, &resultDesc);
337
338     length = AEGetDescDataSize(&resultDesc);
339     buffer = malloc(length);
340
341     AEGetDescData(&resultDesc, buffer, length);
342     AEDisposeDesc(&scriptDesc);
343     AEDisposeDesc(&resultDesc);
344     result = [NSString stringWithCString:buffer length:length];
345     if ( (! [result isEqualToString:@""])      &&
346          ([result characterAtIndex:0] == '\"') &&
347          ([result characterAtIndex:[result length] - 1] == '\"') ) {
348         result = [result substringWithRange:NSMakeRange(1, [result length] - 2)];
349     }
350     free(buffer);
351     buffer = nil;
352     //NSLog(@"You say \"%@\"",result);
353     return result;
354 }
355
356 @end