From 38f0a44fa08ca203cd5b04a859bd45c56a5a6c7e Mon Sep 17 00:00:00 2001 From: Giovanni Harting <539@idlegandalf.com> Date: Fri, 5 Feb 2021 21:01:43 +0100 Subject: [PATCH] removed plextv / pms, added TODO's for reimplementing Jellyfin --- data/interfaces/default/welcome.html | 37 +- jellypy/__init__.py | 15 +- jellypy/activity_handler.py | 194 +- jellypy/activity_pinger.py | 41 +- jellypy/activity_processor.py | 28 +- jellypy/api2.py | 7 +- jellypy/datafactory.py | 103 +- jellypy/libraries.py | 4 +- jellypy/newsletters.py | 4 +- jellypy/notification_handler.py | 96 +- jellypy/notifiers.py | 48 +- jellypy/plextv.py | 978 -------- jellypy/pmsconnect.py | 3173 -------------------------- jellypy/users.py | 1 - jellypy/webauth.py | 42 +- jellypy/webserve.py | 797 ++++--- 16 files changed, 733 insertions(+), 4835 deletions(-) delete mode 100644 jellypy/plextv.py delete mode 100644 jellypy/pmsconnect.py diff --git a/data/interfaces/default/welcome.html b/data/interfaces/default/welcome.html index 05ebbeff..89c21492 100644 --- a/data/interfaces/default/welcome.html +++ b/data/interfaces/default/welcome.html @@ -50,10 +50,7 @@

Welcome!

- Thanks for taking the time to try out Tautulli. Hope you find it useful. -

-

- Tautulli requires a permanent internet connection to ensure a reliable experience. + Thanks for taking the time to try out JellyPy. Hope you find it useful.

This wizard will help you get set up, to continue press Next. @@ -65,11 +62,11 @@

Authentication

- Please setup an admin username and password for Tautulli. + Please setup an admin username and password for JellyPy.

- +
@@ -77,7 +74,7 @@
- +
@@ -91,26 +88,14 @@
-

Plex Account

+

Jellyfin

- Tautulli requires a Plex.tv account. Click the button below to sign in on Plex.tv. You may need to allow popups in your browser. -

-
- - Sign In with Plex - -
- -
-

Plex Media Server

-
-

- Select your Plex Media Server from the dropdown menu or enter an IP address or hostname. + Select your Jellyfin Server from the dropdown menu or enter an IP address or hostname.

- +
@@ -161,7 +146,7 @@
-
+

Activity Logging

@@ -186,7 +171,7 @@

-
+

Notifications

@@ -199,7 +184,7 @@

-
+

Database Import

