From 63ac38d3356a8e3029615eda46f23c5ce84145aa Mon Sep 17 00:00:00 2001 From: Simon Date: Sat, 21 Nov 2020 17:25:35 +0100 Subject: [PATCH] Use `onAVStarted` which requires a min version bump (#16) * only show skip dialog if we didn't already go to the next video * more tolerant cp listener when `getTime` raises * use onAVStarted * remove previous hack * prepare for 0.2.3 release * update comment * init should_start to False --- addon.xml | 8 +++-- resources/lib/player_listener.py | 39 ++++++++++++++++------ resources/lib/sponsorblock/api.py | 3 -- resources/lib/utils/checkpoint_listener.py | 14 ++++++-- 4 files changed, 46 insertions(+), 18 deletions(-) diff --git a/addon.xml b/addon.xml index 8aa859c..40839ec 100644 --- a/addon.xml +++ b/addon.xml @@ -1,7 +1,7 @@ - + - + @@ -28,6 +28,10 @@ Users submit when a sponsor happens and the add-on automatically skips sponsors This is an unoffical port of the SponsorBlock browser extension +[0.2.3] +- Bumped Kodi version to 18 (Leia) to use `onAVStarted` which should hopefully fix issues + with segments starting right at the beginning of a video. + [0.2.2] - Fixed "Playback fails with sponsor section at video start" again. Let's hope it worked this time :) (@bclindner, #14) diff --git a/resources/lib/player_listener.py b/resources/lib/player_listener.py index 0648ba2..45a7697 100644 --- a/resources/lib/player_listener.py +++ b/resources/lib/player_listener.py @@ -77,6 +77,10 @@ def __init__(self, *args, **kwargs): self._segments = [] # List[SponsorSegment] self._next_segment = None # type: Optional[SponsorSegment] + # set by `onPlaybackStarted` and then read (/ reset) by `onAVStarted` + self._should_start = False + self._should_start_lock = threading.Lock() + def preload_segments(self, video_id): assert not self._thread_running @@ -107,18 +111,29 @@ def _prepare_segments(self, video_id): return bool(self._segments) def onPlayBackStarted(self): # type: () -> None - video_id = youtube_api.get_video_id() - if not video_id: - return + with self._should_start_lock: + video_id = youtube_api.get_video_id() + if not video_id: + return - if video_id == self._take_ignore_next_video_id(): - logger.debug("ignoring video %s because it's ignored", video_id) - return + if video_id == self._take_ignore_next_video_id(): + logger.debug("ignoring video %s because it's ignored", video_id) + return - if not self._prepare_segments(video_id): - return + if not self._prepare_segments(video_id): + return + + self._next_segment = self._segments[0] + self._should_start = True + + def onAVStarted(self): # type: () -> None + with self._should_start_lock: + if self._should_start: + self._should_start = False + else: + # `onPlayBackStarted` determined that we don't need to start + return - self._next_segment = self._segments[0] self.start() def _select_next_checkpoint(self): @@ -164,8 +179,10 @@ def _reached_checkpoint(self): else: self.seekTime(seg.end) - if addon.get_config(CONF_SHOW_SKIPPED_DIALOG, bool): - self.__show_skipped_dialog(seg) + # with `playnext` there's no way for the user to "unskip" right now, + # so we only show the dialog if we're still in the same video. + if addon.get_config(CONF_SHOW_SKIPPED_DIALOG, bool): + self.__show_skipped_dialog(seg) if addon.get_config(CONF_SKIP_COUNT_TRACKING, bool): logger.debug("reporting sponsor skipped") diff --git a/resources/lib/sponsorblock/api.py b/resources/lib/sponsorblock/api.py index 3b5101b..03f95a6 100644 --- a/resources/lib/sponsorblock/api.py +++ b/resources/lib/sponsorblock/api.py @@ -90,9 +90,6 @@ def get_skip_segments( segments = [] for raw in data: start, end = raw["segment"] - # segments starting at 0.0 auto-skip the entire video due to a - # quirk with xbmc.Player so we adjust start times accordingly here - start = max(start, 0.5) seg = SponsorSegment(raw["UUID"], raw["category"], start, end) segments.append(seg) diff --git a/resources/lib/utils/checkpoint_listener.py b/resources/lib/utils/checkpoint_listener.py index 0bb2072..0d5ba3c 100644 --- a/resources/lib/utils/checkpoint_listener.py +++ b/resources/lib/utils/checkpoint_listener.py @@ -78,12 +78,18 @@ def _get_current_time(self): # type:() -> float This is problematic for us because we rely on the current time being correct. This function solves this by returning the seek time instead, until the seek time is cleared again. + If `Player.getTime` raises an exception, this function returns 0.0. + Returns: Current time in seconds. """ seek_time = self._seek_time if seek_time is None: - return self.getTime() + try: + return self.getTime() + except RuntimeError: + logger.exception("failed to get playback time, assuming 0.0") + return 0.0 return seek_time @@ -181,7 +187,11 @@ def start(self): # type: () -> None logger.warning("checkpoint listener already running, stopping") self.stop() - logger.info("starting checkpoint listener") + if self.isPlaying(): + logger.info("starting checkpoint listener") + else: + logger.warning("starting checkpoint listener but player isn't playing anything") + self._thread = threading.Thread( target=self.__t_event_loop, name="Checkpoint Listener" )