mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-11 15:56:07 -07:00
Merge branch 'dev'
This commit is contained in:
commit
893c91a15d
38 changed files with 1795 additions and 622 deletions
25
CHANGELOG.md
25
CHANGELOG.md
|
@ -1,5 +1,30 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v1.2.4 (2015-11-24)
|
||||||
|
|
||||||
|
* Add filtering by media type in the history table
|
||||||
|
* Add IFTTT notification agent
|
||||||
|
* Add Telegram notification agent
|
||||||
|
* Add notifications for recently added media
|
||||||
|
* Add notifications for server down and remote access down
|
||||||
|
* Add more metadata to notifications options
|
||||||
|
* Add IP address to notification options (for PMS 0.9.14 and above)
|
||||||
|
* Add server uptime to notification options
|
||||||
|
* Add IP address to current activity
|
||||||
|
* Add IPv6 address logging
|
||||||
|
* Add PMS server name to the page title
|
||||||
|
* Fix bug in "Last Watched" statistic
|
||||||
|
* Fix bug in search query
|
||||||
|
* Fix bug on user pages for usernames with single quotes
|
||||||
|
* Fix name for new Plex Media Center
|
||||||
|
* Fix Pushover notifications with unicode characters
|
||||||
|
* Fix bug with showing old usernames in datatables
|
||||||
|
* Fix bug with "Please verify your server" in settings
|
||||||
|
* Change IP lookup provider
|
||||||
|
* Change notifications custom body text to larger text box
|
||||||
|
* Change movie/tv logging and notifications into individual options
|
||||||
|
|
||||||
|
|
||||||
## v1.2.3 (2015-10-18)
|
## v1.2.3 (2015-10-18)
|
||||||
|
|
||||||
* Added "remaining time" as notification substitution.
|
* Added "remaining time" as notification substitution.
|
||||||
|
|
|
@ -6,11 +6,7 @@ A python based web application for monitoring, analytics and notifications for P
|
||||||
|
|
||||||
This project is based on code from Headphones (https://github.com/rembo10/headphones) and PlexWatchWeb (https://github.com/ecleese/plexWatchWeb).
|
This project is based on code from Headphones (https://github.com/rembo10/headphones) and PlexWatchWeb (https://github.com/ecleese/plexWatchWeb).
|
||||||
|
|
||||||
* plexPy forum thread: https://forums.plex.tv/discussion/169591/plexpy-another-plex-monitoring-program
|
* PlexPy forum thread: https://forums.plex.tv/discussion/169591/plexpy-another-plex-monitoring-program
|
||||||
|
|
||||||
If you'd like to buy me a beer, hit the donate button below. All donations go to the project maintainer (primarily for the procurement of liquid refreshment).
|
|
||||||
|
|
||||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=G9HZK9BDJLKT6)
|
|
||||||
|
|
||||||
|
|
||||||
###Support
|
###Support
|
||||||
|
|
|
@ -7,7 +7,7 @@ from plexpy import version
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>PlexPy - ${title}</title>
|
<title>PlexPy - ${title} | ${server_name}</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="author" content="">
|
<meta name="author" content="">
|
||||||
|
@ -61,7 +61,7 @@ from plexpy import version
|
||||||
<form action="search" method="post" class="form" id="search_form">
|
<form action="search" method="post" class="form" id="search_form">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-textbox">
|
<span class="input-textbox">
|
||||||
<input type="text" class="form-control" name="search_query" id="search_query" aria-label="Search" placeholder="Search..."/>
|
<input type="text" class="form-control" name="query" id="query" aria-label="Search" placeholder="Search..."/>
|
||||||
</span>
|
</span>
|
||||||
<span class="input-group-btn">
|
<span class="input-group-btn">
|
||||||
<button class="btn btn-dark btn-inactive" type="submit" id="search_button"><i class="fa fa-search"></i></button>
|
<button class="btn btn-dark btn-inactive" type="submit" id="search_button"><i class="fa fa-search"></i></button>
|
||||||
|
@ -131,19 +131,19 @@ ${next.headerIncludes()}
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
$('#search_form').submit(function (e) {
|
$('#search_form').submit(function (e) {
|
||||||
if ($('#search_query').hasClass('active') && $('#search_query').val().trim() != '') {
|
if ($('#query').hasClass('active') && $('#query').val().trim() != '') {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'post',
|
type: 'post',
|
||||||
url: 'search',
|
url: 'search',
|
||||||
data: { 'query': $('#search_query').val() }
|
data: { 'query': $('#query').val() }
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$('#search_button').removeClass('btn-inactive');
|
$('#search_button').removeClass('btn-inactive');
|
||||||
$('#search_query').clearQueue().val('').animate({ right: '0', width: '250px' }).addClass('active').focus();
|
$('#query').clearQueue().val('').animate({ right: '0', width: '250px' }).addClass('active').focus();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
$('#search_query').on('blur', function (e) {
|
$('#query').on('blur', function (e) {
|
||||||
if ($(this).val().trim() == '') {
|
if ($(this).val().trim() == '') {
|
||||||
$(this).delay(200).animate({ right: '-250px', width: '0' }, function () {
|
$(this).delay(200).animate({ right: '-250px', width: '0' }, function () {
|
||||||
$('#search_button').addClass('btn-inactive');
|
$('#search_button').addClass('btn-inactive');
|
||||||
|
|
|
@ -341,6 +341,24 @@ input[type="color"],
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
transition: background-color .3s;
|
transition: background-color .3s;
|
||||||
}
|
}
|
||||||
|
textarea.form-control {
|
||||||
|
height: initial;
|
||||||
|
margin: 5px 0 5px 0;
|
||||||
|
color: #fff;
|
||||||
|
border: 0px solid #444;
|
||||||
|
background: #555;
|
||||||
|
padding: 6px 12px;
|
||||||
|
background-color: #555;
|
||||||
|
border-radius: 3px;
|
||||||
|
transition: background-color .3s;
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
textarea.form-control:focus {
|
||||||
|
outline: 0;
|
||||||
|
color: #555;
|
||||||
|
background-color: #fff;
|
||||||
|
transition: background-color .3s;
|
||||||
|
}
|
||||||
.pagination > li > a,
|
.pagination > li > a,
|
||||||
.pagination > li > span {
|
.pagination > li > span {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -688,6 +706,14 @@ a:hover .dashboard-activity-poster {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
|
.dashboard-activity-poster-info-ip-address {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 5px;
|
||||||
|
left: 10px;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
.dashboard-activity-poster-info-time {
|
.dashboard-activity-poster-info-time {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 5px;
|
bottom: 5px;
|
||||||
|
@ -2472,7 +2498,7 @@ table[id^='history_child'] thead th {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
#search_form #search_query {
|
#search_form #query {
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 34px;
|
height: 34px;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
|
@ -2481,7 +2507,7 @@ table[id^='history_child'] thead th {
|
||||||
right: -250px;
|
right: -250px;
|
||||||
border-radius: 3px 0 0 3px;
|
border-radius: 3px 0 0 3px;
|
||||||
}
|
}
|
||||||
#search_form #search_query.active {
|
#search_form #query.active {
|
||||||
width: 250px;
|
width: 250px;
|
||||||
right: 0px;
|
right: 0px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ user_thumb Returns the profile picture of the user owning the s
|
||||||
state Returns the state of the current session. Either 'playing', 'paused' or 'buffering'.
|
state Returns the state of the current session. Either 'playing', 'paused' or 'buffering'.
|
||||||
title Returns the name of the episode, movie or music track.
|
title Returns the name of the episode, movie or music track.
|
||||||
year Returns the year of the episode, movie, or clip.
|
year Returns the year of the episode, movie, or clip.
|
||||||
|
ip_address Returns the ip address of the stream.
|
||||||
player Returns the name of the platform used to play the stream.
|
player Returns the name of the platform used to play the stream.
|
||||||
platform Returns the type of platform used to play the stream.
|
platform Returns the type of platform used to play the stream.
|
||||||
throttled Returns true if the transcode session is throttled.
|
throttled Returns true if the transcode session is throttled.
|
||||||
|
@ -191,6 +192,13 @@ DOCUMENTATION :: END
|
||||||
</div>
|
</div>
|
||||||
% if a['media_type'] != 'photo':
|
% if a['media_type'] != 'photo':
|
||||||
<div class="dashboard-activity-poster-info-bar">
|
<div class="dashboard-activity-poster-info-bar">
|
||||||
|
<div class="dashboard-activity-poster-info-ip-address">
|
||||||
|
% if a['ip_address']:
|
||||||
|
<span>IP: ${a['ip_address']}</span>
|
||||||
|
% else:
|
||||||
|
<span>IP: N/A</span>
|
||||||
|
% endif
|
||||||
|
</div>
|
||||||
<div class="dashboard-activity-poster-info-time">
|
<div class="dashboard-activity-poster-info-time">
|
||||||
<span class="progress_time">${a['view_offset']}</span>/<span class="progress_time">${a['duration']}</span>
|
<span class="progress_time">${a['view_offset']}</span>/<span class="progress_time">${a['duration']}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -76,12 +76,16 @@
|
||||||
<script src="interfaces/default/js/moment-with-locale.js"></script>
|
<script src="interfaces/default/js/moment-with-locale.js"></script>
|
||||||
<script src="interfaces/default/js/tables/history_table.js"></script>
|
<script src="interfaces/default/js/tables/history_table.js"></script>
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function() {
|
$(document).ready(function () {
|
||||||
|
function loadHistoryTable(media_type) {
|
||||||
history_table_options.ajax = {
|
history_table_options.ajax = {
|
||||||
"url": "get_history",
|
url: 'get_history',
|
||||||
type: "post",
|
type: 'post',
|
||||||
data: function ( d ) {
|
data: function (d) {
|
||||||
return { 'json_data': JSON.stringify( d ) };
|
return {
|
||||||
|
'json_data': JSON.stringify(d),
|
||||||
|
'media_type': media_type
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
history_table = $('#history_table').DataTable(history_table_options);
|
history_table = $('#history_table').DataTable(history_table_options);
|
||||||
|
@ -90,6 +94,33 @@
|
||||||
|
|
||||||
clearSearchButton('history_table', history_table);
|
clearSearchButton('history_table', history_table);
|
||||||
|
|
||||||
|
$('#history_table_filter').prepend('<div class="btn-group" data-toggle="buttons" id="media_type-selection" style="padding-right: 15px;"> \
|
||||||
|
<label class="btn btn-dark active"> \
|
||||||
|
<input type="radio" name="media_type-filter" id="history-all" value="all" autocomplete="off"> All \
|
||||||
|
</label> \
|
||||||
|
<label class="btn btn-dark"> \
|
||||||
|
<input type="radio" name="media_type-filter" id="history-movies" value="movie" autocomplete="off"> Movies \
|
||||||
|
</label> \
|
||||||
|
<label class="btn btn-dark"> \
|
||||||
|
<input type="radio" name="media_type-filter" id="history-tv_shows" value="episode" autocomplete="off"> TV Shows \
|
||||||
|
</label> \
|
||||||
|
<label class="btn btn-dark"> \
|
||||||
|
<input type="radio" name="media_type-filter" id="history-music" value="track" autocomplete="off"> Music \
|
||||||
|
</label> \
|
||||||
|
</div>');
|
||||||
|
|
||||||
|
$('#media_type-selection').on('change', function () {
|
||||||
|
$('#media_type-selection > label').removeClass('active');
|
||||||
|
selected_filter = $('input[name=media_type-filter]:checked', '#media_type-selection');
|
||||||
|
$(selected_filter).closest('label').addClass('active');
|
||||||
|
media_type = $(selected_filter).val();
|
||||||
|
history_table.draw();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var media_type = 'all';
|
||||||
|
loadHistoryTable(media_type);
|
||||||
|
|
||||||
$('#row-edit-mode').on('click', function() {
|
$('#row-edit-mode').on('click', function() {
|
||||||
$('#row-edit-mode-alert').fadeIn(200);
|
$('#row-edit-mode-alert').fadeIn(200);
|
||||||
|
|
||||||
|
|
BIN
data/interfaces/default/images/platforms/pmp.png
Normal file
BIN
data/interfaces/default/images/platforms/pmp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4 KiB |
|
@ -33,7 +33,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div class="padded-header" id="library-statistics-header">
|
<div class="padded-header" id="library-statistics-header">
|
||||||
<h3>Library Statistics</h3>
|
<h3>Library Statistics <small>${config['pms_name']}</small></h3>
|
||||||
</div>
|
</div>
|
||||||
<div id="library-stats" class="library-platforms">
|
<div id="library-stats" class="library-platforms">
|
||||||
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading stats...</div>
|
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading stats...</div>
|
||||||
|
@ -98,27 +98,6 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLibraryStatsHeader() {
|
|
||||||
$.ajax({
|
|
||||||
"url": "get_servers_info",
|
|
||||||
type: "post",
|
|
||||||
cache: false,
|
|
||||||
async: true,
|
|
||||||
data: { },
|
|
||||||
complete: function (xhr, status) {
|
|
||||||
server_info = $.parseJSON(xhr.responseText);
|
|
||||||
var server_name = 'Server name not found';
|
|
||||||
for (var i in server_info) {
|
|
||||||
if (server_info[i].machine_identifier == '${config['pms_identifier']}') {
|
|
||||||
server_name = server_info[i].name
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$('#library-statistics-header h3').append(' <small>' + server_name + '</small>')
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLibraryStats() {
|
function getLibraryStats() {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: 'library_stats',
|
url: 'library_stats',
|
||||||
|
@ -170,7 +149,6 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
getHomeStats();
|
getHomeStats();
|
||||||
getLibraryStatsHeader();
|
|
||||||
getLibraryStats();
|
getLibraryStats();
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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> </h1><h1>${data['title']}</h1>
|
<h1> </h1><h1>${data['title']}</h1>
|
||||||
% elif data['type'] == 'season':
|
% elif data['media_type'] == 'season':
|
||||||
<h1> </h1><h1><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a></h1>
|
<h1> </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']} · E${data['index']}</h3>
|
<h3 class="hidden-xs">S${data['parent_index']} · 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> Loading season list...</div>
|
<div id="children-list"><i class="fa fa-refresh fa-spin"></i> 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> Loading episode list...</div>
|
<div id="children-list"><i class="fa fa-refresh fa-spin"></i> 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> Loading album list...</div>
|
<div id="children-list"><i class="fa fa-refresh fa-spin"></i> 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">
|
||||||
|
@ -382,49 +382,38 @@ DOCUMENTATION :: END
|
||||||
<div class="summary-navbar">
|
<div class="summary-navbar">
|
||||||
<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">
|
||||||
% if query:
|
% if query:
|
||||||
% if query['media_type'] == 'movie':
|
% if query['media_type'] == 'movie':
|
||||||
<span>Movies</span>
|
<li><a href="info?item_id=movie">Movies</a></li>
|
||||||
<span class="hidden-xs hidden-sm"><i class="fa fa-chevron-right"></i></span>
|
<li class="active">${query['title']}</li>
|
||||||
<span>${query['title']}</span>
|
|
||||||
% elif query['media_type'] == 'show':
|
% elif query['media_type'] == 'show':
|
||||||
<span>TV Shows</span>
|
<li><a href="info?item_id=show">TV Shows</a></li>
|
||||||
<span class="hidden-xs hidden-sm"><i class="fa fa-chevron-right"></i></span>
|
<li class="active">${query['grandparent_title']}</li>
|
||||||
<span>${query['grandparent_title']}</span>
|
|
||||||
% elif query['media_type'] == 'season':
|
% elif query['media_type'] == 'season':
|
||||||
<span class="hidden-xs hidden-sm">TV Shows</span>
|
<li class="hidden-xs hidden-sm"><a href="info?item_id=show">TV Shows</a></li>
|
||||||
<span class="hidden-xs hidden-sm"><i class="fa fa-chevron-right"></i></span>
|
<li class="hidden-xs hidden-sm">${query['grandparent_title']}</li>
|
||||||
<span class="hidden-xs hidden-sm">${query['grandparent_title']}</span>
|
<li class="active">Season ${query['parent_media_index']}</li>
|
||||||
<span class="hidden-xs hidden-sm"><i class="fa fa-chevron-right"></i></span>
|
|
||||||
<span>Season ${query['parent_media_index']}</span>
|
|
||||||
% elif query['media_type'] == 'episode':
|
% elif query['media_type'] == 'episode':
|
||||||
<span class="hidden-xs hidden-sm">TV Shows</span>
|
<li class="hidden-xs hidden-sm"><a href="info?item_id=show">TV Shows</a></li>
|
||||||
<span class="hidden-xs hidden-sm"><i class="fa fa-chevron-right"></i></span>
|
<li class="hidden-xs hidden-sm">${query['grandparent_title']}</li>
|
||||||
<span class="hidden-xs hidden-sm">${query['grandparent_title']}</span>
|
<li>Season ${query['parent_media_index']}</li>
|
||||||
<span class="hidden-xs hidden-sm"><i class="fa fa-chevron-right"></i></span>
|
<li class="active">Episode ${query['media_index']} - ${query['title']}</li>
|
||||||
<span>Season ${query['parent_media_index']}</span>
|
|
||||||
<span><i class="fa fa-chevron-right"></i></span>
|
|
||||||
<span>Episode ${query['media_index']} - ${query['title']}</span>
|
|
||||||
% elif query['media_type'] == 'artist':
|
% elif query['media_type'] == 'artist':
|
||||||
<span>Music</span>
|
<li><a href="info?item_id=artist">Music</a></li>
|
||||||
<span><i class="fa fa-chevron-right"></i></span>
|
<li class="active">${query['grandparent_title']}</li>
|
||||||
<span>${query['grandparent_title']}</span>
|
|
||||||
% elif query['media_type'] == 'album':
|
% elif query['media_type'] == 'album':
|
||||||
<span class="hidden-xs hidden-sm">Music</span>
|
<li class="hidden-xs hidden-sm"><a href="info?item_id=artist">Music</a></li>
|
||||||
<span class="hidden-xs hidden-sm"><i class="fa fa-chevron-right"></i></span>
|
<li>${query['grandparent_title']}</li>
|
||||||
<span>${query['grandparent_title']}</span>
|
<li class="active">${query['parent_title']}</li>
|
||||||
<span><i class="fa fa-chevron-right"></i></span>
|
|
||||||
<span>${query['parent_title']}</span>
|
|
||||||
% elif query['media_type'] == 'track':
|
% elif query['media_type'] == 'track':
|
||||||
<span class="hidden-xs hidden-sm">Music</span>
|
<li class="hidden-xs hidden-sm"><a href="info?item_id=artist">Music</a></li>
|
||||||
<span class="hidden-xs hidden-sm"><i class="fa fa-chevron-right"></i></span>
|
<li class="hidden-xs hidden-sm">${query['grandparent_title']}</li>
|
||||||
<span class="hidden-xs hidden-sm">${query['grandparent_title']}</span>
|
<li>${query['parent_title']}</li>
|
||||||
<span class="hidden-xs hidden-sm"><i class="fa fa-chevron-right"></i></span>
|
<li class="active">Track ${query['media_index']} - ${query['title']}</li>
|
||||||
<span>${query['parent_title']}</span>
|
|
||||||
<span><i class="fa fa-chevron-right"></i></span>
|
|
||||||
<span>Track ${query['media_index']} - ${query['title']}</span>
|
|
||||||
% endif
|
% endif
|
||||||
% endif
|
% endif
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -510,54 +499,54 @@ 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 = {
|
||||||
"url": "get_history",
|
url: 'get_history',
|
||||||
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 = {
|
||||||
"url": "get_history",
|
url: 'get_history',
|
||||||
type: 'post',
|
type: 'post',
|
||||||
data: function ( d ) {
|
data: function ( d ) {
|
||||||
return { 'json_data': JSON.stringify( d ),
|
return { 'json_data': JSON.stringify( d ),
|
||||||
'grandparent_rating_key': ${data['rating_key']} };
|
'grandparent_rating_key': "${data['rating_key']}" };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</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 = {
|
||||||
"url": "get_history",
|
url: 'get_history',
|
||||||
type: 'post',
|
type: 'post',
|
||||||
data: function ( d ) {
|
data: function ( d ) {
|
||||||
return { 'json_data': JSON.stringify( d ),
|
return { 'json_data': JSON.stringify( d ),
|
||||||
'parent_rating_key': ${data['rating_key']} };
|
'parent_rating_key': "${data['rating_key']}" };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</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 = {
|
||||||
"url": "get_history",
|
url: 'get_history',
|
||||||
type: 'post',
|
type: 'post',
|
||||||
data: function ( d ) {
|
data: function ( d ) {
|
||||||
return { 'json_data': JSON.stringify( d ),
|
return { 'json_data': JSON.stringify( d ),
|
||||||
'rating_key': ${data['rating_key']} };
|
'rating_key': "${data['rating_key']}" };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -610,11 +599,11 @@ 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',
|
||||||
type: "GET",
|
type: 'GET',
|
||||||
async: true,
|
async: true,
|
||||||
data: { rating_key : ${data['rating_key']} },
|
data: { rating_key : ${data['rating_key']} },
|
||||||
complete: function(xhr, status) {
|
complete: function(xhr, status) {
|
||||||
|
@ -622,7 +611,16 @@ DOCUMENTATION :: END
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
% endif
|
% endif
|
||||||
% if data['type'] != 'library' and data['rating']:
|
% if data['media_type'] != 'library':
|
||||||
|
<!--
|
||||||
|
<script>
|
||||||
|
$('#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>');
|
||||||
|
$('#fix-metadata').tooltip();
|
||||||
|
</script>
|
||||||
|
-->
|
||||||
|
% endif
|
||||||
|
% 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);
|
||||||
|
@ -638,9 +636,9 @@ DOCUMENTATION :: END
|
||||||
<script>
|
<script>
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: 'get_search_results_children',
|
url: 'get_search_results_children',
|
||||||
type: "GET",
|
type: 'GET',
|
||||||
async: true,
|
async: true,
|
||||||
data: {'query': "${query['query_string']}",
|
data: {'query': "${query['query_string'].replace('"','\\"')}",
|
||||||
'media_type': "${query['media_type']}",
|
'media_type': "${query['media_type']}",
|
||||||
'season_index': "${query['parent_media_index']}"
|
'season_index': "${query['parent_media_index']}"
|
||||||
},
|
},
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
<h4><strong>Location Details</strong></h4>
|
<h4><strong>Location Details</strong></h4>
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
<li>Country: <strong><span id="country"></span></strong></li>
|
<li>Country: <strong><span id="country"></span></strong></li>
|
||||||
<li>City: <strong><span id="city"></span></strong></li>
|
|
||||||
<li>Region: <strong><span id="region"></span></strong></li>
|
<li>Region: <strong><span id="region"></span></strong></li>
|
||||||
|
<li>City: <strong><span id="city"></span></strong></li>
|
||||||
<li>Timezone: <strong><span id="timezone"></span></strong></li>
|
<li>Timezone: <strong><span id="timezone"></span></strong></li>
|
||||||
<li>Latitude: <strong><span id="lat"></span></strong></li>
|
<li>Latitude: <strong><span id="lat"></span></strong></li>
|
||||||
<li>Longitude: <strong><span id="lon"></span></strong></li>
|
<li>Longitude: <strong><span id="lon"></span></strong></li>
|
||||||
|
@ -27,14 +27,12 @@
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h4><strong>Connection Details</strong></h4>
|
<h4><strong>Connection Details</strong></h4>
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
<li>ISP: <strong><span id="isp"></span></strong></li>
|
<li>Organization: <strong><span id="organization"></span></strong></li>
|
||||||
<li>Organization: <strong><span id="org"></span></strong></li>
|
|
||||||
<li>AS: <strong><span id="as"></span></strong></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<span class="text-muted">Service provided by ip-api.com.</span>
|
<span class="text-muted">Telize service written by <a href="https://github.com/fcambus/telize" target="_blank">Frederic Cambus</a>.</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -43,7 +41,7 @@
|
||||||
<script>
|
<script>
|
||||||
function getUserLocation(ip_address) {
|
function getUserLocation(ip_address) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: 'http://ip-api.com/json/' + ip_address,
|
url: 'https://telize.myhtpc.co.za/geoip/' + ip_address,
|
||||||
cache: true,
|
cache: true,
|
||||||
async: true,
|
async: true,
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
|
@ -55,13 +53,11 @@
|
||||||
$('#modal_header_ip_address').html('<i class="fa fa-map-marker"></i> IP Address: ' + ip_address);
|
$('#modal_header_ip_address').html('<i class="fa fa-map-marker"></i> IP Address: ' + ip_address);
|
||||||
$('#country').html(data.country);
|
$('#country').html(data.country);
|
||||||
$('#city').html(data.city);
|
$('#city').html(data.city);
|
||||||
$('#region').html(data.regionName);
|
$('#region').html(data.region);
|
||||||
$('#timezone').html(data.timezone);
|
$('#timezone').html(data.timezone);
|
||||||
$('#lat').html(data.lat);
|
$('#lat').html(data.latitude);
|
||||||
$('#lon').html(data.lon);
|
$('#lon').html(data.longitude);
|
||||||
$('#isp').html(data.isp);
|
$('#organization').html(data.organization);
|
||||||
$('#org').html(data.org);
|
|
||||||
$('#as').html(data.as);
|
|
||||||
},
|
},
|
||||||
timeout: 5000
|
timeout: 5000
|
||||||
});
|
});
|
||||||
|
|
|
@ -223,6 +223,8 @@ function getPlatformImagePath(platformName) {
|
||||||
return 'interfaces/default/images/platforms/win8.png';
|
return 'interfaces/default/images/platforms/win8.png';
|
||||||
} else if (platformName.indexOf("Windows phone") > -1) {
|
} else if (platformName.indexOf("Windows phone") > -1) {
|
||||||
return 'interfaces/default/images/platforms/wp.png';
|
return 'interfaces/default/images/platforms/wp.png';
|
||||||
|
} else if (platformName.indexOf("Plex Media Player") > -1) {
|
||||||
|
return 'interfaces/default/images/platforms/pmp.png';
|
||||||
} else {
|
} else {
|
||||||
return 'interfaces/default/images/platforms/default.png';
|
return 'interfaces/default/images/platforms/default.png';
|
||||||
}
|
}
|
||||||
|
@ -230,7 +232,9 @@ function getPlatformImagePath(platformName) {
|
||||||
|
|
||||||
function isPrivateIP(ip_address) {
|
function isPrivateIP(ip_address) {
|
||||||
if (ip_address.indexOf(".") > -1) {
|
if (ip_address.indexOf(".") > -1) {
|
||||||
var parts = ip_address.split('.');
|
// get IPv4 mapped address (xxx.xxx.xxx.xxx) from IPv6 addresss (::ffff:xxx.xxx.xxx.xxx)
|
||||||
|
var parts = ip_address.split(":");
|
||||||
|
var parts = parts[parts.length - 1].split('.');
|
||||||
if (parts[0] === '10' ||
|
if (parts[0] === '10' ||
|
||||||
(parts[0] === '172' && (parseInt(parts[1], 10) >= 16 && parseInt(parts[1], 10) <= 31)) ||
|
(parts[0] === '172' && (parseInt(parts[1], 10) >= 16 && parseInt(parts[1], 10) <= 31)) ||
|
||||||
(parts[0] === '192' && parts[1] === '168')) {
|
(parts[0] === '192' && parts[1] === '168')) {
|
||||||
|
|
|
@ -121,6 +121,12 @@ from plexpy import helpers
|
||||||
$('#ajaxMsg').addClass('success').fadeIn().delay(3000).fadeOut();
|
$('#ajaxMsg').addClass('success').fadeIn().delay(3000).fadeOut();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#testIFTTT').click(function () {
|
||||||
|
$.get("/test_ifttt",
|
||||||
|
function (data) { $('#ajaxMsg').html("<i class='fa fa-check'></i> " + data); });
|
||||||
|
$('#ajaxMsg').addClass('success').fadeIn().delay(3000).fadeOut();
|
||||||
|
});
|
||||||
|
|
||||||
// Never send checkbox values directly, always substitute value in hidden input.
|
// Never send checkbox values directly, always substitute value in hidden input.
|
||||||
$('.checkboxes').click(function() {
|
$('.checkboxes').click(function() {
|
||||||
var configToggle = $(this).data('id');
|
var configToggle = $(this).data('id');
|
||||||
|
|
|
@ -57,6 +57,27 @@ 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 class="checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" data-size="small" data-id="${data['id']}" data-config-name="${data['config_prefix']}_on_extdown" ${helpers.checked(data['on_extdown'])} class="toggle-switches">
|
||||||
|
Notify on Plex remote access down
|
||||||
|
</label>
|
||||||
|
<p class="help-block">Trigger notification when the Plex Media Server cannot be reached externally.</p>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" data-size="small" data-id="${data['id']}" data-config-name="${data['config_prefix']}_on_intdown" ${helpers.checked(data['on_intdown'])} class="toggle-switches">
|
||||||
|
Notify on Plex server down
|
||||||
|
</label>
|
||||||
|
<p class="help-block">Trigger notification when the Plex Media Server cannot be reached internally.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -81,6 +102,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 +114,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>
|
||||||
|
|
|
@ -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);">
|
||||||
|
|
|
@ -26,13 +26,13 @@
|
||||||
<%def name="javascriptIncludes()">
|
<%def name="javascriptIncludes()">
|
||||||
<script>
|
<script>
|
||||||
$('#search_button').removeClass('btn-inactive');
|
$('#search_button').removeClass('btn-inactive');
|
||||||
$('#search_query').val('${query}').css({ right: '0', width: '250px' }).addClass('active');
|
$('#query').val("${query.replace('"','\\"')}").css({ right: '0', width: '250px' }).addClass('active');
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: 'get_search_results_children',
|
url: 'get_search_results_children',
|
||||||
type: "GET",
|
type: "GET",
|
||||||
async: true,
|
async: true,
|
||||||
data: {'query': "${query}"},
|
data: {'query': "${query.replace('"','\\"')}"},
|
||||||
complete: function (xhr, status) {
|
complete: function (xhr, status) {
|
||||||
$("#search-results-list").html(xhr.responseText);
|
$("#search-results-list").html(xhr.responseText);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
@ -273,9 +273,10 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||||
<label for="pms_ip">Plex IP or Hostname</label>
|
<label for="pms_ip">Plex IP or Hostname</label>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<input type="text" class="pms-settings form-control" id="pms_ip" name="pms_ip" value="${config['pms_ip']}" size="30" data-parsley-trigger="change" aria-describedby="server-verified" required>
|
<input type="text" class="pms-settings form-control" id="pms_ip" name="pms_ip" value="${config['pms_ip']}" size="30" data-parsley-trigger="change" aria-describedby="server-verified" data-parsley-errors-container="#pms_ip_error" required>
|
||||||
<span class="form-control-feedback" id="pms-verify" aria-hidden="true" style="display: none;"></span>
|
<span class="form-control-feedback" id="pms-verify" aria-hidden="true" style="display: none;"></span>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="pms_ip_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
<p class="help-block">IP Address or hostname for Plex Media Server.</p>
|
<p class="help-block">IP Address or hostname for Plex Media Server.</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -403,21 +404,33 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||||
</label>
|
</label>
|
||||||
<p class="help-block">Instead of polling the server at regular intervals let the server tell us when something happens. This is currently experimental. Encrypted websocket is not currently supported.</p>
|
<p class="help-block">Instead of polling the server at regular intervals let the server tell us when something happens. This is currently experimental. Encrypted websocket is not currently supported.</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="monitor_remote_access" name="monitor_remote_access" value="1" ${config['monitor_remote_access']}> Monitor Plex Remote Access
|
||||||
|
</label>
|
||||||
|
<p class="help-block">Enable to have PlexPy check if remote access to the Plex Media Server goes down. Your server needs to have remote access enabled.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="padded-header">
|
<div class="padded-header">
|
||||||
<h3>History Logging</h3>
|
<h3>History Logging</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" id="video_logging_enable" name="video_logging_enable" value="1" ${config['video_logging_enable']}> Log Movies and TV
|
<input type="checkbox" id="movie_logging_enable" name="movie_logging_enable" value="1" ${config['movie_logging_enable']}> Log Movies
|
||||||
</label>
|
</label>
|
||||||
<p class="help-block">Keep records of all video items played from your Plex Media Server.</p>
|
<p class="help-block">Keep records of all movie items played from your Plex Media Server.</p>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="tv_logging_enable" name="tv_logging_enable" value="1" ${config['tv_logging_enable']}> Log TV Shows
|
||||||
|
</label>
|
||||||
|
<p class="help-block">Keep records of all TV show items played from your Plex Media Server.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" id="music_logging_enable" name="music_logging_enable" value="1" ${config['music_logging_enable']}> Log Music
|
<input type="checkbox" id="music_logging_enable" name="music_logging_enable" value="1" ${config['music_logging_enable']}> Log Music
|
||||||
</label>
|
</label>
|
||||||
<p class="help-block">Keep records of all audio items played from your Plex Media Server. VERY experimental.</p>
|
<p class="help-block">Keep records of all music items played from your Plex Media Server.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="logging_ignore_interval">Ignore Interval</label>
|
<label for="logging_ignore_interval">Ignore Interval</label>
|
||||||
|
@ -435,7 +448,7 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||||
</label>
|
</label>
|
||||||
<span id="debugLogCheck" style="color: #eb8600; padding-left: 10px;"></span>
|
<span id="debugLogCheck" style="color: #eb8600; padding-left: 10px;"></span>
|
||||||
<p class="help-block">
|
<p class="help-block">
|
||||||
Enable this to attempt to log the IP address of the user.
|
Enable this to attempt to log the IP address of the user (for PMS 0.9.12 and below, IP address is automatically logged for PMS 0.9.14 and above).
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -473,7 +486,12 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||||
</div>
|
</div>
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="movie_notify_enable" id="movie_notify_enable" value="1" ${config['movie_notify_enable']}> Enable Movie and TV Notifications
|
<input type="checkbox" name="movie_notify_enable" id="movie_notify_enable" value="1" ${config['movie_notify_enable']}> Enable Movie Notifications
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="tv_notify_enable" id="tv_notify_enable" value="1" ${config['tv_notify_enable']}> Enable TV Show Notifications
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
|
@ -483,7 +501,7 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="padded-header">
|
<div class="padded-header">
|
||||||
<h3>Notification Tuning</h3>
|
<h3>Current Activity Notifications</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -503,6 +521,27 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||||
<p class="help-block">Disable to prevent consecutive notifications (i.e. both watched & stopped notifications).</p>
|
<p class="help-block">Disable to prevent consecutive notifications (i.e. both watched & stopped notifications).</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="padded-header">
|
||||||
|
<h3>Recently Added Notifications</h3>
|
||||||
|
</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="form-group">
|
||||||
|
<label for="notify_recently_added_delay">Notification Delay</label>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-2">
|
||||||
|
<input type="text" class="form-control" data-parsley-type="integer" id="notify_recently_added_delay" name="notify_recently_added_delay" value="${config['notify_recently_added_delay']}" size="5" data-parsley-min="60" data-parsley-trigger="change" data-parsley-errors-container="#notify_recently_added_delay_error" required>
|
||||||
|
</div>
|
||||||
|
<div id="notify_recently_added_delay_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||||
|
</div>
|
||||||
|
<p class="help-block">Set the delay for recently added notifications to allow metadata to be processed. Minimum 60 seconds.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="padded-header">
|
<div class="padded-header">
|
||||||
<h3>Custom Notification Messages</h3>
|
<h3>Custom Notification Messages</h3>
|
||||||
</div>
|
</div>
|
||||||
|
@ -515,9 +554,9 @@ 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 fa-fw"></i> Playback Start<i class="fa fa-chevron-down"></i></div>
|
||||||
<ul class="submenu">
|
<ul class="submenu">
|
||||||
<li>
|
<li>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -527,14 +566,14 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="notify_on_start_body_text">Message Body</label>
|
<label for="notify_on_start_body_text">Message Body</label>
|
||||||
<input class="form-control" type="text" id="notify_on_start_body_text" name="notify_on_start_body_text" value="${config['notify_on_start_body_text']}" data-parsley-trigger="change" required>
|
<textarea class="form-control" id="notify_on_start_body_text" name="notify_on_start_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_start_body_text']}</textarea>
|
||||||
<p class="help-block">Set a custom body.</p>
|
<p class="help-block">Set a custom body.</p>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div class="link"><i class="fa fa-stop"></i>Playback Stop<i class="fa fa-chevron-down"></i></div>
|
<div class="link"><i class="fa fa-stop fa-fw"></i> Playback Stop<i class="fa fa-chevron-down"></i></div>
|
||||||
<ul class="submenu">
|
<ul class="submenu">
|
||||||
<li>
|
<li>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -544,14 +583,14 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="notify_on_stop_body_text">Message Body</label>
|
<label for="notify_on_stop_body_text">Message Body</label>
|
||||||
<input class="form-control" type="text" id="notify_on_stop_body_text" name="notify_on_stop_body_text" value="${config['notify_on_stop_body_text']}" data-parsley-trigger="change" required>
|
<textarea class="form-control" id="notify_on_stop_body_text" name="notify_on_stop_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_stop_body_text']}</textarea>
|
||||||
<p class="help-block">Set a custom body.</p>
|
<p class="help-block">Set a custom body.</p>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div class="link"><i class="fa fa-pause"></i>Playback Pause<i class="fa fa-chevron-down"></i></div>
|
<div class="link"><i class="fa fa-pause fa-fw"></i> Playback Pause<i class="fa fa-chevron-down"></i></div>
|
||||||
<ul class="submenu">
|
<ul class="submenu">
|
||||||
<li>
|
<li>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -561,14 +600,14 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="notify_on_pause_body_text">Message Body</label>
|
<label for="notify_on_pause_body_text">Message Body</label>
|
||||||
<input class="form-control" type="text" id="notify_on_pause_body_text" name="notify_on_pause_body_text" value="${config['notify_on_pause_body_text']}" data-parsley-trigger="change" required>
|
<textarea class="form-control" id="notify_on_pause_body_text" name="notify_on_pause_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_pause_body_text']}</textarea>
|
||||||
<p class="help-block">Set a custom body.</p>
|
<p class="help-block">Set a custom body.</p>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div class="link"><i class="fa fa-play"></i>Playback Resume<i class="fa fa-chevron-down"></i></div>
|
<div class="link"><i class="fa fa-play fa-fw"></i> Playback Resume<i class="fa fa-chevron-down"></i></div>
|
||||||
<ul class="submenu">
|
<ul class="submenu">
|
||||||
<li>
|
<li>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -578,14 +617,14 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="notify_on_resume_body_text">Message Body</label>
|
<label for="notify_on_resume_body_text">Message Body</label>
|
||||||
<input class="form-control" type="text" id="notify_on_resume_body_text" name="notify_on_resume_body_text" value="${config['notify_on_resume_body_text']}" data-parsley-trigger="change" required>
|
<textarea class="form-control" id="notify_on_resume_body_text" name="notify_on_resume_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_resume_body_text']}</textarea>
|
||||||
<p class="help-block">Set a custom body.</p>
|
<p class="help-block">Set a custom body.</p>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div class="link"><i class="fa fa-eye"></i>Watched<i class="fa fa-chevron-down"></i></div>
|
<div class="link"><i class="fa fa-eye fa-fw"></i> Watched<i class="fa fa-chevron-down"></i></div>
|
||||||
<ul class="submenu">
|
<ul class="submenu">
|
||||||
<li>
|
<li>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -595,14 +634,14 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="notify_on_watched_body_text">Message Body</label>
|
<label for="notify_on_watched_body_text">Message Body</label>
|
||||||
<input class="form-control" type="text" id="notify_on_watched_body_text" name="notify_on_watched_body_text" value="${config['notify_on_watched_body_text']}" data-parsley-trigger="change" required>
|
<textarea class="form-control" id="notify_on_watched_body_text" name="notify_on_watched_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_watched_body_text']}</textarea>
|
||||||
<p class="help-block">Set a custom body.</p>
|
<p class="help-block">Set a custom body.</p>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div class="link"><i class="fa fa-spinner"></i>Buffer Warnings<i class="fa fa-chevron-down"></i></div>
|
<div class="link"><i class="fa fa-spinner fa-fw"></i> Buffer Warnings<i class="fa fa-chevron-down"></i></div>
|
||||||
<ul class="submenu">
|
<ul class="submenu">
|
||||||
<li>
|
<li>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -612,7 +651,60 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="notify_on_buffer_body_text">Message Body</label>
|
<label for="notify_on_buffer_body_text">Message Body</label>
|
||||||
<input class="form-control" type="text" id="notify_on_buffer_body_text" name="notify_on_buffer_body_text" value="${config['notify_on_buffer_body_text']}" data-parsley-trigger="change" required>
|
<textarea class="form-control" id="notify_on_buffer_body_text" name="notify_on_buffer_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_buffer_body_text']}</textarea>
|
||||||
|
<p class="help-block">Set a custom body.</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul id="accordion-timeline" class="accordion list-unstyled">
|
||||||
|
<li>
|
||||||
|
<div class="link"><i class="fa fa-download fa-fw"></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>
|
||||||
|
<textarea class="form-control" id="notify_on_created_body_text" name="notify_on_created_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_created_body_text']}</textarea>
|
||||||
|
<p class="help-block">Set a custom body.</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div class="link"><i class="fa fa-server fa-fw"></i> Plex Remote Access Down<i class="fa fa-chevron-down"></i></div>
|
||||||
|
<ul class="submenu">
|
||||||
|
<li>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="notify_on_extdown_subject_text">Subject Line</label>
|
||||||
|
<input class="form-control" type="text" id="notify_on_extdown_subject_text" name="notify_on_extdown_subject_text" value="${config['notify_on_extdown_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_extdown_body_text">Message Body</label>
|
||||||
|
<textarea class="form-control" id="notify_on_extdown_body_text" name="notify_on_extdown_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_extdown_body_text']}</textarea>
|
||||||
|
<p class="help-block">Set a custom body.</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div class="link"><i class="fa fa-server fa-fw"></i> Plex Server Down<i class="fa fa-chevron-down"></i></div>
|
||||||
|
<ul class="submenu">
|
||||||
|
<li>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="notify_on_intdown_subject_text">Subject Line</label>
|
||||||
|
<input class="form-control" type="text" id="notify_on_intdown_subject_text" name="notify_on_intdown_subject_text" value="${config['notify_on_intdown_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_intdown_body_text">Message Body</label>
|
||||||
|
<textarea class="form-control" id="notify_on_intdown_body_text" name="notify_on_intdown_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_intdown_body_text']}</textarea>
|
||||||
<p class="help-block">Set a custom body.</p>
|
<p class="help-block">Set a custom body.</p>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
@ -635,7 +727,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'] or agent['on_extdown'] or agent['on_intdown']:
|
||||||
<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>
|
||||||
|
@ -845,6 +937,10 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||||
<td width="150"><strong>{server_name}</strong></td>
|
<td width="150"><strong>{server_name}</strong></td>
|
||||||
<td>The name of your Plex Server.</td>
|
<td>The name of your Plex Server.</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="150"><strong>{server_uptime}</strong></td>
|
||||||
|
<td>The uptime (in days, hours, mins, secs) of your Plex Server.</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="150"><strong>{user}</strong></td>
|
<td width="150"><strong>{user}</strong></td>
|
||||||
<td>The username of the person streaming.</td>
|
<td>The username of the person streaming.</td>
|
||||||
|
@ -857,13 +953,17 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||||
<td width="150"><strong>{player}</strong></td>
|
<td width="150"><strong>{player}</strong></td>
|
||||||
<td>The name of the device being used for playback.</td>
|
<td>The name of the device being used for playback.</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="150"><strong>{ip_address}</strong></td>
|
||||||
|
<td>The IP address of the device being used for playback. (PMS 0.9.14 and above)</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="150"><strong>{media_type}</strong></td>
|
<td width="150"><strong>{media_type}</strong></td>
|
||||||
<td>The type of media being played (movie, episode, track).</td>
|
<td>The type of media being played (movie, episode, track).</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="150"><strong>{title}</strong></td>
|
<td width="150"><strong>{title}</strong></td>
|
||||||
<td>The title of the item being played.</td>
|
<td>The full title of the item being played.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="150"><strong>{show_name}</strong></td>
|
<td width="150"><strong>{show_name}</strong></td>
|
||||||
|
@ -881,6 +981,10 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||||
<td width="150"><strong>{album_name}</strong></td>
|
<td width="150"><strong>{album_name}</strong></td>
|
||||||
<td>The title of the album being played.</td>
|
<td>The title of the album being played.</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="150"><strong>{track_name}</strong></td>
|
||||||
|
<td>The title of the track being played.</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="150"><strong>{season_num}</strong></td>
|
<td width="150"><strong>{season_num}</strong></td>
|
||||||
<td>The season number for the media item if item is episode.</td>
|
<td>The season number for the media item if item is episode.</td>
|
||||||
|
@ -913,10 +1017,30 @@ available_notification_agents = notifiers.available_notification_agents()
|
||||||
<td width="150"><strong>{content_rating}</strong></td>
|
<td width="150"><strong>{content_rating}</strong></td>
|
||||||
<td>The content rating for the media item. (e.g. TV-MA, TV-PG, etc.)</td>
|
<td>The content rating for the media item. (e.g. TV-MA, TV-PG, etc.)</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="150"><strong>{directors}</strong></td>
|
||||||
|
<td>A list of directors for the media item.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="150"><strong>{writers}</strong></td>
|
||||||
|
<td>A list of writers for the media item.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="150"><strong>{actors}</strong></td>
|
||||||
|
<td>A list of actors for the media item.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="150"><strong>{genres}</strong></td>
|
||||||
|
<td>A list of genres for the media item.</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="150"><strong>{summary}</strong></td>
|
<td width="150"><strong>{summary}</strong></td>
|
||||||
<td>A short plot summary for the media item.</td>
|
<td>A short plot summary for the media item.</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="150"><strong>{tagline}</strong></td>
|
||||||
|
<td>A tagline for the media item.</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="150"><strong>{rating}</strong></td>
|
<td width="150"><strong>{rating}</strong></td>
|
||||||
<td>The rating (out of 10) for the item.</td>
|
<td>The rating (out of 10) for the item.</td>
|
||||||
|
@ -1051,18 +1175,22 @@ $(document).ready(function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
var configForm = $("#configUpdate");
|
var configForm = $("#configUpdate");
|
||||||
$('.save-button').click(function() {
|
function saveSettings() {
|
||||||
if ($("#pms_identifier").val() == "") {
|
|
||||||
showMsg('<i class="fa fa-exclamation-circle"></i> Please verify your server.',false,true,2000,true)
|
|
||||||
} else {
|
|
||||||
if (configForm.parsley().validate()) {
|
if (configForm.parsley().validate()) {
|
||||||
doAjaxCall('configUpdate',$(this),'tabs',true);
|
doAjaxCall('configUpdate', $(this), 'tabs', true);
|
||||||
postSaveChecks();
|
postSaveChecks();
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
showMsg('<i class="fa fa-exclamation-circle"></i> Please verify your settings.',false,true,2000,true)
|
showMsg('<i class="fa fa-exclamation-circle"></i> Please verify your settings.', false, true, 2000, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('.save-button').click(function() {
|
||||||
|
if ($("#pms_identifier").val() == "") {
|
||||||
|
verifyServer(function () { saveSettings() });
|
||||||
|
} else {
|
||||||
|
saveSettings();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#menu_link_shutdown").click(function() {
|
$("#menu_link_shutdown").click(function() {
|
||||||
|
@ -1147,7 +1275,7 @@ $(document).ready(function() {
|
||||||
verifyServer();
|
verifyServer();
|
||||||
});
|
});
|
||||||
|
|
||||||
function verifyServer() {
|
function verifyServer(_callback) {
|
||||||
var pms_ip = $("#pms_ip").val()
|
var pms_ip = $("#pms_ip").val()
|
||||||
var pms_port = $("#pms_port").val()
|
var pms_port = $("#pms_port").val()
|
||||||
if (($("#pms_ip").val() !== '') || ($("#pms_port").val() !== '')) {
|
if (($("#pms_ip").val() !== '') || ($("#pms_port").val() !== '')) {
|
||||||
|
@ -1170,10 +1298,15 @@ $(document).ready(function() {
|
||||||
$("#pms-verify").html('<i class="fa fa-check"></i>');
|
$("#pms-verify").html('<i class="fa fa-check"></i>');
|
||||||
$('#pms-verify').fadeIn('fast');
|
$('#pms-verify').fadeIn('fast');
|
||||||
$("#pms-ip-group").removeClass("has-error");
|
$("#pms-ip-group").removeClass("has-error");
|
||||||
|
|
||||||
|
if (_callback) {
|
||||||
|
_callback();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$("#pms-verify").html('<i class="fa fa-close"></i>');
|
$("#pms-verify").html('<i class="fa fa-close"></i>');
|
||||||
$('#pms-verify').fadeIn('fast');
|
$('#pms-verify').fadeIn('fast');
|
||||||
$("#pms-ip-group").addClass("has-error");
|
$("#pms-ip-group").addClass("has-error");
|
||||||
|
showMsg('<i class="fa fa-exclamation-circle"></i> Could not verify your server.', false, true, 2000, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1181,6 +1314,7 @@ $(document).ready(function() {
|
||||||
$("#pms-verify").html('<i class="fa fa-close"></i>');
|
$("#pms-verify").html('<i class="fa fa-close"></i>');
|
||||||
$('#pms-verify').fadeIn('fast');
|
$('#pms-verify').fadeIn('fast');
|
||||||
$("#pms-ip-group").addClass("has-error");
|
$("#pms-ip-group").addClass("has-error");
|
||||||
|
showMsg('<i class="fa fa-exclamation-circle"></i> Could not verify your server.', false, true, 2000, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1199,7 +1333,7 @@ $(document).ready(function() {
|
||||||
'X-Plex-Version': '${common.VERSION_NUMBER}',
|
'X-Plex-Version': '${common.VERSION_NUMBER}',
|
||||||
'X-Plex-Platform': '${common.PLATFORM}',
|
'X-Plex-Platform': '${common.PLATFORM}',
|
||||||
'X-Plex-Platform-Version': '${common.PLATFORM_VERSION}',
|
'X-Plex-Platform-Version': '${common.PLATFORM_VERSION}',
|
||||||
'X-Plex-Client-Identifier': '${config['pms_uuid']}',
|
'X-Plex-Client-Identifier': '${config["pms_uuid"]}',
|
||||||
'Authorization': 'Basic ' + btoa($("#pms_username").val() + ':' + $("#pms_password").val())
|
'Authorization': 'Basic ' + btoa($("#pms_username").val() + ':' + $("#pms_password").val())
|
||||||
},
|
},
|
||||||
error: function(jqXHR, textStatus, errorThrown) {
|
error: function(jqXHR, textStatus, errorThrown) {
|
||||||
|
@ -1292,7 +1426,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) {
|
||||||
|
@ -1334,6 +1469,14 @@ $(document).ready(function() {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// auto resizing textarea for custom notification message body
|
||||||
|
$('textarea[data-autoresize]').each(function() {
|
||||||
|
var offset = this.offsetHeight - this.clientHeight;
|
||||||
|
var resizeTextarea = function(el) {
|
||||||
|
$(el).css('height', 'auto').css('height', el.scrollHeight + offset);
|
||||||
|
};
|
||||||
|
$(this).on('focus keyup input', function() { resizeTextarea(this); }).removeAttr('data-autoresize');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</%def>
|
</%def>
|
|
@ -262,33 +262,6 @@ from plexpy import helpers
|
||||||
<script src="interfaces/default/js/tables/user_ips.js"></script>
|
<script src="interfaces/default/js/tables/user_ips.js"></script>
|
||||||
<script src="interfaces/default/js/tables/sync_table.js"></script>
|
<script src="interfaces/default/js/tables/sync_table.js"></script>
|
||||||
<script>
|
<script>
|
||||||
function recentlyWatched() {
|
|
||||||
var widthVal = $('body').find("#user-recently-watched").width();
|
|
||||||
var tmp = (widthVal-32) / 180;
|
|
||||||
|
|
||||||
if (tmp > 0) {
|
|
||||||
containerSize = parseInt(tmp);
|
|
||||||
} else {
|
|
||||||
containerSize = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
% if data['user_id']:
|
|
||||||
var user_id = ${data['user_id']};
|
|
||||||
% else:
|
|
||||||
var user_id = null;
|
|
||||||
% endif
|
|
||||||
|
|
||||||
// Populate recently watched
|
|
||||||
$.ajax({
|
|
||||||
url: 'get_user_recently_watched',
|
|
||||||
async: true,
|
|
||||||
data: { user_id: user_id, user: '${data['username']}', limit: containerSize },
|
|
||||||
complete: function(xhr, status) {
|
|
||||||
$("#user-recently-watched").html(xhr.responseText);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
|
||||||
% if data['user_id']:
|
% if data['user_id']:
|
||||||
|
@ -297,13 +270,15 @@ from plexpy import helpers
|
||||||
var user_id = null;
|
var user_id = null;
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
|
var username = '${data['username'].replace("'", "\\'")}';
|
||||||
|
|
||||||
$("#edit-user-tooltip").tooltip();
|
$("#edit-user-tooltip").tooltip();
|
||||||
|
|
||||||
// Populate watch time stats
|
// Populate watch time stats
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: 'get_user_watch_time_stats',
|
url: 'get_user_watch_time_stats',
|
||||||
async: true,
|
async: true,
|
||||||
data: { user_id: user_id, user: '${data['username']}' },
|
data: { user_id: user_id, user: username },
|
||||||
complete: function(xhr, status) {
|
complete: function(xhr, status) {
|
||||||
$("#user-time-stats").html(xhr.responseText);
|
$("#user-time-stats").html(xhr.responseText);
|
||||||
}
|
}
|
||||||
|
@ -313,21 +288,23 @@ from plexpy import helpers
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: 'get_user_player_stats',
|
url: 'get_user_player_stats',
|
||||||
async: true,
|
async: true,
|
||||||
data: { user_id: user_id, user: '${data['username']}' },
|
data: { user_id: user_id, user: username },
|
||||||
complete: function(xhr, status) {
|
complete: function(xhr, status) {
|
||||||
$("#user-player-stats").html(xhr.responseText);
|
$("#user-player-stats").html(xhr.responseText);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$( "#history-tab-btn" ).one( "click", function() {
|
function loadHistoryTable(media_type) {
|
||||||
// Build watch history table
|
// Build watch history table
|
||||||
history_table_options.ajax = {
|
history_table_options.ajax = {
|
||||||
"url": "get_history",
|
url: 'get_history',
|
||||||
type: 'post',
|
type: 'post',
|
||||||
data: function ( d ) {
|
data: function ( d ) {
|
||||||
return { 'json_data': JSON.stringify( d ),
|
return {
|
||||||
|
'json_data': JSON.stringify( d ),
|
||||||
'user_id': user_id,
|
'user_id': user_id,
|
||||||
'user': "${data['username']}"
|
'user': username,
|
||||||
|
'media_type': media_type
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -338,6 +315,34 @@ from plexpy import helpers
|
||||||
$(colvis.button()).appendTo('#button-bar-history');
|
$(colvis.button()).appendTo('#button-bar-history');
|
||||||
|
|
||||||
clearSearchButton('history_table', history_table);
|
clearSearchButton('history_table', history_table);
|
||||||
|
|
||||||
|
$('#history_table_filter').prepend('<div class="btn-group" data-toggle="buttons" id="media_type-selection" style="padding-right: 15px;"> \
|
||||||
|
<label class="btn btn-dark active"> \
|
||||||
|
<input type="radio" name="media_type-filter" id="history-all" value="all" autocomplete="off"> All \
|
||||||
|
</label> \
|
||||||
|
<label class="btn btn-dark"> \
|
||||||
|
<input type="radio" name="media_type-filter" id="history-movies" value="movie" autocomplete="off"> Movies \
|
||||||
|
</label> \
|
||||||
|
<label class="btn btn-dark"> \
|
||||||
|
<input type="radio" name="media_type-filter" id="history-tv_shows" value="episode" autocomplete="off"> TV Shows \
|
||||||
|
</label> \
|
||||||
|
<label class="btn btn-dark"> \
|
||||||
|
<input type="radio" name="media_type-filter" id="history-music" value="track" autocomplete="off"> Music \
|
||||||
|
</label> \
|
||||||
|
</div>');
|
||||||
|
|
||||||
|
$('#media_type-selection').on('change', function () {
|
||||||
|
$('#media_type-selection > label').removeClass('active');
|
||||||
|
selected_filter = $('input[name=media_type-filter]:checked', '#media_type-selection');
|
||||||
|
$(selected_filter).closest('label').addClass('active');
|
||||||
|
media_type = $(selected_filter).val();
|
||||||
|
history_table.draw();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$( "#history-tab-btn" ).one( "click", function() {
|
||||||
|
var media_type = 'all';
|
||||||
|
loadHistoryTable(media_type);
|
||||||
});
|
});
|
||||||
|
|
||||||
$( "#ip-tab-btn" ).one( "click", function() {
|
$( "#ip-tab-btn" ).one( "click", function() {
|
||||||
|
@ -348,7 +353,7 @@ from plexpy import helpers
|
||||||
data: function ( d ) {
|
data: function ( d ) {
|
||||||
return { 'json_data': JSON.stringify( d ),
|
return { 'json_data': JSON.stringify( d ),
|
||||||
'user_id': user_id,
|
'user_id': user_id,
|
||||||
'user': "${data['username']}"
|
'user': username
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -363,7 +368,7 @@ from plexpy import helpers
|
||||||
"url": "get_sync",
|
"url": "get_sync",
|
||||||
"data": function(d) {
|
"data": function(d) {
|
||||||
d.user_id = user_id;
|
d.user_id = user_id;
|
||||||
d.user = "${data['username']}";
|
d.user = username;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sync_table = $('#sync_table').DataTable(sync_table_options);
|
sync_table = $('#sync_table').DataTable(sync_table_options);
|
||||||
|
@ -380,7 +385,7 @@ from plexpy import helpers
|
||||||
$("#edit-user-tooltip").tooltip('hide');
|
$("#edit-user-tooltip").tooltip('hide');
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: 'edit_user_dialog',
|
url: 'edit_user_dialog',
|
||||||
data: { user_id: user_id, user: '${data['username']}' },
|
data: { user_id: user_id, user: username },
|
||||||
cache: false,
|
cache: false,
|
||||||
async: true,
|
async: true,
|
||||||
complete: function(xhr, status) {
|
complete: function(xhr, status) {
|
||||||
|
@ -426,6 +431,33 @@ from plexpy import helpers
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function recentlyWatched() {
|
||||||
|
var widthVal = $('body').find("#user-recently-watched").width();
|
||||||
|
var tmp = (widthVal-32) / 180;
|
||||||
|
|
||||||
|
if (tmp > 0) {
|
||||||
|
containerSize = parseInt(tmp);
|
||||||
|
} else {
|
||||||
|
containerSize = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
% if data['user_id']:
|
||||||
|
var user_id = ${data['user_id']};
|
||||||
|
% else:
|
||||||
|
var user_id = null;
|
||||||
|
% endif
|
||||||
|
|
||||||
|
// Populate recently watched
|
||||||
|
$.ajax({
|
||||||
|
url: 'get_user_recently_watched',
|
||||||
|
async: true,
|
||||||
|
data: { user_id: user_id, user: username, limit: containerSize },
|
||||||
|
complete: function(xhr, status) {
|
||||||
|
$("#user-recently-watched").html(xhr.responseText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
recentlyWatched();
|
recentlyWatched();
|
||||||
$(window).resize(function() {
|
$(window).resize(function() {
|
||||||
recentlyWatched();
|
recentlyWatched();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<%
|
<%
|
||||||
import plexpy
|
import plexpy
|
||||||
from plexpy import common
|
from plexpy import common
|
||||||
%>
|
%>
|
||||||
|
@ -104,9 +104,15 @@ from plexpy import common
|
||||||
|
|
||||||
<div class="wizard-card" data-cardname="card4">
|
<div class="wizard-card" data-cardname="card4">
|
||||||
<h3>Monitoring</h3>
|
<h3>Monitoring</h3>
|
||||||
|
<p class="help-block">Keep records of all movie, TV show, or music items played from your Plex Media Server.</p>
|
||||||
<div class="wizard-input-section">
|
<div class="wizard-input-section">
|
||||||
<input type="checkbox" id="video_logging_enable" name="video_logging_enable" value="1" ${config['video_logging_enable']}> Log Movies and TV
|
<input type="checkbox" id="movie_logging_enable" name="movie_logging_enable" value="1" ${config['movie_logging_enable']}> Log Movies
|
||||||
<p class="help-block">Keep records of all video items played from your Plex Media Server.</p>
|
</div>
|
||||||
|
<div class="wizard-input-section">
|
||||||
|
<input type="checkbox" id="tv_logging_enable" name="tv_logging_enable" value="1" ${config['tv_logging_enable']}> Log TV Shows
|
||||||
|
</div>
|
||||||
|
<div class="wizard-input-section">
|
||||||
|
<input type="checkbox" id="music_logging_enable" name="music_logging_enable" value="1" ${config['music_logging_enable']}> Log Music
|
||||||
</div>
|
</div>
|
||||||
<div class="wizard-input-section">
|
<div class="wizard-input-section">
|
||||||
<label for="logging_ignore_interval">Ignore Interval</label>
|
<label for="logging_ignore_interval">Ignore Interval</label>
|
||||||
|
@ -118,19 +124,16 @@ from plexpy import common
|
||||||
</div>
|
</div>
|
||||||
<p class="help-block">The interval (in seconds) PlexPy will wait for a video item to be active before logging it. 0 to disable.</p>
|
<p class="help-block">The interval (in seconds) PlexPy will wait for a video item to be active before logging it. 0 to disable.</p>
|
||||||
</div>
|
</div>
|
||||||
<!-- Music logging is still very experimental -- leave this for now.
|
|
||||||
<div class="wizard-input-section">
|
|
||||||
<input type="checkbox" id="music_logging_enable" name="music_logging_enable" value="1"> Log Music
|
|
||||||
<p class="help-block">Keep records of all audio items played from your Plex Media Server. VERY experimental.</p>
|
|
||||||
</div>
|
|
||||||
-->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="wizard-card" data-cardname="card5" data-validate="validateNotifications">
|
<div class="wizard-card" data-cardname="card5" data-validate="validateNotifications">
|
||||||
<h3>Notifications</h3>
|
<h3>Notifications</h3>
|
||||||
<p class="help-block">PlexPy supports a wide variety of notification options. To set up a notification agent conifgure this in <strong>Settings -> Notification Agents</strong>
|
<p class="help-block">PlexPy supports a wide variety of notification options. To set up a notification agent conifgure this in <strong>Settings -> Notification Agents</strong>
|
||||||
after you have completed this setup wizard.</p><br/>
|
after you have completed this setup wizard.</p><br/>
|
||||||
<div class="wizard-input-section">
|
<div class="wizard-input-section">
|
||||||
<input type="checkbox" name="movie_notify_enable" id="movie_notify_enable" value="1" ${config['movie_notify_enable']}> Enable notifications on Movie and TV playback
|
<input type="checkbox" name="movie_notify_enable" id="movie_notify_enable" value="1" ${config['movie_notify_enable']}> Enable notifications on Movie playback
|
||||||
|
</div>
|
||||||
|
<div class="wizard-input-section">
|
||||||
|
<input type="checkbox" name="tv_notify_enable" id="tv_notify_enable" value="1" ${config['tv_notify_enable']}> Enable notifications on TV Show playback
|
||||||
</div>
|
</div>
|
||||||
<div class="wizard-input-section">
|
<div class="wizard-input-section">
|
||||||
<input type="checkbox" name="music_notify_enable" id="music_notify_enable" value="1" ${config['music_notify_enable']}> Enable notifications on Music playback
|
<input type="checkbox" name="music_notify_enable" id="music_notify_enable" value="1" ${config['music_notify_enable']}> Enable notifications on Music playback
|
||||||
|
@ -262,6 +265,7 @@ from plexpy import common
|
||||||
if (ci != "undefined") {
|
if (ci != "undefined") {
|
||||||
// To allow next step in the guide.
|
// To allow next step in the guide.
|
||||||
// servers with clientIdentifier is verified
|
// servers with clientIdentifier is verified
|
||||||
|
$("#pms_identifier").val(ci);
|
||||||
$("#pms_valid").val("valid");
|
$("#pms_valid").val("valid");
|
||||||
$("#pms-verify-status").html('<i class="fa fa-check"></i> Server found!').show();
|
$("#pms-verify-status").html('<i class="fa fa-check"></i> Server found!').show();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -31,7 +31,7 @@ except ImportError:
|
||||||
from apscheduler.schedulers.background import BackgroundScheduler
|
from apscheduler.schedulers.background import BackgroundScheduler
|
||||||
from apscheduler.triggers.interval import IntervalTrigger
|
from apscheduler.triggers.interval import IntervalTrigger
|
||||||
|
|
||||||
from plexpy import versioncheck, logger, activity_pinger, plextv
|
from plexpy import versioncheck, logger, activity_pinger, plextv, pmsconnect
|
||||||
import plexpy.config
|
import plexpy.config
|
||||||
|
|
||||||
PROG_DIR = None
|
PROG_DIR = None
|
||||||
|
@ -169,6 +169,7 @@ def initialize(config_file):
|
||||||
# Get the real PMS urls for SSL and remote access
|
# Get the real PMS urls for SSL and remote access
|
||||||
if CONFIG.PMS_TOKEN and CONFIG.PMS_IP and CONFIG.PMS_PORT:
|
if CONFIG.PMS_TOKEN and CONFIG.PMS_IP and CONFIG.PMS_PORT:
|
||||||
plextv.get_real_pms_url()
|
plextv.get_real_pms_url()
|
||||||
|
pmsconnect.get_server_friendly_name()
|
||||||
|
|
||||||
# Refresh the users list on startup
|
# Refresh the users list on startup
|
||||||
if CONFIG.PMS_TOKEN and CONFIG.REFRESH_USERS_ON_STARTUP:
|
if CONFIG.PMS_TOKEN and CONFIG.REFRESH_USERS_ON_STARTUP:
|
||||||
|
@ -280,7 +281,14 @@ def initialize_scheduler():
|
||||||
seconds = 0
|
seconds = 0
|
||||||
|
|
||||||
if CONFIG.PMS_IP and CONFIG.PMS_TOKEN:
|
if CONFIG.PMS_IP and CONFIG.PMS_TOKEN:
|
||||||
schedule_job(plextv.get_real_pms_url, 'Refresh Plex Server URLs', hours=12, minutes=0, seconds=0)
|
schedule_job(plextv.get_real_pms_url, 'Refresh Plex Server URLs',
|
||||||
|
hours=12, minutes=0, seconds=0)
|
||||||
|
schedule_job(pmsconnect.get_server_friendly_name, 'Refresh Plex Server Name',
|
||||||
|
hours=12, minutes=0, seconds=0)
|
||||||
|
schedule_job(activity_pinger.check_recently_added, 'Check for recently added items',
|
||||||
|
hours=0, minutes=0, seconds=seconds)
|
||||||
|
schedule_job(activity_pinger.check_server_response, 'Check for server response',
|
||||||
|
hours=0, minutes=0, seconds=seconds)
|
||||||
|
|
||||||
# If we're not using websockets then fall back to polling
|
# If we're not using websockets then fall back to polling
|
||||||
if not CONFIG.MONITORING_USE_WEBSOCKET or POLLING_FAILOVER:
|
if not CONFIG.MONITORING_USE_WEBSOCKET or POLLING_FAILOVER:
|
||||||
|
@ -552,7 +560,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 +596,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')
|
||||||
|
|
|
@ -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
|
||||||
|
#logger.debug(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()
|
|
@ -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
|
||||||
|
@ -13,13 +13,15 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from plexpy import logger, pmsconnect, notification_handler, database, helpers, activity_processor
|
from plexpy import logger, pmsconnect, plextv, notification_handler, database, helpers, activity_processor
|
||||||
|
|
||||||
import threading
|
import threading
|
||||||
import plexpy
|
import plexpy
|
||||||
import time
|
import time
|
||||||
|
|
||||||
monitor_lock = threading.Lock()
|
monitor_lock = threading.Lock()
|
||||||
|
ext_ping_count = 0
|
||||||
|
int_ping_count = 0
|
||||||
|
|
||||||
|
|
||||||
def check_active_sessions(ws_request=False):
|
def check_active_sessions(ws_request=False):
|
||||||
|
@ -162,3 +164,109 @@ 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:
|
||||||
|
# add delay to allow for metadata processing
|
||||||
|
delay = plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_DELAY
|
||||||
|
time_threshold = int(time.time()) - delay
|
||||||
|
time_interval = plexpy.CONFIG.MONITORING_INTERVAL
|
||||||
|
|
||||||
|
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 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']))
|
||||||
|
|
||||||
|
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 %s" \
|
||||||
|
% str(item['rating_key']))
|
||||||
|
|
||||||
|
if metadata:
|
||||||
|
if not plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT:
|
||||||
|
for item in metadata:
|
||||||
|
if 0 < int(item['added_at']) - time_threshold <= time_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()
|
||||||
|
|
||||||
|
else:
|
||||||
|
item = max(metadata, key=lambda x:x['added_at'])
|
||||||
|
|
||||||
|
if 0 < int(item['added_at']) - time_threshold <= time_interval:
|
||||||
|
if item['media_type'] == 'episode' or item['media_type'] == 'track':
|
||||||
|
metadata_list = pms_connect.get_metadata_details(item['grandparent_rating_key'])
|
||||||
|
|
||||||
|
if metadata_list:
|
||||||
|
item = metadata_list['metadata']
|
||||||
|
else:
|
||||||
|
logger.error(u"PlexPy Monitor :: Unable to retrieve grandparent metadata for grandparent_rating_key %s" \
|
||||||
|
% str(item['rating_key']))
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
def check_server_response():
|
||||||
|
|
||||||
|
with monitor_lock:
|
||||||
|
pms_connect = pmsconnect.PmsConnect()
|
||||||
|
server_response = pms_connect.get_server_response()
|
||||||
|
|
||||||
|
global int_ping_count
|
||||||
|
global ext_ping_count
|
||||||
|
|
||||||
|
# Check for internal server response
|
||||||
|
if not server_response:
|
||||||
|
int_ping_count += 1
|
||||||
|
logger.warn(u"PlexPy Monitor :: Unable to get an internal response from the server, ping attempt %s." \
|
||||||
|
% str(int_ping_count))
|
||||||
|
# Reset internal ping counter
|
||||||
|
else:
|
||||||
|
int_ping_count = 0
|
||||||
|
|
||||||
|
# Check for remote access
|
||||||
|
if server_response and plexpy.CONFIG.MONITOR_REMOTE_ACCESS:
|
||||||
|
|
||||||
|
mapping_state = server_response['mapping_state']
|
||||||
|
mapping_error = server_response['mapping_error']
|
||||||
|
|
||||||
|
# Check if the port is mapped
|
||||||
|
if not mapping_state == 'mapped':
|
||||||
|
ext_ping_count += 1
|
||||||
|
logger.warn(u"PlexPy Monitor :: Plex remote access port not mapped, ping attempt %s." \
|
||||||
|
% str(ext_ping_count))
|
||||||
|
# Check if the port is open
|
||||||
|
elif mapping_error == 'unreachable':
|
||||||
|
ext_ping_count += 1
|
||||||
|
logger.warn(u"PlexPy Monitor :: Plex remote access port mapped, but mapping failed, ping attempt %s." \
|
||||||
|
% str(ext_ping_count))
|
||||||
|
# Reset external ping counter
|
||||||
|
else:
|
||||||
|
ext_ping_count = 0
|
||||||
|
|
||||||
|
if int_ping_count == 3:
|
||||||
|
# Fire off notifications
|
||||||
|
threading.Thread(target=notification_handler.notify_timeline,
|
||||||
|
kwargs=dict(notify_action='intdown')).start()
|
||||||
|
|
||||||
|
if ext_ping_count == 3:
|
||||||
|
# Fire off notifications
|
||||||
|
threading.Thread(target=notification_handler.notify_timeline,
|
||||||
|
kwargs=dict(notify_action='extdown')).start()
|
|
@ -39,6 +39,7 @@ class ActivityProcessor(object):
|
||||||
'parent_title': session['parent_title'],
|
'parent_title': session['parent_title'],
|
||||||
'grandparent_title': session['grandparent_title'],
|
'grandparent_title': session['grandparent_title'],
|
||||||
'friendly_name': session['friendly_name'],
|
'friendly_name': session['friendly_name'],
|
||||||
|
'ip_address': session['ip_address'],
|
||||||
'player': session['player'],
|
'player': session['player'],
|
||||||
'platform': session['platform'],
|
'platform': session['platform'],
|
||||||
'parent_rating_key': session['parent_rating_key'],
|
'parent_rating_key': session['parent_rating_key'],
|
||||||
|
@ -78,16 +79,16 @@ class ActivityProcessor(object):
|
||||||
kwargs=dict(stream_data=values, notify_action='play')).start()
|
kwargs=dict(stream_data=values, notify_action='play')).start()
|
||||||
|
|
||||||
started = int(time.time())
|
started = int(time.time())
|
||||||
|
timestamp = {'started': started}
|
||||||
|
|
||||||
# Try and grab IP address from logs
|
# Try and grab IP address from logs (fallback if not on PMS 0.9.14 and above)
|
||||||
|
if not session['ip_address']:
|
||||||
if plexpy.CONFIG.IP_LOGGING_ENABLE and plexpy.CONFIG.PMS_LOGS_FOLDER:
|
if plexpy.CONFIG.IP_LOGGING_ENABLE and plexpy.CONFIG.PMS_LOGS_FOLDER:
|
||||||
ip_address = self.find_session_ip(rating_key=session['rating_key'],
|
ip_address = self.find_session_ip(rating_key=session['rating_key'],
|
||||||
machine_id=session['machine_id'])
|
machine_id=session['machine_id'])
|
||||||
|
timestamp.update({'ip_address': ip_address})
|
||||||
else:
|
else:
|
||||||
ip_address = None
|
timestamp.update({'ip_address': None})
|
||||||
|
|
||||||
timestamp = {'started': started,
|
|
||||||
'ip_address': ip_address}
|
|
||||||
|
|
||||||
# If it's our first write then time stamp it.
|
# If it's our first write then time stamp it.
|
||||||
self.db.upsert('sessions', timestamp, keys)
|
self.db.upsert('sessions', timestamp, keys)
|
||||||
|
@ -109,8 +110,11 @@ class ActivityProcessor(object):
|
||||||
else:
|
else:
|
||||||
stopped = int(time.time())
|
stopped = int(time.time())
|
||||||
|
|
||||||
if plexpy.CONFIG.VIDEO_LOGGING_ENABLE and str(session['rating_key']).isdigit() and \
|
if plexpy.CONFIG.MOVIE_LOGGING_ENABLE and str(session['rating_key']).isdigit() and \
|
||||||
(session['media_type'] == 'movie' or session['media_type'] == 'episode'):
|
session['media_type'] == 'movie':
|
||||||
|
logging_enabled = True
|
||||||
|
elif plexpy.CONFIG.TV_LOGGING_ENABLE and str(session['rating_key']).isdigit() and \
|
||||||
|
session['media_type'] == 'episode':
|
||||||
logging_enabled = True
|
logging_enabled = True
|
||||||
elif plexpy.CONFIG.MUSIC_LOGGING_ENABLE and str(session['rating_key']).isdigit() and \
|
elif plexpy.CONFIG.MUSIC_LOGGING_ENABLE and str(session['rating_key']).isdigit() and \
|
||||||
session['media_type'] == 'track':
|
session['media_type'] == 'track':
|
||||||
|
@ -278,6 +282,13 @@ class ActivityProcessor(object):
|
||||||
if ipv4:
|
if ipv4:
|
||||||
# The logged IP will always be the first match and we don't want localhost entries
|
# The logged IP will always be the first match and we don't want localhost entries
|
||||||
if ipv4[0] != '127.0.0.1':
|
if ipv4[0] != '127.0.0.1':
|
||||||
|
# check if IPv4 mapped IPv6 address (::ffff:xxx.xxx.xxx.xxx)
|
||||||
|
if '::ffff:' + ipv4[0] in line:
|
||||||
|
logger.debug(u"PlexPy ActivityProcessor :: Matched IP address (%s) for stream ratingKey %s "
|
||||||
|
u"and machineIdentifier %s."
|
||||||
|
% ('::ffff:' + ipv4[0], rating_key, machine_id))
|
||||||
|
return '::ffff:' + ipv4[0]
|
||||||
|
else:
|
||||||
logger.debug(u"PlexPy ActivityProcessor :: Matched IP address (%s) for stream ratingKey %s "
|
logger.debug(u"PlexPy ActivityProcessor :: Matched IP address (%s) for stream ratingKey %s "
|
||||||
u"and machineIdentifier %s."
|
u"and machineIdentifier %s."
|
||||||
% (ipv4[0], rating_key, machine_id))
|
% (ipv4[0], rating_key, machine_id))
|
||||||
|
@ -301,6 +312,11 @@ class ActivityProcessor(object):
|
||||||
if ipv4:
|
if ipv4:
|
||||||
# The logged IP will always be the first match and we don't want localhost entries
|
# The logged IP will always be the first match and we don't want localhost entries
|
||||||
if ipv4[0] != '127.0.0.1':
|
if ipv4[0] != '127.0.0.1':
|
||||||
|
if '::ffff:' + ipv4[0] in line:
|
||||||
|
logger.debug(u"PlexPy ActivityProcessor :: Matched IP address (%s) for stream ratingKey %s." %
|
||||||
|
('::ffff:' + ipv4[0], rating_key))
|
||||||
|
return '::ffff:' + ipv4[0]
|
||||||
|
else:
|
||||||
logger.debug(u"PlexPy ActivityProcessor :: Matched IP address (%s) for stream ratingKey %s." %
|
logger.debug(u"PlexPy ActivityProcessor :: Matched IP address (%s) for stream ratingKey %s." %
|
||||||
(ipv4[0], rating_key))
|
(ipv4[0], rating_key))
|
||||||
return ipv4[0]
|
return ipv4[0]
|
||||||
|
|
|
@ -394,8 +394,8 @@ class Api(object):
|
||||||
"grouping_global_history": bool(plexpy.CONFIG.GROUPING_GLOBAL_HISTORY),
|
"grouping_global_history": bool(plexpy.CONFIG.GROUPING_GLOBAL_HISTORY),
|
||||||
"grouping_user_history": bool(plexpy.CONFIG.GROUPING_USER_HISTORY),
|
"grouping_user_history": bool(plexpy.CONFIG.GROUPING_USER_HISTORY),
|
||||||
"grouping_charts": bool(plexpy.CONFIG.GROUPING_CHARTS),
|
"grouping_charts": bool(plexpy.CONFIG.GROUPING_CHARTS),
|
||||||
"tv_notify_enable": bool(plexpy.CONFIG.TV_NOTIFY_ENABLE),
|
|
||||||
"movie_notify_enable": bool(plexpy.CONFIG.MOVIE_NOTIFY_ENABLE),
|
"movie_notify_enable": bool(plexpy.CONFIG.MOVIE_NOTIFY_ENABLE),
|
||||||
|
"tv_notify_enable": bool(plexpy.CONFIG.TV_NOTIFY_ENABLE),
|
||||||
"music_notify_enable": bool(plexpy.CONFIG.MUSIC_NOTIFY_ENABLE),
|
"music_notify_enable": bool(plexpy.CONFIG.MUSIC_NOTIFY_ENABLE),
|
||||||
"tv_notify_on_start": bool(plexpy.CONFIG.TV_NOTIFY_ON_START),
|
"tv_notify_on_start": bool(plexpy.CONFIG.TV_NOTIFY_ON_START),
|
||||||
"movie_notify_on_start": bool(plexpy.CONFIG.MOVIE_NOTIFY_ON_START),
|
"movie_notify_on_start": bool(plexpy.CONFIG.MOVIE_NOTIFY_ON_START),
|
||||||
|
@ -410,7 +410,8 @@ class Api(object):
|
||||||
"refresh_users_interval": plexpy.CONFIG.REFRESH_USERS_INTERVAL,
|
"refresh_users_interval": plexpy.CONFIG.REFRESH_USERS_INTERVAL,
|
||||||
"refresh_users_on_startup": bool(plexpy.CONFIG.REFRESH_USERS_ON_STARTUP),
|
"refresh_users_on_startup": bool(plexpy.CONFIG.REFRESH_USERS_ON_STARTUP),
|
||||||
"ip_logging_enable": bool(plexpy.CONFIG.IP_LOGGING_ENABLE),
|
"ip_logging_enable": bool(plexpy.CONFIG.IP_LOGGING_ENABLE),
|
||||||
"video_logging_enable": bool(plexpy.CONFIG.VIDEO_LOGGING_ENABLE),
|
"movie_logging_enable": bool(plexpy.CONFIG.MOVIE_LOGGING_ENABLE),
|
||||||
|
"tv_logging_enable": bool(plexpy.CONFIG.TV_LOGGING_ENABLE),
|
||||||
"music_logging_enable": bool(plexpy.CONFIG.MUSIC_LOGGING_ENABLE),
|
"music_logging_enable": bool(plexpy.CONFIG.MUSIC_LOGGING_ENABLE),
|
||||||
"logging_ignore_interval": plexpy.CONFIG.LOGGING_IGNORE_INTERVAL,
|
"logging_ignore_interval": plexpy.CONFIG.LOGGING_IGNORE_INTERVAL,
|
||||||
"pms_is_remote": bool(plexpy.CONFIG.PMS_IS_REMOTE),
|
"pms_is_remote": bool(plexpy.CONFIG.PMS_IS_REMOTE),
|
||||||
|
|
|
@ -42,3 +42,8 @@ notify_strings[NOTIFY_STOPPED] = "Playback stopped"
|
||||||
DEFAULT_USER_THUMB = "interfaces/default/images/gravatar-default-80x80.png"
|
DEFAULT_USER_THUMB = "interfaces/default/images/gravatar-default-80x80.png"
|
||||||
DEFAULT_POSTER_THUMB = "interfaces/default/images/poster.png"
|
DEFAULT_POSTER_THUMB = "interfaces/default/images/poster.png"
|
||||||
DEFAULT_COVER_THUMB = "interfaces/default/images/cover.png"
|
DEFAULT_COVER_THUMB = "interfaces/default/images/cover.png"
|
||||||
|
|
||||||
|
PLATFORM_NAME_OVERRIDES = {'Konvergo': 'Plex Media Player',
|
||||||
|
'Mystery 3': 'Playstation 3',
|
||||||
|
'Mystery 4': 'Playstation 4',
|
||||||
|
'Mystery 5': 'Xbox 360'}
|
||||||
|
|
|
@ -26,6 +26,7 @@ _CONFIG_DEFINITIONS = {
|
||||||
'PMS_IP': (str, 'PMS', '127.0.0.1'),
|
'PMS_IP': (str, 'PMS', '127.0.0.1'),
|
||||||
'PMS_IS_REMOTE': (int, 'PMS', 0),
|
'PMS_IS_REMOTE': (int, 'PMS', 0),
|
||||||
'PMS_LOGS_FOLDER': (str, 'PMS', ''),
|
'PMS_LOGS_FOLDER': (str, 'PMS', ''),
|
||||||
|
'PMS_NAME': (unicode, 'PMS', ''),
|
||||||
'PMS_PORT': (int, 'PMS', 32400),
|
'PMS_PORT': (int, 'PMS', 32400),
|
||||||
'PMS_TOKEN': (str, 'PMS', ''),
|
'PMS_TOKEN': (str, 'PMS', ''),
|
||||||
'PMS_SSL': (int, 'General', 0),
|
'PMS_SSL': (int, 'General', 0),
|
||||||
|
@ -44,6 +45,9 @@ _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),
|
||||||
|
'BOXCAR_ON_EXTDOWN': (int, 'Boxcar', 0),
|
||||||
|
'BOXCAR_ON_INTDOWN': (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 +72,9 @@ _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),
|
||||||
|
'EMAIL_ON_EXTDOWN': (int, 'Email', 0),
|
||||||
|
'EMAIL_ON_INTDOWN': (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 +91,9 @@ _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),
|
||||||
|
'GROWL_ON_EXTDOWN': (int, 'Growl', 0),
|
||||||
|
'GROWL_ON_INTDOWN': (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),
|
||||||
|
@ -99,19 +109,33 @@ _CONFIG_DEFINITIONS = {
|
||||||
'HTTP_USERNAME': (str, 'General', ''),
|
'HTTP_USERNAME': (str, 'General', ''),
|
||||||
'INTERFACE': (str, 'General', 'default'),
|
'INTERFACE': (str, 'General', 'default'),
|
||||||
'IP_LOGGING_ENABLE': (int, 'General', 0),
|
'IP_LOGGING_ENABLE': (int, 'General', 0),
|
||||||
|
'IFTTT_KEY': (str, 'IFTTT', ''),
|
||||||
|
'IFTTT_EVENT': (str, 'IFTTT', 'plexpy'),
|
||||||
|
'IFTTT_ENABLED': (int, 'IFTTT', 0),
|
||||||
|
'IFTTT_ON_PLAY': (int, 'IFTTT', 0),
|
||||||
|
'IFTTT_ON_STOP': (int, 'IFTTT', 0),
|
||||||
|
'IFTTT_ON_PAUSE': (int, 'IFTTT', 0),
|
||||||
|
'IFTTT_ON_RESUME': (int, 'IFTTT', 0),
|
||||||
|
'IFTTT_ON_BUFFER': (int, 'IFTTT', 0),
|
||||||
|
'IFTTT_ON_WATCHED': (int, 'IFTTT', 0),
|
||||||
|
'IFTTT_ON_CREATED': (int, 'IFTTT', 0),
|
||||||
|
'IFTTT_ON_EXTDOWN': (int, 'IFTTT', 0),
|
||||||
|
'IFTTT_ON_INTDOWN': (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', ''),
|
||||||
'LOGGING_IGNORE_INTERVAL': (int, 'Monitoring', 120),
|
'LOGGING_IGNORE_INTERVAL': (int, 'Monitoring', 120),
|
||||||
|
'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),
|
||||||
'MOVIE_NOTIFY_ON_STOP': (int, 'Monitoring', 0),
|
'MOVIE_NOTIFY_ON_STOP': (int, 'Monitoring', 0),
|
||||||
'MOVIE_NOTIFY_ON_PAUSE': (int, 'Monitoring', 0),
|
'MOVIE_NOTIFY_ON_PAUSE': (int, 'Monitoring', 0),
|
||||||
|
'MUSIC_LOGGING_ENABLE': (int, 'Monitoring', 1),
|
||||||
'MUSIC_NOTIFY_ENABLE': (int, 'Monitoring', 0),
|
'MUSIC_NOTIFY_ENABLE': (int, 'Monitoring', 0),
|
||||||
'MUSIC_NOTIFY_ON_START': (int, 'Monitoring', 1),
|
'MUSIC_NOTIFY_ON_START': (int, 'Monitoring', 1),
|
||||||
'MUSIC_NOTIFY_ON_STOP': (int, 'Monitoring', 0),
|
'MUSIC_NOTIFY_ON_STOP': (int, 'Monitoring', 0),
|
||||||
'MUSIC_NOTIFY_ON_PAUSE': (int, 'Monitoring', 0),
|
'MUSIC_NOTIFY_ON_PAUSE': (int, 'Monitoring', 0),
|
||||||
'MUSIC_LOGGING_ENABLE': (int, 'Monitoring', 0),
|
'MONITOR_REMOTE_ACCESS': (int, 'Monitoring', 0),
|
||||||
'MONITORING_INTERVAL': (int, 'Monitoring', 60),
|
'MONITORING_INTERVAL': (int, 'Monitoring', 60),
|
||||||
'MONITORING_USE_WEBSOCKET': (int, 'Monitoring', 0),
|
'MONITORING_USE_WEBSOCKET': (int, 'Monitoring', 0),
|
||||||
'NMA_APIKEY': (str, 'NMA', ''),
|
'NMA_APIKEY': (str, 'NMA', ''),
|
||||||
|
@ -123,7 +147,12 @@ _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),
|
||||||
|
'NMA_ON_EXTDOWN': (int, 'NMA', 0),
|
||||||
|
'NMA_ON_INTDOWN': (int, 'NMA', 0),
|
||||||
'NOTIFY_CONSECUTIVE': (int, 'Monitoring', 1),
|
'NOTIFY_CONSECUTIVE': (int, 'Monitoring', 1),
|
||||||
|
'NOTIFY_RECENTLY_ADDED_GRANDPARENT': (int, 'Monitoring', 0),
|
||||||
|
'NOTIFY_RECENTLY_ADDED_DELAY': (int, 'Monitoring', 60),
|
||||||
'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}.'),
|
||||||
|
@ -137,6 +166,12 @@ _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.'),
|
||||||
|
'NOTIFY_ON_EXTDOWN_SUBJECT_TEXT': (str, 'Monitoring', 'PlexPy ({server_name})'),
|
||||||
|
'NOTIFY_ON_EXTDOWN_BODY_TEXT': (str, 'Monitoring', 'The Plex Media Server remote access is down.'),
|
||||||
|
'NOTIFY_ON_INTDOWN_SUBJECT_TEXT': (str, 'Monitoring', 'PlexPy ({server_name})'),
|
||||||
|
'NOTIFY_ON_INTDOWN_BODY_TEXT': (str, 'Monitoring', 'The Plex Media Server is down.'),
|
||||||
'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),
|
||||||
|
@ -145,6 +180,9 @@ _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),
|
||||||
|
'OSX_NOTIFY_ON_EXTDOWN': (int, 'OSX_Notify', 0),
|
||||||
|
'OSX_NOTIFY_ON_INTDOWN': (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', ''),
|
||||||
|
@ -155,6 +193,9 @@ _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),
|
||||||
|
'PLEX_ON_EXTDOWN': (int, 'Plex', 0),
|
||||||
|
'PLEX_ON_INTDOWN': (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),
|
||||||
|
@ -164,6 +205,9 @@ _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),
|
||||||
|
'PROWL_ON_EXTDOWN': (int, 'Prowl', 0),
|
||||||
|
'PROWL_ON_INTDOWN': (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),
|
||||||
|
@ -172,6 +216,9 @@ _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),
|
||||||
|
'PUSHALOT_ON_EXTDOWN': (int, 'Pushalot', 0),
|
||||||
|
'PUSHALOT_ON_INTDOWN': (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', ''),
|
||||||
|
@ -182,6 +229,9 @@ _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),
|
||||||
|
'PUSHBULLET_ON_EXTDOWN': (int, 'PushBullet', 0),
|
||||||
|
'PUSHBULLET_ON_INTDOWN': (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', ''),
|
||||||
|
@ -193,8 +243,24 @@ _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),
|
||||||
|
'PUSHOVER_ON_EXTDOWN': (int, 'Pushover', 0),
|
||||||
|
'PUSHOVER_ON_INTDOWN': (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),
|
||||||
|
'TELEGRAM_BOT_TOKEN': (str, 'Telegram', ''),
|
||||||
|
'TELEGRAM_ENABLED': (int, 'Telegram', 0),
|
||||||
|
'TELEGRAM_CHAT_ID': (int, 'Telegram', 0),
|
||||||
|
'TELEGRAM_ON_PLAY': (int, 'Telegram', 0),
|
||||||
|
'TELEGRAM_ON_STOP': (int, 'Telegram', 0),
|
||||||
|
'TELEGRAM_ON_PAUSE': (int, 'Telegram', 0),
|
||||||
|
'TELEGRAM_ON_RESUME': (int, 'Telegram', 0),
|
||||||
|
'TELEGRAM_ON_BUFFER': (int, 'Telegram', 0),
|
||||||
|
'TELEGRAM_ON_WATCHED': (int, 'Telegram', 0),
|
||||||
|
'TELEGRAM_ON_CREATED': (int, 'Telegram', 0),
|
||||||
|
'TELEGRAM_ON_EXTDOWN': (int, 'Telegram', 0),
|
||||||
|
'TELEGRAM_ON_INTDOWN': (int, 'Telegram', 0),
|
||||||
|
'TV_LOGGING_ENABLE': (int, 'Monitoring', 1),
|
||||||
'TV_NOTIFY_ENABLE': (int, 'Monitoring', 0),
|
'TV_NOTIFY_ENABLE': (int, 'Monitoring', 0),
|
||||||
'TV_NOTIFY_ON_START': (int, 'Monitoring', 1),
|
'TV_NOTIFY_ON_START': (int, 'Monitoring', 1),
|
||||||
'TV_NOTIFY_ON_STOP': (int, 'Monitoring', 0),
|
'TV_NOTIFY_ON_STOP': (int, 'Monitoring', 0),
|
||||||
|
@ -209,6 +275,9 @@ _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),
|
||||||
|
'TWITTER_ON_EXTDOWN': (int, 'Twitter', 0),
|
||||||
|
'TWITTER_ON_INTDOWN': (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),
|
||||||
|
@ -221,7 +290,10 @@ _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),
|
||||||
|
'XBMC_ON_EXTDOWN': (int, 'XBMC', 0),
|
||||||
|
'XBMC_ON_INTDOWN': (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
|
||||||
|
@ -234,6 +306,7 @@ class Config(object):
|
||||||
self._config = ConfigObj(self._config_file, encoding='utf-8')
|
self._config = ConfigObj(self._config_file, encoding='utf-8')
|
||||||
for key in _CONFIG_DEFINITIONS.keys():
|
for key in _CONFIG_DEFINITIONS.keys():
|
||||||
self.check_setting(key)
|
self.check_setting(key)
|
||||||
|
self._upgrade()
|
||||||
|
|
||||||
def _define(self, name):
|
def _define(self, name):
|
||||||
key = name.upper()
|
key = name.upper()
|
||||||
|
@ -323,3 +396,17 @@ class Config(object):
|
||||||
for name, value in kwargs.items():
|
for name, value in kwargs.items():
|
||||||
key, definition_type, section, ini_key, default = self._define(name)
|
key, definition_type, section, ini_key, default = self._define(name)
|
||||||
self._config[section][ini_key] = definition_type(value)
|
self._config[section][ini_key] = definition_type(value)
|
||||||
|
|
||||||
|
def _upgrade(self):
|
||||||
|
"""
|
||||||
|
Upgrades config file from previous verisions and bumps up config version
|
||||||
|
"""
|
||||||
|
if self.CONFIG_VERSION == '0':
|
||||||
|
# Separate out movie and tv notifications
|
||||||
|
if self.MOVIE_NOTIFY_ENABLE == 1:
|
||||||
|
self.TV_NOTIFY_ENABLE = 1
|
||||||
|
# Separate out movie and tv logging
|
||||||
|
if self.VIDEO_LOGGING_ENABLE == 0:
|
||||||
|
self.MOVIE_LOGGING_ENABLE = 0
|
||||||
|
self.TV_LOGGING_ENABLE = 0
|
||||||
|
self.CONFIG_VERSION = '1'
|
|
@ -41,7 +41,7 @@ class DataFactory(object):
|
||||||
'SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) AS paused_counter',
|
'SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) AS paused_counter',
|
||||||
'session_history.user_id',
|
'session_history.user_id',
|
||||||
'session_history.user',
|
'session_history.user',
|
||||||
'(CASE WHEN users.friendly_name IS NULL THEN user ELSE users.friendly_name END) as friendly_name',
|
'(CASE WHEN users.friendly_name IS NULL THEN users.username ELSE users.friendly_name END) as friendly_name',
|
||||||
'platform',
|
'platform',
|
||||||
'player',
|
'player',
|
||||||
'ip_address',
|
'ip_address',
|
||||||
|
@ -106,10 +106,7 @@ class DataFactory(object):
|
||||||
watched_status = 0
|
watched_status = 0
|
||||||
|
|
||||||
# Rename Mystery platform names
|
# Rename Mystery platform names
|
||||||
platform_names = {'Mystery 3': 'Playstation 3',
|
platform = common.PLATFORM_NAME_OVERRIDES.get(item["platform"], item["platform"])
|
||||||
'Mystery 4': 'Playstation 4',
|
|
||||||
'Mystery 5': 'Xbox 360'}
|
|
||||||
platform = platform_names.get(item["platform"], item["platform"])
|
|
||||||
|
|
||||||
row = {"reference_id": item["reference_id"],
|
row = {"reference_id": item["reference_id"],
|
||||||
"id": item["id"],
|
"id": item["id"],
|
||||||
|
@ -182,7 +179,7 @@ class DataFactory(object):
|
||||||
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stats_count)
|
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stats_count)
|
||||||
result = monitor_db.select(query)
|
result = monitor_db.select(query)
|
||||||
except:
|
except:
|
||||||
logger.warn("Unable to execute database query.")
|
logger.warn("Unable to execute database query for get_home_stats: top_tv.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
for item in result:
|
for item in result:
|
||||||
|
@ -230,7 +227,7 @@ class DataFactory(object):
|
||||||
'LIMIT %s' % (time_range, sort_type, stats_count)
|
'LIMIT %s' % (time_range, sort_type, stats_count)
|
||||||
result = monitor_db.select(query)
|
result = monitor_db.select(query)
|
||||||
except:
|
except:
|
||||||
logger.warn("Unable to execute database query.")
|
logger.warn("Unable to execute database query for get_home_stats: popular_tv.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
for item in result:
|
for item in result:
|
||||||
|
@ -274,7 +271,7 @@ class DataFactory(object):
|
||||||
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stats_count)
|
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stats_count)
|
||||||
result = monitor_db.select(query)
|
result = monitor_db.select(query)
|
||||||
except:
|
except:
|
||||||
logger.warn("Unable to execute database query.")
|
logger.warn("Unable to execute database query for get_home_stats: top_movies.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
for item in result:
|
for item in result:
|
||||||
|
@ -322,7 +319,7 @@ class DataFactory(object):
|
||||||
'LIMIT %s' % (time_range, sort_type, stats_count)
|
'LIMIT %s' % (time_range, sort_type, stats_count)
|
||||||
result = monitor_db.select(query)
|
result = monitor_db.select(query)
|
||||||
except:
|
except:
|
||||||
logger.warn("Unable to execute database query.")
|
logger.warn("Unable to execute database query for get_home_stats: popular_movies.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
for item in result:
|
for item in result:
|
||||||
|
@ -366,7 +363,7 @@ class DataFactory(object):
|
||||||
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stats_count)
|
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stats_count)
|
||||||
result = monitor_db.select(query)
|
result = monitor_db.select(query)
|
||||||
except:
|
except:
|
||||||
logger.warn("Unable to execute database query.")
|
logger.warn("Unable to execute database query for get_home_stats: top_music.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
for item in result:
|
for item in result:
|
||||||
|
@ -414,7 +411,7 @@ class DataFactory(object):
|
||||||
'LIMIT %s' % (time_range, sort_type, stats_count)
|
'LIMIT %s' % (time_range, sort_type, stats_count)
|
||||||
result = monitor_db.select(query)
|
result = monitor_db.select(query)
|
||||||
except:
|
except:
|
||||||
logger.warn("Unable to execute database query.")
|
logger.warn("Unable to execute database query for get_home_stats: popular_music.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
for item in result:
|
for item in result:
|
||||||
|
@ -440,7 +437,7 @@ class DataFactory(object):
|
||||||
top_users = []
|
top_users = []
|
||||||
try:
|
try:
|
||||||
query = 'SELECT session_history.user, ' \
|
query = 'SELECT session_history.user, ' \
|
||||||
'(case when users.friendly_name is null then session_history.user else ' \
|
'(case when users.friendly_name is null then users.username else ' \
|
||||||
'users.friendly_name end) as friendly_name,' \
|
'users.friendly_name end) as friendly_name,' \
|
||||||
'COUNT(session_history.id) as total_plays, ' \
|
'COUNT(session_history.id) as total_plays, ' \
|
||||||
'SUM(case when session_history.stopped > 0 ' \
|
'SUM(case when session_history.stopped > 0 ' \
|
||||||
|
@ -459,7 +456,7 @@ class DataFactory(object):
|
||||||
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stats_count)
|
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stats_count)
|
||||||
result = monitor_db.select(query)
|
result = monitor_db.select(query)
|
||||||
except:
|
except:
|
||||||
logger.warn("Unable to execute database query.")
|
logger.warn("Unable to execute database query for get_home_stats: top_users.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
for item in result:
|
for item in result:
|
||||||
|
@ -507,15 +504,12 @@ class DataFactory(object):
|
||||||
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stats_count)
|
'ORDER BY %s DESC LIMIT %s' % (time_range, sort_type, stats_count)
|
||||||
result = monitor_db.select(query)
|
result = monitor_db.select(query)
|
||||||
except:
|
except:
|
||||||
logger.warn("Unable to execute database query.")
|
logger.warn("Unable to execute database query for get_home_stats: top_platforms.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
for item in result:
|
for item in result:
|
||||||
# Rename Mystery platform names
|
# Rename Mystery platform names
|
||||||
platform_names = {'Mystery 3': 'Playstation 3',
|
platform_type = common.PLATFORM_NAME_OVERRIDES.get(item[0], item[0])
|
||||||
'Mystery 4': 'Playstation 4',
|
|
||||||
'Mystery 5': 'Xbox 360'}
|
|
||||||
platform_type = platform_names.get(item[0], item[0])
|
|
||||||
|
|
||||||
row = {'platform': item[0],
|
row = {'platform': item[0],
|
||||||
'total_plays': item[1],
|
'total_plays': item[1],
|
||||||
|
@ -542,7 +536,7 @@ class DataFactory(object):
|
||||||
try:
|
try:
|
||||||
query = 'SELECT session_history_metadata.id, ' \
|
query = 'SELECT session_history_metadata.id, ' \
|
||||||
'session_history.user, ' \
|
'session_history.user, ' \
|
||||||
'(case when users.friendly_name is null then session_history.user else ' \
|
'(case when users.friendly_name is null then users.username else ' \
|
||||||
'users.friendly_name end) as friendly_name,' \
|
'users.friendly_name end) as friendly_name,' \
|
||||||
'users.user_id, ' \
|
'users.user_id, ' \
|
||||||
'users.custom_avatar_url as user_thumb, ' \
|
'users.custom_avatar_url as user_thumb, ' \
|
||||||
|
@ -564,12 +558,12 @@ class DataFactory(object):
|
||||||
'AND (session_history_metadata.media_type = "movie" ' \
|
'AND (session_history_metadata.media_type = "movie" ' \
|
||||||
'OR session_history_metadata.media_type = "episode") ' \
|
'OR session_history_metadata.media_type = "episode") ' \
|
||||||
'AND percent_complete >= %s ' \
|
'AND percent_complete >= %s ' \
|
||||||
'GROUP BY session_history_metadata.full_title ' \
|
'GROUP BY session_history.id ' \
|
||||||
'ORDER BY last_watch DESC ' \
|
'ORDER BY last_watch DESC ' \
|
||||||
'LIMIT %s' % (time_range, notify_watched_percent, stats_count)
|
'LIMIT %s' % (time_range, notify_watched_percent, stats_count)
|
||||||
result = monitor_db.select(query)
|
result = monitor_db.select(query)
|
||||||
except:
|
except:
|
||||||
logger.warn("Unable to execute database query.")
|
logger.warn("Unable to execute database query for get_home_stats: last_watched.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
for item in result:
|
for item in result:
|
||||||
|
@ -680,7 +674,7 @@ class DataFactory(object):
|
||||||
'ORDER BY started DESC LIMIT ?'
|
'ORDER BY started DESC LIMIT ?'
|
||||||
result = monitor_db.select(query, args=[limit])
|
result = monitor_db.select(query, args=[limit])
|
||||||
except:
|
except:
|
||||||
logger.warn("Unable to execute database query.")
|
logger.warn("Unable to execute database query for get_recently_watched.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
for row in result:
|
for row in result:
|
||||||
|
@ -729,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'],
|
||||||
|
@ -854,7 +848,7 @@ class DataFactory(object):
|
||||||
media_type = 'artist'
|
media_type = 'artist'
|
||||||
|
|
||||||
if query_string and media_type:
|
if query_string and media_type:
|
||||||
query = {'query_string': query_string.replace('"', ''),
|
query = {'query_string': query_string,
|
||||||
'title': title,
|
'title': title,
|
||||||
'parent_title': parent_title,
|
'parent_title': parent_title,
|
||||||
'grandparent_title': grandparent_title,
|
'grandparent_title': grandparent_title,
|
||||||
|
@ -894,7 +888,7 @@ class DataFactory(object):
|
||||||
grandparent_rating_key = result[0]['grandparent_rating_key']
|
grandparent_rating_key = result[0]['grandparent_rating_key']
|
||||||
|
|
||||||
except:
|
except:
|
||||||
logger.warn("Unable to execute database query.")
|
logger.warn("Unable to execute database query for get_rating_keys_list.")
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
query = 'SELECT rating_key, parent_rating_key, grandparent_rating_key, title, parent_title, grandparent_title, ' \
|
query = 'SELECT rating_key, parent_rating_key, grandparent_rating_key, title, parent_title, grandparent_title, ' \
|
||||||
|
@ -1005,3 +999,19 @@ class DataFactory(object):
|
||||||
return 'No updated rating key needed in database. No changes were made.'
|
return 'No updated rating key needed in database. No changes were made.'
|
||||||
# for debugging
|
# for debugging
|
||||||
#return mapping
|
#return mapping
|
||||||
|
|
||||||
|
def get_session_ip(self, session_key=''):
|
||||||
|
monitor_db = database.MonitorDatabase()
|
||||||
|
|
||||||
|
if session_key:
|
||||||
|
query = 'SELECT ip_address FROM sessions WHERE session_key = %d' % int(session_key)
|
||||||
|
result = monitor_db.select(query)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
ip_address = 'N/A'
|
||||||
|
|
||||||
|
for item in result:
|
||||||
|
ip_address = item[0]
|
||||||
|
|
||||||
|
return ip_address
|
||||||
|
|
|
@ -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
|
||||||
|
@ -70,6 +70,39 @@ class DataTables(object):
|
||||||
else:
|
else:
|
||||||
grouping = False
|
grouping = False
|
||||||
|
|
||||||
|
# Build join parameters
|
||||||
|
if join_types:
|
||||||
|
counter = 0
|
||||||
|
for join_type in join_types:
|
||||||
|
if join_type.upper() == 'LEFT OUTER JOIN':
|
||||||
|
join_item = 'LEFT OUTER JOIN %s ON %s = %s ' % \
|
||||||
|
(join_tables[counter], join_evals[counter][0], join_evals[counter][1])
|
||||||
|
elif join_type.upper() == 'JOIN' or join_type.upper() == 'INNER JOIN':
|
||||||
|
join_item = 'JOIN %s ON %s = %s ' % \
|
||||||
|
(join_tables[counter], join_evals[counter][0], join_evals[counter][1])
|
||||||
|
else:
|
||||||
|
join_item = ''
|
||||||
|
|
||||||
|
counter += 1
|
||||||
|
join += join_item
|
||||||
|
|
||||||
|
# Build custom where parameters
|
||||||
|
if custom_where:
|
||||||
|
for w in custom_where:
|
||||||
|
c_where += w[0] + ' = ? AND '
|
||||||
|
|
||||||
|
# The order of our args changes if we are grouping
|
||||||
|
#if grouping:
|
||||||
|
# args.insert(0, w[1])
|
||||||
|
#else:
|
||||||
|
# args.append(w[1])
|
||||||
|
|
||||||
|
# My testing shows that order of args doesn't change
|
||||||
|
args.append(w[1])
|
||||||
|
|
||||||
|
if c_where:
|
||||||
|
c_where = 'WHERE ' + c_where.rstrip(' AND ')
|
||||||
|
|
||||||
# Build ordering
|
# Build ordering
|
||||||
for o in parameters['order']:
|
for o in parameters['order']:
|
||||||
sort_order = ' COLLATE NOCASE'
|
sort_order = ' COLLATE NOCASE'
|
||||||
|
@ -119,36 +152,6 @@ class DataTables(object):
|
||||||
if where:
|
if where:
|
||||||
where = 'WHERE ' + where.rstrip(' OR ')
|
where = 'WHERE ' + where.rstrip(' OR ')
|
||||||
|
|
||||||
# Build join parameters
|
|
||||||
if join_types:
|
|
||||||
counter = 0
|
|
||||||
for join_type in join_types:
|
|
||||||
if join_type.upper() == 'LEFT OUTER JOIN':
|
|
||||||
join_item = 'LEFT OUTER JOIN %s ON %s = %s ' % \
|
|
||||||
(join_tables[counter], join_evals[counter][0], join_evals[counter][1])
|
|
||||||
elif join_type.upper() == 'JOIN' or join_type.upper() == 'INNER JOIN':
|
|
||||||
join_item = 'JOIN %s ON %s = %s ' % \
|
|
||||||
(join_tables[counter], join_evals[counter][0], join_evals[counter][1])
|
|
||||||
else:
|
|
||||||
join_item = ''
|
|
||||||
|
|
||||||
counter += 1
|
|
||||||
join += join_item
|
|
||||||
|
|
||||||
# Build custom where parameters
|
|
||||||
if custom_where:
|
|
||||||
for w in custom_where:
|
|
||||||
c_where += w[0] + ' = ? AND '
|
|
||||||
|
|
||||||
# The order of our args changes if we are grouping
|
|
||||||
if grouping:
|
|
||||||
args.insert(0, w[1])
|
|
||||||
else:
|
|
||||||
args.append(w[1])
|
|
||||||
|
|
||||||
if c_where:
|
|
||||||
c_where = 'WHERE ' + c_where.rstrip(' AND ')
|
|
||||||
|
|
||||||
# Build our queries
|
# Build our queries
|
||||||
if grouping:
|
if grouping:
|
||||||
if c_where == '':
|
if c_where == '':
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from plexpy import logger, database, helpers
|
from plexpy import logger, database, helpers, common
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
@ -386,18 +386,11 @@ class Graphs(object):
|
||||||
series_3 = []
|
series_3 = []
|
||||||
|
|
||||||
for item in result:
|
for item in result:
|
||||||
categories.append(item[0])
|
categories.append(common.PLATFORM_NAME_OVERRIDES.get(item[0], item[0]))
|
||||||
series_1.append(item[1])
|
series_1.append(item[1])
|
||||||
series_2.append(item[2])
|
series_2.append(item[2])
|
||||||
series_3.append(item[3])
|
series_3.append(item[3])
|
||||||
|
|
||||||
# Rename Mystery platform names
|
|
||||||
platform_names = [('Mystery 3', 'Playstation 3'),
|
|
||||||
('Mystery 4', 'Playstation 4'),
|
|
||||||
('Mystery 5', 'Xbox 360')]
|
|
||||||
for old_name, new_name in platform_names:
|
|
||||||
categories = [item.replace(old_name, new_name) for item in categories]
|
|
||||||
|
|
||||||
series_1_output = {'name': 'TV',
|
series_1_output = {'name': 'TV',
|
||||||
'data': series_1}
|
'data': series_1}
|
||||||
series_2_output = {'name': 'Movies',
|
series_2_output = {'name': 'Movies',
|
||||||
|
@ -417,7 +410,7 @@ class Graphs(object):
|
||||||
|
|
||||||
if y_axis == 'plays':
|
if y_axis == 'plays':
|
||||||
query = 'SELECT ' \
|
query = 'SELECT ' \
|
||||||
'(case when users.friendly_name is null then session_history.user else ' \
|
'(case when users.friendly_name is null then users.username else ' \
|
||||||
'users.friendly_name end) as friendly_name,' \
|
'users.friendly_name end) as friendly_name,' \
|
||||||
'SUM(case when media_type = "episode" then 1 else 0 end) as tv_count, ' \
|
'SUM(case when media_type = "episode" then 1 else 0 end) as tv_count, ' \
|
||||||
'SUM(case when media_type = "movie" then 1 else 0 end) as movie_count, ' \
|
'SUM(case when media_type = "movie" then 1 else 0 end) as movie_count, ' \
|
||||||
|
@ -434,7 +427,7 @@ class Graphs(object):
|
||||||
result = monitor_db.select(query)
|
result = monitor_db.select(query)
|
||||||
else:
|
else:
|
||||||
query = 'SELECT ' \
|
query = 'SELECT ' \
|
||||||
'(case when users.friendly_name is null then session_history.user else ' \
|
'(case when users.friendly_name is null then users.username else ' \
|
||||||
'users.friendly_name end) as friendly_name,' \
|
'users.friendly_name end) as friendly_name,' \
|
||||||
'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) ' \
|
'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) ' \
|
||||||
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_duration, ' \
|
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_duration, ' \
|
||||||
|
@ -808,18 +801,11 @@ class Graphs(object):
|
||||||
series_3 = []
|
series_3 = []
|
||||||
|
|
||||||
for item in result:
|
for item in result:
|
||||||
categories.append(item[0])
|
categories.append(common.PLATFORM_NAME_OVERRIDES.get(item[0], item[0]))
|
||||||
series_1.append(item[1])
|
series_1.append(item[1])
|
||||||
series_2.append(item[2])
|
series_2.append(item[2])
|
||||||
series_3.append(item[3])
|
series_3.append(item[3])
|
||||||
|
|
||||||
# Rename Mystery platform names
|
|
||||||
platform_names = [('Mystery 3', 'Playstation 3'),
|
|
||||||
('Mystery 4', 'Playstation 4'),
|
|
||||||
('Mystery 5', 'Xbox 360')]
|
|
||||||
for old_name, new_name in platform_names:
|
|
||||||
categories = [item.replace(old_name, new_name) for item in categories]
|
|
||||||
|
|
||||||
series_1_output = {'name': 'Direct Play',
|
series_1_output = {'name': 'Direct Play',
|
||||||
'data': series_1}
|
'data': series_1}
|
||||||
series_2_output = {'name': 'Direct Stream',
|
series_2_output = {'name': 'Direct Stream',
|
||||||
|
|
|
@ -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
|
||||||
|
@ -144,6 +144,31 @@ def now():
|
||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
return now.strftime("%Y-%m-%d %H:%M:%S")
|
return now.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
|
def human_duration(s):
|
||||||
|
|
||||||
|
hd = ''
|
||||||
|
|
||||||
|
if str(s).isdigit():
|
||||||
|
d = int(s / 84600)
|
||||||
|
h = int((s % 84600) / 3600)
|
||||||
|
m = int(((s % 84600) % 3600) / 60)
|
||||||
|
s = int(((s % 84600) % 3600) % 60)
|
||||||
|
|
||||||
|
hd_list = []
|
||||||
|
if d > 0:
|
||||||
|
hd_list.append(str(d) + ' days')
|
||||||
|
if h > 0:
|
||||||
|
hd_list.append(str(h) + ' hrs')
|
||||||
|
if m > 0:
|
||||||
|
hd_list.append(str(m) + ' mins')
|
||||||
|
if s > 0:
|
||||||
|
hd_list.append(str(s) + ' secs')
|
||||||
|
|
||||||
|
hd = ' '.join(hd_list)
|
||||||
|
|
||||||
|
return hd
|
||||||
|
else:
|
||||||
|
return hd
|
||||||
|
|
||||||
def get_age(date):
|
def get_age(date):
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from plexpy import logger, config, notifiers, database, helpers
|
from plexpy import logger, config, notifiers, database, helpers, plextv, pmsconnect
|
||||||
|
|
||||||
import plexpy
|
import plexpy
|
||||||
import time
|
import time
|
||||||
|
@ -30,8 +30,8 @@ def notify(stream_data=None, notify_action=None):
|
||||||
if not user_details['do_notify']:
|
if not user_details['do_notify']:
|
||||||
return
|
return
|
||||||
|
|
||||||
if stream_data['media_type'] == 'movie' or stream_data['media_type'] == 'episode':
|
if (stream_data['media_type'] == 'movie' and plexpy.CONFIG.MOVIE_NOTIFY_ENABLE) \
|
||||||
if plexpy.CONFIG.MOVIE_NOTIFY_ENABLE or plexpy.CONFIG.TV_NOTIFY_ENABLE:
|
or (stream_data['media_type'] == 'episode' and plexpy.CONFIG.TV_NOTIFY_ENABLE):
|
||||||
|
|
||||||
progress_percent = helpers.get_percent(stream_data['view_offset'], stream_data['duration'])
|
progress_percent = helpers.get_percent(stream_data['view_offset'], stream_data['duration'])
|
||||||
|
|
||||||
|
@ -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,10 +108,9 @@ 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' and plexpy.CONFIG.MUSIC_NOTIFY_ENABLE):
|
||||||
if plexpy.CONFIG.MUSIC_NOTIFY_ENABLE:
|
|
||||||
|
|
||||||
for agent in notifiers.available_notification_agents():
|
for agent in notifiers.available_notification_agents():
|
||||||
if agent['on_play'] and notify_action == 'play':
|
if agent['on_play'] and notify_action == 'play':
|
||||||
|
@ -121,7 +120,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 +129,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 +138,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 +147,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 +156,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 +167,42 @@ 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:
|
||||||
|
if (timeline_data['media_type'] == 'movie' and plexpy.CONFIG.MOVIE_NOTIFY_ENABLE) \
|
||||||
|
or ((timeline_data['media_type'] == 'show' or timeline_data['media_type'] == 'episode') \
|
||||||
|
and plexpy.CONFIG.TV_NOTIFY_ENABLE) \
|
||||||
|
or ((timeline_data['media_type'] == 'artist' or timeline_data['media_type'] == 'track') \
|
||||||
|
and plexpy.CONFIG.MUSIC_NOTIFY_ENABLE):
|
||||||
|
|
||||||
|
for agent in notifiers.available_notification_agents():
|
||||||
|
if agent['on_created'] and notify_action == 'created':
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
elif not timeline_data and notify_action:
|
||||||
|
for agent in notifiers.available_notification_agents():
|
||||||
|
if agent['on_extdown'] and notify_action == 'extdown':
|
||||||
|
# Build and send notification
|
||||||
|
notify_strings = build_server_notify_text(state=notify_action)
|
||||||
|
notifiers.send_notification(config_id=agent['id'],
|
||||||
|
subject=notify_strings[0],
|
||||||
|
body=notify_strings[1])
|
||||||
|
if agent['on_intdown'] and notify_action == 'intdown':
|
||||||
|
# Build and send notification
|
||||||
|
notify_strings = build_server_notify_text(state=notify_action)
|
||||||
|
notifiers.send_notification(config_id=agent['id'],
|
||||||
|
subject=notify_strings[0],
|
||||||
|
body=notify_strings[1])
|
||||||
|
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 +225,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 +258,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 +280,54 @@ 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
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
# Get the server name
|
# Get the server name
|
||||||
pms_connect = pmsconnect.PmsConnect()
|
server_name = plexpy.CONFIG.PMS_NAME
|
||||||
server_name = pms_connect.get_server_pref(pref='FriendlyName')
|
|
||||||
|
# Get the server uptime
|
||||||
|
plex_tv = plextv.PlexTV()
|
||||||
|
server_times = plex_tv.get_server_times()
|
||||||
|
|
||||||
|
if server_times:
|
||||||
|
updated_at = server_times[0]['updated_at']
|
||||||
|
server_uptime = helpers.human_duration(int(time.time() - helpers.cast_to_float(updated_at)))
|
||||||
|
else:
|
||||||
|
logger.error(u"PlexPy Notifier :: Unable to retrieve server uptime.")
|
||||||
|
server_uptime = 'N/A'
|
||||||
|
|
||||||
# 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:
|
pms_connect = pmsconnect.PmsConnect()
|
||||||
item_metadata = metadata['metadata']
|
metadata_list = pms_connect.get_metadata_details(rating_key=rating_key)
|
||||||
|
|
||||||
|
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'] == 'movie':
|
||||||
# Regex pattern to remove the text in the tags we don't want
|
|
||||||
pattern = re.compile('<movie>[^>]+.</movie>|<music>[^>]+.</music>', re.IGNORECASE)
|
|
||||||
elif session['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'] == 'show' or metadata['media_type'] == 'episode':
|
||||||
|
# Regex pattern to remove the text in the tags we don't want
|
||||||
|
pattern = re.compile('<movie>[^>]+.</movie>|<music>[^>]+.</music>', re.IGNORECASE)
|
||||||
|
elif metadata['media_type'] == 'artist' or 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'] == 'movie' \
|
||||||
|
or metadata['media_type'] == 'show' or metadata['media_type'] == 'episode' \
|
||||||
|
or metadata['media_type'] == 'artist' 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 +342,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 +357,29 @@ 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 = ''
|
||||||
|
ip_address = 'N/A'
|
||||||
|
|
||||||
|
# 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 +388,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 +401,42 @@ 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']
|
||||||
|
ip_address = session['ip_address'] if session['ip_address'] != '' else 'N/A'
|
||||||
|
|
||||||
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'],
|
'server_uptime': server_uptime,
|
||||||
'platform': session['platform'],
|
'user': user,
|
||||||
'player': session['player'],
|
'platform': platform,
|
||||||
'media_type': session['media_type'],
|
'player': player,
|
||||||
|
'ip_address': ip_address,
|
||||||
|
'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'],
|
'track_name': metadata['title'],
|
||||||
'season_num00': item_metadata['parent_index'].zfill(2),
|
'season_num': metadata['parent_index'],
|
||||||
'episode_num': item_metadata['index'],
|
'season_num00': metadata['parent_index'].zfill(2),
|
||||||
'episode_num00': item_metadata['index'].zfill(2),
|
'episode_num': metadata['index'],
|
||||||
|
'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'],
|
'directors': ', '.join(metadata['directors']),
|
||||||
'rating': item_metadata['rating'],
|
'writers': ', '.join(metadata['writers']),
|
||||||
|
'actors': ', '.join(metadata['actors']),
|
||||||
|
'genres': ', '.join(metadata['genres']),
|
||||||
|
'summary': metadata['summary'],
|
||||||
|
'tagline': metadata['tagline'],
|
||||||
|
'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,12 +588,106 @@ 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]
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def build_server_notify_text(state=None):
|
||||||
|
# Get the server name
|
||||||
|
server_name = plexpy.CONFIG.PMS_NAME
|
||||||
|
|
||||||
|
# Get the server uptime
|
||||||
|
plex_tv = plextv.PlexTV()
|
||||||
|
server_times = plex_tv.get_server_times()
|
||||||
|
|
||||||
|
if server_times:
|
||||||
|
updated_at = server_times[0]['updated_at']
|
||||||
|
server_uptime = helpers.human_duration(int(time.time() - helpers.cast_to_float(updated_at)))
|
||||||
|
else:
|
||||||
|
logger.error(u"PlexPy Notifier :: Unable to retrieve server uptime.")
|
||||||
|
server_uptime = 'N/A'
|
||||||
|
|
||||||
|
on_extdown_subject = plexpy.CONFIG.NOTIFY_ON_EXTDOWN_SUBJECT_TEXT
|
||||||
|
on_extdown_body = plexpy.CONFIG.NOTIFY_ON_EXTDOWN_BODY_TEXT
|
||||||
|
on_intdown_subject = plexpy.CONFIG.NOTIFY_ON_INTDOWN_SUBJECT_TEXT
|
||||||
|
on_intdown_body = plexpy.CONFIG.NOTIFY_ON_INTDOWN_BODY_TEXT
|
||||||
|
|
||||||
|
available_params = {'server_name': server_name,
|
||||||
|
'server_uptime': server_uptime}
|
||||||
|
|
||||||
|
# Default text
|
||||||
|
subject_text = 'PlexPy (%s)' % server_name
|
||||||
|
|
||||||
|
if state == 'extdown':
|
||||||
|
# Default body text
|
||||||
|
body_text = 'The Plex Media Server remote access is down.'
|
||||||
|
|
||||||
|
if on_extdown_subject and on_extdown_body:
|
||||||
|
try:
|
||||||
|
subject_text = unicode(on_extdown_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_extdown_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]
|
||||||
|
else:
|
||||||
|
return [subject_text, body_text]
|
||||||
|
elif state == 'intdown':
|
||||||
|
# Default body text
|
||||||
|
body_text = 'The Plex Media Server is down.'
|
||||||
|
|
||||||
|
if on_intdown_subject and on_intdown_body:
|
||||||
|
try:
|
||||||
|
subject_text = unicode(on_intdown_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_intdown_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]
|
||||||
|
else:
|
||||||
|
return [subject_text, body_text]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
def strip_tag(data):
|
def strip_tag(data):
|
||||||
import re
|
import re
|
||||||
|
|
|
@ -50,7 +50,9 @@ AGENT_IDS = {"Growl": 0,
|
||||||
"OSX Notify": 8,
|
"OSX Notify": 8,
|
||||||
"Boxcar2": 9,
|
"Boxcar2": 9,
|
||||||
"Email": 10,
|
"Email": 10,
|
||||||
"Twitter": 11}
|
"Twitter": 11,
|
||||||
|
"IFTTT": 12,
|
||||||
|
"Telegram": 13}
|
||||||
|
|
||||||
def available_notification_agents():
|
def available_notification_agents():
|
||||||
agents = [{'name': 'Growl',
|
agents = [{'name': 'Growl',
|
||||||
|
@ -63,7 +65,10 @@ 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,
|
||||||
|
'on_extdown': plexpy.CONFIG.GROWL_ON_EXTDOWN,
|
||||||
|
'on_intdown': plexpy.CONFIG.GROWL_ON_INTDOWN
|
||||||
},
|
},
|
||||||
{'name': 'Prowl',
|
{'name': 'Prowl',
|
||||||
'id': AGENT_IDS['Prowl'],
|
'id': AGENT_IDS['Prowl'],
|
||||||
|
@ -75,7 +80,10 @@ 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,
|
||||||
|
'on_extdown': plexpy.CONFIG.PROWL_ON_EXTDOWN,
|
||||||
|
'on_intdown': plexpy.CONFIG.PROWL_ON_INTDOWN
|
||||||
},
|
},
|
||||||
{'name': 'XBMC',
|
{'name': 'XBMC',
|
||||||
'id': AGENT_IDS['XBMC'],
|
'id': AGENT_IDS['XBMC'],
|
||||||
|
@ -87,7 +95,10 @@ 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,
|
||||||
|
'on_extdown': plexpy.CONFIG.XBMC_ON_EXTDOWN,
|
||||||
|
'on_intdown': plexpy.CONFIG.XBMC_ON_INTDOWN
|
||||||
},
|
},
|
||||||
{'name': 'Plex',
|
{'name': 'Plex',
|
||||||
'id': AGENT_IDS['Plex'],
|
'id': AGENT_IDS['Plex'],
|
||||||
|
@ -99,7 +110,10 @@ 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,
|
||||||
|
'on_extdown': plexpy.CONFIG.PLEX_ON_EXTDOWN,
|
||||||
|
'on_intdown': plexpy.CONFIG.PLEX_ON_INTDOWN
|
||||||
},
|
},
|
||||||
{'name': 'NotifyMyAndroid',
|
{'name': 'NotifyMyAndroid',
|
||||||
'id': AGENT_IDS['NMA'],
|
'id': AGENT_IDS['NMA'],
|
||||||
|
@ -111,7 +125,10 @@ 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,
|
||||||
|
'on_extdown': plexpy.CONFIG.NMA_ON_EXTDOWN,
|
||||||
|
'on_intdown': plexpy.CONFIG.NMA_ON_INTDOWN
|
||||||
},
|
},
|
||||||
{'name': 'Pushalot',
|
{'name': 'Pushalot',
|
||||||
'id': AGENT_IDS['Pushalot'],
|
'id': AGENT_IDS['Pushalot'],
|
||||||
|
@ -123,7 +140,10 @@ 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,
|
||||||
|
'on_extdown': plexpy.CONFIG.PUSHALOT_ON_EXTDOWN,
|
||||||
|
'on_intdown': plexpy.CONFIG.PUSHALOT_ON_INTDOWN
|
||||||
},
|
},
|
||||||
{'name': 'Pushbullet',
|
{'name': 'Pushbullet',
|
||||||
'id': AGENT_IDS['Pushbullet'],
|
'id': AGENT_IDS['Pushbullet'],
|
||||||
|
@ -135,7 +155,10 @@ 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,
|
||||||
|
'on_extdown': plexpy.CONFIG.PUSHBULLET_ON_EXTDOWN,
|
||||||
|
'on_intdown': plexpy.CONFIG.PUSHBULLET_ON_INTDOWN
|
||||||
},
|
},
|
||||||
{'name': 'Pushover',
|
{'name': 'Pushover',
|
||||||
'id': AGENT_IDS['Pushover'],
|
'id': AGENT_IDS['Pushover'],
|
||||||
|
@ -147,7 +170,10 @@ 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,
|
||||||
|
'on_extdown': plexpy.CONFIG.PUSHOVER_ON_EXTDOWN,
|
||||||
|
'on_intdown': plexpy.CONFIG.PUSHOVER_ON_INTDOWN
|
||||||
},
|
},
|
||||||
{'name': 'Boxcar2',
|
{'name': 'Boxcar2',
|
||||||
'id': AGENT_IDS['Boxcar2'],
|
'id': AGENT_IDS['Boxcar2'],
|
||||||
|
@ -159,7 +185,10 @@ 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,
|
||||||
|
'on_extdown': plexpy.CONFIG.BOXCAR_ON_EXTDOWN,
|
||||||
|
'on_intdown': plexpy.CONFIG.BOXCAR_ON_INTDOWN
|
||||||
},
|
},
|
||||||
{'name': 'E-mail',
|
{'name': 'E-mail',
|
||||||
'id': AGENT_IDS['Email'],
|
'id': AGENT_IDS['Email'],
|
||||||
|
@ -171,7 +200,10 @@ 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,
|
||||||
|
'on_extdown': plexpy.CONFIG.EMAIL_ON_EXTDOWN,
|
||||||
|
'on_intdown': plexpy.CONFIG.EMAIL_ON_INTDOWN
|
||||||
},
|
},
|
||||||
{'name': 'Twitter',
|
{'name': 'Twitter',
|
||||||
'id': AGENT_IDS['Twitter'],
|
'id': AGENT_IDS['Twitter'],
|
||||||
|
@ -183,7 +215,40 @@ 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,
|
||||||
|
'on_extdown': plexpy.CONFIG.TWITTER_ON_EXTDOWN,
|
||||||
|
'on_intdown': plexpy.CONFIG.TWITTER_ON_INTDOWN
|
||||||
|
},
|
||||||
|
{'name': 'IFTTT',
|
||||||
|
'id': AGENT_IDS['IFTTT'],
|
||||||
|
'config_prefix': 'ifttt',
|
||||||
|
'has_config': True,
|
||||||
|
'state': checked(plexpy.CONFIG.IFTTT_ENABLED),
|
||||||
|
'on_play': plexpy.CONFIG.IFTTT_ON_PLAY,
|
||||||
|
'on_stop': plexpy.CONFIG.IFTTT_ON_STOP,
|
||||||
|
'on_pause': plexpy.CONFIG.IFTTT_ON_PAUSE,
|
||||||
|
'on_resume': plexpy.CONFIG.IFTTT_ON_RESUME,
|
||||||
|
'on_buffer': plexpy.CONFIG.IFTTT_ON_BUFFER,
|
||||||
|
'on_watched': plexpy.CONFIG.IFTTT_ON_WATCHED,
|
||||||
|
'on_created': plexpy.CONFIG.IFTTT_ON_CREATED,
|
||||||
|
'on_extdown': plexpy.CONFIG.IFTTT_ON_EXTDOWN,
|
||||||
|
'on_intdown': plexpy.CONFIG.IFTTT_ON_INTDOWN
|
||||||
|
},
|
||||||
|
{'name': 'Telegram',
|
||||||
|
'id': AGENT_IDS['Telegram'],
|
||||||
|
'config_prefix': 'telegram',
|
||||||
|
'has_config': True,
|
||||||
|
'state': checked(plexpy.CONFIG.TELEGRAM_ENABLED),
|
||||||
|
'on_play': plexpy.CONFIG.TELEGRAM_ON_PLAY,
|
||||||
|
'on_stop': plexpy.CONFIG.TELEGRAM_ON_STOP,
|
||||||
|
'on_pause': plexpy.CONFIG.TELEGRAM_ON_PAUSE,
|
||||||
|
'on_resume': plexpy.CONFIG.TELEGRAM_ON_RESUME,
|
||||||
|
'on_buffer': plexpy.CONFIG.TELEGRAM_ON_BUFFER,
|
||||||
|
'on_watched': plexpy.CONFIG.TELEGRAM_ON_WATCHED,
|
||||||
|
'on_created': plexpy.CONFIG.TELEGRAM_ON_CREATED,
|
||||||
|
'on_extdown': plexpy.CONFIG.TELEGRAM_ON_EXTDOWN,
|
||||||
|
'on_intdown': plexpy.CONFIG.TELEGRAM_ON_INTDOWN
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -200,7 +265,10 @@ 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,
|
||||||
|
'on_extdown': plexpy.CONFIG.OSX_NOTIFY_ON_EXTDOWN,
|
||||||
|
'on_intdown': plexpy.CONFIG.OSX_NOTIFY_ON_INTDOWN
|
||||||
})
|
})
|
||||||
|
|
||||||
return agents
|
return agents
|
||||||
|
@ -245,6 +313,12 @@ def get_notification_agent_config(config_id):
|
||||||
elif config_id == 11:
|
elif config_id == 11:
|
||||||
tweet = TwitterNotifier()
|
tweet = TwitterNotifier()
|
||||||
return tweet.return_config_options()
|
return tweet.return_config_options()
|
||||||
|
elif config_id == 12:
|
||||||
|
iftttClient = IFTTT()
|
||||||
|
return iftttClient.return_config_options()
|
||||||
|
elif config_id == 13:
|
||||||
|
telegramClient = TELEGRAM()
|
||||||
|
return telegramClient.return_config_options()
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
else:
|
else:
|
||||||
|
@ -290,6 +364,12 @@ def send_notification(config_id, subject, body):
|
||||||
elif config_id == 11:
|
elif config_id == 11:
|
||||||
tweet = TwitterNotifier()
|
tweet = TwitterNotifier()
|
||||||
tweet.notify(subject=subject, message=body)
|
tweet.notify(subject=subject, message=body)
|
||||||
|
elif config_id == 12:
|
||||||
|
iftttClient = IFTTT()
|
||||||
|
iftttClient.notify(subject=subject, message=body)
|
||||||
|
elif config_id == 13:
|
||||||
|
telegramClient = TELEGRAM()
|
||||||
|
telegramClient.notify(message=body, event=subject)
|
||||||
else:
|
else:
|
||||||
logger.debug(u"PlexPy Notifier :: Unknown agent id received.")
|
logger.debug(u"PlexPy Notifier :: Unknown agent id received.")
|
||||||
else:
|
else:
|
||||||
|
@ -868,7 +948,7 @@ class PUSHOVER(object):
|
||||||
|
|
||||||
data = {'token': self.application_token,
|
data = {'token': self.application_token,
|
||||||
'user': plexpy.CONFIG.PUSHOVER_KEYS,
|
'user': plexpy.CONFIG.PUSHOVER_KEYS,
|
||||||
'title': event,
|
'title': event.encode("utf-8"),
|
||||||
'message': message.encode("utf-8"),
|
'message': message.encode("utf-8"),
|
||||||
'sound': plexpy.CONFIG.PUSHOVER_SOUND,
|
'sound': plexpy.CONFIG.PUSHOVER_SOUND,
|
||||||
'priority': plexpy.CONFIG.PUSHOVER_PRIORITY}
|
'priority': plexpy.CONFIG.PUSHOVER_PRIORITY}
|
||||||
|
@ -924,10 +1004,10 @@ class PUSHOVER(object):
|
||||||
return {'': ''}
|
return {'': ''}
|
||||||
|
|
||||||
def return_config_options(self):
|
def return_config_options(self):
|
||||||
config_option = [{'label': 'Pushover User Key',
|
config_option = [{'label': 'Pushover User or Group Key',
|
||||||
'value': self.keys,
|
'value': self.keys,
|
||||||
'name': 'pushover_keys',
|
'name': 'pushover_keys',
|
||||||
'description': 'Your Pushover user key.',
|
'description': 'Your Pushover user or group key.',
|
||||||
'input_type': 'text'
|
'input_type': 'text'
|
||||||
},
|
},
|
||||||
{'label': 'Priority',
|
{'label': 'Priority',
|
||||||
|
@ -1268,6 +1348,8 @@ class Email(object):
|
||||||
|
|
||||||
mailserver.sendmail(plexpy.CONFIG.EMAIL_FROM, plexpy.CONFIG.EMAIL_TO, message.as_string())
|
mailserver.sendmail(plexpy.CONFIG.EMAIL_FROM, plexpy.CONFIG.EMAIL_TO, message.as_string())
|
||||||
mailserver.quit()
|
mailserver.quit()
|
||||||
|
|
||||||
|
logger.info(u"Email notifications sent.")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
|
@ -1320,3 +1402,133 @@ class Email(object):
|
||||||
]
|
]
|
||||||
|
|
||||||
return config_option
|
return config_option
|
||||||
|
|
||||||
|
|
||||||
|
class IFTTT(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.apikey = plexpy.CONFIG.IFTTT_KEY
|
||||||
|
self.event = plexpy.CONFIG.IFTTT_EVENT
|
||||||
|
|
||||||
|
def notify(self, message, subject):
|
||||||
|
if not message or not subject:
|
||||||
|
return
|
||||||
|
|
||||||
|
http_handler = HTTPSConnection("maker.ifttt.com")
|
||||||
|
|
||||||
|
data = {'value1': subject.encode("utf-8"),
|
||||||
|
'value2': message.encode("utf-8")}
|
||||||
|
|
||||||
|
# logger.debug("Ifttt SENDING: %s" % json.dumps(data))
|
||||||
|
|
||||||
|
http_handler.request("POST",
|
||||||
|
"/trigger/%s/with/key/%s" % (self.event, self.apikey),
|
||||||
|
headers={'Content-type': "application/json"},
|
||||||
|
body=json.dumps(data))
|
||||||
|
response = http_handler.getresponse()
|
||||||
|
request_status = response.status
|
||||||
|
# logger.debug(u"Ifttt response status: %r" % request_status)
|
||||||
|
# logger.debug(u"Ifttt response headers: %r" % response.getheaders())
|
||||||
|
# logger.debug(u"Ifttt response body: %r" % response.read())
|
||||||
|
|
||||||
|
if request_status == 200:
|
||||||
|
logger.info(u"Ifttt notifications sent.")
|
||||||
|
return True
|
||||||
|
elif request_status >= 400 and request_status < 500:
|
||||||
|
logger.info(u"Ifttt request failed: %s" % response.reason)
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
logger.info(u"Ifttt notification failed serverside.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def test(self):
|
||||||
|
return self.notify('PlexPy', 'Test Message')
|
||||||
|
|
||||||
|
def return_config_options(self):
|
||||||
|
config_option = [{'label': 'Ifttt Maker Channel Key',
|
||||||
|
'value': self.apikey,
|
||||||
|
'name': 'ifttt_key',
|
||||||
|
'description': 'Your Ifttt key. You can get a key from <a href="https://ifttt.com/maker" target="_blank">here</a>.',
|
||||||
|
'input_type': 'text'
|
||||||
|
},
|
||||||
|
{'label': 'Ifttt Event',
|
||||||
|
'value': self.event,
|
||||||
|
'name': 'ifttt_event',
|
||||||
|
'description': 'The Ifttt maker event to fire. The notification subject and body will be sent'
|
||||||
|
' as value1 and value2 respectively.',
|
||||||
|
'input_type': 'text'
|
||||||
|
},
|
||||||
|
{'label': 'Test Event',
|
||||||
|
'value': 'Test Event',
|
||||||
|
'name': 'testIFTTT',
|
||||||
|
'description': 'Test if IFTTT notifications are working. See logs for troubleshooting.',
|
||||||
|
'input_type': 'button'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return config_option
|
||||||
|
|
||||||
|
class TELEGRAM(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.enabled = plexpy.CONFIG.TELEGRAM_ENABLED
|
||||||
|
self.bot_token = plexpy.CONFIG.TELEGRAM_BOT_TOKEN
|
||||||
|
self.chat_id = plexpy.CONFIG.TELEGRAM_CHAT_ID
|
||||||
|
|
||||||
|
def conf(self, options):
|
||||||
|
return cherrypy.config['config'].get('Telegram', options)
|
||||||
|
|
||||||
|
def notify(self, message, event):
|
||||||
|
if not message or not event:
|
||||||
|
return
|
||||||
|
|
||||||
|
http_handler = HTTPSConnection("api.telegram.org")
|
||||||
|
|
||||||
|
data = {'chat_id': self.chat_id,
|
||||||
|
'text': event.encode('utf-8') + ': ' + message.encode("utf-8")}
|
||||||
|
|
||||||
|
http_handler.request("POST",
|
||||||
|
"/bot%s/%s" % (self.bot_token, "sendMessage"),
|
||||||
|
headers={'Content-type': "application/x-www-form-urlencoded"},
|
||||||
|
body=urlencode(data))
|
||||||
|
|
||||||
|
response = http_handler.getresponse()
|
||||||
|
request_status = response.status
|
||||||
|
|
||||||
|
if request_status == 200:
|
||||||
|
logger.info(u"Telegram notifications sent.")
|
||||||
|
return True
|
||||||
|
elif request_status >= 400 and request_status < 500:
|
||||||
|
logger.info(u"Telegram request failed: %s" % response.reason)
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
logger.info(u"Telegram notification failed serverside.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def updateLibrary(self):
|
||||||
|
#For uniformity reasons not removed
|
||||||
|
return
|
||||||
|
|
||||||
|
def test(self, bot_token, chat_id):
|
||||||
|
self.enabled = True
|
||||||
|
self.bot_token = bot_token
|
||||||
|
self.chat_id = chat_id
|
||||||
|
|
||||||
|
self.notify('Main Screen Activate', 'Test Message')
|
||||||
|
|
||||||
|
def return_config_options(self):
|
||||||
|
config_option = [{'label': 'Telegram Bot Token',
|
||||||
|
'value': self.bot_token,
|
||||||
|
'name': 'telegram_bot_token',
|
||||||
|
'description': 'Your Telegram bot token. Contact <a href="http://telegram.me/BotFather" target="_blank">@BotFather</a> on Telegram to get one.',
|
||||||
|
'input_type': 'text'
|
||||||
|
},
|
||||||
|
{'label': 'Telegram Chat ID',
|
||||||
|
'value': self.chat_id,
|
||||||
|
'name': 'telegram_chat_id',
|
||||||
|
'description': 'Your Telegram Chat ID or Group ID. Contact <a href="http://telegram.me/myidbot" target="_blank">@myidbot</a> on Telegram to get an ID.',
|
||||||
|
'input_type': 'text'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return config_option
|
||||||
|
|
|
@ -444,3 +444,22 @@ class PlexTV(object):
|
||||||
return clean_servers
|
return clean_servers
|
||||||
|
|
||||||
return json.dumps(clean_servers, indent=4)
|
return json.dumps(clean_servers, indent=4)
|
||||||
|
|
||||||
|
def get_server_times(self):
|
||||||
|
servers = self.get_plextv_server_list(output_format='xml')
|
||||||
|
server_times = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
xml_head = servers.getElementsByTagName('Server')
|
||||||
|
except:
|
||||||
|
logger.warn("Error parsing XML for Plex servers.")
|
||||||
|
return []
|
||||||
|
|
||||||
|
for a in xml_head:
|
||||||
|
if helpers.get_xml_attr(a, 'machineIdentifier') == plexpy.CONFIG.PMS_IDENTIFIER:
|
||||||
|
server_times.append({"created_at": helpers.get_xml_attr(a, 'createdAt'),
|
||||||
|
"updated_at": helpers.get_xml_attr(a, 'updatedAt')
|
||||||
|
})
|
||||||
|
break
|
||||||
|
|
||||||
|
return server_times
|
|
@ -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()
|
||||||
|
|
|
@ -13,12 +13,29 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from plexpy import logger, helpers, users, http_handler
|
from plexpy import logger, helpers, users, http_handler, common
|
||||||
from urlparse import urlparse
|
from urlparse import urlparse
|
||||||
|
|
||||||
import plexpy
|
import plexpy
|
||||||
import urllib2
|
import urllib2
|
||||||
|
|
||||||
|
def get_server_friendly_name():
|
||||||
|
logger.info("Requesting name from server...")
|
||||||
|
server_name = PmsConnect().get_server_pref(pref='FriendlyName')
|
||||||
|
|
||||||
|
# If friendly name is blank
|
||||||
|
if not server_name:
|
||||||
|
servers_info = PmsConnect().get_servers_info()
|
||||||
|
for server in servers_info:
|
||||||
|
if server['machine_identifier'] == plexpy.CONFIG.PMS_IDENTIFIER:
|
||||||
|
server_name = server['name']
|
||||||
|
break
|
||||||
|
|
||||||
|
if server_name and server_name != plexpy.CONFIG.PMS_NAME:
|
||||||
|
plexpy.CONFIG.__setattr__('PMS_NAME', server_name)
|
||||||
|
plexpy.CONFIG.write()
|
||||||
|
|
||||||
|
return server_name
|
||||||
|
|
||||||
class PmsConnect(object):
|
class PmsConnect(object):
|
||||||
"""
|
"""
|
||||||
|
@ -254,6 +271,36 @@ class PmsConnect(object):
|
||||||
return request
|
return request
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
Return account details.
|
||||||
|
|
||||||
|
Optional parameters: output_format { dict, json }
|
||||||
|
|
||||||
|
Output: array
|
||||||
|
"""
|
||||||
|
def get_account(self, output_format=''):
|
||||||
|
uri = '/myplex/account'
|
||||||
|
request = self.request_handler.make_request(uri=uri,
|
||||||
|
proto=self.protocol,
|
||||||
|
request_type='GET',
|
||||||
|
output_format=output_format)
|
||||||
|
|
||||||
|
return request
|
||||||
|
|
||||||
|
"""
|
||||||
|
Refresh Plex remote access port mapping.
|
||||||
|
|
||||||
|
Optional parameters: None
|
||||||
|
|
||||||
|
Output: None
|
||||||
|
"""
|
||||||
|
def put_refresh_reachability(self):
|
||||||
|
uri = '/myplex/refreshReachability'
|
||||||
|
request = self.request_handler.make_request(uri=uri,
|
||||||
|
proto=self.protocol,
|
||||||
|
request_type='PUT')
|
||||||
|
|
||||||
|
return request
|
||||||
|
"""
|
||||||
Return processed and validated list of recently added items.
|
Return processed and validated list of recently added items.
|
||||||
|
|
||||||
Parameters required: count { number of results to return }
|
Parameters required: count { number of results to return }
|
||||||
|
@ -281,10 +328,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 +346,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 +421,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,16 +453,16 @@ 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'),
|
||||||
'parent_index': helpers.get_xml_attr(metadata_main, 'parentIndex'),
|
'parent_index': helpers.get_xml_attr(metadata_main, 'parentIndex'),
|
||||||
'parent_title': helpers.get_xml_attr(metadata_main, 'parentTitle'),
|
'parent_title': helpers.get_xml_attr(metadata_main, 'parentTitle'),
|
||||||
'index': helpers.get_xml_attr(metadata_main, 'index'),
|
'index': helpers.get_xml_attr(metadata_main, 'index'),
|
||||||
'studio': helpers.get_xml_attr(metadata_main, 'studio'),
|
'studio': show_details['metadata']['studio'],
|
||||||
'title': helpers.get_xml_attr(metadata_main, 'title'),
|
'title': helpers.get_xml_attr(metadata_main, 'title'),
|
||||||
'content_rating': helpers.get_xml_attr(metadata_main, 'contentRating'),
|
'content_rating': show_details['metadata']['content_rating'],
|
||||||
'summary': show_details['metadata']['summary'],
|
'summary': show_details['metadata']['summary'],
|
||||||
'tagline': helpers.get_xml_attr(metadata_main, 'tagline'),
|
'tagline': helpers.get_xml_attr(metadata_main, 'tagline'),
|
||||||
'rating': helpers.get_xml_attr(metadata_main, 'rating'),
|
'rating': helpers.get_xml_attr(metadata_main, 'rating'),
|
||||||
|
@ -425,14 +477,16 @@ class PmsConnect(object):
|
||||||
'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'),
|
'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'),
|
||||||
'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'),
|
'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'),
|
||||||
'guid': helpers.get_xml_attr(metadata_main, 'guid'),
|
'guid': helpers.get_xml_attr(metadata_main, 'guid'),
|
||||||
'genres': genres,
|
'genres': show_details['metadata']['genres'],
|
||||||
'actors': actors,
|
'actors': show_details['metadata']['actors'],
|
||||||
'writers': writers,
|
'writers': show_details['metadata']['writers'],
|
||||||
'directors': directors
|
'directors': show_details['metadata']['directors']
|
||||||
}
|
}
|
||||||
metadata_list = {'metadata': metadata}
|
metadata_list = {'metadata': metadata}
|
||||||
elif metadata_type == 'episode':
|
elif metadata_type == 'episode':
|
||||||
metadata = {'type': metadata_type,
|
grandparent_rating_key = helpers.get_xml_attr(metadata_main, 'grandparentRatingKey')
|
||||||
|
show_details = self.get_metadata_details(grandparent_rating_key)
|
||||||
|
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'),
|
||||||
|
@ -440,7 +494,7 @@ class PmsConnect(object):
|
||||||
'parent_index': helpers.get_xml_attr(metadata_main, 'parentIndex'),
|
'parent_index': helpers.get_xml_attr(metadata_main, 'parentIndex'),
|
||||||
'parent_title': helpers.get_xml_attr(metadata_main, 'parentTitle'),
|
'parent_title': helpers.get_xml_attr(metadata_main, 'parentTitle'),
|
||||||
'index': helpers.get_xml_attr(metadata_main, 'index'),
|
'index': helpers.get_xml_attr(metadata_main, 'index'),
|
||||||
'studio': helpers.get_xml_attr(metadata_main, 'studio'),
|
'studio': show_details['metadata']['studio'],
|
||||||
'title': helpers.get_xml_attr(metadata_main, 'title'),
|
'title': helpers.get_xml_attr(metadata_main, 'title'),
|
||||||
'content_rating': helpers.get_xml_attr(metadata_main, 'contentRating'),
|
'content_rating': helpers.get_xml_attr(metadata_main, 'contentRating'),
|
||||||
'summary': helpers.get_xml_attr(metadata_main, 'summary'),
|
'summary': helpers.get_xml_attr(metadata_main, 'summary'),
|
||||||
|
@ -457,14 +511,14 @@ class PmsConnect(object):
|
||||||
'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'),
|
'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'),
|
||||||
'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'),
|
'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'),
|
||||||
'guid': helpers.get_xml_attr(metadata_main, 'guid'),
|
'guid': helpers.get_xml_attr(metadata_main, 'guid'),
|
||||||
|
'genres': show_details['metadata']['genres'],
|
||||||
|
'actors': show_details['metadata']['actors'],
|
||||||
'writers': writers,
|
'writers': writers,
|
||||||
'directors': directors,
|
'directors': directors
|
||||||
'genres': genres,
|
|
||||||
'actors': actors
|
|
||||||
}
|
}
|
||||||
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 +548,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 +580,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 +613,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'),
|
||||||
|
@ -584,7 +638,7 @@ class PmsConnect(object):
|
||||||
'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'),
|
'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'),
|
||||||
'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'),
|
'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'),
|
||||||
'guid': helpers.get_xml_attr(metadata_main, 'guid'),
|
'guid': helpers.get_xml_attr(metadata_main, 'guid'),
|
||||||
'genres': genres,
|
'genres': album_details['metadata']['genres'],
|
||||||
'actors': actors,
|
'actors': actors,
|
||||||
'writers': writers,
|
'writers': writers,
|
||||||
'directors': directors
|
'directors': directors
|
||||||
|
@ -595,6 +649,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.
|
||||||
|
|
||||||
|
@ -706,6 +803,7 @@ class PmsConnect(object):
|
||||||
'user_id': user_details['user_id'],
|
'user_id': user_details['user_id'],
|
||||||
'friendly_name': user_details['friendly_name'],
|
'friendly_name': user_details['friendly_name'],
|
||||||
'user_thumb': user_details['thumb'],
|
'user_thumb': user_details['thumb'],
|
||||||
|
'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address'),
|
||||||
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
||||||
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
||||||
'machine_id': machine_id,
|
'machine_id': machine_id,
|
||||||
|
@ -826,6 +924,7 @@ class PmsConnect(object):
|
||||||
'user_id': user_details['user_id'],
|
'user_id': user_details['user_id'],
|
||||||
'friendly_name': user_details['friendly_name'],
|
'friendly_name': user_details['friendly_name'],
|
||||||
'user_thumb': user_details['thumb'],
|
'user_thumb': user_details['thumb'],
|
||||||
|
'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address'),
|
||||||
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
||||||
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
||||||
'machine_id': machine_id,
|
'machine_id': machine_id,
|
||||||
|
@ -882,6 +981,7 @@ class PmsConnect(object):
|
||||||
'user_id': user_details['user_id'],
|
'user_id': user_details['user_id'],
|
||||||
'friendly_name': user_details['friendly_name'],
|
'friendly_name': user_details['friendly_name'],
|
||||||
'user_thumb': user_details['thumb'],
|
'user_thumb': user_details['thumb'],
|
||||||
|
'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address'),
|
||||||
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
||||||
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
||||||
'machine_id': machine_id,
|
'machine_id': machine_id,
|
||||||
|
@ -938,6 +1038,7 @@ class PmsConnect(object):
|
||||||
'user_id': user_details['user_id'],
|
'user_id': user_details['user_id'],
|
||||||
'friendly_name': user_details['friendly_name'],
|
'friendly_name': user_details['friendly_name'],
|
||||||
'user_thumb': user_details['thumb'],
|
'user_thumb': user_details['thumb'],
|
||||||
|
'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address'),
|
||||||
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
||||||
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
||||||
'machine_id': machine_id,
|
'machine_id': machine_id,
|
||||||
|
@ -1027,6 +1128,7 @@ class PmsConnect(object):
|
||||||
'user_id': user_details['user_id'],
|
'user_id': user_details['user_id'],
|
||||||
'friendly_name': user_details['friendly_name'],
|
'friendly_name': user_details['friendly_name'],
|
||||||
'user_thumb': user_details['thumb'],
|
'user_thumb': user_details['thumb'],
|
||||||
|
'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address'),
|
||||||
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
||||||
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
||||||
'machine_id': machine_id,
|
'machine_id': machine_id,
|
||||||
|
@ -1071,10 +1173,7 @@ class PmsConnect(object):
|
||||||
logger.warn(u"No known stream types found in session list.")
|
logger.warn(u"No known stream types found in session list.")
|
||||||
|
|
||||||
# Rename Mystery platform names
|
# Rename Mystery platform names
|
||||||
platform_names = {'Mystery 3': 'Playstation 3',
|
session_output['platform'] = common.PLATFORM_NAME_OVERRIDES.get(session_output['platform'],
|
||||||
'Mystery 4': 'Playstation 4',
|
|
||||||
'Mystery 5': 'Xbox 360'}
|
|
||||||
session_output['platform'] = platform_names.get(session_output['platform'],
|
|
||||||
session_output['platform'])
|
session_output['platform'])
|
||||||
|
|
||||||
return session_output
|
return session_output
|
||||||
|
@ -1432,9 +1531,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
|
||||||
|
|
||||||
|
@ -1443,7 +1542,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'])
|
||||||
|
@ -1455,9 +1554,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
|
||||||
|
|
||||||
|
@ -1580,3 +1679,25 @@ class PmsConnect(object):
|
||||||
}
|
}
|
||||||
|
|
||||||
return key_list
|
return key_list
|
||||||
|
|
||||||
|
def get_server_response(self):
|
||||||
|
# Refresh Plex remote access port mapping first
|
||||||
|
self.put_refresh_reachability()
|
||||||
|
account_data = self.get_account(output_format='xml')
|
||||||
|
|
||||||
|
try:
|
||||||
|
xml_head = account_data.getElementsByTagName('MyPlex')
|
||||||
|
except:
|
||||||
|
logger.warn("Unable to parse XML for get_server_response.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
server_response = {}
|
||||||
|
|
||||||
|
for a in xml_head:
|
||||||
|
server_response = {'mapping_state': helpers.get_xml_attr(a, 'mappingState'),
|
||||||
|
'mapping_error': helpers.get_xml_attr(a, 'mappingError'),
|
||||||
|
'public_address': helpers.get_xml_attr(a, 'publicAddress'),
|
||||||
|
'public_port': helpers.get_xml_attr(a, 'publicPort')
|
||||||
|
}
|
||||||
|
|
||||||
|
return server_response
|
|
@ -85,10 +85,7 @@ class Users(object):
|
||||||
user_thumb = item['user_thumb']
|
user_thumb = item['user_thumb']
|
||||||
|
|
||||||
# Rename Mystery platform names
|
# Rename Mystery platform names
|
||||||
platform_names = {'Mystery 3': 'Playstation 3',
|
platform = common.PLATFORM_NAME_OVERRIDES.get(item["platform"], item["platform"])
|
||||||
'Mystery 4': 'Playstation 4',
|
|
||||||
'Mystery 5': 'Xbox 360'}
|
|
||||||
platform = platform_names.get(item["platform"], item["platform"])
|
|
||||||
|
|
||||||
row = {"id": item['id'],
|
row = {"id": item['id'],
|
||||||
"plays": item['plays'],
|
"plays": item['plays'],
|
||||||
|
@ -179,10 +176,7 @@ class Users(object):
|
||||||
thumb = item["thumb"]
|
thumb = item["thumb"]
|
||||||
|
|
||||||
# Rename Mystery platform names
|
# Rename Mystery platform names
|
||||||
platform_names = {'Mystery 3': 'Playstation 3',
|
platform = common.PLATFORM_NAME_OVERRIDES.get(item["platform"], item["platform"])
|
||||||
'Mystery 4': 'Playstation 4',
|
|
||||||
'Mystery 5': 'Xbox 360'}
|
|
||||||
platform = platform_names.get(item["platform"], item["platform"])
|
|
||||||
|
|
||||||
row = {"id": item['id'],
|
row = {"id": item['id'],
|
||||||
"last_seen": item['last_seen'],
|
"last_seen": item['last_seen'],
|
||||||
|
@ -533,10 +527,7 @@ class Users(object):
|
||||||
|
|
||||||
for item in result:
|
for item in result:
|
||||||
# Rename Mystery platform names
|
# Rename Mystery platform names
|
||||||
platform_names = {'Mystery 3': 'Playstation 3',
|
platform_type = common.PLATFORM_NAME_OVERRIDES.get(item[2], item[2])
|
||||||
'Mystery 4': 'Playstation 4',
|
|
||||||
'Mystery 5': 'Xbox 360'}
|
|
||||||
platform_type = platform_names.get(item[2], item[2])
|
|
||||||
|
|
||||||
row = {'player_name': item[0],
|
row = {'player_name': item[0],
|
||||||
'platform_type': platform_type,
|
'platform_type': platform_type,
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
PLEXPY_VERSION = "master"
|
PLEXPY_VERSION = "master"
|
||||||
PLEXPY_RELEASE_VERSION = "1.2.3"
|
PLEXPY_RELEASE_VERSION = "1.2.4"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -46,9 +46,11 @@ def serve_template(templatename, **kwargs):
|
||||||
|
|
||||||
_hplookup = TemplateLookup(directories=[template_dir])
|
_hplookup = TemplateLookup(directories=[template_dir])
|
||||||
|
|
||||||
|
server_name = plexpy.CONFIG.PMS_NAME
|
||||||
|
|
||||||
try:
|
try:
|
||||||
template = _hplookup.get_template(templatename)
|
template = _hplookup.get_template(templatename)
|
||||||
return template.render(**kwargs)
|
return template.render(server_name=server_name, **kwargs)
|
||||||
except:
|
except:
|
||||||
return exceptions.html_error_template().render()
|
return exceptions.html_error_template().render()
|
||||||
|
|
||||||
|
@ -71,7 +73,8 @@ class WebInterface(object):
|
||||||
"home_stats_length": plexpy.CONFIG.HOME_STATS_LENGTH,
|
"home_stats_length": plexpy.CONFIG.HOME_STATS_LENGTH,
|
||||||
"home_stats_cards": plexpy.CONFIG.HOME_STATS_CARDS,
|
"home_stats_cards": plexpy.CONFIG.HOME_STATS_CARDS,
|
||||||
"home_library_cards": plexpy.CONFIG.HOME_LIBRARY_CARDS,
|
"home_library_cards": plexpy.CONFIG.HOME_LIBRARY_CARDS,
|
||||||
"pms_identifier": plexpy.CONFIG.PMS_IDENTIFIER
|
"pms_identifier": plexpy.CONFIG.PMS_IDENTIFIER,
|
||||||
|
"pms_name": plexpy.CONFIG.PMS_NAME
|
||||||
}
|
}
|
||||||
return serve_template(templatename="index.html", title="Home", config=config)
|
return serve_template(templatename="index.html", title="Home", config=config)
|
||||||
|
|
||||||
|
@ -87,13 +90,14 @@ class WebInterface(object):
|
||||||
"pms_token": plexpy.CONFIG.PMS_TOKEN,
|
"pms_token": plexpy.CONFIG.PMS_TOKEN,
|
||||||
"pms_ssl": checked(plexpy.CONFIG.PMS_SSL),
|
"pms_ssl": checked(plexpy.CONFIG.PMS_SSL),
|
||||||
"pms_uuid": plexpy.CONFIG.PMS_UUID,
|
"pms_uuid": plexpy.CONFIG.PMS_UUID,
|
||||||
"tv_notify_enable": checked(plexpy.CONFIG.TV_NOTIFY_ENABLE),
|
|
||||||
"movie_notify_enable": checked(plexpy.CONFIG.MOVIE_NOTIFY_ENABLE),
|
"movie_notify_enable": checked(plexpy.CONFIG.MOVIE_NOTIFY_ENABLE),
|
||||||
|
"tv_notify_enable": checked(plexpy.CONFIG.TV_NOTIFY_ENABLE),
|
||||||
"music_notify_enable": checked(plexpy.CONFIG.MUSIC_NOTIFY_ENABLE),
|
"music_notify_enable": checked(plexpy.CONFIG.MUSIC_NOTIFY_ENABLE),
|
||||||
"tv_notify_on_start": checked(plexpy.CONFIG.TV_NOTIFY_ON_START),
|
|
||||||
"movie_notify_on_start": checked(plexpy.CONFIG.MOVIE_NOTIFY_ON_START),
|
"movie_notify_on_start": checked(plexpy.CONFIG.MOVIE_NOTIFY_ON_START),
|
||||||
|
"tv_notify_on_start": checked(plexpy.CONFIG.TV_NOTIFY_ON_START),
|
||||||
"music_notify_on_start": checked(plexpy.CONFIG.MUSIC_NOTIFY_ON_START),
|
"music_notify_on_start": checked(plexpy.CONFIG.MUSIC_NOTIFY_ON_START),
|
||||||
"video_logging_enable": checked(plexpy.CONFIG.VIDEO_LOGGING_ENABLE),
|
"movie_logging_enable": checked(plexpy.CONFIG.MOVIE_LOGGING_ENABLE),
|
||||||
|
"tv_logging_enable": checked(plexpy.CONFIG.TV_LOGGING_ENABLE),
|
||||||
"music_logging_enable": checked(plexpy.CONFIG.MUSIC_LOGGING_ENABLE),
|
"music_logging_enable": checked(plexpy.CONFIG.MUSIC_LOGGING_ENABLE),
|
||||||
"logging_ignore_interval": plexpy.CONFIG.LOGGING_IGNORE_INTERVAL,
|
"logging_ignore_interval": plexpy.CONFIG.LOGGING_IGNORE_INTERVAL,
|
||||||
"check_github": checked(plexpy.CONFIG.CHECK_GITHUB)
|
"check_github": checked(plexpy.CONFIG.CHECK_GITHUB)
|
||||||
|
@ -420,8 +424,8 @@ class WebInterface(object):
|
||||||
"grouping_global_history": checked(plexpy.CONFIG.GROUPING_GLOBAL_HISTORY),
|
"grouping_global_history": checked(plexpy.CONFIG.GROUPING_GLOBAL_HISTORY),
|
||||||
"grouping_user_history": checked(plexpy.CONFIG.GROUPING_USER_HISTORY),
|
"grouping_user_history": checked(plexpy.CONFIG.GROUPING_USER_HISTORY),
|
||||||
"grouping_charts": checked(plexpy.CONFIG.GROUPING_CHARTS),
|
"grouping_charts": checked(plexpy.CONFIG.GROUPING_CHARTS),
|
||||||
"tv_notify_enable": checked(plexpy.CONFIG.TV_NOTIFY_ENABLE),
|
|
||||||
"movie_notify_enable": checked(plexpy.CONFIG.MOVIE_NOTIFY_ENABLE),
|
"movie_notify_enable": checked(plexpy.CONFIG.MOVIE_NOTIFY_ENABLE),
|
||||||
|
"tv_notify_enable": checked(plexpy.CONFIG.TV_NOTIFY_ENABLE),
|
||||||
"music_notify_enable": checked(plexpy.CONFIG.MUSIC_NOTIFY_ENABLE),
|
"music_notify_enable": checked(plexpy.CONFIG.MUSIC_NOTIFY_ENABLE),
|
||||||
"tv_notify_on_start": checked(plexpy.CONFIG.TV_NOTIFY_ON_START),
|
"tv_notify_on_start": checked(plexpy.CONFIG.TV_NOTIFY_ON_START),
|
||||||
"movie_notify_on_start": checked(plexpy.CONFIG.MOVIE_NOTIFY_ON_START),
|
"movie_notify_on_start": checked(plexpy.CONFIG.MOVIE_NOTIFY_ON_START),
|
||||||
|
@ -432,16 +436,20 @@ class WebInterface(object):
|
||||||
"tv_notify_on_pause": checked(plexpy.CONFIG.TV_NOTIFY_ON_PAUSE),
|
"tv_notify_on_pause": checked(plexpy.CONFIG.TV_NOTIFY_ON_PAUSE),
|
||||||
"movie_notify_on_pause": checked(plexpy.CONFIG.MOVIE_NOTIFY_ON_PAUSE),
|
"movie_notify_on_pause": checked(plexpy.CONFIG.MOVIE_NOTIFY_ON_PAUSE),
|
||||||
"music_notify_on_pause": checked(plexpy.CONFIG.MUSIC_NOTIFY_ON_PAUSE),
|
"music_notify_on_pause": checked(plexpy.CONFIG.MUSIC_NOTIFY_ON_PAUSE),
|
||||||
|
"monitor_remote_access": checked(plexpy.CONFIG.MONITOR_REMOTE_ACCESS),
|
||||||
"monitoring_interval": plexpy.CONFIG.MONITORING_INTERVAL,
|
"monitoring_interval": plexpy.CONFIG.MONITORING_INTERVAL,
|
||||||
"monitoring_use_websocket": checked(plexpy.CONFIG.MONITORING_USE_WEBSOCKET),
|
"monitoring_use_websocket": checked(plexpy.CONFIG.MONITORING_USE_WEBSOCKET),
|
||||||
"refresh_users_interval": plexpy.CONFIG.REFRESH_USERS_INTERVAL,
|
"refresh_users_interval": plexpy.CONFIG.REFRESH_USERS_INTERVAL,
|
||||||
"refresh_users_on_startup": checked(plexpy.CONFIG.REFRESH_USERS_ON_STARTUP),
|
"refresh_users_on_startup": checked(plexpy.CONFIG.REFRESH_USERS_ON_STARTUP),
|
||||||
"ip_logging_enable": checked(plexpy.CONFIG.IP_LOGGING_ENABLE),
|
"ip_logging_enable": checked(plexpy.CONFIG.IP_LOGGING_ENABLE),
|
||||||
"video_logging_enable": checked(plexpy.CONFIG.VIDEO_LOGGING_ENABLE),
|
"movie_logging_enable": checked(plexpy.CONFIG.MOVIE_LOGGING_ENABLE),
|
||||||
|
"tv_logging_enable": checked(plexpy.CONFIG.TV_LOGGING_ENABLE),
|
||||||
"music_logging_enable": checked(plexpy.CONFIG.MUSIC_LOGGING_ENABLE),
|
"music_logging_enable": checked(plexpy.CONFIG.MUSIC_LOGGING_ENABLE),
|
||||||
"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_recently_added_delay": plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_DELAY,
|
||||||
"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 +463,12 @@ 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,
|
||||||
|
"notify_on_extdown_subject_text": plexpy.CONFIG.NOTIFY_ON_EXTDOWN_SUBJECT_TEXT,
|
||||||
|
"notify_on_extdown_body_text": plexpy.CONFIG.NOTIFY_ON_EXTDOWN_BODY_TEXT,
|
||||||
|
"notify_on_intdown_subject_text": plexpy.CONFIG.NOTIFY_ON_INTDOWN_SUBJECT_TEXT,
|
||||||
|
"notify_on_intdown_body_text": plexpy.CONFIG.NOTIFY_ON_INTDOWN_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,
|
||||||
|
@ -474,12 +488,13 @@ class WebInterface(object):
|
||||||
checked_configs = [
|
checked_configs = [
|
||||||
"launch_browser", "enable_https", "api_enabled", "freeze_db", "check_github",
|
"launch_browser", "enable_https", "api_enabled", "freeze_db", "check_github",
|
||||||
"grouping_global_history", "grouping_user_history", "grouping_charts", "pms_use_bif", "pms_ssl",
|
"grouping_global_history", "grouping_user_history", "grouping_charts", "pms_use_bif", "pms_ssl",
|
||||||
"tv_notify_enable", "movie_notify_enable", "music_notify_enable", "monitoring_use_websocket",
|
"movie_notify_enable", "tv_notify_enable", "music_notify_enable", "monitoring_use_websocket",
|
||||||
"tv_notify_on_start", "movie_notify_on_start", "music_notify_on_start",
|
"tv_notify_on_start", "movie_notify_on_start", "music_notify_on_start",
|
||||||
"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", "movie_logging_enable", "tv_logging_enable", "music_logging_enable",
|
||||||
"group_history_tables", "notify_consecutive"
|
"pms_is_remote", "home_stats_type", "group_history_tables", "notify_consecutive",
|
||||||
|
"notify_recently_added_grandparent", "monitor_remote_access"
|
||||||
]
|
]
|
||||||
for checked_config in checked_configs:
|
for checked_config in checked_configs:
|
||||||
if checked_config not in kwargs:
|
if checked_config not in kwargs:
|
||||||
|
@ -571,27 +586,28 @@ class WebInterface(object):
|
||||||
|
|
||||||
custom_where = []
|
custom_where = []
|
||||||
if user_id:
|
if user_id:
|
||||||
custom_where = [['session_history.user_id', user_id]]
|
custom_where.append(['session_history.user_id', user_id])
|
||||||
elif user:
|
elif user:
|
||||||
custom_where = [['session_history.user', user]]
|
custom_where.append(['session_history.user', user])
|
||||||
if 'rating_key' in kwargs:
|
if 'rating_key' in kwargs:
|
||||||
rating_key = kwargs.get('rating_key', "")
|
rating_key = kwargs.get('rating_key', "")
|
||||||
custom_where = [['session_history.rating_key', rating_key]]
|
custom_where.append(['session_history.rating_key', rating_key])
|
||||||
if 'parent_rating_key' in kwargs:
|
if 'parent_rating_key' in kwargs:
|
||||||
rating_key = kwargs.get('parent_rating_key', "")
|
rating_key = kwargs.get('parent_rating_key', "")
|
||||||
custom_where = [['session_history.parent_rating_key', rating_key]]
|
custom_where.append(['session_history.parent_rating_key', rating_key])
|
||||||
if 'grandparent_rating_key' in kwargs:
|
if 'grandparent_rating_key' in kwargs:
|
||||||
rating_key = kwargs.get('grandparent_rating_key', "")
|
rating_key = kwargs.get('grandparent_rating_key', "")
|
||||||
custom_where = [['session_history.grandparent_rating_key', rating_key]]
|
custom_where.append(['session_history.grandparent_rating_key', rating_key])
|
||||||
if 'start_date' in kwargs:
|
if 'start_date' in kwargs:
|
||||||
start_date = kwargs.get('start_date', "")
|
start_date = kwargs.get('start_date', "")
|
||||||
custom_where = [['strftime("%Y-%m-%d", datetime(date, "unixepoch", "localtime"))', start_date]]
|
custom_where.append(['strftime("%Y-%m-%d", datetime(date, "unixepoch", "localtime"))', start_date])
|
||||||
if 'reference_id' in kwargs:
|
if 'reference_id' in kwargs:
|
||||||
reference_id = kwargs.get('reference_id', "")
|
reference_id = kwargs.get('reference_id', "")
|
||||||
custom_where = [['session_history.reference_id', reference_id]]
|
custom_where.append(['session_history.reference_id', reference_id])
|
||||||
if 'media_type' in kwargs:
|
if 'media_type' in kwargs:
|
||||||
media_type = kwargs.get('media_type', "")
|
media_type = kwargs.get('media_type', "")
|
||||||
custom_where = [['session_history_metadata.media_type', media_type]]
|
if media_type != 'all':
|
||||||
|
custom_where.append(['session_history_metadata.media_type', media_type])
|
||||||
|
|
||||||
data_factory = datafactory.DataFactory()
|
data_factory = datafactory.DataFactory()
|
||||||
history = data_factory.get_history(kwargs=kwargs, custom_where=custom_where, grouping=grouping, watched_percent=watched_percent)
|
history = data_factory.get_history(kwargs=kwargs, custom_where=custom_where, grouping=grouping, watched_percent=watched_percent)
|
||||||
|
@ -652,6 +668,16 @@ class WebInterface(object):
|
||||||
else:
|
else:
|
||||||
return "Error sending tweet"
|
return "Error sending tweet"
|
||||||
|
|
||||||
|
@cherrypy.expose
|
||||||
|
def test_ifttt(self):
|
||||||
|
cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
|
||||||
|
event = notifiers.IFTTT()
|
||||||
|
result = event.test()
|
||||||
|
if result:
|
||||||
|
return "Notification successful."
|
||||||
|
else:
|
||||||
|
return "Error sending event."
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def osxnotifyregister(self, app):
|
def osxnotifyregister(self, app):
|
||||||
cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
|
cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
|
||||||
|
@ -697,6 +723,12 @@ class WebInterface(object):
|
||||||
try:
|
try:
|
||||||
pms_connect = pmsconnect.PmsConnect()
|
pms_connect = pmsconnect.PmsConnect()
|
||||||
result = pms_connect.get_current_activity()
|
result = pms_connect.get_current_activity()
|
||||||
|
|
||||||
|
data_factory = datafactory.DataFactory()
|
||||||
|
for session in result['sessions']:
|
||||||
|
if not session['ip_address']:
|
||||||
|
ip_address = data_factory.get_session_ip(session['session_key'])
|
||||||
|
session['ip_address'] = ip_address
|
||||||
except:
|
except:
|
||||||
return serve_template(templatename="current_activity.html", data=None)
|
return serve_template(templatename="current_activity.html", data=None)
|
||||||
|
|
||||||
|
@ -777,11 +809,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)
|
||||||
|
@ -1117,10 +1149,24 @@ class WebInterface(object):
|
||||||
logger.warn('Unable to retrieve data.')
|
logger.warn('Unable to retrieve data.')
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def get_server_prefs(self, **kwargs):
|
def get_server_friendly_name(self, **kwargs):
|
||||||
|
|
||||||
|
result = pmsconnect.get_server_friendly_name()
|
||||||
|
|
||||||
|
if result:
|
||||||
|
cherrypy.response.headers['Content-type'] = 'application/json'
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
logger.warn('Unable to retrieve data.')
|
||||||
|
|
||||||
|
@cherrypy.expose
|
||||||
|
def get_server_prefs(self, pref=None, **kwargs):
|
||||||
|
|
||||||
|
if pref:
|
||||||
pms_connect = pmsconnect.PmsConnect()
|
pms_connect = pmsconnect.PmsConnect()
|
||||||
result = pms_connect.get_server_prefs(output_format='json')
|
result = pms_connect.get_server_pref(pref=pref)
|
||||||
|
else:
|
||||||
|
result = None
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
cherrypy.response.headers['Content-type'] = 'application/json'
|
cherrypy.response.headers['Content-type'] = 'application/json'
|
||||||
|
@ -1352,8 +1398,7 @@ class WebInterface(object):
|
||||||
return json.dumps({'message': 'no data received'})
|
return json.dumps({'message': 'no data received'})
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def search(self, search_query=''):
|
def search(self, query=''):
|
||||||
query = search_query.replace('"', '')
|
|
||||||
|
|
||||||
return serve_template(templatename="search.html", title="Search", query=query)
|
return serve_template(templatename="search.html", title="Search", query=query)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue