diff --git a/Contents/Code/default_prefs.py b/Contents/Code/default_prefs.py index bfcb1e1d..6e7ba6f5 100644 --- a/Contents/Code/default_prefs.py +++ b/Contents/Code/default_prefs.py @@ -12,6 +12,7 @@ bool_update_collection_metadata_plex_movie='False', bool_update_collection_metadata_legacy='True', int_update_themes_interval='60', + int_update_database_cache_interval='60', int_plexapi_plexapi_timeout='180', int_plexapi_upload_retries_max='3', int_plexapi_upload_threads='3', diff --git a/Contents/Code/scheduled_tasks.py b/Contents/Code/scheduled_tasks.py index 9e6d8e18..9a1f66f4 100644 --- a/Contents/Code/scheduled_tasks.py +++ b/Contents/Code/scheduled_tasks.py @@ -21,6 +21,7 @@ # local imports from constants import plugin_identifier from plex_api_helper import scheduled_update +from webapp import cache_data # setup logging for schedule Log.Info('Adding schedule log handlers to plex plugin logger') @@ -112,5 +113,9 @@ def setup_scheduling(): job_func=run_threaded, target=scheduled_update ) + schedule.every(max(15, int(Prefs['int_update_database_cache_interval']))).minutes.do( + job_func=run_threaded, + target=cache_data + ) run_threaded(target=schedule_loop, daemon=True) # start the schedule loop in a thread diff --git a/Contents/Code/webapp.py b/Contents/Code/webapp.py index ce1523cd..a67cf7b5 100644 --- a/Contents/Code/webapp.py +++ b/Contents/Code/webapp.py @@ -7,7 +7,7 @@ import json import logging import os -from threading import Thread +from threading import Lock, Thread # plex debugging try: @@ -16,6 +16,7 @@ pass else: # the code is running outside of Plex from plexhints.constant_kit import CACHE_1DAY # constant kit + from plexhints.core_kit import Core # core kit from plexhints.log_kit import Log # log kit from plexhints.parse_kit import JSON # parse kit from plexhints.prefs_kit import Prefs # prefs kit @@ -28,7 +29,7 @@ from werkzeug.utils import secure_filename # local imports -from constants import contributes_to, issue_urls, plugin_directory, plugin_identifier +from constants import contributes_to, issue_urls, plugin_directory, plugin_identifier, themerr_data_directory import general_helper from plex_api_helper import get_database_info, setup_plexapi import themerr_db_helper @@ -100,6 +101,10 @@ 'svg': 'image/svg+xml', } +# where the database cache is stored +database_cache_file = os.path.join(themerr_data_directory, 'database_cache.json') +database_cache_lock = Lock() + @babel.localeselector def get_locale(): @@ -182,30 +187,14 @@ def stop_server(): return False -@app.route('/', methods=["GET"]) -@app.route('/home', methods=["GET"]) -def home(): - # type: () -> render_template +def cache_data(): + # type: () -> None """ - Serve the webapp home page. - - This page serves the Themerr completion report for supported Plex libraries. - - Returns - ------- - render_template - The rendered page. + Cache data for use in the Web UI dashboard. - Notes - ----- - The following routes trigger this function. - - - `/` - - `/home` - - Examples - -------- - >>> home() + Because there are many http requests that must be made to gather the data for the dashboard, it can be + time-consuming to populate; therefore, this is performed within this caching function, which runs on a schedule. + This function will create a json file that can be loaded by other functions. """ # get all Plex items from supported metadata agents plex_server = setup_plexapi() @@ -368,7 +357,45 @@ def home(): year=year, )) - return render_template('home.html', title='Home', items=items) + with database_cache_lock: + Core.storage.save(filename=database_cache_file, data=json.dumps(items), binary=False) + + +@app.route('/', methods=["GET"]) +@app.route('/home', methods=["GET"]) +def home(): + # type: () -> render_template + """ + Serve the webapp home page. + + This page serves the Themerr completion report for supported Plex libraries. + + Returns + ------- + render_template + The rendered page. + + Notes + ----- + The following routes trigger this function. + + - `/` + - `/home` + + Examples + -------- + >>> home() + """ + items = [] + try: + items = json.loads(Core.storage.load(filename=database_cache_file, binary=False)) + except IOError: + pass + + if items: + return render_template('home.html', title='Home', items=items) + else: + return render_template('home_db_not_cached.html', title='Home') @app.route("/", methods=["GET"]) diff --git a/Contents/DefaultPrefs.json b/Contents/DefaultPrefs.json index a3904c81..48bc2498 100644 --- a/Contents/DefaultPrefs.json +++ b/Contents/DefaultPrefs.json @@ -76,6 +76,13 @@ "default": "60", "secure": "false" }, + { + "id": "int_update_database_cache_interval", + "type": "text", + "label": "Interval for database cache update task, in minutes (min: 15)", + "default": "60", + "secure": "false" + }, { "id": "int_plexapi_plexapi_timeout", "type": "text", diff --git a/Contents/Resources/web/templates/home_db_not_cached.html b/Contents/Resources/web/templates/home_db_not_cached.html new file mode 100644 index 00000000..e6ae2431 --- /dev/null +++ b/Contents/Resources/web/templates/home_db_not_cached.html @@ -0,0 +1,16 @@ +{% extends 'base.html' %} +{% block modals %} +{% endblock modals %} + +{% block content %} +
+
+ +

{{ _('Database is being cached, please try again soon.') }}

+ +
+
+{% endblock content %} + +{% block scripts %} +{% endblock scripts %} diff --git a/docs/source/about/usage.rst b/docs/source/about/usage.rst index 0e8b1365..7840fd7e 100644 --- a/docs/source/about/usage.rst +++ b/docs/source/about/usage.rst @@ -173,6 +173,18 @@ Default Minimum ``15`` +Interval for database cache update task +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Description + The interval (in minutes) to run the database cache update task. This data is used to display the Web UI dashboard. + +Default + ``60`` + +Minimum + ``15`` + PlexAPI Timeout ^^^^^^^^^^^^^^^ @@ -234,7 +246,7 @@ YouTube Cookies ^^^^^^^^^^^^^^^^ Description - The cookies to use for the requests to YouTube. Should be in Chromium JSON export format. + The cookies to use for the requests to YouTube. Should be in Chromium JSON export format. `Example exporter `__. Default diff --git a/tests/unit/test_webapp.py b/tests/unit/test_webapp.py new file mode 100644 index 00000000..2a68fd1c --- /dev/null +++ b/tests/unit/test_webapp.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- + +# standard imports +import json +import os + +# local imports +from Code import webapp + + +def test_cache_data(): + webapp.cache_data() + assert os.path.isfile(webapp.database_cache_file), "Database cache file not found" + + with open(webapp.database_cache_file, 'r') as f: + data = json.load(f) + + assert data, "Database cache file is empty"