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["ButtonBlueText"].update)
1062 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1064 def hideSubServiceIndication(self):
1065 self["ButtonGreen"].hideWidget()
1066 self["ButtonGreenText"].hide()
1068 def showSubServiceIndication(self):
1069 self["ButtonGreen"].showWidget()
1070 self["ButtonGreenText"].show()
1072 def checkFormat(self, service):
1073 info = service.info()
1074 if info is not None:
1075 aspect = info.getInfo(iServiceInformation.sAspect)
1076 if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1077 self["FormatActive"].showWidget()
1079 self["FormatActive"].hideWidget()
1081 def checkSubservices(self, service):
1082 if service.subServices().getNumberOfSubservices() > 0:
1083 self.showSubServiceIndication()
1085 self.hideSubServiceIndication()
1087 def checkDolby(self, service):
1090 audio = service.audioTracks()
1091 if audio is not None:
1092 n = audio.getNumberOfTracks()
1094 i = audio.getTrackInfo(x)
1095 description = i.getDescription();
1096 if description.find("AC3") != -1 or description.find("DTS") != -1:
1100 self["DolbyActive"].showWidget()
1102 self["DolbyActive"].hideWidget()
1104 def checkCrypted(self, service):
1105 info = service.info()
1106 if info is not None:
1107 if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1108 self["CryptActive"].showWidget()
1110 self["CryptActive"].hideWidget()
1112 def gotServiceEvent(self, ev):
1113 service = self.session.nav.getCurrentService()
1114 if ev == iPlayableService.evUpdatedEventInfo:
1115 self.checkSubservices(service)
1116 self.checkFormat(service)
1117 elif ev == iPlayableService.evUpdatedInfo:
1118 self.checkCrypted(service)
1119 self.checkDolby(service)
1120 elif ev == iPlayableService.evEnd:
1121 self.hideSubServiceIndication()
1122 self["CryptActive"].hideWidget()
1123 self["DolbyActive"].hideWidget()
1124 self["FormatActive"].hideWidget()
1126 class InfoBarNotifications:
1128 self.onExecBegin.append(self.checkNotifications)
1129 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1131 def checkNotificationsIfExecing(self):
1133 self.checkNotifications()
1135 def checkNotifications(self):
1136 if len(Notifications.notifications):
1137 n = Notifications.notifications[0]
1138 Notifications.notifications = Notifications.notifications[1:]
1142 self.session.openWithCallback(cb, *n[1:])
1144 self.session.open(*n[1:])
1146 class InfoBarServiceNotifications:
1148 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1150 iPlayableService.evEnd: self.serviceHasEnded
1153 def serviceHasEnded(self):
1154 print "service end!"
1157 self.setSeekState(self.SEEK_STATE_PLAY)
1161 class InfoBarCueSheetSupport:
1167 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions",
1169 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1170 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1171 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1175 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1177 iPlayableService.evStart: self.__serviceStarted,
1180 def __serviceStarted(self):
1181 print "new service started! trying to download cuts!"
1182 self.downloadCuesheet()
1184 def __getSeekable(self):
1185 service = self.session.nav.getCurrentService()
1188 return service.seek()
1190 def __getCurrentPosition(self):
1191 seek = self.__getSeekable()
1194 r = seek.getPlayPosition()
1199 def jumpPreviousNextMark(self, cmp, alternative=None):
1200 current_pos = self.__getCurrentPosition()
1201 if current_pos is None:
1203 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1204 if mark is not None:
1206 elif alternative is not None:
1211 seekable = self.__getSeekable()
1212 if seekable is not None:
1213 seekable.seekTo(pts)
1215 def jumpPreviousMark(self):
1216 print "jumpPreviousMark"
1217 # we add 2 seconds, so if the play position is <2s after
1218 # the mark, the mark before will be used
1219 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1221 def jumpNextMark(self):
1222 print "jumpNextMark"
1223 self.jumpPreviousNextMark(lambda x: x)
1225 def getNearestCutPoint(self, pts, cmp=abs):
1228 for cp in self.cut_list:
1229 diff = cmp(cp[0] - pts)
1230 if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1234 def toggleMark(self):
1236 current_pos = self.__getCurrentPosition()
1237 if current_pos is None:
1238 print "not seekable"
1241 print "current position: ", current_pos
1243 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1244 print "nearest_cutpoint: ", nearest_cutpoint
1246 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < 5*90000:
1247 self.cut_list.remove(nearest_cutpoint)
1249 bisect.insort(self.cut_list, (current_pos, self.CUT_TYPE_MARK))
1251 self.uploadCuesheet()
1253 def __getCuesheet(self):
1254 service = self.session.nav.getCurrentService()
1257 return service.cueSheet()
1259 def uploadCuesheet(self):
1260 cue = self.__getCuesheet()
1263 print "upload failed, no cuesheet interface"
1265 cue.setCutList(self.cut_list)
1267 def downloadCuesheet(self):
1268 cue = self.__getCuesheet()
1271 print "upload failed, no cuesheet interface"
1273 self.cut_list = cue.getCutList()
1275 print "cuts:", self.cut_list