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.onShown.append(self["ButtonRed"].update)
1042 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1043 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1044 self.onShown.append(self["ButtonRedText"].update)
1046 self["ButtonGreen"] = Pixmap()
1047 self["ButtonGreenText"] = Label(_("Subservices"))
1049 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1050 self["ButtonYellow"].setConnect(lambda: False)
1052 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1053 self["ButtonBlue"].setConnect(lambda: False)
1055 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1057 def hideSubServiceIndication(self):
1058 self["ButtonGreen"].hideWidget()
1059 self["ButtonGreenText"].hide()
1061 def showSubServiceIndication(self):
1062 self["ButtonGreen"].showWidget()
1063 self["ButtonGreenText"].show()
1065 def checkFormat(self, service):
1066 info = service.info()
1067 if info is not None:
1068 aspect = info.getInfo(iServiceInformation.sAspect)
1069 if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1070 self["FormatActive"].showWidget()
1072 self["FormatActive"].hideWidget()
1074 def checkSubservices(self, service):
1075 if service.subServices().getNumberOfSubservices() > 0:
1076 self.showSubServiceIndication()
1078 self.hideSubServiceIndication()
1080 def checkDolby(self, service):
1083 audio = service.audioTracks()
1084 if audio is not None:
1085 n = audio.getNumberOfTracks()
1087 i = audio.getTrackInfo(x)
1088 description = i.getDescription();
1089 if description.find("AC3") != -1 or description.find("DTS") != -1:
1093 self["DolbyActive"].showWidget()
1095 self["DolbyActive"].hideWidget()
1097 def checkCrypted(self, service):
1098 info = service.info()
1099 if info is not None:
1100 if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1101 self["CryptActive"].showWidget()
1103 self["CryptActive"].hideWidget()
1105 def gotServiceEvent(self, ev):
1106 service = self.session.nav.getCurrentService()
1107 if ev == iPlayableService.evUpdatedEventInfo:
1108 self.checkSubservices(service)
1109 self.checkFormat(service)
1110 elif ev == iPlayableService.evUpdatedInfo:
1111 self.checkCrypted(service)
1112 self.checkDolby(service)
1113 elif ev == iPlayableService.evEnd:
1114 self.hideSubServiceIndication()
1115 self["CryptActive"].hideWidget()
1116 self["DolbyActive"].hideWidget()
1117 self["FormatActive"].hideWidget()
1119 class InfoBarNotifications:
1121 self.onExecBegin.append(self.checkNotifications)
1122 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1124 def checkNotificationsIfExecing(self):
1126 self.checkNotifications()
1128 def checkNotifications(self):
1129 if len(Notifications.notifications):
1130 n = Notifications.notifications[0]
1131 Notifications.notifications = Notifications.notifications[1:]
1135 self.session.openWithCallback(cb, *n[1:])
1137 self.session.open(*n[1:])
1139 class InfoBarServiceNotifications:
1141 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1143 iPlayableService.evEnd: self.serviceHasEnded
1146 def serviceHasEnded(self):
1147 print "service end!"
1150 self.setSeekState(self.SEEK_STATE_PLAY)
1154 class InfoBarCueSheetSupport:
1160 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions",
1162 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1163 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1164 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1168 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1170 iPlayableService.evStart: self.__serviceStarted,
1173 def __serviceStarted(self):
1174 print "new service started! trying to download cuts!"
1175 self.downloadCuesheet()
1177 def __getSeekable(self):
1178 service = self.session.nav.getCurrentService()
1181 return service.seek()
1183 def __getCurrentPosition(self):
1184 seek = self.__getSeekable()
1187 r = seek.getPlayPosition()
1192 def jumpPreviousNextMark(self, cmp, alternative=None):
1193 current_pos = self.__getCurrentPosition()
1194 if current_pos is None:
1196 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1197 if mark is not None:
1199 elif alternative is not None:
1204 seekable = self.__getSeekable()
1205 if seekable is not None:
1206 seekable.seekTo(pts)
1208 def jumpPreviousMark(self):
1209 print "jumpPreviousMark"
1210 # we add 2 seconds, so if the play position is <2s after
1211 # the mark, the mark before will be used
1212 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1214 def jumpNextMark(self):
1215 print "jumpNextMark"
1216 self.jumpPreviousNextMark(lambda x: x)
1218 def getNearestCutPoint(self, pts, cmp=abs):
1221 for cp in self.cut_list:
1222 diff = cmp(cp[0] - pts)
1223 if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1227 def toggleMark(self):
1229 current_pos = self.__getCurrentPosition()
1230 if current_pos is None:
1231 print "not seekable"
1234 print "current position: ", current_pos
1236 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1237 print "nearest_cutpoint: ", nearest_cutpoint
1239 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < 5*90000:
1240 self.cut_list.remove(nearest_cutpoint)
1242 bisect.insort(self.cut_list, (current_pos, self.CUT_TYPE_MARK))
1244 self.uploadCuesheet()
1246 def __getCuesheet(self):
1247 service = self.session.nav.getCurrentService()
1250 return service.cueSheet()
1252 def uploadCuesheet(self):
1253 cue = self.__getCuesheet()
1256 print "upload failed, no cuesheet interface"
1258 cue.setCutList(self.cut_list)
1260 def downloadCuesheet(self):
1261 cue = self.__getCuesheet()
1264 print "upload failed, no cuesheet interface"
1266 self.cut_list = cue.getCutList()
1268 print "cuts:", self.cut_list