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):
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
361 self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService)
363 def closed(self, ret):
367 def openMultiServiceEPG(self):
368 bouquets = self.servicelist.getBouquetList()
373 if cnt > 1: # show bouquet list
374 self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG)
376 self.openBouquetEPG(bouquets[0][1])
378 def openSingleServiceEPG(self):
379 ref=self.session.nav.getCurrentlyPlayingServiceReference()
380 ptr=eEPGCache.getInstance()
381 self.session.openWithCallback(self.closed, EPGSelection, ref)
383 def openEventView(self):
385 service = self.session.nav.getCurrentService()
386 ref = self.session.nav.getCurrentlyPlayingServiceReference()
387 info = service.info()
390 self.epglist.append(ptr)
393 self.epglist.append(ptr)
394 if len(self.epglist) == 0:
395 epg = eEPGCache.getInstance()
396 ptr = epg.lookupEventTime(ref, -1)
398 self.epglist.append(ptr)
399 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
401 self.epglist.append(ptr)
402 if len(self.epglist) > 0:
403 self.session.open(EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG)
405 print "no epg for the service avail.. so we show multiepg instead of eventinfo"
406 self.openMultiServiceEPG()
408 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
409 if len(self.epglist) > 1:
410 tmp = self.epglist[0]
411 self.epglist[0]=self.epglist[1]
413 setEvent(self.epglist[0])
418 """provides a snr/agc/ber display"""
420 self["snr"] = Label()
421 self["agc"] = Label()
422 self["ber"] = Label()
423 self["snr_percent"] = Label()
424 self["agc_percent"] = Label()
425 self["ber_count"] = Label()
426 self["snr_progress"] = ProgressBar()
427 self["agc_progress"] = ProgressBar()
428 self["ber_progress"] = ProgressBar()
429 self.timer = eTimer()
430 self.timer.timeout.get().append(self.updateTunerInfo)
431 self.timer.start(1000)
437 return (long)(log(val)/log(2))
440 def updateTunerInfo(self):
441 if self.instance.isVisible():
442 service = self.session.nav.getCurrentService()
446 if service is not None:
447 feinfo = service.frontendStatusInfo()
448 if feinfo is not None:
449 ber=feinfo.getFrontendInfo(iFrontendStatusInformation.bitErrorRate)
450 snr=feinfo.getFrontendInfo(iFrontendStatusInformation.signalPower)*100/65536
451 agc=feinfo.getFrontendInfo(iFrontendStatusInformation.signalQuality)*100/65536
452 self["snr_percent"].setText("%d%%"%(snr))
453 self["agc_percent"].setText("%d%%"%(agc))
454 self["ber_count"].setText("%d"%(ber))
455 self["snr_progress"].setValue(snr)
456 self["agc_progress"].setValue(agc)
457 self["ber_progress"].setValue(self.calc(ber))
460 """provides a current/next event info display"""
462 self["Event_Now_StartTime"] = EventInfo(self.session.nav, EventInfo.Now_StartTime)
463 self["Event_Next_StartTime"] = EventInfo(self.session.nav, EventInfo.Next_StartTime)
465 self["Event_Now"] = EventInfo(self.session.nav, EventInfo.Now)
466 self["Event_Next"] = EventInfo(self.session.nav, EventInfo.Next)
468 self["Event_Now_Duration"] = EventInfo(self.session.nav, EventInfo.Now_Remaining)
469 self["Event_Next_Duration"] = EventInfo(self.session.nav, EventInfo.Next_Duration)
471 self["Now_ProgressBar"] = EventInfoProgress(self.session.nav, EventInfo.Now)
473 class InfoBarServiceName:
475 self["ServiceName"] = ServiceName(self.session.nav)
478 """handles actions like seeking, pause"""
480 # ispause, isff, issm
481 SEEK_STATE_PLAY = (0, 0, 0, ">")
482 SEEK_STATE_PAUSE = (1, 0, 0, "||")
483 SEEK_STATE_FF_2X = (0, 2, 0, ">> 2x")
484 SEEK_STATE_FF_4X = (0, 4, 0, ">> 4x")
485 SEEK_STATE_FF_8X = (0, 8, 0, ">> 8x")
486 SEEK_STATE_FF_32X = (0, 32, 0, ">> 32x")
487 SEEK_STATE_FF_64X = (0, 64, 0, ">> 64x")
488 SEEK_STATE_FF_128X = (0, 128, 0, ">> 128x")
490 SEEK_STATE_BACK_4X = (0, -4, 0, "<< 4x")
491 SEEK_STATE_BACK_32X = (0, -32, 0, "<< 32x")
492 SEEK_STATE_BACK_64X = (0, -64, 0, "<< 64x")
493 SEEK_STATE_BACK_128X = (0, -128, 0, "<< 128x")
495 SEEK_STATE_SM_HALF = (0, 0, 2, "/2")
496 SEEK_STATE_SM_QUARTER = (0, 0, 4, "/4")
497 SEEK_STATE_SM_EIGHTH = (0, 0, 8, "/8")
500 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
502 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
503 iPlayableService.evStart: self.__serviceStarted,
505 iPlayableService.evEOF: self.__evEOF,
506 iPlayableService.evSOF: self.__evSOF,
508 self["SeekActions"] = HelpableActionMap(self, "InfobarSeekActions",
510 "pauseService": (self.pauseService, "pause"),
511 "unPauseService": (self.unPauseService, "continue"),
513 "seekFwd": (self.seekFwd, "skip forward"),
514 "seekFwdUp": (self.seekFwdUp, "skip forward"),
515 "seekBack": (self.seekBack, "skip backward"),
516 "seekBackUp": (self.seekBackUp, "skip backward"),
518 # give them a little more priority to win over color buttons
520 self.seekstate = self.SEEK_STATE_PLAY
521 self.onClose.append(self.delTimer)
523 self.fwdtimer = False
524 self.fwdKeyTimer = eTimer()
525 self.fwdKeyTimer.timeout.get().append(self.fwdTimerFire)
527 self.rwdtimer = False
528 self.rwdKeyTimer = eTimer()
529 self.rwdKeyTimer.timeout.get().append(self.rwdTimerFire)
531 self.onPlayStateChanged = [ ]
533 self.lockedBecauseOfSkipping = False
546 service = self.session.nav.getCurrentService()
550 seek = service.seek()
552 if seek is None or not seek.isCurrentlySeekable():
557 def isSeekable(self):
558 if self.getSeek() is None:
562 def __seekableStatusChanged(self):
563 print "seekable status changed!"
564 if not self.isSeekable():
565 self["SeekActions"].setEnabled(False)
566 print "not seekable, return to play"
567 self.setSeekState(self.SEEK_STATE_PLAY)
569 self["SeekActions"].setEnabled(True)
572 def __serviceStarted(self):
573 self.seekstate = self.SEEK_STATE_PLAY
575 def setSeekState(self, state):
576 service = self.session.nav.getCurrentService()
581 if not self.isSeekable():
582 if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
583 state = self.SEEK_STATE_PLAY
585 pauseable = service.pause()
587 if pauseable is None:
588 print "not pauseable."
589 state = self.SEEK_STATE_PLAY
591 oldstate = self.seekstate
592 self.seekstate = state
595 if oldstate[i] != self.seekstate[i]:
596 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
598 for c in self.onPlayStateChanged:
601 self.checkSkipShowHideLock()
605 def pauseService(self):
606 if self.seekstate == self.SEEK_STATE_PAUSE:
607 print "pause, but in fact unpause"
608 self.unPauseService()
610 if self.seekstate == self.SEEK_STATE_PLAY:
611 print "yes, playing."
613 print "no", self.seekstate
615 self.setSeekState(self.SEEK_STATE_PAUSE);
617 def unPauseService(self):
619 self.setSeekState(self.SEEK_STATE_PLAY);
621 def doSeek(self, seektime):
622 print "doseek", seektime
623 service = self.session.nav.getCurrentService()
627 seekable = self.getSeek()
631 seekable.seekTo(90 * seektime)
634 print "start fwd timer"
636 self.fwdKeyTimer.start(500)
639 print "start rewind timer"
641 self.rwdKeyTimer.start(500)
646 self.fwdKeyTimer.stop()
647 self.fwdtimer = False
649 self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
650 self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
651 self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
652 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
653 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
654 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
655 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
656 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
657 self.SEEK_STATE_BACK_4X: self.SEEK_STATE_PLAY,
658 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_4X,
659 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
660 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
661 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
662 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
663 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER
665 self.setSeekState(lookup[self.seekstate]);
667 def seekBackUp(self):
670 self.rwdKeyTimer.stop()
671 self.rwdtimer = False
674 self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_4X,
675 self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
676 self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
677 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
678 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
679 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
680 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
681 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
682 self.SEEK_STATE_BACK_4X: self.SEEK_STATE_BACK_32X,
683 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
684 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
685 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
686 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
687 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
688 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE
690 self.setSeekState(lookup[self.seekstate]);
692 def fwdTimerFire(self):
693 print "Display seek fwd"
694 self.fwdKeyTimer.stop()
695 self.fwdtimer = False
696 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
698 def fwdSeekTo(self, minutes):
699 print "Seek", minutes, "minutes forward"
701 seekable = self.getSeek()
702 if seekable is not None:
703 seekable.seekRelative(1, minutes * 60 * 90000)
705 def rwdTimerFire(self):
707 self.rwdKeyTimer.stop()
708 self.rwdtimer = False
709 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
711 def rwdSeekTo(self, minutes):
713 self.fwdSeekTo(0 - minutes)
715 def checkSkipShowHideLock(self):
716 wantlock = self.seekstate != self.SEEK_STATE_PLAY
718 if self.lockedBecauseOfSkipping and not wantlock:
720 self.lockedBecauseOfSkipping = False
722 if wantlock and not self.lockedBecauseOfSkipping:
724 self.lockedBecauseOfSkipping = True
727 self.setSeekState(self.SEEK_STATE_PAUSE)
730 self.setSeekState(self.SEEK_STATE_PLAY)
733 def seekRelative(self, diff):
734 seekable = self.getSeek()
735 if seekable is not None:
736 seekable.seekRelative(0, diff)
738 from Screens.PVRState import PVRState
740 class InfoBarPVRState:
742 self.onPlayStateChanged.append(self.__playStateChanged)
743 self.pvrStateDialog = self.session.instantiateDialog(PVRState)
744 self.onShow.append(self.__mayShow)
745 self.onHide.append(self.pvrStateDialog.hide)
748 if self.seekstate != self.SEEK_STATE_PLAY:
749 self.pvrStateDialog.show()
751 def __playStateChanged(self, state):
752 playstateString = state[3]
753 self.pvrStateDialog["state"].setText(playstateString)
756 class InfoBarShowMovies:
758 # i don't really like this class.
759 # it calls a not further specified "movie list" on up/down/movieList,
760 # so this is not more than an action map
762 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
764 "movieList": (self.showMovies, "movie list"),
765 "up": (self.showMovies, "movie list"),
766 "down": (self.showMovies, "movie list")
769 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
773 # Timeshift works the following way:
774 # demux0 demux1 "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
775 # - normal playback TUNER unused PLAY enable disable disable
776 # - user presses "yellow" button. TUNER record PAUSE enable disable enable
777 # - user presess pause again FILE record PLAY enable disable enable
778 # - user fast forwards FILE record FF enable disable enable
779 # - end of timeshift buffer reached TUNER record PLAY enable enable disable
780 # - user backwards FILE record BACK # !! enable disable enable
784 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
785 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
786 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
787 # - the user can now PVR around
788 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
789 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
791 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
792 # - if the user rewinds, or press pause, timeshift will be activated again
794 # note that a timeshift can be enabled ("recording") and
795 # activated (currently time-shifting).
797 class InfoBarTimeshift:
799 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
801 "timeshiftStart": (self.startTimeshift, "start timeshift"), # the "yellow key"
802 "timeshiftStop": (self.stopTimeshift, "stop timeshift") # currently undefined :), probably 'TV'
804 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
806 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
807 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause # something like "backward key"
808 }, prio=-1) # priority over record
810 self.timeshift_enabled = 0
811 self.timeshift_state = 0
812 self.ts_pause_timer = eTimer()
813 self.ts_pause_timer.timeout.get().append(self.pauseService)
815 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
817 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
820 def getTimeshift(self):
821 service = self.session.nav.getCurrentService()
822 return service.timeshift()
824 def startTimeshift(self):
825 print "enable timeshift"
826 ts = self.getTimeshift()
828 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
829 print "no ts interface"
832 if self.timeshift_enabled:
833 print "hu, timeshift already enabled?"
835 if not ts.startTimeshift():
836 self.timeshift_enabled = 1
839 self.setSeekState(self.SEEK_STATE_PAUSE)
841 # enable the "TimeshiftEnableActions", which will override
842 # the startTimeshift actions
843 self.__seekableStatusChanged()
845 print "timeshift failed"
847 def stopTimeshift(self):
848 if not self.timeshift_enabled:
850 print "disable timeshift"
851 ts = self.getTimeshift()
854 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
856 def stopTimeshiftConfirmed(self, confirmed):
860 ts = self.getTimeshift()
865 self.timeshift_enabled = 0
868 self.__seekableStatusChanged()
870 # activates timeshift, and seeks to (almost) the end
871 def activateTimeshiftEnd(self):
872 ts = self.getTimeshift()
877 if ts.isTimeshiftActive():
878 print "!! activate timeshift called - but shouldn't this be a normal pause?"
881 self.setSeekState(self.SEEK_STATE_PLAY)
882 ts.activateTimeshift()
885 # same as activateTimeshiftEnd, but pauses afterwards.
886 def activateTimeshiftEndAndPause(self):
887 state = self.seekstate
888 self.activateTimeshiftEnd()
890 # well, this is "andPause", but it could be pressed from pause,
891 # when pausing on the (fake-)"live" picture, so an un-pause
894 print "now, pauseService"
895 if state == self.SEEK_STATE_PLAY:
896 print "is PLAYING, start pause timer"
897 self.ts_pause_timer.start(200, 1)
900 self.unPauseService()
902 def __seekableStatusChanged(self):
905 print "self.isSeekable", self.isSeekable()
906 print "self.timeshift_enabled", self.timeshift_enabled
908 # when this service is not seekable, but timeshift
909 # is enabled, this means we can activate
911 if not self.isSeekable() and self.timeshift_enabled:
914 print "timeshift activate:", enabled
915 self["TimeshiftActivateActions"].setEnabled(enabled)
917 from RecordTimer import parseEvent
919 class InfoBarInstantRecord:
920 """Instant Record - handles the instantRecord action in order to
921 start/stop instant records"""
923 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
925 "instantRecord": (self.instantRecord, "Instant Record..."),
927 self.recording = None
928 self["BlinkingPoint"] = BlinkingPixmapConditional()
929 self.onLayoutFinish.append(self["BlinkingPoint"].hideWidget)
930 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
932 def stopCurrentRecording(self):
933 self.session.nav.RecordTimer.removeEntry(self.recording)
934 self.recording = None
936 def startInstantRecording(self):
937 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
939 # try to get event info
942 service = self.session.nav.getCurrentService()
943 info = service.info()
944 ev = info.getEvent(0)
949 if event is not None:
950 data = parseEvent(event)
952 end = begin + 3600 * 10
954 data = (begin, end, data[2], data[3], data[4])
956 data = (time.time(), time.time() + 3600 * 10, "instant record", "", None)
958 # fix me, description.
959 self.recording = self.session.nav.recordWithTimer(serviceref, *data)
960 self.recording.dontSave = True
962 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
964 def isInstantRecordRunning(self):
965 if self.recording != None:
966 if self.recording.isRunning():
970 def recordQuestionCallback(self, answer):
974 if self.isInstantRecordRunning():
975 self.stopCurrentRecording()
977 self.startInstantRecording()
979 def instantRecord(self):
981 stat = os.stat(resolveFilename(SCOPE_HDD))
983 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
986 if self.isInstantRecordRunning():
987 self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Do you want to stop the current\n(instant) recording?"))
989 self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Start recording?"))
991 from Screens.AudioSelection import AudioSelection
993 class InfoBarAudioSelection:
995 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
997 "audioSelection": (self.audioSelection, "Audio Options..."),
1000 def audioSelection(self):
1001 service = self.session.nav.getCurrentService()
1002 audio = service.audioTracks()
1003 n = audio.getNumberOfTracks()
1005 self.session.open(AudioSelection, audio)
1007 from Screens.SubserviceSelection import SubserviceSelection
1009 class InfoBarSubserviceSelection:
1011 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1013 "subserviceSelection": (self.subserviceSelection, "Subservice list..."),
1016 def subserviceSelection(self):
1017 service = self.session.nav.getCurrentService()
1018 subservices = service.subServices()
1019 n = subservices.getNumberOfSubservices()
1021 self.session.openWithCallback(self.subserviceSelected, SubserviceSelection, subservices)
1023 def subserviceSelected(self, service):
1024 if not service is None:
1025 self.session.nav.playService(service)
1027 class InfoBarAdditionalInfo:
1029 self["DolbyActive"] = Pixmap()
1030 self["CryptActive"] = Pixmap()
1031 self["FormatActive"] = Pixmap()
1033 self["ButtonRed"] = PixmapConditional(withTimer = False)
1034 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1035 self.onShown.append(self["ButtonRed"].update)
1036 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1037 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1038 self.onShown.append(self["ButtonRedText"].update)
1040 self["ButtonGreen"] = Pixmap()
1041 self["ButtonGreenText"] = Label(_("Subservices"))
1043 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1044 self["ButtonYellow"].setConnect(lambda: False)
1046 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1047 self["ButtonBlue"].setConnect(lambda: False)
1049 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1051 def hideSubServiceIndication(self):
1052 self["ButtonGreen"].hideWidget()
1053 self["ButtonGreenText"].hide()
1055 def showSubServiceIndication(self):
1056 self["ButtonGreen"].showWidget()
1057 self["ButtonGreenText"].show()
1059 def checkFormat(self, service):
1060 info = service.info()
1061 if info is not None:
1062 aspect = info.getInfo(iServiceInformation.sAspect)
1063 if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1064 self["FormatActive"].showWidget()
1066 self["FormatActive"].hideWidget()
1068 def checkSubservices(self, service):
1069 if service.subServices().getNumberOfSubservices() > 0:
1070 self.showSubServiceIndication()
1072 self.hideSubServiceIndication()
1074 def checkDolby(self, service):
1077 audio = service.audioTracks()
1078 if audio is not None:
1079 n = audio.getNumberOfTracks()
1081 i = audio.getTrackInfo(x)
1082 description = i.getDescription();
1083 if description.find("AC3") != -1 or description.find("DTS") != -1:
1087 self["DolbyActive"].showWidget()
1089 self["DolbyActive"].hideWidget()
1091 def checkCrypted(self, service):
1092 info = service.info()
1093 if info is not None:
1094 if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1095 self["CryptActive"].showWidget()
1097 self["CryptActive"].hideWidget()
1099 def gotServiceEvent(self, ev):
1100 service = self.session.nav.getCurrentService()
1101 if ev == iPlayableService.evUpdatedEventInfo:
1102 self.checkSubservices(service)
1103 self.checkFormat(service)
1104 elif ev == iPlayableService.evUpdatedInfo:
1105 self.checkCrypted(service)
1106 self.checkDolby(service)
1107 elif ev == iPlayableService.evEnd:
1108 self.hideSubServiceIndication()
1109 self["CryptActive"].hideWidget()
1110 self["DolbyActive"].hideWidget()
1111 self["FormatActive"].hideWidget()
1113 class InfoBarNotifications:
1115 self.onExecBegin.append(self.checkNotifications)
1116 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1118 def checkNotificationsIfExecing(self):
1120 self.checkNotifications()
1122 def checkNotifications(self):
1123 if len(Notifications.notifications):
1124 n = Notifications.notifications[0]
1125 Notifications.notifications = Notifications.notifications[1:]
1129 self.session.openWithCallback(cb, *n[1:])
1131 self.session.open(*n[1:])
1133 class InfoBarServiceNotifications:
1135 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1137 iPlayableService.evEnd: self.serviceHasEnded
1140 def serviceHasEnded(self):
1141 print "service end!"
1144 self.setSeekState(self.SEEK_STATE_PLAY)
1148 class InfoBarCueSheetSupport:
1154 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions",
1156 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1157 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1158 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1162 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1164 iPlayableService.evStart: self.__serviceStarted,
1167 def __serviceStarted(self):
1168 print "new service started! trying to download cuts!"
1169 self.downloadCuesheet()
1171 def __getSeekable(self):
1172 service = self.session.nav.getCurrentService()
1175 return service.seek()
1177 def __getCurrentPosition(self):
1178 seek = self.__getSeekable()
1181 r = seek.getPlayPosition()
1186 def jumpPreviousNextMark(self, cmp, alternative=None):
1187 current_pos = self.__getCurrentPosition()
1188 if current_pos is None:
1190 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1191 if mark is not None:
1193 elif alternative is not None:
1198 seekable = self.__getSeekable()
1199 if seekable is not None:
1200 seekable.seekTo(pts)
1202 def jumpPreviousMark(self):
1203 print "jumpPreviousMark"
1204 # we add 2 seconds, so if the play position is <2s after
1205 # the mark, the mark before will be used
1206 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1208 def jumpNextMark(self):
1209 print "jumpNextMark"
1210 self.jumpPreviousNextMark(lambda x: x)
1212 def getNearestCutPoint(self, pts, cmp=abs):
1215 for cp in self.cut_list:
1216 diff = cmp(cp[0] - pts)
1217 if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1221 def toggleMark(self):
1223 current_pos = self.__getCurrentPosition()
1224 if current_pos is None:
1225 print "not seekable"
1228 print "current position: ", current_pos
1230 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1231 print "nearest_cutpoint: ", nearest_cutpoint
1233 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < 5*90000:
1234 self.cut_list.remove(nearest_cutpoint)
1236 bisect.insort(self.cut_list, (current_pos, self.CUT_TYPE_MARK))
1238 self.uploadCuesheet()
1240 def __getCuesheet(self):
1241 service = self.session.nav.getCurrentService()
1244 return service.cueSheet()
1246 def uploadCuesheet(self):
1247 cue = self.__getCuesheet()
1250 print "upload failed, no cuesheet interface"
1252 cue.setCutList(self.cut_list)
1254 def downloadCuesheet(self):
1255 cue = self.__getCuesheet()
1258 print "upload failed, no cuesheet interface"
1260 self.cut_list = cue.getCutList()
1262 print "cuts:", self.cut_list