mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-08-20 21:33:18 -07:00
Merge branch 'dev'
This commit is contained in:
commit
1761b14fe9
17 changed files with 648 additions and 161 deletions
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -1,5 +1,16 @@
|
|||
# Changelog
|
||||
|
||||
## v1.1.10 (2015-09-20)
|
||||
|
||||
* Added dedicated settings section for home stats configuration with ability to show/hide selected stats and sections.
|
||||
* Added support for Twitter notifications.
|
||||
* Only show music in graphs if music logging is enabled.
|
||||
* The monitoring ignore interval now excludes paused time.
|
||||
* Fix display bug on activity panel which incorrectly reported transcoding sometimes.
|
||||
* Fix bug with Email notification TLS checkbox when it would be disabled by changing any other settings afterwards.
|
||||
* Fix issue on some Python releases where the webbrowser library isn't included.
|
||||
|
||||
|
||||
## v1.1.9 (2015-09-14)
|
||||
|
||||
* Another JonnyWong release. I'm going to stop thanking you now ;)
|
||||
|
|
|
@ -33,6 +33,33 @@ select.input-sm {
|
|||
color: #999;
|
||||
outline: none;
|
||||
}
|
||||
select[multiple] {
|
||||
height: 125px;
|
||||
margin: 5px 0 5px 0;
|
||||
color: #fff;
|
||||
border: 0px solid #444;
|
||||
background: #555;
|
||||
padding: 2px 2px;
|
||||
background-color: #555;
|
||||
border-radius: 3px;
|
||||
transition: background-color .3s;
|
||||
}
|
||||
select[multiple]:focus {
|
||||
outline: 0;
|
||||
outline: thin dotted \9;
|
||||
color: #555;
|
||||
background-color: #fff;
|
||||
transition: background-color .3s;
|
||||
}
|
||||
select[multiple]:focus::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(0,0,0,.15);
|
||||
}
|
||||
select[multiple] option {
|
||||
padding: 6px 10px;
|
||||
-webkit-border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
img {
|
||||
-webkit-box-sizing: content-box;
|
||||
-moz-box-sizing: content-box;
|
||||
|
|
|
@ -119,6 +119,8 @@ DOCUMENTATION :: END
|
|||
% if a['type'] == 'track':
|
||||
% if a['audio_decision'] == 'direct play':
|
||||
Stream <strong>Direct Play</strong>
|
||||
% elif a['audio_decision'] == 'copy':
|
||||
Stream <strong>Direct Stream</strong>
|
||||
% else:
|
||||
Stream <strong>Transcoding
|
||||
(Speed: ${a['transcode_speed']})
|
||||
|
@ -136,8 +138,10 @@ DOCUMENTATION :: END
|
|||
Audio <strong>Transcode (${a['transcode_audio_codec']}) (${a['transcode_audio_channels']}ch)</strong>
|
||||
% endif
|
||||
% elif a['type'] == 'episode' or a['type'] == 'movie' or a['type'] == 'clip':
|
||||
% if a['video_decision'] == 'direct play':
|
||||
% if a['video_decision'] == 'direct play' and a['audio_decision'] == 'direct play':
|
||||
Stream <strong>Direct Play</strong>
|
||||
% elif a['video_decision'] == 'copy' and a['audio_decision'] == 'copy':
|
||||
Stream <strong>Direct Stream</strong>
|
||||
% else:
|
||||
Stream <strong>Transcoding
|
||||
(Speed: ${a['transcode_speed']})
|
||||
|
@ -165,6 +169,8 @@ DOCUMENTATION :: END
|
|||
% elif a['type'] == 'photo':
|
||||
% if a['video_decision'] == 'direct play':
|
||||
Stream <strong>Direct Play</strong>
|
||||
% elif a['video_decision'] == 'copy':
|
||||
Stream <strong>Direct Stream</strong>
|
||||
% else:
|
||||
Stream <strong>
|
||||
Transcoding
|
||||
|
|
|
@ -294,6 +294,8 @@
|
|||
$('a[data-toggle=tab][href=' + current_tab + ']').trigger('click');
|
||||
}
|
||||
|
||||
var music_visible = (${config['music_logging_enable']} == 1 ? true : false);
|
||||
|
||||
function loadGraphsTab1(time_range, yaxis) {
|
||||
setGraphFormat(yaxis);
|
||||
|
||||
|
@ -319,6 +321,7 @@
|
|||
hc_plays_by_day_options.yAxis.min = 0;
|
||||
hc_plays_by_day_options.xAxis.categories = dateArray;
|
||||
hc_plays_by_day_options.series = data.series;
|
||||
hc_plays_by_day_options.series[2].visible = music_visible;
|
||||
var hc_plays_by_day = new Highcharts.Chart(hc_plays_by_day_options);
|
||||
}
|
||||
});
|
||||
|
@ -331,6 +334,7 @@
|
|||
success: function(data) {
|
||||
hc_plays_by_dayofweek_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_dayofweek_options.series = data.series;
|
||||
hc_plays_by_dayofweek_options.series[2].visible = music_visible;
|
||||
var hc_plays_by_dayofweek = new Highcharts.Chart(hc_plays_by_dayofweek_options);
|
||||
}
|
||||
});
|
||||
|
@ -343,6 +347,7 @@
|
|||
success: function(data) {
|
||||
hc_plays_by_hourofday_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_hourofday_options.series = data.series;
|
||||
hc_plays_by_hourofday_options.series[2].visible = music_visible;
|
||||
var hc_plays_by_hourofday = new Highcharts.Chart(hc_plays_by_hourofday_options);
|
||||
}
|
||||
});
|
||||
|
@ -355,6 +360,7 @@
|
|||
success: function(data) {
|
||||
hc_plays_by_platform_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_platform_options.series = data.series;
|
||||
hc_plays_by_platform_options.series[2].visible = music_visible;
|
||||
var hc_plays_by_platform = new Highcharts.Chart(hc_plays_by_platform_options);
|
||||
}
|
||||
});
|
||||
|
@ -367,6 +373,7 @@
|
|||
success: function(data) {
|
||||
hc_plays_by_user_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_user_options.series = data.series;
|
||||
hc_plays_by_user_options.series[2].visible = music_visible;
|
||||
var hc_plays_by_user = new Highcharts.Chart(hc_plays_by_user_options);
|
||||
}
|
||||
});
|
||||
|
@ -462,6 +469,7 @@
|
|||
hc_plays_by_month_options.yAxis.min = 0;
|
||||
hc_plays_by_month_options.xAxis.categories = data.categories;
|
||||
hc_plays_by_month_options.series = data.series;
|
||||
hc_plays_by_month_options.series[2].visible = music_visible;
|
||||
var hc_plays_by_month = new Highcharts.Chart(hc_plays_by_month_options);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -17,19 +17,19 @@ data[array_index]['rows'] :: Usable parameters
|
|||
|
||||
row_id Return the db row id for a metadata item if one exists
|
||||
|
||||
== Only if 'stat_id' is 'top_tv' or 'popular_tv' or 'top_movies' or 'popular_movies' or 'last_watched' ==
|
||||
== Only if 'stat_id' is 'top_tv' or 'popular_tv' or 'top_movies' or 'popular_movies' or 'top_music' or 'popular_music' or 'last_watched' ==
|
||||
thumb Return the thumb for the media item.
|
||||
|
||||
== Only if 'stat_id' is 'top_tv' or 'popular_tv' ==
|
||||
== Only if 'stat_id' is 'top_tv' or 'popular_tv' or 'top_music' or 'popular_music' ==
|
||||
grandparent_thumb Returns location of the item's thumbnail. Use with pms_image_proxy.
|
||||
rating_key Returns the unique identifier for the media item.
|
||||
title Returns the title for the associated stat.
|
||||
|
||||
== Only if 'stat_id' is 'top_tv' or 'top_movies' or 'top_user' or 'top_platform' ==
|
||||
== Only if 'stat_id' is 'top_tv' or 'top_movies' or 'top_music' or 'top_user' or 'top_platform' ==
|
||||
total_plays Returns the count for the associated stat.
|
||||
total_duration Returns the total duration for the associated stat.
|
||||
|
||||
== Only of 'stat_id' is 'popular_tv' or 'popular_movies' ==
|
||||
== Only of 'stat_id' is 'popular_tv' or 'popular_movies' or 'popular_music' ==
|
||||
users_watched Returns the count for the associated stat.
|
||||
|
||||
== Only if 'stat_id' is 'top_user' or 'last_watched' ==
|
||||
|
@ -372,6 +372,158 @@ DOCUMENTATION :: END
|
|||
% endif
|
||||
</li>
|
||||
</div>
|
||||
% elif top_stat['stat_id'] == 'top_music' and top_stat['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<div class="home-platforms-instance-info">
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Most Listened to Artist</h4>
|
||||
</div>
|
||||
<div class="home-platforms-instance-playcount">
|
||||
<h4>
|
||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}">
|
||||
${top_stat['rows'][0]['title']}
|
||||
</a>
|
||||
</h4>
|
||||
% if top_stat['stat_type'] == 'total_plays':
|
||||
<h3>${top_stat['rows'][0]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
% else:
|
||||
${top_stat['rows'][0]['total_duration'] | hd}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}">
|
||||
% if top_stat['rows'][0]['grandparent_thumb']:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['grandparent_thumb']}&width=300&height=300&fallback=poster);"></div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
%if len(top_stat['rows']) > 1:
|
||||
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
||||
<ul class="list-unstyled">
|
||||
<div class="slider">
|
||||
<div class="home-platforms-instance-list">
|
||||
% for row in top_stat['rows']:
|
||||
%if loop.index > 0:
|
||||
<li>
|
||||
<div class="home-platforms-instance-list-info">
|
||||
<div class="home-platforms-instance-list-name">
|
||||
<h5>
|
||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}">
|
||||
${top_stat['rows'][loop.index]['title']}
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="home-platforms-instance-list-playcount">
|
||||
% if top_stat['stat_type'] == 'total_plays':
|
||||
<h3>${top_stat['rows'][loop.index]['total_plays']}</h3>
|
||||
<p> plays</p>
|
||||
% else:
|
||||
${top_stat['rows'][loop.index]['total_duration'] | hd}
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}">
|
||||
% if top_stat['rows'][loop.index]['grandparent_thumb']:
|
||||
<div class="home-platforms-instance-list-poster">
|
||||
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['grandparent_thumb']}&width=300&height=300&fallback=poster);"></div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster2">
|
||||
<div class="home-platforms-list-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
<div class="home-platforms-instance-list-number">
|
||||
<h4>${loop.index + 1}</h4>
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
% endif
|
||||
</li>
|
||||
</div>
|
||||
% elif top_stat['stat_id'] == 'popular_music' and top_stat['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
<div class="home-platforms-instance-info">
|
||||
<div class="home-platforms-instance-name">
|
||||
<h4>Most Popular Artist</h4>
|
||||
</div>
|
||||
<div class="home-platforms-instance-playcount">
|
||||
<h4>
|
||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}">
|
||||
${top_stat['rows'][0]['title']}
|
||||
</a>
|
||||
</h4>
|
||||
<h3>${top_stat['rows'][0]['users_watched']}</h3>
|
||||
<p> users</p>
|
||||
</div>
|
||||
</div>
|
||||
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}">
|
||||
% if top_stat['rows'][0]['grandparent_thumb'] != '':
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['grandparent_thumb']}&width=300&height=300&fallback=poster);"></div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster">
|
||||
<div class="home-platforms-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
%if len(top_stat['rows']) > 1:
|
||||
<div class="home-platforms-instance-list-chevron"><i class="fa fa-chevron-down"></i></div>
|
||||
<ul class="list-unstyled">
|
||||
<div class="slider">
|
||||
<div class="home-platforms-instance-list">
|
||||
% for row in top_stat['rows']:
|
||||
%if loop.index > 0:
|
||||
<li>
|
||||
<div class="home-platforms-instance-list-info">
|
||||
<div class="home-platforms-instance-list-name">
|
||||
<h5>
|
||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}">
|
||||
${top_stat['rows'][loop.index]['title']}
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="home-platforms-instance-list-playcount">
|
||||
<h3>${top_stat['rows'][loop.index]['users_watched']}</h3>
|
||||
<p> users</p>
|
||||
</div>
|
||||
</div>
|
||||
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}">
|
||||
% if top_stat['rows'][loop.index]['grandparent_thumb']:
|
||||
<div class="home-platforms-instance-list-poster">
|
||||
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['grandparent_thumb']}&width=300&height=300&fallback=poster);"></div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="home-platforms-instance-poster2">
|
||||
<div class="home-platforms-list-poster-face" style="background-image: url(interfaces/default/images/poster.png);"></div>
|
||||
</div>
|
||||
% endif
|
||||
</a>
|
||||
<div class="home-platforms-instance-list-number">
|
||||
<h4>${loop.index + 1}</h4>
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% endfor
|
||||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
% endif
|
||||
</li>
|
||||
</div>
|
||||
% elif top_stat['stat_id'] == 'top_users' and top_stat['rows']:
|
||||
<div class="home-platforms-instance">
|
||||
<li>
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% if config['home_stats_cards'] > 'watch_statistics':
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="padded-header">
|
||||
|
@ -27,6 +28,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
% if config['home_library_cards'] > 'library_statistics':
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="padded-header" id="library-statistics-header">
|
||||
|
@ -38,6 +41,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
<div class='row'>
|
||||
<div class="col-md-12">
|
||||
<div class="padded-header">
|
||||
|
@ -82,12 +86,12 @@
|
|||
currentActivity();
|
||||
setInterval(currentActivity, 15000);
|
||||
|
||||
function getHomeStats(days, stat_type, stat_count) {
|
||||
function getHomeStats(days) {
|
||||
$.ajax({
|
||||
url: 'home_stats',
|
||||
cache: false,
|
||||
async: true,
|
||||
data: {time_range: days, stat_type: stat_type, stat_count: stat_count},
|
||||
data: { },
|
||||
complete: function(xhr, status) {
|
||||
$("#home-stats").html(xhr.responseText);
|
||||
}
|
||||
|
@ -165,7 +169,7 @@
|
|||
}
|
||||
});
|
||||
|
||||
getHomeStats(${config['home_stats_length']}, ${config['home_stats_type']}, ${config['home_stats_count']});
|
||||
getHomeStats();
|
||||
getLibraryStatsHeader();
|
||||
getLibraryStats();
|
||||
|
||||
|
|
|
@ -73,6 +73,6 @@ DOCUMENTATION :: END
|
|||
% endfor
|
||||
</ul>
|
||||
% else:
|
||||
<div class="text-muted">Unable to retrieve data from database. Please check your <a href="settings">settings</a>.
|
||||
<div class="text-muted">Unable to retrieve data from server. Please check your <a href="settings">settings</a>.
|
||||
</div><br>
|
||||
% endif
|
|
@ -1,3 +1,6 @@
|
|||
<%!
|
||||
from plexpy import helpers
|
||||
%>
|
||||
% if data:
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
|
@ -9,9 +12,9 @@
|
|||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<form action="set_notification_config" method="post" class="form" id="set_notification_config" data-parsley-validate>
|
||||
<div class="col-md-6">
|
||||
<div class="col-md-8">
|
||||
% for item in data:
|
||||
% if item['input_type'] != 'checkbox':
|
||||
% if item['input_type'] == 'text' or item['input_type'] == 'number' or item['input_type'] == 'password':
|
||||
<div class="form-group">
|
||||
<label for="${item['name']}">${item['label']}</label>
|
||||
<input type="${item['input_type']}" class="form-control" id="${item['name']}" name="${item['name']}" value="${item['value']}" size="30">
|
||||
|
@ -20,12 +23,18 @@
|
|||
% endif
|
||||
<p class="help-block">${item['description']}</p>
|
||||
</div>
|
||||
% elif item['input_type'] == 'button':
|
||||
<div class="form-group">
|
||||
<input type="${item['input_type']}" class="btn btn-bright" id="${item['name']}" name="${item['name']}" value="${item['value']}">
|
||||
</div>
|
||||
<p class="help-block">${item['description']}</p>
|
||||
% elif item['input_type'] == 'checkbox':
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="${item['name']}" name="${item['name']}" value="1" ${item['value']}> ${item['label']}
|
||||
<input type="checkbox" data-id="${item['name']}" class="checkboxes" value="1" ${helpers.checked(item['value'])}> ${item['label']}
|
||||
</label>
|
||||
<p class="help-block">${item['description']}</p>
|
||||
<input type="hidden" id="${item['name']}" name="${item['name']}" value="${item['value']}">
|
||||
</div>
|
||||
% endif
|
||||
% endfor
|
||||
|
@ -35,7 +44,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<%
|
||||
nosave = any(d['input_type'] == 'nosave' for d in data)
|
||||
%>
|
||||
% if not nosave:
|
||||
<input type="button" id="save-notification-item" class="btn btn-bright" value="Save">
|
||||
% else:
|
||||
<input type="button" class="btn btn-bright" data-dismiss="modal" value="Close">
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -53,4 +69,30 @@
|
|||
doAjaxCall('set_notification_config',$(this),'tabs',true);
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#twitterStep1').click(function () {
|
||||
$.get("/twitterStep1", function (data) {window.open(data); })
|
||||
.done(function () { $('#ajaxMsg').html("<div class='msg'><span class='ui-icon ui-icon-check'></span>Confirm Authorization. Check pop-up blocker if no response.</div>"); });
|
||||
$('#ajaxMsg').addClass('success').fadeIn().delay(3000).fadeOut();
|
||||
});
|
||||
$('#twitterStep2').click(function () {
|
||||
var twitter_key = $("#twitter_key").val();
|
||||
$.get("/twitterStep2", {'key': twitter_key}, function (data) { $('#ajaxMsg').html("<div class='msg'><span class='ui-icon ui-icon-check'></span>"+data+"</div>"); });
|
||||
$('#ajaxMsg').addClass('success').fadeIn().delay(3000).fadeOut();
|
||||
});
|
||||
$('#testTwitter').click(function () {
|
||||
$.get("/testTwitter",
|
||||
function (data) { $('#ajaxMsg').html("<div class='msg'><span class='ui-icon ui-icon-check'></span>"+data+"</div>"); });
|
||||
$('#ajaxMsg').addClass('success').fadeIn().delay(3000).fadeOut();
|
||||
});
|
||||
|
||||
// Never send checkbox values directly, always substitute value in hidden input.
|
||||
$('.checkboxes').click(function() {
|
||||
var configToggle = $(this).data('id');
|
||||
if ($(this).is(":checked")) {
|
||||
$("#"+configToggle).val(1);
|
||||
} else {
|
||||
$("#"+configToggle).val(0);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -34,14 +34,15 @@ available_notification_agents = notifiers.available_notification_agents()
|
|||
<div class="col-md-4">
|
||||
<ul class="nav-settings list-unstyled" role="tablist">
|
||||
<li role="presentation" class="active"><a href="#tabs-1" aria-controls="tabs-1" role="tab" data-toggle="tab">General</a></li>
|
||||
<li role="presentation"><a href="#tabs-2" aria-controls="tabs-2" role="tab" data-toggle="tab">Web Interface</a></li>
|
||||
<li role="presentation"><a href="#tabs-3" aria-controls="tabs-3" role="tab" data-toggle="tab">Access Control</a></li>
|
||||
<li role="presentation"><a href="#tabs-4" aria-controls="tabs-4" role="tab" data-toggle="tab">Plex Media Server</a></li>
|
||||
<li role="presentation"><a href="#tabs-5" aria-controls="tabs-5" role="tab" data-toggle="tab">Plex.tv Account</a></li>
|
||||
<li role="presentation"><a href="#tabs-6" aria-controls="tabs-6" role="tab" data-toggle="tab">Extra Settings</a></li>
|
||||
<li role="presentation"><a href="#tabs-7" aria-controls="tabs-7" role="tab" data-toggle="tab">Monitoring</a></li>
|
||||
<li role="presentation"><a href="#tabs-8" aria-controls="tabs-8" role="tab" data-toggle="tab">Notifications</a></li>
|
||||
<li role="presentation"><a href="#tabs-9" aria-controls="tabs-9" role="tab" data-toggle="tab">Notification Agents</a></li>
|
||||
<li role="presentation"><a href="#tabs-2" aria-controls="tabs-2" role="tab" data-toggle="tab">Homepage Statistics</a></li>
|
||||
<li role="presentation"><a href="#tabs-3" aria-controls="tabs-3" role="tab" data-toggle="tab">Web Interface</a></li>
|
||||
<li role="presentation"><a href="#tabs-4" aria-controls="tabs-4" role="tab" data-toggle="tab">Access Control</a></li>
|
||||
<li role="presentation"><a href="#tabs-5" aria-controls="tabs-5" role="tab" data-toggle="tab">Plex Media Server</a></li>
|
||||
<li role="presentation"><a href="#tabs-6" aria-controls="tabs-6" role="tab" data-toggle="tab">Plex.tv Account</a></li>
|
||||
<li role="presentation"><a href="#tabs-7" aria-controls="tabs-7" role="tab" data-toggle="tab">Extra Settings</a></li>
|
||||
<li role="presentation"><a href="#tabs-8" aria-controls="tabs-8" role="tab" data-toggle="tab">Monitoring</a></li>
|
||||
<li role="presentation"><a href="#tabs-9" aria-controls="tabs-9" role="tab" data-toggle="tab">Notifications</a></li>
|
||||
<li role="presentation"><a href="#tabs-10" aria-controls="tabs-10" role="tab" data-toggle="tab">Notification Agents</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
|
@ -83,10 +84,33 @@ available_notification_agents = notifiers.available_notification_agents()
|
|||
</div>
|
||||
<p class="help-block">Set your preferred time format. <a href="javascript:void(0)" data-target="#dateTimeOptionsModal" data-toggle="modal">Click here</a> to see the parameter list.</p>
|
||||
</div>
|
||||
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-2">
|
||||
<div class="padded-header">
|
||||
<h3>Homepage Statistics</h3>
|
||||
<h3>Watch Statistics</h3>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="home_stats_cards">Cards</label>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<select multiple class="form-control" id="home_stats_cards" name="home_stats_cards" data-parsley-trigger="change">
|
||||
<option id="card-watch_statistics" value="watch_statistics" class="hidden" selected>Watch Statistics</option>
|
||||
<option id="card-top_tv" value="top_tv">Most Watched TV</option>
|
||||
<option id="card-popular_tv" value="popular_tv">Most Popular TV</option>
|
||||
<option id="card-top_movies" value="top_movies">Most Watched Movie</option>
|
||||
<option id="card-popular_movies" value="popular_movies">Most Popular Movie</option>
|
||||
<option id="card-top_music" value="top_music">Most Listened Music</option>
|
||||
<option id="card-popular_music" value="popular_music">Most Popular Music</option>
|
||||
<option id="card-top_users" value="top_users">Most Active User</option>
|
||||
<option id="card-top_platforms" value="top_platforms">Most Active Platform</option>
|
||||
<option id="card-last_watched" value="last_watched">Last Watched</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<p class="help-block">Select the cards to show in the watch statistics on the home page. Select none to disable.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="home_stats_length">Time Frame</label>
|
||||
<div class="row">
|
||||
|
@ -95,7 +119,7 @@ available_notification_agents = notifiers.available_notification_agents()
|
|||
</div>
|
||||
<div id="home_stats_length_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||
</div>
|
||||
<p class="help-block">Specify the number of days for the statistics on the home page. Default is 30 days.</p>
|
||||
<p class="help-block">Specify the number of days for the watch statistics on the home page. Default is 30 days.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="home_stats_count">Top Lists</label>
|
||||
|
@ -105,7 +129,7 @@ available_notification_agents = notifiers.available_notification_agents()
|
|||
</div>
|
||||
<div id="home_stats_count_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||
</div>
|
||||
<p class="help-block">Specify the number of items to show in the top lists for the statistics on the home page. Max is 10 items, default is 5 items, 0 to disable.</p>
|
||||
<p class="help-block">Specify the number of items to show in the top lists for the watch statistics on the home page. Max is 10 items, default is 5 items, 0 to disable.</p>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
|
@ -113,9 +137,26 @@ available_notification_agents = notifiers.available_notification_agents()
|
|||
</label>
|
||||
<p class="help-block">Use play duration instead of play count to generate statistics.</p>
|
||||
</div>
|
||||
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>Library Statistics</h3>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-2">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="home_library_cards">Cards</label>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<select multiple class="form-control" id="home_library_cards" name="home_library_cards" data-parsley-trigger="change">
|
||||
<option id="card-library_statistics" value="library_statistics" class="hidden" selected>Library Statistics</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<p class="help-block">Select the cards to show in the library statistics on the home page. Select none to disable.</p>
|
||||
</div>
|
||||
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
|
||||
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-3">
|
||||
<div class="padded-header">
|
||||
<h3>Web Interface</h3>
|
||||
</div>
|
||||
|
@ -164,7 +205,7 @@ available_notification_agents = notifiers.available_notification_agents()
|
|||
|
||||
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-3">
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-4">
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>Authentication</h3>
|
||||
|
@ -216,7 +257,7 @@ available_notification_agents = notifiers.available_notification_agents()
|
|||
|
||||
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-4">
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-5">
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>Plex Media Server</h3>
|
||||
|
@ -272,7 +313,7 @@ available_notification_agents = notifiers.available_notification_agents()
|
|||
<input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully">
|
||||
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-5">
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-6">
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>Plex.tv Authentication</h3>
|
||||
|
@ -315,7 +356,7 @@ available_notification_agents = notifiers.available_notification_agents()
|
|||
|
||||
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-6">
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-7">
|
||||
<div class="padded-header">
|
||||
<h3>Extra Settings</h3>
|
||||
</div>
|
||||
|
@ -334,7 +375,7 @@ available_notification_agents = notifiers.available_notification_agents()
|
|||
|
||||
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-7">
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-8">
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>Monitoring Settings</h3>
|
||||
|
@ -367,7 +408,7 @@ available_notification_agents = notifiers.available_notification_agents()
|
|||
</div>
|
||||
<div id="logging_ignore_interval_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||
</div>
|
||||
<p class="help-block">The interval (in seconds) PlexPy will wait for a video item to be active before logging it. 0 to disable.</p>
|
||||
<p class="help-block">The interval (in seconds) an item must be in a playing state before logging it. 0 to disable.</p>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
|
@ -416,7 +457,7 @@ available_notification_agents = notifiers.available_notification_agents()
|
|||
|
||||
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-8">
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-9">
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>Global Notification Toggles</h3>
|
||||
|
@ -566,7 +607,7 @@ available_notification_agents = notifiers.available_notification_agents()
|
|||
|
||||
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-9">
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-10">
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>Notification Agents</h3>
|
||||
|
@ -1178,6 +1219,47 @@ $(document).ready(function() {
|
|||
}
|
||||
|
||||
var accordion = new Accordion($('#accordion'), false);
|
||||
|
||||
var cards = "${config['home_stats_cards']}".split(/[\s,]+/);
|
||||
cards.forEach(function (item) {
|
||||
$('#card-'+item).prop('selected', !$(this).prop('selected'));
|
||||
});
|
||||
$('#home_stats_cards').on('mousedown', function(e) {
|
||||
e.preventDefault();
|
||||
var scroll = this.scrollTop;
|
||||
e.target.selected = !e.target.selected;
|
||||
this.scrollTop = scroll;
|
||||
}).on('mousemove', function(e) {
|
||||
e.preventDefault()
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
url: 'get_server_children',
|
||||
data: { },
|
||||
async: true,
|
||||
complete: function (xhr, status) {
|
||||
server_children_info = $.parseJSON(xhr.responseText);
|
||||
libraries_list = server_children_info.libraries_list;
|
||||
for (var i in libraries_list) {
|
||||
title = libraries_list[i].title;
|
||||
key = libraries_list[i].key;
|
||||
$('#home_library_cards').append('<option id="card-' + key + '" value="' + key + '">' + title + '</option>')
|
||||
}
|
||||
var cards = "${config['home_library_cards']}".split(/[\s,]+/);
|
||||
cards.forEach(function (item) {
|
||||
$('#card-'+item).prop('selected', !$(this).prop('selected'));
|
||||
});
|
||||
}
|
||||
});
|
||||
$('#home_library_cards').on('mousedown', function(e) {
|
||||
e.preventDefault();
|
||||
var scroll = this.scrollTop;
|
||||
e.target.selected = !e.target.selected;
|
||||
this.scrollTop = scroll;
|
||||
}).on('mousemove', function(e) {
|
||||
e.preventDefault()
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
</%def>
|
||||
|
|
|
@ -17,11 +17,16 @@ import os
|
|||
import sys
|
||||
import subprocess
|
||||
import threading
|
||||
import webbrowser
|
||||
import sqlite3
|
||||
import cherrypy
|
||||
import datetime
|
||||
import uuid
|
||||
# Some cut down versions of Python may not include this module and it's not critical for us
|
||||
try:
|
||||
import webbrowser
|
||||
no_browser = False
|
||||
except ImportError:
|
||||
no_browser = True
|
||||
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from apscheduler.triggers.interval import IntervalTrigger
|
||||
|
@ -228,6 +233,7 @@ def daemonize():
|
|||
|
||||
|
||||
def launch_browser(host, port, root):
|
||||
if not no_browser:
|
||||
if host == '0.0.0.0':
|
||||
host = 'localhost'
|
||||
|
||||
|
|
|
@ -82,9 +82,11 @@ _CONFIG_DEFINITIONS = {
|
|||
'GROWL_ON_RESUME': (int, 'Growl', 0),
|
||||
'GROWL_ON_BUFFER': (int, 'Growl', 0),
|
||||
'GROWL_ON_WATCHED': (int, 'Growl', 0),
|
||||
'HOME_LIBRARY_CARDS': (str, 'General', 'library_statistics_first'),
|
||||
'HOME_STATS_LENGTH': (int, 'General', 30),
|
||||
'HOME_STATS_TYPE': (int, 'General', 0),
|
||||
'HOME_STATS_COUNT': (int, 'General', 5),
|
||||
'HOME_STATS_CARDS': (str, 'General', 'watch_statistics, top_tv, popular_tv, top_movies, popular_movies, top_music, popular_music, top_users, top_platforms, last_watched'),
|
||||
'HTTPS_CERT': (str, 'General', ''),
|
||||
'HTTPS_KEY': (str, 'General', ''),
|
||||
'HTTP_HOST': (str, 'General', '0.0.0.0'),
|
||||
|
@ -194,8 +196,14 @@ _CONFIG_DEFINITIONS = {
|
|||
'TV_NOTIFY_ON_PAUSE': (int, 'Monitoring', 0),
|
||||
'TWITTER_ENABLED': (int, 'Twitter', 0),
|
||||
'TWITTER_PASSWORD': (str, 'Twitter', ''),
|
||||
'TWITTER_PREFIX': (str, 'Twitter', 'Headphones'),
|
||||
'TWITTER_PREFIX': (str, 'Twitter', 'PlexPy'),
|
||||
'TWITTER_USERNAME': (str, 'Twitter', ''),
|
||||
'TWITTER_ON_PLAY': (int, 'Twitter', 0),
|
||||
'TWITTER_ON_STOP': (int, 'Twitter', 0),
|
||||
'TWITTER_ON_PAUSE': (int, 'Twitter', 0),
|
||||
'TWITTER_ON_RESUME': (int, 'Twitter', 0),
|
||||
'TWITTER_ON_BUFFER': (int, 'Twitter', 0),
|
||||
'TWITTER_ON_WATCHED': (int, 'Twitter', 0),
|
||||
'UPDATE_DB_INTERVAL': (int, 'General', 24),
|
||||
'VERIFY_SSL_CERT': (bool_int, 'Advanced', 1),
|
||||
'VIDEO_LOGGING_ENABLE': (int, 'Monitoring', 1),
|
||||
|
|
|
@ -131,22 +131,14 @@ class DataFactory(object):
|
|||
|
||||
return dict
|
||||
|
||||
def get_home_stats(self, time_range='30', stat_type='0', stat_count='5'):
|
||||
def get_home_stats(self, time_range='30', stats_type=0, stats_count='5', stats_cards='', notify_watched_percent='85'):
|
||||
monitor_db = database.MonitorDatabase()
|
||||
|
||||
if not time_range.isdigit():
|
||||
time_range = '30'
|
||||
sort_type = 'total_plays' if stats_type == 0 else 'total_duration'
|
||||
|
||||
sort_type = 'total_plays' if stat_type == '0' else 'total_duration'
|
||||
|
||||
if not time_range.isdigit():
|
||||
stat_count = '5'
|
||||
|
||||
# This actually determines the output order in the home page
|
||||
stats_queries = ["top_tv", "popular_tv", "top_movies", "popular_movies", "top_users", "top_platforms", "last_watched"]
|
||||
home_stats = []
|
||||
|
||||
for stat in stats_queries:
|
||||
for stat in stats_cards:
|
||||
if 'top_tv' in stat:
|
||||
top_tv = []
|
||||
try:
|
||||
|
@ -166,7 +158,7 @@ class DataFactory(object):
|
|||
'>= datetime("now", "-%s days", "localtime") ' \
|
||||
'AND session_history_metadata.media_type = "episode" ' \
|
||||
'GROUP BY session_history_metadata.grandparent_title ' \
|
||||
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stat_count)
|
||||
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stats_count)
|
||||
result = monitor_db.select(query)
|
||||
except:
|
||||
logger.warn("Unable to execute database query.")
|
||||
|
@ -202,6 +194,10 @@ class DataFactory(object):
|
|||
'session_history_metadata.grandparent_rating_key, ' \
|
||||
'MAX(session_history.started) as last_watch, ' \
|
||||
'COUNT(session_history.id) as total_plays, ' \
|
||||
'SUM(case when session_history.stopped > 0 ' \
|
||||
'then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when session_history.paused_counter is NULL then 0 else session_history.paused_counter end) ' \
|
||||
'else 0 end) as total_duration, ' \
|
||||
'session_history_metadata.grandparent_thumb ' \
|
||||
'FROM session_history_metadata ' \
|
||||
'JOIN session_history ON session_history_metadata.id = session_history.id ' \
|
||||
|
@ -209,8 +205,8 @@ class DataFactory(object):
|
|||
'>= datetime("now", "-%s days", "localtime") ' \
|
||||
'AND session_history_metadata.media_type = "episode" ' \
|
||||
'GROUP BY session_history_metadata.grandparent_title ' \
|
||||
'ORDER BY users_watched DESC, total_plays DESC ' \
|
||||
'LIMIT %s' % (time_range, stat_count)
|
||||
'ORDER BY users_watched DESC, %s DESC ' \
|
||||
'LIMIT %s' % (time_range, sort_type, stats_count)
|
||||
result = monitor_db.select(query)
|
||||
except:
|
||||
logger.warn("Unable to execute database query.")
|
||||
|
@ -222,7 +218,7 @@ class DataFactory(object):
|
|||
'rating_key': item[3],
|
||||
'last_play': item[4],
|
||||
'total_plays': item[5],
|
||||
'grandparent_thumb': item[6],
|
||||
'grandparent_thumb': item[7],
|
||||
'thumb': '',
|
||||
'user': '',
|
||||
'friendly_name': '',
|
||||
|
@ -254,7 +250,7 @@ class DataFactory(object):
|
|||
'>= datetime("now", "-%s days", "localtime") ' \
|
||||
'AND session_history_metadata.media_type = "movie" ' \
|
||||
'GROUP BY session_history_metadata.full_title ' \
|
||||
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stat_count)
|
||||
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stats_count)
|
||||
result = monitor_db.select(query)
|
||||
except:
|
||||
logger.warn("Unable to execute database query.")
|
||||
|
@ -290,6 +286,10 @@ class DataFactory(object):
|
|||
'session_history_metadata.rating_key, ' \
|
||||
'MAX(session_history.started) as last_watch, ' \
|
||||
'COUNT(session_history.id) as total_plays, ' \
|
||||
'SUM(case when session_history.stopped > 0 ' \
|
||||
'then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when session_history.paused_counter is NULL then 0 else session_history.paused_counter end) ' \
|
||||
'else 0 end) as total_duration, ' \
|
||||
'session_history_metadata.thumb ' \
|
||||
'FROM session_history_metadata ' \
|
||||
'JOIN session_history ON session_history_metadata.id = session_history.id ' \
|
||||
|
@ -297,8 +297,8 @@ class DataFactory(object):
|
|||
'>= datetime("now", "-%s days", "localtime") ' \
|
||||
'AND session_history_metadata.media_type = "movie" ' \
|
||||
'GROUP BY session_history_metadata.full_title ' \
|
||||
'ORDER BY users_watched DESC, total_plays DESC ' \
|
||||
'LIMIT %s' % (time_range, stat_count)
|
||||
'ORDER BY users_watched DESC, %s DESC ' \
|
||||
'LIMIT %s' % (time_range, sort_type, stats_count)
|
||||
result = monitor_db.select(query)
|
||||
except:
|
||||
logger.warn("Unable to execute database query.")
|
||||
|
@ -311,7 +311,7 @@ class DataFactory(object):
|
|||
'last_play': item[4],
|
||||
'total_plays': item[5],
|
||||
'grandparent_thumb': '',
|
||||
'thumb': item[6],
|
||||
'thumb': item[7],
|
||||
'user': '',
|
||||
'friendly_name': '',
|
||||
'platform_type': '',
|
||||
|
@ -323,6 +323,98 @@ class DataFactory(object):
|
|||
home_stats.append({'stat_id': stat,
|
||||
'rows': popular_movies})
|
||||
|
||||
elif 'top_music' in stat:
|
||||
top_music = []
|
||||
try:
|
||||
query = 'SELECT session_history_metadata.id, ' \
|
||||
'session_history_metadata.grandparent_title, ' \
|
||||
'COUNT(session_history_metadata.grandparent_title) as total_plays, ' \
|
||||
'SUM(case when session_history.stopped > 0 ' \
|
||||
'then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when session_history.paused_counter is NULL then 0 else session_history.paused_counter end) ' \
|
||||
'else 0 end) as total_duration, ' \
|
||||
'session_history_metadata.grandparent_rating_key, ' \
|
||||
'MAX(session_history.started) as last_watch,' \
|
||||
'session_history_metadata.grandparent_thumb ' \
|
||||
'FROM session_history_metadata ' \
|
||||
'JOIN session_history on session_history_metadata.id = session_history.id ' \
|
||||
'WHERE datetime(session_history.stopped, "unixepoch", "localtime") ' \
|
||||
'>= datetime("now", "-%s days", "localtime") ' \
|
||||
'AND session_history_metadata.media_type = "track" ' \
|
||||
'GROUP BY session_history_metadata.grandparent_title ' \
|
||||
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stats_count)
|
||||
result = monitor_db.select(query)
|
||||
except:
|
||||
logger.warn("Unable to execute database query.")
|
||||
return None
|
||||
|
||||
for item in result:
|
||||
row = {'title': item[1],
|
||||
'total_plays': item[2],
|
||||
'total_duration': item[3],
|
||||
'users_watched': '',
|
||||
'rating_key': item[4],
|
||||
'last_play': item[5],
|
||||
'grandparent_thumb': item[6],
|
||||
'thumb': '',
|
||||
'user': '',
|
||||
'friendly_name': '',
|
||||
'platform_type': '',
|
||||
'platform': '',
|
||||
'row_id': item[0]
|
||||
}
|
||||
top_music.append(row)
|
||||
|
||||
home_stats.append({'stat_id': stat,
|
||||
'stat_type': sort_type,
|
||||
'rows': top_music})
|
||||
|
||||
elif 'popular_music' in stat:
|
||||
popular_music = []
|
||||
try:
|
||||
query = 'SELECT session_history_metadata.id, ' \
|
||||
'session_history_metadata.grandparent_title, ' \
|
||||
'COUNT(DISTINCT session_history.user_id) as users_watched, ' \
|
||||
'session_history_metadata.grandparent_rating_key, ' \
|
||||
'MAX(session_history.started) as last_watch, ' \
|
||||
'COUNT(session_history.id) as total_plays, ' \
|
||||
'SUM(case when session_history.stopped > 0 ' \
|
||||
'then (session_history.stopped - session_history.started) ' \
|
||||
' - (case when session_history.paused_counter is NULL then 0 else session_history.paused_counter end) ' \
|
||||
'else 0 end) as total_duration, ' \
|
||||
'session_history_metadata.grandparent_thumb ' \
|
||||
'FROM session_history_metadata ' \
|
||||
'JOIN session_history ON session_history_metadata.id = session_history.id ' \
|
||||
'WHERE datetime(session_history.stopped, "unixepoch", "localtime") ' \
|
||||
'>= datetime("now", "-%s days", "localtime") ' \
|
||||
'AND session_history_metadata.media_type = "track" ' \
|
||||
'GROUP BY session_history_metadata.grandparent_title ' \
|
||||
'ORDER BY users_watched DESC, %s DESC ' \
|
||||
'LIMIT %s' % (time_range, sort_type, stats_count)
|
||||
result = monitor_db.select(query)
|
||||
except:
|
||||
logger.warn("Unable to execute database query.")
|
||||
return None
|
||||
|
||||
for item in result:
|
||||
row = {'title': item[1],
|
||||
'users_watched': item[2],
|
||||
'rating_key': item[3],
|
||||
'last_play': item[4],
|
||||
'total_plays': item[5],
|
||||
'grandparent_thumb': item[7],
|
||||
'thumb': '',
|
||||
'user': '',
|
||||
'friendly_name': '',
|
||||
'platform_type': '',
|
||||
'platform': '',
|
||||
'row_id': item[0]
|
||||
}
|
||||
popular_music.append(row)
|
||||
|
||||
home_stats.append({'stat_id': stat,
|
||||
'rows': popular_music})
|
||||
|
||||
elif 'top_users' in stat:
|
||||
top_users = []
|
||||
try:
|
||||
|
@ -343,7 +435,7 @@ class DataFactory(object):
|
|||
'WHERE datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-%s days", "localtime") '\
|
||||
'GROUP BY session_history.user_id ' \
|
||||
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stat_count)
|
||||
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stats_count)
|
||||
result = monitor_db.select(query)
|
||||
except:
|
||||
logger.warn("Unable to execute database query.")
|
||||
|
@ -391,7 +483,7 @@ class DataFactory(object):
|
|||
'WHERE datetime(session_history.stopped, "unixepoch", "localtime") ' \
|
||||
'>= datetime("now", "-%s days", "localtime") ' \
|
||||
'GROUP BY session_history.platform ' \
|
||||
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stat_count)
|
||||
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stats_count)
|
||||
result = monitor_db.select(query)
|
||||
except:
|
||||
logger.warn("Unable to execute database query.")
|
||||
|
@ -432,7 +524,11 @@ class DataFactory(object):
|
|||
'session_history_metadata.thumb, ' \
|
||||
'session_history_metadata.grandparent_thumb, ' \
|
||||
'MAX(session_history.started) as last_watch, ' \
|
||||
'session_history.player as platform ' \
|
||||
'session_history.player as platform, ' \
|
||||
'((CASE WHEN session_history.view_offset IS NULL THEN 0.1 ELSE \
|
||||
session_history.view_offset * 1.0 END) / \
|
||||
(CASE WHEN session_history_metadata.duration IS NULL THEN 1.0 ELSE \
|
||||
session_history_metadata.duration * 1.0 END) * 100) as percent_complete ' \
|
||||
'FROM session_history_metadata ' \
|
||||
'JOIN session_history ON session_history_metadata.id = session_history.id ' \
|
||||
'LEFT OUTER JOIN users ON session_history.user_id = users.user_id ' \
|
||||
|
@ -440,9 +536,10 @@ class DataFactory(object):
|
|||
'>= datetime("now", "-%s days", "localtime") ' \
|
||||
'AND (session_history_metadata.media_type = "movie" ' \
|
||||
'OR session_history_metadata.media_type = "episode") ' \
|
||||
'AND percent_complete >= %s ' \
|
||||
'GROUP BY session_history_metadata.full_title ' \
|
||||
'ORDER BY last_watch DESC ' \
|
||||
'LIMIT %s' % (time_range, stat_count)
|
||||
'LIMIT %s' % (time_range, notify_watched_percent, stats_count)
|
||||
result = monitor_db.select(query)
|
||||
except:
|
||||
logger.warn("Unable to execute database query.")
|
||||
|
|
|
@ -241,7 +241,7 @@ class MonitorProcessing(object):
|
|||
|
||||
if is_import:
|
||||
if str(session['stopped']).isdigit():
|
||||
stopped = session['stopped']
|
||||
stopped = int(session['stopped'])
|
||||
else:
|
||||
stopped = int(time.time())
|
||||
else:
|
||||
|
@ -257,21 +257,25 @@ class MonitorProcessing(object):
|
|||
logger.debug(u"PlexPy Monitor :: ratingKey %s not logged. Does not meet logging criteria. "
|
||||
u"Media type is '%s'" % (session['rating_key'], session['media_type']))
|
||||
|
||||
if str(session['paused_counter']).isdigit():
|
||||
real_play_time = stopped - session['started'] - int(session['paused_counter'])
|
||||
else:
|
||||
real_play_time = stopped - session['started']
|
||||
|
||||
if plexpy.CONFIG.LOGGING_IGNORE_INTERVAL and not is_import:
|
||||
if (session['media_type'] == 'movie' or session['media_type'] == 'episode') and \
|
||||
(int(stopped) - session['started'] < int(plexpy.CONFIG.LOGGING_IGNORE_INTERVAL)):
|
||||
(real_play_time < int(plexpy.CONFIG.LOGGING_IGNORE_INTERVAL)):
|
||||
logging_enabled = False
|
||||
logger.debug(u"PlexPy Monitor :: Play duration for ratingKey %s is %s secs which is less than %s "
|
||||
u"seconds, so we're not logging it." %
|
||||
(session['rating_key'], str(int(stopped) - session['started']),
|
||||
plexpy.CONFIG.LOGGING_IGNORE_INTERVAL))
|
||||
(session['rating_key'], str(real_play_time), plexpy.CONFIG.LOGGING_IGNORE_INTERVAL))
|
||||
elif is_import and import_ignore_interval:
|
||||
if (session['media_type'] == 'movie' or session['media_type'] == 'episode') and \
|
||||
(int(stopped) - session['started'] < int(import_ignore_interval)):
|
||||
(real_play_time < int(import_ignore_interval)):
|
||||
logging_enabled = False
|
||||
logger.debug(u"PlexPy Monitor :: Play duration for ratingKey %s is %s secs which is less than %s "
|
||||
u"seconds, so we're not logging it." %
|
||||
(session['rating_key'], str(int(stopped) - session['started']),
|
||||
(session['rating_key'], str(real_play_time),
|
||||
import_ignore_interval))
|
||||
|
||||
if not user_details['keep_history'] and not is_import:
|
||||
|
|
|
@ -49,7 +49,8 @@ AGENT_IDS = {"Growl": 0,
|
|||
"Pushover": 7,
|
||||
"OSX Notify": 8,
|
||||
"Boxcar2": 9,
|
||||
"Email": 10}
|
||||
"Email": 10,
|
||||
"Twitter": 11}
|
||||
|
||||
def available_notification_agents():
|
||||
agents = [{'name': 'Growl',
|
||||
|
@ -171,6 +172,18 @@ def available_notification_agents():
|
|||
'on_resume': plexpy.CONFIG.EMAIL_ON_RESUME,
|
||||
'on_buffer': plexpy.CONFIG.EMAIL_ON_BUFFER,
|
||||
'on_watched': plexpy.CONFIG.EMAIL_ON_WATCHED
|
||||
},
|
||||
{'name': 'Twitter',
|
||||
'id': AGENT_IDS['Twitter'],
|
||||
'config_prefix': 'twitter',
|
||||
'has_config': True,
|
||||
'state': checked(plexpy.CONFIG.TWITTER_ENABLED),
|
||||
'on_play': plexpy.CONFIG.TWITTER_ON_PLAY,
|
||||
'on_stop': plexpy.CONFIG.TWITTER_ON_STOP,
|
||||
'on_pause': plexpy.CONFIG.TWITTER_ON_PAUSE,
|
||||
'on_resume': plexpy.CONFIG.TWITTER_ON_RESUME,
|
||||
'on_buffer': plexpy.CONFIG.TWITTER_ON_BUFFER,
|
||||
'on_watched': plexpy.CONFIG.TWITTER_ON_WATCHED
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -229,6 +242,9 @@ def get_notification_agent_config(config_id):
|
|||
elif config_id == 10:
|
||||
email = Email()
|
||||
return email.return_config_options()
|
||||
elif config_id == 11:
|
||||
tweet = TwitterNotifier()
|
||||
return tweet.return_config_options()
|
||||
else:
|
||||
return []
|
||||
else:
|
||||
|
@ -271,6 +287,9 @@ def send_notification(config_id, subject, body):
|
|||
elif config_id == 10:
|
||||
email = Email()
|
||||
email.notify(subject=subject, message=body)
|
||||
elif config_id == 11:
|
||||
tweet = TwitterNotifier()
|
||||
tweet.notify(subject=subject, message=body)
|
||||
else:
|
||||
logger.debug(u"PlexPy Notifier :: Unknown agent id received.")
|
||||
else:
|
||||
|
@ -912,19 +931,17 @@ class TwitterNotifier(object):
|
|||
SIGNIN_URL = 'https://api.twitter.com/oauth/authenticate'
|
||||
|
||||
def __init__(self):
|
||||
self.consumer_key = "oYKnp2ddX5gbARjqX8ZAAg"
|
||||
self.consumer_secret = "A4Xkw9i5SjHbTk7XT8zzOPqivhj9MmRDR9Qn95YA9sk"
|
||||
self.consumer_key = "2LdJKXHDUwJtjYBsdwJisIOsh"
|
||||
self.consumer_secret = "QWbUcZzAIiL4zbDCIhy2EdUkV8yEEav3qMdo5y3FugxCFelWrA"
|
||||
|
||||
def notify_snatch(self, title):
|
||||
if plexpy.CONFIG.TWITTER_ONSNATCH:
|
||||
self._notifyTwitter(common.notifyStrings[common.NOTIFY_SNATCH] + ': ' + title + ' at ' + helpers.now())
|
||||
|
||||
def notify_download(self, title):
|
||||
if plexpy.CONFIG.TWITTER_ENABLED:
|
||||
self._notifyTwitter(common.notifyStrings[common.NOTIFY_DOWNLOAD] + ': ' + title + ' at ' + helpers.now())
|
||||
def notify(self, subject, message):
|
||||
if not subject or not message:
|
||||
return
|
||||
else:
|
||||
self._send_tweet(subject + ': ' + message)
|
||||
|
||||
def test_notify(self):
|
||||
return self._notifyTwitter("This is a test notification from PlexPy at " + helpers.now(), force=True)
|
||||
return self._send_tweet("This is a test notification from PlexPy at " + helpers.now())
|
||||
|
||||
def _get_authorization(self):
|
||||
|
||||
|
@ -958,7 +975,7 @@ class TwitterNotifier(object):
|
|||
logger.info('Generating and signing request for an access token using key ' + key)
|
||||
|
||||
oauth_consumer = oauth.Consumer(key=self.consumer_key, secret=self.consumer_secret)
|
||||
logger.info('oauth_consumer: ' + str(oauth_consumer))
|
||||
# logger.debug('oauth_consumer: ' + str(oauth_consumer))
|
||||
oauth_client = oauth.Client(oauth_consumer, token)
|
||||
logger.info('oauth_client: ' + str(oauth_client))
|
||||
resp, content = oauth_client.request(self.ACCESS_TOKEN_URL, method='POST', body='oauth_verifier=%s' % key)
|
||||
|
@ -979,7 +996,6 @@ class TwitterNotifier(object):
|
|||
return True
|
||||
|
||||
def _send_tweet(self, message=None):
|
||||
|
||||
username = self.consumer_key
|
||||
password = self.consumer_secret
|
||||
access_token_key = plexpy.CONFIG.TWITTER_USERNAME
|
||||
|
@ -997,13 +1013,36 @@ class TwitterNotifier(object):
|
|||
|
||||
return True
|
||||
|
||||
def _notifyTwitter(self, message='', force=False):
|
||||
prefix = plexpy.CONFIG.TWITTER_PREFIX
|
||||
def return_config_options(self):
|
||||
config_option = [{'label': 'Request Authorisation',
|
||||
'value': 'Request Authorisation',
|
||||
'name': 'twitterStep1',
|
||||
'description': 'Step 1: Click Request button above. (Ensure you allow the browser pop-up).',
|
||||
'input_type': 'button'
|
||||
},
|
||||
{'label': 'Authorisation Key',
|
||||
'value': '',
|
||||
'name': 'twitter_key',
|
||||
'description': 'Step 2: Input the authorisation key you received from Step 1.',
|
||||
'input_type': 'text'
|
||||
},
|
||||
{'label': 'Verify Key',
|
||||
'value': 'Verify Key',
|
||||
'name': 'twitterStep2',
|
||||
'description': 'Step 3: Verify the key.',
|
||||
'input_type': 'button'
|
||||
},
|
||||
{'label': 'Test Twitter',
|
||||
'value': 'Test Twitter',
|
||||
'name': 'testTwitter',
|
||||
'description': 'Test if Twitter notifications are working. See logs for troubleshooting.',
|
||||
'input_type': 'button'
|
||||
},
|
||||
{'input_type': 'nosave'
|
||||
}
|
||||
]
|
||||
|
||||
if not plexpy.CONFIG.TWITTER_ENABLED and not force:
|
||||
return False
|
||||
|
||||
return self._send_tweet(prefix + ": " + message)
|
||||
return config_option
|
||||
|
||||
class OSX_NOTIFY(object):
|
||||
|
||||
|
@ -1204,7 +1243,7 @@ class Email(object):
|
|||
'input_type': 'password'
|
||||
},
|
||||
{'label': 'TLS',
|
||||
'value': checked(plexpy.CONFIG.EMAIL_TLS),
|
||||
'value': plexpy.CONFIG.EMAIL_TLS,
|
||||
'name': 'email_tls',
|
||||
'description': 'Does the server use encryption.',
|
||||
'input_type': 'checkbox'
|
||||
|
|
|
@ -1270,11 +1270,11 @@ class PmsConnect(object):
|
|||
return output
|
||||
|
||||
"""
|
||||
Return processed and validated server statistics.
|
||||
Return processed and validated library statistics.
|
||||
|
||||
Output: array
|
||||
"""
|
||||
def get_library_stats(self):
|
||||
def get_library_stats(self, library_cards=''):
|
||||
server_libraries = self.get_server_children()
|
||||
|
||||
server_library_stats = []
|
||||
|
@ -1285,7 +1285,10 @@ class PmsConnect(object):
|
|||
for library in libraries_list:
|
||||
library_type = library['type']
|
||||
section_key = library['key']
|
||||
if section_key in library_cards:
|
||||
library_list = self.get_library_children(library_type, section_key)
|
||||
else:
|
||||
continue
|
||||
|
||||
if library_list['library_count'] != '0':
|
||||
library_stats = {'title': library['title'],
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
PLEXPY_VERSION = "master"
|
||||
PLEXPY_RELEASE_VERSION = "1.1.9"
|
||||
PLEXPY_RELEASE_VERSION = "1.1.10"
|
||||
|
|
|
@ -66,9 +66,9 @@ class WebInterface(object):
|
|||
def home(self):
|
||||
config = {
|
||||
"home_stats_length": plexpy.CONFIG.HOME_STATS_LENGTH,
|
||||
"home_stats_type": plexpy.CONFIG.HOME_STATS_TYPE,
|
||||
"home_stats_count": plexpy.CONFIG.HOME_STATS_COUNT,
|
||||
"pms_identifier": plexpy.CONFIG.PMS_IDENTIFIER,
|
||||
"home_stats_cards": plexpy.CONFIG.HOME_STATS_CARDS,
|
||||
"home_library_cards": plexpy.CONFIG.HOME_LIBRARY_CARDS,
|
||||
"pms_identifier": plexpy.CONFIG.PMS_IDENTIFIER
|
||||
}
|
||||
return serve_template(templatename="index.html", title="Home", config=config)
|
||||
|
||||
|
@ -121,16 +121,41 @@ class WebInterface(object):
|
|||
return json.dumps(formats)
|
||||
|
||||
@cherrypy.expose
|
||||
def home_stats(self, time_range='30', stat_type='0', stat_count='5', **kwargs):
|
||||
def home_stats(self, **kwargs):
|
||||
data_factory = datafactory.DataFactory()
|
||||
stats_data = data_factory.get_home_stats(time_range=time_range, stat_type=stat_type, stat_count=stat_count)
|
||||
|
||||
time_range = plexpy.CONFIG.HOME_STATS_LENGTH
|
||||
stats_type = plexpy.CONFIG.HOME_STATS_TYPE
|
||||
stats_count = plexpy.CONFIG.HOME_STATS_COUNT
|
||||
stats_cards = plexpy.CONFIG.HOME_STATS_CARDS.split(', ')
|
||||
notify_watched_percent = plexpy.CONFIG.NOTIFY_WATCHED_PERCENT
|
||||
|
||||
stats_data = data_factory.get_home_stats(time_range=time_range,
|
||||
stats_type=stats_type,
|
||||
stats_count=stats_count,
|
||||
stats_cards=stats_cards,
|
||||
notify_watched_percent=notify_watched_percent)
|
||||
|
||||
return serve_template(templatename="home_stats.html", title="Stats", data=stats_data)
|
||||
|
||||
@cherrypy.expose
|
||||
def library_stats(self, **kwargs):
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
stats_data = pms_connect.get_library_stats()
|
||||
|
||||
library_cards = plexpy.CONFIG.HOME_LIBRARY_CARDS.split(', ')
|
||||
|
||||
if library_cards == ['library_statistics_first']:
|
||||
library_cards = ['library_statistics']
|
||||
server_children = pms_connect.get_server_children()
|
||||
server_libraries = server_children['libraries_list']
|
||||
|
||||
for library in server_libraries:
|
||||
library_cards.append(library['key'])
|
||||
|
||||
plexpy.CONFIG.HOME_LIBRARY_CARDS = ', '.join(library_cards)
|
||||
plexpy.CONFIG.write()
|
||||
|
||||
stats_data = pms_connect.get_library_stats(library_cards=library_cards)
|
||||
|
||||
return serve_template(templatename="library_stats.html", title="Library Stats", data=stats_data)
|
||||
|
||||
|
@ -144,7 +169,12 @@ class WebInterface(object):
|
|||
|
||||
@cherrypy.expose
|
||||
def graphs(self):
|
||||
return serve_template(templatename="graphs.html", title="Graphs")
|
||||
|
||||
config = {
|
||||
"music_logging_enable": plexpy.CONFIG.MUSIC_LOGGING_ENABLE
|
||||
}
|
||||
|
||||
return serve_template(templatename="graphs.html", title="Graphs", config=config)
|
||||
|
||||
@cherrypy.expose
|
||||
def sync(self):
|
||||
|
@ -373,46 +403,7 @@ class WebInterface(object):
|
|||
"cache_dir": plexpy.CONFIG.CACHE_DIR,
|
||||
"check_github": checked(plexpy.CONFIG.CHECK_GITHUB),
|
||||
"interface_list": interface_list,
|
||||
"growl_enabled": checked(plexpy.CONFIG.GROWL_ENABLED),
|
||||
"growl_host": plexpy.CONFIG.GROWL_HOST,
|
||||
"growl_password": plexpy.CONFIG.GROWL_PASSWORD,
|
||||
"prowl_enabled": checked(plexpy.CONFIG.PROWL_ENABLED),
|
||||
"prowl_keys": plexpy.CONFIG.PROWL_KEYS,
|
||||
"prowl_priority": plexpy.CONFIG.PROWL_PRIORITY,
|
||||
"xbmc_enabled": checked(plexpy.CONFIG.XBMC_ENABLED),
|
||||
"xbmc_host": plexpy.CONFIG.XBMC_HOST,
|
||||
"xbmc_username": plexpy.CONFIG.XBMC_USERNAME,
|
||||
"xbmc_password": plexpy.CONFIG.XBMC_PASSWORD,
|
||||
"plex_enabled": checked(plexpy.CONFIG.PLEX_ENABLED),
|
||||
"plex_client_host": plexpy.CONFIG.PLEX_CLIENT_HOST,
|
||||
"plex_username": plexpy.CONFIG.PLEX_USERNAME,
|
||||
"plex_password": plexpy.CONFIG.PLEX_PASSWORD,
|
||||
"nma_enabled": checked(plexpy.CONFIG.NMA_ENABLED),
|
||||
"nma_apikey": plexpy.CONFIG.NMA_APIKEY,
|
||||
"nma_priority": int(plexpy.CONFIG.NMA_PRIORITY),
|
||||
"pushalot_enabled": checked(plexpy.CONFIG.PUSHALOT_ENABLED),
|
||||
"pushalot_apikey": plexpy.CONFIG.PUSHALOT_APIKEY,
|
||||
"pushover_enabled": checked(plexpy.CONFIG.PUSHOVER_ENABLED),
|
||||
"pushover_keys": plexpy.CONFIG.PUSHOVER_KEYS,
|
||||
"pushover_apitoken": plexpy.CONFIG.PUSHOVER_APITOKEN,
|
||||
"pushover_priority": plexpy.CONFIG.PUSHOVER_PRIORITY,
|
||||
"pushbullet_enabled": checked(plexpy.CONFIG.PUSHBULLET_ENABLED),
|
||||
"pushbullet_apikey": plexpy.CONFIG.PUSHBULLET_APIKEY,
|
||||
"pushbullet_deviceid": plexpy.CONFIG.PUSHBULLET_DEVICEID,
|
||||
"twitter_enabled": checked(plexpy.CONFIG.TWITTER_ENABLED),
|
||||
"osx_notify_enabled": checked(plexpy.CONFIG.OSX_NOTIFY_ENABLED),
|
||||
"osx_notify_app": plexpy.CONFIG.OSX_NOTIFY_APP,
|
||||
"boxcar_enabled": checked(plexpy.CONFIG.BOXCAR_ENABLED),
|
||||
"boxcar_token": plexpy.CONFIG.BOXCAR_TOKEN,
|
||||
"cache_sizemb": plexpy.CONFIG.CACHE_SIZEMB,
|
||||
"email_enabled": checked(plexpy.CONFIG.EMAIL_ENABLED),
|
||||
"email_from": plexpy.CONFIG.EMAIL_FROM,
|
||||
"email_to": plexpy.CONFIG.EMAIL_TO,
|
||||
"email_smtp_server": plexpy.CONFIG.EMAIL_SMTP_SERVER,
|
||||
"email_smtp_user": plexpy.CONFIG.EMAIL_SMTP_USER,
|
||||
"email_smtp_password": plexpy.CONFIG.EMAIL_SMTP_PASSWORD,
|
||||
"email_smtp_port": int(plexpy.CONFIG.EMAIL_SMTP_PORT),
|
||||
"email_tls": checked(plexpy.CONFIG.EMAIL_TLS),
|
||||
"pms_identifier": plexpy.CONFIG.PMS_IDENTIFIER,
|
||||
"pms_ip": plexpy.CONFIG.PMS_IP,
|
||||
"pms_logs_folder": plexpy.CONFIG.PMS_LOGS_FOLDER,
|
||||
|
@ -421,7 +412,6 @@ class WebInterface(object):
|
|||
"pms_ssl": checked(plexpy.CONFIG.PMS_SSL),
|
||||
"pms_use_bif": checked(plexpy.CONFIG.PMS_USE_BIF),
|
||||
"pms_uuid": plexpy.CONFIG.PMS_UUID,
|
||||
"plexwatch_database": plexpy.CONFIG.PLEXWATCH_DATABASE,
|
||||
"date_format": plexpy.CONFIG.DATE_FORMAT,
|
||||
"time_format": plexpy.CONFIG.TIME_FORMAT,
|
||||
"grouping_global_history": checked(plexpy.CONFIG.GROUPING_GLOBAL_HISTORY),
|
||||
|
@ -463,6 +453,8 @@ class WebInterface(object):
|
|||
"home_stats_length": plexpy.CONFIG.HOME_STATS_LENGTH,
|
||||
"home_stats_type": checked(plexpy.CONFIG.HOME_STATS_TYPE),
|
||||
"home_stats_count": plexpy.CONFIG.HOME_STATS_COUNT,
|
||||
"home_stats_cards": plexpy.CONFIG.HOME_STATS_CARDS,
|
||||
"home_library_cards": plexpy.CONFIG.HOME_LIBRARY_CARDS,
|
||||
"buffer_threshold": plexpy.CONFIG.BUFFER_THRESHOLD,
|
||||
"buffer_wait": plexpy.CONFIG.BUFFER_WAIT
|
||||
}
|
||||
|
@ -474,12 +466,7 @@ class WebInterface(object):
|
|||
# Handle the variable config options. Note - keys with False values aren't getting passed
|
||||
|
||||
checked_configs = [
|
||||
"launch_browser", "enable_https", "api_enabled", "freeze_db", "growl_enabled",
|
||||
"prowl_enabled", "xbmc_enabled", "check_github",
|
||||
"plex_enabled", "nma_enabled", "pushalot_enabled",
|
||||
"pushover_enabled", "pushbullet_enabled",
|
||||
"twitter_enabled", "osx_notify_enabled",
|
||||
"boxcar_enabled", "email_enabled", "email_tls",
|
||||
"launch_browser", "enable_https", "api_enabled", "freeze_db", "check_github",
|
||||
"grouping_global_history", "grouping_user_history", "grouping_charts", "pms_use_bif", "pms_ssl",
|
||||
"tv_notify_enable", "movie_notify_enable", "music_notify_enable",
|
||||
"tv_notify_on_start", "movie_notify_on_start", "music_notify_on_start",
|
||||
|
@ -515,6 +502,14 @@ class WebInterface(object):
|
|||
if kwargs['pms_ip'] != plexpy.CONFIG.PMS_IP:
|
||||
refresh_users = True
|
||||
|
||||
if 'home_stats_cards' in kwargs:
|
||||
if kwargs['home_stats_cards'] != 'watch_statistics':
|
||||
kwargs['home_stats_cards'] = ', '.join(kwargs['home_stats_cards'])
|
||||
|
||||
if 'home_library_cards' in kwargs:
|
||||
if kwargs['home_library_cards'] != 'library_statistics':
|
||||
kwargs['home_library_cards'] = ', '.join(kwargs['home_library_cards'])
|
||||
|
||||
plexpy.CONFIG.process_kwargs(kwargs)
|
||||
|
||||
# Write the config
|
||||
|
@ -535,15 +530,6 @@ class WebInterface(object):
|
|||
|
||||
@cherrypy.expose
|
||||
def set_notification_config(self, **kwargs):
|
||||
# Handle the variable config options. Note - keys with False values aren't getting passed
|
||||
|
||||
checked_configs = [
|
||||
"email_tls"
|
||||
]
|
||||
for checked_config in checked_configs:
|
||||
if checked_config not in kwargs:
|
||||
# checked items should be zero or one. if they were not sent then the item was not checked
|
||||
kwargs[checked_config] = 0
|
||||
|
||||
for plain_config, use_config in [(x[4:], x) for x in kwargs if x.startswith('use_')]:
|
||||
# the use prefix is fairly nice in the html, but does not match the actual config
|
||||
|
@ -1112,6 +1098,18 @@ class WebInterface(object):
|
|||
else:
|
||||
logger.warn('Unable to retrieve data.')
|
||||
|
||||
@cherrypy.expose
|
||||
def get_server_children(self, **kwargs):
|
||||
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
result = pms_connect.get_server_children()
|
||||
|
||||
if result:
|
||||
cherrypy.response.headers['Content-type'] = 'application/json'
|
||||
return json.dumps(result)
|
||||
else:
|
||||
logger.warn('Unable to retrieve data.')
|
||||
|
||||
@cherrypy.expose
|
||||
def get_activity(self, **kwargs):
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue