Merge branch 'master' of /home/tmbinc/enigma2-git into tmbinc/FixTimingBugs
[enigma2.git] / lib / python / Screens / InfoBarGenerics.py
index b0572979acc75bf370a14c6221b5e6cd34571b7f..e64081e6924db11166e8a997f98af79ae17ff91c 100644 (file)
@@ -2,11 +2,9 @@ from ChannelSelection import ChannelSelection, BouquetSelector
 
 from Components.ActionMap import ActionMap, HelpableActionMap
 from Components.ActionMap import NumberActionMap
-from Components.BlinkingPixmap import BlinkingPixmapConditional
 from Components.Harddisk import harddiskmanager
 from Components.Input import Input
 from Components.Label import Label
-from Components.Pixmap import Pixmap
 from Components.PluginComponent import plugins
 from Components.ServiceEventTracker import ServiceEventTracker
 from Components.Sources.Boolean import Boolean
@@ -31,15 +29,17 @@ from Screens.TimeDateInput import TimeDateInput
 from ServiceReference import ServiceReference
 
 from Tools import Notifications
-from Tools.Directories import SCOPE_HDD, resolveFilename
+from Tools.Directories import SCOPE_HDD, resolveFilename, fileExists
 
 from enigma import eTimer, eServiceCenter, eDVBServicePMTHandler, iServiceInformation, \
-       iPlayableService, eServiceReference, eDVBResourceManager, iFrontendInformation, eEPGCache
+       iPlayableService, eServiceReference, eEPGCache
 
 from time import time, localtime, strftime
 from os import stat as os_stat
 from bisect import insort
 
+from RecordTimer import RecordTimerEntry, RecordTimer
+
 # hack alert!
 from Menu import MainMenu, mdom
 
@@ -348,14 +348,14 @@ class InfoBarMenu:
 
        def mainMenu(self):
                print "loading mainmenu XML..."
-               menu = mdom.childNodes[0]
-               assert menu.tagName == "menu", "root element in menu must be 'menu'!"
+               menu = mdom.getroot()
+               assert menu.tag == "menu", "root element in menu must be 'menu'!"
 
                self.session.infobar = self
                # so we can access the currently active infobar from screens opened from within the mainmenu
                # at the moment used from the SubserviceSelection
 
-               self.session.openWithCallback(self.mainMenuClosed, MainMenu, menu, menu.childNodes)
+               self.session.openWithCallback(self.mainMenuClosed, MainMenu, menu)
 
        def mainMenuClosed(self, *val):
                self.session.infobar = None
@@ -404,7 +404,7 @@ class InfoBarEPG:
                self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
                        {
                                "showEventInfo": (self.openEventView, _("show EPG...")),
-                               "showSingleServiceEPG": (self.openSingleServiceEPG, _("show single service EPG...")),
+                               "showEventInfoPlugin": (self.showEventInfoPlugins, _("show single service EPG...")),
                                "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible,
                        })
 
@@ -489,6 +489,23 @@ class InfoBarEPG:
                ref=self.session.nav.getCurrentlyPlayingServiceReference()
                self.session.open(EPGSelection, ref)
 
+       def showEventInfoPlugins(self):
+               list = []
+               for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EVENTINFO):
+                       list.append((p.name, boundFunction(self.runPlugin, p)))
+               if len(list):
+                       list.append((_("show single service EPG..."), self.openSingleServiceEPG))
+                       self.session.openWithCallback(self.EventInfoPluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = list)
+               else:
+                       self.openSingleServiceEPG()
+                       
+       def runPlugin(self, plugin):
+               plugin(session = self.session, servicelist = self.servicelist)
+               
+       def EventInfoPluginChosen(self, answer):
+               if answer is not None:
+                       answer[1]()
+
        def openSimilarList(self, eventid, refstr):
                self.session.open(EPGSelection, refstr, None, eventid)
 
@@ -593,7 +610,7 @@ class InfoBarSeek:
        SEEK_STATE_PAUSE = (1, 0, 0, "||")
        SEEK_STATE_EOF = (1, 0, 0, "END")
 
-       def __init__(self, actionmap = "InfobarSeekActions"):
+       def __init__(self, actionmap = "InfobarSeekActions", useSeekBackHack=True):
                self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
                        {
                                iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
@@ -608,7 +625,7 @@ class InfoBarSeek:
                self.eofInhibitTimer = eTimer()
                self.eofInhibitTimer.timeout.get().append(self.inhibitEof)
 
-#              self.minSpeedBackward = 16
+               self.minSpeedBackward = useSeekBackHack and 16 or 0
 
                class InfoBarSeekActionMap(HelpableActionMap):
                        def __init__(self, screen, *args, **kwargs):
@@ -666,13 +683,12 @@ class InfoBarSeek:
        def makeStateBackward(self, n):
                minspeed = config.seek.stepwise_minspeed.value
                repeat = int(config.seek.stepwise_repeat.value)
-#              if n < self.minSpeedBackward:
-#                      r = (self.minSpeedBackward - 1)/ n + 1
-#                      if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
-#                              r = max(r, repeat)
-#                      return (0, -n * r, r, "<< %dx" % n)
-#              el
-               if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
+               if self.minSpeedBackward and n < self.minSpeedBackward:
+                       r = (self.minSpeedBackward - 1)/ n + 1
+                       if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
+                               r = max(r, repeat)
+                       return (0, -n * r, r, "<< %dx" % n)
+               elif minspeed != "Never" and n >= int(minspeed) and repeat > 1:
                        return (0, -n * repeat, repeat, "<< %dx" % n)
                else:
                        return (0, -n, 0, "<< %dx" % n)
@@ -731,14 +747,14 @@ class InfoBarSeek:
                return True
 
        def __seekableStatusChanged(self):
-               print "seekable status changed!"
+#              print "seekable status changed!"
                if not self.isSeekable():
                        self["SeekActions"].setEnabled(False)
-                       print "not seekable, return to play"
+#                      print "not seekable, return to play"
                        self.setSeekState(self.SEEK_STATE_PLAY)
                else:
                        self["SeekActions"].setEnabled(True)
-                       print "seekable"
+#                      print "seekable"
 
        def __serviceStarted(self):
                self.seekstate = self.SEEK_STATE_PLAY
@@ -763,12 +779,21 @@ class InfoBarSeek:
                        print "not pauseable."
                        state = self.SEEK_STATE_PLAY
 
-               oldstate = self.seekstate
                self.seekstate = state
 
-               for i in range(3):
-                       if oldstate[i] != self.seekstate[i]:
-                               (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
+               if pauseable is not None:
+                       if self.seekstate[0]:
+                               print "resolved to PAUSE"
+                               pauseable.pause()
+                       elif self.seekstate[1]:
+                               print "resolved to FAST FORWARD"
+                               pauseable.setFastForward(self.seekstate[1])
+                       elif self.seekstate[2]:
+                               print "resolved to SLOW MOTION"
+                               pauseable.setSlowMotion(self.seekstate[2])
+                       else:
+                               print "resolved to PLAY"
+                               pauseable.unpause()
 
                for c in self.onPlayStateChanged:
                        c(self.seekstate)
@@ -1008,14 +1033,20 @@ class InfoBarPVRState:
        def __playStateChanged(self, state):
                playstateString = state[3]
                self.pvrStateDialog["state"].setText(playstateString)
-               self._mayShow()
+               
+               # if we return into "PLAY" state, ensure that the dialog gets hidden if there will be no infobar displayed
+               if not config.usage.show_infobar_on_skip.value and self.seekstate == self.SEEK_STATE_PLAY:
+                       self.pvrStateDialog.hide()
+               else:
+                       self._mayShow()
+                       
 
 class InfoBarTimeshiftState(InfoBarPVRState):
        def __init__(self):
                InfoBarPVRState.__init__(self, screen=TimeshiftState)
 
        def _mayShow(self):
-               if self.execing and self.timeshift_enabled:
+               if self.execing and self.timeshift_enabled and self.seekstate != self.SEEK_STATE_PLAY:
                        self.pvrStateDialog.show()
 
 class InfoBarShowMovies:
@@ -1171,8 +1202,8 @@ class InfoBarTimeshift:
        def __seekableStatusChanged(self):
                enabled = False
 
-               print "self.isSeekable", self.isSeekable()
-               print "self.timeshift_enabled", self.timeshift_enabled
+#              print "self.isSeekable", self.isSeekable()
+#              print "self.timeshift_enabled", self.timeshift_enabled
 
                # when this service is not seekable, but timeshift
                # is enabled, this means we can activate
@@ -1180,7 +1211,7 @@ class InfoBarTimeshift:
                if not self.isSeekable() and self.timeshift_enabled:
                        enabled = True
 
-               print "timeshift activate:", enabled
+#              print "timeshift activate:", enabled
                self["TimeshiftActivateActions"].setEnabled(enabled)
 
        def __serviceStarted(self):
@@ -1199,7 +1230,7 @@ class InfoBarExtensions:
                self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
                        {
                                "extensions": (self.showExtensionSelection, _("view extensions...")),
-                       })
+                       }, 1) # lower priority
 
        def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
                self.list.append((type, extension, key))
