diff --git a/Contents/Code/__init__.py b/Contents/Code/__init__.py index 30be7a3c..94c615e7 100644 --- a/Contents/Code/__init__.py +++ b/Contents/Code/__init__.py @@ -29,14 +29,15 @@ # local imports if sys.version_info.major < 3: from default_prefs import default_prefs - from constants import issue_url_games, issue_url_movies + from constants import contributes_to, issue_url_games, issue_url_movies from plex_api_helper import add_themes, get_plex_item, plex_listener from youtube_dl_helper import process_youtube else: from .default_prefs import default_prefs - from .constants import issue_url_games, issue_url_movies + from .constants import contributes_to, issue_url_games, issue_url_movies from .plex_api_helper import add_themes, get_plex_item, plex_listener from .youtube_dl_helper import process_youtube +import completion_report def ValidatePrefs(): @@ -130,6 +131,9 @@ def Start(): plex_listener() Log.Debug('plex_listener started, watching for activity from new Plex Movie agent.') + # generate report + completion_report.generate_report() + @handler(prefix='/music/themerr-plex', name='Themerr-plex', thumb='attribution.png') def main(): @@ -194,12 +198,7 @@ class Themerr(Agent.Movies): primary_provider = False fallback_agent = False accepts_from = [] - contributes_to = [ - 'com.plexapp.agents.imdb', - 'com.plexapp.agents.themoviedb', - # 'com.plexapp.agents.thetvdb', # not available as movie agent - 'dev.lizardbyte.retroarcher-plex' - ] + contributes_to = contributes_to @staticmethod def search(results, media, lang, manual): diff --git a/Contents/Code/completion_report.py b/Contents/Code/completion_report.py new file mode 100644 index 00000000..c7eef666 --- /dev/null +++ b/Contents/Code/completion_report.py @@ -0,0 +1,222 @@ +# -*- coding: utf-8 -*- + +# future imports +from __future__ import division + +# standard imports +import os +import sys + +# plex debugging +try: + import plexhints # noqa: F401 +except ImportError: + pass +else: # the code is running outside of Plex + from plexhints.core_kit import Core # core kit + from plexhints.log_kit import Log # log kit + +# imports from Libraries\Shared +from typing import Optional, Tuple + +# local imports +import plex_api_helper +if sys.version_info.major < 3: + from constants import contributes_to, guid_map, issue_url_games, issue_url_movies, themerr_data_directory + from plex_api_helper import get_database_id +else: + from .constants import contributes_to, guid_map, issue_url_games, issue_url_movies, themerr_data_directory + from .plex_api_helper import get_database_id + + +def build_modify_button(item, database_info, btn_text): + # type: (any, Tuple[str, str], str) -> str + """ + Create the modification button for the plex item. + """ + item_agent = database_info[0] + database_id = database_info[1] + + if item_agent == 'dev.lizardbyte.retroarcher-plex': + issue_url = issue_url_games + elif item_agent in contributes_to: + issue_url = issue_url_movies + else: + issue_url = None + + modify_button = "" + + if issue_url: + issue_title = '%s (%s)' % (item.title, item.year) + item_issue_url = issue_url % (issue_title, database_id) + + if database_id: + modify_button = '{1}'.format(item_issue_url, btn_text) + + return modify_button + + +def generate_report(): + """ + Generate a report with the theme song completion status of all items in the library. + """ + # get all Plex items from supported metadata agents + plex_server = plex_api_helper.setup_plexapi() + plex_library = plex_server.library + Log.Debug('plex_library: %s' % plex_library) + + sections = plex_library.sections() + Log.Debug('sections: %s' % sections) + + html = """ + + + + + Themerr-plex completion report + + + + + + + + + +
+ + """ + for section in sections: + if section.agent not in contributes_to: + # todo - there is a small chance that a library with an unsupported agent could still have + # a individual items that was matched with a supported agent... + continue # skip unsupported metadata agents + + # get all items in the section + all_items = section.all() + Log.Debug('section count items: %s' % len(all_items)) + + # get all items in the section with theme songs + items_with_themes = section.all(theme__exists=True) + Log.Debug('section count items with themes: %s' % len(items_with_themes)) + + # add each section to a html report + # todo - fix me up ... maybe add this to the attribution text? + html += """ +
+
+

{0}

