diff --git a/CHANGELOG.md b/CHANGELOG.md index 1266e43f..d5eb4c5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## v2.13.2 (2023-10-26) +* History: + * New: Added quarter values icons for history watch status. (#2179, #2156) (Thanks @herby2212) +* Graphs: + * New: Added concurrent streams per day graph. (#2046) (Thanks @herby2212) +* Exporter: + * New: Added metadata directory to exporter fields. + * Removed: Banner exporter fields for tv shows. +* UI: + * New: Added last triggered time to notification agents and newsletter agent lists. +* Other: + * New: Added X-Plex-Language header override to config file. + + ## v2.13.1 (2023-08-25) * Notes: * Support for Python 3.7 has been dropped. The minimum Python version is now 3.8. diff --git a/data/interfaces/default/configuration_table.html b/data/interfaces/default/configuration_table.html index 676876d6..66d8ef40 100644 --- a/data/interfaces/default/configuration_table.html +++ b/data/interfaces/default/configuration_table.html @@ -11,6 +11,7 @@ DOCUMENTATION :: END <%! import os + import sqlite3 import sys import plexpy from plexpy import common, logger @@ -71,10 +72,18 @@ DOCUMENTATION :: END System Timezone: ${str(plexpy.SYS_TIMEZONE)} (${'UTC{}'.format(plexpy.SYS_UTC_OFFSET)}) + + System Language: + ${plexpy.SYS_LANGUAGE + (' (override {})'.format(plexpy.CONFIG.PMS_LANGUAGE) if plexpy.CONFIG.PMS_LANGUAGE else '')} + Python Version: ${sys.version} + + SQLite Version: + ${sqlite3.sqlite_version} + Resources: diff --git a/data/interfaces/default/css/tautulli.css b/data/interfaces/default/css/tautulli.css index 8cad039f..0c81f0e5 100644 --- a/data/interfaces/default/css/tautulli.css +++ b/data/interfaces/default/css/tautulli.css @@ -338,20 +338,20 @@ object { } .btn-dark:focus, .btn-dark.focus { - color: #d7d7d7; - background-color: #3B3B3B; + color: #d7d7d7; + background-color: #3B3B3B; } .btn-dark:hover { - color: #eee; - background-color: #333; - border-color: #444; + color: #eee; + background-color: #333; + border-color: #444; } .btn-dark:active, .btn-dark.active, .open > .dropdown-toggle.btn-dark { - color: #eee; - background-color: #333; - border-color: #444; + color: #eee; + background-color: #333; + border-color: #444; } .btn-dark:active:hover, .btn-dark.active:hover, @@ -362,13 +362,13 @@ object { .btn-dark:active.focus, .btn-dark.active.focus, .open > .dropdown-toggle.btn-dark.focus { - color: #eee; - background-color: #333; + color: #eee; + background-color: #333; } .btn-dark:active, .btn-dark.active, .open > .dropdown-toggle.btn-dark { - background-image: none; + background-image: none; } .btn-dark.disabled, .btn-dark[disabled], @@ -388,8 +388,8 @@ fieldset[disabled] .btn-dark:active, .btn-dark.disabled.active, .btn-dark[disabled].active, fieldset[disabled] .btn-dark.active { - background-color: #333; - color: #aaa; + background-color: #333; + color: #aaa; } .btn-dark.inactive:hover { color: #d7d7d7; @@ -398,30 +398,30 @@ fieldset[disabled] .btn-dark.active { cursor: default; } .btn-dark .badge { - color: #e5e5e5; - background-color: #3B3B3B; + color: #e5e5e5; + background-color: #3B3B3B; } .btn-bright { - color: #eee; - background-color: #cc7b19; - box-shadow: inset 0 1px 0 #e7993b; + color: #eee; + background-color: #cc7b19; + box-shadow: inset 0 1px 0 #e7993b; } .btn-bright:focus, .btn-bright.focus { - color: #eee; - background-color: #eb8600; + color: #eee; + background-color: #eb8600; } .btn-bright:hover { - color: #eee; - background-color: #e59029; - box-shadow: inset 0 1px 0 #ebac60; + color: #eee; + background-color: #e59029; + box-shadow: inset 0 1px 0 #ebac60; } .btn-bright:active, .btn-bright.active, .open > .dropdown-toggle.btn-bright { - color: #eee; - background-color: #cc7b19; - box-shadow: inset 0 1px 0 #e7993b; + color: #eee; + background-color: #cc7b19; + box-shadow: inset 0 1px 0 #e7993b; } .btn-bright:active:hover, .btn-bright.active:hover, @@ -432,14 +432,14 @@ fieldset[disabled] .btn-dark.active { .btn-bright:active.focus, .btn-bright.active.focus, .open > .dropdown-toggle.btn-bright.focus { - color: #eee; - background-color: #cc7b19; - box-shadow: inset 0 1px 0 #e7993b; + color: #eee; + background-color: #cc7b19; + box-shadow: inset 0 1px 0 #e7993b; } .btn-bright:active, .btn-bright.active, .open > .dropdown-toggle.btn-bright { - background-image: none; + background-image: none; } .btn-bright.disabled, .btn-bright[disabled], @@ -459,13 +459,13 @@ fieldset[disabled] .btn-bright:active, .btn-bright.disabled.active, .btn-bright[disabled].active, fieldset[disabled] .btn-bright.active { - background-color: #cc7b19; - border-color: #b56d16; + background-color: #cc7b19; + border-color: #b56d16; } .btn-bright .badge { - color: #eee; - background-color: #cc7b19; - box-shadow: inset 0 1px 0 #e7993b; + color: #eee; + background-color: #cc7b19; + box-shadow: inset 0 1px 0 #e7993b; } .btn-danger.btn-edit { color: #d7d7d7; @@ -479,14 +479,14 @@ fieldset[disabled] .btn-bright.active { border-color: #ac2925; } .btn-danger.btn-edit.active { - color: #eee; - background-color: #c9302c; - border-color: #ac2925; + color: #eee; + background-color: #c9302c; + border-color: #ac2925; } .btn-danger.btn-edit.active:hover { - color: #eee; - background-color: #ac2925; - border-color: #761c19; + color: #eee; + background-color: #ac2925; + border-color: #761c19; } .btn-group select { margin-top: 0; @@ -667,12 +667,12 @@ textarea.form-control:focus { white-space: nowrap; vertical-align: middle; -ms-touch-action: manipulation; - touch-action: manipulation; + touch-action: manipulation; cursor: pointer; -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; background-image: none; background-color: #3B3B3B; color: #e5e5e5; @@ -690,10 +690,10 @@ textarea.form-control:focus { } .btn-filter.active, .btn-filter.active.focus { - background-color: #b7800a !important; + background-color: #b7800a !important; } .btn-filter.active:hover { - background-color: #896007 !important; + background-color: #896007 !important; } .form-control-feedback { color: #E5A00D; @@ -1281,7 +1281,7 @@ a .dashboard-activity-metadata-user-thumb:hover { -webkit-backface-visibility: hidden; backface-visibility: hidden; z-index: 1; - -webkit-border-radius: 50%; + -webkit-border-radius: 50%; -moz-border-radius: 50%; border-radius: 350%; overflow: hidden; @@ -2203,8 +2203,8 @@ span.settings-warning { padding-left: 10px; } #menu_link_show_advanced_settings.active { - color: #eee; - background-color: #cc7b19; + color: #eee; + background-color: #cc7b19; } #configUpdate .form-group, #configUpdate .checkbox{ @@ -2854,6 +2854,30 @@ a .home-platforms-list-cover-face:hover overflow: hidden; max-width: 350px; } +.circle { + width: 1.55rem; + height: 1.55rem; + border-radius: 50%; + border: 0.2rem solid #eeeeee; +} +.circle-quarter { + background-image: + linear-gradient(00deg, #2b2b2b 50%, transparent 50%), + linear-gradient(270deg, #eeeeee 50%, transparent 50%); +} +.circle-half { + background-image: + linear-gradient(90deg, #2b2b2b 50%, transparent 50%), + linear-gradient(-90deg, #eeeeee 50%, transparent 50%); + } +.circle-three-quarter { + background-image: + linear-gradient(180deg, transparent 50%, #eeeeee 50%), + linear-gradient(-90deg, #eeeeee 50%, transparent 50%); +} +.circle-full { + background: #eeeeee; +} #graph-tabs { padding-bottom: 10px; float: none; @@ -2991,8 +3015,8 @@ a .home-platforms-list-cover-face:hover border-left: 2px solid #444; border-top: 1px solid #2d2d2d; -webkit-transition: all 0.3s ease; - -o-transition: all 0.3s ease; - transition: all 0.3s ease; + -o-transition: all 0.3s ease; + transition: all 0.3s ease; } .stacked-configs > li > span:hover, .stacked-configs > li > span:focus { diff --git a/data/interfaces/default/graphs.html b/data/interfaces/default/graphs.html index 7cd0c98b..15a96a69 100644 --- a/data/interfaces/default/graphs.html +++ b/data/interfaces/default/graphs.html @@ -137,7 +137,7 @@ -
+

