movie player configuration options, by Anders Holst
authorFelix Domke <tmbinc@elitedvb.net>
Tue, 19 Feb 2008 23:33:24 +0000 (23:33 +0000)
committerFelix Domke <tmbinc@elitedvb.net>
Tue, 19 Feb 2008 23:33:24 +0000 (23:33 +0000)
data/keymap.xml
data/setup.xml
lib/python/Components/RecordingConfig.py
lib/python/Components/UsageConfig.py
lib/python/Components/config.py
lib/python/Plugins/Extensions/CutListEditor/plugin.py
lib/python/Plugins/Extensions/MediaPlayer/plugin.py
lib/python/Screens/InfoBar.py
lib/python/Screens/InfoBarGenerics.py

index 4012866..e83b851 100644 (file)
 
                <key id="KEY_OK" mapto="unPauseService" flags="m" />
                
-               <key id="KEY_1" mapto="seek:-15" flags="b" />
-               <key id="KEY_3" mapto="seek:15" flags="b" />
-               <key id="KEY_1" mapto="seekBackDef" flags="l" />
-               <key id="KEY_3" mapto="seekFwdDef" flags="l" />
-               <key id="KEY_4" mapto="seek:-60" flags="m" />
-               <key id="KEY_6" mapto="seek:60" flags="m" />
-               <key id="KEY_7" mapto="seek:-300" flags="m" />
-               <key id="KEY_9" mapto="seek:300" flags="m" />
+               <key id="KEY_1" mapto="seekdef:1" flags="m" />
+               <key id="KEY_3" mapto="seekdef:3" flags="m" />
+               <key id="KEY_4" mapto="seekdef:4" flags="m" />
+               <key id="KEY_6" mapto="seekdef:6" flags="m" />
+               <key id="KEY_7" mapto="seekdef:7" flags="m" />
+               <key id="KEY_9" mapto="seekdef:9" flags="m" />
        </map>
 
        <map context="MediaPlayerSeekActions">
                        <key id="KEY_FASTFORWARD" mapto="seekFwdManual" flags="l" />
                </device>
                
-               <key id="KEY_1" mapto="seek:-15" flags="b" />
-               <key id="KEY_3" mapto="seek:15" flags="b" />
-               <key id="KEY_1" mapto="seekBackDef" flags="l" />
-               <key id="KEY_3" mapto="seekFwdDef" flags="l" />
-               <key id="KEY_4" mapto="seek:-60" flags="m" />
-               <key id="KEY_6" mapto="seek:60" flags="m" />
-               <key id="KEY_7" mapto="seek:-300" flags="m" />
-               <key id="KEY_9" mapto="seek:300" flags="m" />
+               <key id="KEY_1" mapto="seekdef:1" flags="m" />
+               <key id="KEY_3" mapto="seekdef:3" flags="m" />
+               <key id="KEY_4" mapto="seekdef:4" flags="m" />
+               <key id="KEY_6" mapto="seekdef:6" flags="m" />
+               <key id="KEY_7" mapto="seekdef:7" flags="m" />
+               <key id="KEY_9" mapto="seekdef:9" flags="m" />
        </map>
 
        <map context="InfobarTimeshiftActions">
 
        <map context="InfobarTimeshiftActivateActions">
                <device name="dreambox remote control (native)">
-                       <key id="KEY_RED" mapto="timeshiftActivateEnd" flags="m" />
+                       <key id="KEY_RED" mapto="timeshiftActivateEnd" flags="b" />
                        <key id="KEY_YELLOW" mapto="timeshiftActivateEndAndPause" flags="m" />
                </device>
                <device name="dreambox advanced remote control (native)">
        
        <map context="MediaPlayerCueSheetActions">
                <device name="dreambox remote control (native)">
-                       <key id="KEY_PREVIOUS" mapto="jumpPreviousMark" flags="b" />
-                       <key id="KEY_NEXT" mapto="jumpNextMark" flags="b" />
                        <key id="KEY_0" mapto="toggleMark" flags="m" />
                </device>
                <device name="dreambox advanced remote control (native)">
-                       <key id="KEY_PREVIOUS" mapto="jumpPreviousMark" flags="b" />
                        <key id="KEY_0" mapto="toggleMark" flags="m" />
-                       <key id="KEY_NEXT" mapto="jumpNextMark" flags="b" />
-                       <key id="KEY_RED" mapto="jumpPreviousMark" flags="m" />
                        <key id="KEY_YELLOW" mapto="toggleMark" flags="m" />
-                       <key id="KEY_BLUE" mapto="jumpNextMark" flags="m" />
                </device>
                <device name="dreambox ir keyboard">
-                       <key id="KEY_PREVIOUSSONG" mapto="jumpPreviousMark" flags="b" />
                        <key id="KEY_TAB" mapto="toggleMark" flags="m" />
-                       <key id="KEY_NEXTSONG" mapto="jumpNextMark" flags="b" />
                </device>
        </map>
        
                        <key id="KEY_TV" mapto="stop" flags="b" />
                        <key id="KEY_TV" mapto="shift_stop" flags="l" />
                        <key id="KEY_RADIO" mapto="shift_record" flags="l" />
-                       <key id="KEY_PREVIOUS" mapto="previous" flags="l" />
-                       <key id="KEY_NEXT" mapto="next" flags="l" />
+                       <key id="KEY_PREVIOUS" mapto="previous" flags="m" />
+                       <key id="KEY_NEXT" mapto="next" flags="m" />
                </device>
                <device name="dreambox advanced remote control (native)">
                        <key id="KEY_PLAYPAUSE" mapto="pause" flags="m" />
                        <key id="KEY_STOP" mapto="stop" flags="b" />
                        <key id="KEY_STOP" mapto="shift_stop" flags="l" />
                        <key id="KEY_RECORD" mapto="shift_record" flags="l" />
-                       <key id="KEY_PREVIOUS" mapto="previous" flags="l" />
-                       <key id="KEY_NEXT" mapto="next" flags="l" />
+                       <key id="KEY_PREVIOUS" mapto="previous" flags="m" />
+                       <key id="KEY_NEXT" mapto="next" flags="m" />
+                       <key id="KEY_RED" mapto="previous" flags="m" />
+                       <key id="KEY_BLUE" mapto="next" flags="m" />
                </device>
                <device name="dreambox ir keyboard">
                        <key id="KEY_PAUSE" mapto="pause" flags="m" />
                        <key id="KEY_STOP" mapto="stop" flags="b" />
                        <key id="KEY_STOP" mapto="shift_stop" flags="l" />
                        <key id="KEY_RECORD" mapto="shift_record" flags="l" />
