mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-08-22 06:13:25 -07:00
Merge branch 'nightly' into dependabot/pip/nightly/pyobjc-framework-cocoa-9.2
This commit is contained in:
commit
154da7d2ef
24 changed files with 437 additions and 266 deletions
4
.github/workflows/issues-stale.yml
vendored
4
.github/workflows/issues-stale.yml
vendored
|
@ -10,7 +10,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Stale
|
||||
uses: actions/stale@v7
|
||||
uses: actions/stale@v8
|
||||
with:
|
||||
stale-issue-message: >
|
||||
This issue is stale because it has been open for 30 days with no activity.
|
||||
|
@ -30,7 +30,7 @@ jobs:
|
|||
days-before-close: 5
|
||||
|
||||
- name: Invalid Template
|
||||
uses: actions/stale@v7
|
||||
uses: actions/stale@v8
|
||||
with:
|
||||
stale-issue-message: >
|
||||
Invalid issues template.
|
||||
|
|
2
.github/workflows/publish-docker.yml
vendored
2
.github/workflows/publish-docker.yml
vendored
|
@ -95,7 +95,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get Build Job Status
|
||||
uses: technote-space/workflow-conclusion-action@v3.0
|
||||
uses: technote-space/workflow-conclusion-action@v3
|
||||
|
||||
- name: Combine Job Status
|
||||
id: status
|
||||
|
|
8
.github/workflows/publish-installers.yml
vendored
8
.github/workflows/publish-installers.yml
vendored
|
@ -68,7 +68,7 @@ jobs:
|
|||
pyinstaller -y ./package/Tautulli-${{ matrix.os }}.spec
|
||||
|
||||
- name: Create Windows Installer
|
||||
uses: joncloud/makensis-action@v3.7
|
||||
uses: joncloud/makensis-action@v4
|
||||
if: matrix.os == 'windows'
|
||||
with:
|
||||
script-file: ./package/Tautulli.nsi
|
||||
|
@ -100,10 +100,10 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get Build Job Status
|
||||
uses: technote-space/workflow-conclusion-action@v3.0
|
||||
uses: technote-space/workflow-conclusion-action@v3
|
||||
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3.2.0
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set Release Version
|
||||
id: get_version
|
||||
|
@ -168,7 +168,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get Build Job Status
|
||||
uses: technote-space/workflow-conclusion-action@v3.0
|
||||
uses: technote-space/workflow-conclusion-action@v3
|
||||
|
||||
- name: Combine Job Status
|
||||
id: status
|
||||
|
|
2
.github/workflows/publish-snap.yml
vendored
2
.github/workflows/publish-snap.yml
vendored
|
@ -70,7 +70,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get Build Job Status
|
||||
uses: technote-space/workflow-conclusion-action@v3.0
|
||||
uses: technote-space/workflow-conclusion-action@v3
|
||||
|
||||
- name: Combine Job Status
|
||||
id: status
|
||||
|
|
1
.github/workflows/pull-requests.yml
vendored
1
.github/workflows/pull-requests.yml
vendored
|
@ -18,7 +18,6 @@ jobs:
|
|||
with:
|
||||
message: Pull requests must be made to the `nightly` branch. Thanks.
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
repo-token-user-login: 'github-actions[bot]'
|
||||
|
||||
- name: Fail Workflow
|
||||
if: github.base_ref != 'nightly'
|
||||
|
|
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -1,5 +1,22 @@
|
|||
# Changelog
|
||||
|
||||
## v2.12.5 (2023-07-13)
|
||||
|
||||
* Activity:
|
||||
* New: Added d3d11va to list of hardware decoders.
|
||||
* History:
|
||||
* Fix: Incorrect grouping of play history.
|
||||
* New: Added button in settings to regroup play history.
|
||||
* Notifications:
|
||||
* Fix: Incorrect concurrent streams notifications by IP addresss for IPv6 addresses (#2096) (Thanks @pooley182)
|
||||
* UI:
|
||||
* Fix: Occasional UI crashing on Python 3.11.
|
||||
* New: Added multiselect user filters to History and Graphs pages. (#2090) (Thanks @zdimension)
|
||||
* API:
|
||||
* New: Added regroup_history API command.
|
||||
* Change: Updated graph API commands to accept a comma separated list of user IDs.
|
||||
|
||||
|
||||
## v2.12.4 (2023-05-23)
|
||||
|
||||
* History:
|
||||
|
|
6
data/interfaces/default/css/bootstrap-select.min.css
vendored
Normal file
6
data/interfaces/default/css/bootstrap-select.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -2914,7 +2914,7 @@ a .home-platforms-list-cover-face:hover
|
|||
margin-bottom: -20px;
|
||||
width: 100%;
|
||||
max-width: 1750px;
|
||||
overflow: hidden;
|
||||
display: flow-root;
|
||||
}
|
||||
.table-card-back td {
|
||||
font-size: 12px;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<%inherit file="base.html"/>
|
||||
|
||||
<%def name="headIncludes()">
|
||||
<link rel="stylesheet" href="${http_root}css/bootstrap-select.min.css">
|
||||
<link rel="stylesheet" href="${http_root}css/dataTables.bootstrap.min.css">
|
||||
<link rel="stylesheet" href="${http_root}css/tautulli-dataTables.css">
|
||||
</%def>
|
||||
|
@ -14,9 +15,7 @@
|
|||
<div class="button-bar">
|
||||
<div class="btn-group" id="user-selection">
|
||||
<label>
|
||||
<select name="graph-user" id="graph-user" class="btn" style="color: inherit;">
|
||||
<option value="">All Users</option>
|
||||
<option disabled>────────────</option>
|
||||
<select name="graph-user" id="graph-user" multiple>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -225,6 +224,7 @@
|
|||
</%def>
|
||||
|
||||
<%def name="javascriptIncludes()">
|
||||
<script src="${http_root}js/bootstrap-select.min.js"></script>
|
||||
<script src="${http_root}js/highcharts.min.js"></script>
|
||||
<script src="${http_root}js/jquery.dataTables.min.js"></script>
|
||||
<script src="${http_root}js/dataTables.bootstrap.min.js"></script>
|
||||
|
@ -373,14 +373,35 @@
|
|||
type: 'get',
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
var select = $('#graph-user');
|
||||
let select = $('#graph-user');
|
||||
let by_id = {};
|
||||
data.sort(function(a, b) {
|
||||
return a.friendly_name.localeCompare(b.friendly_name);
|
||||
});
|
||||
data.forEach(function(item) {
|
||||
select.append('<option value="' + item.user_id + '">' +
|
||||
item.friendly_name + '</option>');
|
||||
by_id[item.user_id] = item.friendly_name;
|
||||
});
|
||||
select.selectpicker({
|
||||
countSelectedText: function(sel, total) {
|
||||
if (sel === 0 || sel === total) {
|
||||
return 'All users';
|
||||
} else if (sel > 1) {
|
||||
return sel + ' users';
|
||||
} else {
|
||||
return select.val().map(function(id) {
|
||||
return by_id[id];
|
||||
}).join(', ');
|
||||
}
|
||||
},
|
||||
style: 'btn-dark',
|
||||
actionsBox: true,
|
||||
selectedTextFormat: 'count',
|
||||
noneSelectedText: 'All users'
|
||||
});
|
||||
select.selectpicker('render');
|
||||
select.selectpicker('selectAll');
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -602,11 +623,6 @@
|
|||
$('#nav-tabs-total').tab('show');
|
||||
}
|
||||
|
||||
// Set initial state
|
||||
if (current_tab === '#tabs-plays') { loadGraphsTab1(current_day_range, yaxis); }
|
||||
if (current_tab === '#tabs-stream') { loadGraphsTab2(current_day_range, yaxis); }
|
||||
if (current_tab === '#tabs-total') { loadGraphsTab3(current_month_range, yaxis); }
|
||||
|
||||
// Tab1 opened
|
||||
$('#nav-tabs-plays').on('shown.bs.tab', function (e) {
|
||||
e.preventDefault();
|
||||
|
@ -652,9 +668,20 @@
|
|||
$('.months').text(current_month_range);
|
||||
});
|
||||
|
||||
let graph_user_last_id = undefined;
|
||||
|
||||
// User changed
|
||||
$('#graph-user').on('change', function() {
|
||||
selected_user_id = $(this).val() || null;
|
||||
let val = $(this).val();
|
||||
if (val.length === 0 || val.length === $(this).children().length) {
|
||||
selected_user_id = null; // if all users are selected, just send an empty list
|
||||
} else {
|
||||
selected_user_id = val.join(",");
|
||||
}
|
||||
if (selected_user_id === graph_user_last_id) {
|
||||
return;
|
||||
}
|
||||
graph_user_last_id = selected_user_id;
|
||||
if (current_tab === '#tabs-plays') { loadGraphsTab1(current_day_range, yaxis); }
|
||||
if (current_tab === '#tabs-stream') { loadGraphsTab2(current_day_range, yaxis); }
|
||||
if (current_tab === '#tabs-total') { loadGraphsTab3(current_month_range, yaxis); }
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<%inherit file="base.html"/>
|
||||
|
||||
<%def name="headIncludes()">
|
||||
<link rel="stylesheet" href="${http_root}css/bootstrap-select.min.css">
|
||||
<link rel="stylesheet" href="${http_root}css/dataTables.bootstrap.min.css">
|
||||
<link rel="stylesheet" href="${http_root}css/dataTables.colVis.css">
|
||||
<link rel="stylesheet" href="${http_root}css/tautulli-dataTables.css">
|
||||
|
@ -31,9 +32,7 @@
|
|||
% if _session['user_group'] == 'admin':
|
||||
<div class="btn-group" id="user-selection">
|
||||
<label>
|
||||
<select name="history-user" id="history-user" class="btn" style="color: inherit;">
|
||||
<option value="">All Users</option>
|
||||
<option disabled>────────────</option>
|
||||
<select name="history-user" id="history-user" multiple>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -121,6 +120,7 @@
|
|||
</%def>
|
||||
|
||||
<%def name="javascriptIncludes()">
|
||||
<script src="${http_root}js/bootstrap-select.min.js"></script>
|
||||
<script src="${http_root}js/jquery.dataTables.min.js"></script>
|
||||
<script src="${http_root}js/dataTables.colVis.js"></script>
|
||||
<script src="${http_root}js/dataTables.bootstrap.min.js"></script>
|
||||
|
@ -134,17 +134,40 @@
|
|||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success: function (data) {
|
||||
var select = $('#history-user');
|
||||
let select = $('#history-user');
|
||||
let by_id = {};
|
||||
data.sort(function (a, b) {
|
||||
return a.friendly_name.localeCompare(b.friendly_name);
|
||||
});
|
||||
data.forEach(function (item) {
|
||||
select.append('<option value="' + item.user_id + '">' +
|
||||
item.friendly_name + '</option>');
|
||||
by_id[item.user_id] = item.friendly_name;
|
||||
});
|
||||
select.selectpicker({
|
||||
countSelectedText: function(sel, total) {
|
||||
if (sel === 0 || sel === total) {
|
||||
return 'All users';
|
||||
} else if (sel > 1) {
|
||||
return sel + ' users';
|
||||
} else {
|
||||
return select.val().map(function(id) {
|
||||
return by_id[id];
|
||||
}).join(', ');
|
||||
}
|
||||
},
|
||||
style: 'btn-dark',
|
||||
actionsBox: true,
|
||||
selectedTextFormat: 'count',
|
||||
noneSelectedText: 'All users'
|
||||
});
|
||||
select.selectpicker('render');
|
||||
select.selectpicker('selectAll');
|
||||
}
|
||||
});
|
||||
|
||||
let history_user_last_id = undefined;
|
||||
|
||||
function loadHistoryTable(media_type, transcode_decision, selected_user_id) {
|
||||
history_table_options.ajax = {
|
||||
url: 'get_history',
|
||||
|
@ -187,7 +210,16 @@
|
|||
});
|
||||
|
||||
$('#history-user').on('change', function () {
|
||||
selected_user_id = $(this).val() || null;
|
||||
let val = $(this).val();
|
||||
if (val.length === 0 || val.length === $(this).children().length) {
|
||||
selected_user_id = null; // if all users are selected, just send an empty list
|
||||
} else {
|
||||
selected_user_id = val.join(",");
|
||||
}
|
||||
if (selected_user_id === history_user_last_id) {
|
||||
return;
|
||||
}
|
||||
history_user_last_id = selected_user_id;
|
||||
history_table.draw();
|
||||
});
|
||||
}
|
||||
|
|
9
data/interfaces/default/js/bootstrap-select.min.js
vendored
Normal file
9
data/interfaces/default/js/bootstrap-select.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -132,12 +132,6 @@
|
|||
</label>
|
||||
<p class="help-block">Change the "<em>Play by day of week</em>" graph to start on Monday. Default is start on Sunday.</p>
|
||||
</div>
|
||||
<div class="checkbox advanced-setting">
|
||||
<label>
|
||||
<input type="checkbox" id="group_history_tables" name="group_history_tables" value="1" ${config['group_history_tables']}> Group Play History
|
||||
</label>
|
||||
<p class="help-block">Group play history for the same item and user as a single entry when progress is less than the watched percent.</p>
|
||||
</div>
|
||||
<div class="checkbox advanced-setting">
|
||||
<label>
|
||||
<input type="checkbox" id="history_table_activity" name="history_table_activity" value="1" ${config['history_table_activity']}> Current Activity in History Tables
|
||||
|
@ -227,6 +221,25 @@
|
|||
</div>
|
||||
<p class="help-block">Decide whether to use end credits markers to determine the 'watched' state of video items. When markers are not available the selected threshold percentage will be used.</p>
|
||||
</div>
|
||||
<div class="checkbox advanced-setting">
|
||||
<label>
|
||||
<input type="checkbox" id="group_history_tables" name="group_history_tables" value="1" ${config['group_history_tables']}> Group Play History
|
||||
</label>
|
||||
<p class="help-block">Group play history for the same item and user as a single entry when progress is less than the watched percent.</p>
|
||||
</div>
|
||||
<div class="form-group advanced-setting">
|
||||
<label>Regroup Play History</label>
|
||||
<p class="help-block">
|
||||
Fix grouping of play history in the database.<br />
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-form" type="button" id="regroup_history">Regroup</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group advanced-setting">
|
||||
<label>Flush Temporary Sessions</label>
|
||||
<p class="help-block">
|
||||
|
@ -2484,6 +2497,12 @@ $(document).ready(function() {
|
|||
confirmAjaxCall(url, msg);
|
||||
});
|
||||
|
||||
$("#regroup_history").click(function () {
|
||||
var msg = 'Are you sure you want to regroup play history in the database?<br /><br /><strong>This make take a long time for large databases.<br />Regrouping will continue in the background.</strong>';
|
||||
var url = 'regroup_history';
|
||||
confirmAjaxCall(url, msg);
|
||||
});
|
||||
|
||||
$("#delete_temp_sessions").click(function () {
|
||||
var msg = 'Are you sure you want to flush the temporary sessions?<br /><br /><strong>This will reset all currently active sessions.</strong>';
|
||||
var url = 'delete_temp_sessions';
|
||||
|
|
|
@ -2,10 +2,10 @@ apscheduler==3.10.1
|
|||
importlib-metadata==6.0.0
|
||||
importlib-resources==5.12.0
|
||||
pyinstaller==5.8.0
|
||||
pyopenssl==23.0.0
|
||||
pycryptodomex==3.17
|
||||
pyopenssl==23.2.0
|
||||
pycryptodomex==3.18.0
|
||||
|
||||
pyobjc-core==9.0.1; platform_system == "Darwin"
|
||||
pyobjc-core==9.2; platform_system == "Darwin"
|
||||
pyobjc-framework-Cocoa==9.2; platform_system == "Darwin"
|
||||
|
||||
pywin32==305; platform_system == "Windows"
|
||||
pywin32==306; platform_system == "Windows"
|
||||
|
|
|
@ -326,70 +326,7 @@ class ActivityProcessor(object):
|
|||
|
||||
# Get the last insert row id
|
||||
last_id = self.db.last_insert_id()
|
||||
new_session = prev_session = None
|
||||
watched = False
|
||||
|
||||
if session['live']:
|
||||
# Check if we should group the session, select the last guid from the user
|
||||
query = "SELECT session_history.id, session_history_metadata.guid, session_history.reference_id " \
|
||||
"FROM session_history " \
|
||||
"JOIN session_history_metadata ON session_history.id == session_history_metadata.id " \
|
||||
"WHERE session_history.user_id = ? ORDER BY session_history.id DESC LIMIT 1 "
|
||||
|
||||
args = [session['user_id']]
|
||||
|
||||
result = self.db.select(query=query, args=args)
|
||||
|
||||
if len(result) > 0:
|
||||
new_session = {'id': last_id,
|
||||
'guid': metadata['guid'],
|
||||
'reference_id': last_id}
|
||||
|
||||
prev_session = {'id': result[0]['id'],
|
||||
'guid': result[0]['guid'],
|
||||
'reference_id': result[0]['reference_id']}
|
||||
|
||||
else:
|
||||
# Check if we should group the session, select the last two rows from the user
|
||||
query = "SELECT id, rating_key, view_offset, reference_id FROM session_history " \
|
||||
"WHERE user_id = ? AND rating_key = ? ORDER BY id DESC LIMIT 2 "
|
||||
|
||||
args = [session['user_id'], session['rating_key']]
|
||||
|
||||
result = self.db.select(query=query, args=args)
|
||||
|
||||
if len(result) > 1:
|
||||
new_session = {'id': result[0]['id'],
|
||||
'rating_key': result[0]['rating_key'],
|
||||
'view_offset': result[0]['view_offset'],
|
||||
'reference_id': result[0]['reference_id']}
|
||||
|
||||
prev_session = {'id': result[1]['id'],
|
||||
'rating_key': result[1]['rating_key'],
|
||||
'view_offset': result[1]['view_offset'],
|
||||
'reference_id': result[1]['reference_id']}
|
||||
|
||||
marker_first, marker_final = helpers.get_first_final_marker(metadata['markers'])
|
||||
watched = helpers.check_watched(
|
||||
session['media_type'], session['view_offset'], session['duration'],
|
||||
marker_first, marker_final
|
||||
)
|
||||
|
||||
query = "UPDATE session_history SET reference_id = ? WHERE id = ? "
|
||||
|
||||
# If previous session view offset less than watched percent,
|
||||
# and new session view offset is greater,
|
||||
# then set the reference_id to the previous row,
|
||||
# else set the reference_id to the new id
|
||||
if prev_session is None and new_session is None:
|
||||
args = [last_id, last_id]
|
||||
elif watched and prev_session['view_offset'] <= new_session['view_offset'] or \
|
||||
session['live'] and prev_session['guid'] == new_session['guid']:
|
||||
args = [prev_session['reference_id'], new_session['id']]
|
||||
else:
|
||||
args = [new_session['id'], new_session['id']]
|
||||
|
||||
self.db.action(query=query, args=args)
|
||||
self.group_history(last_id, session, metadata)
|
||||
|
||||
# logger.debug("Tautulli ActivityProcessor :: Successfully written history item, last id for session_history is %s"
|
||||
# % last_id)
|
||||
|
@ -546,6 +483,80 @@ class ActivityProcessor(object):
|
|||
# Return the session row id when the session is successfully written to the database
|
||||
return session['id']
|
||||
|
||||
def group_history(self, last_id, session, metadata=None):
|
||||
new_session = prev_session = None
|
||||
prev_watched = None
|
||||
|
||||
if session['live']:
|
||||
# Check if we should group the session, select the last guid from the user
|
||||
query = "SELECT session_history.id, session_history_metadata.guid, session_history.reference_id " \
|
||||
"FROM session_history " \
|
||||
"JOIN session_history_metadata ON session_history.id == session_history_metadata.id " \
|
||||
"WHERE session_history.id <= ? AND session_history.user_id = ? ORDER BY session_history.id DESC LIMIT 1 "
|
||||
|
||||
args = [last_id, session['user_id']]
|
||||
|
||||
result = self.db.select(query=query, args=args)
|
||||
|
||||
if len(result) > 0:
|
||||
new_session = {'id': last_id,
|
||||
'guid': metadata['guid'] if metadata else session['guid'],
|
||||
'reference_id': last_id}
|
||||
|
||||
prev_session = {'id': result[0]['id'],
|
||||
'guid': result[0]['guid'],
|
||||
'reference_id': result[0]['reference_id']}
|
||||
|
||||
else:
|
||||
# Check if we should group the session, select the last two rows from the user
|
||||
query = "SELECT id, rating_key, view_offset, reference_id FROM session_history " \
|
||||
"WHERE id <= ? AND user_id = ? AND rating_key = ? ORDER BY id DESC LIMIT 2 "
|
||||
|
||||
args = [last_id, session['user_id'], session['rating_key']]
|
||||
|
||||
result = self.db.select(query=query, args=args)
|
||||
|
||||
if len(result) > 1:
|
||||
new_session = {'id': result[0]['id'],
|
||||
'rating_key': result[0]['rating_key'],
|
||||
'view_offset': helpers.cast_to_int(result[0]['view_offset']),
|
||||
'reference_id': result[0]['reference_id']}
|
||||
|
||||
prev_session = {'id': result[1]['id'],
|
||||
'rating_key': result[1]['rating_key'],
|
||||
'view_offset': helpers.cast_to_int(result[1]['view_offset']),
|
||||
'reference_id': result[1]['reference_id']}
|
||||
|
||||
if metadata:
|
||||
marker_first, marker_final = helpers.get_first_final_marker(metadata['markers'])
|
||||
else:
|
||||
marker_first = session['marker_credits_first']
|
||||
marker_final = session['marker_credits_final']
|
||||
|
||||
prev_watched = helpers.check_watched(
|
||||
session['media_type'], prev_session['view_offset'], session['duration'],
|
||||
marker_first, marker_final
|
||||
)
|
||||
|
||||
query = "UPDATE session_history SET reference_id = ? WHERE id = ? "
|
||||
|
||||
# If previous session view offset less than watched threshold,
|
||||
# and new session view offset is greater,
|
||||
# then set the reference_id to the previous row,
|
||||
# else set the reference_id to the new id
|
||||
if (prev_watched is False and prev_session['view_offset'] <= new_session['view_offset'] or
|
||||
session['live'] and prev_session['guid'] == new_session['guid']):
|
||||
if metadata:
|
||||
logger.debug("Tautulli ActivityProcessor :: Grouping history for sessionKey %s", session['session_key'])
|
||||
args = [prev_session['reference_id'], new_session['id']]
|
||||
|
||||
else:
|
||||
if metadata:
|
||||
logger.debug("Tautulli ActivityProcessor :: Not grouping history for sessionKey %s", session['session_key'])
|
||||
args = [last_id, last_id]
|
||||
|
||||
self.db.action(query=query, args=args)
|
||||
|
||||
def get_sessions(self, user_id=None, ip_address=None):
|
||||
query = "SELECT * FROM sessions"
|
||||
args = []
|
||||
|
@ -695,3 +706,36 @@ class ActivityProcessor(object):
|
|||
"ORDER BY stopped DESC",
|
||||
[user_id, machine_id, media_type])
|
||||
return int(started - last_session.get('stopped', 0) >= plexpy.CONFIG.NOTIFY_CONTINUED_SESSION_THRESHOLD)
|
||||
|
||||
def regroup_history(self):
|
||||
logger.info("Tautulli ActivityProcessor :: Creating database backup...")
|
||||
if not database.make_backup():
|
||||
return False
|
||||
|
||||
logger.info("Tautulli ActivityProcessor :: Regrouping session history...")
|
||||
|
||||
query = (
|
||||
"SELECT * FROM session_history "
|
||||
"JOIN session_history_metadata ON session_history.id = session_history_metadata.id"
|
||||
)
|
||||
results = self.db.select(query)
|
||||
count = len(results)
|
||||
progress = 0
|
||||
|
||||
for i, session in enumerate(results, start=1):
|
||||
if int(i / count * 10) > progress:
|
||||
progress = int(i / count * 10)
|
||||
logger.info("Tautulli ActivityProcessor :: Regrouping session history: %d%%", progress * 10)
|
||||
|
||||
try:
|
||||
self.group_history(session['id'], session)
|
||||
except Exception as e:
|
||||
logger.error("Tautulli ActivityProcessor :: Error regrouping session history: %s", e)
|
||||
return False
|
||||
|
||||
logger.info("Tautulli ActivityProcessor :: Regrouping session history complete.")
|
||||
return True
|
||||
|
||||
|
||||
def regroup_history():
|
||||
ActivityProcessor().regroup_history()
|
||||
|
|
|
@ -216,6 +216,7 @@ AUDIO_QUALITY_PROFILES = {
|
|||
AUDIO_QUALITY_PROFILES = OrderedDict(sorted(list(AUDIO_QUALITY_PROFILES.items()), key=lambda k: k[0], reverse=True))
|
||||
|
||||
HW_DECODERS = [
|
||||
'd3d11va',
|
||||
'dxva2',
|
||||
'videotoolbox',
|
||||
'mediacodecndk',
|
||||
|
|
|
@ -177,6 +177,7 @@ _CONFIG_DEFINITIONS = {
|
|||
'NOTIFY_RECENTLY_ADDED_UPGRADE': (int, 'Monitoring', 0),
|
||||
'NOTIFY_REMOTE_ACCESS_THRESHOLD': (int, 'Monitoring', 60),
|
||||
'NOTIFY_CONCURRENT_BY_IP': (int, 'Monitoring', 0),
|
||||
'NOTIFY_CONCURRENT_IPV6_CIDR': (str, 'Monitoring', '/64'),
|
||||
'NOTIFY_CONCURRENT_THRESHOLD': (int, 'Monitoring', 2),
|
||||
'NOTIFY_NEW_DEVICE_INITIAL_ONLY': (int, 'Monitoring', 1),
|
||||
'NOTIFY_SERVER_CONNECTION_THRESHOLD': (int, 'Monitoring', 60),
|
||||
|
@ -536,7 +537,7 @@ class Config(object):
|
|||
Returns something from the ini unless it is a real property
|
||||
of the configuration object or is not all caps.
|
||||
"""
|
||||
if not re.match(r'[A-Z_]+$', name):
|
||||
if not re.match(r'[A-Z0-9_]+$', name):
|
||||
return super(Config, self).__getattr__(name)
|
||||
else:
|
||||
return self.check_setting(name)
|
||||
|
|
|
@ -51,11 +51,7 @@ class Graphs(object):
|
|||
time_range = helpers.cast_to_int(time_range) or 30
|
||||
timestamp = helpers.timestamp() - time_range * 24 * 60 * 60
|
||||
|
||||
user_cond = ''
|
||||
if session.get_session_user_id() and user_id and user_id != str(session.get_session_user_id()):
|
||||
user_cond = 'AND session_history.user_id = %s ' % session.get_session_user_id()
|
||||
elif user_id and user_id.isdigit():
|
||||
user_cond = 'AND session_history.user_id = %s ' % user_id
|
||||
user_cond = self._make_user_cond(user_id)
|
||||
|
||||
if grouping is None:
|
||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||
|
@ -171,11 +167,7 @@ class Graphs(object):
|
|||
time_range = helpers.cast_to_int(time_range) or 30
|
||||
timestamp = helpers.timestamp() - time_range * 24 * 60 * 60
|
||||
|
||||
user_cond = ''
|
||||
if session.get_session_user_id() and user_id and user_id != str(session.get_session_user_id()):
|
||||
user_cond = "AND session_history.user_id = %s " % session.get_session_user_id()
|
||||
elif user_id and user_id.isdigit():
|
||||
user_cond = "AND session_history.user_id = %s " % user_id
|
||||
user_cond = self._make_user_cond(user_id)
|
||||
|
||||
if grouping is None:
|
||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||
|
@ -308,11 +300,7 @@ class Graphs(object):
|
|||
time_range = helpers.cast_to_int(time_range) or 30
|
||||
timestamp = helpers.timestamp() - time_range * 24 * 60 * 60
|
||||
|
||||
user_cond = ''
|
||||
if session.get_session_user_id() and user_id and user_id != str(session.get_session_user_id()):
|
||||
user_cond = 'AND session_history.user_id = %s ' % session.get_session_user_id()
|
||||
elif user_id and user_id.isdigit():
|
||||
user_cond = 'AND session_history.user_id = %s ' % user_id
|
||||
user_cond = self._make_user_cond(user_id)
|
||||
|
||||
if grouping is None:
|
||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||
|
@ -427,11 +415,7 @@ class Graphs(object):
|
|||
time_range = helpers.cast_to_int(time_range) or 12
|
||||
timestamp = arrow.get(helpers.timestamp()).shift(months=-time_range).floor('month').timestamp()
|
||||
|
||||
user_cond = ''
|
||||
if session.get_session_user_id() and user_id and user_id != str(session.get_session_user_id()):
|
||||
user_cond = 'AND session_history.user_id = %s ' % session.get_session_user_id()
|
||||
elif user_id and user_id.isdigit():
|
||||
user_cond = 'AND session_history.user_id = %s ' % user_id
|
||||
user_cond = self._make_user_cond(user_id)
|
||||
|
||||
if grouping is None:
|
||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||
|
@ -554,11 +538,7 @@ class Graphs(object):
|
|||
time_range = helpers.cast_to_int(time_range) or 30
|
||||
timestamp = helpers.timestamp() - time_range * 24 * 60 * 60
|
||||
|
||||
user_cond = ''
|
||||
if session.get_session_user_id() and user_id and user_id != str(session.get_session_user_id()):
|
||||
user_cond = 'AND session_history.user_id = %s ' % session.get_session_user_id()
|
||||
elif user_id and user_id.isdigit():
|
||||
user_cond = 'AND session_history.user_id = %s ' % user_id
|
||||
user_cond = self._make_user_cond(user_id)
|
||||
|
||||
if grouping is None:
|
||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||
|
@ -653,11 +633,7 @@ class Graphs(object):
|
|||
time_range = helpers.cast_to_int(time_range) or 30
|
||||
timestamp = helpers.timestamp() - time_range * 24 * 60 * 60
|
||||
|
||||
user_cond = ''
|
||||
if session.get_session_user_id() and user_id and user_id != str(session.get_session_user_id()):
|
||||
user_cond = 'AND session_history.user_id = %s ' % session.get_session_user_id()
|
||||
elif user_id and user_id.isdigit():
|
||||
user_cond = 'AND session_history.user_id = %s ' % user_id
|
||||
user_cond = self._make_user_cond(user_id)
|
||||
|
||||
if grouping is None:
|
||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||
|
@ -763,11 +739,7 @@ class Graphs(object):
|
|||
time_range = helpers.cast_to_int(time_range) or 30
|
||||
timestamp = helpers.timestamp() - time_range * 24 * 60 * 60
|
||||
|
||||
user_cond = ''
|
||||
if session.get_session_user_id() and user_id and user_id != str(session.get_session_user_id()):
|
||||
user_cond = 'AND session_history.user_id = %s ' % session.get_session_user_id()
|
||||
elif user_id and user_id.isdigit():
|
||||
user_cond = 'AND session_history.user_id = %s ' % user_id
|
||||
user_cond = self._make_user_cond(user_id)
|
||||
|
||||
if grouping is None:
|
||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||
|
@ -860,11 +832,7 @@ class Graphs(object):
|
|||
time_range = helpers.cast_to_int(time_range) or 30
|
||||
timestamp = helpers.timestamp() - time_range * 24 * 60 * 60
|
||||
|
||||
user_cond = ''
|
||||
if session.get_session_user_id() and user_id and user_id != str(session.get_session_user_id()):
|
||||
user_cond = 'AND session_history.user_id = %s ' % session.get_session_user_id()
|
||||
elif user_id and user_id.isdigit():
|
||||
user_cond = 'AND session_history.user_id = %s ' % user_id
|
||||
user_cond = self._make_user_cond(user_id)
|
||||
|
||||
if grouping is None:
|
||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||
|
@ -941,11 +909,7 @@ class Graphs(object):
|
|||
time_range = helpers.cast_to_int(time_range) or 30
|
||||
timestamp = helpers.timestamp() - time_range * 24 * 60 * 60
|
||||
|
||||
user_cond = ''
|
||||
if session.get_session_user_id() and user_id and user_id != str(session.get_session_user_id()):
|
||||
user_cond = 'AND session_history.user_id = %s ' % session.get_session_user_id()
|
||||
elif user_id and user_id.isdigit():
|
||||
user_cond = 'AND session_history.user_id = %s ' % user_id
|
||||
user_cond = self._make_user_cond(user_id)
|
||||
|
||||
if grouping is None:
|
||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||
|
@ -1048,11 +1012,7 @@ class Graphs(object):
|
|||
time_range = helpers.cast_to_int(time_range) or 30
|
||||
timestamp = helpers.timestamp() - time_range * 24 * 60 * 60
|
||||
|
||||
user_cond = ''
|
||||
if session.get_session_user_id() and user_id and user_id != str(session.get_session_user_id()):
|
||||
user_cond = 'AND session_history.user_id = %s ' % session.get_session_user_id()
|
||||
elif user_id and user_id.isdigit():
|
||||
user_cond = 'AND session_history.user_id = %s ' % user_id
|
||||
user_cond = self._make_user_cond(user_id)
|
||||
|
||||
if grouping is None:
|
||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||
|
@ -1128,11 +1088,7 @@ class Graphs(object):
|
|||
time_range = helpers.cast_to_int(time_range) or 30
|
||||
timestamp = helpers.timestamp() - time_range * 24 * 60 * 60
|
||||
|
||||
user_cond = ''
|
||||
if session.get_session_user_id() and user_id and user_id != str(session.get_session_user_id()):
|
||||
user_cond = 'AND session_history.user_id = %s ' % session.get_session_user_id()
|
||||
elif user_id and user_id.isdigit():
|
||||
user_cond = 'AND session_history.user_id = %s ' % user_id
|
||||
user_cond = self._make_user_cond(user_id)
|
||||
|
||||
if grouping is None:
|
||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||
|
@ -1212,3 +1168,16 @@ class Graphs(object):
|
|||
'series': [series_1_output, series_2_output, series_3_output]}
|
||||
|
||||
return output
|
||||
|
||||
def _make_user_cond(self, user_id):
|
||||
"""
|
||||
Expects user_id to be a comma-separated list of ints.
|
||||
"""
|
||||
user_cond = ''
|
||||
if session.get_session_user_id() and user_id and user_id != str(session.get_session_user_id()):
|
||||
user_cond = 'AND session_history.user_id = %s ' % session.get_session_user_id()
|
||||
elif user_id:
|
||||
user_ids = helpers.split_strip(user_id)
|
||||
if all(id.isdigit() for id in user_ids):
|
||||
user_cond = 'AND session_history.user_id IN (%s) ' % ','.join(user_ids)
|
||||
return user_cond
|
||||
|
|
|
@ -33,6 +33,7 @@ from functools import reduce, wraps
|
|||
import hashlib
|
||||
import imghdr
|
||||
from future.moves.itertools import islice, zip_longest
|
||||
from ipaddress import ip_address, ip_network, IPv4Address
|
||||
import ipwhois
|
||||
import ipwhois.exceptions
|
||||
import ipwhois.utils
|
||||
|
@ -1777,3 +1778,18 @@ def check_watched(media_type, view_offset, duration, marker_credits_first=None,
|
|||
|
||||
def pms_name():
|
||||
return plexpy.CONFIG.PMS_NAME_OVERRIDE or plexpy.CONFIG.PMS_NAME
|
||||
|
||||
|
||||
def ip_type(ip: str) -> str:
|
||||
try:
|
||||
return "IPv4" if type(ip_address(ip)) is IPv4Address else "IPv6"
|
||||
except ValueError:
|
||||
return "Invalid"
|
||||
|
||||
|
||||
def get_ipv6_network_address(ip: str) -> str:
|
||||
cidr = "/64"
|
||||
cidr_pattern = re.compile(r'^/(1([0-1]\d|2[0-8]))$|^/(\d\d)$|^/[1-9]$')
|
||||
if cidr_pattern.match(plexpy.CONFIG.NOTIFY_CONCURRENT_IPV6_CIDR):
|
||||
cidr = plexpy.CONFIG.NOTIFY_CONCURRENT_IPV6_CIDR
|
||||
return str(ip_network(ip+cidr, strict=False).network_address)
|
||||
|
|
|
@ -318,7 +318,7 @@ def blacklist_logger():
|
|||
logger.blacklist_config(email_config)
|
||||
|
||||
|
||||
def serve_template(templatename, **kwargs):
|
||||
def serve_template(template_name, **kwargs):
|
||||
if plexpy.CONFIG.NEWSLETTER_CUSTOM_DIR:
|
||||
logger.info("Tautulli Newsletters :: Using custom newsletter template directory.")
|
||||
template_dir = plexpy.CONFIG.NEWSLETTER_CUSTOM_DIR
|
||||
|
@ -327,12 +327,12 @@ def serve_template(templatename, **kwargs):
|
|||
template_dir = os.path.join(str(interface_dir), plexpy.CONFIG.NEWSLETTER_TEMPLATES)
|
||||
|
||||
if not plexpy.CONFIG.NEWSLETTER_INLINE_STYLES:
|
||||
templatename = templatename.replace('.html', '.internal.html')
|
||||
template_name = template_name.replace('.html', '.internal.html')
|
||||
|
||||
_hplookup = TemplateLookup(directories=[template_dir], default_filters=['unicode', 'h'])
|
||||
|
||||
try:
|
||||
template = _hplookup.get_template(templatename)
|
||||
template = _hplookup.get_template(template_name)
|
||||
return template.render(**kwargs), False
|
||||
except:
|
||||
return exceptions.html_error_template().render(), True
|
||||
|
@ -477,7 +477,7 @@ class Newsletter(object):
|
|||
logger.info("Tautulli Newsletters :: Generating newsletter%s." % (' preview' if self.is_preview else ''))
|
||||
|
||||
newsletter_rendered, self.template_error = serve_template(
|
||||
templatename=self._TEMPLATE,
|
||||
template_name=self._TEMPLATE,
|
||||
uuid=self.uuid,
|
||||
subject=self.subject_formatted,
|
||||
body=self.body_formatted,
|
||||
|
|
|
@ -160,6 +160,7 @@ def add_notifier_each(notifier_id=None, notify_action=None, stream_data=None, ti
|
|||
|
||||
def notify_conditions(notify_action=None, stream_data=None, timeline_data=None, **kwargs):
|
||||
logger.debug("Tautulli NotificationHandler :: Checking global notification conditions.")
|
||||
evaluated = False
|
||||
|
||||
# Activity notifications
|
||||
if stream_data:
|
||||
|
@ -187,7 +188,13 @@ def notify_conditions(notify_action=None, stream_data=None, timeline_data=None,
|
|||
user_sessions = [s for s in result['sessions'] if s['user_id'] == stream_data['user_id']]
|
||||
|
||||
if plexpy.CONFIG.NOTIFY_CONCURRENT_BY_IP:
|
||||
evaluated = len(Counter(s['ip_address'] for s in user_sessions)) >= plexpy.CONFIG.NOTIFY_CONCURRENT_THRESHOLD
|
||||
ip_addresses = set()
|
||||
for s in user_sessions:
|
||||
if helpers.ip_type(s['ip_address']) == 'IPv6':
|
||||
ip_addresses.add(helpers.get_ipv6_network_address(s['ip_address']))
|
||||
elif helpers.ip_type(s['ip_address']) == 'IPv4':
|
||||
ip_addresses.add(s['ip_address'])
|
||||
evaluated = len(ip_addresses) >= plexpy.CONFIG.NOTIFY_CONCURRENT_THRESHOLD
|
||||
else:
|
||||
evaluated = len(user_sessions) >= plexpy.CONFIG.NOTIFY_CONCURRENT_THRESHOLD
|
||||
|
||||
|
|
|
@ -3967,6 +3967,14 @@ class TAUTULLIREMOTEAPP(Notifier):
|
|||
2: 'Large image (Non-expandable text)'
|
||||
}
|
||||
})
|
||||
elif platform == 'ios':
|
||||
config_option.append({
|
||||
'label': 'Include Poster Image',
|
||||
'value': self.config['notification_type'],
|
||||
'name': 'remoteapp_notification_type',
|
||||
'description': 'Include a poster with the notifications.',
|
||||
'input_type': 'checkbox'
|
||||
})
|
||||
|
||||
return config_option
|
||||
|
||||
|
|
|
@ -18,4 +18,4 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
PLEXPY_BRANCH = "master"
|
||||
PLEXPY_RELEASE_VERSION = "v2.12.4"
|
||||
PLEXPY_RELEASE_VERSION = "v2.12.5"
|
|
@ -314,7 +314,7 @@ class AuthController(object):
|
|||
|
||||
def get_loginform(self, redirect_uri=''):
|
||||
from plexpy.webserve import serve_template
|
||||
return serve_template(templatename="login.html", title="Login", redirect_uri=unquote(redirect_uri))
|
||||
return serve_template(template_name="login.html", title="Login", redirect_uri=unquote(redirect_uri))
|
||||
|
||||
@cherrypy.expose
|
||||
def index(self, *args, **kwargs):
|
||||
|
|
|
@ -51,6 +51,7 @@ if sys.version_info >= (3, 6):
|
|||
import plexpy
|
||||
if plexpy.PYTHON2:
|
||||
import activity_pinger
|
||||
import activity_processor
|
||||
import common
|
||||
import config
|
||||
import database
|
||||
|
@ -85,6 +86,7 @@ if plexpy.PYTHON2:
|
|||
import macos
|
||||
else:
|
||||
from plexpy import activity_pinger
|
||||
from plexpy import activity_processor
|
||||
from plexpy import common
|
||||
from plexpy import config
|
||||
from plexpy import database
|
||||
|
@ -119,12 +121,16 @@ else:
|
|||
from plexpy import macos
|
||||
|
||||
|
||||
def serve_template(templatename, **kwargs):
|
||||
interface_dir = os.path.join(str(plexpy.PROG_DIR), 'data/interfaces/')
|
||||
template_dir = os.path.join(str(interface_dir), plexpy.CONFIG.INTERFACE)
|
||||
TEMPLATE_LOOKUP = None
|
||||
|
||||
_hplookup = TemplateLookup(directories=[template_dir], default_filters=['unicode', 'h'],
|
||||
error_handler=mako_error_handler)
|
||||
|
||||
def serve_template(template_name, **kwargs):
|
||||
global TEMPLATE_LOOKUP
|
||||
if TEMPLATE_LOOKUP is None:
|
||||
interface_dir = os.path.join(str(plexpy.PROG_DIR), 'data/interfaces/')
|
||||
template_dir = os.path.join(str(interface_dir), plexpy.CONFIG.INTERFACE)
|
||||
TEMPLATE_LOOKUP = TemplateLookup(directories=[template_dir], default_filters=['unicode', 'h'],
|
||||
error_handler=mako_error_handler)
|
||||
|
||||
http_root = plexpy.HTTP_ROOT
|
||||
server_name = helpers.pms_name()
|
||||
|
@ -133,7 +139,7 @@ def serve_template(templatename, **kwargs):
|
|||
_session = get_session_info()
|
||||
|
||||
try:
|
||||
template = _hplookup.get_template(templatename)
|
||||
template = TEMPLATE_LOOKUP.get_template(template_name)
|
||||
return template.render(http_root=http_root, server_name=server_name, cache_param=cache_param,
|
||||
_session=_session, **kwargs)
|
||||
except Exception as e:
|
||||
|
@ -222,7 +228,7 @@ class WebInterface(object):
|
|||
plexpy.initialize_scheduler()
|
||||
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT + "home")
|
||||
else:
|
||||
return serve_template(templatename="welcome.html", title="Welcome", config=config)
|
||||
return serve_template(template_name="welcome.html", title="Welcome", config=config)
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -287,7 +293,7 @@ class WebInterface(object):
|
|||
"update_show_changelog": plexpy.CONFIG.UPDATE_SHOW_CHANGELOG,
|
||||
"first_run_complete": plexpy.CONFIG.FIRST_RUN_COMPLETE
|
||||
}
|
||||
return serve_template(templatename="index.html", title="Home", config=config)
|
||||
return serve_template(template_name="index.html", title="Home", config=config)
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -332,10 +338,10 @@ class WebInterface(object):
|
|||
result = pms_connect.get_current_activity()
|
||||
|
||||
if result:
|
||||
return serve_template(templatename="current_activity.html", data=result)
|
||||
return serve_template(template_name="current_activity.html", data=result)
|
||||
else:
|
||||
logger.warn("Unable to retrieve data for get_current_activity.")
|
||||
return serve_template(templatename="current_activity.html", data=None)
|
||||
return serve_template(template_name="current_activity.html", data=None)
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth()
|
||||
|
@ -346,9 +352,9 @@ class WebInterface(object):
|
|||
|
||||
if result:
|
||||
session = next((s for s in result['sessions'] if s['session_key'] == session_key), None)
|
||||
return serve_template(templatename="current_activity_instance.html", session=session)
|
||||
return serve_template(template_name="current_activity_instance.html", session=session)
|
||||
else:
|
||||
return serve_template(templatename="current_activity_instance.html", session=None)
|
||||
return serve_template(template_name="current_activity_instance.html", session=None)
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -391,7 +397,7 @@ class WebInterface(object):
|
|||
endpoint = endpoint.format(machine_id=plexpy.CONFIG.PMS_IDENTIFIER)
|
||||
|
||||
url = base_url + endpoint + ('?' + urlencode(kwargs) if kwargs else '')
|
||||
return serve_template(templatename="xml_shortcut.html", title="Plex XML", url=url)
|
||||
return serve_template(template_name="xml_shortcut.html", title="Plex XML", url=url)
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth()
|
||||
|
@ -401,7 +407,7 @@ class WebInterface(object):
|
|||
stats_type=stats_type,
|
||||
stats_count=stats_count)
|
||||
|
||||
return serve_template(templatename="home_stats.html", title="Stats", data=stats_data)
|
||||
return serve_template(template_name="home_stats.html", title="Stats", data=stats_data)
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth()
|
||||
|
@ -412,7 +418,7 @@ class WebInterface(object):
|
|||
|
||||
stats_data = data_factory.get_library_stats(library_cards=library_cards)
|
||||
|
||||
return serve_template(templatename="library_stats.html", title="Library Stats", data=stats_data)
|
||||
return serve_template(template_name="library_stats.html", title="Library Stats", data=stats_data)
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth()
|
||||
|
@ -422,13 +428,25 @@ class WebInterface(object):
|
|||
pms_connect = pmsconnect.PmsConnect()
|
||||
result = pms_connect.get_recently_added_details(count=count, media_type=media_type)
|
||||
except IOError as e:
|
||||
return serve_template(templatename="recently_added.html", data=None)
|
||||
return serve_template(template_name="recently_added.html", data=None)
|
||||
|
||||
if result and 'recently_added' in result:
|
||||
return serve_template(templatename="recently_added.html", data=result['recently_added'])
|
||||
return serve_template(template_name="recently_added.html", data=result['recently_added'])
|
||||
else:
|
||||
logger.warn("Unable to retrieve data for get_recently_added.")
|
||||
return serve_template(templatename="recently_added.html", data=None)
|
||||
return serve_template(template_name="recently_added.html", data=None)
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
@requireAuth(member_of("admin"))
|
||||
@addtoapi()
|
||||
def regroup_history(self, **kwargs):
|
||||
""" Regroup play history in the database."""
|
||||
|
||||
threading.Thread(target=activity_processor.regroup_history).start()
|
||||
|
||||
return {'result': 'success',
|
||||
'message': 'Regrouping play history started. Check the logs to monitor any problems.'}
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -463,7 +481,7 @@ class WebInterface(object):
|
|||
@cherrypy.expose
|
||||
@requireAuth()
|
||||
def libraries(self, **kwargs):
|
||||
return serve_template(templatename="libraries.html", title="Libraries")
|
||||
return serve_template(template_name="libraries.html", title="Libraries")
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -616,12 +634,12 @@ class WebInterface(object):
|
|||
library_details = library_data.get_details(section_id=section_id)
|
||||
except:
|
||||
logger.warn("Unable to retrieve library details for section_id %s " % section_id)
|
||||
return serve_template(templatename="library.html", title="Library", data=None, config=config)
|
||||
return serve_template(template_name="library.html", title="Library", data=None, config=config)
|
||||
else:
|
||||
logger.debug("Library page requested but no section_id received.")
|
||||
return serve_template(templatename="library.html", title="Library", data=None, config=config)
|
||||
return serve_template(template_name="library.html", title="Library", data=None, config=config)
|
||||
|
||||
return serve_template(templatename="library.html", title="Library", data=library_details, config=config)
|
||||
return serve_template(template_name="library.html", title="Library", data=library_details, config=config)
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth(member_of("admin"))
|
||||
|
@ -634,7 +652,7 @@ class WebInterface(object):
|
|||
result = None
|
||||
status_message = 'An error occured.'
|
||||
|
||||
return serve_template(templatename="edit_library.html", title="Edit Library",
|
||||
return serve_template(template_name="edit_library.html", title="Edit Library",
|
||||
data=result, server_id=plexpy.CONFIG.PMS_IDENTIFIER, status_message=status_message)
|
||||
|
||||
@cherrypy.expose
|
||||
|
@ -681,7 +699,7 @@ class WebInterface(object):
|
|||
@requireAuth()
|
||||
def library_watch_time_stats(self, section_id=None, **kwargs):
|
||||
if not allow_session_library(section_id):
|
||||
return serve_template(templatename="user_watch_time_stats.html", data=None, title="Watch Stats")
|
||||
return serve_template(template_name="user_watch_time_stats.html", data=None, title="Watch Stats")
|
||||
|
||||
if section_id:
|
||||
library_data = libraries.Libraries()
|
||||
|
@ -690,16 +708,16 @@ class WebInterface(object):
|
|||
result = None
|
||||
|
||||
if result:
|
||||
return serve_template(templatename="user_watch_time_stats.html", data=result, title="Watch Stats")
|
||||
return serve_template(template_name="user_watch_time_stats.html", data=result, title="Watch Stats")
|
||||
else:
|
||||
logger.warn("Unable to retrieve data for library_watch_time_stats.")
|
||||
return serve_template(templatename="user_watch_time_stats.html", data=None, title="Watch Stats")
|
||||
return serve_template(template_name="user_watch_time_stats.html", data=None, title="Watch Stats")
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth()
|
||||
def library_user_stats(self, section_id=None, **kwargs):
|
||||
if not allow_session_library(section_id):
|
||||
return serve_template(templatename="library_user_stats.html", data=None, title="Player Stats")
|
||||
return serve_template(template_name="library_user_stats.html", data=None, title="Player Stats")
|
||||
|
||||
if section_id:
|
||||
library_data = libraries.Libraries()
|
||||
|
@ -708,16 +726,16 @@ class WebInterface(object):
|
|||
result = None
|
||||
|
||||
if result:
|
||||
return serve_template(templatename="library_user_stats.html", data=result, title="Player Stats")
|
||||
return serve_template(template_name="library_user_stats.html", data=result, title="Player Stats")
|
||||
else:
|
||||
logger.warn("Unable to retrieve data for library_user_stats.")
|
||||
return serve_template(templatename="library_user_stats.html", data=None, title="Player Stats")
|
||||
return serve_template(template_name="library_user_stats.html", data=None, title="Player Stats")
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth()
|
||||
def library_recently_watched(self, section_id=None, limit='10', **kwargs):
|
||||
if not allow_session_library(section_id):
|
||||
return serve_template(templatename="user_recently_watched.html", data=None, title="Recently Watched")
|
||||
return serve_template(template_name="user_recently_watched.html", data=None, title="Recently Watched")
|
||||
|
||||
if section_id:
|
||||
library_data = libraries.Libraries()
|
||||
|
@ -726,16 +744,16 @@ class WebInterface(object):
|
|||
result = None
|
||||
|
||||
if result:
|
||||
return serve_template(templatename="user_recently_watched.html", data=result, title="Recently Watched")
|
||||
return serve_template(template_name="user_recently_watched.html", data=result, title="Recently Watched")
|
||||
else:
|
||||
logger.warn("Unable to retrieve data for library_recently_watched.")
|
||||
return serve_template(templatename="user_recently_watched.html", data=None, title="Recently Watched")
|
||||
return serve_template(template_name="user_recently_watched.html", data=None, title="Recently Watched")
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth()
|
||||
def library_recently_added(self, section_id=None, limit='10', **kwargs):
|
||||
if not allow_session_library(section_id):
|
||||
return serve_template(templatename="library_recently_added.html", data=None, title="Recently Added")
|
||||
return serve_template(template_name="library_recently_added.html", data=None, title="Recently Added")
|
||||
|
||||
if section_id:
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
|
@ -744,10 +762,10 @@ class WebInterface(object):
|
|||
result = None
|
||||
|
||||
if result and result['recently_added']:
|
||||
return serve_template(templatename="library_recently_added.html", data=result['recently_added'], title="Recently Added")
|
||||
return serve_template(template_name="library_recently_added.html", data=result['recently_added'], title="Recently Added")
|
||||
else:
|
||||
logger.warn("Unable to retrieve data for library_recently_added.")
|
||||
return serve_template(templatename="library_recently_added.html", data=None, title="Recently Added")
|
||||
return serve_template(template_name="library_recently_added.html", data=None, title="Recently Added")
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -1239,7 +1257,7 @@ class WebInterface(object):
|
|||
@cherrypy.expose
|
||||
@requireAuth()
|
||||
def users(self, **kwargs):
|
||||
return serve_template(templatename="users.html", title="Users")
|
||||
return serve_template(template_name="users.html", title="Users")
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -1355,12 +1373,12 @@ class WebInterface(object):
|
|||
user_details = user_data.get_details(user_id=user_id)
|
||||
except:
|
||||
logger.warn("Unable to retrieve user details for user_id %s " % user_id)
|
||||
return serve_template(templatename="user.html", title="User", data=None)
|
||||
return serve_template(template_name="user.html", title="User", data=None)
|
||||
else:
|
||||
logger.debug("User page requested but no user_id received.")
|
||||
return serve_template(templatename="user.html", title="User", data=None)
|
||||
return serve_template(template_name="user.html", title="User", data=None)
|
||||
|
||||
return serve_template(templatename="user.html", title="User", data=user_details)
|
||||
return serve_template(template_name="user.html", title="User", data=user_details)
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth(member_of("admin"))
|
||||
|
@ -1373,7 +1391,7 @@ class WebInterface(object):
|
|||
result = None
|
||||
status_message = 'An error occured.'
|
||||
|
||||
return serve_template(templatename="edit_user.html", title="Edit User", data=result, status_message=status_message)
|
||||
return serve_template(template_name="edit_user.html", title="Edit User", data=result, status_message=status_message)
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth(member_of("admin"))
|
||||
|
@ -1421,7 +1439,7 @@ class WebInterface(object):
|
|||
@requireAuth()
|
||||
def user_watch_time_stats(self, user=None, user_id=None, **kwargs):
|
||||
if not allow_session_user(user_id):
|
||||
return serve_template(templatename="user_watch_time_stats.html", data=None, title="Watch Stats")
|
||||
return serve_template(template_name="user_watch_time_stats.html", data=None, title="Watch Stats")
|
||||
|
||||
if user_id or user:
|
||||
user_data = users.Users()
|
||||
|
@ -1430,16 +1448,16 @@ class WebInterface(object):
|
|||
result = None
|
||||
|
||||
if result:
|
||||
return serve_template(templatename="user_watch_time_stats.html", data=result, title="Watch Stats")
|
||||
return serve_template(template_name="user_watch_time_stats.html", data=result, title="Watch Stats")
|
||||
else:
|
||||
logger.warn("Unable to retrieve data for user_watch_time_stats.")
|
||||
return serve_template(templatename="user_watch_time_stats.html", data=None, title="Watch Stats")
|
||||
return serve_template(template_name="user_watch_time_stats.html", data=None, title="Watch Stats")
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth()
|
||||
def user_player_stats(self, user=None, user_id=None, **kwargs):
|
||||
if not allow_session_user(user_id):
|
||||
return serve_template(templatename="user_player_stats.html", data=None, title="Player Stats")
|
||||
return serve_template(template_name="user_player_stats.html", data=None, title="Player Stats")
|
||||
|
||||
if user_id or user:
|
||||
user_data = users.Users()
|
||||
|
@ -1448,16 +1466,16 @@ class WebInterface(object):
|
|||
result = None
|
||||
|
||||
if result:
|
||||
return serve_template(templatename="user_player_stats.html", data=result, title="Player Stats")
|
||||
return serve_template(template_name="user_player_stats.html", data=result, title="Player Stats")
|
||||
else:
|
||||
logger.warn("Unable to retrieve data for user_player_stats.")
|
||||
return serve_template(templatename="user_player_stats.html", data=None, title="Player Stats")
|
||||
return serve_template(template_name="user_player_stats.html", data=None, title="Player Stats")
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth()
|
||||
def get_user_recently_watched(self, user=None, user_id=None, limit='10', **kwargs):
|
||||
if not allow_session_user(user_id):
|
||||
return serve_template(templatename="user_recently_watched.html", data=None, title="Recently Watched")
|
||||
return serve_template(template_name="user_recently_watched.html", data=None, title="Recently Watched")
|
||||
|
||||
if user_id or user:
|
||||
user_data = users.Users()
|
||||
|
@ -1466,10 +1484,10 @@ class WebInterface(object):
|
|||
result = None
|
||||
|
||||
if result:
|
||||
return serve_template(templatename="user_recently_watched.html", data=result, title="Recently Watched")
|
||||
return serve_template(template_name="user_recently_watched.html", data=result, title="Recently Watched")
|
||||
else:
|
||||
logger.warn("Unable to retrieve data for get_user_recently_watched.")
|
||||
return serve_template(templatename="user_recently_watched.html", data=None, title="Recently Watched")
|
||||
return serve_template(template_name="user_recently_watched.html", data=None, title="Recently Watched")
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -1875,7 +1893,7 @@ class WebInterface(object):
|
|||
"database_is_importing": database.IS_IMPORTING,
|
||||
}
|
||||
|
||||
return serve_template(templatename="history.html", title="History", config=config)
|
||||
return serve_template(template_name="history.html", title="History", config=config)
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -2063,7 +2081,7 @@ class WebInterface(object):
|
|||
data_factory = datafactory.DataFactory()
|
||||
stream_data = data_factory.get_stream_details(row_id, session_key)
|
||||
|
||||
return serve_template(templatename="stream_data.html", title="Stream Data", data=stream_data, user=user)
|
||||
return serve_template(template_name="stream_data.html", title="Stream Data", data=stream_data, user=user)
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -2154,7 +2172,7 @@ class WebInterface(object):
|
|||
|
||||
public = helpers.is_public_ip(ip_address)
|
||||
|
||||
return serve_template(templatename="ip_address_modal.html", title="IP Address Details",
|
||||
return serve_template(template_name="ip_address_modal.html", title="IP Address Details",
|
||||
data=ip_address, public=public, kwargs=kwargs)
|
||||
|
||||
@cherrypy.expose
|
||||
|
@ -2193,7 +2211,7 @@ class WebInterface(object):
|
|||
@cherrypy.expose
|
||||
@requireAuth()
|
||||
def graphs(self, **kwargs):
|
||||
return serve_template(templatename="graphs.html", title="Graphs")
|
||||
return serve_template(template_name="graphs.html", title="Graphs")
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -2238,7 +2256,7 @@ class WebInterface(object):
|
|||
Optional parameters:
|
||||
time_range (str): The number of days of data to return
|
||||
y_axis (str): "plays" or "duration"
|
||||
user_id (str): The user id to filter the data
|
||||
user_id (str): Comma separated list of user id to filter the data
|
||||
grouping (int): 0 or 1
|
||||
|
||||
Returns:
|
||||
|
@ -2282,7 +2300,7 @@ class WebInterface(object):
|
|||
Optional parameters:
|
||||
time_range (str): The number of days of data to return
|
||||
y_axis (str): "plays" or "duration"
|
||||
user_id (str): The user id to filter the data
|
||||
user_id (str): Comma separated list of user id to filter the data
|
||||
grouping (int): 0 or 1
|
||||
|
||||
Returns:
|
||||
|
@ -2326,7 +2344,7 @@ class WebInterface(object):
|
|||
Optional parameters:
|
||||
time_range (str): The number of days of data to return
|
||||
y_axis (str): "plays" or "duration"
|
||||
user_id (str): The user id to filter the data
|
||||
user_id (str): Comma separated list of user id to filter the data
|
||||
grouping (int): 0 or 1
|
||||
|
||||
Returns:
|
||||
|
@ -2370,7 +2388,7 @@ class WebInterface(object):
|
|||
Optional parameters:
|
||||
time_range (str): The number of months of data to return
|
||||
y_axis (str): "plays" or "duration"
|
||||
user_id (str): The user id to filter the data
|
||||
user_id (str): Comma separated list of user id to filter the data
|
||||
grouping (int): 0 or 1
|
||||
|
||||
Returns:
|
||||
|
@ -2414,7 +2432,7 @@ class WebInterface(object):
|
|||
Optional parameters:
|
||||
time_range (str): The number of days of data to return
|
||||
y_axis (str): "plays" or "duration"
|
||||
user_id (str): The user id to filter the data
|
||||
user_id (str): Comma separated list of user id to filter the data
|
||||
grouping (int): 0 or 1
|
||||
|
||||
Returns:
|
||||
|
@ -2458,7 +2476,7 @@ class WebInterface(object):
|
|||
Optional parameters:
|
||||
time_range (str): The number of days of data to return
|
||||
y_axis (str): "plays" or "duration"
|
||||
user_id (str): The user id to filter the data
|
||||
user_id (str): Comma separated list of user id to filter the data
|
||||
grouping (int): 0 or 1
|
||||
|
||||
Returns:
|
||||
|
@ -2502,7 +2520,7 @@ class WebInterface(object):
|
|||
Optional parameters:
|
||||
time_range (str): The number of days of data to return
|
||||
y_axis (str): "plays" or "duration"
|
||||
user_id (str): The user id to filter the data
|
||||
user_id (str): Comma separated list of user id to filter the data
|
||||
grouping (int): 0 or 1
|
||||
|
||||
Returns:
|
||||
|
@ -2545,7 +2563,7 @@ class WebInterface(object):
|
|||
Optional parameters:
|
||||
time_range (str): The number of days of data to return
|
||||
y_axis (str): "plays" or "duration"
|
||||
user_id (str): The user id to filter the data
|
||||
user_id (str): Comma separated list of user id to filter the data
|
||||
grouping (int): 0 or 1
|
||||
|
||||
Returns:
|
||||
|
@ -2588,7 +2606,7 @@ class WebInterface(object):
|
|||
Optional parameters:
|
||||
time_range (str): The number of days of data to return
|
||||
y_axis (str): "plays" or "duration"
|
||||
user_id (str): The user id to filter the data
|
||||
user_id (str): Comma separated list of user id to filter the data
|
||||
grouping (int): 0 or 1
|
||||
|
||||
Returns:
|
||||
|
@ -2631,7 +2649,7 @@ class WebInterface(object):
|
|||
Optional parameters:
|
||||
time_range (str): The number of days of data to return
|
||||
y_axis (str): "plays" or "duration"
|
||||
user_id (str): The user id to filter the data
|
||||
user_id (str): Comma separated list of user id to filter the data
|
||||
grouping (int): 0 or 1
|
||||
|
||||
Returns:
|
||||
|
@ -2674,7 +2692,7 @@ class WebInterface(object):
|
|||
Optional parameters:
|
||||
time_range (str): The number of days of data to return
|
||||
y_axis (str): "plays" or "duration"
|
||||
user_id (str): The user id to filter the data
|
||||
user_id (str): Comma separated list of user id to filter the data
|
||||
grouping (int): 0 or 1
|
||||
|
||||
Returns:
|
||||
|
@ -2707,9 +2725,9 @@ class WebInterface(object):
|
|||
@requireAuth()
|
||||
def history_table_modal(self, **kwargs):
|
||||
if kwargs.get('user_id') and not allow_session_user(kwargs['user_id']):
|
||||
return serve_template(templatename="history_table_modal.html", title="History Data", data=None)
|
||||
return serve_template(template_name="history_table_modal.html", title="History Data", data=None)
|
||||
|
||||
return serve_template(templatename="history_table_modal.html", title="History Data", data=kwargs)
|
||||
return serve_template(template_name="history_table_modal.html", title="History Data", data=kwargs)
|
||||
|
||||
|
||||
##### Sync #####
|
||||
|
@ -2717,7 +2735,7 @@ class WebInterface(object):
|
|||
@cherrypy.expose
|
||||
@requireAuth()
|
||||
def sync(self, **kwargs):
|
||||
return serve_template(templatename="sync.html", title="Synced Items")
|
||||
return serve_template(template_name="sync.html", title="Synced Items")
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -2776,7 +2794,7 @@ class WebInterface(object):
|
|||
@requireAuth(member_of("admin"))
|
||||
def logs(self, **kwargs):
|
||||
plex_log_files = log_reader.list_plex_logs()
|
||||
return serve_template(templatename="logs.html", title="Log", plex_log_files=plex_log_files)
|
||||
return serve_template(template_name="logs.html", title="Log", plex_log_files=plex_log_files)
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth(member_of("admin"))
|
||||
|
@ -3170,7 +3188,7 @@ class WebInterface(object):
|
|||
for key in ('home_sections', 'home_stats_cards', 'home_library_cards'):
|
||||
settings_dict[key] = json.dumps(settings_dict[key])
|
||||
|
||||
return serve_template(templatename="settings.html", title="Settings", config=settings_dict)
|
||||
return serve_template(template_name="settings.html", title="Settings", config=settings_dict)
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -3361,17 +3379,17 @@ class WebInterface(object):
|
|||
@cherrypy.expose
|
||||
@requireAuth(member_of("admin"))
|
||||
def get_configuration_table(self, **kwargs):
|
||||
return serve_template(templatename="configuration_table.html")
|
||||
return serve_template(template_name="configuration_table.html")
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth(member_of("admin"))
|
||||
def get_scheduler_table(self, **kwargs):
|
||||
return serve_template(templatename="scheduler_table.html")
|
||||
return serve_template(template_name="scheduler_table.html")
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth(member_of("admin"))
|
||||
def get_queue_modal(self, queue=None, **kwargs):
|
||||
return serve_template(templatename="queue_modal.html", queue=queue)
|
||||
return serve_template(template_name="queue_modal.html", queue=queue)
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -3435,7 +3453,7 @@ class WebInterface(object):
|
|||
@requireAuth(member_of("admin"))
|
||||
def get_notifiers_table(self, **kwargs):
|
||||
result = notifiers.get_notifiers()
|
||||
return serve_template(templatename="notifiers_table.html", notifiers_list=result)
|
||||
return serve_template(template_name="notifiers_table.html", notifiers_list=result)
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -3518,7 +3536,7 @@ class WebInterface(object):
|
|||
for category in common.NOTIFICATION_PARAMETERS for param in category['parameters']
|
||||
]
|
||||
|
||||
return serve_template(templatename="notifier_config.html", notifier=result, parameters=parameters)
|
||||
return serve_template(template_name="notifier_config.html", notifier=result, parameters=parameters)
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -3601,7 +3619,7 @@ class WebInterface(object):
|
|||
|
||||
text.append({'media_type': media_type, 'subject': test_subject, 'body': test_body})
|
||||
|
||||
return serve_template(templatename="notifier_text_preview.html", text=text, agent=agent_name)
|
||||
return serve_template(template_name="notifier_text_preview.html", text=text, agent=agent_name)
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -3779,7 +3797,7 @@ class WebInterface(object):
|
|||
@requireAuth(member_of("admin"))
|
||||
def get_mobile_devices_table(self, **kwargs):
|
||||
result = mobile_app.get_mobile_devices()
|
||||
return serve_template(templatename="mobile_devices_table.html", devices_list=result)
|
||||
return serve_template(template_name="mobile_devices_table.html", devices_list=result)
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -3802,7 +3820,7 @@ class WebInterface(object):
|
|||
def get_mobile_device_config_modal(self, mobile_device_id=None, **kwargs):
|
||||
result = mobile_app.get_mobile_device_config(mobile_device_id=mobile_device_id)
|
||||
|
||||
return serve_template(templatename="mobile_device_config.html", device=result)
|
||||
return serve_template(template_name="mobile_device_config.html", device=result)
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -4012,11 +4030,11 @@ class WebInterface(object):
|
|||
@requireAuth(member_of("admin"))
|
||||
def import_database_tool(self, app=None, **kwargs):
|
||||
if app == 'tautulli':
|
||||
return serve_template(templatename="app_import.html", title="Import Tautulli Database", app="Tautulli")
|
||||
return serve_template(template_name="app_import.html", title="Import Tautulli Database", app="Tautulli")
|
||||
elif app == 'plexwatch':
|
||||
return serve_template(templatename="app_import.html", title="Import PlexWatch Database", app="PlexWatch")
|
||||
return serve_template(template_name="app_import.html", title="Import PlexWatch Database", app="PlexWatch")
|
||||
elif app == 'plexivity':
|
||||
return serve_template(templatename="app_import.html", title="Import Plexivity Database", app="Plexivity")
|
||||
return serve_template(template_name="app_import.html", title="Import Plexivity Database", app="Plexivity")
|
||||
|
||||
logger.warn("No app specified for import.")
|
||||
return
|
||||
|
@ -4024,7 +4042,7 @@ class WebInterface(object):
|
|||
@cherrypy.expose
|
||||
@requireAuth(member_of("admin"))
|
||||
def import_config_tool(self, **kwargs):
|
||||
return serve_template(templatename="config_import.html", title="Import Tautulli Configuration")
|
||||
return serve_template(template_name="config_import.html", title="Import Tautulli Configuration")
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -4284,8 +4302,6 @@ class WebInterface(object):
|
|||
|
||||
return update
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth(member_of("admin"))
|
||||
def do_state_change(self, signal, title, timer, **kwargs):
|
||||
message = title
|
||||
quote = self.random_arnold_quotes()
|
||||
|
@ -4297,7 +4313,7 @@ class WebInterface(object):
|
|||
else:
|
||||
new_http_root = '/'
|
||||
|
||||
return serve_template(templatename="shutdown.html", signal=signal, title=title,
|
||||
return serve_template(template_name="shutdown.html", signal=signal, title=title,
|
||||
new_http_root=new_http_root, message=message, timer=timer, quote=quote)
|
||||
|
||||
@cherrypy.expose
|
||||
|
@ -4407,7 +4423,7 @@ class WebInterface(object):
|
|||
if metadata['section_id'] and not allow_session_library(metadata['section_id']):
|
||||
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT)
|
||||
|
||||
return serve_template(templatename="info.html", metadata=metadata, title="Info",
|
||||
return serve_template(template_name="info.html", metadata=metadata, title="Info",
|
||||
config=config, source=source, user_info=user_info)
|
||||
else:
|
||||
if get_session_user_id():
|
||||
|
@ -4423,11 +4439,11 @@ class WebInterface(object):
|
|||
result = pms_connect.get_item_children(rating_key=rating_key, media_type=media_type)
|
||||
|
||||
if result:
|
||||
return serve_template(templatename="info_children_list.html", data=result,
|
||||
return serve_template(template_name="info_children_list.html", data=result,
|
||||
media_type=media_type, title="Children List")
|
||||
else:
|
||||
logger.warn("Unable to retrieve data for get_item_children.")
|
||||
return serve_template(templatename="info_children_list.html", data=None, title="Children List")
|
||||
return serve_template(template_name="info_children_list.html", data=None, title="Children List")
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth()
|
||||
|
@ -4437,9 +4453,9 @@ class WebInterface(object):
|
|||
result = pms_connect.get_item_children_related(rating_key=rating_key)
|
||||
|
||||
if result:
|
||||
return serve_template(templatename="info_collection_list.html", data=result, title=title)
|
||||
return serve_template(template_name="info_collection_list.html", data=result, title=title)
|
||||
else:
|
||||
return serve_template(templatename="info_collection_list.html", data=None, title=title)
|
||||
return serve_template(template_name="info_collection_list.html", data=None, title=title)
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth()
|
||||
|
@ -4451,10 +4467,10 @@ class WebInterface(object):
|
|||
result = None
|
||||
|
||||
if result:
|
||||
return serve_template(templatename="user_watch_time_stats.html", data=result, title="Watch Stats")
|
||||
return serve_template(template_name="user_watch_time_stats.html", data=result, title="Watch Stats")
|
||||
else:
|
||||
logger.warn("Unable to retrieve data for item_watch_time_stats.")
|
||||
return serve_template(templatename="user_watch_time_stats.html", data=None, title="Watch Stats")
|
||||
return serve_template(template_name="user_watch_time_stats.html", data=None, title="Watch Stats")
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth()
|
||||
|
@ -4466,10 +4482,10 @@ class WebInterface(object):
|
|||
result = None
|
||||
|
||||
if result:
|
||||
return serve_template(templatename="library_user_stats.html", data=result, title="Player Stats")
|
||||
return serve_template(template_name="library_user_stats.html", data=result, title="Player Stats")
|
||||
else:
|
||||
logger.warn("Unable to retrieve data for item_user_stats.")
|
||||
return serve_template(templatename="library_user_stats.html", data=None, title="Player Stats")
|
||||
return serve_template(template_name="library_user_stats.html", data=None, title="Player Stats")
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -5091,7 +5107,7 @@ class WebInterface(object):
|
|||
@cherrypy.expose
|
||||
@requireAuth()
|
||||
def search(self, query='', **kwargs):
|
||||
return serve_template(templatename="search.html", title="Search", query=query)
|
||||
return serve_template(template_name="search.html", title="Search", query=query)
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -5148,10 +5164,10 @@ class WebInterface(object):
|
|||
if season['media_index'] == season_index]
|
||||
|
||||
if result:
|
||||
return serve_template(templatename="info_search_results_list.html", data=result, title="Search Result List")
|
||||
return serve_template(template_name="info_search_results_list.html", data=result, title="Search Result List")
|
||||
else:
|
||||
logger.warn("Unable to retrieve data for get_search_results_children.")
|
||||
return serve_template(templatename="info_search_results_list.html", data=None, title="Search Result List")
|
||||
return serve_template(template_name="info_search_results_list.html", data=None, title="Search Result List")
|
||||
|
||||
|
||||
##### Update Metadata #####
|
||||
|
@ -5168,10 +5184,10 @@ class WebInterface(object):
|
|||
query['query_string'] = query_string
|
||||
|
||||
if query:
|
||||
return serve_template(templatename="update_metadata.html", query=query, update=update, title="Info")
|
||||
return serve_template(template_name="update_metadata.html", query=query, update=update, title="Info")
|
||||
else:
|
||||
logger.warn("Unable to retrieve data for update_metadata.")
|
||||
return serve_template(templatename="update_metadata.html", query=query, update=update, title="Info")
|
||||
return serve_template(template_name="update_metadata.html", query=query, update=update, title="Info")
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -6574,7 +6590,7 @@ class WebInterface(object):
|
|||
@requireAuth(member_of("admin"))
|
||||
def get_newsletters_table(self, **kwargs):
|
||||
result = newsletters.get_newsletters()
|
||||
return serve_template(templatename="newsletters_table.html", newsletters_list=result)
|
||||
return serve_template(template_name="newsletters_table.html", newsletters_list=result)
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -6649,7 +6665,7 @@ class WebInterface(object):
|
|||
@requireAuth(member_of("admin"))
|
||||
def get_newsletter_config_modal(self, newsletter_id=None, **kwargs):
|
||||
result = newsletters.get_newsletter_config(newsletter_id=newsletter_id, mask_passwords=True)
|
||||
return serve_template(templatename="newsletter_config.html", newsletter=result)
|
||||
return serve_template(template_name="newsletter_config.html", newsletter=result)
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -6757,7 +6773,7 @@ class WebInterface(object):
|
|||
elif kwargs.pop('key', None) == plexpy.CONFIG.NEWSLETTER_PASSWORD:
|
||||
return self.newsletter_auth(*args, **kwargs)
|
||||
else:
|
||||
return serve_template(templatename="newsletter_auth.html",
|
||||
return serve_template(template_name="newsletter_auth.html",
|
||||
title="Newsletter Login",
|
||||
uri=request_uri)
|
||||
|
||||
|
@ -6794,7 +6810,7 @@ class WebInterface(object):
|
|||
@requireAuth(member_of("admin"))
|
||||
def newsletter_preview(self, **kwargs):
|
||||
kwargs['preview'] = 'true'
|
||||
return serve_template(templatename="newsletter_preview.html",
|
||||
return serve_template(template_name="newsletter_preview.html",
|
||||
title="Newsletter",
|
||||
kwargs=kwargs)
|
||||
|
||||
|
@ -6833,7 +6849,7 @@ class WebInterface(object):
|
|||
@cherrypy.expose
|
||||
@requireAuth(member_of("admin"))
|
||||
def support(self, **kwargs):
|
||||
return serve_template(templatename="support.html", title="Support")
|
||||
return serve_template(template_name="support.html", title="Support")
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
|
@ -6988,7 +7004,7 @@ class WebInterface(object):
|
|||
if media_type == 'photo_album':
|
||||
media_type = 'photoalbum'
|
||||
|
||||
return serve_template(templatename="export_modal.html", title="Export Metadata",
|
||||
return serve_template(template_name="export_modal.html", title="Export Metadata",
|
||||
section_id=section_id, user_id=user_id, rating_key=rating_key,
|
||||
media_type=media_type, sub_media_type=sub_media_type,
|
||||
export_type=export_type, file_formats=file_formats)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue