1 from Screen import Screen
2 from Components.ActionMap import ActionMap, HelpableActionMap
3 from Components.ActionMap import NumberActionMap
4 from Components.Label import *
5 from Components.ProgressBar import *
6 from Components.config import configfile, configsequencearg
7 from Components.config import config, configElement, ConfigSubsection, configSequence
8 from ChannelSelection import ChannelSelection, BouquetSelector
10 from Components.Pixmap import Pixmap, PixmapConditional
11 from Components.BlinkingPixmap import BlinkingPixmapConditional
12 from Components.ServiceName import ServiceName
13 from Components.EventInfo import EventInfo, EventInfoProgress
15 from ServiceReference import ServiceReference
16 from EpgSelection import EPGSelection
18 from Screens.MessageBox import MessageBox
19 from Screens.ChoiceBox import ChoiceBox
20 from Screens.Dish import Dish
21 from Screens.Standby import Standby
22 from Screens.EventView import EventViewEPGSelect, EventViewSimple
23 from Screens.MinuteInput import MinuteInput
24 from Components.Harddisk import harddiskmanager
26 from Components.ServiceEventTracker import ServiceEventTracker
28 from Tools import Notifications
29 from Tools.Directories import *
31 #from enigma import eTimer, eDVBVolumecontrol, quitMainloop
38 from Components.config import config, currentConfigSelectionElement
41 from Menu import MainMenu, mdom
45 self.dishDialog = self.session.instantiateDialog(Dish)
46 self.onLayoutFinish.append(self.dishDialog.show)
48 class InfoBarShowHide:
49 """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
57 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
59 "toggleShow": self.toggleShow,
63 self.__state = self.STATE_SHOWN
66 self.onExecBegin.append(self.show)
68 self.hideTimer = eTimer()
69 self.hideTimer.timeout.get().append(self.doTimerHide)
70 self.hideTimer.start(5000, True)
72 self.onShow.append(self.__onShow)
73 self.onHide.append(self.__onHide)
76 self.__state = self.STATE_SHOWN
79 def startHideTimer(self):
80 if self.__state == self.STATE_SHOWN and not self.__locked:
81 self.hideTimer.start(5000, True)
84 self.__state = self.STATE_HIDDEN
90 def doTimerHide(self):
92 if self.__state == self.STATE_SHOWN:
96 if self.__state == self.STATE_SHOWN:
99 elif self.__state == self.STATE_HIDDEN:
103 self.__locked = self.__locked + 1
106 self.hideTimer.stop()
108 def unlockShow(self):
109 self.__locked = self.__locked - 1
111 self.startHideTimer()
113 # def startShow(self):
114 # self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
115 # self.__state = self.STATE_SHOWN
117 # def startHide(self):
118 # self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
119 # self.__state = self.STATE_HIDDEN
121 class NumberZap(Screen):
128 self.close(int(self["number"].getText()))
130 def keyNumberGlobal(self, number):
131 self.Timer.start(3000, True) #reset timer
132 self.field = self.field + str(number)
133 self["number"].setText(self.field)
134 if len(self.field) >= 4:
137 def __init__(self, session, number):
138 Screen.__init__(self, session)
139 self.field = str(number)
141 self["channel"] = Label(_("Channel:"))
143 self["number"] = Label(self.field)
145 self["actions"] = NumberActionMap( [ "SetupActions" ],
149 "1": self.keyNumberGlobal,
150 "2": self.keyNumberGlobal,
151 "3": self.keyNumberGlobal,
152 "4": self.keyNumberGlobal,
153 "5": self.keyNumberGlobal,
154 "6": self.keyNumberGlobal,
155 "7": self.keyNumberGlobal,
156 "8": self.keyNumberGlobal,
157 "9": self.keyNumberGlobal,
158 "0": self.keyNumberGlobal
161 self.Timer = eTimer()
162 self.Timer.timeout.get().append(self.keyOK)
163 self.Timer.start(3000, True)
165 class InfoBarPowerKey:
166 """ PowerKey stuff - handles the powerkey press and powerkey release actions"""
169 self.powerKeyTimer = eTimer()
170 self.powerKeyTimer.timeout.get().append(self.powertimer)
171 self["PowerKeyActions"] = HelpableActionMap(self, "PowerKeyActions",
173 "powerdown": self.powerdown,
174 "powerup": self.powerup,
175 "discreteStandby": (self.standby, "Go standby"),
176 "discretePowerOff": (self.quit, "Go to deep standby"),
179 def powertimer(self):
180 print "PowerOff - Now!"
184 self.standbyblocked = 0
185 self.powerKeyTimer.start(3000, True)
188 self.powerKeyTimer.stop()
189 if self.standbyblocked == 0:
190 self.standbyblocked = 1
194 self.session.open(Standby, self)
200 class InfoBarNumberZap:
201 """ Handles an initial number for NumberZapping """
203 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
205 "1": self.keyNumberGlobal,
206 "2": self.keyNumberGlobal,
207 "3": self.keyNumberGlobal,
208 "4": self.keyNumberGlobal,
209 "5": self.keyNumberGlobal,
210 "6": self.keyNumberGlobal,
211 "7": self.keyNumberGlobal,
212 "8": self.keyNumberGlobal,
213 "9": self.keyNumberGlobal,
214 "0": self.keyNumberGlobal,
217 def keyNumberGlobal(self, number):
218 # print "You pressed number " + str(number)
220 self.servicelist.recallPrevService()
223 self.session.openWithCallback(self.numberEntered, NumberZap, number)
225 def numberEntered(self, retval):
226 # print self.servicelist
228 self.zapToNumber(retval)
230 def searchNumberHelper(self, serviceHandler, num, bouquet):
231 servicelist = serviceHandler.list(bouquet)
232 if not servicelist is None:
234 serviceIterator = servicelist.getNext()
235 if not serviceIterator.valid(): #check end of list
237 if serviceIterator.flags: #assume normal dvb service have no flags set
240 if not num: #found service with searched number ?
241 return serviceIterator, 0
244 def zapToNumber(self, number):
245 bouquet = self.servicelist.bouquet_root
247 serviceHandler = eServiceCenter.getInstance()
248 if bouquet.toString().find('FROM BOUQUET "bouquets.') == -1: #FIXME HACK
249 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
251 bouquetlist = serviceHandler.list(bouquet)
252 if not bouquetlist is None:
254 bouquet = self.servicelist.appendDVBTypes(bouquetlist.getNext())
255 if not bouquet.valid(): #check end of list
257 if (bouquet.flags & eServiceReference.flagDirectory) != eServiceReference.flagDirectory:
259 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
260 if not service is None:
261 if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
262 self.servicelist.clearPath()
263 if self.servicelist.bouquet_root != bouquet:
264 self.servicelist.enterPath(self.servicelist.bouquet_root)
265 self.servicelist.enterPath(bouquet)
266 self.servicelist.setCurrentSelection(service) #select the service in servicelist
267 self.servicelist.zap()
269 class InfoBarChannelSelection:
270 """ ChannelSelection - handles the channelSelection dialog and the initial
271 channelChange actions which open the channelSelection dialog """
274 self.servicelist = self.session.instantiateDialog(ChannelSelection)
276 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
278 "switchChannelUp": self.switchChannelUp,
279 "switchChannelDown": self.switchChannelDown,
280 "zapUp": (self.zapUp, _("previous channel")),
281 "zapDown": (self.zapDown, _("next channel")),
282 "historyBack": (self.historyBack, _("previous channel in history")),
283 "historyNext": (self.historyNext, _("next channel in history"))
286 def historyBack(self):
287 self.servicelist.historyBack()
289 def historyNext(self):
290 self.servicelist.historyNext()
292 def switchChannelUp(self):
293 self.servicelist.moveUp()
294 self.session.execDialog(self.servicelist)
296 def switchChannelDown(self):
297 self.servicelist.moveDown()
298 self.session.execDialog(self.servicelist)
301 if currentConfigSelectionElement(config.usage.quickzap_bouquet_change) == "yes":
302 if self.servicelist.inBouquet() and self.servicelist.atBegin():
303 self.servicelist.prevBouquet()
304 self.servicelist.moveUp()
305 self.servicelist.zap()
309 if currentConfigSelectionElement(config.usage.quickzap_bouquet_change) == "yes" and self.servicelist.inBouquet() and self.servicelist.atEnd():
310 self.servicelist.nextBouquet()
312 self.servicelist.moveDown()
313 self.servicelist.zap()
317 """ Handles a menu action, to open the (main) menu """
319 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions",
321 "mainMenu": (self.mainMenu, "Enter main menu..."),
325 print "loading mainmenu XML..."
326 menu = mdom.childNodes[0]
327 assert menu.tagName == "menu", "root element in menu must be 'menu'!"
328 self.session.open(MainMenu, menu, menu.childNodes)
330 class InfoBarSimpleEventView:
331 """ Opens the Eventview for now/next """
333 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
335 "showEventInfo": (self.openEventView, _("show event details")),
338 def openEventView(self):
340 service = self.session.nav.getCurrentService()
341 ref = self.session.nav.getCurrentlyPlayingServiceReference()
342 info = service.info()
345 self.epglist.append(ptr)
348 self.epglist.append(ptr)
349 if len(self.epglist) > 0:
350 self.session.open(EventViewSimple, self.epglist[0], ServiceReference(ref), self.eventViewCallback)
352 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
353 if len(self.epglist) > 1:
354 tmp = self.epglist[0]
355 self.epglist[0]=self.epglist[1]
357 setEvent(self.epglist[0])
360 """ EPG - Opens an EPG list when the showEPGList action fires """
362 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
364 "showEventInfo": (self.openEventView, _("show EPG...")),
367 def zapToService(self, service):
368 if not service is None:
369 if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
370 self.servicelist.clearPath()
371 if self.servicelist.bouquet_root != self.epg_bouquet:
372 self.servicelist.enterPath(self.servicelist.bouquet_root)
373 self.servicelist.enterPath(self.epg_bouquet)
374 self.servicelist.setCurrentSelection(service) #select the service in servicelist
375 self.servicelist.zap()
377 def openBouquetEPG(self, bouquet, withCallback=True):
378 ptr=eEPGCache.getInstance()
380 servicelist = eServiceCenter.getInstance().list(bouquet)
381 if not servicelist is None:
383 service = servicelist.getNext()
384 if not service.valid(): #check if end of list
386 if service.flags: #ignore non playable services
388 services.append(ServiceReference(service))
390 self.epg_bouquet = bouquet
392 self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService)
394 self.session.open(EPGSelection, services, self.zapToService)
396 def closed(self, ret):
400 def openMultiServiceEPG(self, withCallback=True):
401 bouquets = self.servicelist.getBouquetList()
406 if cnt > 1: # show bouquet list
408 self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG)
410 self.session.open(BouquetSelector, bouquets, self.openBouquetEPG)
412 self.openBouquetEPG(bouquets[0][1], withCallback)
414 def openSingleServiceEPG(self):
415 ref=self.session.nav.getCurrentlyPlayingServiceReference()
416 ptr=eEPGCache.getInstance()
417 self.session.openWithCallback(self.closed, EPGSelection, ref)
419 def openEventView(self):
421 service = self.session.nav.getCurrentService()
422 ref = self.session.nav.getCurrentlyPlayingServiceReference()
423 info = service.info()
426 self.epglist.append(ptr)
429 self.epglist.append(ptr)
430 if len(self.epglist) == 0:
431 epg = eEPGCache.getInstance()
432 ptr = epg.lookupEventTime(ref, -1)
434 self.epglist.append(ptr)
435 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
437 self.epglist.append(ptr)
438 if len(self.epglist) > 0:
439 self.session.open(EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG)
441 print "no epg for the service avail.. so we show multiepg instead of eventinfo"
442 self.openMultiServiceEPG(False)
444 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
445 if len(self.epglist) > 1:
446 tmp = self.epglist[0]
447 self.epglist[0]=self.epglist[1]
449 setEvent(self.epglist[0])
454 """provides a snr/agc/ber display"""
456 self["snr"] = Label()
457 self["agc"] = Label()
458 self["ber"] = Label()
459 self["snr_percent"] = Label()
460 self["agc_percent"] = Label()
461 self["ber_count"] = Label()
462 self["snr_progress"] = ProgressBar()
463 self["agc_progress"] = ProgressBar()
464 self["ber_progress"] = ProgressBar()
465 self.timer = eTimer()
466 self.timer.timeout.get().append(self.updateTunerInfo)
467 self.timer.start(1000)
473 return (long)(log(val)/log(2))
476 def updateTunerInfo(self):
477 if self.instance.isVisible():
478 service = self.session.nav.getCurrentService()
482 if service is not None:
483 feinfo = service.frontendStatusInfo()
484 if feinfo is not None:
485 ber=feinfo.getFrontendInfo(iFrontendStatusInformation.bitErrorRate)
486 snr=feinfo.getFrontendInfo(iFrontendStatusInformation.signalPower)*100/65536
487 agc=feinfo.getFrontendInfo(iFrontendStatusInformation.signalQuality)*100/65536
488 self["snr_percent"].setText("%d%%"%(snr))
489 self["agc_percent"].setText("%d%%"%(agc))
490 self["ber_count"].setText("%d"%(ber))
491 self["snr_progress"].setValue(snr)
492 self["agc_progress"].setValue(agc)
493 self["ber_progress"].setValue(self.calc(ber))
496 """provides a current/next event info display"""
498 self["Event_Now_StartTime"] = EventInfo(self.session.nav, EventInfo.Now_StartTime)
499 self["Event_Next_StartTime"] = EventInfo(self.session.nav, EventInfo.Next_StartTime)
501 self["Event_Now"] = EventInfo(self.session.nav, EventInfo.Now)
502 self["Event_Next"] = EventInfo(self.session.nav, EventInfo.Next)
504 self["Event_Now_Duration"] = EventInfo(self.session.nav, EventInfo.Now_Remaining)
505 self["Event_Next_Duration"] = EventInfo(self.session.nav, EventInfo.Next_Duration)
507 self["Now_ProgressBar"] = EventInfoProgress(self.session.nav, EventInfo.Now)
509 class InfoBarServiceName:
511 self["ServiceName"] = ServiceName(self.session.nav)
514 """handles actions like seeking, pause"""
516 # ispause, isff, issm
517 SEEK_STATE_PLAY = (0, 0, 0, ">")
518 SEEK_STATE_PAUSE = (1, 0, 0, "||")
519 SEEK_STATE_FF_2X = (0, 2, 0, ">> 2x")
520 SEEK_STATE_FF_4X = (0, 4, 0, ">> 4x")
521 SEEK_STATE_FF_8X = (0, 8, 0, ">> 8x")
522 SEEK_STATE_FF_32X = (0, 32, 0, ">> 32x")
523 SEEK_STATE_FF_64X = (0, 64, 0, ">> 64x")
524 SEEK_STATE_FF_128X = (0, 128, 0, ">> 128x")
526 SEEK_STATE_BACK_16X = (0, -16, 0, "<< 16x")
527 SEEK_STATE_BACK_32X = (0, -32, 0, "<< 32x")
528 SEEK_STATE_BACK_64X = (0, -64, 0, "<< 64x")
529 SEEK_STATE_BACK_128X = (0, -128, 0, "<< 128x")
531 SEEK_STATE_SM_HALF = (0, 0, 2, "/2")
532 SEEK_STATE_SM_QUARTER = (0, 0, 4, "/4")
533 SEEK_STATE_SM_EIGHTH = (0, 0, 8, "/8")
536 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
538 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
539 iPlayableService.evStart: self.__serviceStarted,
541 iPlayableService.evEOF: self.__evEOF,
542 iPlayableService.evSOF: self.__evSOF,
545 class InfoBarSeekActionMap(HelpableActionMap):
546 def __init__(self, screen, *args, **kwargs):
547 HelpableActionMap.__init__(self, screen, *args, **kwargs)
550 def action(self, contexts, action):
551 if action[:5] == "seek:":
552 time = int(action[5:])
553 self.screen.seekRelative(time * 90000)
555 HelpableActionMap.action(self, contexts, action)
557 self["SeekActions"] = InfoBarSeekActionMap(self, "InfobarSeekActions",
559 "pauseService": (self.pauseService, "pause"),
560 "unPauseService": (self.unPauseService, "continue"),
562 "seekFwd": (self.seekFwd, "skip forward"),
563 "seekFwdDown": self.seekFwdDown,
564 "seekFwdUp": self.seekFwdUp,
565 "seekBack": (self.seekBack, "skip backward"),
566 "seekBackDown": self.seekBackDown,
567 "seekBackUp": self.seekBackUp,
569 # give them a little more priority to win over color buttons
571 self.seekstate = self.SEEK_STATE_PLAY
572 self.onClose.append(self.delTimer)
574 self.fwdtimer = False
575 self.fwdKeyTimer = eTimer()
576 self.fwdKeyTimer.timeout.get().append(self.fwdTimerFire)
578 self.rwdtimer = False
579 self.rwdKeyTimer = eTimer()
580 self.rwdKeyTimer.timeout.get().append(self.rwdTimerFire)
582 self.onPlayStateChanged = [ ]
584 self.lockedBecauseOfSkipping = False
597 service = self.session.nav.getCurrentService()
601 seek = service.seek()
603 if seek is None or not seek.isCurrentlySeekable():
608 def isSeekable(self):
609 if self.getSeek() is None:
613 def __seekableStatusChanged(self):
614 print "seekable status changed!"
615 if not self.isSeekable():
616 self["SeekActions"].setEnabled(False)
617 print "not seekable, return to play"
618 self.setSeekState(self.SEEK_STATE_PLAY)
620 self["SeekActions"].setEnabled(True)
623 def __serviceStarted(self):
624 self.seekstate = self.SEEK_STATE_PLAY
626 def setSeekState(self, state):
627 service = self.session.nav.getCurrentService()
632 if not self.isSeekable():
633 if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
634 state = self.SEEK_STATE_PLAY
636 pauseable = service.pause()
638 if pauseable is None:
639 print "not pauseable."
640 state = self.SEEK_STATE_PLAY
642 oldstate = self.seekstate
643 self.seekstate = state
646 if oldstate[i] != self.seekstate[i]:
647 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
649 for c in self.onPlayStateChanged:
652 self.checkSkipShowHideLock()
656 def pauseService(self):
657 if self.seekstate == self.SEEK_STATE_PAUSE:
658 print "pause, but in fact unpause"
659 self.unPauseService()
661 if self.seekstate == self.SEEK_STATE_PLAY:
662 print "yes, playing."
664 print "no", self.seekstate
666 self.setSeekState(self.SEEK_STATE_PAUSE);
668 def unPauseService(self):
670 self.setSeekState(self.SEEK_STATE_PLAY);
672 def doSeek(self, seektime):
673 print "doseek", seektime
674 service = self.session.nav.getCurrentService()
678 seekable = self.getSeek()
682 seekable.seekTo(90 * seektime)
684 def seekFwdDown(self):
685 print "start fwd timer"
687 self.fwdKeyTimer.start(1000)
689 def seekBackDown(self):
690 print "start rewind timer"
692 self.rwdKeyTimer.start(1000)
697 self.fwdKeyTimer.stop()
698 self.fwdtimer = False
703 self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
704 self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
705 self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
706 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
707 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
708 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
709 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
710 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
711 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_PLAY,
712 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_16X,
713 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
714 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
715 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
716 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
717 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER
719 self.setSeekState(lookup[self.seekstate])
721 def seekBackUp(self):
724 self.rwdKeyTimer.stop()
725 self.rwdtimer = False
730 self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_16X,
731 self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
732 self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
733 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
734 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
735 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
736 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
737 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
738 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_BACK_32X,
739 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
740 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
741 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
742 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
743 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
744 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE
746 self.setSeekState(lookup[self.seekstate])
748 if self.seekstate == self.SEEK_STATE_PAUSE:
749 seekable = self.getSeek()
750 if seekable is not None:
751 seekable.seekRelative(-1, 2)
753 def fwdTimerFire(self):
754 print "Display seek fwd"
755 self.fwdKeyTimer.stop()
756 self.fwdtimer = False
757 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
759 def fwdSeekTo(self, minutes):
760 print "Seek", minutes, "minutes forward"
762 seekable = self.getSeek()
763 if seekable is not None:
764 seekable.seekRelative(1, minutes * 60 * 90000)
766 def rwdTimerFire(self):
768 self.rwdKeyTimer.stop()
769 self.rwdtimer = False
770 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
772 def rwdSeekTo(self, minutes):
774 self.fwdSeekTo(0 - minutes)
776 def checkSkipShowHideLock(self):
777 wantlock = self.seekstate != self.SEEK_STATE_PLAY
779 if self.lockedBecauseOfSkipping and not wantlock:
781 self.lockedBecauseOfSkipping = False
783 if wantlock and not self.lockedBecauseOfSkipping:
785 self.lockedBecauseOfSkipping = True
788 if self.seekstate != self.SEEK_STATE_PLAY:
789 self.setSeekState(self.SEEK_STATE_PAUSE)
791 #self.getSeek().seekRelative(1, -90000)
792 self.setSeekState(self.SEEK_STATE_PLAY)
794 self.setSeekState(self.SEEK_STATE_PAUSE)
797 self.setSeekState(self.SEEK_STATE_PLAY)
800 def seekRelative(self, diff):
801 seekable = self.getSeek()
802 if seekable is not None:
803 seekable.seekRelative(1, diff)
805 from Screens.PVRState import PVRState
807 class InfoBarPVRState:
809 self.onPlayStateChanged.append(self.__playStateChanged)
810 self.pvrStateDialog = self.session.instantiateDialog(PVRState)
811 self.onShow.append(self.__mayShow)
812 self.onHide.append(self.pvrStateDialog.hide)
815 if self.seekstate != self.SEEK_STATE_PLAY:
816 self.pvrStateDialog.show()
818 def __playStateChanged(self, state):
819 playstateString = state[3]
820 self.pvrStateDialog["state"].setText(playstateString)
823 class InfoBarShowMovies:
825 # i don't really like this class.
826 # it calls a not further specified "movie list" on up/down/movieList,
827 # so this is not more than an action map
829 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
831 "movieList": (self.showMovies, "movie list"),
832 "up": (self.showMovies, "movie list"),
833 "down": (self.showMovies, "movie list")
836 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
840 # Timeshift works the following way:
841 # demux0 demux1 "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
842 # - normal playback TUNER unused PLAY enable disable disable
843 # - user presses "yellow" button. TUNER record PAUSE enable disable enable
844 # - user presess pause again FILE record PLAY enable disable enable
845 # - user fast forwards FILE record FF enable disable enable
846 # - end of timeshift buffer reached TUNER record PLAY enable enable disable
847 # - user backwards FILE record BACK # !! enable disable enable
851 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
852 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
853 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
854 # - the user can now PVR around
855 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
856 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
858 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
859 # - if the user rewinds, or press pause, timeshift will be activated again
861 # note that a timeshift can be enabled ("recording") and
862 # activated (currently time-shifting).
864 class InfoBarTimeshift:
866 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
868 "timeshiftStart": (self.startTimeshift, "start timeshift"), # the "yellow key"
869 "timeshiftStop": (self.stopTimeshift, "stop timeshift") # currently undefined :), probably 'TV'
871 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
873 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
874 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause # something like "backward key"
875 }, prio=-1) # priority over record
877 self.timeshift_enabled = 0
878 self.timeshift_state = 0
879 self.ts_pause_timer = eTimer()
880 self.ts_pause_timer.timeout.get().append(self.pauseService)
882 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
884 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
887 def getTimeshift(self):
888 service = self.session.nav.getCurrentService()
889 return service.timeshift()
891 def startTimeshift(self):
892 print "enable timeshift"
893 ts = self.getTimeshift()
895 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
896 print "no ts interface"
899 if self.timeshift_enabled:
900 print "hu, timeshift already enabled?"
902 if not ts.startTimeshift():
903 self.timeshift_enabled = 1
906 self.setSeekState(self.SEEK_STATE_PAUSE)
908 # enable the "TimeshiftEnableActions", which will override
909 # the startTimeshift actions
910 self.__seekableStatusChanged()
912 print "timeshift failed"
914 def stopTimeshift(self):
915 if not self.timeshift_enabled:
917 print "disable timeshift"
918 ts = self.getTimeshift()
921 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
923 def stopTimeshiftConfirmed(self, confirmed):
927 ts = self.getTimeshift()
932 self.timeshift_enabled = 0
935 self.__seekableStatusChanged()
937 # activates timeshift, and seeks to (almost) the end
938 def activateTimeshiftEnd(self):
939 ts = self.getTimeshift()
944 if ts.isTimeshiftActive():
945 print "!! activate timeshift called - but shouldn't this be a normal pause?"
948 self.setSeekState(self.SEEK_STATE_PLAY)
949 ts.activateTimeshift()
952 # same as activateTimeshiftEnd, but pauses afterwards.
953 def activateTimeshiftEndAndPause(self):
954 state = self.seekstate
955 self.activateTimeshiftEnd()
957 # well, this is "andPause", but it could be pressed from pause,
958 # when pausing on the (fake-)"live" picture, so an un-pause
961 print "now, pauseService"
962 if state == self.SEEK_STATE_PLAY:
963 print "is PLAYING, start pause timer"
964 self.ts_pause_timer.start(200, 1)
967 self.unPauseService()
969 def __seekableStatusChanged(self):
972 print "self.isSeekable", self.isSeekable()
973 print "self.timeshift_enabled", self.timeshift_enabled
975 # when this service is not seekable, but timeshift
976 # is enabled, this means we can activate
978 if not self.isSeekable() and self.timeshift_enabled:
981 print "timeshift activate:", enabled
982 self["TimeshiftActivateActions"].setEnabled(enabled)
984 from RecordTimer import parseEvent
986 class InfoBarInstantRecord:
987 """Instant Record - handles the instantRecord action in order to
988 start/stop instant records"""
990 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
992 "instantRecord": (self.instantRecord, "Instant Record..."),
994 self.recording = None
995 self["BlinkingPoint"] = BlinkingPixmapConditional()
996 self["BlinkingPoint"].hide()
997 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
999 def stopCurrentRecording(self):
1000 self.session.nav.RecordTimer.removeEntry(self.recording)
1001 self.recording = None
1003 def startInstantRecording(self, limitEvent = False):
1004 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1006 # try to get event info
1010 service = self.session.nav.getCurrentService()
1011 epg = eEPGCache.getInstance()
1012 event = epg.lookupEventTime(serviceref, -1, 0)
1014 info = service.info()
1015 ev = info.getEvent(0)
1020 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1022 if event is not None:
1023 data = parseEvent(event)
1025 end = begin + 3600 * 10
1026 data = (begin, end, data[2], data[3], data[4])
1028 data = (time.time(), time.time() + 3600 * 10, "instant record", "", None)
1030 # fix me, description.
1031 self.recording = self.session.nav.recordWithTimer(serviceref, *data)
1032 self.recording.dontSave = True
1034 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
1036 def isInstantRecordRunning(self):
1037 if self.recording != None:
1038 if self.recording.isRunning():
1042 def recordQuestionCallback(self, answer):
1043 if answer is None or answer[1] == "no":
1046 if self.isInstantRecordRunning():
1047 self.stopCurrentRecording()
1050 if answer[1] == "event":
1052 self.startInstantRecording(limitEvent = limitEvent)
1054 def instantRecord(self):
1056 stat = os.stat(resolveFilename(SCOPE_HDD))
1058 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1061 if self.isInstantRecordRunning():
1062 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, title=_("Do you want to stop the current\n(instant) recording?"), list=[(_("yes"), "yes"), (_("no"), "no")])
1063 # self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Do you want to stop the current\n(instant) recording?"))
1065 self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, title=_("Do you want to stop the current\n(instant) recording?"), list=[(_("record indefinitely"), "indefinitely"), (_("stop after current event"), "event"), (_("don't record"), "no")])
1066 #self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Start recording?"))
1068 from Screens.AudioSelection import AudioSelection
1070 class InfoBarAudioSelection:
1072 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1074 "audioSelection": (self.audioSelection, "Audio Options..."),
1077 def audioSelection(self):
1078 service = self.session.nav.getCurrentService()
1079 audio = service.audioTracks()
1080 n = audio.getNumberOfTracks()
1082 self.session.open(AudioSelection, audio)
1084 from Screens.SubserviceSelection import SubserviceSelection
1086 class InfoBarSubserviceSelection:
1088 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1090 "subserviceSelection": (self.subserviceSelection, "Subservice list..."),
1093 def subserviceSelection(self):
1094 service = self.session.nav.getCurrentService()
1095 subservices = service.subServices()
1096 n = subservices.getNumberOfSubservices()
1098 self.session.openWithCallback(self.subserviceSelected, SubserviceSelection, subservices)
1100 def subserviceSelected(self, service):
1101 if not service is None:
1102 self.session.nav.playService(service)
1104 class InfoBarAdditionalInfo:
1106 self["DolbyActive"] = Pixmap()
1107 self["CryptActive"] = Pixmap()
1108 self["FormatActive"] = Pixmap()
1110 self["ButtonRed"] = PixmapConditional(withTimer = False)
1111 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1112 self.onLayoutFinish.append(self["ButtonRed"].update)
1113 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1114 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1115 self.onLayoutFinish.append(self["ButtonRedText"].update)
1117 self["ButtonGreen"] = Pixmap()
1118 self["ButtonGreenText"] = Label(_("Subservices"))
1120 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1121 self["ButtonYellow"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1122 self["ButtonYellowText"] = LabelConditional(text = _("Timeshifting"), withTimer = False)
1123 self["ButtonYellowText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1124 self.onLayoutFinish.append(self["ButtonYellow"].update)
1125 self.onLayoutFinish.append(self["ButtonYellowText"].update)
1127 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1128 self["ButtonBlue"].setConnect(lambda: False)
1129 self["ButtonBlueText"] = LabelConditional(text = _("Extensions"), withTimer = False)
1130 self["ButtonBlueText"].setConnect(lambda: False)
1131 self.onLayoutFinish.append(self["ButtonBlue"].update)
1132 self.onLayoutFinish.append(self["ButtonBlueText"].update)
1134 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1136 def hideSubServiceIndication(self):
1137 self["ButtonGreen"].hide()
1138 self["ButtonGreenText"].hide()
1140 def showSubServiceIndication(self):
1141 self["ButtonGreen"].show()
1142 self["ButtonGreenText"].show()
1144 def checkFormat(self, service):
1145 info = service.info()
1146 if info is not None:
1147 aspect = info.getInfo(iServiceInformation.sAspect)
1148 if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1149 self["FormatActive"].show()
1151 self["FormatActive"].hide()
1153 def checkSubservices(self, service):
1154 if service.subServices().getNumberOfSubservices() > 0:
1155 self.showSubServiceIndication()
1157 self.hideSubServiceIndication()
1159 def checkDolby(self, service):
1162 audio = service.audioTracks()
1163 if audio is not None:
1164 n = audio.getNumberOfTracks()
1166 i = audio.getTrackInfo(x)
1167 description = i.getDescription();
1168 if description.find("AC3") != -1 or description.find("DTS") != -1:
1172 self["DolbyActive"].show()
1174 self["DolbyActive"].hide()
1176 def checkCrypted(self, service):
1177 info = service.info()
1178 if info is not None:
1179 if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1180 self["CryptActive"].show()
1182 self["CryptActive"].hide()
1184 def gotServiceEvent(self, ev):
1185 service = self.session.nav.getCurrentService()
1186 if ev == iPlayableService.evUpdatedEventInfo:
1187 self.checkSubservices(service)
1188 self.checkFormat(service)
1189 elif ev == iPlayableService.evUpdatedInfo:
1190 self.checkCrypted(service)
1191 self.checkDolby(service)
1192 elif ev == iPlayableService.evEnd:
1193 self.hideSubServiceIndication()
1194 self["CryptActive"].hide()
1195 self["DolbyActive"].hide()
1196 self["FormatActive"].hide()
1198 class InfoBarNotifications:
1200 self.onExecBegin.append(self.checkNotifications)
1201 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1203 def checkNotificationsIfExecing(self):
1205 self.checkNotifications()
1207 def checkNotifications(self):
1208 if len(Notifications.notifications):
1209 n = Notifications.notifications[0]
1210 Notifications.notifications = Notifications.notifications[1:]
1214 self.session.openWithCallback(cb, *n[1:])
1216 self.session.open(*n[1:])
1218 class InfoBarServiceNotifications:
1220 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1222 iPlayableService.evEnd: self.serviceHasEnded
1225 def serviceHasEnded(self):
1226 print "service end!"
1229 self.setSeekState(self.SEEK_STATE_PLAY)
1233 class InfoBarCueSheetSupport:
1239 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions",
1241 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1242 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1243 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1247 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1249 iPlayableService.evStart: self.__serviceStarted,
1252 def __serviceStarted(self):
1253 print "new service started! trying to download cuts!"
1254 self.downloadCuesheet()
1256 def __getSeekable(self):
1257 service = self.session.nav.getCurrentService()
1260 return service.seek()
1262 def cueGetCurrentPosition(self):
1263 seek = self.__getSeekable()
1266 r = seek.getPlayPosition()
1271 def jumpPreviousNextMark(self, cmp, alternative=None):
1272 current_pos = self.cueGetCurrentPosition()
1273 if current_pos is None:
1275 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1276 if mark is not None:
1278 elif alternative is not None:
1283 seekable = self.__getSeekable()
1284 if seekable is not None:
1285 seekable.seekTo(pts)
1287 def jumpPreviousMark(self):
1288 # we add 2 seconds, so if the play position is <2s after
1289 # the mark, the mark before will be used
1290 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1292 def jumpNextMark(self):
1293 self.jumpPreviousNextMark(lambda x: x)
1295 def getNearestCutPoint(self, pts, cmp=abs):
1298 for cp in self.cut_list:
1299 diff = cmp(cp[0] - pts)
1300 if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1304 def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
1305 current_pos = self.cueGetCurrentPosition()
1306 if current_pos is None:
1307 print "not seekable"
1310 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1312 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1314 return nearest_cutpoint
1316 self.removeMark(nearest_cutpoint)
1317 elif not onlyremove and not onlyreturn:
1318 self.addMark((current_pos, self.CUT_TYPE_MARK))
1323 def addMark(self, point):
1324 bisect.insort(self.cut_list, point)
1325 self.uploadCuesheet()
1327 def removeMark(self, point):
1328 self.cut_list.remove(point)
1329 self.uploadCuesheet()
1331 def __getCuesheet(self):
1332 service = self.session.nav.getCurrentService()
1335 return service.cueSheet()
1337 def uploadCuesheet(self):
1338 cue = self.__getCuesheet()
1341 print "upload failed, no cuesheet interface"
1343 cue.setCutList(self.cut_list)
1345 def downloadCuesheet(self):
1346 cue = self.__getCuesheet()
1349 print "upload failed, no cuesheet interface"
1351 self.cut_list = cue.getCutList()