-                       <key id="KEY_PREVIOUSSONG" mapto="previous" flags="l" />
-                       <key id="KEY_NEXTSONG" mapto="next" flags="l" />
+                       <key id="KEY_PREVIOUSSONG" mapto="previous" flags="m" />
+                       <key id="KEY_NEXTSONG" mapto="next" flags="m" />
                </device>
                
                <key id="KEY_MENU" mapto="menu" flags="m" />
index 8f6399a..c49d629 100644 (file)
                        <item level="2" text="Show infobar on channel change">config.usage.show_infobar_on_zap</item>
                        <item level="2" text="Show infobar on skip forward/backward">config.usage.show_infobar_on_skip</item>
                        <item level="2" text="Show infobar on event change">config.usage.show_infobar_on_event_change</item>
-                       <item level="2" text="Custom skip time for '1'/'3'-keys">config.usage.self_defined_seek</item>
+                       <item level="2" text="Behavior when a movie is started">config.usage.on_movie_start</item>
+                       <item level="2" text="Behavior when a movie is stopped">config.usage.on_movie_stop</item>
+                       <item level="2" text="Behavior when a movie reaches the end">config.usage.on_movie_eof</item>
+                       <item level="2" text="Custom skip time for '1'/'3'-keys">config.seek.selfdefined_13</item>
+                       <item level="2" text="Custom skip time for '4'/'6'-keys">config.seek.selfdefined_46</item>
+                       <item level="2" text="Custom skip time for '7'/'9'-keys">config.seek.selfdefined_79</item>
+                       <item level="2" text="Fast Forward speeds">config.seek.speeds_forward</item>
+                       <item level="2" text="Rewind speeds">config.seek.speeds_backward</item>
+                       <item level="2" text="Slow Motion speeds">config.seek.speeds_slowmotion</item>
+                       <item level="2" text="Enter Fast Forward at speed">config.seek.enter_forward</item>
+                       <item level="2" text="Enter Rewind at speed">config.seek.enter_backward</item>
+                       <item level="2" text="Discontinuous playback at speeds above">config.seek.stepwise_minspeed</item>
+                       <item level="2" text="Discontinuous playback frame repeat count">config.seek.stepwise_repeat</item>
+                       <item level="2" text="Behaviour of 'pause' when paused">config.seek.on_pause</item>
                        <item level="2" text="Behaviour of 0 key in PiP-mode">config.usage.pip_zero_button</item>
                        <item level="2" text="Alternative services tuner priority">config.usage.alternatives_priority</item>
                </setup>
index d3a0daf..9a13bd6 100644 (file)
@@ -1,9 +1,9 @@
-from config import ConfigInteger, ConfigYesNo, ConfigSubsection, config
+from config import ConfigNumber, ConfigYesNo, ConfigSubsection, config
 
 def InitRecordingConfig():
        config.recording = ConfigSubsection();
        # actually this is "recordings always have priority". "Yes" does mean: don't ask. The RecordTimer will ask when value is 0.
        config.recording.asktozap = ConfigYesNo(default=True)
-       config.recording.margin_before = ConfigInteger(default=0, limits=(0,30))
-       config.recording.margin_after = ConfigInteger(default=0, limits=(0,30))
+       config.recording.margin_before = ConfigNumber(default=0)
+       config.recording.margin_after = ConfigNumber(default=0)
        config.recording.debug = ConfigYesNo(default = False)
index 36d149c..3df3a44 100644 (file)
@@ -1,4 +1,4 @@
-from config import ConfigSubsection, ConfigYesNo, config, ConfigSelection, ConfigText, ConfigInteger
+from config import ConfigSubsection, ConfigYesNo, config, ConfigSelection, ConfigText, ConfigNumber, ConfigSet, ConfigNothing
 from enigma import Misc_Options, setTunerTypePriorityOrder;
 from SystemInfo import SystemInfo
 import os
@@ -24,12 +24,18 @@ def InitUsageConfig():
                ("248", "4 " + _("hours")) ])
        config.usage.output_12V = ConfigSelection(default = "do not change", choices = [
                ("do not change", _("do not change")), ("off", _("off")), ("on", _("on")) ])
-       config.usage.self_defined_seek = ConfigInteger(default=10, limits=(1,9999))
 
        config.usage.pip_zero_button = ConfigSelection(default = "standard", choices = [
                ("standard", _("standard")), ("swap", _("swap PiP and main picture")),
                ("swapstop", _("move PiP to main picture")), ("stop", _("stop PiP")) ])
 