diff --git a/jellypy/__init__.py b/jellypy/__init__.py index 2b575e3a..85b68764 100644 --- a/jellypy/__init__.py +++ b/jellypy/__init__.py @@ -47,7 +47,6 @@ from jellypy import newsletters from jellypy import newsletter_handler from jellypy import notification_handler from jellypy import notifiers -from jellypy import plextv from jellypy import users from jellypy import versioncheck from jellypy import web_socket @@ -426,8 +425,9 @@ def initialize_scheduler(): hours=backup_hours, minutes=0, seconds=0, args=(True, True)) if WS_CONNECTED and CONFIG.PMS_IP and CONFIG.PMS_TOKEN: - schedule_job(plextv.get_server_resources, 'Refresh Plex server URLs', - hours=12 * (not bool(CONFIG.PMS_URL_MANUAL)), minutes=0, seconds=0) + # TODO: Jellyfin + # schedule_job(plextv.get_server_resources, 'Refresh Plex server URLs', + # hours=12 * (not bool(CONFIG.PMS_URL_MANUAL)), minutes=0, seconds=0) schedule_job(activity_pinger.check_server_updates, 'Check for Plex updates', hours=pms_update_check_hours * bool(CONFIG.MONITOR_PMS_UPDATES), minutes=0, seconds=0) @@ -448,8 +448,9 @@ def initialize_scheduler(): else: # Cancel all jobs - schedule_job(plextv.get_server_resources, 'Refresh Plex server URLs', - hours=0, minutes=0, seconds=0) + # TODO: Jellyfin + # schedule_job(plextv.get_server_resources, 'Refresh Plex server URLs', + # hours=0, minutes=0, seconds=0) schedule_job(activity_pinger.check_server_updates, 'Check for Plex updates', hours=0, minutes=0, seconds=0) @@ -532,7 +533,9 @@ def start(): def startup_refresh(): # Get the real PMS urls for SSL and remote access if CONFIG.PMS_TOKEN and CONFIG.PMS_IP and CONFIG.PMS_PORT: - plextv.get_server_resources() + pass + # TODO: Jellyfin + # plextv.get_server_resources() # Connect server after server resource is refreshed if CONFIG.FIRST_RUN_COMPLETE: diff --git a/jellypy/activity_handler.py b/jellypy/activity_handler.py index e19488f8..bdb5d7f5 100644 --- a/jellypy/activity_handler.py +++ b/jellypy/activity_handler.py @@ -27,7 +27,6 @@ from jellypy import datafactory from jellypy import helpers from jellypy import logger from jellypy import notification_handler -from jellypy import pmsconnect ACTIVITY_SCHED = None @@ -60,27 +59,29 @@ class ActivityHandler(object): def get_metadata(self, skip_cache=False): cache_key = None if skip_cache else self.get_session_key() - pms_connect = pmsconnect.PmsConnect() - metadata = pms_connect.get_metadata_details(rating_key=self.get_rating_key(), cache_key=cache_key) - - if metadata: - return metadata + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # metadata = pms_connect.get_metadata_details(rating_key=self.get_rating_key(), cache_key=cache_key) + # + # if metadata: + # return metadata return None def get_live_session(self, skip_cache=False): - pms_connect = pmsconnect.PmsConnect() - session_list = pms_connect.get_current_activity(skip_cache=skip_cache) - - if session_list: - for session in session_list['sessions']: - if int(session['session_key']) == self.get_session_key(): - # Live sessions don't have rating keys in sessions - # Get it from the websocket data - if not session['rating_key']: - session['rating_key'] = self.get_rating_key() - session['rating_key_websocket'] = self.get_rating_key() - return session + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # session_list = pms_connect.get_current_activity(skip_cache=skip_cache) + # + # if session_list: + # for session in session_list['sessions']: + # if int(session['session_key']) == self.get_session_key(): + # # Live sessions don't have rating keys in sessions + # # Get it from the websocket data + # if not session['rating_key']: + # session['rating_key'] = self.get_rating_key() + # session['rating_key_websocket'] = self.get_rating_key() + # return session return None @@ -390,11 +391,12 @@ class TimelineHandler(object): return None def get_metadata(self): - pms_connect = pmsconnect.PmsConnect() - metadata = pms_connect.get_metadata_details(self.get_rating_key()) - - if metadata: - return metadata + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # metadata = pms_connect.get_metadata_details(self.get_rating_key()) + # + # if metadata: + # return metadata return None @@ -522,9 +524,11 @@ class ReachabilityHandler(object): return False def remote_access_enabled(self): - pms_connect = pmsconnect.PmsConnect() - pref = pms_connect.get_server_pref(pref='PublishServerOnPlexOnlineKey') - return helpers.bool_true(pref) + return False + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # pref = pms_connect.get_server_pref(pref='PublishServerOnPlexOnlineKey') + # return helpers.bool_true(pref) def on_down(self, server_response): jellypy.NOTIFY_QUEUE.put({'notify_action': 'on_extdown', 'remote_access_info': server_response}) @@ -541,39 +545,40 @@ class ReachabilityHandler(object): if self.is_reachable() and jellypy.PLEX_REMOTE_ACCESS_UP: return - pms_connect = pmsconnect.PmsConnect() - server_response = pms_connect.get_server_response() - - if server_response: - # Waiting for port mapping - if server_response['mapping_state'] == 'waiting': - logger.warn("Tautulli ReachabilityHandler :: Remote access waiting for port mapping.") - - elif jellypy.PLEX_REMOTE_ACCESS_UP is not False and server_response['reason']: - logger.warn("Tautulli ReachabilityHandler :: Remote access failed: %s" % server_response['reason']) - logger.info("Tautulli ReachabilityHandler :: Plex remote access is down.") - - jellypy.PLEX_REMOTE_ACCESS_UP = False - - if not ACTIVITY_SCHED.get_job('on_extdown'): - logger.debug("Tautulli ReachabilityHandler :: Schedule remote access down callback in %d seconds.", - jellypy.CONFIG.NOTIFY_REMOTE_ACCESS_THRESHOLD) - schedule_callback('on_extdown', func=self.on_down, args=[server_response], - seconds=jellypy.CONFIG.NOTIFY_REMOTE_ACCESS_THRESHOLD) - - elif jellypy.PLEX_REMOTE_ACCESS_UP is False and not server_response['reason']: - logger.info("Tautulli ReachabilityHandler :: Plex remote access is back up.") - - jellypy.PLEX_REMOTE_ACCESS_UP = True - - if ACTIVITY_SCHED.get_job('on_extdown'): - logger.debug("Tautulli ReachabilityHandler :: Cancelling scheduled remote access down callback.") - schedule_callback('on_extdown', remove_job=True) - else: - self.on_up(server_response) - - elif jellypy.PLEX_REMOTE_ACCESS_UP is None: - jellypy.PLEX_REMOTE_ACCESS_UP = self.is_reachable() + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # server_response = pms_connect.get_server_response() + # + # if server_response: + # # Waiting for port mapping + # if server_response['mapping_state'] == 'waiting': + # logger.warn("Tautulli ReachabilityHandler :: Remote access waiting for port mapping.") + # + # elif jellypy.PLEX_REMOTE_ACCESS_UP is not False and server_response['reason']: + # logger.warn("Tautulli ReachabilityHandler :: Remote access failed: %s" % server_response['reason']) + # logger.info("Tautulli ReachabilityHandler :: Plex remote access is down.") + # + # jellypy.PLEX_REMOTE_ACCESS_UP = False + # + # if not ACTIVITY_SCHED.get_job('on_extdown'): + # logger.debug("Tautulli ReachabilityHandler :: Schedule remote access down callback in %d seconds.", + # jellypy.CONFIG.NOTIFY_REMOTE_ACCESS_THRESHOLD) + # schedule_callback('on_extdown', func=self.on_down, args=[server_response], + # seconds=jellypy.CONFIG.NOTIFY_REMOTE_ACCESS_THRESHOLD) + # + # elif jellypy.PLEX_REMOTE_ACCESS_UP is False and not server_response['reason']: + # logger.info("Tautulli ReachabilityHandler :: Plex remote access is back up.") + # + # jellypy.PLEX_REMOTE_ACCESS_UP = True + # + # if ACTIVITY_SCHED.get_job('on_extdown'): + # logger.debug("Tautulli ReachabilityHandler :: Cancelling scheduled remote access down callback.") + # schedule_callback('on_extdown', remove_job=True) + # else: + # self.on_up(server_response) + # + # elif jellypy.PLEX_REMOTE_ACCESS_UP is None: + # jellypy.PLEX_REMOTE_ACCESS_UP = self.is_reachable() def del_keys(key): @@ -670,41 +675,42 @@ def clear_recently_added_queue(rating_key, title): def on_created(rating_key, **kwargs): logger.debug("Tautulli TimelineHandler :: Library item %s added to Plex." % str(rating_key)) - pms_connect = pmsconnect.PmsConnect() - metadata = pms_connect.get_metadata_details(rating_key) - - if metadata: - notify = True - # now = helpers.timestamp() - # - # if helpers.cast_to_int(metadata['added_at']) < now - 86400: # Updated more than 24 hours ago - # logger.debug("Tautulli TimelineHandler :: Library item %s added more than 24 hours ago. Not notifying." - # % str(rating_key)) - # notify = False - - data_factory = datafactory.DataFactory() - if 'child_keys' not in kwargs: - if data_factory.get_recently_added_item(rating_key): - logger.debug("Tautulli TimelineHandler :: Library item %s added already. Not notifying again." - % str(rating_key)) - notify = False - - if notify: - data = {'timeline_data': metadata, 'notify_action': 'on_created'} - data.update(kwargs) - jellypy.NOTIFY_QUEUE.put(data) - - all_keys = [rating_key] - if 'child_keys' in kwargs: - all_keys.extend(kwargs['child_keys']) - - for key in all_keys: - data_factory.set_recently_added_item(key) - - logger.debug("Added %s items to the recently_added database table." % str(len(all_keys))) - - else: - logger.error("Tautulli TimelineHandler :: Unable to retrieve metadata for rating_key %s" % str(rating_key)) + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # metadata = pms_connect.get_metadata_details(rating_key) + # + # if metadata: + # notify = True + # # now = helpers.timestamp() + # # + # # if helpers.cast_to_int(metadata['added_at']) < now - 86400: # Updated more than 24 hours ago + # # logger.debug("Tautulli TimelineHandler :: Library item %s added more than 24 hours ago. Not notifying." + # # % str(rating_key)) + # # notify = False + # + # data_factory = datafactory.DataFactory() + # if 'child_keys' not in kwargs: + # if data_factory.get_recently_added_item(rating_key): + # logger.debug("Tautulli TimelineHandler :: Library item %s added already. Not notifying again." + # % str(rating_key)) + # notify = False + # + # if notify: + # data = {'timeline_data': metadata, 'notify_action': 'on_created'} + # data.update(kwargs) + # jellypy.NOTIFY_QUEUE.put(data) + # + # all_keys = [rating_key] + # if 'child_keys' in kwargs: + # all_keys.extend(kwargs['child_keys']) + # + # for key in all_keys: + # data_factory.set_recently_added_item(key) + # + # logger.debug("Added %s items to the recently_added database table." % str(len(all_keys))) + # + # else: + # logger.error("Tautulli TimelineHandler :: Unable to retrieve metadata for rating_key %s" % str(rating_key)) def delete_metadata_cache(session_key): diff --git a/jellypy/activity_pinger.py b/jellypy/activity_pinger.py index a692e964..e3ced372 100644 --- a/jellypy/activity_pinger.py +++ b/jellypy/activity_pinger.py @@ -23,8 +23,6 @@ from jellypy import database from jellypy import helpers from jellypy import logger from jellypy import notification_handler -from jellypy import plextv -from jellypy import pmsconnect from jellypy import web_socket monitor_lock = threading.Lock() @@ -43,8 +41,10 @@ def check_active_sessions(ws_request=False): for stream in db_streams: activity_handler.delete_metadata_cache(stream['session_key']) - pms_connect = pmsconnect.PmsConnect() - session_list = pms_connect.get_current_activity() + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # session_list = pms_connect.get_current_activity() + session_list = None logger.debug("Tautulli Monitor :: Checking for active streams.") @@ -229,8 +229,10 @@ def connect_server(log=True, startup=False): if log: logger.info("Tautulli Monitor :: Checking for Plex Cloud server status...") - plex_tv = plextv.PlexTV() - status = plex_tv.get_cloud_server_status() + # TODO: Jellyfin + # plex_tv = plextv.PlexTV() + # status = plex_tv.get_cloud_server_status() + status = None if status is True: logger.info("Tautulli Monitor :: Plex Cloud server is active.") @@ -261,16 +263,17 @@ def check_server_updates(): with monitor_lock: logger.info("Tautulli Monitor :: Checking for PMS updates...") - plex_tv = plextv.PlexTV() - download_info = plex_tv.get_plex_downloads() - - if download_info: - logger.info("Tautulli Monitor :: Current PMS version: %s", jellypy.CONFIG.PMS_VERSION) - - if download_info['update_available']: - logger.info("Tautulli Monitor :: PMS update available version: %s", download_info['version']) - - jellypy.NOTIFY_QUEUE.put({'notify_action': 'on_pmsupdate', 'pms_download_info': download_info}) - - else: - logger.info("Tautulli Monitor :: No PMS update available.") + # TODO: Jellyfin + # plex_tv = plextv.PlexTV() + # download_info = plex_tv.get_plex_downloads() + # + # if download_info: + # logger.info("Tautulli Monitor :: Current PMS version: %s", jellypy.CONFIG.PMS_VERSION) + # + # if download_info['update_available']: + # logger.info("Tautulli Monitor :: PMS update available version: %s", download_info['version']) + # + # jellypy.NOTIFY_QUEUE.put({'notify_action': 'on_pmsupdate', 'pms_download_info': download_info}) + # + # else: + # logger.info("Tautulli Monitor :: No PMS update available.") diff --git a/jellypy/activity_processor.py b/jellypy/activity_processor.py index d20c12d4..cc66cceb 100644 --- a/jellypy/activity_processor.py +++ b/jellypy/activity_processor.py @@ -22,7 +22,6 @@ from jellypy import database from jellypy import helpers from jellypy import libraries from jellypy import logger -from jellypy import pmsconnect from jellypy import users @@ -260,19 +259,20 @@ class ActivityProcessor(object): if not is_import: logger.debug( "Tautulli ActivityProcessor :: Fetching metadata for item ratingKey %s" % session['rating_key']) - pms_connect = pmsconnect.PmsConnect() - 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] + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # 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. diff --git a/jellypy/api2.py b/jellypy/api2.py index 88978c03..8ee12770 100644 --- a/jellypy/api2.py +++ b/jellypy/api2.py @@ -40,7 +40,6 @@ from jellypy import notification_handler from jellypy import notifiers from jellypy import newsletter_handler from jellypy import newsletters -from jellypy import plextv from jellypy import users from jellypy.password import check_hash @@ -451,11 +450,13 @@ class API2(object): mobile_app.set_temp_device_token(True) - plex_server = plextv.get_server_resources(return_info=True) + # TODO: Jellyfin + # plex_server = plextv.get_server_resources(return_info=True) tautulli = jellypy.get_tautulli_info() data = {"server_id": jellypy.CONFIG.PMS_UUID} - data.update(plex_server) + # TODO: Jellyfin + # data.update(plex_server) data.update(tautulli) return data diff --git a/jellypy/datafactory.py b/jellypy/datafactory.py index a521415e..1e225f6e 100644 --- a/jellypy/datafactory.py +++ b/jellypy/datafactory.py @@ -25,7 +25,6 @@ from jellypy import database from jellypy import datatables from jellypy import helpers from jellypy import logger -from jellypy import pmsconnect from jellypy import session @@ -1589,7 +1588,7 @@ class DataFactory(object): return key_list def update_metadata(self, old_key_list='', new_key_list='', media_type=''): - pms_connect = pmsconnect.PmsConnect() + # TODO: pms_connect = pmsconnect.PmsConnect() monitor_db = database.MonitorDatabase() # function to map rating keys pairs @@ -1608,37 +1607,38 @@ class DataFactory(object): if old_key_list and new_key_list: mapping = get_pairs(old_key_list, new_key_list) - if mapping: - logger.info("Tautulli DataFactory :: Updating metadata in the database.") - for old_key, new_key in mapping.items(): - metadata = pms_connect.get_metadata_details(new_key) - - if metadata: - if metadata['media_type'] == 'show' or metadata['media_type'] == 'artist': - # check grandparent_rating_key (2 tables) - monitor_db.action( - 'UPDATE session_history SET grandparent_rating_key = ? WHERE grandparent_rating_key = ?', - [new_key, old_key]) - monitor_db.action( - 'UPDATE session_history_metadata SET grandparent_rating_key = ? WHERE grandparent_rating_key = ?', - [new_key, old_key]) - elif metadata['media_type'] == 'season' or metadata['media_type'] == 'album': - # check parent_rating_key (2 tables) - monitor_db.action( - 'UPDATE session_history SET parent_rating_key = ? WHERE parent_rating_key = ?', - [new_key, old_key]) - monitor_db.action( - 'UPDATE session_history_metadata SET parent_rating_key = ? WHERE parent_rating_key = ?', - [new_key, old_key]) - else: - # check rating_key (2 tables) - monitor_db.action('UPDATE session_history SET rating_key = ? WHERE rating_key = ?', - [new_key, old_key]) - monitor_db.action('UPDATE session_history_media_info SET rating_key = ? WHERE rating_key = ?', - [new_key, old_key]) - - # update session_history_metadata table - self.update_metadata_details(old_key, new_key, metadata) + # TODO: Jellyfin + # if mapping: + # logger.info("Tautulli DataFactory :: Updating metadata in the database.") + # for old_key, new_key in mapping.items(): + # metadata = pms_connect.get_metadata_details(new_key) + # + # if metadata: + # if metadata['media_type'] == 'show' or metadata['media_type'] == 'artist': + # # check grandparent_rating_key (2 tables) + # monitor_db.action( + # 'UPDATE session_history SET grandparent_rating_key = ? WHERE grandparent_rating_key = ?', + # [new_key, old_key]) + # monitor_db.action( + # 'UPDATE session_history_metadata SET grandparent_rating_key = ? WHERE grandparent_rating_key = ?', + # [new_key, old_key]) + # elif metadata['media_type'] == 'season' or metadata['media_type'] == 'album': + # # check parent_rating_key (2 tables) + # monitor_db.action( + # 'UPDATE session_history SET parent_rating_key = ? WHERE parent_rating_key = ?', + # [new_key, old_key]) + # monitor_db.action( + # 'UPDATE session_history_metadata SET parent_rating_key = ? WHERE parent_rating_key = ?', + # [new_key, old_key]) + # else: + # # check rating_key (2 tables) + # monitor_db.action('UPDATE session_history SET rating_key = ? WHERE rating_key = ?', + # [new_key, old_key]) + # monitor_db.action('UPDATE session_history_media_info SET rating_key = ? WHERE rating_key = ?', + # [new_key, old_key]) + # + # # update session_history_metadata table + # self.update_metadata_details(old_key, new_key, metadata) return 'Updated metadata in database.' else: @@ -1886,23 +1886,24 @@ class DataFactory(object): def set_recently_added_item(self, rating_key=''): monitor_db = database.MonitorDatabase() - pms_connect = pmsconnect.PmsConnect() - metadata = pms_connect.get_metadata_details(rating_key) - - keys = {'rating_key': metadata['rating_key']} - - values = {'added_at': metadata['added_at'], - 'section_id': metadata['section_id'], - 'parent_rating_key': metadata['parent_rating_key'], - 'grandparent_rating_key': metadata['grandparent_rating_key'], - 'media_type': metadata['media_type'], - 'media_info': json.dumps(metadata['media_info']) - } - - try: - monitor_db.upsert(table_name='recently_added', key_dict=keys, value_dict=values) - except Exception as e: - logger.warn("Tautulli DataFactory :: Unable to execute database query for set_recently_added_item: %s." % e) - return False + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # metadata = pms_connect.get_metadata_details(rating_key) + # + # keys = {'rating_key': metadata['rating_key']} + # + # values = {'added_at': metadata['added_at'], + # 'section_id': metadata['section_id'], + # 'parent_rating_key': metadata['parent_rating_key'], + # 'grandparent_rating_key': metadata['grandparent_rating_key'], + # 'media_type': metadata['media_type'], + # 'media_info': json.dumps(metadata['media_info']) + # } + # + # try: + # monitor_db.upsert(table_name='recently_added', key_dict=keys, value_dict=values) + # except Exception as e: + # logger.warn("Tautulli DataFactory :: Unable to execute database query for set_recently_added_item: %s." % e) + # return False return True diff --git a/jellypy/libraries.py b/jellypy/libraries.py index 4d0a76d6..6b3caa3a 100644 --- a/jellypy/libraries.py +++ b/jellypy/libraries.py @@ -25,8 +25,6 @@ from jellypy import database from jellypy import datatables from jellypy import helpers from jellypy import logger -from jellypy import plextv -from jellypy import pmsconnect from jellypy import session from jellypy import users from jellypy.jellyfin import Jellyfin @@ -1159,7 +1157,7 @@ class Libraries(object): monitor_db = database.MonitorDatabase() # Refresh the PMS_URL to make sure the server_id is updated - plextv.get_server_resources() + # TODO: plextv.get_server_resources() server_id = jellypy.CONFIG.PMS_IDENTIFIER diff --git a/jellypy/newsletters.py b/jellypy/newsletters.py index aef9aafc..ae2b76ed 100644 --- a/jellypy/newsletters.py +++ b/jellypy/newsletters.py @@ -32,7 +32,6 @@ from jellypy import helpers from jellypy import libraries from jellypy import logger from jellypy import newsletter_handler -from jellypy import pmsconnect from jellypy.notifiers import send_notification, EMAIL AGENT_IDS = { @@ -693,7 +692,8 @@ class RecentlyAdded(Newsletter): def _get_recently_added(self, media_type=None): from jellypy.notification_handler import format_group_index - pms_connect = pmsconnect.PmsConnect() + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() recently_added = [] done = False diff --git a/jellypy/notification_handler.py b/jellypy/notification_handler.py index aab495dc..5364a4f2 100644 --- a/jellypy/notification_handler.py +++ b/jellypy/notification_handler.py @@ -39,7 +39,6 @@ from jellypy import datafactory from jellypy import logger from jellypy import helpers from jellypy import notifiers -from jellypy import pmsconnect from jellypy import request from jellypy.newsletter_handler import notify as notify_newsletter @@ -164,18 +163,20 @@ def notify_conditions(notify_action=None, stream_data=None, timeline_data=None): # return False if notify_action == 'on_concurrent': - pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_current_activity() - - user_sessions = [] - if result: - user_sessions = [s for s in result['sessions'] if s['user_id'] == stream_data['user_id']] - - if jellypy.CONFIG.NOTIFY_CONCURRENT_BY_IP: - evaluated = len( - Counter(s['ip_address'] for s in user_sessions)) >= jellypy.CONFIG.NOTIFY_CONCURRENT_THRESHOLD - else: - evaluated = len(user_sessions) >= jellypy.CONFIG.NOTIFY_CONCURRENT_THRESHOLD + pass + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # result = pms_connect.get_current_activity() + # + # user_sessions = [] + # if result: + # user_sessions = [s for s in result['sessions'] if s['user_id'] == stream_data['user_id']] + # + # if jellypy.CONFIG.NOTIFY_CONCURRENT_BY_IP: + # evaluated = len( + # Counter(s['ip_address'] for s in user_sessions)) >= jellypy.CONFIG.NOTIFY_CONCURRENT_THRESHOLD + # else: + # evaluated = len(user_sessions) >= jellypy.CONFIG.NOTIFY_CONCURRENT_THRESHOLD elif notify_action == 'on_newdevice': data_factory = datafactory.DataFactory() @@ -536,10 +537,11 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, m notify_params.update(media_part_info) child_metadata = grandchild_metadata = [] - for key in kwargs.pop('child_keys', []): - child_metadata.append(pmsconnect.PmsConnect().get_metadata_details(rating_key=key)) - for key in kwargs.pop('grandchild_keys', []): - grandchild_metadata.append(pmsconnect.PmsConnect().get_metadata_details(rating_key=key)) + # TODO: Jellyfin + # for key in kwargs.pop('child_keys', []): + # child_metadata.append(pmsconnect.PmsConnect().get_metadata_details(rating_key=key)) + # for key in kwargs.pop('grandchild_keys', []): + # grandchild_metadata.append(pmsconnect.PmsConnect().get_metadata_details(rating_key=key)) # Session values session = session or {} @@ -1097,7 +1099,8 @@ def build_server_notify_params(notify_action=None, **kwargs): date_format = jellypy.CONFIG.DATE_FORMAT.replace('Do', '') time_format = jellypy.CONFIG.TIME_FORMAT.replace('Do', '') - update_channel = pmsconnect.PmsConnect().get_server_update_channel() + # TODO: Jellyfin + # update_channel = pmsconnect.PmsConnect().get_server_update_channel() pms_download_info = defaultdict(str, kwargs.pop('pms_download_info', {})) plexpy_download_info = defaultdict(str, kwargs.pop('plexpy_download_info', {})) @@ -1146,7 +1149,8 @@ def build_server_notify_params(notify_action=None, **kwargs): 'update_url': pms_download_info['download_url'], 'update_release_date': arrow.get(pms_download_info['release_date']).format(date_format) if pms_download_info['release_date'] else '', - 'update_channel': 'Beta' if update_channel == 'beta' else 'Public', + # TODO: Jellyfin + # 'update_channel': 'Beta' if update_channel == 'beta' else 'Public', 'update_platform': pms_download_info['platform'], 'update_distro': pms_download_info['distro'], 'update_distro_build': pms_download_info['build'], @@ -1410,32 +1414,34 @@ def get_img_info(img=None, rating_key=None, title='', width=1000, height=1500, img_info = database_img_info[0] elif not database_img_info and img: - pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_image(refresh=True, **image_info) - - if result and result[0]: - img_url = delete_hash = '' - - if service == 'imgur': - img_url, delete_hash = helpers.upload_to_imgur(img_data=result[0], - img_title=title, - rating_key=rating_key, - fallback=fallback) - elif service == 'cloudinary': - img_url = helpers.upload_to_cloudinary(img_data=result[0], - img_title=title, - rating_key=rating_key, - fallback=fallback) - - if img_url: - img_hash = set_hash_image_info(**image_info) - data_factory.set_img_info(img_hash=img_hash, - img_title=title, - img_url=img_url, - delete_hash=delete_hash, - service=service) - - img_info = {'img_title': title, 'img_url': img_url} + pass + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # result = pms_connect.get_image(refresh=True, **image_info) + # + # if result and result[0]: + # img_url = delete_hash = '' + # + # if service == 'imgur': + # img_url, delete_hash = helpers.upload_to_imgur(img_data=result[0], + # img_title=title, + # rating_key=rating_key, + # fallback=fallback) + # elif service == 'cloudinary': + # img_url = helpers.upload_to_cloudinary(img_data=result[0], + # img_title=title, + # rating_key=rating_key, + # fallback=fallback) + # + # if img_url: + # img_hash = set_hash_image_info(**image_info) + # data_factory.set_img_info(img_hash=img_hash, + # img_title=title, + # img_url=img_url, + # delete_hash=delete_hash, + # service=service) + # + # img_info = {'img_title': title, 'img_url': img_url} if img_info['img_url'] and service == 'cloudinary': # Transform image using Cloudinary diff --git a/jellypy/notifiers.py b/jellypy/notifiers.py index 0bd87ce7..f685a492 100644 --- a/jellypy/notifiers.py +++ b/jellypy/notifiers.py @@ -61,7 +61,6 @@ from jellypy import database from jellypy import helpers from jellypy import logger from jellypy import mobile_app -from jellypy import pmsconnect from jellypy import request from jellypy import users @@ -1517,29 +1516,30 @@ class GROUPME(Notifier): if self.config['incl_poster'] and kwargs.get('parameters'): pretty_metadata = PrettyMetadata(kwargs.get('parameters')) - # Retrieve the poster from Plex - result = pmsconnect.PmsConnect().get_image(img=pretty_metadata.parameters.get('poster_thumb', '')) - if result and result[0]: - poster_content = result[0] - else: - poster_content = '' - logger.error("Tautulli Notifiers :: Unable to retrieve image for {name}.".format(name=self.NAME)) - - if poster_content: - headers = {'X-Access-Token': self.config['access_token'], - 'Content-Type': 'image/png'} - - r = requests.post('https://image.groupme.com/pictures', headers=headers, data=poster_content) - - if r.status_code == 200: - logger.info("Tautulli Notifiers :: {name} poster sent.".format(name=self.NAME)) - r_content = r.json() - data['attachments'] = [{'type': 'image', - 'url': r_content['payload']['picture_url']}] - else: - logger.error("Tautulli Notifiers :: {name} poster failed: " - "[{r.status_code}] {r.reason}".format(name=self.NAME, r=r)) - logger.debug("Tautulli Notifiers :: Request response: {}".format(request.server_message(r, True))) + # TODO: Jellyfin + # # Retrieve the poster from Plex + # result = pmsconnect.PmsConnect().get_image(img=pretty_metadata.parameters.get('poster_thumb', '')) + # if result and result[0]: + # poster_content = result[0] + # else: + # poster_content = '' + # logger.error("Tautulli Notifiers :: Unable to retrieve image for {name}.".format(name=self.NAME)) + # + # if poster_content: + # headers = {'X-Access-Token': self.config['access_token'], + # 'Content-Type': 'image/png'} + # + # r = requests.post('https://image.groupme.com/pictures', headers=headers, data=poster_content) + # + # if r.status_code == 200: + # logger.info("Tautulli Notifiers :: {name} poster sent.".format(name=self.NAME)) + # r_content = r.json() + # data['attachments'] = [{'type': 'image', + # 'url': r_content['payload']['picture_url']}] + # else: + # logger.error("Tautulli Notifiers :: {name} poster failed: " + # "[{r.status_code}] {r.reason}".format(name=self.NAME, r=r)) + # logger.debug("Tautulli Notifiers :: Request response: {}".format(request.server_message(r, True))) return self.make_request('https://api.groupme.com/v3/bots/post', json=data) diff --git a/jellypy/plextv.py b/jellypy/plextv.py deleted file mode 100644 index 5a39ff6a..00000000 --- a/jellypy/plextv.py +++ /dev/null @@ -1,978 +0,0 @@ -# -*- coding: utf-8 -*- - -# This file is part of Tautulli. -# -# Tautulli is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Tautulli is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Tautulli. If not, see . - -import json - -import jellypy - -from jellypy import common -from jellypy import helpers -from jellypy import http_handler -from jellypy import logger -from jellypy import users -from jellypy import pmsconnect -from jellypy import session - - -def get_server_resources(return_presence=False, return_server=False, return_info=False, **kwargs): - if not return_presence and not return_info: - logger.info("Tautulli PlexTV :: Requesting resources for server...") - - server = {'pms_name': jellypy.CONFIG.PMS_NAME, - 'pms_version': jellypy.CONFIG.PMS_VERSION, - 'pms_platform': jellypy.CONFIG.PMS_PLATFORM, - 'pms_ip': jellypy.CONFIG.PMS_IP, - 'pms_port': jellypy.CONFIG.PMS_PORT, - 'pms_ssl': jellypy.CONFIG.PMS_SSL, - 'pms_is_remote': jellypy.CONFIG.PMS_IS_REMOTE, - 'pms_is_cloud': jellypy.CONFIG.PMS_IS_CLOUD, - 'pms_url': jellypy.CONFIG.PMS_URL, - 'pms_url_manual': jellypy.CONFIG.PMS_URL_MANUAL, - 'pms_identifier': jellypy.CONFIG.PMS_IDENTIFIER, - 'pms_plexpass': jellypy.CONFIG.PMS_PLEXPASS - } - - if return_info: - return server - - if kwargs: - server.update(kwargs) - for k in ['pms_ssl', 'pms_is_remote', 'pms_is_cloud', 'pms_url_manual']: - server[k] = int(server[k]) - - if server['pms_url_manual'] and server['pms_ssl'] or server['pms_is_cloud']: - scheme = 'https' - else: - scheme = 'http' - - fallback_url = '{scheme}://{hostname}:{port}'.format(scheme=scheme, - hostname=server['pms_ip'], - port=server['pms_port']) - - plex_tv = PlexTV() - result = plex_tv.get_server_connections(pms_identifier=server['pms_identifier'], - pms_ip=server['pms_ip'], - pms_port=server['pms_port'], - include_https=server['pms_ssl']) - - if result: - connections = result.pop('connections', []) - server.update(result) - presence = server.pop('pms_presence', 0) - else: - connections = [] - presence = 0 - - if return_presence: - return presence - - plexpass = plex_tv.get_plexpass_status() - server['pms_plexpass'] = int(plexpass) - - # Only need to retrieve PMS_URL if using SSL - if not server['pms_url_manual'] and server['pms_ssl']: - if connections: - if server['pms_is_remote']: - # Get all remote connections - conns = [c for c in connections if - c['local'] == '0' and ('plex.direct' in c['uri'] or 'plex.service' in c['uri'])] - else: - # Get all local connections - conns = [c for c in connections if - c['local'] == '1' and ('plex.direct' in c['uri'] or 'plex.service' in c['uri'])] - - if conns: - # Get connection with matching address, otherwise return first connection - conn = next((c for c in conns if c['address'] == server['pms_ip'] - and c['port'] == str(server['pms_port'])), conns[0]) - server['pms_url'] = conn['uri'] - logger.info("Tautulli PlexTV :: Server URL retrieved.") - - # get_server_urls() failed or PMS_URL not found, fallback url doesn't use SSL - if not server['pms_url']: - server['pms_url'] = fallback_url - logger.warn("Tautulli PlexTV :: Unable to retrieve server URLs. Using user-defined value without SSL.") - - # Not using SSL, remote has no effect - else: - server['pms_url'] = fallback_url - logger.info("Tautulli PlexTV :: Using user-defined URL.") - - if return_server: - return server - - logger.info("Tautulli PlexTV :: Selected server: %s (%s) (%s - Version %s)", - server['pms_name'], server['pms_url'], server['pms_platform'], server['pms_version']) - - jellypy.CONFIG.process_kwargs(server) - jellypy.CONFIG.write() - - -class PlexTV(object): - """ - Plex.tv authentication - """ - - def __init__(self, username=None, password=None, token=None, headers=None): - self.username = username - self.password = password - self.token = token - - self.urls = 'https://plex.tv' - self.timeout = jellypy.CONFIG.PMS_TIMEOUT - self.ssl_verify = jellypy.CONFIG.VERIFY_SSL_CERT - - if self.username is None and self.password is None: - if not self.token: - # Check if we should use the admin token, or the guest server token - if session.get_session_user_id(): - user_data = users.Users() - user_tokens = user_data.get_tokens(user_id=session.get_session_user_id()) - self.token = user_tokens['server_token'] - else: - self.token = jellypy.CONFIG.PMS_TOKEN - - if not self.token: - logger.error("Tautulli PlexTV :: PlexTV called, but no token provided.") - return - - self.request_handler = http_handler.HTTPHandler(urls=self.urls, - token=self.token, - timeout=self.timeout, - ssl_verify=self.ssl_verify, - headers=headers) - - def get_plex_auth(self, output_format='raw'): - uri = '/api/v2/users/signin' - headers = {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', - 'Accept': 'application/xml'} - data = {'login': self.username, - 'password': self.password, - 'rememberMe': True} - - request = self.request_handler.make_request(uri=uri, - request_type='POST', - headers=headers, - data=data, - output_format=output_format, - no_token=True, - encode_multipart=False) - - return request - - def get_token(self): - plextv_response = self.get_plex_auth(output_format='xml') - - if plextv_response: - try: - xml_head = plextv_response.getElementsByTagName('user') - if xml_head: - user = {'auth_token': xml_head[0].getAttribute('authToken'), - 'user_id': xml_head[0].getAttribute('id') - } - else: - logger.warn("Tautulli PlexTV :: Could not get Plex authentication token.") - except Exception as e: - logger.warn("Tautulli PlexTV :: Unable to parse XML for get_token: %s." % e) - return None - - return user - else: - return None - - def get_plexpy_pms_token(self, force=False): - if force: - logger.debug("Tautulli PlexTV :: Forcing refresh of Plex.tv token.") - devices_list = self.get_devices_list() - device_id = next((d for d in devices_list if d['device_identifier'] == jellypy.CONFIG.PMS_UUID), {}).get( - 'device_id', None) - - if device_id: - logger.debug("Tautulli PlexTV :: Removing Tautulli from Plex.tv devices.") - try: - self.delete_plextv_device(device_id=device_id) - except: - logger.error("Tautulli PlexTV :: Failed to remove Tautulli from Plex.tv devices.") - return None - else: - logger.warn("Tautulli PlexTV :: No existing Tautulli device found.") - - logger.info("Tautulli PlexTV :: Fetching a new Plex.tv token for Tautulli.") - user = self.get_token() - if user: - token = user['auth_token'] - jellypy.CONFIG.__setattr__('PMS_TOKEN', token) - jellypy.CONFIG.write() - logger.info("Tautulli PlexTV :: Updated Plex.tv token for Tautulli.") - return token - - def get_server_token(self): - servers = self.get_plextv_resources(output_format='xml') - server_token = '' - - try: - xml_head = servers.getElementsByTagName('Device') - except Exception as e: - logger.warn("Tautulli PlexTV :: Unable to parse XML for get_server_token: %s." % e) - return None - - for a in xml_head: - if helpers.get_xml_attr(a, 'clientIdentifier') == jellypy.CONFIG.PMS_IDENTIFIER \ - and 'server' in helpers.get_xml_attr(a, 'provides'): - server_token = helpers.get_xml_attr(a, 'accessToken') - break - - return server_token - - def get_plextv_pin(self, pin='', output_format=''): - if pin: - uri = '/api/v2/pins/' + pin - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format, - no_token=True) - else: - uri = '/api/v2/pins?strong=true' - request = self.request_handler.make_request(uri=uri, - request_type='POST', - output_format=output_format, - no_token=True) - return request - - def get_pin(self, pin=''): - plextv_response = self.get_plextv_pin(pin=pin, - output_format='xml') - - if plextv_response: - try: - xml_head = plextv_response.getElementsByTagName('pin') - if xml_head: - pin = {'id': xml_head[0].getAttribute('id'), - 'code': xml_head[0].getAttribute('code'), - 'token': xml_head[0].getAttribute('authToken') - } - return pin - else: - logger.warn("Tautulli PlexTV :: Could not get Plex authentication pin.") - return None - - except Exception as e: - logger.warn("Tautulli PlexTV :: Unable to parse XML for get_pin: %s." % e) - return None - - else: - return None - - def get_plextv_user_data(self): - plextv_response = self.get_plex_auth(output_format='dict') - - if plextv_response: - return plextv_response - else: - return [] - - def get_plextv_friends(self, output_format=''): - uri = '/api/users' - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_plextv_user_details(self, output_format=''): - uri = '/users/account' - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_plextv_devices_list(self, output_format=''): - uri = '/devices.xml' - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_plextv_server_list(self, output_format=''): - uri = '/pms/servers.xml' - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_plextv_shared_servers(self, machine_id='', output_format=''): - uri = '/api/servers/%s/shared_servers' % machine_id - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_plextv_sync_lists(self, machine_id='', output_format=''): - uri = '/servers/%s/sync_lists' % machine_id - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_plextv_resources(self, include_https=False, output_format=''): - if include_https: - uri = '/api/resources?includeHttps=1' - else: - uri = '/api/resources' - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_plextv_downloads(self, plexpass=False, output_format=''): - if plexpass: - uri = '/api/downloads/5.json?channel=plexpass' - else: - uri = '/api/downloads/1.json' - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def delete_plextv_device(self, device_id='', output_format=''): - uri = '/devices/%s.xml' % device_id - request = self.request_handler.make_request(uri=uri, - request_type='DELETE', - output_format=output_format) - - return request - - def delete_plextv_device_sync_lists(self, client_id='', output_format=''): - uri = '/devices/%s/sync_items' % client_id - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def delete_plextv_sync(self, client_id='', sync_id='', output_format=''): - uri = '/devices/%s/sync_items/%s' % (client_id, sync_id) - request = self.request_handler.make_request(uri=uri, - request_type='DELETE', - output_format=output_format) - - return request - - def cloud_server_status(self, output_format=''): - uri = '/api/v2/cloud_server' - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_plextv_geoip(self, ip_address='', output_format=''): - uri = '/api/v2/geoip?ip_address=%s' % ip_address - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_full_users_list(self): - own_account = self.get_plextv_user_details(output_format='xml') - friends_list = self.get_plextv_friends(output_format='xml') - shared_servers = self.get_plextv_shared_servers(machine_id=jellypy.CONFIG.PMS_IDENTIFIER, - output_format='xml') - - users_list = [] - - try: - xml_head = own_account.getElementsByTagName('user') - except Exception as e: - logger.warn("Tautulli PlexTV :: Unable to parse own account XML for get_full_users_list: %s." % e) - return [] - - for a in xml_head: - own_details = {"user_id": helpers.get_xml_attr(a, 'id'), - "username": helpers.get_xml_attr(a, 'username'), - "thumb": helpers.get_xml_attr(a, 'thumb'), - "email": helpers.get_xml_attr(a, 'email'), - "is_active": 1, - "is_admin": 1, - "is_home_user": helpers.get_xml_attr(a, 'home'), - "is_allow_sync": 1, - "is_restricted": helpers.get_xml_attr(a, 'restricted'), - "filter_all": helpers.get_xml_attr(a, 'filterAll'), - "filter_movies": helpers.get_xml_attr(a, 'filterMovies'), - "filter_tv": helpers.get_xml_attr(a, 'filterTelevision'), - "filter_music": helpers.get_xml_attr(a, 'filterMusic'), - "filter_photos": helpers.get_xml_attr(a, 'filterPhotos'), - "user_token": helpers.get_xml_attr(a, 'authToken'), - "server_token": helpers.get_xml_attr(a, 'authToken'), - "shared_libraries": None, - } - - users_list.append(own_details) - - try: - xml_head = friends_list.getElementsByTagName('User') - except Exception as e: - logger.warn("Tautulli PlexTV :: Unable to parse friends list XML for get_full_users_list: %s." % e) - return [] - - for a in xml_head: - friend = {"user_id": helpers.get_xml_attr(a, 'id'), - "username": helpers.get_xml_attr(a, 'title'), - "thumb": helpers.get_xml_attr(a, 'thumb'), - "email": helpers.get_xml_attr(a, 'email'), - "is_active": 1, - "is_admin": 0, - "is_home_user": helpers.get_xml_attr(a, 'home'), - "is_allow_sync": helpers.get_xml_attr(a, 'allowSync'), - "is_restricted": helpers.get_xml_attr(a, 'restricted'), - "filter_all": helpers.get_xml_attr(a, 'filterAll'), - "filter_movies": helpers.get_xml_attr(a, 'filterMovies'), - "filter_tv": helpers.get_xml_attr(a, 'filterTelevision'), - "filter_music": helpers.get_xml_attr(a, 'filterMusic'), - "filter_photos": helpers.get_xml_attr(a, 'filterPhotos') - } - - users_list.append(friend) - - try: - xml_head = shared_servers.getElementsByTagName('SharedServer') - except Exception as e: - logger.warn("Tautulli PlexTV :: Unable to parse shared server list XML for get_full_users_list: %s." % e) - return [] - - user_map = {} - for a in xml_head: - user_id = helpers.get_xml_attr(a, 'userID') - server_token = helpers.get_xml_attr(a, 'accessToken') - - sections = a.getElementsByTagName('Section') - shared_libraries = [helpers.get_xml_attr(s, 'key') - for s in sections if helpers.get_xml_attr(s, 'shared') == '1'] - - user_map[user_id] = {'server_token': server_token, - 'shared_libraries': shared_libraries} - - for u in users_list: - d = user_map.get(u['user_id'], {}) - u.update(d) - - return users_list - - def get_synced_items(self, machine_id=None, client_id_filter=None, user_id_filter=None, - rating_key_filter=None, sync_id_filter=None): - - if not machine_id: - machine_id = jellypy.CONFIG.PMS_IDENTIFIER - - if isinstance(rating_key_filter, list): - rating_key_filter = [str(k) for k in rating_key_filter] - elif rating_key_filter: - rating_key_filter = [str(rating_key_filter)] - - if isinstance(user_id_filter, list): - user_id_filter = [str(k) for k in user_id_filter] - elif user_id_filter: - user_id_filter = [str(user_id_filter)] - - sync_list = self.get_plextv_sync_lists(machine_id, output_format='xml') - user_data = users.Users() - - synced_items = [] - - try: - xml_head = sync_list.getElementsByTagName('SyncList') - except Exception as e: - logger.warn("Tautulli PlexTV :: Unable to parse XML for get_synced_items: %s." % e) - return {} - - for a in xml_head: - client_id = helpers.get_xml_attr(a, 'clientIdentifier') - - # Filter by client_id - if client_id_filter and str(client_id_filter) != client_id: - continue - - sync_list_id = helpers.get_xml_attr(a, 'id') - sync_device = a.getElementsByTagName('Device') - - for device in sync_device: - device_user_id = helpers.get_xml_attr(device, 'userID') - try: - device_username = user_data.get_details(user_id=device_user_id)['username'] - device_friendly_name = user_data.get_details(user_id=device_user_id)['friendly_name'] - except: - device_username = '' - device_friendly_name = '' - device_name = helpers.get_xml_attr(device, 'name') - device_product = helpers.get_xml_attr(device, 'product') - device_product_version = helpers.get_xml_attr(device, 'productVersion') - device_platform = helpers.get_xml_attr(device, 'platform') - device_platform_version = helpers.get_xml_attr(device, 'platformVersion') - device_type = helpers.get_xml_attr(device, 'device') - device_model = helpers.get_xml_attr(device, 'model') - device_last_seen = helpers.get_xml_attr(device, 'lastSeenAt') - - # Filter by user_id - if user_id_filter and device_user_id not in user_id_filter: - continue - - for synced in a.getElementsByTagName('SyncItems'): - sync_item = synced.getElementsByTagName('SyncItem') - for item in sync_item: - - for location in item.getElementsByTagName('Location'): - clean_uri = helpers.get_xml_attr(location, 'uri').split('%2F') - - rating_key = next((clean_uri[(idx + 1) % len(clean_uri)] - for idx, item in enumerate(clean_uri) if item == 'metadata'), None) - - # Filter by rating_key - if rating_key_filter and rating_key not in rating_key_filter: - continue - - sync_id = helpers.get_xml_attr(item, 'id') - - # Filter by sync_id - if sync_id_filter and str(sync_id_filter) != sync_id: - continue - - sync_version = helpers.get_xml_attr(item, 'version') - sync_root_title = helpers.get_xml_attr(item, 'rootTitle') - sync_title = helpers.get_xml_attr(item, 'title') - sync_metadata_type = helpers.get_xml_attr(item, 'metadataType') - sync_content_type = helpers.get_xml_attr(item, 'contentType') - - for status in item.getElementsByTagName('Status'): - status_failure_code = helpers.get_xml_attr(status, 'failureCode') - status_failure = helpers.get_xml_attr(status, 'failure') - status_state = helpers.get_xml_attr(status, 'state') - status_item_count = helpers.get_xml_attr(status, 'itemsCount') - status_item_complete_count = helpers.get_xml_attr(status, 'itemsCompleteCount') - status_item_downloaded_count = helpers.get_xml_attr(status, 'itemsDownloadedCount') - status_item_ready_count = helpers.get_xml_attr(status, 'itemsReadyCount') - status_item_successful_count = helpers.get_xml_attr(status, 'itemsSuccessfulCount') - status_total_size = helpers.get_xml_attr(status, 'totalSize') - status_item_download_percent_complete = helpers.get_percent( - status_item_downloaded_count, status_item_count) - - for settings in item.getElementsByTagName('MediaSettings'): - settings_video_bitrate = helpers.get_xml_attr(settings, 'maxVideoBitrate') - settings_video_quality = helpers.get_xml_attr(settings, 'videoQuality') - settings_video_resolution = helpers.get_xml_attr(settings, 'videoResolution') - settings_audio_boost = helpers.get_xml_attr(settings, 'audioBoost') - settings_audio_bitrate = helpers.get_xml_attr(settings, 'musicBitrate') - settings_photo_quality = helpers.get_xml_attr(settings, 'photoQuality') - settings_photo_resolution = helpers.get_xml_attr(settings, 'photoResolution') - - sync_details = {"device_name": device_name, - "platform": device_platform, - "user_id": device_user_id, - "user": device_friendly_name, - "username": device_username, - "root_title": sync_root_title, - "sync_title": sync_title, - "metadata_type": sync_metadata_type, - "content_type": sync_content_type, - "rating_key": rating_key, - "state": status_state, - "item_count": status_item_count, - "item_complete_count": status_item_complete_count, - "item_downloaded_count": status_item_downloaded_count, - "item_downloaded_percent_complete": status_item_download_percent_complete, - "video_bitrate": settings_video_bitrate, - "audio_bitrate": settings_audio_bitrate, - "photo_quality": settings_photo_quality, - "video_quality": settings_video_quality, - "total_size": status_total_size, - "failure": status_failure, - "client_id": client_id, - "sync_id": sync_id - } - - synced_items.append(sync_details) - - return session.filter_session_info(synced_items, filter_key='user_id') - - def delete_sync(self, client_id, sync_id): - logger.info("Tautulli PlexTV :: Deleting sync item '%s'." % sync_id) - self.delete_plextv_sync(client_id=client_id, sync_id=sync_id) - - def get_server_connections(self, pms_identifier='', pms_ip='', pms_port=32400, include_https=True): - - if not pms_identifier: - logger.error("Tautulli PlexTV :: Unable to retrieve server connections: no pms_identifier provided.") - return {} - - plextv_resources = self.get_plextv_resources(include_https=include_https, - output_format='xml') - try: - xml_head = plextv_resources.getElementsByTagName('Device') - except Exception as e: - logger.warn("Tautulli PlexTV :: Unable to parse XML for get_server_urls: %s." % e) - return {} - - # Function to get all connections for a device - def get_connections(device): - conn = [] - connections = device.getElementsByTagName('Connection') - - server = {'pms_identifier': helpers.get_xml_attr(device, 'clientIdentifier'), - 'pms_name': helpers.get_xml_attr(device, 'name'), - 'pms_version': helpers.get_xml_attr(device, 'productVersion'), - 'pms_platform': helpers.get_xml_attr(device, 'platform'), - 'pms_presence': helpers.get_xml_attr(device, 'presence'), - 'pms_is_cloud': 1 if helpers.get_xml_attr(device, 'platform') == 'Cloud' else 0 - } - - for c in connections: - server_details = {'protocol': helpers.get_xml_attr(c, 'protocol'), - 'address': helpers.get_xml_attr(c, 'address'), - 'port': helpers.get_xml_attr(c, 'port'), - 'uri': helpers.get_xml_attr(c, 'uri'), - 'local': helpers.get_xml_attr(c, 'local') - } - conn.append(server_details) - - server['connections'] = conn - return server - - server = {} - - # Try to match the device - for a in xml_head: - if helpers.get_xml_attr(a, 'clientIdentifier') == pms_identifier: - server = get_connections(a) - break - - # Else no device match found - if not server: - # Try to match the PMS_IP and PMS_PORT - for a in xml_head: - if helpers.get_xml_attr(a, 'provides') == 'server': - connections = a.getElementsByTagName('Connection') - - for connection in connections: - if helpers.get_xml_attr(connection, 'address') == pms_ip and \ - helpers.get_xml_attr(connection, 'port') == str(pms_port): - server = get_connections(a) - break - - if server.get('connections'): - break - - return server - - def get_server_times(self): - servers = self.get_plextv_server_list(output_format='xml') - server_times = {} - - try: - xml_head = servers.getElementsByTagName('Server') - except Exception as e: - logger.warn("Tautulli PlexTV :: Unable to parse XML for get_server_times: %s." % e) - return {} - - for a in xml_head: - if helpers.get_xml_attr(a, 'machineIdentifier') == jellypy.CONFIG.PMS_IDENTIFIER: - server_times = {"created_at": helpers.get_xml_attr(a, 'createdAt'), - "updated_at": helpers.get_xml_attr(a, 'updatedAt'), - "version": helpers.get_xml_attr(a, 'version') - } - break - - return server_times - - def discover(self, include_cloud=True, all_servers=False): - """ Query plex for all servers online. Returns the ones you own in a selectize format """ - - # Try to discover localhost server - local_machine_identifier = None - request_handler = http_handler.HTTPHandler(urls='http://127.0.0.1:32400', timeout=1, - ssl_verify=False, silent=True) - request = request_handler.make_request(uri='/identity', request_type='GET', output_format='xml') - if request: - xml_head = request.getElementsByTagName('MediaContainer')[0] - local_machine_identifier = xml_head.getAttribute('machineIdentifier') - - local_server = {'httpsRequired': '0', - 'clientIdentifier': local_machine_identifier, - 'label': 'Local', - 'ip': '127.0.0.1', - 'port': '32400', - 'uri': 'http://127.0.0.1:32400', - 'local': '1', - 'value': '127.0.0.1:32400', - 'is_cloud': False - } - - servers = self.get_plextv_resources(include_https=True, output_format='xml') - clean_servers = [] - - try: - xml_head = servers.getElementsByTagName('MediaContainer') - except Exception as e: - logger.warn("Tautulli PlexTV :: Failed to get servers from plex: %s." % e) - return [] - - for a in xml_head: - if a.getAttribute('size'): - if a.getAttribute('size') == '0': - return [] - - if a.getElementsByTagName('Device'): - devices = a.getElementsByTagName('Device') - - for d in devices: - if helpers.get_xml_attr(d, 'presence') == '1' and \ - helpers.get_xml_attr(d, 'owned') == '1' and \ - helpers.get_xml_attr(d, 'provides') == 'server': - - is_cloud = (helpers.get_xml_attr(d, 'platform').lower() == 'cloud') - if not include_cloud and is_cloud: - continue - - connections = d.getElementsByTagName('Connection') - - for c in connections: - if not all_servers: - # If this is a remote server don't show any local IPs. - if helpers.get_xml_attr(d, 'publicAddressMatches') == '0' and \ - helpers.get_xml_attr(c, 'local') == '1': - continue - - # If this is a local server don't show any remote IPs. - if helpers.get_xml_attr(d, 'publicAddressMatches') == '1' and \ - helpers.get_xml_attr(c, 'local') == '0': - continue - - if helpers.get_xml_attr(d, 'clientIdentifier') == local_machine_identifier: - local_server['httpsRequired'] = helpers.get_xml_attr(d, 'httpsRequired') - local_server['label'] = helpers.get_xml_attr(d, 'name') - clean_servers.append(local_server) - local_machine_identifier = None - - server = {'httpsRequired': '1' if is_cloud else helpers.get_xml_attr(d, 'httpsRequired'), - 'clientIdentifier': helpers.get_xml_attr(d, 'clientIdentifier'), - 'label': helpers.get_xml_attr(d, 'name'), - 'ip': helpers.get_xml_attr(c, 'address'), - 'port': helpers.get_xml_attr(c, 'port'), - 'uri': helpers.get_xml_attr(c, 'uri'), - 'local': helpers.get_xml_attr(c, 'local'), - 'value': helpers.get_xml_attr(c, 'address') + ':' + helpers.get_xml_attr(c, - 'port'), - 'is_cloud': is_cloud - } - clean_servers.append(server) - - if local_machine_identifier: - clean_servers.append(local_server) - - clean_servers.sort(key=lambda s: (s['label'], -int(s['local']), s['ip'])) - - return clean_servers - - def get_plex_downloads(self): - logger.debug("Tautulli PlexTV :: Retrieving current server version.") - - pms_connect = pmsconnect.PmsConnect() - pms_connect.set_server_version() - - update_channel = pms_connect.get_server_update_channel() - - logger.debug("Tautulli PlexTV :: Plex update channel is %s." % update_channel) - plex_downloads = self.get_plextv_downloads(plexpass=(update_channel == 'beta')) - - try: - available_downloads = json.loads(plex_downloads) - except Exception as e: - logger.warn("Tautulli PlexTV :: Unable to load JSON for get_plex_updates.") - return {} - - # Get the updates for the platform - pms_platform = common.PMS_PLATFORM_NAME_OVERRIDES.get(jellypy.CONFIG.PMS_PLATFORM, jellypy.CONFIG.PMS_PLATFORM) - platform_downloads = available_downloads.get('computer').get(pms_platform) or \ - available_downloads.get('nas').get(pms_platform) - - if not platform_downloads: - logger.error("Tautulli PlexTV :: Unable to retrieve Plex updates: Could not match server platform: %s." - % pms_platform) - return {} - - v_old = helpers.cast_to_int( - "".join(v.zfill(4) for v in jellypy.CONFIG.PMS_VERSION.split('-')[0].split('.')[:4])) - v_new = helpers.cast_to_int( - "".join(v.zfill(4) for v in platform_downloads.get('version', '').split('-')[0].split('.')[:4])) - - if not v_old: - logger.error("Tautulli PlexTV :: Unable to retrieve Plex updates: Invalid current server version: %s." - % jellypy.CONFIG.PMS_VERSION) - return {} - if not v_new: - logger.error("Tautulli PlexTV :: Unable to retrieve Plex updates: Invalid new server version: %s." - % platform_downloads.get('version')) - return {} - - # Get proper download - releases = platform_downloads.get('releases', [{}]) - release = next((r for r in releases if r['distro'] == jellypy.CONFIG.PMS_UPDATE_DISTRO and - r['build'] == jellypy.CONFIG.PMS_UPDATE_DISTRO_BUILD), releases[0]) - - download_info = {'update_available': v_new > v_old, - 'platform': platform_downloads.get('name'), - 'release_date': platform_downloads.get('release_date'), - 'version': platform_downloads.get('version'), - 'requirements': platform_downloads.get('requirements'), - 'extra_info': platform_downloads.get('extra_info'), - 'changelog_added': platform_downloads.get('items_added'), - 'changelog_fixed': platform_downloads.get('items_fixed'), - 'label': release.get('label'), - 'distro': release.get('distro'), - 'distro_build': release.get('build'), - 'download_url': release.get('url'), - } - - return download_info - - def get_plexpass_status(self): - account_data = self.get_plextv_user_details(output_format='xml') - - try: - subscription = account_data.getElementsByTagName('subscription') - except Exception as e: - logger.warn("Tautulli PlexTV :: Unable to parse XML for get_plexpass_status: %s." % e) - return False - - if subscription and helpers.get_xml_attr(subscription[0], 'active') == '1': - jellypy.CONFIG.__setattr__('PMS_PLEXPASS', 1) - jellypy.CONFIG.write() - return True - else: - logger.debug("Tautulli PlexTV :: Plex Pass subscription not found.") - jellypy.CONFIG.__setattr__('PMS_PLEXPASS', 0) - jellypy.CONFIG.write() - return False - - def get_devices_list(self): - devices = self.get_plextv_devices_list(output_format='xml') - - try: - xml_head = devices.getElementsByTagName('Device') - except Exception as e: - logger.warn("Tautulli PlexTV :: Unable to parse XML for get_devices_list: %s." % e) - return [] - - devices_list = [] - for a in xml_head: - device = {"device_name": helpers.get_xml_attr(a, 'name'), - "product": helpers.get_xml_attr(a, 'product'), - "product_version": helpers.get_xml_attr(a, 'productVersion'), - "platform": helpers.get_xml_attr(a, 'platform'), - "platform_version": helpers.get_xml_attr(a, 'platformVersion'), - "device": helpers.get_xml_attr(a, 'device'), - "model": helpers.get_xml_attr(a, 'model'), - "vendor": helpers.get_xml_attr(a, 'vendor'), - "provides": helpers.get_xml_attr(a, 'provides'), - "device_identifier": helpers.get_xml_attr(a, 'clientIdentifier'), - "device_id": helpers.get_xml_attr(a, 'id'), - "token": helpers.get_xml_attr(a, 'token') - } - devices_list.append(device) - - return devices_list - - def get_cloud_server_status(self): - cloud_status = self.cloud_server_status(output_format='xml') - - try: - status_info = cloud_status.getElementsByTagName('info') - except Exception as e: - logger.warn("Tautulli PlexTV :: Unable to parse XML for get_cloud_server_status: %s." % e) - return False - - for info in status_info: - servers = info.getElementsByTagName('server') - for s in servers: - if helpers.get_xml_attr(s, 'address') == jellypy.CONFIG.PMS_IP: - if helpers.get_xml_attr(info, 'running') == '1': - return True - else: - return False - - def get_plex_account_details(self): - account_data = self.get_plextv_user_details(output_format='xml') - - try: - xml_head = account_data.getElementsByTagName('user') - except Exception as e: - logger.warn("Tautulli PlexTV :: Unable to parse XML for get_plex_account_details: %s." % e) - return None - - for a in xml_head: - account_details = {"user_id": helpers.get_xml_attr(a, 'id'), - "username": helpers.get_xml_attr(a, 'username'), - "thumb": helpers.get_xml_attr(a, 'thumb'), - "email": helpers.get_xml_attr(a, 'email'), - "is_home_user": helpers.get_xml_attr(a, 'home'), - "is_restricted": helpers.get_xml_attr(a, 'restricted'), - "filter_all": helpers.get_xml_attr(a, 'filterAll'), - "filter_movies": helpers.get_xml_attr(a, 'filterMovies'), - "filter_tv": helpers.get_xml_attr(a, 'filterTelevision'), - "filter_music": helpers.get_xml_attr(a, 'filterMusic'), - "filter_photos": helpers.get_xml_attr(a, 'filterPhotos'), - "user_token": helpers.get_xml_attr(a, 'authToken') - } - return account_details - - def get_geoip_lookup(self, ip_address=''): - if not ip_address or not helpers.is_valid_ip(ip_address): - return - - geoip_data = self.get_plextv_geoip(ip_address=ip_address, output_format='xml') - - try: - xml_head = geoip_data.getElementsByTagName('location') - except Exception as e: - logger.warn(u"Tautulli PlexTV :: Unable to parse XML for get_geoip_lookup: %s." % e) - return None - - for a in xml_head: - coordinates = helpers.get_xml_attr(a, 'coordinates').split(',') - latitude = longitude = None - if len(coordinates) == 2: - latitude, longitude = [helpers.cast_to_float(c) for c in coordinates] - - geo_info = {"code": helpers.get_xml_attr(a, 'code') or None, - "country": helpers.get_xml_attr(a, 'country') or None, - "region": helpers.get_xml_attr(a, 'subdivisions') or None, - "city": helpers.get_xml_attr(a, 'city') or None, - "postal_code": helpers.get_xml_attr(a, 'postal_code') or None, - "timezone": helpers.get_xml_attr(a, 'time_zone') or None, - "latitude": latitude, - "longitude": longitude, - "continent": None, # keep for backwards compatibility with GeoLite2 - "accuracy": None # keep for backwards compatibility with GeoLite2 - } - - return geo_info diff --git a/jellypy/pmsconnect.py b/jellypy/pmsconnect.py deleted file mode 100644 index 5edb8a8d..00000000 --- a/jellypy/pmsconnect.py +++ /dev/null @@ -1,3173 +0,0 @@ -# -*- coding: utf-8 -*- - -# This file is part of Tautulli. -# -# Tautulli is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Tautulli is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Tautulli. If not, see . - -import json -import os -from urllib.parse import quote_plus, quote, urlencode -from xml.dom.minidom import Node - -import jellypy -from jellypy import activity_processor -from jellypy import common -from jellypy import helpers -from jellypy import http_handler -from jellypy import libraries -from jellypy import logger -from jellypy import plextv -from jellypy import session -from jellypy import users - - -def get_server_friendly_name(): - logger.info("Tautulli Pmsconnect :: Requesting name from server...") - server_name = PmsConnect().get_server_pref(pref='FriendlyName') - - # If friendly name is blank - if not server_name: - servers_info = PmsConnect().get_servers_info() - for server in servers_info: - if server['machine_identifier'] == jellypy.CONFIG.PMS_IDENTIFIER: - server_name = server['name'] - break - - if server_name and server_name != jellypy.CONFIG.PMS_NAME: - jellypy.CONFIG.__setattr__('PMS_NAME', server_name) - jellypy.CONFIG.write() - logger.info("Tautulli Pmsconnect :: Server name retrieved.") - - return server_name - - -class PmsConnect(object): - """ - Retrieve data from Plex Server - """ - - def __init__(self, url=None, token=None): - self.url = url - self.token = token - - if not self.url and jellypy.CONFIG.PMS_URL: - self.url = jellypy.CONFIG.PMS_URL - elif not self.url: - self.url = 'http://{hostname}:{port}'.format(hostname=jellypy.CONFIG.PMS_IP, - port=jellypy.CONFIG.PMS_PORT) - self.timeout = jellypy.CONFIG.PMS_TIMEOUT - - if not self.token: - # Check if we should use the admin token, or the guest server token - if session.get_session_user_id(): - user_data = users.Users() - user_tokens = user_data.get_tokens(user_id=session.get_session_user_id()) - self.token = user_tokens['server_token'] - else: - self.token = jellypy.CONFIG.PMS_TOKEN - - self.request_handler = http_handler.HTTPHandler(urls=self.url, - token=self.token, - timeout=self.timeout) - - def get_sessions(self, output_format=''): - """ - Return current sessions. - - Optional parameters: output_format { dict, json } - - Output: array - """ - uri = '/status/sessions' - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_sessions_terminate(self, session_id='', reason='', output_format=''): - """ - Return current sessions. - - Optional parameters: output_format { dict, json } - - Output: array - """ - uri = '/status/sessions/terminate?sessionId=%s&reason=%s' % (session_id, quote_plus(reason)) - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_metadata(self, rating_key='', output_format=''): - """ - Return metadata for request item. - - Parameters required: rating_key { Plex ratingKey } - Optional parameters: output_format { dict, json } - - Output: array - """ - uri = '/library/metadata/' + rating_key - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_metadata_children(self, rating_key='', output_format=''): - """ - Return metadata for children of the request item. - - Parameters required: rating_key { Plex ratingKey } - Optional parameters: output_format { dict, json } - - Output: array - """ - uri = '/library/metadata/' + rating_key + '/children' - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_metadata_grandchildren(self, rating_key='', output_format=''): - """ - Return metadata for graandchildren of the request item. - - Parameters required: rating_key { Plex ratingKey } - Optional parameters: output_format { dict, json } - - Output: array - """ - uri = '/library/metadata/' + rating_key + '/grandchildren' - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_playlist_items(self, rating_key='', output_format=''): - """ - Return metadata for items of the requested playlist. - - Parameters required: rating_key { Plex ratingKey } - Optional parameters: output_format { dict, json } - - Output: array - """ - uri = '/playlists/' + rating_key + '/items' - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_recently_added(self, start='0', count='0', output_format=''): - """ - Return list of recently added items. - - Parameters required: count { number of results to return } - Optional parameters: output_format { dict, json } - - Output: array - """ - uri = '/library/recentlyAdded?X-Plex-Container-Start=%s&X-Plex-Container-Size=%s' % (start, count) - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_library_recently_added(self, section_id='', start='0', count='0', output_format=''): - """ - Return list of recently added items. - - Parameters required: count { number of results to return } - Optional parameters: output_format { dict, json } - - Output: array - """ - uri = '/library/sections/%s/recentlyAdded?X-Plex-Container-Start=%s&X-Plex-Container-Size=%s' % ( - section_id, start, count) - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_children_list_related(self, rating_key='', output_format=''): - """ - Return list of related children in requested collection item. - - Parameters required: rating_key { ratingKey of parent } - Optional parameters: output_format { dict, json } - - Output: array - """ - uri = '/hubs/metadata/' + rating_key + '/related' - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_childrens_list(self, rating_key='', output_format=''): - """ - Return list of children in requested library item. - - Parameters required: rating_key { ratingKey of parent } - Optional parameters: output_format { dict, json } - - Output: array - """ - uri = '/library/metadata/' + rating_key + '/allLeaves' - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_server_list(self, output_format=''): - """ - Return list of local servers. - - Optional parameters: output_format { dict, json } - - Output: array - """ - uri = '/servers' - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_server_prefs(self, output_format=''): - """ - Return the local servers preferences. - - Optional parameters: output_format { dict, json } - - Output: array - """ - uri = '/:/prefs' - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_local_server_identity(self, output_format=''): - """ - Return the local server identity. - - Optional parameters: output_format { dict, json } - - Output: array - """ - uri = '/identity' - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_libraries_list(self, output_format=''): - """ - Return list of libraries on server. - - Optional parameters: output_format { dict, json } - - Output: array - """ - uri = '/library/sections' - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_library_list(self, section_id='', list_type='all', count='0', sort_type='', label_key='', output_format=''): - """ - Return list of items in library on server. - - Optional parameters: output_format { dict, json } - - Output: array - """ - count = '&X-Plex-Container-Size=' + count if count else '' - label_key = '&label=' + label_key if label_key else '' - - uri = '/library/sections/' + section_id + '/' + list_type + '?X-Plex-Container-Start=0' + count + sort_type + label_key - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_library_labels(self, section_id='', output_format=''): - """ - Return list of labels for a library on server. - - Optional parameters: output_format { dict, json } - - Output: array - """ - uri = '/library/sections/' + section_id + '/label' - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_sync_item(self, sync_id='', output_format=''): - """ - Return sync item details. - - Parameters required: sync_id { unique sync id for item } - Optional parameters: output_format { dict, json } - - Output: array - """ - uri = '/sync/items/' + sync_id - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_sync_transcode_queue(self, output_format=''): - """ - Return sync transcode queue. - - Optional parameters: output_format { dict, json } - - Output: array - """ - uri = '/sync/transcodeQueue' - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_search(self, query='', limit='', output_format=''): - """ - Return search results. - - Optional parameters: output_format { dict, json } - - Output: array - """ - uri = '/hubs/search?query=' + quote(query.encode('utf8')) + '&limit=' + limit + '&includeCollections=1' - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_account(self, output_format=''): - """ - Return account details. - - Optional parameters: output_format { dict, json } - - Output: array - """ - uri = '/myplex/account' - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def put_refresh_reachability(self): - """ - Refresh Plex remote access port mapping. - - Optional parameters: None - - Output: None - """ - uri = '/myplex/refreshReachability' - request = self.request_handler.make_request(uri=uri, - request_type='PUT') - - return request - - def put_updater(self, output_format=''): - """ - Refresh updater status. - - Optional parameters: output_format { dict, json } - - Output: array - """ - uri = '/updater/check?download=0' - request = self.request_handler.make_request(uri=uri, - request_type='PUT', - output_format=output_format) - - return request - - def get_updater(self, output_format=''): - """ - Return updater status. - - Optional parameters: output_format { dict, json } - - Output: array - """ - uri = '/updater/status' - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_hub_recently_added(self, start='0', count='0', media_type='', other_video=False, output_format=''): - """ - Return Plex hub recently added. - - Parameters required: start { item number to start from } - count { number of results to return } - media_type { str } - Optional parameters: output_format { dict, json } - - Output: array - """ - personal = '&personal=1' if other_video else '' - uri = '/hubs/home/recentlyAdded?X-Plex-Container-Start=%s&X-Plex-Container-Size=%s&type=%s%s' \ - % (start, count, media_type, personal) - request = self.request_handler.make_request(uri=uri, - request_type='GET', - output_format=output_format) - - return request - - def get_recently_added_details(self, start='0', count='0', media_type='', section_id=''): - """ - Return processed and validated list of recently added items. - - Parameters required: count { number of results to return } - - Output: array - """ - media_types = ('movie', 'show', 'artist', 'other_video') - recents_list = [] - - if media_type in media_types: - other_video = False - if media_type == 'movie': - media_type = '1' - elif media_type == 'show': - media_type = '2' - elif media_type == 'artist': - media_type = '8' - elif media_type == 'other_video': - media_type = '1' - other_video = True - recent = self.get_hub_recently_added(start, count, media_type, other_video, output_format='xml') - elif section_id: - recent = self.get_library_recently_added(section_id, start, count, output_format='xml') - else: - for media_type in media_types: - recents = self.get_recently_added_details(start, count, media_type) - recents_list += recents['recently_added'] - - output = {'recently_added': sorted(recents_list, key=lambda k: k['added_at'], reverse=True)[:int(count)]} - return output - - try: - xml_head = recent.getElementsByTagName('MediaContainer') - except Exception as e: - logger.warn("Tautulli Pmsconnect :: Unable to parse XML for get_recently_added: %s." % e) - return [] - - for a in xml_head: - if a.getAttribute('size'): - if a.getAttribute('size') == '0': - output = {'recently_added': []} - return output - - recents_main = [] - if a.getElementsByTagName('Directory'): - recents_main += a.getElementsByTagName('Directory') - if a.getElementsByTagName('Video'): - recents_main += a.getElementsByTagName('Video') - - for m in recents_main: - directors = [] - writers = [] - actors = [] - genres = [] - labels = [] - collections = [] - guids = [] - - if m.getElementsByTagName('Director'): - for director in m.getElementsByTagName('Director'): - directors.append(helpers.get_xml_attr(director, 'tag')) - - if m.getElementsByTagName('Writer'): - for writer in m.getElementsByTagName('Writer'): - writers.append(helpers.get_xml_attr(writer, 'tag')) - - if m.getElementsByTagName('Role'): - for actor in m.getElementsByTagName('Role'): - actors.append(helpers.get_xml_attr(actor, 'tag')) - - if m.getElementsByTagName('Genre'): - for genre in m.getElementsByTagName('Genre'): - genres.append(helpers.get_xml_attr(genre, 'tag')) - - if m.getElementsByTagName('Label'): - for label in m.getElementsByTagName('Label'): - labels.append(helpers.get_xml_attr(label, 'tag')) - - if m.getElementsByTagName('Collection'): - for collection in m.getElementsByTagName('Collection'): - collections.append(helpers.get_xml_attr(collection, 'tag')) - - if m.getElementsByTagName('Guid'): - for guid in m.getElementsByTagName('Guid'): - guids.append(helpers.get_xml_attr(guid, 'id')) - - recent_item = {'media_type': helpers.get_xml_attr(m, 'type'), - 'section_id': helpers.get_xml_attr(m, 'librarySectionID'), - 'library_name': helpers.get_xml_attr(m, 'librarySectionTitle'), - 'rating_key': helpers.get_xml_attr(m, 'ratingKey'), - 'parent_rating_key': helpers.get_xml_attr(m, 'parentRatingKey'), - 'grandparent_rating_key': helpers.get_xml_attr(m, 'grandparentRatingKey'), - 'title': helpers.get_xml_attr(m, 'title'), - 'parent_title': helpers.get_xml_attr(m, 'parentTitle'), - 'grandparent_title': helpers.get_xml_attr(m, 'grandparentTitle'), - 'original_title': helpers.get_xml_attr(m, 'originalTitle'), - 'sort_title': helpers.get_xml_attr(m, 'titleSort'), - 'media_index': helpers.get_xml_attr(m, 'index'), - 'parent_media_index': helpers.get_xml_attr(m, 'parentIndex'), - 'studio': helpers.get_xml_attr(m, 'studio'), - 'content_rating': helpers.get_xml_attr(m, 'contentRating'), - 'summary': helpers.get_xml_attr(m, 'summary'), - 'tagline': helpers.get_xml_attr(m, 'tagline'), - 'rating': helpers.get_xml_attr(m, 'rating'), - 'rating_image': helpers.get_xml_attr(m, 'ratingImage'), - 'audience_rating': helpers.get_xml_attr(m, 'audienceRating'), - 'audience_rating_image': helpers.get_xml_attr(m, 'audienceRatingImage'), - 'user_rating': helpers.get_xml_attr(m, 'userRating'), - 'duration': helpers.get_xml_attr(m, 'duration'), - 'year': helpers.get_xml_attr(m, 'year'), - 'thumb': helpers.get_xml_attr(m, 'thumb'), - 'parent_thumb': helpers.get_xml_attr(m, 'parentThumb'), - 'grandparent_thumb': helpers.get_xml_attr(m, 'grandparentThumb'), - 'art': helpers.get_xml_attr(m, 'art'), - 'banner': helpers.get_xml_attr(m, 'banner'), - 'originally_available_at': helpers.get_xml_attr(m, 'originallyAvailableAt'), - 'added_at': helpers.get_xml_attr(m, 'addedAt'), - 'updated_at': helpers.get_xml_attr(m, 'updatedAt'), - 'last_viewed_at': helpers.get_xml_attr(m, 'lastViewedAt'), - 'guid': helpers.get_xml_attr(m, 'guid'), - 'directors': directors, - 'writers': writers, - 'actors': actors, - 'genres': genres, - 'labels': labels, - 'collections': collections, - 'guids': guids, - 'full_title': helpers.get_xml_attr(m, 'title'), - 'child_count': helpers.get_xml_attr(m, 'childCount') - } - - recents_list.append(recent_item) - - output = {'recently_added': sorted(recents_list, key=lambda k: k['added_at'], reverse=True)} - - return output - - def get_metadata_details(self, rating_key='', sync_id='', plex_guid='', section_id='', - skip_cache=False, cache_key=None, return_cache=False, media_info=True): - """ - Return processed and validated metadata list for requested item. - - Parameters required: rating_key { Plex ratingKey } - - Output: array - """ - metadata = {} - - if not skip_cache and cache_key: - in_file_folder = os.path.join(jellypy.CONFIG.CACHE_DIR, 'session_metadata') - in_file_path = os.path.join(in_file_folder, 'metadata-sessionKey-%s.json' % cache_key) - - if not os.path.exists(in_file_folder): - os.mkdir(in_file_folder) - - try: - with open(in_file_path, 'r') as inFile: - metadata = json.load(inFile) - except (IOError, ValueError) as e: - pass - - if metadata: - _cache_time = metadata.pop('_cache_time', 0) - # Return cached metadata if less than cache_seconds ago - if return_cache or helpers.timestamp() - _cache_time <= jellypy.CONFIG.METADATA_CACHE_SECONDS: - return metadata - - if rating_key: - metadata_xml = self.get_metadata(str(rating_key), output_format='xml') - elif sync_id: - metadata_xml = self.get_sync_item(str(sync_id), output_format='xml') - elif plex_guid.startswith(('plex://movie', 'plex://episode')): - rating_key = plex_guid.rsplit('/', 1)[-1] - plextv_metadata = PmsConnect(url='https://metadata.provider.plex.tv', token=jellypy.CONFIG.PMS_TOKEN) - metadata_xml = plextv_metadata.get_metadata(rating_key, output_format='xml') - else: - return metadata - - try: - xml_head = metadata_xml.getElementsByTagName('MediaContainer') - except Exception as e: - logger.warn("Tautulli Pmsconnect :: Unable to parse XML for get_metadata_details: %s." % e) - return {} - - for a in xml_head: - if a.getAttribute('size'): - if a.getAttribute('size') == '0': - return metadata - - if a.getElementsByTagName('Directory'): - metadata_main_list = a.getElementsByTagName('Directory') - elif a.getElementsByTagName('Video'): - metadata_main_list = a.getElementsByTagName('Video') - elif a.getElementsByTagName('Track'): - metadata_main_list = a.getElementsByTagName('Track') - elif a.getElementsByTagName('Photo'): - metadata_main_list = a.getElementsByTagName('Photo') - elif a.getElementsByTagName('Playlist'): - metadata_main_list = a.getElementsByTagName('Playlist') - else: - logger.debug("Tautulli Pmsconnect :: Metadata failed") - return {} - - if sync_id and len(metadata_main_list) > 1: - for metadata_main in metadata_main_list: - if helpers.get_xml_attr(metadata_main, 'ratingKey') == rating_key: - break - else: - metadata_main = metadata_main_list[0] - - metadata_type = helpers.get_xml_attr(metadata_main, 'type') - if metadata_main.nodeName == 'Directory' and metadata_type == 'photo': - metadata_type = 'photo_album' - - section_id = helpers.get_xml_attr(a, 'librarySectionID') or section_id - library_name = helpers.get_xml_attr(a, 'librarySectionTitle') - - if not library_name and section_id: - library_data = libraries.Libraries().get_details(section_id) - library_name = library_data['section_name'] - - directors = [] - writers = [] - actors = [] - genres = [] - labels = [] - collections = [] - guids = [] - - if metadata_main.getElementsByTagName('Director'): - for director in metadata_main.getElementsByTagName('Director'): - directors.append(helpers.get_xml_attr(director, 'tag')) - - if metadata_main.getElementsByTagName('Writer'): - for writer in metadata_main.getElementsByTagName('Writer'): - writers.append(helpers.get_xml_attr(writer, 'tag')) - - if metadata_main.getElementsByTagName('Role'): - for actor in metadata_main.getElementsByTagName('Role'): - actors.append(helpers.get_xml_attr(actor, 'tag')) - - if metadata_main.getElementsByTagName('Genre'): - for genre in metadata_main.getElementsByTagName('Genre'): - genres.append(helpers.get_xml_attr(genre, 'tag')) - - if metadata_main.getElementsByTagName('Label'): - for label in metadata_main.getElementsByTagName('Label'): - labels.append(helpers.get_xml_attr(label, 'tag')) - - if metadata_main.getElementsByTagName('Collection'): - for collection in metadata_main.getElementsByTagName('Collection'): - collections.append(helpers.get_xml_attr(collection, 'tag')) - - if metadata_main.getElementsByTagName('Guid'): - for guid in metadata_main.getElementsByTagName('Guid'): - guids.append(helpers.get_xml_attr(guid, 'id')) - - if metadata_type == 'movie': - metadata = {'media_type': metadata_type, - 'section_id': section_id, - 'library_name': library_name, - 'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'), - 'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'), - 'grandparent_rating_key': helpers.get_xml_attr(metadata_main, 'grandparentRatingKey'), - 'title': helpers.get_xml_attr(metadata_main, 'title'), - 'parent_title': helpers.get_xml_attr(metadata_main, 'parentTitle'), - 'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'), - 'original_title': helpers.get_xml_attr(metadata_main, 'originalTitle'), - 'sort_title': helpers.get_xml_attr(metadata_main, 'titleSort'), - 'media_index': helpers.get_xml_attr(metadata_main, 'index'), - 'parent_media_index': helpers.get_xml_attr(metadata_main, 'parentIndex'), - 'studio': helpers.get_xml_attr(metadata_main, 'studio'), - 'content_rating': helpers.get_xml_attr(metadata_main, 'contentRating'), - 'summary': helpers.get_xml_attr(metadata_main, 'summary'), - 'tagline': helpers.get_xml_attr(metadata_main, 'tagline'), - 'rating': helpers.get_xml_attr(metadata_main, 'rating'), - 'rating_image': helpers.get_xml_attr(metadata_main, 'ratingImage'), - 'audience_rating': helpers.get_xml_attr(metadata_main, 'audienceRating'), - 'audience_rating_image': helpers.get_xml_attr(metadata_main, 'audienceRatingImage'), - 'user_rating': helpers.get_xml_attr(metadata_main, 'userRating'), - 'duration': helpers.get_xml_attr(metadata_main, 'duration'), - 'year': helpers.get_xml_attr(metadata_main, 'year'), - 'thumb': helpers.get_xml_attr(metadata_main, 'thumb'), - 'parent_thumb': helpers.get_xml_attr(metadata_main, 'parentThumb'), - 'grandparent_thumb': helpers.get_xml_attr(metadata_main, 'grandparentThumb'), - 'art': helpers.get_xml_attr(metadata_main, 'art'), - 'banner': helpers.get_xml_attr(metadata_main, 'banner'), - 'originally_available_at': helpers.get_xml_attr(metadata_main, 'originallyAvailableAt'), - 'added_at': helpers.get_xml_attr(metadata_main, 'addedAt'), - 'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'), - 'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'), - 'guid': helpers.get_xml_attr(metadata_main, 'guid'), - 'parent_guid': helpers.get_xml_attr(metadata_main, 'parentGuid'), - 'grandparent_guid': helpers.get_xml_attr(metadata_main, 'grandparentGuid'), - 'directors': directors, - 'writers': writers, - 'actors': actors, - 'genres': genres, - 'labels': labels, - 'collections': collections, - 'guids': guids, - 'full_title': helpers.get_xml_attr(metadata_main, 'title'), - 'children_count': helpers.cast_to_int(helpers.get_xml_attr(metadata_main, 'leafCount')), - 'live': int(helpers.get_xml_attr(metadata_main, 'live') == '1') - } - - elif metadata_type == 'show': - # Workaround for for duration sometimes reported in minutes for a show - duration = helpers.get_xml_attr(metadata_main, 'duration') - if duration.isdigit() and int(duration) < 1000: - duration = str(int(duration) * 60 * 1000) - - metadata = {'media_type': metadata_type, - 'section_id': section_id, - 'library_name': library_name, - 'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'), - 'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'), - 'grandparent_rating_key': helpers.get_xml_attr(metadata_main, 'grandparentRatingKey'), - 'title': helpers.get_xml_attr(metadata_main, 'title'), - 'parent_title': helpers.get_xml_attr(metadata_main, 'parentTitle'), - 'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'), - 'original_title': helpers.get_xml_attr(metadata_main, 'originalTitle'), - 'sort_title': helpers.get_xml_attr(metadata_main, 'titleSort'), - 'media_index': helpers.get_xml_attr(metadata_main, 'index'), - 'parent_media_index': helpers.get_xml_attr(metadata_main, 'parentIndex'), - 'studio': helpers.get_xml_attr(metadata_main, 'studio'), - 'content_rating': helpers.get_xml_attr(metadata_main, 'contentRating'), - 'summary': helpers.get_xml_attr(metadata_main, 'summary'), - 'tagline': helpers.get_xml_attr(metadata_main, 'tagline'), - 'rating': helpers.get_xml_attr(metadata_main, 'rating'), - 'rating_image': helpers.get_xml_attr(metadata_main, 'ratingImage'), - 'audience_rating': helpers.get_xml_attr(metadata_main, 'audienceRating'), - 'audience_rating_image': helpers.get_xml_attr(metadata_main, 'audienceRatingImage'), - 'user_rating': helpers.get_xml_attr(metadata_main, 'userRating'), - 'duration': duration, - 'year': helpers.get_xml_attr(metadata_main, 'year'), - 'thumb': helpers.get_xml_attr(metadata_main, 'thumb'), - 'parent_thumb': helpers.get_xml_attr(metadata_main, 'parentThumb'), - 'grandparent_thumb': helpers.get_xml_attr(metadata_main, 'grandparentThumb'), - 'art': helpers.get_xml_attr(metadata_main, 'art'), - 'banner': helpers.get_xml_attr(metadata_main, 'banner'), - 'originally_available_at': helpers.get_xml_attr(metadata_main, 'originallyAvailableAt'), - 'added_at': helpers.get_xml_attr(metadata_main, 'addedAt'), - 'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'), - 'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'), - 'guid': helpers.get_xml_attr(metadata_main, 'guid'), - 'parent_guid': helpers.get_xml_attr(metadata_main, 'parentGuid'), - 'grandparent_guid': helpers.get_xml_attr(metadata_main, 'grandparentGuid'), - 'directors': directors, - 'writers': writers, - 'actors': actors, - 'genres': genres, - 'labels': labels, - 'collections': collections, - 'guids': guids, - 'full_title': helpers.get_xml_attr(metadata_main, 'title'), - 'children_count': helpers.cast_to_int(helpers.get_xml_attr(metadata_main, 'leafCount')), - 'live': int(helpers.get_xml_attr(metadata_main, 'live') == '1') - } - - elif metadata_type == 'season': - parent_rating_key = helpers.get_xml_attr(metadata_main, 'parentRatingKey') - parent_guid = helpers.get_xml_attr(metadata_main, 'parentGuid') - show_details = {} - if plex_guid and parent_guid: - show_details = self.get_metadata_details(plex_guid=parent_guid) - elif not plex_guid and parent_rating_key: - show_details = self.get_metadata_details(parent_rating_key) - - metadata = {'media_type': metadata_type, - 'section_id': section_id, - 'library_name': library_name, - 'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'), - 'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'), - 'grandparent_rating_key': helpers.get_xml_attr(metadata_main, 'grandparentRatingKey'), - 'title': helpers.get_xml_attr(metadata_main, 'title'), - 'parent_title': helpers.get_xml_attr(metadata_main, 'parentTitle'), - 'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'), - 'original_title': helpers.get_xml_attr(metadata_main, 'originalTitle'), - 'sort_title': helpers.get_xml_attr(metadata_main, 'titleSort'), - 'media_index': helpers.get_xml_attr(metadata_main, 'index'), - 'parent_media_index': helpers.get_xml_attr(metadata_main, 'parentIndex'), - 'studio': show_details.get('studio', ''), - 'content_rating': show_details.get('content_rating', ''), - 'summary': show_details.get('summary', ''), - 'tagline': helpers.get_xml_attr(metadata_main, 'tagline'), - 'rating': helpers.get_xml_attr(metadata_main, 'rating'), - 'rating_image': helpers.get_xml_attr(metadata_main, 'ratingImage'), - 'audience_rating': helpers.get_xml_attr(metadata_main, 'audienceRating'), - 'audience_rating_image': helpers.get_xml_attr(metadata_main, 'audienceRatingImage'), - 'user_rating': helpers.get_xml_attr(metadata_main, 'userRating'), - 'duration': show_details.get('duration', ''), - 'year': helpers.get_xml_attr(metadata_main, 'year'), - 'thumb': helpers.get_xml_attr(metadata_main, 'thumb'), - 'parent_thumb': helpers.get_xml_attr(metadata_main, 'parentThumb'), - 'grandparent_thumb': helpers.get_xml_attr(metadata_main, 'grandparentThumb'), - 'art': helpers.get_xml_attr(metadata_main, 'art'), - 'banner': show_details.get('banner', ''), - 'originally_available_at': helpers.get_xml_attr(metadata_main, 'originallyAvailableAt'), - 'added_at': helpers.get_xml_attr(metadata_main, 'addedAt'), - 'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'), - 'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'), - 'guid': helpers.get_xml_attr(metadata_main, 'guid'), - 'parent_guid': helpers.get_xml_attr(metadata_main, 'parentGuid'), - 'grandparent_guid': helpers.get_xml_attr(metadata_main, 'grandparentGuid'), - 'directors': show_details.get('directors', []), - 'writers': show_details.get('writers', []), - 'actors': show_details.get('actors', []), - 'genres': show_details.get('genres', []), - 'labels': show_details.get('labels', []), - 'collections': show_details.get('collections', []), - 'guids': show_details.get('guids', []), - 'full_title': '{} - {}'.format(helpers.get_xml_attr(metadata_main, 'parentTitle'), - helpers.get_xml_attr(metadata_main, 'title')), - 'children_count': helpers.cast_to_int(helpers.get_xml_attr(metadata_main, 'leafCount')), - 'live': int(helpers.get_xml_attr(metadata_main, 'live') == '1') - } - - elif metadata_type == 'episode': - grandparent_rating_key = helpers.get_xml_attr(metadata_main, 'grandparentRatingKey') - grandparent_guid = helpers.get_xml_attr(metadata_main, 'grandparentGuid') - show_details = {} - if plex_guid and grandparent_guid: - show_details = self.get_metadata_details(plex_guid=grandparent_guid) - elif not plex_guid and grandparent_rating_key: - show_details = self.get_metadata_details(grandparent_rating_key) - - parent_rating_key = helpers.get_xml_attr(metadata_main, 'parentRatingKey') - parent_media_index = helpers.get_xml_attr(metadata_main, 'parentIndex') - parent_thumb = helpers.get_xml_attr(metadata_main, 'parentThumb') - - if not plex_guid and not parent_rating_key: - # Try getting the parent_rating_key from the parent_thumb - if parent_thumb.startswith('/library/metadata/'): - parent_rating_key = parent_thumb.split('/')[3] - - # Try getting the parent_rating_key from the grandparent's children - if not parent_rating_key and grandparent_rating_key: - children_list = self.get_item_children(grandparent_rating_key) - parent_rating_key = next((c['rating_key'] for c in children_list['children_list'] - if c['media_index'] == parent_media_index), '') - - metadata = {'media_type': metadata_type, - 'section_id': section_id, - 'library_name': library_name, - 'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'), - 'parent_rating_key': parent_rating_key, - 'grandparent_rating_key': helpers.get_xml_attr(metadata_main, 'grandparentRatingKey'), - 'title': helpers.get_xml_attr(metadata_main, 'title'), - 'parent_title': 'Season %s' % helpers.get_xml_attr(metadata_main, 'parentIndex'), - 'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'), - 'original_title': helpers.get_xml_attr(metadata_main, 'originalTitle'), - 'sort_title': helpers.get_xml_attr(metadata_main, 'titleSort'), - 'media_index': helpers.get_xml_attr(metadata_main, 'index'), - 'parent_media_index': parent_media_index, - 'studio': show_details.get('studio', ''), - 'content_rating': helpers.get_xml_attr(metadata_main, 'contentRating'), - 'summary': helpers.get_xml_attr(metadata_main, 'summary'), - 'tagline': helpers.get_xml_attr(metadata_main, 'tagline'), - 'rating': helpers.get_xml_attr(metadata_main, 'rating'), - 'rating_image': helpers.get_xml_attr(metadata_main, 'ratingImage'), - 'audience_rating': helpers.get_xml_attr(metadata_main, 'audienceRating'), - 'audience_rating_image': helpers.get_xml_attr(metadata_main, 'audienceRatingImage'), - 'user_rating': helpers.get_xml_attr(metadata_main, 'userRating'), - 'duration': helpers.get_xml_attr(metadata_main, 'duration'), - 'year': helpers.get_xml_attr(metadata_main, 'year'), - 'thumb': helpers.get_xml_attr(metadata_main, 'thumb'), - 'parent_thumb': parent_thumb, - 'grandparent_thumb': helpers.get_xml_attr(metadata_main, 'grandparentThumb'), - 'art': helpers.get_xml_attr(metadata_main, 'art'), - 'banner': show_details.get('banner', ''), - 'originally_available_at': helpers.get_xml_attr(metadata_main, 'originallyAvailableAt'), - 'added_at': helpers.get_xml_attr(metadata_main, 'addedAt'), - 'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'), - 'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'), - 'guid': helpers.get_xml_attr(metadata_main, 'guid'), - 'parent_guid': helpers.get_xml_attr(metadata_main, 'parentGuid'), - 'grandparent_guid': helpers.get_xml_attr(metadata_main, 'grandparentGuid'), - 'directors': directors, - 'writers': writers, - 'actors': show_details.get('actors', []), - 'genres': show_details.get('genres', []), - 'labels': show_details.get('labels', []), - 'collections': show_details.get('collections', []), - 'guids': show_details.get('guids', []), - 'full_title': '{} - {}'.format(helpers.get_xml_attr(metadata_main, 'grandparentTitle'), - helpers.get_xml_attr(metadata_main, 'title')), - 'children_count': helpers.cast_to_int(helpers.get_xml_attr(metadata_main, 'leafCount')), - 'live': int(helpers.get_xml_attr(metadata_main, 'live') == '1') - } - - elif metadata_type == 'artist': - metadata = {'media_type': metadata_type, - 'section_id': section_id, - 'library_name': library_name, - 'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'), - 'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'), - 'grandparent_rating_key': helpers.get_xml_attr(metadata_main, 'grandparentRatingKey'), - 'title': helpers.get_xml_attr(metadata_main, 'title'), - 'parent_title': helpers.get_xml_attr(metadata_main, 'parentTitle'), - 'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'), - 'original_title': helpers.get_xml_attr(metadata_main, 'originalTitle'), - 'sort_title': helpers.get_xml_attr(metadata_main, 'titleSort'), - 'media_index': helpers.get_xml_attr(metadata_main, 'index'), - 'parent_media_index': helpers.get_xml_attr(metadata_main, 'parentIndex'), - 'studio': helpers.get_xml_attr(metadata_main, 'studio'), - 'content_rating': helpers.get_xml_attr(metadata_main, 'contentRating'), - 'summary': helpers.get_xml_attr(metadata_main, 'summary'), - 'tagline': helpers.get_xml_attr(metadata_main, 'tagline'), - 'rating': helpers.get_xml_attr(metadata_main, 'rating'), - 'rating_image': helpers.get_xml_attr(metadata_main, 'ratingImage'), - 'audience_rating': helpers.get_xml_attr(metadata_main, 'audienceRating'), - 'audience_rating_image': helpers.get_xml_attr(metadata_main, 'audienceRatingImage'), - 'user_rating': helpers.get_xml_attr(metadata_main, 'userRating'), - 'duration': helpers.get_xml_attr(metadata_main, 'duration'), - 'year': helpers.get_xml_attr(metadata_main, 'year'), - 'thumb': helpers.get_xml_attr(metadata_main, 'thumb'), - 'parent_thumb': helpers.get_xml_attr(metadata_main, 'parentThumb'), - 'grandparent_thumb': helpers.get_xml_attr(metadata_main, 'grandparentThumb'), - 'art': helpers.get_xml_attr(metadata_main, 'art'), - 'banner': helpers.get_xml_attr(metadata_main, 'banner'), - 'originally_available_at': helpers.get_xml_attr(metadata_main, 'originallyAvailableAt'), - 'added_at': helpers.get_xml_attr(metadata_main, 'addedAt'), - 'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'), - 'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'), - 'guid': helpers.get_xml_attr(metadata_main, 'guid'), - 'parent_guid': helpers.get_xml_attr(metadata_main, 'parentGuid'), - 'grandparent_guid': helpers.get_xml_attr(metadata_main, 'grandparentGuid'), - 'directors': directors, - 'writers': writers, - 'actors': actors, - 'genres': genres, - 'labels': labels, - 'collections': collections, - 'guids': guids, - 'full_title': helpers.get_xml_attr(metadata_main, 'title'), - 'children_count': helpers.cast_to_int(helpers.get_xml_attr(metadata_main, 'leafCount')), - 'live': int(helpers.get_xml_attr(metadata_main, 'live') == '1') - } - - elif metadata_type == 'album': - parent_rating_key = helpers.get_xml_attr(metadata_main, 'parentRatingKey') - artist_details = self.get_metadata_details(parent_rating_key) if parent_rating_key else {} - metadata = {'media_type': metadata_type, - 'section_id': section_id, - 'library_name': library_name, - 'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'), - 'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'), - 'grandparent_rating_key': helpers.get_xml_attr(metadata_main, 'grandparentRatingKey'), - 'title': helpers.get_xml_attr(metadata_main, 'title'), - 'parent_title': helpers.get_xml_attr(metadata_main, 'parentTitle'), - 'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'), - 'original_title': helpers.get_xml_attr(metadata_main, 'originalTitle'), - 'sort_title': helpers.get_xml_attr(metadata_main, 'titleSort'), - 'media_index': helpers.get_xml_attr(metadata_main, 'index'), - 'parent_media_index': helpers.get_xml_attr(metadata_main, 'parentIndex'), - 'studio': helpers.get_xml_attr(metadata_main, 'studio'), - 'content_rating': helpers.get_xml_attr(metadata_main, 'contentRating'), - 'summary': helpers.get_xml_attr(metadata_main, 'summary') or artist_details.get('summary', ''), - 'tagline': helpers.get_xml_attr(metadata_main, 'tagline'), - 'rating': helpers.get_xml_attr(metadata_main, 'rating'), - 'rating_image': helpers.get_xml_attr(metadata_main, 'ratingImage'), - 'audience_rating': helpers.get_xml_attr(metadata_main, 'audienceRating'), - 'audience_rating_image': helpers.get_xml_attr(metadata_main, 'audienceRatingImage'), - 'user_rating': helpers.get_xml_attr(metadata_main, 'userRating'), - 'duration': helpers.get_xml_attr(metadata_main, 'duration'), - 'year': helpers.get_xml_attr(metadata_main, 'year'), - 'thumb': helpers.get_xml_attr(metadata_main, 'thumb'), - 'parent_thumb': helpers.get_xml_attr(metadata_main, 'parentThumb'), - 'grandparent_thumb': helpers.get_xml_attr(metadata_main, 'grandparentThumb'), - 'art': helpers.get_xml_attr(metadata_main, 'art'), - 'banner': artist_details.get('banner', ''), - 'originally_available_at': helpers.get_xml_attr(metadata_main, 'originallyAvailableAt'), - 'added_at': helpers.get_xml_attr(metadata_main, 'addedAt'), - 'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'), - 'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'), - 'guid': helpers.get_xml_attr(metadata_main, 'guid'), - 'parent_guid': helpers.get_xml_attr(metadata_main, 'parentGuid'), - 'grandparent_guid': helpers.get_xml_attr(metadata_main, 'grandparentGuid'), - 'directors': directors, - 'writers': writers, - 'actors': actors, - 'genres': genres, - 'labels': labels, - 'collections': collections, - 'guids': guids, - 'full_title': '{} - {}'.format(helpers.get_xml_attr(metadata_main, 'parentTitle'), - helpers.get_xml_attr(metadata_main, 'title')), - 'children_count': helpers.cast_to_int(helpers.get_xml_attr(metadata_main, 'leafCount')), - 'live': int(helpers.get_xml_attr(metadata_main, 'live') == '1') - } - - elif metadata_type == 'track': - parent_rating_key = helpers.get_xml_attr(metadata_main, 'parentRatingKey') - album_details = self.get_metadata_details(parent_rating_key) if parent_rating_key else {} - track_artist = helpers.get_xml_attr(metadata_main, 'originalTitle') or \ - helpers.get_xml_attr(metadata_main, 'grandparentTitle') - metadata = {'media_type': metadata_type, - 'section_id': section_id, - 'library_name': library_name, - 'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'), - 'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'), - 'grandparent_rating_key': helpers.get_xml_attr(metadata_main, 'grandparentRatingKey'), - 'title': helpers.get_xml_attr(metadata_main, 'title'), - 'parent_title': helpers.get_xml_attr(metadata_main, 'parentTitle'), - 'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'), - 'original_title': helpers.get_xml_attr(metadata_main, 'originalTitle'), - 'sort_title': helpers.get_xml_attr(metadata_main, 'titleSort'), - 'media_index': helpers.get_xml_attr(metadata_main, 'index'), - 'parent_media_index': helpers.get_xml_attr(metadata_main, 'parentIndex'), - 'studio': helpers.get_xml_attr(metadata_main, 'studio'), - 'content_rating': helpers.get_xml_attr(metadata_main, 'contentRating'), - 'summary': helpers.get_xml_attr(metadata_main, 'summary'), - 'tagline': helpers.get_xml_attr(metadata_main, 'tagline'), - 'rating': helpers.get_xml_attr(metadata_main, 'rating'), - 'rating_image': helpers.get_xml_attr(metadata_main, 'ratingImage'), - 'audience_rating': helpers.get_xml_attr(metadata_main, 'audienceRating'), - 'audience_rating_image': helpers.get_xml_attr(metadata_main, 'audienceRatingImage'), - 'user_rating': helpers.get_xml_attr(metadata_main, 'userRating'), - 'duration': helpers.get_xml_attr(metadata_main, 'duration'), - 'year': album_details.get('year', ''), - 'thumb': helpers.get_xml_attr(metadata_main, 'thumb'), - 'parent_thumb': helpers.get_xml_attr(metadata_main, 'parentThumb'), - 'grandparent_thumb': helpers.get_xml_attr(metadata_main, 'grandparentThumb'), - 'art': helpers.get_xml_attr(metadata_main, 'art'), - 'banner': album_details.get('banner', ''), - 'originally_available_at': helpers.get_xml_attr(metadata_main, 'originallyAvailableAt'), - 'added_at': helpers.get_xml_attr(metadata_main, 'addedAt'), - 'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'), - 'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'), - 'guid': helpers.get_xml_attr(metadata_main, 'guid'), - 'parent_guid': helpers.get_xml_attr(metadata_main, 'parentGuid'), - 'grandparent_guid': helpers.get_xml_attr(metadata_main, 'grandparentGuid'), - 'directors': directors, - 'writers': writers, - 'actors': actors, - 'genres': album_details.get('genres', []), - 'labels': album_details.get('labels', []), - 'collections': album_details.get('collections', []), - 'guids': album_details.get('guids', []), - 'full_title': '{} - {}'.format(helpers.get_xml_attr(metadata_main, 'title'), - track_artist), - 'children_count': helpers.cast_to_int(helpers.get_xml_attr(metadata_main, 'leafCount')), - 'live': int(helpers.get_xml_attr(metadata_main, 'live') == '1') - } - - elif metadata_type == 'photo_album': - metadata = {'media_type': metadata_type, - 'section_id': section_id, - 'library_name': library_name, - 'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'), - 'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'), - 'grandparent_rating_key': helpers.get_xml_attr(metadata_main, 'grandparentRatingKey'), - 'title': helpers.get_xml_attr(metadata_main, 'title'), - 'parent_title': helpers.get_xml_attr(metadata_main, 'parentTitle'), - 'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'), - 'original_title': helpers.get_xml_attr(metadata_main, 'originalTitle'), - 'sort_title': helpers.get_xml_attr(metadata_main, 'titleSort'), - 'media_index': helpers.get_xml_attr(metadata_main, 'index'), - 'parent_media_index': helpers.get_xml_attr(metadata_main, 'parentIndex'), - 'studio': helpers.get_xml_attr(metadata_main, 'studio'), - 'content_rating': helpers.get_xml_attr(metadata_main, 'contentRating'), - 'summary': helpers.get_xml_attr(metadata_main, 'summary'), - 'tagline': helpers.get_xml_attr(metadata_main, 'tagline'), - 'rating': helpers.get_xml_attr(metadata_main, 'rating'), - 'rating_image': helpers.get_xml_attr(metadata_main, 'ratingImage'), - 'audience_rating': helpers.get_xml_attr(metadata_main, 'audienceRating'), - 'audience_rating_image': helpers.get_xml_attr(metadata_main, 'audienceRatingImage'), - 'user_rating': helpers.get_xml_attr(metadata_main, 'userRating'), - 'duration': helpers.get_xml_attr(metadata_main, 'duration'), - 'year': helpers.get_xml_attr(metadata_main, 'year'), - 'thumb': helpers.get_xml_attr(metadata_main, 'thumb'), - 'parent_thumb': helpers.get_xml_attr(metadata_main, 'parentThumb'), - 'grandparent_thumb': helpers.get_xml_attr(metadata_main, 'grandparentThumb'), - 'art': helpers.get_xml_attr(metadata_main, 'art'), - 'banner': helpers.get_xml_attr(metadata_main, 'banner'), - 'originally_available_at': helpers.get_xml_attr(metadata_main, 'originallyAvailableAt'), - 'added_at': helpers.get_xml_attr(metadata_main, 'addedAt'), - 'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'), - 'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'), - 'guid': helpers.get_xml_attr(metadata_main, 'guid'), - 'parent_guid': helpers.get_xml_attr(metadata_main, 'parentGuid'), - 'grandparent_guid': helpers.get_xml_attr(metadata_main, 'grandparentGuid'), - 'directors': directors, - 'writers': writers, - 'actors': actors, - 'genres': genres, - 'labels': labels, - 'collections': collections, - 'guids': guids, - 'full_title': helpers.get_xml_attr(metadata_main, 'title'), - 'children_count': helpers.cast_to_int(helpers.get_xml_attr(metadata_main, 'leafCount')), - 'live': int(helpers.get_xml_attr(metadata_main, 'live') == '1') - } - - elif metadata_type == 'photo': - parent_rating_key = helpers.get_xml_attr(metadata_main, 'parentRatingKey') - photo_album_details = self.get_metadata_details(parent_rating_key) if parent_rating_key else {} - metadata = {'media_type': metadata_type, - 'section_id': section_id, - 'library_name': library_name, - 'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'), - 'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'), - 'grandparent_rating_key': helpers.get_xml_attr(metadata_main, 'grandparentRatingKey'), - 'title': helpers.get_xml_attr(metadata_main, 'title'), - 'parent_title': helpers.get_xml_attr(metadata_main, 'parentTitle'), - 'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'), - 'original_title': helpers.get_xml_attr(metadata_main, 'originalTitle'), - 'sort_title': helpers.get_xml_attr(metadata_main, 'titleSort'), - 'media_index': helpers.get_xml_attr(metadata_main, 'index'), - 'parent_media_index': helpers.get_xml_attr(metadata_main, 'parentIndex'), - 'studio': helpers.get_xml_attr(metadata_main, 'studio'), - 'content_rating': helpers.get_xml_attr(metadata_main, 'contentRating'), - 'summary': helpers.get_xml_attr(metadata_main, 'summary'), - 'tagline': helpers.get_xml_attr(metadata_main, 'tagline'), - 'rating': helpers.get_xml_attr(metadata_main, 'rating'), - 'rating_image': helpers.get_xml_attr(metadata_main, 'ratingImage'), - 'audience_rating': helpers.get_xml_attr(metadata_main, 'audienceRating'), - 'audience_rating_image': helpers.get_xml_attr(metadata_main, 'audienceRatingImage'), - 'user_rating': helpers.get_xml_attr(metadata_main, 'userRating'), - 'duration': helpers.get_xml_attr(metadata_main, 'duration'), - 'year': helpers.get_xml_attr(metadata_main, 'year'), - 'thumb': helpers.get_xml_attr(metadata_main, 'thumb'), - 'parent_thumb': helpers.get_xml_attr(metadata_main, 'parentThumb'), - 'grandparent_thumb': helpers.get_xml_attr(metadata_main, 'grandparentThumb'), - 'art': helpers.get_xml_attr(metadata_main, 'art'), - 'banner': photo_album_details.get('banner', ''), - 'originally_available_at': helpers.get_xml_attr(metadata_main, 'originallyAvailableAt'), - 'added_at': helpers.get_xml_attr(metadata_main, 'addedAt'), - 'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'), - 'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'), - 'guid': helpers.get_xml_attr(metadata_main, 'guid'), - 'parent_guid': helpers.get_xml_attr(metadata_main, 'parentGuid'), - 'grandparent_guid': helpers.get_xml_attr(metadata_main, 'grandparentGuid'), - 'directors': directors, - 'writers': writers, - 'actors': actors, - 'genres': photo_album_details.get('genres', []), - 'labels': photo_album_details.get('labels', []), - 'collections': photo_album_details.get('collections', []), - 'guids': photo_album_details.get('guids', []), - 'full_title': '{} - {}'.format( - helpers.get_xml_attr(metadata_main, 'parentTitle') or library_name, - helpers.get_xml_attr(metadata_main, 'title')), - 'children_count': helpers.cast_to_int(helpers.get_xml_attr(metadata_main, 'leafCount')), - 'live': int(helpers.get_xml_attr(metadata_main, 'live') == '1') - } - - elif metadata_type == 'collection': - metadata = {'media_type': metadata_type, - 'sub_media_type': helpers.get_xml_attr(metadata_main, 'subtype'), - 'section_id': section_id, - 'library_name': library_name, - 'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'), - 'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'), - 'grandparent_rating_key': helpers.get_xml_attr(metadata_main, 'grandparentRatingKey'), - 'title': helpers.get_xml_attr(metadata_main, 'title'), - 'parent_title': helpers.get_xml_attr(metadata_main, 'parentTitle'), - 'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'), - 'original_title': helpers.get_xml_attr(metadata_main, 'originalTitle'), - 'sort_title': helpers.get_xml_attr(metadata_main, 'titleSort'), - 'media_index': helpers.get_xml_attr(metadata_main, 'index'), - 'parent_media_index': helpers.get_xml_attr(metadata_main, 'parentIndex'), - 'studio': helpers.get_xml_attr(metadata_main, 'studio'), - 'content_rating': helpers.get_xml_attr(metadata_main, 'contentRating'), - 'summary': helpers.get_xml_attr(metadata_main, 'summary'), - 'tagline': helpers.get_xml_attr(metadata_main, 'tagline'), - 'rating': helpers.get_xml_attr(metadata_main, 'rating'), - 'rating_image': helpers.get_xml_attr(metadata_main, 'ratingImage'), - 'audience_rating': helpers.get_xml_attr(metadata_main, 'audienceRating'), - 'audience_rating_image': helpers.get_xml_attr(metadata_main, 'audienceRatingImage'), - 'user_rating': helpers.get_xml_attr(metadata_main, 'userRating'), - 'duration': helpers.get_xml_attr(metadata_main, 'duration'), - 'year': helpers.get_xml_attr(metadata_main, 'year'), - 'min_year': helpers.get_xml_attr(metadata_main, 'minYear'), - 'max_year': helpers.get_xml_attr(metadata_main, 'maxYear'), - 'thumb': helpers.get_xml_attr(metadata_main, 'thumb').split('?')[0], - 'parent_thumb': helpers.get_xml_attr(metadata_main, 'parentThumb'), - 'grandparent_thumb': helpers.get_xml_attr(metadata_main, 'grandparentThumb'), - 'art': helpers.get_xml_attr(metadata_main, 'art'), - 'banner': helpers.get_xml_attr(metadata_main, 'banner'), - 'originally_available_at': helpers.get_xml_attr(metadata_main, 'originallyAvailableAt'), - 'added_at': helpers.get_xml_attr(metadata_main, 'addedAt'), - 'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'), - 'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'), - 'guid': helpers.get_xml_attr(metadata_main, 'guid'), - 'parent_guid': helpers.get_xml_attr(metadata_main, 'parentGuid'), - 'grandparent_guid': helpers.get_xml_attr(metadata_main, 'grandparentGuid'), - 'child_count': helpers.get_xml_attr(metadata_main, 'childCount'), - 'directors': directors, - 'writers': writers, - 'actors': actors, - 'genres': genres, - 'labels': labels, - 'collections': collections, - 'guids': guids, - 'full_title': helpers.get_xml_attr(metadata_main, 'title'), - 'children_count': helpers.cast_to_int(helpers.get_xml_attr(metadata_main, 'childCount')), - 'live': int(helpers.get_xml_attr(metadata_main, 'live') == '1') - } - - elif metadata_type == 'playlist': - metadata = {'media_type': metadata_type, - 'section_id': section_id, - 'library_name': library_name, - 'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'), - 'guid': helpers.get_xml_attr(metadata_main, 'guid'), - 'title': helpers.get_xml_attr(metadata_main, 'title'), - 'summary': helpers.get_xml_attr(metadata_main, 'summary'), - 'duration': helpers.get_xml_attr(metadata_main, 'duration'), - 'composite': helpers.get_xml_attr(metadata_main, 'composite'), - 'thumb': helpers.get_xml_attr(metadata_main, 'composite'), - 'added_at': helpers.get_xml_attr(metadata_main, 'addedAt'), - 'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'), - 'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'), - 'children_count': helpers.cast_to_int(helpers.get_xml_attr(metadata_main, 'leafCount')), - 'smart': helpers.cast_to_int(helpers.get_xml_attr(metadata_main, 'smart')), - 'playlist_type': helpers.get_xml_attr(metadata_main, 'playlistType'), - 'live': int(helpers.get_xml_attr(metadata_main, 'live') == '1') - } - - elif metadata_type == 'clip': - metadata = {'media_type': metadata_type, - 'section_id': section_id, - 'library_name': library_name, - 'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'), - 'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'), - 'grandparent_rating_key': helpers.get_xml_attr(metadata_main, 'grandparentRatingKey'), - 'title': helpers.get_xml_attr(metadata_main, 'title'), - 'parent_title': helpers.get_xml_attr(metadata_main, 'parentTitle'), - 'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'), - 'original_title': helpers.get_xml_attr(metadata_main, 'originalTitle'), - 'sort_title': helpers.get_xml_attr(metadata_main, 'titleSort'), - 'media_index': helpers.get_xml_attr(metadata_main, 'index'), - 'parent_media_index': helpers.get_xml_attr(metadata_main, 'parentIndex'), - 'studio': helpers.get_xml_attr(metadata_main, 'studio'), - 'content_rating': helpers.get_xml_attr(metadata_main, 'contentRating'), - 'summary': helpers.get_xml_attr(metadata_main, 'summary'), - 'tagline': helpers.get_xml_attr(metadata_main, 'tagline'), - 'rating': helpers.get_xml_attr(metadata_main, 'rating'), - 'rating_image': helpers.get_xml_attr(metadata_main, 'ratingImage'), - 'audience_rating': helpers.get_xml_attr(metadata_main, 'audienceRating'), - 'audience_rating_image': helpers.get_xml_attr(metadata_main, 'audienceRatingImage'), - 'user_rating': helpers.get_xml_attr(metadata_main, 'userRating'), - 'duration': helpers.get_xml_attr(metadata_main, 'duration'), - 'year': helpers.get_xml_attr(metadata_main, 'year'), - 'thumb': helpers.get_xml_attr(metadata_main, 'thumb'), - 'parent_thumb': helpers.get_xml_attr(metadata_main, 'parentThumb'), - 'grandparent_thumb': helpers.get_xml_attr(metadata_main, 'grandparentThumb'), - 'art': helpers.get_xml_attr(metadata_main, 'art'), - 'banner': helpers.get_xml_attr(metadata_main, 'banner'), - 'originally_available_at': helpers.get_xml_attr(metadata_main, 'originallyAvailableAt'), - 'added_at': helpers.get_xml_attr(metadata_main, 'addedAt'), - 'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'), - 'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'), - 'guid': helpers.get_xml_attr(metadata_main, 'guid'), - 'parent_guid': helpers.get_xml_attr(metadata_main, 'parentGuid'), - 'grandparent_guid': helpers.get_xml_attr(metadata_main, 'grandparentGuid'), - 'directors': directors, - 'writers': writers, - 'actors': actors, - 'genres': genres, - 'labels': labels, - 'collections': collections, - 'guids': guids, - 'full_title': helpers.get_xml_attr(metadata_main, 'title'), - 'extra_type': helpers.get_xml_attr(metadata_main, 'extraType'), - 'sub_type': helpers.get_xml_attr(metadata_main, 'subtype'), - 'live': int(helpers.get_xml_attr(metadata_main, 'live') == '1') - } - - else: - return metadata - - # Get additional metadata from metadata.provider.plex.tv - if not plex_guid and metadata['live']: - metadata['section_id'] = common.LIVE_TV_SECTION_ID - metadata['library_name'] = common.LIVE_TV_SECTION_NAME - - plextv_metadata = self.get_metadata_details(plex_guid=metadata['guid']) - if plextv_metadata: - keys_to_update = ['summary', 'rating', 'thumb', 'grandparent_thumb', 'duration', - 'guid', 'grandparent_guid', 'genres'] - for key in keys_to_update: - metadata[key] = plextv_metadata[key] - metadata['originally_available_at'] = helpers.iso_to_YMD(plextv_metadata['originally_available_at']) - - if metadata and media_info: - medias = [] - media_items = metadata_main.getElementsByTagName('Media') - for media in media_items: - video_full_resolution_scan_type = '' - - parts = [] - part_items = media.getElementsByTagName('Part') - for part in part_items: - - streams = [] - stream_items = part.getElementsByTagName('Stream') - for stream in stream_items: - if helpers.get_xml_attr(stream, 'streamType') == '1': - video_scan_type = helpers.get_xml_attr(stream, 'scanType') - video_full_resolution_scan_type = (video_full_resolution_scan_type or video_scan_type) - - streams.append({'id': helpers.get_xml_attr(stream, 'id'), - 'type': helpers.get_xml_attr(stream, 'streamType'), - 'video_codec': helpers.get_xml_attr(stream, 'codec'), - 'video_codec_level': helpers.get_xml_attr(stream, 'level'), - 'video_bitrate': helpers.get_xml_attr(stream, 'bitrate'), - 'video_bit_depth': helpers.get_xml_attr(stream, 'bitDepth'), - 'video_chroma_subsampling': helpers.get_xml_attr(stream, - 'chromaSubsampling'), - 'video_color_primaries': helpers.get_xml_attr(stream, 'colorPrimaries'), - 'video_color_range': helpers.get_xml_attr(stream, 'colorRange'), - 'video_color_space': helpers.get_xml_attr(stream, 'colorSpace'), - 'video_color_trc': helpers.get_xml_attr(stream, 'colorTrc'), - 'video_frame_rate': helpers.get_xml_attr(stream, 'frameRate'), - 'video_ref_frames': helpers.get_xml_attr(stream, 'refFrames'), - 'video_height': helpers.get_xml_attr(stream, 'height'), - 'video_width': helpers.get_xml_attr(stream, 'width'), - 'video_language': helpers.get_xml_attr(stream, 'language'), - 'video_language_code': helpers.get_xml_attr(stream, 'languageCode'), - 'video_profile': helpers.get_xml_attr(stream, 'profile'), - 'video_scan_type': helpers.get_xml_attr(stream, 'scanType'), - 'selected': int(helpers.get_xml_attr(stream, 'selected') == '1') - }) - - elif helpers.get_xml_attr(stream, 'streamType') == '2': - streams.append({'id': helpers.get_xml_attr(stream, 'id'), - 'type': helpers.get_xml_attr(stream, 'streamType'), - 'audio_codec': helpers.get_xml_attr(stream, 'codec'), - 'audio_bitrate': helpers.get_xml_attr(stream, 'bitrate'), - 'audio_bitrate_mode': helpers.get_xml_attr(stream, 'bitrateMode'), - 'audio_channels': helpers.get_xml_attr(stream, 'channels'), - 'audio_channel_layout': helpers.get_xml_attr(stream, 'audioChannelLayout'), - 'audio_sample_rate': helpers.get_xml_attr(stream, 'samplingRate'), - 'audio_language': helpers.get_xml_attr(stream, 'language'), - 'audio_language_code': helpers.get_xml_attr(stream, 'languageCode'), - 'audio_profile': helpers.get_xml_attr(stream, 'profile'), - 'selected': int(helpers.get_xml_attr(stream, 'selected') == '1') - }) - - elif helpers.get_xml_attr(stream, 'streamType') == '3': - streams.append({'id': helpers.get_xml_attr(stream, 'id'), - 'type': helpers.get_xml_attr(stream, 'streamType'), - 'subtitle_codec': helpers.get_xml_attr(stream, 'codec'), - 'subtitle_container': helpers.get_xml_attr(stream, 'container'), - 'subtitle_format': helpers.get_xml_attr(stream, 'format'), - 'subtitle_forced': int(helpers.get_xml_attr(stream, 'forced') == '1'), - 'subtitle_location': 'external' if helpers.get_xml_attr(stream, - 'key') else 'embedded', - 'subtitle_language': helpers.get_xml_attr(stream, 'language'), - 'subtitle_language_code': helpers.get_xml_attr(stream, 'languageCode'), - 'selected': int(helpers.get_xml_attr(stream, 'selected') == '1') - }) - - parts.append({'id': helpers.get_xml_attr(part, 'id'), - 'file': helpers.get_xml_attr(part, 'file'), - 'file_size': helpers.get_xml_attr(part, 'size'), - 'indexes': int(helpers.get_xml_attr(part, 'indexes') == 'sd'), - 'streams': streams, - 'selected': int(helpers.get_xml_attr(part, 'selected') == '1') - }) - - video_resolution = helpers.get_xml_attr(media, 'videoResolution').lower().rstrip('ip') - video_full_resolution = common.VIDEO_RESOLUTION_OVERRIDES.get( - video_resolution, video_resolution + (video_full_resolution_scan_type[:1] or 'p') - ) - - audio_channels = helpers.get_xml_attr(media, 'audioChannels') - - media_info = {'id': helpers.get_xml_attr(media, 'id'), - 'container': helpers.get_xml_attr(media, 'container'), - 'bitrate': helpers.get_xml_attr(media, 'bitrate'), - 'height': helpers.get_xml_attr(media, 'height'), - 'width': helpers.get_xml_attr(media, 'width'), - 'aspect_ratio': helpers.get_xml_attr(media, 'aspectRatio'), - 'video_codec': helpers.get_xml_attr(media, 'videoCodec'), - 'video_resolution': video_resolution, - 'video_full_resolution': video_full_resolution, - 'video_framerate': helpers.get_xml_attr(media, 'videoFrameRate'), - 'video_profile': helpers.get_xml_attr(media, 'videoProfile'), - 'audio_codec': helpers.get_xml_attr(media, 'audioCodec'), - 'audio_channels': audio_channels, - 'audio_channel_layout': common.AUDIO_CHANNELS.get(audio_channels, audio_channels), - 'audio_profile': helpers.get_xml_attr(media, 'audioProfile'), - 'optimized_version': int(helpers.get_xml_attr(media, 'proxyType') == '42'), - 'channel_call_sign': helpers.get_xml_attr(media, 'channelCallSign'), - 'channel_identifier': helpers.get_xml_attr(media, 'channelIdentifier'), - 'channel_thumb': helpers.get_xml_attr(media, 'channelThumb'), - 'parts': parts - } - - medias.append(media_info) - - metadata['media_info'] = medias - - if metadata: - if cache_key: - metadata['_cache_time'] = helpers.timestamp() - - out_file_folder = os.path.join(jellypy.CONFIG.CACHE_DIR, 'session_metadata') - out_file_path = os.path.join(out_file_folder, 'metadata-sessionKey-%s.json' % cache_key) - - if not os.path.exists(out_file_folder): - os.mkdir(out_file_folder) - - try: - with open(out_file_path, 'w') as outFile: - json.dump(metadata, outFile) - except (IOError, ValueError) as e: - logger.error("Tautulli Pmsconnect :: Unable to create cache file for metadata (sessionKey %s): %s" - % (cache_key, e)) - - return metadata - else: - return metadata - - def get_metadata_children_details(self, rating_key='', get_children=False): - """ - Return processed and validated metadata list for all children of requested item. - - Parameters required: rating_key { Plex ratingKey } - - Output: array - """ - metadata = self.get_metadata_children(str(rating_key), output_format='xml') - - try: - xml_head = metadata.getElementsByTagName('MediaContainer') - except Exception as e: - logger.warn("Tautulli Pmsconnect :: Unable to parse XML for get_metadata_children: %s." % e) - return [] - - metadata_list = [] - - for a in xml_head: - if a.getAttribute('size'): - if a.getAttribute('size') == '0': - return metadata_list - - if a.getElementsByTagName('Video'): - metadata_main = a.getElementsByTagName('Video') - for item in metadata_main: - child_rating_key = helpers.get_xml_attr(item, 'ratingKey') - metadata = self.get_metadata_details(str(child_rating_key)) - if metadata: - metadata_list.append(metadata) - - elif a.getElementsByTagName('Track'): - metadata_main = a.getElementsByTagName('Track') - for item in metadata_main: - child_rating_key = helpers.get_xml_attr(item, 'ratingKey') - metadata = self.get_metadata_details(str(child_rating_key)) - if metadata: - metadata_list.append(metadata) - - elif get_children and a.getElementsByTagName('Directory'): - dir_main = a.getElementsByTagName('Directory') - metadata_main = [d for d in dir_main if helpers.get_xml_attr(d, 'ratingKey')] - for item in metadata_main: - child_rating_key = helpers.get_xml_attr(item, 'ratingKey') - metadata = self.get_metadata_children_details(str(child_rating_key), get_children) - if metadata: - metadata_list.extend(metadata) - - return metadata_list - - def get_library_metadata_details(self, section_id=''): - """ - Return processed and validated metadata list for requested library. - - Parameters required: section_id { Plex library key } - - Output: array - """ - libraries_data = self.get_libraries_list(output_format='xml') - - try: - xml_head = libraries_data.getElementsByTagName('MediaContainer') - except Exception as e: - logger.warn("Tautulli Pmsconnect :: Unable to parse XML for get_library_metadata_details: %s." % e) - return [] - - metadata_list = [] - - for a in xml_head: - if a.getAttribute('size'): - if a.getAttribute('size') == '0': - metadata_list = {'metadata': None} - return metadata_list - - if a.getElementsByTagName('Directory'): - result_data = a.getElementsByTagName('Directory') - for result in result_data: - key = helpers.get_xml_attr(result, 'key') - if key == section_id: - metadata = {'media_type': 'library', - 'section_id': helpers.get_xml_attr(result, 'key'), - 'library': helpers.get_xml_attr(result, 'type'), - 'title': helpers.get_xml_attr(result, 'title'), - 'art': helpers.get_xml_attr(result, 'art'), - 'thumb': helpers.get_xml_attr(result, 'thumb') - } - if metadata['library'] == 'movie': - metadata['section_type'] = 'movie' - elif metadata['library'] == 'show': - metadata['section_type'] = 'episode' - elif metadata['library'] == 'artist': - metadata['section_type'] = 'track' - - metadata_list = {'metadata': metadata} - - return metadata_list - - def get_current_activity(self, skip_cache=False): - """ - Return processed and validated session list. - - Output: array - """ - session_data = self.get_sessions(output_format='xml') - - try: - xml_head = session_data.getElementsByTagName('MediaContainer') - except Exception as e: - logger.warn("Tautulli Pmsconnect :: Unable to parse XML for get_current_activity: %s." % e) - return [] - - session_list = [] - - for a in xml_head: - if a.getAttribute('size'): - if a.getAttribute('size') == '0': - session_list = {'stream_count': '0', - 'sessions': [] - } - return session_list - - if a.getElementsByTagName('Track'): - session_data = a.getElementsByTagName('Track') - for session_ in session_data: - # Filter out background theme music sessions - if helpers.get_xml_attr(session_, 'guid').startswith('library://'): - continue - session_output = self.get_session_each(session_, skip_cache=skip_cache) - session_list.append(session_output) - if a.getElementsByTagName('Video'): - session_data = a.getElementsByTagName('Video') - for session_ in session_data: - session_output = self.get_session_each(session_, skip_cache=skip_cache) - session_list.append(session_output) - if a.getElementsByTagName('Photo'): - session_data = a.getElementsByTagName('Photo') - for session_ in session_data: - session_output = self.get_session_each(session_, skip_cache=skip_cache) - session_list.append(session_output) - - session_list = sorted(session_list, key=lambda k: k['session_key']) - - output = {'stream_count': helpers.get_xml_attr(xml_head[0], 'size'), - 'sessions': session.mask_session_info(session_list) - } - - return output - - def get_session_each(self, session=None, skip_cache=False): - """ - Return selected data from current sessions. - This function processes and validates session data - - Parameters required: session { the session dictionary } - Output: dict - """ - - # Get the source media type - media_type = helpers.get_xml_attr(session, 'type') - rating_key = helpers.get_xml_attr(session, 'ratingKey') - session_key = helpers.get_xml_attr(session, 'sessionKey') - - # Get the user details - user_info = session.getElementsByTagName('User')[0] - user_id = helpers.get_xml_attr(user_info, 'id') - if user_id == '1': # Admin user - user_details = users.Users().get_details(user=helpers.get_xml_attr(user_info, 'title')) - else: - user_details = users.Users().get_details(user_id=user_id) - - # Get the player details - player_info = session.getElementsByTagName('Player')[0] - - # Override platform names - platform = helpers.get_xml_attr(player_info, 'platform') - platform = common.PLATFORM_NAME_OVERRIDES.get(platform, platform) - if not platform and helpers.get_xml_attr(player_info, 'product') == 'DLNA': - platform = 'DLNA' - - platform_name = next((v for k, v in common.PLATFORM_NAMES.items() if k in platform.lower()), 'default') - - player_details = {'ip_address': helpers.get_xml_attr(player_info, 'address').split('::ffff:')[-1], - 'ip_address_public': - helpers.get_xml_attr(player_info, 'remotePublicAddress').split('::ffff:')[-1], - 'device': helpers.get_xml_attr(player_info, 'device'), - 'platform': platform, - 'platform_name': platform_name, - 'platform_version': helpers.get_xml_attr(player_info, 'platformVersion'), - 'product': helpers.get_xml_attr(player_info, 'product'), - 'product_version': helpers.get_xml_attr(player_info, 'version'), - 'profile': helpers.get_xml_attr(player_info, 'profile'), - 'player': helpers.get_xml_attr(player_info, 'title') or helpers.get_xml_attr(player_info, - 'product'), - 'machine_id': helpers.get_xml_attr(player_info, 'machineIdentifier'), - 'state': helpers.get_xml_attr(player_info, 'state'), - 'local': int(helpers.get_xml_attr(player_info, 'local') == '1'), - 'relayed': helpers.get_xml_attr(player_info, 'relayed', default_return=None), - 'secure': helpers.get_xml_attr(player_info, 'secure', default_return=None) - } - - # Get the session details - if session.getElementsByTagName('Session'): - session_info = session.getElementsByTagName('Session')[0] - - session_details = {'session_id': helpers.get_xml_attr(session_info, 'id'), - 'bandwidth': helpers.get_xml_attr(session_info, 'bandwidth'), - 'location': helpers.get_xml_attr(session_info, 'location') - } - else: - session_details = {'session_id': '', - 'bandwidth': '', - 'location': 'lan' if player_details['local'] else 'wan' - } - - # Check if using Plex Relay - if player_details['relayed'] is None: - player_details['relayed'] = int(session_details['location'] != 'lan' and - player_details['ip_address_public'] == '127.0.0.1') - - else: - player_details['relayed'] = helpers.cast_to_int(player_details['relayed']) - - # Check if secure connection - if player_details['secure'] is not None: - player_details['secure'] = int(player_details['secure'] == '1') - - # Get the transcode details - if session.getElementsByTagName('TranscodeSession'): - transcode_session = True - - transcode_info = session.getElementsByTagName('TranscodeSession')[0] - - transcode_progress = helpers.get_xml_attr(transcode_info, 'progress') - transcode_speed = helpers.get_xml_attr(transcode_info, 'speed') - - transcode_details = {'transcode_key': helpers.get_xml_attr(transcode_info, 'key'), - 'transcode_throttled': int(helpers.get_xml_attr(transcode_info, 'throttled') == '1'), - 'transcode_progress': int(round(helpers.cast_to_float(transcode_progress), 0)), - 'transcode_speed': str(round(helpers.cast_to_float(transcode_speed), 1)), - 'transcode_audio_channels': helpers.get_xml_attr(transcode_info, 'audioChannels'), - 'transcode_audio_codec': helpers.get_xml_attr(transcode_info, 'audioCodec'), - 'transcode_video_codec': helpers.get_xml_attr(transcode_info, 'videoCodec'), - 'transcode_width': helpers.get_xml_attr(transcode_info, 'width'), - # Blank but keep for backwards compatibility - 'transcode_height': helpers.get_xml_attr(transcode_info, 'height'), - # Blank but keep backwards compatibility - 'transcode_container': helpers.get_xml_attr(transcode_info, 'container'), - 'transcode_protocol': helpers.get_xml_attr(transcode_info, 'protocol'), - 'transcode_hw_requested': int( - helpers.get_xml_attr(transcode_info, 'transcodeHwRequested') == '1'), - 'transcode_hw_decode': helpers.get_xml_attr(transcode_info, 'transcodeHwDecoding'), - 'transcode_hw_decode_title': helpers.get_xml_attr(transcode_info, - 'transcodeHwDecodingTitle'), - 'transcode_hw_encode': helpers.get_xml_attr(transcode_info, 'transcodeHwEncoding'), - 'transcode_hw_encode_title': helpers.get_xml_attr(transcode_info, - 'transcodeHwEncodingTitle'), - 'transcode_hw_full_pipeline': int( - helpers.get_xml_attr(transcode_info, 'transcodeHwFullPipeline') == '1'), - 'audio_decision': helpers.get_xml_attr(transcode_info, 'audioDecision'), - 'video_decision': helpers.get_xml_attr(transcode_info, 'videoDecision'), - 'subtitle_decision': helpers.get_xml_attr(transcode_info, 'subtitleDecision'), - 'throttled': '1' if helpers.get_xml_attr(transcode_info, 'throttled') == '1' else '0' - # Keep for backwards compatibility - } - else: - transcode_session = False - - transcode_details = {'transcode_key': '', - 'transcode_throttled': 0, - 'transcode_progress': 0, - 'transcode_speed': '', - 'transcode_audio_channels': '', - 'transcode_audio_codec': '', - 'transcode_video_codec': '', - 'transcode_width': '', - 'transcode_height': '', - 'transcode_container': '', - 'transcode_protocol': '', - 'transcode_hw_requested': 0, - 'transcode_hw_decode': '', - 'transcode_hw_decode_title': '', - 'transcode_hw_encode': '', - 'transcode_hw_encode_title': '', - 'transcode_hw_full_pipeline': 0, - 'audio_decision': 'direct play', - 'video_decision': 'direct play', - 'subtitle_decision': '', - 'throttled': '0' # Keep for backwards compatibility - } - - # Check HW decoding/encoding - transcode_details['transcode_hw_decoding'] = int( - transcode_details['transcode_hw_decode'].lower() in common.HW_DECODERS) - transcode_details['transcode_hw_encoding'] = int( - transcode_details['transcode_hw_encode'].lower() in common.HW_ENCODERS) - - # Determine if a synced version is being played - sync_id = synced_session_data = synced_item_details = None - if media_type not in ('photo', 'clip') \ - and not session.getElementsByTagName('Session') \ - and not session.getElementsByTagName('TranscodeSession') \ - and helpers.get_xml_attr(session, 'ratingKey').isdigit() \ - and jellypy.CONFIG.PMS_PLEXPASS: - plex_tv = plextv.PlexTV() - parent_rating_key = helpers.get_xml_attr(session, 'parentRatingKey') - grandparent_rating_key = helpers.get_xml_attr(session, 'grandparentRatingKey') - - synced_items = plex_tv.get_synced_items(client_id_filter=player_details['machine_id'], - rating_key_filter=[rating_key, parent_rating_key, - grandparent_rating_key]) - if synced_items: - synced_item_details = synced_items[0] - sync_id = synced_item_details['sync_id'] - synced_xml = self.get_sync_item(sync_id=sync_id, output_format='xml') - synced_xml_head = synced_xml.getElementsByTagName('MediaContainer') - - synced_xml_items = [] - if synced_xml_head[0].getElementsByTagName('Track'): - synced_xml_items = synced_xml_head[0].getElementsByTagName('Track') - elif synced_xml_head[0].getElementsByTagName('Video'): - synced_xml_items = synced_xml_head[0].getElementsByTagName('Video') - - for synced_session_data in synced_xml_items: - if helpers.get_xml_attr(synced_session_data, 'ratingKey') == rating_key: - break - - # Figure out which version is being played - if sync_id and synced_session_data: - media_info_all = synced_session_data.getElementsByTagName('Media') - else: - media_info_all = session.getElementsByTagName('Media') - stream_media_info = next((m for m in media_info_all if helpers.get_xml_attr(m, 'selected') == '1'), - media_info_all[0]) - part_info_all = stream_media_info.getElementsByTagName('Part') - stream_media_parts_info = next((p for p in part_info_all if helpers.get_xml_attr(p, 'selected') == '1'), - part_info_all[0]) - - # Get the stream details - video_stream_info = audio_stream_info = subtitle_stream_info = None - for stream in stream_media_parts_info.getElementsByTagName('Stream'): - if helpers.get_xml_attr(stream, 'streamType') == '1': - if video_stream_info is None or helpers.get_xml_attr(stream, 'selected') == '1': - video_stream_info = stream - - elif helpers.get_xml_attr(stream, 'streamType') == '2': - if audio_stream_info is None or helpers.get_xml_attr(stream, 'selected') == '1': - audio_stream_info = stream - - elif helpers.get_xml_attr(stream, 'streamType') == '3': - if subtitle_stream_info is None or helpers.get_xml_attr(stream, 'selected') == '1': - subtitle_stream_info = stream - - video_id = audio_id = subtitle_id = None - if video_stream_info: - video_id = helpers.get_xml_attr(video_stream_info, 'id') - video_details = {'stream_video_bitrate': helpers.get_xml_attr(video_stream_info, 'bitrate'), - 'stream_video_bit_depth': helpers.get_xml_attr(video_stream_info, 'bitDepth'), - 'stream_video_chroma_subsampling': helpers.get_xml_attr(video_stream_info, - 'chromaSubsampling'), - 'stream_video_color_primaries': helpers.get_xml_attr(video_stream_info, 'colorPrimaries'), - 'stream_video_color_range': helpers.get_xml_attr(video_stream_info, 'colorRange'), - 'stream_video_color_space': helpers.get_xml_attr(video_stream_info, 'colorSpace'), - 'stream_video_color_trc': helpers.get_xml_attr(video_stream_info, 'colorTrc'), - 'stream_video_codec_level': helpers.get_xml_attr(video_stream_info, 'level'), - 'stream_video_ref_frames': helpers.get_xml_attr(video_stream_info, 'refFrames'), - 'stream_video_language': helpers.get_xml_attr(video_stream_info, 'language'), - 'stream_video_language_code': helpers.get_xml_attr(video_stream_info, 'languageCode'), - 'stream_video_scan_type': helpers.get_xml_attr(video_stream_info, 'scanType'), - 'stream_video_decision': helpers.get_xml_attr(video_stream_info, - 'decision') or 'direct play' - } - else: - video_details = {'stream_video_bitrate': '', - 'stream_video_bit_depth': '', - 'stream_video_chroma_subsampling': '', - 'stream_video_color_primaries': '', - 'stream_video_color_range': '', - 'stream_video_color_space': '', - 'stream_video_color_trc': '', - 'stream_video_codec_level': '', - 'stream_video_ref_frames': '', - 'stream_video_language': '', - 'stream_video_language_code': '', - 'stream_video_scan_type': '', - 'stream_video_decision': '' - } - - if audio_stream_info: - audio_id = helpers.get_xml_attr(audio_stream_info, 'id') - audio_details = {'stream_audio_bitrate': helpers.get_xml_attr(audio_stream_info, 'bitrate'), - 'stream_audio_bitrate_mode': helpers.get_xml_attr(audio_stream_info, 'bitrateMode'), - 'stream_audio_sample_rate': helpers.get_xml_attr(audio_stream_info, 'samplingRate'), - 'stream_audio_channel_layout_': helpers.get_xml_attr(audio_stream_info, - 'audioChannelLayout'), - 'stream_audio_language': helpers.get_xml_attr(audio_stream_info, 'language'), - 'stream_audio_language_code': helpers.get_xml_attr(audio_stream_info, 'languageCode'), - 'stream_audio_decision': helpers.get_xml_attr(audio_stream_info, - 'decision') or 'direct play' - } - else: - audio_details = {'stream_audio_bitrate': '', - 'stream_audio_bitrate_mode': '', - 'stream_audio_sample_rate': '', - 'stream_audio_channel_layout_': '', - 'stream_audio_language': '', - 'stream_audio_language_code': '', - 'stream_audio_decision': '' - } - - if subtitle_stream_info: - subtitle_id = helpers.get_xml_attr(subtitle_stream_info, 'id') - subtitle_selected = helpers.get_xml_attr(subtitle_stream_info, 'selected') - subtitle_details = {'stream_subtitle_codec': helpers.get_xml_attr(subtitle_stream_info, 'codec'), - 'stream_subtitle_container': helpers.get_xml_attr(subtitle_stream_info, 'container'), - 'stream_subtitle_format': helpers.get_xml_attr(subtitle_stream_info, 'format'), - 'stream_subtitle_forced': int( - helpers.get_xml_attr(subtitle_stream_info, 'forced') == '1'), - 'stream_subtitle_location': helpers.get_xml_attr(subtitle_stream_info, 'location'), - 'stream_subtitle_language': helpers.get_xml_attr(subtitle_stream_info, 'language'), - 'stream_subtitle_language_code': helpers.get_xml_attr(subtitle_stream_info, - 'languageCode'), - 'stream_subtitle_decision': helpers.get_xml_attr(subtitle_stream_info, 'decision'), - 'stream_subtitle_transient': int( - helpers.get_xml_attr(subtitle_stream_info, 'transient') == '1') - } - else: - subtitle_selected = None - subtitle_details = {'stream_subtitle_codec': '', - 'stream_subtitle_container': '', - 'stream_subtitle_format': '', - 'stream_subtitle_forced': 0, - 'stream_subtitle_location': '', - 'stream_subtitle_language': '', - 'stream_subtitle_language_code': '', - 'stream_subtitle_decision': '', - 'stream_subtitle_transient': 0 - } - - # Get the bif thumbnail - indexes = helpers.get_xml_attr(stream_media_parts_info, 'indexes') - view_offset = helpers.get_xml_attr(session, 'viewOffset') - if indexes == 'sd': - part_id = helpers.get_xml_attr(stream_media_parts_info, 'id') - bif_thumb = '/library/parts/{part_id}/indexes/sd/{view_offset}'.format(part_id=part_id, - view_offset=view_offset) - else: - bif_thumb = '' - - stream_video_width = helpers.get_xml_attr(stream_media_info, 'width') - if helpers.cast_to_int(stream_video_width) >= 3840: - stream_video_resolution = '4k' - else: - stream_video_resolution = helpers.get_xml_attr(stream_media_info, 'videoResolution').lower().rstrip('ip') - - stream_audio_channels = helpers.get_xml_attr(stream_media_info, 'audioChannels') - - stream_details = {'stream_container': helpers.get_xml_attr(stream_media_info, 'container'), - 'stream_bitrate': helpers.get_xml_attr(stream_media_info, 'bitrate'), - 'stream_aspect_ratio': helpers.get_xml_attr(stream_media_info, 'aspectRatio'), - 'stream_audio_codec': helpers.get_xml_attr(stream_media_info, 'audioCodec'), - 'stream_audio_channels': stream_audio_channels, - 'stream_audio_channel_layout': audio_details.get( - 'stream_audio_channel_layout_') or common.AUDIO_CHANNELS.get(stream_audio_channels, - stream_audio_channels), - 'stream_video_codec': helpers.get_xml_attr(stream_media_info, 'videoCodec'), - 'stream_video_framerate': helpers.get_xml_attr(stream_media_info, 'videoFrameRate'), - 'stream_video_resolution': stream_video_resolution, - 'stream_video_height': helpers.get_xml_attr(stream_media_info, 'height'), - 'stream_video_width': helpers.get_xml_attr(stream_media_info, 'width'), - 'stream_duration': helpers.get_xml_attr(stream_media_info, - 'duration') or helpers.get_xml_attr(session, - 'duration'), - 'stream_container_decision': 'direct play' if sync_id else helpers.get_xml_attr( - stream_media_parts_info, 'decision').replace('directplay', 'direct play'), - 'optimized_version': int(helpers.get_xml_attr(stream_media_info, 'proxyType') == '42'), - 'optimized_version_title': helpers.get_xml_attr(stream_media_info, 'title'), - 'synced_version': 1 if sync_id else 0, - 'live': int(helpers.get_xml_attr(session, 'live') == '1'), - 'live_uuid': helpers.get_xml_attr(stream_media_info, 'uuid'), - 'indexes': int(indexes == 'sd'), - 'bif_thumb': bif_thumb, - 'subtitles': 1 if subtitle_id and subtitle_selected else 0 - } - - # Get the source media info - source_media_details = source_media_part_details = \ - source_video_details = source_audio_details = source_subtitle_details = {} - - if not helpers.get_xml_attr(session, 'ratingKey').isdigit(): - channel_stream = 1 - - audio_channels = helpers.get_xml_attr(stream_media_info, 'audioChannels') - metadata_details = {'media_type': media_type, - 'section_id': helpers.get_xml_attr(session, 'librarySectionID'), - 'library_name': helpers.get_xml_attr(session, 'librarySectionTitle'), - 'rating_key': helpers.get_xml_attr(session, 'ratingKey'), - 'parent_rating_key': helpers.get_xml_attr(session, 'parentRatingKey'), - 'grandparent_rating_key': helpers.get_xml_attr(session, 'grandparentRatingKey'), - 'title': helpers.get_xml_attr(session, 'title'), - 'parent_title': helpers.get_xml_attr(session, 'parentTitle'), - 'grandparent_title': helpers.get_xml_attr(session, 'grandparentTitle'), - 'original_title': helpers.get_xml_attr(session, 'originalTitle'), - 'sort_title': helpers.get_xml_attr(session, 'titleSort'), - 'media_index': helpers.get_xml_attr(session, 'index'), - 'parent_media_index': helpers.get_xml_attr(session, 'parentIndex'), - 'studio': helpers.get_xml_attr(session, 'studio'), - 'content_rating': helpers.get_xml_attr(session, 'contentRating'), - 'summary': helpers.get_xml_attr(session, 'summary'), - 'tagline': helpers.get_xml_attr(session, 'tagline'), - 'rating': helpers.get_xml_attr(session, 'rating'), - 'rating_image': helpers.get_xml_attr(session, 'ratingImage'), - 'audience_rating': helpers.get_xml_attr(session, 'audienceRating'), - 'audience_rating_image': helpers.get_xml_attr(session, 'audienceRatingImage'), - 'user_rating': helpers.get_xml_attr(session, 'userRating'), - 'duration': helpers.get_xml_attr(session, 'duration'), - 'year': helpers.get_xml_attr(session, 'year'), - 'thumb': helpers.get_xml_attr(session, 'thumb'), - 'parent_thumb': helpers.get_xml_attr(session, 'parentThumb'), - 'grandparent_thumb': helpers.get_xml_attr(session, 'grandparentThumb'), - 'art': helpers.get_xml_attr(session, 'art'), - 'banner': helpers.get_xml_attr(session, 'banner'), - 'originally_available_at': helpers.get_xml_attr(session, 'originallyAvailableAt'), - 'added_at': helpers.get_xml_attr(session, 'addedAt'), - 'updated_at': helpers.get_xml_attr(session, 'updatedAt'), - 'last_viewed_at': helpers.get_xml_attr(session, 'lastViewedAt'), - 'guid': helpers.get_xml_attr(session, 'guid'), - 'directors': [], - 'writers': [], - 'actors': [], - 'genres': [], - 'labels': [], - 'full_title': helpers.get_xml_attr(session, 'title'), - 'container': helpers.get_xml_attr(stream_media_info, 'container') \ - or helpers.get_xml_attr(stream_media_parts_info, 'container'), - 'bitrate': helpers.get_xml_attr(stream_media_info, 'bitrate'), - 'height': helpers.get_xml_attr(stream_media_info, 'height'), - 'width': helpers.get_xml_attr(stream_media_info, 'width'), - 'aspect_ratio': helpers.get_xml_attr(stream_media_info, 'aspectRatio'), - 'video_codec': helpers.get_xml_attr(stream_media_info, 'videoCodec'), - 'video_resolution': helpers.get_xml_attr(stream_media_info, 'videoResolution').lower(), - 'video_full_resolution': helpers.get_xml_attr(stream_media_info, - 'videoResolution').lower(), - 'video_framerate': helpers.get_xml_attr(stream_media_info, 'videoFrameRate'), - 'video_profile': helpers.get_xml_attr(stream_media_info, 'videoProfile'), - 'audio_codec': helpers.get_xml_attr(stream_media_info, 'audioCodec'), - 'audio_channels': audio_channels, - 'audio_channel_layout': common.AUDIO_CHANNELS.get(audio_channels, audio_channels), - 'audio_profile': helpers.get_xml_attr(stream_media_info, 'audioProfile'), - 'channel_icon': helpers.get_xml_attr(session, 'sourceIcon'), - 'channel_title': helpers.get_xml_attr(session, 'sourceTitle'), - 'extra_type': helpers.get_xml_attr(session, 'extraType'), - 'sub_type': helpers.get_xml_attr(session, 'subtype') - } - else: - channel_stream = 0 - - media_id = helpers.get_xml_attr(stream_media_info, 'id') - part_id = helpers.get_xml_attr(stream_media_parts_info, 'id') - - if sync_id: - metadata_details = self.get_metadata_details(rating_key=rating_key, sync_id=sync_id, - skip_cache=skip_cache, cache_key=session_key) - else: - metadata_details = self.get_metadata_details(rating_key=rating_key, - skip_cache=skip_cache, cache_key=session_key) - - # Get the media info, fallback to first item if match id is not found - source_medias = metadata_details.pop('media_info', []) - source_media_details = next((m for m in source_medias if m['id'] == media_id), - next((m for m in source_medias), {})) - source_media_parts = source_media_details.pop('parts', []) - source_media_part_details = next((p for p in source_media_parts if p['id'] == part_id), - next((p for p in source_media_parts), {})) - source_media_part_streams = source_media_part_details.pop('streams', []) - - source_video_details = {'id': '', - 'type': '', - 'video_codec': '', - 'video_codec_level': '', - 'video_bitrate': '', - 'video_bit_depth': '', - 'video_chroma_subsampling': '', - 'video_color_primaries': '', - 'video_color_range': '', - 'video_color_space': '', - 'video_color_trc': '', - 'video_frame_rate': '', - 'video_ref_frames': '', - 'video_height': '', - 'video_width': '', - 'video_language': '', - 'video_language_code': '', - 'video_scan_type': '', - 'video_profile': '' - } - source_audio_details = {'id': '', - 'type': '', - 'audio_codec': '', - 'audio_bitrate': '', - 'audio_bitrate_mode': '', - 'audio_channels': '', - 'audio_channel_layout': '', - 'audio_sample_rate': '', - 'audio_language': '', - 'audio_language_code': '', - 'audio_profile': '' - } - source_subtitle_details = {'id': '', - 'type': '', - 'subtitle_codec': '', - 'subtitle_container': '', - 'subtitle_format': '', - 'subtitle_forced': 0, - 'subtitle_location': '', - 'subtitle_language': '', - 'subtitle_language_code': '' - } - if video_id: - source_video_details = next((p for p in source_media_part_streams if p['id'] == video_id), - next((p for p in source_media_part_streams if p['type'] == '1'), - source_video_details)) - if audio_id: - source_audio_details = next((p for p in source_media_part_streams if p['id'] == audio_id), - next((p for p in source_media_part_streams if p['type'] == '2'), - source_audio_details)) - if subtitle_id: - source_subtitle_details = next((p for p in source_media_part_streams if p['id'] == subtitle_id), - next((p for p in source_media_part_streams if p['type'] == '3'), - source_subtitle_details)) - - # Override the thumb for clips - if media_type == 'clip' and metadata_details.get('extra_type') and metadata_details['art']: - metadata_details['thumb'] = metadata_details['art'].replace('/art', '/thumb') - - # Overrides for live sessions - if stream_details['live'] and transcode_session: - stream_details['stream_container_decision'] = 'transcode' - stream_details['stream_container'] = transcode_details['transcode_container'] - - video_details['stream_video_decision'] = transcode_details['video_decision'] - stream_details['stream_video_codec'] = transcode_details['transcode_video_codec'] - - audio_details['stream_audio_decision'] = transcode_details['audio_decision'] - stream_details['stream_audio_codec'] = transcode_details['transcode_audio_codec'] - stream_details['stream_audio_channels'] = transcode_details['transcode_audio_channels'] - stream_details['stream_audio_channel_layout'] = common.AUDIO_CHANNELS.get( - transcode_details['transcode_audio_channels'], transcode_details['transcode_audio_channels']) - - # Generate a combined transcode decision value - if video_details['stream_video_decision'] == 'transcode' or audio_details[ - 'stream_audio_decision'] == 'transcode': - transcode_decision = 'transcode' - elif video_details['stream_video_decision'] == 'copy' or audio_details['stream_audio_decision'] == 'copy': - transcode_decision = 'copy' - else: - transcode_decision = 'direct play' - - stream_details['transcode_decision'] = transcode_decision - stream_details['container_decision'] = stream_details['stream_container_decision'] - - # Override * in audio codecs - if stream_details['stream_audio_codec'] == '*': - stream_details['stream_audio_codec'] = source_audio_details.get('audio_codec', '') - if transcode_details['transcode_audio_codec'] == '*': - transcode_details['transcode_audio_codec'] = source_audio_details.get('audio_codec', '') - - # Override * in video codecs - if stream_details['stream_video_codec'] == '*': - stream_details['stream_video_codec'] = source_video_details.get('video_codec', '') - if transcode_details['transcode_video_codec'] == '*': - transcode_details['transcode_video_codec'] = source_video_details.get('video_codec', '') - - if media_type in ('movie', 'episode', 'clip'): - # Set the full resolution by combining stream_video_resolution and stream_video_scan_type - stream_details['stream_video_full_resolution'] = common.VIDEO_RESOLUTION_OVERRIDES.get( - stream_details['stream_video_resolution'], - stream_details['stream_video_resolution'] + (video_details['stream_video_scan_type'][:1] or 'p')) - - if helpers.cast_to_int(source_video_details.get('video_bit_depth')) > 8 \ - and source_video_details.get('video_color_space') == 'bt2020nc': - stream_details['video_dynamic_range'] = 'HDR' - else: - stream_details['video_dynamic_range'] = 'SDR' - - if stream_details['video_dynamic_range'] == 'HDR' \ - and video_details['stream_video_decision'] != 'transcode' \ - or helpers.cast_to_int(video_details['stream_video_bit_depth']) > 8 \ - and video_details['stream_video_color_space'] == 'bt2020nc': - stream_details['stream_video_dynamic_range'] = 'HDR' - else: - stream_details['stream_video_dynamic_range'] = 'SDR' - else: - stream_details['video_dynamic_range'] = '' - stream_details['stream_video_dynamic_range'] = '' - - # Get the quality profile - if media_type in ('movie', 'episode', 'clip') and 'stream_bitrate' in stream_details: - if sync_id: - quality_profile = 'Original' - - synced_item_bitrate = helpers.cast_to_int(synced_item_details['video_bitrate']) - try: - synced_bitrate = max(b for b in common.VIDEO_QUALITY_PROFILES if b <= synced_item_bitrate) - synced_version_profile = common.VIDEO_QUALITY_PROFILES[synced_bitrate] - except ValueError: - synced_version_profile = 'Original' - else: - synced_version_profile = '' - - stream_bitrate = helpers.cast_to_int(stream_details['stream_bitrate']) - source_bitrate = helpers.cast_to_int(source_media_details.get('bitrate')) - try: - quailtiy_bitrate = min( - b for b in common.VIDEO_QUALITY_PROFILES if stream_bitrate <= b <= source_bitrate) - quality_profile = common.VIDEO_QUALITY_PROFILES[quailtiy_bitrate] - except ValueError: - quality_profile = 'Original' - - if stream_details['optimized_version']: - source_bitrate = helpers.cast_to_int(source_media_details.get('bitrate')) - optimized_version_profile = '{} Mbps {}'.format(round(source_bitrate / 1000.0, 1), - source_media_details.get('video_full_resolution')) - else: - optimized_version_profile = '' - - elif media_type == 'track' and 'stream_bitrate' in stream_details: - if sync_id: - quality_profile = 'Original' - - synced_item_bitrate = helpers.cast_to_int(synced_item_details['audio_bitrate']) - try: - synced_bitrate = max(b for b in common.AUDIO_QUALITY_PROFILES if b <= synced_item_bitrate) - synced_version_profile = common.AUDIO_QUALITY_PROFILES[synced_bitrate] - except ValueError: - synced_version_profile = 'Original' - else: - synced_version_profile = '' - - stream_bitrate = helpers.cast_to_int(stream_details['stream_bitrate']) - source_bitrate = helpers.cast_to_int(source_media_details.get('bitrate')) - try: - quailtiy_bitrate = min( - b for b in common.AUDIO_QUALITY_PROFILES if stream_bitrate <= b <= source_bitrate) - quality_profile = common.AUDIO_QUALITY_PROFILES[quailtiy_bitrate] - except ValueError: - quality_profile = 'Original' - - optimized_version_profile = '' - - elif media_type == 'photo': - quality_profile = 'Original' - synced_version_profile = '' - optimized_version_profile = '' - - else: - quality_profile = 'Unknown' - synced_version_profile = '' - optimized_version_profile = '' - - # Entire session output (single dict for backwards compatibility) - session_output = {'session_key': session_key, - 'media_type': media_type, - 'view_offset': view_offset, - 'progress_percent': str(helpers.get_percent(view_offset, stream_details['stream_duration'])), - 'quality_profile': quality_profile, - 'synced_version_profile': synced_version_profile, - 'optimized_version_profile': optimized_version_profile, - 'user': user_details['username'], # Keep for backwards compatibility - 'channel_stream': channel_stream - } - - session_output.update(metadata_details) - session_output.update(source_media_details) - session_output.update(source_media_part_details) - session_output.update(source_video_details) - session_output.update(source_audio_details) - session_output.update(source_subtitle_details) - session_output.update(user_details) - session_output.update(player_details) - session_output.update(session_details) - session_output.update(transcode_details) - session_output.update(stream_details) - session_output.update(video_details) - session_output.update(audio_details) - session_output.update(subtitle_details) - - return session_output - - def terminate_session(self, session_key='', session_id='', message=''): - """ - Terminates a streaming session. - - Output: bool - """ - plex_tv = plextv.PlexTV() - if not plex_tv.get_plexpass_status(): - msg = 'No Plex Pass subscription' - logger.warn("Tautulli Pmsconnect :: Failed to terminate session: %s." % msg) - return msg - - message = message.encode('utf-8') or 'The server owner has ended the stream.' - - ap = activity_processor.ActivityProcessor() - - if session_key: - session = ap.get_session_by_key(session_key=session_key) - if session and not session_id: - session_id = session['session_id'] - - elif session_id: - session = ap.get_session_by_id(session_id=session_id) - if session and not session_key: - session_key = session['session_key'] - - else: - session = session_key = session_id = None - - if not session: - msg = 'Invalid session_key (%s) or session_id (%s)' % (session_key, session_id) - logger.warn("Tautulli Pmsconnect :: Failed to terminate session: %s." % msg) - return msg - - if session_id: - logger.info("Tautulli Pmsconnect :: Terminating session %s (session_id %s)." % (session_key, session_id)) - result = self.get_sessions_terminate(session_id=session_id, reason=message) - return True - else: - msg = 'Missing session_id' - logger.warn("Tautulli Pmsconnect :: Failed to terminate session: %s." % msg) - return msg - - def get_item_children(self, rating_key='', media_type=None, get_grandchildren=False): - """ - Return processed and validated children list. - - Output: array - """ - if media_type == 'playlist': - children_data = self.get_playlist_items(rating_key, output_format='xml') - elif get_grandchildren: - children_data = self.get_metadata_grandchildren(rating_key, output_format='xml') - else: - children_data = self.get_metadata_children(rating_key, output_format='xml') - - try: - xml_head = children_data.getElementsByTagName('MediaContainer') - except Exception as e: - logger.warn("Tautulli Pmsconnect :: Unable to parse XML for get_item_children: %s." % e) - return [] - - children_list = [] - - for a in xml_head: - if a.getAttribute('size'): - if a.getAttribute('size') == '0': - logger.debug("Tautulli Pmsconnect :: No children data.") - children_list = {'children_count': 0, - 'children_list': [] - } - return children_list - - result_data = [] - - for x in a.childNodes: - if x.nodeType == Node.ELEMENT_NODE and x.tagName in ('Directory', 'Video', 'Track', 'Photo'): - result_data.append(x) - - if result_data: - for m in result_data: - directors = [] - writers = [] - actors = [] - genres = [] - labels = [] - - if m.getElementsByTagName('Director'): - for director in m.getElementsByTagName('Director'): - directors.append(helpers.get_xml_attr(director, 'tag')) - - if m.getElementsByTagName('Writer'): - for writer in m.getElementsByTagName('Writer'): - writers.append(helpers.get_xml_attr(writer, 'tag')) - - if m.getElementsByTagName('Role'): - for actor in m.getElementsByTagName('Role'): - actors.append(helpers.get_xml_attr(actor, 'tag')) - - if m.getElementsByTagName('Genre'): - for genre in m.getElementsByTagName('Genre'): - genres.append(helpers.get_xml_attr(genre, 'tag')) - - if m.getElementsByTagName('Label'): - for label in m.getElementsByTagName('Label'): - labels.append(helpers.get_xml_attr(label, 'tag')) - - media_type = helpers.get_xml_attr(m, 'type') - if m.nodeName == 'Directory' and media_type == 'photo': - media_type = 'photo_album' - - children_output = {'media_type': media_type, - 'section_id': helpers.get_xml_attr(m, 'librarySectionID'), - 'library_name': helpers.get_xml_attr(m, 'librarySectionTitle'), - 'rating_key': helpers.get_xml_attr(m, 'ratingKey'), - 'parent_rating_key': helpers.get_xml_attr(m, 'parentRatingKey'), - 'grandparent_rating_key': helpers.get_xml_attr(m, 'grandparentRatingKey'), - 'title': helpers.get_xml_attr(m, 'title'), - 'parent_title': helpers.get_xml_attr(m, 'parentTitle'), - 'grandparent_title': helpers.get_xml_attr(m, 'grandparentTitle'), - 'original_title': helpers.get_xml_attr(m, 'originalTitle'), - 'sort_title': helpers.get_xml_attr(m, 'titleSort'), - 'media_index': helpers.get_xml_attr(m, 'index'), - 'parent_media_index': helpers.get_xml_attr(m, 'parentIndex'), - 'studio': helpers.get_xml_attr(m, 'studio'), - 'content_rating': helpers.get_xml_attr(m, 'contentRating'), - 'summary': helpers.get_xml_attr(m, 'summary'), - 'tagline': helpers.get_xml_attr(m, 'tagline'), - 'rating': helpers.get_xml_attr(m, 'rating'), - 'rating_image': helpers.get_xml_attr(m, 'ratingImage'), - 'audience_rating': helpers.get_xml_attr(m, 'audienceRating'), - 'audience_rating_image': helpers.get_xml_attr(m, 'audienceRatingImage'), - 'user_rating': helpers.get_xml_attr(m, 'userRating'), - 'duration': helpers.get_xml_attr(m, 'duration'), - 'year': helpers.get_xml_attr(m, 'year'), - 'thumb': helpers.get_xml_attr(m, 'thumb'), - 'parent_thumb': helpers.get_xml_attr(m, 'parentThumb'), - 'grandparent_thumb': helpers.get_xml_attr(m, 'grandparentThumb'), - 'art': helpers.get_xml_attr(m, 'art'), - 'banner': helpers.get_xml_attr(m, 'banner'), - 'originally_available_at': helpers.get_xml_attr(m, 'originallyAvailableAt'), - 'added_at': helpers.get_xml_attr(m, 'addedAt'), - 'updated_at': helpers.get_xml_attr(m, 'updatedAt'), - 'last_viewed_at': helpers.get_xml_attr(m, 'lastViewedAt'), - 'guid': helpers.get_xml_attr(m, 'guid'), - 'directors': directors, - 'writers': writers, - 'actors': actors, - 'genres': genres, - 'labels': labels, - 'full_title': helpers.get_xml_attr(m, 'title') - } - children_list.append(children_output) - - output = {'children_count': helpers.cast_to_int(helpers.get_xml_attr(xml_head[0], 'size')), - 'children_type': helpers.get_xml_attr(xml_head[0], 'viewGroup'), - 'title': helpers.get_xml_attr(xml_head[0], 'title2'), - 'children_list': children_list - } - - return output - - def get_item_children_related(self, rating_key=''): - """ - Return processed and validated children list. - - Output: array - """ - children_data = self.get_children_list_related(rating_key, output_format='xml') - - try: - xml_head = children_data.getElementsByTagName('MediaContainer') - except Exception as e: - logger.warn("Tautulli Pmsconnect :: Unable to parse XML for get_item_children_related: %s." % e) - return [] - - children_results_list = {'movie': [], - 'show': [], - 'season': [], - 'episode': [], - 'artist': [], - 'album': [], - 'track': [], - } - - for a in xml_head: - section_id = helpers.get_xml_attr(a, 'librarySectionID') - hubs = a.getElementsByTagName('Hub') - - for h in hubs: - size = helpers.get_xml_attr(h, 'size') - media_type = helpers.get_xml_attr(h, 'type') - title = helpers.get_xml_attr(h, 'title') - hub_identifier = helpers.get_xml_attr(h, 'hubIdentifier') - - if size == '0' or not hub_identifier.startswith('collection.related') or \ - media_type not in children_results_list: - continue - - result_data = [] - - if h.getElementsByTagName('Video'): - result_data = h.getElementsByTagName('Video') - if h.getElementsByTagName('Directory'): - result_data = h.getElementsByTagName('Directory') - if h.getElementsByTagName('Track'): - result_data = h.getElementsByTagName('Track') - - for result in result_data: - children_output = {'section_id': section_id, - 'rating_key': helpers.get_xml_attr(result, 'ratingKey'), - 'parent_rating_key': helpers.get_xml_attr(result, 'parentRatingKey'), - 'media_index': helpers.get_xml_attr(result, 'index'), - 'title': helpers.get_xml_attr(result, 'title'), - 'parent_title': helpers.get_xml_attr(result, 'parentTitle'), - 'year': helpers.get_xml_attr(result, 'year'), - 'thumb': helpers.get_xml_attr(result, 'thumb'), - 'parent_thumb': helpers.get_xml_attr(a, 'thumb'), - 'duration': helpers.get_xml_attr(result, 'duration') - } - children_results_list[media_type].append(children_output) - - output = {'results_count': sum(len(v) for k, v in children_results_list.items()), - 'results_list': children_results_list, - } - - return output - - def get_servers_info(self): - """ - Return the list of local servers. - - Output: array - """ - recent = self.get_server_list(output_format='xml') - - try: - xml_head = recent.getElementsByTagName('Server') - except Exception as e: - logger.warn("Tautulli Pmsconnect :: Unable to parse XML for get_server_list: %s." % e) - return [] - - server_info = [] - for a in xml_head: - output = {"name": helpers.get_xml_attr(a, 'name'), - "machine_identifier": helpers.get_xml_attr(a, 'machineIdentifier'), - "host": helpers.get_xml_attr(a, 'host'), - "port": helpers.get_xml_attr(a, 'port'), - "version": helpers.get_xml_attr(a, 'version') - } - - server_info.append(output) - - return server_info - - def get_server_identity(self): - """ - Return the local machine identity. - - Output: dict - """ - identity = self.get_local_server_identity(output_format='xml') - - try: - xml_head = identity.getElementsByTagName('MediaContainer') - except Exception as e: - logger.warn("Tautulli Pmsconnect :: Unable to parse XML for get_local_server_identity: %s." % e) - return {} - - server_identity = {} - for a in xml_head: - server_identity = {"machine_identifier": helpers.get_xml_attr(a, 'machineIdentifier'), - "version": helpers.get_xml_attr(a, 'version') - } - - return server_identity - - def get_server_pref(self, pref=None): - """ - Return a specified server preference. - - Parameters required: pref { name of preference } - - Output: string - """ - if pref: - prefs = self.get_server_prefs(output_format='xml') - - try: - xml_head = prefs.getElementsByTagName('Setting') - except Exception as e: - logger.warn("Tautulli Pmsconnect :: Unable to parse XML for get_local_server_name: %s." % e) - return '' - - pref_value = 'None' - for a in xml_head: - if helpers.get_xml_attr(a, 'id') == pref: - pref_value = helpers.get_xml_attr(a, 'value') - break - - return pref_value - else: - logger.debug("Tautulli Pmsconnect :: Server preferences queried but no parameter received.") - return None - - def get_server_children(self): - """ - Return processed and validated server libraries list. - - Output: array - """ - libraries_data = self.get_libraries_list(output_format='xml') - - try: - xml_head = libraries_data.getElementsByTagName('MediaContainer') - except Exception as e: - logger.warn("Tautulli Pmsconnect :: Unable to parse XML for get_libraries_list: %s." % e) - return [] - - libraries_list = [] - - for a in xml_head: - if a.getAttribute('size'): - if a.getAttribute('size') == '0': - logger.debug("Tautulli Pmsconnect :: No libraries data.") - libraries_list = {'libraries_count': '0', - 'libraries_list': [] - } - return libraries_list - - if a.getElementsByTagName('Directory'): - result_data = a.getElementsByTagName('Directory') - for result in result_data: - libraries_output = {'section_id': helpers.get_xml_attr(result, 'key'), - 'section_type': helpers.get_xml_attr(result, 'type'), - 'section_name': helpers.get_xml_attr(result, 'title'), - 'agent': helpers.get_xml_attr(result, 'agent'), - 'thumb': helpers.get_xml_attr(result, 'thumb'), - 'art': helpers.get_xml_attr(result, 'art') - } - libraries_list.append(libraries_output) - - output = {'libraries_count': helpers.get_xml_attr(xml_head[0], 'size'), - 'title': helpers.get_xml_attr(xml_head[0], 'title1'), - 'libraries_list': libraries_list - } - - return output - - def get_library_children_details(self, section_id='', section_type='', list_type='all', count='', - rating_key='', label_key='', get_media_info=False): - """ - Return processed and validated server library items list. - - Parameters required: section_type { movie, show, episode, artist } - section_id { unique library key } - - Output: array - """ - - if section_type == 'movie': - sort_type = '&type=1' - elif section_type == 'show': - sort_type = '&type=2' - elif section_type == 'season': - sort_type = '&type=3' - elif section_type == 'episode': - sort_type = '&type=4' - elif section_type == 'artist': - sort_type = '&type=8' - elif section_type == 'album': - sort_type = '&type=9' - elif section_type == 'track': - sort_type = '&type=10' - elif section_type == 'photo': - sort_type = '' - elif section_type == 'photo_album': - sort_type = '&type=14' - elif section_type == 'picture': - sort_type = '&type=13&clusterZoomLevel=1' - elif section_type == 'clip': - sort_type = '&type=12&clusterZoomLevel=1' - else: - sort_type = '' - - if str(section_id).isdigit(): - library_data = self.get_library_list(str(section_id), list_type, count, sort_type, label_key, - output_format='xml') - elif str(rating_key).isdigit(): - library_data = self.get_metadata_children(str(rating_key), output_format='xml') - else: - logger.warn( - "Tautulli Pmsconnect :: get_library_children called by invalid section_id or rating_key provided.") - return [] - - try: - xml_head = library_data.getElementsByTagName('MediaContainer') - except Exception as e: - logger.warn("Tautulli Pmsconnect :: Unable to parse XML for get_library_children_details: %s." % e) - return [] - - children_list = [] - - for a in xml_head: - if a.getAttribute('size'): - if a.getAttribute('size') == '0': - logger.debug("Tautulli Pmsconnect :: No library data.") - children_list = {'library_count': '0', - 'children_list': [] - } - return children_list - - if rating_key: - library_count = helpers.get_xml_attr(xml_head[0], 'size') - else: - library_count = helpers.get_xml_attr(xml_head[0], 'totalSize') - - # Get show/season info from xml_head - - item_main = [] - if a.getElementsByTagName('Directory'): - dir_main = a.getElementsByTagName('Directory') - item_main += [d for d in dir_main if helpers.get_xml_attr(d, 'ratingKey')] - if a.getElementsByTagName('Video'): - item_main += a.getElementsByTagName('Video') - if a.getElementsByTagName('Track'): - item_main += a.getElementsByTagName('Track') - if a.getElementsByTagName('Photo'): - item_main += a.getElementsByTagName('Photo') - - for item in item_main: - media_type = helpers.get_xml_attr(item, 'type') - if item.nodeName == 'Directory' and media_type == 'photo': - media_type = 'photo_album' - - item_info = {'section_id': helpers.get_xml_attr(a, 'librarySectionID'), - 'media_type': media_type, - 'rating_key': helpers.get_xml_attr(item, 'ratingKey'), - 'parent_rating_key': helpers.get_xml_attr(item, 'parentRatingKey'), - 'grandparent_rating_key': helpers.get_xml_attr(item, 'grandparentRatingKey'), - 'title': helpers.get_xml_attr(item, 'title'), - 'parent_title': helpers.get_xml_attr(item, 'parentTitle'), - 'grandparent_title': helpers.get_xml_attr(item, 'grandparentTitle'), - 'original_title': helpers.get_xml_attr(item, 'originalTitle'), - 'sort_title': helpers.get_xml_attr(item, 'titleSort'), - 'media_index': helpers.get_xml_attr(item, 'index'), - 'parent_media_index': helpers.get_xml_attr(item, 'parentIndex'), - 'year': helpers.get_xml_attr(item, 'year'), - 'thumb': helpers.get_xml_attr(item, 'thumb'), - 'parent_thumb': helpers.get_xml_attr(item, 'thumb'), - 'grandparent_thumb': helpers.get_xml_attr(item, 'grandparentThumb'), - 'added_at': helpers.get_xml_attr(item, 'addedAt') - } - - if get_media_info: - item_media = item.getElementsByTagName('Media') - for media in item_media: - media_info = {'container': helpers.get_xml_attr(media, 'container'), - 'bitrate': helpers.get_xml_attr(media, 'bitrate'), - 'video_codec': helpers.get_xml_attr(media, 'videoCodec'), - 'video_resolution': helpers.get_xml_attr(media, 'videoResolution').lower(), - 'video_framerate': helpers.get_xml_attr(media, 'videoFrameRate'), - 'audio_codec': helpers.get_xml_attr(media, 'audioCodec'), - 'audio_channels': helpers.get_xml_attr(media, 'audioChannels'), - 'file': helpers.get_xml_attr(media.getElementsByTagName('Part')[0], 'file'), - 'file_size': helpers.get_xml_attr(media.getElementsByTagName('Part')[0], 'size'), - } - item_info.update(media_info) - - children_list.append(item_info) - - output = {'library_count': library_count, - 'children_list': children_list - } - - return output - - def get_library_details(self): - """ - Return processed and validated library statistics. - - Output: array - """ - server_libraries = self.get_server_children() - - server_library_stats = [] - - if server_libraries and server_libraries['libraries_count'] != '0': - libraries_list = server_libraries['libraries_list'] - - for library in libraries_list: - section_type = library['section_type'] - section_id = library['section_id'] - children_list = self.get_library_children_details(section_id=section_id, section_type=section_type, - count='1') - - if children_list: - library_stats = {'section_id': section_id, - 'section_name': library['section_name'], - 'section_type': section_type, - 'agent': library['agent'], - 'thumb': library['thumb'], - 'art': library['art'], - 'count': children_list['library_count'], - 'is_active': 1 - } - - if section_type == 'show': - parent_list = self.get_library_children_details(section_id=section_id, section_type='season', - count='1') - if parent_list: - parent_stats = {'parent_count': parent_list['library_count']} - library_stats.update(parent_stats) - - child_list = self.get_library_children_details(section_id=section_id, section_type='episode', - count='1') - if child_list: - child_stats = {'child_count': child_list['library_count']} - library_stats.update(child_stats) - - if section_type == 'artist': - parent_list = self.get_library_children_details(section_id=section_id, section_type='album', - count='1') - if parent_list: - parent_stats = {'parent_count': parent_list['library_count']} - library_stats.update(parent_stats) - - child_list = self.get_library_children_details(section_id=section_id, section_type='track', - count='1') - if child_list: - child_stats = {'child_count': child_list['library_count']} - library_stats.update(child_stats) - - if section_type == 'photo': - parent_list = self.get_library_children_details(section_id=section_id, section_type='picture', - count='1') - if parent_list: - parent_stats = {'parent_count': parent_list['library_count']} - library_stats.update(parent_stats) - - child_list = self.get_library_children_details(section_id=section_id, section_type='clip', - count='1') - if child_list: - child_stats = {'child_count': child_list['library_count']} - library_stats.update(child_stats) - - server_library_stats.append(library_stats) - - return server_library_stats - - def get_library_label_details(self, section_id=''): - labels_data = self.get_library_labels(section_id=str(section_id), output_format='xml') - - try: - xml_head = labels_data.getElementsByTagName('MediaContainer') - except Exception as e: - logger.warn("Tautulli Pmsconnect :: Unable to parse XML for get_library_label_details: %s." % e) - return None - - labels_list = [] - - for a in xml_head: - if a.getAttribute('size'): - if a.getAttribute('size') == '0': - logger.debug("Tautulli Pmsconnect :: No labels data.") - return labels_list - - if a.getElementsByTagName('Directory'): - labels_main = a.getElementsByTagName('Directory') - for item in labels_main: - label = {'label_key': helpers.get_xml_attr(item, 'key'), - 'label_title': helpers.get_xml_attr(item, 'title') - } - labels_list.append(label) - - return labels_list - - def get_image(self, img=None, width=1000, height=1500, opacity=None, background=None, blur=None, - img_format='png', clip=False, refresh=False, **kwargs): - """ - Return image data as array. - Array contains the image content type and image binary - - Parameters required: img { Plex image location } - Optional parameters: width { the image width } - height { the image height } - opacity { the image opacity 0-100 } - background { the image background HEX } - blur { the image blur 0-100 } - Output: array - """ - - width = width or 1000 - height = height or 1500 - - if img: - web_img = img.startswith('http') - - if refresh and not web_img: - img = '{}/{}'.format(img.rstrip('/'), helpers.timestamp()) - - if web_img: - params = {'url': '%s' % img} - elif clip: - params = {'url': '%s&%s' % (img, urlencode({'X-Plex-Token': self.token}))} - else: - params = {'url': 'http://127.0.0.1:32400%s?%s' % (img, urlencode({'X-Plex-Token': self.token}))} - - params['width'] = width - params['height'] = height - params['format'] = img_format - - if opacity: - params['opacity'] = opacity - if background: - params['background'] = background - if blur: - params['blur'] = blur - - uri = '/photo/:/transcode?%s' % urlencode(params) - result = self.request_handler.make_request(uri=uri, - request_type='GET', - return_type=True) - - if result is None: - return - else: - return result[0], result[1] - - else: - logger.error("Tautulli Pmsconnect :: Image proxy queried but no input received.") - - def get_search_results(self, query='', limit=''): - """ - Return processed list of search results. - - Output: array - """ - search_results = self.get_search(query=query, limit=limit, output_format='xml') - - try: - xml_head = search_results.getElementsByTagName('MediaContainer') - except Exception as e: - logger.warn("Tautulli Pmsconnect :: Unable to parse XML for get_search_result: %s." % e) - return [] - - search_results_list = {'movie': [], - 'show': [], - 'season': [], - 'episode': [], - 'artist': [], - 'album': [], - 'track': [], - 'collection': [] - } - - for a in xml_head: - hubs = a.getElementsByTagName('Hub') - - for h in hubs: - if helpers.get_xml_attr(h, 'size') == '0' or \ - helpers.get_xml_attr(h, 'type') not in search_results_list: - continue - - if h.getElementsByTagName('Video'): - result_data = h.getElementsByTagName('Video') - for result in result_data: - rating_key = helpers.get_xml_attr(result, 'ratingKey') - metadata = self.get_metadata_details(rating_key=rating_key) - search_results_list[metadata['media_type']].append(metadata) - - if h.getElementsByTagName('Directory'): - result_data = h.getElementsByTagName('Directory') - for result in result_data: - rating_key = helpers.get_xml_attr(result, 'ratingKey') - metadata = self.get_metadata_details(rating_key=rating_key) - search_results_list[metadata['media_type']].append(metadata) - - if metadata['media_type'] == 'show': - show_seasons = self.get_item_children(rating_key=metadata['rating_key']) - if show_seasons['children_count'] != 0: - for season in show_seasons['children_list']: - if season['rating_key']: - metadata = self.get_metadata_details(rating_key=season['rating_key']) - search_results_list['season'].append(metadata) - - if h.getElementsByTagName('Track'): - result_data = h.getElementsByTagName('Track') - for result in result_data: - rating_key = helpers.get_xml_attr(result, 'ratingKey') - metadata = self.get_metadata_details(rating_key=rating_key) - search_results_list[metadata['media_type']].append(metadata) - - output = {'results_count': sum(len(s) for s in search_results_list.values()), - 'results_list': search_results_list - } - - return output - - def get_rating_keys_list(self, rating_key='', media_type=''): - """ - Return processed list of grandparent/parent/child rating keys. - - Output: array - """ - - if media_type == 'movie': - key_list = {0: {'rating_key': int(rating_key)}} - return key_list - - if media_type == 'artist' or media_type == 'album' or media_type == 'track': - match_type = 'title' - else: - match_type = 'index' - - section_id = None - library_name = None - - # get grandparent rating key - if media_type == 'season' or media_type == 'album': - try: - metadata = self.get_metadata_details(rating_key=rating_key) - rating_key = metadata['parent_rating_key'] - section_id = metadata['section_id'] - library_name = metadata['library_name'] - except Exception as e: - logger.warn("Tautulli Pmsconnect :: Unable to get parent_rating_key for get_rating_keys_list: %s." % e) - return {} - - elif media_type == 'episode' or media_type == 'track': - try: - metadata = self.get_metadata_details(rating_key=rating_key) - rating_key = metadata['grandparent_rating_key'] - section_id = metadata['section_id'] - library_name = metadata['library_name'] - except Exception as e: - logger.warn( - "Tautulli Pmsconnect :: Unable to get grandparent_rating_key for get_rating_keys_list: %s." % e) - return {} - - # get parent_rating_keys - metadata = self.get_metadata_children(str(rating_key), output_format='xml') - - try: - xml_head = metadata.getElementsByTagName('MediaContainer') - except Exception as e: - logger.warn("Tautulli Pmsconnect :: Unable to parse XML for get_rating_keys_list: %s." % e) - return {} - - for a in xml_head: - if a.getAttribute('size'): - if a.getAttribute('size') == '0': - return {} - - title = helpers.get_xml_attr(a, 'title2') - - if a.getElementsByTagName('Directory'): - parents_metadata = a.getElementsByTagName('Directory') - else: - parents_metadata = [] - - parents = {} - for item in parents_metadata: - parent_rating_key = helpers.get_xml_attr(item, 'ratingKey') - parent_index = helpers.get_xml_attr(item, 'index') - parent_title = helpers.get_xml_attr(item, 'title') - - if parent_rating_key: - # get rating_keys - metadata = self.get_metadata_children(str(parent_rating_key), output_format='xml') - - try: - xml_head = metadata.getElementsByTagName('MediaContainer') - except Exception as e: - logger.warn("Tautulli Pmsconnect :: Unable to parse XML for get_rating_keys_list: %s." % e) - return {} - - for a in xml_head: - if a.getAttribute('size'): - if a.getAttribute('size') == '0': - return {} - - if a.getElementsByTagName('Video'): - children_metadata = a.getElementsByTagName('Video') - elif a.getElementsByTagName('Track'): - children_metadata = a.getElementsByTagName('Track') - else: - children_metadata = [] - - children = {} - for item in children_metadata: - child_rating_key = helpers.get_xml_attr(item, 'ratingKey') - child_index = helpers.get_xml_attr(item, 'index') - child_title = helpers.get_xml_attr(item, 'title') - - if child_rating_key: - key = int(child_index) if child_index else child_title - children.update({key: {'rating_key': int(child_rating_key)}}) - - key = int(parent_index) if match_type == 'index' else parent_title - parents.update({key: - {'rating_key': int(parent_rating_key), - 'children': children} - }) - - key = 0 if match_type == 'index' else title - key_list = {key: {'rating_key': int(rating_key), - 'children': parents}, - 'section_id': section_id, - 'library_name': library_name - } - - return key_list - - def get_server_response(self): - account_data = self.get_account(output_format='xml') - - try: - xml_head = account_data.getElementsByTagName('MyPlex') - except Exception as e: - logger.warn("Tautulli Pmsconnect :: Unable to parse XML for get_server_response: %s." % e) - return None - - server_response = {} - - for a in xml_head: - server_response = {'mapping_state': helpers.get_xml_attr(a, 'mappingState'), - 'mapping_error': helpers.get_xml_attr(a, 'mappingError'), - 'sign_in_state': helpers.get_xml_attr(a, 'signInState'), - 'public_address': helpers.get_xml_attr(a, 'publicAddress'), - 'public_port': helpers.get_xml_attr(a, 'publicPort'), - 'private_address': helpers.get_xml_attr(a, 'privateAddress'), - 'private_port': helpers.get_xml_attr(a, 'privatePort') - } - - if server_response['mapping_state'] == 'unknown': - server_response['reason'] = 'Plex remote access port mapping unknown' - elif server_response['mapping_state'] not in ('mapped', 'waiting'): - server_response['reason'] = 'Plex remote access port not mapped' - elif server_response['mapping_error'] == 'unreachable': - server_response['reason'] = 'Plex remote access port mapped, ' \ - 'but the port is unreachable from Plex.tv' - elif server_response['mapping_error'] == 'publisherror': - server_response['reason'] = 'Plex remote access port mapped, ' \ - 'but failed to publish the port to Plex.tv' - else: - server_response['reason'] = '' - - return server_response - - def get_update_staus(self): - # Refresh the Plex updater status first - self.put_updater() - updater_status = self.get_updater(output_format='xml') - - try: - xml_head = updater_status.getElementsByTagName('MediaContainer') - except Exception as e: - logger.warn("Tautulli Pmsconnect :: Unable to parse XML for get_update_staus: %s." % e) - - # Catch the malformed XML on certain PMX version. - # XML parser helper returns empty list if there is an error parsing XML - if updater_status == []: - logger.warn( - "Plex API updater XML is broken on the current PMS version. Please update your PMS manually.") - logger.info("Tautulli is unable to check for Plex updates. Disabling check for Plex updates.") - - # Disable check for Plex updates - jellypy.CONFIG.MONITOR_PMS_UPDATES = 0 - jellypy.initialize_scheduler() - jellypy.CONFIG.write() - - return {} - - updater_info = {} - for a in xml_head: - if a.getElementsByTagName('Release'): - release = a.getElementsByTagName('Release') - for item in release: - updater_info = {'can_install': helpers.get_xml_attr(a, 'canInstall'), - 'download_url': helpers.get_xml_attr(a, 'downloadURL'), - 'version': helpers.get_xml_attr(item, 'version'), - 'state': helpers.get_xml_attr(item, 'state'), - 'changelog': helpers.get_xml_attr(item, 'fixed') - } - - return updater_info - - def set_server_version(self): - identity = self.get_server_identity() - version = identity.get('version', jellypy.CONFIG.PMS_VERSION) - - jellypy.CONFIG.__setattr__('PMS_VERSION', version) - jellypy.CONFIG.write() - - def get_server_update_channel(self): - if jellypy.CONFIG.PMS_UPDATE_CHANNEL == 'plex': - update_channel_value = self.get_server_pref('ButlerUpdateChannel') - - if update_channel_value == '8': - return 'beta' - else: - return 'public' - - return jellypy.CONFIG.PMS_UPDATE_CHANNEL diff --git a/jellypy/users.py b/jellypy/users.py index c6eaa321..b6982494 100644 --- a/jellypy/users.py +++ b/jellypy/users.py @@ -25,7 +25,6 @@ from jellypy import datatables from jellypy import helpers from jellypy import libraries from jellypy import logger -from jellypy import plextv from jellypy import session diff --git a/jellypy/webauth.py b/jellypy/webauth.py index efd36d0b..cf547045 100644 --- a/jellypy/webauth.py +++ b/jellypy/webauth.py @@ -33,7 +33,6 @@ from jellypy.database import MonitorDatabase from jellypy.helpers import timestamp from jellypy.password import check_hash from jellypy.users import Users, refresh_users -from jellypy.plextv import PlexTV # Monkey patch SameSite support into cookies. # https://stackoverflow.com/a/50813092 @@ -51,21 +50,22 @@ def plex_user_login(username=None, password=None, token=None, headers=None): user_token = None user_id = None - # Try to login to Plex.tv to check if the user has a vaild account - if username and password: - plex_tv = PlexTV(username=username, password=password, headers=headers) - plex_user = plex_tv.get_token() - if plex_user: - user_token = plex_user['auth_token'] - user_id = plex_user['user_id'] - elif token: - plex_tv = PlexTV(token=token, headers=headers) - plex_user = plex_tv.get_plex_account_details() - if plex_user: - user_token = token - user_id = plex_user['user_id'] - else: - return None + # TODO: Jellyfin + # # Try to login to Plex.tv to check if the user has a vaild account + # if username and password: + # plex_tv = PlexTV(username=username, password=password, headers=headers) + # plex_user = plex_tv.get_token() + # if plex_user: + # user_token = plex_user['auth_token'] + # user_id = plex_user['user_id'] + # elif token: + # plex_tv = PlexTV(token=token, headers=headers) + # plex_user = plex_tv.get_plex_account_details() + # if plex_user: + # user_token = token + # user_id = plex_user['user_id'] + # else: + # return None if user_token and user_id: # Try to retrieve the user from the database. @@ -86,10 +86,12 @@ def plex_user_login(username=None, password=None, token=None, headers=None): if not jellypy.CONFIG.ALLOW_GUEST_ACCESS: return None - # The user is in the database, and guest access is enabled, so try to retrieve a server token. - # If a server token is returned, then the user is a valid friend of the server. - plex_tv = PlexTV(token=user_token, headers=headers) - server_token = plex_tv.get_server_token() + # TODO: Jellyfin + # # The user is in the database, and guest access is enabled, so try to retrieve a server token. + # # If a server token is returned, then the user is a valid friend of the server. + # plex_tv = PlexTV(token=user_token, headers=headers) + # server_token = plex_tv.get_server_token() + server_token = None if server_token: # Register the new user / update the access tokens. diff --git a/jellypy/webserve.py b/jellypy/webserve.py index 7837a254..ca121895 100644 --- a/jellypy/webserve.py +++ b/jellypy/webserve.py @@ -44,7 +44,6 @@ from jellypy import datafactory from jellypy import exporter from jellypy import graphs from jellypy import helpers -from jellypy import http_handler from jellypy import libraries from jellypy import log_reader from jellypy import logger @@ -53,14 +52,11 @@ from jellypy import newsletter_handler from jellypy import newsletters from jellypy import notification_handler from jellypy import notifiers -from jellypy import plextv -from jellypy import pmsconnect from jellypy import users from jellypy import versioncheck -from jellypy import web_socket from jellypy import webstart from jellypy.api2 import API2 -from jellypy.helpers import checked, addtoapi, get_ip, create_https_certificates, build_datatables_json, sanitize_out +from jellypy.helpers import checked, addtoapi, create_https_certificates, build_datatables_json, sanitize_out from jellypy.password import make_hash from jellypy.session import get_session_info, get_session_user_id, allow_session_user, allow_session_library from jellypy.webauth import AuthController, requireAuth, member_of, check_auth @@ -213,9 +209,11 @@ class WebInterface(object): include_cloud = not (include_cloud == 'false') all_servers = not (all_servers == 'false') - plex_tv = plextv.PlexTV() - servers_list = plex_tv.discover(include_cloud=include_cloud, - all_servers=all_servers) + # TODO: Jellyfin + # plex_tv = plextv.PlexTV() + # servers_list = plex_tv.discover(include_cloud=include_cloud, + # all_servers=all_servers) + servers_list = None if servers_list: return servers_list @@ -273,28 +271,30 @@ class WebInterface(object): @cherrypy.expose @requireAuth() def get_current_activity(self, **kwargs): - - pms_connect = pmsconnect.PmsConnect(token=jellypy.CONFIG.PMS_TOKEN) - result = pms_connect.get_current_activity() - - if result: - return serve_template(templatename="current_activity.html", data=result) - else: - logger.warn("Unable to retrieve data for get_current_activity.") - return serve_template(templatename="current_activity.html", data=None) + pass + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect(token=jellypy.CONFIG.PMS_TOKEN) + # result = pms_connect.get_current_activity() + # + # if result: + # return serve_template(templatename="current_activity.html", data=result) + # else: + # logger.warn("Unable to retrieve data for get_current_activity.") + # return serve_template(templatename="current_activity.html", data=None) @cherrypy.expose @requireAuth() def get_current_activity_instance(self, session_key=None, **kwargs): - - pms_connect = pmsconnect.PmsConnect(token=jellypy.CONFIG.PMS_TOKEN) - result = pms_connect.get_current_activity() - - if result: - session = next((s for s in result['sessions'] if s['session_key'] == session_key), None) - return serve_template(templatename="current_activity_instance.html", session=session) - else: - return serve_template(templatename="current_activity_instance.html", session=None) + pass + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect(token=jellypy.CONFIG.PMS_TOKEN) + # result = pms_connect.get_current_activity() + # + # if result: + # session = next((s for s in result['sessions'] if s['session_key'] == session_key), None) + # return serve_template(templatename="current_activity_instance.html", session=session) + # else: + # return serve_template(templatename="current_activity_instance.html", session=None) @cherrypy.expose @cherrypy.tools.json_out() @@ -315,15 +315,16 @@ class WebInterface(object): None ``` """ - pms_connect = pmsconnect.PmsConnect() - result = pms_connect.terminate_session(session_key=session_key, session_id=session_id, message=message) - - if result is True: - return {'result': 'success', 'message': 'Session terminated.'} - elif result: - return {'result': 'error', 'message': 'Failed to terminate session: {}.'.format(result)} - else: - return {'result': 'error', 'message': 'Failed to terminate session.'} + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # result = pms_connect.terminate_session(session_key=session_key, session_id=session_id, message=message) + # + # if result is True: + # return {'result': 'success', 'message': 'Session terminated.'} + # elif result: + # return {'result': 'error', 'message': 'Failed to terminate session: {}.'.format(result)} + # else: + # return {'result': 'error', 'message': 'Failed to terminate session.'} @cherrypy.expose @cherrypy.tools.json_out() @@ -368,18 +369,19 @@ class WebInterface(object): @cherrypy.expose @requireAuth() def get_recently_added(self, count='0', media_type='', **kwargs): - - try: - pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_recently_added_details(count=count, media_type=media_type) - except IOError as e: - return serve_template(templatename="recently_added.html", data=None) - - if result: - return serve_template(templatename="recently_added.html", data=result['recently_added']) - else: - logger.warn("Unable to retrieve data for get_recently_added.") - return serve_template(templatename="recently_added.html", data=None) + pass + # TODO: Jellyfin + # try: + # pms_connect = pmsconnect.PmsConnect() + # result = pms_connect.get_recently_added_details(count=count, media_type=media_type) + # except IOError as e: + # return serve_template(templatename="recently_added.html", data=None) + # + # if result: + # return serve_template(templatename="recently_added.html", data=result['recently_added']) + # else: + # logger.warn("Unable to retrieve data for get_recently_added.") + # return serve_template(templatename="recently_added.html", data=None) @cherrypy.expose @cherrypy.tools.json_out() @@ -688,8 +690,10 @@ class WebInterface(object): return serve_template(templatename="library_recently_added.html", data=None, title="Recently Added") if section_id: - pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_recently_added_details(section_id=section_id, count=limit) + pass + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # result = pms_connect.get_recently_added_details(section_id=section_id, count=limit) else: result = None @@ -2589,8 +2593,10 @@ class WebInterface(object): if get_session_user_id(): user_id = get_session_user_id() - plex_tv = plextv.PlexTV(token=jellypy.CONFIG.PMS_TOKEN) - result = plex_tv.get_synced_items(machine_id=machine_id, user_id_filter=user_id) + # TODO: Jellyfin + # plex_tv = plextv.PlexTV(token=jellypy.CONFIG.PMS_TOKEN) + # result = plex_tv.get_synced_items(machine_id=machine_id, user_id_filter=user_id) + result = None if result: output = {"data": result} @@ -2620,8 +2626,9 @@ class WebInterface(object): ``` """ if client_id and sync_id: - plex_tv = plextv.PlexTV() - delete_row = plex_tv.delete_sync(client_id=client_id, sync_id=sync_id) + # TODO: Jellyfin + # plex_tv = plextv.PlexTV() + # delete_row = plex_tv.delete_sync(client_id=client_id, sync_id=sync_id) return {'result': 'success', 'message': 'Synced item deleted successfully.'} else: return {'result': 'error', 'message': 'Missing client ID and sync ID.'} @@ -3273,9 +3280,11 @@ class WebInterface(object): # Get new server URLs for SSL communications and get new server friendly name if server_changed: - plextv.get_server_resources() - if jellypy.WS_CONNECTED: - web_socket.reconnect() + pass + # TODO: Jellyfin + # plextv.get_server_resources() + # if jellypy.WS_CONNECTED: + # web_socket.reconnect() # If first run, start websocket if first_run: @@ -3304,7 +3313,9 @@ class WebInterface(object): @cherrypy.tools.json_out() @requireAuth(member_of("admin")) def get_server_resources(self, **kwargs): - return plextv.get_server_resources(return_server=True, **kwargs) + pass + # TODO: Jellyfin + # return plextv.get_server_resources(return_server=True, **kwargs) @cherrypy.expose @cherrypy.tools.json_out() @@ -3338,18 +3349,20 @@ class WebInterface(object): @cherrypy.tools.json_out() @requireAuth(member_of("admin")) def get_server_update_params(self, **kwargs): - plex_tv = plextv.PlexTV() - plexpass = plex_tv.get_plexpass_status() - - update_channel = pmsconnect.PmsConnect().get_server_update_channel() - - return {'plexpass': plexpass, - 'pms_platform': common.PMS_PLATFORM_NAME_OVERRIDES.get( - jellypy.CONFIG.PMS_PLATFORM, jellypy.CONFIG.PMS_PLATFORM), - 'pms_update_channel': jellypy.CONFIG.PMS_UPDATE_CHANNEL, - 'pms_update_distro': jellypy.CONFIG.PMS_UPDATE_DISTRO, - 'pms_update_distro_build': jellypy.CONFIG.PMS_UPDATE_DISTRO_BUILD, - 'plex_update_channel': 'plexpass' if update_channel == 'beta' else 'public'} + pass + # TODO: Jellyfin + # plex_tv = plextv.PlexTV() + # plexpass = plex_tv.get_plexpass_status() + # + # update_channel = pmsconnect.PmsConnect().get_server_update_channel() + # + # return {'plexpass': plexpass, + # 'pms_platform': common.PMS_PLATFORM_NAME_OVERRIDES.get( + # jellypy.CONFIG.PMS_PLATFORM, jellypy.CONFIG.PMS_PLATFORM), + # 'pms_update_channel': jellypy.CONFIG.PMS_UPDATE_CHANNEL, + # 'pms_update_distro': jellypy.CONFIG.PMS_UPDATE_DISTRO, + # 'pms_update_distro_build': jellypy.CONFIG.PMS_UPDATE_DISTRO_BUILD, + # 'plex_update_channel': 'plexpass' if update_channel == 'beta' else 'public'} @cherrypy.expose @cherrypy.tools.json_out() @@ -3988,8 +4001,10 @@ class WebInterface(object): if not username and not password: return None - plex_tv = plextv.PlexTV(username=username, password=password) - result = plex_tv.get_token() + # TODO: Jellyfin + # plex_tv = plextv.PlexTV(username=username, password=password) + # result = plex_tv.get_token() + result = None if result: return result['auth_token'] @@ -4007,8 +4022,10 @@ class WebInterface(object): force = helpers.bool_true(force) - plex_tv = plextv.PlexTV(username=username, password=password) - token = plex_tv.get_plexpy_pms_token(force=force) + # TODO: Jellyfin + # plex_tv = plextv.PlexTV(username=username, password=password) + # token = plex_tv.get_plexpy_pms_token(force=force) + token = None if token: return {'result': 'success', 'message': 'Authentication successful.', 'token': token} @@ -4040,30 +4057,32 @@ class WebInterface(object): # Attempt to get the pms_identifier from plex.tv if the server is published # Works for all PMS SSL settings if not identifier and hostname and port: - plex_tv = plextv.PlexTV() - servers = plex_tv.discover() - ip_address = get_ip(hostname) - - for server in servers: - if (server['ip'] == hostname or server['ip'] == ip_address) and server['port'] == port: - identifier = server['clientIdentifier'] - break - - # Fallback to checking /identity endpoint if the server is unpublished - # Cannot set SSL settings on the PMS if unpublished so 'http' is okay - if not identifier: - scheme = 'https' if helpers.cast_to_int(ssl) else 'http' - url = '{scheme}://{hostname}:{port}'.format(scheme=scheme, hostname=hostname, port=port) - uri = '/identity' - - request_handler = http_handler.HTTPHandler(urls=url, - ssl_verify=False) - request = request_handler.make_request(uri=uri, - request_type='GET', - output_format='xml') - if request: - xml_head = request.getElementsByTagName('MediaContainer')[0] - identifier = xml_head.getAttribute('machineIdentifier') + pass + # TODO: Jellyfin + # plex_tv = plextv.PlexTV() + # servers = plex_tv.discover() + # ip_address = get_ip(hostname) + # + # for server in servers: + # if (server['ip'] == hostname or server['ip'] == ip_address) and server['port'] == port: + # identifier = server['clientIdentifier'] + # break + # + # # Fallback to checking /identity endpoint if the server is unpublished + # # Cannot set SSL settings on the PMS if unpublished so 'http' is okay + # if not identifier: + # scheme = 'https' if helpers.cast_to_int(ssl) else 'http' + # url = '{scheme}://{hostname}:{port}'.format(scheme=scheme, hostname=hostname, port=port) + # uri = '/identity' + # + # request_handler = http_handler.HTTPHandler(urls=url, + # ssl_verify=False) + # request = request_handler.make_request(uri=uri, + # request_type='GET', + # output_format='xml') + # if request: + # xml_head = request.getElementsByTagName('MediaContainer')[0] + # identifier = xml_head.getAttribute('machineIdentifier') result = {'identifier': identifier} @@ -4128,9 +4147,10 @@ class WebInterface(object): } ``` """ - server = plextv.get_server_resources(return_info=True) - server.pop('pms_is_cloud', None) - return server + # TODO: Jellyfin + # server = plextv.get_server_resources(return_info=True) + # server.pop('pms_is_cloud', None) + # return server @cherrypy.expose @requireAuth(member_of("admin")) @@ -4147,14 +4167,15 @@ class WebInterface(object): ``` """ - pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_server_pref(pref=pref) - - if result: - return result - else: - logger.warn("Unable to retrieve data for get_server_pref.") - return result + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # result = pms_connect.get_server_pref(pref=pref) + # + # if result: + # return result + # else: + # logger.warn("Unable to retrieve data for get_server_pref.") + # return result @cherrypy.expose @cherrypy.tools.json_out() @@ -4346,10 +4367,11 @@ class WebInterface(object): else: user_info = {} - # Try to get metadata from the Plex server first - if rating_key: - pms_connect = pmsconnect.PmsConnect() - metadata = pms_connect.get_metadata_details(rating_key=rating_key, section_id=section_id) + # TODO: Jellyfin + # # Try to get metadata from the Plex server first + # if rating_key: + # pms_connect = pmsconnect.PmsConnect() + # metadata = pms_connect.get_metadata_details(rating_key=rating_key, section_id=section_id) # If the item is not found on the Plex server, get the metadata from history if not metadata and source == 'history': @@ -4378,28 +4400,30 @@ class WebInterface(object): @cherrypy.expose @requireAuth() def get_item_children(self, rating_key='', media_type=None, **kwargs): - - pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_item_children(rating_key=rating_key, media_type=media_type) - - if result: - return serve_template(templatename="info_children_list.html", data=result, - media_type=media_type, title="Children List") - else: - logger.warn("Unable to retrieve data for get_item_children.") - return serve_template(templatename="info_children_list.html", data=None, title="Children List") + pass + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # result = pms_connect.get_item_children(rating_key=rating_key, media_type=media_type) + # + # if result: + # return serve_template(templatename="info_children_list.html", data=result, + # media_type=media_type, title="Children List") + # else: + # logger.warn("Unable to retrieve data for get_item_children.") + # return serve_template(templatename="info_children_list.html", data=None, title="Children List") @cherrypy.expose @requireAuth() def get_item_children_related(self, rating_key='', title='', **kwargs): - - pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_item_children_related(rating_key=rating_key) - - if result: - return serve_template(templatename="info_collection_list.html", data=result, title=title) - else: - return serve_template(templatename="info_collection_list.html", data=None, title=title) + pass + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # result = pms_connect.get_item_children_related(rating_key=rating_key) + # + # if result: + # return serve_template(templatename="info_collection_list.html", data=result, title=title) + # else: + # return serve_template(templatename="info_collection_list.html", data=None, title=title) @cherrypy.expose @cherrypy.tools.json_out() @@ -4423,24 +4447,25 @@ class WebInterface(object): } ``` """ - if rating_key: - pms_connect = pmsconnect.PmsConnect() - metadata = pms_connect.get_metadata_details(rating_key=rating_key) - data = {'timeline_data': metadata, 'notify_action': 'on_created', 'manual_trigger': True} - - if metadata['media_type'] not in ('movie', 'episode', 'track'): - children = pms_connect.get_item_children(rating_key=rating_key) - child_keys = [child['rating_key'] for child in children['children_list'] if child['rating_key']] - data['child_keys'] = child_keys - - if notifier_id: - data['notifier_id'] = notifier_id - - jellypy.NOTIFY_QUEUE.put(data) - return {'result': 'success', 'message': 'Notification queued.'} - - else: - return {'result': 'error', 'message': 'Notification failed.'} + # TODO: Jellyfin + # if rating_key: + # pms_connect = pmsconnect.PmsConnect() + # metadata = pms_connect.get_metadata_details(rating_key=rating_key) + # data = {'timeline_data': metadata, 'notify_action': 'on_created', 'manual_trigger': True} + # + # if metadata['media_type'] not in ('movie', 'episode', 'track'): + # children = pms_connect.get_item_children(rating_key=rating_key) + # child_keys = [child['rating_key'] for child in children['children_list'] if child['rating_key']] + # data['child_keys'] = child_keys + # + # if notifier_id: + # data['notifier_id'] = notifier_id + # + # jellypy.NOTIFY_QUEUE.put(data) + # return {'result': 'success', 'message': 'Notification queued.'} + # + # else: + # return {'result': 'error', 'message': 'Notification failed.'} @cherrypy.expose def pms_image_proxy(self, **kwargs): @@ -4535,16 +4560,18 @@ class WebInterface(object): except NotFound: # the image does not exist, download it from pms try: - pms_connect = pmsconnect.PmsConnect() - 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) + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # 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) + result = None if result and result[0]: cherrypy.response.headers['Content-type'] = result[1] @@ -4807,35 +4834,37 @@ class WebInterface(object): } ``` """ - pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_search_results(query=query, limit=limit) - - if result: - return result - else: - logger.warn("Unable to retrieve data for search_results.") - return result + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # result = pms_connect.get_search_results(query=query, limit=limit) + # + # if result: + # return result + # else: + # logger.warn("Unable to retrieve data for search_results.") + # return result @cherrypy.expose @requireAuth() def get_search_results_children(self, query='', limit='', media_type=None, season_index=None, **kwargs): + pass + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # result = pms_connect.get_search_results(query=query, limit=limit) + # + # if media_type: + # result['results_list'] = {media_type: result['results_list'][media_type]} + # if media_type == 'season' and season_index: + # result['results_list']['season'] = [season for season in result['results_list']['season'] + # if season['media_index'] == season_index] + # + # if result: + # return serve_template(templatename="info_search_results_list.html", data=result, title="Search Result List") + # else: + # logger.warn("Unable to retrieve data for get_search_results_children.") + # return serve_template(templatename="info_search_results_list.html", data=None, title="Search Result List") - pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_search_results(query=query, limit=limit) - - if media_type: - result['results_list'] = {media_type: result['results_list'][media_type]} - if media_type == 'season' and season_index: - result['results_list']['season'] = [season for season in result['results_list']['season'] - if season['media_index'] == season_index] - - if result: - return serve_template(templatename="info_search_results_list.html", data=result, title="Search Result List") - else: - logger.warn("Unable to retrieve data for get_search_results_children.") - return serve_template(templatename="info_search_results_list.html", data=None, title="Search Result List") - - ##### Update Metadata ##### + # Update Metadata @cherrypy.expose @requireAuth(member_of("admin")) @@ -4876,21 +4905,22 @@ class WebInterface(object): None ``` """ - if new_rating_key: - data_factory = datafactory.DataFactory() - pms_connect = pmsconnect.PmsConnect() - - old_key_list = data_factory.get_rating_keys_list(rating_key=old_rating_key, media_type=media_type) - new_key_list = pms_connect.get_rating_keys_list(rating_key=new_rating_key, media_type=media_type) - - result = data_factory.update_metadata(old_key_list=old_key_list, - new_key_list=new_key_list, - media_type=media_type) - - if result: - return {'message': result} - else: - return {'message': 'no data received'} + # TODO: Jellyfin + # if new_rating_key: + # data_factory = datafactory.DataFactory() + # pms_connect = pmsconnect.PmsConnect() + # + # old_key_list = data_factory.get_rating_keys_list(rating_key=old_rating_key, media_type=media_type) + # new_key_list = pms_connect.get_rating_keys_list(rating_key=new_rating_key, media_type=media_type) + # + # result = data_factory.update_metadata(old_key_list=old_key_list, + # new_key_list=new_key_list, + # media_type=media_type) + # + # if result: + # return {'message': result} + # else: + # return {'message': 'no data received'} # test code @cherrypy.expose @@ -4913,15 +4943,15 @@ class WebInterface(object): {} ``` """ - - pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_rating_keys_list(rating_key=rating_key, media_type=media_type) - - if result: - return result - else: - logger.warn("Unable to retrieve data for get_new_rating_keys.") - return result + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # result = pms_connect.get_rating_keys_list(rating_key=rating_key, media_type=media_type) + # + # if result: + # return result + # else: + # logger.warn("Unable to retrieve data for get_new_rating_keys.") + # return result @cherrypy.expose @cherrypy.tools.json_out() @@ -4958,14 +4988,15 @@ class WebInterface(object): @requireAuth(member_of("admin")) def get_pms_sessions_json(self, **kwargs): """ Get all the current sessions. """ - pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_sessions('json') - - if result: - return result - else: - logger.warn("Unable to retrieve data for get_pms_sessions_json.") - return False + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # result = pms_connect.get_sessions('json') + # + # if result: + # return result + # else: + # logger.warn("Unable to retrieve data for get_pms_sessions_json.") + # return False @cherrypy.expose @cherrypy.tools.json_out() @@ -5127,15 +5158,16 @@ class WebInterface(object): } ``` """ - pms_connect = pmsconnect.PmsConnect() - metadata = pms_connect.get_metadata_details(rating_key=rating_key, - sync_id=sync_id) - - if metadata: - return metadata - else: - logger.warn("Unable to retrieve data for get_metadata_details.") - return metadata + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # metadata = pms_connect.get_metadata_details(rating_key=rating_key, + # sync_id=sync_id) + # + # if metadata: + # return metadata + # else: + # logger.warn("Unable to retrieve data for get_metadata_details.") + # return metadata @cherrypy.expose @cherrypy.tools.json_out() @@ -5218,86 +5250,90 @@ class WebInterface(object): } ``` """ - # For backwards compatibility - if 'type' in kwargs: - media_type = kwargs['type'] - - pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_recently_added_details(start=start, count=count, media_type=media_type, - section_id=section_id) - - if result: - return result - else: - logger.warn("Unable to retrieve data for get_recently_added_details.") - return result + # TODO: Jellyfin + # # For backwards compatibility + # if 'type' in kwargs: + # media_type = kwargs['type'] + # + # pms_connect = pmsconnect.PmsConnect() + # result = pms_connect.get_recently_added_details(start=start, count=count, media_type=media_type, + # section_id=section_id) + # + # if result: + # return result + # else: + # logger.warn("Unable to retrieve data for get_recently_added_details.") + # return result @cherrypy.expose @cherrypy.tools.json_out() @requireAuth(member_of("admin")) def get_friends_list(self, **kwargs): """ Get the friends list of the server owner for Plex.tv. """ - - plex_tv = plextv.PlexTV() - result = plex_tv.get_plextv_friends('json') - - if result: - return result - else: - logger.warn("Unable to retrieve data for get_friends_list.") + # TODO: Jellyfin + # plex_tv = plextv.PlexTV() + # result = plex_tv.get_plextv_friends('json') + # + # if result: + # return result + # else: + # logger.warn("Unable to retrieve data for get_friends_list.") @cherrypy.expose @cherrypy.tools.json_out() @requireAuth(member_of("admin")) def get_user_details(self, **kwargs): """ Get all details about a the server's owner from Plex.tv. """ - - plex_tv = plextv.PlexTV() - result = plex_tv.get_plextv_user_details('json') - - if result: - return result - else: - logger.warn("Unable to retrieve data for get_user_details.") + # TODO: Jellyfin + # plex_tv = plextv.PlexTV() + # result = plex_tv.get_plextv_user_details('json') + # + # if result: + # return result + # else: + # logger.warn("Unable to retrieve data for get_user_details.") @cherrypy.expose @cherrypy.tools.json_out() @requireAuth(member_of("admin")) def get_server_list(self, **kwargs): """ Find all servers published on Plex.tv """ - - plex_tv = plextv.PlexTV() - result = plex_tv.get_plextv_server_list('json') - - if result: - return result - else: - logger.warn("Unable to retrieve data for get_server_list.") + # TODO: Jellyfin + # plex_tv = plextv.PlexTV() + # result = plex_tv.get_plextv_server_list('json') + # + # if result: + # return result + # else: + # logger.warn("Unable to retrieve data for get_server_list.") @cherrypy.expose @cherrypy.tools.json_out() @requireAuth(member_of("admin")) def get_sync_lists(self, machine_id='', **kwargs): """ Get all items that are currently synced from the PMS. """ - plex_tv = plextv.PlexTV() - result = plex_tv.get_plextv_sync_lists(machine_id=machine_id, output_format='json') - - if result: - return result - else: - logger.warn("Unable to retrieve data for get_sync_lists.") + # TODO: Jellyfin + # plex_tv = plextv.PlexTV() + # result = plex_tv.get_plextv_sync_lists(machine_id=machine_id, output_format='json') + # + # if result: + # return result + # else: + # logger.warn("Unable to retrieve data for get_sync_lists.") @cherrypy.expose @cherrypy.tools.json_out() @requireAuth(member_of("admin")) def get_servers(self, **kwargs): - pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_server_list(output_format='json') - - if result: - return result - else: - logger.warn("Unable to retrieve data for get_servers.") + pass + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # result = pms_connect.get_server_list(output_format='json') + # + # if result: + # return result + # else: + # logger.warn("Unable to retrieve data for get_servers.") @cherrypy.expose @cherrypy.tools.json_out() @@ -5324,14 +5360,15 @@ class WebInterface(object): ] ``` """ - pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_servers_info() - - if result: - return result - else: - logger.warn("Unable to retrieve data for get_servers_info.") - return result + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # result = pms_connect.get_servers_info() + # + # if result: + # return result + # else: + # logger.warn("Unable to retrieve data for get_servers_info.") + # return result @cherrypy.expose @cherrypy.tools.json_out() @@ -5355,14 +5392,15 @@ class WebInterface(object): ] ``` """ - pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_server_identity() - - if result: - return result - else: - logger.warn("Unable to retrieve data for get_server_identity.") - return result + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # result = pms_connect.get_server_identity() + # + # if result: + # return result + # else: + # logger.warn("Unable to retrieve data for get_server_identity.") + # return result @cherrypy.expose @cherrypy.tools.json_out() @@ -5382,13 +5420,14 @@ class WebInterface(object): string: "Winterfell-Server" ``` """ - result = pmsconnect.get_server_friendly_name() - - if result: - return result - else: - logger.warn("Unable to retrieve data for get_server_friendly_name.") - return result + # TODO: Jellyfin + # result = pmsconnect.get_server_friendly_name() + # + # if result: + # return result + # else: + # logger.warn("Unable to retrieve data for get_server_friendly_name.") + # return result @cherrypy.expose @cherrypy.tools.json_out() @@ -5653,45 +5692,46 @@ class WebInterface(object): } ``` """ - try: - pms_connect = pmsconnect.PmsConnect(token=jellypy.CONFIG.PMS_TOKEN) - result = pms_connect.get_current_activity() - - if result: - if session_key: - return next((s for s in result['sessions'] if s['session_key'] == session_key), {}) - if session_id: - return next((s for s in result['sessions'] if s['session_id'] == session_id), {}) - - counts = {'stream_count_direct_play': 0, - 'stream_count_direct_stream': 0, - 'stream_count_transcode': 0, - 'total_bandwidth': 0, - 'lan_bandwidth': 0, - 'wan_bandwidth': 0} - - for s in result['sessions']: - if s['transcode_decision'] == 'transcode': - counts['stream_count_transcode'] += 1 - elif s['transcode_decision'] == 'copy': - counts['stream_count_direct_stream'] += 1 - else: - counts['stream_count_direct_play'] += 1 - - counts['total_bandwidth'] += helpers.cast_to_int(s['bandwidth']) - if s['location'] == 'lan': - counts['lan_bandwidth'] += helpers.cast_to_int(s['bandwidth']) - else: - counts['wan_bandwidth'] += helpers.cast_to_int(s['bandwidth']) - - result.update(counts) - - return result - else: - logger.warn("Unable to retrieve data for get_activity.") - return {} - except Exception as e: - logger.exception("Unable to retrieve data for get_activity: %s" % e) + # TODO: Jellyfin + # try: + # pms_connect = pmsconnect.PmsConnect(token=jellypy.CONFIG.PMS_TOKEN) + # result = pms_connect.get_current_activity() + # + # if result: + # if session_key: + # return next((s for s in result['sessions'] if s['session_key'] == session_key), {}) + # if session_id: + # return next((s for s in result['sessions'] if s['session_id'] == session_id), {}) + # + # counts = {'stream_count_direct_play': 0, + # 'stream_count_direct_stream': 0, + # 'stream_count_transcode': 0, + # 'total_bandwidth': 0, + # 'lan_bandwidth': 0, + # 'wan_bandwidth': 0} + # + # for s in result['sessions']: + # if s['transcode_decision'] == 'transcode': + # counts['stream_count_transcode'] += 1 + # elif s['transcode_decision'] == 'copy': + # counts['stream_count_direct_stream'] += 1 + # else: + # counts['stream_count_direct_play'] += 1 + # + # counts['total_bandwidth'] += helpers.cast_to_int(s['bandwidth']) + # if s['location'] == 'lan': + # counts['lan_bandwidth'] += helpers.cast_to_int(s['bandwidth']) + # else: + # counts['wan_bandwidth'] += helpers.cast_to_int(s['bandwidth']) + # + # result.update(counts) + # + # return result + # else: + # logger.warn("Unable to retrieve data for get_activity.") + # return {} + # except Exception as e: + # logger.exception("Unable to retrieve data for get_activity: %s" % e) @cherrypy.expose @cherrypy.tools.json_out() @@ -5724,14 +5764,15 @@ class WebInterface(object): ] ``` """ - pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_library_details() - - if result: - return result - else: - logger.warn("Unable to retrieve data for get_full_libraries_list.") - return result + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # result = pms_connect.get_library_details() + # + # if result: + # return result + # else: + # logger.warn("Unable to retrieve data for get_full_libraries_list.") + # return result @cherrypy.expose @cherrypy.tools.json_out() @@ -5830,27 +5871,29 @@ class WebInterface(object): ] ``` """ - plex_tv = plextv.PlexTV() - result = plex_tv.get_synced_items(machine_id=machine_id, user_id_filter=user_id) - - if result: - return result - else: - logger.warn("Unable to retrieve data for get_synced_items.") - return result + # TODO: Jellyfin + # plex_tv = plextv.PlexTV() + # result = plex_tv.get_synced_items(machine_id=machine_id, user_id_filter=user_id) + # + # if result: + # return result + # else: + # logger.warn("Unable to retrieve data for get_synced_items.") + # return result @cherrypy.expose @cherrypy.tools.json_out() @requireAuth(member_of("admin")) def get_sync_transcode_queue(self, **kwargs): """ Return details for currently syncing items. """ - pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_sync_transcode_queue(output_format='json') - - if result: - return result - else: - logger.warn("Unable to retrieve data for get_sync_transcode_queue.") + # TODO: Jellyfin + # pms_connect = pmsconnect.PmsConnect() + # result = pms_connect.get_sync_transcode_queue(output_format='json') + # + # if result: + # return result + # else: + # logger.warn("Unable to retrieve data for get_sync_transcode_queue.") @cherrypy.expose @cherrypy.tools.json_out() @@ -6046,9 +6089,10 @@ class WebInterface(object): } ``` """ - plex_tv = plextv.PlexTV() - result = plex_tv.get_plex_downloads() - return result + # TODO: Jellyfin + # plex_tv = plextv.PlexTV() + # result = plex_tv.get_plex_downloads() + # return result @cherrypy.expose @cherrypy.tools.json_out() @@ -6078,20 +6122,21 @@ class WebInterface(object): } ``` """ - message = '' - if not ip_address: - message = 'No IP address provided.' - elif not helpers.is_valid_ip(ip_address): - message = 'Invalid IP address provided: %s' % ip_address - - if message: - return {'result': 'error', 'message': message} - - plex_tv = plextv.PlexTV() - geo_info = plex_tv.get_geoip_lookup(ip_address) - if geo_info: - return {'result': 'success', 'data': geo_info} - return {'result': 'error', 'message': 'Failed to lookup GeoIP info for address: %s' % ip_address} + # TODO: Jellyfin + # message = '' + # if not ip_address: + # message = 'No IP address provided.' + # elif not helpers.is_valid_ip(ip_address): + # message = 'Invalid IP address provided: %s' % ip_address + # + # if message: + # return {'result': 'error', 'message': message} + # + # plex_tv = plextv.PlexTV() + # geo_info = plex_tv.get_geoip_lookup(ip_address) + # if geo_info: + # return {'result': 'success', 'data': geo_info} + # return {'result': 'error', 'message': 'Failed to lookup GeoIP info for address: %s' % ip_address} @cherrypy.expose @cherrypy.tools.json_out()