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.Dish import Dish
20 from Screens.Standby import Standby
21 from Screens.EventView import EventViewEPGSelect
22 from Screens.MinuteInput import MinuteInput
23 from Components.Harddisk import harddiskmanager
25 from Components.ServiceEventTracker import ServiceEventTracker
27 from Tools import Notifications
28 from Tools.Directories import *
30 #from enigma import eTimer, eDVBVolumecontrol, quitMainloop
37 from Components.config import config, currentConfigSelectionElement
40 from Menu import MainMenu, mdom
44 self.dishDialog = self.session.instantiateDialog(Dish)
45 self.onLayoutFinish.append(self.dishDialog.show)
47 class InfoBarShowHide:
48 """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
56 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
58 "toggleShow": self.toggleShow,
62 self.__state = self.STATE_SHOWN
65 self.onExecBegin.append(self.show)
67 self.hideTimer = eTimer()
68 self.hideTimer.timeout.get().append(self.doTimerHide)
69 self.hideTimer.start(5000, True)
71 self.onShow.append(self.__onShow)
72 self.onHide.append(self.__onHide)
75 self.__state = self.STATE_SHOWN
78 def startHideTimer(self):
79 if self.__state == self.STATE_SHOWN and not self.__locked:
80 self.hideTimer.start(5000, True)
83 self.__state = self.STATE_HIDDEN
89 def doTimerHide(self):
91 if self.__state == self.STATE_SHOWN:
95 if self.__state == self.STATE_SHOWN:
98 elif self.__state == self.STATE_HIDDEN:
102 self.__locked = self.__locked + 1
105 self.hideTimer.stop()
107 def unlockShow(self):
108 self.__locked = self.__locked - 1
110 self.startHideTimer()
112 # def startShow(self):
113 # self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
114 # self.__state = self.STATE_SHOWN
116 # def startHide(self):
117 # self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
118 # self.__state = self.STATE_HIDDEN
120 class NumberZap(Screen):
127 self.close(int(self["number"].getText()))
129 def keyNumberGlobal(self, number):
130 self.Timer.start(3000, True) #reset timer
131 self.field = self.field + str(number)
132 self["number"].setText(self.field)
133 if len(self.field) >= 4:
136 def __init__(self, session, number):
137 Screen.__init__(self, session)
138 self.field = str(number)
140 self["channel"] = Label(_("Channel:"))
142 self["number"] = Label(self.field)
144 self["actions"] = NumberActionMap( [ "SetupActions" ],
148 "1": self.keyNumberGlobal,
149 "2": self.keyNumberGlobal,
150 "3": self.keyNumberGlobal,
151 "4": self.keyNumberGlobal,
152 "5": self.keyNumberGlobal,
153 "6": self.keyNumberGlobal,
154 "7": self.keyNumberGlobal,
155 "8": self.keyNumberGlobal,
156 "9": self.keyNumberGlobal,
157 "0": self.keyNumberGlobal
160 self.Timer = eTimer()
161 self.Timer.timeout.get().append(self.keyOK)
162 self.Timer.start(3000, True)
164 class InfoBarPowerKey:
165 """ PowerKey stuff - handles the powerkey press and powerkey release actions"""
168 self.powerKeyTimer = eTimer()
169 self.powerKeyTimer.timeout.get().append(self.powertimer)
170 self["PowerKeyActions"] = HelpableActionMap(self, "PowerKeyActions",
172 "powerdown": self.powerdown,
173 "powerup": self.powerup,
174 "discreteStandby": (self.standby, "Go standby"),
175 "discretePowerOff": (self.quit, "Go to deep standby"),
178 def powertimer(self):
179 print "PowerOff - Now!"
183 self.standbyblocked = 0
184 self.powerKeyTimer.start(3000, True)
187 self.powerKeyTimer.stop()
188 if self.standbyblocked == 0:
189 self.standbyblocked = 1
193 self.session.open(Standby, self)
199 class InfoBarNumberZap:
200 """ Handles an initial number for NumberZapping """
202 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
204 "1": self.keyNumberGlobal,
205 "2": self.keyNumberGlobal,
206 "3": self.keyNumberGlobal,
207 "4": self.keyNumberGlobal,
208 "5": self.keyNumberGlobal,
209 "6": self.keyNumberGlobal,
210 "7": self.keyNumberGlobal,
211 "8": self.keyNumberGlobal,
212 "9": self.keyNumberGlobal,
213 "0": self.keyNumberGlobal,
216 def keyNumberGlobal(self, number):
217 # print "You pressed number " + str(number)
219 self.servicelist.recallPrevService()
222 self.session.openWithCallback(self.numberEntered, NumberZap, number)
224 def numberEntered(self, retval):
225 # print self.servicelist
227 self.zapToNumber(retval)
229 def searchNumberHelper(self, serviceHandler, num, bouquet):
230 servicelist = serviceHandler.list(bouquet)
231 if not servicelist is None:
233 serviceIterator = servicelist.getNext()
234 if not serviceIterator.valid(): #check end of list
236 if serviceIterator.flags: #assume normal dvb service have no flags set
239 if not num: #found service with searched number ?
240 return serviceIterator, 0
243 def zapToNumber(self, number):
244 bouquet = self.servicelist.bouquet_root
246 serviceHandler = eServiceCenter.getInstance()
247 if bouquet.toString().find('FROM BOUQUET "bouquets.') == -1: #FIXME HACK
248 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
250 bouquetlist = serviceHandler.list(bouquet)
251 if not bouquetlist is None:
253 bouquet = self.servicelist.appendDVBTypes(bouquetlist.getNext())
254 if not bouquet.valid(): #check end of list
256 if (bouquet.flags & eServiceReference.flagDirectory) != eServiceReference.flagDirectory:
258 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
259 if not service is None:
260 if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
261 self.servicelist.clearPath()
262 if self.servicelist.bouquet_root != bouquet:
263 self.servicelist.enterPath(self.servicelist.bouquet_root)
264 self.servicelist.enterPath(bouquet)
265 self.servicelist.setCurrentSelection(service) #select the service in servicelist
266 self.servicelist.zap()
268 class InfoBarChannelSelection:
269 """ ChannelSelection - handles the channelSelection dialog and the initial
270 channelChange actions which open the channelSelection dialog """
273 self.servicelist = self.session.instantiateDialog(ChannelSelection)
275 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
277 "switchChannelUp": self.switchChannelUp,
278 "switchChannelDown": self.switchChannelDown,
279 "zapUp": (self.zapUp, _("previous channel")),
280 "zapDown": (self.zapDown, _("next channel")),
281 "historyBack": (self.historyBack, _("previous channel in history")),
282 "historyNext": (self.historyNext, _("next channel in history"))
285 def historyBack(self):
286 self.servicelist.historyBack()
288 def historyNext(self):
289 self.servicelist.historyNext()
291 def switchChannelUp(self):
292 self.servicelist.moveUp()
293 self.session.execDialog(self.servicelist)
295 def switchChannelDown(self):
296 self.servicelist.moveDown()
297 self.session.execDialog(self.servicelist)
300 if currentConfigSelectionElement(config.usage.quickzap_bouquet_change) == "yes":
301 if self.servicelist.inBouquet() and self.servicelist.atBegin():
302 self.servicelist.prevBouquet()
303 self.servicelist.moveUp()
304 self.servicelist.zap()
308 if currentConfigSelectionElement(config.usage.quickzap_bouquet_change) == "yes" and self.servicelist.inBouquet() and self.servicelist.atEnd():
309 self.servicelist.nextBouquet()
311 self.servicelist.moveDown()
312 self.servicelist.zap()
316 """ Handles a menu action, to open the (main) menu """
318 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions",
320 "mainMenu": (self.mainMenu, "Enter main menu..."),
324 print "loading mainmenu XML..."
325 menu = mdom.childNodes[0]
326 assert menu.tagName == "menu", "root element in menu must be 'menu'!"
327 self.session.open(MainMenu, menu, menu.childNodes)
330 """ EPG - Opens an EPG list when the showEPGList action fires """
332 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
334 "showEventInfo": (self.openEventView, _("show EPG...")),
337 def zapToService(self, service):
338 if not service is None:
339 if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
340 self.servicelist.clearPath()
341 if self.servicelist.bouquet_root != self.epg_bouquet:
342 self.servicelist.enterPath(self.servicelist.bouquet_root)
343 self.servicelist.enterPath(self.epg_bouquet)
344 self.servicelist.setCurrentSelection(service) #select the service in servicelist
345 self.servicelist.zap()
347 def openBouquetEPG(self, bouquet, withCallback=True):
348 ptr=eEPGCache.getInstance()
350 servicelist = eServiceCenter.getInstance().list(bouquet)
351 if not servicelist is None:
353 service = servicelist.getNext()
354 if not service.valid(): #check if end of list
356 if service.flags: #ignore non playable services
358 services.append(ServiceReference(service))
360 self.epg_bouquet = bouquet
362 self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService)
364 self.session.open(EPGSelection, services, self.zapToService)
366 def closed(self, ret):
370 def openMultiServiceEPG(self, withCallback=True):
371 bouquets = self.servicelist.getBouquetList()
376 if cnt > 1: # show bouquet list
378 self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG)
380 self.session.open(BouquetSelector, bouquets, self.openBouquetEPG)
382 self.openBouquetEPG(bouquets[0][1], withCallback)
384 def openSingleServiceEPG(self):
385 ref=self.session.nav.getCurrentlyPlayingServiceReference()
386 ptr=eEPGCache.getInstance()
387 self.session.openWithCallback(self.closed, EPGSelection, ref)
389 def openEventView(self):
391 service = self.session.nav.getCurrentService()
392 ref = self.session.nav.getCurrentlyPlayingServiceReference()
393 info = service.info()
396 self.epglist.append(ptr)
399 self.epglist.append(ptr)
400 if len(self.epglist) == 0:
401 epg = eEPGCache.getInstance()
402 ptr = epg.lookupEventTime(ref, -1)
404 self.epglist.append(ptr)
405 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
407 self.epglist.append(ptr)
408 if len(self.epglist) > 0:
409 self.session.open(EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG)
411 print "no epg for the service avail.. so we show multiepg instead of eventinfo"
412 self.openMultiServiceEPG(False)
414 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
415 if len(self.epglist) > 1:
416 tmp = self.epglist[0]
417 self.epglist[0]=self.epglist[1]
419 setEvent(self.epglist[0])
424 """provides a snr/agc/ber display"""
426 self["snr"] = Label()
427 self["agc"] = Label()
428 self["ber"] = Label()
429 self["snr_percent"] = Label()
430 self["agc_percent"] = Label()
431 self["ber_count"] = Label()
432 self["snr_progress"] = ProgressBar()
433 self["agc_progress"] = ProgressBar()
434 self["ber_progress"] = ProgressBar()
435 self.timer = eTimer()
436 self.timer.timeout.get().append(self.updateTunerInfo)
437 self.timer.start(1000)
443 return (long)(log(val)/log(2))
446 def updateTunerInfo(self):
447 if self.instance.isVisible():
448 service = self.session.nav.getCurrentService()
452 if service is not None:
453 feinfo = service.frontendStatusInfo()
454 if feinfo is not None:
455 ber=feinfo.getFrontendInfo(iFrontendStatusInformation.bitErrorRate)
456 snr=feinfo.getFrontendInfo(iFrontendStatusInformation.signalPower)*100/65536
457 agc=feinfo.getFrontendInfo(iFrontendStatusInformation.signalQuality)*100/65536
458 self["snr_percent"].setText("%d%%"%(snr))
459 self["agc_percent"].setText("%d%%"%(agc))
460 self["ber_count"].setText("%d"%(ber))
461 self["snr_progress"].setValue(snr)
462 self["agc_progress"].setValue(agc)
463 self["ber_progress"].setValue(self.calc(ber))
466 """provides a current/next event info display"""
468 self["Event_Now_StartTime"] = EventInfo(self.session.nav, EventInfo.Now_StartTime)
469 self["Event_Next_StartTime"] = EventInfo(self.session.nav, EventInfo.Next_StartTime)
471 self["Event_Now"] = EventInfo(self.session.nav, EventInfo.Now)
472 self["Event_Next"] = EventInfo(self.session.nav, EventInfo.Next)
474 self["Event_Now_Duration"] = EventInfo(self.session.nav, EventInfo.Now_Remaining)
475 self["Event_Next_Duration"] = EventInfo(self.session.nav, EventInfo.Next_Duration)
477 self["Now_ProgressBar"] = EventInfoProgress(self.session.nav, EventInfo.Now)
479 class InfoBarServiceName:
481 self["ServiceName"] = ServiceName(self.session.nav)
484 """handles actions like seeking, pause"""
486 # ispause, isff, issm
487 SEEK_STATE_PLAY = (0, 0, 0, ">")
488 SEEK_STATE_PAUSE = (1, 0, 0, "||")
489 SEEK_STATE_FF_2X = (0, 2, 0, ">> 2x")
490 SEEK_STATE_FF_4X = (0, 4, 0, ">> 4x")
491 SEEK_STATE_FF_8X = (0, 8, 0, ">> 8x")
492 SEEK_STATE_FF_32X = (0, 32, 0, ">> 32x")
493 SEEK_STATE_FF_64X = (0, 64, 0, ">> 64x")
494 SEEK_STATE_FF_128X = (0, 128, 0, ">> 128x")
496 SEEK_STATE_BACK_16X = (0, -16, 0, "<< 16x")
497 SEEK_STATE_BACK_32X = (0, -32, 0, "<< 32x")
498 SEEK_STATE_BACK_64X = (0, -64, 0, "<< 64x")
499 SEEK_STATE_BACK_128X = (0, -128, 0, "<< 128x")
501 SEEK_STATE_SM_HALF = (0, 0, 2, "/2")
502 SEEK_STATE_SM_QUARTER = (0, 0, 4, "/4")
503 SEEK_STATE_SM_EIGHTH = (0, 0, 8, "/8")
506 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
508 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
509 iPlayableService.evStart: self.__serviceStarted,
511 iPlayableService.evEOF: self.__evEOF,
512 iPlayableService.evSOF: self.__evSOF,
514 self["SeekActions"] = HelpableActionMap(self, "InfobarSeekActions",
516 "pauseService": (self.pauseService, "pause"),
517 "unPauseService": (self.unPauseService, "continue"),
519 "seekFwd": (self.seekFwd, "skip forward"),
520 "seekFwdDown": self.seekFwdDown,
521 "seekFwdUp": self.seekFwdUp,
522 "seekBack": (self.seekBack, "skip backward"),
523 "seekBackDown": self.seekBackDown,
524 "seekBackUp": self.seekBackUp,
526 # give them a little more priority to win over color buttons
528 self.seekstate = self.SEEK_STATE_PLAY
529 self.onClose.append(self.delTimer)
531 self.fwdtimer = False
532 self.fwdKeyTimer = eTimer()
533 self.fwdKeyTimer.timeout.get().append(self.fwdTimerFire)
535 self.rwdtimer = False
536 self.rwdKeyTimer = eTimer()
537 self.rwdKeyTimer.timeout.get().append(self.rwdTimerFire)
539 self.onPlayStateChanged = [ ]
541 self.lockedBecauseOfSkipping = False
554 service = self.session.nav.getCurrentService()
558 seek = service.seek()
560 if seek is None or not seek.isCurrentlySeekable():
565 def isSeekable(self):
566 if self.getSeek() is None:
570 def __seekableStatusChanged(self):
571 print "seekable status changed!"
572 if not self.isSeekable():
573 self["SeekActions"].setEnabled(False)
574 print "not seekable, return to play"
575 self.setSeekState(self.SEEK_STATE_PLAY)
577 self["SeekActions"].setEnabled(True)
580 def __serviceStarted(self):
581 self.seekstate = self.SEEK_STATE_PLAY
583 def setSeekState(self, state):
584 service = self.session.nav.getCurrentService()
589 if not self.isSeekable():
590 if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
591 state = self.SEEK_STATE_PLAY
593 pauseable = service.pause()
595 if pauseable is None:
596 print "not pauseable."
597 state = self.SEEK_STATE_PLAY
599 oldstate = self.seekstate
600 self.seekstate = state
603 if oldstate[i] != self.seekstate[i]:
604 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
606 for c in self.onPlayStateChanged:
609 self.checkSkipShowHideLock()
613 def pauseService(self):
614 if self.seekstate == self.SEEK_STATE_PAUSE:
615 print "pause, but in fact unpause"
616 self.unPauseService()
618 if self.seekstate == self.SEEK_STATE_PLAY:
619 print "yes, playing."
621 print "no", self.seekstate
623 self.setSeekState(self.SEEK_STATE_PAUSE);
625 def unPauseService(self):
627 self.setSeekState(self.SEEK_STATE_PLAY);
629 def doSeek(self, seektime):
630 print "doseek", seektime
631 service = self.session.nav.getCurrentService()
635 seekable = self.getSeek()
639 seekable.seekTo(90 * seektime)
641 def seekFwdDown(self):
642 print "start fwd timer"
644 self.fwdKeyTimer.start(1000)
646 def seekBackDown(self):
647 print "start rewind timer"
649 self.rwdKeyTimer.start(1000)
654 self.fwdKeyTimer.stop()
655 self.fwdtimer = False
660 self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
661 self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
662 self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
663 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
664 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
665 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
666 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
667 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
668 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_PLAY,
669 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_16X,
670 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
671 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
672 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
673 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
674 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER
676 self.setSeekState(lookup[self.seekstate])
678 def seekBackUp(self):
681 self.rwdKeyTimer.stop()
682 self.rwdtimer = False
687 self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_16X,
688 self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
689 self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
690 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
691 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
692 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
693 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
694 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
695 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_BACK_32X,
696 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
697 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
698 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
699 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
700 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
701 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE
703 self.setSeekState(lookup[self.seekstate])
705 def fwdTimerFire(self):
706 print "Display seek fwd"
707 self.fwdKeyTimer.stop()
708 self.fwdtimer = False
709 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
711 def fwdSeekTo(self, minutes):
712 print "Seek", minutes, "minutes forward"
714 seekable = self.getSeek()
715 if seekable is not None:
716 seekable.seekRelative(1, minutes * 60 * 90000)
718 def rwdTimerFire(self):
720 self.rwdKeyTimer.stop()
721 self.rwdtimer = False
722 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
724 def rwdSeekTo(self, minutes):
726 self.fwdSeekTo(0 - minutes)
728 def checkSkipShowHideLock(self):
729 wantlock = self.seekstate != self.SEEK_STATE_PLAY
731 if self.lockedBecauseOfSkipping and not wantlock:
733 self.lockedBecauseOfSkipping = False
735 if wantlock and not self.lockedBecauseOfSkipping:
737 self.lockedBecauseOfSkipping = True
740 if self.seekstate != self.SEEK_STATE_PLAY:
741 self.setSeekState(self.SEEK_STATE_PAUSE)
743 self.getSeek().seekRelative(1, -90000)
744 self.setSeekState(self.SEEK_STATE_PLAY)
746 self.setSeekState(self.SEEK_STATE_PAUSE)
749 self.setSeekState(self.SEEK_STATE_PLAY)
752 def seekRelative(self, diff):
753 seekable = self.getSeek()
754 if seekable is not None:
755 seekable.seekRelative(0, diff)
757 from Screens.PVRState import PVRState
759 class InfoBarPVRState:
761 self.onPlayStateChanged.append(self.__playStateChanged)
762 self.pvrStateDialog = self.session.instantiateDialog(PVRState)
763 self.onShow.append(self.__mayShow)
764 self.onHide.append(self.pvrStateDialog.hide)
767 if self.seekstate != self.SEEK_STATE_PLAY:
768 self.pvrStateDialog.show()
770 def __playStateChanged(self, state):
771 playstateString = state[3]
772 self.pvrStateDialog["state"].setText(playstateString)
775 class InfoBarShowMovies:
777 # i don't really like this class.
778 # it calls a not further specified "movie list" on up/down/movieList,
779 # so this is not more than an action map
781 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
783 "movieList": (self.showMovies, "movie list"),
784 "up": (self.showMovies, "movie list"),
785 "down": (self.showMovies, "movie list")
788 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
792 # Timeshift works the following way:
793 # demux0 demux1 "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
794 # - normal playback TUNER unused PLAY enable disable disable
795 # - user presses "yellow" button. TUNER record PAUSE enable disable enable
796 # - user presess pause again FILE record PLAY enable disable enable
797 # - user fast forwards FILE record FF enable disable enable
798 # - end of timeshift buffer reached TUNER record PLAY enable enable disable
799 # - user backwards FILE record BACK # !! enable disable enable
803 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
804 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
805 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
806 # - the user can now PVR around
807 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
808 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
810 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
811 # - if the user rewinds, or press pause, timeshift will be activated again
813 # note that a timeshift can be enabled ("recording") and
814 # activated (currently time-shifting).
816 class InfoBarTimeshift:
818 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
820 "timeshiftStart": (self.startTimeshift, "start timeshift"), # the "yellow key"
821 "timeshiftStop": (self.stopTimeshift, "stop timeshift") # currently undefined :), probably 'TV'
823 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
825 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
826 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause # something like "backward key"
827 }, prio=-1) # priority over record
829 self.timeshift_enabled = 0
830 self.timeshift_state = 0
831 self.ts_pause_timer = eTimer()
832 self.ts_pause_timer.timeout.get().append(self.pauseService)
834 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
836 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
839 def getTimeshift(self):
840 service = self.session.nav.getCurrentService()
841 return service.timeshift()
843 def startTimeshift(self):
844 print "enable timeshift"
845 ts = self.getTimeshift()
847 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
848 print "no ts interface"
851 if self.timeshift_enabled:
852 print "hu, timeshift already enabled?"
854 if not ts.startTimeshift():
855 self.timeshift_enabled = 1
858 self.setSeekState(self.SEEK_STATE_PAUSE)
860 # enable the "TimeshiftEnableActions", which will override
861 # the startTimeshift actions
862 self.__seekableStatusChanged()
864 print "timeshift failed"
866 def stopTimeshift(self):
867 if not self.timeshift_enabled:
869 print "disable timeshift"
870 ts = self.getTimeshift()
873 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
875 def stopTimeshiftConfirmed(self, confirmed):
879 ts = self.getTimeshift()
884 self.timeshift_enabled = 0
887 self.__seekableStatusChanged()
889 # activates timeshift, and seeks to (almost) the end
890 def activateTimeshiftEnd(self):
891 ts = self.getTimeshift()
896 if ts.isTimeshiftActive():
897 print "!! activate timeshift called - but shouldn't this be a normal pause?"
900 self.setSeekState(self.SEEK_STATE_PLAY)
901 ts.activateTimeshift()
904 # same as activateTimeshiftEnd, but pauses afterwards.
905 def activateTimeshiftEndAndPause(self):
906 state = self.seekstate
907 self.activateTimeshiftEnd()
909 # well, this is "andPause", but it could be pressed from pause,
910 # when pausing on the (fake-)"live" picture, so an un-pause
913 print "now, pauseService"
914 if state == self.SEEK_STATE_PLAY:
915 print "is PLAYING, start pause timer"
916 self.ts_pause_timer.start(200, 1)
919 self.unPauseService()
921 def __seekableStatusChanged(self):
924 print "self.isSeekable", self.isSeekable()
925 print "self.timeshift_enabled", self.timeshift_enabled
927 # when this service is not seekable, but timeshift
928 # is enabled, this means we can activate
930 if not self.isSeekable() and self.timeshift_enabled:
933 print "timeshift activate:", enabled
934 self["TimeshiftActivateActions"].setEnabled(enabled)
936 from RecordTimer import parseEvent
938 class InfoBarInstantRecord:
939 """Instant Record - handles the instantRecord action in order to
940 start/stop instant records"""
942 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
944 "instantRecord": (self.instantRecord, "Instant Record..."),
946 self.recording = None
947 self["BlinkingPoint"] = BlinkingPixmapConditional()
948 self.onLayoutFinish.append(self["BlinkingPoint"].hideWidget)
949 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
951 def stopCurrentRecording(self):
952 self.session.nav.RecordTimer.removeEntry(self.recording)
953 self.recording = None
955 def startInstantRecording(self):
956 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
958 # try to get event info
961 service = self.session.nav.getCurrentService()
962 info = service.info()
963 ev = info.getEvent(0)
968 if event is not None:
969 data = parseEvent(event)
971 end = begin + 3600 * 10
973 data = (begin, end, data[2], data[3], data[4])
975 data = (time.time(), time.time() + 3600 * 10, "instant record", "", None)
977 # fix me, description.
978 self.recording = self.session.nav.recordWithTimer(serviceref, *data)
979 self.recording.dontSave = True
981 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
983 def isInstantRecordRunning(self):
984 if self.recording != None:
985 if self.recording.isRunning():
989 def recordQuestionCallback(self, answer):
993 if self.isInstantRecordRunning():
994 self.stopCurrentRecording()
996 self.startInstantRecording()
998 def instantRecord(self):
1000 stat = os.stat(resolveFilename(SCOPE_HDD))
1002 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1005 if self.isInstantRecordRunning():
1006 self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Do you want to stop the current\n(instant) recording?"))
1008 self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Start recording?"))
1010 from Screens.AudioSelection import AudioSelection
1012 class InfoBarAudioSelection:
1014 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1016 "audioSelection": (self.audioSelection, "Audio Options..."),
1019 def audioSelection(self):
1020 service = self.session.nav.getCurrentService()
1021 audio = service.audioTracks()
1022 n = audio.getNumberOfTracks()
1024 self.session.open(AudioSelection, audio)
1026 from Screens.SubserviceSelection import SubserviceSelection
1028 class InfoBarSubserviceSelection:
1030 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1032 "subserviceSelection": (self.subserviceSelection, "Subservice list..."),
1035 def subserviceSelection(self):
1036 service = self.session.nav.getCurrentService()
1037 subservices = service.subServices()
1038 n = subservices.getNumberOfSubservices()
1040 self.session.openWithCallback(self.subserviceSelected, SubserviceSelection, subservices)
1042 def subserviceSelected(self, service):
1043 if not service is None:
1044 self.session.nav.playService(service)
1046 class InfoBarAdditionalInfo:
1048 self["DolbyActive"] = Pixmap()
1049 self["CryptActive"] = Pixmap()
1050 self["FormatActive"] = Pixmap()
1052 self["ButtonRed"] = PixmapConditional(withTimer = False)
1053 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1054 self.onLayoutFinish.append(self["ButtonRed"].update)
1055 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1056 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1057 self.onLayoutFinish.append(self["ButtonRedText"].update)
1059 self["ButtonGreen"] = Pixmap()
1060 self["ButtonGreenText"] = Label(_("Subservices"))
1062 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1063 self["ButtonYellow"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1064 self["ButtonYellowText"] = LabelConditional(text = _("Timeshifting"), withTimer = False)
1065 self["ButtonYellowText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1066 self.onLayoutFinish.append(self["ButtonYellow"].update)
1067 self.onLayoutFinish.append(self["ButtonYellowText"].update)
1069 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1070 self["ButtonBlue"].setConnect(lambda: False)
1071 self["ButtonBlueText"] = LabelConditional(text = _("Extensions"), withTimer = False)
1072 self["ButtonBlueText"].setConnect(lambda: False)
1073 self.onLayoutFinish.append(self["ButtonBlue"].update)
1074 self.onLayoutFinish.append(self["ButtonBlueText"].update)
1076 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1078 def hideSubServiceIndication(self):
1079 self["ButtonGreen"].hideWidget()
1080 self["ButtonGreenText"].hide()
1082 def showSubServiceIndication(self):
1083 self["ButtonGreen"].showWidget()
1084 self["ButtonGreenText"].show()
1086 def checkFormat(self, service):
1087 info = service.info()
1088 if info is not None:
1089 aspect = info.getInfo(iServiceInformation.sAspect)
1090 if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1091 self["FormatActive"].showWidget()
1093 self["FormatActive"].hideWidget()
1095 def checkSubservices(self, service):
1096 if service.subServices().getNumberOfSubservices() > 0:
1097 self.showSubServiceIndication()
1099 self.hideSubServiceIndication()
1101 def checkDolby(self, service):
1104 audio = service.audioTracks()
1105 if audio is not None:
1106 n = audio.getNumberOfTracks()
1108 i = audio.getTrackInfo(x)
1109 description = i.getDescription();
1110 if description.find("AC3") != -1 or description.find("DTS") != -1:
1114 self["DolbyActive"].showWidget()
1116 self["DolbyActive"].hideWidget()
1118 def checkCrypted(self, service):
1119 info = service.info()
1120 if info is not None:
1121 if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1122 self["CryptActive"].showWidget()
1124 self["CryptActive"].hideWidget()
1126 def gotServiceEvent(self, ev):
1127 service = self.session.nav.getCurrentService()
1128 if ev == iPlayableService.evUpdatedEventInfo:
1129 self.checkSubservices(service)
1130 self.checkFormat(service)
1131 elif ev == iPlayableService.evUpdatedInfo:
1132 self.checkCrypted(service)
1133 self.checkDolby(service)
1134 elif ev == iPlayableService.evEnd:
1135 self.hideSubServiceIndication()
1136 self["CryptActive"].hideWidget()
1137 self["DolbyActive"].hideWidget()
1138 self["FormatActive"].hideWidget()
1140 class InfoBarNotifications:
1142 self.onExecBegin.append(self.checkNotifications)
1143 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1145 def checkNotificationsIfExecing(self):
1147 self.checkNotifications()
1149 def checkNotifications(self):
1150 if len(Notifications.notifications):
1151 n = Notifications.notifications[0]
1152 Notifications.notifications = Notifications.notifications[1:]
1156 self.session.openWithCallback(cb, *n[1:])
1158 self.session.open(*n[1:])
1160 class InfoBarServiceNotifications:
1162 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1164 iPlayableService.evEnd: self.serviceHasEnded
1167 def serviceHasEnded(self):
1168 print "service end!"
1171 self.setSeekState(self.SEEK_STATE_PLAY)
1175 class InfoBarCueSheetSupport:
1181 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions",
1183 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1184 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1185 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1189 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1191 iPlayableService.evStart: self.__serviceStarted,
1194 def __serviceStarted(self):
1195 print "new service started! trying to download cuts!"
1196 self.downloadCuesheet()
1198 def __getSeekable(self):
1199 service = self.session.nav.getCurrentService()
1202 return service.seek()
1204 def __getCurrentPosition(self):
1205 seek = self.__getSeekable()
1208 r = seek.getPlayPosition()
1213 def jumpPreviousNextMark(self, cmp, alternative=None):
1214 current_pos = self.__getCurrentPosition()
1215 if current_pos is None:
1217 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1218 if mark is not None:
1220 elif alternative is not None:
1225 seekable = self.__getSeekable()
1226 if seekable is not None:
1227 seekable.seekTo(pts)
1229 def jumpPreviousMark(self):
1230 print "jumpPreviousMark"
1231 # we add 2 seconds, so if the play position is <2s after
1232 # the mark, the mark before will be used
1233 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1235 def jumpNextMark(self):
1236 print "jumpNextMark"
1237 self.jumpPreviousNextMark(lambda x: x)
1239 def getNearestCutPoint(self, pts, cmp=abs):
1242 for cp in self.cut_list:
1243 diff = cmp(cp[0] - pts)
1244 if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1248 def toggleMark(self):
1250 current_pos = self.__getCurrentPosition()
1251 if current_pos is None:
1252 print "not seekable"
1255 print "current position: ", current_pos
1257 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1258 print "nearest_cutpoint: ", nearest_cutpoint
1260 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < 5*90000:
1261 self.removeMark(self, *nearest_cutpoint)
1263 self.addMark(self, current_pos, self.CUT_TYPE_MARK)
1265 def addMark(self, where, type):
1266 bisect.insort(self.cut_list, (current_pos, self.CUT_TYPE_MARK))
1267 self.uploadCuesheet()
1269 def removeMark(self, where, type):
1270 self.cut_list.remove(nearest_cutpoint)
1271 self.uploadCuesheet()
1273 def __getCuesheet(self):
1274 service = self.session.nav.getCurrentService()
1277 return service.cueSheet()
1279 def uploadCuesheet(self):
1280 cue = self.__getCuesheet()
1283 print "upload failed, no cuesheet interface"
1285 cue.setCutList(self.cut_list)
1287 def downloadCuesheet(self):
1288 cue = self.__getCuesheet()
1291 print "upload failed, no cuesheet interface"
1293 self.cut_list = cue.getCutList()
1295 print "cuts:", self.cut_list