+       config.usage.on_movie_start = ConfigSelection(default = "ask", choices = [
+               ("ask", _("Ask user")), ("resume", _("Resume from last position")), ("beginning", _("Start from the beginning")) ])
+       config.usage.on_movie_stop = ConfigSelection(default = "ask", choices = [
+               ("ask", _("Ask user")), ("movielist", _("Return to movie list")), ("quit", _("Return to previous service")) ])
+       config.usage.on_movie_eof = ConfigSelection(default = "ask", choices = [
+               ("ask", _("Ask user")), ("movielist", _("Return to movie list")), ("quit", _("Return to previous service")), ("pause", _("Pause movie at end")) ])
+
        config.usage.setup_level = ConfigSelection(default = "intermediate", choices = [
                ("simple", _("Simple")),
                ("intermediate", _("Intermediate")),
@@ -67,3 +73,50 @@ def InitUsageConfig():
        SystemInfo["12V_Output"] = Misc_Options.getInstance().detected_12V_output()
 
        config.usage.keymap = ConfigText(default = "/usr/share/enigma2/keymap.xml")
+
+       config.seek = ConfigSubsection()
+       config.seek.selfdefined_13 = ConfigNumber(default=15)
+       config.seek.selfdefined_46 = ConfigNumber(default=60)
+       config.seek.selfdefined_79 = ConfigNumber(default=300)
+
+       config.seek.speeds_forward = ConfigSet(default=[2, 4, 8, 16, 32, 64, 128], choices=[2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128])
+       config.seek.speeds_backward = ConfigSet(default=[2, 4, 8, 16, 32, 64, 128], choices=[1, 2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128])
+       config.seek.speeds_slowmotion = ConfigSet(default=[2, 4, 8], choices=[2, 4, 6, 8, 12, 16, 25])
+
+       config.seek.enter_forward = ConfigSelection(default = "2", choices = ["2"])
+       config.seek.enter_backward = ConfigSelection(default = "2", choices = ["2"])
+       config.seek.stepwise_minspeed = ConfigSelection(default = "16", choices = ["Never", "2", "4", "6", "8", "12", "16", "24", "32", "48", "64", "96", "128"])
+       config.seek.stepwise_repeat = ConfigSelection(default = "3", choices = ["2", "3", "4", "5", "6"])
+
+       config.seek.on_pause = ConfigSelection(default = "play", choices = [
+               ("play", _("Play")),
+               ("step", _("Singlestep (GOP)")),
+               ("last", _("Last speed")) ])
+
+       def updateEnterForward(configElement):
+               if not configElement.value:
+                       configElement.value = [2]
+               updateChoices(config.seek.enter_forward, configElement.value)
+
+       config.seek.speeds_forward.addNotifier(updateEnterForward)
+
+       def updateEnterBackward(configElement):
+               if not configElement.value:
+                       configElement.value = [2]
+               updateChoices(config.seek.enter_backward, configElement.value)
+
+       config.seek.speeds_backward.addNotifier(updateEnterBackward)
+
+def updateChoices(sel, choices):
+       if choices:
+                defval = None
+                val = int(sel.value)
+               if not val in choices:
+                        tmp = choices+[]
+                        tmp.reverse()
+                        for x in tmp:
+                                if x < val:
+                                        defval = str(x)
+                                        break
+               sel.setChoices(map(str, choices), defval)
+
index 861e70b..59fb725 100644 (file)
@@ -133,6 +133,10 @@ def getKeyNumber(key):
 class ConfigSelection(ConfigElement):
        def __init__(self, choices, default = None):
                ConfigElement.__init__(self)
+               self._value = None
+               self.setChoices(choices, default)
+
+       def setChoices(self, choices, default = None):
                self.choices = []
                self.description = {}
                
@@ -163,7 +167,10 @@ class ConfigSelection(ConfigElement):
                for x in self.choices:
                        assert isinstance(x, str), "ConfigSelection choices must be strings"
                
-               self.value = self.default = default
+               self.default = default
+
+               if self.value == None or not self.value in self.choices:
+                       self.value = default
 
        def setValue(self, value):
                if value in self.choices:
@@ -699,7 +706,7 @@ class ConfigText(ConfigElement, NumericalTextInput):
        _value = property(getValue, setValue)
 
        def getText(self):
-               return self.value
+               return self.text.encode("utf-8")
 
        def getMulti(self, selected):
                if self.visible_width:
@@ -713,7 +720,7 @@ class ConfigText(ConfigElement, NumericalTextInput):
                                mark = range(0, len(self.text))
                        else:
                                mark = [self.marked_pos]
-                       return ("mtext"[1-selected:], self.value+" ", mark)
+                       return ("mtext"[1-selected:], self.text.encode("utf-8")+" ", mark)
 
        def onSelect(self, session):
                self.allmarked = (self.value != "")
@@ -735,6 +742,54 @@ class ConfigText(ConfigElement, NumericalTextInput):
        def unsafeAssign(self, value):
                self.value = str(value)
 
+class ConfigNumber(ConfigText):
+       def __init__(self, default = 0):
+               ConfigText.__init__(self, str(default), fixed_size = False)
+
+       def getValue(self):
+               return int(self.text)
+               
+       def setValue(self, val):
+               self.text = str(val)
+
+       value = property(getValue, setValue)
+       _value = property(getValue, setValue)
+
+       def conform(self):
+               pos = len(self.text) - self.marked_pos
+               self.text = self.text.lstrip("0")
+               if self.text == "":
+                       self.text = "0"
+               if pos > len(self.text):
+                       self.marked_pos = 0
+               else:
+                       self.marked_pos = len(self.text) - pos
+
+       def handleKey(self, key):
+               if key in KEY_NUMBERS or key == KEY_ASCII:
+                       if key == KEY_ASCII:
+                               ascii = getPrevAsciiCode()
+                               if not (48 <= ascii <= 57):
+                                       return
+                       else:
+                               ascii = getKeyNumber(key) + 48
+                       newChar = unichr(ascii)
+                       if self.allmarked:
+                               self.deleteAllChars()
+                               self.allmarked = False
+                       self.insertChar(newChar, self.marked_pos, False)
+                       self.marked_pos += 1
+               else:
+                       ConfigText.handleKey(self, key)
+               self.conform()
+
+       def onSelect(self, session):
+               self.allmarked = (self.value != "")
+
+       def onDeselect(self, session):
+               self.marked_pos = 0
+               self.offset = 0
+
 # a slider.
 class ConfigSlider(ConfigElement):
        def __init__(self, default = 0, increment = 1, limits = (0, 100)):
@@ -968,6 +1023,77 @@ class ConfigSubsection(object):
        def dict(self):
                return self.content.items
 
+class ConfigSet(ConfigElement):
+       def __init__(self, choices, default = []):
+               ConfigElement.__init__(self)
+               choices.sort()
+               self.choices = choices
+               self.pos = -1
+               default.sort()
+               self.default = default
+               self.value = default+[]
+
+       def toggleChoice(self, choice):
+               if choice in self.value:
+                       self.value.remove(choice)
+               else:
+                       self.value.append(choice)
+                       self.value.sort()
+
+       def handleKey(self, key):
+               if key in KEY_NUMBERS + [KEY_DELETE, KEY_BACKSPACE]:
+                       if self.pos != -1:
+                               self.toggleChoice(self.choices[self.pos])
+               elif key == KEY_LEFT:
+                       self.pos -= 1
+                       if self.pos < -1:
+                           self.pos = len(self.choices)-1
+               elif key == KEY_RIGHT:
+                       self.pos += 1
+                       if self.pos >= len(self.choices):
+                           self.pos = -1
+               elif key in [KEY_HOME, KEY_END]:
+                       self.pos = -1
+
+       def genString(self, lst):
+               res = ""
+               for x in lst:
+                       res += str(x)+" "
+               return res
+
+       def getText(self):
+               self.genString(self.value)
+
+       def getMulti(self, selected):
+               if not selected or self.pos == -1:
+                       return ("text", self.genString(self.value))
+               else:
+                       tmp = self.value+[]
+                       ch = self.choices[self.pos]
+                       mem = ch in self.value
+                       if not mem:
+                               tmp.append(ch)
+                               tmp.sort()
+                       ind = tmp.index(ch)
+                       val1 = self.genString(tmp[:ind])
+                       val2 = " "+self.genString(tmp[ind+1:])
+                       if mem:
+                               chstr = " "+str(ch)+" "
+                       else:
+                               chstr = "("+str(ch)+")"
+                       return ("mtext", val1+chstr+val2, range(len(val1),len(val1)+len(chstr)))
+
+       def onDeselect(self, session):
+               self.pos = -1
+               self.changed()
+               
+       def tostring(self, value):
+               return str(value)
+
+       def fromstring(self, val):
+               return eval(val)
+
+
 # the root config object, which also can "pickle" (=serialize)
 # down the whole config tree.
 #
index 6f793d7..76a7bdc 100644 (file)
@@ -396,7 +396,7 @@ Then seek to the end, press OK, select 'end cut'. That's it.
        # we modify the "play" behaviour a bit:
        # if we press pause while being in slowmotion, we will pause (and not play)
        def playpauseService(self):
-               if self.seekstate not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_SM_HALF, self.SEEK_STATE_SM_QUARTER, self.SEEK_STATE_SM_EIGHTH]:
+               if self.seekstate != self.SEEK_STATE_PLAY and not self.isStateSlowMotion(self.seekstate):
                        self.unPauseService()
                else:
                        self.pauseService()
index 03d7617..fa7f164 100644 (file)
@@ -110,8 +110,8 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
                                "play": (self.xplayEntry, _("play entry")),
                                "pause": (self.pauseEntry, _("pause")),
                                "stop": (self.stopEntry, _("stop entry")),
-                               "previous": (self.previousEntry, _("play previous playlist entry")),
-                               "next": (self.nextEntry, _("play next playlist entry")),
+                               "previous": (self.previousMarkOrEntry, _("play from previous mark or playlist entry")),
+                               "next": (self.nextMarkOrEntry, _("play from next mark or playlist entry")),
                                "menu": (self.showMenu, _("menu")),
                                "skipListbegin": (self.skip_listbegin, _("jump to listbegin")),
                                "skipListend": (self.skip_listend, _("jump to listend")),
@@ -146,14 +146,6 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
 
                InfoBarSeek.__init__(self, actionmap = "MediaPlayerSeekActions")
 
-               self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
-                       {
-                               #iPlayableService.evStart: self.__serviceStarted,
-                               #iPlayableService.evSeekableStatusChanged: InfoBarSeek.__seekableStatusChanged,
-
-                               iPlayableService.evEOF: self.__evEOF,
-                       })
-
                self.onClose.append(self.delMPTimer)
                self.onClose.append(self.__onClose)
 
@@ -196,8 +188,11 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
        def checkSkipShowHideLock(self):
                self.updatedSeekState()
 
-       def __evEOF(self):
-               self.nextEntry()
+       def doEofInternal(self, playing):
+               if playing:
+                       self.nextEntry()
+               else:
+                       self.show()
 
        def __onClose(self):
                self.session.nav.playService(self.oldService)
@@ -570,10 +565,19 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
                if next < len(self.playlist):
                        self.changeEntry(next)
 
-       def previousEntry(self):
-               next = self.playlist.getCurrentIndex() - 1
-               if next >= 0:
-                       self.changeEntry(next)
+       def nextMarkOrEntry(self):
+               if not self.jumpPreviousNextMark(lambda x: x):
+                       next = self.playlist.getCurrentIndex() + 1
+                       if next < len(self.playlist):
+                               self.changeEntry(next)
+                       else:
+                               self.doSeek(-1)
+
+       def previousMarkOrEntry(self):
+               if not self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True):
+                       next = self.playlist.getCurrentIndex() - 1
+                       if next >= 0:
+                               self.changeEntry(next)
 
        def deleteEntry(self):
                self.playlist.deleteFile(self.playlist.getSelectionIndex())
@@ -675,21 +679,9 @@ class MediaPlayer(Screen, InfoBarSeek, InfoBarAudioSelection, InfoBarCueSheetSup
                        self.playlist.pauseFile()
                elif self.seekstate == self.SEEK_STATE_PLAY:
                        self.playlist.playFile()
-               elif self.seekstate in ( self.SEEK_STATE_FF_2X,
-                                                                self.SEEK_STATE_FF_4X,
-                                                                self.SEEK_STATE_FF_8X,
-                                                                self.SEEK_STATE_FF_16X,
-                                                                self.SEEK_STATE_FF_32X,
-                                                                self.SEEK_STATE_FF_48X,
-                                                                self.SEEK_STATE_FF_64X,
-                                                                self.SEEK_STATE_FF_128X):
+               elif self.isStateForward(self.seekstate):
                        self.playlist.forwardFile()
-               elif self.seekstate in ( self.SEEK_STATE_BACK_8X,
-                                                                self.SEEK_STATE_BACK_16X,
-                                                                self.SEEK_STATE_BACK_32X,
-                                                                self.SEEK_STATE_BACK_48X,
-                                                                self.SEEK_STATE_BACK_64X,
-                                                                self.SEEK_STATE_BACK_128X):
+               elif self.isStateBackward(self.seekstate):
                        self.playlist.rewindFile()
 
        def pauseEntry(self):
index 80b4239..90aa2dc 100644 (file)
@@ -149,29 +149,60 @@ class MoviePlayer(InfoBarShowHide, \
 
                self.lastservice = self.session.nav.getCurrentlyPlayingServiceReference()
                self.session.nav.playService(service)
+               self.returning = False
 
        def leavePlayer(self):
                self.is_closing = True
 
-               list = []
-               list.append((_("Yes"), "quit"))
-               list.append((_("No"), "continue"))
-               if config.usage.setup_level.index >= 2: # expert+
-                       list.append((_("No, but restart from begin"), "restart"))
-               self.session.openWithCallback(self.leavePlayerConfirmed, ChoiceBox, title=_("Stop playing this movie?"), list = list)
+               if config.usage.on_movie_stop.value == "ask":
+                       list = []
+                       list.append((_("Yes"), "quit"))
+                       if config.usage.setup_level.index >= 2: # expert+
+                               list.append((_("Yes, returning to movie list"), "movielist"))
+                       list.append((_("No"), "continue"))
+                       if config.usage.setup_level.index >= 2: # expert+
+                               list.append((_("No, but restart from begin"), "restart"))
+                       self.session.openWithCallback(self.leavePlayerConfirmed, ChoiceBox, title=_("Stop playing this movie?"), list = list)
+               else:
+                       self.leavePlayerConfirmed([True, config.usage.on_movie_stop.value])
 
        def leavePlayerConfirmed(self, answer):
                answer = answer and answer[1]
                if answer == "quit":
                        self.session.nav.playService(self.lastservice)
                        self.close()
+               elif answer == "movielist":
+                       ref = self.session.nav.getCurrentlyPlayingServiceReference()
+                       self.returning = True
+                       self.session.openWithCallback(self.movieSelected, MovieSelection, ref)
+                       self.session.nav.playService(self.lastservice)
                elif answer == "restart":
                        self.doSeek(0)
 
+       def doEofInternal(self, playing):
+               if not playing:
+                       return
+               self.is_closing = True
+               if config.usage.on_movie_eof.value == "ask":
+                       list = []
+                       list.append((_("Yes"), "quit"))
+                       if config.usage.setup_level.index >= 2: # expert+
+                               list.append((_("Yes, returning to movie list"), "movielist"))
+                       list.append((_("No"), "continue"))
+                       if config.usage.setup_level.index >= 2: # expert+
+                               list.append((_("No, but restart from begin"), "restart"))
+                       self.session.openWithCallback(self.leavePlayerConfirmed, ChoiceBox, title=_("Stop playing this movie?"), list = list)
+               else:
+                       self.leavePlayerConfirmed([True, config.usage.on_movie_eof.value])
+
        def showMovies(self):
                ref = self.session.nav.getCurrentlyPlayingServiceReference()
                self.session.openWithCallback(self.movieSelected, MovieSelection, ref)
 
        def movieSelected(self, service):
                if service is not None:
+                       self.is_closing = False
                        self.session.nav.playService(service)
+                       self.returning = False
+               elif self.returning:
+                       self.close()
index b62a466..3bc8a41 100644 (file)
@@ -604,29 +604,8 @@ class InfoBarServiceName:
 class InfoBarSeek:
        """handles actions like seeking, pause"""
 
-       # ispause, isff, issm
        SEEK_STATE_PLAY = (0, 0, 0, ">")
        SEEK_STATE_PAUSE = (1, 0, 0, "||")
-       SEEK_STATE_FF_2X = (0, 2, 0, ">> 2x")
-       SEEK_STATE_FF_4X = (0, 4, 0, ">> 4x")
-       SEEK_STATE_FF_8X = (0, 8, 0, ">> 8x")
-       SEEK_STATE_FF_16X = (0, 16, 0, ">> 16x")
-       SEEK_STATE_FF_32X = (0, 32, 0, ">> 32x")
-       SEEK_STATE_FF_48X = (0, 48, 0, ">> 48x")
-       SEEK_STATE_FF_64X = (0, 64, 0, ">> 64x")
-       SEEK_STATE_FF_128X = (0, 128, 0, ">> 128x")
-
-       SEEK_STATE_BACK_8X = (0, -8, 0, "<< 8x")
-       SEEK_STATE_BACK_16X = (0, -16, 0, "<< 16x")
-       SEEK_STATE_BACK_32X = (0, -32, 0, "<< 32x")
-       SEEK_STATE_BACK_48X = (0, -48, 0, "<< 48x")
-       SEEK_STATE_BACK_64X = (0, -64, 0, "<< 64x")
-       SEEK_STATE_BACK_128X = (0, -128, 0, "<< 128x")
-
-       SEEK_STATE_SM_HALF = (0, 0, 2, "/2")
-       SEEK_STATE_SM_QUARTER = (0, 0, 4, "/4")
-       SEEK_STATE_SM_EIGHTH = (0, 0, 8, "/8")
-
        SEEK_STATE_EOF = (1, 0, 0, "END")
 
        def __init__(self, actionmap = "InfobarSeekActions"):
@@ -638,6 +617,13 @@ class InfoBarSeek:
                                iPlayableService.evEOF: self.__evEOF,
                                iPlayableService.evSOF: self.__evSOF,
                        })
