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
14 from Components.Clock import Clock
15 from Components.Input import Input
17 from ServiceReference import ServiceReference
18 from EpgSelection import EPGSelection
20 from Screens.MessageBox import MessageBox
21 from Screens.ChoiceBox import ChoiceBox
22 from Screens.InputBox import InputBox
23 from Screens.Dish import Dish
24 from Screens.Standby import Standby
25 from Screens.EventView import EventViewEPGSelect, EventViewSimple
26 from Screens.MinuteInput import MinuteInput
27 from Components.Harddisk import harddiskmanager
29 from Components.ServiceEventTracker import ServiceEventTracker
31 from Tools import Notifications
32 from Tools.Directories import *
34 #from enigma import eTimer, eDVBVolumecontrol, quitMainloop
41 from Components.config import config, currentConfigSelectionElement
44 from Menu import MainMenu, mdom
48 self.dishDialog = self.session.instantiateDialog(Dish)
49 self.onLayoutFinish.append(self.dishDialog.show)
51 class InfoBarShowHide:
52 """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
60 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
62 "toggleShow": self.toggleShow,
66 self.__state = self.STATE_SHOWN
69 self.onExecBegin.append(self.show)
71 self.hideTimer = eTimer()
72 self.hideTimer.timeout.get().append(self.doTimerHide)
73 self.hideTimer.start(5000, True)
75 self.onShow.append(self.__onShow)
76 self.onHide.append(self.__onHide)
79 self.__state = self.STATE_SHOWN
82 def startHideTimer(self):
83 if self.__state == self.STATE_SHOWN and not self.__locked:
84 self.hideTimer.start(5000, True)
87 self.__state = self.STATE_HIDDEN
93 def doTimerHide(self):
95 if self.__state == self.STATE_SHOWN:
99 if self.__state == self.STATE_SHOWN:
101 self.hideTimer.stop()
102 elif self.__state == self.STATE_HIDDEN:
106 self.__locked = self.__locked + 1
109 self.hideTimer.stop()
111 def unlockShow(self):
112 self.__locked = self.__locked - 1
114 self.startHideTimer()
116 # def startShow(self):
117 # self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
118 # self.__state = self.STATE_SHOWN
120 # def startHide(self):
121 # self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
122 # self.__state = self.STATE_HIDDEN
124 class NumberZap(Screen):
131 self.close(int(self["number"].getText()))
133 def keyNumberGlobal(self, number):
134 self.Timer.start(3000, True) #reset timer
135 self.field = self.field + str(number)
136 self["number"].setText(self.field)
137 if len(self.field) >= 4:
140 def __init__(self, session, number):
141 Screen.__init__(self, session)
142 self.field = str(number)
144 self["channel"] = Label(_("Channel:"))
146 self["number"] = Label(self.field)
148 self["actions"] = NumberActionMap( [ "SetupActions" ],
152 "1": self.keyNumberGlobal,
153 "2": self.keyNumberGlobal,
154 "3": self.keyNumberGlobal,
155 "4": self.keyNumberGlobal,
156 "5": self.keyNumberGlobal,
157 "6": self.keyNumberGlobal,
158 "7": self.keyNumberGlobal,
159 "8": self.keyNumberGlobal,
160 "9": self.keyNumberGlobal,
161 "0": self.keyNumberGlobal
164 self.Timer = eTimer()
165 self.Timer.timeout.get().append(self.keyOK)
166 self.Timer.start(3000, True)
168 class InfoBarPowerKey:
169 """ PowerKey stuff - handles the powerkey press and powerkey release actions"""
172 self.powerKeyTimer = eTimer()
173 self.powerKeyTimer.timeout.get().append(self.powertimer)
174 self["PowerKeyActions"] = HelpableActionMap(self, "PowerKeyActions",
176 "powerdown": self.powerdown,
177 "powerup": self.powerup,
178 "discreteStandby": (self.standby, "Go standby"),
179 "discretePowerOff": (self.quit, "Go to deep standby"),
182 def powertimer(self):
183 print "PowerOff - Now!"
187 self.standbyblocked = 0
188 self.powerKeyTimer.start(3000, True)
191 self.powerKeyTimer.stop()
192 if self.standbyblocked == 0:
193 self.standbyblocked = 1
197 self.session.open(Standby, self)
203 class InfoBarNumberZap:
204 """ Handles an initial number for NumberZapping """
206 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
208 "1": self.keyNumberGlobal,
209 "2": self.keyNumberGlobal,
210 "3": self.keyNumberGlobal,
211 "4": self.keyNumberGlobal,
212 "5": self.keyNumberGlobal,
213 "6": self.keyNumberGlobal,
214 "7": self.keyNumberGlobal,
215 "8": self.keyNumberGlobal,
216 "9": self.keyNumberGlobal,
217 "0": self.keyNumberGlobal,
220 def keyNumberGlobal(self, number):
221 # print "You pressed number " + str(number)
223 self.servicelist.recallPrevService()
226 self.session.openWithCallback(self.numberEntered, NumberZap, number)
228 def numberEntered(self, retval):
229 # print self.servicelist
231 self.zapToNumber(retval)
233 def searchNumberHelper(self, serviceHandler, num, bouquet):
234 servicelist = serviceHandler.list(bouquet)
235 if not servicelist is None:
237 serviceIterator = servicelist.getNext()
238 if not serviceIterator.valid(): #check end of list
240 if serviceIterator.flags: #assume normal dvb service have no flags set
243 if not num: #found service with searched number ?
244 return serviceIterator, 0
247 def zapToNumber(self, number):
248 bouquet = self.servicelist.bouquet_root
250 serviceHandler = eServiceCenter.getInstance()
251 if bouquet.toString().find('FROM BOUQUET "bouquets.') == -1: #FIXME HACK
252 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
254 bouquetlist = serviceHandler.list(bouquet)
255 if not bouquetlist is None:
257 bouquet = self.servicelist.appendDVBTypes(bouquetlist.getNext())
258 if not bouquet.valid(): #check end of list
260 if (bouquet.flags & eServiceReference.flagDirectory) != eServiceReference.flagDirectory:
262 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
263 if not service is None:
264 if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
265 self.servicelist.clearPath()
266 if self.servicelist.bouquet_root != bouquet:
267 self.servicelist.enterPath(self.servicelist.bouquet_root)
268 self.servicelist.enterPath(bouquet)
269 self.servicelist.setCurrentSelection(service) #select the service in servicelist
270 self.servicelist.zap()
272 class InfoBarChannelSelection:
273 """ ChannelSelection - handles the channelSelection dialog and the initial
274 channelChange actions which open the channelSelection dialog """
277 self.servicelist = self.session.instantiateDialog(ChannelSelection)
279 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
281 "switchChannelUp": self.switchChannelUp,
282 "switchChannelDown": self.switchChannelDown,
283 "zapUp": (self.zapUp, _("previous channel")),
284 "zapDown": (self.zapDown, _("next channel")),
285 "historyBack": (self.historyBack, _("previous channel in history")),
286 "historyNext": (self.historyNext, _("next channel in history"))
289 def historyBack(self):
290 self.servicelist.historyBack()
292 def historyNext(self):
293 self.servicelist.historyNext()
295 def switchChannelUp(self):
296 self.servicelist.moveUp()
297 self.session.execDialog(self.servicelist)
299 def switchChannelDown(self):
300 self.servicelist.moveDown()
301 self.session.execDialog(self.servicelist)
304 if currentConfigSelectionElement(config.usage.quickzap_bouquet_change) == "yes":
305 if self.servicelist.inBouquet() and self.servicelist.atBegin():
306 self.servicelist.prevBouquet()
307 self.servicelist.moveUp()
308 self.servicelist.zap()
312 if currentConfigSelectionElement(config.usage.quickzap_bouquet_change) == "yes" and self.servicelist.inBouquet() and self.servicelist.atEnd():
313 self.servicelist.nextBouquet()
315 self.servicelist.moveDown()
316 self.servicelist.zap()
320 """ Handles a menu action, to open the (main) menu """
322 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions",
324 "mainMenu": (self.mainMenu, "Enter main menu..."),
328 print "loading mainmenu XML..."
329 menu = mdom.childNodes[0]
330 assert menu.tagName == "menu", "root element in menu must be 'menu'!"
331 self.session.open(MainMenu, menu, menu.childNodes)
333 class InfoBarSimpleEventView:
334 """ Opens the Eventview for now/next """
336 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
338 "showEventInfo": (self.openEventView, _("show event details")),
341 def openEventView(self):
343 service = self.session.nav.getCurrentService()
344 ref = self.session.nav.getCurrentlyPlayingServiceReference()
345 info = service.info()
348 self.epglist.append(ptr)
351 self.epglist.append(ptr)
352 if len(self.epglist) > 0:
353 self.session.open(EventViewSimple, self.epglist[0], ServiceReference(ref), self.eventViewCallback)
355 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
356 if len(self.epglist) > 1:
357 tmp = self.epglist[0]
358 self.epglist[0]=self.epglist[1]
360 setEvent(self.epglist[0])
363 """ EPG - Opens an EPG list when the showEPGList action fires """
365 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
367 "showEventInfo": (self.openEventView, _("show EPG...")),
370 def zapToService(self, service):
371 if not service is None:
372 if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
373 self.servicelist.clearPath()
374 if self.servicelist.bouquet_root != self.epg_bouquet:
375 self.servicelist.enterPath(self.servicelist.bouquet_root)
376 self.servicelist.enterPath(self.epg_bouquet)
377 self.servicelist.setCurrentSelection(service) #select the service in servicelist
378 self.servicelist.zap()
380 def openBouquetEPG(self, bouquet, withCallback=True):
381 ptr=eEPGCache.getInstance()
383 servicelist = eServiceCenter.getInstance().list(bouquet)
384 if not servicelist is None:
386 service = servicelist.getNext()
387 if not service.valid(): #check if end of list
389 if service.flags: #ignore non playable services
391 services.append(ServiceReference(service))
393 self.epg_bouquet = bouquet
395 self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService)
397 self.session.open(EPGSelection, services, self.zapToService)
399 def closed(self, ret):
403 def openMultiServiceEPG(self, withCallback=True):
404 bouquets = self.servicelist.getBouquetList()
409 if cnt > 1: # show bouquet list
411 self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG)
413 self.session.open(BouquetSelector, bouquets, self.openBouquetEPG)
415 self.openBouquetEPG(bouquets[0][1], withCallback)
417 def openSingleServiceEPG(self):
418 ref=self.session.nav.getCurrentlyPlayingServiceReference()
419 ptr=eEPGCache.getInstance()
420 self.session.openWithCallback(self.closed, EPGSelection, ref)
422 def openEventView(self):
424 service = self.session.nav.getCurrentService()
425 ref = self.session.nav.getCurrentlyPlayingServiceReference()
426 info = service.info()
429 self.epglist.append(ptr)
432 self.epglist.append(ptr)
433 if len(self.epglist) == 0:
434 epg = eEPGCache.getInstance()
435 ptr = epg.lookupEventTime(ref, -1)
437 self.epglist.append(ptr)
438 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
440 self.epglist.append(ptr)
441 if len(self.epglist) > 0:
442 self.session.open(EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG)
444 print "no epg for the service avail.. so we show multiepg instead of eventinfo"
445 self.openMultiServiceEPG(False)
447 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
448 if len(self.epglist) > 1:
449 tmp = self.epglist[0]
450 self.epglist[0]=self.epglist[1]
452 setEvent(self.epglist[0])
457 """provides a snr/agc/ber display"""
459 self["snr"] = Label()
460 self["agc"] = Label()
461 self["ber"] = Label()
462 self["snr_percent"] = Label()
463 self["agc_percent"] = Label()
464 self["ber_count"] = Label()
465 self["snr_progress"] = ProgressBar()
466 self["agc_progress"] = ProgressBar()
467 self["ber_progress"] = ProgressBar()
468 self.timer = eTimer()
469 self.timer.timeout.get().append(self.updateTunerInfo)
470 self.timer.start(1000)
476 return (long)(log(val)/log(2))
479 def updateTunerInfo(self):
480 if self.instance.isVisible():
481 service = self.session.nav.getCurrentService()
485 if service is not None:
486 feinfo = service.frontendStatusInfo()
487 if feinfo is not None:
488 ber=feinfo.getFrontendInfo(iFrontendStatusInformation.bitErrorRate)
489 snr=feinfo.getFrontendInfo(iFrontendStatusInformation.signalPower)*100/65536
490 agc=feinfo.getFrontendInfo(iFrontendStatusInformation.signalQuality)*100/65536
491 self["snr_percent"].setText("%d%%"%(snr))
492 self["agc_percent"].setText("%d%%"%(agc))
493 self["ber_count"].setText("%d"%(ber))
494 self["snr_progress"].setValue(snr)
495 self["agc_progress"].setValue(agc)
496 self["ber_progress"].setValue(self.calc(ber))
499 """provides a current/next event info display"""
501 self["Event_Now_StartTime"] = EventInfo(self.session.nav, EventInfo.Now_StartTime)
502 self["Event_Next_StartTime"] = EventInfo(self.session.nav, EventInfo.Next_StartTime)
504 self["Event_Now"] = EventInfo(self.session.nav, EventInfo.Now)
505 self["Event_Next"] = EventInfo(self.session.nav, EventInfo.Next)
507 self["Event_Now_Duration"] = EventInfo(self.session.nav, EventInfo.Now_Remaining)
508 self["Event_Next_Duration"] = EventInfo(self.session.nav, EventInfo.Next_Duration)
510 self["Now_ProgressBar"] = EventInfoProgress(self.session.nav, EventInfo.Now)
512 class InfoBarServiceName:
514 self["ServiceName"] = ServiceName(self.session.nav)
517 """handles actions like seeking, pause"""
519 # ispause, isff, issm
520 SEEK_STATE_PLAY = (0, 0, 0, ">")
521 SEEK_STATE_PAUSE = (1, 0, 0, "||")
522 SEEK_STATE_FF_2X = (0, 2, 0, ">> 2x")
523 SEEK_STATE_FF_4X = (0, 4, 0, ">> 4x")
524 SEEK_STATE_FF_8X = (0, 8, 0, ">> 8x")
525 SEEK_STATE_FF_32X = (0, 32, 0, ">> 32x")
526 SEEK_STATE_FF_64X = (0, 64, 0, ">> 64x")
527 SEEK_STATE_FF_128X = (0, 128, 0, ">> 128x")
529 SEEK_STATE_BACK_16X = (0, -16, 0, "<< 16x")
530 SEEK_STATE_BACK_32X = (0, -32, 0, "<< 32x")
531 SEEK_STATE_BACK_64X = (0, -64, 0, "<< 64x")
532 SEEK_STATE_BACK_128X = (0, -128, 0, "<< 128x")
534 SEEK_STATE_SM_HALF = (0, 0, 2, "/2")
535 SEEK_STATE_SM_QUARTER = (0, 0, 4, "/4")
536 SEEK_STATE_SM_EIGHTH = (0, 0, 8, "/8")
539 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
541 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
542 iPlayableService.evStart: self.__serviceStarted,
544 iPlayableService.evEOF: self.__evEOF,
545 iPlayableService.evSOF: self.__evSOF,
548 class InfoBarSeekActionMap(HelpableActionMap):
549 def __init__(self, screen, *args, **kwargs):
550 HelpableActionMap.__init__(self, screen, *args, **kwargs)
553 def action(self, contexts, action):
554 if action[:5] == "seek:":
555 time = int(action[5:])
556 self.screen.seekRelative(time * 90000)
558 HelpableActionMap.action(self, contexts, action)
560 self["SeekActions"] = InfoBarSeekActionMap(self, "InfobarSeekActions",
562 "pauseService": (self.pauseService, "pause"),
563 "unPauseService": (self.unPauseService, "continue"),
565 "seekFwd": (self.seekFwd, "skip forward"),
566 "seekFwdDown": self.seekFwdDown,
567 "seekFwdUp": self.seekFwdUp,
568 "seekBack": (self.seekBack, "skip backward"),
569 "seekBackDown": self.seekBackDown,
570 "seekBackUp": self.seekBackUp,
572 # give them a little more priority to win over color buttons
574 self.seekstate = self.SEEK_STATE_PLAY
575 self.onClose.append(self.delTimer)
577 self.fwdtimer = False
578 self.fwdKeyTimer = eTimer()
579 self.fwdKeyTimer.timeout.get().append(self.fwdTimerFire)
581 self.rwdtimer = False
582 self.rwdKeyTimer = eTimer()
583 self.rwdKeyTimer.timeout.get().append(self.rwdTimerFire)
585 self.onPlayStateChanged = [ ]
587 self.lockedBecauseOfSkipping = False
600 service = self.session.nav.getCurrentService()
604 seek = service.seek()
606 if seek is None or not seek.isCurrentlySeekable():
611 def isSeekable(self):
612 if self.getSeek() is None:
616 def __seekableStatusChanged(self):
617 print "seekable status changed!"
618 if not self.isSeekable():
619 self["SeekActions"].setEnabled(False)
620 print "not seekable, return to play"
621 self.setSeekState(self.SEEK_STATE_PLAY)
623 self["SeekActions"].setEnabled(True)
626 def __serviceStarted(self):
627 self.seekstate = self.SEEK_STATE_PLAY
629 def setSeekState(self, state):
630 service = self.session.nav.getCurrentService()
635 if not self.isSeekable():
636 if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
637 state = self.SEEK_STATE_PLAY
639 pauseable = service.pause()
641 if pauseable is None:
642 print "not pauseable."
643 state = self.SEEK_STATE_PLAY
645 oldstate = self.seekstate
646 self.seekstate = state
649 if oldstate[i] != self.seekstate[i]:
650 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
652 for c in self.onPlayStateChanged:
655 self.checkSkipShowHideLock()
659 def pauseService(self):
660 if self.seekstate == self.SEEK_STATE_PAUSE:
661 print "pause, but in fact unpause"
662 self.unPauseService()
664 if self.seekstate == self.SEEK_STATE_PLAY:
665 print "yes, playing."
667 print "no", self.seekstate
669 self.setSeekState(self.SEEK_STATE_PAUSE);
671 def unPauseService(self):
673 self.setSeekState(self.SEEK_STATE_PLAY);
675 def doSeek(self, seektime):
676 print "doseek", seektime
677 service = self.session.nav.getCurrentService()
681 seekable = self.getSeek()
685 seekable.seekTo(90 * seektime)
687 def seekFwdDown(self):
688 print "start fwd timer"
690 self.fwdKeyTimer.start(1000)
692 def seekBackDown(self):
693 print "start rewind timer"
695 self.rwdKeyTimer.start(1000)
700 self.fwdKeyTimer.stop()
701 self.fwdtimer = False
706 self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
707 self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
708 self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
709 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
710 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
711 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
712 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
713 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
714 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_PLAY,
715 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_16X,
716 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
717 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
718 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
719 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
720 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER
722 self.setSeekState(lookup[self.seekstate])
724 def seekBackUp(self):
727 self.rwdKeyTimer.stop()
728 self.rwdtimer = False
733 self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_16X,
734 self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
735 self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
736 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
737 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
738 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
739 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
740 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
741 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_BACK_32X,
742 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
743 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
744 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
745 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
746 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
747 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE
749 self.setSeekState(lookup[self.seekstate])
751 if self.seekstate == self.SEEK_STATE_PAUSE:
752 seekable = self.getSeek()
753 if seekable is not None:
754 seekable.seekRelative(-1, 3)
756 def fwdTimerFire(self):
757 print "Display seek fwd"
758 self.fwdKeyTimer.stop()
759 self.fwdtimer = False
760 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
762 def fwdSeekTo(self, minutes):
763 print "Seek", minutes, "minutes forward"
765 seekable = self.getSeek()
766 if seekable is not None:
767 seekable.seekRelative(1, minutes * 60 * 90000)
769 def rwdTimerFire(self):
771 self.rwdKeyTimer.stop()
772 self.rwdtimer = False
773 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
775 def rwdSeekTo(self, minutes):
777 self.fwdSeekTo(0 - minutes)
779 def checkSkipShowHideLock(self):
780 wantlock = self.seekstate != self.SEEK_STATE_PLAY
782 if self.lockedBecauseOfSkipping and not wantlock:
784 self.lockedBecauseOfSkipping = False
786 if wantlock and not self.lockedBecauseOfSkipping:
788 self.lockedBecauseOfSkipping = True
791 if self.seekstate != self.SEEK_STATE_PLAY:
792 self.setSeekState(self.SEEK_STATE_PAUSE)
794 #self.getSeek().seekRelative(1, -90000)
795 self.setSeekState(self.SEEK_STATE_PLAY)
797 self.setSeekState(self.SEEK_STATE_PAUSE)
800 self.setSeekState(self.SEEK_STATE_PLAY)
803 def seekRelative(self, diff):
804 seekable = self.getSeek()
805 if seekable is not None:
806 seekable.seekRelative(1, diff)
808 from Screens.PVRState import PVRState
810 class InfoBarPVRState:
812 self.onPlayStateChanged.append(self.__playStateChanged)
813 self.pvrStateDialog = self.session.instantiateDialog(PVRState)
814 self.onShow.append(self.__mayShow)
815 self.onHide.append(self.pvrStateDialog.hide)
818 if self.seekstate != self.SEEK_STATE_PLAY:
819 self.pvrStateDialog.show()
821 def __playStateChanged(self, state):
822 playstateString = state[3]
823 self.pvrStateDialog["state"].setText(playstateString)
826 class InfoBarShowMovies:
828 # i don't really like this class.
829 # it calls a not further specified "movie list" on up/down/movieList,
830 # so this is not more than an action map
832 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
834 "movieList": (self.showMovies, "movie list"),
835 "up": (self.showMovies, "movie list"),
836 "down": (self.showMovies, "movie list")
839 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
843 # Timeshift works the following way:
844 # demux0 demux1 "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
845 # - normal playback TUNER unused PLAY enable disable disable
846 # - user presses "yellow" button. TUNER record PAUSE enable disable enable
847 # - user presess pause again FILE record PLAY enable disable enable
848 # - user fast forwards FILE record FF enable disable enable
849 # - end of timeshift buffer reached TUNER record PLAY enable enable disable
850 # - user backwards FILE record BACK # !! enable disable enable
854 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
855 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
856 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
857 # - the user can now PVR around
858 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
859 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
861 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
862 # - if the user rewinds, or press pause, timeshift will be activated again
864 # note that a timeshift can be enabled ("recording") and
865 # activated (currently time-shifting).
867 class InfoBarTimeshift:
869 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
871 "timeshiftStart": (self.startTimeshift, "start timeshift"), # the "yellow key"
872 "timeshiftStop": (self.stopTimeshift, "stop timeshift") # currently undefined :), probably 'TV'
874 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
876 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
877 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause # something like "backward key"
878 }, prio=-1) # priority over record
880 self.timeshift_enabled = 0
881 self.timeshift_state = 0
882 self.ts_pause_timer = eTimer()
883 self.ts_pause_timer.timeout.get().append(self.pauseService)
885 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
887 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
890 def getTimeshift(self):
891 service = self.session.nav.getCurrentService()
892 return service.timeshift()
894 def startTimeshift(self):
895 print "enable timeshift"
896 ts = self.getTimeshift()
898 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
899 print "no ts interface"
902 if self.timeshift_enabled:
903 print "hu, timeshift already enabled?"
905 if not ts.startTimeshift():
906 self.timeshift_enabled = 1
909 self.setSeekState(self.SEEK_STATE_PAUSE)
911 # enable the "TimeshiftEnableActions", which will override
912 # the startTimeshift actions
913 self.__seekableStatusChanged()
915 print "timeshift failed"
917 def stopTimeshift(self):
918 if not self.timeshift_enabled:
920 print "disable timeshift"
921 ts = self.getTimeshift()
924 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
926 def stopTimeshiftConfirmed(self, confirmed):
930 ts = self.getTimeshift()
935 self.timeshift_enabled = 0
938 self.__seekableStatusChanged()
940 # activates timeshift, and seeks to (almost) the end
941 def activateTimeshiftEnd(self):
942 ts = self.getTimeshift()
947 if ts.isTimeshiftActive():
948 print "!! activate timeshift called - but shouldn't this be a normal pause?"
951 self.setSeekState(self.SEEK_STATE_PLAY)
952 ts.activateTimeshift()
955 # same as activateTimeshiftEnd, but pauses afterwards.
956 def activateTimeshiftEndAndPause(self):
957 state = self.seekstate
958 self.activateTimeshiftEnd()
960 # well, this is "andPause", but it could be pressed from pause,
961 # when pausing on the (fake-)"live" picture, so an un-pause
964 print "now, pauseService"
965 if state == self.SEEK_STATE_PLAY:
966 print "is PLAYING, start pause timer"
967 self.ts_pause_timer.start(200, 1)
970 self.unPauseService()
972 def __seekableStatusChanged(self):
975 print "self.isSeekable", self.isSeekable()
976 print "self.timeshift_enabled", self.timeshift_enabled
978 # when this service is not seekable, but timeshift
979 # is enabled, this means we can activate
981 if not self.isSeekable() and self.timeshift_enabled:
984 print "timeshift activate:", enabled
985 self["TimeshiftActivateActions"].setEnabled(enabled)
987 from RecordTimer import parseEvent
989 class InfoBarInstantRecord:
990 """Instant Record - handles the instantRecord action in order to
991 start/stop instant records"""
993 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
995 "instantRecord": (self.instantRecord, "Instant Record..."),
997 self.recording = None
998 self["BlinkingPoint"] = BlinkingPixmapConditional()
999 self["BlinkingPoint"].hide()
1000 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
1002 def stopCurrentRecording(self):
1003 self.session.nav.RecordTimer.removeEntry(self.recording)
1004 self.recording = None
1006 def startInstantRecording(self, limitEvent = False):
1007 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1009 # try to get event info
1012 service = self.session.nav.getCurrentService()
1013 epg = eEPGCache.getInstance()
1014 event = epg.lookupEventTime(serviceref, -1, 0)
1016 info = service.info()
1017 ev = info.getEvent(0)
1023 end = time.time() + 3600 * 10
1024 name = "instant record"
1028 if event is not None:
1029 curEvent = parseEvent(event)
1031 description = curEvent[3]
1032 eventid = curEvent[4]
1037 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1039 data = (begin, end, name, description, eventid)
1041 self.recording = self.session.nav.recordWithTimer(serviceref, *data)
1042 self.recording.dontSave = True
1044 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
1046 def isInstantRecordRunning(self):
1047 if self.recording != None:
1048 if self.recording.isRunning():
1052 def recordQuestionCallback(self, answer):
1053 if answer is None or answer[1] == "no":
1056 if self.isInstantRecordRunning():
1057 if answer[1] == "manualduration":
1058 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1060 self.stopCurrentRecording()
1063 if answer[1] == "event":
1065 if answer[1] == "manualduration":
1066 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1067 self.startInstantRecording(limitEvent = limitEvent)
1069 def inputCallback(self, value):
1070 if value is not None:
1071 print "stopping recording after", int(value), "minutes."
1072 if self.recording is not None:
1073 self.recording.end = time.time() + 60 * int(value)
1074 self.session.nav.RecordTimer.timeChanged(self.recording)
1076 def instantRecord(self):
1078 stat = os.stat(resolveFilename(SCOPE_HDD))
1080 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1083 if self.isInstantRecordRunning():
1084 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")])
1085 # self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Do you want to stop the current\n(instant) recording?"))
1087 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")])
1088 #self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Start recording?"))
1090 from Screens.AudioSelection import AudioSelection
1092 class InfoBarAudioSelection:
1094 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1096 "audioSelection": (self.audioSelection, "Audio Options..."),
1099 def audioSelection(self):
1100 service = self.session.nav.getCurrentService()
1101 audio = service.audioTracks()
1102 n = audio.getNumberOfTracks()
1104 self.session.open(AudioSelection, audio)
1106 from Screens.SubserviceSelection import SubserviceSelection
1108 class InfoBarSubserviceSelection:
1110 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1112 "subserviceSelection": (self.subserviceSelection, "Subservice list..."),
1115 def subserviceSelection(self):
1116 service = self.session.nav.getCurrentService()
1117 subservices = service.subServices()
1118 n = subservices.getNumberOfSubservices()
1120 self.session.openWithCallback(self.subserviceSelected, SubserviceSelection, subservices)
1122 def subserviceSelected(self, service):
1123 if not service is None:
1124 self.session.nav.playService(service)
1126 class InfoBarAdditionalInfo:
1128 self["DolbyActive"] = Pixmap()
1129 self["CryptActive"] = Pixmap()
1130 self["FormatActive"] = Pixmap()
1132 self["ButtonRed"] = PixmapConditional(withTimer = False)
1133 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1134 self.onLayoutFinish.append(self["ButtonRed"].update)
1135 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1136 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1137 self.onLayoutFinish.append(self["ButtonRedText"].update)
1139 self["ButtonGreen"] = Pixmap()
1140 self["ButtonGreenText"] = Label(_("Subservices"))
1142 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1143 self["ButtonYellow"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1144 self["ButtonYellowText"] = LabelConditional(text = _("Timeshifting"), withTimer = False)
1145 self["ButtonYellowText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1146 self.onLayoutFinish.append(self["ButtonYellow"].update)
1147 self.onLayoutFinish.append(self["ButtonYellowText"].update)
1149 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1150 self["ButtonBlue"].setConnect(lambda: False)
1151 self["ButtonBlueText"] = LabelConditional(text = _("Extensions"), withTimer = False)
1152 self["ButtonBlueText"].setConnect(lambda: False)
1153 self.onLayoutFinish.append(self["ButtonBlue"].update)
1154 self.onLayoutFinish.append(self["ButtonBlueText"].update)
1156 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1158 def hideSubServiceIndication(self):
1159 self["ButtonGreen"].hide()
1160 self["ButtonGreenText"].hide()
1162 def showSubServiceIndication(self):
1163 self["ButtonGreen"].show()
1164 self["ButtonGreenText"].show()
1166 def checkFormat(self, service):
1167 info = service.info()
1168 if info is not None:
1169 aspect = info.getInfo(iServiceInformation.sAspect)
1170 if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1171 self["FormatActive"].show()
1173 self["FormatActive"].hide()
1175 def checkSubservices(self, service):
1176 if service.subServices().getNumberOfSubservices() > 0:
1177 self.showSubServiceIndication()
1179 self.hideSubServiceIndication()
1181 def checkDolby(self, service):
1184 audio = service.audioTracks()
1185 if audio is not None:
1186 n = audio.getNumberOfTracks()
1188 i = audio.getTrackInfo(x)
1189 description = i.getDescription();
1190 if description.find("AC3") != -1 or description.find("DTS") != -1:
1194 self["DolbyActive"].show()
1196 self["DolbyActive"].hide()
1198 def checkCrypted(self, service):
1199 info = service.info()
1200 if info is not None:
1201 if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1202 self["CryptActive"].show()
1204 self["CryptActive"].hide()
1206 def gotServiceEvent(self, ev):
1207 service = self.session.nav.getCurrentService()
1208 if ev == iPlayableService.evUpdatedEventInfo:
1209 self.checkSubservices(service)
1210 self.checkFormat(service)
1211 elif ev == iPlayableService.evUpdatedInfo:
1212 self.checkCrypted(service)
1213 self.checkDolby(service)
1214 elif ev == iPlayableService.evEnd:
1215 self.hideSubServiceIndication()
1216 self["CryptActive"].hide()
1217 self["DolbyActive"].hide()
1218 self["FormatActive"].hide()
1220 class InfoBarNotifications:
1222 self.onExecBegin.append(self.checkNotifications)
1223 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1225 def checkNotificationsIfExecing(self):
1227 self.checkNotifications()
1229 def checkNotifications(self):
1230 if len(Notifications.notifications):
1231 n = Notifications.notifications[0]
1232 Notifications.notifications = Notifications.notifications[1:]
1236 self.session.openWithCallback(cb, *n[1:])
1238 self.session.open(*n[1:])
1240 class InfoBarServiceNotifications:
1242 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1244 iPlayableService.evEnd: self.serviceHasEnded
1247 def serviceHasEnded(self):
1248 print "service end!"
1251 self.setSeekState(self.SEEK_STATE_PLAY)
1255 class InfoBarCueSheetSupport:
1261 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions",
1263 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1264 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1265 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1269 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1271 iPlayableService.evStart: self.__serviceStarted,
1274 def __serviceStarted(self):
1275 print "new service started! trying to download cuts!"
1276 self.downloadCuesheet()
1278 def __getSeekable(self):
1279 service = self.session.nav.getCurrentService()
1282 return service.seek()
1284 def cueGetCurrentPosition(self):
1285 seek = self.__getSeekable()
1288 r = seek.getPlayPosition()
1293 def jumpPreviousNextMark(self, cmp, alternative=None):
1294 current_pos = self.cueGetCurrentPosition()
1295 if current_pos is None:
1297 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1298 if mark is not None:
1300 elif alternative is not None:
1305 seekable = self.__getSeekable()
1306 if seekable is not None:
1307 seekable.seekTo(pts)
1309 def jumpPreviousMark(self):
1310 # we add 2 seconds, so if the play position is <2s after
1311 # the mark, the mark before will be used
1312 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1314 def jumpNextMark(self):
1315 self.jumpPreviousNextMark(lambda x: x)
1317 def getNearestCutPoint(self, pts, cmp=abs):
1320 for cp in self.cut_list:
1321 diff = cmp(cp[0] - pts)
1322 if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1326 def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
1327 current_pos = self.cueGetCurrentPosition()
1328 if current_pos is None:
1329 print "not seekable"
1332 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1334 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1336 return nearest_cutpoint
1338 self.removeMark(nearest_cutpoint)
1339 elif not onlyremove and not onlyreturn:
1340 self.addMark((current_pos, self.CUT_TYPE_MARK))
1345 def addMark(self, point):
1346 bisect.insort(self.cut_list, point)
1347 self.uploadCuesheet()
1349 def removeMark(self, point):
1350 self.cut_list.remove(point)
1351 self.uploadCuesheet()
1353 def __getCuesheet(self):
1354 service = self.session.nav.getCurrentService()
1357 return service.cueSheet()
1359 def uploadCuesheet(self):
1360 cue = self.__getCuesheet()
1363 print "upload failed, no cuesheet interface"
1365 cue.setCutList(self.cut_list)
1367 def downloadCuesheet(self):
1368 cue = self.__getCuesheet()
1371 print "upload failed, no cuesheet interface"
1373 self.cut_list = cue.getCutList()
1375 class InfoBarSummary(Screen):
1377 <screen position="0,0" size="132,64">
1378 <widget name="Clock" position="50,46" size="82,18" font="Regular;16" />
1379 <widget name="CurrentService" position="0,4" size="132,42" font="Regular;18" />
1382 def __init__(self, session, parent):
1383 Screen.__init__(self, session)
1384 self["CurrentService"] = ServiceName(self.session.nav)
1385 self["Clock"] = Clock()
1387 class InfoBarSummarySupport:
1391 def createSummary(self):
1392 return InfoBarSummary