source: trunk/StreamVision/StreamVision.py@ 243

Last change on this file since 243 was 235, checked in by Nicholas Riley, 18 years ago

StreamVision.py: Pass through cmd-ctrl-\ in Emacs and X11 (C-M-\ is used in Emacs, and it doesn't handle being zoomed through the accessibility interface properly anyway); handle exceptions better when app doesn't respond to zooming Apple Events

File size: 7.2 KB
Line 
1#!/usr/bin/pythonw
2# -*- coding: utf-8 -*-
3
4from appscript import app, k, its, CommandError
5from AppKit import NSApplication, NSBeep, NSSystemDefined, NSURL, NSWorkspace
6from Foundation import NSDistributedNotificationCenter
7from PyObjCTools import AppHelper
8from Carbon.CarbonEvt import RegisterEventHotKey, GetApplicationEventTarget
9from Carbon.Events import cmdKey, shiftKey, controlKey
10import struct
11import scrape
12import HotKey
13
14GROWL_APP_NAME = 'StreamVision'
15NOTIFICATION_TRACK_INFO = 'iTunes Track Info'
16NOTIFICATIONS_ALL = [NOTIFICATION_TRACK_INFO]
17
18kEventHotKeyPressedSubtype = 6
19kEventHotKeyReleasedSubtype = 9
20
21growl = app('GrowlHelperApp')
22
23growl.register(
24 as_application=GROWL_APP_NAME,
25 all_notifications=NOTIFICATIONS_ALL,
26 default_notifications=NOTIFICATIONS_ALL,
27 icon_of_application='iTunes.app')
28 # if we leave off the .app, we can get Classic iTunes's icon
29
30def growlNotify(title, description, **kw):
31 growl.notify(
32 with_name=NOTIFICATION_TRACK_INFO,
33 title=title,
34 description=description,
35 application_name=GROWL_APP_NAME,
36 **kw)
37
38def radioParadiseURL():
39 session = scrape.Session()
40 session.go('http://www2.radioparadise.com/nowplay_b.php')
41 return session.region.firsttag('a')['href']
42
43def cleanStreamTitle(title):
44 if title == k.MissingValue:
45 return ''
46 title = title.split(' [')[0] # XXX move to description
47 title = title.replace('`', u'’')
48 return title
49
50def cleanStreamTrackName(name):
51 name = name.split('. ')[0]
52 name = name.split(': ')[0]
53 name = name.split(' - ')
54 if len(name) > 1:
55 name = ' - '.join(name[:-1])
56 else:
57 name = name[0]
58 return name
59
60def iTunesApp(): return app(id='com.apple.iTunes')
61def XTensionApp(): return app(creator='SHEx')
62
63HAVE_XTENSION = False
64try:
65 XTensionApp()
66 HAVE_XTENSION = True
67except:
68 pass
69
70class StreamVision(NSApplication):
71
72 hotKeyActions = {}
73 hotKeys = []
74
75 def displayTrackInfo(self):
76 iTunes = iTunesApp()
77
78 trackClass = iTunes.current_track.class_.get()
79 trackName = ''
80 if trackClass != k.Property:
81 trackName = iTunes.current_track.name.get()
82
83 if iTunes.player_state.get() != k.playing:
84 growlNotify('iTunes is not playing.', trackName)
85 return
86 if trackClass == k.URL_track:
87 growlNotify(cleanStreamTitle(iTunes.current_stream_title.get()),
88 cleanStreamTrackName(trackName))
89 return
90 if trackClass == k.Property:
91 growlNotify('iTunes is playing.', '')
92 return
93 kw = {}
94 # XXX iTunes doesn't let you get artwork for shared tracks
95 if trackClass != k.shared_track:
96 artwork = iTunes.current_track.artworks.get()
97 if artwork:
98 kw['pictImage'] = artwork[0].data.get()
99 growlNotify(trackName + ' ' +
100 '★' * (iTunes.current_track.rating.get() / 20),
101 iTunes.current_track.album.get() + "\n" +
102 iTunes.current_track.artist.get(),
103 **kw)
104
105 def goToSite(self):
106 iTunes = iTunesApp()
107 if iTunes.player_state.get() == k.playing:
108 url = iTunes.current_stream_URL.get()
109 if url:
110 if 'radioparadise.com' in url:
111 url = radioParadiseURL()
112 NSWorkspace.sharedWorkspace().openURL_(NSURL.URLWithString_(url))
113 return
114 NSBeep()
115
116 def registerHotKey(self, func, keyCode, mods=0):
117 hotKeyRef = RegisterEventHotKey(keyCode, mods, (0, 0),
118 GetApplicationEventTarget(), 0)
119 self.hotKeys.append(hotKeyRef)
120 self.hotKeyActions[HotKey.HotKeyAddress(hotKeyRef)] = func
121 return hotKeyRef
122
123 def unregisterHotKey(self, hotKeyRef):
124 self.hotKeys.remove(hotKeyRef)
125 del self.hotKeyActions[HotKey.HotKeyAddress(hotKeyRef)]
126 hotKeyRef.UnregisterEventHotKey()
127
128 def incrementRatingBy(self, increment):
129 iTunes = iTunesApp()
130 rating = iTunes.current_track.rating.get()
131 rating += increment
132 if rating < 0:
133 rating = 0
134 NSBeep()
135 elif rating > 100:
136 rating = 100
137 NSBeep()
138 iTunes.current_track.rating.set(rating)
139
140 def playPause(self):
141 iTunes = iTunesApp()
142 was_playing = (iTunes.player_state.get() == k.playing)
143 iTunes.playpause()
144 if not was_playing and iTunes.player_state.get() == k.stopped:
145 # most likely, we're focused on the iPod, so playing does nothing
146 iTunes.browser_windows[1].view.set(iTunes.user_playlists.filter(its.name=='Stations')[1].get())
147 iTunes.play()
148 if HAVE_XTENSION:
149 if iTunes.player_state.get() == k.playing:
150 XTensionApp().turnon('Stereo')
151 else:
152 XTensionApp().turnoff('Stereo')
153
154 def registerZoomWindowHotKey(self):
155 self.zoomWindowHotKey = self.registerHotKey(self.zoomWindow, 42, cmdKey | controlKey) # cmd-ctrl-\
156
157 def unregisterZoomWindowHotKey(self):
158 self.unregisterHotKey(self.zoomWindowHotKey)
159 self.zoomWindowHotKey = None
160
161 def zoomWindow(self):
162 systemEvents = app(id='com.apple.systemEvents')
163 frontName = systemEvents.processes.filter(its.frontmost)[1].name()
164 if frontName == 'iTunes':
165 systemEvents.processes['iTunes'].menu_bars[1]. \
166 menu_bar_items['Window'].menus.menu_items['Zoom'].click()
167 return
168 elif frontName in ('X11', 'Emacs'): # preserve C-M-\
169 self.unregisterZoomWindowHotKey()
170 systemEvents.key_code(42, using=[k.command_down, k.control_down])
171 self.registerZoomWindowHotKey()
172 return
173 try:
174 zoomed = app(frontName).windows[1].zoomed
175 zoomed.set(not zoomed())
176 except (CommandError, RuntimeError):
177 systemEvents.processes[frontName].windows. \
178 filter(its.subrole == 'AXStandardWindow').windows[1]. \
179 buttons.filter(its.subrole == 'AXZoomButton').buttons[1].click()
180
181 def finishLaunching(self):
182 super(StreamVision, self).finishLaunching()
183 self.registerHotKey(self.displayTrackInfo, 100) # F8
184 self.registerHotKey(self.goToSite, 100, cmdKey) # cmd-F8
185 self.registerHotKey(self.playPause, 101) # F9
186 self.registerHotKey(lambda: iTunesApp().previous_track(), 109) # F10
187 self.registerHotKey(lambda: iTunesApp().next_track(), 103) # F11
188 self.registerHotKey(lambda: self.incrementRatingBy(-20), 109, shiftKey) # shift-F10
189 self.registerHotKey(lambda: self.incrementRatingBy(20), 103, shiftKey) # shift-F11
190 self.registerZoomWindowHotKey()
191 NSDistributedNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, self.displayTrackInfo, 'com.apple.iTunes.playerInfo', None)
192
193 def sendEvent_(self, theEvent):
194 if theEvent.type() == NSSystemDefined and \
195 theEvent.subtype() == kEventHotKeyPressedSubtype:
196 self.hotKeyActions[theEvent.data1()]()
197 super(StreamVision, self).sendEvent_(theEvent)
198
199if __name__ == "__main__":
200 AppHelper.runEventLoop()
Note: See TracBrowser for help on using the repository browser.