+               self.eofState = 0
+               self.eofTimer = eTimer()
+               self.eofTimer.timeout.get().append(self.doEof)
+               self.eofInhibitTimer = eTimer()
+               self.eofInhibitTimer.timeout.get().append(self.inhibitEof)
+
+               self.minSpeedBackward = 16
 
                class InfoBarSeekActionMap(HelpableActionMap):
                        def __init__(self, screen, *args, **kwargs):
@@ -648,10 +634,15 @@ class InfoBarSeek:
                                print "action:", action
                                if action[:5] == "seek:":
                                        time = int(action[5:])
-                                       self.screen.seekRelative(time * 90000)
-                                       if config.usage.show_infobar_on_skip.value:
-                                               self.screen.showAfterSeek()
+                                       self.screen.doSeekRelative(time * 90000)
                                        return 1
+                               elif action[:8] == "seekdef:":
+                                       key = int(action[8:])
+                                       time = [-config.seek.selfdefined_13.value, False, config.seek.selfdefined_13.value,
+                                               -config.seek.selfdefined_46.value, False, config.seek.selfdefined_46.value,
+                                               -config.seek.selfdefined_79.value, False, config.seek.selfdefined_79.value][key-1]
+                                       self.screen.doSeekRelative(time * 90000)
+                                       return 1                                        
                                else:
                                        return HelpableActionMap.action(self, contexts, action)
 
@@ -664,18 +655,14 @@ class InfoBarSeek:
                                "seekFwd": (self.seekFwd, _("skip forward")),
                                "seekFwdManual": (self.seekFwdManual, _("skip forward (enter time)")),
                                "seekBack": (self.seekBack, _("skip backward")),
-                               "seekBackManual": (self.seekBackManual, _("skip backward (enter time)")),
-
-                               "seekFwdDef": (self.seekFwdDef, _("skip forward (self defined)")),
-                               "seekBackDef": (self.seekBackDef, _("skip backward (self defined)"))
+                               "seekBackManual": (self.seekBackManual, _("skip backward (enter time)"))
                        }, prio=-1)
                        # give them a little more priority to win over color buttons
 
                self["SeekActions"].setEnabled(False)
 
                self.seekstate = self.SEEK_STATE_PLAY
-
-               self.seek_flag = True
+               self.lastseekstate = self.SEEK_STATE_PLAY
 
                self.onPlayStateChanged = [ ]
 
