From 846d47542f24c2e9e5850d05e0b0afdea3cf5dd8 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Fri, 26 Jan 2024 18:09:25 -0500 Subject: [PATCH] better support for new plex series agent --- .github/workflows/CI.yml | 1 - Contents/Code/default_prefs.py | 2 + Contents/Code/general_helper.py | 82 ++++++++++++++++++++++++++++++-- Contents/Code/plex_api_helper.py | 16 +++++-- Contents/DefaultPrefs.json | 14 ++++++ docs/source/about/usage.rst | 22 +++++++++ 6 files changed, 127 insertions(+), 10 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index f242d146..a607d05d 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -180,7 +180,6 @@ jobs: - name: Bootstrap Plex server env: PLEXAPI_PLEXAPI_TIMEOUT: "60" - THEMERR_CI_TESTING: "true" id: bootstrap uses: LizardByte/plexhints@feat(action)-add-legacy-tv-agents # todo - switch back to release version with: diff --git a/Contents/Code/default_prefs.py b/Contents/Code/default_prefs.py index b9b793ee..e6520f29 100644 --- a/Contents/Code/default_prefs.py +++ b/Contents/Code/default_prefs.py @@ -2,6 +2,8 @@ default_prefs = dict( bool_plex_movie_support='True', + bool_plex_series_support='True', + bool_overwrite_plex_provided_themes='False', bool_prefer_mp4a_codec='True', bool_remove_unused_theme_songs='True', bool_remove_unused_art='False', diff --git a/Contents/Code/general_helper.py b/Contents/Code/general_helper.py index 2d01d011..41f17134 100644 --- a/Contents/Code/general_helper.py +++ b/Contents/Code/general_helper.py @@ -16,6 +16,8 @@ from plexhints.log_kit import Log # log kit from plexhints.prefs_kit import Prefs # prefs kit +# imports from Libraries\Shared +import lxml.etree as ET # local imports from constants import metadata_base_directory, metadata_type_map, themerr_data_directory @@ -26,6 +28,36 @@ ] +def _get_metadata_path(item): + # type: (any) -> str + """ + Get the metadata path of the item. + + Get the hashed path of the metadata directory for the item specified by the ``item``. + + Parameters + ---------- + item : any + The item to get the theme upload path for. + + Returns + ------- + str + The path to the metadata directory. + + Examples + -------- + >>> _get_metadata_path(item=...) + "...bundle" + """ + guid = item.guid + full_hash = hashlib.sha1(guid).hexdigest() + metadata_path = os.path.join( + metadata_base_directory, metadata_type_map[item.type], + full_hash[0], full_hash[1:] + '.bundle') + return metadata_path + + def get_media_upload_path(item, media_type): # type: (any, str) -> str """ @@ -66,14 +98,54 @@ def get_media_upload_path(item, media_type): 'media_type must be one of: {}'.format(allowed_media_types) ) - guid = item.guid - full_hash = hashlib.sha1(guid).hexdigest() - theme_upload_path = os.path.join( - metadata_base_directory, metadata_type_map[item.type], - full_hash[0], full_hash[1:] + '.bundle', 'Uploads', media_type) + theme_upload_path = os.path.join(_get_metadata_path(item=item), media_type) return theme_upload_path +def plex_provided_theme(item): + # type: (any) -> bool + """ + Determine if the theme was provided by Plex. + + Open, and parse, the combined Info.xml file for the item specified by the ``item`` and determine if the theme was + provided by Plex. + + Parameters + ---------- + item : any + The item to get the theme upload path for. + + Returns + ------- + bool + True if the theme is provided by Plex, False otherwise. + """ + if item.type != 'show': + return False + + metadata_path = _get_metadata_path(item=item) + info_xml_path = os.path.join(metadata_path, 'Contents', '_combined', 'Info.xml') + + if not os.path.isfile(info_xml_path): + return False + + try: + tree = ET.parse(info_xml_path) + + if tree.getroot().tag != 'TV_Show': + return False + + # theme is nested in TV_Show > themes > item tags + themes = tree.find('.//themes/item') + if themes.attrib.get('provider') == 'com.plexapp.agents.plexthememusic': + return True + except Exception as e: + Log.Error('Error parsing Info.xml file: %s' % e) + return False + + return False + + def get_themerr_json_path(item): # type: (any) -> str """ diff --git a/Contents/Code/plex_api_helper.py b/Contents/Code/plex_api_helper.py index 6236d297..c0326f1d 100644 --- a/Contents/Code/plex_api_helper.py +++ b/Contents/Code/plex_api_helper.py @@ -193,7 +193,11 @@ def update_plex_item(rating_key): except Exception as e: Log.Error('{}: Error updating summary: {}'.format(item.ratingKey, e)) - if item.isLocked(field='theme') and not os.environ.get('THEMERR_CI_TESTING'): + if item.isLocked(field='theme') and not ( + item.type == 'show' and + Prefs['bool_overwrite_plex_provided_themes'] and + general_helper.plex_provided_theme(item=item) + ): Log.Debug('Not overwriting locked theme for {}: {}'.format(item.type, item.title)) else: # get youtube_url @@ -669,7 +673,7 @@ def plex_listener_handler(data): Process events from ``plex_listener()``. Check if we need to add an item to the queue. This is used to automatically add themes to items from the - new Plex Movie agent, since metadata agents cannot extend it. + new Plex agents, since metadata agents cannot extend them. Parameters ---------- @@ -689,11 +693,12 @@ def plex_listener_handler(data): # known search types: # https://github.com/pkkid/python-plexapi/blob/8b3235445f6b3051c39ff6d6fc5d49f4e674d576/plexapi/utils.py#L35-L55 - if (reverseSearchType(libtype=entry['type']) == 'movie' + if ((reverseSearchType(libtype=entry['type']) == 'movie' + or reverseSearchType(libtype=entry['type']) == 'show') and entry['state'] == 5 and entry['identifier'] == 'com.plexapp.plugins.library'): # identifier always appears to be `com.plexapp.plugins.library` for updating library metadata - # entry['title'] = movie title + # entry['title'] = item title # entry['itemID'] = rating key rating_key = int(entry['itemID']) @@ -737,6 +742,9 @@ def scheduled_update(): if section.agent == 'tv.plex.agents.movie': if not Prefs['bool_plex_movie_support']: continue + elif section.agent == 'tv.plex.agents.series': + if not Prefs['bool_plex_series_support']: + continue elif section.agent in contributes_to: # check if the agent is enabled if not plex_token: diff --git a/Contents/DefaultPrefs.json b/Contents/DefaultPrefs.json index a5cc064a..254b16f4 100644 --- a/Contents/DefaultPrefs.json +++ b/Contents/DefaultPrefs.json @@ -6,6 +6,20 @@ "default": "True", "secure": "false" }, + { + "id": "bool_plex_series_support", + "type": "bool", + "label": "Plex Series agent support (Add themes to the updated Plex Series agent)", + "default": "True", + "secure": "false" + }, + { + "id": "bool_overwrite_plex_provided_themes", + "type": "bool", + "label": "Overwrite Plex provided themes", + "default": "False", + "secure": "false" + }, { "id": "bool_prefer_mp4a_codec", "type": "bool", diff --git a/docs/source/about/usage.rst b/docs/source/about/usage.rst index d9ed4fc2..7bd2042b 100644 --- a/docs/source/about/usage.rst +++ b/docs/source/about/usage.rst @@ -66,6 +66,28 @@ Description Default ``True`` +Plex Series agent support +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Description + When enabled, Themerr-plex will add themes to shows using the Plex Series agent. This is the new agent that is + not using the Plex plugin framework, so Themerr-plex cannot contribute to this agent with standard techniques. + Instead Themerr-plex will start a websocket server and listen for events from the Plex server. Whenever a show + is added or has it's metadata refreshed, Themerr-plex will attempt to add a theme song to the show (if the theme + song is available in ThemerrDB). + +Default + ``True`` + +Overwrite Plex provided themes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Description + When enabled, Themerr-plex will overwrite any TV Show theme songs provided by Plex. + +Default + ``False`` + Prefer MP4A AAC Codec ^^^^^^^^^^^^^^^^^^^^^