1 from ChannelSelection import ChannelSelection, BouquetSelector
3 from Components.ActionMap import ActionMap, HelpableActionMap
4 from Components.ActionMap import NumberActionMap
5 from Components.BlinkingPixmap import BlinkingPixmapConditional
6 from Components.Clock import Clock
7 from Components.EventInfo import EventInfo, EventInfoProgress
8 from Components.Harddisk import harddiskmanager
9 from Components.Input import Input
10 from Components.Label import *
11 from Components.Pixmap import Pixmap, PixmapConditional
12 from Components.PluginComponent import plugins
13 from Components.ProgressBar import *
14 from Components.ServiceEventTracker import ServiceEventTracker
15 from Components.ServiceName import ServiceName
16 from Components.config import config, configElement, ConfigSubsection, configSequence, configElementBoolean
17 from Components.config import configfile, configsequencearg
19 from EpgSelection import EPGSelection
20 from Plugins.Plugin import PluginDescriptor
22 from Screen import Screen
23 from Screens.ChoiceBox import ChoiceBox
24 from Screens.Dish import Dish
25 from Screens.EventView import EventViewEPGSelect, EventViewSimple
26 from Screens.InputBox import InputBox
27 from Screens.MessageBox import MessageBox
28 from Screens.MinuteInput import MinuteInput
29 from Screens.Standby import Standby
30 from ServiceReference import ServiceReference
32 from Tools import Notifications
33 from Tools.Directories import *
35 #from enigma import eTimer, eDVBVolumecontrol, quitMainloop
42 from Components.config import config, currentConfigSelectionElement
45 from Menu import MainMenu, mdom
49 self.dishDialog = self.session.instantiateDialog(Dish)
50 self.onLayoutFinish.append(self.dishDialog.show)
52 class InfoBarShowHide:
53 """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
61 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
63 "toggleShow": self.toggleShow,
67 self.__state = self.STATE_SHOWN
70 self.onExecBegin.append(self.show)
72 self.hideTimer = eTimer()
73 self.hideTimer.timeout.get().append(self.doTimerHide)
74 self.hideTimer.start(5000, True)
76 self.onShow.append(self.__onShow)
77 self.onHide.append(self.__onHide)
80 self.__state = self.STATE_SHOWN
83 def startHideTimer(self):
84 if self.__state == self.STATE_SHOWN and not self.__locked:
85 self.hideTimer.start(5000, True)
88 self.__state = self.STATE_HIDDEN
94 def doTimerHide(self):
96 if self.__state == self.STATE_SHOWN:
100 if self.__state == self.STATE_SHOWN:
102 self.hideTimer.stop()
103 elif self.__state == self.STATE_HIDDEN:
107 self.__locked = self.__locked + 1
110 self.hideTimer.stop()
112 def unlockShow(self):
113 self.__locked = self.__locked - 1
115 self.startHideTimer()
117 # def startShow(self):
118 # self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
119 # self.__state = self.STATE_SHOWN
121 # def startHide(self):
122 # self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
123 # self.__state = self.STATE_HIDDEN
125 class NumberZap(Screen):
132 self.close(int(self["number"].getText()))
134 def keyNumberGlobal(self, number):
135 self.Timer.start(3000, True) #reset timer
136 self.field = self.field + str(number)
137 self["number"].setText(self.field)
138 if len(self.field) >= 4:
141 def __init__(self, session, number):
142 Screen.__init__(self, session)
143 self.field = str(number)
145 self["channel"] = Label(_("Channel:"))
147 self["number"] = Label(self.field)
149 self["actions"] = NumberActionMap( [ "SetupActions" ],
153 "1": self.keyNumberGlobal,
154 "2": self.keyNumberGlobal,
155 "3": self.keyNumberGlobal,
156 "4": self.keyNumberGlobal,
157 "5": self.keyNumberGlobal,
158 "6": self.keyNumberGlobal,
159 "7": self.keyNumberGlobal,
160 "8": self.keyNumberGlobal,
161 "9": self.keyNumberGlobal,
162 "0": self.keyNumberGlobal
165 self.Timer = eTimer()
166 self.Timer.timeout.get().append(self.keyOK)
167 self.Timer.start(3000, True)
169 class InfoBarPowerKey:
170 """ PowerKey stuff - handles the powerkey press and powerkey release actions"""
173 self.powerKeyTimer = eTimer()
174 self.powerKeyTimer.timeout.get().append(self.powertimer)
175 self["PowerKeyActions"] = HelpableActionMap(self, "PowerKeyActions",
177 "powerdown": self.powerdown,
178 "powerup": self.powerup,
179 "discreteStandby": (self.standby, "Go standby"),
180 "discretePowerOff": (self.quit, "Go to deep standby"),
183 def powertimer(self):
184 print "PowerOff - Now!"
188 self.standbyblocked = 0
189 self.powerKeyTimer.start(3000, True)
192 self.powerKeyTimer.stop()
193 if self.standbyblocked == 0:
194 self.standbyblocked = 1
198 self.session.open(Standby, self)
204 class InfoBarNumberZap:
205 """ Handles an initial number for NumberZapping """
207 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
209 "1": self.keyNumberGlobal,
210 "2": self.keyNumberGlobal,
211 "3": self.keyNumberGlobal,
212 "4": self.keyNumberGlobal,
213 "5": self.keyNumberGlobal,
214 "6": self.keyNumberGlobal,
215 "7": self.keyNumberGlobal,
216 "8": self.keyNumberGlobal,
217 "9": self.keyNumberGlobal,
218 "0": self.keyNumberGlobal,
221 def keyNumberGlobal(self, number):
222 # print "You pressed number " + str(number)
224 self.servicelist.recallPrevService()
227 self.session.openWithCallback(self.numberEntered, NumberZap, number)
229 def numberEntered(self, retval):
230 # print self.servicelist
232 self.zapToNumber(retval)
234 def searchNumberHelper(self, serviceHandler, num, bouquet):
235 servicelist = serviceHandler.list(bouquet)
236 if not servicelist is None:
238 serviceIterator = servicelist.getNext()
239 if not serviceIterator.valid(): #check end of list
241 if serviceIterator.flags: #assume normal dvb service have no flags set
244 if not num: #found service with searched number ?
245 return serviceIterator, 0
248 def zapToNumber(self, number):
249 bouquet = self.servicelist.bouquet_root
251 serviceHandler = eServiceCenter.getInstance()
252 if bouquet.toString().find('FROM BOUQUET "bouquets.') == -1: #FIXME HACK
253 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
255 bouquetlist = serviceHandler.list(bouquet)
256 if not bouquetlist is None:
258 bouquet = self.servicelist.appendDVBTypes(bouquetlist.getNext())
259 if not bouquet.valid(): #check end of list
261 if (bouquet.flags & eServiceReference.flagDirectory) != eServiceReference.flagDirectory:
263 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
264 if not service is None:
265 if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
266 self.servicelist.clearPath()
267 if self.servicelist.bouquet_root != bouquet:
268 self.servicelist.enterPath(self.servicelist.bouquet_root)
269 self.servicelist.enterPath(bouquet)
270 self.servicelist.setCurrentSelection(service) #select the service in servicelist
271 self.servicelist.zap()
273 config.misc.initialchannelselection = configElementBoolean("config.misc.initialchannelselection", 1);
275 class InfoBarChannelSelection:
276 """ ChannelSelection - handles the channelSelection dialog and the initial
277 channelChange actions which open the channelSelection dialog """
280 self.servicelist = self.session.instantiateDialog(ChannelSelection)
282 if config.misc.initialchannelselection.value == 1:
283 self.onShown.append(self.firstRun)
285 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
287 "switchChannelUp": self.switchChannelUp,
288 "switchChannelDown": self.switchChannelDown,
289 "zapUp": (self.zapUp, _("previous channel")),
290 "zapDown": (self.zapDown, _("next channel")),
291 "historyBack": (self.historyBack, _("previous channel in history")),
292 "historyNext": (self.historyNext, _("next channel in history"))
296 self.onShown.remove(self.firstRun)
297 config.misc.initialchannelselection.value = 0
298 config.misc.initialchannelselection.save()
299 self.switchChannelDown()
301 def historyBack(self):
302 self.servicelist.historyBack()
304 def historyNext(self):
305 self.servicelist.historyNext()
307 def switchChannelUp(self):
308 self.servicelist.moveUp()
309 self.session.execDialog(self.servicelist)
311 def switchChannelDown(self):
312 self.servicelist.moveDown()
313 self.session.execDialog(self.servicelist)
316 if currentConfigSelectionElement(config.usage.quickzap_bouquet_change) == "yes":
317 if self.servicelist.inBouquet() and self.servicelist.atBegin():
318 self.servicelist.prevBouquet()
319 self.servicelist.moveUp()
320 self.servicelist.zap()
324 if currentConfigSelectionElement(config.usage.quickzap_bouquet_change) == "yes" and self.servicelist.inBouquet() and self.servicelist.atEnd():
325 self.servicelist.nextBouquet()
327 self.servicelist.moveDown()
328 self.servicelist.zap()
332 """ Handles a menu action, to open the (main) menu """
334 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions",
336 "mainMenu": (self.mainMenu, "Enter main menu..."),
340 print "loading mainmenu XML..."
341 menu = mdom.childNodes[0]
342 assert menu.tagName == "menu", "root element in menu must be 'menu'!"
343 self.session.open(MainMenu, menu, menu.childNodes)
345 class InfoBarSimpleEventView:
346 """ Opens the Eventview for now/next """
348 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
350 "showEventInfo": (self.openEventView, _("show event details")),
353 def openEventView(self):
355 service = self.session.nav.getCurrentService()
356 ref = self.session.nav.getCurrentlyPlayingServiceReference()
357 info = service.info()
360 self.epglist.append(ptr)
363 self.epglist.append(ptr)
364 if len(self.epglist) > 0:
365 self.session.open(EventViewSimple, self.epglist[0], ServiceReference(ref), self.eventViewCallback)
367 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
368 if len(self.epglist) > 1:
369 tmp = self.epglist[0]
370 self.epglist[0]=self.epglist[1]
372 setEvent(self.epglist[0])
375 """ EPG - Opens an EPG list when the showEPGList action fires """
377 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
379 "showEventInfo": (self.openEventView, _("show EPG...")),
382 def zapToService(self, service):
383 if not service is None:
384 if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
385 self.servicelist.clearPath()
386 if self.servicelist.bouquet_root != self.epg_bouquet:
387 self.servicelist.enterPath(self.servicelist.bouquet_root)
388 self.servicelist.enterPath(self.epg_bouquet)
389 self.servicelist.setCurrentSelection(service) #select the service in servicelist
390 self.servicelist.zap()
392 def openBouquetEPG(self, bouquet, withCallback=True):
393 ptr=eEPGCache.getInstance()
395 servicelist = eServiceCenter.getInstance().list(bouquet)
396 if not servicelist is None:
398 service = servicelist.getNext()
399 if not service.valid(): #check if end of list
401 if service.flags: #ignore non playable services
403 services.append(ServiceReference(service))
405 self.epg_bouquet = bouquet
407 self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService)
409 self.session.open(EPGSelection, services, self.zapToService)
411 def closed(self, ret):
415 def openMultiServiceEPG(self, withCallback=True):
416 bouquets = self.servicelist.getBouquetList()
421 if cnt > 1: # show bouquet list
423 self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG)
425 self.session.open(BouquetSelector, bouquets, self.openBouquetEPG)
427 self.openBouquetEPG(bouquets[0][1], withCallback)
429 def openSingleServiceEPG(self):
430 ref=self.session.nav.getCurrentlyPlayingServiceReference()
431 ptr=eEPGCache.getInstance()
432 self.session.openWithCallback(self.closed, EPGSelection, ref)
434 def openEventView(self):
436 service = self.session.nav.getCurrentService()
437 ref = self.session.nav.getCurrentlyPlayingServiceReference()
438 info = service.info()
441 self.epglist.append(ptr)
444 self.epglist.append(ptr)
445 if len(self.epglist) == 0:
446 epg = eEPGCache.getInstance()
447 ptr = epg.lookupEventTime(ref, -1)
449 self.epglist.append(ptr)
450 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
452 self.epglist.append(ptr)
453 if len(self.epglist) > 0:
454 self.session.open(EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG)
456 print "no epg for the service avail.. so we show multiepg instead of eventinfo"
457 self.openMultiServiceEPG(False)
459 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
460 if len(self.epglist) > 1:
461 tmp = self.epglist[0]
462 self.epglist[0]=self.epglist[1]
464 setEvent(self.epglist[0])
469 """provides a snr/agc/ber display"""
471 self["snr"] = Label()
472 self["agc"] = Label()
473 self["ber"] = Label()
474 self["snr_percent"] = Label()
475 self["agc_percent"] = Label()
476 self["ber_count"] = Label()
477 self["snr_progress"] = ProgressBar()
478 self["agc_progress"] = ProgressBar()
479 self["ber_progress"] = ProgressBar()
480 self.timer = eTimer()
481 self.timer.timeout.get().append(self.updateTunerInfo)
482 self.timer.start(1000)
488 return (long)(log(val)/log(2))
491 def updateTunerInfo(self):
492 if self.instance.isVisible():
493 service = self.session.nav.getCurrentService()
497 if service is not None:
498 feinfo = service.frontendStatusInfo()
499 if feinfo is not None:
500 ber=feinfo.getFrontendInfo(iFrontendStatusInformation.bitErrorRate)
501 snr=feinfo.getFrontendInfo(iFrontendStatusInformation.signalPower)*100/65536
502 agc=feinfo.getFrontendInfo(iFrontendStatusInformation.signalQuality)*100/65536
503 self["snr_percent"].setText("%d%%"%(snr))
504 self["agc_percent"].setText("%d%%"%(agc))
505 self["ber_count"].setText("%d"%(ber))
506 self["snr_progress"].setValue(snr)
507 self["agc_progress"].setValue(agc)
508 self["ber_progress"].setValue(self.calc(ber))
511 """provides a current/next event info display"""
513 self["Event_Now_StartTime"] = EventInfo(self.session.nav, EventInfo.Now_StartTime)
514 self["Event_Next_StartTime"] = EventInfo(self.session.nav, EventInfo.Next_StartTime)
516 self["Event_Now"] = EventInfo(self.session.nav, EventInfo.Now)
517 self["Event_Next"] = EventInfo(self.session.nav, EventInfo.Next)
519 self["Event_Now_Duration"] = EventInfo(self.session.nav, EventInfo.Now_Remaining)
520 self["Event_Next_Duration"] = EventInfo(self.session.nav, EventInfo.Next_Duration)
522 self["Now_ProgressBar"] = EventInfoProgress(self.session.nav, EventInfo.Now)
524 class InfoBarServiceName:
526 self["ServiceName"] = ServiceName(self.session.nav)
529 """handles actions like seeking, pause"""
531 # ispause, isff, issm
532 SEEK_STATE_PLAY = (0, 0, 0, ">")
533 SEEK_STATE_PAUSE = (1, 0, 0, "||")
534 SEEK_STATE_FF_2X = (0, 2, 0, ">> 2x")
535 SEEK_STATE_FF_4X = (0, 4, 0, ">> 4x")
536 SEEK_STATE_FF_8X = (0, 8, 0, ">> 8x")
537 SEEK_STATE_FF_32X = (0, 32, 0, ">> 32x")
538 SEEK_STATE_FF_64X = (0, 64, 0, ">> 64x")
539 SEEK_STATE_FF_128X = (0, 128, 0, ">> 128x")
541 SEEK_STATE_BACK_16X = (0, -16, 0, "<< 16x")
542 SEEK_STATE_BACK_32X = (0, -32, 0, "<< 32x")
543 SEEK_STATE_BACK_64X = (0, -64, 0, "<< 64x")
544 SEEK_STATE_BACK_128X = (0, -128, 0, "<< 128x")
546 SEEK_STATE_SM_HALF = (0, 0, 2, "/2")
547 SEEK_STATE_SM_QUARTER = (0, 0, 4, "/4")
548 SEEK_STATE_SM_EIGHTH = (0, 0, 8, "/8")
551 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
553 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
554 iPlayableService.evStart: self.__serviceStarted,
556 iPlayableService.evEOF: self.__evEOF,
557 iPlayableService.evSOF: self.__evSOF,
560 class InfoBarSeekActionMap(HelpableActionMap):
561 def __init__(self, screen, *args, **kwargs):
562 HelpableActionMap.__init__(self, screen, *args, **kwargs)
565 def action(self, contexts, action):
566 if action[:5] == "seek:":
567 time = int(action[5:])
568 self.screen.seekRelative(time * 90000)
571 return HelpableActionMap.action(self, contexts, action)
573 self["SeekActions"] = InfoBarSeekActionMap(self, "InfobarSeekActions",
575 "pauseService": (self.pauseService, "pause"),
576 "unPauseService": (self.unPauseService, "continue"),
578 "seekFwd": (self.seekFwd, "skip forward"),
579 "seekFwdDown": self.seekFwdDown,
580 "seekFwdUp": self.seekFwdUp,
581 "seekBack": (self.seekBack, "skip backward"),
582 "seekBackDown": self.seekBackDown,
583 "seekBackUp": self.seekBackUp,
585 # give them a little more priority to win over color buttons
587 self.seekstate = self.SEEK_STATE_PLAY
588 self.onClose.append(self.delTimer)
590 self.fwdtimer = False
591 self.fwdKeyTimer = eTimer()
592 self.fwdKeyTimer.timeout.get().append(self.fwdTimerFire)
594 self.rwdtimer = False
595 self.rwdKeyTimer = eTimer()
596 self.rwdKeyTimer.timeout.get().append(self.rwdTimerFire)
598 self.onPlayStateChanged = [ ]
600 self.lockedBecauseOfSkipping = False
613 service = self.session.nav.getCurrentService()
617 seek = service.seek()
619 if seek is None or not seek.isCurrentlySeekable():
624 def isSeekable(self):
625 if self.getSeek() is None:
629 def __seekableStatusChanged(self):
630 print "seekable status changed!"
631 if not self.isSeekable():
632 self["SeekActions"].setEnabled(False)
633 print "not seekable, return to play"
634 self.setSeekState(self.SEEK_STATE_PLAY)
636 self["SeekActions"].setEnabled(True)
639 def __serviceStarted(self):
640 self.seekstate = self.SEEK_STATE_PLAY
642 def setSeekState(self, state):
643 service = self.session.nav.getCurrentService()
648 if not self.isSeekable():
649 if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
650 state = self.SEEK_STATE_PLAY
652 pauseable = service.pause()
654 if pauseable is None:
655 print "not pauseable."
656 state = self.SEEK_STATE_PLAY
658 oldstate = self.seekstate
659 self.seekstate = state
662 if oldstate[i] != self.seekstate[i]:
663 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
665 for c in self.onPlayStateChanged:
668 self.checkSkipShowHideLock()
672 def pauseService(self):
673 if self.seekstate == self.SEEK_STATE_PAUSE:
674 print "pause, but in fact unpause"
675 self.unPauseService()
677 if self.seekstate == self.SEEK_STATE_PLAY:
678 print "yes, playing."
680 print "no", self.seekstate
682 self.setSeekState(self.SEEK_STATE_PAUSE);
684 def unPauseService(self):
686 self.setSeekState(self.SEEK_STATE_PLAY);
688 def doSeek(self, seektime):
689 print "doseek", seektime
690 service = self.session.nav.getCurrentService()
694 seekable = self.getSeek()
698 seekable.seekTo(90 * seektime)
700 def seekFwdDown(self):
701 print "start fwd timer"
703 self.fwdKeyTimer.start(1000)
705 def seekBackDown(self):
706 print "start rewind timer"
708 self.rwdKeyTimer.start(1000)
713 self.fwdKeyTimer.stop()
714 self.fwdtimer = False
719 self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
720 self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
721 self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
722 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
723 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
724 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
725 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
726 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
727 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_PLAY,
728 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_16X,
729 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
730 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
731 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
732 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
733 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER
735 self.setSeekState(lookup[self.seekstate])
737 def seekBackUp(self):
740 self.rwdKeyTimer.stop()
741 self.rwdtimer = False
746 self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_16X,
747 self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
748 self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
749 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
750 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
751 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
752 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
753 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
754 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_BACK_32X,
755 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
756 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
757 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
758 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
759 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
760 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE
762 self.setSeekState(lookup[self.seekstate])
764 if self.seekstate == self.SEEK_STATE_PAUSE:
765 seekable = self.getSeek()
766 if seekable is not None:
767 seekable.seekRelative(-1, 3)
769 def fwdTimerFire(self):
770 print "Display seek fwd"
771 self.fwdKeyTimer.stop()
772 self.fwdtimer = False
773 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
775 def fwdSeekTo(self, minutes):
776 print "Seek", minutes, "minutes forward"
778 seekable = self.getSeek()
779 if seekable is not None:
780 seekable.seekRelative(1, minutes * 60 * 90000)
782 def rwdTimerFire(self):
784 self.rwdKeyTimer.stop()
785 self.rwdtimer = False
786 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
788 def rwdSeekTo(self, minutes):
790 self.fwdSeekTo(0 - minutes)
792 def checkSkipShowHideLock(self):
793 wantlock = self.seekstate != self.SEEK_STATE_PLAY
795 if self.lockedBecauseOfSkipping and not wantlock:
797 self.lockedBecauseOfSkipping = False
799 if wantlock and not self.lockedBecauseOfSkipping:
801 self.lockedBecauseOfSkipping = True
804 if self.seekstate != self.SEEK_STATE_PLAY:
805 self.setSeekState(self.SEEK_STATE_PAUSE)
807 #self.getSeek().seekRelative(1, -90000)
808 self.setSeekState(self.SEEK_STATE_PLAY)
810 self.setSeekState(self.SEEK_STATE_PAUSE)
813 self.setSeekState(self.SEEK_STATE_PLAY)
816 def seekRelative(self, diff):
817 seekable = self.getSeek()
818 if seekable is not None:
819 seekable.seekRelative(1, diff)
821 from Screens.PVRState import PVRState, TimeshiftState
823 class InfoBarPVRState:
824 def __init__(self, screen=PVRState):
825 self.onPlayStateChanged.append(self.__playStateChanged)
826 self.pvrStateDialog = self.session.instantiateDialog(screen)
827 self.onShow.append(self.__mayShow)
828 self.onHide.append(self.pvrStateDialog.hide)
831 if self.seekstate != self.SEEK_STATE_PLAY and self.execing:
832 self.pvrStateDialog.show()
834 def __playStateChanged(self, state):
835 playstateString = state[3]
836 self.pvrStateDialog["state"].setText(playstateString)
839 class InfoBarTimeshiftState(InfoBarPVRState):
841 InfoBarPVRState.__init__(self, screen=TimeshiftState)
844 class InfoBarShowMovies:
846 # i don't really like this class.
847 # it calls a not further specified "movie list" on up/down/movieList,
848 # so this is not more than an action map
850 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
852 "movieList": (self.showMovies, "movie list"),
853 "up": (self.showMovies, "movie list"),
854 "down": (self.showMovies, "movie list")
857 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
861 # Timeshift works the following way:
862 # demux0 demux1 "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
863 # - normal playback TUNER unused PLAY enable disable disable
864 # - user presses "yellow" button. TUNER record PAUSE enable disable enable
865 # - user presess pause again FILE record PLAY enable disable enable
866 # - user fast forwards FILE record FF enable disable enable
867 # - end of timeshift buffer reached TUNER record PLAY enable enable disable
868 # - user backwards FILE record BACK # !! enable disable enable
872 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
873 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
874 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
875 # - the user can now PVR around
876 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
877 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
879 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
880 # - if the user rewinds, or press pause, timeshift will be activated again
882 # note that a timeshift can be enabled ("recording") and
883 # activated (currently time-shifting).
885 class InfoBarTimeshift:
887 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
889 "timeshiftStart": (self.startTimeshift, "start timeshift"), # the "yellow key"
890 "timeshiftStop": (self.stopTimeshift, "stop timeshift") # currently undefined :), probably 'TV'
892 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
894 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
895 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause # something like "backward key"
896 }, prio=-1) # priority over record
898 self.timeshift_enabled = 0
899 self.timeshift_state = 0
900 self.ts_pause_timer = eTimer()
901 self.ts_pause_timer.timeout.get().append(self.pauseService)
903 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
905 iPlayableService.evStart: self.__serviceStarted,
906 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
909 def getTimeshift(self):
910 service = self.session.nav.getCurrentService()
911 return service.timeshift()
913 def startTimeshift(self):
914 print "enable timeshift"
915 ts = self.getTimeshift()
917 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
918 print "no ts interface"
921 if self.timeshift_enabled:
922 print "hu, timeshift already enabled?"
924 if not ts.startTimeshift():
926 self.timeshift_enabled = 1
927 self.pvrStateDialog["timeshift"].setRelative(time.time())
930 self.setSeekState(self.SEEK_STATE_PAUSE)
932 # enable the "TimeshiftEnableActions", which will override
933 # the startTimeshift actions
934 self.__seekableStatusChanged()
936 print "timeshift failed"
938 def stopTimeshift(self):
939 if not self.timeshift_enabled:
941 print "disable timeshift"
942 ts = self.getTimeshift()
945 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
947 def stopTimeshiftConfirmed(self, confirmed):
951 ts = self.getTimeshift()
956 self.timeshift_enabled = 0
959 self.__seekableStatusChanged()
961 # activates timeshift, and seeks to (almost) the end
962 def activateTimeshiftEnd(self):
963 ts = self.getTimeshift()
968 if ts.isTimeshiftActive():
969 print "!! activate timeshift called - but shouldn't this be a normal pause?"
972 self.setSeekState(self.SEEK_STATE_PLAY)
973 ts.activateTimeshift()
976 # same as activateTimeshiftEnd, but pauses afterwards.
977 def activateTimeshiftEndAndPause(self):
978 state = self.seekstate
979 self.activateTimeshiftEnd()
981 # well, this is "andPause", but it could be pressed from pause,
982 # when pausing on the (fake-)"live" picture, so an un-pause
985 print "now, pauseService"
986 if state == self.SEEK_STATE_PLAY:
987 print "is PLAYING, start pause timer"
988 self.ts_pause_timer.start(200, 1)
991 self.unPauseService()
993 def __seekableStatusChanged(self):
996 print "self.isSeekable", self.isSeekable()
997 print "self.timeshift_enabled", self.timeshift_enabled
999 # when this service is not seekable, but timeshift
1000 # is enabled, this means we can activate
1002 if not self.isSeekable() and self.timeshift_enabled:
1005 print "timeshift activate:", enabled
1006 self["TimeshiftActivateActions"].setEnabled(enabled)
1008 def __serviceStarted(self):
1009 self.timeshift_enabled = False
1010 self.__seekableStatusChanged()
1012 from RecordTimer import parseEvent
1014 class InfoBarInstantRecord:
1015 """Instant Record - handles the instantRecord action in order to
1016 start/stop instant records"""
1018 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1020 "instantRecord": (self.instantRecord, "Instant Record..."),
1022 self.recording = None
1023 self["BlinkingPoint"] = BlinkingPixmapConditional()
1024 self["BlinkingPoint"].hide()
1025 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
1027 def stopCurrentRecording(self):
1028 self.session.nav.RecordTimer.removeEntry(self.recording)
1029 self.recording = None
1031 def startInstantRecording(self, limitEvent = False):
1032 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1034 # try to get event info
1037 service = self.session.nav.getCurrentService()
1038 epg = eEPGCache.getInstance()
1039 event = epg.lookupEventTime(serviceref, -1, 0)
1041 info = service.info()
1042 ev = info.getEvent(0)
1048 end = time.time() + 3600 * 10
1049 name = "instant record"
1053 if event is not None:
1054 curEvent = parseEvent(event)
1056 description = curEvent[3]
1057 eventid = curEvent[4]
1062 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1064 data = (begin, end, name, description, eventid)
1066 self.recording = self.session.nav.recordWithTimer(serviceref, *data)
1067 self.recording.dontSave = True
1069 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
1071 def isInstantRecordRunning(self):
1072 if self.recording != None:
1073 if self.recording.isRunning():
1077 def recordQuestionCallback(self, answer):
1078 if answer is None or answer[1] == "no":
1081 if self.isInstantRecordRunning():
1082 if answer[1] == "manualduration":
1083 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1085 self.stopCurrentRecording()
1088 if answer[1] == "event":
1090 if answer[1] == "manualduration":
1091 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1092 self.startInstantRecording(limitEvent = limitEvent)
1094 def inputCallback(self, value):
1095 if value is not None:
1096 print "stopping recording after", int(value), "minutes."
1097 if self.recording is not None:
1098 self.recording.end = time.time() + 60 * int(value)
1099 self.session.nav.RecordTimer.timeChanged(self.recording)
1101 def instantRecord(self):
1103 stat = os.stat(resolveFilename(SCOPE_HDD))
1105 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1108 if self.isInstantRecordRunning():
1109 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, title=_("A recording is currently running.\nWhat do you want to do?"), list=[(_("stop recording"), "yes"), (_("enter recording duration"), "manualduration"), (_("do nothing"), "no")])
1110 # self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Do you want to stop the current\n(instant) recording?"))
1112 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, title=_("Start recording?"), list=[(_("record indefinitely"), "indefinitely"), (_("stop after current event"), "event"), (_("enter recording duration"), "manualduration"),(_("don't record"), "no")])
1113 #self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Start recording?"))
1115 from Screens.AudioSelection import AudioSelection
1117 class InfoBarAudioSelection:
1119 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1121 "audioSelection": (self.audioSelection, "Audio Options..."),
1124 def audioSelection(self):
1125 service = self.session.nav.getCurrentService()
1126 audio = service.audioTracks()
1127 n = audio.getNumberOfTracks()
1129 self.session.open(AudioSelection, audio)
1131 from Screens.SubserviceSelection import SubserviceSelection
1133 class InfoBarSubserviceSelection:
1135 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1137 "subserviceSelection": (self.subserviceSelection, "Subservice list..."),
1140 def subserviceSelection(self):
1141 service = self.session.nav.getCurrentService()
1142 subservices = service.subServices()
1143 n = subservices.getNumberOfSubservices()
1145 self.session.openWithCallback(self.subserviceSelected, SubserviceSelection, subservices)
1147 def subserviceSelected(self, service):
1148 if not service is None:
1149 self.session.nav.playService(service)
1151 class InfoBarAdditionalInfo:
1153 self["DolbyActive"] = Pixmap()
1154 self["CryptActive"] = Pixmap()
1155 self["FormatActive"] = Pixmap()
1157 self["ButtonRed"] = PixmapConditional(withTimer = False)
1158 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1159 self.onLayoutFinish.append(self["ButtonRed"].update)
1160 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1161 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1162 self.onLayoutFinish.append(self["ButtonRedText"].update)
1164 self["ButtonGreen"] = Pixmap()
1165 self["ButtonGreenText"] = Label(_("Subservices"))
1167 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1168 self["ButtonYellow"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1169 self["ButtonYellowText"] = LabelConditional(text = _("Timeshifting"), withTimer = False)
1170 self["ButtonYellowText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1171 self.onLayoutFinish.append(self["ButtonYellow"].update)
1172 self.onLayoutFinish.append(self["ButtonYellowText"].update)
1174 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1175 self["ButtonBlue"].setConnect(lambda: False)
1176 self["ButtonBlueText"] = LabelConditional(text = _("Extensions"), withTimer = False)
1177 self["ButtonBlueText"].setConnect(lambda: False)
1178 self.onLayoutFinish.append(self["ButtonBlue"].update)
1179 self.onLayoutFinish.append(self["ButtonBlueText"].update)
1181 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1183 def hideSubServiceIndication(self):
1184 self["ButtonGreen"].hide()
1185 self["ButtonGreenText"].hide()
1187 def showSubServiceIndication(self):
1188 self["ButtonGreen"].show()
1189 self["ButtonGreenText"].show()
1191 def checkFormat(self, service):
1192 info = service.info()
1193 if info is not None:
1194 aspect = info.getInfo(iServiceInformation.sAspect)
1195 if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1196 self["FormatActive"].show()
1198 self["FormatActive"].hide()
1200 def checkSubservices(self, service):
1201 if service.subServices().getNumberOfSubservices() > 0:
1202 self.showSubServiceIndication()
1204 self.hideSubServiceIndication()
1206 def checkDolby(self, service):
1209 audio = service.audioTracks()
1210 if audio is not None:
1211 n = audio.getNumberOfTracks()
1213 i = audio.getTrackInfo(x)
1214 description = i.getDescription();
1215 if description.find("AC3") != -1 or description.find("DTS") != -1:
1219 self["DolbyActive"].show()
1221 self["DolbyActive"].hide()
1223 def checkCrypted(self, service):
1224 info = service.info()
1225 if info is not None:
1226 if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1227 self["CryptActive"].show()
1229 self["CryptActive"].hide()
1231 def gotServiceEvent(self, ev):
1232 service = self.session.nav.getCurrentService()
1233 if ev == iPlayableService.evUpdatedEventInfo:
1234 self.checkSubservices(service)
1235 self.checkFormat(service)
1236 elif ev == iPlayableService.evUpdatedInfo:
1237 self.checkCrypted(service)
1238 self.checkDolby(service)
1239 elif ev == iPlayableService.evEnd:
1240 self.hideSubServiceIndication()
1241 self["CryptActive"].hide()
1242 self["DolbyActive"].hide()
1243 self["FormatActive"].hide()
1245 class InfoBarNotifications:
1247 self.onExecBegin.append(self.checkNotifications)
1248 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1250 def checkNotificationsIfExecing(self):
1252 self.checkNotifications()
1254 def checkNotifications(self):
1255 if len(Notifications.notifications):
1256 n = Notifications.notifications[0]
1257 Notifications.notifications = Notifications.notifications[1:]
1261 self.session.openWithCallback(cb, *n[1:])
1263 self.session.open(*n[1:])
1265 class InfoBarServiceNotifications:
1267 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1269 iPlayableService.evEnd: self.serviceHasEnded
1272 def serviceHasEnded(self):
1273 print "service end!"
1276 self.setSeekState(self.SEEK_STATE_PLAY)
1280 class InfoBarCueSheetSupport:
1286 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions",
1288 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1289 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1290 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1294 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1296 iPlayableService.evStart: self.__serviceStarted,
1299 def __serviceStarted(self):
1300 print "new service started! trying to download cuts!"
1301 self.downloadCuesheet()
1303 def __getSeekable(self):
1304 service = self.session.nav.getCurrentService()
1307 return service.seek()
1309 def cueGetCurrentPosition(self):
1310 seek = self.__getSeekable()
1313 r = seek.getPlayPosition()
1318 def jumpPreviousNextMark(self, cmp, alternative=None):
1319 current_pos = self.cueGetCurrentPosition()
1320 if current_pos is None:
1322 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1323 if mark is not None:
1325 elif alternative is not None:
1330 seekable = self.__getSeekable()
1331 if seekable is not None:
1332 seekable.seekTo(pts)
1334 def jumpPreviousMark(self):
1335 # we add 2 seconds, so if the play position is <2s after
1336 # the mark, the mark before will be used
1337 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1339 def jumpNextMark(self):
1340 self.jumpPreviousNextMark(lambda x: x)
1342 def getNearestCutPoint(self, pts, cmp=abs):
1345 for cp in self.cut_list:
1346 diff = cmp(cp[0] - pts)
1347 if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1351 def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
1352 current_pos = self.cueGetCurrentPosition()
1353 if current_pos is None:
1354 print "not seekable"
1357 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1359 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1361 return nearest_cutpoint
1363 self.removeMark(nearest_cutpoint)
1364 elif not onlyremove and not onlyreturn:
1365 self.addMark((current_pos, self.CUT_TYPE_MARK))
1370 def addMark(self, point):
1371 bisect.insort(self.cut_list, point)
1372 self.uploadCuesheet()
1374 def removeMark(self, point):
1375 self.cut_list.remove(point)
1376 self.uploadCuesheet()
1378 def __getCuesheet(self):
1379 service = self.session.nav.getCurrentService()
1382 return service.cueSheet()
1384 def uploadCuesheet(self):
1385 cue = self.__getCuesheet()
1388 print "upload failed, no cuesheet interface"
1390 cue.setCutList(self.cut_list)
1392 def downloadCuesheet(self):
1393 cue = self.__getCuesheet()
1396 print "upload failed, no cuesheet interface"
1398 self.cut_list = cue.getCutList()
1400 class InfoBarSummary(Screen):
1402 <screen position="0,0" size="132,64">
1403 <widget name="Clock" position="50,46" size="82,18" font="Regular;16" />
1404 <widget name="CurrentService" position="0,4" size="132,42" font="Regular;18" />
1407 def __init__(self, session, parent):
1408 Screen.__init__(self, session)
1409 self["CurrentService"] = ServiceName(self.session.nav)
1410 self["Clock"] = Clock()
1412 class InfoBarSummarySupport:
1416 def createSummary(self):
1417 return InfoBarSummary
1419 class InfoBarTeletextPlugin:
1421 self.teletext_plugin = None
1423 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
1424 self.teletext_plugin = p
1426 if self.teletext_plugin is not None:
1427 self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
1429 "startTeletext": (self.startTeletext, "View teletext...")
1432 print "no teletext plugin found!"
1434 def startTeletext(self):
1435 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())