@@ -683,6 +670,53 @@ class InfoBarSeek:
 
                self.__seekableStatusChanged()
 
+       def makeStateForward(self, n):
+               minspeed = config.seek.stepwise_minspeed.value
+               repeat = int(config.seek.stepwise_repeat.value)
+               if minspeed != "Never" and n >= int(minspeed) and repeat > 1:
+                       return (0, n * repeat, repeat, ">> %dx" % n)
+               else:
+                       return (0, n, 0, ">> %dx" % n)
+
+       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)
+               elif minspeed != "Never" and n >= int(minspeed) and repeat > 1:
+                       return (0, -n * repeat, repeat, "<< %dx" % n)
+               else:
+                       return (0, -n, 0, "<< %dx" % n)
+
+       def makeStateSlowMotion(self, n):
+               return (0, 0, n, "/%d" % n)
+
+       def isStateForward(self, state):
+               return state[1] > 1
+
+       def isStateBackward(self, state):
+               return state[1] < 0
+
+       def isStateSlowMotion(self, state):
+               return state[1] == 0 and state[2] > 1
+
+       def getHigher(self, n, lst):
+               for x in lst:
+                       if x > n:
+                               return x
+               return False
+
+       def getLower(self, n, lst):
+               lst = lst+[]
+               lst.reverse()
+               for x in lst:
+                       if x < n:
+                               return x
+               return False
+
        def showAfterSeek(self):
                if isinstance(self, InfoBarShowHide):
                        self.doShow()
@@ -723,6 +757,9 @@ class InfoBarSeek:
        def __serviceStarted(self):
                self.seekstate = self.SEEK_STATE_PLAY
                self.__seekableStatusChanged()
+               if self.eofState != 0:
+                       self.eofTimer.stop()
+               self.eofState = 0
 
        def setSeekState(self, state):
                service = self.session.nav.getCurrentService()
@@ -762,14 +799,16 @@ class InfoBarSeek:
 
        def pauseService(self):
                if self.seekstate == self.SEEK_STATE_PAUSE:
-                       print "pause, but in fact unpause"
-                       self.unPauseService()
+                       if config.seek.on_pause.value == "play":
+                               self.unPauseService()
+                       elif config.seek.on_pause.value == "step":
+                               self.doSeekRelative(0)
+                       elif config.seek.on_pause.value == "last":
+                               self.setSeekState(self.lastseekstate)
+                               self.lastseekstate = self.SEEK_STATE_PLAY
                else:
-                       if self.seekstate == self.SEEK_STATE_PLAY:
-                               print "yes, playing."
-                       else:
-                               print "no", self.seekstate
-                       print "pause"
+                       if self.seekstate != self.SEEK_STATE_EOF:
+                               self.lastseekstate = self.seekstate
                        self.setSeekState(self.SEEK_STATE_PAUSE);
 
        def unPauseService(self):
@@ -778,105 +817,112 @@ class InfoBarSeek:
                        return 0
                self.setSeekState(self.SEEK_STATE_PLAY)
 
-       def doSeek(self, seektime):
-               print "doseek", seektime
-               service = self.session.nav.getCurrentService()
-               if service is None:
+       def doSeek(self, pts):
+               seekable = self.getSeek()
+               if seekable is None:
                        return
+               prevstate = self.seekstate
+               if self.eofState == 1:
+                       self.eofState = 2
+                       self.inhibitEof()
+               if self.seekstate == self.SEEK_STATE_EOF:
+                       if prevstate == self.SEEK_STATE_PAUSE:
+                               self.setSeekState(self.SEEK_STATE_PAUSE)
+                       else:
+                               self.setSeekState(self.SEEK_STATE_PLAY)
+               self.eofInhibitTimer.start(200, True)
+               seekable.seekTo(pts)
 
+       def doSeekRelative(self, pts):
                seekable = self.getSeek()
                if seekable is None:
                        return
-
-               seekable.seekTo(90 * seektime)
+               prevstate = self.seekstate
+               if self.eofState == 1:
+                       self.eofState = 2
+                       self.inhibitEof()
+               if self.seekstate == self.SEEK_STATE_EOF:
+                       if prevstate == self.SEEK_STATE_PAUSE:
+                               self.setSeekState(self.SEEK_STATE_PAUSE)
+                       else:
+                               self.setSeekState(self.SEEK_STATE_PLAY)
+               self.eofInhibitTimer.start(200, True)
+               seekable.seekRelative(pts<0 and -1 or 1, abs(pts))
+               if abs(pts) > 100 and config.usage.show_infobar_on_skip.value:
+                       self.showAfterSeek()
 
        def seekFwd(self):
-               lookup = {
-                               self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
-                               self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
-                               self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
-                               self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
-                               self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_16X,
-                               self.SEEK_STATE_FF_16X: self.SEEK_STATE_FF_32X,
-                               self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_48X,
-                               self.SEEK_STATE_FF_48X: self.SEEK_STATE_FF_64X,
-                               self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
-                               self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
-                               self.SEEK_STATE_BACK_8X: self.SEEK_STATE_PLAY,
-                               self.SEEK_STATE_BACK_16X: self.SEEK_STATE_BACK_8X,
-                               self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_16X,
-                               self.SEEK_STATE_BACK_48X: self.SEEK_STATE_BACK_32X,
-                               self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_48X,
-                               self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
-                               self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
-                               self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
-                               self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER,
-                               self.SEEK_STATE_EOF: self.SEEK_STATE_EOF,
-                       }
-               self.setSeekState(lookup[self.seekstate])
+               if self.seekstate == self.SEEK_STATE_PLAY:
+                       self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
+               elif self.seekstate == self.SEEK_STATE_PAUSE:
+                       if config.seek.speeds_slowmotion:
+                               self.setSeekState(self.makeStateSlowMotion(config.seek.speeds_slowmotion.value[-1]))
+                       else:
+                               self.setSeekState(self.makeStateForward(int(config.seek.enter_forward.value)))
+               elif self.seekstate == self.SEEK_STATE_EOF:
+                       pass
+               elif self.isStateForward(self.seekstate):
+                       speed = self.seekstate[1]
+                       if self.seekstate[2]:
+                               speed /= self.seekstate[2]
+                       speed = self.getHigher(speed, config.seek.speeds_forward.value) or config.seek.speeds_forward.value[-1]
+                       self.setSeekState(self.makeStateForward(speed))
+               elif self.isStateBackward(self.seekstate):
+                       speed = -self.seekstate[1]
+                       if self.seekstate[2]:
+                               speed /= self.seekstate[2]
+                       speed = self.getLower(speed, config.seek.speeds_backward.value)
+                       if speed:
+                               self.setSeekState(self.makeStateBackward(speed))
+                       else:
+                               self.setSeekState(self.SEEK_STATE_PLAY)
+               elif self.isStateSlowMotion(self.seekstate):
+                       speed = self.getLower(self.seekstate[2], config.seek.speeds_slowmotion.value) or config.seek.speeds_slowmotion.value[0]
+                       self.setSeekState(self.makeStateSlowMotion(speed))
 
        def seekBack(self):
