Merge pull request #259 from JonnyWong16/recently-added

Recently added
This commit is contained in:
JonnyWong16 2015-10-26 18:04:11 -07:00
commit aa75cf2b73
15 changed files with 489 additions and 162 deletions

View file

@ -11,7 +11,7 @@ data :: Usable parameters (if not applicable for media type, blank value will be
== Global keys == == Global keys ==
rating_key Returns the unique identifier for the media item. rating_key Returns the unique identifier for the media item.
type Returns the type of media. Either 'movie', 'show', 'season', 'episode', 'artist', 'album', or 'track'. media_type Returns the type of media. Either 'movie', 'show', 'season', 'episode', 'artist', 'album', or 'track'.
art Returns the location of the item's artwork art Returns the location of the item's artwork
title Returns the name of the movie, show, episode, artist, album, or track. title Returns the name of the movie, show, episode, artist, album, or track.
duration Returns the standard runtime of the media. duration Returns the standard runtime of the media.
@ -60,7 +60,7 @@ DOCUMENTATION :: END
% if data: % if data:
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
% if data['type'] != 'library': % if data['media_type'] != 'library':
<div class="art-face" style="background-image:url(pms_image_proxy?img=${data['art']}&width=1920&height=1080)"></div> <div class="art-face" style="background-image:url(pms_image_proxy?img=${data['art']}&width=1920&height=1080)"></div>
% endif % endif
<div class="summary-container"> <div class="summary-container">
@ -68,7 +68,7 @@ DOCUMENTATION :: END
<div class="col-md-12"> <div class="col-md-12">
<div class="summary-navbar-list"> <div class="summary-navbar-list">
<ul class="list-unstyled breadcrumb"> <ul class="list-unstyled breadcrumb">
% if data['type'] == 'library': % if data['media_type'] == 'library':
% if data['library'] == 'movie': % if data['library'] == 'movie':
<li class="active">Movies</li> <li class="active">Movies</li>
% elif data['library'] == 'show': % elif data['library'] == 'show':
@ -76,29 +76,29 @@ DOCUMENTATION :: END
% elif data['library'] == 'artist': % elif data['library'] == 'artist':
<li class="active">Music</li> <li class="active">Music</li>
% endif % endif
% elif data['type'] == 'movie': % elif data['media_type'] == 'movie':
<li><a href="info?item_id=movie">Movies</a></li> <li><a href="info?item_id=movie">Movies</a></li>
<li class="active">${data['title']}</li> <li class="active">${data['title']}</li>
% elif data['type'] == 'show': % elif data['media_type'] == 'show':
<li><a href="info?item_id=show">TV Shows</a></li> <li><a href="info?item_id=show">TV Shows</a></li>
<li class="active">${data['title']}</li> <li class="active">${data['title']}</li>
% elif data['type'] == 'season': % elif data['media_type'] == 'season':
<li class="hidden-xs hidden-sm"><a href="info?item_id=show">TV Shows</a></li> <li class="hidden-xs hidden-sm"><a href="info?item_id=show">TV Shows</a></li>
<li><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a></li> <li><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a></li>
<li class="active">Season ${data['index']}</li> <li class="active">Season ${data['index']}</li>
% elif data['type'] == 'episode': % elif data['media_type'] == 'episode':
<li class="hidden-xs hidden-sm"><a href="info?item_id=show">TV Shows</a></li> <li class="hidden-xs hidden-sm"><a href="info?item_id=show">TV Shows</a></li>
<li class="hidden-xs hidden-sm"><a href="info?item_id=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></li> <li class="hidden-xs hidden-sm"><a href="info?item_id=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></li>
<li><a href="info?item_id=${data['parent_rating_key']}">Season ${data['parent_index']}</a></li> <li><a href="info?item_id=${data['parent_rating_key']}">Season ${data['parent_index']}</a></li>
<li class="active">Episode ${data['index']} - ${data['title']}</li> <li class="active">Episode ${data['index']} - ${data['title']}</li>
% elif data['type'] == 'artist': % elif data['media_type'] == 'artist':
<li><a href="info?item_id=artist">Music</a></li> <li><a href="info?item_id=artist">Music</a></li>
<li class="active">${data['title']}</li> <li class="active">${data['title']}</li>
% elif data['type'] == 'album': % elif data['media_type'] == 'album':
<li class="hidden-xs hidden-sm"><a href="info?item_id=artist">Music</a></li> <li class="hidden-xs hidden-sm"><a href="info?item_id=artist">Music</a></li>
<li><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a></li> <li><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a></li>
<li class="active">${data['title']}</li> <li class="active">${data['title']}</li>
% elif data['type'] == 'track': % elif data['media_type'] == 'track':
<li class="hidden-xs hidden-sm"><a href="info?item_id=artist">Music</a></li> <li class="hidden-xs hidden-sm"><a href="info?item_id=artist">Music</a></li>
<li class="hidden-xs hidden-sm"><a href="info?item_id=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></li> <li class="hidden-xs hidden-sm"><a href="info?item_id=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></li>
<li><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a></li> <li><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a></li>
@ -108,28 +108,28 @@ DOCUMENTATION :: END
</div> </div>
</div> </div>
</div> </div>
% if data['type'] != 'library': % if data['media_type'] != 'library':
<div class="summary-content-title-wrapper"> <div class="summary-content-title-wrapper">
<div class="col-md-9"> <div class="col-md-9">
<div class="summary-content-poster hidden-xs hidden-sm"> <div class="summary-content-poster hidden-xs hidden-sm">
% if data['type'] == 'track': % if data['media_type'] == 'track':
<a href="http://app.plex.tv/web/app#!/server/${config['pms_identifier']}/details/%2Flibrary%2Fmetadata%2F${data['parent_rating_key']}" target="Plex/Web" title="View in Plex/Web"> <a href="http://app.plex.tv/web/app#!/server/${config['pms_identifier']}/details/%2Flibrary%2Fmetadata%2F${data['parent_rating_key']}" target="Plex/Web" title="View in Plex/Web">
% elif data['type'] != 'library': % elif data['media_type'] != 'library':
<a href="http://app.plex.tv/web/app#!/server/${config['pms_identifier']}/details/%2Flibrary%2Fmetadata%2F${data['rating_key']}" target="Plex/Web" title="View in Plex/Web"> <a href="http://app.plex.tv/web/app#!/server/${config['pms_identifier']}/details/%2Flibrary%2Fmetadata%2F${data['rating_key']}" target="Plex/Web" title="View in Plex/Web">
% endif % endif
% if data['type'] == 'episode': % if data['media_type'] == 'episode':
<div class="summary-poster-face-episode" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=500&height=280&fallback=poster);"> <div class="summary-poster-face-episode" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=500&height=280&fallback=poster);">
<div class="summary-poster-face-overlay"> <div class="summary-poster-face-overlay">
<span></span> <span></span>
</div> </div>
</div> </div>
% elif data['type'] == 'artist' or data['type'] == 'album' or data['type'] == 'track': % elif data['media_type'] == 'artist' or data['media_type'] == 'album' or data['media_type'] == 'track':
<div class="summary-poster-face-track" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=500&height=500&fallback=poster);"> <div class="summary-poster-face-track" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=500&height=500&fallback=poster);">
<div class="summary-poster-face-overlay"> <div class="summary-poster-face-overlay">
<span></span> <span></span>
</div> </div>
</div> </div>
% elif data['type'] != 'library': % elif data['media_type'] != 'library':
<div class="summary-poster-face" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=300&height=450&fallback=poster);"> <div class="summary-poster-face" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=300&height=450&fallback=poster);">
<div class="summary-poster-face-overlay"> <div class="summary-poster-face-overlay">
<span></span> <span></span>
@ -139,19 +139,19 @@ DOCUMENTATION :: END
</a> </a>
</div> </div>
<div class="summary-content-title"> <div class="summary-content-title">
% if data['type'] == 'movie' or data['type'] == 'show' or data['type'] == 'artist': % if data['media_type'] == 'movie' or data['media_type'] == 'show' or data['media_type'] == 'artist':
<h1>&nbsp;</h1><h1>${data['title']}</h1> <h1>&nbsp;</h1><h1>${data['title']}</h1>
% elif data['type'] == 'season': % elif data['media_type'] == 'season':
<h1>&nbsp;</h1><h1><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a></h1> <h1>&nbsp;</h1><h1><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a></h1>
<h3 class="hidden-xs">S${data['index']}</h3> <h3 class="hidden-xs">S${data['index']}</h3>
% elif data['type'] == 'episode': % elif data['media_type'] == 'episode':
<h1><a href="info?item_id=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></h1> <h1><a href="info?item_id=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></h1>
<h2>${data['title']}</h2> <h2>${data['title']}</h2>
<h3 class="hidden-xs">S${data['parent_index']} &middot; E${data['index']}</h3> <h3 class="hidden-xs">S${data['parent_index']} &middot; E${data['index']}</h3>
% elif data['type'] == 'album': % elif data['media_type'] == 'album':
<h1><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a></h1> <h1><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a></h1>
<h2>${data['title']}</h2> <h2>${data['title']}</h2>
% elif data['type'] == 'track': % elif data['media_type'] == 'track':
<h1><a href="info?item_id=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></h1> <h1><a href="info?item_id=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></h1>
<h2><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a> - ${data['title']}</h2> <h2><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a> - ${data['title']}</h2>
<h3 class="hidden-xs">T${data['index']}</h3> <h3 class="hidden-xs">T${data['index']}</h3>
@ -161,13 +161,13 @@ DOCUMENTATION :: END
</div> </div>
% endif % endif
<div class="summary-content-wrapper"> <div class="summary-content-wrapper">
% if data['type'] != 'library': % if data['media_type'] != 'library':
<div class="col-md-9"> <div class="col-md-9">
% if data['type'] == 'movie' or data['type'] == 'show' or data['type'] == 'season': % if data['media_type'] == 'movie' or data['media_type'] == 'show' or data['media_type'] == 'season':
<div class="summary-content-padding hidden-xs hidden-sm" style="height: 275px;"></div> <div class="summary-content-padding hidden-xs hidden-sm" style="height: 275px;"></div>
% elif data['type'] == 'episode': % elif data['media_type'] == 'episode':
<div class="summary-content-padding hidden-xs hidden-sm" style="height: 40px;"></div> <div class="summary-content-padding hidden-xs hidden-sm" style="height: 40px;"></div>
% elif data['type'] == 'artist' or data['type'] == 'album' or data['type'] == 'track': % elif data['media_type'] == 'artist' or data['media_type'] == 'album' or data['media_type'] == 'track':
<div class="summary-content-padding hidden-xs hidden-sm" style="height: 150px;"></div> <div class="summary-content-padding hidden-xs hidden-sm" style="height: 150px;"></div>
% else: % else:
<div class="summary-content-padding hidden-xs hidden-sm"></div> <div class="summary-content-padding hidden-xs hidden-sm"></div>
@ -189,13 +189,13 @@ DOCUMENTATION :: END
% endif % endif
</div> </div>
<div class="summary-content-details-tag"> <div class="summary-content-details-tag">
% if data['type'] == 'movie': % if data['media_type'] == 'movie':
Year <strong> ${data['year']}</strong> Year <strong> ${data['year']}</strong>
% elif data['type'] == 'show': % elif data['media_type'] == 'show':
Aired <strong> ${data['year']}</strong> Aired <strong> ${data['year']}</strong>
% elif data['type'] == 'episode': % elif data['media_type'] == 'episode':
Aired <strong> <span id="airdate">${data['originally_available_at']}</span></strong> Aired <strong> <span id="airdate">${data['originally_available_at']}</span></strong>
% elif data['type'] == 'album' or data['type'] == 'track': % elif data['media_type'] == 'album' or data['media_type'] == 'track':
Released <strong> ${data['year']}</strong> Released <strong> ${data['year']}</strong>
% endif % endif
</div> </div>
@ -268,7 +268,7 @@ DOCUMENTATION :: END
% endif % endif
</div> </div>
</div> </div>
% if data['type'] == 'show': % if data['media_type'] == 'show':
<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">
@ -279,7 +279,7 @@ DOCUMENTATION :: END
<div id="children-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading season list...</div> <div id="children-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading season list...</div>
</div> </div>
</div> </div>
% elif data['type'] == 'season': % elif data['media_type'] == 'season':
<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">
@ -290,7 +290,7 @@ DOCUMENTATION :: END
<div id="children-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading episode list...</div> <div id="children-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading episode list...</div>
</div> </div>
</div> </div>
% elif data['type'] == 'artist': % elif data['media_type'] == 'artist':
<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">
@ -301,7 +301,7 @@ DOCUMENTATION :: END
<div id="children-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading album list...</div> <div id="children-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading album list...</div>
</div> </div>
</div> </div>
% elif data['type'] == 'album': % elif data['media_type'] == 'album':
<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">
@ -499,7 +499,7 @@ DOCUMENTATION :: END
% if data: % if data:
<script src="interfaces/default/js/tables/history_table.js"></script> <script src="interfaces/default/js/tables/history_table.js"></script>
% if data['type'] == 'library': % if data['media_type'] == 'library':
<script> <script>
function get_history() { function get_history() {
history_table_options.ajax = { history_table_options.ajax = {
@ -507,12 +507,12 @@ DOCUMENTATION :: END
type: 'post', type: 'post',
data: function ( d ) { data: function ( d ) {
return { 'json_data': JSON.stringify( d ), return { 'json_data': JSON.stringify( d ),
'media_type': "${data['media_type']}" }; 'media_type': "${data['media_type_filter']}" };
} }
} }
} }
</script> </script>
% elif data['type'] == 'show' or data['type'] == 'artist': % elif data['media_type'] == 'show' or data['media_type'] == 'artist':
<script> <script>
function get_history() { function get_history() {
history_table_options.ajax = { history_table_options.ajax = {
@ -525,7 +525,7 @@ DOCUMENTATION :: END
} }
} }
</script> </script>
% elif data['type'] == 'season' or data['type'] == 'album': % elif data['media_type'] == 'season' or data['media_type'] == 'album':
<script> <script>
function get_history() { function get_history() {
history_table_options.ajax = { history_table_options.ajax = {
@ -538,7 +538,7 @@ DOCUMENTATION :: END
} }
} }
</script> </script>
% elif data['type'] == 'episode' or data['type'] == 'track' or data['type'] == 'movie': % elif data['media_type'] == 'episode' or data['media_type'] == 'track' or data['media_type'] == 'movie':
<script> <script>
function get_history() { function get_history() {
history_table_options.ajax = { history_table_options.ajax = {
@ -599,7 +599,7 @@ DOCUMENTATION :: END
}); });
}); });
</script> </script>
% if data['type'] == 'show' or data['type'] == 'season' or data['type'] == 'artist' or data['type'] == 'album': % if data['media_type'] == 'show' or data['media_type'] == 'season' or data['media_type'] == 'artist' or data['media_type'] == 'album':
<script> <script>
$.ajax({ $.ajax({
url: 'get_item_children', url: 'get_item_children',
@ -611,14 +611,14 @@ DOCUMENTATION :: END
}); });
</script> </script>
% endif % endif
% if data['type'] != 'library': % if data['media_type'] != 'library':
<script> <script>
$('#row-edit-mode').after('<a href="info?item_id=${data['rating_key']}" class="btn btn-danger btn-edit" id="fix-metadata" \ $('#row-edit-mode').after('<a href="info?item_id=${data['rating_key']}" class="btn btn-danger btn-edit" id="fix-metadata" \
data-toggle="tooltip" data-placement="left" title="Fix metadata if the item was moved in Plex"><i class="fa fa-wrench"></i> Fix Metadata</a>'); data-toggle="tooltip" data-placement="left" title="Fix metadata if the item was moved in Plex"><i class="fa fa-wrench"></i> Fix Metadata</a>');
$('#fix-metadata').tooltip(); $('#fix-metadata').tooltip();
</script> </script>
% endif % endif
% if data['type'] != 'library' and data['rating']: % if data['media_type'] != 'library' and data['rating']:
<script> <script>
// Convert rating to 5 star rating type // Convert rating to 5 star rating type
var starRating = Math.round(${data['rating']} / 2); var starRating = Math.round(${data['rating']} / 2);

