147f547fcfd15bbe28c02f68edc824a722b42649
[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 urllib2 import urlopen
19 from xml.dom.minidom import parse
20 from VlcPlayer import VlcPlayer, 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 getVideoNorm(self):
116                 return self.cfg.videonorm.value
117
118         def videoNorm(self):
119                 return self.cfg.videonorm
120
121         def getOverscanCorrection(self):
122                 return self.cfg.overscancorrection.value
123
124         def overscanCorrection(self):
125                 return self.cfg.overscancorrection
126
127         def getSOverlay(self):
128                 return self.cfg.soverlay.value
129
130         def sOverlay(self):
131                 return self.cfg.soverlay
132
133         def getTranscodeVideo(self):
134                 return self.cfg.transcodeVideo.value
135
136         def transcodeVideo(self):
137                 return self.cfg.transcodeVideo
138
139         def getTranscodeAudio(self):
140                 return self.cfg.transcodeAudio.value
141
142         def transcodeAudio(self):
143                 return self.cfg.transcodeAudio
144
145         def dvdPath(self):
146                 return self.cfg.dvdPath
147
148         def getDvdPath(self):
149                 return self.cfg.dvdPath.value
150
151         def __xmlRequest(self, request, params):
152                 uri = "/requests/" + request + ".xml"
153                 if params is not None: uri = uri + "?" + urlencode(params).replace('+', '%20')
154                 location = "%s:%d" % (self.getHost(), self.getHttpPort())
155                 resp = urlopen("http://" + location + uri)
156                 if resp is None:
157                         raise IOError, "No response from Server"
158                 xml = parse(resp)
159                 resp.close()
160                 return xml
161
162         def getFilesAndDirs(self, directory, regex):
163                 files = []
164                 directories = []
165                 response = self.__xmlRequest("browse", {"dir": directory})
166                 for element in response.getElementsByTagName("element"):
167                         if element.hasAttribute("type"):
168                                 name = element.getAttribute("name").encode("utf8")
169                                 path = normpath(element.getAttribute("path").encode("utf8"))
170                                 if path is not None:
171                                         elementType = element.getAttribute("type")
172                                         if elementType == "directory":
173                                                 directories.append([name, path])
174                                         elif elementType == "file":
175                                                 if regex is None or regex.search(path):
176                                                         files.append([name, path])
177                 return (files, directories)
178
179         def getPlaylistEntries(self):
180                 xml = self.__xmlRequest("playlist", None)
181                 files = []
182                 for e in xml.getElementsByTagName("leaf"):
183                         if e.hasAttribute("uri") is not None:
184                                 name = e.getAttribute("name").encode("utf8")
185                                 if len(name) >= 50:
186                                         name = "..." + name[-50:]
187                                 path = e.getAttribute("uri").encode("utf8")
188                                 files.append([name, path])
189                 return files
190
191         def getCurrentElement(self):
192                 xml = self.__xmlRequest("playlist", None)
193                 for e in xml.getElementsByTagName("leaf"):
194                         if e.hasAttribute("current"):
195                                 return e
196                 return None
197
198         def play(self, session, media, name, currentList = None, player = None):
199                 if player is None:
200 # or not isinstance(player, VlcPlayer):
201                         player = VlcPlayer
202                 dlg = session.open(player, self, currentList)
203                 dlg.playfile(media, name)
204                 return dlg
205         
206         def playFile(self, filename, videoPid, audioPid):
207                 streamName = "dream" + str(randint(0, maxint))
208                 transcode = []
209
210                 doDirect = isDvdUrl(filename) or re.match("(?i).*\.(mpg|mpeg|ts)$", filename.lower())
211
212                 if not doDirect or self.getTranscodeVideo():
213                         videoNormList = self.getVideoNorm().split(",")
214                         # ,height=%s
215                         # Video settings\r
216                         transcode.append("vcodec=%s,vb=%d,venc=ffmpeg{strict-rc=1},fps=%s" % (\r
217                                 self.getVideoCodec(),self.getVideoBitrate(),
218                                  videoNormList[3]\r
219                         ))\r
220                         # Old canvas settings\r
221                         #transcode.append("width=%s,height=%s,canvas-width=%s,canvas-height=%s,canvas-aspect=%s" % (             \r
222                         #       str(int(float(videoNormList[0]) - float(videoNormList[0]) * float(self.getOverscanCorrection()) / 100)),\r
223                         #       str(int(float(videoNormList[1]) - float(videoNormList[1]) * float(self.getOverscanCorrection()) / 100)),\r
224                         #       videoNormList[0], videoNormList[1], videoNormList[2],\r
225                         #))\r
226                         \r
227                         #New canvas - since VLC 0.9\r
228                         transcode.append("vfilter=canvas{width=%s,height=%s,aspect=%s}" % (              \r
229                                 str(int(float(videoNormList[0]) - float(videoNormList[0]) * float(self.getOverscanCorrection()) / 100)),
230                                 str(int(float(videoNormList[1]) - float(videoNormList[1]) * float(self.getOverscanCorrection()) / 100)),
231                                 videoNormList[2]\r
232                         ))
233                         if self.getSOverlay():
234                                 transcode.append("soverlay")
235                 if not doDirect or self.getTranscodeAudio():
236                         transcode.append("acodec=%s,ab=%d,channels=%d,samplerate=%s" % (
237                                 self.getAudioCodec(),
238                                 self.getAudioBitrate(),
239                                 self.getAudioChannels(),
240                                 self.getSamplerate()
241                         ))
242                 if re.match("[a-zA-Z]:", filename):
243                         # Fix for subtitles with VLC on Windows.
244                         filename = filename.replace("/", "\\")
245
246                 filename = filename.replace("\\", "\\\\").replace("'", "\\'")
247 #               input = filename + " :dvdread-caching=3000 :sub-track=1 :audio-track=1 :sout=#"
248                 input = filename + " :sout=#"
249
250                 if len(transcode) > 0:
251                         input += "transcode{%s}:" % (",".join(transcode))
252
253                 mux="ts{pid-video=%d,pid-audio=%d}" % (videoPid, audioPid)
254                 input += "std{access=http,mux=%s,dst=/%s.ts} :sout-all :sout-keep" % (mux, streamName)
255
256                 print "[VLC] playfile", input
257
258                 xml = self.__xmlRequest("status", {"command": "in_play", "input": input})
259                 error = xml.getElementsByTagName("error")
260                 if error is not None and len(error) > 0:
261                         self.lastError = getText(error[0].childNodes).strip()
262                         if len(self.lastError) == 0:
263                                 self.lastError = None
264                         else:
265                                 print "[VLC] VlcControl error:", self.lastError
266                         return None
267                 else:
268                         self.lastError = None
269                 return "http://%s:%d/%s.ts" % (self.getHost(), self.getHttpPort(), streamName)
270
271         def unpause(self):
272                 self.__xmlRequest("status", {"command": "pl_pause"})
273
274         def stop(self):
275                 self.__xmlRequest("status", {"command": "pl_stop"})
276
277         def pause(self):
278                 self.__xmlRequest("status", {"command": "pl_pause"})
279
280         def delete(self, id):
281                 self.__xmlRequest("status", {"command": "pl_delete", "id": str(id)})
282
283         def deleteCurrentTree(self):
284                 print "[VLC] delete current tree"
285                 currentElement = self.getCurrentElement()
286                 while currentElement is not None and currentElement.parentNode.getAttribute("ro") != "ro":
287                         currentElement = currentElement.parentNode
288                 id = int(currentElement.getAttribute("id"))
289                 self.delete(id)
290                 
291         def seek(self, value):
292                 """  Allowed values are of the form:
293   [+ or -][<int><H or h>:][<int><M or m or '>:][<int><nothing or S or s or ">]
294   or [+ or -]<int>%
295   (value between [ ] are optional, value between < > are mandatory)
296 examples:
297   1000 -> seek to the 1000th second
298   +1H:2M -> seek 1 hour and 2 minutes forward
299   -10% -> seek 10% back"""
300                 self.__xmlRequest("status", {"command": "seek", "val": str(value)})
301
302         def status(self):
303                 xml = self.__xmlRequest("status", None)
304                 stats = {}
305                 for e in xml.documentElement.childNodes:
306                         if e.nodeType == e.ELEMENT_NODE:
307                                 if e.firstChild is None:
308                                         stats[e.nodeName.encode("latin_1", "replace")] = None
309                                 else:
310                                         stats[e.nodeName.encode("latin_1", "replace")] = e.firstChild.nodeValue.encode("latin_1", "replace")
311                 return stats
312
313         def loadPlaylist(self, playlist):
314                 self.__xmlRequest("status", {"command": "in_play", "input": playlist})
315                 self.__xmlRequest("status", {"command": "pl_stop"})
316                 xml = self.__xmlRequest("playlist", None)
317                 id = None
318                 for n in xml.getElementsByTagName("node"):
319                         if n.hasAttribute("name") is not None:
320                                 if n.getAttribute("name").encode("utf8", "replace") == playlist:
321                                         if id is None:
322                                                 id = n.getAttribute("id")
323                                         elif int(id) < int(n.getAttribute("id")):
324                                                 id = n.getAttribute("id")
325                 return id