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