1 from Plugins.Plugin import PluginDescriptor
4 from xml.etree.cElementTree import fromstring, ElementTree
6 from enigma import gFont, eTimer, eConsoleAppContainer, ePicLoad, loadPNG, getDesktop, eServiceReference, iPlayableService, eListboxPythonMultiContent, RT_HALIGN_LEFT, RT_HALIGN_RIGHT, RT_HALIGN_CENTER, RT_VALIGN_CENTER
8 from Screens.Screen import Screen
9 from Screens.ChoiceBox import ChoiceBox
10 from Screens.MessageBox import MessageBox
11 from Screens.InfoBarGenerics import InfoBarNotifications
13 from Components.Button import Button
14 from Components.Label import Label
15 from Components.ConfigList import ConfigListScreen
16 from Components.Sources.StaticText import StaticText
17 from Components.ActionMap import NumberActionMap, ActionMap
18 from Components.config import config, ConfigSelection, getConfigListEntry, ConfigText, ConfigDirectory, ConfigYesNo, ConfigSelection
19 from Components.FileList import FileList, FileEntryComponent
20 from Components.MenuList import MenuList
21 from Components.Pixmap import Pixmap, MovingPixmap
22 from Components.AVSwitch import AVSwitch
23 from Components.ServiceEventTracker import ServiceEventTracker
25 from Tools.Directories import fileExists, resolveFilename, SCOPE_PLUGINS
27 PLUGIN_PATH = resolveFilename(SCOPE_PLUGINS, "Extensions/StreamTV")
29 class StreamTVPlayer(Screen, InfoBarNotifications):
31 <screen name="StreamTVPlayer" flags="wfNoBorder" position="0,570" size="1280,190" title="StreamTV Player" backgroundColor="#41000000" >
32 <ePixmap position="80,25" size="117,72" pixmap="%s/channel_background.png" zPosition="-1" transparent="1" alphatest="blend" />
33 <widget name="channel_icon" position="121,43" zPosition="10" size="35,35" backgroundColor="#41000000" />
34 <widget name="channel_name" position="250,20" size="650,40" font="Regular;36" halign="left" valign="center" foregroundColor="#ffffff" backgroundColor="#41000000" />
35 <widget name="channel_uri" position="250,70" size="950,60" font="Regular;22" halign="left" valign="top" foregroundColor="#ffffff" backgroundColor="#41000000" />
36 <widget source="session.CurrentService" render="Label" position="805,20" size="300,40" font="Regular;30" halign="right" valign="center" foregroundColor="#f4df8d" backgroundColor="#41000000" transparent="1" >
37 <convert type="ServicePosition">Position</convert>
45 def __init__(self, session, service, cbServiceCommand, chName, chURL, chIcon):
46 Screen.__init__(self, session)
47 InfoBarNotifications.__init__(self)
49 isEmpty = lambda x: x is None or len(x)==0 or x == 'None'
50 if isEmpty(chName): chName = 'Unknown'
51 if isEmpty(chURL): chURL = 'Unknown'
52 if isEmpty(chIcon): chIcon = 'default.png'
53 chIcon = '%s/icons/%s'%(PLUGIN_PATH,chIcon)
54 self.session = session
55 self.service = service
56 self.cbServiceCommand = cbServiceCommand
57 self["actions"] = ActionMap(["OkCancelActions", "InfobarSeekActions", "MediaPlayerActions", "MovieSelectionActions"], {
58 "ok": self.doInfoAction,
59 "cancel": self.doExit,
61 "playpauseService": self.playpauseService,
64 self.__event_tracker = ServiceEventTracker(screen = self, eventmap = {
65 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
66 iPlayableService.evStart: self.__serviceStarted,
67 iPlayableService.evEOF: self.__evEOF,
70 self.hidetimer = eTimer()
71 self.hidetimer.timeout.get().append(self.doInfoAction)
73 self.state = self.PLAYER_PLAYING
74 self.lastseekstate = self.PLAYER_PLAYING
75 self.__seekableStatusChanged()
77 self.onClose.append(self.__onClose)
80 self['channel_icon'] = Pixmap()
81 self['channel_name'] = Label(chName)
82 self['channel_uri'] = Label(chURL)
84 self.picload = ePicLoad()
85 self.scale = AVSwitch().getFramebufferScale()
86 self.picload.PictureData.get().append(self.cbDrawChannelIcon)
89 self.picload.setPara((35, 35, self.scale[0], self.scale[1], False, 0, "#00000000"))
90 self.picload.startDecode(chIcon)
92 self.bypassExit = False
93 self.cbServiceCommand(('docommand',self.doCommand))
95 def doCommand(self, cmd):
96 if cmd == 'bypass_exit':
97 self.bypassExit = True
99 def cbDrawChannelIcon(self, picInfo=None):
100 ptr = self.picload.getData()
102 self["channel_icon"].instance.setPixmap(ptr.__deref__())
103 self["channel_icon"].show()
106 self.session.nav.stopService()
108 def __seekableStatusChanged(self):
109 service = self.session.nav.getCurrentService()
110 if service is not None:
111 seek = service.seek()
112 if seek is None or not seek.isCurrentlySeekable():
113 self.setSeekState(self.PLAYER_PLAYING)
115 def __serviceStarted(self):
116 self.state = self.PLAYER_PLAYING
117 self.__seekableStatusChanged()
124 def __setHideTimer(self):
125 self.hidetimer.start(5000)
128 list = ((_("Yes"), "y"), (_("No"), "n"),)
129 self.session.openWithCallback(self.cbDoExit, ChoiceBox, title=_("Stop playing this stream?"), list=list)
131 def cbDoExit(self, answer):
132 answer = answer and answer[1]
134 self.cbServiceCommand()
137 def setSeekState(self, wantstate):
138 service = self.session.nav.getCurrentService()
140 print "No Service found"
143 pauseable = service.pause()
144 if pauseable is not None:
145 if wantstate == self.PLAYER_PAUSED:
147 self.state = self.PLAYER_PAUSED
149 self.hidetimer.stop()
151 elif wantstate == self.PLAYER_PLAYING:
153 self.state = self.PLAYER_PLAYING
155 self.__setHideTimer()
157 self.state = self.PLAYER_PLAYING
159 def doInfoAction(self):
161 self.hidetimer.stop()
165 if self.state == self.PLAYER_PLAYING:
166 self.__setHideTimer()
169 if self.state == self.PLAYER_PAUSED:
171 self.__setHideTimer()
172 self.state = self.PLAYER_PLAYING
173 self.session.nav.playService(self.service)
175 self.__setHideTimer()
177 def playpauseService(self):
178 if self.state == self.PLAYER_PLAYING:
179 self.setSeekState(self.PLAYER_PAUSED)
180 elif self.state == self.PLAYER_PAUSED:
181 self.setSeekState(self.PLAYER_PLAYING)
183 class StreamURIParser:
184 def __init__(self, xml):
187 def parseStreamList(self):
192 for iptv in tree.findall('iptv'):
193 n = str(iptv.findtext('name'))
194 i = str(iptv.findtext('icon'))
195 u = str(iptv.findtext('uri'))
196 t = str(iptv.findtext('type'))
197 tvlist.append({'name':n, 'icon':i, 'type':t, 'uri':self.parseStreamURI(u)})
200 def parseStreamURI(self, uri):
202 splitedURI = uri.split()
203 uriInfo['URL'] = splitedURI[0]
204 for x in splitedURI[1:]:
206 uriInfo[x[:i].upper()] = str(x[i+1:])
209 def streamListEntry(entry):
211 uriInfo = entry[1].get('uri')
213 (eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, 5, 1, 35, 35, loadPNG('%s/icons/%s' % (PLUGIN_PATH, str(entry[1].get('icon'))) )),
214 (eListboxPythonMultiContent.TYPE_TEXT,45,7,200,37,0,RT_HALIGN_LEFT,entry[0]),
215 (eListboxPythonMultiContent.TYPE_TEXT,250,7,310,37,1,RT_HALIGN_LEFT,str(uriInfo.get('URL')))
218 class StreamTVList(Screen):
220 <screen name="StreamTVList" position="center,center" size="600,350" title="StreamTV List">
221 <widget name="streamlist" position="0,0" size="600,350" backgroundColor="#000000" zPosition="10" scrollbarMode="showOnDemand" />
224 def __init__(self, session):
225 self.session = session
226 Screen.__init__(self, session)
227 self["actions"] = ActionMap(["OkCancelActions", "ShortcutActions", "WizardActions", "ColorActions", "SetupActions", "NumberActions", "MenuActions"], {
229 "cancel": self.keyCancel,
231 "down" : self.keyDown,
232 "left" : self.keyLeft,
233 "right" : self.keyRight,
236 self.streamBin = resolveFilename(SCOPE_PLUGINS, "Extensions/StreamTV/rtmpdump")
237 self.streamFile = resolveFilename(SCOPE_PLUGINS, "Extensions/StreamTV/stream.xml")
240 self.makeStreamList()
242 self.streamMenuList = MenuList([], enableWrapAround=True, content=eListboxPythonMultiContent)
243 self.streamMenuList.l.setFont(0, gFont('Regular', 22))
244 self.streamMenuList.l.setFont(1, gFont('Regular', 18))
245 self.streamMenuList.l.setItemHeight(37)
246 self['streamlist'] = self.streamMenuList
247 self.streamMenuList.setList(map(streamListEntry, self.streamList))
249 self.onLayoutFinish.append(self.layoutFinished)
251 self.rtmpConsole = None
252 self.beforeService = None
253 self.currentService = None
254 self.playerStoped = False
255 self.serviceDoCommand = None
257 self.keyLocked = False
259 def layoutFinished(self):
260 rc = os.popen('ps -ef | grep rtmpdump | grep -v grep').read()
261 print "a process already running :", rc
264 os.system('killall -INT rtmpdump')
268 self['streamlist'].pageUp()
273 self['streamlist'].pageDown()
278 self['streamlist'].up()
283 self['streamlist'].down()
286 self.cbAppClosed(True)
292 self.keyLocked = True
293 self.rtmpConsole = None
294 self.beforeService = None
295 self.currentService = None
296 self.playerStoped = False
297 self.serviceDoCommand = None
299 streamInfo = self["streamlist"].getCurrent()[0][1]
300 uriInfo = streamInfo.get('uri')
301 typeInfo = streamInfo.get('type').split(':')
303 protocol = typeInfo[0]
304 url = uriInfo.get('URL')
305 if protocol == 'rtmp':
306 self.layoutFinished()
307 self.rtmpConsole = eConsoleAppContainer()
308 self.rtmpConsole.dataAvail.append(self.cbDataAvail)
309 self.rtmpConsole.appClosed.append(self.cbAppClosed)
310 self.rtmpConsole.execute(self.makeCommand(uriInfo))
311 elif protocol in ('rtsp', 'http', 'hls'):
312 serviceType = typeInfo[1]
313 bufferSize = typeInfo[2]
314 self.doStreamAction(url, serviceType, bufferSize)
316 def doStreamAction(self, url=None, serviceType='4097', bufferSize=None):
318 url='/tmp/stream.avi'
319 self.streamPlayerTimer.stop()
320 #if os.path.exists(url):
323 serviceType = int(serviceType)
324 except: serviceType = 4097
326 bufferSize = int(bufferSize)
327 except: bufferSize = None
329 service = eServiceReference(serviceType, 0, url)
330 #if bufferSize is not None:
331 # service.setData(2, bufferSize*1024)
333 streamInfo = self["streamlist"].getCurrent()[0][1]
334 uriInfo = streamInfo.get('uri')
335 self.beforeService = self.session.nav.getCurrentlyPlayingServiceReference()
336 self.currentService = self.session.openWithCallback(self.cbFinishedStream,
339 cbServiceCommand=self.cbServiceCommand,
340 chName=str(streamInfo.get('name')),
341 chURL =str(uriInfo.get('URL')),
342 chIcon=str(streamInfo.get('icon')))
344 def cbServiceCommand(self, params=None):
346 self.playerStoped = True
348 if params[0] == 'docommand':
349 self.serviceDoCommand = params[1]
351 def cbAppClosed(self, ret):
354 if self.currentService is not None and not self.playerStoped:
355 self.serviceDoCommand('bypass_exit')
356 message = "The connection was terminated from the stream server."
357 self.session.open(MessageBox, message, type=MessageBox.TYPE_INFO)
358 self.currentService.close()
359 self.currentService = None
360 self.serviceDoCommand = None
362 def cbDataAvail(self, data):
364 if str(data) == 'Connected...':
365 self.streamPlayerTimer = eTimer()
366 self.streamPlayerTimer.timeout.get().append(self.doStreamAction)
367 self.streamPlayerTimer.start(1000)
369 def cbFinishedStream(self):
373 def doConsoleStop(self):
374 self.keyLocked = False
375 if self.rtmpConsole is not None:
376 self.rtmpConsole.sendCtrlC()
377 self.rtmpConsole = None
379 def makeCommand(self, uriInfo):
380 def appendCommand(key, option):
384 return "-%s %s " %(option, d)
387 command = '%s -v ' % (self.streamBin)
388 command += appendCommand('URL', 'r')
389 command += appendCommand('PLAYPATH', 'y')
390 command += appendCommand('SWFURL', 'W')
393 def makeStreamList(self):
394 streamDB = StreamURIParser(self.streamFile).parseStreamList()
397 self.streamList.append((x.get('name'), x))
399 def main(session, **kwargs):
400 session.open(StreamTVList)
402 def Plugins(**kwargs):
403 return PluginDescriptor(name=_("StreamTVPalyer"), description="Watching IPTV implemented by RTSP/RTMP protocol.", where = PluginDescriptor.WHERE_PLUGINMENU, fnc=main)