[FCC] Show extension menu on InfoBar only.
[vuplus_dvbapp] / lib / python / Plugins / SystemPlugins / FastChannelChange / plugin.py
1
2 from Plugins.Plugin import PluginDescriptor
3 import NavigationInstance
4
5 from Screens.Screen import Screen
6 from Screens.InfoBar import InfoBar
7 from Screens.MessageBox import MessageBox
8 from Screens.PictureInPicture import on_pip_start_stop
9
10 from Components.NimManager import nimmanager
11 from Components.config import config, getConfigListEntry, ConfigSubsection, ConfigYesNo, ConfigSelection
12 from Components.ConfigList import ConfigListScreen
13 from Components.ActionMap import ActionMap
14 from Components.Sources.StaticText import StaticText
15 from Components.ServiceEventTracker import ServiceEventTracker
16
17 from enigma import iPlayableService, iServiceInformation, eEnv, eTimer, eServiceReference, iRecordableService
18
19 import os
20 import glob
21
22 from enigma import eFCCServiceManager
23
24 g_max_fcc = len(glob.glob('/dev/fcc?'))
25 g_default_fcc = (g_max_fcc) > 5 and 5 or g_max_fcc
26
27 config.plugins.fccsetup = ConfigSubsection()
28 config.plugins.fccsetup.activate = ConfigYesNo(default = False)
29 config.plugins.fccsetup.maxfcc = ConfigSelection(default = str(g_default_fcc), choices = list((str(n), str(n)) for n in range(2, g_max_fcc+1)))
30 config.plugins.fccsetup.zapupdown = ConfigYesNo(default = True)
31 config.plugins.fccsetup.history = ConfigYesNo(default = False)
32 config.plugins.fccsetup.priority = ConfigSelection(default = "zapupdown", choices = { "zapupdown" : _("Zap Up/Down"), "historynextback" : _("History Prev/Next") })
33 config.plugins.fccsetup.disableforrec = ConfigYesNo(default = True)
34
35 FccInstance = None
36
37 def FCCChanged():
38         if FccInstance:
39                 FccInstance.FCCSetupChanged()
40
41 def checkSupportFCC():
42         global g_max_fcc
43         return bool(g_max_fcc)
44
45 class FCCSupport:
46         def __init__(self, session):
47                 self.session = session
48
49                 self.fccmgr = eFCCServiceManager.getInstance();
50
51                 self.fccList = []
52
53                 self.createListTimer = eTimer()
54                 self.createListTimer.callback.append(self.FCCCreateList)
55
56                 self.getSrefTimer = eTimer()
57                 self.getSrefTimer.callback.append(self.FCCGetCurSref)
58
59                 self.eventList = []
60                 self.fccEventTimer = eTimer()
61                 self.fccEventTimer.callback.append(self.FCCApplyEvent)
62
63                 self.fccForceStartTimer = eTimer()
64                 self.fccForceStartTimer.callback.append(self.FCCForceStart)
65
66                 self.fccResetTimer = eTimer()
67                 self.fccResetTimer.callback.append(self.FCCResetTimerForREC)
68
69                 self.activating = False
70
71                 self.fccSetupActivate = checkSupportFCC() and config.plugins.fccsetup.activate.value
72                 self.maxFCC = int(config.plugins.fccsetup.maxfcc.value)
73                 self.zapdownEnable = config.plugins.fccsetup.zapupdown.value
74                 self.historyEnable = config.plugins.fccsetup.history.value
75                 self.priority = config.plugins.fccsetup.priority.value
76                 self.disableforrec = config.plugins.fccsetup.disableforrec.value
77                 self.fccmgr.setFCCEnable(int(self.fccSetupActivate))
78
79                 self.setProcFCC(self.fccSetupActivate)
80                 self.fccTimeoutTimer = eTimer()
81                 self.fccTimeoutTimer.callback.append(self.FCCTimeout)
82                 self.fccTimeoutEventCode = 0x102
83                 self.fccTimeoutWait = None
84
85                 self.fccmgr.m_fcc_event.get().append(self.FCCGetEvent)
86
87                 self.getRecordings()
88
89                 self.__event_tracker = None
90                 self.onClose = []
91                 self.changeEventTracker()
92
93                 on_pip_start_stop.append(self.FCCForceStopforPIP)
94
95         def setProcFCC(self, value):
96                 procPath = "/proc/stb/frontend/fbc/fcc"
97                 if os.access(procPath, os.W_OK):
98                         fd = open(procPath,'w')
99                         fd.write(value and "enable" or "disable")
100                         fd.close()
101                 else:
102                         print "[FCCSupport] write fail! : ", procPath
103
104         def gotRecordEvent(self, service, event):
105                 if self.disableforrec:
106                         if (not self.recordings) and (event == iRecordableService.evTuneStart):
107                                 self.getRecordings()
108                                 if self.recordings:
109                                         self.FCCForceStopForREC()
110
111                         elif event == iRecordableService.evEnd:
112                                 self.getRecordings()
113                                 if not self.recordings:
114                                         self.FCCForceStart()
115                 else:
116                         if event == iRecordableService.evTuneStart:
117                                 self.FCCForceStopAndStart()
118
119                         elif event == iRecordableService.evEnd:
120                                 self.fccForceStartTimer.stop()
121                                 self.fccResetTimer.start(2000, True)
122
123         def FCCForceStart(self):
124                 self.enableEventTracker(True)
125                 self.getEvStart()
126                 self.getEvTunedIn()
127
128         def FCCForceStop(self):
129                 self.enableEventTracker(False)
130                 self.FCCDisableServices()
131                 self.FCCStopAllServices()
132
133         def FCCForceStopAndStart(self):
134                 self.fccResetTimer.stop()
135                 self.FCCForceStop()
136                 self.fccForceStartTimer.start(2000, True)
137
138         def FCCForceStopforPIP(self):
139                 self.FCCForceStopAndStart()
140
141         def FCCForceStopForREC(self):
142                 self.FCCForceStop()
143
144         def FCCResetTimerForREC(self):
145                 self.FCCForceStopForREC()
146                 self.FCCForceStart()
147
148         def FCCSetupChanged(self):
149                 fcc_changed = False
150
151                 newFccSetupActivate = checkSupportFCC() and config.plugins.fccsetup.activate.value
152                 if self.fccSetupActivate != newFccSetupActivate:
153                         self.fccSetupActivate = newFccSetupActivate
154                         self.setProcFCC(self.fccSetupActivate)
155                         fcc_changed = True
156
157                 if int(config.plugins.fccsetup.maxfcc.value) != self.maxFCC:
158                         self.maxFCC = int(config.plugins.fccsetup.maxfcc.value)
159                         fcc_changed = True
160
161                 if self.zapdownEnable != config.plugins.fccsetup.zapupdown.value:
162                         self.zapdownEnable = config.plugins.fccsetup.zapupdown.value
163                         fcc_changed = True
164
165                 if self.historyEnable != config.plugins.fccsetup.history.value:
166                         self.historyEnable = config.plugins.fccsetup.history.value
167                         fcc_changed = True
168
169                 if self.priority != config.plugins.fccsetup.priority.value:
170                         self.priority = config.plugins.fccsetup.priority.value
171                         fcc_changed = True
172
173                 if self.disableforrec != config.plugins.fccsetup.disableforrec.value:
174                         self.disableforrec = config.plugins.fccsetup.disableforrec.value
175                         fcc_changed = True
176
177                 self.getRecordings()
178                 self.changeEventTracker()
179
180                 if (not self.fccSetupActivate) or (self.disableforrec and self.recordings):
181                         self.FCCDisableServices()
182
183                 if fcc_changed:
184                         self.fccmgr.setFCCEnable(int(self.fccSetupActivate))
185                         curPlaying = self.session.nav.getCurrentlyPlayingServiceReference()
186                         if curPlaying:
187                                 self.session.nav.stopService()
188                                 self.session.nav.playService(curPlaying)
189
190         # get current recording state
191         def getRecordings(self):
192                 self.recordings = bool(self.session.nav.getRecordings())
193
194         def addRecordEventCallback(self, enable=True):
195                 if enable:
196                         if self.gotRecordEvent not in self.session.nav.record_event:
197                                 self.session.nav.record_event.append(self.gotRecordEvent)
198                 else:
199                         if self.gotRecordEvent in self.session.nav.record_event:
200                                 self.session.nav.record_event.remove(self.gotRecordEvent)
201
202         def changeEventTracker(self):
203                 if self.fccSetupActivate:
204                         self.addRecordEventCallback(True)
205                         if self.disableforrec and self.recordings:
206                                 self.enableEventTracker(False)
207                         else:
208                                 self.enableEventTracker(True)
209                 else:
210                         self.addRecordEventCallback(False)
211                         self.enableEventTracker(False)
212
213         def enableEventTracker(self, activate):
214                 if activate:
215                         if not self.__event_tracker:
216                                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
217                                 {
218                                         iPlayableService.evStart: self.getEvStart,
219                                         iPlayableService.evEnd: self.getEvEnd,
220                                         iPlayableService.evTunedIn: self.getEvTunedIn,
221                                         iPlayableService.evTuneFailed: self.getEvTuneFailed
222                                         })
223
224                 elif self.__event_tracker:
225                         # run ServiceEventTracker.__del_event()
226                         for x in self.onClose:
227                                 x()
228
229                         self.onClose = []
230                         self.__event_tracker = None
231
232         def getEvStart(self):
233                 self.createListTimer.start(0,True)
234
235         def getEvEnd(self):
236                 self.FCCDisableServices()
237
238         def getEvTunedIn(self):
239                 self.FCCTryStart()
240
241         def getEvTuneFailed(self):
242                 self.FCCTryStart()
243
244         def isPlayableFCC(self, sref):
245                 playable = True
246                 if isinstance(sref, str):
247                         sref = eServiceReference(sref)
248
249                 if sref.type != 1:
250                         playable = False
251
252                 elif sref.getPath(): # is PVR? or streaming?
253                         playable = False
254
255                 elif int(sref.getData(0)) in (2, 10): # is RADIO?
256                         playable = False
257
258                 return playable
259
260         def getZapUpDownList(self):
261                 fccZapUpDownList = []
262                 serviceList = InfoBar.instance.servicelist.servicelist.getList()
263                 curServiceRef = InfoBar.instance.servicelist.servicelist.getCurrent().toString()
264
265                 serviceRefList = []
266                 for idx in range(len(serviceList)):
267                         sref = serviceList[idx].toString()
268                         if (sref.split(':')[1] == '0') and self.isPlayableFCC(sref) : # remove marker
269                                 serviceRefList.append(sref)
270
271                 if curServiceRef in serviceRefList:
272                         serviceRefListSize = len(serviceRefList)
273                         curServiceIndex = serviceRefList.index(curServiceRef)
274
275                         for x in range(self.maxFCC-1):
276                                 if x > (serviceRefListSize-2): # if not ((x+1) <= (serviceRefListSize-1))
277                                         break
278
279                                 idx = (x / 2) + 1
280                                 if x % 2:
281                                         idx *= -1 # idx : [ 1, -1, 2, -2, 3, -3, 4, -4 ....]
282                                 idx = (curServiceIndex+idx) % serviceRefListSize # calc wraparound
283                                 try:
284                                         fccZapUpDownList.append(serviceRefList[idx])
285                                 except:
286                                         print "[FCCCreateList] append error, idx : %d" % idx
287                                         break
288
289                 return fccZapUpDownList
290
291         def getHistoryPrevNextList(self):
292                 historyList = []
293                 history = InfoBar.instance.servicelist.history[:]
294                 history_pos = InfoBar.instance.servicelist.history_pos
295                 history_len = len(history)
296
297                 if history_len > 1 and history_pos > 0:
298                         historyPrev = history[history_pos-1][:][-1].toString()
299                         if self.isPlayableFCC(historyPrev):
300                                 historyList.append(historyPrev)
301
302                 if history_len > 1 and history_pos < (history_len-1):
303                         historyNext = history[history_pos+1][:][-1].toString()
304                         if self.isPlayableFCC(historyNext):
305                                 historyList.append(historyNext)
306
307                 return historyList
308
309         def FCCCreateList(self):
310                 if (not self.fccSetupActivate) or (self.disableforrec and self.recordings):
311                         return
312
313                 if InfoBar.instance:
314                         self.fccList = []
315                         fccZapUpDownList = []
316                         historyList = []
317
318                         if self.zapdownEnable:
319                                 fccZapUpDownList = self.getZapUpDownList()
320
321                         if self.historyEnable:
322                                 historyList = self.getHistoryPrevNextList()
323
324                         if self.priority == "zapupdown":
325                                 fccZapDownLen = len(fccZapUpDownList)
326                                 if fccZapDownLen:
327                                         size = fccZapDownLen > 2 and 2 or fccZapDownLen
328                                         self.fccList = fccZapUpDownList[:size]
329                                         fccZapUpDownList = fccZapUpDownList[size:]
330
331                                 self.addFCCList(historyList)
332                                 self.addFCCList(fccZapUpDownList)
333                         else:
334                                 self.addFCCList(historyList)
335                                 self.addFCCList(fccZapUpDownList)
336
337                         self.FCCReconfigureFccList()
338
339         def addFCCList(self, newlist):
340                 fccListMaxLen = self.maxFCC-1
341                 for sref in newlist:
342                         if len(self.fccList) >= fccListMaxLen:
343                                 break
344
345                         if sref not in self.fccList:
346                                 self.fccList.append(sref)
347
348         def FCCReconfigureFccList(self):
349                 stopFCCList = []
350                 currentFCCList = self.fccmgr.getFCCServiceList()
351
352                 for (sref, value) in currentFCCList.items():
353                         state = value[0]
354
355                         if state == 2: # fcc_state_failed
356                                 stopFCCList.append(sref)
357
358                         elif sref in self.fccList: # check conflict FCC channel (decoder/prepare)
359                                 self.fccList.remove(sref)
360
361                         elif state == 0: # fcc_state_preparing
362                                 stopFCCList.append(sref)
363
364                 for sref in stopFCCList:
365                         self.fccmgr.stopFCCService(eServiceReference(sref))
366
367         def FCCTryStart(self):
368                 self.getSrefTimer.start(0, True)
369
370         def FCCGetCurSref(self):
371                 if (not self.fccSetupActivate) or (self.disableforrec and self.recordings):
372                         return
373
374                 if self.createListTimer.isActive():
375                         self.createListTimer.stop()
376                         self.FCCCreateList()
377
378                 curSref = self.session.nav.getCurrentlyPlayingServiceReference()
379
380                 if curSref and self.isPlayableFCC(curSref):
381                         self.FCCStart()
382                 else:
383                         print "[FCCSupport][FCCGetCurSref] get current serviceReference failed!!"
384
385         def FCCStart(self):
386                 self.activating = True
387                 self.FCCGetEvent(iPlayableService.evTunedIn)
388
389         def FCCGetEvent(self, event):
390                 if self.activating and event in (iPlayableService.evTunedIn, iPlayableService.evTuneFailed, iPlayableService.evFccFailed, self.fccTimeoutEventCode):
391                         self.eventList.append(event)
392                         self.fccEventTimer.start(0, True)
393
394         def FCCApplyEvent(self):
395                 if not self.activating:
396                         return
397
398                 while self.eventList:
399                         event = self.eventList.pop(0)
400
401                         self.FCCTimeoutTimerStop()
402
403                         if event in (iPlayableService.evTuneFailed, iPlayableService.evFccFailed):
404                                 self.fccmgr.stopFCCService() # stop FCC Services in failed state
405
406                         if not self.FCCCheckAndTimerStart() and len(self.fccList):
407                                 sref = self.fccList.pop(0)
408                                 if self.isPlayableFCC(sref): # remove PVR, streaming, radio channels
409                                         self.fccmgr.playFCCService(eServiceReference(sref))
410                                         self.FCCTimeoutTimerStart(sref)
411
412         def FCCStopAllServices(self):
413                 self.FCCTimeoutTimerStop()
414                 fccServiceList = self.fccmgr.getFCCServiceList()
415                 for (sref, value) in fccServiceList.items():
416                         state = value[0]
417                         if state != 1 : # 1  : fcc_state_decoding
418                                 self.fccmgr.stopFCCService(eServiceReference(sref))
419
420         def FCCDisableServices(self):
421                 self.FCCTimeoutTimerStop()
422                 self.getSrefTimer.stop()
423                 self.activating = False
424                 self.fccList = []
425
426                 self.fccEventTimer.stop()
427                 self.fccmgr.stopFCCService()
428                 self.eventList = []
429
430         def FCCCheckNoLocked(self):
431                 for (sref, value) in self.fccmgr.getFCCServiceList().items():
432                         state = value[0]
433                         locked = value[1]
434                         if state != 1 and locked == 0: # no fcc decoding and no locked
435                                 return sref
436                 return None
437
438         def FCCTimeout(self):
439                 sref = self.FCCCheckNoLocked()
440                 if sref and sref == self.fccTimeoutWait:
441                         self.fccmgr.stopFCCService(eServiceReference(sref))
442                         self.FCCGetEvent(self.fccTimeoutEventCode)
443
444         def FCCCheckAndTimerStart(self):
445                 sref = self.FCCCheckNoLocked()
446                 if sref:
447                         self.FCCTimeoutTimerStart(sref)
448                         return True
449                 return False
450
451         def FCCTimeoutTimerStart(self, sref):
452                 self.fccTimeoutWait = sref
453                 self.fccTimeoutTimer.start(5000, True)
454
455         def FCCTimeoutTimerStop(self):
456                 self.fccTimeoutWait = None
457                 self.fccTimeoutTimer.stop()
458
459 class FCCSetup(Screen, ConfigListScreen):
460         skin =  """
461                 <screen position="center,center" size="590,320" >
462                         <ePixmap pixmap="skin_default/buttons/red.png" position="90,15" size="140,40" alphatest="on" />
463                         <ePixmap pixmap="skin_default/buttons/green.png" position="360,15" size="140,40" alphatest="on" />
464                         <widget source="key_red" render="Label" position="90,15" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" foregroundColor="#ffffff" transparent="1" />
465                         <widget source="key_green" render="Label" position="360,15" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" foregroundColor="#ffffff" transparent="1" />
466                         <widget name="config" zPosition="2" position="15,80" size="560,140" scrollbarMode="showOnDemand" transparent="1" />
467                         <widget source="description" render="Label" position="30,240" size="530,60" font="Regular;24" halign="center" valign="center" />
468                 </screen>
469                 """
470
471         def __init__(self,session):
472                 Screen.__init__(self,session)
473                 self.title = _("Fast Channel Change Setup")
474                 self.session = session
475                 self["shortcuts"] = ActionMap(["ShortcutActions", "SetupActions" ],
476                 {
477                         "ok": self.keySave,
478                         "cancel": self.keyCancel,
479                         "red": self.keyCancel,
480                         "green": self.keySave,
481                 }, -2)
482                 self.list = []
483                 ConfigListScreen.__init__(self, self.list,session = self.session)
484                 self["key_red"] = StaticText(_("Cancel"))
485                 self["key_green"] = StaticText(_("Save"))
486
487                 self.isSupport = checkSupportFCC()
488
489                 if self.isSupport:
490                         self["description"] = StaticText("")
491                         self.createConfig()
492                         self.createSetup()
493                 else:
494                         self["description"] = StaticText(_("Box or driver is not support FCC."))
495
496         def createConfig(self):
497                 self.enableEntry = getConfigListEntry(_("FCC enabled"), config.plugins.fccsetup.activate)
498                 self.fccmaxEntry = getConfigListEntry(_("Max Channels"), config.plugins.fccsetup.maxfcc)
499                 self.zapupdownEntry = getConfigListEntry(_("Zap Up/Down"), config.plugins.fccsetup.zapupdown)
500                 self.historyEntry = getConfigListEntry(_("History Prev/Next"), config.plugins.fccsetup.history)
501                 self.priorityEntry = getConfigListEntry(_("priority"), config.plugins.fccsetup.priority)
502                 self.recEntry = getConfigListEntry(_("Disable FCC during recordings"), config.plugins.fccsetup.disableforrec)
503
504         def createSetup(self):
505                 self.list = []
506                 self.list.append( self.enableEntry )
507                 if self.enableEntry[1].value:
508                         self.list.append( self.fccmaxEntry )
509                         self.list.append( self.zapupdownEntry )
510                         self.list.append( self.historyEntry )
511                         if self.zapupdownEntry[1].value and self.historyEntry[1].value:
512                                 self.list.append( self.priorityEntry )
513                         self.list.append(self.recEntry)
514
515                 self["config"].list = self.list
516                 self["config"].l.setList(self.list)
517
518         def keyLeft(self):
519                 ConfigListScreen.keyLeft(self)
520                 self.setupChanged()
521
522         def keyRight(self):
523                 ConfigListScreen.keyRight(self)
524                 self.setupChanged()
525
526         def setupChanged(self):
527                 currentEntry = self["config"].getCurrent()
528                 if currentEntry in (self.zapupdownEntry, self.historyEntry, self.enableEntry):
529                         if not (self.zapupdownEntry[1].value or self.historyEntry[1].value):
530                                 if currentEntry == self.historyEntry:
531                                         self.zapupdownEntry[1].value = True
532                                 else:
533                                         self.historyEntry[1].value = True
534                         elif self.zapupdownEntry[1].value and self.historyEntry[1].value:
535                                 if int(self.fccmaxEntry[1].value) < 5:
536                                         if g_max_fcc < 5:
537                                                 self.fccmaxEntry[1].value = str(g_max_fcc)
538                                         else:
539                                                 self.fccmaxEntry[1].value = str(5)
540
541                         self.createSetup()
542
543         def keySave(self):
544                 if not self.isSupport:
545                         self.keyCancel()
546                         return
547
548                 ConfigListScreen.keySave(self)
549                 FCCChanged()
550
551 def getExtensionName():
552         if config.plugins.fccsetup.activate.value:
553                 return _("Disable FastChannelChange")
554
555         return _("Enable FastChannelChange")
556
557 def ToggleUpdate():
558         if config.plugins.fccsetup.activate.value:
559                 config.plugins.fccsetup.activate.value = False
560         else:
561                 config.plugins.fccsetup.activate.value = True
562         config.plugins.fccsetup.activate.save()
563         FCCChanged()
564
565 def FCCSupportInit(reason, **kwargs):
566         if kwargs.has_key("session"):
567                 global FccInstance
568                 FccInstance = FCCSupport(kwargs["session"])
569
570 def showFCCExtentionMenu():
571         currentScreenName = None
572         if FccInstance:
573                 currentScreenName = FccInstance.session.current_dialog.__class__.__name__
574         return (currentScreenName == "InfoBar")
575
576 def addExtentions(infobarExtensions):
577         infobarExtensions.addExtension((getExtensionName, ToggleUpdate, showFCCExtentionMenu), None)
578
579 def main(session, **kwargs):
580         session.open(FCCSetup)
581
582 def Plugins(**kwargs):
583         list = []
584
585         global g_max_fcc
586         if g_max_fcc:
587                 list.append(
588                         PluginDescriptor(name="FCCSupport",
589                         description="Fast Channel Change Support",
590                         where = [PluginDescriptor.WHERE_SESSIONSTART],
591                         fnc = FCCSupportInit))
592
593                 list.append(
594                         PluginDescriptor(name="FCCExtensionMenu",
595                         description="Fast Channel Change Extension Menu",
596                         where = [PluginDescriptor.WHERE_EXTENSIONSINGLE],
597                         fnc = addExtentions))
598
599         list.append(
600                 PluginDescriptor(name=_("FCCSetup"),
601                 description=_("Fast Channel Change Setup"),
602                 where = [PluginDescriptor.WHERE_PLUGINMENU],
603                 needsRestart = False,
604                 fnc = main))
605
606         return list
607