mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-14 09:12:58 -07:00
Add queued tasks modal
This commit is contained in:
parent
c098175ba9
commit
d731ad851c
6 changed files with 154 additions and 17 deletions
|
@ -3367,6 +3367,34 @@ pre::-webkit-scrollbar-thumb {
|
||||||
.notification-params tr:nth-child(even) td {
|
.notification-params tr:nth-child(even) td {
|
||||||
background-color: rgba(255,255,255,0.010);
|
background-color: rgba(255,255,255,0.010);
|
||||||
}
|
}
|
||||||
|
.activity-queue {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 10px;
|
||||||
|
background-color: #282828;
|
||||||
|
}
|
||||||
|
.activity-queue th {
|
||||||
|
padding-left: 10px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
.activity-queue th:first-child {
|
||||||
|
width: 268px;
|
||||||
|
}
|
||||||
|
.activity-queue th:nth-child(2) {
|
||||||
|
width: 125px;
|
||||||
|
}
|
||||||
|
.activity-queue th:nth-child(3) {
|
||||||
|
width: 175px;
|
||||||
|
}
|
||||||
|
.activity-queue td {
|
||||||
|
height: 25px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
.activity-queue tr:nth-child(odd) td {
|
||||||
|
background-color: rgba(255,255,255,0.035);
|
||||||
|
}
|
||||||
|
.activity-queue tr:nth-child(even) td {
|
||||||
|
background-color: rgba(255,255,255,0.010);
|
||||||
|
}
|
||||||
#days-selection label,
|
#days-selection label,
|
||||||
#months-selection label {
|
#months-selection label {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
|
66
data/interfaces/default/queue_modal.html
Normal file
66
data/interfaces/default/queue_modal.html
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<%
|
||||||
|
import arrow
|
||||||
|
from plexpy.activity_handler import ACTIVITY_SCHED, schedule_callback
|
||||||
|
|
||||||
|
if queue == 'active sessions':
|
||||||
|
filter_key = 'session_key-'
|
||||||
|
title_format = '{2} / {1} ({0})'
|
||||||
|
title_key = title_format.format('Session Key', 'Title', 'User')
|
||||||
|
description = 'Queue to flush stuck active sessions to the database.'
|
||||||
|
else:
|
||||||
|
filter_key = 'rating_key-'
|
||||||
|
title_format = '{1} ({0})'
|
||||||
|
title_key = title_format.format('Rating Key', 'Title')
|
||||||
|
description = 'Queue to flush recently added items to the database and send notifications if enabled.'
|
||||||
|
|
||||||
|
scheduled_jobs = [j.id for j in ACTIVITY_SCHED.get_jobs() if j.id.startswith(filter_key)]
|
||||||
|
%>
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
|
||||||
|
<h4 class="modal-title">${queue.title()} Queue</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p class="help-block">
|
||||||
|
${description}
|
||||||
|
</p>
|
||||||
|
<table class="activity-queue">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
${title_key}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Next Flush In
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Next Flush Time
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
% if scheduled_jobs:
|
||||||
|
% for job in scheduled_jobs:
|
||||||
|
<%
|
||||||
|
sched_job = ACTIVITY_SCHED.get_job(job)
|
||||||
|
next_run_in = arrow.get(sched_job.next_run_time).timestamp - arrow.now().timestamp
|
||||||
|
%>
|
||||||
|
<tr>
|
||||||
|
<td><strong>${title_format.format(*sched_job.args)}</strong></td>
|
||||||
|
<td>${arrow.get(next_run_in).format('HH:mm:ss')}</td>
|
||||||
|
<td>${arrow.get(sched_job.next_run_time).format('YYYY-MM-DD HH:mm:ss')}</td>
|
||||||
|
</tr>
|
||||||
|
% endfor
|
||||||
|
% else:
|
||||||
|
<tr>
|
||||||
|
<td colspan="3" style="text-align: center;"><i class="fa fa-check"></i> Nothing in the ${queue} queue</td>
|
||||||
|
</tr>
|
||||||
|
% endif
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -44,7 +44,13 @@ DOCUMENTATION :: END
|
||||||
</tr>
|
</tr>
|
||||||
% elif job in ('Check for server response', 'Check for active sessions', 'Check for recently added items') and plexpy.WS_CONNECTED:
|
% elif job in ('Check for server response', 'Check for active sessions', 'Check for recently added items') and plexpy.WS_CONNECTED:
|
||||||
<tr>
|
<tr>
|
||||||
|
% if job == 'Check for active sessions':
|
||||||
|
<td><a class="queue-modal-link" href="#" data-queue="active sessions">${job}</a></td>
|
||||||
|
% elif job == 'Check for recently added items':
|
||||||
|
<td><a class="queue-modal-link" href="#" data-queue="recently added">${job}</a></td>
|
||||||
|
% else:
|
||||||
<td>${job}</td>
|
<td>${job}</td>
|
||||||
|
% endif
|
||||||
<td><i class="fa fa-sm fa-fw fa-check"></i> Websocket</td>
|
<td><i class="fa fa-sm fa-fw fa-check"></i> Websocket</td>
|
||||||
<td>N/A</td>
|
<td>N/A</td>
|
||||||
<td>N/A</td>
|
<td>N/A</td>
|
||||||
|
@ -62,3 +68,20 @@ DOCUMENTATION :: END
|
||||||
% endfor
|
% endfor
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<script>
|
||||||
|
$('.queue-modal-link').on('click', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$.ajax({
|
||||||
|
url: 'get_queue_modal',
|
||||||
|
data: {
|
||||||
|
queue: $(this).data('queue')
|
||||||
|
},
|
||||||
|
cache: false,
|
||||||
|
async: true,
|
||||||
|
complete: function(xhr, status) {
|
||||||
|
$("#queue-modal").html(xhr.responseText);
|
||||||
|
$('#queue-modal').modal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
|
@ -1316,6 +1316,7 @@
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="modalIncludes()">
|
<%def name="modalIncludes()">
|
||||||
|
<div id="queue-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="queue-modal"></div>
|
||||||
<div id="guidelines-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="guidelines-modal">
|
<div id="guidelines-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="guidelines-modal">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
|
|
|
@ -115,6 +115,12 @@ class ActivityHandler(object):
|
||||||
# Write the new session to our temp session table
|
# Write the new session to our temp session table
|
||||||
self.update_db_session(session=session)
|
self.update_db_session(session=session)
|
||||||
|
|
||||||
|
# Schedule a callback to force stop a stale stream 5 minutes later
|
||||||
|
schedule_callback('session_key-{}'.format(self.get_session_key()),
|
||||||
|
func=force_stop_stream,
|
||||||
|
args=[self.get_session_key(), session['full_title'], session['username']],
|
||||||
|
minutes=5)
|
||||||
|
|
||||||
def on_stop(self, force_stop=False):
|
def on_stop(self, force_stop=False):
|
||||||
if self.is_valid_session():
|
if self.is_valid_session():
|
||||||
logger.debug(u"Tautulli ActivityHandler :: Session %s %sstopped."
|
logger.debug(u"Tautulli ActivityHandler :: Session %s %sstopped."
|
||||||
|
@ -147,8 +153,10 @@ class ActivityHandler(object):
|
||||||
ap.delete_session(row_id=row_id)
|
ap.delete_session(row_id=row_id)
|
||||||
delete_metadata_cache(self.get_session_key())
|
delete_metadata_cache(self.get_session_key())
|
||||||
else:
|
else:
|
||||||
schedule_callback('session_key-{}'.format(self.get_session_key()), func=force_stop_stream,
|
schedule_callback('session_key-{}'.format(self.get_session_key()),
|
||||||
args=[self.get_session_key()], seconds=30)
|
func=force_stop_stream,
|
||||||
|
args=[self.get_session_key(), db_session['full_title'], db_session['user']],
|
||||||
|
seconds=30)
|
||||||
|
|
||||||
def on_pause(self, still_paused=False):
|
def on_pause(self, still_paused=False):
|
||||||
if self.is_valid_session():
|
if self.is_valid_session():
|
||||||
|
@ -250,7 +258,9 @@ class ActivityHandler(object):
|
||||||
if db_session:
|
if db_session:
|
||||||
# Re-schedule the callback to reset the 5 minutes timer
|
# Re-schedule the callback to reset the 5 minutes timer
|
||||||
schedule_callback('session_key-{}'.format(self.get_session_key()),
|
schedule_callback('session_key-{}'.format(self.get_session_key()),
|
||||||
func=force_stop_stream, args=[self.get_session_key()], minutes=5)
|
func=force_stop_stream,
|
||||||
|
args=[self.get_session_key(), db_session['full_title'], db_session['user']],
|
||||||
|
minutes=5)
|
||||||
|
|
||||||
last_state = db_session['state']
|
last_state = db_session['state']
|
||||||
last_rating_key = str(db_session['rating_key'])
|
last_rating_key = str(db_session['rating_key'])
|
||||||
|
@ -320,10 +330,6 @@ class ActivityHandler(object):
|
||||||
if this_state != 'buffering':
|
if this_state != 'buffering':
|
||||||
self.on_start()
|
self.on_start()
|
||||||
|
|
||||||
# Schedule a callback to force stop a stale stream 5 minutes later
|
|
||||||
schedule_callback('session_key-{}'.format(self.get_session_key()),
|
|
||||||
func=force_stop_stream, args=[self.get_session_key()], minutes=5)
|
|
||||||
|
|
||||||
|
|
||||||
class TimelineHandler(object):
|
class TimelineHandler(object):
|
||||||
|
|
||||||
|
@ -388,6 +394,7 @@ class TimelineHandler(object):
|
||||||
if media_type in ('episode', 'track'):
|
if media_type in ('episode', 'track'):
|
||||||
metadata = self.get_metadata()
|
metadata = self.get_metadata()
|
||||||
if metadata:
|
if metadata:
|
||||||
|
grandparent_title = metadata['grandparent_title']
|
||||||
grandparent_rating_key = int(metadata['grandparent_rating_key'])
|
grandparent_rating_key = int(metadata['grandparent_rating_key'])
|
||||||
parent_rating_key = int(metadata['parent_rating_key'])
|
parent_rating_key = int(metadata['parent_rating_key'])
|
||||||
|
|
||||||
|
@ -405,12 +412,15 @@ class TimelineHandler(object):
|
||||||
% (title, str(rating_key), str(grandparent_rating_key)))
|
% (title, str(rating_key), str(grandparent_rating_key)))
|
||||||
|
|
||||||
# Schedule a callback to clear the recently added queue
|
# Schedule a callback to clear the recently added queue
|
||||||
schedule_callback('rating_key-{}'.format(grandparent_rating_key), func=clear_recently_added_queue,
|
schedule_callback('rating_key-{}'.format(grandparent_rating_key),
|
||||||
args=[grandparent_rating_key], seconds=plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_DELAY)
|
func=clear_recently_added_queue,
|
||||||
|
args=[grandparent_rating_key, grandparent_title],
|
||||||
|
seconds=plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_DELAY)
|
||||||
|
|
||||||
elif media_type in ('season', 'album'):
|
elif media_type in ('season', 'album'):
|
||||||
metadata = self.get_metadata()
|
metadata = self.get_metadata()
|
||||||
if metadata:
|
if metadata:
|
||||||
|
parent_title = metadata['parent_title']
|
||||||
parent_rating_key = int(metadata['parent_rating_key'])
|
parent_rating_key = int(metadata['parent_rating_key'])
|
||||||
|
|
||||||
parent_set = RECENTLY_ADDED_QUEUE.get(parent_rating_key, set())
|
parent_set = RECENTLY_ADDED_QUEUE.get(parent_rating_key, set())
|
||||||
|
@ -421,8 +431,10 @@ class TimelineHandler(object):
|
||||||
% (title, str(rating_key), str(parent_rating_key)))
|
% (title, str(rating_key), str(parent_rating_key)))
|
||||||
|
|
||||||
# Schedule a callback to clear the recently added queue
|
# Schedule a callback to clear the recently added queue
|
||||||
schedule_callback('rating_key-{}'.format(parent_rating_key), func=clear_recently_added_queue,
|
schedule_callback('rating_key-{}'.format(parent_rating_key),
|
||||||
args=[parent_rating_key], seconds=plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_DELAY)
|
func=clear_recently_added_queue,
|
||||||
|
args=[parent_rating_key, parent_title],
|
||||||
|
seconds=plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_DELAY)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
queue_set = RECENTLY_ADDED_QUEUE.get(rating_key, set())
|
queue_set = RECENTLY_ADDED_QUEUE.get(rating_key, set())
|
||||||
|
@ -432,8 +444,10 @@ class TimelineHandler(object):
|
||||||
% (title, str(rating_key)))
|
% (title, str(rating_key)))
|
||||||
|
|
||||||
# Schedule a callback to clear the recently added queue
|
# Schedule a callback to clear the recently added queue
|
||||||
schedule_callback('rating_key-{}'.format(rating_key), func=clear_recently_added_queue,
|
schedule_callback('rating_key-{}'.format(rating_key),
|
||||||
args=[rating_key], seconds=plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_DELAY)
|
func=clear_recently_added_queue,
|
||||||
|
args=[rating_key, title],
|
||||||
|
seconds=plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_DELAY)
|
||||||
|
|
||||||
# A movie, show, or artist is done processing
|
# A movie, show, or artist is done processing
|
||||||
elif media_type in ('movie', 'show', 'artist') and section_id > 0 and \
|
elif media_type in ('movie', 'show', 'artist') and section_id > 0 and \
|
||||||
|
@ -476,7 +490,7 @@ def schedule_callback(id, func=None, remove_job=False, args=None, **kwargs):
|
||||||
run_date=datetime.datetime.now() + datetime.timedelta(**kwargs)))
|
run_date=datetime.datetime.now() + datetime.timedelta(**kwargs)))
|
||||||
|
|
||||||
|
|
||||||
def force_stop_stream(session_key):
|
def force_stop_stream(session_key, title, user):
|
||||||
ap = activity_processor.ActivityProcessor()
|
ap = activity_processor.ActivityProcessor()
|
||||||
session = ap.get_session_by_key(session_key=session_key)
|
session = ap.get_session_by_key(session_key=session_key)
|
||||||
|
|
||||||
|
@ -500,7 +514,7 @@ def force_stop_stream(session_key):
|
||||||
|
|
||||||
# Reschedule for 30 seconds later
|
# Reschedule for 30 seconds later
|
||||||
schedule_callback('session_key-{}'.format(session_key), func=force_stop_stream,
|
schedule_callback('session_key-{}'.format(session_key), func=force_stop_stream,
|
||||||
args=[session_key], seconds=30)
|
args=[session_key, session['full_title'], session['user']], seconds=30)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.warn(u"Tautulli ActivityHandler :: Failed to write stream with sessionKey %s ratingKey %s to the database. " \
|
logger.warn(u"Tautulli ActivityHandler :: Failed to write stream with sessionKey %s ratingKey %s to the database. " \
|
||||||
|
@ -512,7 +526,7 @@ def force_stop_stream(session_key):
|
||||||
delete_metadata_cache(session_key)
|
delete_metadata_cache(session_key)
|
||||||
|
|
||||||
|
|
||||||
def clear_recently_added_queue(rating_key):
|
def clear_recently_added_queue(rating_key, title):
|
||||||
child_keys = RECENTLY_ADDED_QUEUE[rating_key]
|
child_keys = RECENTLY_ADDED_QUEUE[rating_key]
|
||||||
|
|
||||||
if plexpy.CONFIG.NOTIFY_GROUP_RECENTLY_ADDED_GRANDPARENT and len(child_keys) > 1:
|
if plexpy.CONFIG.NOTIFY_GROUP_RECENTLY_ADDED_GRANDPARENT and len(child_keys) > 1:
|
||||||
|
|
|
@ -2995,6 +2995,11 @@ class WebInterface(object):
|
||||||
def get_scheduler_table(self, **kwargs):
|
def get_scheduler_table(self, **kwargs):
|
||||||
return serve_template(templatename="scheduler_table.html")
|
return serve_template(templatename="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)
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
@cherrypy.tools.json_out()
|
@cherrypy.tools.json_out()
|
||||||
@requireAuth(member_of("admin"))
|
@requireAuth(member_of("admin"))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue