1 # -*- coding: ISO-8859-1 -*-
2 #===============================================================================
3 # VLC Player Plugin by A. Lätsch 2007
4 # modified by Volker Christian 2008
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
10 #===============================================================================
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
27 path = path.replace("\\","/").replace("//", "/")
30 if len(path) > 0 and path[0] != '/':
31 path = posixpath.normpath('/' + path)[1:]
33 path = posixpath.normpath(path)
35 if len(path) == 0 or path == "//":
43 def __init__(self, cfg):
50 return self.cfg.name.value
55 def getAddressType(self):
56 return self.cfg.addressType.value
58 def addressType(self):
59 return self.cfg.addressType
62 return self.cfg.hostip.tostring(self.cfg.hostip.value)
65 return self.cfg.hostip
67 def getHttpPort(self):
68 return self.cfg.httpport.value
71 return self.cfg.httpport
74 return self.cfg.basedir.value
77 return self.cfg.basedir
79 def getVideoCodec(self):
80 return self.cfg.videocodec.value
83 return self.cfg.videocodec
85 def getVideoBitrate(self):
86 return self.cfg.videobitrate.value
88 def videoBitrate(self):
89 return self.cfg.videobitrate
91 def getAudioCodec(self):
92 return self.cfg.audiocodec.value
95 return self.cfg.audiocodec
97 def getAudioBitrate(self):
98 return self.cfg.audiobitrate.value
100 def audioBitrate(self):
101 return self.cfg.audiobitrate
103 def getSamplerate(self):
104 return self.cfg.samplerate.value
106 def samplerate(self):
107 return self.cfg.samplerate
109 def getAudioChannels(self):
110 return self.cfg.audiochannels.value
112 def audioChannels(self):
113 return self.cfg.audiochannels
115 def getVideoNorm(self):
116 return self.cfg.videonorm.value
119 return self.cfg.videonorm
121 def getOverscanCorrection(self):
122 return self.cfg.overscancorrection.value
124 def overscanCorrection(self):
125 return self.cfg.overscancorrection
127 def getSOverlay(self):
128 return self.cfg.soverlay.value
131 return self.cfg.soverlay
133 def getTranscodeVideo(self):
134 return self.cfg.transcodeVideo.value
136 def transcodeVideo(self):
137 return self.cfg.transcodeVideo
139 def getTranscodeAudio(self):
140 return self.cfg.transcodeAudio.value
142 def transcodeAudio(self):
143 return self.cfg.transcodeAudio
146 return self.cfg.dvdPath
148 def getDvdPath(self):
149 return self.cfg.dvdPath.value
151 def __xmlRequest(self, request, params):
152 uri = "/requests/" + request + ".xml"
153 if params is not None: uri = uri + "?" + urlencode(params)
154 location = "%s:%d" % (self.getHost(), self.getHttpPort())
155 resp = urlopen("http://" + location + uri)
157 raise IOError, "No response from Server"
162 def getFilesAndDirs(self, directory, regex):
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"))
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)
179 def getPlaylistEntries(self):
180 xml = self.__xmlRequest("playlist", None)
182 for e in xml.getElementsByTagName("leaf"):
183 if e.hasAttribute("uri") is not None:
184 name = e.getAttribute("name").encode("utf8")
186 name = "..." + name[-50:]
187 path = e.getAttribute("uri").encode("utf8")
188 files.append([name, path])
191 def getCurrentElement(self):
192 xml = self.__xmlRequest("playlist", None)
193 for e in xml.getElementsByTagName("leaf"):
194 if e.hasAttribute("current"):
198 def play(self, session, media, name, currentList = None, player = None):
200 # or not isinstance(player, VlcPlayer):
202 dlg = session.open(player, self, currentList)
203 dlg.playfile(media, name)
206 def playFile(self, filename, videoPid, audioPid):
207 streamName = "dream" + str(randint(0, maxint))
210 doDirect = isDvdUrl(filename) or re.match("(?i).*\.(mpg|mpeg|ts)$", filename.lower())
212 if not doDirect or self.getTranscodeVideo():
213 videoNormList = self.getVideoNorm().split(",")
215 transcode.append("vcodec=%s,vb=%d,venc=ffmpeg{strict-rc=1},width=%s,height=%s,canvas-width=%s,canvas-height=%s,canvas-aspect=%s,fps=%s" % (
216 self.getVideoCodec(),self.getVideoBitrate(),
217 str(int(float(videoNormList[0]) - float(videoNormList[0]) * float(self.getOverscanCorrection()) / 100)),
218 str(int(float(videoNormList[1]) - float(videoNormList[1]) * float(self.getOverscanCorrection()) / 100)),
219 videoNormList[0], videoNormList[1], videoNormList[2], videoNormList[3]
221 if self.getSOverlay():
222 transcode.append("soverlay")
223 if not doDirect or self.getTranscodeAudio():
224 transcode.append("acodec=%s,ab=%d,channels=%d,samplerate=%s" % (
225 self.getAudioCodec(),
226 self.getAudioBitrate(),
227 self.getAudioChannels(),
230 if re.match("[a-zA-Z]:", filename):
231 # Fix for subtitles with VLC on Windows.
232 filename = filename.replace("/", "\\")
234 filename = filename.replace("\\", "\\\\").replace("'", "\\'")
235 # input = filename + " :dvdread-caching=3000 :sub-track=1 :audio-track=1 :sout=#"
236 input = filename + " :sout=#"
238 if len(transcode) > 0:
239 input += "transcode{%s}:" % (",".join(transcode))
241 mux="ts{pid-video=%d,pid-audio=%d}" % (videoPid, audioPid)
242 input += "std{access=http,mux=%s,dst=/%s.ts} :sout-all :sout-keep" % (mux, streamName)
244 print "[VLC] playfile", input
246 xml = self.__xmlRequest("status", {"command": "in_play", "input": input})
247 error = xml.getElementsByTagName("error")
248 if error is not None and len(error) > 0:
249 self.lastError = getText(error[0].childNodes).strip()
250 if len(self.lastError) == 0:
251 self.lastError = None
253 print "[VLC] VlcControl error:", self.lastError
256 self.lastError = None
257 return "http://%s:%d/%s.ts" % (self.getHost(), self.getHttpPort(), streamName)
260 self.__xmlRequest("status", {"command": "pl_pause"})
263 self.__xmlRequest("status", {"command": "pl_stop"})
266 self.__xmlRequest("status", {"command": "pl_pause"})
268 def delete(self, id):
269 self.__xmlRequest("status", {"command": "pl_delete", "id": str(id)})
271 def deleteCurrentTree(self):
272 print "[VLC] delete current tree"
273 currentElement = self.getCurrentElement()
274 while currentElement is not None and currentElement.parentNode.getAttribute("ro") != "ro":
275 currentElement = currentElement.parentNode
276 id = int(currentElement.getAttribute("id"))
279 def seek(self, value):
280 """ Allowed values are of the form:
281 [+ or -][<int><H or h>:][<int><M or m or '>:][<int><nothing or S or s or ">]
283 (value between [ ] are optional, value between < > are mandatory)
285 1000 -> seek to the 1000th second
286 +1H:2M -> seek 1 hour and 2 minutes forward
287 -10% -> seek 10% back"""
288 self.__xmlRequest("status", {"command": "seek", "val": str(value)})
291 xml = self.__xmlRequest("status", None)
293 for e in xml.documentElement.childNodes:
294 if e.nodeType == e.ELEMENT_NODE:
295 if e.firstChild is None:
296 stats[e.nodeName.encode("latin_1", "replace")] = None
298 stats[e.nodeName.encode("latin_1", "replace")] = e.firstChild.nodeValue.encode("latin_1", "replace")
301 def loadPlaylist(self, playlist):
302 self.__xmlRequest("status", {"command": "in_play", "input": playlist})
303 self.__xmlRequest("status", {"command": "pl_stop"})
304 xml = self.__xmlRequest("playlist", None)
306 for n in xml.getElementsByTagName("node"):
307 if n.hasAttribute("name") is not None:
308 if n.getAttribute("name").encode("utf8", "replace") == playlist:
310 id = n.getAttribute("id")
311 elif int(id) < int(n.getAttribute("id")):
312 id = n.getAttribute("id")