@@ -1259,7 +1290,6 @@ class InfoBarExtensions:
 from Tools.BoundFunction import boundFunction
 
 # depends on InfoBarExtensions
-from Components.PluginComponent import plugins
 
 class InfoBarPlugins:
        def __init__(self):
@@ -1277,13 +1307,32 @@ class InfoBarPlugins:
        def runPlugin(self, plugin):
                plugin(session = self.session, servicelist = self.servicelist)
 
+from Components.Task import job_manager
+class InfoBarJobman:
+       def __init__(self):
+               self.addExtension(extension = self.getJobList, type = InfoBarExtensions.EXTENSION_LIST)
+
+       def getJobList(self):
+               list = []
+               for job in job_manager.getPendingJobs():
+                       list.append(((boundFunction(self.getJobName, job), boundFunction(self.showJobView, job), lambda: True), None))
+               return list
+
+       def getJobName(self, job):
+               return "%s: %s (%d%%)" % (job.getStatustext(), job.name, int(100*job.progress/float(job.end)))
+
+       def showJobView(self, job):
+               from Screens.TaskView import JobView
+               job_manager.in_background = False
+               self.session.openWithCallback(self.JobViewCB, JobView, job)
+       
+       def JobViewCB(self, in_background):
+               job_manager.in_background = in_background
+
 # depends on InfoBarExtensions
 class InfoBarSleepTimer:
        def __init__(self):
-               self.addExtension((self.getSleepTimerName, self.showSleepTimerSetup, self.available), "1")
-
-       def available(self):
-               return True
+               self.addExtension((self.getSleepTimerName, self.showSleepTimerSetup, lambda: True), "1")
 
        def getSleepTimerName(self):
                return _("Sleep Timer")
@@ -1296,13 +1345,10 @@ class InfoBarPiP:
        def __init__(self):
                self.session.pipshown = False
                if SystemInfo.get("NumVideoDecoders", 1) > 1:
-                       self.addExtension((self.getShowHideName, self.showPiP, self.available), "blue")
+                       self.addExtension((self.getShowHideName, self.showPiP, lambda: True), "blue")
                        self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
                        self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
 
-       def available(self):
-               return SystemInfo.get("NumVideoDecoders", 1) > 1
-
        def pipShown(self):
                return self.session.pipshown
 
@@ -1363,7 +1409,7 @@ class InfoBarPiP:
                elif "stop" == use:
                        self.showPiP()
 
