source: trunk/StreamVision/StreamVision.py@ 304

Last change on this file since 304 was 302, checked in by Nicholas Riley, 17 years ago

setup.py: Use setuptools.

StreamVision.py: HIDRemote helpers aren't compiled for Intel yet; just
skip them for now.

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