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
49 self.dishDialog = self.session.instantiateDialog(Dish)
50 self.dishDialog.setAnimationMode(0)
52 class InfoBarUnhandledKey:
54 self.unhandledKeyDialog = self.session.instantiateDialog(UnhandledKey)
55 self.unhandledKeyDialog.setAnimationMode(0)
56 self.hideUnhandledKeySymbolTimer = eTimer()
57 self.hideUnhandledKeySymbolTimer.callback.append(self.unhandledKeyDialog.hide)
58 self.checkUnusedTimer = eTimer()
59 self.checkUnusedTimer.callback.append(self.checkUnused)
60 self.onLayoutFinish.append(self.unhandledKeyDialog.hide)
61 eActionMap.getInstance().bindAction('', -0x7FFFFFFF, self.actionA) #highest prio
62 eActionMap.getInstance().bindAction('', 0x7FFFFFFF, self.actionB) #lowest prio
66 #this function is called on every keypress!
67 def actionA(self, key, flag):
69 if self.flags & (1<<1):
70 self.flags = self.uflags = 0
71 self.flags |= (1<<flag)
73 self.checkUnusedTimer.start(0, True)
76 #this function is only called when no other action has handled this key
77 def actionB(self, key, flag):
79 self.uflags |= (1<<flag)
81 def checkUnused(self):
82 if self.flags == self.uflags:
83 self.unhandledKeyDialog.show()
84 self.hideUnhandledKeySymbolTimer.start(2000, True)
86 class InfoBarShowHide:
87 """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
95 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
97 "toggleShow": self.toggleShow,
99 }, 1) # lower prio to make it possible to override ok and cancel..
101 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
103 iPlayableService.evStart: self.serviceStarted,
106 self.__state = self.STATE_SHOWN
109 self.hideTimer = eTimer()
110 self.hideTimer.callback.append(self.doTimerHide)
111 self.hideTimer.start(5000, True)
113 self.onShow.append(self.__onShow)
114 self.onHide.append(self.__onHide)
116 def serviceStarted(self):
118 if config.usage.show_infobar_on_zap.value:
122 self.__state = self.STATE_SHOWN
123 self.startHideTimer()
125 def startHideTimer(self):
126 if self.__state == self.STATE_SHOWN and not self.__locked:
127 idx = config.usage.infobar_timeout.index
129 self.hideTimer.start(idx*1000, True)
132 self.__state = self.STATE_HIDDEN
136 self.startHideTimer()
138 def doTimerHide(self):
139 self.hideTimer.stop()
140 if self.__state == self.STATE_SHOWN:
143 def toggleShow(self):
144 if self.__state == self.STATE_SHOWN:
146 self.hideTimer.stop()
147 elif self.__state == self.STATE_HIDDEN:
151 self.__locked = self.__locked + 1
154 self.hideTimer.stop()
156 def unlockShow(self):
157 self.__locked = self.__locked - 1
159 self.startHideTimer()
161 # def startShow(self):
162 # self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
163 # self.__state = self.STATE_SHOWN
165 # def startHide(self):
166 # self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
167 # self.__state = self.STATE_HIDDEN
169 class NumberZap(Screen):
176 self.close(int(self["number"].getText()))
178 def keyNumberGlobal(self, number):
179 self.Timer.start(3000, True) #reset timer
180 self.field = self.field + str(number)
181 self["number"].setText(self.field)
182 if len(self.field) >= 4:
185 def __init__(self, session, number):
186 Screen.__init__(self, session)
187 self.field = str(number)
189 self["channel"] = Label(_("Channel:"))
191 self["number"] = Label(self.field)
193 self["actions"] = NumberActionMap( [ "SetupActions" ],
197 "1": self.keyNumberGlobal,
198 "2": self.keyNumberGlobal,
199 "3": self.keyNumberGlobal,
200 "4": self.keyNumberGlobal,
201 "5": self.keyNumberGlobal,
202 "6": self.keyNumberGlobal,
203 "7": self.keyNumberGlobal,
204 "8": self.keyNumberGlobal,
205 "9": self.keyNumberGlobal,
206 "0": self.keyNumberGlobal
209 self.Timer = eTimer()
210 self.Timer.callback.append(self.keyOK)
211 self.Timer.start(3000, True)
213 class InfoBarNumberZap:
214 """ Handles an initial number for NumberZapping """
216 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
218 "1": self.keyNumberGlobal,
219 "2": self.keyNumberGlobal,
220 "3": self.keyNumberGlobal,
221 "4": self.keyNumberGlobal,
222 "5": self.keyNumberGlobal,
223 "6": self.keyNumberGlobal,
224 "7": self.keyNumberGlobal,
225 "8": self.keyNumberGlobal,
226 "9": self.keyNumberGlobal,
227 "0": self.keyNumberGlobal,
230 def keyNumberGlobal(self, number):
231 # print "You pressed number " + str(number)
233 if isinstance(self, InfoBarPiP) and self.pipHandles0Action():
234 self.pipDoHandle0Action()
236 self.servicelist.recallPrevService()
238 if not (self.has_key("TimeshiftActions") and self.timeshift_enabled):
239 self.session.openWithCallback(self.numberEntered, NumberZap, number)
241 def numberEntered(self, retval):
242 # print self.servicelist
244 self.zapToNumber(retval)
246 def searchNumberHelper(self, serviceHandler, num, bouquet):
247 servicelist = serviceHandler.list(bouquet)
248 if not servicelist is None:
250 serviceIterator = servicelist.getNext()
251 if not serviceIterator.valid(): #check end of list
253 playable = not (serviceIterator.flags & (eServiceReference.isMarker|eServiceReference.isDirectory))
256 if not num: #found service with searched number ?
257 return serviceIterator, 0
260 def zapToNumber(self, number):
261 bouquet = self.servicelist.bouquet_root
263 serviceHandler = eServiceCenter.getInstance()
264 if not config.usage.multibouquet.value:
265 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
267 bouquetlist = serviceHandler.list(bouquet)
268 if not bouquetlist is None:
270 bouquet = bouquetlist.getNext()
271 if not bouquet.valid(): #check end of list
273 if bouquet.flags & eServiceReference.isDirectory:
274 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
275 if not service is None:
276 if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
277 self.servicelist.clearPath()
278 if self.servicelist.bouquet_root != bouquet:
279 self.servicelist.enterPath(self.servicelist.bouquet_root)
280 self.servicelist.enterPath(bouquet)
281 self.servicelist.setCurrentSelection(service) #select the service in servicelist
282 self.servicelist.zap()
284 config.misc.initialchannelselection = ConfigBoolean(default = True)
286 class InfoBarChannelSelection:
287 """ ChannelSelection - handles the channelSelection dialog and the initial
288 channelChange actions which open the channelSelection dialog """
291 self.servicelist = self.session.instantiateDialog(ChannelSelection)
293 if config.misc.initialchannelselection.value:
294 self.onShown.append(self.firstRun)
296 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
298 "switchChannelUp": (self.switchChannelUp, _("open servicelist(up)")),
299 "switchChannelDown": (self.switchChannelDown, _("open servicelist(down)")),
300 "zapUp": (self.zapUp, _("previous channel")),
301 "zapDown": (self.zapDown, _("next channel")),
302 "historyBack": (self.historyBack, _("previous channel in history")),
303 "historyNext": (self.historyNext, _("next channel in history")),
304 "openServiceList": (self.openServiceList, _("open servicelist")),
307 def showTvChannelList(self, zap=False):
308 self.servicelist.setModeTv()
310 self.servicelist.zap()
311 self.session.execDialog(self.servicelist)
313 def showRadioChannelList(self, zap=False):
314 self.servicelist.setModeRadio()
316 self.servicelist.zap()
317 self.session.execDialog(self.servicelist)
320 self.onShown.remove(self.firstRun)
321 config.misc.initialchannelselection.value = False
322 config.misc.initialchannelselection.save()
323 self.switchChannelDown()
325 def historyBack(self):
326 self.servicelist.historyBack()
328 def historyNext(self):
329 self.servicelist.historyNext()
331 def switchChannelUp(self):
332 self.servicelist.moveUp()
333 self.session.execDialog(self.servicelist)
335 def switchChannelDown(self):
336 self.servicelist.moveDown()
337 self.session.execDialog(self.servicelist)
339 def openServiceList(self):
340 self.session.execDialog(self.servicelist)
343 if self.servicelist.inBouquet():
344 prev = self.servicelist.getCurrentSelection()
346 prev = prev.toString()
348 if config.usage.quickzap_bouquet_change.value:
349 if self.servicelist.atBegin():
350 self.servicelist.prevBouquet()
351 self.servicelist.moveUp()
352 cur = self.servicelist.getCurrentSelection()
353 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
356 self.servicelist.moveUp()
357 self.servicelist.zap()
360 if self.servicelist.inBouquet():
361 prev = self.servicelist.getCurrentSelection()
363 prev = prev.toString()
365 if config.usage.quickzap_bouquet_change.value and self.servicelist.atEnd():
366 self.servicelist.nextBouquet()
368 self.servicelist.moveDown()
369 cur = self.servicelist.getCurrentSelection()
370 if not cur or (not (cur.flags & 64)) or cur.toString() == prev:
373 self.servicelist.moveDown()
374 self.servicelist.zap()
377 """ Handles a menu action, to open the (main) menu """
379 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions",
381 "mainMenu": (self.mainMenu, _("Enter main menu...")),
383 self.session.infobar = None
386 print "loading mainmenu XML..."
387 menu = mdom.getroot()
388 assert menu.tag == "menu", "root element in menu must be 'menu'!"
390 self.session.infobar = self
391 # so we can access the currently active infobar from screens opened from within the mainmenu
392 # at the moment used from the SubserviceSelection
394 self.session.openWithCallback(self.mainMenuClosed, MainMenu, menu)
396 def mainMenuClosed(self, *val):
397 self.session.infobar = None
399 class InfoBarSimpleEventView:
400 """ Opens the Eventview for now/next """
402 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
404 "showEventInfo": (self.openEventView, _("show event details")),
405 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
408 def showEventInfoWhenNotVisible(self):
415 def openEventView(self):
417 self.epglist = epglist
418 service = self.session.nav.getCurrentService()
419 ref = self.session.nav.getCurrentlyPlayingServiceReference()
420 info = service.info()
428 self.session.open(EventViewSimple, epglist[0], ServiceReference(ref), self.eventViewCallback)
430 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
431 epglist = self.epglist
434 epglist[0] = epglist[1]
438 class SimpleServicelist:
439 def __init__(self, services):
440 self.services = services
441 self.length = len(services)
444 def selectService(self, service):
450 while self.services[self.current].ref != service:
452 if self.current >= self.length:
456 def nextService(self):
459 if self.current+1 < self.length:
464 def prevService(self):
467 if self.current-1 > -1:
470 self.current = self.length - 1
472 def currentService(self):
473 if not self.length or self.current >= self.length:
475 return self.services[self.current]
478 """ EPG - Opens an EPG list when the showEPGList action fires """
480 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
482 iPlayableService.evUpdatedEventInfo: self.__evEventInfoChanged,
485 self.is_now_next = False
487 self.bouquetSel = None
488 self.eventView = None
489 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
491 "showEventInfo": (self.openEventView, _("show EPG...")),
492 "showEventInfoPlugin": (self.showEventInfoPlugins, _("list of EPG views...")),
493 "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
496 def showEventInfoWhenNotVisible(self):
503 def zapToService(self, service):
504 if not service is None:
505 if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
506 self.servicelist.clearPath()
507 if self.servicelist.bouquet_root != self.epg_bouquet:
508 self.servicelist.enterPath(self.servicelist.bouquet_root)
509 self.servicelist.enterPath(self.epg_bouquet)
510 self.servicelist.setCurrentSelection(service) #select the service in servicelist
511 self.servicelist.zap()
513 def getBouquetServices(self, bouquet):
515 servicelist = eServiceCenter.getInstance().list(bouquet)
516 if not servicelist is None:
518 service = servicelist.getNext()
519 if not service.valid(): #check if end of list
521 if service.flags & (eServiceReference.isDirectory | eServiceReference.isMarker): #ignore non playable services
523 services.append(ServiceReference(service))
526 def openBouquetEPG(self, bouquet, withCallback=True):
527 services = self.getBouquetServices(bouquet)
529 self.epg_bouquet = bouquet
531 self.dlg_stack.append(self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService, None, self.changeBouquetCB))
533 self.session.open(EPGSelection, services, self.zapToService, None, self.changeBouquetCB)
535 def changeBouquetCB(self, direction, epg):
538 self.bouquetSel.down()
541 bouquet = self.bouquetSel.getCurrent()
542 services = self.getBouquetServices(bouquet)
544 self.epg_bouquet = bouquet
545 epg.setServices(services)
547 def closed(self, ret=False):
548 closedScreen = self.dlg_stack.pop()
549 if self.bouquetSel and closedScreen == self.bouquetSel:
550 self.bouquetSel = None
551 elif self.eventView and closedScreen == self.eventView:
552 self.eventView = None
554 dlgs=len(self.dlg_stack)
556 self.dlg_stack[dlgs-1].close(dlgs > 1)
558 def openMultiServiceEPG(self, withCallback=True):
559 bouquets = self.servicelist.getBouquetList()
564 if config.usage.multiepg_ask_bouquet.value:
565 self.openMultiServiceEPGAskBouquet(bouquets, cnt, withCallback)
567 self.openMultiServiceEPGSilent(bouquets, cnt, withCallback)
569 def openMultiServiceEPGAskBouquet(self, bouquets, cnt, withCallback):
570 if cnt > 1: # show bouquet list
572 self.bouquetSel = self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
573 self.dlg_stack.append(self.bouquetSel)
575 self.bouquetSel = self.session.open(BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
577 self.openBouquetEPG(bouquets[0][1], withCallback)
579 def openMultiServiceEPGSilent(self, bouquets, cnt, withCallback):
580 root = self.servicelist.getRoot()
581 rootstr = root.toCompareString()
583 for bouquet in bouquets:
584 if bouquet[1].toCompareString() == rootstr:
589 if cnt > 1: # create bouquet list for bouq+/-
590 self.bouquetSel = SilentBouquetSelector(bouquets, True, self.servicelist.getBouquetNumOffset(root))
592 self.openBouquetEPG(root, withCallback)
594 def changeServiceCB(self, direction, epg):
597 self.serviceSel.nextService()
599 self.serviceSel.prevService()
600 epg.setService(self.serviceSel.currentService())
602 def SingleServiceEPGClosed(self, ret=False):
603 self.serviceSel = None
605 def openSingleServiceEPG(self):
606 ref=self.session.nav.getCurrentlyPlayingServiceReference()
608 if self.servicelist.getMutableList() is not None: # bouquet in channellist
609 current_path = self.servicelist.getRoot()
610 services = self.getBouquetServices(current_path)
611 self.serviceSel = SimpleServicelist(services)
612 if self.serviceSel.selectService(ref):
613 self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref, serviceChangeCB = self.changeServiceCB)
615 self.session.openWithCallback(self.SingleServiceEPGClosed, EPGSelection, ref)
617 self.session.open(EPGSelection, ref)
619 def showEventInfoPlugins(self):
620 list = [(p.name, boundFunction(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EVENTINFO)]
623 list.append((_("show single service EPG..."), self.openSingleServiceEPG))
624 list.append((_("Multi EPG"), self.openMultiServiceEPG))
625 self.session.openWithCallback(self.EventInfoPluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = list, skin_name = "EPGExtensionsList")
627 self.openSingleServiceEPG()
629 def runPlugin(self, plugin):
630 plugin(session = self.session, servicelist = self.servicelist)
632 def EventInfoPluginChosen(self, answer):
633 if answer is not None:
636 def openSimilarList(self, eventid, refstr):
637 self.session.open(EPGSelection, refstr, None, eventid)
639 def getNowNext(self):
641 service = self.session.nav.getCurrentService()
642 info = service and service.info()
643 ptr = info and info.getEvent(0)
646 ptr = info and info.getEvent(1)
649 self.epglist = epglist
651 def __evEventInfoChanged(self):
652 if self.is_now_next and len(self.dlg_stack) == 1:
654 assert self.eventView
656 self.eventView.setEvent(self.epglist[0])
658 def openEventView(self):
659 ref = self.session.nav.getCurrentlyPlayingServiceReference()
661 epglist = self.epglist
663 self.is_now_next = False
664 epg = eEPGCache.getInstance()
665 ptr = ref and ref.valid() and epg.lookupEventTime(ref, -1)
668 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
672 self.is_now_next = True
674 self.eventView = self.session.openWithCallback(self.closed, EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList)
675 self.dlg_stack.append(self.eventView)
677 print "no epg for the service avail.. so we show multiepg instead of eventinfo"
678 self.openMultiServiceEPG(False)
680 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
681 epglist = self.epglist
684 epglist[0]=epglist[1]
688 class InfoBarRdsDecoder:
689 """provides RDS and Rass support/display"""
691 self.rds_display = self.session.instantiateDialog(RdsInfoDisplay)
692 self.rds_display.setAnimationMode(0)
693 self.rass_interactive = None
695 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
697 iPlayableService.evEnd: self.__serviceStopped,
698 iPlayableService.evUpdatedRassSlidePic: self.RassSlidePicChanged
701 self["RdsActions"] = ActionMap(["InfobarRdsActions"],
703 "startRassInteractive": self.startRassInteractive
706 self["RdsActions"].setEnabled(False)
708 self.onLayoutFinish.append(self.rds_display.show)
709 self.rds_display.onRassInteractivePossibilityChanged.append(self.RassInteractivePossibilityChanged)
711 def RassInteractivePossibilityChanged(self, state):
712 self["RdsActions"].setEnabled(state)
714 def RassSlidePicChanged(self):
715 if not self.rass_interactive:
716 service = self.session.nav.getCurrentService()
717 decoder = service and service.rdsDecoder()
719 decoder.showRassSlidePicture()
721 def __serviceStopped(self):
722 if self.rass_interactive is not None:
723 rass_interactive = self.rass_interactive
724 self.rass_interactive = None
725 rass_interactive.close()
727 def startRassInteractive(self):
728 self.rds_display.hide()
729 self.rass_interactive = self.session.openWithCallback(self.RassInteractiveClosed, RassInteractive)
731 def RassInteractiveClosed(self, *val):
732 if self.rass_interactive is not None:
733 self.rass_interactive = None
734 self.RassSlidePicChanged()
735 self.rds_display.show()
738 """handles actions like seeking, pause"""
740 SEEK_STATE_PLAY = (0, 0, 0, ">")
741 SEEK_STATE_PAUSE = (1, 0, 0, "||")
742 SEEK_STATE_EOF = (1, 0, 0, "END")
744 def __init__(self, actionmap = "InfobarSeekActions"):
745 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
747 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
748 iPlayableService.evStart: self.__serviceStarted,
750 iPlayableService.evEOF: self.__evEOF,
751 iPlayableService.evSOF: self.__evSOF,
753 self.fast_winding_hint_message_showed = False
755 class InfoBarSeekActionMap(HelpableActionMap):
756 def __init__(self, screen, *args, **kwargs):
757 HelpableActionMap.__init__(self, screen, *args, **kwargs)
760 def action(self, contexts, action):
761 print "action:", action
762 if action[:5] == "seek:":
763 time = int(action[5:])
764 self.screen.doSeekRelative(time * 90000)
766 elif action[:8] == "seekdef:":
767 key = int(action[8:])
768 time = (-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
769 -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
770 -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value)[key-1]
771 self.screen.doSeekRelative(time * 90000)
774 return HelpableActionMap.action(self, contexts, action)
776 self["SeekActions"] = InfoBarSeekActionMap(self, actionmap,
778 "playpauseService": (self.playpauseService, _("Pause/Continue")),
779 "pauseService": (self.pauseService, _("pause")),
780 "unPauseService": (self.unPauseService, _("continue")),
782 "seekFwd": (self.seekFwd, _("skip forward")),
783 "seekFwdManual": (self.seekFwdManual, _("skip forward (enter time)")),
784 "seekBack": (self.seekBack, _("skip backward")),
785 "seekBackManual": (self.seekBackManual, _("skip backward (enter time)"))
787 # give them a little more priority to win over color buttons
789 self["SeekActions"].setEnabled(False)
791 self.seekstate = self.SEEK_STATE_PLAY
792 self.lastseekstate = self.SEEK_STATE_PLAY
794 self.onPlayStateChanged = [ ]
796 self.lockedBecauseOfSkipping = False
798 self.__seekableStatusChanged()
800 def makeStateForward(self, n):
801 return (0, n, 0, ">> %dx" % n)
803 def makeStateBackward(self, n):
804 return (0, -n, 0, "<< %dx" % n)
806 def makeStateSlowMotion(self, n):
807 return (0, 0, n, "/%d" % n)
809 def isStateForward(self, state):
812 def isStateBackward(self, state):
815 def isStateSlowMotion(self, state):
816 return state[1] == 0 and state[2] > 1
818 def getHigher(self, n, lst):
824 def getLower(self, n, lst):
832 def showAfterSeek(self):
833 if isinstance(self, InfoBarShowHide):
843 service = self.session.nav.getCurrentService()
847 seek = service.seek()
849 if seek is None or not seek.isCurrentlySeekable():
854 def isSeekable(self):
855 if self.getSeek() is None:
859 def __seekableStatusChanged(self):
860 # print "seekable status changed!"
861 if not self.isSeekable():
862 self["SeekActions"].setEnabled(False)
863 # print "not seekable, return to play"
864 self.setSeekState(self.SEEK_STATE_PLAY)
866 self["SeekActions"].setEnabled(True)
869 def __serviceStarted(self):
870 self.fast_winding_hint_message_showed = False
871 self.seekstate = self.SEEK_STATE_PLAY
872 self.__seekableStatusChanged()
874 def setSeekState(self, state):
875 service = self.session.nav.getCurrentService()
880 if not self.isSeekable():
881 if state not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE):
882 state = self.SEEK_STATE_PLAY
884 pauseable = service.pause()
886 if pauseable is None:
887 print "not pauseable."
888 state = self.SEEK_STATE_PLAY
890 self.seekstate = state
892 if pauseable is not None:
893 if self.seekstate[0]:
894 print "resolved to PAUSE"
896 elif self.seekstate[1]:
897 print "resolved to FAST FORWARD"
898 pauseable.setFastForward(self.seekstate[1])
899 elif self.seekstate[2]:
900 print "resolved to SLOW MOTION"
901 pauseable.setSlowMotion(self.seekstate[2])
903 print "resolved to PLAY"
906 for c in self.onPlayStateChanged:
909 self.checkSkipShowHideLock()
913 def playpauseService(self):
914 if self.seekstate != self.SEEK_STATE_PLAY:
915 self.unPauseService()
919 def pauseService(self):
920 if self.seekstate == self.SEEK_STATE_PAUSE:
921 if config.seek.on_pause.value == "play":
922 self.unPauseService()
923 elif config.seek.on_pause.value == "step":
924 self.doSeekRelative(1)
925 elif config.seek.on_pause.value == "last":
926 self.setSeekState(self.lastseekstate)
927 self.lastseekstate = self.SEEK_STATE_PLAY
929 if self.seekstate != self.SEEK_STATE_EOF:
930 self.lastseekstate = self.seekstate
931 self.setSeekState(self.SEEK_STATE_PAUSE);
933 def unPauseService(self):
935 if self.seekstate == self.SEEK_STATE_PLAY:
937 self.setSeekState(self.SEEK_STATE_PLAY)
939 def doSeek(self, pts):
940 seekable = self.getSeek()
945 def doSeekRelative(self, pts):
946 seekable = self.getSeek()
949 prevstate = self.seekstate
951 if self.seekstate == self.SEEK_STATE_EOF:
952 if prevstate == self.SEEK_STATE_PAUSE:
953 self.setSeekState(self.SEEK_STATE_PAUSE)
955 self.setSeekState(self.SEEK_STATE_PLAY)
956 seekable.seekRelative(pts<0 and -1 or 1, abs(pts))
957 if abs(pts) > 100 and config.usage.show_infobar_on_skip.value:
961 seek = self.getSeek()
962 if seek and not (seek.isCurrentlySeekable() & 2):
963 if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
964 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)
965 self.fast_winding_hint_message_showed = True
967 return 0 # trade as unhandled action
968 if self.seekstate == self.SEEK_STATE_PLAY:
969 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
970 elif self.seekstate == self.SEEK_STATE_PAUSE:
971 if len(config.seek.speeds_slowmotion.value):
972 self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
974 self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
975 elif self.seekstate == self.SEEK_STATE_EOF:
977 elif self.isStateForward(self.seekstate):
978 speed = self.seekstate[1]
979 if self.seekstate[2]:
980 speed /= self.seekstate[2]
981 speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
982 self.setSeekState(self.makeStateForward(speed))
983 elif self.isStateBackward(self.seekstate):
984 speed = -self.seekstate[1]
985 if self.seekstate[2]:
986 speed /= self.seekstate[2]
987 speed = self.getLower(speed, config.seek.speeds_backward.value)
989 self.setSeekState(self.makeStateBackward(speed))
991 self.setSeekState(self.SEEK_STATE_PLAY)
992 elif self.isStateSlowMotion(self.seekstate):
993 speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
994 self.setSeekState(self.makeStateSlowMotion(speed))
997 seek = self.getSeek()
998 if seek and not (seek.isCurrentlySeekable() & 2):
999 if not self.fast_winding_hint_message_showed and (seek.isCurrentlySeekable() & 1):
1000 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)
1001 self.fast_winding_hint_message_showed = True
1003 return 0 # trade as unhandled action
1004 seekstate = self.seekstate
1005 if seekstate == self.SEEK_STATE_PLAY:
1006 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1007 elif seekstate == self.SEEK_STATE_EOF:
1008 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1009 self.doSeekRelative(-6)
1010 elif seekstate == self.SEEK_STATE_PAUSE:
1011 self.doSeekRelative(-1)
1012 elif self.isStateForward(seekstate):
1013 speed = seekstate[1]
1015 speed /= seekstate[2]
1016 speed = self.getLower(speed, config.seek.speeds_forward.value)
1018 self.setSeekState(self.makeStateForward(speed))
1020 self.setSeekState(self.SEEK_STATE_PLAY)
1021 elif self.isStateBackward(seekstate):
1022 speed = -seekstate[1]
1024 speed /= seekstate[2]
1025 speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
1026 self.setSeekState(self.makeStateBackward(speed))
1027 elif self.isStateSlowMotion(seekstate):
1028 speed = self.getHigher(seekstate[2], config.seek.speeds_slowmotion.value)
1030 self.setSeekState(self.makeStateSlowMotion(speed))
1032 self.setSeekState(self.SEEK_STATE_PAUSE)
1034 def seekFwdManual(self):
1035 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
1037 def fwdSeekTo(self, minutes):
1038 print "Seek", minutes, "minutes forward"
1039 self.doSeekRelative(minutes * 60 * 90000)
1041 def seekBackManual(self):
1042 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
1044 def rwdSeekTo(self, minutes):
1046 self.doSeekRelative(-minutes * 60 * 90000)
1048 def checkSkipShowHideLock(self):
1049 wantlock = self.seekstate != self.SEEK_STATE_PLAY
1051 if config.usage.show_infobar_on_skip.value:
1052 if self.lockedBecauseOfSkipping and not wantlock:
1054 self.lockedBecauseOfSkipping = False
1056 if wantlock and not self.lockedBecauseOfSkipping:
1058 self.lockedBecauseOfSkipping = True
1060 def calcRemainingTime(self):
1061 seekable = self.getSeek()
1062 if seekable is not None:
1063 len = seekable.getLength()
1065 tmp = self.cueGetEndCutPosition()
1070 pos = seekable.getPlayPosition()
1071 speednom = self.seekstate[1] or 1
1072 speedden = self.seekstate[2] or 1
1073 if not len[0] and not pos[0]:
1074 if len[1] <= pos[1]:
1076 time = (len[1] - pos[1])*speedden/(90*speednom)
1081 if self.seekstate == self.SEEK_STATE_EOF:
1084 # if we are seeking forward, we try to end up ~1s before the end, and pause there.
1085 seekstate = self.seekstate
1086 if self.seekstate != self.SEEK_STATE_PAUSE:
1087 self.setSeekState(self.SEEK_STATE_EOF)
1089 if seekstate not in (self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE): # if we are seeking
1090 seekable = self.getSeek()
1091 if seekable is not None:
1093 if seekstate == self.SEEK_STATE_PLAY: # regular EOF
1094 self.doEofInternal(True)
1096 self.doEofInternal(False)
1098 def doEofInternal(self, playing):
1099 pass # Defined in subclasses
1102 self.setSeekState(self.SEEK_STATE_PLAY)
1105 from Screens.PVRState import PVRState, TimeshiftState
1107 class InfoBarPVRState:
1108 def __init__(self, screen=PVRState, force_show = False):
1109 self.onPlayStateChanged.append(self.__playStateChanged)
1110 self.pvrStateDialog = self.session.instantiateDialog(screen)
1111 self.pvrStateDialog.setAnimationMode(0)
1112 self.onShow.append(self._mayShow)
1113 self.onHide.append(self.pvrStateDialog.hide)
1114 self.force_show = force_show
1117 if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
1118 self.pvrStateDialog.show()
1120 def __playStateChanged(self, state):
1121 playstateString = state[3]
1122 self.pvrStateDialog["state"].setText(playstateString)
1124 # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
1125 if not config.usage.show_infobar_on_skip.value and self.seekstate == self.SEEK_STATE_PLAY and not self.force_show:
1126 self.pvrStateDialog.hide()
1130 class InfoBarTimeshiftState(InfoBarPVRState):
1132 InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True)
1133 self.__hideTimer = eTimer()
1134 self.__hideTimer.callback.append(self.__hideTimeshiftState)
1137 if self.execing and self.timeshift_enabled:
1138 self.pvrStateDialog.show()
1139 if self.seekstate == self.SEEK_STATE_PLAY and not self.shown:
1140 self.__hideTimer.start(5*1000, True)
1142 def __hideTimeshiftState(self):
1143 self.pvrStateDialog.hide()
1145 class InfoBarShowMovies:
1147 # i don't really like this class.
1148 # it calls a not further specified "movie list" on up/down/movieList,
1149 # so this is not more than an action map
1151 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
1153 "movieList": (self.showMovies, _("movie list")),
1154 "up": (self.showMovies, _("movie list")),
1155 "down": (self.showMovies, _("movie list"))
1158 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
1162 # Timeshift works the following way:
1163 # demux0 demux1 "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
1164 # - normal playback TUNER unused PLAY enable disable disable
1165 # - user presses "yellow" button. FILE record PAUSE enable disable enable
1166 # - user presess pause again FILE record PLAY enable disable enable
1167 # - user fast forwards FILE record FF enable disable enable
1168 # - end of timeshift buffer reached TUNER record PLAY enable enable disable
1169 # - user backwards FILE record BACK # !! enable disable enable
1173 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1174 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1175 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1176 # - the user can now PVR around
1177 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1178 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1180 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1181 # - if the user rewinds, or press pause, timeshift will be activated again
1183 # note that a timeshift can be enabled ("recording") and
1184 # activated (currently time-shifting).
1186 class InfoBarTimeshift:
1188 if SystemInfo["PVRSupport"]:
1189 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
1191 "timeshiftStart": (self.startTimeshift, _("start timeshift")), # the "yellow key"
1192 "timeshiftStop": (self.stopTimeshift, _("stop timeshift")) # currently undefined :), probably 'TV'
1194 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1196 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1197 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause # something like "pause key"
1198 }, prio=-1) # priority over record
1200 self.timeshift_enabled = 0
1201 self.timeshift_state = 0
1202 self.ts_rewind_timer = eTimer()
1203 self.ts_rewind_timer.callback.append(self.rewindService)
1205 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1207 iPlayableService.evStart: self.__serviceStarted,
1208 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
1209 iPlayableService.evUser+3: self.__lowDiskspaceMessage
1212 def getTimeshift(self):
1213 service = self.session.nav.getCurrentService()
1214 return service and service.timeshift()
1216 def startTimeshift(self):
1217 print "enable timeshift"
1218 ts = self.getTimeshift()
1220 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
1221 print "no ts interface"
1224 if self.timeshift_enabled:
1225 print "hu, timeshift already enabled?"
1227 from Components import Harddisk
1228 if Harddisk.getMountPath(config.usage.timeshift_path.value) != '/' and \
1229 SystemInfo.get("DisableUsbRecord", True) and \
1230 Harddisk.isUsbStorage(config.usage.timeshift_path.value):
1231 self.session.open(MessageBox, _("Timeshift not possible on a USB storage."), MessageBox.TYPE_ERROR)
1234 if not ts.startTimeshift():
1235 self.timeshift_enabled = 1
1237 # we remove the "relative time" for now.
1238 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1241 #self.setSeekState(self.SEEK_STATE_PAUSE)
1242 self.activateTimeshiftEnd(False)
1244 # enable the "TimeshiftEnableActions", which will override
1245 # the startTimeshift actions
1246 self.__seekableStatusChanged()
1248 print "timeshift failed"
1250 def stopTimeshift(self):
1251 if not self.timeshift_enabled:
1253 print "disable timeshift"
1254 ts = self.getTimeshift()
1257 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
1259 def stopTimeshiftConfirmed(self, confirmed):
1263 ts = self.getTimeshift()
1268 self.timeshift_enabled = 0
1271 self.__seekableStatusChanged()
1273 # activates timeshift, and seeks to (almost) the end
1274 def activateTimeshiftEnd(self, back = True):
1275 ts = self.getTimeshift()
1276 print "activateTimeshiftEnd"
1281 if ts.isTimeshiftActive():
1282 print "!! activate timeshift called - but shouldn't this be a normal pause?"
1286 ts.activateTimeshift() # activate timeshift will automatically pause
1287 self.setSeekState(self.SEEK_STATE_PAUSE)
1290 self.ts_rewind_timer.start(200, 1)
1292 def rewindService(self):
1293 self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
1295 # same as activateTimeshiftEnd, but pauses afterwards.
1296 def activateTimeshiftEndAndPause(self):
1297 print "activateTimeshiftEndAndPause"
1298 #state = self.seekstate
1299 self.activateTimeshiftEnd(False)
1301 def __seekableStatusChanged(self):
1304 # print "self.isSeekable", self.isSeekable()
1305 # print "self.timeshift_enabled", self.timeshift_enabled
1307 # when this service is not seekable, but timeshift
1308 # is enabled, this means we can activate
1310 if not self.isSeekable() and self.timeshift_enabled:
1313 # print "timeshift activate:", enabled
1314 self["TimeshiftActivateActions"].setEnabled(enabled)
1316 def __serviceStarted(self):
1317 self.timeshift_enabled = False
1318 self.__seekableStatusChanged()
1320 def __lowDiskspaceMessage(self):
1321 Notifications.AddPopup(text = _("Write error. Not enough space for writing.\n"), type = MessageBox.TYPE_ERROR, timeout = 0, id = "DiskFullMessage")
1323 from Screens.PiPSetup import PiPSetup
1325 class InfoBarExtensions:
1326 EXTENSION_SINGLE = 0
1332 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1334 "extensions": (self.showExtensionSelection, _("view extensions...")),
1335 }, 1) # lower priority
1337 for p in plugins.getPlugins(PluginDescriptor.WHERE_EXTENSIONSINGLE):
1340 def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1341 self.list.append((type, extension, key))
1343 def updateExtension(self, extension, key = None):
1344 self.extensionsList.append(extension)
1346 if self.extensionKeys.has_key(key):
1350 for x in self.availableKeys:
1351 if not self.extensionKeys.has_key(x):
1356 self.extensionKeys[key] = len(self.extensionsList) - 1
1358 def updateExtensions(self):
1359 self.extensionsList = []
1360 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1361 self.extensionKeys = {}
1363 if x[0] == self.EXTENSION_SINGLE:
1364 self.updateExtension(x[1], x[2])
1367 self.updateExtension(y[0], y[1])
1370 def showExtensionSelection(self):
1371 self.updateExtensions()
1372 extensionsList = self.extensionsList[:]
1375 for x in self.availableKeys:
1376 if self.extensionKeys.has_key(x):
1377 entry = self.extensionKeys[x]
1378 extension = self.extensionsList[entry]
1380 name = str(extension[0]())
1381 list.append((extension[0](), extension))
1383 extensionsList.remove(extension)
1385 extensionsList.remove(extension)
1386 list.extend([(x[0](), x) for x in extensionsList])
1388 keys += [""] * len(extensionsList)
1389 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys, skin_name = "ExtensionsList")
1391 def extensionCallback(self, answer):
1392 if answer is not None:
1395 from Tools.BoundFunction import boundFunction
1398 # depends on InfoBarExtensions
1400 class InfoBarPlugins:
1402 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1404 def getPluginName(self, name):
1407 def getPluginList(self):
1409 for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU):
1410 args = inspect.getargspec(p.__call__)[0]
1411 if len(args) == 1 or len(args) == 2 and isinstance(self, InfoBarChannelSelection):
1412 l.append(((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name))
1413 l.sort(key = lambda e: e[2]) # sort by name
1416 def runPlugin(self, plugin):
1417 if isinstance(self, InfoBarChannelSelection):
1418 plugin(session = self.session, servicelist = self.servicelist)
1420 plugin(session = self.session)
1422 from Components.Task import job_manager
1423 class InfoBarJobman:
1425 self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
1427 def getJobList(self):
1428 return [((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None) for job in job_manager.getPendingJobs()]
1430 def getJobName(self, job):
1431 return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
1433 def showJobView(self, job):
1434 from Screens.TaskView import JobView
1435 job_manager.in_background = False
1436 self.session.openWithCallback(self.JobViewCB, JobView, job)
1438 def JobViewCB(self, in_background):
1439 job_manager.in_background = in_background
1441 # depends on InfoBarExtensions
1445 self.session.pipshown
1447 self.session.pipshown = False
1448 if SystemInfo.get("NumVideoDecoders", 1) > 1:
1450 self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
1451 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1452 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1454 self.addExtension((self.getShowHideName, self.showPiP, self.pipShown), "blue")
1455 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1458 return self.session.pipshown
1460 def pipHandles0Action(self):
1461 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1463 def getShowHideName(self):
1464 if self.session.pipshown:
1465 return _("Disable Picture in Picture")
1467 return _("Activate Picture in Picture")
1469 def getSwapName(self):
1470 return _("Swap Services")
1472 def getMoveName(self):
1473 return _("Move Picture in Picture")
1476 if self.session.pipshown:
1477 del self.session.pip
1478 self.session.pipshown = False
1480 self.session.pip = self.session.instantiateDialog(PictureInPicture)
1481 self.session.pip.setAnimationMode(0)
1482 self.session.pip.show()
1483 newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1484 if self.session.pip.playService(newservice):
1485 self.session.pipshown = True
1486 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1488 self.session.pipshown = False
1489 del self.session.pip
1490 self.session.nav.playService(newservice)
1493 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1494 if self.session.pip.servicePath:
1495 servicepath = self.servicelist.getCurrentServicePath()
1496 ref=servicepath[len(servicepath)-1]
1497 pipref=self.session.pip.getCurrentService()
1498 self.session.pip.playService(swapservice)
1499 self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1500 if pipref.toString() != ref.toString(): # is a subservice ?
1501 self.session.nav.stopService() # stop portal
1502 self.session.nav.playService(pipref) # start subservice
1503 self.session.pip.servicePath=servicepath
1506 self.session.open(PiPSetup, pip = self.session.pip)
1508 def pipDoHandle0Action(self):
1509 use = config.usage.pip_zero_button.value
1512 elif "swapstop" == use:
1518 from RecordTimer import parseEvent, RecordTimerEntry
1520 class InfoBarInstantRecord:
1521 """Instant Record - handles the instantRecord action in order to
1522 start/stop instant records"""
1524 if SystemInfo["PVRSupport"]:
1525 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1527 "instantRecord": (self.instantRecord, _("Instant Record...")),
1531 def stopCurrentRecording(self, entry = -1):
1532 if entry is not None and entry != -1:
1533 self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1534 self.recording.remove(self.recording[entry])
1536 def startInstantRecording(self, limitEvent = False):
1537 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1539 # try to get event info
1542 service = self.session.nav.getCurrentService()
1543 epg = eEPGCache.getInstance()
1544 event = epg.lookupEventTime(serviceref, -1, 0)
1546 info = service.info()
1547 ev = info.getEvent(0)
1553 end = begin + 3600 # dummy
1554 name = "instant record"
1558 if event is not None:
1559 curEvent = parseEvent(event)
1561 description = curEvent[3]
1562 eventid = curEvent[4]
1567 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1569 if isinstance(serviceref, eServiceReference):
1570 serviceref = ServiceReference(serviceref)
1572 recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = preferredInstantRecordPath())
1573 recording.dontSave = True
1575 if event is None or limitEvent == False:
1576 recording.autoincrease = True
1577 recording.setAutoincreaseEnd()
1579 simulTimerList = self.session.nav.RecordTimer.record(recording)
1581 if simulTimerList is None: # no conflict
1582 self.recording.append(recording)
1584 if len(simulTimerList) > 1: # with other recording
1585 name = simulTimerList[1].name
1586 name_date = ' '.join((name, strftime('%c', localtime(simulTimerList[1].begin))))
1587 print "[TIMER] conflicts with", name_date
1588 recording.autoincrease = True # start with max available length, then increment
1589 if recording.setAutoincreaseEnd():
1590 self.session.nav.RecordTimer.record(recording)
1591 self.recording.append(recording)
1592 self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
1594 self.session.open(MessageBox, _("Couldn't record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
1596 self.session.open(MessageBox, _("Couldn't record due to invalid service %s") % serviceref, MessageBox.TYPE_INFO)
1597 recording.autoincrease = False
1599 def isInstantRecordRunning(self):
1600 print "self.recording:", self.recording
1602 for x in self.recording:
1607 def recordQuestionCallback(self, answer):
1608 print "pre:\n", self.recording
1610 if answer is None or answer[1] == "no":
1613 recording = self.recording[:]
1615 if not x in self.session.nav.RecordTimer.timer_list:
1616 self.recording.remove(x)
1617 elif x.dontSave and x.isRunning():
1618 list.append((x, False))
1620 if answer[1] == "changeduration":
1621 if len(self.recording) == 1:
1622 self.changeDuration(0)
1624 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1625 elif answer[1] == "changeendtime":
1626 if len(self.recording) == 1:
1629 self.session.openWithCallback(self.setEndtime, TimerSelection, list)
1630 elif answer[1] == "stop":
1631 if len(self.recording) == 1:
1632 self.stopCurrentRecording(0)
1634 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1635 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1636 self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1637 if answer[1] == "manualduration":
1638 self.changeDuration(len(self.recording)-1)
1639 elif answer[1] == "manualendtime":
1640 self.setEndtime(len(self.recording)-1)
1641 print "after:\n", self.recording
1643 def setEndtime(self, entry):
1644 if entry is not None and entry >= 0:
1645 self.selectedEntry = entry
1646 self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1647 dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1648 dlg.setTitle(_("Please change recording endtime"))
1650 def TimeDateInputClosed(self, ret):
1653 localendtime = localtime(ret[1])
1654 print "stopping recording at", strftime("%c", localendtime)
1655 if self.recording[self.selectedEntry].end != ret[1]:
1656 self.recording[self.selectedEntry].autoincrease = False
1657 self.recording[self.selectedEntry].end = ret[1]
1658 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1660 def changeDuration(self, entry):
1661 if entry is not None and entry >= 0:
1662 self.selectedEntry = entry
1663 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1665 def inputCallback(self, value):
1666 if value is not None:
1667 print "stopping recording after", int(value), "minutes."
1668 entry = self.recording[self.selectedEntry]
1670 entry.autoincrease = False
1671 entry.end = int(time()) + 60 * int(value)
1672 self.session.nav.RecordTimer.timeChanged(entry)
1674 def instantRecord(self):
1675 if not fileExists("/hdd", 0):
1676 print "not found /hdd"
1677 os_system("ln -s /media/hdd /hdd")
1679 recPath = preferredInstantRecordPath()
1680 if not findSafeRecordPath(recPath) and not findSafeRecordPath(defaultMoviePath()):
1683 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1686 if self.isInstantRecordRunning():
1687 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1688 title=_("A recording is currently running.\nWhat do you want to do?"), \
1689 list=((_("stop recording"), "stop"), \
1690 (_("add recording (stop after current event)"), "event"), \
1691 (_("add recording (indefinitely)"), "indefinitely"), \
1692 (_("add recording (enter recording duration)"), "manualduration"), \
1693 (_("add recording (enter recording endtime)"), "manualendtime"), \
1694 (_("change recording (duration)"), "changeduration"), \
1695 (_("change recording (endtime)"), "changeendtime"), \
1696 (_("do nothing"), "no")))
1698 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1699 title=_("Start recording?"), \
1700 list=((_("add recording (stop after current event)"), "event"), \
1701 (_("add recording (indefinitely)"), "indefinitely"), \
1702 (_("add recording (enter recording duration)"), "manualduration"), \
1703 (_("add recording (enter recording endtime)"), "manualendtime"), \
1704 (_("don't record"), "no")))
1706 from Tools.ISO639 import LanguageCodes
1708 class InfoBarAudioSelection:
1710 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1712 "audioSelection": (self.audioSelection, _("Audio Options...")),
1715 def audioSelection(self):
1716 from Screens.AudioSelection import AudioSelection
1717 self.session.openWithCallback(self.audioSelected, AudioSelection, infobar=self)
1719 def audioSelected(self, ret=None):
1720 print "[infobar::audioSelected]", ret
1722 class InfoBarSubserviceSelection:
1724 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1726 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1729 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1731 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1732 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1734 self["SubserviceQuickzapAction"].setEnabled(False)
1736 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1738 iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
1743 def checkSubservicesAvail(self):
1744 service = self.session.nav.getCurrentService()
1745 subservices = service and service.subServices()
1746 if not subservices or subservices.getNumberOfSubservices() == 0:
1747 self["SubserviceQuickzapAction"].setEnabled(False)
1749 def nextSubservice(self):
1750 self.changeSubservice(+1)
1752 def prevSubservice(self):
1753 self.changeSubservice(-1)
1755 def changeSubservice(self, direction):
1756 service = self.session.nav.getCurrentService()
1757 subservices = service and service.subServices()
1758 n = subservices and subservices.getNumberOfSubservices()
1761 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1764 if subservices.getSubservice(idx).toString() == ref.toString():
1769 selection += direction
1774 newservice = subservices.getSubservice(selection)
1775 if newservice.valid():
1778 self.session.nav.playService(newservice, False)
1780 def subserviceSelection(self):
1781 service = self.session.nav.getCurrentService()
1782 subservices = service and service.subServices()
1783 self.bouquets = self.servicelist.getBouquetList()
1784 n = subservices and subservices.getNumberOfSubservices()
1787 ref = self.session.nav.getCurrentlyPlayingServiceReference()
1791 i = subservices.getSubservice(idx)
1792 if i.toString() == ref.toString():
1794 tlist.append((i.getName(), i))
1797 if self.bouquets and len(self.bouquets):
1798 keys = ["red", "blue", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1799 if config.usage.multibouquet.value:
1800 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1802 tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1805 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1806 keys = ["red", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1809 self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys, skin_name = "SubserviceSelection")
1811 def subserviceSelected(self, service):
1813 if not service is None:
1814 if isinstance(service[1], str):
1815 if service[1] == "quickzap":
1816 from Screens.SubservicesQuickzap import SubservicesQuickzap
1817 self.session.open(SubservicesQuickzap, service[2])
1819 self["SubserviceQuickzapAction"].setEnabled(True)
1820 self.session.nav.playService(service[1], False)
1822 def addSubserviceToBouquetCallback(self, service):
1823 if len(service) > 1 and isinstance(service[1], eServiceReference):
1824 self.selectedSubservice = service
1825 if self.bouquets is None:
1828 cnt = len(self.bouquets)
1829 if cnt > 1: # show bouquet list
1830 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1831 elif cnt == 1: # add to only one existing bouquet
1832 self.addSubserviceToBouquet(self.bouquets[0][1])
1833 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1835 def bouquetSelClosed(self, confirmed):
1837 del self.selectedSubservice
1839 self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1841 def addSubserviceToBouquet(self, dest):
1842 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1844 self.bsel.close(True)
1846 del self.selectedSubservice
1848 from Components.Sources.HbbtvApplication import HbbtvApplication
1849 gHbbtvApplication = HbbtvApplication()
1850 class InfoBarRedButton:
1852 if not (config.misc.rcused.value == 1):
1853 self["RedButtonActions"] = HelpableActionMap(self, "InfobarRedButtonActions",
1855 "activateRedButton": (self.activateRedButton, _("Red button...")),
1857 self["HbbtvApplication"] = gHbbtvApplication
1859 self["HbbtvApplication"] = Boolean(fixed=0)
1860 self["HbbtvApplication"].name = "" #is this a hack?
1862 self.onHBBTVActivation = [ ]
1863 self.onRedButtonActivation = [ ]
1864 self.onReadyForAIT = [ ]
1865 self.__et = ServiceEventTracker(screen=self, eventmap=
1867 iPlayableService.evHBBTVInfo: self.detectedHbbtvApplication,
1868 iPlayableService.evUpdatedInfo: self.updateInfomation
1871 def updateAIT(self, orgId=0):
1872 for x in self.onReadyForAIT:
1875 except Exception, ErrMsg:
1877 #self.onReadyForAIT.remove(x)
1879 def updateInfomation(self):
1881 self["HbbtvApplication"].setApplicationName("")
1883 except Exception, ErrMsg:
1886 def detectedHbbtvApplication(self):
1887 service = self.session.nav.getCurrentService()
1888 info = service and service.info()
1890 for x in info.getInfoObject(iServiceInformation.sHBBTVUrl):
1893 self.updateAIT(x[3])
1894 self["HbbtvApplication"].setApplicationName(x[1])
1896 except Exception, ErrMsg:
1899 def activateRedButton(self):
1900 service = self.session.nav.getCurrentService()
1901 info = service and service.info()
1902 if info and info.getInfoString(iServiceInformation.sHBBTVUrl) != "":
1903 for x in self.onHBBTVActivation:
1905 elif False: # TODO: other red button services
1906 for x in self.onRedButtonActivation:
1909 class InfoBarAdditionalInfo:
1912 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0 and config.misc.rcused.value == 1)
1913 self["TimeshiftPossible"] = self["RecordingPossible"]
1914 self["ShowTimeshiftOnYellow"] = Boolean(fixed=(config.misc.rcused.value == 1))
1915 self["ShowAudioOnYellow"] = Boolean(fixed=config.misc.rcused.value != 1)
1916 self["ShowRecordOnRed"] = Boolean(fixed=config.misc.rcused.value == 1)
1917 self["ExtensionsAvailable"] = Boolean(fixed=1)
1919 class InfoBarNotifications:
1921 self.onExecBegin.append(self.checkNotifications)
1922 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1923 self.onClose.append(self.__removeNotification)
1925 def __removeNotification(self):
1926 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1928 def checkNotificationsIfExecing(self):
1930 self.checkNotifications()
1932 def checkNotifications(self):
1933 notifications = Notifications.notifications
1935 n = notifications[0]
1937 del notifications[0]
1940 if n[3].has_key("onSessionOpenCallback"):
1941 n[3]["onSessionOpenCallback"]()
1942 del n[3]["onSessionOpenCallback"]
1945 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1947 dlg = self.session.open(n[1], *n[2], **n[3])
1949 # remember that this notification is currently active
1951 Notifications.current_notifications.append(d)
1952 dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1954 def __notificationClosed(self, d):
1955 Notifications.current_notifications.remove(d)
1957 class InfoBarServiceNotifications:
1959 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1961 iPlayableService.evEnd: self.serviceHasEnded
1964 def serviceHasEnded(self):
1965 print "service end!"
1968 self.setSeekState(self.SEEK_STATE_PLAY)
1972 class InfoBarCueSheetSupport:
1978 ENABLE_RESUME_SUPPORT = False
1980 def __init__(self, actionmap = "InfobarCueSheetActions"):
1981 self["CueSheetActions"] = HelpableActionMap(self, actionmap,
1983 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to previous marked position")),
1984 "jumpNextMark": (self.jumpNextMark, _("jump to next marked position")),
1985 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
1989 self.is_closing = False
1990 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1992 iPlayableService.evStart: self.__serviceStarted,
1995 def __serviceStarted(self):
1998 print "new service started! trying to download cuts!"
1999 self.downloadCuesheet()
2001 if self.ENABLE_RESUME_SUPPORT:
2004 for (pts, what) in self.cut_list:
2005 if what == self.CUT_TYPE_LAST:
2008 if last is not None:
2009 self.resume_point = last
2012 if config.usage.on_movie_start.value == "ask":
2013 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)
2014 elif config.usage.on_movie_start.value == "resume":
2015 # TRANSLATORS: The string "Resuming playback" flashes for a moment
2016 # TRANSLATORS: at the start of a movie, when the user has selected
2017 # TRANSLATORS: "Resume from last position" as start behavior.
2018 # TRANSLATORS: The purpose is to notify the user that the movie starts
2019 # TRANSLATORS: in the middle somewhere and not from the beginning.
2020 # TRANSLATORS: (Some translators seem to have interpreted it as a
2021 # TRANSLATORS: question or a choice, but it is a statement.)
2022 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
2024 def playLastCB(self, answer):
2026 self.doSeek(self.resume_point)
2027 self.hideAfterResume()
2029 def hideAfterResume(self):
2030 if isinstance(self, InfoBarShowHide):
2033 def __getSeekable(self):
2034 service = self.session.nav.getCurrentService()
2037 return service.seek()
2039 def cueGetCurrentPosition(self):
2040 seek = self.__getSeekable()
2043 r = seek.getPlayPosition()
2048 def cueGetEndCutPosition(self):
2051 for cp in self.cut_list:
2052 if cp[1] == self.CUT_TYPE_OUT:
2056 elif cp[1] == self.CUT_TYPE_IN:
2060 def jumpPreviousNextMark(self, cmp, start=False):
2061 current_pos = self.cueGetCurrentPosition()
2062 if current_pos is None:
2064 mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
2065 if mark is not None:
2073 def jumpPreviousMark(self):
2074 # we add 5 seconds, so if the play position is <5s after
2075 # the mark, the mark before will be used
2076 self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
2078 def jumpNextMark(self):
2079 if not self.jumpPreviousNextMark(lambda x: x-90000):
2082 def getNearestCutPoint(self, pts, cmp=abs, start=False):
2089 bestdiff = cmp(0 - pts)
2091 nearest = [0, False]
2092 for cp in self.cut_list:
2093 if beforecut and cp[1] in (self.CUT_TYPE_IN, self.CUT_TYPE_OUT):
2095 if cp[1] == self.CUT_TYPE_IN: # Start is here, disregard previous marks
2096 diff = cmp(cp[0] - pts)
2097 if start and diff >= 0:
2103 if cp[1] == self.CUT_TYPE_IN:
2105 elif cp[1] == self.CUT_TYPE_OUT:
2107 elif cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST):
2108 diff = cmp(cp[0] - pts)
2109 if instate and diff >= 0 and (nearest is None or bestdiff > diff):
2114 def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
2115 current_pos = self.cueGetCurrentPosition()
2116 if current_pos is None:
2117 print "not seekable"
2120 nearest_cutpoint = self.getNearestCutPoint(current_pos)
2122 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
2124 return nearest_cutpoint
2126 self.removeMark(nearest_cutpoint)
2127 elif not onlyremove and not onlyreturn:
2128 self.addMark((current_pos, self.CUT_TYPE_MARK))
2133 def addMark(self, point):
2134 insort(self.cut_list, point)
2135 self.uploadCuesheet()
2136 self.showAfterCuesheetOperation()
2138 def removeMark(self, point):
2139 self.cut_list.remove(point)
2140 self.uploadCuesheet()
2141 self.showAfterCuesheetOperation()
2143 def showAfterCuesheetOperation(self):
2144 if isinstance(self, InfoBarShowHide):
2147 def __getCuesheet(self):
2148 service = self.session.nav.getCurrentService()
2151 return service.cueSheet()
2153 def uploadCuesheet(self):
2154 cue = self.__getCuesheet()
2157 print "upload failed, no cuesheet interface"
2159 cue.setCutList(self.cut_list)
2161 def downloadCuesheet(self):
2162 cue = self.__getCuesheet()
2165 print "download failed, no cuesheet interface"
2168 self.cut_list = cue.getCutList()
2170 class InfoBarSummary(Screen):
2172 <screen position="0,0" size="132,64">
2173 <widget source="global.CurrentTime" render="Label" position="62,46" size="82,18" font="Regular;16" >
2174 <convert type="ClockToText">WithSeconds</convert>
2176 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="82,18" zPosition="1" >
2177 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2178 <convert type="ConditionalShowHide">Blink</convert>
2180 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2181 <convert type="ServiceName">Name</convert>
2183 <widget source="session.Event_Now" render="Progress" position="6,46" size="46,18" borderWidth="1" >
2184 <convert type="EventTime">Progress</convert>
2188 # for picon: (path="piconlcd" will use LCD picons)
2189 # <widget source="session.CurrentService" render="Picon" position="6,0" size="120,64" path="piconlcd" >
2190 # <convert type="ServiceName">Reference</convert>
2193 class InfoBarSummarySupport:
2197 def createSummary(self):
2198 return InfoBarSummary
2200 class InfoBarMoviePlayerSummary(Screen):
2202 <screen position="0,0" size="132,64">
2203 <widget source="global.CurrentTime" render="Label" position="62,46" size="64,18" font="Regular;16" halign="right" >
2204 <convert type="ClockToText">WithSeconds</convert>
2206 <widget source="session.RecordState" render="FixedLabel" text=" " position="62,46" size="64,18" zPosition="1" >
2207 <convert type="ConfigEntryTest">config.usage.blinking_display_clock_during_recording,True,CheckSourceBoolean</convert>
2208 <convert type="ConditionalShowHide">Blink</convert>
2210 <widget source="session.CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
2211 <convert type="ServiceName">Name</convert>
2213 <widget source="session.CurrentService" render="Progress" position="6,46" size="56,18" borderWidth="1" >
2214 <convert type="ServicePosition">Position</convert>
2218 class InfoBarMoviePlayerSummarySupport:
2222 def createSummary(self):
2223 return InfoBarMoviePlayerSummary
2225 class InfoBarTeletextPlugin:
2227 self.teletext_plugin = None
2229 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
2230 self.teletext_plugin = p
2232 if self.teletext_plugin is not None:
2233 self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
2235 "startTeletext": (self.startTeletext, _("View teletext..."))
2238 print "no teletext plugin found!"
2240 def startTeletext(self):
2241 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
2243 class InfoBarSubtitleSupport(object):
2245 object.__init__(self)
2246 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
2247 self.subtitle_window.setAnimationMode(0)
2248 self.__subtitles_enabled = False
2250 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2252 iPlayableService.evEnd: self.__serviceStopped,
2253 iPlayableService.evUpdatedInfo: self.__updatedInfo
2255 self.cached_subtitle_checked = False
2256 self.__selected_subtitle = None
2258 def __serviceStopped(self):
2259 self.cached_subtitle_checked = False
2260 if self.__subtitles_enabled:
2261 self.subtitle_window.hide()
2262 self.__subtitles_enabled = False
2263 self.__selected_subtitle = None
2265 def __updatedInfo(self):
2266 if not self.__selected_subtitle:
2267 subtitle = self.getCurrentServiceSubtitle()
2268 self.setSelectedSubtitle(subtitle and subtitle.getCachedSubtitle())
2269 if self.__selected_subtitle:
2270 self.setSubtitlesEnable(True)
2272 def getCurrentServiceSubtitle(self):
2273 service = self.session.nav.getCurrentService()
2274 return service and service.subtitle()
2276 def setSubtitlesEnable(self, enable=True):
2277 subtitle = self.getCurrentServiceSubtitle()
2279 if self.__selected_subtitle:
2280 if subtitle and not self.__subtitles_enabled:
2281 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
2282 self.subtitle_window.show()
2283 self.__subtitles_enabled = True
2286 subtitle.disableSubtitles(self.subtitle_window.instance)
2287 self.__selected_subtitle = None
2288 self.__subtitles_enabled = False
2289 self.subtitle_window.hide()
2291 def setSelectedSubtitle(self, subtitle):
2292 self.__selected_subtitle = subtitle
2294 subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
2295 selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
2297 class InfoBarServiceErrorPopupSupport:
2299 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
2301 iPlayableService.evTuneFailed: self.__tuneFailed,
2302 iPlayableService.evStart: self.__serviceStarted
2304 self.__serviceStarted()
2306 def __serviceStarted(self):
2307 self.last_error = None
2308 Notifications.RemovePopup(id = "ZapError")
2310 def __tuneFailed(self):
2311 service = self.session.nav.getCurrentService()
2312 info = service and service.info()
2313 error = info and info.getInfo(iServiceInformation.sDVBState)
2315 if error == self.last_error:
2318 self.last_error = error
2321 eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2322 eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2323 eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2324 eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2325 eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2326 eDVBServicePMTHandler.eventNewProgramInfo: None,
2327 eDVBServicePMTHandler.eventTuned: None,
2328 eDVBServicePMTHandler.eventSOF: None,
2329 eDVBServicePMTHandler.eventEOF: None,
2330 eDVBServicePMTHandler.eventMisconfiguration: _("Service unavailable!\nCheck tuner configuration!"),
2331 }.get(error) #this returns None when the key not exist in the dict
2333 if error is not None:
2334 Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
2336 Notifications.RemovePopup(id = "ZapError")
2340 self.hdmiInServiceRef = eServiceReference('8192:0:1:0:0:0:0:0:0:0:')
2342 if SystemInfo.get("HdmiInSupport", False):
2343 self.addExtension((self.getShowHdmiInName, self.HDMIIn, lambda: True), None)
2344 self.addExtension((self.getShowHdmiInPIPName, self.HDMIInPIP, self.showHDMIPIPMenu), None)
2346 def getShowHdmiInName(self):
2347 curref = self.session.nav.getCurrentlyPlayingServiceReference()
2348 if curref and curref.type == 8192:
2349 name = _("Disable HDMI-IN on Main Screen")
2351 name = _("Enable HDMI-IN on Main Screen")
2355 def getShowHdmiInPIPName(self):
2356 return _("Enable HDMI-IN on PIP")
2358 def showHDMIPIPMenu(self):
2359 _pipAvailable = SystemInfo.get("NumVideoDecoders", 1) > 1
2361 hdmiin_enabled = False
2362 curref = self.session.nav.getCurrentlyPlayingServiceReference()
2363 if curref and curref.type == 8192:
2364 hdmiin_enabled = True
2366 hdmiin_pip_shown = False
2367 if self.session.pipshown:
2368 pipref=self.session.pip.getCurrentService()
2369 if pipref and pipref.type == 8192:
2370 hdmiin_pip_shown = True
2372 return _pipAvailable and not hdmiin_enabled and not hdmiin_pip_shown
2374 def getCurrentServiceRef(self):
2375 slist = self.servicelist
2376 currentServiceSref = slist.servicelist.getCurrent()
2377 return currentServiceSref
2380 curref = self.session.nav.getCurrentlyPlayingServiceReference()
2381 if curref and curref.type == 8192:
2382 self.session.nav.playService(self.getCurrentServiceRef())
2384 self.session.nav.playService(self.hdmiInServiceRef)
2386 def HDMIInPIP(self):
2387 if self.session.pipshown:
2388 del self.session.pip
2390 self.session.pip = self.session.instantiateDialog(PictureInPicture)
2391 self.session.pip.setAnimationMode(0)
2392 self.session.pip.show()
2393 newservice = self.hdmiInServiceRef
2394 if self.session.pip.playService(newservice):
2395 self.session.pipshown = True
2396 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
2398 self.session.pipshown = False
2399 del self.session.pip
2400 self.session.openWithCallback(self.close, MessageBox, _("Could not open Picture in Picture"), MessageBox.TYPE_ERROR)