From b4ccfb7e968562b49df035eba88d0f059550baf4 Mon Sep 17 00:00:00 2001 From: dmunozv04 <39565245+dmunozv04@users.noreply.github.com> Date: Mon, 10 Mar 2025 10:31:53 +0100 Subject: [PATCH] Add support for different playback speeds --- src/iSponsorBlockTV/main.py | 27 ++++++++++++++------------- src/iSponsorBlockTV/ytlounge.py | 9 +++++++++ 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/iSponsorBlockTV/main.py b/src/iSponsorBlockTV/main.py index 5ab26f2..b724ecc 100644 --- a/src/iSponsorBlockTV/main.py +++ b/src/iSponsorBlockTV/main.py @@ -76,11 +76,11 @@ class DeviceListener: # Method called on playback state change async def __call__(self, state): + time_start = time.monotonic() try: self.task.cancel() except BaseException: pass - time_start = time.time() self.task = asyncio.create_task(self.process_playstatus(state, time_start)) # Processes the playback state change @@ -100,28 +100,29 @@ class DeviceListener: start_next_segment = None next_segment = None for segment in segments: - if position < 2 and (segment["start"] <= position < segment["end"]): + segment_start = segment["start"] + is_within_start_range = position < 1 and segment_start <= position < segment["end"] and segment["end"] > 1 + is_beyond_current_position = segment_start > position + + if is_within_start_range or is_beyond_current_position: next_segment = segment - start_next_segment = ( - position # different variable so segment doesn't change - ) - break - if segment["start"] > position: - next_segment = segment - start_next_segment = next_segment["start"] + start_next_segment = position if is_within_start_range else segment_start break if start_next_segment: time_to_next = ( - start_next_segment - position - (time.time() - time_start) - self.offset - ) + (start_next_segment - position - (time.monotonic() - time_start)) + / self.lounge_controller.playback_speed + ) - self.offset await self.skip(time_to_next, next_segment["end"], next_segment["UUID"]) # Skips to the next segment (waits for the time to pass) async def skip(self, time_to, position, uuids): await asyncio.sleep(time_to) self.logger.info("Skipping segment: seeking to %s", position) - await asyncio.create_task(self.api_helper.mark_viewed_segments(uuids)) - await asyncio.create_task(self.lounge_controller.seek_to(position)) + await asyncio.gather( + asyncio.create_task(self.lounge_controller.seek_to(position)), + asyncio.create_task(self.api_helper.mark_viewed_segments(uuids)), + ) async def cancel(self): self.cancelled = True diff --git a/src/iSponsorBlockTV/ytlounge.py b/src/iSponsorBlockTV/ytlounge.py index 02825e7..f9f6301 100644 --- a/src/iSponsorBlockTV/ytlounge.py +++ b/src/iSponsorBlockTV/ytlounge.py @@ -25,6 +25,7 @@ class YtLoungeApi(pyytlounge.YtLoungeApi): self.auth.lounge_id_token = None self.api_helper = api_helper self.volume_state = {} + self.playback_speed = 1.0 self.subscribe_task = None self.subscribe_task_watchdog = None self.callback = None @@ -154,6 +155,11 @@ class YtLoungeApi(pyytlounge.YtLoungeApi): self.shorts_disconnected = True elif event_type == "onAutoplayModeChanged": create_task(self.set_auto_play_mode(self.auto_play)) + + elif event_type == "onPlaybackSpeedChanged": + data = args[0] + self.playback_speed = float(data.get("playbackSpeed", "1")) + create_task(self.get_now_playing()) super()._process_event(event_type, args) @@ -185,6 +191,9 @@ class YtLoungeApi(pyytlounge.YtLoungeApi): async def play_video(self, video_id: str) -> bool: return await self._command("setPlaylist", {"videoId": video_id}) + + async def get_now_playing(self): + return await super()._command("getNowPlaying") # Test to wrap the command function in a mutex to avoid race conditions with # the _command_offset (TODO: move to upstream if it works)