This commit is contained in:
brees 2023-10-29 16:00:26 -04:00
commit b536593fc1
12 changed files with 133 additions and 64 deletions

View file

@ -1,5 +1,19 @@
# Changelog # 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) ## v2.13.1 (2023-08-25)
* Notes: * Notes:
* Support for Python 3.7 has been dropped. The minimum Python version is now 3.8. * Support for Python 3.7 has been dropped. The minimum Python version is now 3.8.

View file

@ -11,6 +11,7 @@ DOCUMENTATION :: END
<%! <%!
import os import os
import sqlite3
import sys import sys
import plexpy import plexpy
from plexpy import common, logger from plexpy import common, logger
@ -71,10 +72,18 @@ DOCUMENTATION :: END
<td>System Timezone:</td> <td>System Timezone:</td>
<td>${str(plexpy.SYS_TIMEZONE)} (${'UTC{}'.format(plexpy.SYS_UTC_OFFSET)}) <td>${str(plexpy.SYS_TIMEZONE)} (${'UTC{}'.format(plexpy.SYS_UTC_OFFSET)})
</tr> </tr>
<tr>
<td>System Language:</td>
<td>${plexpy.SYS_LANGUAGE + (' (override {})'.format(plexpy.CONFIG.PMS_LANGUAGE) if plexpy.CONFIG.PMS_LANGUAGE else '')}</td>
</tr>
<tr> <tr>
<td>Python Version:</td> <td>Python Version:</td>
<td>${sys.version}</td> <td>${sys.version}</td>
</tr> </tr>
<tr>
<td>SQLite Version:</td>
<td>${sqlite3.sqlite_version}</td>
</tr>
<tr> <tr>
<td class="top-line">Resources:</td> <td class="top-line">Resources:</td>
<td class="top-line"> <td class="top-line">

View file

