- Some minor problems fixed
[enigma2-plugins.git] / 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 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 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 play(self, screen, media, name, currentList = None):
209                 dlg = screen.session.open(VlcPlayer, self, currentList)
210                 dlg.playfile(media, name)
211         
212         def playFile(self, filename, videoPid, audioPid):
213                 streamName = "dream" + str(randint(0, maxint))
214                 transcode = []
215
216                 doDirect = isDvdUrl(filename) or re.match("(?i).*\.(mpg|mpeg|ts)$", filename)
217
218                 if not doDirect or self.getTranscodeVideo():
219                         transcode.append("vcodec=%s,vb=%d,venc=ffmpeg{strict-rc=1},width=%s,height=%s,fps=%s,scale=1" % (
220                                 self.getVideoCodec(),
221                                 self.getVideoBitrate(),
222                                 self.getVideoWidth(),
223                                 self.getVideoHeight(),
224                                 self.getFramesPerSecond()
225                         ))
226                         if self.getAspectRatio() != "none":
227                                 transcode.append("canvas-width=%s,canvas-height=%s,canvas-aspect=%s" % (
228                                         self.getVideoWidth(),
229                                         self.getVideoHeight(),
230                                         self.getAspectRatio()
231                                 ))
232
233                 if not doDirect or self.getTranscodeAudio():
234                         transcode.append("acodec=%s,ab=%d,channels=%d,samplerate=%s" % (
235                                 self.getAudioCodec(),
236                                 self.getAudioBitrate(),
237                                 self.getAudioChannels(),
238                                 self.getSamplerate()
239                         ))
240
241                 if self.getSOverlay():
242                         transcode.append("soverlay")
243
244                 if re.match("[a-zA-Z]:", filename):
245                         # Fix for subtitles with VLC on Windows.
246                         filename = filename.replace("/", "\\")
247
248                 filename = filename.replace("\\", "\\\\").replace("'", "\\'")
249 #               input = filename + " :dvdread-caching=3000 :sub-track=1 :audio-track=1 :sout=#"
250                 input = filename + " :sout=#"
251
252                 if len(transcode) > 0:
253                         input += "transcode{%s}:" % (",".join(transcode))
254
255                 mux="ts{pid-video=%d,pid-audio=%d}" % (videoPid, audioPid)
256                 input += "std{access=http,mux=%s,dst=/%s.ts} :sout-all :sout-keep" % (mux, streamName)
257
258                 print "[VLC] playfile", input
259
260                 xml = self.__xmlRequest("status", {"command": "in_play", "input": input})
261                 error = xml.getElementsByTagName("error")
262                 if error is not None and len(error) > 0:
263                         self.lastError = getText(error[0].childNodes).strip()
264                         if len(self.lastError) == 0:
265                                 self.lastError = None
266                         else:
267                                 print "[VLC] VlcControl error:", self.lastError
268                         return None
269                 else:
270                         self.lastError = None
271                 return "http://%s:%d/%s.ts" % (self.getHost(), self.getHttpPort(), streamName)
272
273         def unpause(self):
274                 self.__xmlRequest("status", {"command": "pl_pause"})
275
276         def stop(self):
277                 self.__xmlRequest("status", {"command": "pl_stop"})
278
279         def pause(self):
280                 self.__xmlRequest("status", {"command": "pl_pause"})
281
282         def delete(self, id):
283                 self.__xmlRequest("status", {"command": "pl_delete", "id": str(id)})
284
285         def deleteCurrentTree(self):
286                 print "[VLC] delete current tree"
287                 currentElement = self.getCurrentElement()
288                 while currentElement is not None and currentElement.parentNode.getAttribute("ro") != "ro":
289                         currentElement = currentElement.parentNode
290                 id = int(currentElement.getAttribute("id"))
291                 self.delete(id)
292                 
293         def seek(self, value):
294                 """  Allowed values are of the form:
295   [+ or -][<int><H or h>:][<int><M or m or '>:][<int><nothing or S or s or ">]
296   or [+ or -]<int>%
297   (value between [ ] are optional, value between < > are mandatory)
298 examples:
299   1000 -> seek to the 1000th second
300   +1H:2M -> seek 1 hour and 2 minutes forward
301   -10% -> seek 10% back"""
302                 self.__xmlRequest("status", {"command": "seek", "val": str(value)})
303
304         def status(self):
305                 xml = self.__xmlRequest("status", None)
306                 stats = {}
307                 for e in xml.documentElement.childNodes:
308                         if e.nodeType == e.ELEMENT_NODE:
309                                 if e.firstChild is None:
310                                         stats[e.nodeName.encode("latin_1", "replace")] = None
311                                 else:
312                                         stats[e.nodeName.encode("latin_1", "replace")] = e.firstChild.nodeValue.encode("latin_1", "replace")
313                 return stats
314
315         def loadPlaylist(self, playlist):
316                 self.__xmlRequest("status", {"command": "in_play", "input": playlist})
317                 self.__xmlRequest("status", {"command": "pl_stop"})
318                 xml = self.__xmlRequest("playlist", None)
319                 id = None
320                 for n in xml.getElementsByTagName("node"):
321                         if n.hasAttribute("name") is not None:
322                                 if n.getAttribute("name").encode("utf8", "replace") == playlist:
323                                         if id is None:
324                                                 id = n.getAttribute("id")
325                                         elif int(id) < int(n.getAttribute("id")):
326                                                 id = n.getAttribute("id")
327                 return id