1
import sys, os, fcntl, gobject, time, re
6
# Provides simple piped I/O to an mplayer process.
10
pymp, mplayerIn, mplayerOut = None, None, None
11
inputHandler, eofHandler, statusQuery = 0, 0, 0
14
streamTitleChangeListeners = []
17
# Initializes this Mplayer with the specified Pymp.
19
def __init__(self, pymp):
23
# Plays the specified target.
25
def play(self, target):
27
mpc = "mplayer -slave -quiet " + target + " 2>/dev/null"
29
self.mplayerIn, self.mplayerOut = os.popen2(mpc) #open pipe
30
fcntl.fcntl(self.mplayerOut, fcntl.F_SETFL, os.O_NONBLOCK)
32
self.startInputHandler()
33
self.startEofHandler()
34
self.startStatusQuery()
37
# Issues command to mplayer.
40
def record(self, target, savefile):
42
mpc = "mplayer -slave -quiet " + target + " -ao pcm:file=" + savefile
44
self.mplayer_record_In, self.mplayer_record_Out = os.popen2(mpc) #open pipe
45
fcntl.fcntl(self.mplayer_record_Out, fcntl.F_SETFL, os.O_NONBLOCK)
49
# Issues command to mplayer.
52
def cmd(self, command):
54
if not self.mplayerIn:
58
self.mplayerIn.write(command + "\n")
59
self.mplayerIn.flush() #flush pipe
64
# Toggles pausing of the current mplayer job and status query.
67
if not self.mplayerIn:
70
if self.paused: #unpause
71
self.startStatusQuery()
75
self.stopStatusQuery()
81
# Seeks the amount using the specified mode. See mplayer docs.
83
def seek(self, amount, mode=0):
87
# Cleanly closes any IPC resources to mplayer.
90
self.stopStatusQuery() #cancel query
91
self.stopEofHandler() #cancel eof monitor
92
self.stopInputHandler() #cancel input monitor
94
if self.paused: #untoggle pause to cleanly quit
97
self.cmd("quit") #ask mplayer to quit
100
self.mplayerIn.close() #close pipes
101
self.mplayerOut.close()
102
except StandardError:
105
self.mplayerIn, self.mplayerOut = None, None
106
#self.pymp.control.setProgress(-1) #reset bar
b'\tdef close_record(self):'
110
self.mplayer_record_In.write("quit\n")
111
self.mplayer_record_In.flush() #flush pipe
112
except StandardError:
115
#self.cmd("quit") #ask mplayer to quit
119
self.mplayer_record_In.close() #close pipes_record_
120
self.mplayer_record_Out.close()
121
except StandardError:
124
self.mplayer_record_In, self.mplayer_record_Out = None, None
126
# Triggered when mplayer's stdout reaches EOF.
128
def handleEof(self, source, condition):
130
self.stopStatusQuery() #cancel query
132
self.mplayerIn, self.mplayerOut = None, None
134
# if self.pymp.playlist.continuous: #play next target
135
# self.pymp.playlist.next(None, None)
136
# else: #reset progress bar
137
# self.pymp.control.setProgress(-1)
142
# Triggered when mplayer's stdout reaches EOF.
144
def handleInput(self, source, condition):
146
for line in self.mplayerOut:
147
self.lookForStreamTitle(line)
152
# Triggered when mplayer prints something stdout.
154
def lookForStreamTitle(self, line):
155
matches = re.search("(?<=StreamTitle=\')(.*)(\';StreamUrl=)", line)
157
self.streamTitle = matches.group(1)
158
for listener in self.streamTitleChangeListeners:
159
keepListener = listener(self, self.streamTitle)
161
self.streamTitleChangeListeners.remove(listener)
164
# Queries mplayer's playback status and upates the progress bar.
166
def queryStatus(self):
168
self.cmd("get_percent_pos") #submit status query
169
self.cmd("get_time_pos")
171
time.sleep(0.05) #allow time for output
173
line, percent, seconds = None, -1, -1
176
try: #attempt to fetch last line of output
177
line = self.mplayerOut.readline()
178
except StandardError, detail:
183
if line.startswith("ANS_PERCENT_POSITION"):
184
percent = int(line.replace("ANS_PERCENT_POSITION=", ""))
186
if line.startswith("ANS_TIME_POSITION"):
187
seconds = float(line.replace("ANS_TIME_POSITION=", ""))
189
#self.pymp.control.setProgress(percent, seconds)
193
# Add a listener that will be called every time a change in stream title is detected.
194
# The signature of the callback should be:
195
# def callback(source, newStreamTitle)
197
def addStreamTitleChangeListener(self, callback):
198
self.streamTitleChangeListeners.append(callback)
201
# Removes a stream title change listener.
203
def removeStreamTitleChangeListener(self, callback):
204
self.streamTitleChangeListeners.remove(callback)
207
# Inserts the status query monitor.
209
def startStatusQuery(self):
210
self.statusQuery = gobject.timeout_add(STATUS_TIMEOUT, self.queryStatus)
213
# Removes the status query monitor.
215
def stopStatusQuery(self):
217
gobject.source_remove(self.statusQuery)
221
# Inserts the EOF monitor.
223
def startEofHandler(self):
224
self.eofHandler = gobject.io_add_watch(self.mplayerOut, gobject.IO_HUP, self.handleEof)
227
# Removes the EOF monitor.
229
def stopEofHandler(self):
231
gobject.source_remove(self.eofHandler)
235
# Inserts the input monitoy.
237
def startInputHandler(self):
238
self.inputHandler = gobject.io_add_watch(self.mplayerOut, gobject.IO_IN, self.handleInput)
241
# Removes the EOF monitor.
243
def stopInputHandler(self):
244
if self.inputHandler:
245
gobject.source_remove(self.inputHandler)
246
self.inputHandler = 0