From b2b12044e3342ccc77dbbaa9c90771e8e13a38dd Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Mon, 20 Feb 2023 17:14:35 -0800 Subject: [PATCH] Trigger on_watched based on credits markers --- plexpy/activity_handler.py | 92 +++++++++++++++++++++++++------------- plexpy/helpers.py | 40 +++++++++++++++++ 2 files changed, 101 insertions(+), 31 deletions(-) diff --git a/plexpy/activity_handler.py b/plexpy/activity_handler.py index 8933c1ac..851372d5 100644 --- a/plexpy/activity_handler.py +++ b/plexpy/activity_handler.py @@ -110,11 +110,13 @@ class ActivityHandler(object): self.set_session_state() self.get_db_session() - def set_session_state(self): - self.ap.set_session_state(session_key=self.session_key, - state=self.state, - view_offset=self.view_offset, - stopped=helpers.timestamp()) + def set_session_state(self, view_offset=None): + self.ap.set_session_state( + session_key=self.session_key, + state=self.state, + view_offset=view_offset or self.view_offset, + stopped=helpers.timestamp() + ) def put_notification(self, notify_action, **kwargs): notification = {'stream_data': self.db_session.copy(), 'notify_action': notify_action} @@ -246,26 +248,34 @@ class ActivityHandler(object): self.put_notification('on_change') def on_intro(self, marker): - if self.get_live_session(): - logger.debug("Tautulli ActivityHandler :: Session %s reached intro marker." % str(self.session_key)) + logger.debug("Tautulli ActivityHandler :: Session %s reached intro marker." % str(self.session_key)) - self.put_notification('on_intro', marker=marker) + self.set_session_state(view_offset=marker['start_time_offset']) + + self.put_notification('on_intro', marker=marker) def on_commercial(self, marker): - if self.get_live_session(): - logger.debug("Tautulli ActivityHandler :: Session %s reached commercial marker." % str(self.session_key)) + logger.debug("Tautulli ActivityHandler :: Session %s reached commercial marker." % str(self.session_key)) - self.put_notification('on_commercial', marker=marker) + self.set_session_state(view_offset=marker['start_time_offset']) + + self.put_notification('on_commercial', marker=marker) def on_credits(self, marker): - if self.get_live_session(): - logger.debug("Tautulli ActivityHandler :: Session %s reached credits marker." % str(self.session_key)) + logger.debug("Tautulli ActivityHandler :: Session %s reached credits marker." % str(self.session_key)) - self.put_notification('on_credits', marker=marker) + self.set_session_state(view_offset=marker['start_time_offset']) - def on_watched(self): + self.put_notification('on_credits', marker=marker) + + def on_watched(self, marker=None): logger.debug("Tautulli ActivityHandler :: Session %s watched." % str(self.session_key)) + if marker: + self.set_session_state(view_offset=marker['start_time_offset']) + else: + self.update_db_session() + watched_notifiers = notification_handler.get_notify_state_enabled( session=self.db_session, notify_action='on_watched', notified=False) @@ -368,38 +378,58 @@ class ActivityHandler(object): if self.db_session['marker'] != marker_idx: self.ap.set_marker(session_key=self.session_key, marker_idx=marker_idx, marker_type=marker['type']) - callback_func = getattr(self, 'on_{}'.format(marker['type'])) if self.view_offset < marker['start_time_offset']: # Schedule a callback for the exact offset of the marker schedule_callback( 'session_key-{}-marker-{}'.format(self.session_key, marker_idx), - func=callback_func, + func=self._marker_callback, args=[marker], milliseconds=marker['start_time_offset'] - self.view_offset ) else: - callback_func(marker) + self._marker_callback(marker) break if not marker_flag: self.ap.set_marker(session_key=self.session_key, marker_idx=0) - def check_watched(self): - # Monitor if the stream has reached the watch percentage for notifications - if not self.db_session['watched'] and self.timeline['state'] != 'buffering': - progress_percent = helpers.get_percent(self.timeline['viewOffset'], self.db_session['duration']) - watched_percent = { - 'movie': plexpy.CONFIG.MOVIE_WATCHED_PERCENT, - 'episode': plexpy.CONFIG.TV_WATCHED_PERCENT, - 'track': plexpy.CONFIG.MUSIC_WATCHED_PERCENT, - 'clip': plexpy.CONFIG.TV_WATCHED_PERCENT - } + def _marker_callback(self, marker): + if self.get_live_session(): + # Reset ActivityProcessor object for new database thread + self.ap = activity_processor.ActivityProcessor() - if progress_percent >= watched_percent.get(self.db_session['media_type'], 101): - self.ap.set_watched(session_key=self.session_key) - self.on_watched() + if marker['type'] == 'intro': + self.on_intro(marker) + elif marker['type'] == 'commercial': + self.on_commercial(marker) + elif marker['type'] == 'credits': + self.on_credits(marker) + + if not self.db_session['watched']: + if marker['final'] and plexpy.CONFIG.WATCHED_MARKER == 1: + self._marker_watched(marker) + elif marker['first'] and (plexpy.CONFIG.WATCHED_MARKER in (2, 3)): + self._marker_watched(marker) + + def _marker_watched(self, marker): + if not self.db_session['watched']: + self._watched_callback(marker) + + def check_watched(self): + if plexpy.CONFIG.WATCHED_MARKER == 1 or plexpy.CONFIG.WATCHED_MARKER == 2: + return + + # Monitor if the stream has reached the watch percentage for notifications + if not self.db_session['watched'] and self.state != 'buffering' and helpers.check_watched( + self.db_session['media_type'], self.view_offset, self.db_session['duration'] + ): + self._watched_callback() + + def _watched_callback(self, marker=None): + self.ap.set_watched(session_key=self.session_key) + self.on_watched(marker) class TimelineHandler(object): diff --git a/plexpy/helpers.py b/plexpy/helpers.py index b0995849..89b047fd 100644 --- a/plexpy/helpers.py +++ b/plexpy/helpers.py @@ -1733,3 +1733,43 @@ def short_season(title): if title.startswith('Season ') and title[7:].isdigit(): return 'S%s' % title[7:] return title + + +def get_first_final_marker(markers): + first = None + final = None + for marker in markers: + if marker['first']: + first = marker + if marker['final']: + final = marker + return first, final + + +def check_watched(media_type, view_offset, duration, marker_credits_first=None, marker_credits_final=None): + if isinstance(marker_credits_first, dict): + marker_credits_first = marker_credits_first['start_time_offset'] + if isinstance(marker_credits_final, dict): + marker_credits_final = marker_credits_final['start_time_offset'] + + view_offset = cast_to_int(view_offset) + duration = cast_to_int(duration) + + watched_percent = { + 'movie': plexpy.CONFIG.MOVIE_WATCHED_PERCENT, + 'episode': plexpy.CONFIG.TV_WATCHED_PERCENT, + 'track': plexpy.CONFIG.MUSIC_WATCHED_PERCENT, + 'clip': plexpy.CONFIG.TV_WATCHED_PERCENT + } + threshold = watched_percent.get(media_type, 0) / 100 * duration + if not threshold: + return False + + if plexpy.CONFIG.WATCHED_MARKER == 1 and marker_credits_final: + return view_offset >= marker_credits_final + elif plexpy.CONFIG.WATCHED_MARKER == 2 and marker_credits_first: + return view_offset >= marker_credits_first + elif plexpy.CONFIG.WATCHED_MARKER == 3 and marker_credits_first: + return view_offset >= min(threshold, marker_credits_first) + else: + return view_offset >= threshold