-               lookup = {
-                               self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_8X,
-                               self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
-                               self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
-                               self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
-                               self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
-                               self.SEEK_STATE_FF_16X: self.SEEK_STATE_FF_8X,
-                               self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_16X,
-                               self.SEEK_STATE_FF_48X: self.SEEK_STATE_FF_32X,
-                               self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_48X,
-                               self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
-                               self.SEEK_STATE_BACK_8X: self.SEEK_STATE_BACK_16X,
-                               self.SEEK_STATE_BACK_16X: self.SEEK_STATE_BACK_32X,
-                               self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_48X,
-                               self.SEEK_STATE_BACK_48X: self.SEEK_STATE_BACK_64X,
-                               self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
-                               self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
-                               self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
-                               self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
-                               self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE,
-                               self.SEEK_STATE_EOF: self.SEEK_STATE_BACK_8X,
-                       }
-               self.setSeekState(lookup[self.seekstate])
-
-               if self.seekstate == self.SEEK_STATE_PAUSE:
-                       seekable = self.getSeek()
-                       if seekable is not None:
-                               seekable.seekRelative(-1, 3)
-
-       def seekFwdDef(self):
-               self.seek_flag = False
-               seconds = config.usage.self_defined_seek.value
-               print "Seek", seconds, "seconds self defined forward"
-               seekable = self.getSeek()
-               if seekable is not None:
-                       seekable.seekRelative(1, seconds * 90000)
-
-       def seekBackDef(self):
-               self.seek_flag = False
-               seconds = config.usage.self_defined_seek.value
-               print "Seek", seconds, "seconds self defined backward"
-               seekable = self.getSeek()
-               if seekable is not None:
-                       seekable.seekRelative(1, 0 - seconds * 90000)
+               if self.seekstate == self.SEEK_STATE_PLAY:
+                       self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
+               elif self.seekstate == self.SEEK_STATE_EOF:
+                       self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
+                       self.doSeekRelative(-6)
+               elif self.seekstate == self.SEEK_STATE_PAUSE:
+                       self.doSeekRelative(-3)
+               elif self.isStateForward(self.seekstate):
+                       speed = self.seekstate[1]
+                       if self.seekstate[2]:
+                               speed /= self.seekstate[2]
+                       speed = self.getLower(speed, config.seek.speeds_forward.value)
+                       if speed:
+                               self.setSeekState(self.makeStateForward(speed))
+                       else:
+                               self.setSeekState(self.SEEK_STATE_PLAY)
+               elif self.isStateBackward(self.seekstate):
+                       speed = -self.seekstate[1]
+                       if self.seekstate[2]:
+                               speed /= self.seekstate[2]
+                       speed = self.getHigher(speed, config.seek.speeds_backward.value) or config.seek.speeds_backward.value[-1]
+                       self.setSeekState(self.makeStateBackward(speed))
+               elif self.isStateSlowMotion(self.seekstate):
+                       speed = self.getHigher(self.seekstate[2], config.seek.speeds_slowmotion.value)
+                       if speed:
+                               self.setSeekState(self.makeStateSlowMotion(speed))
+                       else:
+                               self.setSeekState(self.SEEK_STATE_PAUSE)
 
        def seekFwdManual(self):
                self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
 
        def fwdSeekTo(self, minutes):
                print "Seek", minutes, "minutes forward"
-               if minutes != 0:
-                       seekable = self.getSeek()
-                       if seekable is not None:
-                               seekable.seekRelative(1, minutes * 60 * 90000)
+               self.doSeekRelative(minutes * 60 * 90000)
 
        def seekBackManual(self):
                self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
 
        def rwdSeekTo(self, minutes):
                print "rwdSeekTo"
-               self.fwdSeekTo(0 - minutes)
+               self.doSeekRelative(-minutes * 60 * 90000)
 
        def checkSkipShowHideLock(self):
                wantlock = self.seekstate != self.SEEK_STATE_PLAY
@@ -890,50 +936,75 @@ class InfoBarSeek:
                                self.lockShow()
                                self.lockedBecauseOfSkipping = True
 
+       def calcRemainingTime(self):
+               seekable = self.getSeek()
+               if seekable is not None:
+                       len = seekable.getLength()
+                       try:
+                               tmp = self.cueGetEndCutPosition()
+                               if tmp:
+                                       len = [False, tmp]
+                       except:
+                               pass
+                       pos = seekable.getPlayPosition()
+                       speednom = self.seekstate[1] or 1
+                       speedden = self.seekstate[2] or 1
+                       if not len[0] and not pos[0]:
+                               if len[1] <= pos[1]:
+                                       return 0
+                               time = (len[1] - pos[1])*speedden/(90*speednom)
+                               return time
+               return False
+               
        def __evEOF(self):
+               if self.eofState == 0 and self.seekstate != self.SEEK_STATE_EOF:
+                       self.eofState = 1
+                       time = self.calcRemainingTime()
+                       if not time:
+                               time = 3000   # Failed to calc, use default
+                       elif time == 0:
+                               time = 300    # Passed end, shortest wait
+                       elif time > 15000:
+                               self.eofState = -2  # Too long, block eof
+                               time = 15000
+                       else:
+                               time += 1000  # Add margin
+                       self.eofTimer.start(time, True)
+
+       def inhibitEof(self):
+               if self.eofState >= 1:
+                       self.eofState = -self.eofState
+                       self.eofTimer.stop()
+                       self.doEof()
+
+       def doEof(self):
                if self.seekstate == self.SEEK_STATE_EOF:
                        return
-               if self.seekstate[1] < 0: # SEEK_STATE_BACK_*X
-                       print "end of stream while seeking back, ignoring."
+               if self.eofState == -2 or self.isStateBackward(self.seekstate):
+                       self.eofState = 0
                        return
 
                # if we are seeking, we try to end up ~1s before the end, and pause there.
