[streamtv plugin] fix bug.
[vuplus_dvbapp] / lib / python / Plugins / Extensions / StreamTV / plugin.py
1 from Plugins.Plugin import PluginDescriptor
2
3 import os
4 from xml.etree.cElementTree import fromstring, ElementTree
5
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
7
8 from Screens.Screen import Screen
9 from Screens.ChoiceBox import ChoiceBox
10 from Screens.MessageBox import MessageBox
11 from Screens.InfoBarGenerics import InfoBarNotifications
12
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
24
25 from Tools.Directories import fileExists, resolveFilename, SCOPE_PLUGINS
26
27 PLUGIN_PATH = resolveFilename(SCOPE_PLUGINS, "Extensions/StreamTV")
28
29 class StreamTVPlayer(Screen, InfoBarNotifications):
30         skin =  """
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>
38                         </widget>
39                 </screen>
40                 """ % (PLUGIN_PATH)
41
42         PLAYER_IDLE     = 0
43         PLAYER_PLAYING  = 1
44         PLAYER_PAUSED   = 2
45         def __init__(self, session, service, cbServiceCommand, chName, chURL, chIcon):
46                 Screen.__init__(self, session)
47                 InfoBarNotifications.__init__(self)
48
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,
60                         "stop": self.doExit,
61                         "playpauseService": self.playpauseService,
62                 }, -2)
63
64                 self.__event_tracker = ServiceEventTracker(screen = self, eventmap = {
65                         iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
66                         iPlayableService.evStart: self.__serviceStarted,
67                         iPlayableService.evEOF: self.__evEOF,
68                 })
69
70                 self.hidetimer = eTimer()
71                 self.hidetimer.timeout.get().append(self.doInfoAction)
72
73                 self.state = self.PLAYER_PLAYING
74                 self.lastseekstate = self.PLAYER_PLAYING
75                 self.__seekableStatusChanged()
76         
77                 self.onClose.append(self.__onClose)
78                 self.doPlay()
79
80                 self['channel_icon'] = Pixmap()
81                 self['channel_name'] = Label(chName)
82                 self['channel_uri']  = Label(chURL)
83
84                 self.picload = ePicLoad()
85                 self.scale   = AVSwitch().getFramebufferScale()
86                 self.picload.PictureData.get().append(self.cbDrawChannelIcon)
87                 print self.scale[0]
88                 print self.scale[1]
89                 self.picload.setPara((35, 35, self.scale[0], self.scale[1], False, 0, "#00000000"))
90                 self.picload.startDecode(chIcon)
91
92                 self.bypassExit = False
93                 self.cbServiceCommand(('docommand',self.doCommand))
94
95         def doCommand(self, cmd):
96                 if cmd == 'bypass_exit':
97                         self.bypassExit = True
98                         
99         def cbDrawChannelIcon(self, picInfo=None):
100                 ptr = self.picload.getData()
101                 if ptr != None:
102                         self["channel_icon"].instance.setPixmap(ptr.__deref__())
103                         self["channel_icon"].show()
104
105         def __onClose(self):
106                 self.session.nav.stopService()
107
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)
114
115         def __serviceStarted(self):
116                 self.state = self.PLAYER_PLAYING
117                 self.__seekableStatusChanged()
118
119         def __evEOF(self):
120                 if self.bypassExit:
121                         return
122                 self.doExit()
123
124         def __setHideTimer(self):
125                 self.hidetimer.start(5000)
126
127         def doExit(self):
128                 list = ((_("Yes"), "y"), (_("No"), "n"),)
129                 self.session.openWithCallback(self.cbDoExit, ChoiceBox, title=_("Stop playing this stream?"), list=list)
130
131         def cbDoExit(self, answer):
132                 answer = answer and answer[1]
133                 if answer == "y":
134                         self.cbServiceCommand()
135                         self.close()
136
137         def setSeekState(self, wantstate):
138                 service = self.session.nav.getCurrentService()
139                 if service is None:
140                         print "No Service found"
141                         return
142
143                 pauseable = service.pause()
144                 if pauseable is not None:
145                         if wantstate == self.PLAYER_PAUSED:
146                                 pauseable.pause()
147                                 self.state = self.PLAYER_PAUSED
148                                 if not self.shown:
149                                         self.hidetimer.stop()
150                                         self.show()
151                         elif wantstate == self.PLAYER_PLAYING:
152                                 pauseable.unpause()
153                                 self.state = self.PLAYER_PLAYING
154                                 if self.shown:
155                                         self.__setHideTimer()
156                 else:
157                         self.state = self.PLAYER_PLAYING
158
159         def doInfoAction(self):
160                 if self.shown:
161                         self.hidetimer.stop()
162                         self.hide()
163                 else:
164                         self.show()
165                         if self.state == self.PLAYER_PLAYING:
166                                 self.__setHideTimer()
167
168         def doPlay(self):
169                 if self.state == self.PLAYER_PAUSED:
170                         if self.shown:
171                                 self.__setHideTimer()   
172                 self.state = self.PLAYER_PLAYING
173                 self.session.nav.playService(self.service)
174                 if self.shown:
175                         self.__setHideTimer()
176
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)
182
183 class StreamURIParser:
184         def __init__(self, xml):
185                 self.xml = xml
186
187         def parseStreamList(self):
188                 tvlist = []
189                 tree = ElementTree()
190                 tree.parse(self.xml)
191
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)})
198                 return tvlist
199
200         def parseStreamURI(self, uri):
201                 uriInfo = {}
202                 splitedURI = uri.split()
203                 uriInfo['URL'] = splitedURI[0]
204                 for x in splitedURI[1:]:
205                         i = x.find('=')
206                         uriInfo[x[:i].upper()] = str(x[i+1:])
207                 return uriInfo
208
209 def streamListEntry(entry):
210         #print entry
211         uriInfo = entry[1].get('uri')
212         return [entry,
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')))
216         ] 
217
218 class StreamTVList(Screen):
219         skin =  """
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" />
222                 </screen>
223                 """
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"], {
228                         "ok"    : self.keyOK,
229                         "cancel": self.keyCancel,
230                         "up"    : self.keyUp,
231                         "down"  : self.keyDown,
232                         "left"  : self.keyLeft,
233                         "right" : self.keyRight,
234                 }, -1)
235
236                 self.streamBin  = resolveFilename(SCOPE_PLUGINS, "Extensions/StreamTV/rtmpdump")
237                 self.streamFile = resolveFilename(SCOPE_PLUGINS, "Extensions/StreamTV/stream.xml")
238
239                 self.streamList = []
240                 self.makeStreamList()
241
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))
248
249                 self.onLayoutFinish.append(self.layoutFinished)
250
251                 self.rtmpConsole    = None
252                 self.beforeService  = None
253                 self.currentService = None
254                 self.playerStoped   = False
255                 self.serviceDoCommand = None
256
257                 self.keyLocked = False
258
259         def layoutFinished(self):
260                 rc = os.popen('ps -ef | grep rtmpdump | grep -v grep').read()
261                 print "a process already running :", rc
262                 if rc is not None:
263                         if rc.strip() != '':
264                                 os.system('killall -INT rtmpdump')
265         def keyLeft(self):
266                 if self.keyLocked:
267                         return
268                 self['streamlist'].pageUp()
269
270         def keyRight(self):
271                 if self.keyLocked:
272                         return
273                 self['streamlist'].pageDown()
274
275         def keyUp(self):
276                 if self.keyLocked:
277                         return
278                 self['streamlist'].up()
279
280         def keyDown(self):
281                 if self.keyLocked:
282                         return
283                 self['streamlist'].down()
284
285         def keyCancel(self):
286                 self.cbAppClosed(True)
287                 self.close()
288
289         def keyOK(self):
290                 if self.keyLocked:
291                         return
292                 self.keyLocked = True
293                 self.rtmpConsole    = None
294                 self.beforeService  = None
295                 self.currentService = None
296                 self.playerStoped   = False
297                 self.serviceDoCommand = None
298
299                 streamInfo = self["streamlist"].getCurrent()[0][1]
300                 uriInfo    = streamInfo.get('uri')
301                 typeInfo   = streamInfo.get('type').split(':')
302
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)
315
316         def doStreamAction(self, url=None, serviceType='4097', bufferSize=None):
317                 if url is None:
318                         url='/tmp/stream.avi'
319                         self.streamPlayerTimer.stop()
320                         #if os.path.exists(url):
321                         #       os.unlink(url)
322                 try:
323                         serviceType = int(serviceType)
324                 except: serviceType = 4097
325                 try:
326                         bufferSize = int(bufferSize)
327                 except: bufferSize = None
328
329                 service = eServiceReference(serviceType, 0, url)
330                 #if bufferSize is not None:
331                 #       service.setData(2, bufferSize*1024)
332
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, 
337                                                                     StreamTVPlayer, 
338                                                                     service, 
339                                                                     cbServiceCommand=self.cbServiceCommand,
340                                                                     chName=str(streamInfo.get('name')),
341                                                                     chURL =str(uriInfo.get('URL')),
342                                                                     chIcon=str(streamInfo.get('icon')))
343
344         def cbServiceCommand(self, params=None):
345                 if params is None:
346                         self.playerStoped = True
347                         return
348                 if params[0] == 'docommand':
349                         self.serviceDoCommand = params[1]
350
351         def cbAppClosed(self, ret):
352                 print ret
353                 self.doConsoleStop()
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
361
362         def cbDataAvail(self, data):
363                 print data
364                 if str(data) == 'Connected...':
365                         self.streamPlayerTimer = eTimer()
366                         self.streamPlayerTimer.timeout.get().append(self.doStreamAction)
367                         self.streamPlayerTimer.start(1000)
368
369         def cbFinishedStream(self):
370                 self.doConsoleStop()
371                 self.session.nav.playService(self.beforeService)
372                 print 'player done!!'
373
374         def doConsoleStop(self):
375                 self.keyLocked = False
376                 if self.rtmpConsole is not None:
377                         self.rtmpConsole.sendCtrlC()
378                         self.rtmpConsole = None
379
380         def makeCommand(self, uriInfo):
381                 def appendCommand(key, option):
382                         try:
383                                 d = uriInfo.get(key)
384                                 if d is not None:
385                                         return "-%s %s " %(option, d)
386                         except: pass
387                         return ''
388                 command  = '%s -v ' % (self.streamBin)
389                 command += appendCommand('URL', 'r')
390                 command += appendCommand('PLAYPATH', 'y')
391                 command += appendCommand('SWFURL', 'W')
392                 return command
393
394         def makeStreamList(self):
395                 streamDB = StreamURIParser(self.streamFile).parseStreamList()
396                 self.streamList = []
397                 for x in streamDB:
398                         self.streamList.append((x.get('name'), x))
399
400 def main(session, **kwargs):
401         session.open(StreamTVList)
402                                                            
403 def Plugins(**kwargs):
404         return PluginDescriptor(name=_("StreamTVPlayer"), description="Watching IPTV implemented by RTSP/RTMP protocol.", where = PluginDescriptor.WHERE_PLUGINMENU, fnc=main)
405
406