+
+
+ + """.format(section.title) + + # add a percent completion gauge to the report + percent_complete = int(len(items_with_themes) / len(all_items) * 100) if len(items_with_themes) else 0 + if percent_complete == 100: + progress_color = 'bg-success' + btn_color = 'btn-success' + elif percent_complete > 50: + progress_color = 'bg-warning' + btn_color = 'btn-warning' + else: + progress_color = 'bg-danger' + btn_color = 'btn-warning' + html += """ +
+
+
+
{1}%
+
+
+
+ + """.format(progress_color, percent_complete) + + if items_with_themes: + html += """ +
+
+ +
+ + + + + + + + """.format('btn-success', section.title) + + for item in items_with_themes: + database_info = get_database_id(item=item) + modify_button = build_modify_button(item=item, database_info=database_info, btn_text='Edit') + html += """ + + + + + + """.format(item.title, item.year, modify_button) + + html += """ +
Items with theme songs
Item NameYearEdit in ThemerrDB
{0}{1}{2}
+
+
+
+ """ + + # use list comprehension to get all items without theme songs + items_without_themes = [item for item in all_items if item not in items_with_themes] + + if items_without_themes: + html += """ +
+
+ +
+ + + + + + + + """.format(btn_color, section.title) + + for item in items_without_themes: + database_info = get_database_id(item=item) + modify_button = build_modify_button(item=item, database_info=database_info, btn_text='Add') + html += """ + + + + + + """.format(item.title, item.year, modify_button) + + html += """ +
Items missing theme songs
Item NameYearAdd to ThemerrDB
{0}{1}{2}
+
+
+
+ """ + + # complete html + html += """ +
+ + + + """ + + # write the report to a file + report_file = os.path.join(themerr_data_directory, 'themerr-completion-report.html') + Core.storage.save(filename=report_file, data=html, binary=False) diff --git a/Contents/Code/constants.py b/Contents/Code/constants.py index 55b28439..79cc983a 100644 --- a/Contents/Code/constants.py +++ b/Contents/Code/constants.py @@ -1,3 +1,30 @@ +# -*- coding: utf-8 -*- + +# standard imports +import os + +# plex debugging +try: + import plexhints # noqa: F401 +except ImportError: + pass +else: # the code is running outside of Plex + from plexhints.core_kit import Core # core kit + +app_support_directory = Core.app_support_path +plugin_identifier = 'dev.lizardbyte.themerr-plex' +plugin_support_directory = os.path.join(app_support_directory, 'Plug-in Support') +plugin_support_data_directory = os.path.join(plugin_support_directory, 'Data') +themerr_data_directory = os.path.join(plugin_support_data_directory, plugin_identifier, 'DataItems') + +contributes_to = [ + 'tv.plex.agents.movie', + 'com.plexapp.agents.imdb', + 'com.plexapp.agents.themoviedb', + # 'com.plexapp.agents.thetvdb', # not available as movie agent + 'dev.lizardbyte.retroarcher-plex' +] + guid_map = dict( imdb='imdb', tmdb='themoviedb', diff --git a/Contents/Code/plex_api_helper.py b/Contents/Code/plex_api_helper.py index 2108fb8b..8a964260 100644 --- a/Contents/Code/plex_api_helper.py +++ b/Contents/Code/plex_api_helper.py @@ -22,7 +22,7 @@ # imports from Libraries\Shared from future.moves import queue import requests -from typing import Optional +from typing import Optional, Tuple import urllib3 from urllib3.exceptions import InsecureRequestWarning from plexapi.alert import AlertListener @@ -32,10 +32,10 @@ # local imports if sys.version_info.major < 3: - from constants import guid_map, issue_url_movies + from constants import app_support_directory, guid_map, issue_url_movies from youtube_dl_helper import process_youtube else: - from .constants import guid_map, issue_url_movies + from .constants import app_support_directory, guid_map, issue_url_movies from .youtube_dl_helper import process_youtube plex = None @@ -45,7 +45,6 @@ processing_completed = [] # constants -app_support_directory = Core.app_support_path metadata_movie_directory = os.path.join(app_support_directory, 'Metadata', 'Movies') @@ -234,6 +233,50 @@ def upload_theme(plex_item, filepath=None, url=None): return False +def get_database_id(item): + # type: (any) -> Tuple[Optional[str], Optional[str]] + agent = None + database_id = None + imdb_id = None # if this gets set we need to do additional processing + + if item.guids: # guids is a blank list for items from legacy agents, only available for new agent items + agent = 'tv.plex.agents.movie' + for guid in item.guids: + split_guid = guid.id.split('://') + database = guid_map[split_guid[0]] + database_id = split_guid[1] + + if database == 'igdb': + imdb_id = database_id + database_id = None + + if database == 'themoviedb': + imdb_id = None # reset this as we won't need to process it + break + elif item.guid: + split_guid = item.guid.split('://') + agent = split_guid[0] + if agent == 'dev.lizardbyte.retroarcher-plex': + # dev.lizardbyte.retroarcher-plex://{igdb-1638}{platform-4}{(USA)}?lang=en + database_id = item.guid.split('igdb-')[1].split('}')[0] + elif agent == 'com.plexapp.agents.themoviedb': + # com.plexapp.agents.themoviedb://363088?lang=en + database_id = item.guid.split('://')[1].split('?')[0] + elif agent == 'com.plexapp.agents.imdb': + # com.plexapp.agents.imdb://tt0113189?lang=en + imdb_id = item.guid.split('://')[1].split('?')[0] + + if imdb_id: + themerr_url = 'https://app.lizardbyte.dev/ThemerrDB/%s/%s/%s.json' % ('movies', 'imdb', imdb_id) + themerr_response = requests.get(url=themerr_url) + + if themerr_response.status_code == requests.codes.ok: + themerr_json = themerr_response.json() + database_id = themerr_json['id'] + + return agent, database_id + + def get_plex_item(rating_key): # type: (int) -> any """ diff --git a/README.rst b/README.rst index 6bd387fd..5a2c076f 100644 --- a/README.rst +++ b/README.rst @@ -10,7 +10,7 @@ Themerr-plex is a metadata agent plug-in for Plex Media Player. The plug-in adds This plugin contributes to the following metadata agents. - - Plex Movie + - Plex Movie - `tv.plex.agents.movie` - Plex Movie (Legacy) - `com.plexapp.agents.imdb` - The Movie Database - `com.plexapp.agents.themoviedb` - `RetroArcher `_ - `dev.lizardbyte.retroarcher-plex`