The way the address of a vlc-server is specified has changed. Now it is possible...
[vuplus_dvbapp-plugin] / vlcplayer / src / VlcServer.py
1 # -*- coding: ISO-8859-1 -*-
2 #===============================================================================
3 # VLC Player Plugin by A. Lätsch 2007
4 #                   modified by Volker Christian 2008
5 #
6 # This is free software; you can redistribute it and/or modify it under
7 # the terms of the GNU General Public License as published by the Free
8 # Software Foundation; either version 2, or (at your option) any later
9 # version.
10 #===============================================================================
11
12
13 import re
14 import posixpath
15 from sys import maxint
16 from random import randint, seed
17 from urllib import urlencode
18 from urllib import urlopen
19 from xml.dom.minidom import parse
20 from VlcPlayer import isDvdUrl
21
22 seed()
23
24 def normpath(path):
25         if path is None:
26                 return None
27         path = path.replace("\\","/").replace("//", "/")
28         if path == "/..":
29                 return None
30         if len(path) > 0 and path[0] != '/':
31                 path = posixpath.normpath('/' + path)[1:]
32         else:
33                 path = posixpath.normpath(path)
34
35         if len(path) == 0 or path == "//":
36                 return "/"
37         elif path == ".":
38                 return None
39         return path
40         
41
42 class VlcServer:
43         def __init__(self, cfg):
44                 self.cfg = cfg
45
46         def getCfg(self):
47                 return self.cfg
48
49         def getName(self):
50                 return self.cfg.name.value
51
52         def name(self):
53                 return self.cfg.name
54
55         def getAddressType(self):
56                 return self.cfg.addressType.value
57
58         def addressType(self):
59                 return self.cfg.addressType
60
61         def getHost(self):
62                 return self.cfg.hostip.tostring(self.cfg.hostip.value)
63
64         def host(self):
65                 return self.cfg.hostip
66
67         def getHttpPort(self):
68                 return self.cfg.httpport.value
69
70         def httpPort(self):
71                 return self.cfg.httpport
72
73         def getBasedir(self):
74                 return self.cfg.basedir.value
75
76         def basedir(self):
77                 return self.cfg.basedir
78
79         def getVideoCodec(self):
80                 return self.cfg.videocodec.value
81
82         def videoCodec(self):
83                 return self.cfg.videocodec
84
85         def getVideoBitrate(self):
86                 return self.cfg.videobitrate.value
87
88         def videoBitrate(self):
89                 return self.cfg.videobitrate
90
91         def getAudioCodec(self):
92                 return self.cfg.audiocodec.value
93
94         def audioCodec(self):
95                 return self.cfg.audiocodec
96
97         def getAudioBitrate(self):
98                 return self.cfg.audiobitrate.value
99
100         def audioBitrate(self):
101                 return self.cfg.audiobitrate
102
103         def getSamplerate(self):
104                 return self.cfg.samplerate.value
105
106         def samplerate(self):
107                 return self.cfg.samplerate
108
109         def getAudioChannels(self):
110                 return self.cfg.audiochannels.value
111
112         def audioChannels(self):
113                 return self.cfg.audiochannels
114
115         def getVideoWidth(self):
116                 return self.cfg.videowidth.value
117
118         def videoWidth(self):
119                 return self.cfg.videowidth
120
121         def getVideoHeight(self):
122                 return self.cfg.videoheight.value
123
124         def videoHeight(self):
125                 return self.cfg.videoheight
126
127         def getFramesPerSecond(self):
128                 return self.cfg.framespersecond.value
129
130         def framesPerSecond(self):
131                 return self.cfg.framespersecond
132
133         def getAspectRatio(self):
134                 return self.cfg.aspectratio.value
135
136         def aspectRatio(self):
137                 return self.cfg.aspectratio
138
139         def getSOverlay(self):
140                 return self.cfg.soverlay.value
141
142         def sOverlay(self):
143                 return self.cfg.soverlay
144
145         def getTranscodeVideo(self):
146                 return self.cfg.transcodeVideo.value
147
148         def transcodeVideo(self):
149                 return self.cfg.transcodeVideo
150
151         def getTranscodeAudio(self):
152                 return self.cfg.transcodeAudio.value
153
154         def transcodeAudio(self):
155                 return self.cfg.transcodeAudio
156
157         def dvdPath(self):
158                 return self.cfg.dvdPath
159
160         def getDvdPath(self):
161                 return self.cfg.dvdPath.value
162
163         def __xmlRequest(self, request, params):
164                 uri = "/requests/" + request + ".xml"
165                 if params is not None: uri = uri + "?" + urlencode(params)
166                 location = "%s:%d" % (self.getHost(), self.getHttpPort())
167                 resp = urlopen("http://" + location + uri)
168                 if resp is None:
169                         raise IOError, "No response from Server"
170                 return parse(resp)
171
172         def getFilesAndDirs(self, directory, regex):
173                 files = []
174                 directories = []
175                 response = self.__xmlRequest("browse", {"dir": directory})
176                 for element in response.getElementsByTagName("element"):
177                         if element.hasAttribute("type"):
178                                 name = element.getAttribute("name").encode("utf8")
179                                 path = normpath(element.getAttribute("path").encode("utf8"))
180                                 if path is not None:
181                                         elementType = element.getAttribute("type")
182                                         if elementType == "directory":
183                                                 directories.append([name, path])
184                                         elif elementType == "file":
185                                                 if regex is None or regex.search(path):
186                                                         files.append([name, path])
187                 return (files, directories)
188
189         def getPlaylistEntries(self):
190                 xml = self.__xmlRequest("playlist", None)
191                 files = []
192                 for e in xml.getElementsByTagName("leaf"):
193                         if e.hasAttribute("uri") is not None:
194                                 name = e.getAttribute("name").encode("utf8")
195                                 if len(name) >= 50:
196                                         name = "..." + name[-50:]
197                                 path = e.getAttribute("uri").encode("utf8")
198                                 files.append([name, path])
199                 return files
200
201         def getCurrentElement(self):
202                 xml = self.__xmlRequest("playlist", None)
203                 for e in xml.getElementsByTagName("leaf"):
204                         if e.hasAttribute("current"):
205                                 return e
206                 return None
207
208         def playFile(self, filename, videoPid, audioPid):
209                 streamName = "dream" + str(randint(0, maxint))
210                 transcode = []
211
212                 doDirect = isDvdUrl(filename) or re.match("(?i).*\.(mpg|mpeg|ts)$", filename)
213
214                 if not doDirect or self.getTranscodeVideo():
215                         transcode.append("vcodec=%s,vb=%d,venc=ffmpeg{strict-rc=1},width=%s,height=%s,fps=%s,scale=1" % (
216                                 self.getVideoCodec(),
217                                 self.getVideoBitrate(),
218                                 self.getVideoWidth(),
219                                 self.getVideoHeight(),
220                                 self.getFramesPerSecond()
221                         ))
222                         if self.getAspectRatio() != "none":
223                                 transcode.append("canvas-width=%s,canvas-height=%s,canvas-aspect=%s" % (
224                                         self.getVideoWidth(),
225                                         self.getVideoHeight(),
226                                         self.getAspectRatio()
227                                 ))
228
229                 if not doDirect or self.getTranscodeAudio():
230                         transcode.append("acodec=%s,ab=%d,channels=%d,samplerate=%s" % (
231                                 self.getAudioCodec(),
232                                 self.getAudioBitrate(),
233                                 self.getAudioChannels(),
234                                 self.getSamplerate()
235                         ))
236
237                 if self.getSOverlay():
238                         transcode.append("soverlay")
239
240                 if re.match("[a-zA-Z]:", filename):
241                         # Fix for subtitles with VLC on Windows.
242                         filename = filename.replace("/", "\\")
243
244                 filename = filename.replace("\\", "\\\\").replace("'", "\\'")
245 #               input = filename + " :dvdread-caching=3000 :sub-track=1 :audio-track=1 :sout=#"
246                 input = filename + " :sout=#"
247
248                 if len(transcode) > 0:
249                         input += "transcode{%s}:" % (",".join(transcode))
250
251                 mux="ts{pid-video=%d,pid-audio=%d}" % (videoPid, audioPid)
252                 input += "std{access=http,mux=%s,dst=/%s.ts} :sout-all :sout-keep" % (mux, streamName)
253
254                 print "[VLC] playfile", input
255
256                 xml = self.__xmlRequest("status", {"command": "in_play", "input": input})
257                 error = xml.getElementsByTagName("error")
258                 if error is not None and len(error) > 0:
259                         self.lastError = getText(error[0].childNodes).strip()
260                         if len(self.lastError) == 0:
261                                 self.lastError = None
262                         else:
263                                 print "[VLC] VlcControl error:", self.lastError
264                         return None
265                 else:
266                         self.lastError = None
267                 return "http://%s:%d/%s.ts" % (self.getHost(), self.getHttpPort(), streamName)
268
269         def play(self):
270                 self.__xmlRequest("status", {"command": "pl_pause"})
271
272         def stop(self):
273                 self.__xmlRequest("status", {"command": "pl_stop"})
274
275         def pause(self):
276                 self.__xmlRequest("status", {"command": "pl_pause"})
277
278         def delete(self, id):
279                 self.__xmlRequest("status", {"command": "pl_delete", "id": str(id)})
280
281         def deleteCurrentTree(self):
282                 print "[VLC] delete current tree"
283                 currentElement = self.getCurrentElement()
284                 while currentElement is not None and currentElement.parentNode.getAttribute("ro") != "ro":
285                         currentElement = currentElement.parentNode
286                 id = int(currentElement.getAttribute("id"))
287                 self.delete(id)
288                 
289         def seek(self, value):
290                 """  Allowed values are of the form:
291   [+ or -][<int><H or h>:][<int><M or m or '>:][<int><nothing or S or s or ">]
292   or [+ or -]<int>%
293   (value between [ ] are optional, value between < > are mandatory)
294 examples:
295   1000 -> seek to the 1000th second
296   +1H:2M -> seek 1 hour and 2 minutes forward
297   -10% -> seek 10% back"""
298                 self.__xmlRequest("status", {"command": "seek", "val": str(value)})
299
300         def status(self):
301                 xml = self.__xmlRequest("status", None)
302                 stats = {}
303                 for e in xml.documentElement.childNodes:
304                         if e.nodeType == e.ELEMENT_NODE:
305                                 if e.firstChild is None:
306                                         stats[e.nodeName.encode("latin_1", "replace")] = None
307                                 else:
308                                         stats[e.nodeName.encode("latin_1", "replace")] = e.firstChild.nodeValue.encode("latin_1", "replace")
309                 return stats
310
311         def loadPlaylist(self, playlist):
312                 self.__xmlRequest("status", {"command": "in_play", "input": playlist})
313                 self.__xmlRequest("status", {"command": "pl_stop"})
314                 xml = self.__xmlRequest("playlist", None)
315                 id = None
316                 for n in xml.getElementsByTagName("node"):
317                         if n.hasAttribute("name") is not None:
318                                 if n.getAttribute("name").encode("latin_1", "replace") == playlist:
319                                         if id is None:
320                                                 id = n.getAttribute("id")
321                                         elif int(id) < int(n.getAttribute("id")):
322                                                 id = n.getAttribute("id")
323                 return id