View file

@ -57,6 +57,13 @@ from plexpy import helpers
</label> </label>
<p class="help-block">Trigger notification when a media item triggers the defined buffer threshold.</p> <p class="help-block">Trigger notification when a media item triggers the defined buffer threshold.</p>
</div> </div>
<div class="checkbox">
<label>
<input type="checkbox" data-size="small" data-id="${data['id']}" data-config-name="${data['config_prefix']}_on_created" ${helpers.checked(data['on_created'])} class="toggle-switches">
Notify on recently added
</label>
<p class="help-block">Trigger notification when a media item is added to the Plex Media Server.</p>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -81,6 +88,7 @@ from plexpy import helpers
console.log('success'); console.log('success');
} }
}); });
$('.toggle-notification-triggers-modal[data-id=' + configToggle + ']').addClass('active');
} else { } else {
var data = {}; var data = {};
data[$(this).data('config-name')] = 0; data[$(this).data('config-name')] = 0;
@ -92,6 +100,9 @@ from plexpy import helpers
console.log('success'); console.log('success');
} }
}); });
if (!($('.toggle-switches').is(":checked"))) {
$('.toggle-notification-triggers-modal[data-id=' + configToggle + ']').removeClass('active');
}
} }
}); });
</script> </script>

View file

@ -11,13 +11,13 @@ data[array_index] :: Usable parameters
== Global keys == == Global keys ==
rating_key Returns the unique identifier for the media item. rating_key Returns the unique identifier for the media item.
type Returns the type of media. Either 'movie' or 'season'. media_type Returns the media type of media. Either 'movie' or 'season' or 'album'.
thumb Returns the location of the item's thumbnail. Use with pms_image_proxy. thumb Returns the location of the item's thumbnail. Use with pms_image_proxy.
added_at Returns the time when the media was added to the library. added_at Returns the time when the media was added to the library.
title Returns the name of the movie or season. title Returns the name of the movie or season.
parent_title Returns the name of the TV Show a season belongs too. parent_title Returns the name of the TV Show a season belongs too.
== Only if 'type' is 'movie' == == Only if 'media_type' is 'movie' ==
year Returns the movie release year. year Returns the movie release year.
DOCUMENTATION :: END DOCUMENTATION :: END
@ -29,7 +29,7 @@ DOCUMENTATION :: END
% for item in data: % for item in data:
<div class="dashboard-recent-media-instance"> <div class="dashboard-recent-media-instance">
<li> <li>
% if item['type'] == 'season' or item['type'] == 'movie': % if item['media_type'] == 'season' or item['media_type'] == 'movie':
<a href="info?item_id=${item['rating_key']}"> <a href="info?item_id=${item['rating_key']}">
<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);">
@ -43,16 +43,16 @@ DOCUMENTATION :: END
</div> </div>
</div> </div>
<div class="dashboard-recent-media-metacontainer"> <div class="dashboard-recent-media-metacontainer">
% if item['type'] == 'season': % if item['media_type'] == 'season':
<h3 title="${item['parent_title']}">${item['parent_title']}</h3> <h3 title="${item['parent_title']}">${item['parent_title']}</h3>
<h3 class="text-muted">${item['title']}</h3> <h3 class="text-muted">${item['title']}</h3>
% elif item['type'] == 'movie': % elif item['media_type'] == 'movie':
<h3 title="${item['title']}">${item['title']}</h3> <h3 title="${item['title']}">${item['title']}</h3>
<h3 class="text-muted">${item['year']}</h3> <h3 class="text-muted">${item['year']}</h3>
% endif % endif
</div> </div>
</a> </a>
% elif item['type'] == 'album': % elif item['media_type'] == 'album':
<a href="info?item_id=${item['rating_key']}"> <a href="info?item_id=${item['rating_key']}">
<div class="dashboard-recent-media-cover"> <div class="dashboard-recent-media-cover">
<div class="dashboard-recent-media-cover-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=300&fallback=cover);"> <div class="dashboard-recent-media-cover-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=300&fallback=cover);">

View file

@ -3,7 +3,7 @@
import plexpy import plexpy
from plexpy import notifiers, common, versioncheck from plexpy import notifiers, common, versioncheck
available_notification_agents = notifiers.available_notification_agents() available_notification_agents = sorted(notifiers.available_notification_agents(), key=lambda k: k['name'])
%> %>
<%def name="headIncludes()"> <%def name="headIncludes()">
</%def> </%def>
@ -502,6 +502,12 @@ available_notification_agents = notifiers.available_notification_agents()
</label> </label>
<p class="help-block">Disable to prevent consecutive notifications (i.e. both watched &amp; stopped notifications).</p> <p class="help-block">Disable to prevent consecutive notifications (i.e. both watched &amp; stopped notifications).</p>
</div> </div>
<div class="checkbox">
<label>
<input type="checkbox" name="notify_recently_added_grandparent" id="notify_recently_added_grandparent" value="1" ${config['notify_recently_added_grandparent']}> Group notifications for recently added TV Shows or Music
</label>
<p class="help-block">Enable to only get one notification for recently added Episodes or Tracks. Movies are unaffected.</p>
</div>
<div class="padded-header"> <div class="padded-header">
<h3>Custom Notification Messages</h3> <h3>Custom Notification Messages</h3>
@ -515,7 +521,7 @@ available_notification_agents = notifiers.available_notification_agents()
<a href="#notify-text-tags-modal" data-toggle="modal">here</a> to view usage information. <a href="#notify-text-tags-modal" data-toggle="modal">here</a> to view usage information.
</p> </p>
<br/> <br/>
<ul id="accordion" class="accordion list-unstyled"> <ul id="accordion-session" class="accordion list-unstyled">
<li> <li>
<div class="link"><i class="fa fa-play"></i>Playback Start<i class="fa fa-chevron-down"></i></div> <div class="link"><i class="fa fa-play"></i>Playback Start<i class="fa fa-chevron-down"></i></div>
<ul class="submenu"> <ul class="submenu">
@ -619,6 +625,25 @@ available_notification_agents = notifiers.available_notification_agents()
</ul> </ul>
</li> </li>
</ul> </ul>
<ul id="accordion-timeline" class="accordion list-unstyled">
<li>
<div class="link"><i class="fa fa-download"></i>Recently Added<i class="fa fa-chevron-down"></i></div>
<ul class="submenu">
<li>
<div class="form-group">
<label for="notify_on_created_subject_text">Subject Line</label>
<input class="form-control" type="text" id="notify_on_created_subject_text" name="notify_on_created_subject_text" value="${config['notify_on_created_subject_text']}" data-parsley-trigger="change" required>
<p class="help-block">Set a custom subject line.</p>
</div>
<div class="form-group">
<label for="notify_on_created_body_text">Message Body</label>
<input class="form-control" type="text" id="notify_on_created_body_text" name="notify_on_created_body_text" value="${config['notify_on_created_body_text']}" data-parsley-trigger="change" required>
<p class="help-block">Set a custom body.</p>
</div>
</li>
</ul>
</li>
</ul>
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p> <p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
</div> </div>
@ -635,7 +660,7 @@ available_notification_agents = notifiers.available_notification_agents()
% for agent in available_notification_agents: % for agent in available_notification_agents:
<li> <li>
<span> <span>
% if agent['on_play'] or agent['on_stop'] or agent['on_pause'] or agent['on_resume'] or agent['on_buffer'] or agent['on_watched']: % if agent['on_play'] or agent['on_stop'] or agent['on_pause'] or agent['on_resume'] or agent['on_buffer'] or agent['on_watched'] or agent['on_created']:
<a href="javascript:void(0)" data-target="#notification-triggers-modal" data-id="${agent['id']}" class="toggle-notification-triggers-modal toggle-left active" data-toggle="modal"><i class="fa fa-lg fa-bell"></i></a> <a href="javascript:void(0)" data-target="#notification-triggers-modal" data-id="${agent['id']}" class="toggle-notification-triggers-modal toggle-left active" data-toggle="modal"><i class="fa fa-lg fa-bell"></i></a>
% else: % else:
<a href="javascript:void(0)" data-target="#notification-triggers-modal" data-id="${agent['id']}" class="toggle-notification-triggers-modal toggle-left" data-toggle="modal"><i class="fa fa-lg fa-bell"></i></a> <a href="javascript:void(0)" data-target="#notification-triggers-modal" data-id="${agent['id']}" class="toggle-notification-triggers-modal toggle-left" data-toggle="modal"><i class="fa fa-lg fa-bell"></i></a>
@ -1292,7 +1317,8 @@ $(document).ready(function() {
} }
} }
var accordion = new Accordion($('#accordion'), false); var accordion_session = new Accordion($('#accordion-session'), false);
var accordion_timeline = new Accordion($('#accordion-timeline'), false);
var cards = "${config['home_stats_cards']}".split(/[\s,]+/); var cards = "${config['home_stats_cards']}".split(/[\s,]+/);
cards.forEach(function (item) { cards.forEach(function (item) {

View file

@ -286,6 +286,8 @@ def initialize_scheduler():
if not CONFIG.MONITORING_USE_WEBSOCKET or POLLING_FAILOVER: if not CONFIG.MONITORING_USE_WEBSOCKET or POLLING_FAILOVER:
schedule_job(activity_pinger.check_active_sessions, 'Check for active sessions', schedule_job(activity_pinger.check_active_sessions, 'Check for active sessions',
hours=0, minutes=0, seconds=seconds) hours=0, minutes=0, seconds=seconds)
schedule_job(activity_pinger.check_recently_added, 'Check for recently added items',
hours=0, minutes=0, seconds=seconds)
# Refresh the users list # Refresh the users list
if CONFIG.REFRESH_USERS_INTERVAL: if CONFIG.REFRESH_USERS_INTERVAL:
@ -552,7 +554,7 @@ def dbcheck():
'CREATE TABLE IF NOT EXISTS notify_log (id INTEGER PRIMARY KEY AUTOINCREMENT, ' 'CREATE TABLE IF NOT EXISTS notify_log (id INTEGER PRIMARY KEY AUTOINCREMENT, '
'session_key INTEGER, rating_key INTEGER, user_id INTEGER, user TEXT, ' 'session_key INTEGER, rating_key INTEGER, user_id INTEGER, user TEXT, '
'agent_id INTEGER, agent_name TEXT, on_play INTEGER, on_stop INTEGER, on_watched INTEGER, ' 'agent_id INTEGER, agent_name TEXT, on_play INTEGER, on_stop INTEGER, on_watched INTEGER, '
'on_pause INTEGER, on_resume INTEGER, on_buffer INTEGER)' 'on_pause INTEGER, on_resume INTEGER, on_buffer INTEGER, on_created INTEGER)'
) )
# Upgrade users table from earlier versions # Upgrade users table from earlier versions
@ -588,6 +590,15 @@ def dbcheck():
'ALTER TABLE notify_log ADD COLUMN on_buffer INTEGER' 'ALTER TABLE notify_log ADD COLUMN on_buffer INTEGER'
) )
# Upgrade notify_log table from earlier versions
try:
c_db.execute('SELECT on_created from notify_log')
except sqlite3.OperationalError:
logger.debug(u"Altering database. Updating database table notify_log.")
c_db.execute(
'ALTER TABLE notify_log ADD COLUMN on_created INTEGER'
)
# Upgrade sessions table from earlier versions # Upgrade sessions table from earlier versions
try: try:
c_db.execute('SELECT buffer_count from sessions') c_db.execute('SELECT buffer_count from sessions')

View file

@ -1,4 +1,4 @@
# This file is part of PlexPy. # This file is part of PlexPy.
# #
# PlexPy is free software: you can redistribute it and/or modify # PlexPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -213,3 +213,53 @@ class ActivityHandler(object):
# We don't have this session in our table yet, start a new one. # We don't have this session in our table yet, start a new one.
if this_state != 'buffering': if this_state != 'buffering':
self.on_start() self.on_start()
class TimelineHandler(object):
def __init__(self, timeline):
self.timeline = timeline
# print timeline
def is_item(self):
if 'itemID' in self.timeline:
return True
return False
def get_rating_key(self):
if self.is_item():
return int(self.timeline['itemID'])
return None
def get_metadata(self):
pms_connect = pmsconnect.PmsConnect()
metadata_list = pms_connect.get_metadata_details(self.get_rating_key())
if metadata_list:
return metadata_list['metadata']
return None
def on_created(self):
if self.is_item():
logger.debug(u"PlexPy TimelineHandler :: Library item %s has been added to Plex." % str(self.get_rating_key()))
# Fire off notifications
threading.Thread(target=notification_handler.notify_timeline,
kwargs=dict(timeline_data=self.get_metadata(), notify_action='created')).start()
# This function receives events from our websocket connection
def process(self):
if self.is_item():
this_state = self.timeline['state']
this_type = self.timeline['type']
this_metadataState = self.timeline.get('metadataState', None)
this_mediaState = self.timeline.get('mediaState', None)
# state: 5: done processing metadata
# type: 1: movie, 2: tv show, 4: episode, 8: artist, 10: track
types = [1, 2, 4, 8, 10]
if this_state == 5 and this_type in types and this_metadataState == None and this_mediaState == None:
self.on_created()

View file

@ -1,4 +1,4 @@
# This file is part of PlexPy. # This file is part of PlexPy.
# #
# PlexPy is free software: you can redistribute it and/or modify # PlexPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -162,3 +162,50 @@ def check_active_sessions(ws_request=False):
monitor_process.write_session(session) monitor_process.write_session(session)
else: else:
logger.debug(u"PlexPy Monitor :: Unable to read session list.") logger.debug(u"PlexPy Monitor :: Unable to read session list.")
def check_recently_added():
with monitor_lock:
current_time = int(time.time())
pms_connect = pmsconnect.PmsConnect()
recently_added_list = pms_connect.get_recently_added_details(count='10')
if recently_added_list:
recently_added = recently_added_list['recently_added']
for item in recently_added:
if int(item['added_at']) >= current_time - plexpy.CONFIG.MONITORING_INTERVAL:
if item['media_type'] == 'movie':
metadata_list = pms_connect.get_metadata_details(item['rating_key'])
if metadata_list:
metadata = [metadata_list['metadata']]
else:
logger.error(u"PlexPy Monitor :: Unable to retrieve metadata for rating_key %s" % str(item['rating_key']))
elif plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT:
metadata_list = pms_connect.get_metadata_details(item['parent_rating_key'])
if metadata_list:
metadata = [metadata_list['metadata']]
else:
logger.error(u"PlexPy Monitor :: Unable to retrieve metadata for parent_rating_key %s" % str(item['parent_rating_key']))
else:
metadata_list = pms_connect.get_metadata_children_details(item['rating_key'])
if metadata_list:
metadata = metadata_list['metadata']
else:
logger.error(u"PlexPy Monitor :: Unable to retrieve children metadata for rating_key" % str(item['rating_key']))
if metadata:
for item in metadata:
if (plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT \
and int(item['updated_at']) >= current_time - plexpy.CONFIG.MONITORING_INTERVAL) \
or (not plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT \
and int(item['added_at']) >= current_time - plexpy.CONFIG.MONITORING_INTERVAL):
logger.debug(u"PlexPy Monitor :: Library item %s has been added to Plex." % str(item['rating_key']))
# Fire off notifications
threading.Thread(target=notification_handler.notify_timeline,
kwargs=dict(timeline_data=item, notify_action='created')).start()

View file

@ -44,6 +44,7 @@ _CONFIG_DEFINITIONS = {
'BOXCAR_ON_RESUME': (int, 'Boxcar', 0), 'BOXCAR_ON_RESUME': (int, 'Boxcar', 0),
'BOXCAR_ON_BUFFER': (int, 'Boxcar', 0), 'BOXCAR_ON_BUFFER': (int, 'Boxcar', 0),
'BOXCAR_ON_WATCHED': (int, 'Boxcar', 0), 'BOXCAR_ON_WATCHED': (int, 'Boxcar', 0),
'BOXCAR_ON_CREATED': (int, 'Boxcar', 0),
'BUFFER_THRESHOLD': (int, 'Monitoring', 3), 'BUFFER_THRESHOLD': (int, 'Monitoring', 3),
'BUFFER_WAIT': (int, 'Monitoring', 900), 'BUFFER_WAIT': (int, 'Monitoring', 900),
'CACHE_DIR': (str, 'General', ''), 'CACHE_DIR': (str, 'General', ''),
@ -68,6 +69,7 @@ _CONFIG_DEFINITIONS = {
'EMAIL_ON_RESUME': (int, 'Email', 0), 'EMAIL_ON_RESUME': (int, 'Email', 0),
'EMAIL_ON_BUFFER': (int, 'Email', 0), 'EMAIL_ON_BUFFER': (int, 'Email', 0),
'EMAIL_ON_WATCHED': (int, 'Email', 0), 'EMAIL_ON_WATCHED': (int, 'Email', 0),
'EMAIL_ON_CREATED': (int, 'Email', 0),
'ENABLE_HTTPS': (int, 'General', 0), 'ENABLE_HTTPS': (int, 'General', 0),
'FIRST_RUN_COMPLETE': (int, 'General', 0), 'FIRST_RUN_COMPLETE': (int, 'General', 0),
'FREEZE_DB': (int, 'General', 0), 'FREEZE_DB': (int, 'General', 0),
@ -84,6 +86,7 @@ _CONFIG_DEFINITIONS = {
'GROWL_ON_RESUME': (int, 'Growl', 0), 'GROWL_ON_RESUME': (int, 'Growl', 0),
'GROWL_ON_BUFFER': (int, 'Growl', 0), 'GROWL_ON_BUFFER': (int, 'Growl', 0),
'GROWL_ON_WATCHED': (int, 'Growl', 0), 'GROWL_ON_WATCHED': (int, 'Growl', 0),
'GROWL_ON_CREATED': (int, 'Growl', 0),
'HOME_LIBRARY_CARDS': (str, 'General', 'library_statistics_first'), 'HOME_LIBRARY_CARDS': (str, 'General', 'library_statistics_first'),
'HOME_STATS_LENGTH': (int, 'General', 30), 'HOME_STATS_LENGTH': (int, 'General', 30),
'HOME_STATS_TYPE': (int, 'General', 0), 'HOME_STATS_TYPE': (int, 'General', 0),
@ -108,6 +111,7 @@ _CONFIG_DEFINITIONS = {
'IFTTT_ON_RESUME': (int, 'IFTTT', 0), 'IFTTT_ON_RESUME': (int, 'IFTTT', 0),
'IFTTT_ON_BUFFER': (int, 'IFTTT', 0), 'IFTTT_ON_BUFFER': (int, 'IFTTT', 0),
'IFTTT_ON_WATCHED': (int, 'IFTTT', 0), 'IFTTT_ON_WATCHED': (int, 'IFTTT', 0),
'IFTTT_ON_CREATED': (int, 'IFTTT', 0),
'JOURNAL_MODE': (str, 'Advanced', 'wal'), 'JOURNAL_MODE': (str, 'Advanced', 'wal'),
'LAUNCH_BROWSER': (int, 'General', 1), 'LAUNCH_BROWSER': (int, 'General', 1),
'LOG_DIR': (str, 'General', ''), 'LOG_DIR': (str, 'General', ''),
@ -132,7 +136,9 @@ _CONFIG_DEFINITIONS = {
'NMA_ON_RESUME': (int, 'NMA', 0), 'NMA_ON_RESUME': (int, 'NMA', 0),
'NMA_ON_BUFFER': (int, 'NMA', 0), 'NMA_ON_BUFFER': (int, 'NMA', 0),
'NMA_ON_WATCHED': (int, 'NMA', 0), 'NMA_ON_WATCHED': (int, 'NMA', 0),
'NMA_ON_CREATED': (int, 'NMA', 0),
'NOTIFY_CONSECUTIVE': (int, 'Monitoring', 1), 'NOTIFY_CONSECUTIVE': (int, 'Monitoring', 1),
'NOTIFY_RECENTLY_ADDED_GRANDPARENT': (int, 'Monitoring', 0),
'NOTIFY_WATCHED_PERCENT': (int, 'Monitoring', 85), 'NOTIFY_WATCHED_PERCENT': (int, 'Monitoring', 85),
'NOTIFY_ON_START_SUBJECT_TEXT': (str, 'Monitoring', 'PlexPy ({server_name})'), 'NOTIFY_ON_START_SUBJECT_TEXT': (str, 'Monitoring', 'PlexPy ({server_name})'),
'NOTIFY_ON_START_BODY_TEXT': (str, 'Monitoring', '{user} ({player}) started playing {title}.'), 'NOTIFY_ON_START_BODY_TEXT': (str, 'Monitoring', '{user} ({player}) started playing {title}.'),
@ -146,6 +152,8 @@ _CONFIG_DEFINITIONS = {
'NOTIFY_ON_BUFFER_BODY_TEXT': (str, 'Monitoring', '{user} ({player}) is buffering {title}.'), 'NOTIFY_ON_BUFFER_BODY_TEXT': (str, 'Monitoring', '{user} ({player}) is buffering {title}.'),
'NOTIFY_ON_WATCHED_SUBJECT_TEXT': (str, 'Monitoring', 'PlexPy ({server_name})'), 'NOTIFY_ON_WATCHED_SUBJECT_TEXT': (str, 'Monitoring', 'PlexPy ({server_name})'),
'NOTIFY_ON_WATCHED_BODY_TEXT': (str, 'Monitoring', '{user} ({player}) has watched {title}.'), 'NOTIFY_ON_WATCHED_BODY_TEXT': (str, 'Monitoring', '{user} ({player}) has watched {title}.'),
'NOTIFY_ON_CREATED_SUBJECT_TEXT': (str, 'Monitoring', 'PlexPy ({server_name})'),
'NOTIFY_ON_CREATED_BODY_TEXT': (str, 'Monitoring', '{title} was recently added to Plex.'),
'OSX_NOTIFY_APP': (str, 'OSX_Notify', '/Applications/PlexPy'), 'OSX_NOTIFY_APP': (str, 'OSX_Notify', '/Applications/PlexPy'),
'OSX_NOTIFY_ENABLED': (int, 'OSX_Notify', 0), 'OSX_NOTIFY_ENABLED': (int, 'OSX_Notify', 0),
'OSX_NOTIFY_ON_PLAY': (int, 'OSX_Notify', 0), 'OSX_NOTIFY_ON_PLAY': (int, 'OSX_Notify', 0),
@ -154,6 +162,7 @@ _CONFIG_DEFINITIONS = {
'OSX_NOTIFY_ON_RESUME': (int, 'OSX_Notify', 0), 'OSX_NOTIFY_ON_RESUME': (int, 'OSX_Notify', 0),
'OSX_NOTIFY_ON_BUFFER': (int, 'OSX_Notify', 0), 'OSX_NOTIFY_ON_BUFFER': (int, 'OSX_Notify', 0),
'OSX_NOTIFY_ON_WATCHED': (int, 'OSX_Notify', 0), 'OSX_NOTIFY_ON_WATCHED': (int, 'OSX_Notify', 0),
'OSX_NOTIFY_ON_CREATED': (int, 'OSX_Notify', 0),
'PLEX_CLIENT_HOST': (str, 'Plex', ''), 'PLEX_CLIENT_HOST': (str, 'Plex', ''),
'PLEX_ENABLED': (int, 'Plex', 0), 'PLEX_ENABLED': (int, 'Plex', 0),
'PLEX_PASSWORD': (str, 'Plex', ''), 'PLEX_PASSWORD': (str, 'Plex', ''),
@ -164,6 +173,7 @@ _CONFIG_DEFINITIONS = {
'PLEX_ON_RESUME': (int, 'Plex', 0), 'PLEX_ON_RESUME': (int, 'Plex', 0),
'PLEX_ON_BUFFER': (int, 'Plex', 0), 'PLEX_ON_BUFFER': (int, 'Plex', 0),
'PLEX_ON_WATCHED': (int, 'Plex', 0), 'PLEX_ON_WATCHED': (int, 'Plex', 0),
'PLEX_ON_CREATED': (int, 'Plex', 0),
'PROWL_ENABLED': (int, 'Prowl', 0), 'PROWL_ENABLED': (int, 'Prowl', 0),
'PROWL_KEYS': (str, 'Prowl', ''), 'PROWL_KEYS': (str, 'Prowl', ''),
'PROWL_PRIORITY': (int, 'Prowl', 0), 'PROWL_PRIORITY': (int, 'Prowl', 0),
@ -173,6 +183,7 @@ _CONFIG_DEFINITIONS = {
'PROWL_ON_RESUME': (int, 'Prowl', 0), 'PROWL_ON_RESUME': (int, 'Prowl', 0),
'PROWL_ON_BUFFER': (int, 'Prowl', 0), 'PROWL_ON_BUFFER': (int, 'Prowl', 0),
'PROWL_ON_WATCHED': (int, 'Prowl', 0), 'PROWL_ON_WATCHED': (int, 'Prowl', 0),
'PROWL_ON_CREATED': (int, 'Prowl', 0),
'PUSHALOT_APIKEY': (str, 'Pushalot', ''), 'PUSHALOT_APIKEY': (str, 'Pushalot', ''),
'PUSHALOT_ENABLED': (int, 'Pushalot', 0), 'PUSHALOT_ENABLED': (int, 'Pushalot', 0),
'PUSHALOT_ON_PLAY': (int, 'Pushalot', 0), 'PUSHALOT_ON_PLAY': (int, 'Pushalot', 0),
@ -181,6 +192,7 @@ _CONFIG_DEFINITIONS = {
'PUSHALOT_ON_RESUME': (int, 'Pushalot', 0), 'PUSHALOT_ON_RESUME': (int, 'Pushalot', 0),
'PUSHALOT_ON_BUFFER': (int, 'Pushalot', 0), 'PUSHALOT_ON_BUFFER': (int, 'Pushalot', 0),
'PUSHALOT_ON_WATCHED': (int, 'Pushalot', 0), 'PUSHALOT_ON_WATCHED': (int, 'Pushalot', 0),
'PUSHALOT_ON_CREATED': (int, 'Pushalot', 0),
'PUSHBULLET_APIKEY': (str, 'PushBullet', ''), 'PUSHBULLET_APIKEY': (str, 'PushBullet', ''),
'PUSHBULLET_DEVICEID': (str, 'PushBullet', ''), 'PUSHBULLET_DEVICEID': (str, 'PushBullet', ''),
'PUSHBULLET_CHANNEL_TAG': (str, 'PushBullet', ''), 'PUSHBULLET_CHANNEL_TAG': (str, 'PushBullet', ''),
@ -191,6 +203,7 @@ _CONFIG_DEFINITIONS = {
'PUSHBULLET_ON_RESUME': (int, 'PushBullet', 0), 'PUSHBULLET_ON_RESUME': (int, 'PushBullet', 0),
'PUSHBULLET_ON_BUFFER': (int, 'PushBullet', 0), 'PUSHBULLET_ON_BUFFER': (int, 'PushBullet', 0),
'PUSHBULLET_ON_WATCHED': (int, 'PushBullet', 0), 'PUSHBULLET_ON_WATCHED': (int, 'PushBullet', 0),
'PUSHBULLET_ON_CREATED': (int, 'PushBullet', 0),
'PUSHOVER_APITOKEN': (str, 'Pushover', ''), 'PUSHOVER_APITOKEN': (str, 'Pushover', ''),
'PUSHOVER_ENABLED': (int, 'Pushover', 0), 'PUSHOVER_ENABLED': (int, 'Pushover', 0),
'PUSHOVER_KEYS': (str, 'Pushover', ''), 'PUSHOVER_KEYS': (str, 'Pushover', ''),
@ -202,6 +215,7 @@ _CONFIG_DEFINITIONS = {
'PUSHOVER_ON_RESUME': (int, 'Pushover', 0), 'PUSHOVER_ON_RESUME': (int, 'Pushover', 0),
'PUSHOVER_ON_BUFFER': (int, 'Pushover', 0), 'PUSHOVER_ON_BUFFER': (int, 'Pushover', 0),
'PUSHOVER_ON_WATCHED': (int, 'Pushover', 0), 'PUSHOVER_ON_WATCHED': (int, 'Pushover', 0),
'PUSHOVER_ON_CREATED': (int, 'Pushover', 0),
'REFRESH_USERS_INTERVAL': (int, 'Monitoring', 12), 'REFRESH_USERS_INTERVAL': (int, 'Monitoring', 12),
'REFRESH_USERS_ON_STARTUP': (int, 'Monitoring', 1), 'REFRESH_USERS_ON_STARTUP': (int, 'Monitoring', 1),
'TV_NOTIFY_ENABLE': (int, 'Monitoring', 0), 'TV_NOTIFY_ENABLE': (int, 'Monitoring', 0),
@ -218,6 +232,7 @@ _CONFIG_DEFINITIONS = {
'TWITTER_ON_RESUME': (int, 'Twitter', 0), 'TWITTER_ON_RESUME': (int, 'Twitter', 0),
'TWITTER_ON_BUFFER': (int, 'Twitter', 0), 'TWITTER_ON_BUFFER': (int, 'Twitter', 0),
'TWITTER_ON_WATCHED': (int, 'Twitter', 0), 'TWITTER_ON_WATCHED': (int, 'Twitter', 0),
'TWITTER_ON_CREATED': (int, 'Twitter', 0),
'UPDATE_DB_INTERVAL': (int, 'General', 24), 'UPDATE_DB_INTERVAL': (int, 'General', 24),
'VERIFY_SSL_CERT': (bool_int, 'Advanced', 1), 'VERIFY_SSL_CERT': (bool_int, 'Advanced', 1),
'VIDEO_LOGGING_ENABLE': (int, 'Monitoring', 1), 'VIDEO_LOGGING_ENABLE': (int, 'Monitoring', 1),
@ -230,7 +245,8 @@ _CONFIG_DEFINITIONS = {
'XBMC_ON_PAUSE': (int, 'XBMC', 0), 'XBMC_ON_PAUSE': (int, 'XBMC', 0),
'XBMC_ON_RESUME': (int, 'XBMC', 0), 'XBMC_ON_RESUME': (int, 'XBMC', 0),
'XBMC_ON_BUFFER': (int, 'XBMC', 0), 'XBMC_ON_BUFFER': (int, 'XBMC', 0),
'XBMC_ON_WATCHED': (int, 'XBMC', 0) 'XBMC_ON_WATCHED': (int, 'XBMC', 0),
'XBMC_ON_CREATED': (int, 'XBMC', 0)
} }
# pylint:disable=R0902 # pylint:disable=R0902
# it might be nice to refactor for fewer instance variables # it might be nice to refactor for fewer instance variables

View file

@ -723,7 +723,7 @@ class DataFactory(object):
actors = item['actors'].split(';') if item['actors'] else [] actors = item['actors'].split(';') if item['actors'] else []
genres = item['genres'].split(';') if item['genres'] else [] genres = item['genres'].split(';') if item['genres'] else []
metadata = {'type': item['media_type'], metadata = {'media_type': item['media_type'],
'rating_key': item['rating_key'], 'rating_key': item['rating_key'],
'parent_rating_key': item['parent_rating_key'], 'parent_rating_key': item['parent_rating_key'],
'grandparent_rating_key': item['grandparent_rating_key'], 'grandparent_rating_key': item['grandparent_rating_key'],

View file

@ -43,7 +43,7 @@ def notify(stream_data=None, notify_action=None):
subject=notify_strings[0], subject=notify_strings[0],
body=notify_strings[1]) body=notify_strings[1])
# Set the notification state in the db # Set the notification state in the db
set_notify_state(session=stream_data, state='play', agent_info=agent) set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
elif agent['on_stop'] and notify_action == 'stop' \ elif agent['on_stop'] and notify_action == 'stop' \
and (plexpy.CONFIG.NOTIFY_CONSECUTIVE or progress_percent < plexpy.CONFIG.NOTIFY_WATCHED_PERCENT): and (plexpy.CONFIG.NOTIFY_CONSECUTIVE or progress_percent < plexpy.CONFIG.NOTIFY_WATCHED_PERCENT):
@ -53,7 +53,7 @@ def notify(stream_data=None, notify_action=None):
subject=notify_strings[0], subject=notify_strings[0],
body=notify_strings[1]) body=notify_strings[1])
set_notify_state(session=stream_data, state='stop', agent_info=agent) set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
elif agent['on_pause'] and notify_action == 'pause' \ elif agent['on_pause'] and notify_action == 'pause' \
and (plexpy.CONFIG.NOTIFY_CONSECUTIVE or progress_percent < 99): and (plexpy.CONFIG.NOTIFY_CONSECUTIVE or progress_percent < 99):
@ -63,7 +63,7 @@ def notify(stream_data=None, notify_action=None):
subject=notify_strings[0], subject=notify_strings[0],
body=notify_strings[1]) body=notify_strings[1])
set_notify_state(session=stream_data, state='pause', agent_info=agent) set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
elif agent['on_resume'] and notify_action == 'resume' \ elif agent['on_resume'] and notify_action == 'resume' \
and (plexpy.CONFIG.NOTIFY_CONSECUTIVE or progress_percent < 99): and (plexpy.CONFIG.NOTIFY_CONSECUTIVE or progress_percent < 99):
@ -73,7 +73,7 @@ def notify(stream_data=None, notify_action=None):
subject=notify_strings[0], subject=notify_strings[0],
body=notify_strings[1]) body=notify_strings[1])
set_notify_state(session=stream_data, state='resume', agent_info=agent) set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
elif agent['on_buffer'] and notify_action == 'buffer': elif agent['on_buffer'] and notify_action == 'buffer':
# Build and send notification # Build and send notification
@ -82,7 +82,7 @@ def notify(stream_data=None, notify_action=None):
subject=notify_strings[0], subject=notify_strings[0],
body=notify_strings[1]) body=notify_strings[1])
set_notify_state(session=stream_data, state='buffer', agent_info=agent) set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
elif agent['on_watched'] and notify_action == 'watched': elif agent['on_watched'] and notify_action == 'watched':
# Get the current states for notifications from our db # Get the current states for notifications from our db
@ -96,7 +96,7 @@ def notify(stream_data=None, notify_action=None):
subject=notify_strings[0], subject=notify_strings[0],
body=notify_strings[1]) body=notify_strings[1])
# Set the notification state in the db # Set the notification state in the db
set_notify_state(session=stream_data, state='watched', agent_info=agent) set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
else: else:
# Check in our notify log if the notification has already been sent # Check in our notify log if the notification has already been sent
@ -108,7 +108,7 @@ def notify(stream_data=None, notify_action=None):
subject=notify_strings[0], subject=notify_strings[0],
body=notify_strings[1]) body=notify_strings[1])
# Set the notification state in the db # Set the notification state in the db
set_notify_state(session=stream_data, state='watched', agent_info=agent) set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
elif stream_data['media_type'] == 'track': elif stream_data['media_type'] == 'track':
if plexpy.CONFIG.MUSIC_NOTIFY_ENABLE: if plexpy.CONFIG.MUSIC_NOTIFY_ENABLE:
@ -121,7 +121,7 @@ def notify(stream_data=None, notify_action=None):
subject=notify_strings[0], subject=notify_strings[0],
body=notify_strings[1]) body=notify_strings[1])
# Set the notification state in the db # Set the notification state in the db
set_notify_state(session=stream_data, state='play', agent_info=agent) set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
elif agent['on_stop'] and notify_action == 'stop': elif agent['on_stop'] and notify_action == 'stop':
# Build and send notification # Build and send notification
@ -130,7 +130,7 @@ def notify(stream_data=None, notify_action=None):
subject=notify_strings[0], subject=notify_strings[0],
body=notify_strings[1]) body=notify_strings[1])
# Set the notification state in the db # Set the notification state in the db
set_notify_state(session=stream_data, state='stop', agent_info=agent) set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
elif agent['on_pause'] and notify_action == 'pause': elif agent['on_pause'] and notify_action == 'pause':
# Build and send notification # Build and send notification
@ -139,7 +139,7 @@ def notify(stream_data=None, notify_action=None):
subject=notify_strings[0], subject=notify_strings[0],
body=notify_strings[1]) body=notify_strings[1])
# Set the notification state in the db # Set the notification state in the db
set_notify_state(session=stream_data, state='pause', agent_info=agent) set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
elif agent['on_resume'] and notify_action == 'resume': elif agent['on_resume'] and notify_action == 'resume':
# Build and send notification # Build and send notification
@ -148,7 +148,7 @@ def notify(stream_data=None, notify_action=None):
subject=notify_strings[0], subject=notify_strings[0],
body=notify_strings[1]) body=notify_strings[1])
# Set the notification state in the db # Set the notification state in the db
set_notify_state(session=stream_data, state='resume', agent_info=agent) set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
elif agent['on_buffer'] and notify_action == 'buffer': elif agent['on_buffer'] and notify_action == 'buffer':
# Build and send notification # Build and send notification
@ -157,7 +157,7 @@ def notify(stream_data=None, notify_action=None):
subject=notify_strings[0], subject=notify_strings[0],
body=notify_strings[1]) body=notify_strings[1])
# Set the notification state in the db # Set the notification state in the db
set_notify_state(session=stream_data, state='buffer', agent_info=agent) set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
elif stream_data['media_type'] == 'clip': elif stream_data['media_type'] == 'clip':
pass pass
@ -168,6 +168,25 @@ def notify(stream_data=None, notify_action=None):
logger.debug(u"PlexPy Notifier :: Notify called but incomplete data received.") logger.debug(u"PlexPy Notifier :: Notify called but incomplete data received.")
def notify_timeline(timeline_data=None, notify_action=None):
if timeline_data and notify_action:
for agent in notifiers.available_notification_agents():
if agent['on_created'] and notify_action == 'created':
if (plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT \
and (timeline_data['media_type'] == 'movie' or timeline_data['media_type'] == 'show' or timeline_data['media_type'] == 'artist')) \
or (not plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT \
and (timeline_data['media_type'] == 'movie' or timeline_data['media_type'] == 'episode' or timeline_data['media_type'] == 'track')):
# Build and send notification
notify_strings = build_notify_text(timeline=timeline_data, state=notify_action)
notifiers.send_notification(config_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1])
# Set the notification state in the db
set_notify_state(session=timeline_data, state=notify_action, agent_info=agent)
else:
logger.debug(u"PlexPy Notifier :: Notify timeline called but incomplete data received.")
def get_notify_state(session): def get_notify_state(session):
monitor_db = database.MonitorDatabase() monitor_db = database.MonitorDatabase()
result = monitor_db.select('SELECT on_play, on_stop, on_pause, on_resume, on_buffer, on_watched, agent_id ' result = monitor_db.select('SELECT on_play, on_stop, on_pause, on_resume, on_buffer, on_watched, agent_id '
@ -190,6 +209,21 @@ def get_notify_state(session):
return notify_states return notify_states
def get_notify_state_timeline(timeline):
monitor_db = database.MonitorDatabase()
result = monitor_db.select('SELECT on_created, agent_id '
'FROM notify_log '
'WHERE rating_key = ? '
'ORDER BY id DESC',
args=[timeline['rating_key']])
notify_states = []
for item in result:
notify_state = {'on_created': item[0],
'agent_id': item[1]}
notify_states.append(notify_state)
return notify_states
def set_notify_state(session, state, agent_info): def set_notify_state(session, state, agent_info):
@ -208,9 +242,16 @@ def set_notify_state(session, state, agent_info):
values = {'on_buffer': int(time.time())} values = {'on_buffer': int(time.time())}
elif state == 'watched': elif state == 'watched':
values = {'on_watched': int(time.time())} values = {'on_watched': int(time.time())}
elif state == 'created':
values = {'on_created': int(time.time())}
else: else:
return return
if state == 'created':
keys = {'rating_key': session['rating_key'],
'agent_id': agent_info['id'],
'agent_name': agent_info['name']}
else:
keys = {'session_key': session['session_key'], keys = {'session_key': session['session_key'],
'rating_key': session['rating_key'], 'rating_key': session['rating_key'],
'user_id': session['user_id'], 'user_id': session['user_id'],
@ -223,37 +264,49 @@ def set_notify_state(session, state, agent_info):
logger.error('PlexPy Notifier :: Unable to set notify state.') logger.error('PlexPy Notifier :: Unable to set notify state.')
def build_notify_text(session, state): def build_notify_text(session=None, timeline=None, state=None):
from plexpy import pmsconnect, helpers from plexpy import pmsconnect, helpers
import re import re
# Get the server name # Get the server name
pms_connect = pmsconnect.PmsConnect() pms_connect = pmsconnect.PmsConnect()
server_name = pms_connect.get_server_pref(pref='FriendlyName') server_name = pms_connect.get_server_pref(pref='FriendlyName')
# If friendly name is blank
if not server_name:
servers_info = pms_connect.get_servers_info()
for server in servers_info:
if server['machine_identifier'] == plexpy.CONFIG.PMS_IDENTIFIER:
server_name = server['name']
break
# Get metadata feed for item # Get metadata feed for item
metadata = pms_connect.get_metadata_details(rating_key=session['rating_key']) if session:
rating_key = session['rating_key']
elif timeline:
rating_key = timeline['rating_key']
if metadata: metadata_list = pms_connect.get_metadata_details(rating_key=rating_key)
item_metadata = metadata['metadata']
if metadata_list:
metadata = metadata_list['metadata']
else: else:
logger.error(u"PlexPy Notifier :: Unable to retrieve metadata for rating_key %s" % str(session['rating_key'])) logger.error(u"PlexPy Notifier :: Unable to retrieve metadata for rating_key %s" % str(rating_key))
return [] return []
# Check for exclusion tags # Check for exclusion tags
if session['media_type'] == 'episode': if metadata['media_type'] == 'episode':
# Regex pattern to remove the text in the tags we don't want # Regex pattern to remove the text in the tags we don't want
pattern = re.compile('<movie>[^>]+.</movie>|<music>[^>]+.</music>', re.IGNORECASE) pattern = re.compile('<movie>[^>]+.</movie>|<music>[^>]+.</music>', re.IGNORECASE)
elif session['media_type'] == 'movie': elif metadata['media_type'] == 'movie':
# Regex pattern to remove the text in the tags we don't want # Regex pattern to remove the text in the tags we don't want
pattern = re.compile('<tv>[^>]+.</tv>|<music>[^>]+.</music>', re.IGNORECASE) pattern = re.compile('<tv>[^>]+.</tv>|<music>[^>]+.</music>', re.IGNORECASE)
elif session['media_type'] == 'track': elif metadata['media_type'] == 'track':
# Regex pattern to remove the text in the tags we don't want # Regex pattern to remove the text in the tags we don't want
pattern = re.compile('<tv>[^>]+.</tv>|<movie>[^>]+.</movie>', re.IGNORECASE) pattern = re.compile('<tv>[^>]+.</tv>|<movie>[^>]+.</movie>', re.IGNORECASE)
else: else:
pattern = None pattern = None
if session['media_type'] == 'episode' or session['media_type'] == 'movie' or session['media_type'] == 'track' \ if metadata['media_type'] == 'episode' or metadata['media_type'] == 'movie' or metadata['media_type'] == 'track' \
and pattern: and pattern:
# Remove the unwanted tags and strip any unmatch tags too. # Remove the unwanted tags and strip any unmatch tags too.
on_start_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_START_SUBJECT_TEXT)) on_start_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_START_SUBJECT_TEXT))
@ -268,6 +321,8 @@ def build_notify_text(session, state):
on_buffer_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_BUFFER_BODY_TEXT)) on_buffer_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_BUFFER_BODY_TEXT))
on_watched_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_WATCHED_SUBJECT_TEXT)) on_watched_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_WATCHED_SUBJECT_TEXT))
on_watched_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_WATCHED_BODY_TEXT)) on_watched_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_WATCHED_BODY_TEXT))
on_created_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_CREATED_SUBJECT_TEXT))
on_created_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_CREATED_BODY_TEXT))
else: else:
on_start_subject = plexpy.CONFIG.NOTIFY_ON_START_SUBJECT_TEXT on_start_subject = plexpy.CONFIG.NOTIFY_ON_START_SUBJECT_TEXT
on_start_body = plexpy.CONFIG.NOTIFY_ON_START_BODY_TEXT on_start_body = plexpy.CONFIG.NOTIFY_ON_START_BODY_TEXT
@ -281,17 +336,28 @@ def build_notify_text(session, state):
on_buffer_body = plexpy.CONFIG.NOTIFY_ON_BUFFER_BODY_TEXT on_buffer_body = plexpy.CONFIG.NOTIFY_ON_BUFFER_BODY_TEXT
on_watched_subject = plexpy.CONFIG.NOTIFY_ON_WATCHED_SUBJECT_TEXT on_watched_subject = plexpy.CONFIG.NOTIFY_ON_WATCHED_SUBJECT_TEXT
on_watched_body = plexpy.CONFIG.NOTIFY_ON_WATCHED_BODY_TEXT on_watched_body = plexpy.CONFIG.NOTIFY_ON_WATCHED_BODY_TEXT
on_created_subject = plexpy.CONFIG.NOTIFY_ON_CREATED_SUBJECT_TEXT
on_created_body = plexpy.CONFIG.NOTIFY_ON_CREATED_BODY_TEXT
# Create a title # Create a title
if session['media_type'] == 'episode': if metadata['media_type'] == 'episode' or metadata['media_type'] == 'track':
full_title = '%s - %s' % (session['grandparent_title'], full_title = '%s - %s' % (metadata['grandparent_title'],
session['title']) metadata['title'])
elif session['media_type'] == 'track':
full_title = '%s - %s' % (session['grandparent_title'],
session['title'])
else: else:
full_title = session['title'] full_title = metadata['title']
duration = helpers.convert_milliseconds_to_minutes(metadata['duration'])
# Default values
transcode_decision = ''
stream_duration = 0
view_offset = 0
user = ''
platform = ''
player = ''
# Session values
if session:
# Generate a combined transcode decision value # Generate a combined transcode decision value
if session['video_decision']: if session['video_decision']:
if session['video_decision'] == 'transcode': if session['video_decision'] == 'transcode':
@ -300,15 +366,12 @@ def build_notify_text(session, state):
transcode_decision = 'Direct Stream' transcode_decision = 'Direct Stream'
else: else:
transcode_decision = 'Direct Play' transcode_decision = 'Direct Play'
else: elif session['audio_decision']:
if session['audio_decision'] == 'transcode': if session['audio_decision'] == 'transcode':
transcode_decision = 'Transcode' transcode_decision = 'Transcode'
else: else:
transcode_decision = 'Direct Play' transcode_decision = 'Direct Play'
duration = helpers.convert_milliseconds_to_minutes(item_metadata['duration'])
view_offset = helpers.convert_milliseconds_to_minutes(session['view_offset'])
stream_duration = 0
if state != 'play': if state != 'play':
if session['paused_counter']: if session['paused_counter']:
stream_duration = int((time.time() - helpers.cast_to_float(session['started']) - stream_duration = int((time.time() - helpers.cast_to_float(session['started']) -
@ -316,28 +379,33 @@ def build_notify_text(session, state):
else: else:
stream_duration = int((time.time() - helpers.cast_to_float(session['started'])) / 60) stream_duration = int((time.time() - helpers.cast_to_float(session['started'])) / 60)
view_offset = helpers.convert_milliseconds_to_minutes(session['view_offset'])
user = session['friendly_name']
platform = session['platform']
player = session['player']
progress_percent = helpers.get_percent(view_offset, duration) progress_percent = helpers.get_percent(view_offset, duration)
available_params = {'server_name': server_name, available_params = {'server_name': server_name,
'user': session['friendly_name'], 'user': user,
'platform': session['platform'], 'platform': platform,
'player': session['player'], 'player': player,
'media_type': session['media_type'], 'media_type': metadata['media_type'],
'title': full_title, 'title': full_title,
'show_name': item_metadata['grandparent_title'], 'show_name': metadata['grandparent_title'],
'episode_name': item_metadata['title'], 'episode_name': metadata['title'],
'artist_name': item_metadata['grandparent_title'], 'artist_name': metadata['grandparent_title'],
'album_name': item_metadata['parent_title'], 'album_name': metadata['parent_title'],
'season_num': item_metadata['parent_index'], 'season_num': metadata['parent_index'],
'season_num00': item_metadata['parent_index'].zfill(2), 'season_num00': metadata['parent_index'].zfill(2),
'episode_num': item_metadata['index'], 'episode_num': metadata['index'],
'episode_num00': item_metadata['index'].zfill(2), 'episode_num00': metadata['index'].zfill(2),
'transcode_decision': transcode_decision, 'transcode_decision': transcode_decision,
'year': item_metadata['year'], 'year': metadata['year'],
'studio': item_metadata['studio'], 'studio': metadata['studio'],
'content_rating': item_metadata['content_rating'], 'content_rating': metadata['content_rating'],
'summary': item_metadata['summary'], 'summary': metadata['summary'],
'rating': item_metadata['rating'], 'rating': metadata['rating'],
'duration': duration, 'duration': duration,
'stream_duration': stream_duration, 'stream_duration': stream_duration,
'remaining_duration': duration - view_offset, 'remaining_duration': duration - view_offset,
@ -489,6 +557,28 @@ def build_notify_text(session, state):
except: except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification body. Using fallback.") logger.error(u"PlexPy Notifier :: Unable to parse custom notification body. Using fallback.")
return [subject_text, body_text]
else:
return [subject_text, body_text]
elif state == 'created':
# Default body text
body_text = '%s was recently added to Plex.' % full_title
if on_created_subject and on_created_body:
try:
subject_text = unicode(on_created_subject).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.")
try:
body_text = unicode(on_created_body).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification body. Using fallback.")
return [subject_text, body_text] return [subject_text, body_text]
else: else:
return [subject_text, body_text] return [subject_text, body_text]

View file

@ -64,7 +64,8 @@ def available_notification_agents():
'on_pause': plexpy.CONFIG.GROWL_ON_PAUSE, 'on_pause': plexpy.CONFIG.GROWL_ON_PAUSE,
'on_resume': plexpy.CONFIG.GROWL_ON_RESUME, 'on_resume': plexpy.CONFIG.GROWL_ON_RESUME,
'on_buffer': plexpy.CONFIG.GROWL_ON_BUFFER, 'on_buffer': plexpy.CONFIG.GROWL_ON_BUFFER,
'on_watched': plexpy.CONFIG.GROWL_ON_WATCHED 'on_watched': plexpy.CONFIG.GROWL_ON_WATCHED,
'on_created': plexpy.CONFIG.GROWL_ON_CREATED
}, },
{'name': 'Prowl', {'name': 'Prowl',
'id': AGENT_IDS['Prowl'], 'id': AGENT_IDS['Prowl'],
@ -76,7 +77,8 @@ def available_notification_agents():
'on_pause': plexpy.CONFIG.PROWL_ON_PAUSE, 'on_pause': plexpy.CONFIG.PROWL_ON_PAUSE,
'on_resume': plexpy.CONFIG.PROWL_ON_RESUME, 'on_resume': plexpy.CONFIG.PROWL_ON_RESUME,
'on_buffer': plexpy.CONFIG.PROWL_ON_BUFFER, 'on_buffer': plexpy.CONFIG.PROWL_ON_BUFFER,
'on_watched': plexpy.CONFIG.PROWL_ON_WATCHED 'on_watched': plexpy.CONFIG.PROWL_ON_WATCHED,
'on_created': plexpy.CONFIG.PROWL_ON_CREATED
}, },
{'name': 'XBMC', {'name': 'XBMC',
'id': AGENT_IDS['XBMC'], 'id': AGENT_IDS['XBMC'],
@ -88,7 +90,8 @@ def available_notification_agents():
'on_pause': plexpy.CONFIG.XBMC_ON_PAUSE, 'on_pause': plexpy.CONFIG.XBMC_ON_PAUSE,
'on_resume': plexpy.CONFIG.XBMC_ON_RESUME, 'on_resume': plexpy.CONFIG.XBMC_ON_RESUME,
'on_buffer': plexpy.CONFIG.XBMC_ON_BUFFER, 'on_buffer': plexpy.CONFIG.XBMC_ON_BUFFER,
'on_watched': plexpy.CONFIG.XBMC_ON_WATCHED 'on_watched': plexpy.CONFIG.XBMC_ON_WATCHED,
'on_created': plexpy.CONFIG.XBMC_ON_CREATED
}, },
{'name': 'Plex', {'name': 'Plex',
'id': AGENT_IDS['Plex'], 'id': AGENT_IDS['Plex'],
@ -100,7 +103,8 @@ def available_notification_agents():
'on_pause': plexpy.CONFIG.PLEX_ON_PAUSE, 'on_pause': plexpy.CONFIG.PLEX_ON_PAUSE,
'on_resume': plexpy.CONFIG.PLEX_ON_RESUME, 'on_resume': plexpy.CONFIG.PLEX_ON_RESUME,
'on_buffer': plexpy.CONFIG.PLEX_ON_BUFFER, 'on_buffer': plexpy.CONFIG.PLEX_ON_BUFFER,
'on_watched': plexpy.CONFIG.PLEX_ON_WATCHED 'on_watched': plexpy.CONFIG.PLEX_ON_WATCHED,
'on_created': plexpy.CONFIG.PLEX_ON_CREATED
}, },
{'name': 'NotifyMyAndroid', {'name': 'NotifyMyAndroid',
'id': AGENT_IDS['NMA'], 'id': AGENT_IDS['NMA'],
@ -112,7 +116,8 @@ def available_notification_agents():
'on_pause': plexpy.CONFIG.NMA_ON_PAUSE, 'on_pause': plexpy.CONFIG.NMA_ON_PAUSE,
'on_resume': plexpy.CONFIG.NMA_ON_RESUME, 'on_resume': plexpy.CONFIG.NMA_ON_RESUME,
'on_buffer': plexpy.CONFIG.NMA_ON_BUFFER, 'on_buffer': plexpy.CONFIG.NMA_ON_BUFFER,
'on_watched': plexpy.CONFIG.NMA_ON_WATCHED 'on_watched': plexpy.CONFIG.NMA_ON_WATCHED,
'on_created': plexpy.CONFIG.NMA_ON_CREATED
}, },
{'name': 'Pushalot', {'name': 'Pushalot',
'id': AGENT_IDS['Pushalot'], 'id': AGENT_IDS['Pushalot'],
@ -124,7 +129,8 @@ def available_notification_agents():
'on_pause': plexpy.CONFIG.PUSHALOT_ON_PAUSE, 'on_pause': plexpy.CONFIG.PUSHALOT_ON_PAUSE,
'on_resume': plexpy.CONFIG.PUSHALOT_ON_RESUME, 'on_resume': plexpy.CONFIG.PUSHALOT_ON_RESUME,
'on_buffer': plexpy.CONFIG.PUSHALOT_ON_BUFFER, 'on_buffer': plexpy.CONFIG.PUSHALOT_ON_BUFFER,
'on_watched': plexpy.CONFIG.PUSHALOT_ON_WATCHED 'on_watched': plexpy.CONFIG.PUSHALOT_ON_WATCHED,
'on_created': plexpy.CONFIG.PUSHALOT_ON_CREATED
}, },
{'name': 'Pushbullet', {'name': 'Pushbullet',
'id': AGENT_IDS['Pushbullet'], 'id': AGENT_IDS['Pushbullet'],
@ -136,7 +142,8 @@ def available_notification_agents():
'on_pause': plexpy.CONFIG.PUSHBULLET_ON_PAUSE, 'on_pause': plexpy.CONFIG.PUSHBULLET_ON_PAUSE,
'on_resume': plexpy.CONFIG.PUSHBULLET_ON_RESUME, 'on_resume': plexpy.CONFIG.PUSHBULLET_ON_RESUME,
'on_buffer': plexpy.CONFIG.PUSHBULLET_ON_BUFFER, 'on_buffer': plexpy.CONFIG.PUSHBULLET_ON_BUFFER,
'on_watched': plexpy.CONFIG.PUSHBULLET_ON_WATCHED 'on_watched': plexpy.CONFIG.PUSHBULLET_ON_WATCHED,
'on_created': plexpy.CONFIG.PUSHBULLET_ON_CREATED
}, },
{'name': 'Pushover', {'name': 'Pushover',
'id': AGENT_IDS['Pushover'], 'id': AGENT_IDS['Pushover'],
@ -148,7 +155,8 @@ def available_notification_agents():
'on_pause': plexpy.CONFIG.PUSHOVER_ON_PAUSE, 'on_pause': plexpy.CONFIG.PUSHOVER_ON_PAUSE,
'on_resume': plexpy.CONFIG.PUSHOVER_ON_RESUME, 'on_resume': plexpy.CONFIG.PUSHOVER_ON_RESUME,
'on_buffer': plexpy.CONFIG.PUSHOVER_ON_BUFFER, 'on_buffer': plexpy.CONFIG.PUSHOVER_ON_BUFFER,
'on_watched': plexpy.CONFIG.PUSHOVER_ON_WATCHED 'on_watched': plexpy.CONFIG.PUSHOVER_ON_WATCHED,
'on_created': plexpy.CONFIG.PUSHOVER_ON_CREATED
}, },
{'name': 'Boxcar2', {'name': 'Boxcar2',
'id': AGENT_IDS['Boxcar2'], 'id': AGENT_IDS['Boxcar2'],
@ -160,7 +168,8 @@ def available_notification_agents():
'on_pause': plexpy.CONFIG.BOXCAR_ON_PAUSE, 'on_pause': plexpy.CONFIG.BOXCAR_ON_PAUSE,
'on_resume': plexpy.CONFIG.BOXCAR_ON_RESUME, 'on_resume': plexpy.CONFIG.BOXCAR_ON_RESUME,
'on_buffer': plexpy.CONFIG.BOXCAR_ON_BUFFER, 'on_buffer': plexpy.CONFIG.BOXCAR_ON_BUFFER,
'on_watched': plexpy.CONFIG.BOXCAR_ON_WATCHED 'on_watched': plexpy.CONFIG.BOXCAR_ON_WATCHED,
'on_created': plexpy.CONFIG.BOXCAR_ON_CREATED
}, },
{'name': 'E-mail', {'name': 'E-mail',
'id': AGENT_IDS['Email'], 'id': AGENT_IDS['Email'],
@ -172,7 +181,8 @@ def available_notification_agents():
'on_pause': plexpy.CONFIG.EMAIL_ON_PAUSE, 'on_pause': plexpy.CONFIG.EMAIL_ON_PAUSE,
'on_resume': plexpy.CONFIG.EMAIL_ON_RESUME, 'on_resume': plexpy.CONFIG.EMAIL_ON_RESUME,
'on_buffer': plexpy.CONFIG.EMAIL_ON_BUFFER, 'on_buffer': plexpy.CONFIG.EMAIL_ON_BUFFER,
'on_watched': plexpy.CONFIG.EMAIL_ON_WATCHED 'on_watched': plexpy.CONFIG.EMAIL_ON_WATCHED,
'on_created': plexpy.CONFIG.EMAIL_ON_CREATED
}, },
{'name': 'Twitter', {'name': 'Twitter',
'id': AGENT_IDS['Twitter'], 'id': AGENT_IDS['Twitter'],
@ -184,7 +194,8 @@ def available_notification_agents():
'on_pause': plexpy.CONFIG.TWITTER_ON_PAUSE, 'on_pause': plexpy.CONFIG.TWITTER_ON_PAUSE,
'on_resume': plexpy.CONFIG.TWITTER_ON_RESUME, 'on_resume': plexpy.CONFIG.TWITTER_ON_RESUME,
'on_buffer': plexpy.CONFIG.TWITTER_ON_BUFFER, 'on_buffer': plexpy.CONFIG.TWITTER_ON_BUFFER,
'on_watched': plexpy.CONFIG.TWITTER_ON_WATCHED 'on_watched': plexpy.CONFIG.TWITTER_ON_WATCHED,
'on_created': plexpy.CONFIG.TWITTER_ON_CREATED
}, },
{'name': 'IFTTT', {'name': 'IFTTT',
'id': AGENT_IDS['IFTTT'], 'id': AGENT_IDS['IFTTT'],
@ -196,7 +207,8 @@ def available_notification_agents():
'on_pause': plexpy.CONFIG.IFTTT_ON_PAUSE, 'on_pause': plexpy.CONFIG.IFTTT_ON_PAUSE,
'on_resume': plexpy.CONFIG.IFTTT_ON_RESUME, 'on_resume': plexpy.CONFIG.IFTTT_ON_RESUME,
'on_buffer': plexpy.CONFIG.IFTTT_ON_BUFFER, 'on_buffer': plexpy.CONFIG.IFTTT_ON_BUFFER,
'on_watched': plexpy.CONFIG.IFTTT_ON_WATCHED 'on_watched': plexpy.CONFIG.IFTTT_ON_WATCHED,
'on_created': plexpy.CONFIG.IFTTT_ON_CREATED
} }
] ]
@ -213,7 +225,8 @@ def available_notification_agents():
'on_pause': plexpy.CONFIG.OSX_NOTIFY_ON_PAUSE, 'on_pause': plexpy.CONFIG.OSX_NOTIFY_ON_PAUSE,
'on_resume': plexpy.CONFIG.OSX_NOTIFY_ON_RESUME, 'on_resume': plexpy.CONFIG.OSX_NOTIFY_ON_RESUME,
'on_buffer': plexpy.CONFIG.OSX_NOTIFY_ON_BUFFER, 'on_buffer': plexpy.CONFIG.OSX_NOTIFY_ON_BUFFER,
'on_watched': plexpy.CONFIG.OSX_NOTIFY_ON_WATCHED 'on_watched': plexpy.CONFIG.OSX_NOTIFY_ON_WATCHED,
'on_created': plexpy.CONFIG.OSX_NOTIFY_ON_CREATED
}) })
return agents return agents

View file

@ -1,4 +1,4 @@
# This file is part of PlexPy. # This file is part of PlexPy.
# #
# PlexPy is free software: you can redistribute it and/or modify # PlexPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -247,6 +247,8 @@ def import_from_plexwatch(database=None, table_name=None, import_ignore_interval
logger.debug(u"PlexPy Importer :: Disabling monitoring while import in progress.") logger.debug(u"PlexPy Importer :: Disabling monitoring while import in progress.")
plexpy.schedule_job(activity_pinger.check_active_sessions, 'Check for active sessions', plexpy.schedule_job(activity_pinger.check_active_sessions, 'Check for active sessions',
hours=0, minutes=0, seconds=0) hours=0, minutes=0, seconds=0)
plexpy.schedule_job(activity_pinger.check_recently_added, 'Check for recently added items',
hours=0, minutes=0, seconds=0)
ap = activity_processor.ActivityProcessor() ap = activity_processor.ActivityProcessor()
user_data = users.Users() user_data = users.Users()

View file

@ -281,10 +281,13 @@ class PmsConnect(object):
recents_main = a.getElementsByTagName('Directory') recents_main = a.getElementsByTagName('Directory')
for item in recents_main: for item in recents_main:
recent_type = helpers.get_xml_attr(item, 'type') recent_type = helpers.get_xml_attr(item, 'type')
recent_items = {'type': recent_type, recent_items = {'media_type': recent_type,
'rating_key': helpers.get_xml_attr(item, 'ratingKey'), 'rating_key': helpers.get_xml_attr(item, 'ratingKey'),
'parent_rating_key': helpers.get_xml_attr(item, 'parentRatingKey'),
'title': helpers.get_xml_attr(item, 'title'), 'title': helpers.get_xml_attr(item, 'title'),
'parent_title': helpers.get_xml_attr(item, 'parentTitle'), 'parent_title': helpers.get_xml_attr(item, 'parentTitle'),
'library_id': helpers.get_xml_attr(item, 'librarySectionID'),
'library_title': helpers.get_xml_attr(item, 'librarySectionTitle'),
'thumb': helpers.get_xml_attr(item, 'thumb'), 'thumb': helpers.get_xml_attr(item, 'thumb'),
'added_at': helpers.get_xml_attr(item, 'addedAt') 'added_at': helpers.get_xml_attr(item, 'addedAt')
} }
@ -296,10 +299,12 @@ class PmsConnect(object):
recent_type = helpers.get_xml_attr(item, 'type') recent_type = helpers.get_xml_attr(item, 'type')
if recent_type == 'movie': if recent_type == 'movie':
recent_items = {'type': recent_type, recent_items = {'media_type': recent_type,
'rating_key': helpers.get_xml_attr(item, 'ratingKey'), 'rating_key': helpers.get_xml_attr(item, 'ratingKey'),
'title': helpers.get_xml_attr(item, 'title'), 'title': helpers.get_xml_attr(item, 'title'),
'parent_title': helpers.get_xml_attr(item, 'parentTitle'), 'parent_title': helpers.get_xml_attr(item, 'parentTitle'),
'library_id': helpers.get_xml_attr(item, 'librarySectionID'),
'library_title': helpers.get_xml_attr(item, 'librarySectionTitle'),
'year': helpers.get_xml_attr(item, 'year'), 'year': helpers.get_xml_attr(item, 'year'),
'thumb': helpers.get_xml_attr(item, 'thumb'), 'thumb': helpers.get_xml_attr(item, 'thumb'),
'added_at': helpers.get_xml_attr(item, 'addedAt') 'added_at': helpers.get_xml_attr(item, 'addedAt')
@ -369,7 +374,7 @@ class PmsConnect(object):
directors.append(helpers.get_xml_attr(director, 'tag')) directors.append(helpers.get_xml_attr(director, 'tag'))
if metadata_type == 'show': if metadata_type == 'show':
metadata = {'type': metadata_type, metadata = {'media_type': metadata_type,
'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'), 'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'),
'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'), 'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'),
'parent_index': helpers.get_xml_attr(metadata_main, 'parentIndex'), 'parent_index': helpers.get_xml_attr(metadata_main, 'parentIndex'),
@ -401,7 +406,7 @@ class PmsConnect(object):
elif metadata_type == 'season': elif metadata_type == 'season':
parent_rating_key = helpers.get_xml_attr(metadata_main, 'parentRatingKey') parent_rating_key = helpers.get_xml_attr(metadata_main, 'parentRatingKey')
show_details = self.get_metadata_details(parent_rating_key) show_details = self.get_metadata_details(parent_rating_key)
metadata = {'type': metadata_type, metadata = {'media_type': metadata_type,
'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'), 'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'),
'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'), 'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'),
'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'), 'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'),
@ -432,7 +437,7 @@ class PmsConnect(object):
} }
metadata_list = {'metadata': metadata} metadata_list = {'metadata': metadata}
elif metadata_type == 'episode': elif metadata_type == 'episode':
metadata = {'type': metadata_type, metadata = {'media_type': metadata_type,
'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'), 'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'),
'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'), 'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'),
'grandparent_rating_key': helpers.get_xml_attr(metadata_main, 'grandparentRatingKey'), 'grandparent_rating_key': helpers.get_xml_attr(metadata_main, 'grandparentRatingKey'),
@ -464,7 +469,7 @@ class PmsConnect(object):
} }
metadata_list = {'metadata': metadata} metadata_list = {'metadata': metadata}
elif metadata_type == 'movie': elif metadata_type == 'movie':
metadata = {'type': metadata_type, metadata = {'media_type': metadata_type,
'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'), 'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'),
'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'), 'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'),
'parent_index': helpers.get_xml_attr(metadata_main, 'parentIndex'), 'parent_index': helpers.get_xml_attr(metadata_main, 'parentIndex'),
@ -494,7 +499,7 @@ class PmsConnect(object):
} }
metadata_list = {'metadata': metadata} metadata_list = {'metadata': metadata}
elif metadata_type == 'artist': elif metadata_type == 'artist':
metadata = {'type': metadata_type, metadata = {'media_type': metadata_type,
'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'), 'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'),
'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'), 'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'),
'parent_index': helpers.get_xml_attr(metadata_main, 'parentIndex'), 'parent_index': helpers.get_xml_attr(metadata_main, 'parentIndex'),
@ -526,7 +531,7 @@ class PmsConnect(object):
elif metadata_type == 'album': elif metadata_type == 'album':
parent_rating_key = helpers.get_xml_attr(metadata_main, 'parentRatingKey') parent_rating_key = helpers.get_xml_attr(metadata_main, 'parentRatingKey')
artist_details = self.get_metadata_details(parent_rating_key) artist_details = self.get_metadata_details(parent_rating_key)
metadata = {'type': metadata_type, metadata = {'media_type': metadata_type,
'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'), 'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'),
'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'), 'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'),
'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'), 'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'),
@ -559,7 +564,7 @@ class PmsConnect(object):
elif metadata_type == 'track': elif metadata_type == 'track':
parent_rating_key = helpers.get_xml_attr(metadata_main, 'parentRatingKey') parent_rating_key = helpers.get_xml_attr(metadata_main, 'parentRatingKey')
album_details = self.get_metadata_details(parent_rating_key) album_details = self.get_metadata_details(parent_rating_key)
metadata = {'type': metadata_type, metadata = {'media_type': metadata_type,
'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'), 'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'),
'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'), 'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'),
'grandparent_rating_key': helpers.get_xml_attr(metadata_main, 'grandparentRatingKey'), 'grandparent_rating_key': helpers.get_xml_attr(metadata_main, 'grandparentRatingKey'),
@ -595,6 +600,49 @@ class PmsConnect(object):
return metadata_list return metadata_list
"""
Return processed and validated metadata list for all children of requested item.
Parameters required: rating_key { Plex ratingKey }
Output: array
"""
def get_metadata_children_details(self, rating_key=''):
metadata = self.get_metadata_children(str(rating_key), output_format='xml')
try:
xml_head = metadata.getElementsByTagName('MediaContainer')
except:
logger.warn("Unable to parse XML for get_metadata_children.")
return []
metadata_list = []
for a in xml_head:
if a.getAttribute('size'):
if a.getAttribute('size') == '0':
metadata_list = {'metadata': None}
return metadata_list
if a.getElementsByTagName('Video'):
metadata_main = a.getElementsByTagName('Video')
for item in metadata_main:
child_rating_key = helpers.get_xml_attr(item, 'ratingKey')
metadata = self.get_metadata_details(str(child_rating_key))
if metadata:
metadata_list.append(metadata['metadata'])
elif a.getElementsByTagName('Track'):
metadata_main = a.getElementsByTagName('Track')
for item in metadata_main:
child_rating_key = helpers.get_xml_attr(item, 'ratingKey')
metadata = self.get_metadata_details(str(child_rating_key))
if metadata:
metadata_list.append(metadata['metadata'])
output = {'metadata': metadata_list}
return output
""" """
Return processed and validated session list. Return processed and validated session list.
@ -1429,9 +1477,9 @@ class PmsConnect(object):
for result in result_data: for result in result_data:
rating_key = helpers.get_xml_attr(result, 'ratingKey') rating_key = helpers.get_xml_attr(result, 'ratingKey')
metadata = self.get_metadata_details(rating_key=rating_key) metadata = self.get_metadata_details(rating_key=rating_key)
if metadata['metadata']['type'] == 'movie': if metadata['metadata']['media_type'] == 'movie':
search_results_list['movie'].append(metadata['metadata']) search_results_list['movie'].append(metadata['metadata'])
elif metadata['metadata']['type'] == 'episode': elif metadata['metadata']['media_type'] == 'episode':
search_results_list['episode'].append(metadata['metadata']) search_results_list['episode'].append(metadata['metadata'])
search_results_count += 1 search_results_count += 1
@ -1440,7 +1488,7 @@ class PmsConnect(object):
for result in result_data: for result in result_data:
rating_key = helpers.get_xml_attr(result, 'ratingKey') rating_key = helpers.get_xml_attr(result, 'ratingKey')
metadata = self.get_metadata_details(rating_key=rating_key) metadata = self.get_metadata_details(rating_key=rating_key)
if metadata['metadata']['type'] == 'show': if metadata['metadata']['media_type'] == 'show':
search_results_list['show'].append(metadata['metadata']) search_results_list['show'].append(metadata['metadata'])
show_seasons = self.get_item_children(rating_key=metadata['metadata']['rating_key']) show_seasons = self.get_item_children(rating_key=metadata['metadata']['rating_key'])
@ -1452,9 +1500,9 @@ class PmsConnect(object):
search_results_list['season'].append(metadata['metadata']) search_results_list['season'].append(metadata['metadata'])
search_results_count += 1 search_results_count += 1
elif metadata['metadata']['type'] == 'artist': elif metadata['metadata']['media_type'] == 'artist':
search_results_list['artist'].append(metadata['metadata']) search_results_list['artist'].append(metadata['metadata'])
elif metadata['metadata']['type'] == 'album': elif metadata['metadata']['media_type'] == 'album':
search_results_list['album'].append(metadata['metadata']) search_results_list['album'].append(metadata['metadata'])
search_results_count += 1 search_results_count += 1

