source: trunk/StreamVision/StreamVision.py@ 301

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

StreamVision.py: Play/pause VLC or RealPlayer if in front with Logitech headphones.

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