@ -338,20 +338,20 @@ object {
} }
.btn-dark:focus, .btn-dark:focus,
.btn-dark.focus { .btn-dark.focus {
color: #d7d7d7; color: #d7d7d7;
background-color: #3B3B3B; background-color: #3B3B3B;
} }
.btn-dark:hover { .btn-dark:hover {
color: #eee; color: #eee;
background-color: #333; background-color: #333;
border-color: #444; border-color: #444;
} }
.btn-dark:active, .btn-dark:active,
.btn-dark.active, .btn-dark.active,
.open > .dropdown-toggle.btn-dark { .open > .dropdown-toggle.btn-dark {
color: #eee; color: #eee;
background-color: #333; background-color: #333;
border-color: #444; border-color: #444;
} }
.btn-dark:active:hover, .btn-dark:active:hover,
.btn-dark.active:hover, .btn-dark.active:hover,
@ -362,13 +362,13 @@ object {
.btn-dark:active.focus, .btn-dark:active.focus,
.btn-dark.active.focus, .btn-dark.active.focus,
.open > .dropdown-toggle.btn-dark.focus { .open > .dropdown-toggle.btn-dark.focus {
color: #eee; color: #eee;
background-color: #333; background-color: #333;
} }
.btn-dark:active, .btn-dark:active,
.btn-dark.active, .btn-dark.active,
.open > .dropdown-toggle.btn-dark { .open > .dropdown-toggle.btn-dark {
background-image: none; background-image: none;
} }
.btn-dark.disabled, .btn-dark.disabled,
.btn-dark[disabled], .btn-dark[disabled],
@ -388,8 +388,8 @@ fieldset[disabled] .btn-dark:active,
.btn-dark.disabled.active, .btn-dark.disabled.active,
.btn-dark[disabled].active, .btn-dark[disabled].active,
fieldset[disabled] .btn-dark.active { fieldset[disabled] .btn-dark.active {
background-color: #333; background-color: #333;
color: #aaa; color: #aaa;
} }
.btn-dark.inactive:hover { .btn-dark.inactive:hover {
color: #d7d7d7; color: #d7d7d7;
@ -398,30 +398,30 @@ fieldset[disabled] .btn-dark.active {
cursor: default; cursor: default;
} }
.btn-dark .badge { .btn-dark .badge {
color: #e5e5e5; color: #e5e5e5;
background-color: #3B3B3B; background-color: #3B3B3B;
} }
.btn-bright { .btn-bright {
color: #eee; color: #eee;
background-color: #cc7b19; background-color: #cc7b19;
box-shadow: inset 0 1px 0 #e7993b; box-shadow: inset 0 1px 0 #e7993b;
} }
.btn-bright:focus, .btn-bright:focus,
.btn-bright.focus { .btn-bright.focus {
color: #eee; color: #eee;
background-color: #eb8600; background-color: #eb8600;
} }
.btn-bright:hover { .btn-bright:hover {
color: #eee; color: #eee;
background-color: #e59029; background-color: #e59029;
box-shadow: inset 0 1px 0 #ebac60; box-shadow: inset 0 1px 0 #ebac60;
} }
.btn-bright:active, .btn-bright:active,
.btn-bright.active, .btn-bright.active,
.open > .dropdown-toggle.btn-bright { .open > .dropdown-toggle.btn-bright {
color: #eee; color: #eee;
background-color: #cc7b19; background-color: #cc7b19;
box-shadow: inset 0 1px 0 #e7993b; box-shadow: inset 0 1px 0 #e7993b;
} }
.btn-bright:active:hover, .btn-bright:active:hover,
.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,
.btn-bright.active.focus, .btn-bright.active.focus,
.open > .dropdown-toggle.btn-bright.focus { .open > .dropdown-toggle.btn-bright.focus {
color: #eee; color: #eee;
background-color: #cc7b19; background-color: #cc7b19;
box-shadow: inset 0 1px 0 #e7993b; box-shadow: inset 0 1px 0 #e7993b;
} }
.btn-bright:active, .btn-bright:active,
.btn-bright.active, .btn-bright.active,
.open > .dropdown-toggle.btn-bright { .open > .dropdown-toggle.btn-bright {
background-image: none; background-image: none;
} }
.btn-bright.disabled, .btn-bright.disabled,
.btn-bright[disabled], .btn-bright[disabled],
@ -459,13 +459,13 @@ fieldset[disabled] .btn-bright:active,
.btn-bright.disabled.active, .btn-bright.disabled.active,
.btn-bright[disabled].active, .btn-bright[disabled].active,
fieldset[disabled] .btn-bright.active { fieldset[disabled] .btn-bright.active {
background-color: #cc7b19; background-color: #cc7b19;
border-color: #b56d16; border-color: #b56d16;
} }
.btn-bright .badge { .btn-bright .badge {
color: #eee; color: #eee;
background-color: #cc7b19; background-color: #cc7b19;
box-shadow: inset 0 1px 0 #e7993b; box-shadow: inset 0 1px 0 #e7993b;
} }
.btn-danger.btn-edit { .btn-danger.btn-edit {
color: #d7d7d7; color: #d7d7d7;
@ -479,14 +479,14 @@ fieldset[disabled] .btn-bright.active {
border-color: #ac2925; border-color: #ac2925;
} }
.btn-danger.btn-edit.active { .btn-danger.btn-edit.active {
color: #eee; color: #eee;
background-color: #c9302c; background-color: #c9302c;
border-color: #ac2925; border-color: #ac2925;
} }
.btn-danger.btn-edit.active:hover { .btn-danger.btn-edit.active:hover {
color: #eee; color: #eee;
background-color: #ac2925; background-color: #ac2925;
border-color: #761c19; border-color: #761c19;
} }
.btn-group select { .btn-group select {
margin-top: 0; margin-top: 0;
@ -667,12 +667,12 @@ textarea.form-control:focus {
white-space: nowrap; white-space: nowrap;
vertical-align: middle; vertical-align: middle;
-ms-touch-action: manipulation; -ms-touch-action: manipulation;
touch-action: manipulation; touch-action: manipulation;
cursor: pointer; cursor: pointer;
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none; -ms-user-select: none;
user-select: none; user-select: none;
background-image: none; background-image: none;
background-color: #3B3B3B; background-color: #3B3B3B;
color: #e5e5e5; color: #e5e5e5;
@ -690,10 +690,10 @@ textarea.form-control:focus {
} }
.btn-filter.active, .btn-filter.active,
.btn-filter.active.focus { .btn-filter.active.focus {
background-color: #b7800a !important; background-color: #b7800a !important;
} }
.btn-filter.active:hover { .btn-filter.active:hover {
background-color: #896007 !important; background-color: #896007 !important;
} }
.form-control-feedback { .form-control-feedback {
color: #E5A00D; color: #E5A00D;
@ -1281,7 +1281,7 @@ a .dashboard-activity-metadata-user-thumb:hover {
-webkit-backface-visibility: hidden; -webkit-backface-visibility: hidden;
backface-visibility: hidden; backface-visibility: hidden;
z-index: 1; z-index: 1;
-webkit-border-radius: 50%; -webkit-border-radius: 50%;
-moz-border-radius: 50%; -moz-border-radius: 50%;
border-radius: 350%; border-radius: 350%;
overflow: hidden; overflow: hidden;
@ -2203,8 +2203,8 @@ span.settings-warning {
padding-left: 10px; padding-left: 10px;
} }
#menu_link_show_advanced_settings.active { #menu_link_show_advanced_settings.active {
color: #eee; color: #eee;
background-color: #cc7b19; background-color: #cc7b19;
} }
#configUpdate .form-group, #configUpdate .form-group,
#configUpdate .checkbox{ #configUpdate .checkbox{
@ -2854,6 +2854,30 @@ a .home-platforms-list-cover-face:hover
overflow: hidden; overflow: hidden;
max-width: 350px; 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 { #graph-tabs {
padding-bottom: 10px; padding-bottom: 10px;
float: none; float: none;
@ -2991,8 +3015,8 @@ a .home-platforms-list-cover-face:hover
border-left: 2px solid #444; border-left: 2px solid #444;
border-top: 1px solid #2d2d2d; border-top: 1px solid #2d2d2d;
-webkit-transition: all 0.3s ease; -webkit-transition: all 0.3s ease;
-o-transition: all 0.3s ease; -o-transition: all 0.3s ease;
transition: all 0.3s ease; transition: all 0.3s ease;
} }
.stacked-configs > li > span:hover, .stacked-configs > li > span:hover,
.stacked-configs > li > span:focus { .stacked-configs > li > span:focus {

View file

@ -137,7 +137,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row" id="concurrent-graph">
<div class="col-md-12"> <div class="col-md-12">
<h4><i class="fa fa-video-camera"></i> Daily concurrent stream count</span> by stream type <small>Last <span class="days">30</span> days</small></h4> <h4><i class="fa fa-video-camera"></i> Daily concurrent stream count</span> by stream type <small>Last <span class="days">30</span> days</small></h4>
<p class="help-block"> <p class="help-block">
@ -372,6 +372,10 @@
break break
} }
if (window.location.hash === '#concurrent-graph') {
current_tab = '#tabs-stream';
}
$('#yaxis-' + yaxis).prop('checked', true); $('#yaxis-' + yaxis).prop('checked', true);
$('#yaxis-' + yaxis).closest('label').addClass('active'); $('#yaxis-' + yaxis).closest('label').addClass('active');
$('#graph-days').val(current_day_range); $('#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) { function loadGraphsTab3(time_range, yaxis) {

View file

@ -177,7 +177,9 @@ DOCUMENTATION :: END
% elif stat_id == 'top_platforms': % elif stat_id == 'top_platforms':
${row['platform']} ${row['platform']}
% elif stat_id == 'most_concurrent': % elif stat_id == 'most_concurrent':
${row['title']} <a href="graphs#concurrent-graph" title="${row['title']}">
${row['title']}
</a>
% endif % endif
</div> </div>
<div class="sub-count"> <div class="sub-count">

View file

@ -263,13 +263,17 @@ history_table_options = {
"targets": [12], "targets": [12],
"data": "watched_status", "data": "watched_status",
"createdCell": function (td, cellData, rowData, row, col) { "createdCell": function (td, cellData, rowData, row, col) {
var circleValue = "";
if (cellData == 1) { if (cellData == 1) {
$(td).html('<span class="watched-tooltip" data-toggle="tooltip" title="' + rowData['percent_complete'] + '%"><i class="fa fa-lg fa-circle"></i></span>'); circleValue = " circle-full";
} else if (cellData == 0.75) {
circleValue = " circle-three-quarter";
} else if (cellData == 0.5) { } else if (cellData == 0.5) {
$(td).html('<span class="watched-tooltip" data-toggle="tooltip" title="' + rowData['percent_complete'] + '%"><i class="fa fa-lg fa-adjust fa-rotate-180"></i></span>'); circleValue = " circle-half";
} else { } else if (cellData == 0.25) {
$(td).html('<span class="watched-tooltip" data-toggle="tooltip" title="' + rowData['percent_complete'] + '%"><i class="fa fa-lg fa-circle-o"></i></span>'); circleValue = " circle-quarter";
} }
$(td).html('<span class="watched-tooltip" data-toggle="tooltip" title="' + rowData['percent_complete'] + '%"><div class="circle' + circleValue + '" /></span>');
}, },
"searchable": false, "searchable": false,
"orderable": false, "orderable": false,

View file

@ -236,6 +236,9 @@ def initialize(config_file):
logger.info("{} (UTC{})".format( logger.info("{} (UTC{})".format(
str(SYS_TIMEZONE), SYS_UTC_OFFSET 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( logger.info("Python {}".format(
sys.version.replace('\n', '') sys.version.replace('\n', '')
)) ))

View file

@ -311,7 +311,9 @@ class ActivityHandler(object):
last_state = self.db_session['state'] last_state = self.db_session['state']
last_rating_key = str(self.db_session['rating_key']) last_rating_key = str(self.db_session['rating_key'])
last_live_uuid = self.db_session['live_uuid'] 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_paused = self.db_session['last_paused']
last_rating_key_websocket = self.db_session['rating_key_websocket'] last_rating_key_websocket = self.db_session['rating_key_websocket']
last_guid = self.db_session['guid'] last_guid = self.db_session['guid']

View file

@ -55,6 +55,7 @@ _CONFIG_DEFINITIONS = {
'PMS_IP': (str, 'PMS', '127.0.0.1'), 'PMS_IP': (str, 'PMS', '127.0.0.1'),
'PMS_IS_CLOUD': (int, 'PMS', 0), 'PMS_IS_CLOUD': (int, 'PMS', 0),
'PMS_IS_REMOTE': (int, 'PMS', 0), 'PMS_IS_REMOTE': (int, 'PMS', 0),
'PMS_LANGUAGE': (str, 'PMS', ''),
'PMS_LOGS_FOLDER': (str, 'PMS', ''), 'PMS_LOGS_FOLDER': (str, 'PMS', ''),
'PMS_LOGS_LINE_CAP': (int, 'PMS', 1000), 'PMS_LOGS_LINE_CAP': (int, 'PMS', 1000),
'PMS_NAME': (str, 'PMS', ''), 'PMS_NAME': (str, 'PMS', ''),

View file

@ -283,13 +283,19 @@ class DataFactory(object):
if item['live']: if item['live']:
item['percent_complete'] = 100 item['percent_complete'] = 100
base_watched_value = watched_percent[item['media_type']] / 4.0
if helpers.check_watched( if helpers.check_watched(
item['media_type'], item['view_offset'], item['duration'], item['media_type'], item['view_offset'], item['duration'],
item['marker_credits_first'], item['marker_credits_final'] item['marker_credits_first'], item['marker_credits_final']
): ):
watched_status = 1 watched_status = 1
elif item['percent_complete'] >= watched_percent[item['media_type']] / 2.0: elif item['percent_complete'] >= base_watched_value * 3.0:
watched_status = 0.5 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: else:
watched_status = 0 watched_status = 0

View file

@ -62,7 +62,7 @@ class HTTPHandler(object):
plexpy.common.PLATFORM_RELEASE), plexpy.common.PLATFORM_RELEASE),
'X-Plex-Device-Name': '{} ({})'.format(plexpy.common.PLATFORM_DEVICE_NAME, 'X-Plex-Device-Name': '{} ({})'.format(plexpy.common.PLATFORM_DEVICE_NAME,
plexpy.common.PRODUCT), plexpy.common.PRODUCT),
'Accept-Language': plexpy.SYS_LANGUAGE 'X-Plex-Language': plexpy.CONFIG.PMS_LANGUAGE or plexpy.SYS_LANGUAGE
} }
self.token = token self.token = token

View file

@ -18,4 +18,4 @@
from __future__ import unicode_literals from __future__ import unicode_literals
PLEXPY_BRANCH = "master" PLEXPY_BRANCH = "master"
PLEXPY_RELEASE_VERSION = "v2.13.1" PLEXPY_RELEASE_VERSION = "v2.13.2"