View file

@ -1,4 +1,4 @@
# This file is part of PlexPy. # This file is part of PlexPy.
# #
# PlexPy is free software: you can redistribute it and/or modify # PlexPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -139,4 +139,14 @@ def process(opcode, data):
activity = activity_handler.ActivityHandler(timeline=time_line[0]) activity = activity_handler.ActivityHandler(timeline=time_line[0])
activity.process() activity.process()
if type == 'timeline':
try:
time_line = info.get('_children')
except:
logger.debug(u"PlexPy WebSocket :: Timeline event found but unable to get timeline data.")
return False
activity = activity_handler.TimelineHandler(timeline=time_line[0])
activity.process()
return True return True

View file

@ -442,6 +442,7 @@ class WebInterface(object):
"logging_ignore_interval": plexpy.CONFIG.LOGGING_IGNORE_INTERVAL, "logging_ignore_interval": plexpy.CONFIG.LOGGING_IGNORE_INTERVAL,
"pms_is_remote": checked(plexpy.CONFIG.PMS_IS_REMOTE), "pms_is_remote": checked(plexpy.CONFIG.PMS_IS_REMOTE),
"notify_consecutive": checked(plexpy.CONFIG.NOTIFY_CONSECUTIVE), "notify_consecutive": checked(plexpy.CONFIG.NOTIFY_CONSECUTIVE),
"notify_recently_added_grandparent": checked(plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT),
"notify_watched_percent": plexpy.CONFIG.NOTIFY_WATCHED_PERCENT, "notify_watched_percent": plexpy.CONFIG.NOTIFY_WATCHED_PERCENT,
"notify_on_start_subject_text": plexpy.CONFIG.NOTIFY_ON_START_SUBJECT_TEXT, "notify_on_start_subject_text": plexpy.CONFIG.NOTIFY_ON_START_SUBJECT_TEXT,
"notify_on_start_body_text": plexpy.CONFIG.NOTIFY_ON_START_BODY_TEXT, "notify_on_start_body_text": plexpy.CONFIG.NOTIFY_ON_START_BODY_TEXT,
@ -455,6 +456,8 @@ class WebInterface(object):
"notify_on_buffer_body_text": plexpy.CONFIG.NOTIFY_ON_BUFFER_BODY_TEXT, "notify_on_buffer_body_text": plexpy.CONFIG.NOTIFY_ON_BUFFER_BODY_TEXT,
"notify_on_watched_subject_text": plexpy.CONFIG.NOTIFY_ON_WATCHED_SUBJECT_TEXT, "notify_on_watched_subject_text": plexpy.CONFIG.NOTIFY_ON_WATCHED_SUBJECT_TEXT,
"notify_on_watched_body_text": plexpy.CONFIG.NOTIFY_ON_WATCHED_BODY_TEXT, "notify_on_watched_body_text": plexpy.CONFIG.NOTIFY_ON_WATCHED_BODY_TEXT,
"notify_on_created_subject_text": plexpy.CONFIG.NOTIFY_ON_CREATED_SUBJECT_TEXT,
"notify_on_created_body_text": plexpy.CONFIG.NOTIFY_ON_CREATED_BODY_TEXT,
"home_stats_length": plexpy.CONFIG.HOME_STATS_LENGTH, "home_stats_length": plexpy.CONFIG.HOME_STATS_LENGTH,
"home_stats_type": checked(plexpy.CONFIG.HOME_STATS_TYPE), "home_stats_type": checked(plexpy.CONFIG.HOME_STATS_TYPE),
"home_stats_count": plexpy.CONFIG.HOME_STATS_COUNT, "home_stats_count": plexpy.CONFIG.HOME_STATS_COUNT,
@ -479,7 +482,7 @@ class WebInterface(object):
"tv_notify_on_stop", "movie_notify_on_stop", "music_notify_on_stop", "tv_notify_on_stop", "movie_notify_on_stop", "music_notify_on_stop",
"tv_notify_on_pause", "movie_notify_on_pause", "music_notify_on_pause", "refresh_users_on_startup", "tv_notify_on_pause", "movie_notify_on_pause", "music_notify_on_pause", "refresh_users_on_startup",
"ip_logging_enable", "video_logging_enable", "music_logging_enable", "pms_is_remote", "home_stats_type", "ip_logging_enable", "video_logging_enable", "music_logging_enable", "pms_is_remote", "home_stats_type",
"group_history_tables", "notify_consecutive" "group_history_tables", "notify_consecutive", "notify_recently_added_grandparent"
] ]
for checked_config in checked_configs: for checked_config in checked_configs:
if checked_config not in kwargs: if checked_config not in kwargs:
@ -788,11 +791,11 @@ class WebInterface(object):
data_factory = datafactory.DataFactory() data_factory = datafactory.DataFactory()
metadata = data_factory.get_metadata_details(row_id=item_id) metadata = data_factory.get_metadata_details(row_id=item_id)
elif item_id == 'movie': elif item_id == 'movie':
metadata = {'type': 'library', 'library': 'movie', 'media_type': 'movie', 'title': 'Movies'} metadata = {'media_type': 'library', 'library': 'movie', 'media_type_filter': 'movie', 'title': 'Movies'}
elif item_id == 'show': elif item_id == 'show':
metadata = {'type': 'library', 'library': 'show', 'media_type': 'episode', 'title': 'TV Shows'} metadata = {'media_type': 'library', 'library': 'show', 'media_type_filter': 'episode', 'title': 'TV Shows'}
elif item_id == 'artist': elif item_id == 'artist':
metadata = {'type': 'library', 'library': 'artist', 'media_type': 'track', 'title': 'Music'} metadata = {'media_type': 'library', 'library': 'artist', 'media_type_filter': 'track', 'title': 'Music'}
else: else:
pms_connect = pmsconnect.PmsConnect() pms_connect = pmsconnect.PmsConnect()
result = pms_connect.get_metadata_details(rating_key=item_id) result = pms_connect.get_metadata_details(rating_key=item_id)