Daily concurrent stream count by stream type Last 30 days

@@ -372,6 +372,10 @@ break } + if (window.location.hash === '#concurrent-graph') { + current_tab = '#tabs-stream'; + } + $('#yaxis-' + yaxis).prop('checked', true); $('#yaxis-' + yaxis).closest('label').addClass('active'); $('#graph-days').val(current_day_range); @@ -639,7 +643,7 @@ } }); - $('#nav-tabs-2').tab('show'); + $('#nav-tabs-stream').tab('show'); } function loadGraphsTab3(time_range, yaxis) { diff --git a/data/interfaces/default/home_stats.html b/data/interfaces/default/home_stats.html index 1d9e03dd..5e0d0855 100644 --- a/data/interfaces/default/home_stats.html +++ b/data/interfaces/default/home_stats.html @@ -177,7 +177,9 @@ DOCUMENTATION :: END % elif stat_id == 'top_platforms': ${row['platform']} % elif stat_id == 'most_concurrent': - ${row['title']} + + ${row['title']} + % endif

diff --git a/data/interfaces/default/js/tables/history_table.js b/data/interfaces/default/js/tables/history_table.js index b56304ee..40c3ab6c 100644 --- a/data/interfaces/default/js/tables/history_table.js +++ b/data/interfaces/default/js/tables/history_table.js @@ -263,13 +263,17 @@ history_table_options = { "targets": [12], "data": "watched_status", "createdCell": function (td, cellData, rowData, row, col) { + var circleValue = ""; if (cellData == 1) { - $(td).html(''); + circleValue = " circle-full"; + } else if (cellData == 0.75) { + circleValue = " circle-three-quarter"; } else if (cellData == 0.5) { - $(td).html(''); - } else { - $(td).html(''); + circleValue = " circle-half"; + } else if (cellData == 0.25) { + circleValue = " circle-quarter"; } + $(td).html('
'); }, "searchable": false, "orderable": false, diff --git a/plexpy/__init__.py b/plexpy/__init__.py index a2062c43..1a1564ac 100644 --- a/plexpy/__init__.py +++ b/plexpy/__init__.py @@ -236,6 +236,9 @@ def initialize(config_file): logger.info("{} (UTC{})".format( str(SYS_TIMEZONE), SYS_UTC_OFFSET )) + logger.info("Language {}{} / Encoding {}".format( + SYS_LANGUAGE, f' (override {CONFIG.PMS_LANGUAGE})' if CONFIG.PMS_LANGUAGE else '', SYS_ENCODING + )) logger.info("Python {}".format( sys.version.replace('\n', '') )) diff --git a/plexpy/activity_handler.py b/plexpy/activity_handler.py index 0dd6e5e5..3280d055 100644 --- a/plexpy/activity_handler.py +++ b/plexpy/activity_handler.py @@ -311,7 +311,9 @@ class ActivityHandler(object): last_state = self.db_session['state'] last_rating_key = str(self.db_session['rating_key']) last_live_uuid = self.db_session['live_uuid'] - last_transcode_key = self.db_session['transcode_key'].split('/')[-1] + last_transcode_key = self.db_session['transcode_key'] + if isinstance(last_transcode_key, str): + last_transcode_key = last_transcode_key.split('/')[-1] last_paused = self.db_session['last_paused'] last_rating_key_websocket = self.db_session['rating_key_websocket'] last_guid = self.db_session['guid'] diff --git a/plexpy/config.py b/plexpy/config.py index 6f2926d9..dbcd294d 100644 --- a/plexpy/config.py +++ b/plexpy/config.py @@ -55,6 +55,7 @@ _CONFIG_DEFINITIONS = { 'PMS_IP': (str, 'PMS', '127.0.0.1'), 'PMS_IS_CLOUD': (int, 'PMS', 0), 'PMS_IS_REMOTE': (int, 'PMS', 0), + 'PMS_LANGUAGE': (str, 'PMS', ''), 'PMS_LOGS_FOLDER': (str, 'PMS', ''), 'PMS_LOGS_LINE_CAP': (int, 'PMS', 1000), 'PMS_NAME': (str, 'PMS', ''), diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py index dfe64992..05af1285 100644 --- a/plexpy/datafactory.py +++ b/plexpy/datafactory.py @@ -283,13 +283,19 @@ class DataFactory(object): if item['live']: item['percent_complete'] = 100 + base_watched_value = watched_percent[item['media_type']] / 4.0 + if helpers.check_watched( item['media_type'], item['view_offset'], item['duration'], item['marker_credits_first'], item['marker_credits_final'] ): watched_status = 1 - elif item['percent_complete'] >= watched_percent[item['media_type']] / 2.0: - watched_status = 0.5 + elif item['percent_complete'] >= base_watched_value * 3.0: + watched_status = 0.75 + elif item['percent_complete'] >= base_watched_value * 2.0: + watched_status = 0.50 + elif item['percent_complete'] >= base_watched_value: + watched_status = 0.25 else: watched_status = 0 diff --git a/plexpy/http_handler.py b/plexpy/http_handler.py index 87c4cff5..79bb3562 100644 --- a/plexpy/http_handler.py +++ b/plexpy/http_handler.py @@ -62,7 +62,7 @@ class HTTPHandler(object): plexpy.common.PLATFORM_RELEASE), 'X-Plex-Device-Name': '{} ({})'.format(plexpy.common.PLATFORM_DEVICE_NAME, plexpy.common.PRODUCT), - 'Accept-Language': plexpy.SYS_LANGUAGE + 'X-Plex-Language': plexpy.CONFIG.PMS_LANGUAGE or plexpy.SYS_LANGUAGE } self.token = token diff --git a/plexpy/version.py b/plexpy/version.py index c5ee1521..5ca70297 100644 --- a/plexpy/version.py +++ b/plexpy/version.py @@ -18,4 +18,4 @@ from __future__ import unicode_literals PLEXPY_BRANCH = "master" -PLEXPY_RELEASE_VERSION = "v2.13.1" \ No newline at end of file +PLEXPY_RELEASE_VERSION = "v2.13.2" \ No newline at end of file