Compare commits

..

23 Commits

Author SHA1 Message Date
JonnyWong16
644fea6665 v2.0.12-beta 2018-01-07 23:44:18 -08:00
JonnyWong16
a1349ff8a6 Add css for selectize to match input boxes 2018-01-07 23:37:18 -08:00
JonnyWong16
71c20002b8 Update build notify text error message 2018-01-07 18:45:55 -08:00
JonnyWong16
157af84226 Only update the database sessions every 60 seconds while playing 2018-01-07 17:10:33 -08:00
JonnyWong16
9b4536f132 Move webserver notify to API 2018-01-07 14:46:01 -08:00
JonnyWong16
29ab470e42 Make metadata cache an advanced config option 2018-01-07 10:01:17 -08:00
JonnyWong16
c67fa480a7 Make condition logic optional
* Implicit "and" between all conditions if logic is blank
2018-01-07 09:42:57 -08:00
JonnyWong16
0a1a691c73 Fix Plex URL notification parameter 2018-01-07 08:28:06 -08:00
JonnyWong16
48588f23bf Add LAN/WAN bandwidth to activity header 2018-01-06 23:06:21 -08:00
JonnyWong16
cf14fbc3f0 v2.0.11-beta 2018-01-05 21:50:35 -08:00
JonnyWong16
e471d5207d Remove experimental tag from calculate file sizes 2018-01-05 21:50:24 -08:00
JonnyWong16
5722a52082 Fix None values in stream data for pre v2 history 2018-01-05 21:37:54 -08:00
JonnyWong16
08c32e875e Fix login using hashed password 2018-01-05 21:01:32 -08:00
JonnyWong16
7d3ee3afb3 Fix recently added show title 2018-01-05 21:01:10 -08:00
JonnyWong16
def8600f5c Reload notify params from raw stream info 2018-01-05 14:22:20 -08:00
JonnyWong16
74a68f3c7d v2.0.10-beta 2018-01-04 19:55:13 -08:00
JonnyWong16
64c9247dd1 Remove library/user notification toggles
* Filter out notifications using custom conditions
2018-01-04 19:39:16 -08:00
JonnyWong16
1bfcd34247 Some formatting for common.py 2018-01-04 13:40:34 -08:00
JonnyWong16
19864e97e6 Fix media type in collection header 2018-01-04 13:40:34 -08:00
JonnyWong16
ec5c5e1420 Merge pull request #1195 from Tommatheussen/patch-1
Update date formats
2018-01-04 13:39:25 -08:00
Tom Matheussen
803f4e14ca Added some additional formats 2018-01-04 22:18:40 +01:00
Tom Matheussen
6cc254b80a Update Date Formats
Added correct Year date formats, replaced generic numeric values with actual examples
2018-01-04 21:32:59 +01:00
JonnyWong16
59593ab1aa Fix HW indicator on activity refresh 2018-01-03 20:29:52 -08:00
25 changed files with 451 additions and 307 deletions

View File

@@ -1,5 +1,36 @@
# Changelog # Changelog
## v2.0.12-beta (2018-01-07)
* Notifications:
* Fix: Incorrect Plex URL parameter value.
* Change: Custom condition logic is now optional. An implicit "and" is applied between all conditions if the logic is blank.
* UI:
* New: Added separate required LAN/WAN bandwidth in the activity header.
* API:
* Fix: Notify API command not sending notifications.
## v2.0.11-beta (2018-01-05)
* Notifications:
* Fix: Some notification parameters showing up blank.
* UI:
* Fix: Stream data showing up as "None" for pre-v2 history.
* Other:
* Fix: Ability to login using the hashed password.
## v2.0.10-beta (2018-01-04)
* Monitoring:
* Fix: HW transcoding indicator on activity cards incorrect after refreshing.
* Notifications:
* Remove: Notification toggles from library and user settings. Use custom conditions to filter out notifications instead.
* UI:
* Fix: Incorrect examples for some date format options. Also added a few missing date format options. (Thanks @Tommatheussen)
## v2.0.9-beta (2018-01-03) ## v2.0.9-beta (2018-01-03)
* Notifications: * Notifications:
@@ -9,8 +40,8 @@
## v2.0.8-beta (2018-01-03) ## v2.0.8-beta (2018-01-03)
* Monitoring: * Monitoring:
* Fix: Fix HW transcoding indicator on activity cards. * Fix: Incorrect HW transcoding indicator on activity cards.
* Fix: Fix long product/player names hidden behind platform icon on activity cards. * Fix: Long product/player names hidden behind platform icon on activity cards.
* Notifications: * Notifications:
* Fix: Notifications failing due to some missing notification parameters. * Fix: Notifications failing due to some missing notification parameters.

View File