-               if not self.seekstate in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
+               eofstate = self.eofState
+               seekstate = self.seekstate
+               self.eofState = 0
+               if not self.seekstate == self.SEEK_STATE_PAUSE:
                        self.setSeekState(self.SEEK_STATE_EOF)
-                       self.seekRelativeToEnd(-90000)
-               else:
-                       self.setSeekState(self.SEEK_STATE_EOF)
-
-       def __evSOF(self):
-               self.setSeekState(self.SEEK_STATE_PLAY)
-               self.doSeek(0)
-
-       def seekRelative(self, diff):
-               if self.seek_flag == True:
+               if eofstate == -1 or not seekstate in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
                        seekable = self.getSeek()
                        if seekable is not None:
-                               print "seekRelative: res:", seekable.seekRelative(1, diff)
-                       else:
-                               print "seek failed!"
+                               seekable.seekTo(-1)
+               if eofstate == 1 and seekstate == self.SEEK_STATE_PLAY:
+                       self.doEofInternal(True)
                else:
-                       self.seek_flag = True
-
-       def seekRelativeToEnd(self, diff):
-               assert diff <= 0, "diff is expected to be negative!"
-
-               # might sound like an evil hack, but:
-               # if we seekRelativeToEnd(0), we expect to be at the end, which is what we want,
-               # and we don't get that by passing 0 here (it would seek to begin).
-               if diff == 0:
-                       diff = -1
+                       self.doEofInternal(False)
 
-               # relative-to-end seeking is implemented as absolutes seeks with negative time
-               self.seekAbsolute(diff)
+       def doEofInternal(self, playing):
+               pass            # Defined in subclasses
 
-       def seekAbsolute(self, abs):
-               seekable = self.getSeek()
-               if seekable is not None:
-                       seekable.seekTo(abs)
+       def __evSOF(self):
+               self.setSeekState(self.SEEK_STATE_PLAY)
+               self.doSeek(0)
 
 from Screens.PVRState import PVRState, TimeshiftState
 
@@ -1095,13 +1166,15 @@ class InfoBarTimeshift:
                        print "play, ..."
                        ts.activateTimeshift() # activate timeshift will automatically pause
                        self.setSeekState(self.SEEK_STATE_PAUSE)
-                       self.seekRelativeToEnd(-90000) # seek approx. 1 sec before end
 
                if back:
+                       self.doSeek(-5) # seek some gops before end
                        self.ts_rewind_timer.start(200, 1)
+               else:
+                       self.doSeek(-1) # seek 1 gop before end
 
        def rewindService(self):
-               self.setSeekState(self.SEEK_STATE_BACK_16X)
+               self.setSeekState(self.makeStateBackward(int(config.seek.enter_backward.value)))
 
        # same as activateTimeshiftEnd, but pauses afterwards.
        def activateTimeshiftEndAndPause(self):
@@ -1807,13 +1880,14 @@ class InfoBarCueSheetSupport:
 
                        if last is not None:
                                self.resume_point = last
-                               Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
+                               if config.usage.on_movie_start.value == "ask":
+                                       Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
+                               elif config.usage.on_movie_start.value == "resume":
+                                       Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Resuming playback"), timeout=2, type=MessageBox.TYPE_INFO)
 
        def playLastCB(self, answer):
                if answer == True:
-                       seekable = self.__getSeekable()
-                       if seekable is not None:
-                               seekable.seekTo(self.resume_point)
+                       self.doSeek(self.resume_point)
                self.hideAfterResume()
 
        def hideAfterResume(self):
@@ -1835,37 +1909,64 @@ class InfoBarCueSheetSupport:
                        return None
                return long(r[1])
 
-       def jumpPreviousNextMark(self, cmp, alternative=None):
+       def cueGetEndCutPosition(self):
+               ret = False
+               isin = True
+               for cp in self.cut_list:
+                       if cp[1] == self.CUT_TYPE_OUT:
+                               if isin:
+                                       isin = False
+                                       ret = cp[0]
+                       elif cp[1] == self.CUT_TYPE_IN:
+                               isin = True
+               return ret
+               
+       def jumpPreviousNextMark(self, cmp, start=False):
                current_pos = self.cueGetCurrentPosition()
                if current_pos is None:
-                       return
-               mark = self.getNearestCutPoint(current_pos, cmp=cmp)
+                       return False
+               mark = self.getNearestCutPoint(current_pos, cmp=cmp, start=start)
                if mark is not None:
                        pts = mark[0]
-               elif alternative is not None:
-                       pts = alternative
                else:
-                       return
+                       return False
 
-               seekable = self.__getSeekable()
-               if seekable is not None:
-                       seekable.seekTo(pts)
+               self.doSeek(pts)
+               return True
 
        def jumpPreviousMark(self):
                # we add 2 seconds, so if the play position is <2s after
                # the mark, the mark before will be used
-               self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
+               self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True)
 
        def jumpNextMark(self):
-               self.jumpPreviousNextMark(lambda x: x)
+               if not self.jumpPreviousNextMark(lambda x: x):
+                       self.doSeek(-1)
 
-       def getNearestCutPoint(self, pts, cmp=abs):
+       def getNearestCutPoint(self, pts, cmp=abs, start=False):
                # can be optimized
+               beforecut = False
                nearest = None
+               if start:
+                       beforecut = True
+                       bestdiff = cmp(0 - pts)
+                       if bestdiff >= 0:
+                               nearest = [0, False]
                for cp in self.cut_list:
-                       diff = cmp(cp[0] - pts)
-                       if cp[1] == self.CUT_TYPE_MARK and diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
-                               nearest = cp
+                       if beforecut and cp[1] in [self.CUT_TYPE_IN, self.CUT_TYPE_OUT]:
+                               beforecut = False
+                               if cp[1] == self.CUT_TYPE_IN:  # Start is here, disregard previous marks
+                                       diff = cmp(cp[0] - pts)
+                                       if diff >= 0:
+                                               nearest = cp
+                                               bestdiff = diff
+                                       else:
+                                               nearest = None
+                       if cp[1] in [self.CUT_TYPE_MARK, self.CUT_TYPE_LAST]:
+                               diff = cmp(cp[0] - pts)
+                               if diff >= 0 and (nearest is None or bestdiff > diff):
+                                       nearest = cp
+                                       bestdiff = diff
                return nearest
 
        def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):