From 87eebeffe2e00a49d38667b5713ca27a37446dc1 Mon Sep 17 00:00:00 2001 From: tyler breese Date: Fri, 20 Oct 2023 03:53:59 -0400 Subject: [PATCH] random optimizations fixes to watch history fixes to library watch count --- data/interfaces/default/index.html | 3 -- plexpy/__init__.py | 2 ++ plexpy/activity_handler.py | 56 +++++++++++++++--------------- plexpy/activity_processor.py | 32 ++++++++--------- plexpy/datafactory.py | 4 +-- plexpy/pmsconnect.py | 39 ++++++++++++--------- plexpy/server_manager.py | 27 +++++++++++--- plexpy/web_socket.py | 1 - plexpy/webserve.py | 41 +++++++++++----------- 9 files changed, 114 insertions(+), 91 deletions(-) diff --git a/data/interfaces/default/index.html b/data/interfaces/default/index.html index ee200dcb..af53ca3a 100644 --- a/data/interfaces/default/index.html +++ b/data/interfaces/default/index.html @@ -427,7 +427,6 @@ if (s.media_type === 'track') { // Update if artist changed if (s.grandparent_rating_key !== instance.data('grandparent_rating_key').toString()) { - debugger; $('#background-' + key).css('background-image', 'url(' + page('pms_image_proxy', s.art, s.rating_key, 500, 280, 40, '282828', 3, 'art', true, server_id=s.server_id) + ')'); $('#metadata-grandparent_title-' + key) .attr('href', page('info', s.grandparent_rating_key, server_id=s.server_id)) @@ -436,7 +435,6 @@ } // Update cover if album changed if (s.parent_rating_key !== instance.data('parent_rating_key').toString()) { - debugger; $('#poster-' + key).css('background-image', 'url(' + page('pms_image_proxy', s.parent_thumb, s.parent_rating_key, 300, 300, null, null, null, 'poster', true, server_id=s.server_id) + ')'); $('#poster-' + key + '-bg').css('background-image', 'url(' + page('pms_image_proxy', s.parent_thumb, s.parent_rating_key, 300, 300, 60, '282828', 3, 'poster', true, server_id=s.server_id) + ')'); $('#poster-url-' + key) @@ -809,7 +807,6 @@ [height, fallback_poster, fallback_art] = [450, 'poster-live', 'art-live']; } var href = '#'; - debugger; if (stat_id === 'most_concurrent') { return } else if (stat_id === 'top_libraries') { diff --git a/plexpy/__init__.py b/plexpy/__init__.py index c5681b6f..a2062c43 100644 --- a/plexpy/__init__.py +++ b/plexpy/__init__.py @@ -144,6 +144,8 @@ DEV = False TRACKER = None +PLEX_REMOTE_ACCESS_UP = False + WIN_SYS_TRAY_ICON = None MAC_SYS_TRAY_ICON = None diff --git a/plexpy/activity_handler.py b/plexpy/activity_handler.py index f5d4a3c3..0dd6e5e5 100644 --- a/plexpy/activity_handler.py +++ b/plexpy/activity_handler.py @@ -639,40 +639,40 @@ def schedule_callback(id, func=None, remove_job=False, args=None, **kwargs): def force_stop_stream(session_key, title, user): ap = activity_processor.ActivityProcessor() session = ap.get_session_by_key(session_key=session_key) + if session: + row_id = ap.write_session_history(session=session) - row_id = ap.write_session_history(session=session) + if row_id: + plexpy.NOTIFY_QUEUE.put({'stream_data': session.copy(), 'notify_action': 'on_stop'}) - if row_id: - plexpy.NOTIFY_QUEUE.put({'stream_data': session.copy(), 'notify_action': 'on_stop'}) - - # If session is written to the database successfully, remove the session from the session table - logger.info("Tautulli ActivityHandler :: Removing stale stream with sessionKey %s ratingKey %s from session queue" - % (session['session_key'], session['rating_key'])) - ap.delete_session(row_id=row_id) - delete_metadata_cache(session_key) - - else: - session['write_attempts'] += 1 - - if session['write_attempts'] < plexpy.CONFIG.SESSION_DB_WRITE_ATTEMPTS: - logger.warn("Tautulli ActivityHandler :: Failed to write stream with sessionKey %s ratingKey %s to the database. " \ - "Will try again in 30 seconds. Write attempt %s." - % (session['session_key'], session['rating_key'], str(session['write_attempts']))) - ap.increment_write_attempts(session_key=session_key) - - # Reschedule for 30 seconds later - schedule_callback('session_key-{}'.format(session_key), func=force_stop_stream, - args=[session_key, session['full_title'], session['user']], seconds=30) - - else: - logger.warn("Tautulli ActivityHandler :: Failed to write stream with sessionKey %s ratingKey %s to the database. " \ - "Removing session from the database. Write attempt %s." - % (session['session_key'], session['rating_key'], str(session['write_attempts']))) + # If session is written to the database successfully, remove the session from the session table logger.info("Tautulli ActivityHandler :: Removing stale stream with sessionKey %s ratingKey %s from session queue" % (session['session_key'], session['rating_key'])) - ap.delete_session(session_key=session_key) + ap.delete_session(row_id=row_id) delete_metadata_cache(session_key) + else: + session['write_attempts'] += 1 + + if session['write_attempts'] < plexpy.CONFIG.SESSION_DB_WRITE_ATTEMPTS: + logger.warn("Tautulli ActivityHandler :: Failed to write stream with sessionKey %s ratingKey %s to the database. " \ + "Will try again in 30 seconds. Write attempt %s." + % (session['session_key'], session['rating_key'], str(session['write_attempts']))) + ap.increment_write_attempts(session_key=session_key) + + # Reschedule for 30 seconds later + schedule_callback('session_key-{}'.format(session_key), func=force_stop_stream, + args=[session_key, session['full_title'], session['user']], seconds=30) + + else: + logger.warn("Tautulli ActivityHandler :: Failed to write stream with sessionKey %s ratingKey %s to the database. " \ + "Removing session from the database. Write attempt %s." + % (session['session_key'], session['rating_key'], str(session['write_attempts']))) + logger.info("Tautulli ActivityHandler :: Removing stale stream with sessionKey %s ratingKey %s from session queue" + % (session['session_key'], session['rating_key'])) + ap.delete_session(session_key=session_key) + delete_metadata_cache(session_key) + def clear_recently_added_queue(rating_key, title): logger.debug("Tautulli TimelineHandler :: Starting callback for library item '%s' (%s) after delay.", diff --git a/plexpy/activity_processor.py b/plexpy/activity_processor.py index 69f05132..b422904a 100644 --- a/plexpy/activity_processor.py +++ b/plexpy/activity_processor.py @@ -231,7 +231,7 @@ class ActivityProcessor(object): self.write_continued_session(user_id=session['user_id'], machine_id=session['machine_id'], media_type=session['media_type'], - stopped=stopped) + stopped=stopped, server_id=session['server_id']) if str(session['rating_key']).isdigit() and session['media_type'] in ('movie', 'episode', 'track'): logging_enabled = True @@ -278,19 +278,19 @@ class ActivityProcessor(object): if not is_import: logger.debug("Tautulli ActivityProcessor :: Fetching metadata for item ratingKey %s" % session['rating_key']) - for pms_connect in server_manager.ServerManger().get_server_list(): - if session['live']: - metadata = pms_connect.get_metadata_details(rating_key=str(session['rating_key']), - cache_key=session['session_key'], - return_cache=True) - else: - metadata = pms_connect.get_metadata_details(rating_key=str(session['rating_key'])) - if not metadata: - return False - else: - media_info = {} - if 'media_info' in metadata and len(metadata['media_info']) > 0: - media_info = metadata['media_info'][0] + pms_connect = server_manager.ServerManger().get_server(server_id=session['server_id']) + if session['live']: + metadata = pms_connect.get_metadata_details(rating_key=str(session['rating_key']), + cache_key=session['session_key'], + return_cache=True) + else: + metadata = pms_connect.get_metadata_details(rating_key=str(session['rating_key'])) + if not metadata: + return False + else: + media_info = {} + if 'media_info' in metadata and len(metadata['media_info']) > 0: + media_info = metadata['media_info'][0] else: metadata = import_metadata ## TODO: Fix media info from imports. Temporary media info from import session. @@ -701,8 +701,8 @@ class ActivityProcessor(object): "WHERE session_key = ?", [1, session_key]) - def write_continued_session(self, user_id=None, machine_id=None, media_type=None, stopped=None): - keys = {'user_id': user_id, 'machine_id': machine_id, 'media_type': media_type} + def write_continued_session(self, user_id=None, machine_id=None, media_type=None, stopped=None, server_id=None): + keys = {'user_id': user_id, 'machine_id': machine_id, 'media_type': media_type, 'server_id': server_id} values = {'stopped': stopped} self.db.upsert(table_name='sessions_continued', key_dict=keys, value_dict=values) diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py index af98be46..9acdfe50 100644 --- a/plexpy/datafactory.py +++ b/plexpy/datafactory.py @@ -731,8 +731,8 @@ class DataFactory(object): " GROUP BY %s) AS sh " \ "JOIN session_history_metadata AS shm ON shm.id = sh.id " \ "LEFT OUTER JOIN (SELECT * FROM library_sections WHERE deleted_section = 0) " \ - " AS ls ON sh.section_id = ls.section_id " \ - "GROUP BY sh.section_id " \ + " AS ls ON sh.section_id = ls.section_id AND sh.server_id = ls.server_id " \ + "GROUP BY sh.server_id " \ "ORDER BY %s DESC, sh.started DESC " \ "LIMIT %s OFFSET %s " % (timestamp, where_id, group_by, sort_type, stats_count, stats_start) result = monitor_db.select(query) diff --git a/plexpy/pmsconnect.py b/plexpy/pmsconnect.py index d440a4e7..41d0b8dd 100644 --- a/plexpy/pmsconnect.py +++ b/plexpy/pmsconnect.py @@ -86,6 +86,8 @@ class PmsConnect(object): self.timeout = plexpy.CONFIG.PMS_TIMEOUT self.server_id = server_id + + self.server_info = None if not self.token: # Check if we should use the admin token, or the guest server token @@ -118,7 +120,7 @@ class PmsConnect(object): return request - def get_sessions_terminate(self, session_id='', reason='', server_id=None): + def get_sessions_terminate(self, session_id='', reason=''): """ Return current sessions. @@ -649,7 +651,7 @@ class PmsConnect(object): 'guids': guids, 'full_title': helpers.get_xml_attr(m, 'title'), 'child_count': helpers.get_xml_attr(m, 'childCount'), - 'server_id': server_info['machine_identifier'] + 'server_id': self.server_id } recents_list.append(recent_item) @@ -2309,7 +2311,7 @@ class PmsConnect(object): 'optimized_version_profile': optimized_version_profile, 'user': user_details['username'], # Keep for backwards compatibility 'channel_stream': channel_stream, - 'server_id': server_info['machine_identifier'], + 'server_id': self.server_id, 'server_name': server_info['name'], 'server_ip': server_info['host'], 'server_port': server_info['port'], @@ -2332,7 +2334,7 @@ class PmsConnect(object): return session_output - def terminate_session(self, session_key='', session_id='', message='', server_id=None): + def terminate_session(self, session_key='', session_id='', message=''): """ Terminates a streaming session. """ @@ -2347,7 +2349,7 @@ class PmsConnect(object): ap = activity_processor.ActivityProcessor() if session_key: - session = ap.get_session_by_key(session_key=session_key, server_id=server_id) + session = ap.get_session_by_key(session_key=session_key, server_id=self.server_id) if session and not session_id: session_id = session['session_id'] @@ -2359,7 +2361,7 @@ class PmsConnect(object): if session_id: logger.info("Tautulli Pmsconnect :: Terminating session %s (session_id %s)." % (session_key, session_id)) - response = self.get_sessions_terminate(session_id=session_id, reason=message, server_id=server_id) + response = self.get_sessions_terminate(session_id=session_id, reason=message, server_id=self.server_id) return response.ok else: msg = 'Missing session_id' @@ -2495,7 +2497,7 @@ class PmsConnect(object): 'labels': labels, 'collections': collections, 'full_title': helpers.get_xml_attr(m, 'title'), - 'server_id': self.get_server_info()['machine_identifier'] + 'server_id': self.server_id } children_list.append(children_output) @@ -2602,17 +2604,21 @@ class PmsConnect(object): def get_server_info(self): """ - Return the base info of the current pms connection. + Return the base info of the current pms connection (cache it). Output: dict """ - server_info = {} - servers = self.get_servers_info() - current = self.get_server_identity() - for server in servers: - if server['machine_identifier'] == current['machine_identifier']: - server_info = server - return server_info + if self.server_info is None: + servers = self.get_servers_info() + current = "" + if self.server_id is not None: + current = self.server_id + else: + current = self.get_server_identity()['machine_identifier'] + for server in servers: + if server['machine_identifier'] == current: + self.server_info = server + return self.server_info def get_server_identity(self): """ @@ -2633,7 +2639,8 @@ class PmsConnect(object): server_identity = {"machine_identifier": helpers.get_xml_attr(a, 'machineIdentifier'), "version": helpers.get_xml_attr(a, 'version'), } - + if self.server_id is None: + self.server_id = server_identity['machine_identifier'] return server_identity def get_server_pref(self, pref=None): diff --git a/plexpy/server_manager.py b/plexpy/server_manager.py index d39448d7..2099a4f4 100644 --- a/plexpy/server_manager.py +++ b/plexpy/server_manager.py @@ -21,24 +21,43 @@ if plexpy.PYTHON2: else: from plexpy import pmsconnect +pmsServers = [] +totalServers = 0 + class ServerManger(object): """ - Return processed and validated library statistics. + Return list of cached servers - Output: array + Output: array of servers """ def get_server_list(self): - pmsServers = [] + global totalServers + global pmsServers + if totalServers != 0 : + return pmsServers for server in pmsconnect.PmsConnect().get_servers_info(): url = 'http://{hostname}:{port}'.format(hostname=server["host"], port=server["port"]) pmsServers.append(pmsconnect.PmsConnect(server['machine_identifier'], url=url)) + totalServers = len(pmsServers) return pmsServers + """ + Return server by id or None + + Output: PmsConnect + + """ def get_server(self, server_id): if server_id is not None: + global pmsServers + for server in pmsServers: + if server_id == server.server_id: + return server for server in pmsconnect.PmsConnect().get_servers_info(): if server['machine_identifier'] == server_id: url = 'http://{hostname}:{port}'.format(hostname=server["host"], port=server["port"]) - return pmsconnect.PmsConnect(server_id, url=url) + new = pmsconnect.PmsConnect(server_id, url=url) + pmsServers.append(new) + return new return None \ No newline at end of file diff --git a/plexpy/web_socket.py b/plexpy/web_socket.py index 0a609e98..48d94280 100644 --- a/plexpy/web_socket.py +++ b/plexpy/web_socket.py @@ -92,7 +92,6 @@ class WebSocketServer(object): self.WEBSOCKET = None self.WS_CONNECTED = False self.PLEX_SERVER_UP = None - self.PLEX_REMOTE_ACCESS_UP = None self.server_id = server_id def on_connect(self): diff --git a/plexpy/webserve.py b/plexpy/webserve.py index b80807ae..be711629 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -393,7 +393,7 @@ class WebInterface(object): ``` """ pms_connect = server_manager.ServerManger().get_server(server_id=server_id) - result = pms_connect.terminate_session(session_key=session_key, session_id=session_id, message=message, server_id=server_id) + result = pms_connect.terminate_session(session_key=session_key, session_id=session_id, message=message) if isinstance(result, str): return {'result': 'error', 'message': 'Failed to terminate session: {}.'.format(result)} @@ -4845,29 +4845,28 @@ class WebInterface(object): # the image does not exist, download it from pms try: pms_connect = server_manager.ServerManger().get_server(server_id=server_id) - if pms_connect is not None: - pms_connect.request_handler._silent = True - result = pms_connect.get_image(img=img, - width=width, - height=height, - opacity=opacity, - background=background, - blur=blur, - img_format=img_format, - clip=clip, - refresh=refresh) + if pms_connect is None: + pms_connect = server_manager.ServerManger().get_server_list()[0] + pms_connect.request_handler._silent = True + result = pms_connect.get_image(img=img, + width=width, + height=height, + opacity=opacity, + background=background, + blur=blur, + img_format=img_format, + clip=clip, + refresh=refresh) - if result and result[0]: - cherrypy.response.headers['Content-type'] = result[1] - if plexpy.CONFIG.CACHE_IMAGES and 'indexes' not in img: - with open(ffp, 'wb') as f: - f.write(result[0]) + if result and result[0]: + cherrypy.response.headers['Content-type'] = result[1] + if plexpy.CONFIG.CACHE_IMAGES and 'indexes' not in img: + with open(ffp, 'wb') as f: + f.write(result[0]) - return result[0] - else: - raise Exception('PMS image request failed') + return result[0] else: - raise Exception('PMS server not found') + raise Exception('PMS image request failed') except Exception as e: logger.warn("Failed to get image %s, falling back to %s." % (img, fallback))