@@ -71,11 +71,13 @@ select.form-control {
border-radius: 3px; border-radius: 3px;
transition: background-color .3s; transition: background-color .3s;
} }
.react-selectize.root-node .react-selectize-control { .react-selectize.root-node .react-selectize-control,
.selectize-control.form-control .selectize-input {
color: #fff !important; color: #fff !important;
border: 0px solid #444 !important; border: 0px solid #444 !important;
background: #555 !important; background: #555 !important;
padding: 1px 2px; padding: 1px 2px;
transition: background-color .3s;
} }
.react-selectize.root-node .react-selectize-control .react-selectize-placeholder { .react-selectize.root-node .react-selectize-control .react-selectize-placeholder {
color: #fff !important; color: #fff !important;
@@ -83,6 +85,13 @@ select.form-control {
.react-selectize.root-node .react-selectize-control .react-selectize-toggle-button path { .react-selectize.root-node .react-selectize-control .react-selectize-toggle-button path {
fill: #fff !important; fill: #fff !important;
} }
.react-selectize.root-node .simple-value,
.selectize-control.multi .selectize-input > div {
background: #444444 !important;
color: #ffffff !important;
padding-bottom: 2px !important;
transition: background-color .3s;
}
.react-selectize.root-node .simple-value span { .react-selectize.root-node .simple-value span {
padding-bottom: 2px !important; padding-bottom: 2px !important;
} }
@@ -90,13 +99,25 @@ select.form-control {
padding-top: 3px !important; padding-top: 3px !important;
padding-bottom: 3px !important; padding-bottom: 3px !important;
} }
select.form-control:focus { select.form-control:focus,
.react-selectize.root-node.open .react-selectize-control,
.selectize-control.form-control .selectize-input.focus {
outline: 0; outline: 0;
outline: thin dotted \9; outline: thin dotted \9;
color: #555; color: #555 !important;
background-color: #fff; background-color: #fff !important;
transition: background-color .3s; transition: background-color .3s;
} }
.react-selectize.root-node.open .simple-value,
.selectize-control.multi .selectize-input.focus > div,
.selectize-control.multi .selectize-input > div.active{
background: #efefef !important;
color: #333333 !important;
transition: background-color .3s;
}
.react-selectize.root-node.open .react-selectize-control .react-selectize-toggle-button path {
fill: #999 !important;
}
select.form-control option { select.form-control option {
color: #555; color: #555;
background-color: #fff; background-color: #fff;
@@ -3722,7 +3743,11 @@ a:hover .overlay-refresh-image:hover {
.no-image { .no-image {
background-image: none !important; background-image: none !important;
} }
#info-modal .stream-info-current {
color: #aaa;
text-align: center;
padding-bottom: 10px;
}
#info-modal .stream-info-item { #info-modal .stream-info-item {
display: flex; display: flex;
flex-direction: row; flex-direction: row;

View File

@@ -47,24 +47,12 @@ DOCUMENTATION :: END
</div> </div>
<p class="help-block">Change the library's picture in Tautulli. To reset to default, leave this field empty and save.</p> <p class="help-block">Change the library's picture in Tautulli. To reset to default, leave this field empty and save.</p>
</div> </div>
<div class="checkbox">
<label>
<input type="checkbox" id="do_notify" name="do_notify" value="1" ${helpers.checked(data['do_notify'])}> Enable notifications
</label>
<p class="help-block">Uncheck this if you do not want to receive notifications for this library's activity.</p>
</div>
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" id="keep_history" name="keep_history" value="1" ${helpers.checked(data['keep_history'])}> Keep history <input type="checkbox" id="keep_history" name="keep_history" value="1" ${helpers.checked(data['keep_history'])}> Keep history
</label> </label>
<p class="help-block">Uncheck this if you do not want to keep any history on this library's activity.</p> <p class="help-block">Uncheck this if you do not want to keep any history on this library's activity.</p>
</div> </div>
<div class="checkbox">
<label>
<input type="checkbox" id="do_notify_created" name="do_notify_created" value="1" ${helpers.checked(data['do_notify_created'])}> Enable recently added notifications
</label>
<p class="help-block">Uncheck this if you do not want to receive recently added notifications for this library.</p>
</div>
% if data['section_id']: % if data['section_id']:
<div class="form-group"> <div class="form-group">
<button class="btn btn-danger" id="delete-all-history">Purge</button> <button class="btn btn-danger" id="delete-all-history">Purge</button>
@@ -85,15 +73,7 @@ DOCUMENTATION :: END
// Save library options // Save library options
$("#save_library").on('click', function () { $("#save_library").on('click', function () {
var custom_thumb = $("#custom_thumb_url").val(); var custom_thumb = $("#custom_thumb_url").val();
var do_notify = 0;
var do_notify_created = 0;
var keep_history = 0; var keep_history = 0;
if ($("#do_notify").is(":checked")) {
do_notify = 1;
}
if ($("#do_notify_created").is(":checked")) {
do_notify_created = 1;
}
if ($("#keep_history").is(":checked")) { if ($("#keep_history").is(":checked")) {
keep_history = 1; keep_history = 1;
} }
@@ -103,8 +83,6 @@ DOCUMENTATION :: END
data: { data: {
section_id: '${data["section_id"]}', section_id: '${data["section_id"]}',
custom_thumb: custom_thumb, custom_thumb: custom_thumb,
do_notify: do_notify,
do_notify_created: do_notify_created,
keep_history: keep_history keep_history: keep_history
}, },
cache: false, cache: false,

View File

@@ -56,12 +56,6 @@ DOCUMENTATION :: END
</div> </div>
<p class="help-block">Change the users profile picture in Tautulli. To reset to default, leave this field empty and save.</p> <p class="help-block">Change the users profile picture in Tautulli. To reset to default, leave this field empty and save.</p>
</div> </div>
<div class="checkbox">
<label>
<input type="checkbox" id="do_notify" name="do_notify" value="1" ${helpers.checked(data['do_notify'])}> Enable notifications
</label>
<p class="help-block">Uncheck this if you do not want to receive notifications for this user's activity.</p>
</div>
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" id="keep_history" name="keep_history" value="1" ${helpers.checked(data['keep_history'])}> Keep history <input type="checkbox" id="keep_history" name="keep_history" value="1" ${helpers.checked(data['keep_history'])}> Keep history
@@ -95,12 +89,8 @@ DOCUMENTATION :: END
$("#save_user").on('click', function () { $("#save_user").on('click', function () {
var friendly_name = $("input#friendly_name").val(); var friendly_name = $("input#friendly_name").val();
var custom_thumb = $("#custom_avatar_url").val(); var custom_thumb = $("#custom_avatar_url").val();
var do_notify = 0;
var keep_history = 0; var keep_history = 0;
var allow_guest = 0; var allow_guest = 0;
if ($("#do_notify").is(":checked")) {
do_notify = 1;
}
if ($("#keep_history").is(":checked")) { if ($("#keep_history").is(":checked")) {
keep_history = 1; keep_history = 1;
} }
@@ -114,7 +104,6 @@ DOCUMENTATION :: END
user_id: '${data["user_id"]}', user_id: '${data["user_id"]}',
friendly_name: friendly_name, friendly_name: friendly_name,
custom_thumb: custom_thumb, custom_thumb: custom_thumb,
do_notify: do_notify,
keep_history: keep_history, keep_history: keep_history,
allow_guest: allow_guest allow_guest: allow_guest
}, },

View File

@@ -292,7 +292,9 @@
var sc_dp = current_activity.stream_count_direct_play, var sc_dp = current_activity.stream_count_direct_play,
sc_ds = current_activity.stream_count_direct_stream, sc_ds = current_activity.stream_count_direct_stream,
sc_tc = current_activity.stream_count_transcode, sc_tc = current_activity.stream_count_transcode,
total_bw = current_activity.total_bandwidth; total_bw = current_activity.total_bandwidth,
lan_bw = current_activity.lan_bandwidth,
wan_bw = current_activity.wan_bandwidth;
var streams_header = stream_count + ' stream' + (stream_count > 1 ? 's' : '') + ' ('; var streams_header = stream_count + ' stream' + (stream_count > 1 ? 's' : '') + ' (';
if (sc_dp) { if (sc_dp) {
streams_header += sc_dp + ' direct play' + (sc_dp > 1 ? 's' : '') + ', '; streams_header += sc_dp + ' direct play' + (sc_dp > 1 ? 's' : '') + ', ';
@@ -306,7 +308,14 @@
streams_header = streams_header.replace(/, $/, '') + ')'; streams_header = streams_header.replace(/, $/, '') + ')';
$('#currentActivityHeader-streams').text(streams_header); $('#currentActivityHeader-streams').text(streams_header);
var bandwidth_header = (total_bw > 1000) ? ((total_bw / 1000).toFixed(1) + ' Mbps') : (total_bw + ' kbps'); var bandwidth_header = ((total_bw > 1000) ? ((total_bw / 1000).toFixed(1) + ' Mbps') : (total_bw + ' kbps')) + ' (';
if (lan_bw) {
bandwidth_header += 'LAN: ' + ((lan_bw > 1000) ? ((lan_bw / 1000).toFixed(1) + ' Mbps') : (lan_bw + ' kbps')) + ', ';
}
if (wan_bw) {
bandwidth_header += 'WAN: ' + ((wan_bw > 1000) ? ((wan_bw / 1000).toFixed(1) + ' Mbps') : (wan_bw + ' kbps')) + ', ';
}
bandwidth_header = bandwidth_header.replace(/, $/, '') + ')';
$('#currentActivityHeader-bandwidth').text(bandwidth_header); $('#currentActivityHeader-bandwidth').text(bandwidth_header);
$('#currentActivityHeader').show(); $('#currentActivityHeader').show();
@@ -325,25 +334,26 @@
} }
// Update play state icon // Update play state icon
var state_icon = '';
switch (s.state) { switch (s.state) {
case 'playing': case 'playing':
var state_icon = '<i class="fa fa-fw fa-play"></i>&nbsp;'; state_icon = '<i class="fa fa-fw fa-play"></i>&nbsp;';
break; break;
case 'paused': case 'paused':
var state_icon = '<i class="fa fa-fw fa-pause"></i>&nbsp;'; state_icon = '<i class="fa fa-fw fa-pause"></i>&nbsp;';
break; break;
case 'buffering': case 'buffering':
var state_icon = '<i class="fa fa-fw fa-spinner"></i>&nbsp;'; state_icon = '<i class="fa fa-fw fa-spinner"></i>&nbsp;';
break; break;
default: default:
var state_icon = '<i class="fa fa-fw fa-question-circle"></i>&nbsp;'; state_icon = '<i class="fa fa-fw fa-question-circle"></i>&nbsp;';
} }
$('#play-state-' + key).html(state_icon).attr('title', capitalizeFirstLetter(s.state)); $('#play-state-' + key).html(state_icon).attr('title', capitalizeFirstLetter(s.state));
// Switching tracks can be under the same session key, so need to update the info. // Switching tracks can be under the same session key, so need to update the info.
if (s.media_type === 'track') { if (s.media_type === 'track') {
// Update if artist changed // Update if artist changed
if (s.grandparent_rating_key != instance.data('grandparent_rating_key')) { if (s.grandparent_rating_key !== instance.data('grandparent_rating_key')) {
$('#background-' + key).css('background-image', 'url(pms_image_proxy?img=' + s.art + '&width=500&height=280&fallback=art&refresh=true)'); $('#background-' + key).css('background-image', 'url(pms_image_proxy?img=' + s.art + '&width=500&height=280&fallback=art&refresh=true)');
$('#metadata-grandparent_title-' + key) $('#metadata-grandparent_title-' + key)
.attr('href', 'info?rating_key=' + s.grandparent_rating_key) .attr('href', 'info?rating_key=' + s.grandparent_rating_key)
@@ -351,7 +361,7 @@
.text(s.grandparent_title); .text(s.grandparent_title);
} }
// Update cover if album changed // Update cover if album changed
if (s.parent_rating_key != instance.data('parent_rating_key')) { if (s.parent_rating_key !== instance.data('parent_rating_key')) {
$('#poster-' + key).css('background-image', 'url(pms_image_proxy?img=' + s.parent_thumb + '&width=300&height=300&fallback=poster&refresh=true)'); $('#poster-' + key).css('background-image', 'url(pms_image_proxy?img=' + s.parent_thumb + '&width=300&height=300&fallback=poster&refresh=true)');
$('#poster-' + key + '-bg').css('background-image', 'url(pms_image_proxy?img=' + s.parent_thumb + '&width=300&height=300&fallback=poster&refresh=true)'); $('#poster-' + key + '-bg').css('background-image', 'url(pms_image_proxy?img=' + s.parent_thumb + '&width=300&height=300&fallback=poster&refresh=true)');
$('#poster-url-' + key) $('#poster-url-' + key)
@@ -363,7 +373,7 @@
.text(s.parent_title); .text(s.parent_title);
} }
// Update cover if track changed // Update cover if track changed
if (s.parent_rating_key != instance.data('parent_rating_key')) { if (s.parent_rating_key !== instance.data('parent_rating_key')) {
$('#metadata-title-' + key) $('#metadata-title-' + key)
.attr('href', 'info?rating_key=' + s.rating_key) .attr('href', 'info?rating_key=' + s.rating_key)
.attr('title', s.title) .attr('title', s.title)
@@ -374,7 +384,7 @@
// Update the transcode state // Update the transcode state
var transcode_decision = ''; var transcode_decision = '';
if (s.transcode_decision === 'transcode') { if (s.transcode_decision === 'transcode') {
var throttled = (s.transcode_throttled == 1) ? ' (Throttled)' : ' (Speed: ' + s.transcode_speed + ')'; var throttled = (s.transcode_throttled === 1) ? ' (Throttled)' : ' (Speed: ' + s.transcode_speed + ')';
transcode_decision = 'Transcode' + throttled; transcode_decision = 'Transcode' + throttled;
} else if (s.transcode_decision === 'copy') { } else if (s.transcode_decision === 'copy') {
transcode_decision = 'Direct Stream'; transcode_decision = 'Direct Stream';
@@ -392,36 +402,32 @@
$('#transcode_container-' + key).html(transcode_container); $('#transcode_container-' + key).html(transcode_container);
var video_decision = ''; var video_decision = '';
if (['movie', 'episode', 'clip'].indexOf(s.media_type) > -1 && s.video_decision != '') { if (['movie', 'episode', 'clip'].indexOf(s.media_type) > -1 && s.video_decision !== '') {
var v_res= '';
switch (s.video_resolution.toLowerCase()) { switch (s.video_resolution.toLowerCase()) {
case 'sd': case 'sd':
var v_res = 'SD'; v_res = 'SD';
break; break;
case '4k': case '4k':
var v_res = '4k'; v_res = '4k';
break; break;
default: default:
var v_res = s.video_resolution + 'p' v_res = s.video_resolution + 'p'
} }
var sv_res = '';
switch (s.stream_video_resolution.toLowerCase()) { switch (s.stream_video_resolution.toLowerCase()) {
case 'sd': case 'sd':
var sv_res = 'SD'; sv_res = 'SD';
break; break;
case '4k': case '4k':
var sv_res = '4k'; sv_res = '4k';
break; break;
default: default:
var sv_res = s.stream_video_resolution + 'p' sv_res = s.stream_video_resolution + 'p'
} }
if (s.stream_video_decision === 'transcode') { if (s.stream_video_decision === 'transcode') {
var hw_d = ''; var hw_d = (s.transcode_hw_decoding === 1) ? ' (HW)' : '';
var hw_e = ''; var hw_e = (s.transcode_hw_encoding === 1) ? ' (HW)' : '';
if (s.transcode_hw_requested === 1 && s.transcode_hw_full_pipeline === 0) {
hw_d = ' (HW)';
} else if (s.transcode_hw_requested === 1 && s.transcode_hw_full_pipeline === 1) {
hw_d = ' (HW)';
hw_e = ' (HW)';
}
video_decision = 'Transcode (' + s.video_codec.toUpperCase() + hw_d + ' ' + v_res + ' &rarr; ' + s.stream_video_codec.toUpperCase() + hw_e + ' ' + sv_res + ')'; video_decision = 'Transcode (' + s.video_codec.toUpperCase() + hw_d + ' ' + v_res + ' &rarr; ' + s.stream_video_codec.toUpperCase() + hw_e + ' ' + sv_res + ')';
} else if (s.stream_video_decision === 'copy') { } else if (s.stream_video_decision === 'copy') {
video_decision = 'Direct Stream (' + s.stream_video_codec.toUpperCase() + ' ' + sv_res + ')'; video_decision = 'Direct Stream (' + s.stream_video_codec.toUpperCase() + ' ' + sv_res + ')';
@@ -434,7 +440,7 @@
$('#video_decision-' + key).html(video_decision); $('#video_decision-' + key).html(video_decision);
var audio_decision = ''; var audio_decision = '';
if (['movie', 'episode', 'clip', 'track'].indexOf(s.media_type) > -1 && s.audio_codec) { if (['movie', 'episode', 'clip', 'track'].indexOf(s.media_type) > -1 && s.audio_decision) {
var a_codec = (s.audio_codec === 'truehd') ? 'TrueHD' : s.audio_codec.toUpperCase(); var a_codec = (s.audio_codec === 'truehd') ? 'TrueHD' : s.audio_codec.toUpperCase();
var sa_codec = (s.stream_audio_codec === 'truehd') ? 'TrueHD' : s.stream_audio_codec.toUpperCase(); var sa_codec = (s.stream_audio_codec === 'truehd') ? 'TrueHD' : s.stream_audio_codec.toUpperCase();
if (s.stream_audio_decision === 'transcode') { if (s.stream_audio_decision === 'transcode') {
@@ -456,13 +462,13 @@
} else if (s.stream_subtitle_decision === 'burn') { } else if (s.stream_subtitle_decision === 'burn') {
subtitle_decision = 'Burn (' + s.subtitle_codec.toUpperCase() + ')'; subtitle_decision = 'Burn (' + s.subtitle_codec.toUpperCase() + ')';
} else { } else {
subtitle_decision = 'Direct Play (' + ((s.synced_version == '1') ? s.stream_subtitle_codec.toUpperCase() : s.subtitle_codec.toUpperCase()) + ')'; subtitle_decision = 'Direct Play (' + ((s.synced_version === '1') ? s.stream_subtitle_codec.toUpperCase() : s.subtitle_codec.toUpperCase()) + ')';
} }
} }
$('#subtitle_decision-' + key).html(subtitle_decision); $('#subtitle_decision-' + key).html(subtitle_decision);
// Update the stream quality profile and bandwidth // Update the stream quality profile and bandwidth
if (s.media_type != 'photo' && s.quality_profile != 'Unknown') { if (s.media_type !== 'photo' && s.quality_profile !== 'Unknown') {
var br = parseInt(s.stream_bitrate) || ''; var br = parseInt(s.stream_bitrate) || '';
if (br) { if (br) {
if (br > 1000) { if (br > 1000) {
@@ -478,9 +484,9 @@
$('#optimized_version-' + key).html(s.optimized_version_profile + ' (' + s.optimized_version_title + ')'); $('#optimized_version-' + key).html(s.optimized_version_profile + ' (' + s.optimized_version_title + ')');
$('#synced_quality_profile-' + key).html(s.synced_quality_profile); $('#synced_quality_profile-' + key).html(s.synced_quality_profile);
if (s.media_type != 'photo' && parseInt(s.bandwidth)) { if (s.media_type !== 'photo' && parseInt(s.bandwidth)) {
var bw = parseInt(s.bandwidth); var bw = parseInt(s.bandwidth);
if (bw != "Unknown") { if (bw !== "Unknown") {
if (bw > 1000) { if (bw > 1000) {
bw = (bw / 1000).toFixed(1) + ' Mbps'; bw = (bw / 1000).toFixed(1) + ' Mbps';
} else { } else {
@@ -492,17 +498,19 @@
// Update the stream progress times // Update the stream progress times
$('#stream-eta-' + key).html(moment().add(parseInt(s.duration) - parseInt(s.view_offset), 'milliseconds').format(time_format)); $('#stream-eta-' + key).html(moment().add(parseInt(s.duration) - parseInt(s.view_offset), 'milliseconds').format(time_format));
$('#stream-view-offset-' + key).data('state', s.state); var stream_view_offset = $('#stream-view-offset-' + key);
if ($('#stream-view-offset-' + key).data('last_view_offset') != s.view_offset) { stream_view_offset.data('state', s.state);
$('#stream-view-offset-' + key).data('last_view_offset', s.view_offset).data('view_offset', s.view_offset); if (stream_view_offset.data('last_view_offset') !== s.view_offset) {
stream_view_offset.data('last_view_offset', s.view_offset).data('view_offset', s.view_offset);
} }
// Update the progress bars, percent - 3 because of 3px padding-right // Update the progress bars, percent - 3 because of 3px padding-right
$('#buffer-bar-' + key).width(parseInt(s.transcode_progress) - 3 + '%').html(s.transcode_progress + '%') $('#buffer-bar-' + key).width(parseInt(s.transcode_progress) - 3 + '%').html(s.transcode_progress + '%')
.attr('data-original-title', 'Transcoder Progress ' + s.transcode_progress + '%'); .attr('data-original-title', 'Transcoder Progress ' + s.transcode_progress + '%');
$('#progress-bar-' + key).data('state', s.state); var progress_bar = $('#progress-bar-' + key);
if ($('#progress-bar-' + key).data('last_view_offset') != s.view_offset) { progress_bar.data('state', s.state);
$('#progress-bar-' + key).data('last_view_offset', s.view_offset).data('view_offset', s.view_offset); if (progress_bar.data('last_view_offset') !== s.view_offset) {
progress_bar.data('last_view_offset', s.view_offset).data('view_offset', s.view_offset);
} }
// Add temporary class so we know which instances are still active // Add temporary class so we know which instances are still active
@@ -771,13 +779,13 @@
leftTotal = Math.max(Math.min(leftTotal + scrollAmount, 0), leftMax); leftTotal = Math.max(Math.min(leftTotal + scrollAmount, 0), leftMax);
scroller.animate({ left: leftTotal }, 250); scroller.animate({ left: leftTotal }, 250);
if (leftTotal == 0) { if (leftTotal === 0) {
$("#recently-added-page-left").addClass("disabled").blur(); $("#recently-added-page-left").addClass("disabled").blur();
} else { } else {
$("#recently-added-page-left").removeClass("disabled"); $("#recently-added-page-left").removeClass("disabled");
} }
if (leftTotal == leftMax) { if (leftTotal === leftMax) {
$("#recently-added-page-right").addClass("disabled").blur(); $("#recently-added-page-right").addClass("disabled").blur();
} else { } else {
$("#recently-added-page-right").removeClass("disabled"); $("#recently-added-page-right").removeClass("disabled");

View File

@@ -38,20 +38,21 @@ DOCUMENTATION :: END
<%! <%!
import re import re
from plexpy import common, notifiers from plexpy import notifiers
from plexpy.common import MEDIA_TYPE_HEADERS, MEDIA_FLAGS_AUDIO, MEDIA_FLAGS_VIDEO
# Get audio codec file # Get audio codec file
def af(codec): def af(codec):
for pattern, file in common.MEDIA_FLAGS_AUDIO.iteritems(): for pattern, file_type in MEDIA_FLAGS_AUDIO.iteritems():
if re.match(pattern, codec): if re.match(pattern, codec):
return file return file_type
return codec return codec
# Get audio codec file # Get audio codec file
def vf(codec): def vf(codec):
for pattern, file in common.MEDIA_FLAGS_VIDEO.iteritems(): for pattern, file_type in MEDIA_FLAGS_VIDEO.iteritems():
if re.match(pattern, codec): if re.match(pattern, codec):
return file return file_type
return codec return codec
def br(text): def br(text):
@@ -356,7 +357,7 @@ DOCUMENTATION :: END
<div class="col-md-12"> <div class="col-md-12">
<div class="table-card-header"> <div class="table-card-header">
<div class="header-bar"> <div class="header-bar">
<span>Movies in <strong>${data['title']}</strong> collection</span> <span>${MEDIA_TYPE_HEADERS[data['sub_media_type']]} in <strong>${data['title']}</strong> collection</span>
</div> </div>
</div> </div>
<div class="table-card-back"> <div class="table-card-back">

View File

@@ -28,22 +28,15 @@ DOCUMENTATION :: END
% if data != None: % if data != None:
<% <%
from plexpy.common import MEDIA_TYPE_HEADERS
types = ('movie', 'show', 'artist', 'album') types = ('movie', 'show', 'artist', 'album')
headers = {'movie': 'Movies',
'show': 'TV Shows',
'season': 'Seasons',
'episode': 'Episodes',
'artist': 'Artists',
'album': 'Albums',
'track': 'Tracks',
}
%> %>
% for media_type in types: % for media_type in types:
% if data['results_list'][media_type]: % if data['results_list'][media_type]:
<div class="col-md-12"> <div class="col-md-12">
<div class="table-card-header"> <div class="table-card-header">
<div class="header-bar"> <div class="header-bar">
<span>${headers[media_type]} in <strong>${title}</strong> collection</span> <span>${MEDIA_TYPE_HEADERS[media_type]} in <strong>${title}</strong> collection</span>
</div> </div>
</div> </div>
<div class="table-card-back"> <div class="table-card-back">

View File

@@ -28,9 +28,7 @@ libraries_list_table_options = {
$(td).html('<div class="edit-library-toggles">' + $(td).html('<div class="edit-library-toggles">' +
'<button class="btn btn-xs btn-warning delete-library" data-id="' + rowData['section_id'] + '" data-toggle="button"><i class="fa fa-trash-o fa-fw"></i> Delete</button>&nbsp' + '<button class="btn btn-xs btn-warning delete-library" data-id="' + rowData['section_id'] + '" data-toggle="button"><i class="fa fa-trash-o fa-fw"></i> Delete</button>&nbsp' +
'<button class="btn btn-xs btn-warning purge-library" data-id="' + rowData['section_id'] + '" data-toggle="button"><i class="fa fa-eraser fa-fw"></i> Purge</button>&nbsp&nbsp&nbsp' + '<button class="btn btn-xs btn-warning purge-library" data-id="' + rowData['section_id'] + '" data-toggle="button"><i class="fa fa-eraser fa-fw"></i> Purge</button>&nbsp&nbsp&nbsp' +
'<input type="checkbox" id="do_notify-' + rowData['section_id'] + '" name="do_notify" value="1" ' + rowData['do_notify'] + '><label class="edit-tooltip" for="do_notify-' + rowData['section_id'] + '" data-toggle="tooltip" title="Toggle Notifications"><i class="fa fa-bell fa-lg fa-fw"></i></label>&nbsp' +
'<input type="checkbox" id="keep_history-' + rowData['section_id'] + '" name="keep_history" value="1" ' + rowData['keep_history'] + '><label class="edit-tooltip" for="keep_history-' + rowData['section_id'] + '" data-toggle="tooltip" title="Toggle History"><i class="fa fa-history fa-lg fa-fw"></i></label>&nbsp' + '<input type="checkbox" id="keep_history-' + rowData['section_id'] + '" name="keep_history" value="1" ' + rowData['keep_history'] + '><label class="edit-tooltip" for="keep_history-' + rowData['section_id'] + '" data-toggle="tooltip" title="Toggle History"><i class="fa fa-history fa-lg fa-fw"></i></label>&nbsp' +
'<input type="checkbox" id="do_notify_created-' + rowData['section_id'] + '" name="do_notify_created" value="1" ' + rowData['do_notify_created'] + '><label class="edit-tooltip" for="do_notify_created-' + rowData['section_id'] + '" data-toggle="tooltip" title="Toggle Recently Added"><i class="fa fa-download fa-lg fa-fw"></i></label>&nbsp' +
'</div>'); '</div>');
}, },
"width": "7%", "width": "7%",
@@ -258,15 +256,7 @@ $('#libraries_list_table').on('change', 'td.edit-control > .edit-library-toggles
var row = libraries_list_table.row(tr); var row = libraries_list_table.row(tr);
var rowData = row.data(); var rowData = row.data();
var do_notify = 0;
var do_notify_created = 0;
var keep_history = 0; var keep_history = 0;
if ($('#do_notify-' + rowData['section_id']).is(':checked')) {
do_notify = 1;
}
if ($('#do_notify_created-' + rowData['section_id']).is(':checked')) {
do_notify_created = 1;
}
if ($('#keep_history-' + rowData['section_id']).is(':checked')) { if ($('#keep_history-' + rowData['section_id']).is(':checked')) {
keep_history = 1; keep_history = 1;
} }
@@ -280,8 +270,6 @@ $('#libraries_list_table').on('change', 'td.edit-control > .edit-library-toggles
url: 'edit_library', url: 'edit_library',
data: { data: {
section_id: rowData['section_id'], section_id: rowData['section_id'],
do_notify: do_notify,
do_notify_created: do_notify_created,
keep_history: keep_history, keep_history: keep_history,
custom_thumb: custom_thumb custom_thumb: custom_thumb
}, },

View File

@@ -45,7 +45,6 @@ users_list_table_options = {
$(td).html('<div class="edit-user-toggles">' + $(td).html('<div class="edit-user-toggles">' +
'<button class="btn btn-xs btn-warning delete-user" data-id="' + rowData['user_id'] + '" data-toggle="button"><i class="fa fa-trash-o fa-fw"></i> Delete</button>&nbsp' + '<button class="btn btn-xs btn-warning delete-user" data-id="' + rowData['user_id'] + '" data-toggle="button"><i class="fa fa-trash-o fa-fw"></i> Delete</button>&nbsp' +
'<button class="btn btn-xs btn-warning purge-user" data-id="' + rowData['user_id'] + '" data-toggle="button"><i class="fa fa-eraser fa-fw"></i> Purge</button>&nbsp&nbsp&nbsp' + '<button class="btn btn-xs btn-warning purge-user" data-id="' + rowData['user_id'] + '" data-toggle="button"><i class="fa fa-eraser fa-fw"></i> Purge</button>&nbsp&nbsp&nbsp' +
'<input type="checkbox" id="do_notify-' + rowData['user_id'] + '" name="do_notify" value="1" ' + rowData['do_notify'] + '><label class="edit-tooltip" for="do_notify-' + rowData['user_id'] + '" data-toggle="tooltip" title="Toggle Notifications"><i class="fa fa-bell fa-lg fa-fw"></i></label>&nbsp' +
'<input type="checkbox" id="keep_history-' + rowData['user_id'] + '" name="keep_history" value="1" ' + rowData['keep_history'] + '><label class="edit-tooltip" for="keep_history-' + rowData['user_id'] + '" data-toggle="tooltip" title="Toggle History"><i class="fa fa-history fa-lg fa-fw"></i></label>&nbsp' + '<input type="checkbox" id="keep_history-' + rowData['user_id'] + '" name="keep_history" value="1" ' + rowData['keep_history'] + '><label class="edit-tooltip" for="keep_history-' + rowData['user_id'] + '" data-toggle="tooltip" title="Toggle History"><i class="fa fa-history fa-lg fa-fw"></i></label>&nbsp' +
'<input type="checkbox" id="allow_guest-' + rowData['user_id'] + '" name="allow_guest" value="1" ' + rowData['allow_guest'] + '><label class="edit-tooltip" for="allow_guest-' + rowData['user_id'] + '" data-toggle="tooltip" title="Toggle Guest Access"><i class="fa fa-unlock-alt fa-lg fa-fw"></i></label>&nbsp' + '<input type="checkbox" id="allow_guest-' + rowData['user_id'] + '" name="allow_guest" value="1" ' + rowData['allow_guest'] + '><label class="edit-tooltip" for="allow_guest-' + rowData['user_id'] + '" data-toggle="tooltip" title="Toggle Guest Access"><i class="fa fa-unlock-alt fa-lg fa-fw"></i></label>&nbsp' +
'</div>'); '</div>');
@@ -284,12 +283,8 @@ $('#users_list_table').on('change', 'td.edit-control > .edit-user-toggles > inpu
var row = users_list_table.row(tr); var row = users_list_table.row(tr);
var rowData = row.data(); var rowData = row.data();
var do_notify = 0;
var keep_history = 0; var keep_history = 0;
var allow_guest = 0; var allow_guest = 0;
if ($('#do_notify-' + rowData['user_id']).is(':checked')) {
do_notify = 1;
}
if ($('#keep_history-' + rowData['user_id']).is(':checked')) { if ($('#keep_history-' + rowData['user_id']).is(':checked')) {
keep_history = 1; keep_history = 1;
} }
@@ -304,7 +299,6 @@ $('#users_list_table').on('change', 'td.edit-control > .edit-user-toggles > inpu
data: { data: {
user_id: rowData['user_id'], user_id: rowData['user_id'],
friendly_name: friendly_name, friendly_name: friendly_name,
do_notify: do_notify,
keep_history: keep_history, keep_history: keep_history,
allow_guest: allow_guest, allow_guest: allow_guest,
thumb: rowData['user_thumb'] thumb: rowData['user_thumb']

View File

@@ -132,12 +132,9 @@
<div role="tabpanel" class="tab-pane" id="tabs-notify_conditions"> <div role="tabpanel" class="tab-pane" id="tabs-notify_conditions">
<label>Notification Conditions</label> <label>Notification Conditions</label>
<p class="help-block"> <p class="help-block">
Add custom notification conditions. Add custom conditions to filter out notifications.
<a href="#notify-text-sub-modal" data-toggle="modal">Click here</a> for a description of all the parameters. <a href="#notify-text-sub-modal" data-toggle="modal">Click here</a> for a description of all the parameters.
</p> </p>
<p class="help-block">
Note: Conditions are checked after the notification trigger and the notification will only be sent if the condition logic is satisfied.
</p>
<div id="condition-widget"></div> <div id="condition-widget"></div>
<input type="hidden" name="custom_conditions" id="custom_conditions" /> <input type="hidden" name="custom_conditions" id="custom_conditions" />
@@ -146,7 +143,8 @@
<input type="text" class="form-control" name="custom_conditions_logic" id="custom_conditions_logic" value="${notifier['custom_conditions_logic']}" required /> <input type="text" class="form-control" name="custom_conditions_logic" id="custom_conditions_logic" value="${notifier['custom_conditions_logic']}" required />
<div id="custom_conditions_logic_error" class="alert alert-danger" role="alert" style="padding-top: 5px; padding-bottom: 5px; margin: 0; display: none;"><i class="fa fa-exclamation-triangle" style="color: #a94442;"></i> <span></span></div> <div id="custom_conditions_logic_error" class="alert alert-danger" role="alert" style="padding-top: 5px; padding-bottom: 5px; margin: 0; display: none;"><i class="fa fa-exclamation-triangle" style="color: #a94442;"></i> <span></span></div>
<p class="help-block"> <p class="help-block">
Enter the logic to use when evaluating the conditions (e.g. <span class="inline-pre">{1} and ({2} or {3})</span>). Optional: Enter custom logic to use when evaluating the conditions (e.g. <span class="inline-pre">{1} and ({2} or {3})</span>).
Leave blank for implicit <span class="inline-pre">and</span> between all conditions.
</p> </p>
<p class="help-block"> <p class="help-block">
Note: Only the keywords <span class="inline-pre">and</span>/<span class="inline-pre">or</span> and brackets <span class="inline-pre">()</span> are supported. Note: Only the keywords <span class="inline-pre">and</span>/<span class="inline-pre">or</span> and brackets <span class="inline-pre">()</span> are supported.

View File

@@ -63,7 +63,7 @@ DOCUMENTATION :: END
<h3 class="text-muted">&nbsp;</h3> <h3 class="text-muted">&nbsp;</h3>
</div> </div>
% elif item['media_type'] == 'show': % elif item['media_type'] == 'show':
<a href="info?rating_key=${item['rating_key']}" title="${item['parent_title']}"> <a href="info?rating_key=${item['rating_key']}" title="${item['title']}">
<div class="dashboard-recent-media-poster"> <div class="dashboard-recent-media-poster">
<div class="dashboard-recent-media-poster-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=450&fallback=poster);"> <div class="dashboard-recent-media-poster-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=450&fallback=poster);">
<div class="dashboard-recent-media-overlay"> <div class="dashboard-recent-media-overlay">

View File

@@ -918,7 +918,7 @@
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" id="get_file_sizes" name="get_file_sizes" value="1" ${config['get_file_sizes']}> Calculate Total File Sizes <span style="color: #eb8600; padding-left: 10px;">[experimental]</span> <input type="checkbox" id="get_file_sizes" name="get_file_sizes" value="1" ${config['get_file_sizes']}> Calculate Total File Sizes
</label> </label>
<p class="help-block">Enable if you want Tautulli to calculate the total file size for TV Shows/Seasons and Artists/Albums on the media info tables.</p> <p class="help-block">Enable if you want Tautulli to calculate the total file size for TV Shows/Seasons and Artists/Albums on the media info tables.</p>
</div> </div>

View File

@@ -54,6 +54,11 @@ DOCUMENTATION :: END
</h4> </h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
% if data['current_session']:
<div class="col-sm-12 text-muted stream-info-current">
<i class="fa fa-exclamation-circle"></i> Current session. Updated stream details below may be delayed.
</div>
% endif
<table class="stream-info" style="margin-top: 0;"> <table class="stream-info" style="margin-top: 0;">
<thead> <thead>
<tr> <tr>

View File

@@ -1189,6 +1189,27 @@ def dbcheck():
) )
# Upgrade session_history_media_info table from earlier versions
try:
result = c_db.execute('SELECT stream_container FROM session_history_media_info '
'WHERE stream_container IS NULL').fetchall()
if len(result) > 0:
logger.debug(u"Altering database. Removing NULL values from session_history_media_info table.")
c_db.execute(
'UPDATE session_history_media_info SET stream_container = "" WHERE stream_container IS NULL '
)
c_db.execute(
'UPDATE session_history_media_info SET stream_video_codec = "" WHERE stream_video_codec IS NULL '
)
c_db.execute(
'UPDATE session_history_media_info SET stream_audio_codec = "" WHERE stream_audio_codec IS NULL '
)
c_db.execute(
'UPDATE session_history_media_info SET stream_subtitle_codec = "" WHERE stream_subtitle_codec IS NULL '
)
except sqlite3.OperationalError:
logger.warn(u"Unable to remove NULL values from session_history_media_info table.")
# Upgrade users table from earlier versions # Upgrade users table from earlier versions
try: try:
c_db.execute('SELECT do_notify FROM users') c_db.execute('SELECT do_notify FROM users')
@@ -1370,8 +1391,8 @@ def dbcheck():
# Upgrade library_sections table from earlier versions (remove duplicated libraries) # Upgrade library_sections table from earlier versions (remove duplicated libraries)
try: try:
result = c_db.execute('SELECT * FROM library_sections WHERE server_id = ""') result = c_db.execute('SELECT * FROM library_sections WHERE server_id = ""').fetchall()
if result.rowcount > 0: if len(result) > 0:
logger.debug(u"Altering database. Removing duplicate libraries from library_sections table.") logger.debug(u"Altering database. Removing duplicate libraries from library_sections table.")
c_db.execute( c_db.execute(
'DELETE FROM library_sections WHERE server_id = ""' 'DELETE FROM library_sections WHERE server_id = ""'

View File

@@ -33,6 +33,7 @@ ACTIVITY_SCHED = BackgroundScheduler()
RECENTLY_ADDED_QUEUE = {} RECENTLY_ADDED_QUEUE = {}
class ActivityHandler(object): class ActivityHandler(object):
def __init__(self, timeline): def __init__(self, timeline):
@@ -229,6 +230,8 @@ class ActivityHandler(object):
# Update the session state and viewOffset # Update the session state and viewOffset
if this_state == 'playing': if this_state == 'playing':
# Update the session in our temp session table # Update the session in our temp session table
# if the last set temporary stopped time exceeds 15 seconds
if int(time.time()) - db_session['stopped'] > 60:
session = self.get_live_session() session = self.get_live_session()
if session: if session:
self.update_db_session(session=session) self.update_db_session(session=session)

View File

@@ -35,6 +35,8 @@ import database
import libraries import libraries
import logger import logger
import mobile_app import mobile_app
import notification_handler
import notifiers
import users import users
@@ -397,6 +399,50 @@ class API2:
return return
def notify(self, notifier_id='', subject='Tautulli', body='Test notification', **kwargs):
""" Send a notification using Tautulli.
```
Required parameters:
notifier_id (int): The ID number of the notification agent
subject (str): The subject of the message
body (str): The body of the message
Optional parameters:
None
Returns:
None
```
"""
if not notifier_id:
self._api_msg = 'Notification failed: no notifier id provided.'
self._api_result_type = 'error'
return
notifier = notifiers.get_notifier_config(notifier_id=notifier_id)
if not notifier:
self._api_msg = 'Notification failed: invalid notifier_id provided %s.' % notifier_id
self._api_result_type = 'error'
return
logger.api_debug(u'Tautulli APIv2 :: Sending notification.')
success = notification_handler.notify(notifier_id=notifier_id,
notify_action='api',
subject=subject,
body=body,
**kwargs)
if success:
self._api_msg = 'Notification sent.'
self._api_result_type = 'success'
else:
self._api_msg = 'Notification failed.'
self._api_result_type = 'error'
return
def _api_make_md(self): def _api_make_md(self):
""" Tries to make a API.md to simplify the api docs. """ """ Tries to make a API.md to simplify the api docs. """
@@ -581,8 +627,8 @@ General optional parameters:
if isinstance(result, (dict, list)): if isinstance(result, (dict, list)):
ret = result ret = result
else: else:
raise raise Exception
except: except Exception:
try: try:
ret = json.loads(result) ret = json.loads(result)
except (ValueError, TypeError): except (ValueError, TypeError):

View File

@@ -32,17 +32,30 @@ DEFAULT_POSTER_THUMB = "interfaces/default/images/poster.png"
DEFAULT_COVER_THUMB = "interfaces/default/images/cover.png" DEFAULT_COVER_THUMB = "interfaces/default/images/cover.png"
DEFAULT_ART = "interfaces/default/images/art.png" DEFAULT_ART = "interfaces/default/images/art.png"
PLATFORM_NAME_OVERRIDES = {'Konvergo': 'Plex Media Player', MEDIA_TYPE_HEADERS = {
'movie': 'Movies',
'show': 'TV Shows',
'season': 'Seasons',
'episode': 'Episodes',
'artist': 'Artists',
'album': 'Albums',
'track': 'Tracks',
}
PLATFORM_NAME_OVERRIDES = {
'Konvergo': 'Plex Media Player',
'Mystery 3': 'Playstation 3', 'Mystery 3': 'Playstation 3',
'Mystery 4': 'Playstation 4', 'Mystery 4': 'Playstation 4',
'Mystery 5': 'Xbox 360', 'Mystery 5': 'Xbox 360',
'WebMAF': 'Playstation 4' 'WebMAF': 'Playstation 4'
} }
PMS_PLATFORM_NAME_OVERRIDES = {'MacOSX': 'Mac' PMS_PLATFORM_NAME_OVERRIDES = {
'MacOSX': 'Mac'
} }
PLATFORM_NAMES = {'android': 'android', PLATFORM_NAMES = {
'android': 'android',
'apple tv': 'atv', 'apple tv': 'atv',
'chrome': 'chrome', 'chrome': 'chrome',
'chromecast': 'chromecast', 'chromecast': 'chromecast',
@@ -79,20 +92,25 @@ PLATFORM_NAMES = {'android': 'android',
} }
PLATFORM_NAMES = OrderedDict(sorted(PLATFORM_NAMES.items(), key=lambda k: k[0], reverse=True)) PLATFORM_NAMES = OrderedDict(sorted(PLATFORM_NAMES.items(), key=lambda k: k[0], reverse=True))
MEDIA_FLAGS_AUDIO = {'ac.?3': 'dolby_digital', MEDIA_FLAGS_AUDIO = {
'ac.?3': 'dolby_digital',
'truehd': 'dolby_truehd', 'truehd': 'dolby_truehd',
'(dca|dta)': 'dts', '(dca|dta)': 'dts',
'dts(hd_|-hd|-)?ma': 'dca-ma', 'dts(hd_|-hd|-)?ma': 'dca-ma',
'vorbis': 'ogg' 'vorbis': 'ogg'
} }
MEDIA_FLAGS_VIDEO = {'avc1': 'h264', MEDIA_FLAGS_VIDEO = {
'avc1': 'h264',
'wmv(1|2)': 'wmv', 'wmv(1|2)': 'wmv',
'wmv3': 'wmvhd' 'wmv3': 'wmvhd'
} }
AUDIO_CODEC_OVERRIDES = {'truehd': 'TrueHD'} AUDIO_CODEC_OVERRIDES = {
'truehd': 'TrueHD'
}
VIDEO_RESOLUTION_OVERRIDES = {'sd': 'SD', VIDEO_RESOLUTION_OVERRIDES = {
'sd': 'SD',
'480': '480p', '480': '480p',
'540': '540p', '540': '540p',
'576': '576p', '576': '576p',
@@ -101,7 +119,8 @@ VIDEO_RESOLUTION_OVERRIDES = {'sd': 'SD',
'4k': '4k' '4k': '4k'
} }
AUDIO_CHANNELS = {'1': 'Mono', AUDIO_CHANNELS = {
'1': 'Mono',
'2': 'Stereo', '2': 'Stereo',
'3': '2.1', '3': '2.1',
'4': '3.1', '4': '3.1',
@@ -110,7 +129,8 @@ AUDIO_CHANNELS = {'1': 'Mono',
'8': '7.1' '8': '7.1'
} }
VIDEO_QUALITY_PROFILES = {20000: '20 Mbps 1080p', VIDEO_QUALITY_PROFILES = {
20000: '20 Mbps 1080p',
12000: '12 Mbps 1080p', 12000: '12 Mbps 1080p',
10000: '10 Mbps 1080p', 10000: '10 Mbps 1080p',
8000: '8 Mbps 1080p', 8000: '8 Mbps 1080p',
@@ -126,7 +146,8 @@ VIDEO_QUALITY_PROFILES = {20000: '20 Mbps 1080p',
} }
VIDEO_QUALITY_PROFILES = OrderedDict(sorted(VIDEO_QUALITY_PROFILES.items(), key=lambda k: k[0], reverse=True)) VIDEO_QUALITY_PROFILES = OrderedDict(sorted(VIDEO_QUALITY_PROFILES.items(), key=lambda k: k[0], reverse=True))
AUDIO_QUALITY_PROFILES = {512: '512 kbps', AUDIO_QUALITY_PROFILES = {
512: '512 kbps',
320: '320 kbps', 320: '320 kbps',
256: '256 kbps', 256: '256 kbps',
192: '192 kbps', 192: '192 kbps',
@@ -135,10 +156,24 @@ AUDIO_QUALITY_PROFILES = {512: '512 kbps',
} }
AUDIO_QUALITY_PROFILES = OrderedDict(sorted(AUDIO_QUALITY_PROFILES.items(), key=lambda k: k[0], reverse=True)) AUDIO_QUALITY_PROFILES = OrderedDict(sorted(AUDIO_QUALITY_PROFILES.items(), key=lambda k: k[0], reverse=True))
HW_DECODERS = ['dxva2', 'videotoolbox', 'mediacodecndk', 'vaapi'] HW_DECODERS = [
HW_ENCODERS = ['qsv', 'nvenc', 'mf', 'videotoolbox', 'mediacodecndk', 'vaapi', 'nvenc'] 'dxva2',
'videotoolbox',
'mediacodecndk',
'vaapi'
]
HW_ENCODERS = [
'qsv',
'nvenc',
'mf',
'videotoolbox',
'mediacodecndk',
'vaapi',
'nvenc'
]
SCHEDULER_LIST = ['Check GitHub for updates', SCHEDULER_LIST = [
'Check GitHub for updates',
'Check for active sessions', 'Check for active sessions',
'Check for recently added items', 'Check for recently added items',
'Check for Plex updates', 'Check for Plex updates',
@@ -154,11 +189,19 @@ SCHEDULER_LIST = ['Check GitHub for updates',
DATE_TIME_FORMATS = [ DATE_TIME_FORMATS = [
{ {
'category': 'Year', 'category': 'Year',
'parameters': [
{'value': 'YYYY', 'description': 'Numeric, four digits', 'example': '1999, 2003'},
{'value': 'YY', 'description': 'Numeric, two digits', 'example': '99, 03'}
]
},
{
'category': 'Month',
'parameters': [ 'parameters': [
{'value': 'MMMM', 'description': 'Textual, full', 'example': 'January-December'}, {'value': 'MMMM', 'description': 'Textual, full', 'example': 'January-December'},
{'value': 'MMM', 'description': 'Textual, three letters', 'example': 'Jan-Dec'}, {'value': 'MMM', 'description': 'Textual, three letters', 'example': 'Jan-Dec'},
{'value': 'MM', 'description': 'Numeric, with leading zeros', 'example': '42747'}, {'value': 'MM', 'description': 'Numeric, with leading zeros', 'example': '01-12'},
{'value': 'M', 'description': 'Numeric, without leading zeros', 'example': '42747'}, {'value': 'M', 'description': 'Numeric, without leading zeros', 'example': '1-12'},
{'value': 'Mo', 'description': 'Numeric, with suffix', 'example': '1st, 2nd ... 12th'},
] ]
}, },
{ {
@@ -166,14 +209,15 @@ DATE_TIME_FORMATS = [
'parameters': [ 'parameters': [
{'value': 'DDDD', 'description': 'Numeric, with leading zeros', 'example': '001-365'}, {'value': 'DDDD', 'description': 'Numeric, with leading zeros', 'example': '001-365'},
{'value': 'DDD', 'description': 'Numeric, without leading zeros', 'example': '1-365'}, {'value': 'DDD', 'description': 'Numeric, without leading zeros', 'example': '1-365'},
{'value': 'DDDo', 'description': 'Numeric, with suffix', 'example': '1st, 2nd, ... 365th'},
] ]
}, },
{ {
'category': 'Day of the Month', 'category': 'Day of the Month',
'parameters': [ 'parameters': [
{'value': 'DD', 'description': 'Numeric, with leading zeros', 'example': '42766'}, {'value': 'DD', 'description': 'Numeric, with leading zeros', 'example': '01-31'},
{'value': 'D', 'description': 'Numeric, without leading zeros', 'example': '42766'}, {'value': 'D', 'description': 'Numeric, without leading zeros', 'example': '1-31'},
{'value': 'Do', 'description': 'Numeric, with suffix', 'example': 'E.g. 1st, 2nd ... 31st.'}, {'value': 'Do', 'description': 'Numeric, with suffix', 'example': '1st, 2nd ... 31st'},
] ]
}, },
{ {
@@ -181,7 +225,9 @@ DATE_TIME_FORMATS = [
'parameters': [ 'parameters': [
{'value': 'dddd', 'description': 'Textual, full', 'example': 'Sunday-Saturday'}, {'value': 'dddd', 'description': 'Textual, full', 'example': 'Sunday-Saturday'},
{'value': 'ddd', 'description': 'Textual, three letters', 'example': 'Sun-Sat'}, {'value': 'ddd', 'description': 'Textual, three letters', 'example': 'Sun-Sat'},
{'value': 'dd', 'description': 'Textual, two letters', 'example': 'Su-Sa'},
{'value': 'd', 'description': 'Numeric', 'example': '0-6'}, {'value': 'd', 'description': 'Numeric', 'example': '0-6'},
{'value': 'do', 'description': 'Numeric, with suffix', 'example': '0th, 1st ... 6th'},
] ]
}, },
{ {
@@ -189,8 +235,8 @@ DATE_TIME_FORMATS = [
'parameters': [ 'parameters': [
{'value': 'HH', 'description': '24-hour, with leading zeros', 'example': '00-23'}, {'value': 'HH', 'description': '24-hour, with leading zeros', 'example': '00-23'},
{'value': 'H', 'description': '24-hour, without leading zeros', 'example': '0-23'}, {'value': 'H', 'description': '24-hour, without leading zeros', 'example': '0-23'},
{'value': 'hh', 'description': '12-hour, with leading zeros', 'example': '42747'}, {'value': 'hh', 'description': '12-hour, with leading zeros', 'example': '01-12'},
{'value': 'h', 'description': '12-hour, without leading zeros', 'example': '42747'}, {'value': 'h', 'description': '12-hour, without leading zeros', 'example': '1-12'},
] ]
}, },
{ {
@@ -217,8 +263,8 @@ DATE_TIME_FORMATS = [
{ {
'category': 'Timezone', 'category': 'Timezone',
'parameters': [ 'parameters': [
{'value': 'ZZ', 'description': 'UTC offset', 'example': 'E.g. +0100, -0700'}, {'value': 'ZZ', 'description': 'UTC offset', 'example': '+0100, -0700'},
{'value': 'Z', 'description': 'UTC offset', 'example': 'E.g. +01:00, -07:00'}, {'value': 'Z', 'description': 'UTC offset', 'example': '+01:00, -07:00'},
] ]
}, },
{ {

View File

@@ -289,6 +289,7 @@ _CONFIG_DEFINITIONS = {
'LOG_BLACKLIST': (int, 'General', 1), 'LOG_BLACKLIST': (int, 'General', 1),
'LOG_DIR': (str, 'General', ''), 'LOG_DIR': (str, 'General', ''),
'LOGGING_IGNORE_INTERVAL': (int, 'Monitoring', 120), 'LOGGING_IGNORE_INTERVAL': (int, 'Monitoring', 120),
'METADATA_CACHE_SECONDS': (int, 'Advanced', 1800),
'MOVIE_LOGGING_ENABLE': (int, 'Monitoring', 1), 'MOVIE_LOGGING_ENABLE': (int, 'Monitoring', 1),
'MOVIE_NOTIFY_ENABLE': (int, 'Monitoring', 0), 'MOVIE_NOTIFY_ENABLE': (int, 'Monitoring', 0),
'MOVIE_NOTIFY_ON_START': (int, 'Monitoring', 1), 'MOVIE_NOTIFY_ON_START': (int, 'Monitoring', 1),

View File

@@ -951,9 +951,11 @@ class DataFactory(object):
'transcode_hw_encoding': item['transcode_hw_encoding'], 'transcode_hw_encoding': item['transcode_hw_encoding'],
'media_type': item['media_type'], 'media_type': item['media_type'],
'title': item['title'], 'title': item['title'],
'grandparent_title': item['grandparent_title'] 'grandparent_title': item['grandparent_title'],
'current_session': 1 if session_key else 0
} }
stream_output = {k: v or '' for k, v in stream_output.iteritems()}
return stream_output return stream_output
def get_metadata_details(self, rating_key): def get_metadata_details(self, rating_key):

View File

@@ -131,19 +131,19 @@ def notify_conditions(notify_action=None, stream_data=None, timeline_data=None):
if stream_data: if stream_data:
# Check if notifications enabled for user and library # Check if notifications enabled for user and library
user_data = users.Users() # user_data = users.Users()
user_details = user_data.get_details(user_id=stream_data['user_id']) # user_details = user_data.get_details(user_id=stream_data['user_id'])
#
# library_data = libraries.Libraries()
# library_details = library_data.get_details(section_id=stream_data['section_id'])
library_data = libraries.Libraries() # if not user_details['do_notify']:
library_details = library_data.get_details(section_id=stream_data['section_id']) # logger.debug(u"Tautulli NotificationHandler :: Notifications for user '%s' are disabled." % user_details['username'])
# return False
if not user_details['do_notify']: #
logger.debug(u"Tautulli NotificationHandler :: Notifications for user '%s' are disabled." % user_details['username']) # elif not library_details['do_notify'] and notify_action not in ('on_concurrent', 'on_newdevice'):
return False # logger.debug(u"Tautulli NotificationHandler :: Notifications for library '%s' are disabled." % library_details['section_name'])
# return False
elif not library_details['do_notify'] and notify_action not in ('on_concurrent', 'on_newdevice'):
logger.debug(u"Tautulli NotificationHandler :: Notifications for library '%s' are disabled." % library_details['section_name'])
return False
if notify_action == 'on_concurrent': if notify_action == 'on_concurrent':
pms_connect = pmsconnect.PmsConnect() pms_connect = pmsconnect.PmsConnect()
@@ -188,12 +188,12 @@ def notify_conditions(notify_action=None, stream_data=None, timeline_data=None):
elif timeline_data: elif timeline_data:
# Check if notifications enabled for library # Check if notifications enabled for library
library_data = libraries.Libraries() # library_data = libraries.Libraries()
library_details = library_data.get_details(section_id=timeline_data['section_id']) # library_details = library_data.get_details(section_id=timeline_data['section_id'])
#
if not library_details['do_notify_created']: # if not library_details['do_notify_created']:
# logger.debug(u"Tautulli NotificationHandler :: Notifications for library '%s' is disabled." % library_details['section_name']) # # logger.debug(u"Tautulli NotificationHandler :: Notifications for library '%s' is disabled." % library_details['section_name'])
return False # return False
return True return True
@@ -206,12 +206,14 @@ def notify_custom_conditions(notifier_id=None, parameters=None):
notifier_config = notifiers.get_notifier_config(notifier_id=notifier_id) notifier_config = notifiers.get_notifier_config(notifier_id=notifier_id)
custom_conditions_logic = notifier_config['custom_conditions_logic'] custom_conditions_logic = notifier_config['custom_conditions_logic']
custom_conditions = json.loads(notifier_config['custom_conditions']) or []
if custom_conditions_logic or any(c for c in custom_conditions if c['value']):
logger.debug(u"Tautulli NotificationHandler :: Checking custom notification conditions for notifier_id %s."
% notifier_id)
logic_groups = None
if custom_conditions_logic: if custom_conditions_logic:
logger.debug(u"Tautulli NotificationHandler :: Checking custom notification conditions for notifier_id %s." % notifier_id)
custom_conditions = json.loads(notifier_config['custom_conditions'])
try: try:
# Parse and validate the custom conditions logic # Parse and validate the custom conditions logic
logic_groups = helpers.parse_condition_logic_string(custom_conditions_logic, len(custom_conditions)) logic_groups = helpers.parse_condition_logic_string(custom_conditions_logic, len(custom_conditions))
@@ -227,10 +229,11 @@ def notify_custom_conditions(notifier_id=None, parameters=None):
operator = condition['operator'] operator = condition['operator']
values = condition['value'] values = condition['value']
parameter_type = condition['type'] parameter_type = condition['type']
parameter_value = parameters.get(parameter, "")
# Set blank conditions to None # Set blank conditions to True (skip)
if not parameter or not operator or not values: if not parameter or not operator or not values:
evaluated_conditions.append(None) evaluated_conditions.append(True)
continue continue
# Make sure the condition values is in a list # Make sure the condition values is in a list
@@ -248,25 +251,25 @@ def notify_custom_conditions(notifier_id=None, parameters=None):
elif parameter_type == 'float': elif parameter_type == 'float':
values = [float(v) for v in values] values = [float(v) for v in values]
except Exception as e: except ValueError as e:
logger.error(u"Tautulli NotificationHandler :: Unable to cast condition '%s' to type '%s'." logger.error(u"Tautulli NotificationHandler :: Unable to cast condition '%s', values '%s', to type '%s'."
% (parameter, parameter_type)) % (parameter, values, parameter_type))
return False return False
# Cast the parameter value to the correct type # Cast the parameter value to the correct type
try: try:
if parameter_type == 'str': if parameter_type == 'str':
parameter_value = unicode(parameters[parameter]).lower() parameter_value = unicode(parameter_value).lower()
elif parameter_type == 'int': elif parameter_type == 'int':
parameter_value = int(parameters[parameter]) parameter_value = int(parameter_value)
elif parameter_type == 'float': elif parameter_type == 'float':
parameter_value = float(parameters[parameter]) parameter_value = float(parameter_value)
except Exception as e: except ValueError as e:
logger.error(u"Tautulli NotificationHandler :: Unable to cast parameter '%s' to type '%s'." logger.error(u"Tautulli NotificationHandler :: Unable to cast parameter '%s', value '%s', to type '%s'."
% (parameter, parameter_type)) % (parameter, parameter_value, parameter_type))
return False return False
# Check each condition # Check each condition
@@ -298,12 +301,15 @@ def notify_custom_conditions(notifier_id=None, parameters=None):
logger.warn(u"Tautulli NotificationHandler :: Invalid condition operator '%s'." % operator) logger.warn(u"Tautulli NotificationHandler :: Invalid condition operator '%s'." % operator)
evaluated_conditions.append(None) evaluated_conditions.append(None)
if logic_groups:
# Format and evaluate the logic string # Format and evaluate the logic string
try: try:
evaluated_logic = helpers.eval_logic_groups_to_bool(logic_groups, evaluated_conditions) evaluated_logic = helpers.eval_logic_groups_to_bool(logic_groups, evaluated_conditions)
except Exception as e: except Exception as e:
logger.error(u"Tautulli NotificationHandler :: Unable to evaluate custom condition logic: %s." % e) logger.error(u"Tautulli NotificationHandler :: Unable to evaluate custom condition logic: %s." % e)
return False return False
else:
evaluated_logic = all(evaluated_conditions[1:])
logger.debug(u"Tautulli NotificationHandler :: Custom condition evaluated to '%s'." % str(evaluated_logic)) logger.debug(u"Tautulli NotificationHandler :: Custom condition evaluated to '%s'." % str(evaluated_logic))
return evaluated_logic return evaluated_logic
@@ -326,7 +332,7 @@ def notify(notifier_id=None, notify_action=None, stream_data=None, timeline_data
if not notifier_config: if not notifier_config:
return return
if notify_action == 'test': if notify_action in ('test', 'api'):
subject = kwargs.pop('subject', 'Tautulli') subject = kwargs.pop('subject', 'Tautulli')
body = kwargs.pop('body', 'Test Notification') body = kwargs.pop('body', 'Test Notification')
script_args = kwargs.pop('script_args', []) script_args = kwargs.pop('script_args', [])
@@ -344,8 +350,8 @@ def notify(notifier_id=None, notify_action=None, stream_data=None, timeline_data
# Set the notification state in the db # Set the notification state in the db
notification_id = set_notify_state(session=stream_data or timeline_data, notification_id = set_notify_state(session=stream_data or timeline_data,
notify_action=notify_action,
notifier=notifier_config, notifier=notifier_config,
notify_action=notify_action,
subject=subject, subject=subject,
body=body, body=body,
script_args=script_args) script_args=script_args)
@@ -384,9 +390,9 @@ def get_notify_state(session):
return notify_states return notify_states
def set_notify_state(notify_action, notifier, subject, body, script_args, session=None): def set_notify_state(notifier, notify_action, subject='', body='', script_args='', session=None):
if notify_action and notifier: if notifier and notify_action:
monitor_db = database.MonitorDatabase() monitor_db = database.MonitorDatabase()
session = session or {} session = session or {}
@@ -451,7 +457,11 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, m
notify_params = defaultdict(str) notify_params = defaultdict(str)
if session: if session:
# Reload json from raw stream info
if session.get('raw_stream_info'):
session.update(json.loads(session['raw_stream_info']))
notify_params.update(session) notify_params.update(session)
if timeline: if timeline:
notify_params.update(timeline) notify_params.update(timeline)
@@ -513,7 +523,7 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, m
remaining_duration = duration - view_offset remaining_duration = duration - view_offset
# Build Plex URL # Build Plex URL
notify_params['plex_url'] = '{web_url}#!/server/{pms_identifier}/details?key=%2Flibrary%2Fnotify_params%2F{rating_key}'.format( notify_params['plex_url'] = '{web_url}#!/server/{pms_identifier}/details?key=%2Flibrary%2Fmetadata%2F{rating_key}'.format(
web_url=plexpy.CONFIG.PMS_WEB_URL, web_url=plexpy.CONFIG.PMS_WEB_URL,
pms_identifier=plexpy.CONFIG.PMS_IDENTIFIER, pms_identifier=plexpy.CONFIG.PMS_IDENTIFIER,
rating_key=rating_key) rating_key=rating_key)
@@ -937,7 +947,7 @@ def build_notify_text(subject='', body='', notify_action=None, parameters=None,
try: try:
script_args = [custom_formatter.format(unicode(arg), **parameters) for arg in subject.split()] script_args = [custom_formatter.format(unicode(arg), **parameters) for arg in subject.split()]
except LookupError as e: except LookupError as e:
logger.error(u"Tautulli NotificationHandler :: Unable to parse field %s in script argument. Using fallback." % e) logger.error(u"Tautulli NotificationHandler :: Unable to parse parameter %s in script argument. Using fallback." % e)
script_args = [] script_args = []
except Exception as e: except Exception as e:
logger.error(u"Tautulli NotificationHandler :: Unable to parse custom script arguments: %s. Using fallback." % e) logger.error(u"Tautulli NotificationHandler :: Unable to parse custom script arguments: %s. Using fallback." % e)
@@ -948,7 +958,7 @@ def build_notify_text(subject='', body='', notify_action=None, parameters=None,
try: try:
subject = custom_formatter.format(unicode(subject), **parameters) subject = custom_formatter.format(unicode(subject), **parameters)
except LookupError as e: except LookupError as e:
logger.error(u"Tautulli NotificationHandler :: Unable to parse field %s in notification subject. Using fallback." % e) logger.error(u"Tautulli NotificationHandler :: Unable to parse parameter %s in notification subject. Using fallback." % e)
subject = unicode(default_subject).format(**parameters) subject = unicode(default_subject).format(**parameters)
except Exception as e: except Exception as e:
logger.error(u"Tautulli NotificationHandler :: Unable to parse custom notification subject: %s. Using fallback." % e) logger.error(u"Tautulli NotificationHandler :: Unable to parse custom notification subject: %s. Using fallback." % e)
@@ -957,7 +967,7 @@ def build_notify_text(subject='', body='', notify_action=None, parameters=None,
try: try:
body = custom_formatter.format(unicode(body), **parameters) body = custom_formatter.format(unicode(body), **parameters)
except LookupError as e: except LookupError as e:
logger.error(u"Tautulli NotificationHandler :: Unable to parse field %s in notification body. Using fallback." % e) logger.error(u"Tautulli NotificationHandler :: Unable to parse parameter %s in notification body. Using fallback." % e)
body = unicode(default_body).format(**parameters) body = unicode(default_body).format(**parameters)
except Exception as e: except Exception as e:
logger.error(u"Tautulli NotificationHandler :: Unable to parse custom notification body: %s. Using fallback." % e) logger.error(u"Tautulli NotificationHandler :: Unable to parse custom notification body: %s. Using fallback." % e)

View File

@@ -61,7 +61,6 @@ import mobile_app
import pmsconnect import pmsconnect
import request import request
from plexpy.config import _BLACKLIST_KEYS, _WHITELIST_KEYS from plexpy.config import _BLACKLIST_KEYS, _WHITELIST_KEYS
from plexpy.helpers import checked
AGENT_IDS = {'growl': 0, AGENT_IDS = {'growl': 0,

View File

@@ -542,8 +542,8 @@ class PmsConnect(object):
if metadata: if metadata:
_cache_time = metadata.pop('_cache_time', 0) _cache_time = metadata.pop('_cache_time', 0)
# Return cached metadata if less than 30 minutes ago # Return cached metadata if less than METADATA_CACHE_SECONDS ago
if int(time.time()) - _cache_time <= 1800: if int(time.time()) - _cache_time <= plexpy.CONFIG.METADATA_CACHE_SECONDS:
return metadata return metadata
if rating_key: if rating_key:
@@ -1155,9 +1155,9 @@ class PmsConnect(object):
metadata['media_info'] = medias metadata['media_info'] = medias
if metadata: if metadata:
if cache_key:
metadata['_cache_time'] = int(time.time()) metadata['_cache_time'] = int(time.time())
if cache_key:
out_file_path = os.path.join(plexpy.CONFIG.CACHE_DIR, 'metadata-sessionKey-%s.json' % cache_key) out_file_path = os.path.join(plexpy.CONFIG.CACHE_DIR, 'metadata-sessionKey-%s.json' % cache_key)
try: try:
with open(out_file_path, 'w') as outFile: with open(out_file_path, 'w') as outFile:

View File

@@ -1,2 +1,2 @@
PLEXPY_BRANCH = "beta" PLEXPY_BRANCH = "beta"
PLEXPY_RELEASE_VERSION = "v2.0.9-beta" PLEXPY_RELEASE_VERSION = "v2.0.12-beta"

View File

@@ -96,7 +96,8 @@ def check_credentials(username, password, admin_login='0'):
if plexpy.CONFIG.HTTP_HASHED_PASSWORD and \ if plexpy.CONFIG.HTTP_HASHED_PASSWORD and \
username == plexpy.CONFIG.HTTP_USERNAME and check_hash(password, plexpy.CONFIG.HTTP_PASSWORD): username == plexpy.CONFIG.HTTP_USERNAME and check_hash(password, plexpy.CONFIG.HTTP_PASSWORD):
return True, u'admin' return True, u'admin'
elif username == plexpy.CONFIG.HTTP_USERNAME and password == plexpy.CONFIG.HTTP_PASSWORD: elif not plexpy.CONFIG.HTTP_HASHED_PASSWORD and \
username == plexpy.CONFIG.HTTP_USERNAME and password == plexpy.CONFIG.HTTP_PASSWORD:
return True, u'admin' return True, u'admin'
elif not admin_login == '1' and plexpy.CONFIG.ALLOW_GUEST_ACCESS and user_login(username, password): elif not admin_login == '1' and plexpy.CONFIG.ALLOW_GUEST_ACCESS and user_login(username, password):
return True, u'guest' return True, u'guest'

View File

@@ -3079,7 +3079,6 @@ class WebInterface(object):
@cherrypy.expose @cherrypy.expose
@requireAuth(member_of("admin")) @requireAuth(member_of("admin"))
@addtoapi("notify")
def send_notification(self, notifier_id=None, subject='Tautulli', body='Test notification', notify_action='', **kwargs): def send_notification(self, notifier_id=None, subject='Tautulli', body='Test notification', notify_action='', **kwargs):
""" Send a notification using Tautulli. """ Send a notification using Tautulli.
@@ -4440,7 +4439,9 @@ class WebInterface(object):
counts = {'stream_count_direct_play': 0, counts = {'stream_count_direct_play': 0,
'stream_count_direct_stream': 0, 'stream_count_direct_stream': 0,
'stream_count_transcode': 0, 'stream_count_transcode': 0,
'total_bandwidth': 0} 'total_bandwidth': 0,
'lan_bandwidth': 0,
'wan_bandwidth': 0}
for s in result['sessions']: for s in result['sessions']:
if s['transcode_decision'] == 'transcode': if s['transcode_decision'] == 'transcode':
@@ -4451,6 +4452,10 @@ class WebInterface(object):
counts['stream_count_direct_play'] += 1 counts['stream_count_direct_play'] += 1
counts['total_bandwidth'] += helpers.cast_to_int(s['bandwidth']) counts['total_bandwidth'] += helpers.cast_to_int(s['bandwidth'])
if s['location'] == 'lan':
counts['lan_bandwidth'] += helpers.cast_to_int(s['bandwidth'])
elif s['location'] == 'wan':
counts['wan_bandwidth'] += helpers.cast_to_int(s['bandwidth'])
result.update(counts) result.update(counts)