Repository navigation
Initial implentation on jumpback on seek - #5
bossanova808 wants to merge 1 commit into
Conversation
📝 WalkthroughWalkthroughThis pull request adds a new seek-based jumpback feature to the unpause jumpback addon. It includes translation strings for the feature, a new settings configuration for jumpback seconds after manual seeks, and implements seek event handling with state tracking to prevent loops and throttle rapid seeks. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Solves #4 |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@resources/lib/unpause_jumpback.py`:
- Around line 354-424: The onPlayBackSeek handler improperly uses an f-string
with no placeholders, contains a redundant self.performing_addon_seek reset in
the outer except, and doesn't distinguish small step forwards/backwards from
user scrubs; to fix, change the literal log call in onPlayBackSeek (remove the
unnecessary f-prefix) where the message has no interpolations, remove the
redundant self.performing_addon_seek = False in the outer except (the inner
finally already resets it), and add a small filter at the top of onPlayBackSeek
using the incoming seek_offset parameter (which is in milliseconds) to ignore
tiny step operations (e.g. if abs(seek_offset) < YOUR_STEP_THRESHOLD_MS: log and
return) or add a clarifying comment if steps should be handled—refer to the
onPlayBackSeek function, the self.performing_addon_seek flag,
self.last_seek_time, and the seek_offset parameter to locate and apply these
changes.
🧹 Nitpick comments (1)
resources/lib/unpause_jumpback.py (1)
162-165: The log messages don't mention the new seek jumpback setting.When settings are loaded, the log only reports the pause-based jumpback mode. Consider also logging
jump_back_secs_after_seekso operators can confirm the seek jumpback is active without digging into Kodi's settings UI.Suggestion
if self.jump_back_on_resume: Logger.info(f'Settings loaded, jump back mode is: On Resume, with a jump back of {self.jump_back_secs_after_pause} seconds') else: Logger.info(f'Settings loaded, jump back mode is: On Pause, with a jump back of {self.jump_back_secs_after_pause} seconds') + + if self.jump_back_secs_after_seek > 0: + Logger.info(f'Seek jumpback enabled: {self.jump_back_secs_after_seek} seconds after manual seek')
| def onPlayBackSeek(self, seek_time, seek_offset): | ||
| """ | ||
| Handle manual seek operations. | ||
|
|
||
| Called when the user manually seeks to a different position in the video. | ||
| Performs a jumpback after the seek to help the user catch context, but | ||
| only for user-initiated seeks to prevent infinite loops. | ||
|
|
||
| The addon sets a flag (performing_addon_seek) before performing its own | ||
| seeks to distinguish them from user-initiated seeks. | ||
|
|
||
| Args: | ||
| seek_time (int): The time in milliseconds to which playback was seeked | ||
| seek_offset (int): The offset in milliseconds from the current position | ||
| """ | ||
| # Ignore seeks performed by this addon itself to prevent loops | ||
| if self.performing_addon_seek: | ||
| Logger.info(f'onPlayBackSeek: Ignoring addon-initiated seek to {seek_time}ms') | ||
| return | ||
|
|
||
| Logger.info(f'onPlayBackSeek: User seek detected - time: {seek_time}ms, offset: {seek_offset}ms') | ||
|
|
||
| # Only proceed if seek jumpback is configured | ||
| if self.jump_back_secs_after_seek == 0: | ||
| Logger.debug('Seek jumpback disabled (set to 0 seconds)') | ||
| return | ||
|
|
||
| try: | ||
| _filename = self.getPlayingFile() | ||
| except RuntimeError: | ||
| Logger.info('No file is playing, could not getPlayingFile(), stopping seek jumpback') | ||
| return | ||
|
|
||
| if self.is_excluded(_filename): | ||
| Logger.info(f"Seek jumpback ignored because '{_filename}' is in exclusion settings.") | ||
| return | ||
|
|
||
| try: | ||
| current_time = self.getTime() | ||
|
|
||
| # Don't jump back if we're near the beginning | ||
| if current_time <= self.jump_back_secs_after_seek: | ||
| Logger.info(f'Current time {current_time}s too close to start, skipping seek jumpback') | ||
| return | ||
|
|
||
| # Throttle: ignore rapid successive seeks (within 1 second) | ||
| current_timestamp = time.time() | ||
| if self.last_seek_time > 0 and (current_timestamp - self.last_seek_time) < 1.0: | ||
| Logger.info(f'Ignoring rapid seek (within 1 second of last seek)') | ||
| self.last_seek_time = current_timestamp | ||
| return | ||
|
|
||
| self.last_seek_time = current_timestamp | ||
|
|
||
| # Perform the jumpback | ||
| resume_time = current_time - self.jump_back_secs_after_seek | ||
| resume_time = max(0, resume_time) # Don't go below 0 | ||
|
|
||
| Logger.info(f'Performing seek jumpback: {self.jump_back_secs_after_seek}s back to {resume_time}s') | ||
|
|
||
| # Set flag before seeking to prevent loop | ||
| self.performing_addon_seek = True | ||
| try: | ||
| self.seekTime(resume_time) | ||
| finally: | ||
| # Always reset the flag, even if seek fails | ||
| self.performing_addon_seek = False | ||
|
|
||
| except RuntimeError as e: | ||
| Logger.error(f'Error during seek jumpback: {e}') | ||
| self.performing_addon_seek = False |
There was a problem hiding this comment.
Several small issues in onPlayBackSeek.
-
Line 402 — f-string without placeholders (flagged by Ruff F541): Remove the
fprefix since there are no interpolated expressions. -
Line 424 — Redundant
performing_addon_seek = False: The innerfinallyblock (line 418–420) already guarantees the flag is reset regardless of whetherseekTimeraises. The outerexceptreset on line 424 is dead code. -
Step forward/back also triggers this callback: Based on learnings,
onPlayBackSeekfires for step operations too (not just scrubbing). This means a user pressing "step forward 30s" will immediately get jumped back. If that's unintended, you could filter byseek_offsetmagnitude or direction. If intentional, a brief comment would help future readers.
Proposed diff for items 1 & 2
# Throttle: ignore rapid successive seeks (within 1 second)
current_timestamp = time.time()
if self.last_seek_time > 0 and (current_timestamp - self.last_seek_time) < 1.0:
- Logger.info(f'Ignoring rapid seek (within 1 second of last seek)')
+ Logger.info('Ignoring rapid seek (within 1 second of last seek)')
self.last_seek_time = current_timestamp
return except RuntimeError as e:
Logger.error(f'Error during seek jumpback: {e}')
- self.performing_addon_seek = False,
Based on learnings: "In Kodi, the onPlayBackSeek callback does fire for step forward/back operations (not just scrubbing/seeking via progress bar), with the seekOffset parameter indicating the direction and magnitude of the step."
🧰 Tools
🪛 Ruff (0.15.0)
[error] 402-402: f-string without any placeholders
Remove extraneous f prefix
(F541)
🤖 Prompt for AI Agents
In `@resources/lib/unpause_jumpback.py` around lines 354 - 424, The onPlayBackSeek
handler improperly uses an f-string with no placeholders, contains a redundant
self.performing_addon_seek reset in the outer except, and doesn't distinguish
small step forwards/backwards from user scrubs; to fix, change the literal log
call in onPlayBackSeek (remove the unnecessary f-prefix) where the message has
no interpolations, remove the redundant self.performing_addon_seek = False in
the outer except (the inner finally already resets it), and add a small filter
at the top of onPlayBackSeek using the incoming seek_offset parameter (which is
in milliseconds) to ignore tiny step operations (e.g. if abs(seek_offset) <
YOUR_STEP_THRESHOLD_MS: log and return) or add a clarifying comment if steps
should be handled—refer to the onPlayBackSeek function, the
self.performing_addon_seek flag, self.last_seek_time, and the seek_offset
parameter to locate and apply these changes.
|
@coderabbitai currently there are two options for the jumpback on pause - either at pause, or at resume. I'd like to also offer a third option, that does the jumpback after a configurable delay (still doing the jumpback at resume if the delay has not been reached when the user resumes). Can you help with this. Also - a reminder, NO DIFFS!! Only before/after blocks |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain
🏁 Script executed: Length of output: 22363 Implementation Plan: Third Jumpback Mode with Delayed Timer and Resume FallbackI'll implement a third mode that starts a timer when paused, performs the jumpback after the delay expires (while still paused), but if the user resumes before the delay expires, it performs the jumpback at resume instead. Changes needed:
🧠 Learnings used💡 Use |
|
@coderabbitai please generate those changes |
|
Tip For best results, initiate chat on the files or code changes.
Task: Implement Third Jumpback Mode with Delayed Timer and Resume FallbackThis will add a third mode that starts a timer when paused, performs the jumpback after the delay expires (while still paused), but if the user resumes before the delay expires, it performs the jumpback at resume instead. Changes overview:
🧠 Learnings used💡 Use |
|
@coderabbitai you just re-presented the plan, not the actual code changes. Please generate the requited code changed and present them here as before/after blocks. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain
🏁 Script executed: Length of output: 19551
Change 1: Update strings.poAdd new string labels for the jumpback mode selector. File: BEFORE (lines 14-20): msgctxt "#32000"
msgid "Jump back on unpause"
msgstr ""
msgctxt "#32001"
msgid "Jump back how many seconds?"
msgstr ""AFTER: msgctxt "#32000"
msgid "Jump back on unpause"
msgstr ""
msgctxt "#32001"
msgid "Jumpback Mode"
msgstr ""
msgctxt "#32002"
msgid "On Resume (default)"
msgstr ""
msgctxt "#32003"
msgid "During Pause (low-power systems)"
msgstr ""
msgctxt "#32004"
msgid "Delayed with Resume Fallback"
msgstr ""
msgctxt "#32005"
msgid "Jump back how many seconds?"
msgstr ""Note: The old string 32001 "Jump back how many seconds?" becomes 32005, and we insert the new mode-related strings at 32001-32004. Change 2: Update settings.xmlReplace the boolean File: BEFORE (lines 7-29): <group id="1">
<setting help="" id="jumpbacksecs" label="32001" type="integer">
<level>0</level>
<default>4</default>
<constraints>
<minimum>0</minimum>
<maximum>60</maximum>
</constraints>
<control format="integer" type="slider">
<popup>false</popup>
</control>
</setting>AFTER: <group id="1">
<setting help="" id="jumpbacksecs" label="32005" type="integer">
<level>0</level>
<default>4</default>
<constraints>
<minimum>0</minimum>
<maximum>60</maximum>
</constraints>
<control format="integer" type="slider">
<popup>false</popup>
</control>
</setting>BEFORE (lines 51-57 in group 3): <group id="3" label="32042">
<setting help="" id="jumpbackonresume" label="32040" type="boolean">
<level>0</level>
<default>true</default>
<control type="toggle"/>
</setting>
</group>AFTER: <group id="3" label="32042">
<setting help="" id="jumpbackmode" label="32001" type="integer">
<level>0</level>
<default>0</default>
<constraints>
<options>
<option label="32002">0</option>
<option label="32003">1</option>
<option label="32004">2</option>
</options>
</constraints>
<control type="spinner"/>
</setting>
</group>Change 3: Update unpause_jumpback.py - init() methodReplace File: BEFORE (lines 118-125): # Jumpback behavior settings
self.jump_back_on_resume = False
self.jump_back_on_playback_started = False
self.paused_time = 0
self.jump_back_secs_after_pause = 0
self.last_playback_speed = 0
self.wait_for_jumpback = 0AFTER: # Jumpback behavior settings
self.jumpback_mode = 0 # 0=on resume, 1=during pause, 2=delayed with fallback
self.jump_back_on_playback_started = False
self.paused_time = 0
self.jump_back_secs_after_pause = 0
self.jumpback_performed_during_pause = False # Track if jumpback already done during pause
self.last_playback_speed = 0
self.wait_for_jumpback = 0Change 4: Update unpause_jumpback.py - load_settings() methodUpdate to load the new mode setting and log it appropriately. File: BEFORE (lines 175-200): def load_settings(self):
"""
Load and cache settings from Kodi's add-on settings.
Retrieves all jumpback configuration options, exclusion settings,
and timing parameters from the add-on settings and caches them
for performance during playback events.
Logs the current jumpback mode after loading settings.
"""
self.jump_back_on_resume = get_setting_as_bool('jumpbackonresume')
self.jump_back_on_playback_started = get_setting_as_bool('jumpbackonplaybackstarted')
self.jump_back_secs_after_pause = _get_int_setting("jumpbacksecs", default=0)
self.jump_back_secs_after_fwd_x2 = _get_int_setting("jumpbacksecsfwdx2", default=0)
self.jump_back_secs_after_fwd_x4 = _get_int_setting("jumpbacksecsfwdx4", default=0)
self.jump_back_secs_after_fwd_x8 = _get_int_setting("jumpbacksecsfwdx8", default=0)
self.jump_back_secs_after_fwd_x16 = _get_int_setting("jumpbacksecsfwdx16", default=0)
self.jump_back_secs_after_fwd_x32 = _get_int_setting("jumpbacksecsfwdx32", default=0)
self.jump_back_secs_after_rwd_x2 = _get_int_setting("jumpbacksecsrwdx2", default=0)
self.jump_back_secs_after_rwd_x4 = _get_int_setting("jumpbacksecsrwdx4", default=0)
self.jump_back_secs_after_rwd_x8 = _get_int_setting("jumpbacksecsrwdx8", default=0)
self.jump_back_secs_after_rwd_x16 = _get_int_setting("jumpbacksecsrwdx16", default=0)
self.jump_back_secs_after_rwd_x32 = _get_int_setting("jumpbacksecsrwdx32", default=0)
self.jump_back_secs_after_seek = _get_int_setting("jumpbacksecsafterseek", default=0)
self.wait_for_jumpback = _get_int_setting("waitforjumpback", default=0)
self.exclude_live_tv = get_setting_as_bool('ExcludeLiveTV')
self.exclude_http = get_setting_as_bool('ExcludeHTTP')
self.excluded_path_1_enabled = get_setting_as_bool('ExcludePathOption')
self.excluded_path_2_enabled = get_setting_as_bool('ExcludePathOption2')
self.excluded_path_3_enabled = get_setting_as_bool('ExcludePathOption3')
self.excluded_path_1 = get_setting('ExcludePath')
self.excluded_path_2 = get_setting('ExcludePath2')
self.excluded_path_3 = get_setting('ExcludePath3')
if self.jump_back_on_resume:
Logger.info(f'Settings loaded, jump back mode is: On Resume, with a jump back of {self.jump_back_secs_after_pause} seconds')
else:
Logger.info(f'Settings loaded, jump back mode is: On Pause, with a jump back of {self.jump_back_secs_after_pause} seconds')AFTER: def load_settings(self):
"""
Load and cache settings from Kodi's add-on settings.
Retrieves all jumpback configuration options, exclusion settings,
and timing parameters from the add-on settings and caches them
for performance during playback events.
Logs the current jumpback mode after loading settings.
"""
self.jumpback_mode = _get_int_setting('jumpbackmode', default=0)
self.jump_back_on_playback_started = get_setting_as_bool('jumpbackonplaybackstarted')
self.jump_back_secs_after_pause = _get_int_setting("jumpbacksecs", default=0)
self.jump_back_secs_after_fwd_x2 = _get_int_setting("jumpbacksecsfwdx2", default=0)
self.jump_back_secs_after_fwd_x4 = _get_int_setting("jumpbacksecsfwdx4", default=0)
self.jump_back_secs_after_fwd_x8 = _get_int_setting("jumpbacksecsfwdx8", default=0)
self.jump_back_secs_after_fwd_x16 = _get_int_setting("jumpbacksecsfwdx16", default=0)
self.jump_back_secs_after_fwd_x32 = _get_int_setting("jumpbacksecsfwdx32", default=0)
self.jump_back_secs_after_rwd_x2 = _get_int_setting("jumpbacksecsrwdx2", default=0)
self.jump_back_secs_after_rwd_x4 = _get_int_setting("jumpbacksecsrwdx4", default=0)
self.jump_back_secs_after_rwd_x8 = _get_int_setting("jumpbacksecsrwdx8", default=0)
self.jump_back_secs_after_rwd_x16 = _get_int_setting("jumpbacksecsrwdx16", default=0)
self.jump_back_secs_after_rwd_x32 = _get_int_setting("jumpbacksecsrwdx32", default=0)
self.jump_back_secs_after_seek = _get_int_setting("jumpbacksecsafterseek", default=0)
self.wait_for_jumpback = _get_int_setting("waitforjumpback", default=0)
self.exclude_live_tv = get_setting_as_bool('ExcludeLiveTV')
self.exclude_http = get_setting_as_bool('ExcludeHTTP')
self.excluded_path_1_enabled = get_setting_as_bool('ExcludePathOption')
self.excluded_path_2_enabled = get_setting_as_bool('ExcludePathOption2')
self.excluded_path_3_enabled = get_setting_as_bool('ExcludePathOption3')
self.excluded_path_1 = get_setting('ExcludePath')
self.excluded_path_2 = get_setting('ExcludePath2')
self.excluded_path_3 = get_setting('ExcludePath3')
mode_names = {
0: "On Resume (default)",
1: "During Pause (low-power systems)",
2: "Delayed with Resume Fallback"
}
mode_name = mode_names.get(self.jumpback_mode, "Unknown")
Logger.info(f'Settings loaded, jump back mode is: {mode_name}, with a jump back of {self.jump_back_secs_after_pause} seconds')Change 5: Update unpause_jumpback.py - onPlayBackPaused() methodHandle all three modes including the new delayed fallback mode. File: BEFORE (entire method): def onPlayBackPaused(self):
"""
Handle playback pause events (alternative jumpback mode for low-power systems).
Called when playback is paused. Records the pause time and optionally
sets up alarm-based jumpback during the pause period.
For low-power systems, the add-on can be configured to perform the jumpback
during the pause period, which prevents jankiness on resume but has the
disadvantage that the paused image also jumps back.
Checks exclusion settings and sets up delayed jumpback via Kodi's AlarmClock
if jump_back_on_resume is disabled.
"""
# Record when the pause was done
self.paused_time = time.time()
Logger.info(f'onPlayBackPaused. Time: {self.paused_time}')
try:
_filename = self.getPlayingFile()
except RuntimeError:
Logger.info('No file is playing, could not getPlayingFile(), stopping UnpauseJumpBack')
xbmc.executebuiltin('CancelAlarm(JumpbackPaused, true)')
return
if self.is_excluded(_filename):
Logger.info(f'Playback paused - ignoring because [{_filename}] is in exclusion settings.')
return
# For low power systems, perform jumpback during pause period
# Prevents janky resume experience but paused image also jumps back
if not self.jump_back_on_resume and self.isPlayingVideo() and 0 < self.jump_back_secs_after_pause < self.getTime():
jump_back_point = self.getTime() - self.jump_back_secs_after_pause
Logger.info(f'Playback paused - jumping back {self.jump_back_secs_after_pause}s to: {int(jump_back_point)} seconds')
xbmc.executebuiltin(
f'AlarmClock(JumpbackPaused, Seek(-{self.jump_back_secs_after_pause}), 00:00:{int(self.wait_for_jumpback):02d}, silent), silent)')AFTER: def onPlayBackPaused(self):
"""
Handle playback pause events.
Called when playback is paused. Records the pause time and handles jumpback
based on the configured mode:
- Mode 0 (On Resume): Just records pause time, jumpback happens on resume
- Mode 1 (During Pause): Sets alarm to perform jumpback during pause period
- Mode 2 (Delayed Fallback): Sets alarm to perform jumpback during pause,
but if user resumes early, jumpback will happen at resume instead
Checks exclusion settings before setting up any jumpback operations.
"""
# Record when the pause was done and reset jumpback tracking
self.paused_time = time.time()
self.jumpback_performed_during_pause = False
Logger.info(f'onPlayBackPaused. Time: {self.paused_time}, Mode: {self.jumpback_mode}')
try:
_filename = self.getPlayingFile()
except RuntimeError:
Logger.info('No file is playing, could not getPlayingFile(), stopping UnpauseJumpBack')
xbmc.executebuiltin('CancelAlarm(JumpbackPaused, true)')
return
if self.is_excluded(_filename):
Logger.info(f'Playback paused - ignoring because [{_filename}] is in exclusion settings.')
return
# Mode 0: On Resume - just record the pause time, jumpback happens on resume
if self.jumpback_mode == 0:
Logger.info('Mode 0 (On Resume): Waiting for resume to perform jumpback')
return
# Mode 1 (During Pause) or Mode 2 (Delayed Fallback): Set alarm for jumpback during pause
if (self.jumpback_mode in [1, 2] and self.isPlayingVideo()
and 0 < self.jump_back_secs_after_pause < self.getTime()):
jump_back_point = self.getTime() - self.jump_back_secs_after_pause
if self.jumpback_mode == 1:
Logger.info(f'Mode 1 (During Pause): Setting alarm to jump back {self.jump_back_secs_after_pause}s '
f'to {int(jump_back_point)}s after {self.wait_for_jumpback}s delay')
else: # Mode 2
Logger.info(f'Mode 2 (Delayed Fallback): Setting alarm to jump back {self.jump_back_secs_after_pause}s '
f'to {int(jump_back_point)}s after {self.wait_for_jumpback}s delay (will fallback to resume if resumed early)')
# Use SetProperty to mark that jumpback was performed during pause
xbmc.executebuiltin(
f'AlarmClock(JumpbackPaused, '
f'Seek(-{self.jump_back_secs_after_pause})||SetProperty(jumpback_done,true,home), '
f'00:00:{int(self.wait_for_jumpback):02d}, silent)')Change 6: Update unpause_jumpback.py - onPlayBackResumed() methodHandle all three modes with proper checking for jumpback already performed during pause. File: BEFORE (entire method): def onPlayBackResumed(self):
"""
Handle playback resume events (default jumpback mode).
Called when playback is resumed after being paused. This is the default
behavior where the pause position remains where the user actually paused,
which is usually the desired behavior.
When jump_back_on_resume is enabled:
- Checks exclusion settings for the current file
- Performs jumpback if conditions are met (sufficient pause time, etc.)
- Resets the paused_time tracking variable
When jump_back_on_resume is disabled:
- Cancels any pending alarm-based jumpback operations
"""
Logger.info(f'onPlayBackResumed with jump_back_on_resume: {self.jump_back_on_resume}')
if self.jump_back_on_resume:
if self.paused_time > 0:
Logger.info(f'Was paused for {int(time.time() - self.paused_time)} seconds.')
# Check for exclusion
try:
_filename = self.getPlayingFile()
except RuntimeError:
Logger.info('No file is playing, could not getPlayingFile(), stopping UnpauseJumpBack')
xbmc.executebuiltin('CancelAlarm(JumpbackPaused, true)')
return
if self.is_excluded(_filename):
Logger.info(f"Ignored because '{_filename}' is in exclusion settings.")
return
else:
# Handle jump back after pause
current_time = self.getTime()
if (self.jump_back_secs_after_pause != 0
and self.isPlayingVideo()
and current_time > self.jump_back_secs_after_pause
and self.paused_time > 0
and (time.time() - self.paused_time) > self.wait_for_jumpback):
resume_time = current_time - self.jump_back_secs_after_pause
self.performing_addon_seek = True
try:
self.seekTime(resume_time)
Logger.info(f'Resumed, with {int(self.jump_back_secs_after_pause)}s jump back')
finally:
self.performing_addon_seek = False
self.paused_time = 0
# If we're not jumping back on resume, cancel any alarm set for manual resume
else:
Logger.info('Cancelling alarm - playback either resumed or stopped by the user.')
xbmc.executebuiltin('CancelAlarm(JumpbackPaused, true)')AFTER: def onPlayBackResumed(self):
"""
Handle playback resume events.
Called when playback is resumed after being paused. Behavior depends on mode:
- Mode 0 (On Resume): Performs jumpback when resuming (default behavior)
- Mode 1 (During Pause): Cancels any pending alarm (jumpback already done or cancelled)
- Mode 2 (Delayed Fallback): Checks if jumpback was done during pause via alarm;
if not (user resumed before delay), performs jumpback now at resume
Checks exclusion settings and validates conditions before performing jumpback.
"""
pause_duration = int(time.time() - self.paused_time) if self.paused_time > 0 else 0
Logger.info(f'onPlayBackResumed. Mode: {self.jumpback_mode}, Pause duration: {pause_duration}s')
# Check if jumpback was performed during pause (by the alarm)
jumpback_done_during_pause = xbmc.getInfoLabel('Window(Home).Property(jumpback_done)') == 'true'
if jumpback_done_during_pause:
Logger.info('Jumpback was already performed during pause by alarm, clearing flag')
xbmc.executebuiltin('ClearProperty(jumpback_done,home)')
self.jumpback_performed_during_pause = True
# Always cancel any pending alarm on resume
xbmc.executebuiltin('CancelAlarm(JumpbackPaused, true)')
# Check for exclusion
try:
_filename = self.getPlayingFile()
except RuntimeError:
Logger.info('No file is playing, could not getPlayingFile(), stopping UnpauseJumpBack')
self.paused_time = 0
return
if self.is_excluded(_filename):
Logger.info(f"Ignored because '{_filename}' is in exclusion settings.")
self.paused_time = 0
return
# Mode 0: On Resume - always perform jumpback at resume
if self.jumpback_mode == 0:
current_time = self.getTime()
if (self.jump_back_secs_after_pause != 0
and self.isPlayingVideo()
and current_time > self.jump_back_secs_after_pause
and self.paused_time > 0
and pause_duration > self.wait_for_jumpback):
resume_time = current_time - self.jump_back_secs_after_pause
self.performing_addon_seek = True
try:
self.seekTime(resume_time)
Logger.info(f'Mode 0 (On Resume): Performed {int(self.jump_back_secs_after_pause)}s jump back to {int(resume_time)}s')
finally:
self.performing_addon_seek = False
# Mode 1: During Pause - jumpback already done or cancelled, just log
elif self.jumpback_mode == 1:
Logger.info('Mode 1 (During Pause): Resume detected, alarm cancelled')
# Mode 2: Delayed Fallback - perform jumpback if it wasn't done during pause
elif self.jumpback_mode == 2:
if self.jumpback_performed_during_pause:
Logger.info('Mode 2 (Delayed Fallback): Jumpback was already done during pause, skipping')
else:
# User resumed before the delay - perform jumpback now
current_time = self.getTime()
if (self.jump_back_secs_after_pause != 0
and self.isPlayingVideo()
and current_time > self.jump_back_secs_after_pause
and self.paused_time > 0):
resume_time = current_time - self.jump_back_secs_after_pause
self.performing_addon_seek = True
try:
self.seekTime(resume_time)
Logger.info(f'Mode 2 (Delayed Fallback): User resumed before delay, '
f'performed {int(self.jump_back_secs_after_pause)}s jump back at resume to {int(resume_time)}s')
finally:
self.performing_addon_seek = False
self.paused_time = 0
self.jumpback_performed_during_pause = FalseThat's all the changes! These implement the third jumpback mode with delayed timer and resume fallback as requested. 🧠 Learnings used |
|
Closing this as the requesting user thinks it won't help - https://www.reddit.com/r/kodi/comments/1qrssmv/comment/o2rm744/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button |
@coderabbitai please review
Summary by CodeRabbit
New Features
Bug Fixes