Merge branch 'dev'

This commit is contained in:
JonnyWong16 2016-02-21 20:32:14 -08:00
commit 965fd170bd
15 changed files with 664 additions and 271 deletions

View file

@ -1,5 +1,18 @@
# Changelog
## v1.3.8 (2016-02-21)
* Fix: Regression unable to clear HTTP password.
* Fix: Remove media tags from script arguments for server notifications.
* Fix: Encode poster titles to UTF-8 for Imgur upload.
* Fix: Allow notifications to send without poster if Imgur upload fails.
* Add: Notification Logs table in the Logs tab.
* Add: Toggle in settings to enable posters in notifications. (Disabled by default.)
* Change: Save Imgur poster URL to database so upload is not needed every time.
* Change: Notify log in database to log each event as a separate entry.
* Change: Monitor remote access is unchecked if remote access is disabled on server.
## v1.3.7 (2016-02-20)
* Fix: Verifying server with SSL enabled.

View file

@ -3,10 +3,43 @@
## Issues
In case you read this because you are posting an issue, please take a minute and conside the things below. The issue tracker is not a support forum. It is primarily intended to submit bugs, improvements or feature requests. However, we are glad to help you, and make sure the problem is not caused by PlexPy, but don't expect step-by-step answers.
* Use the search function. Chances are that your problem is already discussed. Do not append to (closed) issues if your problem does not fit the discussion.
* Visit the [Troubleshooting](../../wiki/TroubleShooting) wiki first.
* Use [proper formatting](https://help.github.com/articles/github-flavored-markdown/). Paste your logs in code blocks.
* Close your issue if you resolved it.
##### Many issues can simply be solved by:
- Making sure you update to the latest version.
- Turning your device off and on again.
- Analyzing your logs, you just might find the solution yourself!
- Using the **search** function to see if this issue has already been reported/solved.
- Checking the [Wiki](https://github.com/drzoidberg33/plexpy/wiki) for
[ [Installation] ](https://github.com/drzoidberg33/plexpy/wiki/Installation) and
[ [FAQs] ](https://github.com/drzoidberg33/plexpy/wiki/Frequently-Asked-Questions-(FAQ)).
- For basic questions try asking on [Gitter](https://gitter.im/drzoidberg33/plexpy) or the [Plex Forums](https://forums.plex.tv/discussion/169591/plexpy-another-plex-monitoring-program) first before opening an issue.
##### If nothing has worked:
1. Open a new issue on the GitHub [issue tracker](http://github.com/drzoidberg33/plexpy/issues).
2. Provide a clear title to easily help identify your problem.
3. Use proper [markdown syntax](https://help.github.com/articles/github-flavored-markdown) to structure your post (i.e. code/log in code blocks).
4. Make sure you provide the following information:
- [ ] Version
- [ ] Branch
- [ ] Commit hash
- [ ] Operating system
- [ ] Python version
- [ ] What you did?
- [ ] What happened?
- [ ] What you expected?
- [ ] How can we reproduce your issue?
- [ ] What are your (relevant) settings?
- [ ] Include a link to your **FULL** (not just a few lines!) log file that has the error. Please use [Gist](http://gist.github.com) or [Pastebin](http://pastebin.com/).
5. Close your issue when it's solved! If you found the solution yourself please comment so that others benefit from it.
## Feature Requests
1. Search for similar existing 'issues', feature requests can be recognized by the blue `enhancement` label.
2. If a similar request exists, post a comment (+1, or add a new idea to the existing request).
3. If no similar requests exist, you can create a new one.
4. Provide a clear title to easily identify the feature request.
5. Tag your feature request with `[Feature Request]` so it can be identified easily.
## Pull Requests
If you think you can contribute code to the PlexPy repository, do not hesitate to submit a pull request.

View file

@ -50,9 +50,11 @@ This project is based on code from [Headphones](https://github.com/rembo10/headp
2. Provide a clear title to easily help identify your problem.
3. Use proper [markdown syntax](https://help.github.com/articles/github-flavored-markdown) to structure your post (i.e. code/log in code blocks).
4. Make sure you provide the following information:
- [ ] Version
- [ ] Branch
- [ ] Version/Commit hash
- [ ] Your operating system and python version
- [ ] Commit hash
- [ ] Operating system
- [ ] Python version
- [ ] What you did?
- [ ] What happened?
- [ ] What you expected?

View file

@ -0,0 +1,89 @@
notification_log_table_options = {
"destroy": true,
"serverSide": true,
"processing": false,
"pagingType": "bootstrap",
"order": [ 0, 'desc'],
"pageLength": 50,
"stateSave": false,
"language": {
"search":"Search: ",
"lengthMenu":"Show _MENU_ lines per page",
"emptyTable": "No log information available",
"info":"Showing _START_ to _END_ of _TOTAL_ lines",
"infoEmpty":"Showing 0 to 0 of 0 lines",
"infoFiltered":"(filtered from _MAX_ total lines)"},
"columnDefs": [
{
"targets": [0],
"data": "timestamp",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== '') {
$(td).html(moment(cellData, "X").format('YYYY-MM-DD HH:mm:ss'));
}
},
"width": "10%",
"className": "no-wrap hidden-xs"
},
{
"targets": [1],
"data": "agent_name",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== '') {
$(td).html(cellData);
}
},
"width": "7%",
"className": "no-wrap hidden-sm hidden-xs"
},
{
"targets": [2],
"data": "notify_action",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== '') {
$(td).html(cellData);
}
},
"width": "5%"
},
{
"targets": [3],
"data": "subject_text",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== '') {
$(td).html(cellData);
}
},
"width": "20%"
},
{
"targets": [4],
"data": "body_text",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== '') {
$(td).html(cellData);
}
},
"width": "38%"
},
{
"targets": [5],
"data": "script_args",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== '') {
$(td).html(cellData);
}
},
"width": "20%"
}
],
"drawCallback": function (settings) {
// Jump to top of page
//$('html,body').scrollTop(0);
$('#ajaxMsg').fadeOut();
},
"preDrawCallback": function(settings) {
var msg = "<i class='fa fa-refresh fa-spin'></i>&nbspFetching rows...";
showMsg(msg, false, false, 0)
}
}

View file

@ -30,6 +30,7 @@ from plexpy import helpers
<li role="presentation" class="active"><a id="plexpy-logs-btn" href="#tabs-1" aria-controls="tabs-1" role="tab" data-toggle="tab">PlexPy Logs</a></li>
<li role="presentation"><a id="plex-logs-btn" href="#tabs-2" aria-controls="tabs-2" role="tab" data-toggle="tab">Plex Media Server Logs</a></li>
<li role="presentation"><a id="plex-scanner-logs-btn" href="#tabs-3" aria-controls="tabs-3" role="tab" data-toggle="tab">Plex Media Scanner Logs</a></li>
<li role="presentation"><a id="notification-logs-btn" href="#tabs-4" aria-controls="tabs-4" role="tab" data-toggle="tab">Notification Logs</a></li>
</ul>
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="tabs-1">
@ -61,14 +62,28 @@ from plexpy import helpers
<div role="tabpanel" class="tab-pane" id="tabs-3">
<table class="display" id="plex_scanner_log_table" width="100%">
<thead>
<tr>
<th align='left' id="plex_scanner_timestamp">Timestamp</th>
<th align='left' id="plex_scanner_level">Level</th>
<th align='left' id="plex_scanner_message">Message</th>
</tr>
<tr>
<th align='left' id="plex_scanner_timestamp">Timestamp</th>
<th align='left' id="plex_scanner_level">Level</th>
<th align='left' id="plex_scanner_message">Message</th>
</tr>
</thead>
<tbody>
</tbody>
<tbody></tbody>
</table>
</div>
<div role="tabpanel" class="tab-pane" id="tabs-4">
<table class="display" id="notification_log_table" width="100%">
<thead>
<tr>
<th align='left' id="notification_timestamp">Timestamp</th>
<th align='left' id="notification_agent_name">Agent</th>
<th align='left' id="notification_action">Action</th>
<th align='left' id="notification_poster_url">Subject Text</th>
<th align='left' id="notification_poster_url">Body Text</th>
<th align='left' id="notification_poster_url">Script Args</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
@ -94,8 +109,10 @@ from plexpy import helpers
<script src="interfaces/default/js/jquery.dataTables.min.js"></script>
<script src="interfaces/default/js/dataTables.bootstrap.min.js"></script>
<script src="interfaces/default/js/dataTables.bootstrap.pagination.js"></script>
<script src="interfaces/default/js/moment-with-locale.js"></script>
<script src="interfaces/default/js/tables/logs.js"></script>
<script src="interfaces/default/js/tables/plex_logs.js"></script>
<script src="interfaces/default/js/tables/notification_logs.js"></script>
<script>
$(document).ready(function() {
@ -105,44 +122,63 @@ from plexpy import helpers
function LoadPlexPyLogs() {
log_table_options.ajax = {
"url": "getLog"
url: "getLog"
}
log_table = $('#log_table').DataTable(log_table_options);
}
function LoadPlexLogs() {
plex_log_table_options.ajax = {
"url": "get_plex_log?log_type=server"
url: "get_plex_log?log_type=server"
}
plex_log_table = $('#plex_log_table').DataTable(plex_log_table_options);
}
function LoadPlexScannerLogs() {
plex_log_table_options.ajax = {
"url": "get_plex_log?log_type=scanner"
url: "get_plex_log?log_type=scanner"
}
plex_scanner_log_table = $('#plex_scanner_log_table').DataTable(plex_log_table_options);
}
$("#plexpy-logs-btn").click(function() {
function LoadNotificationLogs() {
notification_log_table_options.ajax = {
url: "get_notification_log",
type: 'post',
data: function (d) {
return {
json_data: JSON.stringify(d)
};
}
}
notification_log_table = $('#notification_log_table').DataTable(notification_log_table_options);
}
$("#plexpy-logs-btn").click(function () {
$("#clear-logs").show();
LoadPlexPyLogs();
clearSearchButton('log_table', log_table);
});
$("#plex-logs-btn").click(function() {
$("#plex-logs-btn").click(function () {
$("#clear-logs").hide();
LoadPlexLogs();
clearSearchButton('plex_log_table', plex_log_table);
});
$("#plex-scanner-logs-btn").click(function() {
$("#plex-scanner-logs-btn").click(function () {
$("#clear-logs").hide();
LoadPlexScannerLogs();
clearSearchButton('plex_scanner_log_table', plex_scanner_log_table);
});
$("#clear-logs").click(function() {
$("#notification-logs-btn").click(function () {
$("#clear-logs").hide();
LoadNotificationLogs();
clearSearchButton('notification_log_table', notification_log_table);
});
$("#clear-logs").click(function () {
var r = confirm("Are you sure you want to clear the PlexPy log?");
if (r == true) {
window.location.href = "clearLogs";

View file

@ -64,13 +64,6 @@ from plexpy import helpers
</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">
@ -78,13 +71,6 @@ from plexpy import helpers
</label>
<p class="help-block">Trigger notification when the Plex Media Server cannot be reached internally.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" data-size="small" data-id="${data['id']}" data-config-name="${data['config_prefix']}_on_extup" ${helpers.checked(data['on_extup'])} class="toggle-switches">
Notify on Plex remote access back up
</label>
<p class="help-block">Trigger notification when the Plex Media Server can be reached externally after being down.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" data-size="small" data-id="${data['id']}" data-config-name="${data['config_prefix']}_on_intup" ${helpers.checked(data['on_intup'])} class="toggle-switches">
@ -92,6 +78,20 @@ from plexpy import helpers
</label>
<p class="help-block">Trigger notification when the Plex Media Server can be reached internally after being down.</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_extup" ${helpers.checked(data['on_extup'])} class="toggle-switches">
Notify on Plex remote access back up
</label>
<p class="help-block">Trigger notification when the Plex Media Server can be reached externally after being down.</p>
</div>
</div>
</div>
</div>

View file

@ -68,7 +68,7 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
<td><a class="no-highlight" href="${anon_url('https://github.com/drzoidberg33/plexpy/tree/%s' % plexpy.CONFIG.GIT_BRANCH)}">${plexpy.CONFIG.GIT_BRANCH}</a></td>
</tr>
<tr>
<td>Git Hash:</td>
<td>Git Commit Hash:</td>
<td><a class="no-highlight" href="${anon_url('https://github.com/drzoidberg33/plexpy/commit/%s' % plexpy.CURRENT_VERSION)}">${plexpy.CURRENT_VERSION}</a></td>
</tr>
% endif
@ -571,10 +571,9 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="get_file_sizes" name="get_file_sizes" value="1" ${config['get_file_sizes']}> Calculate Total File Sizes [experimental]
<input type="checkbox" id="get_file_sizes" name="get_file_sizes" value="1" ${config['get_file_sizes']}> Calculate Total File Sizes <span style="color: #eb8600; padding-left: 10px;">[experimental]</span>
</label>
<p class="help-block">Enable if you want PlexPy to calculate the total file size for TV Shows/Seasons and Artists/Albums on the media info tables.<br />
This is currently experimental.</p>
<p class="help-block">Enable if you want PlexPy to calculate the total file size for TV Shows/Seasons and Artists/Albums on the media info tables.</p>
</div>
<div class="form-group">
<label for="anon_redirect">Anonymous Redirect</label>
@ -610,10 +609,9 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
</div>
<div class="checkbox">
<label>
<input type="checkbox" class="monitor-settings" id="monitoring_use_websocket" name="monitoring_use_websocket" value="1" ${config['monitoring_use_websocket']}> Use Websocket (requires restart) [experimental]
<input type="checkbox" class="monitor-settings" id="monitoring_use_websocket" name="monitoring_use_websocket" value="1" ${config['monitoring_use_websocket']}> Use Websocket (requires restart) <span style="color: #eb8600; padding-left: 10px;">[experimental]</span>
</label>
<p class="help-block">Instead of polling the server at regular intervals let the server tell us when something happens.<br />
This is currently experimental.</p>
<p class="help-block">Instead of polling the server at regular intervals let the server tell PlexPy when something happens.</p>
</div>
<div class="checkbox">
<label>
@ -715,6 +713,13 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
</label>
</div>
<div class="checkbox" style="padding-top: 15px;">
<label>
<input type="checkbox" name="notify_upload_posters" id="notify_upload_posters" value="1" ${config['notify_upload_posters']}> Enable Posters in Notifications
</label>
<p class="help-block">Allow PlexPy to upload Plex poster to Imgur for notifications.</p>
</div>
<div class="padded-header">
<h3>Current Activity Notifications</h3>
</div>
@ -892,23 +897,6 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
</li>
</ul>
</li>
<li>
<div class="link"><i class="fa fa-server fa-fw"></i>&nbsp;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>&nbsp;Plex Server Down<i class="fa fa-chevron-down"></i></div>
<ul class="submenu">
@ -926,23 +914,6 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
</li>
</ul>
</li>
<li>
<div class="link"><i class="fa fa-server fa-fw"></i>&nbsp;Plex Remote Access Back Up<i class="fa fa-chevron-down"></i></div>
<ul class="submenu">
<li>
<div class="form-group">
<label for="notify_on_extup_subject_text">Subject Line</label>
<input class="form-control" type="text" id="notify_on_extup_subject_text" name="notify_on_extup_subject_text" value="${config['notify_on_extup_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_extup_body_text">Message Body</label>
<textarea class="form-control" id="notify_on_extup_body_text" name="notify_on_extup_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_extup_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>&nbsp;Plex Server Back Up<i class="fa fa-chevron-down"></i></div>
<ul class="submenu">
@ -960,6 +931,40 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
</li>
</ul>
</li>
<li>
<div class="link"><i class="fa fa-server fa-fw"></i>&nbsp;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>&nbsp;Plex Remote Access Back Up<i class="fa fa-chevron-down"></i></div>
<ul class="submenu">
<li>
<div class="form-group">
<label for="notify_on_extup_subject_text">Subject Line</label>
<input class="form-control" type="text" id="notify_on_extup_subject_text" name="notify_on_extup_subject_text" value="${config['notify_on_extup_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_extup_body_text">Message Body</label>
<textarea class="form-control" id="notify_on_extup_body_text" name="notify_on_extup_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_extup_body_text']}</textarea>
<p class="help-block">Set a custom body.</p>
</div>
</li>
</ul>
</li>
</ul>
<ul id="accordion-scripts" class="accordion list-unstyled">
<li>
@ -1482,8 +1487,7 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
</tr>
<tr>
<td><strong>{poster_url}</strong></td>
<td>A URL for the movie or TV show poster.
<p class="small-muted">(PMS agent must be Freebase or TheTVDB)</p></td>
<td>A URL for the movie, TV show, or album poster.</td>
</tr>
<tr>
<td><strong>{imdb_id}</strong></td>
@ -1947,6 +1951,7 @@ $(document).ready(function() {
if (data !== 'true') {
$("#remoteAccessCheck").html("Remote access must be enabled on your Plex Server. <a target='_blank' href='${anon_url('https://support.plex.tv/hc/en-us/articles/200484543-Enabling-Remote-Access-for-a-Server')}'>Click here</a> for help.");
$("#monitor_remote_access").attr("disabled", true);
$("#monitor_remote_access").attr("checked", false);
}
}
});

View file

@ -443,10 +443,10 @@ def dbcheck():
# notify_log table :: This is a table which logs notifications sent
c_db.execute(
'CREATE TABLE IF NOT EXISTS notify_log (id INTEGER PRIMARY KEY AUTOINCREMENT, '
'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, '
'on_pause INTEGER, on_resume INTEGER, on_buffer INTEGER, on_created INTEGER)'
'CREATE TABLE IF NOT EXISTS notify_log (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER, '
'session_key INTEGER, rating_key INTEGER, parent_rating_key INTEGER, grandparent_rating_key INTEGER, '
'user_id INTEGER, user TEXT, agent_id INTEGER, agent_name TEXT, notify_action TEXT, '
'subject_text TEXT, body_text TEXT, script_args TEXT, poster_url TEXT)'
)
# library_sections table :: This table keeps record of the servers library sections
@ -724,6 +724,53 @@ def dbcheck():
'ALTER TABLE notify_log ADD COLUMN on_created INTEGER'
)
# Upgrade notify_log table from earlier versions
try:
c_db.execute('SELECT poster_url 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 poster_url TEXT'
)
# Upgrade notify_log table from earlier versions (populate table with data from notify_log)
try:
c_db.execute('SELECT timestamp FROM notify_log')
except sqlite3.OperationalError:
logger.debug(u"Altering database. Updating database table notify_log.")
c_db.execute(
'CREATE TABLE IF NOT EXISTS notify_log_temp (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER, '
'session_key INTEGER, rating_key INTEGER, parent_rating_key INTEGER, grandparent_rating_key INTEGER, '
'user_id INTEGER, user TEXT, agent_id INTEGER, agent_name TEXT, notify_action TEXT, '
'subject_text TEXT, body_text TEXT, script_args TEXT, poster_url TEXT)'
)
c_db.execute(
'INSERT INTO notify_log_temp (session_key, rating_key, user_id, user, agent_id, agent_name, '
'poster_url, timestamp, notify_action) '
'SELECT session_key, rating_key, user_id, user, agent_id, agent_name, poster_url, timestamp, '
'notify_action FROM notify_log_temp '
'UNION ALL SELECT session_key, rating_key, user_id, user, agent_id, agent_name, poster_url, '
'on_play, "play" FROM notify_log WHERE on_play '
'UNION ALL SELECT session_key, rating_key, user_id, user, agent_id, agent_name, poster_url, '
'on_stop, "stop" FROM notify_log WHERE on_stop '
'UNION ALL SELECT session_key, rating_key, user_id, user, agent_id, agent_name, poster_url, '
'on_watched, "watched" FROM notify_log WHERE on_watched '
'UNION ALL SELECT session_key, rating_key, user_id, user, agent_id, agent_name, poster_url, '
'on_pause, "pause" FROM notify_log WHERE on_pause '
'UNION ALL SELECT session_key, rating_key, user_id, user, agent_id, agent_name, poster_url, '
'on_resume, "resume" FROM notify_log WHERE on_resume '
'UNION ALL SELECT session_key, rating_key, user_id, user, agent_id, agent_name, poster_url, '
'on_buffer, "buffer" FROM notify_log WHERE on_buffer '
'UNION ALL SELECT session_key, rating_key, user_id, user, agent_id, agent_name, poster_url, '
'on_created, "created" FROM notify_log WHERE on_created '
'ORDER BY timestamp ')
c_db.execute(
'DROP TABLE notify_log'
)
c_db.execute(
'ALTER TABLE notify_log_temp RENAME TO notify_log'
)
# Upgrade library_sections table from earlier versions (remove UNIQUE constraint on section_id)
try:
result = c_db.execute('SELECT SQL FROM sqlite_master WHERE type="table" AND name="library_sections"').fetchone()

View file

@ -191,6 +191,7 @@ _CONFIG_DEFINITIONS = {
'NMA_ON_EXTUP': (int, 'NMA', 0),
'NMA_ON_INTUP': (int, 'NMA', 0),
'NOTIFY_CONSECUTIVE': (int, 'Monitoring', 1),
'NOTIFY_UPLOAD_POSTERS': (int, 'Monitoring', 0),
'NOTIFY_RECENTLY_ADDED': (int, 'Monitoring', 0),
'NOTIFY_RECENTLY_ADDED_GRANDPARENT': (int, 'Monitoring', 0),
'NOTIFY_RECENTLY_ADDED_DELAY': (int, 'Monitoring', 60),

View file

@ -843,19 +843,45 @@ class DataFactory(object):
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'
if session_key:
try:
query = 'SELECT ip_address FROM sessions WHERE session_key = %d' % int(session_key)
result = monitor_db.select(query)
except Exception as e:
logger.warn(u"PlexPy DataFactory :: Unable to execute database query for get_session_ip: %s." % e)
return ip_address
else:
return ip_address
for item in result:
ip_address = item['ip_address']
return ip_address
def get_poster_url(self, rating_key=''):
monitor_db = database.MonitorDatabase()
poster_url = ''
if rating_key:
try:
query = 'SELECT id, poster_url FROM notify_log ' \
'WHERE rating_key = %d OR parent_rating_key = %d OR grandparent_rating_key = %d ' \
'ORDER BY id DESC LIMIT 1' % (int(rating_key), int(rating_key), int(rating_key))
result = monitor_db.select(query)
except Exception as e:
logger.warn(u"PlexPy DataFactory :: Unable to execute database query for get_poster_url: %s." % e)
return poster_url
else:
return poster_url
for item in result:
poster_url = item['poster_url']
return poster_url
def get_search_query(self, rating_key=''):
monitor_db = database.MonitorDatabase()
@ -1094,4 +1120,72 @@ class DataFactory(object):
metadata['studio'],
old_rating_key]
monitor_db.action(query=query, args=args)
monitor_db.action(query=query, args=args)
def get_notification_log(self, kwargs=None):
data_tables = datatables.DataTables()
columns = ['notify_log.id',
'notify_log.timestamp',
'notify_log.session_key',
'notify_log.rating_key',
'notify_log.user_id',
'notify_log.user',
'notify_log.agent_id',
'notify_log.agent_name',
'notify_log.notify_action',
'notify_log.subject_text',
'notify_log.body_text',
'notify_log.script_args',
'notify_log.poster_url',
]
try:
query = data_tables.ssp_query(table_name='notify_log',
columns=columns,
custom_where=[],
group_by=[],
join_types=[],
join_tables=[],
join_evals=[],
kwargs=kwargs)
except Exception as e:
logger.warn(u"PlexPy DataFactory :: Unable to execute database query for get_notification_log: %s." % e)
return {'recordsFiltered': 0,
'recordsTotal': 0,
'draw': 0,
'data': 'null',
'error': 'Unable to execute database query.'}
notifications = query['result']
rows = []
for item in notifications:
if item['body_text']:
body_text = item['body_text'].replace('\r\n', '<br />').replace('\n', '<br />')
else:
body_text = ''
row = {'id': item['id'],
'timestamp': item['timestamp'],
'session_key': item['session_key'],
'rating_key': item['rating_key'],
'user_id': item['user_id'],
'user': item['user'],
'agent_id': item['agent_id'],
'agent_name': item['agent_name'],
'notify_action': item['notify_action'],
'subject_text': item['subject_text'],
'body_text': body_text,
'script_args': item['script_args'],
'poster_url': item['poster_url']
}
rows.append(row)
dict = {'recordsFiltered': query['filteredCount'],
'recordsTotal': query['totalCount'],
'data': rows,
'draw': query['draw']
}
return dict

View file

@ -543,19 +543,22 @@ def uploadToImgur(imgPath, imgTitle=''):
data = {'type': 'base64',
'image': base64.b64encode(img)}
if imgTitle:
data['title'] = imgTitle
data['name'] = imgTitle + '.jpg'
data['title'] = imgTitle.encode('utf-8')
data['name'] = imgTitle.encode('utf-8') + '.jpg'
request = urllib2.Request('https://api.imgur.com/3/image', headers=headers, data=urllib.urlencode(data))
response = urllib2.urlopen(request)
response = json.loads(response.read())
try:
request = urllib2.Request('https://api.imgur.com/3/image', headers=headers, data=urllib.urlencode(data))
response = urllib2.urlopen(request)
response = json.loads(response.read())
if response.get('status') == 200:
logger.debug(u"PlexPy Helpers :: Image uploaded to Imgur.")
img_url = response.get('data').get('link', '')
elif response.get('status') >= 400 and response.get('status') < 500:
logger.warn(u"PlexPy Helpers :: Unable to upload image to Imgur: %s" % response.reason)
else:
logger.warn(u"PlexPy Helpers :: Unable to upload image to Imgur.")
if response.get('status') == 200:
logger.debug(u"PlexPy Helpers :: Image uploaded to Imgur.")
img_url = response.get('data').get('link', '')
elif response.get('status') >= 400 and response.get('status') < 500:
logger.warn(u"PlexPy Helpers :: Unable to upload image to Imgur: %s" % response.reason)
else:
logger.warn(u"PlexPy Helpers :: Unable to upload image to Imgur.")
except urllib2.HTTPError as e:
logger.warn(u"PlexPy Helpers :: Unable to upload image to Imgur: %s" % e)
return img_url

View file

@ -20,7 +20,7 @@ import re
import time
import urllib
from plexpy import logger, config, notifiers, database, helpers, plextv, pmsconnect
from plexpy import logger, config, notifiers, database, helpers, plextv, pmsconnect, datafactory
import plexpy
@ -50,168 +50,201 @@ def notify(stream_data=None, notify_action=None):
for agent in notifiers.available_notification_agents():
if agent['on_play'] and notify_action == 'play':
# Build and send notification
notify_strings = build_notify_text(session=stream_data, state=notify_action)
notify_strings, metadata = build_notify_text(session=stream_data, notify_action=notify_action)
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
notify_action=notify_action,
script_args=notify_strings[2],
metadata=notify_strings[3])
notify_action=notify_action,
metadata=metadata)
# Set the notification state in the db
set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
set_notify_state(session=stream_data,
notify_action=notify_action,
agent_info=agent,
notify_strings=notify_strings,
metadata=metadata)
elif agent['on_stop'] and notify_action == 'stop' \
and (plexpy.CONFIG.NOTIFY_CONSECUTIVE or progress_percent < plexpy.CONFIG.NOTIFY_WATCHED_PERCENT):
# Build and send notification
notify_strings = build_notify_text(session=stream_data, state=notify_action)
notify_strings, metadata = build_notify_text(session=stream_data, notify_action=notify_action)
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
notify_action=notify_action,
script_args=notify_strings[2],
metadata=notify_strings[3])
notify_action=notify_action,
metadata=metadata)
set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
# Set the notification state in the db
set_notify_state(session=stream_data,
notify_action=notify_action,
agent_info=agent,
notify_strings=notify_strings,
metadata=metadata)
elif agent['on_pause'] and notify_action == 'pause' \
and (plexpy.CONFIG.NOTIFY_CONSECUTIVE or progress_percent < 99):
# Build and send notification
notify_strings = build_notify_text(session=stream_data, state=notify_action)
notify_strings, metadata = build_notify_text(session=stream_data, notify_action=notify_action)
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
notify_action=notify_action,
script_args=notify_strings[2],
metadata=notify_strings[3])
notify_action=notify_action,
metadata=metadata)
set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
# Set the notification state in the db
set_notify_state(session=stream_data,
notify_action=notify_action,
agent_info=agent,
notify_strings=notify_strings,
metadata=metadata)
elif agent['on_resume'] and notify_action == 'resume' \
and (plexpy.CONFIG.NOTIFY_CONSECUTIVE or progress_percent < 99):
# Build and send notification
notify_strings = build_notify_text(session=stream_data, state=notify_action)
notify_strings, metadata = build_notify_text(session=stream_data, notify_action=notify_action)
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
notify_action=notify_action,
script_args=notify_strings[2],
metadata=notify_strings[3])
notify_action=notify_action,
metadata=metadata)
set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
# Set the notification state in the db
set_notify_state(session=stream_data,
notify_action=notify_action,
agent_info=agent,
notify_strings=notify_strings,
metadata=metadata)
elif agent['on_buffer'] and notify_action == 'buffer':
# Build and send notification
notify_strings = build_notify_text(session=stream_data, state=notify_action)
notify_strings, metadata = build_notify_text(session=stream_data, notify_action=notify_action)
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
script_args=notify_strings[2],
notify_action=notify_action,
script_args=notify_strings[2])
metadata=metadata)
set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
# Set the notification state in the db
set_notify_state(session=stream_data,
notify_action=notify_action,
agent_info=agent,
notify_strings=notify_strings,
metadata=metadata)
elif agent['on_watched'] and notify_action == 'watched':
# Get the current states for notifications from our db
notify_states = get_notify_state(session=stream_data)
# If there is nothing in the notify_log for our agent id but it is enabled we should notify
if not any(d['agent_id'] == agent['id'] for d in notify_states):
if not any(d['agent_id'] == agent['id'] and d['notify_action'] == notify_action for d in notify_states):
# Build and send notification
notify_strings = build_notify_text(session=stream_data, state=notify_action)
notify_strings, metadata = build_notify_text(session=stream_data, notify_action=notify_action)
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
notify_action=notify_action,
script_args=notify_strings[2],
metadata=notify_strings[3])
notify_action=notify_action,
metadata=metadata)
# Set the notification state in the db
set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
else:
# Check in our notify log if the notification has already been sent
for notify_state in notify_states:
if not notify_state['on_watched'] and (notify_state['agent_id'] == agent['id']):
# Build and send notification
notify_strings = build_notify_text(session=stream_data, state=notify_action)
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
notify_action=notify_action,
script_args=notify_strings[2],
metadata=notify_strings[3])
# Set the notification state in the db
set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
set_notify_state(session=stream_data,
notify_action=notify_action,
agent_info=agent,
notify_strings=notify_strings,
metadata=metadata)
elif (stream_data['media_type'] == 'track' and plexpy.CONFIG.MUSIC_NOTIFY_ENABLE):
for agent in notifiers.available_notification_agents():
if agent['on_play'] and notify_action == 'play':
# Build and send notification
notify_strings = build_notify_text(session=stream_data, state=notify_action)
notify_strings, metadata = build_notify_text(session=stream_data, notify_action=notify_action)
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
notify_action=notify_action,
script_args=notify_strings[2],
metadata=notify_strings[3])
notify_action=notify_action,
metadata=metadata)
# Set the notification state in the db
set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
set_notify_state(session=stream_data,
notify_action=notify_action,
agent_info=agent,
notify_strings=notify_strings,
metadata=metadata)
elif agent['on_stop'] and notify_action == 'stop':
# Build and send notification
notify_strings = build_notify_text(session=stream_data, state=notify_action)
notify_strings, metadata = build_notify_text(session=stream_data, notify_action=notify_action)
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
notify_action=notify_action,
script_args=notify_strings[2],
metadata=notify_strings[3])
notify_action=notify_action,
metadata=metadata)
# Set the notification state in the db
set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
set_notify_state(session=stream_data,
notify_action=notify_action,
agent_info=agent,
notify_strings=notify_strings,
metadata=metadata)
elif agent['on_pause'] and notify_action == 'pause':
# Build and send notification
notify_strings = build_notify_text(session=stream_data, state=notify_action)
notify_strings, metadata = build_notify_text(session=stream_data, notify_action=notify_action)
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
notify_action=notify_action,
script_args=notify_strings[2],
metadata=notify_strings[3])
notify_action=notify_action,
metadata=metadata)
# Set the notification state in the db
set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
set_notify_state(session=stream_data,
notify_action=notify_action,
agent_info=agent,
notify_strings=notify_strings,
metadata=metadata)
elif agent['on_resume'] and notify_action == 'resume':
# Build and send notification
notify_strings = build_notify_text(session=stream_data, state=notify_action)
notify_strings, metadata = build_notify_text(session=stream_data, notify_action=notify_action)
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
notify_action=notify_action,
script_args=notify_strings[2],
metadata=notify_strings[3])
notify_action=notify_action,
metadata=metadata)
# Set the notification state in the db
set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
set_notify_state(session=stream_data,
notify_action=notify_action,
agent_info=agent,
notify_strings=notify_strings,
metadata=metadata)
elif agent['on_buffer'] and notify_action == 'buffer':
# Build and send notification
notify_strings = build_notify_text(session=stream_data, state=notify_action)
notify_strings, metadata = build_notify_text(session=stream_data, notify_action=notify_action)
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
notify_action=notify_action,
script_args=notify_strings[2],
metadata=notify_strings[3])
notify_action=notify_action,
metadata=metadata)
# Set the notification state in the db
set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
set_notify_state(session=stream_data,
notify_action=notify_action,
agent_info=agent,
notify_strings=notify_strings,
metadata=metadata)
elif stream_data['media_type'] == 'clip':
pass
@ -227,133 +260,142 @@ def notify_timeline(timeline_data=None, notify_action=None):
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)
notify_strings, metadata = build_notify_text(timeline=timeline_data, notify_action=notify_action)
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
notify_action=notify_action,
script_args=notify_strings[2],
metadata=notify_strings[3])
notify_action=notify_action,
metadata=metadata)
# Set the notification state in the db
set_notify_state(session=timeline_data, state=notify_action, agent_info=agent)
set_notify_state(session=stream_data,
notify_action=notify_action,
agent_info=agent,
notify_strings=notify_strings,
metadata=metadata)
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)
notify_strings = build_server_notify_text(notify_action=notify_action)
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
notify_action=notify_action,
script_args=notify_strings[2])
script_args=notify_strings[2],
notify_action=notify_action)
# Set the notification state in the db
set_notify_state(session={},
notify_action=notify_action,
agent_info=agent,
notify_strings=notify_strings,
metadata={})
if agent['on_intdown'] and notify_action == 'intdown':
# Build and send notification
notify_strings = build_server_notify_text(state=notify_action)
notify_strings = build_server_notify_text(notify_action=notify_action)
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
notify_action=notify_action,
script_args=notify_strings[2])
script_args=notify_strings[2],
notify_action=notify_action)
# Set the notification state in the db
set_notify_state(session={},
notify_action=notify_action,
agent_info=agent,
notify_strings=notify_strings,
metadata={})
if agent['on_extup'] and notify_action == 'extup':
# Build and send notification
notify_strings = build_server_notify_text(state=notify_action)
notify_strings = build_server_notify_text(notify_action=notify_action)
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
notify_action=notify_action,
script_args=notify_strings[2])
script_args=notify_strings[2],
notify_action=notify_action)
# Set the notification state in the db
set_notify_state(session={},
notify_action=notify_action,
agent_info=agent,
notify_strings=notify_strings,
metadata={})
if agent['on_intup'] and notify_action == 'intup':
# Build and send notification
notify_strings = build_server_notify_text(state=notify_action)
notify_strings = build_server_notify_text(notify_action=notify_action)
notifiers.send_notification(agent_id=agent['id'],
subject=notify_strings[0],
body=notify_strings[1],
notify_action=notify_action,
script_args=notify_strings[2])
script_args=notify_strings[2],
notify_action=notify_action)
# Set the notification state in the db
set_notify_state(session={},
notify_action=notify_action,
agent_info=agent,
notify_strings=notify_strings,
metadata={})
else:
logger.debug(u"PlexPy NotificationHandler :: Notify timeline called but incomplete data received.")
def get_notify_state(session):
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 timestamp, notify_action, agent_id '
'FROM notify_log '
'WHERE session_key = ? '
'AND rating_key = ? '
'AND user = ? '
'AND user_id = ? '
'ORDER BY id DESC',
args=[session['session_key'], session['rating_key'], session['user']])
args=[session['session_key'], session['rating_key'], session['user_id']])
notify_states = []
for item in result:
notify_state = {'on_play': item['on_play'],
'on_stop': item['on_stop'],
'on_pause': item['on_pause'],
'on_resume': item['on_resume'],
'on_buffer': item['on_buffer'],
'on_watched': item['on_watched'],
notify_state = {'timestamp': item['timestamp'],
'notify_action': item['notify_action'],
'agent_id': item['agent_id']}
notify_states.append(notify_state)
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['on_created'],
'agent_id': item['agent_id']}
notify_states.append(notify_state)
def set_notify_state(session, notify_action, agent_info, notify_strings, metadata):
return notify_states
def set_notify_state(session, state, agent_info):
if session and state and agent_info:
if notify_action and agent_info:
monitor_db = database.MonitorDatabase()
if state == 'play':
values = {'on_play': int(time.time())}
elif state == 'stop':
values = {'on_stop': int(time.time())}
elif state == 'pause':
values = {'on_pause': int(time.time())}
elif state == 'resume':
values = {'on_resume': int(time.time())}
elif state == 'buffer':
values = {'on_buffer': int(time.time())}
elif state == 'watched':
values = {'on_watched': int(time.time())}
elif state == 'created':
values = {'on_created': int(time.time())}
if notify_strings[2]:
script_args = '[' + ', '.join(notify_strings[2]) + ']'
else:
return
script_args = None
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'],
'rating_key': session['rating_key'],
'user_id': session['user_id'],
'user': session['user'],
'agent_id': agent_info['id'],
'agent_name': agent_info['name']}
keys = {'timestamp': int(time.time()),
'session_key': session.get('session_key', None),
'rating_key': session.get('rating_key', None),
'user_id': session.get('user_id', None),
'agent_id': agent_info['id'],
'notify_action': notify_action}
values = {'parent_rating_key': session.get('parent_rating_key', None),
'grandparent_rating_key': session.get('grandparent_rating_key', None),
'user': session.get('user', None),
'agent_name': agent_info['name'],
'subject_text': notify_strings[0],
'body_text': notify_strings[1],
'script_args': script_args,
'poster_url': metadata.get('poster_url', None)}
monitor_db.upsert(table_name='notify_log', key_dict=keys, value_dict=values)
else:
logger.error(u"PlexPy NotificationHandler :: Unable to set notify state.")
def build_notify_text(session=None, timeline=None, state=None):
def build_notify_text(session=None, timeline=None, notify_action=None):
# Get time formats
date_format = plexpy.CONFIG.DATE_FORMAT.replace('Do','').replace('zz','')
time_format = plexpy.CONFIG.TIME_FORMAT.replace('Do','').replace('zz','')
@ -459,7 +501,7 @@ def build_notify_text(session=None, timeline=None, state=None):
else:
transcode_decision = 'Direct Play'
if state != 'play':
if notify_action != 'play':
stream_duration = int((time.time() -
helpers.cast_to_int(session.get('started', 0)) -
helpers.cast_to_int(session.get('paused_counter', 0))) / 60)
@ -504,22 +546,36 @@ def build_notify_text(session=None, timeline=None, state=None):
if metadata['media_type'] == 'movie' or metadata['media_type'] == 'show' or metadata['media_type'] == 'artist':
thumb = metadata['thumb']
poster_key = metadata['rating_key']
poster_title = metadata['title']
elif metadata['media_type'] == 'episode':
thumb = metadata['grandparent_thumb']
poster_key = metadata['grandparent_rating_key']
poster_title = metadata['grandparent_title']
elif metadata['media_type'] == 'track':
thumb = metadata['parent_thumb']
poster_key = metadata['parent_rating_key']
poster_title = metadata['parent_title']
else:
thumb = None
if thumb:
# Retrieve the poster from Plex and cache to file
urllib.urlretrieve(plexpy.CONFIG.PMS_URL + thumb + '?X-Plex-Token=' + plexpy.CONFIG.PMS_TOKEN,
os.path.join(plexpy.CONFIG.CACHE_DIR, 'cache-poster.jpg'))
# Upload thumb to Imgur and get link
metadata['poster_url'] = helpers.uploadToImgur(os.path.join(plexpy.CONFIG.CACHE_DIR, 'cache-poster.jpg'), full_title)
# Try to retrieve a poster_url from the database
data_factory = datafactory.DataFactory()
poster_url = data_factory.get_poster_url(rating_key=poster_key)
# If no previous poster_url
if not poster_url and plexpy.CONFIG.NOTIFY_UPLOAD_POSTERS:
# Retrieve the poster from Plex and cache to file
urllib.urlretrieve(plexpy.CONFIG.PMS_URL + thumb + '?X-Plex-Token=' + plexpy.CONFIG.PMS_TOKEN,
os.path.join(plexpy.CONFIG.CACHE_DIR, 'cache-poster.jpg'))
# Upload thumb to Imgur and get link
poster_url = helpers.uploadToImgur(os.path.join(plexpy.CONFIG.CACHE_DIR, 'cache-poster.jpg'), poster_title)
metadata['poster_url'] = poster_url
# Fix metadata params for notify recently added grandparent
if state == 'created' and plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT:
if notify_action == 'created' and plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT:
show_name = metadata['title']
episode_name = ''
artist_name = metadata['title']
@ -535,7 +591,7 @@ def build_notify_text(session=None, timeline=None, state=None):
available_params = {# Global paramaters
'server_name': server_name,
'server_uptime': server_uptime,
'action': state.title(),
'action': notify_action.title(),
'datestamp': arrow.now().format(date_format),
'timestamp': arrow.now().format(time_format),
# Stream parameters
@ -628,7 +684,7 @@ def build_notify_text(session=None, timeline=None, state=None):
except Exception as e:
logger.error(u"PlexPy Notifier :: Unable to parse custom script arguments %s. Using fallback." % e)
if state == 'play':
if notify_action == 'play':
# Default body text
body_text = '%s (%s) started playing %s' % (session['friendly_name'],
session['player'],
@ -649,10 +705,10 @@ def build_notify_text(session=None, timeline=None, state=None):
except:
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification body. Using fallback.")
return [subject_text, body_text, script_args, metadata]
return [subject_text, body_text, script_args], metadata
else:
return [subject_text, body_text, script_args, metadata]
elif state == 'stop':
return [subject_text, body_text, script_args], metadata
elif notify_action == 'stop':
# Default body text
body_text = '%s (%s) has stopped %s' % (session['friendly_name'],
session['player'],
@ -673,10 +729,10 @@ def build_notify_text(session=None, timeline=None, state=None):
except:
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification body. Using fallback.")
return [subject_text, body_text, script_args, metadata]
return [subject_text, body_text, script_args], metadata
else:
return [subject_text, body_text, script_args, metadata]
elif state == 'pause':
return [subject_text, body_text, script_args], metadata
elif notify_action == 'pause':
# Default body text
body_text = '%s (%s) has paused %s' % (session['friendly_name'],
session['player'],
@ -697,10 +753,10 @@ def build_notify_text(session=None, timeline=None, state=None):
except:
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification body. Using fallback.")
return [subject_text, body_text, script_args, metadata]
return [subject_text, body_text, script_args], metadata
else:
return [subject_text, body_text, script_args, metadata]
elif state == 'resume':
return [subject_text, body_text, script_args], metadata
elif notify_action == 'resume':
# Default body text
body_text = '%s (%s) has resumed %s' % (session['friendly_name'],
session['player'],
@ -721,10 +777,10 @@ def build_notify_text(session=None, timeline=None, state=None):
except:
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification body. Using fallback.")
return [subject_text, body_text, script_args, metadata]
return [subject_text, body_text, script_args], metadata
else:
return [subject_text, body_text, script_args, metadata]
elif state == 'buffer':
return [subject_text, body_text, script_args], metadata
elif notify_action == 'buffer':
# Default body text
body_text = '%s (%s) is buffering %s' % (session['friendly_name'],
session['player'],
@ -745,10 +801,10 @@ def build_notify_text(session=None, timeline=None, state=None):
except:
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification body. Using fallback.")
return [subject_text, body_text, script_args, metadata]
return [subject_text, body_text, script_args], metadata
else:
return [subject_text, body_text, script_args, metadata]
elif state == 'watched':
return [subject_text, body_text, script_args], metadata
elif notify_action == 'watched':
# Default body text
body_text = '%s (%s) has watched %s' % (session['friendly_name'],
session['player'],
@ -769,10 +825,10 @@ def build_notify_text(session=None, timeline=None, state=None):
except:
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification body. Using fallback.")
return [subject_text, body_text, script_args, metadata]
return [subject_text, body_text, script_args], metadata
else:
return [subject_text, body_text, script_args, metadata]
elif state == 'created':
return [subject_text, body_text, script_args], metadata
elif notify_action == 'created':
# Default body text
body_text = '%s was recently added to Plex.' % full_title
@ -791,14 +847,14 @@ def build_notify_text(session=None, timeline=None, state=None):
except:
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification body. Using fallback.")
return [subject_text, body_text, script_args, metadata]
return [subject_text, body_text, script_args], metadata
else:
return [subject_text, body_text, script_args, metadata]
return [subject_text, body_text, script_args], metadata
else:
return None
def build_server_notify_text(state=None):
def build_server_notify_text(notify_action=None):
# Get time formats
date_format = plexpy.CONFIG.DATE_FORMAT.replace('Do','').replace('zz','')
time_format = plexpy.CONFIG.TIME_FORMAT.replace('Do','').replace('zz','')
@ -817,6 +873,8 @@ def build_server_notify_text(state=None):
logger.error(u"PlexPy NotificationHandler :: Unable to retrieve server uptime.")
server_uptime = 'N/A'
pattern = re.compile('\n*<tv>[^>]+.</tv>\n*|\n*<movie>[^>]+.</movie>\n*|\n*?<music>[^>]+.</music>\n*', re.IGNORECASE | re.DOTALL)
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
@ -825,12 +883,12 @@ def build_server_notify_text(state=None):
on_extup_body = plexpy.CONFIG.NOTIFY_ON_EXTUP_BODY_TEXT
on_intup_subject = plexpy.CONFIG.NOTIFY_ON_INTUP_SUBJECT_TEXT
on_intup_body = plexpy.CONFIG.NOTIFY_ON_INTUP_BODY_TEXT
script_args_text = plexpy.CONFIG.NOTIFY_SCRIPTS_ARGS_TEXT
script_args_text = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_SCRIPTS_ARGS_TEXT))
available_params = {# Global paramaters
'server_name': server_name,
'server_uptime': server_uptime,
'action': state.title(),
'action': notify_action.title(),
'datestamp': arrow.now().format(date_format),
'timestamp': arrow.now().format(time_format)}
@ -848,7 +906,7 @@ def build_server_notify_text(state=None):
except Exception as e:
logger.error(u"PlexPy Notifier :: Unable to parse custom script arguments %s. Using fallback." % e)
if state == 'extdown':
if notify_action == 'extdown':
# Default body text
body_text = 'The Plex Media Server remote access is down.'
@ -871,7 +929,7 @@ def build_server_notify_text(state=None):
else:
return [subject_text, body_text, script_args]
elif state == 'intdown':
elif notify_action == 'intdown':
# Default body text
body_text = 'The Plex Media Server is down.'
@ -893,7 +951,7 @@ def build_server_notify_text(state=None):
return [subject_text, body_text, script_args]
else:
return [subject_text, body_text, script_args]
if state == 'extup':
if notify_action == 'extup':
# Default body text
body_text = 'The Plex Media Server remote access is back up.'
@ -915,7 +973,7 @@ def build_server_notify_text(state=None):
return [subject_text, body_text, script_args]
else:
return [subject_text, body_text, script_args]
elif state == 'intup':
elif notify_action == 'intup':
# Default body text
body_text = 'The Plex Media Server is back up.'

View file

@ -1,2 +1,2 @@
PLEXPY_VERSION = "master"
PLEXPY_RELEASE_VERSION = "1.3.7"
PLEXPY_RELEASE_VERSION = "1.3.8"

View file

@ -39,11 +39,13 @@ def run():
if plexpy.CONFIG.PMS_SSL and plexpy.CONFIG.PMS_URL[:5] == 'https':
uri = plexpy.CONFIG.PMS_URL.replace('https://', 'wss://') + '/:/websockets/notifications'
secure = ' secure'
else:
uri = 'ws://%s:%s/:/websockets/notifications' % (
plexpy.CONFIG.PMS_IP,
plexpy.CONFIG.PMS_PORT
)
secure = ''
# Set authentication token (if one is available)
if plexpy.CONFIG.PMS_TOKEN:
@ -55,7 +57,7 @@ def run():
# Try an open the websocket connection - if it fails after 15 retries fallback to polling
while not ws_connected and reconnects <= 15:
try:
logger.info(u'PlexPy WebSocket :: Opening websocket, connection attempt %s.' % str(reconnects + 1))
logger.info(u'PlexPy WebSocket :: Opening%s websocket, connection attempt %s.' % (secure, str(reconnects + 1)))
ws = create_connection(uri)
reconnects = 0
ws_connected = True

View file

@ -1082,6 +1082,15 @@ class WebInterface(object):
cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps(log_lines)
@cherrypy.expose
@addtoapi()
def get_notification_log(self, **kwargs):
data_factory = datafactory.DataFactory()
notifications = data_factory.get_notification_log(kwargs=kwargs)
cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps(notifications)
@cherrypy.expose
def clearLogs(self):
plexpy.LOG_LIST = []
@ -1183,6 +1192,7 @@ class WebInterface(object):
"logging_ignore_interval": plexpy.CONFIG.LOGGING_IGNORE_INTERVAL,
"pms_is_remote": checked(plexpy.CONFIG.PMS_IS_REMOTE),
"notify_consecutive": checked(plexpy.CONFIG.NOTIFY_CONSECUTIVE),
"notify_upload_posters": checked(plexpy.CONFIG.NOTIFY_UPLOAD_POSTERS),
"notify_recently_added": checked(plexpy.CONFIG.NOTIFY_RECENTLY_ADDED),
"notify_recently_added_grandparent": checked(plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT),
"notify_recently_added_delay": plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_DELAY,
@ -1235,7 +1245,7 @@ class WebInterface(object):
"tv_notify_on_pause", "movie_notify_on_pause", "music_notify_on_pause",
"refresh_libraries_on_startup", "refresh_users_on_startup",
"ip_logging_enable", "movie_logging_enable", "tv_logging_enable", "music_logging_enable",
"pms_is_remote", "home_stats_type", "group_history_tables", "notify_consecutive",
"pms_is_remote", "home_stats_type", "group_history_tables", "notify_consecutive", "notify_upload_posters",
"notify_recently_added", "notify_recently_added_grandparent", "monitor_remote_access", "get_file_sizes"
]
for checked_config in checked_configs:
@ -1245,7 +1255,7 @@ class WebInterface(object):
# If http password exists in config, do not overwrite when blank value received
if kwargs.get('http_password'):
if kwargs['http_password'].strip() == '' and plexpy.CONFIG.HTTP_PASSWORD != '':
if kwargs['http_password'] == ' ' and plexpy.CONFIG.HTTP_PASSWORD != '':
kwargs['http_password'] = plexpy.CONFIG.HTTP_PASSWORD
for plain_config, use_config in [(x[4:], x) for x in kwargs if x.startswith('use_')]: