1 from ChannelSelection import ChannelSelection, BouquetSelector, SilentBouquetSelector
3 from Components.ActionMap import ActionMap, HelpableActionMap
4 from Components.ActionMap import NumberActionMap
5 from Components.Harddisk import harddiskmanager
6 from Components.Input import Input
7 from Components.Label import Label
8 from Components.PluginComponent import plugins
9 from Components.ServiceEventTracker import ServiceEventTracker
10 from Components.Sources.Boolean import Boolean
11 from Components.config import config, ConfigBoolean, ConfigClock
12 from Components.SystemInfo import SystemInfo
13 from Components.UsageConfig import preferredInstantRecordPath, defaultMoviePath
14 from EpgSelection import EPGSelection
15 from Plugins.Plugin import PluginDescriptor
17 from Screen import Screen
18 from Screens.ChoiceBox import ChoiceBox
19 from Screens.Dish import Dish
20 from Screens.EventView import EventViewEPGSelect, EventViewSimple
21 from Screens.InputBox import InputBox
22 from Screens.MessageBox import MessageBox
23 from Screens.MinuteInput import MinuteInput
24 from Screens.TimerSelection import TimerSelection
25 from Screens.PictureInPicture import PictureInPicture
26 from Screens.SubtitleDisplay import SubtitleDisplay
27 from Screens.RdsDisplay import RdsInfoDisplay, RassInteractive
28 from Screens.TimeDateInput import TimeDateInput
29 from Screens.UnhandledKey import UnhandledKey
30 from ServiceReference import ServiceReference
32 from Tools import Notifications
33 from Tools.Directories import fileExists
35 from enigma import eTimer, eServiceCenter, eDVBServicePMTHandler, iServiceInformation, \
36 iPlayableService, eServiceReference, eEPGCache, eActionMap
38 from time import time, localtime, strftime
39 from os import stat as os_stat, system as os_system
40 from bisect import insort
42 from RecordTimer import RecordTimerEntry, RecordTimer, findSafeRecordPath
45 from Menu import MainMenu, mdom
47 def isStandardInfoBar(self):
48 return self.__class__.__name__ == "InfoBar"
52 self.dishDialog = self.session.instantiateDialog(Dish)
53 self.dishDialog.setAnimationMode(0)
55 class InfoBarUnhandledKey:
57 self.unhandledKeyDialog = self.session.instantiateDialog(UnhandledKey)
58 self.unhandledKeyDialog.setAnimationMode(0)
59 self.hideUnhandledKeySymbolTimer = eTimer()
60 self.hideUnhandledKeySymbolTimer.callback.append(self.unhandledKeyDialog.hide)
61 self.checkUnusedTimer = eTimer()
62 self.checkUnusedTimer.callback.append(self.checkUnused)
63 self.onLayoutFinish.append(self.unhandledKeyDialog.hide)
64 eActionMap.getInstance().bindAction('', -0x7FFFFFFF, self.actionA) #highest prio
65 eActionMap.getInstance().bindAction('', 0x7FFFFFFF, self.actionB) #lowest prio
69 #this function is called on every keypress!
70 def actionA(self, key, flag):
72 if self.flags & (1<<1):
73 self.flags = self.uflags = 0
74 self.flags |= (1<<flag)
76 self.checkUnusedTimer.start(0, True)
79 #this function is only called when no other action has handled this key
80 def actionB(self, key, flag):
82 self.uflags |= (1<<flag)
84 def checkUnused(self):
85 if self.flags == self.uflags:
86 self.unhandledKeyDialog.show()
87 self.hideUnhandledKeySymbolTimer.start(2000, True)
89 class InfoBarShowHide:
90 """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
98 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
100 "toggleShow": self.toggleShow,
102 }, 1) # lower prio to make it possible to override ok and cancel..
104 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
106 iPlayableService.evStart: self.serviceStarted,
109 self.__state = self.STATE_SHOWN
112 self.hideTimer = eTimer()
113 self.hideTimer.callback.append(self.doTimerHide)
114 self.hideTimer.start(5000, True)
116 self.onShow.append(self.__onShow)
117 self.onHide.append(self.__onHide)
119 def serviceStarted(self):
121 if config.usage.show_infobar_on_zap.value:
125 self.__state = self.STATE_SHOWN
126 self.startHideTimer()
128 def startHideTimer(self):
129 if self.__state == self.STATE_SHOWN and not self.__locked:
130 idx = config.usage.infobar_timeout.index
132 self.hideTimer.start(idx*1000, True)
135 self.__state = self.STATE_HIDDEN
139 self.startHideTimer()
141 def doTimerHide(self):
142 self.hideTimer.stop()
143 if self.__state == self.STATE_SHOWN:
146 def toggleShow(self):
147 if self.__state == self.STATE_SHOWN:
149 self.hideTimer.stop()
150 elif self.__state == self.STATE_HIDDEN:
154 self.__locked = self.__locked + 1
157 self.hideTimer.stop()
159 def unlockShow(self):
160 self.__locked = self.__locked - 1
162 self.startHideTimer()
164 # def startShow(self):
165 # self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
166 # self.__state = self.STATE_SHOWN
168 # def startHide(self):
169 # self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
170 # self.__state = self.STATE_HIDDEN
172 class NumberZap(Screen):
179 self.close(int(self["number"].getText()))
181 def keyNumberGlobal(self, number):
182 self.Timer.start(3000, True) #reset timer
183 self.field = self.field + str(number)
184 self["number"].setText(self.field)
185 if len(self.field) >= 4:
188 def __init__(self, session, number):
189 Screen.__init__(self, session)
190 self.field = str(number)
192 self["channel"] = Label(_("Channel:"))
194 self["number"] = Label(self.field)
196 self["actions"] = NumberActionMap( [ "SetupActions" ],
200 "1": self.keyNumberGlobal,
201 "2": self.keyNumberGlobal,
202 "3": self.keyNumberGlobal,
203 "4": self.keyNumberGlobal,
204 "5": self.keyNumberGlobal,
205 "6": self.keyNumberGlobal,
206 "7": self.keyNumberGlobal,
207 "8": self.keyNumberGlobal,
208 "9": self.keyNumberGlobal,
209 "0": self.keyNumberGlobal
212 self.Timer = eTimer()
213 self.Timer.callback.append(self.keyOK)
214 self.Timer.start(3000, True)
216 class InfoBarNumberZap:
217 """ Handles an initial number for NumberZapping """
219 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
221 "1": self.keyNumberGlobal,
222 "2": self.keyNumberGlobal,
223 "3": self.keyNumberGlobal,
224 "4": self.keyNumberGlobal,
225 "5": self.keyNumberGlobal,
226 "6": self.keyNumberGlobal,
227 "7": self.keyNumberGlobal,
228 "8": self.keyNumberGlobal,
229 "9": self.keyNumberGlobal,
230 "0": self.keyNumberGlobal,
233 def keyNumberGlobal(self, number):
234 # print "You pressed number " + str(number)
236 if isinstance(self, InfoBarPiP) and self.pipHandles0Action():
237 self.pipDoHandle0Action()
239 self.servicelist.recallPrevService()
241 if not (self.has_key("TimeshiftActions") and self.timeshift_enabled):
242 self.session.openWithCallback(self.numberEntered, NumberZap, number)
244 def numberEntered(self, retval):
245 # print self.servicelist
247 self.zapToNumber(retval)
249 def searchNumberHelper(self, serviceHandler, num, bouquet):
250 servicelist = serviceHandler.list(bouquet)
251 if not servicelist is None:
253 serviceIterator = servicelist.getNext()
254 if not serviceIterator.valid(): #check end of list
256 playable = not (serviceIterator.flags & (eServiceReference.isMarker|eServiceReference.isDirectory))
259 if not num: #found service with searched number ?
260 return serviceIterator, 0
263 def zapToNumber(self, number):
264 bouquet = self.servicelist.bouquet_root
266 serviceHandler = eServiceCenter.getInstance()
267 if not config.usage.multibouquet.value:
268 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
270 bouquetlist = serviceHandler.list(bouquet)
271 if not bouquetlist is None:
273 bouquet = bouquetlist.getNext()
274 if not bouquet.valid(): #check end of list
276 if bouquet.flags & eServiceReference.isDirectory:
277 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
278 if not service is None:
279 if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
280 self.servicelist.clearPath()
281 if self.servicelist.bouquet_root != bouquet:
282 self.servicelist.enterPath(self.servicelist.bouquet_root)
283 self.servicelist.enterPath(bouquet)
284 self.servicelist.setCurrentSelection(service) #select the service in servicelist
285 self.servicelist.zap()
287 config.misc.initialchannelselection = ConfigBoolean(default = True)
289 class InfoBarChannelSelection:
290 """ ChannelSelection - handles the channelSelection dialog and the initial
291 channelChange actions which open the channelSelection dialog """
294 self.servicelist = self.session.instantiateDialog(ChannelSelection)
296 if config.misc.initialchannelselection.value:
297 self.onShown.append(self.firstRun)
299 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
301 "switchChannelUp": (self.switchChannelUp, _("open servicelist(up)")),
302 "switchChannelDown": (self.switchChannelDown, _("open servicelist(down)")),
303 "zapUp": (self.zapUp, _("previous channel")),
304 "zapDown": (self.zapDown, _("next channel")),
305 "historyBack": (self.historyBack, _("previous channel in history")),
306 "historyNext": (self.historyNext, _("next channel in history")),
307 "openServiceList": (self.openServiceList, _("open servicelist")),
310 def showTvChannelList(self, zap=False):
311 self.servicelist.setModeTv()
313 self.servicelist.zap()
314 self.session.execDialog(self.servicelist)
316 def showRadioChannelList(self, zap=False):
317 self.servicelist.setModeRadio()
319 self.servicelist.zap()
320 self.session.execDialog(self.servicelist)
323 self.onShown.remove(self.firstRun)
324 config.misc.initialchannelselection.value = False
325 config.misc.initialchannelselection.save()
326 self.switchChannelDown()
328 def historyBack(self):
329 self.servicelist.historyBack()
331 def historyNext(self):
332 self.servicelist.historyNext()
334 def switchChannelUp(self):
335 self.servicelist.moveUp()
336 self.session.execDialog(self.servicelist)
338 def switchChannelDown(self):
339 self.servicelist.moveDown()
340 self.session.execDialog(self.servicelist)
342 def openServiceList(self):
343 self.session.execDialog(self.servicelist)
346 if self.servicelist.inBouquet():
347 prev = self.servicelist.getCurrentSelection()
349 prev = prev.toString()
351 if config.usage.quickzap_bouquet_change.value:
352 if self.servicelist.atBegin():
353 self.servicelist.prevBouquet()
354 self.servicelist.moveUp()
355 cur = self.servicelist.getCurrentSelection()
356 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
359 self.servicelist.moveUp()
360 self.servicelist.zap()
363 if self.servicelist.inBouquet():
364 prev = self.servicelist.getCurrentSelection()
366 prev = prev.toString()
368 if config.usage.quickzap_bouquet_change.value and self.servicelist.atEnd():
369 self.servicelist.nextBouquet()
371 self.servicelist.moveDown()
372 cur = self.servicelist.getCurrentSelection()
373 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
376 self.servicelist.moveDown()
377 self.servicelist.zap()
380 """ Handles a menu action, to open the (main) menu """
382 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions",
384 "mainMenu": (self.mainMenu, _("Enter main menu...")),
386 self.session.infobar = None
389 print "loading mainmenu XML..."
390 menu = mdom.getroot()
391 assert menu.tag == "menu", "root element in menu must be 'menu'!"
393 self.session.infobar = self
394 # so we can access the currently active infobar from screens opened from within the mainmenu
395 # at the moment used from the SubserviceSelection
397 self.session.openWithCallback(self.mainMenuClosed, MainMenu, menu)
399 def mainMenuClosed(self, *val):
400 self.session.infobar = None
402 class InfoBarSimpleEventView:
403 """ Opens the Eventview for now/next """
405 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
407 "showEventInfo": (self.openEventView, _("show event details")),
408 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
411 def showEventInfoWhenNotVisible(self):
418 def openEventView(self):
420 self.epglist = epglist
421 service = self.session.nav.getCurrentService()
422 ref = self.session.nav.getCurrentlyPlayingServiceReference()
423 info = service.info()
431 self.session.open(EventViewSimple, epglist[0], ServiceReference(ref), self.eventViewCallback)
433 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
434 epglist = self.epglist
437 epglist[0] = epglist[1]
441 class SimpleServicelist:
442 def __init__(self, services):
443 self.services = services
444 self.length = len(services)
447 def selectService(self, service):
453 while self.services[self.current].ref != service:
455 if self.current >= self.length:
459 def nextService(self):
462 if self.current+1 < self.length:
467 def prevService(self):
470 if self.current-1 > -1:
473 self.current = self.length - 1
475 def currentService(self):
476 if not self.length or self.current >= self.length:
478 return self.services[self.current]
481 """ EPG - Opens an EPG list when the showEPGList action fires """
483 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
485 iPlayableService.evUpdatedEventInfo: self.__evEventInfoChanged,
488 self.is_now_next = False
490 self.bouquetSel = None
491 self.eventView = None
492 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
494 "showEventInfo": (self.openEventView, _("show EPG...")),
495 "showEventInfoPlugin": (self.showEventInfoPlugins, _("list of EPG views...")),
496 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
499 def showEventInfoWhenNotVisible(self):
506 def zapToService(self, service):
507 if not service is None:
508 if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
509 self.servicelist.clearPath()
510 if self.servicelist.bouquet_root != self.epg_bouquet:
511 self.servicelist.enterPath(self.servicelist.bouquet_root)
512 self.servicelist.enterPath(self.epg_bouquet)
513 self.servicelist.setCurrentSelection(service) #select the service in servicelist
514 self.servicelist.zap()
516 def getBouquetServices(self, bouquet):
518 servicelist = eServiceCenter.getInstance().list(bouquet)
519 if not servicelist is None:
521 service = servicelist.getNext()
522 if not service.valid(): #check if end of list
524 if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker): #ignore non playable services
526 services.append(ServiceReference(service))
529 def openBouquetEPG(self, bouquet, withCallback=True):
530 services = self.getBouquetServices(bouquet)
532 self.epg_bouquet = bouquet
534 self.dlg_stack.append(self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService, None, self.changeBouquetCB))
536 self.session.open(EPGSelection, services, self.zapToService, None, self.changeBouquetCB)
538 def changeBouquetCB(self, direction, epg):
541 self.bouquetSel.down()
544 bouquet = self.bouquetSel.getCurrent()
545 services = self.getBouquetServices(bouquet)
547 self.epg_bouquet = bouquet
548 epg.setServices(services)
550 def closed(self, ret=False):
551 closedScreen = self.dlg_stack.pop()
552 if self.bouquetSel and closedScreen == self.bouquetSel:
553 self.bouquetSel = None
554 elif self.eventView and closedScreen == self.eventView:
555 self.eventView = None
557 dlgs=len(self.dlg_stack)
559 self.dlg_stack[dlgs-1].close(dlgs > 1)
561 def openMultiServiceEPG(self, withCallback=True):
562 bouquets = self.servicelist.getBouquetList()
567 if config.usage.multiepg_ask_bouquet.value:
568 self.openMultiServiceEPGAskBouquet(bouquets, cnt, withCallback)
570 self.openMultiServiceEPGSilent(bouquets, cnt, withCallback)
572 def openMultiServiceEPGAskBouquet(self, bouquets, cnt, withCallback):
573 if cnt > 1: # show bouquet list
575 self.bouquetSel = self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
576 self.dlg_stack.append(self.bouquetSel)
578 self.bouquetSel = self.session.open(BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
580 self.openBouquetEPG(bouquets[0][1], withCallback)
582 def openMultiServiceEPGSilent(self, bouquets, cnt, withCallback):
583 root = self.servicelist.getRoot()
584 rootstr = root.toCompareString()
586 for bouquet in bouquets:
587 if bouquet[1].toCompareString() == rootstr:
592 if cnt > 1: # create bouquet list for bouq+/-
593 self.bouquetSel = SilentBouquetSelector(bouquets, True, self.servicelist.getBouquetNumOffset(root))
595 self.openBouquetEPG(root, withCallback)
597 def changeServiceCB(self, direction, epg):
600 self.serviceSel.nextService()
602 self.serviceSel.prevService()
603 epg.setService(self.serviceSel.currentService())
605 def SingleServiceEPGClosed(self, ret=False):
606 self.serviceSel = None
608 def openSingleServiceEPG(self):
609 ref=self.session.nav.getCurrentlyPlayingServiceReference()
611 if self.servicelist.getMutableList() is not None: # bouquet in channellist
612 current_path = self.servicelist.getRoot()
613 services = self.getBouquetServices(current_path)
614 self.serviceSel = SimpleServicelist(services)
615 if self.serviceSel.selectService(ref):
616 self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref, serviceChangeCB = self.changeServiceCB)
618 self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref)
620 self.session.open(EPGSelection, ref)
622 def showEventInfoPlugins(self):
623 list = [(p.name, boundFunction(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EVENTINFO)]
626 list.append((_("show single service EPG..."), self.openSingleServiceEPG))
627 list.append((_("Multi EPG"), self.openMultiServiceEPG))
628 self.session.openWithCallback(self.EventInfoPluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = list, skin_name = "EPGExtensionsList")
630 self.openSingleServiceEPG()
632 def runPlugin(self, plugin):
633 plugin(session = self.session, servicelist = self.servicelist)
635 def EventInfoPluginChosen(self, answer):
636 if answer is not None:
639 def openSimilarList(self, eventid, refstr):
640 self.session.open(EPGSelection, refstr, None, eventid)
642 def getNowNext(self):
644 service = self.session.nav.getCurrentService()
645 info = service and service.info()
646 ptr = info and info.getEvent(0)
649 ptr = info and info.getEvent(1)
652 self.epglist = epglist
654 def __evEventInfoChanged(self):
655 if self.is_now_next and len(self.dlg_stack) == 1:
657 assert self.eventView
659 self.eventView.setEvent(self.epglist[0])
661 def openEventView(self):
662 ref = self.session.nav.getCurrentlyPlayingServiceReference()
664 epglist = self.epglist
666 self.is_now_next = False
667 epg = eEPGCache.getInstance()
668 ptr = ref and ref.valid() and epg.lookupEventTime(ref, -1)
671 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
675 self.is_now_next = True
677 self.eventView = self.session.openWithCallback(self.closed, EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
678 self.dlg_stack.append(self.eventView)
680 print "no epg for the service avail.. so we show multiepg instead of eventinfo"
681 self.openMultiServiceEPG(False)
683 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
684 epglist = self.epglist
687 epglist[0]=epglist[1]
691 class InfoBarRdsDecoder:
692 """provides RDS and Rass support/display"""
694 self.rds_display = self.session.instantiateDialog(RdsInfoDisplay)
695 self.rds_display.setAnimationMode(0)
696 self.rass_interactive = None
698 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
700 iPlayableService.evEnd: self.__serviceStopped,
701 iPlayableService.evUpdatedRassSlidePic: self.RassSlidePicChanged
704 self["RdsActions"] = ActionMap(["InfobarRdsActions"],
706 "startRassInteractive": self.startRassInteractive
709 self["RdsActions"].setEnabled(False)
711 self.onLayoutFinish.append(self.rds_display.show)
712 self.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
714 def RassInteractivePossibilityChanged(self, state):
715 self["RdsActions"].setEnabled(state)
717 def RassSlidePicChanged(self):
718 if not self.rass_interactive:
719 service = self.session.nav.getCurrentService()
720 decoder = service and service.rdsDecoder()
722 decoder.showRassSlidePicture()
724 def __serviceStopped(self):
725 if self.rass_interactive is not None:
726 rass_interactive = self.rass_interactive
727 self.rass_interactive = None
728 rass_interactive.close()
730 def startRassInteractive(self):
731 self.rds_display.hide()
732 self.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
734 def RassInteractiveClosed(self, *val):
735 if self.rass_interactive is not None:
736 self.rass_interactive = None
737 self.RassSlidePicChanged()
738 self.rds_display.show()
741 """handles actions like seeking, pause"""
743 SEEK_STATE_PLAY = (0, 0, 0, ">")
744 SEEK_STATE_PAUSE = (1, 0, 0, "||")
745 SEEK_STATE_EOF = (1, 0, 0, "END")
747 def __init__(self, actionmap = "InfobarSeekActions"):
748 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
750 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
751 iPlayableService.evStart: self.__serviceStarted,
753 iPlayableService.evEOF: self.__evEOF,
754 iPlayableService.evSOF: self.__evSOF,
756 self.fast_winding_hint_message_showed = False
758 class InfoBarSeekActionMap(HelpableActionMap):
759 def __init__(self, screen, *args, **kwargs):
760 HelpableActionMap.__init__(self, screen, *args, **kwargs)
763 def action(self, contexts, action):
764 print "action:", action
765 if action[:5] == "seek:":
766 time = int(action[5:])
767 self.screen.doSeekRelative(time * 90000)
769 elif action[:8] == "seekdef:":
770 key = int(action[8:])
771 time = (-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
772 -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
773 -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value)[key-1]
774 self.screen.doSeekRelative(time * 90000)
777 return HelpableActionMap.action(self, contexts, action)
779 self["SeekActions"] = InfoBarSeekActionMap(self, actionmap,
781 "playpauseService": (self.playpauseService, _("Pause/Continue")),
782 "pauseService": (self.pauseService, _("pause")),
783 "unPauseService": (self.unPauseService, _("continue")),
785 "seekFwd": (self.seekFwd, _("skip forward")),
786 "seekFwdManual": (self.seekFwdManual, _("skip forward (enter time)")),
787 "seekBack": (self.seekBack, _("skip backward")),
788 "seekBackManual": (self.seekBackManual, _("skip backward (enter time)"))
790 # give them a little more priority to win over color buttons
792 self["SeekActions"].setEnabled(False)
794 self.seekstate = self.SEEK_STATE_PLAY
795 self.lastseekstate = self.SEEK_STATE_PLAY
797 self.onPlayStateChanged = [ ]
799 self.lockedBecauseOfSkipping = False
801 self.__seekableStatusChanged()
803 def makeStateForward(self, n):
804 return (0, n, 0, ">> %dx" % n)
806 def makeStateBackward(self, n):
807 return (0, -n, 0, "<< %dx" % n)
809 def makeStateSlowMotion(self, n):
810 return (0, 0, n, "/%d" % n)
812 def isStateForward(self, state):
815 def isStateBackward(self, state):
818 def isStateSlowMotion(self, state):
819 return state[1] == 0 and state[2] > 1
821 def getHigher(self, n, lst):
827 def getLower(self, n, lst):
835 def showAfterSeek(self):
836 if isinstance(self, InfoBarShowHide):
846 service = self.session.nav.getCurrentService()
850 seek = service.seek()
852 if seek is None or not seek.isCurrentlySeekable():
857 def isSeekable(self):
858 if self.getSeek() is None or (isStandardInfoBar(self) and not self.timeshift_enabled):
862 def __seekableStatusChanged(self):
863 # print "seekable status changed!"
864 if not self.isSeekable():
865 self["SeekActions"].setEnabled(False)
866 # print "not seekable, return to play"
867 self.setSeekState(self.SEEK_STATE_PLAY)
869 self["SeekActions"].setEnabled(True)
872 def __serviceStarted(self):
873 self.fast_winding_hint_message_showed = False
874 self.seekstate = self.SEEK_STATE_PLAY
875 self.__seekableStatusChanged()
877 def setSeekState(self, state):
878 service = self.session.nav.getCurrentService()
883 if not self.isSeekable():
884 if state not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE):
885 state = self.SEEK_STATE_PLAY
887 pauseable = service.pause()
889 if pauseable is None:
890 print "not pauseable."
891 state = self.SEEK_STATE_PLAY
893 self.seekstate = state
895 if pauseable is not None:
896 if self.seekstate[0]:
897 print "resolved to PAUSE"
899 elif self.seekstate[1]:
900 print "resolved to FAST FORWARD"
901 pauseable.setFastForward(self.seekstate[1])
902 elif self.seekstate[2]:
903 print "resolved to SLOW MOTION"
904 pauseable.setSlowMotion(self.seekstate[2])
906 print "resolved to PLAY"
909 for c in self.onPlayStateChanged:
912 self.checkSkipShowHideLock()
916 def playpauseService(self):
917 if self.seekstate != self.SEEK_STATE_PLAY:
918 self.unPauseService()
922 def pauseService(self):
923 if self.seekstate == self.SEEK_STATE_PAUSE:
924 if config.seek.on_pause.value == "play":
925 self.unPauseService()
926 elif config.seek.on_pause.value == "step":
927 self.doSeekRelative(1)
928 elif config.seek.on_pause.value == "last":
929 self.setSeekState(self.lastseekstate)
930 self.lastseekstate = self.SEEK_STATE_PLAY
932 if self.seekstate != self.SEEK_STATE_EOF:
933 self.lastseekstate = self.seekstate
934 self.setSeekState(self.SEEK_STATE_PAUSE);
936 def unPauseService(self):
938 if self.seekstate == self.SEEK_STATE_PLAY:
940 self.setSeekState(self.SEEK_STATE_PLAY)
942 def doSeek(self, pts):
943 seekable = self.getSeek()
948 def doSeekRelative(self, pts):
949 seekable = self.getSeek()
952 prevstate = self.seekstate
954 if self.seekstate == self.SEEK_STATE_EOF:
955 if prevstate == self.SEEK_STATE_PAUSE:
956 self.setSeekState(self.SEEK_STATE_PAUSE)
958 self.setSeekState(self.SEEK_STATE_PLAY)
959 seekable.seekRelative(pts<0 and -1 or 1, abs(pts))
960 if abs(pts) > 100 and config.usage.show_infobar_on_skip.value:
964 seek = self.getSeek()
965 if seek and not (seek.isCurrentlySeekable() & 2):
966 if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
967 self.session.open(MessageBox, _("No fast winding possible yet.. but you can use the number buttons to skip forward/backward!"), MessageBox.TYPE_INFO, timeout=10)
968 self.fast_winding_hint_message_showed = True
970 return 0 # trade as unhandled action
971 if self.seekstate == self.SEEK_STATE_PLAY:
972 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
973 elif self.seekstate == self.SEEK_STATE_PAUSE:
974 if len(config.seek.speeds_slowmotion.value):
975 self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
977 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
978 elif self.seekstate == self.SEEK_STATE_EOF:
980 elif self.isStateForward(self.seekstate):
981 speed = self.seekstate[1]
982 if self.seekstate[2]:
983 speed /= self.seekstate[2]
984 speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
985 self.setSeekState(self.makeStateForward(speed))
986 elif self.isStateBackward(self.seekstate):
987 speed = -self.seekstate[1]
988 if self.seekstate[2]:
989 speed /= self.seekstate[2]
990 speed = self.getLower(speed, config.seek.speeds_backward.value)
992 self.setSeekState(self.makeStateBackward(speed))
994 self.setSeekState(self.SEEK_STATE_PLAY)
995 elif self.isStateSlowMotion(self.seekstate):
996 speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
997 self.setSeekState(self.makeStateSlowMotion(speed))
1000 seek = self.getSeek()
1001 if seek and not (seek.isCurrentlySeekable() & 2):
1002 if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
1003 self.session.open(MessageBox, _("No fast winding possible yet.. but you can use the number buttons to skip forward/backward!"), MessageBox.TYPE_INFO, timeout=10)
1004 self.fast_winding_hint_message_showed = True
1006 return 0 # trade as unhandled action
1007 seekstate = self.seekstate
1008 if seekstate == self.SEEK_STATE_PLAY:
1009 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1010 elif seekstate == self.SEEK_STATE_EOF:
1011 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1012 self.doSeekRelative(-6)
1013 elif seekstate == self.SEEK_STATE_PAUSE:
1014 self.doSeekRelative(-1)
1015 elif self.isStateForward(seekstate):
1016 speed = seekstate[1]
1018 speed /= seekstate[2]
1019 speed = self.getLower(speed, config.seek.speeds_forward.value)
1021 self.setSeekState(self.makeStateForward(speed))
1023 self.setSeekState(self.SEEK_STATE_PLAY)
1024 elif self.isStateBackward(seekstate):
1025 speed = -seekstate[1]
1027 speed /= seekstate[2]
1028 speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
1029 self.setSeekState(self.makeStateBackward(speed))
1030 elif self.isStateSlowMotion(seekstate):
1031 speed = self.getHigher(seekstate[2], config.seek.speeds_slowmotion.value)
1033 self.setSeekState(self.makeStateSlowMotion(speed))
1035 self.setSeekState(self.SEEK_STATE_PAUSE)
1037 def seekFwdManual(self):
1038 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
1040 def fwdSeekTo(self, minutes):
1041 print "Seek", minutes, "minutes forward"
1042 self.doSeekRelative(minutes * 60 * 90000)
1044 def seekBackManual(self):
1045 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
1047 def rwdSeekTo(self, minutes):
1049 self.doSeekRelative(-minutes * 60 * 90000)
1051 def checkSkipShowHideLock(self):
1052 wantlock = self.seekstate != self.SEEK_STATE_PLAY
1054 if config.usage.show_infobar_on_skip.value:
1055 if self.lockedBecauseOfSkipping and not wantlock:
1057 self.lockedBecauseOfSkipping = False
1059 if wantlock and not self.lockedBecauseOfSkipping:
1061 self.lockedBecauseOfSkipping = True
1063 def calcRemainingTime(self):
1064 seekable = self.getSeek()
1065 if seekable is not None:
1066 len = seekable.getLength()
1068 tmp = self.cueGetEndCutPosition()
1073 pos = seekable.getPlayPosition()
1074 speednom = self.seekstate[1] or 1
1075 speedden = self.seekstate[2] or 1
1076 if not len[0] and not pos[0]:
1077 if len[1] <= pos[1]:
1079 time = (len[1] - pos[1])*speedden/(90*speednom)
1084 if self.seekstate == self.SEEK_STATE_EOF:
1087 # if we are seeking forward, we try to end up ~1s before the end, and pause there.
1088 seekstate = self.seekstate
1089 if self.seekstate != self.SEEK_STATE_PAUSE:
1090 self.setSeekState(self.SEEK_STATE_EOF)
1092 if seekstate not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE): # if we are seeking
1093 seekable = self.getSeek()
1094 if seekable is not None:
1096 if seekstate == self.SEEK_STATE_PLAY: # regular EOF
1097 self.doEofInternal(True)
1099 self.doEofInternal(False)
1101 def doEofInternal(self, playing):
1102 pass # Defined in subclasses
1105 self.setSeekState(self.SEEK_STATE_PLAY)
1108 from Screens.PVRState import PVRState, TimeshiftState
1110 class InfoBarPVRState:
1111 def __init__(self, screen=PVRState, force_show = False):
1112 self.onPlayStateChanged.append(self.__playStateChanged)
1113 self.pvrStateDialog = self.session.instantiateDialog(screen)
1114 self.pvrStateDialog.setAnimationMode(0)
1115 self.onShow.append(self._mayShow)
1116 self.onHide.append(self.pvrStateDialog.hide)
1117 self.force_show = force_show
1120 if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
1121 self.pvrStateDialog.show()
1123 def __playStateChanged(self, state):
1124 playstateString = state[3]
1125 self.pvrStateDialog["state"].setText(playstateString)
1127 # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
1128 if not config.usage.show_infobar_on_skip.value and self.seekstate == self.SEEK_STATE_PLAY and not self.force_show:
1129 self.pvrStateDialog.hide()
1133 class InfoBarTimeshiftState(InfoBarPVRState):
1135 InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True)
1136 self.__hideTimer = eTimer()
1137 self.__hideTimer.callback.append(self.__hideTimeshiftState)
1140 if self.execing and self.timeshift_enabled:
1141 self.pvrStateDialog.show()
1142 if self.seekstate == self.SEEK_STATE_PLAY and not self.shown:
1143 self.__hideTimer.start(5*1000, True)
1145 def __hideTimeshiftState(self):
1146 self.pvrStateDialog.hide()
1148 class InfoBarShowMovies:
1150 # i don't really like this class.
1151 # it calls a not further specified "movie list" on up/down/movieList,
1152 # so this is not more than an action map
1154 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
1156 "movieList": (self.showMovies, _("movie list")),
1157 "up": (self.showMovies, _("movie list")),
1158 "down": (self.showMovies, _("movie list"))
1161 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
1165 # Timeshift works the following way:
1166 # demux0 demux1 "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
1167 # - normal playback TUNER unused PLAY enable disable disable
1168 # - user presses "yellow" button. FILE record PAUSE enable disable enable
1169 # - user presess pause again FILE record PLAY enable disable enable
1170 # - user fast forwards FILE record FF enable disable enable
1171 # - end of timeshift buffer reached TUNER record PLAY enable enable disable
1172 # - user backwards FILE record BACK # !! enable disable enable
1176 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1177 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1178 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1179 # - the user can now PVR around
1180 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1181 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1183 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1184 # - if the user rewinds, or press pause, timeshift will be activated again
1186 # note that a timeshift can be enabled ("recording") and
1187 # activated (currently time-shifting).
1189 class InfoBarTimeshift:
1191 if SystemInfo["PVRSupport"]:
1192 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
1194 "timeshiftStart": (self.startTimeshift, _("start timeshift")), # the "yellow key"
1195 "timeshiftStop": (self.stopTimeshift, _("stop timeshift")) # currently undefined :), probably 'TV'
1197 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1199 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1200 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause # something like "pause key"
1201 }, prio=-1) # priority over record
1203 self.timeshift_enabled = 0
1204 self.timeshift_state = 0
1205 self.ts_rewind_timer = eTimer()
1206 self.ts_rewind_timer.callback.append(self.rewindService)
1208 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1210 iPlayableService.evStart: self.__serviceStarted,
1211 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
1212 iPlayableService.evUser+3: self.__lowDiskspaceMessage
1215 def getTimeshift(self):
1216 service = self.session.nav.getCurrentService()
1217 return service and service.timeshift()
1219 def startTimeshift(self):
1220 print "enable timeshift"
1221 ts = self.getTimeshift()
1223 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
1224 print "no ts interface"
1227 if self.timeshift_enabled:
1228 print "hu, timeshift already enabled?"
1230 from Components import Harddisk
1231 if Harddisk.getMountPath(config.usage.timeshift_path.value) != '/' and \
1232 SystemInfo.get("DisableUsbRecord", True) and \
1233 Harddisk.isUsbStorage(config.usage.timeshift_path.value):
1234 self.session.open(MessageBox, _("Timeshift not possible on a USB storage."), MessageBox.TYPE_ERROR)
1237 if not ts.startTimeshift():
1238 self.timeshift_enabled = 1
1240 # we remove the "relative time" for now.
1241 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1244 #self.setSeekState(self.SEEK_STATE_PAUSE)
1245 self.activateTimeshiftEnd(False)
1247 # enable the "TimeshiftEnableActions", which will override
1248 # the startTimeshift actions
1249 self.__seekableStatusChanged()
1251 print "timeshift failed"
1253 def stopTimeshift(self):
1254 if not self.timeshift_enabled:
1256 print "disable timeshift"
1257 ts = self.getTimeshift()
1260 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
1262 def stopTimeshiftConfirmed(self, confirmed):
1266 ts = self.getTimeshift()
1271 self.timeshift_enabled = 0
1274 self.__seekableStatusChanged()
1276 # activates timeshift, and seeks to (almost) the end
1277 def activateTimeshiftEnd(self, back = True):
1278 ts = self.getTimeshift()
1279 print "activateTimeshiftEnd"
1284 if ts.isTimeshiftActive():
1285 print "!! activate timeshift called - but shouldn't this be a normal pause?"
1289 ts.activateTimeshift() # activate timeshift will automatically pause
1290 self.setSeekState(self.SEEK_STATE_PAUSE)
1293 self.ts_rewind_timer.start(200, 1)
1295 def rewindService(self):
1296 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1298 # same as activateTimeshiftEnd, but pauses afterwards.
1299 def activateTimeshiftEndAndPause(self):
1300 print "activateTimeshiftEndAndPause"
1301 #state = self.seekstate
1302 self.activateTimeshiftEnd(False)
1304 def __seekableStatusChanged(self):
1307 # print "self.isSeekable", self.isSeekable()
1308 # print "self.timeshift_enabled", self.timeshift_enabled
1310 # when this service is not seekable, but timeshift
1311 # is enabled, this means we can activate
1313 if not self.isSeekable() and self.timeshift_enabled:
1316 # print "timeshift activate:", enabled
1317 self["TimeshiftActivateActions"].setEnabled(enabled)
1319 def __serviceStarted(self):
1320 self.timeshift_enabled = False
1321 self.__seekableStatusChanged()
1323 def __lowDiskspaceMessage(self):
1324 Notifications.AddPopup(text = _("Write error. Not enough space for writing.\n"), type = MessageBox.TYPE_ERROR, timeout = 0, id = "DiskFullMessage")
1326 from Screens.PiPSetup import PiPSetup
1328 class InfoBarExtensions:
1329 EXTENSION_SINGLE = 0
1335 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1337 "extensions": (self.showExtensionSelection, _("view extensions...")),
1338 }, 1) # lower priority
1340 for p in plugins.getPlugins(PluginDescriptor.WHERE_EXTENSIONSINGLE):
1343 def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1344 self.list.append((type, extension, key))
1346 def updateExtension(self, extension, key = None):
1347 self.extensionsList.append(extension)
1349 if self.extensionKeys.has_key(key):
1353 for x in self.availableKeys:
1354 if not self.extensionKeys.has_key(x):
1359 self.extensionKeys[key] = len(self.extensionsList) - 1
1361 def updateExtensions(self):
1362 self.extensionsList = []
1363 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1364 self.extensionKeys = {}
1366 if x[0] == self.EXTENSION_SINGLE:
1367 self.updateExtension(x[1], x[2])
1370 self.updateExtension(y[0], y[1])
1373 def showExtensionSelection(self):
1374 self.updateExtensions()
1375 extensionsList = self.extensionsList[:]
1378 for x in self.availableKeys:
1379 if self.extensionKeys.has_key(x):
1380 entry = self.extensionKeys[x]
1381 extension = self.extensionsList[entry]
1383 name = str(extension[0]())
1384 list.append((extension[0](), extension))
1386 extensionsList.remove(extension)
1388 extensionsList.remove(extension)
1389 list.extend([(x[0](), x) for x in extensionsList])
1391 keys += [""] * len(extensionsList)
1392 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys, skin_name = "ExtensionsList")
1394 def extensionCallback(self, answer):
1395 if answer is not None:
1398 from Tools.BoundFunction import boundFunction
1401 # depends on InfoBarExtensions
1403 class InfoBarPlugins:
1405 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1407 def getPluginName(self, name):
1410 def getPluginList(self):
1412 for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU):
1413 args = inspect.getargspec(p.__call__)[0]
1414 if len(args) == 1 or len(args) == 2 and isinstance(self, InfoBarChannelSelection):
1415 l.append(((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name))
1416 l.sort(key = lambda e: e[2]) # sort by name
1419 def runPlugin(self, plugin):
1420 if isinstance(self, InfoBarChannelSelection):
1421 plugin(session = self.session, servicelist = self.servicelist)
1423 plugin(session = self.session)
1425 from Components.Task import job_manager
1426 class InfoBarJobman:
1428 self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
1430 def getJobList(self):
1431 return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
1433 def getJobName(self, job):
1434 return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
1436 def showJobView(self, job):
1437 from Screens.TaskView import JobView
1438 job_manager.in_background = False
1439 self.session.openWithCallback(self.JobViewCB, JobView, job)
1441 def JobViewCB(self, in_background):
1442 job_manager.in_background = in_background
1444 # depends on InfoBarExtensions
1448 self.session.pipshown
1450 self.session.pipshown = False
1451 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1453 self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
1454 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1455 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1457 self.addExtension((self.getShowHideName, self.showPiP, self.pipShown), "blue")
1458 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1461 return self.session.pipshown
1463 def pipHandles0Action(self):
1464 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1466 def getShowHideName(self):
1467 if self.session.pipshown:
1468 return _("Disable Picture in Picture")
1470 return _("Activate Picture in Picture")
1472 def getSwapName(self):
1473 return _("Swap Services")
1475 def getMoveName(self):
1476 return _("Move Picture in Picture")
1479 if self.session.pipshown:
1480 del self.session.pip
1481 self.session.pipshown = False
1483 self.session.pip = self.session.instantiateDialog(PictureInPicture)
1484 self.session.pip.setAnimationMode(0)
1485 self.session.pip.show()
1486 newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1487 if self.session.pip.playService(newservice):
1488 self.session.pipshown = True
1489 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1491 self.session.pipshown = False
1492 del self.session.pip
1493 self.session.nav.playService(newservice)
1496 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1497 if self.session.pip.servicePath:
1498 servicepath = self.servicelist.getCurrentServicePath()
1499 ref=servicepath[len(servicepath)-1]
1500 pipref=self.session.pip.getCurrentService()
1501 self.session.pip.playService(swapservice)
1502 self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1503 if pipref.toString() != ref.toString(): # is a subservice ?
1504 self.session.nav.stopService() # stop portal
1505 self.session.nav.playService(pipref) # start subservice
1506 self.session.pip.servicePath=servicepath
1509 self.session.open(PiPSetup, pip = self.session.pip)
1511 def pipDoHandle0Action(self):
1512 use = config.usage.pip_zero_button.value
1515 elif "swapstop" == use:
1521 from RecordTimer import parseEvent, RecordTimerEntry
1523 class InfoBarInstantRecord:
1524 """Instant Record - handles the instantRecord action in order to
1525 start/stop instant records"""
1527 if SystemInfo["PVRSupport"]:
1528 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1530 "instantRecord": (self.instantRecord, _("Instant Record...")),
1534 def stopCurrentRecording(self, entry = -1):
1535 if entry is not None and entry != -1:
1536 self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1537 self.recording.remove(self.recording[entry])
1539 def startInstantRecording(self, limitEvent = False):
1540 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1542 # try to get event info
1545 service = self.session.nav.getCurrentService()
1546 epg = eEPGCache.getInstance()
1547 event = epg.lookupEventTime(serviceref, -1, 0)
1549 info = service.info()
1550 ev = info.getEvent(0)
1556 end = begin + 3600 # dummy
1557 name = "instant record"
1561 if event is not None:
1562 curEvent = parseEvent(event)
1564 description = curEvent[3]
1565 eventid = curEvent[4]
1570 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1572 if isinstance(serviceref, eServiceReference):
1573 serviceref = ServiceReference(serviceref)
1575 recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = preferredInstantRecordPath())
1576 recording.dontSave = True
1578 if event is None or limitEvent == False:
1579 recording.autoincrease = True
1580 recording.setAutoincreaseEnd()
1582 simulTimerList = self.session.nav.RecordTimer.record(recording)
1584 if simulTimerList is None: # no conflict
1585 self.recording.append(recording)
1587 if len(simulTimerList) > 1: # with other recording
1588 name = simulTimerList[1].name
1589 name_date = ' '.join((name, strftime('%c', localtime(simulTimerList[1].begin))))
1590 print "[TIMER] conflicts with", name_date
1591 recording.autoincrease = True # start with max available length, then increment
1592 if recording.setAutoincreaseEnd():
1593 self.session.nav.RecordTimer.record(recording)
1594 self.recording.append(recording)
1595 self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
1597 self.session.open(MessageBox, _("Couldn't record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
1599 self.session.open(MessageBox, _("Couldn't record due to invalid service %s") % serviceref, MessageBox.TYPE_INFO)
1600 recording.autoincrease = False
1602 def isInstantRecordRunning(self):
1603 print "self.recording:", self.recording
1605 for x in self.recording:
1610 def recordQuestionCallback(self, answer):
1611 print "pre:\n", self.recording
1613 if answer is None or answer[1] == "no":
1616 recording = self.recording[:]
1618 if not x in self.session.nav.RecordTimer.timer_list:
1619 self.recording.remove(x)
1620 elif x.dontSave and x.isRunning():
1621 list.append((x, False))
1623 if answer[1] == "changeduration":
1624 if len(self.recording) == 1:
1625 self.changeDuration(0)
1627 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1628 elif answer[1] == "changeendtime":
1629 if len(self.recording) == 1:
1632 self.session.openWithCallback(self.setEndtime, TimerSelection, list)
1633 elif answer[1] == "stop":
1634 if len(self.recording) == 1:
1635 self.stopCurrentRecording(0)
1637 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1638 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1639 self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1640 if answer[1] == "manualduration":
1641 self.changeDuration(len(self.recording)-1)
1642 elif answer[1] == "manualendtime":
1643 self.setEndtime(len(self.recording)-1)
1644 print "after:\n", self.recording
1646 def setEndtime(self, entry):
1647 if entry is not None and entry >= 0:
1648 self.selectedEntry = entry
1649 self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1650 dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1651 dlg.setTitle(_("Please change recording endtime"))
1653 def TimeDateInputClosed(self, ret):
1656 localendtime = localtime(ret[1])
1657 print "stopping recording at", strftime("%c", localendtime)
1658 if self.recording[self.selectedEntry].end != ret[1]:
1659 self.recording[self.selectedEntry].autoincrease = False
1660 self.recording[self.selectedEntry].end = ret[1]
1661 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1663 def changeDuration(self, entry):
1664 if entry is not None and entry >= 0:
1665 self.selectedEntry = entry
1666 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1668 def inputCallback(self, value):
1669 if value is not None:
1670 print "stopping recording after", int(value), "minutes."
1671 entry = self.recording[self.selectedEntry]
1673 entry.autoincrease = False
1674 entry.end = int(time()) + 60 * int(value)
1675 self.session.nav.RecordTimer.timeChanged(entry)
1677 def instantRecord(self):
1678 if not fileExists("/hdd", 0):
1679 print "not found /hdd"
1680 os_system("ln -s /media/hdd /hdd")
1682 recPath = preferredInstantRecordPath()
1683 if not findSafeRecordPath(recPath) and not findSafeRecordPath(defaultMoviePath()):
1686 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1689 if self.isInstantRecordRunning():
1690 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1691 title=_("A recording is currently running.\nWhat do you want to do?"), \
1692 list=((_("stop recording"), "stop"), \
1693 (_("add recording (stop after current event)"), "event"), \
1694 (_("add recording (indefinitely)"), "indefinitely"), \
1695 (_("add recording (enter recording duration)"), "manualduration"), \
1696 (_("add recording (enter recording endtime)"), "manualendtime"), \
1697 (_("change recording (duration)"), "changeduration"), \
1698 (_("change recording (endtime)"), "changeendtime"), \
1699 (_("do nothing"), "no")))
1701 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1702 title=_("Start recording?"), \
1703 list=((_("add recording (stop after current event)"), "event"), \
1704 (_("add recording (indefinitely)"), "indefinitely"), \
1705 (_("add recording (enter recording duration)"), "manualduration"), \
1706 (_("add recording (enter recording endtime)"), "manualendtime"), \
1707 (_("don't record"), "no")))
1709 from Tools.ISO639 import LanguageCodes
1711 class InfoBarAudioSelection:
1713 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1715 "audioSelection": (self.audioSelection, _("Audio Options...")),
1718 def audioSelection(self):
1719 from Screens.AudioSelection import AudioSelection
1720 self.session.openWithCallback(self.audioSelected, AudioSelection, infobar=self)
1722 def audioSelected(self, ret=None):
1723 print "[infobar::audioSelected]", ret
1725 class InfoBarSubserviceSelection:
1727 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1729 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1732 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1734 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1735 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1737 self["SubserviceQuickzapAction"].setEnabled(False)
1739 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1741 iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
1746 def checkSubservicesAvail(self):
1747 service = self.session.nav.getCurrentService()
1748 subservices = service and service.subServices()
1749 if not subservices or subservices.getNumberOfSubservices() == 0:
1750 self["SubserviceQuickzapAction"].setEnabled(False)
1752 def nextSubservice(self):
1753 self.changeSubservice(+1)
1755 def prevSubservice(self):
1756 self.changeSubservice(-1)
1758 def changeSubservice(self, direction):
1759 service = self.session.nav.getCurrentService()
1760 subservices = service and service.subServices()
1761 n = subservices and subservices.getNumberOfSubservices()
1764 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1767 if subservices.getSubservice(idx).toString() == ref.toString():
1772 selection += direction
1777 newservice = subservices.getSubservice(selection)
1778 if newservice.valid():
1781 self.session.nav.playService(newservice, False)
1783 def subserviceSelection(self):
1784 service = self.session.nav.getCurrentService()
1785 subservices = service and service.subServices()
1786 self.bouquets = self.servicelist.getBouquetList()
1787 n = subservices and subservices.getNumberOfSubservices()
1790 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1794 i = subservices.getSubservice(idx)
1795 if i.toString() == ref.toString():
1797 tlist.append((i.getName(), i))
1800 if self.bouquets and len(self.bouquets):
1801 keys = ["red", "blue", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1802 if config.usage.multibouquet.value:
1803 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1805 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1808 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1809 keys = ["red", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1812 self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys, skin_name = "SubserviceSelection")
1814 def subserviceSelected(self, service):
1816 if not service is None:
1817 if isinstance(service[1], str):
1818 if service[1] == "quickzap":
1819 from Screens.SubservicesQuickzap import SubservicesQuickzap
1820 self.session.open(SubservicesQuickzap, service[2])
1822 self["SubserviceQuickzapAction"].setEnabled(True)
1823 self.session.nav.playService(service[1], False)
1825 def addSubserviceToBouquetCallback(self, service):
1826 if len(service) > 1 and isinstance(service[1], eServiceReference):
1827 self.selectedSubservice = service
1828 if self.bouquets is None:
1831 cnt = len(self.bouquets)
1832 if cnt > 1: # show bouquet list
1833 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1834 elif cnt == 1: # add to only one existing bouquet
1835 self.addSubserviceToBouquet(self.bouquets[0][1])
1836 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1838 def bouquetSelClosed(self, confirmed):
1840 del self.selectedSubservice
1842 self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1844 def addSubserviceToBouquet(self, dest):
1845 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1847 self.bsel.close(True)
1849 del self.selectedSubservice
1851 from Components.Sources.HbbtvApplication import HbbtvApplication
1852 gHbbtvApplication = HbbtvApplication()
1853 class InfoBarRedButton:
1855 if not (config.misc.rcused.value == 1):
1856 self["RedButtonActions"] = HelpableActionMap(self, "InfobarRedButtonActions",
1858 "activateRedButton": (self.activateRedButton, _("Red button...")),
1860 self["HbbtvApplication"] = gHbbtvApplication
1862 self["HbbtvApplication"] = Boolean(fixed=0)
1863 self["HbbtvApplication"].name = "" #is this a hack?
1865 self.onHBBTVActivation = [ ]
1866 self.onRedButtonActivation = [ ]
1867 self.onReadyForAIT = [ ]
1868 self.__et = ServiceEventTracker(screen=self, eventmap=
1870 iPlayableService.evHBBTVInfo: self.detectedHbbtvApplication,
1871 iPlayableService.evUpdatedInfo: self.updateInfomation
1874 def updateAIT(self, orgId=0):
1875 for x in self.onReadyForAIT:
1878 except Exception, ErrMsg:
1880 #self.onReadyForAIT.remove(x)
1882 def updateInfomation(self):
1884 self["HbbtvApplication"].setApplicationName("")
1886 except Exception, ErrMsg:
1889 def detectedHbbtvApplication(self):
1890 service = self.session.nav.getCurrentService()
1891 info = service and service.info()
1893 for x in info.getInfoObject(iServiceInformation.sHBBTVUrl):
1896 self.updateAIT(x[3])
1897 self["HbbtvApplication"].setApplicationName(x[1])
1899 except Exception, ErrMsg:
1902 def activateRedButton(self):
1903 service = self.session.nav.getCurrentService()
1904 info = service and service.info()
1905 if info and info.getInfoString(iServiceInformation.sHBBTVUrl) != "":
1906 for x in self.onHBBTVActivation:
1908 elif False: # TODO: other red button services
1909 for x in self.onRedButtonActivation:
1912 class InfoBarAdditionalInfo:
1915 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0 and config.misc.rcused.value == 1)
1916 self["TimeshiftPossible"] = self["RecordingPossible"]
1917 self["ShowTimeshiftOnYellow"] = Boolean(fixed=(config.misc.rcused.value == 1))
1918 self["ShowAudioOnYellow"] = Boolean(fixed=config.misc.rcused.value != 1)
1919 self["ShowRecordOnRed"] = Boolean(fixed=config.misc.rcused.value == 1)
1920 self["ExtensionsAvailable"] = Boolean(fixed=1)
1922 class InfoBarNotifications:
1924 self.onExecBegin.append(self.checkNotifications)
1925 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1926 self.onClose.append(self.__removeNotification)
1928 def __removeNotification(self):
1929 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1931 def checkNotificationsIfExecing(self):
1933 self.checkNotifications()
1935 def checkNotifications(self):
1936 notifications = Notifications.notifications
1938 n = notifications[0]
1940 del notifications[0]
1943 if n[3].has_key("onSessionOpenCallback"):
1944 n[3]["onSessionOpenCallback"]()
1945 del n[3]["onSessionOpenCallback"]
1948 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1950 dlg = self.session.open(n[1], *n[2], **n[3])
1952 # remember that this notification is currently active
1954 Notifications.current_notifications.append(d)
1955 dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1957 def __notificationClosed(self, d):
1958 Notifications.current_notifications.remove(d)
1960 class InfoBarServiceNotifications:
1962 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1964 iPlayableService.evEnd: self.serviceHasEnded
1967 def serviceHasEnded(self):
1968 print "service end!"
1971 self.setSeekState(self.SEEK_STATE_PLAY)
1975 class InfoBarCueSheetSupport:
1981 ENABLE_RESUME_SUPPORT = False
1983 def __init__(self, actionmap = "InfobarCueSheetActions"):
1984 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
1986 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to previous marked position")),
1987 "jumpNextMark": (self.jumpNextMark, _("jump to next marked position")),
1988 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
1992 self.is_closing = False
1993 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1995 iPlayableService.evStart: self.__serviceStarted,
1998 def __serviceStarted(self):
2001 print "new service started! trying to download cuts!"
2002 self.downloadCuesheet()
2004 if self.ENABLE_RESUME_SUPPORT:
2007 for (pts, what) in self.cut_list:
2008 if what == self.CUT_TYPE_LAST:
2011 if last is not None:
2012 self.resume_point = last
2015 if config.usage.on_movie_start.value == "ask":
2016 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?") + "\n" + (_("Resume position at %s") % ("%d:%02d:%02d" % (l/3600, l%3600/60, l%60))), timeout=10)
2017 elif config.usage.on_movie_start.value == "resume":
2018 # TRANSLATORS: The string "Resuming playback" flashes for a moment
2019 # TRANSLATORS: at the start of a movie, when the user has selected
2020 # TRANSLATORS: "Resume from last position" as start behavior.
2021 # TRANSLATORS: The purpose is to notify the user that the movie starts
2022 # TRANSLATORS: in the middle somewhere and not from the beginning.
2023 # TRANSLATORS: (Some translators seem to have interpreted it as a
2024 # TRANSLATORS: question or a choice, but it is a statement.)
2025 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
2027 def playLastCB(self, answer):
2029 self.doSeek(self.resume_point)
2030 self.hideAfterResume()
2032 def hideAfterResume(self):
2033 if isinstance(self, InfoBarShowHide):
2036 def __getSeekable(self):
2037 service = self.session.nav.getCurrentService()
2040 return service.seek()
2042 def cueGetCurrentPosition(self):
2043 seek = self.__getSeekable()
2046 r = seek.getPlayPosition()
2051 def cueGetEndCutPosition(self):
2054 for cp in self.cut_list:
2055 if cp[1] == self.CUT_TYPE_OUT:
2059 elif cp[1] == self.CUT_TYPE_IN:
2063 def jumpPreviousNextMark(self, cmp, start=False):
2064 current_pos = self.cueGetCurrentPosition()
2065 if current_pos is None:
2067 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
2068 if mark is not None:
2076 def jumpPreviousMark(self):
2077 # we add 5 seconds, so if the play position is <5s after
2078 # the mark, the mark before will be used
2079 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
2081 def jumpNextMark(self):
2082 if not self.jumpPreviousNextMark(lambda x: x-90000):
2085 def getNearestCutPoint(self, pts, cmp=abs, start=False):
2092 bestdiff = cmp(0 - pts)
2094 nearest = [0, False]
2095 for cp in self.cut_list:
2096 if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
2098 if cp[1] == self.CUT_TYPE_IN: # Start is here, disregard previous marks
2099 diff = cmp(cp[0] - pts)
2100 if start and diff >= 0:
2106 if cp[1] == self.CUT_TYPE_IN:
2108 elif cp[1] == self.CUT_TYPE_OUT:
2110 elif cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
2111 diff = cmp(cp[0] - pts)
2112 if instate and diff >= 0 and (nearest is None or bestdiff > diff):
2117 def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
2118 current_pos = self.cueGetCurrentPosition()
2119 if current_pos is None:
2120 print "not seekable"
2123 nearest_cutpoint = self.getNearestCutPoint(current_pos)
2125 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
2127 return nearest_cutpoint
2129 self.removeMark(nearest_cutpoint)
2130 elif not onlyremove and not onlyreturn:
2131 self.addMark((current_pos, self.CUT_TYPE_MARK))
2136 def addMark(self, point):
2137 insort(self.cut_list, point)
2138 self.uploadCuesheet()
2139 self.showAfterCuesheetOperation()
2141 def removeMark(self, point):
2142 self.cut_list.remove(point)
2143 self.uploadCuesheet()
2144 self.showAfterCuesheetOperation()
2146 def showAfterCuesheetOperation(self):
2147 if isinstance(self, InfoBarShowHide):
2150 def __getCuesheet(self):
2151 service = self.session.nav.getCurrentService()
2154 return service.cueSheet()
2156 def uploadCuesheet(self):
2157 cue = self.__getCuesheet()
2160 print "upload failed, no cuesheet interface"
2162 cue.setCutList(self.cut_list)
2164 def downloadCuesheet(self):
2165 cue = self.__getCuesheet()
2168 print "download failed, no cuesheet interface"
2171 self.cut_list = cue.getCutList()
2173 class InfoBarSummary(Screen):
2175 <screen position="0,0" size="132,64">
2176 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
2177 <convert type="ClockToText">WithSeconds</convert>
2179 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
2180 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2181 <convert type="ConditionalShowHide">Blink</convert>
2183 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2184 <convert type="ServiceName">Name</convert>
2186 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
2187 <convert type="EventTime">Progress</convert>
2191 # for picon: (path="piconlcd" will use LCD picons)
2192 # <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
2193 # <convert type="ServiceName">Reference</convert>
2196 class InfoBarSummarySupport:
2200 def createSummary(self):
2201 return InfoBarSummary
2203 class InfoBarMoviePlayerSummary(Screen):
2205 <screen position="0,0" size="132,64">
2206 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
2207 <convert type="ClockToText">WithSeconds</convert>
2209 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2210 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2211 <convert type="ConditionalShowHide">Blink</convert>
2213 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2214 <convert type="ServiceName">Name</convert>
2216 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2217 <convert type="ServicePosition">Position</convert>
2221 class InfoBarMoviePlayerSummarySupport:
2225 def createSummary(self):
2226 return InfoBarMoviePlayerSummary
2228 class InfoBarTeletextPlugin:
2230 self.teletext_plugin = None
2232 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
2233 self.teletext_plugin = p
2235 if self.teletext_plugin is not None:
2236 self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2238 "startTeletext": (self.startTeletext, _("View teletext..."))
2241 print "no teletext plugin found!"
2243 def startTeletext(self):
2244 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
2246 class InfoBarSubtitleSupport(object):
2248 object.__init__(self)
2249 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2250 self.subtitle_window.setAnimationMode(0)
2251 self.__subtitles_enabled = False
2253 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2255 iPlayableService.evEnd: self.__serviceStopped,
2256 iPlayableService.evUpdatedInfo: self.__updatedInfo
2258 self.cached_subtitle_checked = False
2259 self.__selected_subtitle = None
2261 def __serviceStopped(self):
2262 self.cached_subtitle_checked = False
2263 if self.__subtitles_enabled:
2264 self.subtitle_window.hide()
2265 self.__subtitles_enabled = False
2266 self.__selected_subtitle = None
2268 def __updatedInfo(self):
2269 if not self.__selected_subtitle:
2270 subtitle = self.getCurrentServiceSubtitle()
2271 self.setSelectedSubtitle(subtitle and subtitle.getCachedSubtitle())
2272 if self.__selected_subtitle:
2273 self.setSubtitlesEnable(True)
2275 def getCurrentServiceSubtitle(self):
2276 service = self.session.nav.getCurrentService()
2277 return service and service.subtitle()
2279 def setSubtitlesEnable(self, enable=True):
2280 subtitle = self.getCurrentServiceSubtitle()
2282 if self.__selected_subtitle:
2283 if subtitle and not self.__subtitles_enabled:
2284 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2285 self.subtitle_window.show()
2286 self.__subtitles_enabled = True
2289 subtitle.disableSubtitles(self.subtitle_window.instance)
2290 self.__selected_subtitle = None
2291 self.__subtitles_enabled = False
2292 self.subtitle_window.hide()
2294 def setSelectedSubtitle(self, subtitle):
2295 self.__selected_subtitle = subtitle
2297 subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
2298 selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
2300 class InfoBarServiceErrorPopupSupport:
2302 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2304 iPlayableService.evTuneFailed: self.__tuneFailed,
2305 iPlayableService.evStart: self.__serviceStarted
2307 self.__serviceStarted()
2309 def __serviceStarted(self):
2310 self.last_error = None
2311 Notifications.RemovePopup(id = "ZapError")
2313 def __tuneFailed(self):
2314 service = self.session.nav.getCurrentService()
2315 info = service and service.info()
2316 error = info and info.getInfo(iServiceInformation.sDVBState)
2318 if error == self.last_error:
2321 self.last_error = error
2324 eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2325 eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2326 eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2327 eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2328 eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2329 eDVBServicePMTHandler.eventNewProgramInfo: None,
2330 eDVBServicePMTHandler.eventTuned: None,
2331 eDVBServicePMTHandler.eventSOF: None,
2332 eDVBServicePMTHandler.eventEOF: None,
2333 eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2334 }.get(error) #this returns None when the key not exist in the dict
2336 if error is not None:
2337 Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
2339 Notifications.RemovePopup(id = "ZapError")
2343 self.hdmiInServiceRef = eServiceReference('8192:0:1:0:0:0:0:0:0:0:')
2345 if SystemInfo.get("HdmiInSupport", False):
2346 self.addExtension((self.getShowHdmiInName, self.HDMIIn, lambda: True), None)
2347 self.addExtension((self.getShowHdmiInPIPName, self.HDMIInPIP, self.showHDMIPIPMenu), None)
2349 def getShowHdmiInName(self):
2350 curref = self.session.nav.getCurrentlyPlayingServiceReference()
2351 if curref and curref.type == 8192:
2352 name = _("Disable HDMI-IN on Main Screen")
2354 name = _("Enable HDMI-IN on Main Screen")
2358 def getShowHdmiInPIPName(self):
2359 return _("Enable HDMI-IN on PIP")
2361 def showHDMIPIPMenu(self):
2362 _pipAvailable = SystemInfo.get("NumVideoDecoders", 1) > 1
2364 hdmiin_enabled = False
2365 curref = self.session.nav.getCurrentlyPlayingServiceReference()
2366 if curref and curref.type == 8192:
2367 hdmiin_enabled = True
2369 hdmiin_pip_shown = False
2370 if self.session.pipshown:
2371 pipref=self.session.pip.getCurrentService()
2372 if pipref and pipref.type == 8192:
2373 hdmiin_pip_shown = True
2375 return _pipAvailable and not hdmiin_enabled and not hdmiin_pip_shown
2377 def getCurrentServiceRef(self):
2378 slist = self.servicelist
2379 currentServiceSref = slist.servicelist.getCurrent()
2380 return currentServiceSref
2383 curref = self.session.nav.getCurrentlyPlayingServiceReference()
2384 if curref and curref.type == 8192:
2385 self.session.nav.playService(self.getCurrentServiceRef())
2387 self.session.nav.playService(self.hdmiInServiceRef)
2389 def HDMIInPIP(self):
2390 if self.session.pipshown:
2391 del self.session.pip
2393 self.session.pip = self.session.instantiateDialog(PictureInPicture)
2394 self.session.pip.setAnimationMode(0)
2395 self.session.pip.show()
2396 newservice = self.hdmiInServiceRef
2397 if self.session.pip.playService(newservice):
2398 self.session.pipshown = True
2399 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
2401 self.session.pipshown = False
2402 del self.session.pip
2403 self.session.openWithCallback(self.close, MessageBox, _("Could not open Picture in Picture"), MessageBox.TYPE_ERROR)