From d07f17ff288741375d605a00d5294c52952b9dbe Mon Sep 17 00:00:00 2001 From: Brett824 Date: Wed, 31 May 2023 20:24:58 -0400 Subject: [PATCH] add credits skipping --- README.md | 2 ++ plex_mpv_shim/conf.py | 2 ++ plex_mpv_shim/media.py | 12 +++++++++ plex_mpv_shim/player.py | 54 +++++++++++++++++++++++++++++++++++++---- 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f0ada17..1b0f65c 100644 --- a/README.md +++ b/README.md @@ -242,6 +242,8 @@ Currently on Windows the built-in MPV does not work with SVP. You must download - `stop_idle` - Stop the player when idle. (Requires `idle_when_paused`.) Default: `false` - `skip_intro_always` - Always skip intros, without asking. Default: `false` - `skip_intro_prompt` - Prompt to skip intro via seeking. Default: `true` + - `skip_credits_always` - Always skip credits, without asking. Default: `false` + - `skip_credits_prompt` - Prompt to skip credits via seeking. Default: `true` - `menu_mouse` - Enable mouse support in the menu. Default: `true` - This requires MPV to be compiled with lua support. diff --git a/plex_mpv_shim/conf.py b/plex_mpv_shim/conf.py index 1beebf7..c704185 100644 --- a/plex_mpv_shim/conf.py +++ b/plex_mpv_shim/conf.py @@ -70,6 +70,8 @@ class Settings(object): "seek_left": -5, "skip_intro_always": False, "skip_intro_prompt": True, + "skip_credits_always": False, + "skip_credits_prompt": True, "shader_pack_enable": True, "shader_pack_custom": False, "shader_pack_remember": True, diff --git a/plex_mpv_shim/media.py b/plex_mpv_shim/media.py index b2470f8..6a9c1a2 100644 --- a/plex_mpv_shim/media.py +++ b/plex_mpv_shim/media.py @@ -90,6 +90,8 @@ def __init__(self, node, parent, media=0, part=0): self.trs_ovr = None self.intro_start = None self.intro_end = None + self.credits_start = None + self.credits_end = None if media: self.select_media(media, part) @@ -106,6 +108,16 @@ def __init__(self, node, parent, media=0, part=0): except: log.error("Could not detect intro.", exc_info=True) + # TODO de-duplicate this code in some way - it's ugly + try: + marker = self.node.find("./Marker[@type='credits']") + if marker is not None: + self.credits_start = float(marker.get('startTimeOffset')) / 1e3 + self.credits_end = float(marker.get('endTimeOffset')) / 1e3 + log.info("Credits Detected: {0} - {1}".format(self.credits_start, self.credits_end)) + except: + log.error("Could not detect credits.", exc_info=True) + self.map_streams() def map_streams(self): diff --git a/plex_mpv_shim/player.py b/plex_mpv_shim/player.py index f095ed1..3a45c82 100644 --- a/plex_mpv_shim/player.py +++ b/plex_mpv_shim/player.py @@ -88,7 +88,9 @@ def __init__(self): self.url = None self.evt_queue = Queue() self.is_in_intro = False + self.is_in_credits = False self.intro_has_triggered = False + self.credits_has_triggered = False if is_using_ext_mpv: mpv_options.update( @@ -156,6 +158,8 @@ def handle_media_next(): if settings.media_key_seek: if self.is_in_intro: self.skip_intro() + elif self.is_in_credits: + self.skip_credits() else: self._player.command("seek", 30) else: @@ -201,6 +205,8 @@ def menu_right(): else: if self.is_in_intro: self.skip_intro() + elif self.is_in_credits: + self.skip_credits() else: self._player.command("seek", settings.seek_right) @@ -211,6 +217,8 @@ def menu_up(): else: if self.is_in_intro: self.skip_intro() + elif self.is_in_credits: + self.skip_credits() else: self._player.command("seek", settings.seek_up) @@ -289,20 +297,30 @@ def timeline_handle(self): if self.timeline_trigger: self.timeline_trigger.set() - def skip_intro(self): + def skip_marker(self, end_point): if self._media_item.media_type == MediaType.VIDEO: - self._player.playback_time = self._media_item.intro_end + self._player.playback_time = end_point self.timeline_handle() + return True + return False + + def skip_intro(self): + end_point = self._media_item.intro_end + if self.skip_marker(end_point): self.is_in_intro = False - @synchronous('_lock') - def update(self): + def skip_credits(self): + end_point = self._media_item.credits_end + if self.skip_marker(end_point): + self.is_in_credits = False + + def check_intro_or_credits(self): if ((settings.skip_intro_always or settings.skip_intro_prompt) and self._media_item is not None and self._media_item.media_type == MediaType.VIDEO and self._media_item.intro_start is not None and self._player.playback_time is not None and self._player.playback_time > self._media_item.intro_start and self._player.playback_time < self._media_item.intro_end): - + if not self.is_in_intro: if settings.skip_intro_always and not self.intro_has_triggered: self.intro_has_triggered = True @@ -314,6 +332,28 @@ def update(self): else: self.is_in_intro = False + # TODO de-duplicate this code in some way - it's ugly + if ((settings.skip_credits_always or settings.skip_credits_prompt) + and self._media_item is not None and self._media_item.media_type == MediaType.VIDEO and self._media_item.credits_start is not None + and self._player.playback_time is not None + and self._player.playback_time > self._media_item.credits_start + and self._player.playback_time < self._media_item.credits_end): + + if not self.is_in_credits: + if settings.skip_credits_always and not self.credits_has_triggered: + self.credits_has_triggered = True + self.skip_credits() + self._player.show_text("Skipped Credits", 3000, 1) + elif settings.skip_credits_prompt: + self._player.show_text("Seek to Skip Credits", 3000, 1) + self.is_in_credits = True + else: + self.is_in_credits = False + + + @synchronous('_lock') + def update(self): + self.check_intro_or_credits() while not self.evt_queue.empty(): func, args = self.evt_queue.get() func(*args) @@ -344,7 +384,9 @@ def _play_media(self, media_item, url, offset=0): self._player.force_media_title = media_item.get_proper_title() self._media_item = media_item self.is_in_intro = False + self.is_in_credits = False self.intro_has_triggered = False + self.credits_has_triggered = False self.update_subtitle_visuals(False) self.upd_player_hide() self.external_subtitles = {} @@ -423,6 +465,8 @@ def seek(self, offset): if not self._player.playback_abort: if self.is_in_intro and offset > self._player.playback_time: self.skip_intro() + elif self.is_in_credits and offset > self._player.playback_time: + self.skip_credits() else: self._player.playback_time = offset self.timeline_handle()