VlcServer.py: show directories again with vlc 2.0
[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 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" or elementType == "dir":
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                         # Video settings
215                         transcode.append("vcodec=%s,vb=%d,venc=ffmpeg,fps=%s" % (
216                                 self.getVideoCodec(),self.getVideoBitrate(),
217                                 videoNormList[3]
218                         ))
219                         #New canvas - since VLC 0.9
220                         transcode.append("vfilter=canvas{width=%s,height=%s,aspect=%s}" % (
221                                 str(int(float(videoNormList[0]) - float(videoNormList[0]) * float(self.getOverscanCorrection()) / 100)),
222                                 str(int(float(videoNormList[1]) - float(videoNormList[1]) * float(self.getOverscanCorrection()) / 100)),
223                                 videoNormList[2]
224                         ))
225                         if self.getSOverlay():
226                                 transcode.append("soverlay")
227                 if not doDirect or self.getTranscodeAudio():
228                         transcode.append("acodec=%s,ab=%d,channels=%d,samplerate=%s" % (
229                                 self.getAudioCodec(),
230                                 self.getAudioBitrate(),
231                                 self.getAudioChannels(),
232                                 self.getSamplerate()
233                         ))
234                 if re.match("[a-zA-Z]:", filename):
235                         # Fix for subtitles with VLC on Windows.
236                         filename = filename.replace("/", "\\")
237
238                 filename = filename.replace("\\", "\\\\").replace("'", "\\'")
239                 input = filename + " :sout=#"
240
241                 if len(transcode) > 0:
242                         input += "transcode{%s}:" % (",".join(transcode))
243
244                 mux="ts{pid-video=%d,pid-audio=%d}" % (videoPid, audioPid)
245                 input += "std{access=http,mux=%s,dst=/%s.ts} :sout-all :sout-keep" % (mux, streamName)
246
247                 print "[VLC] playfile", input
248
249                 xml = self.__xmlRequest("status", {"command": "in_play", "input": input})
250                 error = xml.getElementsByTagName("error")
251                 if error is not None and len(error) > 0:
252                         self.lastError = getText(error[0].childNodes).strip()
253                         if len(self.lastError) == 0:
254                                 self.lastError = None
255                         else:
256                                 print "[VLC] VlcControl error:", self.lastError
257                         return None
258                 else:
259                         self.lastError = None
260                 return "http://%s:%d/%s.ts" % (self.getHost(), self.getHttpPort(), streamName)
261
262         def unpause(self):
263                 self.__xmlRequest("status", {"command": "pl_pause"})
264
265         def stop(self):
266                 self.__xmlRequest("status", {"command": "pl_stop"})
267
268         def pause(self):
269                 self.__xmlRequest("status", {"command": "pl_pause"})
270
271         def delete(self, id):
272                 self.__xmlRequest("status", {"command": "pl_delete", "id": str(id)})
273
274         def deleteCurrentTree(self):
275                 print "[VLC] delete current tree"
276                 currentElement = self.getCurrentElement()
277                 while currentElement is not None and currentElement.parentNode.getAttribute("ro") != "ro":
278                         currentElement = currentElement.parentNode
279                 id = int(currentElement.getAttribute("id"))
280                 self.delete(id)
281                 
282         def seek(self, value):
283                 """  Allowed values are of the form:
284   [+ or -][<int><H or h>:][<int><M or m or '>:][<int><nothing or S or s or ">]
285   or [+ or -]<int>%
286   (value between [ ] are optional, value between < > are mandatory)
287 examples:
288   1000 -> seek to the 1000th second
289   +1H:2M -> seek 1 hour and 2 minutes forward
290   -10% -> seek 10% back"""
291                 self.__xmlRequest("status", {"command": "seek", "val": str(value)})
292
293         def status(self):
294                 xml = self.__xmlRequest("status", None)
295                 stats = {}
296                 for e in xml.documentElement.childNodes:
297                         if e.nodeType == e.ELEMENT_NODE:
298                                 if e.firstChild is None:
299                                         stats[e.nodeName.encode("latin_1", "replace")] = None
300                                 else:
301                                         stats[e.nodeName.encode("latin_1", "replace")] = e.firstChild.nodeValue.encode("latin_1", "replace")
302                 return stats
303
304         def loadPlaylist(self, playlist):
305                 self.__xmlRequest("status", {"command": "in_play", "input": playlist})
306                 self.__xmlRequest("status", {"command": "pl_stop"})
307                 xml = self.__xmlRequest("playlist", None)
308                 id = None
309                 for n in xml.getElementsByTagName("node"):
310                         if n.hasAttribute("name") is not None:
311                                 if n.getAttribute("name").encode("utf8", "replace") == playlist:
312                                         if id is None:
313                                                 id = n.getAttribute("id")
314                                         elif int(id) < int(n.getAttribute("id")):
315                                                 id = n.getAttribute("id")
316                 return id