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, _("next channel")),
280 "zapDown": (self.zapDown, _("previous 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_4X = (0, -4, 0, "<< 4x")
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 "seekFwdUp": (self.seekFwdUp, "skip forward"),
521 "seekBack": (self.seekBack, "skip backward"),
522 "seekBackUp": (self.seekBackUp, "skip backward"),
524 # give them a little more priority to win over color buttons
526 self.seekstate = self.SEEK_STATE_PLAY
527 self.onClose.append(self.delTimer)
529 self.fwdtimer = False
530 self.fwdKeyTimer = eTimer()
531 self.fwdKeyTimer.timeout.get().append(self.fwdTimerFire)
533 self.rwdtimer = False
534 self.rwdKeyTimer = eTimer()
535 self.rwdKeyTimer.timeout.get().append(self.rwdTimerFire)
537 self.onPlayStateChanged = [ ]
539 self.lockedBecauseOfSkipping = False
552 service = self.session.nav.getCurrentService()
556 seek = service.seek()
558 if seek is None or not seek.isCurrentlySeekable():
563 def isSeekable(self):
564 if self.getSeek() is None:
568 def __seekableStatusChanged(self):
569 print "seekable status changed!"
570 if not self.isSeekable():
571 self["SeekActions"].setEnabled(False)
572 print "not seekable, return to play"
573 self.setSeekState(self.SEEK_STATE_PLAY)
575 self["SeekActions"].setEnabled(True)
578 def __serviceStarted(self):
579 self.seekstate = self.SEEK_STATE_PLAY
581 def setSeekState(self, state):
582 service = self.session.nav.getCurrentService()
587 if not self.isSeekable():
588 if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
589 state = self.SEEK_STATE_PLAY
591 pauseable = service.pause()
593 if pauseable is None:
594 print "not pauseable."
595 state = self.SEEK_STATE_PLAY
597 oldstate = self.seekstate
598 self.seekstate = state
601 if oldstate[i] != self.seekstate[i]:
602 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
604 for c in self.onPlayStateChanged:
607 self.checkSkipShowHideLock()
611 def pauseService(self):
612 if self.seekstate == self.SEEK_STATE_PAUSE:
613 print "pause, but in fact unpause"
614 self.unPauseService()
616 if self.seekstate == self.SEEK_STATE_PLAY:
617 print "yes, playing."
619 print "no", self.seekstate
621 self.setSeekState(self.SEEK_STATE_PAUSE);
623 def unPauseService(self):
625 self.setSeekState(self.SEEK_STATE_PLAY);
627 def doSeek(self, seektime):
628 print "doseek", seektime
629 service = self.session.nav.getCurrentService()
633 seekable = self.getSeek()
637 seekable.seekTo(90 * seektime)
640 print "start fwd timer"
642 self.fwdKeyTimer.start(500)
645 print "start rewind timer"
647 self.rwdKeyTimer.start(500)
652 self.fwdKeyTimer.stop()
653 self.fwdtimer = False
655 self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
656 self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
657 self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
658 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
659 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
660 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
661 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
662 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
663 self.SEEK_STATE_BACK_4X: self.SEEK_STATE_PLAY,
664 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_4X,
665 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
666 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
667 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
668 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
669 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER
671 self.setSeekState(lookup[self.seekstate]);
673 def seekBackUp(self):
676 self.rwdKeyTimer.stop()
677 self.rwdtimer = False
680 self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_4X,
681 self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
682 self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
683 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
684 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
685 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
686 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
687 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
688 self.SEEK_STATE_BACK_4X: self.SEEK_STATE_BACK_32X,
689 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
690 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
691 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
692 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
693 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
694 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE
696 self.setSeekState(lookup[self.seekstate]);
698 def fwdTimerFire(self):
699 print "Display seek fwd"
700 self.fwdKeyTimer.stop()
701 self.fwdtimer = False
702 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
704 def fwdSeekTo(self, minutes):
705 print "Seek", minutes, "minutes forward"
707 seekable = self.getSeek()
708 if seekable is not None:
709 seekable.seekRelative(1, minutes * 60 * 90000)
711 def rwdTimerFire(self):
713 self.rwdKeyTimer.stop()
714 self.rwdtimer = False
715 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
717 def rwdSeekTo(self, minutes):
719 self.fwdSeekTo(0 - minutes)
721 def checkSkipShowHideLock(self):
722 wantlock = self.seekstate != self.SEEK_STATE_PLAY
724 if self.lockedBecauseOfSkipping and not wantlock:
726 self.lockedBecauseOfSkipping = False
728 if wantlock and not self.lockedBecauseOfSkipping:
730 self.lockedBecauseOfSkipping = True
733 self.setSeekState(self.SEEK_STATE_PAUSE)
736 self.setSeekState(self.SEEK_STATE_PLAY)
739 def seekRelative(self, diff):
740 seekable = self.getSeek()
741 if seekable is not None:
742 seekable.seekRelative(0, diff)
744 from Screens.PVRState import PVRState
746 class InfoBarPVRState:
748 self.onPlayStateChanged.append(self.__playStateChanged)
749 self.pvrStateDialog = self.session.instantiateDialog(PVRState)
750 self.onShow.append(self.__mayShow)
751 self.onHide.append(self.pvrStateDialog.hide)
754 if self.seekstate != self.SEEK_STATE_PLAY:
755 self.pvrStateDialog.show()
757 def __playStateChanged(self, state):
758 playstateString = state[3]
759 self.pvrStateDialog["state"].setText(playstateString)
762 class InfoBarShowMovies:
764 # i don't really like this class.
765 # it calls a not further specified "movie list" on up/down/movieList,
766 # so this is not more than an action map
768 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
770 "movieList": (self.showMovies, "movie list"),
771 "up": (self.showMovies, "movie list"),
772 "down": (self.showMovies, "movie list")
775 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
779 # Timeshift works the following way:
780 # demux0 demux1 "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
781 # - normal playback TUNER unused PLAY enable disable disable
782 # - user presses "yellow" button. TUNER record PAUSE enable disable enable
783 # - user presess pause again FILE record PLAY enable disable enable
784 # - user fast forwards FILE record FF enable disable enable
785 # - end of timeshift buffer reached TUNER record PLAY enable enable disable
786 # - user backwards FILE record BACK # !! enable disable enable
790 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
791 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
792 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
793 # - the user can now PVR around
794 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
795 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
797 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
798 # - if the user rewinds, or press pause, timeshift will be activated again
800 # note that a timeshift can be enabled ("recording") and
801 # activated (currently time-shifting).
803 class InfoBarTimeshift:
805 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
807 "timeshiftStart": (self.startTimeshift, "start timeshift"), # the "yellow key"
808 "timeshiftStop": (self.stopTimeshift, "stop timeshift") # currently undefined :), probably 'TV'
810 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
812 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
813 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause # something like "backward key"
814 }, prio=-1) # priority over record
816 self.timeshift_enabled = 0
817 self.timeshift_state = 0
818 self.ts_pause_timer = eTimer()
819 self.ts_pause_timer.timeout.get().append(self.pauseService)
821 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
823 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
826 def getTimeshift(self):
827 service = self.session.nav.getCurrentService()
828 return service.timeshift()
830 def startTimeshift(self):
831 print "enable timeshift"
832 ts = self.getTimeshift()
834 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
835 print "no ts interface"
838 if self.timeshift_enabled:
839 print "hu, timeshift already enabled?"
841 if not ts.startTimeshift():
842 self.timeshift_enabled = 1
845 self.setSeekState(self.SEEK_STATE_PAUSE)
847 # enable the "TimeshiftEnableActions", which will override
848 # the startTimeshift actions
849 self.__seekableStatusChanged()
851 print "timeshift failed"
853 def stopTimeshift(self):
854 if not self.timeshift_enabled:
856 print "disable timeshift"
857 ts = self.getTimeshift()
860 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
862 def stopTimeshiftConfirmed(self, confirmed):
866 ts = self.getTimeshift()
871 self.timeshift_enabled = 0
874 self.__seekableStatusChanged()
876 # activates timeshift, and seeks to (almost) the end
877 def activateTimeshiftEnd(self):
878 ts = self.getTimeshift()
883 if ts.isTimeshiftActive():
884 print "!! activate timeshift called - but shouldn't this be a normal pause?"
887 self.setSeekState(self.SEEK_STATE_PLAY)
888 ts.activateTimeshift()
891 # same as activateTimeshiftEnd, but pauses afterwards.
892 def activateTimeshiftEndAndPause(self):
893 state = self.seekstate
894 self.activateTimeshiftEnd()
896 # well, this is "andPause", but it could be pressed from pause,
897 # when pausing on the (fake-)"live" picture, so an un-pause
900 print "now, pauseService"
901 if state == self.SEEK_STATE_PLAY:
902 print "is PLAYING, start pause timer"
903 self.ts_pause_timer.start(200, 1)
906 self.unPauseService()
908 def __seekableStatusChanged(self):
911 print "self.isSeekable", self.isSeekable()
912 print "self.timeshift_enabled", self.timeshift_enabled
914 # when this service is not seekable, but timeshift
915 # is enabled, this means we can activate
917 if not self.isSeekable() and self.timeshift_enabled:
920 print "timeshift activate:", enabled
921 self["TimeshiftActivateActions"].setEnabled(enabled)
923 from RecordTimer import parseEvent
925 class InfoBarInstantRecord:
926 """Instant Record - handles the instantRecord action in order to
927 start/stop instant records"""
929 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
931 "instantRecord": (self.instantRecord, "Instant Record..."),
933 self.recording = None
934 self["BlinkingPoint"] = BlinkingPixmapConditional()
935 self.onLayoutFinish.append(self["BlinkingPoint"].hideWidget)
936 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
938 def stopCurrentRecording(self):
939 self.session.nav.RecordTimer.removeEntry(self.recording)
940 self.recording = None
942 def startInstantRecording(self):
943 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
945 # try to get event info
948 service = self.session.nav.getCurrentService()
949 info = service.info()
950 ev = info.getEvent(0)
955 if event is not None:
956 data = parseEvent(event)
958 end = begin + 3600 * 10
960 data = (begin, end, data[2], data[3], data[4])
962 data = (time.time(), time.time() + 3600 * 10, "instant record", "", None)
964 # fix me, description.
965 self.recording = self.session.nav.recordWithTimer(serviceref, *data)
966 self.recording.dontSave = True
968 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
970 def isInstantRecordRunning(self):
971 if self.recording != None:
972 if self.recording.isRunning():
976 def recordQuestionCallback(self, answer):
980 if self.isInstantRecordRunning():
981 self.stopCurrentRecording()
983 self.startInstantRecording()
985 def instantRecord(self):
987 stat = os.stat(resolveFilename(SCOPE_HDD))
989 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
992 if self.isInstantRecordRunning():
993 self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Do you want to stop the current\n(instant) recording?"))
995 self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Start recording?"))
997 from Screens.AudioSelection import AudioSelection
999 class InfoBarAudioSelection:
1001 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1003 "audioSelection": (self.audioSelection, "Audio Options..."),
1006 def audioSelection(self):
1007 service = self.session.nav.getCurrentService()
1008 audio = service.audioTracks()
1009 n = audio.getNumberOfTracks()
1011 self.session.open(AudioSelection, audio)
1013 from Screens.SubserviceSelection import SubserviceSelection
1015 class InfoBarSubserviceSelection:
1017 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1019 "subserviceSelection": (self.subserviceSelection, "Subservice list..."),
1022 def subserviceSelection(self):
1023 service = self.session.nav.getCurrentService()
1024 subservices = service.subServices()
1025 n = subservices.getNumberOfSubservices()
1027 self.session.openWithCallback(self.subserviceSelected, SubserviceSelection, subservices)
1029 def subserviceSelected(self, service):
1030 if not service is None:
1031 self.session.nav.playService(service)
1033 class InfoBarAdditionalInfo:
1035 self["DolbyActive"] = Pixmap()
1036 self["CryptActive"] = Pixmap()
1037 self["FormatActive"] = Pixmap()
1039 self["ButtonRed"] = PixmapConditional(withTimer = False)
1040 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1041 self.onLayoutFinish.append(self["ButtonRed"].update)
1042 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1043 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1044 self.onLayoutFinish.append(self["ButtonRedText"].update)
1046 self["ButtonGreen"] = Pixmap()
1047 self["ButtonGreenText"] = Label(_("Subservices"))
1049 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1050 self["ButtonYellow"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1051 self["ButtonYellowText"] = LabelConditional(text = _("Timeshifting"), withTimer = False)
1052 self["ButtonYellowText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1053 self.onLayoutFinish.append(self["ButtonYellow"].update)
1054 self.onLayoutFinish.append(self["ButtonYellowText"].update)
1056 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1057 self["ButtonBlue"].setConnect(lambda: False)
1058 self["ButtonBlueText"] = LabelConditional(text = _("Extensions"), withTimer = False)
1059 self["ButtonBlueText"].setConnect(lambda: False)
1060 self.onLayoutFinish.append(self["ButtonBlue"].update)
1061 self.onLayoutFinish.append(self["ButtonBlueText"].update)
1063 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1065 def hideSubServiceIndication(self):
1066 self["ButtonGreen"].hideWidget()
1067 self["ButtonGreenText"].hide()
1069 def showSubServiceIndication(self):
1070 self["ButtonGreen"].showWidget()
1071 self["ButtonGreenText"].show()
1073 def checkFormat(self, service):
1074 info = service.info()
1075 if info is not None:
1076 aspect = info.getInfo(iServiceInformation.sAspect)
1077 if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1078 self["FormatActive"].showWidget()
1080 self["FormatActive"].hideWidget()
1082 def checkSubservices(self, service):
1083 if service.subServices().getNumberOfSubservices() > 0:
1084 self.showSubServiceIndication()
1086 self.hideSubServiceIndication()
1088 def checkDolby(self, service):
1091 audio = service.audioTracks()
1092 if audio is not None:
1093 n = audio.getNumberOfTracks()
1095 i = audio.getTrackInfo(x)
1096 description = i.getDescription();
1097 if description.find("AC3") != -1 or description.find("DTS") != -1:
1101 self["DolbyActive"].showWidget()
1103 self["DolbyActive"].hideWidget()
1105 def checkCrypted(self, service):
1106 info = service.info()
1107 if info is not None:
1108 if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1109 self["CryptActive"].showWidget()
1111 self["CryptActive"].hideWidget()
1113 def gotServiceEvent(self, ev):
1114 service = self.session.nav.getCurrentService()
1115 if ev == iPlayableService.evUpdatedEventInfo:
1116 self.checkSubservices(service)
1117 self.checkFormat(service)
1118 elif ev == iPlayableService.evUpdatedInfo:
1119 self.checkCrypted(service)
1120 self.checkDolby(service)
1121 elif ev == iPlayableService.evEnd:
1122 self.hideSubServiceIndication()
1123 self["CryptActive"].hideWidget()
1124 self["DolbyActive"].hideWidget()
1125 self["FormatActive"].hideWidget()
1127 class InfoBarNotifications:
1129 self.onExecBegin.append(self.checkNotifications)
1130 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1132 def checkNotificationsIfExecing(self):
1134 self.checkNotifications()
1136 def checkNotifications(self):
1137 if len(Notifications.notifications):
1138 n = Notifications.notifications[0]
1139 Notifications.notifications = Notifications.notifications[1:]
1143 self.session.openWithCallback(cb, *n[1:])
1145 self.session.open(*n[1:])
1147 class InfoBarServiceNotifications:
1149 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1151 iPlayableService.evEnd: self.serviceHasEnded
1154 def serviceHasEnded(self):
1155 print "service end!"
1158 self.setSeekState(self.SEEK_STATE_PLAY)
1162 class InfoBarCueSheetSupport:
1168 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions",
1170 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1171 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1172 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1176 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1178 iPlayableService.evStart: self.__serviceStarted,
1181 def __serviceStarted(self):
1182 print "new service started! trying to download cuts!"
1183 self.downloadCuesheet()
1185 def __getSeekable(self):
1186 service = self.session.nav.getCurrentService()
1189 return service.seek()
1191 def __getCurrentPosition(self):
1192 seek = self.__getSeekable()
1195 r = seek.getPlayPosition()
1200 def jumpPreviousNextMark(self, cmp, alternative=None):
1201 current_pos = self.__getCurrentPosition()
1202 if current_pos is None:
1204 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1205 if mark is not None:
1207 elif alternative is not None:
1212 seekable = self.__getSeekable()
1213 if seekable is not None:
1214 seekable.seekTo(pts)
1216 def jumpPreviousMark(self):
1217 print "jumpPreviousMark"
1218 # we add 2 seconds, so if the play position is <2s after
1219 # the mark, the mark before will be used
1220 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1222 def jumpNextMark(self):
1223 print "jumpNextMark"
1224 self.jumpPreviousNextMark(lambda x: x)
1226 def getNearestCutPoint(self, pts, cmp=abs):
1229 for cp in self.cut_list:
1230 diff = cmp(cp[0] - pts)
1231 if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1235 def toggleMark(self):
1237 current_pos = self.__getCurrentPosition()
1238 if current_pos is None:
1239 print "not seekable"
1242 print "current position: ", current_pos
1244 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1245 print "nearest_cutpoint: ", nearest_cutpoint
1247 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < 5*90000:
1248 self.cut_list.remove(nearest_cutpoint)
1250 bisect.insort(self.cut_list, (current_pos, self.CUT_TYPE_MARK))
1252 self.uploadCuesheet()
1254 def __getCuesheet(self):
1255 service = self.session.nav.getCurrentService()
1258 return service.cueSheet()
1260 def uploadCuesheet(self):
1261 cue = self.__getCuesheet()
1264 print "upload failed, no cuesheet interface"
1266 cue.setCutList(self.cut_list)
1268 def downloadCuesheet(self):
1269 cue = self.__getCuesheet()
1272 print "upload failed, no cuesheet interface"
1274 self.cut_list = cue.getCutList()
1276 print "cuts:", self.cut_list