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, EventViewSimple
22 from Screens.MinuteInput import MinuteInput
23 from Components.Harddisk import harddiskmanager
25 from Components.ServiceEventTracker import ServiceEventTracker
27 from Tools import Notifications
28 from Tools.Directories import *
30 #from enigma import eTimer, eDVBVolumecontrol, quitMainloop
37 from Components.config import config, currentConfigSelectionElement
40 from Menu import MainMenu, mdom
44 self.dishDialog = self.session.instantiateDialog(Dish)
45 self.onLayoutFinish.append(self.dishDialog.show)
47 class InfoBarShowHide:
48 """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
56 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
58 "toggleShow": self.toggleShow,
62 self.__state = self.STATE_SHOWN
65 self.onExecBegin.append(self.show)
67 self.hideTimer = eTimer()
68 self.hideTimer.timeout.get().append(self.doTimerHide)
69 self.hideTimer.start(5000, True)
71 self.onShow.append(self.__onShow)
72 self.onHide.append(self.__onHide)
75 self.__state = self.STATE_SHOWN
78 def startHideTimer(self):
79 if self.__state == self.STATE_SHOWN and not self.__locked:
80 self.hideTimer.start(5000, True)
83 self.__state = self.STATE_HIDDEN
89 def doTimerHide(self):
91 if self.__state == self.STATE_SHOWN:
95 if self.__state == self.STATE_SHOWN:
98 elif self.__state == self.STATE_HIDDEN:
102 self.__locked = self.__locked + 1
105 self.hideTimer.stop()
107 def unlockShow(self):
108 self.__locked = self.__locked - 1
110 self.startHideTimer()
112 # def startShow(self):
113 # self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
114 # self.__state = self.STATE_SHOWN
116 # def startHide(self):
117 # self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
118 # self.__state = self.STATE_HIDDEN
120 class NumberZap(Screen):
127 self.close(int(self["number"].getText()))
129 def keyNumberGlobal(self, number):
130 self.Timer.start(3000, True) #reset timer
131 self.field = self.field + str(number)
132 self["number"].setText(self.field)
133 if len(self.field) >= 4:
136 def __init__(self, session, number):
137 Screen.__init__(self, session)
138 self.field = str(number)
140 self["channel"] = Label(_("Channel:"))
142 self["number"] = Label(self.field)
144 self["actions"] = NumberActionMap( [ "SetupActions" ],
148 "1": self.keyNumberGlobal,
149 "2": self.keyNumberGlobal,
150 "3": self.keyNumberGlobal,
151 "4": self.keyNumberGlobal,
152 "5": self.keyNumberGlobal,
153 "6": self.keyNumberGlobal,
154 "7": self.keyNumberGlobal,
155 "8": self.keyNumberGlobal,
156 "9": self.keyNumberGlobal,
157 "0": self.keyNumberGlobal
160 self.Timer = eTimer()
161 self.Timer.timeout.get().append(self.keyOK)
162 self.Timer.start(3000, True)
164 class InfoBarPowerKey:
165 """ PowerKey stuff - handles the powerkey press and powerkey release actions"""
168 self.powerKeyTimer = eTimer()
169 self.powerKeyTimer.timeout.get().append(self.powertimer)
170 self["PowerKeyActions"] = HelpableActionMap(self, "PowerKeyActions",
172 "powerdown": self.powerdown,
173 "powerup": self.powerup,
174 "discreteStandby": (self.standby, "Go standby"),
175 "discretePowerOff": (self.quit, "Go to deep standby"),
178 def powertimer(self):
179 print "PowerOff - Now!"
183 self.standbyblocked = 0
184 self.powerKeyTimer.start(3000, True)
187 self.powerKeyTimer.stop()
188 if self.standbyblocked == 0:
189 self.standbyblocked = 1
193 self.session.open(Standby, self)
199 class InfoBarNumberZap:
200 """ Handles an initial number for NumberZapping """
202 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
204 "1": self.keyNumberGlobal,
205 "2": self.keyNumberGlobal,
206 "3": self.keyNumberGlobal,
207 "4": self.keyNumberGlobal,
208 "5": self.keyNumberGlobal,
209 "6": self.keyNumberGlobal,
210 "7": self.keyNumberGlobal,
211 "8": self.keyNumberGlobal,
212 "9": self.keyNumberGlobal,
213 "0": self.keyNumberGlobal,
216 def keyNumberGlobal(self, number):
217 # print "You pressed number " + str(number)
219 self.servicelist.recallPrevService()
222 self.session.openWithCallback(self.numberEntered, NumberZap, number)
224 def numberEntered(self, retval):
225 # print self.servicelist
227 self.zapToNumber(retval)
229 def searchNumberHelper(self, serviceHandler, num, bouquet):
230 servicelist = serviceHandler.list(bouquet)
231 if not servicelist is None:
233 serviceIterator = servicelist.getNext()
234 if not serviceIterator.valid(): #check end of list
236 if serviceIterator.flags: #assume normal dvb service have no flags set
239 if not num: #found service with searched number ?
240 return serviceIterator, 0
243 def zapToNumber(self, number):
244 bouquet = self.servicelist.bouquet_root
246 serviceHandler = eServiceCenter.getInstance()
247 if bouquet.toString().find('FROM BOUQUET "bouquets.') == -1: #FIXME HACK
248 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
250 bouquetlist = serviceHandler.list(bouquet)
251 if not bouquetlist is None:
253 bouquet = self.servicelist.appendDVBTypes(bouquetlist.getNext())
254 if not bouquet.valid(): #check end of list
256 if (bouquet.flags & eServiceReference.flagDirectory) != eServiceReference.flagDirectory:
258 service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
259 if not service is None:
260 if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
261 self.servicelist.clearPath()
262 if self.servicelist.bouquet_root != bouquet:
263 self.servicelist.enterPath(self.servicelist.bouquet_root)
264 self.servicelist.enterPath(bouquet)
265 self.servicelist.setCurrentSelection(service) #select the service in servicelist
266 self.servicelist.zap()
268 class InfoBarChannelSelection:
269 """ ChannelSelection - handles the channelSelection dialog and the initial
270 channelChange actions which open the channelSelection dialog """
273 self.servicelist = self.session.instantiateDialog(ChannelSelection)
275 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
277 "switchChannelUp": self.switchChannelUp,
278 "switchChannelDown": self.switchChannelDown,
279 "zapUp": (self.zapUp, _("previous channel")),
280 "zapDown": (self.zapDown, _("next channel")),
281 "historyBack": (self.historyBack, _("previous channel in history")),
282 "historyNext": (self.historyNext, _("next channel in history"))
285 def historyBack(self):
286 self.servicelist.historyBack()
288 def historyNext(self):
289 self.servicelist.historyNext()
291 def switchChannelUp(self):
292 self.servicelist.moveUp()
293 self.session.execDialog(self.servicelist)
295 def switchChannelDown(self):
296 self.servicelist.moveDown()
297 self.session.execDialog(self.servicelist)
300 if currentConfigSelectionElement(config.usage.quickzap_bouquet_change) == "yes":
301 if self.servicelist.inBouquet() and self.servicelist.atBegin():
302 self.servicelist.prevBouquet()
303 self.servicelist.moveUp()
304 self.servicelist.zap()
308 if currentConfigSelectionElement(config.usage.quickzap_bouquet_change) == "yes" and self.servicelist.inBouquet() and self.servicelist.atEnd():
309 self.servicelist.nextBouquet()
311 self.servicelist.moveDown()
312 self.servicelist.zap()
316 """ Handles a menu action, to open the (main) menu """
318 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions",
320 "mainMenu": (self.mainMenu, "Enter main menu..."),
324 print "loading mainmenu XML..."
325 menu = mdom.childNodes[0]
326 assert menu.tagName == "menu", "root element in menu must be 'menu'!"
327 self.session.open(MainMenu, menu, menu.childNodes)
329 class InfoBarSimpleEventView:
330 """ Opens the Eventview for now/next """
332 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
334 "showEventInfo": (self.openEventView, _("show event details")),
337 def openEventView(self):
339 service = self.session.nav.getCurrentService()
340 ref = self.session.nav.getCurrentlyPlayingServiceReference()
341 info = service.info()
344 self.epglist.append(ptr)
347 self.epglist.append(ptr)
348 if len(self.epglist) > 0:
349 self.session.open(EventViewSimple, self.epglist[0], ServiceReference(ref), self.eventViewCallback)
351 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
352 if len(self.epglist) > 1:
353 tmp = self.epglist[0]
354 self.epglist[0]=self.epglist[1]
356 setEvent(self.epglist[0])
359 """ EPG - Opens an EPG list when the showEPGList action fires """
361 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
363 "showEventInfo": (self.openEventView, _("show EPG...")),
366 def zapToService(self, service):
367 if not service is None:
368 if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
369 self.servicelist.clearPath()
370 if self.servicelist.bouquet_root != self.epg_bouquet:
371 self.servicelist.enterPath(self.servicelist.bouquet_root)
372 self.servicelist.enterPath(self.epg_bouquet)
373 self.servicelist.setCurrentSelection(service) #select the service in servicelist
374 self.servicelist.zap()
376 def openBouquetEPG(self, bouquet, withCallback=True):
377 ptr=eEPGCache.getInstance()
379 servicelist = eServiceCenter.getInstance().list(bouquet)
380 if not servicelist is None:
382 service = servicelist.getNext()
383 if not service.valid(): #check if end of list
385 if service.flags: #ignore non playable services
387 services.append(ServiceReference(service))
389 self.epg_bouquet = bouquet
391 self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService)
393 self.session.open(EPGSelection, services, self.zapToService)
395 def closed(self, ret):
399 def openMultiServiceEPG(self, withCallback=True):
400 bouquets = self.servicelist.getBouquetList()
405 if cnt > 1: # show bouquet list
407 self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG)
409 self.session.open(BouquetSelector, bouquets, self.openBouquetEPG)
411 self.openBouquetEPG(bouquets[0][1], withCallback)
413 def openSingleServiceEPG(self):
414 ref=self.session.nav.getCurrentlyPlayingServiceReference()
415 ptr=eEPGCache.getInstance()
416 self.session.openWithCallback(self.closed, EPGSelection, ref)
418 def openEventView(self):
420 service = self.session.nav.getCurrentService()
421 ref = self.session.nav.getCurrentlyPlayingServiceReference()
422 info = service.info()
425 self.epglist.append(ptr)
428 self.epglist.append(ptr)
429 if len(self.epglist) == 0:
430 epg = eEPGCache.getInstance()
431 ptr = epg.lookupEventTime(ref, -1)
433 self.epglist.append(ptr)
434 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
436 self.epglist.append(ptr)
437 if len(self.epglist) > 0:
438 self.session.open(EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG)
440 print "no epg for the service avail.. so we show multiepg instead of eventinfo"
441 self.openMultiServiceEPG(False)
443 def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
444 if len(self.epglist) > 1:
445 tmp = self.epglist[0]
446 self.epglist[0]=self.epglist[1]
448 setEvent(self.epglist[0])
453 """provides a snr/agc/ber display"""
455 self["snr"] = Label()
456 self["agc"] = Label()
457 self["ber"] = Label()
458 self["snr_percent"] = Label()
459 self["agc_percent"] = Label()
460 self["ber_count"] = Label()
461 self["snr_progress"] = ProgressBar()
462 self["agc_progress"] = ProgressBar()
463 self["ber_progress"] = ProgressBar()
464 self.timer = eTimer()
465 self.timer.timeout.get().append(self.updateTunerInfo)
466 self.timer.start(1000)
472 return (long)(log(val)/log(2))
475 def updateTunerInfo(self):
476 if self.instance.isVisible():
477 service = self.session.nav.getCurrentService()
481 if service is not None:
482 feinfo = service.frontendStatusInfo()
483 if feinfo is not None:
484 ber=feinfo.getFrontendInfo(iFrontendStatusInformation.bitErrorRate)
485 snr=feinfo.getFrontendInfo(iFrontendStatusInformation.signalPower)*100/65536
486 agc=feinfo.getFrontendInfo(iFrontendStatusInformation.signalQuality)*100/65536
487 self["snr_percent"].setText("%d%%"%(snr))
488 self["agc_percent"].setText("%d%%"%(agc))
489 self["ber_count"].setText("%d"%(ber))
490 self["snr_progress"].setValue(snr)
491 self["agc_progress"].setValue(agc)
492 self["ber_progress"].setValue(self.calc(ber))
495 """provides a current/next event info display"""
497 self["Event_Now_StartTime"] = EventInfo(self.session.nav, EventInfo.Now_StartTime)
498 self["Event_Next_StartTime"] = EventInfo(self.session.nav, EventInfo.Next_StartTime)
500 self["Event_Now"] = EventInfo(self.session.nav, EventInfo.Now)
501 self["Event_Next"] = EventInfo(self.session.nav, EventInfo.Next)
503 self["Event_Now_Duration"] = EventInfo(self.session.nav, EventInfo.Now_Remaining)
504 self["Event_Next_Duration"] = EventInfo(self.session.nav, EventInfo.Next_Duration)
506 self["Now_ProgressBar"] = EventInfoProgress(self.session.nav, EventInfo.Now)
508 class InfoBarServiceName:
510 self["ServiceName"] = ServiceName(self.session.nav)
513 """handles actions like seeking, pause"""
515 # ispause, isff, issm
516 SEEK_STATE_PLAY = (0, 0, 0, ">")
517 SEEK_STATE_PAUSE = (1, 0, 0, "||")
518 SEEK_STATE_FF_2X = (0, 2, 0, ">> 2x")
519 SEEK_STATE_FF_4X = (0, 4, 0, ">> 4x")
520 SEEK_STATE_FF_8X = (0, 8, 0, ">> 8x")
521 SEEK_STATE_FF_32X = (0, 32, 0, ">> 32x")
522 SEEK_STATE_FF_64X = (0, 64, 0, ">> 64x")
523 SEEK_STATE_FF_128X = (0, 128, 0, ">> 128x")
525 SEEK_STATE_BACK_16X = (0, -16, 0, "<< 16x")
526 SEEK_STATE_BACK_32X = (0, -32, 0, "<< 32x")
527 SEEK_STATE_BACK_64X = (0, -64, 0, "<< 64x")
528 SEEK_STATE_BACK_128X = (0, -128, 0, "<< 128x")
530 SEEK_STATE_SM_HALF = (0, 0, 2, "/2")
531 SEEK_STATE_SM_QUARTER = (0, 0, 4, "/4")
532 SEEK_STATE_SM_EIGHTH = (0, 0, 8, "/8")
535 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
537 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
538 iPlayableService.evStart: self.__serviceStarted,
540 iPlayableService.evEOF: self.__evEOF,
541 iPlayableService.evSOF: self.__evSOF,
543 self["SeekActions"] = HelpableActionMap(self, "InfobarSeekActions",
545 "pauseService": (self.pauseService, "pause"),
546 "unPauseService": (self.unPauseService, "continue"),
548 "seekFwd": (self.seekFwd, "skip forward"),
549 "seekFwdDown": self.seekFwdDown,
550 "seekFwdUp": self.seekFwdUp,
551 "seekBack": (self.seekBack, "skip backward"),
552 "seekBackDown": self.seekBackDown,
553 "seekBackUp": self.seekBackUp,
555 # give them a little more priority to win over color buttons
557 self.seekstate = self.SEEK_STATE_PLAY
558 self.onClose.append(self.delTimer)
560 self.fwdtimer = False
561 self.fwdKeyTimer = eTimer()
562 self.fwdKeyTimer.timeout.get().append(self.fwdTimerFire)
564 self.rwdtimer = False
565 self.rwdKeyTimer = eTimer()
566 self.rwdKeyTimer.timeout.get().append(self.rwdTimerFire)
568 self.onPlayStateChanged = [ ]
570 self.lockedBecauseOfSkipping = False
583 service = self.session.nav.getCurrentService()
587 seek = service.seek()
589 if seek is None or not seek.isCurrentlySeekable():
594 def isSeekable(self):
595 if self.getSeek() is None:
599 def __seekableStatusChanged(self):
600 print "seekable status changed!"
601 if not self.isSeekable():
602 self["SeekActions"].setEnabled(False)
603 print "not seekable, return to play"
604 self.setSeekState(self.SEEK_STATE_PLAY)
606 self["SeekActions"].setEnabled(True)
609 def __serviceStarted(self):
610 self.seekstate = self.SEEK_STATE_PLAY
612 def setSeekState(self, state):
613 service = self.session.nav.getCurrentService()
618 if not self.isSeekable():
619 if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
620 state = self.SEEK_STATE_PLAY
622 pauseable = service.pause()
624 if pauseable is None:
625 print "not pauseable."
626 state = self.SEEK_STATE_PLAY
628 oldstate = self.seekstate
629 self.seekstate = state
632 if oldstate[i] != self.seekstate[i]:
633 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
635 for c in self.onPlayStateChanged:
638 self.checkSkipShowHideLock()
642 def pauseService(self):
643 if self.seekstate == self.SEEK_STATE_PAUSE:
644 print "pause, but in fact unpause"
645 self.unPauseService()
647 if self.seekstate == self.SEEK_STATE_PLAY:
648 print "yes, playing."
650 print "no", self.seekstate
652 self.setSeekState(self.SEEK_STATE_PAUSE);
654 def unPauseService(self):
656 self.setSeekState(self.SEEK_STATE_PLAY);
658 def doSeek(self, seektime):
659 print "doseek", seektime
660 service = self.session.nav.getCurrentService()
664 seekable = self.getSeek()
668 seekable.seekTo(90 * seektime)
670 def seekFwdDown(self):
671 print "start fwd timer"
673 self.fwdKeyTimer.start(1000)
675 def seekBackDown(self):
676 print "start rewind timer"
678 self.rwdKeyTimer.start(1000)
683 self.fwdKeyTimer.stop()
684 self.fwdtimer = False
689 self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
690 self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
691 self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
692 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
693 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
694 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
695 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
696 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
697 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_PLAY,
698 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_16X,
699 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
700 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
701 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
702 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
703 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER
705 self.setSeekState(lookup[self.seekstate])
707 def seekBackUp(self):
710 self.rwdKeyTimer.stop()
711 self.rwdtimer = False
716 self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_16X,
717 self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
718 self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
719 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
720 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
721 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
722 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
723 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
724 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_BACK_32X,
725 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
726 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
727 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
728 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
729 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
730 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE
732 self.setSeekState(lookup[self.seekstate])
734 def fwdTimerFire(self):
735 print "Display seek fwd"
736 self.fwdKeyTimer.stop()
737 self.fwdtimer = False
738 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
740 def fwdSeekTo(self, minutes):
741 print "Seek", minutes, "minutes forward"
743 seekable = self.getSeek()
744 if seekable is not None:
745 seekable.seekRelative(1, minutes * 60 * 90000)
747 def rwdTimerFire(self):
749 self.rwdKeyTimer.stop()
750 self.rwdtimer = False
751 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
753 def rwdSeekTo(self, minutes):
755 self.fwdSeekTo(0 - minutes)
757 def checkSkipShowHideLock(self):
758 wantlock = self.seekstate != self.SEEK_STATE_PLAY
760 if self.lockedBecauseOfSkipping and not wantlock:
762 self.lockedBecauseOfSkipping = False
764 if wantlock and not self.lockedBecauseOfSkipping:
766 self.lockedBecauseOfSkipping = True
769 if self.seekstate != self.SEEK_STATE_PLAY:
770 self.setSeekState(self.SEEK_STATE_PAUSE)
772 #self.getSeek().seekRelative(1, -90000)
773 self.setSeekState(self.SEEK_STATE_PLAY)
775 self.setSeekState(self.SEEK_STATE_PAUSE)
778 self.setSeekState(self.SEEK_STATE_PLAY)
781 def seekRelative(self, diff):
782 seekable = self.getSeek()
783 if seekable is not None:
784 seekable.seekRelative(0, diff)
786 from Screens.PVRState import PVRState
788 class InfoBarPVRState:
790 self.onPlayStateChanged.append(self.__playStateChanged)
791 self.pvrStateDialog = self.session.instantiateDialog(PVRState)
792 self.onShow.append(self.__mayShow)
793 self.onHide.append(self.pvrStateDialog.hide)
796 if self.seekstate != self.SEEK_STATE_PLAY:
797 self.pvrStateDialog.show()
799 def __playStateChanged(self, state):
800 playstateString = state[3]
801 self.pvrStateDialog["state"].setText(playstateString)
804 class InfoBarShowMovies:
806 # i don't really like this class.
807 # it calls a not further specified "movie list" on up/down/movieList,
808 # so this is not more than an action map
810 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions",
812 "movieList": (self.showMovies, "movie list"),
813 "up": (self.showMovies, "movie list"),
814 "down": (self.showMovies, "movie list")
817 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
821 # Timeshift works the following way:
822 # demux0 demux1 "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
823 # - normal playback TUNER unused PLAY enable disable disable
824 # - user presses "yellow" button. TUNER record PAUSE enable disable enable
825 # - user presess pause again FILE record PLAY enable disable enable
826 # - user fast forwards FILE record FF enable disable enable
827 # - end of timeshift buffer reached TUNER record PLAY enable enable disable
828 # - user backwards FILE record BACK # !! enable disable enable
832 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
833 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
834 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
835 # - the user can now PVR around
836 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
837 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
839 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
840 # - if the user rewinds, or press pause, timeshift will be activated again
842 # note that a timeshift can be enabled ("recording") and
843 # activated (currently time-shifting).
845 class InfoBarTimeshift:
847 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions",
849 "timeshiftStart": (self.startTimeshift, "start timeshift"), # the "yellow key"
850 "timeshiftStop": (self.stopTimeshift, "stop timeshift") # currently undefined :), probably 'TV'
852 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
854 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
855 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause # something like "backward key"
856 }, prio=-1) # priority over record
858 self.timeshift_enabled = 0
859 self.timeshift_state = 0
860 self.ts_pause_timer = eTimer()
861 self.ts_pause_timer.timeout.get().append(self.pauseService)
863 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
865 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
868 def getTimeshift(self):
869 service = self.session.nav.getCurrentService()
870 return service.timeshift()
872 def startTimeshift(self):
873 print "enable timeshift"
874 ts = self.getTimeshift()
876 self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
877 print "no ts interface"
880 if self.timeshift_enabled:
881 print "hu, timeshift already enabled?"
883 if not ts.startTimeshift():
884 self.timeshift_enabled = 1
887 self.setSeekState(self.SEEK_STATE_PAUSE)
889 # enable the "TimeshiftEnableActions", which will override
890 # the startTimeshift actions
891 self.__seekableStatusChanged()
893 print "timeshift failed"
895 def stopTimeshift(self):
896 if not self.timeshift_enabled:
898 print "disable timeshift"
899 ts = self.getTimeshift()
902 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
904 def stopTimeshiftConfirmed(self, confirmed):
908 ts = self.getTimeshift()
913 self.timeshift_enabled = 0
916 self.__seekableStatusChanged()
918 # activates timeshift, and seeks to (almost) the end
919 def activateTimeshiftEnd(self):
920 ts = self.getTimeshift()
925 if ts.isTimeshiftActive():
926 print "!! activate timeshift called - but shouldn't this be a normal pause?"
929 self.setSeekState(self.SEEK_STATE_PLAY)
930 ts.activateTimeshift()
933 # same as activateTimeshiftEnd, but pauses afterwards.
934 def activateTimeshiftEndAndPause(self):
935 state = self.seekstate
936 self.activateTimeshiftEnd()
938 # well, this is "andPause", but it could be pressed from pause,
939 # when pausing on the (fake-)"live" picture, so an un-pause
942 print "now, pauseService"
943 if state == self.SEEK_STATE_PLAY:
944 print "is PLAYING, start pause timer"
945 self.ts_pause_timer.start(200, 1)
948 self.unPauseService()
950 def __seekableStatusChanged(self):
953 print "self.isSeekable", self.isSeekable()
954 print "self.timeshift_enabled", self.timeshift_enabled
956 # when this service is not seekable, but timeshift
957 # is enabled, this means we can activate
959 if not self.isSeekable() and self.timeshift_enabled:
962 print "timeshift activate:", enabled
963 self["TimeshiftActivateActions"].setEnabled(enabled)
965 from RecordTimer import parseEvent
967 class InfoBarInstantRecord:
968 """Instant Record - handles the instantRecord action in order to
969 start/stop instant records"""
971 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
973 "instantRecord": (self.instantRecord, "Instant Record..."),
975 self.recording = None
976 self["BlinkingPoint"] = BlinkingPixmapConditional()
977 self["BlinkingPoint"].hide()
978 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
980 def stopCurrentRecording(self):
981 self.session.nav.RecordTimer.removeEntry(self.recording)
982 self.recording = None
984 def startInstantRecording(self):
985 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
987 # try to get event info
990 service = self.session.nav.getCurrentService()
991 epg = eEPGCache.getInstance()
992 event = epg.lookupEventTime(serviceref, -1, 0)
994 info = service.info()
995 ev = info.getEvent(0)
1000 if event is not None:
1001 data = parseEvent(event)
1003 end = begin + 3600 * 10
1004 data = (begin, end, data[2], data[3], data[4])
1006 data = (time.time(), time.time() + 3600 * 10, "instant record", "", None)
1008 # fix me, description.
1009 self.recording = self.session.nav.recordWithTimer(serviceref, *data)
1010 self.recording.dontSave = True
1012 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
1014 def isInstantRecordRunning(self):
1015 if self.recording != None:
1016 if self.recording.isRunning():
1020 def recordQuestionCallback(self, answer):
1024 if self.isInstantRecordRunning():
1025 self.stopCurrentRecording()
1027 self.startInstantRecording()
1029 def instantRecord(self):
1031 stat = os.stat(resolveFilename(SCOPE_HDD))
1033 self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1036 if self.isInstantRecordRunning():
1037 self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Do you want to stop the current\n(instant) recording?"))
1039 self.session.openWithCallback(self.recordQuestionCallback, MessageBox, _("Start recording?"))
1041 from Screens.AudioSelection import AudioSelection
1043 class InfoBarAudioSelection:
1045 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions",
1047 "audioSelection": (self.audioSelection, "Audio Options..."),
1050 def audioSelection(self):
1051 service = self.session.nav.getCurrentService()
1052 audio = service.audioTracks()
1053 n = audio.getNumberOfTracks()
1055 self.session.open(AudioSelection, audio)
1057 from Screens.SubserviceSelection import SubserviceSelection
1059 class InfoBarSubserviceSelection:
1061 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1063 "subserviceSelection": (self.subserviceSelection, "Subservice list..."),
1066 def subserviceSelection(self):
1067 service = self.session.nav.getCurrentService()
1068 subservices = service.subServices()
1069 n = subservices.getNumberOfSubservices()
1071 self.session.openWithCallback(self.subserviceSelected, SubserviceSelection, subservices)
1073 def subserviceSelected(self, service):
1074 if not service is None:
1075 self.session.nav.playService(service)
1077 class InfoBarAdditionalInfo:
1079 self["DolbyActive"] = Pixmap()
1080 self["CryptActive"] = Pixmap()
1081 self["FormatActive"] = Pixmap()
1083 self["ButtonRed"] = PixmapConditional(withTimer = False)
1084 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1085 self.onLayoutFinish.append(self["ButtonRed"].update)
1086 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1087 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1088 self.onLayoutFinish.append(self["ButtonRedText"].update)
1090 self["ButtonGreen"] = Pixmap()
1091 self["ButtonGreenText"] = Label(_("Subservices"))
1093 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1094 self["ButtonYellow"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1095 self["ButtonYellowText"] = LabelConditional(text = _("Timeshifting"), withTimer = False)
1096 self["ButtonYellowText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1097 self.onLayoutFinish.append(self["ButtonYellow"].update)
1098 self.onLayoutFinish.append(self["ButtonYellowText"].update)
1100 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1101 self["ButtonBlue"].setConnect(lambda: False)
1102 self["ButtonBlueText"] = LabelConditional(text = _("Extensions"), withTimer = False)
1103 self["ButtonBlueText"].setConnect(lambda: False)
1104 self.onLayoutFinish.append(self["ButtonBlue"].update)
1105 self.onLayoutFinish.append(self["ButtonBlueText"].update)
1107 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1109 def hideSubServiceIndication(self):
1110 self["ButtonGreen"].hide()
1111 self["ButtonGreenText"].hide()
1113 def showSubServiceIndication(self):
1114 self["ButtonGreen"].show()
1115 self["ButtonGreenText"].show()
1117 def checkFormat(self, service):
1118 info = service.info()
1119 if info is not None:
1120 aspect = info.getInfo(iServiceInformation.sAspect)
1121 if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1122 self["FormatActive"].show()
1124 self["FormatActive"].hide()
1126 def checkSubservices(self, service):
1127 if service.subServices().getNumberOfSubservices() > 0:
1128 self.showSubServiceIndication()
1130 self.hideSubServiceIndication()
1132 def checkDolby(self, service):
1135 audio = service.audioTracks()
1136 if audio is not None:
1137 n = audio.getNumberOfTracks()
1139 i = audio.getTrackInfo(x)
1140 description = i.getDescription();
1141 if description.find("AC3") != -1 or description.find("DTS") != -1:
1145 self["DolbyActive"].show()
1147 self["DolbyActive"].hide()
1149 def checkCrypted(self, service):
1150 info = service.info()
1151 if info is not None:
1152 if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1153 self["CryptActive"].show()
1155 self["CryptActive"].hide()
1157 def gotServiceEvent(self, ev):
1158 service = self.session.nav.getCurrentService()
1159 if ev == iPlayableService.evUpdatedEventInfo:
1160 self.checkSubservices(service)
1161 self.checkFormat(service)
1162 elif ev == iPlayableService.evUpdatedInfo:
1163 self.checkCrypted(service)
1164 self.checkDolby(service)
1165 elif ev == iPlayableService.evEnd:
1166 self.hideSubServiceIndication()
1167 self["CryptActive"].hide()
1168 self["DolbyActive"].hide()
1169 self["FormatActive"].hide()
1171 class InfoBarNotifications:
1173 self.onExecBegin.append(self.checkNotifications)
1174 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1176 def checkNotificationsIfExecing(self):
1178 self.checkNotifications()
1180 def checkNotifications(self):
1181 if len(Notifications.notifications):
1182 n = Notifications.notifications[0]
1183 Notifications.notifications = Notifications.notifications[1:]
1187 self.session.openWithCallback(cb, *n[1:])
1189 self.session.open(*n[1:])
1191 class InfoBarServiceNotifications:
1193 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1195 iPlayableService.evEnd: self.serviceHasEnded
1198 def serviceHasEnded(self):
1199 print "service end!"
1202 self.setSeekState(self.SEEK_STATE_PLAY)
1206 class InfoBarCueSheetSupport:
1212 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions",
1214 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1215 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1216 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1220 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1222 iPlayableService.evStart: self.__serviceStarted,
1225 def __serviceStarted(self):
1226 print "new service started! trying to download cuts!"
1227 self.downloadCuesheet()
1229 def __getSeekable(self):
1230 service = self.session.nav.getCurrentService()
1233 return service.seek()
1235 def cueGetCurrentPosition(self):
1236 seek = self.__getSeekable()
1239 r = seek.getPlayPosition()
1244 def jumpPreviousNextMark(self, cmp, alternative=None):
1245 current_pos = self.cueGetCurrentPosition()
1246 if current_pos is None:
1248 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1249 if mark is not None:
1251 elif alternative is not None:
1256 seekable = self.__getSeekable()
1257 if seekable is not None:
1258 seekable.seekTo(pts)
1260 def jumpPreviousMark(self):
1261 # we add 2 seconds, so if the play position is <2s after
1262 # the mark, the mark before will be used
1263 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1265 def jumpNextMark(self):
1266 self.jumpPreviousNextMark(lambda x: x)
1268 def getNearestCutPoint(self, pts, cmp=abs):
1271 for cp in self.cut_list:
1272 diff = cmp(cp[0] - pts)
1273 if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1277 def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000):
1278 current_pos = self.cueGetCurrentPosition()
1279 if current_pos is None:
1280 print "not seekable"
1283 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1285 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1287 self.removeMark(nearest_cutpoint)
1288 elif not onlyremove:
1289 self.addMark((current_pos, self.CUT_TYPE_MARK))
1291 def addMark(self, point):
1292 bisect.insort(self.cut_list, point)
1293 self.uploadCuesheet()
1295 def removeMark(self, point):
1296 self.cut_list.remove(point)
1297 self.uploadCuesheet()
1299 def __getCuesheet(self):
1300 service = self.session.nav.getCurrentService()
1303 return service.cueSheet()
1305 def uploadCuesheet(self):
1306 cue = self.__getCuesheet()
1309 print "upload failed, no cuesheet interface"
1311 cue.setCutList(self.cut_list)
1313 def downloadCuesheet(self):
1314 cue = self.__getCuesheet()
1317 print "upload failed, no cuesheet interface"
1319 self.cut_list = cue.getCutList()