Добавлен юнит для systemd
[vpproxy.git] / vlcclient / vlcclient.py
1 '''
2 Minimal VLC VLM client for AceProxy. Client class.
3 '''
4
5 import gevent
6 import gevent.event
7 import gevent.coros
8 import telnetlib
9 import logging
10 from vlcmessages import *
11 import time
12
13 class VlcException(Exception):
14
15     '''
16     Exception from VlcClient
17     '''
18     pass
19
20
21 class VlcClient(object):
22
23     '''
24     VLC Client class
25     '''
26
27     def __init__(
28         self, host='127.0.0.1', port=4212, password='admin', connect_timeout=10,
29             result_timeout=10, out_port=8081):
30         # Receive buffer
31         self._recvbuffer = None
32         # Output port
33         self._out_port = out_port
34         # VLC socket
35         self._socket = None
36         # Result timeout
37         self._resulttimeout = result_timeout
38         # Shutting down flag
39         self._shuttingDown = gevent.event.Event()
40         # Authentication done event
41         self._auth = gevent.event.AsyncResult()
42         # Request lock
43         self._resultlock = gevent.coros.RLock()
44         # Request result
45         self._result = gevent.event.AsyncResult()
46         # VLC version string
47         self._vlcver = None
48         # Saving password
49         self._password = password
50
51         # Logger
52         logger = logging.getLogger('VlcClient_init')
53
54         # Streams
55         self.streams=dict()
56
57         # Making connection
58         try:
59             self._socket = telnetlib.Telnet(host, port, connect_timeout)
60             logger.info("Successfully connected with VLC socket!")
61         except Exception as e:
62             raise VlcException(
63                 "Socket creation error! VLC is not running? ERROR: " + repr(e))
64
65         # Spawning recvData greenlet
66         gevent.spawn(self._recvData)
67         gevent.sleep()
68
69         # Waiting for authentication event
70         try:
71             if self._auth.get(timeout=self._resulttimeout) == False:
72                 errmsg = "Authentication error"
73                 logger.error(errmsg)
74                 raise VlcException(errmsg)
75         except gevent.Timeout:
76             errmsg = "Authentication timeout"
77             logger.error(errmsg)
78             raise VlcException(errmsg)
79
80     def __del__(self):
81         # Destructor just calls destroy() method
82         self.destroy()
83
84     def destroy(self):
85         # Logger
86         logger = logging.getLogger("VlcClient_destroy")
87
88         if self._shuttingDown.isSet():
89             # Already in the middle of destroying
90             return
91
92         # If socket is still alive (connected)
93         if self._socket:
94             try:
95                 logger.info("Destroying VlcClient...")
96                 self._write(VlcMessage.request.SHUTDOWN)
97                 # Set shuttingDown flag for recvData
98                 self._shuttingDown.set()
99             except:
100                 # Ignore exceptions on destroy
101                 pass
102
103     def _write(self, message):
104
105         logger = logging.getLogger("VlcClient_write")
106
107         # Return if in the middle of destroying
108         if self._shuttingDown.isSet():
109             return
110
111         try:
112             # Write message
113             logger.info('VLC command: ' + message)
114             self._socket.write(message + "\r\n")
115         except EOFError as e:
116             raise VlcException("Vlc Write error! ERROR: " + repr(e))
117
118     def _broadcast(self, brtype, stream_name, input=None, muxer='ts', pre_access='', qtype='default'):
119         if self._shuttingDown.isSet():
120             return
121
122         # Start/stop broadcast with VLC
123         # Logger
124         if brtype == True:
125             broadcast = 'startBroadcast'
126         else:
127             broadcast = 'stopBroadcast'
128
129         logger = logging.getLogger("VlcClient_" + broadcast)
130         # Clear AsyncResult
131         self._result = gevent.event.AsyncResult()
132         # Get lock
133         self._resultlock.acquire()
134         # Write message to VLC socket
135         if brtype == True:
136             msg = VlcMessage.request.startBroadcast(stream_name, input, self._out_port, muxer, pre_access, qtype)
137             self._write(msg)
138         else:
139             if stream_name not in self.streams:
140                 self._resultlock.release()
141                 logger.error("Attempting to delete not existing stream %s" % stream_name)
142                 return
143             self._write(VlcMessage.request.stopBroadcast(stream_name))
144
145         try:
146             gevent.sleep()
147             result = self._result.get(timeout=self._resulttimeout)
148             if result == False:
149                 logger.error(broadcast + " error")
150                 raise VlcException(broadcast + " error")
151         except gevent.Timeout:
152             logger.error(broadcast + " result timeout")
153             raise VlcException(broadcast + " result timeout")
154         finally:
155             logger.info("working with %s stream: %s" % (stream_name,broadcast))
156             if brtype == True:
157                 self.streams[stream_name]=time.time()
158             else:
159                 del self.streams[stream_name]
160             self._resultlock.release()
161             logger.info("worked with %s stream: %s" % (stream_name,broadcast))
162
163         if brtype == True:
164             logger.info("Broadcast started")
165         else:
166             logger.info("Broadcast stopped")
167
168     def startBroadcast(self, stream_name, input, muxer='ts', pre_access='', qtype='default'):
169         logger = logging.getLogger("VlcClient_startBroadcast")
170         logger.debug("Starting broadcast......")
171         return self._broadcast(True, stream_name, input, muxer, pre_access, qtype)
172
173     def stopBroadcast(self, stream_name):
174         return self._broadcast(False, stream_name)
175
176     def mark(self,stream_name):
177       self.streams[stream_name]=time.time()
178
179     def clean_streams(self,timeout=15):
180       self._resultlock.acquire()
181       to_stop=set()
182       for stream,lasttime in self.streams.iteritems():
183         if time.time()-lasttime>timeout:
184           to_stop.add(stream)
185       for stream in to_stop:    
186         try:
187           self.stopBroadcast(stream)
188         except:
189           pass  
190       self._resultlock.release()
191
192     def check_stream(self,stream_name):
193       if stream_name in self.streams:
194         self.streams[stream_name]=time.time()
195         return True
196       else:
197         return False  
198
199     def pauseBroadcast(self, stream_name):
200         return self._write(VlcMessage.request.pauseBroadcast(stream_name))
201
202     def playBroadcast(self, stream_name):
203         return self._write(VlcMessage.request.playBroadcast(stream_name))
204
205     def _recvData(self):
206         # Logger
207         logger = logging.getLogger("VlcClient_recvData")
208
209         while True:
210             gevent.sleep()
211             try:
212                 self._recvbuffer = self._socket.read_until("\n")
213                 # Stripping "> " from VLC
214                 self._recvbuffer = self._recvbuffer.lstrip("> ")
215             except:
216                 # If something happened during read, abandon reader
217                 if not self._shuttingDown.isSet():
218                     logger.error("Exception at socket read")
219                     self._shuttingDown.set()
220                 return
221
222             # Parsing everything only if the string is not empty
223             if self._recvbuffer:
224                 if not self._vlcver:
225                     # First line (VLC version)
226                     self._vlcver = self._recvbuffer.strip()
227                     # Send password here since PASSWORD doesn't have \n
228                     self._write(self._password)
229
230                 elif self._recvbuffer.startswith(VlcMessage.response.SHUTDOWN):
231                     # Exit from this loop
232                     logger.debug("Got SHUTDOWN from VLC")
233                     return
234
235                 elif self._recvbuffer.startswith(VlcMessage.response.WRONGPASS):
236                     # Wrong password
237                     logger.error("Wrong VLC password!")
238                     self._auth.set(False)
239                     return
240
241                 elif self._recvbuffer.startswith(VlcMessage.response.AUTHOK):
242                     # Authentication OK
243                     logger.info("Authentication successful")
244                     self._auth.set(True)
245
246                 elif VlcMessage.response.BROADCASTEXISTS in self._recvbuffer:
247                     # Broadcast already exists
248                     logger.error("Broadcast already exists!")
249                     self._result.set(False)
250
251                 elif VlcMessage.response.STOPERR in self._recvbuffer:
252                     # Media unknown (stopping non-existent stream)
253                     logger.error("Broadcast does not exist!")
254                     self._result.set(False)
255
256                 # Do not move this before error handlers!
257                 elif self._recvbuffer.startswith(VlcMessage.response.STARTOK):
258                     # Broadcast started
259                     logger.info("Broadcast started")
260                     self._result.set(True)
261
262                 elif self._recvbuffer.startswith(VlcMessage.response.STOPOK):
263                     # Broadcast stopped
264                     logger.info("Broadcast stopped")
265                     self._result.set(True)