-from RecordTimer import parseEvent
+from RecordTimer import parseEvent, RecordTimerEntry
 
 class InfoBarInstantRecord:
        """Instant Record - handles the instantRecord action in order to
@@ -1396,8 +1442,8 @@ class InfoBarInstantRecord:
                except:
                        pass
 
-               begin = time()
-               end = time() + 3600 * 10
+               begin = int(time())
+               end = begin + 3600      # dummy
                name = "instant record"
                description = ""
                eventid = None
@@ -1413,11 +1459,33 @@ class InfoBarInstantRecord:
                        if limitEvent:
                                self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
 
-               data = (begin, end, name, description, eventid)
+               if isinstance(serviceref, eServiceReference):
+                       serviceref = ServiceReference(serviceref)
 
-               recording = self.session.nav.recordWithTimer(serviceref, *data)
+               recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = config.movielist.last_videodir.value)
                recording.dontSave = True
-               self.recording.append(recording)
+               
+               if event is None or limitEvent == False:
+                       recording.autoincrease = True
+                       if recording.setAutoincreaseEnd():
+                               self.session.nav.RecordTimer.record(recording)
+                               self.recording.append(recording)
+               else:
+                               simulTimerList = self.session.nav.RecordTimer.record(recording)
+                               if simulTimerList is not None:  # conflict with other recording
+                                       name = simulTimerList[1].name
+                                       name_date = name + strftime(" %c", localtime(simulTimerList[1].begin))
+                                       print "[TIMER] conflicts with", name_date
+                                       recording.autoincrease = True   # start with max available length, then increment
+                                       if recording.setAutoincreaseEnd():
+                                               self.session.nav.RecordTimer.record(recording)
+                                               self.recording.append(recording)
+                                               self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO)
+                                       else:
+                                               self.session.open(MessageBox, _("Couldn't record due to conflicting timer %s") % name, MessageBox.TYPE_INFO)
+                                       recording.autoincrease = False
+                               else:
+                                       self.recording.append(recording)
 
        def isInstantRecordRunning(self):
                print "self.recording:", self.recording
@@ -1475,6 +1543,8 @@ class InfoBarInstantRecord:
                        if ret[0]:
                                localendtime = localtime(ret[1])
                                print "stopping recording at", strftime("%c", localendtime)
+                               if self.recording[self.selectedEntry].end != ret[1]:
+                                       self.recording[self.selectedEntry].autoincrease = False
                                self.recording[self.selectedEntry].end = ret[1]
                                self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
 
@@ -1486,34 +1556,40 @@ class InfoBarInstantRecord:
        def inputCallback(self, value):
                if value is not None:
                        print "stopping recording after", int(value), "minutes."
-                       self.recording[self.selectedEntry].end = time() + 60 * int(value)
+                       if int(value) != 0:
+                               self.recording[self.selectedEntry].autoincrease = False
+                       self.recording[self.selectedEntry].end = int(time()) + 60 * int(value)
                        self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
 
        def instantRecord(self):
+               dir = config.movielist.last_videodir.value
+               if not fileExists(dir, 'w'):
+                       dir = resolveFilename(SCOPE_HDD)
                try:
-                       stat = os_stat(resolveFilename(SCOPE_HDD))
+                       stat = os_stat(dir)
                except:
+                       # XXX: this message is a little odd as we might be recording to a remote device
                        self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
                        return
 
                if self.isInstantRecordRunning():
                        self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
                                title=_("A recording is currently running.\nWhat do you want to do?"), \
-                               list=[(_("stop recording"), "stop"), \
-                               (_("change recording (duration)"), "changeduration"), \
-                               (_("change recording (endtime)"), "changeendtime"), \
-                               (_("add recording (indefinitely)"), "indefinitely"), \
-                               (_("add recording (stop after current event)"), "event"), \
+                               list=[(_("add recording (stop after current event)"), "event"), \
                                (_("add recording (enter recording duration)"), "manualduration"), \
                                (_("add recording (enter recording endtime)"), "manualendtime"), \
+                               (_("add recording (indefinitely)"), "indefinitely"), \
+                               (_("change recording (duration)"), "changeduration"), \
+                               (_("change recording (endtime)"), "changeendtime"), \
+                               (_("stop recording"), "stop"), \
                                (_("do nothing"), "no")])
                else:
                        self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
                                title=_("Start recording?"), \
-                               list=[(_("add recording (indefinitely)"), "indefinitely"), \
-                               (_("add recording (stop after current event)"), "event"), \
+                               list=[(_("add recording (stop after current event)"), "event"), \
                                (_("add recording (enter recording duration)"), "manualduration"), \
                                (_("add recording (enter recording endtime)"), "manualendtime"), \
+                               (_("add recording (indefinitely)"), "indefinitely"), \
                                (_("don't record"), "no")])
 
 from Tools.ISO639 import LanguageCodes
@@ -1527,10 +1603,8 @@ class InfoBarAudioSelection:
 
        def audioSelection(self):
                service = self.session.nav.getCurrentService()
-               audio = service and service.audioTracks()
-               self.audioTracks = audio
+               self.audioTracks = audio = service and service.audioTracks()
                n = audio and audio.getNumberOfTracks() or 0
-               keys = [ "red", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
                tlist = []
                if n > 0:
                        self.audioChannel = service.audioChannel()
@@ -1550,21 +1624,45 @@ class InfoBarAudioSelection:
 
                                tlist.append((description, x))
 
-                       selectedAudio = audio.getCurrentTrack()
                        tlist.sort(key=lambda x: x[0])
 
-                       selection = 2
+                       selectedAudio = self.audioTracks.getCurrentTrack()
+
+                       selection = 0
+
                        for x in tlist:
                                if x[1] != selectedAudio:
                                        selection += 1
                                else:
                                        break
 
-                       tlist = [([_("Left"), _("Stereo"), _("Right")][self.audioChannel.getCurrentChannel()], "mode"), ("--", "")] + tlist
+                       if SystemInfo["CanDownmixAC3"]:
+                               tlist = [(_("AC3 downmix") + " - " +[_("Off"), _("On")][config.av.downmix_ac3.value and 1 or 0], "CALLFUNC", self.changeAC3Downmix),
+                                       ([_("Left"), _("Stereo"), _("Right")][self.audioChannel.getCurrentChannel()], "mode"),
+                                       ("--", "")] + tlist
+                               keys = [ "red", "green", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
+                               selection += 3
+                       else:
+                               tlist = [([_("Left"), _("Stereo"), _("Right")][self.audioChannel.getCurrentChannel()], "mode"), ("--", "")] + tlist
+                               keys = [ "red", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
+                               selection += 2
                        self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection, keys = keys)
                else:
                        del self.audioTracks
 
+       def changeAC3Downmix(self, arg):
+               choicelist = self.session.current_dialog["list"]
+               list = choicelist.list
+               t = list[0][1]
+               list[0][1]=(t[0], t[1], t[2], t[3], t[4], t[5], t[6],
+                       _("AC3 downmix") + " - " +[_("On"), _("Off")][config.av.downmix_ac3.value and 1 or 0])
+               choicelist.setList(list)
+               if config.av.downmix_ac3.value:
+                       config.av.downmix_ac3.value = False
+               else:
+                       config.av.downmix_ac3.value = True
+               config.av.downmix_ac3.save()
+
        def audioSelected(self, audio):
                if audio is not None:
                        if isinstance(audio[1], str):
@@ -1600,16 +1698,18 @@ class InfoBarSubserviceSelection:
                        }, -1)
                self["SubserviceQuickzapAction"].setEnabled(False)
 
-               self.session.nav.event.append(self.checkSubservicesAvail) # we like to get service events
+               self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
+                       {
+                               iPlayableService.evUpdatedEventInfo: self.checkSubservicesAvail
+                       })
 
                self.bsel = None
 
-       def checkSubservicesAvail(self, ev):
-               if ev == iPlayableService.evUpdatedEventInfo:
-                       service = self.session.nav.getCurrentService()
-                       subservices = service and service.subServices()
-                       if not subservices or subservices.getNumberOfSubservices() == 0:
-                               self["SubserviceQuickzapAction"].setEnabled(False)
+       def checkSubservicesAvail(self):
+               service = self.session.nav.getCurrentService()
+               subservices = service and service.subServices()
+               if not subservices or subservices.getNumberOfSubservices() == 0:
+                       self["SubserviceQuickzapAction"].setEnabled(False)
 
        def nextSubservice(self):
                self.changeSubservice(+1)
@@ -1637,7 +1737,7 @@ class InfoBarSubserviceSelection:
                                if newservice.valid():
                                        del subservices
                                        del service
-                                       self.session.nav.playService(newservice)
+                                       self.session.nav.playService(newservice, False)
 
        def subserviceSelection(self):
                service = self.session.nav.getCurrentService()
@@ -1655,7 +1755,7 @@ class InfoBarSubserviceSelection:
                                tlist.append((i.getName(), i))
 
                        if self.bouquets and len(self.bouquets):
-                               keys = ["red", "green", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
+                               keys = ["red", "blue", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
                                if config.usage.multibouquet.value:
                                        tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
                                else:
@@ -1677,7 +1777,7 @@ class InfoBarSubserviceSelection:
                                        self.session.open(SubservicesQuickzap, service[2])
                        else:
                                self["SubserviceQuickzapAction"].setEnabled(True)
-                               self.session.nav.playService(service[1])
+                               self.session.nav.playService(service[1], False)
 
        def addSubserviceToBouquetCallback(self, service):
                if len(service) > 1 and isinstance(service[1], eServiceReference):
@@ -1708,8 +1808,11 @@ class InfoBarSubserviceSelection:
 class InfoBarAdditionalInfo:
        def __init__(self):
 
-               self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0)
+               self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0 and config.misc.rcused.value == 1)
                self["TimeshiftPossible"] = self["RecordingPossible"]
+               self["ShowTimeshiftOnYellow"] = Boolean(fixed=(not config.misc.rcused.value == 0))
+               self["ShowAudioOnYellow"] = Boolean(fixed=config.misc.rcused.value == 0)
+               self["ShowRecordOnRed"] = Boolean(fixed=config.misc.rcused.value == 1)
                self["ExtensionsAvailable"] = Boolean(fixed=1)
 
 class InfoBarNotifications: