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