Compare commits
78 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
cbf3488de9 | ||
![]() |
c72314fb71 | ||
![]() |
c4af6feb92 | ||
![]() |
08537c1d69 | ||
![]() |
425da82f5f | ||
![]() |
2cfbf7c39a | ||
![]() |
d4eed9f8fd | ||
![]() |
fe10170826 | ||
![]() |
8dc3b0b250 | ||
![]() |
75da1220af | ||
![]() |
37a2c3c631 | ||
![]() |
5c5722714d | ||
![]() |
2ba529f9e3 | ||
![]() |
fd760ff015 | ||
![]() |
daab1d917b | ||
![]() |
4c3a63a7e1 | ||
![]() |
7cc58b84da | ||
![]() |
2bac4ac1a7 | ||
![]() |
c5b2b86786 | ||
![]() |
5652a2b6c2 | ||
![]() |
bd19f543a2 | ||
![]() |
cc1e888227 | ||
![]() |
d73e379dcf | ||
![]() |
0569abd00d | ||
![]() |
7f5d9bec87 | ||
![]() |
b39e7bbb6d | ||
![]() |
70270a8e3b | ||
![]() |
efdc050a28 | ||
![]() |
e8a65df7f0 | ||
![]() |
59628a72fb | ||
![]() |
a4d6c6c0d8 | ||
![]() |
bea82c6640 | ||
![]() |
1ba3bdfbda | ||
![]() |
98b4000bc0 | ||
![]() |
14f6824931 | ||
![]() |
795d7d0a93 | ||
![]() |
673fa2b556 | ||
![]() |
0e2504fc78 | ||
![]() |
2afca9f2b4 | ||
![]() |
464d2a541d | ||
![]() |
fa8c5e0982 | ||
![]() |
5e15884d8f | ||
![]() |
b5e9ff3b4e | ||
![]() |
fed7d4cc34 | ||
![]() |
d7ab066ff8 | ||
![]() |
4100917016 | ||
![]() |
5d2c1ffb88 | ||
![]() |
ddb0f198a9 | ||
![]() |
13438e3e25 | ||
![]() |
05a410b327 | ||
![]() |
23fa64d289 | ||
![]() |
1920c9b7e3 | ||
![]() |
eedd0d9c07 | ||
![]() |
9ef389d335 | ||
![]() |
6542997520 | ||
![]() |
a58b2e2038 | ||
![]() |
6860e348dc | ||
![]() |
5e094e7597 | ||
![]() |
2f2cb8386b | ||
![]() |
965fd170bd | ||
![]() |
2610d29b60 | ||
![]() |
064131c842 | ||
![]() |
00b6bf8394 | ||
![]() |
2a885d709d | ||
![]() |
5bed46c0aa | ||
![]() |
8b27c7e01a | ||
![]() |
177902a286 | ||
![]() |
48b0f7dc27 | ||
![]() |
d5f4a1a48a | ||
![]() |
de9f60aa7f | ||
![]() |
c93b65b299 | ||
![]() |
3c6a6cdc5b | ||
![]() |
b669f3d715 | ||
![]() |
f663fac220 | ||
![]() |
bc42e79bb5 | ||
![]() |
ca29333cd0 | ||
![]() |
f9f478e100 | ||
![]() |
0873beaed2 |
116
CHANGELOG.md
116
CHANGELOG.md
@@ -1,5 +1,59 @@
|
||||
# Changelog
|
||||
|
||||
## v1.3.11 (2016-03-15)
|
||||
|
||||
* Fix: Typo preventing history logging for websockets.
|
||||
|
||||
|
||||
## v1.3.10 (2016-03-12)
|
||||
|
||||
* Fix: Actually allow HTML tags for Pushover.
|
||||
* Fix: PlexPy not restarting on Windows if there is a space in the folder path.
|
||||
* Fix: Reconnect websocket when changing PMS SSL setting.
|
||||
* Fix: Datatables not loading when view_offset or duration is blank.
|
||||
* Fix: Bug when checking the PMS version in the settings.
|
||||
* Fix: Auto-refreshing of log tables.
|
||||
* Fix: Logging of IPv6 addresses. (PMS version >0.9.14 only.)
|
||||
* Fix: Hide days selection from the Play Totals graph page.
|
||||
* Fix: PlexPy overwriting user's own SSL certificate/key.
|
||||
* Fix: Multiple watched notifications when using websocket.
|
||||
* Fix: Some missing python library imports.
|
||||
* Fix: Some typos in settings and PlexWatch importer.
|
||||
* New: Ability to get notified of PMS updates.
|
||||
* New: Ability to disable the link to Plex Web with Facebook notifications and use IMDB, TVDB, TMDb, or Last.fm instead.
|
||||
* New: Ability to reset Imgur poster url from the info page if the poster is changed.
|
||||
* New: Tooltips on the current activity progress bars.
|
||||
* New: Side scrolling of Recently Added/Recently Played items.
|
||||
* New: Document all date/time format options.
|
||||
* New: Button to clear notification logs.
|
||||
* New: Customizable backup, cache, and log directories.
|
||||
* Change: Retry writing sessions to history if it fails, so sessions don't get lost. (Activity pinger only, not availble for websocket.)
|
||||
* Change: Save any unknown sessions to the "Local" user.
|
||||
* Change: History table modal is filtered depending on which graph series is clicked.
|
||||
* Change: Revert back to saving the state of datatables (search, sorting, entries per page, etc.).
|
||||
* Change: Newlines are not longer stripped from notification text which allows for finer control of how notifications look.
|
||||
* Change: Updated FreeNAS/FreeBSD init scripts. (Must have updated jails.) (Thanks @chiviak)
|
||||
|
||||
|
||||
## v1.3.9 (2016-02-21)
|
||||
|
||||
* Fix: Recently added notification not sent to all notification agents.
|
||||
* New: Pushover HTML support. (Thanks @elseym)
|
||||
|
||||
|
||||
## 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.
|
||||
* New: Notification Logs table in the Logs tab.
|
||||
* New: 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.
|
||||
@@ -7,18 +61,18 @@
|
||||
* Fix: Video metadata flags showing up for track info.
|
||||
* Fix: Custom library icons not applied to Library Statistics.
|
||||
* Fix: Typos in the Web UI.
|
||||
* Add: ETA to Current Activity overlay.
|
||||
* Add: Total duration to Libraries and Users tables.
|
||||
* Add: {machine_id} to notification options.
|
||||
* Add: IMDB, TVDB, TMDb, Last.fm, and Trackt IDs/URLs to notification options.
|
||||
* Add: {poster_url} to notification options using Imgur.
|
||||
* Add: Poster and link for Facebook notifications.
|
||||
* Add: Log javascript errors from the Web UI.
|
||||
* Add: Configuration and Scheduler info to the settings page.
|
||||
* Add: Schedule background task to backup the PlexPy database.
|
||||
* Add: URL anonymizer for external links.
|
||||
* Add: Plex Media Scanner log file to Log viewer.
|
||||
* Add: API v2 (sill very experimental) (Thanks @Hellowlol)
|
||||
* New: ETA to Current Activity overlay.
|
||||
* New: Total duration to Libraries and Users tables.
|
||||
* New: {machine_id} to notification options.
|
||||
* New: IMDB, TVDB, TMDb, Last.fm, and Trackt IDs/URLs to notification options.
|
||||
* New: {poster_url} to notification options using Imgur.
|
||||
* New: Poster and link for Facebook notifications.
|
||||
* New: Log javascript errors from the Web UI.
|
||||
* New: Configuration and Scheduler info to the settings page.
|
||||
* New: Schedule background task to backup the PlexPy database.
|
||||
* New: URL anonymizer for external links.
|
||||
* New: Plex Media Scanner log file to Log viewer.
|
||||
* New: API v2 (sill very experimental). (Thanks @Hellowlol)
|
||||
* Change: Allow secure websocket connections.
|
||||
* Change: History grouping now accounts for the view offset.
|
||||
* Change: Subject line can be toggled off for Facebook, Slack, Telegram, and Twitter.
|
||||
@@ -41,9 +95,9 @@
|
||||
* Fix: Unable to expand media info table when missing "Added At" date.
|
||||
* Fix: Server verification for unpublished servers.
|
||||
* Fix: Updating PMS identifier for server change.
|
||||
* Add: {stream_time}, {remaining_time}, and {progress_time} to notification options.
|
||||
* Add: Powershell script support. (Thanks @Hellowlol)
|
||||
* Add: Method to delete duplicate libraries.
|
||||
* New: {stream_time}, {remaining_time}, and {progress_time} to notification options.
|
||||
* New: Powershell script support. (Thanks @Hellowlol)
|
||||
* New: Method to delete duplicate libraries.
|
||||
* Change: Daemonize before running start up tasks.
|
||||
|
||||
|
||||
@@ -55,7 +109,7 @@
|
||||
* Fix: Libraries and Users lists not refreshing.
|
||||
* Fix: Server verification in settings.
|
||||
* Fix: Empty libraries not added to database.
|
||||
* Add: Unique identifiers to notification options.
|
||||
* New: Unique identifiers to notification options.
|
||||
* Remove: Requirement of media type toggles for recently added notifications.
|
||||
* Remove: Built in Twitter key and secret.
|
||||
* Change: Unnecessary quoting of script arguments.
|
||||
@@ -88,21 +142,21 @@
|
||||
|
||||
## v1.3.0 (2016-01-23)
|
||||
|
||||
* Add: Brand new Libraries section.
|
||||
* Add: Lots of new library statistics.
|
||||
* Add: Media info table for libraries.
|
||||
* Add: Web app for Android and iOS. (Thanks @zobe123)
|
||||
* Add: Slack notification agent. (Thanks @richipargo)
|
||||
* Add: Facebook notification agent.
|
||||
* Add: Custom script notification agent. (Thanks @Hellowlol)
|
||||
* Add: Custom "From Name" to email notification agent.
|
||||
* Add: Ability to test notifications / send custom one-off notifications.
|
||||
* Add: 'datestamp' and 'timestamp' notification options.
|
||||
* Add: More concurrent stream statistics.
|
||||
* Add: Media info flags on the info pages.
|
||||
* Add: Ability to fix broken metadata if the item has been moved in Plex.
|
||||
* Add: Ability to rearrange the homepage statistics cards.
|
||||
* Add: CentOS startup script (Thanks @PHoSawyer)
|
||||
* New: Brand new Libraries section.
|
||||
* New: Lots of new library statistics.
|
||||
* New: Media info table for libraries.
|
||||
* New: Web app for Android and iOS. (Thanks @zobe123)
|
||||
* New: Slack notification agent. (Thanks @richipargo)
|
||||
* New: Facebook notification agent.
|
||||
* New: Custom script notification agent. (Thanks @Hellowlol)
|
||||
* New: Custom "From Name" to email notification agent.
|
||||
* New: Ability to test notifications / send custom one-off notifications.
|
||||
* New: 'datestamp' and 'timestamp' notification options.
|
||||
* New: More concurrent stream statistics.
|
||||
* New: Media info flags on the info pages.
|
||||
* New: Ability to fix broken metadata if the item has been moved in Plex.
|
||||
* New: Ability to rearrange the homepage statistics cards.
|
||||
* New: CentOS startup script (Thanks @PHoSawyer)
|
||||
* Fix: Server name blank after first run wizard.
|
||||
* Fix: Incorrect duration for grouped home stats.
|
||||
* Fix: Allow SSL when verifying server in settings.
|
||||
|
@@ -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.
|
||||
|
@@ -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?
|
||||
|
@@ -1,6 +1,7 @@
|
||||
<%
|
||||
import plexpy
|
||||
from plexpy import version
|
||||
from plexpy.helpers import anon_url
|
||||
%>
|
||||
<!doctype html>
|
||||
|
||||
@@ -136,15 +137,15 @@ from plexpy import version
|
||||
<div id="ajaxMsg" class="ajaxMsg"></div>
|
||||
% if plexpy.CONFIG.CHECK_GITHUB and not plexpy.CURRENT_VERSION:
|
||||
<div id="updatebar" style="display: none;">
|
||||
You're running an unknown version of PlexPy. <a href="update">Update</a> or
|
||||
<a href="#" id="updateDismiss">Close</a>
|
||||
You're running an unknown version of PlexPy.<br />
|
||||
<a href="update">Update</a> or <a href="#" id="updateDismiss">Close</a>
|
||||
</div>
|
||||
% elif plexpy.CONFIG.CHECK_GITHUB and plexpy.CURRENT_VERSION != plexpy.LATEST_VERSION and plexpy.COMMITS_BEHIND > 0 and plexpy.INSTALL_TYPE != 'win':
|
||||
<div id="updatebar" style="display: none;">
|
||||
A <a
|
||||
href="https://github.com/${plexpy.CONFIG.GIT_USER}/plexpy/compare/${plexpy.CURRENT_VERSION}...${plexpy.LATEST_VERSION}" target="_blank">
|
||||
newer version</a> is available. You're ${plexpy.COMMITS_BEHIND} commits behind. <a href="update">Update</a> or
|
||||
<a href="#" id="updateDismiss">Close</a>
|
||||
A <a href="${anon_url('https://github.com/%s/plexpy/compare/%s...%s' % (plexpy.CONFIG.GIT_USER, plexpy.CURRENT_VERSION, plexpy.LATEST_VERSION))}" target="_blank">
|
||||
newer version</a> is available.<br />
|
||||
You're ${plexpy.COMMITS_BEHIND} commits behind.<br />
|
||||
<a href="update">Update</a> or <a href="#" id="updateDismiss">Close</a>
|
||||
</div>
|
||||
% endif
|
||||
<nav class="navbar navbar-fixed-top">
|
||||
|
@@ -886,6 +886,7 @@ a .dashboard-activity-metadata-user-thumb:hover {
|
||||
text-align: center;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.dashboard-recent-media {
|
||||
width: 100%;
|
||||
@@ -2407,6 +2408,9 @@ a .home-platforms-instance-list-oval:hover,
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
.history-thumbnail-popover.popover.left {
|
||||
margin-left: -15px;
|
||||
}
|
||||
.history-thumbnail-popover.popover.right {
|
||||
margin-left: 15px;
|
||||
}
|
||||
@@ -2655,6 +2659,7 @@ table[id^='media_info_child'] table[id^='media_info_child'] thead th {
|
||||
margin: 5px 0 0 0.5em;
|
||||
}
|
||||
.notification-params {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
background-color: #282828;
|
||||
}
|
||||
@@ -2669,6 +2674,14 @@ table[id^='media_info_child'] table[id^='media_info_child'] thead th {
|
||||
padding-left: 10px;
|
||||
width: 200px;
|
||||
}
|
||||
.notification-params.time-options td:first-child {
|
||||
padding-left: 10px;
|
||||
width: 125px;
|
||||
}
|
||||
.notification-params.time-options td:nth-child(2) {
|
||||
padding-left: 10px;
|
||||
width: 275px;
|
||||
}
|
||||
.notification-params td:not(:first-child) {
|
||||
padding-right: 10px;
|
||||
}
|
||||
@@ -2759,3 +2772,21 @@ a.no-highlight:hover {
|
||||
.save-button {
|
||||
margin-top: 15px;
|
||||
}
|
||||
.nav-dashboard > li {
|
||||
float: left;
|
||||
}
|
||||
.btn-gray.disabled,
|
||||
.btn-gray.disabled:focus,
|
||||
.btn-gray.disabled:hover,
|
||||
.btn-gray.disabled:active {
|
||||
color: #323232;
|
||||
cursor: default;
|
||||
}
|
||||
.nav-header > li > a:focus,
|
||||
.nav-header > li > a:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
#recently-added-row-scroller,
|
||||
#recently-watched-row-scroller {
|
||||
position: relative;
|
||||
}
|
@@ -217,8 +217,8 @@ DOCUMENTATION :: END
|
||||
% endif
|
||||
<div class="dashboard-activity-progress">
|
||||
<div class="dashboard-activity-progress-bar">
|
||||
<div class="bufferbar" style="width: ${a['transcode_progress']}%">${a['transcode_progress']}%</div>
|
||||
<div class="bar" style="width: ${a['progress_percent']}%">${a['progress_percent']}%</div>
|
||||
<div class="bufferbar" style="width: ${a['transcode_progress']}%" data-toggle="tooltip" title="Transcoder Progress">${a['transcode_progress']}%</div>
|
||||
<div class="bar" style="width: ${a['progress_percent']}%" data-toggle="tooltip" title="Stream Progress">${a['progress_percent']}%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-activity-metadata-wrapper">
|
||||
@@ -261,11 +261,7 @@ DOCUMENTATION :: END
|
||||
% endif
|
||||
</div>
|
||||
<div class="dashboard-activity-metadata-user">
|
||||
% if a['user_id']:
|
||||
<a href="user?user_id=${a['user_id']}" title="${a['friendly_name']}">${a['friendly_name']}</a>
|
||||
% else:
|
||||
<a href="user?user=${a['user']}" title="${a['friendly_name']}">${a['friendly_name']}</a>
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -289,11 +285,13 @@ DOCUMENTATION :: END
|
||||
});
|
||||
|
||||
// Add hover class to dashboard-instance
|
||||
$('.dashboard-activity-poster').hover(function() {
|
||||
$('.dashboard-activity-poster, .dashboard-activity-progress-bar').hover(function() {
|
||||
$(this).closest('.dashboard-instance').addClass('hover');
|
||||
}, function() {
|
||||
$(this).closest('.dashboard-instance').removeClass('hover');
|
||||
});
|
||||
|
||||
$('.bar, .bufferbar').tooltip({container: 'body', placement: 'right', delay: 50});
|
||||
</script>
|
||||
% else:
|
||||
<div class="text-muted">Nothing is currently being watched.</div><br>
|
||||
|
@@ -249,7 +249,7 @@
|
||||
|
||||
<script>
|
||||
// Modal popup dialog
|
||||
function selectHandler(selectedDate) {
|
||||
function selectHandler(selectedDate, selectedSeries) {
|
||||
|
||||
try
|
||||
{
|
||||
@@ -259,10 +259,25 @@
|
||||
var y = dateValue.getFullYear();
|
||||
var dateString = '' + y + '-' + (m<=9 ? '0' + m : m) + '-' + (d <= 9 ? '0' + d : d);
|
||||
|
||||
var media_type = 'all';
|
||||
var transcode_decision = null;
|
||||
switch(selectedSeries) {
|
||||
case "TV": media_type = 'episode'; break;
|
||||
case "Movies": media_type = 'movie'; break;
|
||||
case "Music": media_type = 'track'; break;
|
||||
case "Direct Play": transcode_decision = 'direct play'; break;
|
||||
case "Direct Stream": transcode_decision = 'copy'; break;
|
||||
case "Transcode": transcode_decision = 'transcode'; break;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
"url": "history_table_modal",
|
||||
url: "history_table_modal",
|
||||
type: 'post',
|
||||
data: { 'start_date': dateString },
|
||||
data: {
|
||||
start_date: dateString,
|
||||
media_type: media_type,
|
||||
transcode_decision: transcode_decision
|
||||
},
|
||||
complete: function(xhr, status) {
|
||||
$('#history-modal').modal('show');
|
||||
$("#history-modal").html(xhr.responseText);
|
||||
@@ -271,7 +286,7 @@
|
||||
}
|
||||
catch(err)
|
||||
{
|
||||
console.log("Failed to retrieve data");
|
||||
console.log("Failed to retrieve history modal data.");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -299,6 +314,8 @@
|
||||
var music_visible = (${config['music_logging_enable']} == 1 ? true : false);
|
||||
|
||||
function loadGraphsTab1(time_range, yaxis) {
|
||||
$('#days-selection').show();
|
||||
|
||||
setGraphFormat(yaxis);
|
||||
|
||||
$.ajax({
|
||||
@@ -382,6 +399,8 @@
|
||||
}
|
||||
|
||||
function loadGraphsTab2(time_range, yaxis) {
|
||||
$('#days-selection').show();
|
||||
|
||||
setGraphFormat(yaxis);
|
||||
|
||||
$.ajax({
|
||||
@@ -460,6 +479,8 @@
|
||||
}
|
||||
|
||||
function loadGraphsTab3(yaxis) {
|
||||
$('#days-selection').hide();
|
||||
|
||||
setGraphFormat(yaxis);
|
||||
|
||||
$.ajax({
|
||||
|
@@ -22,7 +22,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-card-back'>
|
||||
<table class="display" id="history_table" width="100%">
|
||||
<table class="display history_table" id="history_table" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="left" id="delete_row">Delete</th>
|
||||
|
@@ -5,12 +5,12 @@
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
|
||||
<h4 class="modal-title" id="myModalLabel">
|
||||
<strong><span id="modal_header_ip_address">
|
||||
<i class="fa fa-history"></i> History for <span id="date-header">${data}</span>
|
||||
<i class="fa fa-history"></i> History for <span id="date-header">${data['start_date']}</span>
|
||||
</span></strong>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body" id="modal-text">
|
||||
<table class="display" id="history_table" width="100%">
|
||||
<table class="display history_table" id="history_table_modal" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="left" id="started">Started</th>
|
||||
@@ -32,7 +32,7 @@
|
||||
<script src="interfaces/default/js/tables/history_table_modal.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('#date-header').html(moment('${data}','YYYY-MM-DD').format('ddd MMM Do YYYY'));
|
||||
$('#date-header').html(moment('${data["start_date"]}','YYYY-MM-DD').format('ddd MMM Do YYYY'));
|
||||
history_table_modal_options.ajax = {
|
||||
url: 'get_history',
|
||||
type: 'post',
|
||||
@@ -40,14 +40,16 @@
|
||||
return {
|
||||
json_data: JSON.stringify(d),
|
||||
grouping: false,
|
||||
start_date: '${data}'
|
||||
start_date: "${data['start_date']}",
|
||||
media_type: "${data.get('media_type')}",
|
||||
transcode_decision: "${data.get('transcode_decision')}"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
history_table = $('#history_table').DataTable(history_table_modal_options);
|
||||
history_table = $('#history_table_modal').DataTable(history_table_modal_options);
|
||||
|
||||
clearSearchButton('history_table', history_table);
|
||||
clearSearchButton('history_table_modal', history_table);
|
||||
|
||||
// Move #info-modal to parent container
|
||||
if (!($('#history-modal').next().is('#info-modal'))) {
|
||||
|
@@ -23,7 +23,7 @@
|
||||
<h3>Watch Statistics <small>Last ${config['home_stats_length']} days</small></h3>
|
||||
</div>
|
||||
<div id="home-stats" class="home-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>
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
@@ -36,19 +36,27 @@
|
||||
<h3>Library Statistics <small>${config['pms_name']}</small></h3>
|
||||
</div>
|
||||
<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>
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
<div class='row'>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="padded-header">
|
||||
<ul class="nav nav-header nav-dashboard pull-right">
|
||||
<li>
|
||||
<a href="#" id="recently-added-page-left" class="paginate btn-gray disabled" data-id="+1"><i class="fa fa-lg fa-chevron-left"></i></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" id="recently-added-page-right" class="paginate btn-gray disabled" data-id="-1"><i class="fa fa-lg fa-chevron-right"></i></a>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>Recently Added</h3>
|
||||
</div>
|
||||
<div id='recentlyAdded'>
|
||||
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Looking for new items...</div>
|
||||
<div id="recentlyAdded" style="margin-right: -15px;">
|
||||
<div class="text-muted"><i class="fa fa-refresh fa-spin"></i> Looking for new items...</div>
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
@@ -59,7 +67,6 @@
|
||||
<%def name="javascriptIncludes()">
|
||||
<script src="interfaces/default/js/moment-with-locale.js"></script>
|
||||
<script>
|
||||
|
||||
function currentActivityHeader() {
|
||||
$.ajax({
|
||||
url: 'get_current_activity_header',
|
||||
@@ -71,7 +78,6 @@
|
||||
});
|
||||
}
|
||||
currentActivityHeader();
|
||||
setInterval(currentActivityHeader, 15000);
|
||||
|
||||
function currentActivity() {
|
||||
$.ajax({
|
||||
@@ -84,7 +90,12 @@
|
||||
});
|
||||
}
|
||||
currentActivity();
|
||||
setInterval(currentActivity, 15000);
|
||||
|
||||
setInterval(function () {
|
||||
$('.bar, .bufferbar').tooltip('destroy');
|
||||
currentActivityHeader();
|
||||
currentActivity();
|
||||
}, 15000);
|
||||
|
||||
function getHomeStats(days) {
|
||||
$.ajax({
|
||||
@@ -97,6 +108,7 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
getHomeStats();
|
||||
|
||||
function getLibraryStats() {
|
||||
$.ajax({
|
||||
@@ -109,33 +121,21 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
getLibraryStats();
|
||||
|
||||
function recentlyAdded() {
|
||||
var widthVal = $('body').find(".container-fluid").width();
|
||||
var tmp = (widthVal-20) / 182;
|
||||
|
||||
if (tmp > 0) {
|
||||
containerSize = parseInt(tmp);
|
||||
} else {
|
||||
containerSize = 1;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: 'get_recently_added',
|
||||
type: "GET",
|
||||
async: true,
|
||||
data: { count : containerSize },
|
||||
data: { count : 50 },
|
||||
complete: function(xhr, status) {
|
||||
$("#recentlyAdded").html(xhr.responseText);
|
||||
highlightAddedScrollerButton();
|
||||
}
|
||||
});
|
||||
}
|
||||
$(document).ready(function () {
|
||||
recentlyAdded();
|
||||
$(window).resize(function() {
|
||||
recentlyAdded();
|
||||
});
|
||||
});
|
||||
|
||||
var date_format = 'YYYY-MM-DD';
|
||||
var time_format = 'hh:mm a';
|
||||
@@ -148,10 +148,44 @@
|
||||
}
|
||||
});
|
||||
|
||||
getHomeStats();
|
||||
getLibraryStats();
|
||||
function highlightAddedScrollerButton() {
|
||||
var scroller = $("#recently-added-row-scroller");
|
||||
var numElems = scroller.find("li").length;
|
||||
scroller.width(numElems * 175);
|
||||
if (scroller.width() > $("body").find(".container-fluid").width()) {
|
||||
$("#recently-added-page-right").removeClass("disabled");
|
||||
} else {
|
||||
$("#recently-added-page-right").addClass("disabled");
|
||||
}
|
||||
}
|
||||
|
||||
$(window).resize(function () {
|
||||
highlightAddedScrollerButton();
|
||||
});
|
||||
|
||||
var leftTotal = 0;
|
||||
$(".paginate").click(function (e) {
|
||||
e.preventDefault();
|
||||
var scroller = $("#recently-added-row-scroller");
|
||||
var containerWidth = $("body").find(".container-fluid").width();
|
||||
var scrollAmount = $(this).data("id") * parseInt((containerWidth - 15) / 175) * 175;
|
||||
var leftMax = Math.min(-parseInt(scroller.width()) + Math.abs(scrollAmount), 0);
|
||||
|
||||
leftTotal = Math.max(Math.min(leftTotal + scrollAmount, 0), leftMax);
|
||||
scroller.animate({ left: leftTotal }, 250);
|
||||
|
||||
if (leftTotal == 0) {
|
||||
$("#recently-added-page-left").addClass("disabled").blur();
|
||||
} else {
|
||||
$("#recently-added-page-left").removeClass("disabled");
|
||||
}
|
||||
|
||||
if (leftTotal == leftMax) {
|
||||
$("#recently-added-page-right").addClass("disabled").blur();
|
||||
} else {
|
||||
$("#recently-added-page-right").removeClass("disabled");
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</%def>
|
||||
|
@@ -344,25 +344,41 @@ DOCUMENTATION :: END
|
||||
<button class="btn btn-danger btn-edit" data-toggle="button" aria-pressed="false" autocomplete="off" id="row-edit-mode">
|
||||
<i class="fa fa-trash-o"></i> Delete mode
|
||||
</button>
|
||||
% if source == 'history':
|
||||
<a href="update_metadata?rating_key=${data['rating_key']}&update=True" class="btn btn-danger btn-edit" id="fix-metadata">
|
||||
<i class="fa fa-wrench"></i> Fix Metadata
|
||||
</a>
|
||||
% endif
|
||||
% if data.get('poster_url'):
|
||||
% if data['media_type'] == 'artist' or data['media_type'] == 'album' or data['media_type'] == 'track':
|
||||
<span class="imgur-poster-tooltip" data-toggle="popover" data-img="${data['poster_url']}" data-height="80" data-width="80" style="display: inline-flex;">
|
||||
% else:
|
||||
<span class="imgur-poster-tooltip" data-toggle="popover" data-img="${data['poster_url']}" data-height="120" data-width="80" style="display: inline-flex;">
|
||||
% endif
|
||||
<button class="btn btn-danger btn-edit" data-toggle="button" aria-pressed="false" autocomplete="off" id="delete-imgur-poster">
|
||||
<i class="fa fa-picture-o"></i> Reset Imgur Poster
|
||||
</button>
|
||||
</span>
|
||||
% endif
|
||||
<div class="alert alert-danger alert-edit" role="alert" id="row-edit-mode-alert"><i class="fa fa-exclamation-triangle"></i> Select rows to delete. Data is deleted upon exiting delete mode.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-card-back">
|
||||
<table class="display" id="history_table" width="100%">
|
||||
<table class="display history_table" id="history_table-RK-${data['rating_key']}" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th align='left' id="delete">Delete</th>
|
||||
<th align='left' id="time">Time</th>
|
||||
<th align='left' id="friendly_name">User</th>
|
||||
<th align='left' id="ip_address">IP Address</th>
|
||||
<th align='left' id="platform">Platform</th>
|
||||
<th align='left' id="player">Player</th>
|
||||
<th align='left' id="title">Title</th>
|
||||
<th align='left' id="started">Started</th>
|
||||
<th align='left' id="paused_counter">Paused</th>
|
||||
<th align='left' id="stopped">Stopped</th>
|
||||
<th align='left' id="duration">Duration</th>
|
||||
<th align='left' id="percent_complete"></th>
|
||||
<th align="left" id="delete">Delete</th>
|
||||
<th align="left" id="time">Time</th>
|
||||
<th align="left" id="friendly_name">User</th>
|
||||
<th align="left" id="ip_address">IP Address</th>
|
||||
<th align="left" id="platform">Platform</th>
|
||||
<th align="left" id="player">Player</th>
|
||||
<th align="left" id="title">Title</th>
|
||||
<th align="left" id="started">Started</th>
|
||||
<th align="left" id="paused_counter">Paused</th>
|
||||
<th align="left" id="stopped">Stopped</th>
|
||||
<th align="left" id="duration">Duration</th>
|
||||
<th align="left" id="percent_complete"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
@@ -458,11 +474,11 @@ DOCUMENTATION :: END
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
get_history();
|
||||
history_table = $('#history_table').DataTable(history_table_options);
|
||||
history_table = $('#history_table-RK-${data["rating_key"]}').DataTable(history_table_options);
|
||||
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 11] });
|
||||
$(colvis.button()).appendTo('div.colvis-button-bar');
|
||||
|
||||
clearSearchButton('history_table', history_table);
|
||||
clearSearchButton('history_table-RK-${data["rating_key"]}', history_table);
|
||||
|
||||
$('#row-edit-mode').on('click', function() {
|
||||
$('#row-edit-mode-alert').fadeIn(200);
|
||||
@@ -519,10 +535,33 @@ DOCUMENTATION :: END
|
||||
$("#runtime").html(millisecondsToMinutes($("#runtime").text(), true));
|
||||
$('div.art-face').animate({ opacity: 0.2 }, { duration: 1000 });
|
||||
</script>
|
||||
% if source == 'history':
|
||||
% if data.get('poster_url'):
|
||||
<script>
|
||||
$('#row-edit-mode').after('<a href="update_metadata?rating_key=${data['rating_key']}&update=True" class="btn btn-danger btn-edit" id="fix-metadata"> \
|
||||
<i class="fa fa-wrench"></i> Fix Metadata</a>');
|
||||
$('.imgur-poster-tooltip').popover({
|
||||
html: true,
|
||||
container: 'body',
|
||||
trigger: 'hover',
|
||||
placement: 'left',
|
||||
template: '<div class="popover history-thumbnail-popover" role="tooltip"><div class="arrow" style="top: 50%;"></div><div class="popover-content"></div></div>',
|
||||
content: function () {
|
||||
return '<div class="history-thumbnail" style="background-image: url(' + $(this).data('img') + '); height: ' + $(this).data('height') + 'px; width: ' + $(this).data('width') + 'px;" />';
|
||||
}
|
||||
});
|
||||
|
||||
$('#delete-imgur-poster').on('click', function() {
|
||||
$.ajax({
|
||||
url: 'delete_poster_url',
|
||||
type: 'POST',
|
||||
async: true,
|
||||
data: { poster_url : "${data['poster_url']}" },
|
||||
success: function (data) {
|
||||
var msg = '<i class="fa fa-check"></i> Imgur poster reset';
|
||||
showMsg(msg, false, true, 2000);
|
||||
$('.imgur-poster-tooltip').popover('destroy');
|
||||
$('#delete-imgur-poster').closest('span').remove();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
% endif
|
||||
% endif
|
||||
|
@@ -32,7 +32,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<span class="text-muted">Telize service written by <a href="https://github.com/fcambus/telize" target="_blank">Frederic Cambus</a>.</span>
|
||||
<% from plexpy.helpers import anon_url %>
|
||||
<span class="text-muted">Telize service written by <a href="${anon_url('https://github.com/fcambus/telize')}" target="_blank">Frederic Cambus</a>.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -29,7 +29,7 @@ var hc_plays_by_day_options = {
|
||||
point: {
|
||||
events: {
|
||||
click: function () {
|
||||
selectHandler(this.category);
|
||||
selectHandler(this.category, this.series.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,8 @@ var hc_plays_by_day_options = {
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
shared: true
|
||||
shared: true,
|
||||
crosshairs: true
|
||||
},
|
||||
series: [{}]
|
||||
};
|
@@ -29,7 +29,7 @@ var hc_plays_by_stream_type_options = {
|
||||
point: {
|
||||
events: {
|
||||
click: function() {
|
||||
selectHandler(this.category);
|
||||
selectHandler(this.category, this.series.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,8 @@ var hc_plays_by_stream_type_options = {
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
shared: true
|
||||
shared: true,
|
||||
crosshairs: true
|
||||
},
|
||||
series: [{}]
|
||||
};
|
@@ -22,7 +22,7 @@ history_table_options = {
|
||||
"emptyTable": "No data in table"
|
||||
},
|
||||
"pagingType": "bootstrap",
|
||||
"stateSave": false,
|
||||
"stateSave": true,
|
||||
"processing": false,
|
||||
"serverSide": true,
|
||||
"pageLength": 25,
|
||||
@@ -107,7 +107,7 @@ history_table_options = {
|
||||
}
|
||||
},
|
||||
"width": "10%",
|
||||
"className": "no-wrap hidden-md hidden-sm hidden-xs modal-control"
|
||||
"className": "no-wrap hidden-md hidden-sm hidden-xs"
|
||||
},
|
||||
{
|
||||
"targets": [5],
|
||||
@@ -115,11 +115,11 @@ history_table_options = {
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== '') {
|
||||
var transcode_dec = '';
|
||||
if (rowData['video_decision'] === 'transcode' || rowData['audio_decision'] === 'transcode') {
|
||||
if (rowData['transcode_decision'] === 'transcode') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Transcode"><i class="fa fa-server fa-fw"></i></span>';
|
||||
} else if (rowData['video_decision'] === 'copy' || rowData['audio_decision'] === 'copy') {
|
||||
} else if (rowData['transcode_decision'] === 'copy') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Direct Stream"><i class="fa fa-video-camera fa-fw"></i></span>';
|
||||
} else if (rowData['video_decision'] === 'direct play' || rowData['audio_decision'] === 'direct play') {
|
||||
} else if (rowData['transcode_decision'] === 'direct play') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Direct Play"><i class="fa fa-play-circle fa-fw"></i></span>';
|
||||
}
|
||||
$(td).html('<div><a href="#" data-target="#info-modal" data-toggle="modal"><div style="float: left;">' + transcode_dec + ' ' + cellData + '</div></a></div>');
|
||||
@@ -307,7 +307,7 @@ history_table_options = {
|
||||
}
|
||||
|
||||
// Parent table platform modal
|
||||
$('#history_table').on('click', '> tbody > tr > td.modal-control', function () {
|
||||
$('.history_table').on('click', '> tbody > tr > td.modal-control', function () {
|
||||
var tr = $(this).closest('tr');
|
||||
var row = history_table.row( tr );
|
||||
var rowData = row.data();
|
||||
@@ -327,7 +327,7 @@ $('#history_table').on('click', '> tbody > tr > td.modal-control', function () {
|
||||
});
|
||||
|
||||
// Parent table ip address modal
|
||||
$('#history_table').on('click', '> tbody > tr > td.modal-control-ip', function () {
|
||||
$('.history_table').on('click', '> tbody > tr > td.modal-control-ip', function () {
|
||||
var tr = $(this).closest('tr');
|
||||
var row = history_table.row( tr );
|
||||
var rowData = row.data();
|
||||
@@ -350,7 +350,7 @@ $('#history_table').on('click', '> tbody > tr > td.modal-control-ip', function (
|
||||
});
|
||||
|
||||
// Parent table delete mode
|
||||
$('#history_table').on('click', '> tbody > tr > td.delete-control > button', function () {
|
||||
$('.history_table').on('click', '> tbody > tr > td.delete-control > button', function () {
|
||||
var tr = $(this).closest('tr');
|
||||
var row = history_table.row( tr );
|
||||
var rowData = row.data();
|
||||
@@ -399,7 +399,7 @@ $('#history_table').on('click', '> tbody > tr > td.delete-control > button', fun
|
||||
});
|
||||
|
||||
// Parent table expand detailed history
|
||||
$('#history_table').on('click', '> tbody > tr > td.expand-history a', function () {
|
||||
$('.history_table').on('click', '> tbody > tr > td.expand-history a', function () {
|
||||
var tr = $(this).closest('tr');
|
||||
var row = history_table.row(tr);
|
||||
var rowData = row.data();
|
||||
|
@@ -79,11 +79,11 @@ history_table_modal_options = {
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== '') {
|
||||
var transcode_dec = '';
|
||||
if (rowData['video_decision'] === 'transcode' || rowData['audio_decision'] === 'transcode') {
|
||||
if (rowData['transcode_decision'] === 'transcode') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Transcode"><i class="fa fa-server fa-fw"></i></span>';
|
||||
} else if (rowData['video_decision'] === 'copy' || rowData['audio_decision'] === 'copy') {
|
||||
} else if (rowData['transcode_decision'] === 'copy') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Direct Stream"><i class="fa fa-video-camera fa-fw"></i></span>';
|
||||
} else if (rowData['video_decision'] === 'direct play' || rowData['audio_decision'] === 'direct play') {
|
||||
} else if (rowData['transcode_decision'] === 'direct play') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Direct Play"><i class="fa fa-play-circle fa-fw"></i></span>';
|
||||
}
|
||||
$(td).html('<div><a href="#" data-target="#info-modal" data-toggle="modal"><div style="float: left;">' + transcode_dec + ' ' + cellData + '</div></a></div>');
|
||||
@@ -148,7 +148,7 @@ history_table_modal_options = {
|
||||
}
|
||||
}
|
||||
|
||||
$('#history_table').on('click', 'td.modal-control', function () {
|
||||
$('.history_table').on('click', 'td.modal-control', function () {
|
||||
var tr = $(this).parents('tr');
|
||||
var row = history_table.row(tr);
|
||||
var rowData = row.data();
|
||||
|
@@ -16,7 +16,7 @@ libraries_list_table_options = {
|
||||
"pageLength": 10,
|
||||
"order": [ 2, 'asc'],
|
||||
"autoWidth": true,
|
||||
"stateSave": false,
|
||||
"stateSave": true,
|
||||
"pagingType": "bootstrap",
|
||||
"columnDefs": [
|
||||
{
|
||||
|
@@ -5,7 +5,7 @@ var log_table_options = {
|
||||
"pagingType": "bootstrap",
|
||||
"order": [ 0, 'desc'],
|
||||
"pageLength": 50,
|
||||
"stateSave": false,
|
||||
"stateSave": true,
|
||||
"language": {
|
||||
"search":"Search: ",
|
||||
"lengthMenu":"Show _MENU_ lines per page",
|
||||
|
@@ -23,7 +23,7 @@ media_info_table_options = {
|
||||
"emptyTable": "No data in table"
|
||||
},
|
||||
"pagingType": "bootstrap",
|
||||
"stateSave": false,
|
||||
"stateSave": true,
|
||||
"processing": false,
|
||||
"serverSide": true,
|
||||
"pageLength": 25,
|
||||
@@ -294,7 +294,7 @@ media_info_table_options = {
|
||||
}
|
||||
|
||||
// Parent table expand detailed media info
|
||||
$('#media_info_table').on('click', '> tbody > tr > td.expand-media-info a', function () {
|
||||
$('.media_info_table').on('click', '> tbody > tr > td.expand-media-info a', function () {
|
||||
var tr = $(this).closest('tr');
|
||||
var row = media_info_table.row(tr);
|
||||
var rowData = row.data();
|
||||
|
89
data/interfaces/default/js/tables/notification_logs.js
Normal file
89
data/interfaces/default/js/tables/notification_logs.js
Normal file
@@ -0,0 +1,89 @@
|
||||
notification_log_table_options = {
|
||||
"destroy": true,
|
||||
"serverSide": true,
|
||||
"processing": false,
|
||||
"pagingType": "bootstrap",
|
||||
"order": [ 0, 'desc'],
|
||||
"pageLength": 50,
|
||||
"stateSave": true,
|
||||
"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> Fetching rows...";
|
||||
showMsg(msg, false, false, 0)
|
||||
}
|
||||
}
|
@@ -5,7 +5,7 @@ var plex_log_table_options = {
|
||||
"pagingType": "bootstrap",
|
||||
"order": [ 0, 'desc'],
|
||||
"pageLength": 50,
|
||||
"stateSave": false,
|
||||
"stateSave": true,
|
||||
"language": {
|
||||
"search":"Search: ",
|
||||
"lengthMenu":"Show _MENU_ lines per page",
|
||||
|
@@ -4,7 +4,7 @@ sync_table_options = {
|
||||
"pagingType": "bootstrap",
|
||||
"order": [ [ 0, 'desc'], [ 1, 'asc'], [2, 'asc'] ],
|
||||
"pageLength": 25,
|
||||
"stateSave": false,
|
||||
"stateSave": true,
|
||||
"language": {
|
||||
"search":"Search: ",
|
||||
"lengthMenu":"Show _MENU_ lines per page",
|
||||
|
@@ -8,7 +8,7 @@ user_ip_table_options = {
|
||||
"infoFiltered":"(filtered from _MAX_ total entries)",
|
||||
"emptyTable": "No data in table",
|
||||
},
|
||||
"stateSave": false,
|
||||
"stateSave": true,
|
||||
"pagingType": "bootstrap",
|
||||
"processing": false,
|
||||
"serverSide": true,
|
||||
@@ -56,7 +56,7 @@ user_ip_table_options = {
|
||||
}
|
||||
},
|
||||
"width": "15%",
|
||||
"className": "no-wrap hidden-md hidden-sm hidden-xs modal-control"
|
||||
"className": "no-wrap hidden-md hidden-sm hidden-xs"
|
||||
},
|
||||
{
|
||||
"targets": [3],
|
||||
@@ -64,11 +64,11 @@ user_ip_table_options = {
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData) {
|
||||
var transcode_dec = '';
|
||||
if (rowData['video_decision'] === 'transcode' || rowData['audio_decision'] === 'transcode') {
|
||||
if (rowData['transcode_decision'] === 'transcode') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Transcode"><i class="fa fa-server fa-fw"></i></span>';
|
||||
} else if (rowData['video_decision'] === 'copy' || rowData['audio_decision'] === 'copy') {
|
||||
} else if (rowData['transcode_decision'] === 'copy') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Direct Stream"><i class="fa fa-video-camera fa-fw"></i></span>';
|
||||
} else if (rowData['video_decision'] === 'direct play' || rowData['audio_decision'] === 'direct play') {
|
||||
} else if (rowData['transcode_decision'] === 'direct play') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Direct Play"><i class="fa fa-play-circle fa-fw"></i></span>';
|
||||
}
|
||||
$(td).html('<div><a href="#" data-target="#info-modal" data-toggle="modal"><div style="float: left;">' + transcode_dec + ' ' + cellData + '</div></a></div>');
|
||||
@@ -146,11 +146,11 @@ user_ip_table_options = {
|
||||
}
|
||||
}
|
||||
|
||||
$('#user_ip_table').on('mouseenter', 'td.modal-control span', function () {
|
||||
$('.user_ip_table').on('mouseenter', 'td.modal-control span', function () {
|
||||
$(this).tooltip();
|
||||
});
|
||||
|
||||
$('#user_ip_table').on('click', 'td.modal-control', function () {
|
||||
$('.user_ip_table').on('click', 'td.modal-control', function () {
|
||||
var tr = $(this).parents('tr');
|
||||
var row = user_ip_table.row(tr);
|
||||
var rowData = row.data();
|
||||
@@ -169,7 +169,7 @@ $('#user_ip_table').on('click', 'td.modal-control', function () {
|
||||
showStreamDetails();
|
||||
});
|
||||
|
||||
$('#user_ip_table').on('click', 'td.modal-control-ip', function () {
|
||||
$('.user_ip_table').on('click', 'td.modal-control-ip', function () {
|
||||
var tr = $(this).parents('tr');
|
||||
var row = user_ip_table.row( tr );
|
||||
var rowData = row.data();
|
||||
|
@@ -16,7 +16,7 @@ users_list_table_options = {
|
||||
"pageLength": 10,
|
||||
"order": [ 2, 'asc'],
|
||||
"autoWidth": true,
|
||||
"stateSave": false,
|
||||
"stateSave": true,
|
||||
"pagingType": "bootstrap",
|
||||
"columnDefs": [
|
||||
{
|
||||
@@ -120,11 +120,11 @@ users_list_table_options = {
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
if (cellData !== null && cellData !== '') {
|
||||
var transcode_dec = '';
|
||||
if (rowData['video_decision'] === 'transcode' || rowData['audio_decision'] === 'transcode') {
|
||||
if (rowData['transcode_decision'] === 'transcode') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Transcode"><i class="fa fa-server fa-fw"></i></span>';
|
||||
} else if (rowData['video_decision'] === 'copy' || rowData['audio_decision'] === 'copy') {
|
||||
} else if (rowData['transcode_decision'] === 'copy') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Direct Stream"><i class="fa fa-video-camera fa-fw"></i></span>';
|
||||
} else if (rowData['video_decision'] === 'direct play' || rowData['audio_decision'] === 'direct play') {
|
||||
} else if (rowData['transcode_decision'] === 'direct play') {
|
||||
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Direct Play"><i class="fa fa-play-circle fa-fw"></i></span>';
|
||||
}
|
||||
$(td).html('<div><a href="#" data-target="#info-modal" data-toggle="modal"><div style="float: left;">' + transcode_dec + ' ' + cellData + '</div></a></div>');
|
||||
|
@@ -118,8 +118,16 @@ DOCUMENTATION :: END
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="table-card-header">
|
||||
<ul class="nav nav-header nav-dashboard pull-right">
|
||||
<li>
|
||||
<a href="#" id="recently-watched-page-left" class="paginate-watched btn-gray disabled" data-id="+1"><i class="fa fa-lg fa-chevron-left"></i></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" id="recently-watched-page-right" class="paginate-watched btn-gray disabled" data-id="-1"><i class="fa fa-lg fa-chevron-right"></i></a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="header-bar">
|
||||
<span><i class="fa fa-history"></i> Recently Watched</span>
|
||||
<span><i class="fa fa-history"></i> Recently Played</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-card-back">
|
||||
@@ -135,6 +143,14 @@ DOCUMENTATION :: END
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="table-card-header">
|
||||
<ul class="nav nav-header nav-dashboard pull-right">
|
||||
<li>
|
||||
<a href="#" id="recently-added-page-left" class="paginate-added btn-gray disabled" data-id="+1"><i class="fa fa-lg fa-chevron-left"></i></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" id="recently-added-page-right" class="paginate-added btn-gray disabled" data-id="-1"><i class="fa fa-lg fa-chevron-right"></i></a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="header-bar">
|
||||
<span><i class="fa fa-history"></i> Recently Added</span>
|
||||
</div>
|
||||
@@ -170,7 +186,7 @@ DOCUMENTATION :: END
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-card-back">
|
||||
<table class="display" id="history_table" width="100%">
|
||||
<table class="display history_table" id="history_table-SID-${data['section_id']}" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="left" id="delete">Delete</th>
|
||||
@@ -229,7 +245,7 @@ DOCUMENTATION :: END
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-card-back">
|
||||
<table class="display" id="media_info_table" width="100%">
|
||||
<table class="display media_info_table" id="media_info_table-SID-${data['section_id']}" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="left" id="added_at">Added At</th>
|
||||
@@ -364,12 +380,12 @@ DOCUMENTATION :: END
|
||||
};
|
||||
}
|
||||
}
|
||||
history_table = $('#history_table').DataTable(history_table_options);
|
||||
history_table = $('#history_table-SID-${data["section_id"]}').DataTable(history_table_options);
|
||||
|
||||
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 11] });
|
||||
$(colvis.button()).appendTo('#button-bar-history');
|
||||
|
||||
clearSearchButton('history_table', history_table);
|
||||
clearSearchButton('history_table-SID-${data["section_id"]}', history_table);
|
||||
}
|
||||
|
||||
function loadMediaInfoTable() {
|
||||
@@ -385,12 +401,12 @@ DOCUMENTATION :: END
|
||||
};
|
||||
}
|
||||
}
|
||||
media_info_table = $('#media_info_table').DataTable(media_info_table_options);
|
||||
media_info_table = $('#media_info_table-SID-${data["section_id"]}').DataTable(media_info_table_options);
|
||||
|
||||
var colvis = new $.fn.dataTable.ColVis(media_info_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark' });
|
||||
$(colvis.button()).appendTo('#button-bar-media-info');
|
||||
|
||||
clearSearchButton('media_info_table', media_info_table);
|
||||
clearSearchButton('media_info_table-SID-${data["section_id"]}', media_info_table);
|
||||
}
|
||||
|
||||
$( "#history-tab-btn" ).one( "click", function() {
|
||||
@@ -460,61 +476,116 @@ DOCUMENTATION :: END
|
||||
});
|
||||
|
||||
function recentlyWatched() {
|
||||
var widthVal = $("#library-recently-watched").width();
|
||||
var tmp = (widthVal-25) / 175;
|
||||
|
||||
if (tmp > 0) {
|
||||
var containerSize = parseInt(tmp);
|
||||
} else {
|
||||
var containerSize = 1;
|
||||
}
|
||||
|
||||
// Populate recently watched
|
||||
$.ajax({
|
||||
url: 'get_library_recently_watched',
|
||||
async: true,
|
||||
data: {
|
||||
section_id: section_id,
|
||||
limit: containerSize
|
||||
limit: 50
|
||||
},
|
||||
complete: function(xhr, status) {
|
||||
$("#library-recently-watched").html(xhr.responseText);
|
||||
highlightWatchedScrollerButton();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function recentlyAdded() {
|
||||
var widthVal = $("#library-recently-added").width();
|
||||
var tmp = (widthVal-25) / 175;
|
||||
|
||||
if (tmp > 0) {
|
||||
var containerSize = parseInt(tmp);
|
||||
} else {
|
||||
var containerSize = 1;
|
||||
}
|
||||
|
||||
// Populate recently added
|
||||
$.ajax({
|
||||
url: 'get_library_recently_added',
|
||||
async: true,
|
||||
data: {
|
||||
section_id: section_id,
|
||||
limit: containerSize
|
||||
limit: 50
|
||||
},
|
||||
complete: function(xhr, status) {
|
||||
$("#library-recently-added").html(xhr.responseText);
|
||||
highlightAddedScrollerButton();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
recentlyWatched();
|
||||
recentlyAdded();
|
||||
|
||||
function highlightWatchedScrollerButton() {
|
||||
var scroller = $("#recently-watched-row-scroller");
|
||||
var numElems = scroller.find("li").length;
|
||||
scroller.width(numElems * 175);
|
||||
if (scroller.width() > $("#library-recently-watched").width()) {
|
||||
$("#recently-watched-page-right").removeClass("disabled");
|
||||
} else {
|
||||
$("#recently-watched-page-right").addClass("disabled");
|
||||
}
|
||||
}
|
||||
|
||||
function highlightAddedScrollerButton() {
|
||||
var scroller = $("#recently-added-row-scroller");
|
||||
var numElems = scroller.find("li").length;
|
||||
scroller.width(numElems * 175);
|
||||
if (scroller.width() > $("#library-recently-added").width()) {
|
||||
$("#recently-added-page-right").removeClass("disabled");
|
||||
} else {
|
||||
$("#recently-added-page-right").addClass("disabled");
|
||||
}
|
||||
}
|
||||
|
||||
$(window).resize(function() {
|
||||
recentlyWatched();
|
||||
recentlyAdded();
|
||||
highlightWatchedScrollerButton();
|
||||
highlightAddedScrollerButton();
|
||||
});
|
||||
|
||||
$('div.art-face').animate({ opacity: 0.2 }, { duration: 1000 });
|
||||
|
||||
var leftTotalWatched = 0;
|
||||
$(".paginate-watched").click(function (e) {
|
||||
e.preventDefault();
|
||||
var scroller = $("#recently-watched-row-scroller");
|
||||
var containerWidth = $("#library-recently-watched").width();
|
||||
var scrollAmount = $(this).data("id") * parseInt(containerWidth / 175) * 175;
|
||||
var leftMax = Math.min(-parseInt(scroller.width()) + Math.abs(scrollAmount), 0);
|
||||
|
||||
leftTotalWatched = Math.max(Math.min(leftTotalWatched + scrollAmount, 0), leftMax);
|
||||
scroller.animate({ left: leftTotalWatched }, 250);
|
||||
|
||||
if (leftTotalWatched == 0) {
|
||||
$("#recently-watched-page-left").addClass("disabled").blur();
|
||||
} else {
|
||||
$("#recently-watched-page-left").removeClass("disabled");
|
||||
}
|
||||
|
||||
if (leftTotalWatched == leftMax) {
|
||||
$("#recently-watched-page-right").addClass("disabled").blur();
|
||||
} else {
|
||||
$("#recently-watched-page-right").removeClass("disabled");
|
||||
}
|
||||
});
|
||||
|
||||
var leftTotalAdded = 0;
|
||||
$(".paginate-added").click(function (e) {
|
||||
e.preventDefault();
|
||||
var scroller = $("#recently-added-row-scroller");
|
||||
var containerWidth = $("#library-recently-added").width();
|
||||
var scrollAmount = $(this).data("id") * parseInt(containerWidth / 175) * 175;
|
||||
var leftMax = Math.min(-parseInt(scroller.width()) + Math.abs(scrollAmount), 0);
|
||||
|
||||
leftTotalAdded = Math.max(Math.min(leftTotalAdded + scrollAmount, 0), leftMax);
|
||||
scroller.animate({ left: leftTotalAdded }, 250);
|
||||
|
||||
if (leftTotalAdded == 0) {
|
||||
$("#recently-added-page-left").addClass("disabled").blur();
|
||||
} else {
|
||||
$("#recently-added-page-left").removeClass("disabled");
|
||||
}
|
||||
|
||||
if (leftTotalAdded == leftMax) {
|
||||
$("#recently-added-page-right").addClass("disabled").blur();
|
||||
} else {
|
||||
$("#recently-added-page-right").removeClass("disabled");
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
% endif
|
||||
|
@@ -32,6 +32,7 @@ DOCUMENTATION :: END
|
||||
|
||||
% if data:
|
||||
<div class="dashboard-recent-media-row">
|
||||
<div id="recently-added-row-scroller" style="left: 0;">
|
||||
<ul class="dashboard-recent-media list-unstyled">
|
||||
% for item in data:
|
||||
<li>
|
||||
@@ -90,6 +91,7 @@ DOCUMENTATION :: END
|
||||
% endfor
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="text-muted">No stats to show.
|
||||
</div><br>
|
||||
|
@@ -22,6 +22,7 @@ from plexpy import helpers
|
||||
</div>
|
||||
<div class="button-bar">
|
||||
<button class="btn btn-dark" id="clear-logs"><i class="fa fa-trash-o"></i> Clear log</button>
|
||||
<button class="btn btn-dark" id="clear-notify-logs" style="display: none;"><i class="fa fa-trash-o"></i> Clear log</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class='table-card-back'>
|
||||
@@ -30,6 +31,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">
|
||||
@@ -67,8 +69,22 @@ from plexpy import helpers
|
||||
<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 +110,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,43 +123,66 @@ 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);
|
||||
}
|
||||
|
||||
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();
|
||||
$("#clear-notify-logs").hide();
|
||||
LoadPlexPyLogs();
|
||||
clearSearchButton('log_table', log_table);
|
||||
});
|
||||
|
||||
$("#plex-logs-btn").click(function () {
|
||||
$("#clear-logs").hide();
|
||||
$("#clear-notify-logs").hide();
|
||||
LoadPlexLogs();
|
||||
clearSearchButton('plex_log_table', plex_log_table);
|
||||
});
|
||||
|
||||
$("#plex-scanner-logs-btn").click(function () {
|
||||
$("#clear-logs").hide();
|
||||
$("#clear-notify-logs").hide();
|
||||
LoadPlexScannerLogs();
|
||||
clearSearchButton('plex_scanner_log_table', plex_scanner_log_table);
|
||||
});
|
||||
|
||||
$("#notification-logs-btn").click(function () {
|
||||
$("#clear-logs").hide();
|
||||
$("#clear-notify-logs").show();
|
||||
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) {
|
||||
@@ -149,6 +190,19 @@ from plexpy import helpers
|
||||
}
|
||||
});
|
||||
|
||||
$("#clear-notify-logs").click(function () {
|
||||
var r = confirm("Are you sure you want to clear the PlexPy notification log?");
|
||||
if (r == true) {
|
||||
$.ajax({
|
||||
url: 'clearNotifyLogs',
|
||||
type: 'POST',
|
||||
success: function (data) {
|
||||
notification_log_table.draw();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var timer;
|
||||
function setRefresh()
|
||||
{
|
||||
@@ -164,8 +218,12 @@ from plexpy import helpers
|
||||
timer = setInterval(function() {
|
||||
if ($("#tabs-1").hasClass("active")) {
|
||||
log_table.ajax.reload();
|
||||
} else {
|
||||
} else if ($("#tabs-2").hasClass("active")) {
|
||||
plex_log_table.ajax.reload();
|
||||
} else if ($("#tabs-3").hasClass("active")) {
|
||||
plex_scanner_log_table.ajax.reload();
|
||||
} else if ($("#tabs-4").hasClass("active")) {
|
||||
notification_log_table.ajax.reload();
|
||||
}
|
||||
}, 1000*refreshrate.value);
|
||||
}
|
||||
|
@@ -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,6 +71,20 @@ 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_intup" ${helpers.checked(data['on_intup'])} class="toggle-switches">
|
||||
Notify on Plex server back up
|
||||
</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">
|
||||
@@ -87,10 +94,10 @@ from plexpy import helpers
|
||||
</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">
|
||||
Notify on Plex server back up
|
||||
<input type="checkbox" data-size="small" data-id="${data['id']}" data-config-name="${data['config_prefix']}_on_pmsupdate" ${helpers.checked(data['on_pmsupdate'])} class="toggle-switches">
|
||||
Notify on Plex update available
|
||||
</label>
|
||||
<p class="help-block">Trigger notification when the Plex Media Server can be reached internally after being down.</p>
|
||||
<p class="help-block">Trigger notification when an update for the Plex Media Server is available.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -32,6 +32,7 @@ DOCUMENTATION :: END
|
||||
|
||||
% if data != None:
|
||||
<div class="dashboard-recent-media-row">
|
||||
<div id="recently-added-row-scroller" style="left: 0;">
|
||||
<ul class="dashboard-recent-media list-unstyled">
|
||||
% for item in data:
|
||||
<div class="dashboard-recent-media-instance">
|
||||
@@ -83,6 +84,7 @@ DOCUMENTATION :: END
|
||||
% endfor
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="text-muted">There was an error communicating with your Plex Server. Please check your <a href="settings">settings</a>.
|
||||
</div><br>
|
||||
|
@@ -43,7 +43,7 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
||||
<li role="presentation"><a href="#tabs-5" aria-controls="tabs-5" role="tab" data-toggle="tab">Plex Media Server</a></li>
|
||||
<li role="presentation"><a href="#tabs-6" aria-controls="tabs-6" role="tab" data-toggle="tab">Plex.tv Account</a></li>
|
||||
<li role="presentation"><a href="#tabs-7" aria-controls="tabs-7" role="tab" data-toggle="tab">Extra Settings</a></li>
|
||||
<li role="presentation"><a href="#tabs-8" aria-controls="tabs-8" role="tab" data-toggle="tab">Monitoring</a></li>
|
||||
<li role="presentation"><a href="#tabs-8" aria-controls="tabs-8" role="tab" data-toggle="tab">Activity Monitoring</a></li>
|
||||
<li role="presentation"><a href="#tabs-9" aria-controls="tabs-9" role="tab" data-toggle="tab">Notifications</a></li>
|
||||
<li role="presentation"><a href="#tabs-10" aria-controls="tabs-10" role="tab" data-toggle="tab">Notification Agents</a></li>
|
||||
</ul>
|
||||
@@ -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
|
||||
@@ -82,15 +82,15 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Backup Directory:</td>
|
||||
<td>${plexpy.CONFIG.BACKUP_DIR}</td>
|
||||
<td>${config['backup_dir']}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Cache Directory:</td>
|
||||
<td>${plexpy.CONFIG.CACHE_DIR}</td>
|
||||
<td>${config['cache_dir']}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Log Directory:</td>
|
||||
<td>${plexpy.CONFIG.LOG_DIR}</td>
|
||||
<td>${config['log_dir']}</td>
|
||||
</tr>
|
||||
% if plexpy.ARGS:
|
||||
<tr>
|
||||
@@ -169,6 +169,35 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
||||
</label>
|
||||
<p class="help-block">Group successive play history by the same user as a single entry in the tables and watch statistics.</p>
|
||||
</div>
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>Directories</h3>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="backup_dir">Backup Directory</label>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control directory-settings" id="backup_dir" name="backup_dir" value="${config['backup_dir']}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="cache_dir">Cache Directory</label>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control directory-settings" id="cache_dir" name="cache_dir" value="${config['cache_dir']}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="log_dir">Log Directory</label>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control directory-settings" id="log_dir" name="log_dir" value="${config['log_dir']}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-2">
|
||||
@@ -437,9 +466,24 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
||||
<div role="tabpanel" class="tab-pane" id="tabs-5">
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>Plex Media Server</h3>
|
||||
<h3>Plex Media Server <small style="color: #fff;">Version <span id="pms_version">unknown</span></small></h3>
|
||||
</div>
|
||||
<p class="help-block">If you're using websocket monitoring, any server changes require a restart of PlexPy.</p>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="monitor_pms_updates" name="monitor_pms_updates" value="1" ${config['monitor_pms_updates']}> Monitor Plex Updates
|
||||
</label>
|
||||
<p class="help-block">Enable to have PlexPy check if updates are available for the Plex Media Server.<br />
|
||||
Note: The Plex updater is broken on certain Plex Pass version of Plex Media Server. PlexPy will automatically disable checking for Plex updates if one of these versions is found.</p>
|
||||
</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>
|
||||
<span id="remoteAccessCheck" style="color: #eb8600; padding-left: 10px;"></span>
|
||||
<p class="help-block">Enable to have PlexPy check if remote access to the Plex Media Server goes down.</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group has-feedback" id="pms-ip-group">
|
||||
<label for="pms_ip">Plex IP or Hostname</label>
|
||||
<div class="row">
|
||||
@@ -485,8 +529,9 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
||||
<label for="pms_logs_folder">Logs Folder</label>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="pms_logs_folder" name="pms_logs_folder" value="${config['pms_logs_folder']}" size="30" data-parsley-trigger="change">
|
||||
<input type="text" class="form-control" id="pms_logs_folder" name="pms_logs_folder" value="${config['pms_logs_folder']}" size="30" data-parsley-trigger="change" data-parsley-pattern="^[^\~\%]" data-parsley-errors-container="#pms_logs_folder_error" data-parsley-error-message="Shortcuts are not recognized.">
|
||||
</div>
|
||||
<div id="pms_logs_folder_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||
</div>
|
||||
<p class="help-block">Set the complete folder path where your Plex Server logs are, shortcuts are not recognized.<br />
|
||||
<a href="${anon_url('https://support.plex.tv/hc/en-us/articles/200250417-Plex-Media-Server-Log-Files')}" target="_blank">Click here</a> for help. This is required if you enable IP logging (for PMS 0.9.12 and below). </p>
|
||||
@@ -571,10 +616,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,17 +654,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>
|
||||
</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>
|
||||
<span id="remoteAccessCheck" style="color: #eb8600; padding-left: 10px;"></span>
|
||||
<p class="help-block">Enable to have PlexPy check if remote access to the Plex Media Server goes down.</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="padded-header">
|
||||
@@ -715,6 +751,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">Enable to upload Plex posters to Imgur for notifications. Disable if posters are not being used to save bandwidth.</p>
|
||||
</div>
|
||||
|
||||
<div class="padded-header">
|
||||
<h3>Current Activity Notifications</h3>
|
||||
</div>
|
||||
@@ -744,8 +787,14 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
||||
<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 TV Show or Artist notification for a batch of recently added Episodes or Tracks. Movies are unaffected.<br />
|
||||
Note: No Season/Episode or Album/Track metadata will be available.</p>
|
||||
<p class="help-block">
|
||||
Enable to only get one TV Show or Artist notification for a batch of recently added Episodes or Tracks. Movies are unaffected.<br />
|
||||
% if config['notify_recently_added_grandparent'] == 'Checked':
|
||||
<span id="notify_recently_added_grandparent_note" style="color: #eb8600;">Note: No Season/Episode or Album/Track metadata will be available.</span>
|
||||
% else:
|
||||
<span id="notify_recently_added_grandparent_note">Note: No Season/Episode or Album/Track metadata will be available.</span>
|
||||
% endif
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="notify_recently_added_delay">Notification Delay</label>
|
||||
@@ -892,23 +941,6 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
||||
</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">
|
||||
@@ -926,6 +958,40 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<div class="link"><i class="fa fa-server fa-fw"></i> Plex Server Back Up<i class="fa fa-chevron-down"></i></div>
|
||||
<ul class="submenu">
|
||||
<li>
|
||||
<div class="form-group">
|
||||
<label for="notify_on_intup_subject_text">Subject Line</label>
|
||||
<input class="form-control" type="text" id="notify_on_intup_subject_text" name="notify_on_intup_subject_text" value="${config['notify_on_intup_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_intup_body_text">Message Body</label>
|
||||
<textarea class="form-control" id="notify_on_intup_body_text" name="notify_on_intup_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_intup_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 Remote Access Back Up<i class="fa fa-chevron-down"></i></div>
|
||||
<ul class="submenu">
|
||||
@@ -944,17 +1010,17 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<div class="link"><i class="fa fa-server fa-fw"></i> Plex Server Back Up<i class="fa fa-chevron-down"></i></div>
|
||||
<div class="link"><i class="fa fa-refresh fa-fw"></i> Plex Update Available<i class="fa fa-chevron-down"></i></div>
|
||||
<ul class="submenu">
|
||||
<li>
|
||||
<div class="form-group">
|
||||
<label for="notify_on_intup_subject_text">Subject Line</label>
|
||||
<input class="form-control" type="text" id="notify_on_intup_subject_text" name="notify_on_intup_subject_text" value="${config['notify_on_intup_subject_text']}" data-parsley-trigger="change" required>
|
||||
<label for="notify_on_pmsupdate_subject_text">Subject Line</label>
|
||||
<input class="form-control" type="text" id="notify_on_pmsupdate_subject_text" name="notify_on_pmsupdate_subject_text" value="${config['notify_on_pmsupdate_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_intup_body_text">Message Body</label>
|
||||
<textarea class="form-control" id="notify_on_intup_body_text" name="notify_on_intup_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_intup_body_text']}</textarea>
|
||||
<label for="notify_on_extup_body_text">Message Body</label>
|
||||
<textarea class="form-control" id="notify_on_pmsupdate_body_text" name="notify_on_pmsupdate_body_text" data-parsley-trigger="change" data-autoresize required>${config['notify_on_pmsupdate_body_text']}</textarea>
|
||||
<p class="help-block">Set a custom body.</p>
|
||||
</div>
|
||||
</li>
|
||||
@@ -1020,24 +1086,45 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
||||
<h4 class="modal-title">Date & Time Format Options</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<table>
|
||||
<table class="notification-params time-options">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Year
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" colspan="3"><h5>Year</h5></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>YYYY</strong></td>
|
||||
<td>Numeric, 4 digits</td>
|
||||
<td>Numeric, four digits</td>
|
||||
<td>Eg., 1999, 2003</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>YY</strong></td>
|
||||
<td>Numeric, 2 digits</td>
|
||||
<td>Numeric, two digits</td>
|
||||
<td>Eg., 99, 03</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="notification-params time-options">
|
||||
<thead>
|
||||
<tr>
|
||||
<td align="center" colspan="3"><h5>Month</h5></td>
|
||||
<th>
|
||||
Month
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>MMMM</strong></td>
|
||||
<td>Textual, full</td>
|
||||
<td>January-December</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>MMM</strong></td>
|
||||
<td>Textual, three letters</td>
|
||||
<td>Jan-Dec</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>MM</strong></td>
|
||||
@@ -1049,23 +1136,41 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
||||
<td>Numeric, without leading zeros</td>
|
||||
<td>1-12</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="notification-params time-options">
|
||||
<thead>
|
||||
<tr>
|
||||
<td><strong>MMMM</strong></td>
|
||||
<td>Textual full</td>
|
||||
<td>January-December</td>
|
||||
<th>
|
||||
Day of the Year
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>DDDD</strong></td>
|
||||
<td>Numeric, with leading zeros</td>
|
||||
<td>001-365</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>MMM</strong></td>
|
||||
<td>Textual three letters</td>
|
||||
<td>Jan-Dec</td>
|
||||
<td><strong>DDD</strong></td>
|
||||
<td>Numeric, without leading zeros</td>
|
||||
<td>1-365</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="notification-params time-options">
|
||||
<thead>
|
||||
<tr>
|
||||
<td align="center" colspan="3"><h5>Day</h5></td>
|
||||
<th>
|
||||
Day of the Month
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="100"><strong>DD</strong></td>
|
||||
<td width="300">Numeric, with leading zeros</td>
|
||||
<td><strong>DD</strong></td>
|
||||
<td>Numeric, with leading zeros</td>
|
||||
<td>01-31</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -1075,57 +1180,165 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Do</strong></td>
|
||||
<td>The English suffix for the day of the month</td>
|
||||
<td>st, nd or th in the 1st, 2nd or 15th.</td>
|
||||
<td>Numeric, with suffix</td>
|
||||
<td>Eg., 1st, 2nd ... 31st.</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="notification-params time-options">
|
||||
<thead>
|
||||
<tr>
|
||||
<td align="center" colspan="3"><h5>Time</h5></td>
|
||||
<th>
|
||||
Day of the Week
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>dddd</strong></td>
|
||||
<td>Textual, full</td>
|
||||
<td>Sunday-Saturday</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>a</strong></td>
|
||||
<td width="300">am/pm Lowercase</td>
|
||||
<td>am, pm</td>
|
||||
<td><strong>ddd</strong></td>
|
||||
<td>Textual, three letters</td>
|
||||
<td>Sun-Sat</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>A</strong></td>
|
||||
<td>AM/PM Uppercase</td>
|
||||
<td>AM, PM</td>
|
||||
<td><strong>d</strong></td>
|
||||
<td>Numeric</td>
|
||||
<td>0-6</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="notification-params time-options">
|
||||
<thead>
|
||||
<tr>
|
||||
<td><strong>h</strong></td>
|
||||
<td>Hour, 12-hour, without leading zeros</td>
|
||||
<td>1-12</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>hh</strong></td>
|
||||
<td>Hour, 12-hour, with leading zeros</td>
|
||||
<td>01-12</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>H</strong></td>
|
||||
<td>Hour, 24-hour, without leading zeros</td>
|
||||
<td>0-23</td>
|
||||
<th>
|
||||
Hour
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>HH</strong></td>
|
||||
<td>Hour, 24-hour, with leading zeros</td>
|
||||
<td>24-hour, with leading zeros</td>
|
||||
<td>00-23</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>H</strong></td>
|
||||
<td>24-hour, without leading zeros</td>
|
||||
<td>0-23</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>hh</strong></td>
|
||||
<td>12-hour, with leading zeros</td>
|
||||
<td>01-12</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>h</strong></td>
|
||||
<td>12-hour, without leading zeros</td>
|
||||
<td>1-12</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="notification-params time-options">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Minute
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>mm</strong></td>
|
||||
<td>Minutes, with leading zeros</td>
|
||||
<td>Numeric, with leading zeros</td>
|
||||
<td>00-59</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>m</strong></td>
|
||||
<td>Numeric, without leading zeros</td>
|
||||
<td>0-59</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="notification-params time-options">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Second
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>ss</strong></td>
|
||||
<td>Seconds, with leading zeros</td>
|
||||
<td>Numeric, with leading zeros</td>
|
||||
<td>00-59</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>zz</strong></td>
|
||||
<td>Timezone abbreviation</td>
|
||||
<td>Eg., EST, MDT ...</td>
|
||||
<td><strong>s</strong></td>
|
||||
<td>Numeric, without leading zeros</td>
|
||||
<td>0-59</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="notification-params time-options">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
AM / PM
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>A</strong></td>
|
||||
<td>AM/PM uppercase</td>
|
||||
<td>AM, PM</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>a</strong></td>
|
||||
<td width="300">am/pm lowercase</td>
|
||||
<td>am, pm</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="notification-params time-options">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Timezone
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>ZZ</strong></td>
|
||||
<td>UTC offset</td>
|
||||
<td>Eg., +0100, -0700</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Z</strong></td>
|
||||
<td>UTC offset</td>
|
||||
<td>Eg., +01:00, -07:00</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="notification-params time-options">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Timestamp
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>X</strong></td>
|
||||
<td>Unix timestamp</td>
|
||||
<td>Eg., 1456887825</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -1210,6 +1423,10 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
||||
<td><strong>{server_uptime}</strong></td>
|
||||
<td>The uptime (in days, hours, mins, secs) of your Plex Server.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>{server_version}</strong></td>
|
||||
<td>The current version of your Plex Server.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>{action}</strong></td>
|
||||
<td>The action that triggered the notification.</td>
|
||||
@@ -1402,7 +1619,7 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>{artist_name}</strong></td>
|
||||
<td>The name of the artistd.</td>
|
||||
<td>The name of the artist.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>{album_name}</strong></td>
|
||||
@@ -1482,8 +1699,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>
|
||||
@@ -1517,7 +1733,7 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>{lastfm_url}</strong></td>
|
||||
<td>The last.fm URL for the album.
|
||||
<td>The Last.fm URL for the album.
|
||||
<p class="small-muted">(PMS agent must be Last.fm)</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -1542,6 +1758,29 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="notification-params">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Plex Update Available
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>{update_version}</strong></td>
|
||||
<td>The available update version for your Plex Server.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>{update_url}</strong></td>
|
||||
<td>The available update download URL.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>{update_changelog}</strong></td>
|
||||
<td>The changelog for the available update.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer"></div>
|
||||
@@ -1646,10 +1885,11 @@ $(document).ready(function() {
|
||||
authChanged = false;
|
||||
httpChanged = false;
|
||||
monitorChanged = false;
|
||||
directoryChanged = false;
|
||||
|
||||
// Alert the user that their changes require a restart.
|
||||
function postSaveChecks() {
|
||||
if ((serverChanged && $('#monitoring_use_websocket').is(":checked")) || authChanged || httpChanged || monitorChanged) {
|
||||
if ((serverChanged && $('#monitoring_use_websocket').is(":checked")) || authChanged || httpChanged || monitorChanged || directoryChanged) {
|
||||
$('#restart-modal').modal('show');
|
||||
}
|
||||
}
|
||||
@@ -1762,6 +2002,10 @@ $(document).ready(function() {
|
||||
monitorChanged = true;
|
||||
});
|
||||
|
||||
$( ".directory-settings" ).change(function() {
|
||||
directoryChanged = true;
|
||||
});
|
||||
|
||||
$( ".pms-settings" ).change(function() {
|
||||
serverChanged = true;
|
||||
$("#pms_identifier").val("");
|
||||
@@ -1895,49 +2139,61 @@ $(document).ready(function() {
|
||||
$.get("/osxnotifyregister", { 'app': osx_notify_app }, function (data) { showMsg("<div class='msg'><span class='ui-icon ui-icon-check'></span>" + data + "</div>", false, true, 3000); });
|
||||
})
|
||||
|
||||
pms_version = false;
|
||||
pms_logs_debug = false;
|
||||
pms_logs = false;
|
||||
|
||||
// Checks to see if PMS server version is >= 0.9.14 with automaatically logged IP addresses
|
||||
$.ajax({
|
||||
url: 'get_server_identity',
|
||||
async: true,
|
||||
success: function(data) {
|
||||
var version = data.version.split('.')
|
||||
if (parseInt(version[0]) >= 0 && parseInt(version[1]) >= 9 && parseInt(version[2]) >= 14) {
|
||||
if (data.version){ $("#pms_version").text(data.version); }
|
||||
var version = (data.version ? data.version.split('.') : null);
|
||||
if (version && parseInt(version[0]) >= 0 && parseInt(version[1]) >= 9 && parseInt(version[2]) >= 14) {
|
||||
$("#debugLogCheck").html("IP address is automatically logged for PMS version 0.9.14 and above.");
|
||||
$("#ip_logging_enable").attr("disabled", true);
|
||||
$("#ip_logging_enable").attr("checked", true);
|
||||
pms_version = true;
|
||||
checkLogsPath();
|
||||
} else {
|
||||
// Check to see if debug logs are enabled on the PMS.
|
||||
$.ajax({
|
||||
url: 'get_server_pref',
|
||||
data: { pref: 'logDebug' },
|
||||
async: true,
|
||||
success: function(data) {
|
||||
if (data !== 'true') {
|
||||
$("#debugLogCheck").html("Debug logging must be enabled on your Plex Server. <a target='_blank' href='${anon_url('https://support.plex.tv/hc/en-us/articles/201643703-Reporting-issues-with-Plex-Media-Server')}'>Click here</a> for help.");
|
||||
$("#ip_logging_enable").attr("disabled", true);
|
||||
$("#ip_logging_enable").attr("checked", false);
|
||||
pms_logs_debug = (data == 'true' ? true : false);
|
||||
// Check to see if our logs folder is set before allowing IP logging to be enabled.
|
||||
checkLogsPath();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Check to see if our logs folder is set before allowing IP logging to be enabled.
|
||||
checkLogsPath();
|
||||
|
||||
$("#pms_logs_folder").change(function() {
|
||||
checkLogsPath();
|
||||
});
|
||||
|
||||
function checkLogsPath() {
|
||||
if ($("#pms_logs_folder").val() == '') {
|
||||
$("#debugLogCheck").html("You must first define your Plex Server Logs folder path under the Plex Media Server tab.");
|
||||
pms_logs = ($("#pms_logs_folder").val() == '' ? false : true);
|
||||
// Toggle IP logging checkbox depending on debug logs, and logs path
|
||||
if (!(pms_version)) {
|
||||
if (pms_logs_debug && pms_logs) {
|
||||
$("#ip_logging_enable").attr("disabled", false);
|
||||
$("#debugLogCheck").html("");
|
||||
} else if (!(pms_logs_debug)) {
|
||||
$("#debugLogCheck").html("Debug logging must be enabled on your Plex Server. <a target='_blank' href='${anon_url('https://support.plex.tv/hc/en-us/articles/201643703-Reporting-issues-with-Plex-Media-Server')}'>Click here</a> for help.");
|
||||
$("#ip_logging_enable").attr("disabled", true);
|
||||
$("#ip_logging_enable").attr("checked", false);
|
||||
} else {
|
||||
$("#ip_logging_enable").attr("disabled", false);
|
||||
$("#debugLogCheck").html("");
|
||||
$("#debugLogCheck").html("You must first define your Plex Server Logs folder path under the Plex Media Server tab.");
|
||||
$("#ip_logging_enable").attr("disabled", true);
|
||||
$("#ip_logging_enable").attr("checked", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
url: 'get_server_pref',
|
||||
@@ -1947,6 +2203,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);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -2050,6 +2307,10 @@ $(document).ready(function() {
|
||||
}
|
||||
getSchedulerTable();
|
||||
|
||||
$("#notify_recently_added_grandparent").change(function () {
|
||||
var c = this.checked ? '#eb8600' : '#737373';
|
||||
$('#notify_recently_added_grandparent_note').css('color', c);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</%def>
|
@@ -112,8 +112,16 @@ from plexpy import helpers
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="table-card-header">
|
||||
<ul class="nav nav-header nav-dashboard pull-right">
|
||||
<li>
|
||||
<a href="#" id="recently-watched-page-left" class="paginate btn-gray disabled" data-id="+1"><i class="fa fa-lg fa-chevron-left"></i></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" id="recently-watched-page-right" class="paginate btn-gray" data-id="-1"><i class="fa fa-lg fa-chevron-right"></i></a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="header-bar">
|
||||
<span><i class="fa fa-history"></i> Recently Watched</span>
|
||||
<span><i class="fa fa-history"></i> Recently Played</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-card-back">
|
||||
@@ -140,7 +148,7 @@ from plexpy import helpers
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-card-back">
|
||||
<table id="user_ip_table" class="display" width="100%">
|
||||
<table class="display user_ip_table" id="user_ip_table-UID-${data['user_id']}" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="left">Last Seen</th>
|
||||
@@ -178,7 +186,7 @@ from plexpy import helpers
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-card-back">
|
||||
<table class="display" id="history_table" width="100%">
|
||||
<table class="display history_table" id="history_table-UID-${data['user_id']}" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="left" id="delete">Delete</th>
|
||||
@@ -218,7 +226,7 @@ from plexpy import helpers
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-card-back">
|
||||
<table class="display" id="sync_table" width="100%">
|
||||
<table class="display" id="sync_table-UID-${data['user_id']}" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="left" id="state">State</th>
|
||||
@@ -348,13 +356,13 @@ from plexpy import helpers
|
||||
};
|
||||
}
|
||||
}
|
||||
history_table = $('#history_table').DataTable(history_table_options);
|
||||
history_table = $('#history_table-UID-${data["user_id"]}').DataTable(history_table_options);
|
||||
history_table.column(2).visible(false);
|
||||
|
||||
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 11] });
|
||||
$(colvis.button()).appendTo('#button-bar-history');
|
||||
|
||||
clearSearchButton('history_table', history_table);
|
||||
clearSearchButton('history_table-UID-${data["user_id"]}', 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"> \
|
||||
@@ -397,9 +405,9 @@ from plexpy import helpers
|
||||
};
|
||||
}
|
||||
}
|
||||
user_ip_table = $('#user_ip_table').DataTable(user_ip_table_options);
|
||||
user_ip_table = $('#user_ip_table-UID-${data["user_id"]}').DataTable(user_ip_table_options);
|
||||
|
||||
clearSearchButton('user_ip_table', user_ip_table);
|
||||
clearSearchButton('user_ip_table-UID-${data["user_id"]}', user_ip_table);
|
||||
});
|
||||
|
||||
$( "#sync-tab-btn" ).one( "click", function() {
|
||||
@@ -410,13 +418,13 @@ from plexpy import helpers
|
||||
d.user_id = user_id;
|
||||
}
|
||||
}
|
||||
sync_table = $('#sync_table').DataTable(sync_table_options);
|
||||
sync_table = $('#sync_table-UID-${data["user_id"]}').DataTable(sync_table_options);
|
||||
sync_table.column(1).visible(false);
|
||||
|
||||
var colvis_sync = new $.fn.dataTable.ColVis( sync_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark' } );
|
||||
$( colvis_sync.button() ).appendTo('#button-bar-sync');
|
||||
|
||||
clearSearchButton('sync_table', sync_table);
|
||||
clearSearchButton('sync_table-UID-${data["user_id"]}', sync_table);
|
||||
});
|
||||
|
||||
// Load edit user modal
|
||||
@@ -471,32 +479,60 @@ from plexpy import helpers
|
||||
});
|
||||
|
||||
function recentlyWatched() {
|
||||
var widthVal = $("#user-recently-watched").width();
|
||||
var tmp = (widthVal-25) / 175;
|
||||
|
||||
if (tmp > 0) {
|
||||
var containerSize = parseInt(tmp);
|
||||
} else {
|
||||
var containerSize = 1;
|
||||
}
|
||||
|
||||
// Populate recently watched
|
||||
$.ajax({
|
||||
url: 'get_user_recently_watched',
|
||||
async: true,
|
||||
data: {
|
||||
user_id: user_id,
|
||||
limit: containerSize
|
||||
limit: 50
|
||||
},
|
||||
complete: function(xhr, status) {
|
||||
$("#user-recently-watched").html(xhr.responseText);
|
||||
highlightWatchedScrollerButton();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
recentlyWatched();
|
||||
|
||||
function highlightWatchedScrollerButton() {
|
||||
var scroller = $("#recently-watched-row-scroller");
|
||||
var numElems = scroller.find("li").length;
|
||||
scroller.width(numElems * 175);
|
||||
if (scroller.width() > $("#user-recently-watched").width()) {
|
||||
$("#recently-watched-page-right").removeClass("disabled");
|
||||
} else {
|
||||
$("#recently-watched-page-right").addClass("disabled");
|
||||
}
|
||||
}
|
||||
|
||||
$(window).resize(function() {
|
||||
recentlyWatched();
|
||||
highlightWatchedScrollerButton();
|
||||
});
|
||||
|
||||
var leftTotal = 0;
|
||||
$(".paginate").click(function (e) {
|
||||
e.preventDefault();
|
||||
var scroller = $("#recently-watched-row-scroller");
|
||||
var containerWidth = $("#user-recently-watched").width();
|
||||
var scrollAmount = $(this).data("id") * parseInt(containerWidth / 175) * 175;
|
||||
var leftMax = Math.min(-parseInt(scroller.width()) + Math.abs(scrollAmount), 0);
|
||||
|
||||
leftTotal = Math.max(Math.min(leftTotal + scrollAmount, 0), leftMax);
|
||||
scroller.animate({ left: leftTotal }, 250);
|
||||
|
||||
if (leftTotal == 0) {
|
||||
$("#recently-watched-page-left").addClass("disabled").blur();
|
||||
} else {
|
||||
$("#recently-watched-page-left").removeClass("disabled");
|
||||
}
|
||||
|
||||
if (leftTotal == leftMax) {
|
||||
$("#recently-watched-page-right").addClass("disabled").blur();
|
||||
} else {
|
||||
$("#recently-watched-page-right").removeClass("disabled");
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
@@ -28,6 +28,7 @@ DOCUMENTATION :: END
|
||||
|
||||
% if data:
|
||||
<div class="dashboard-recent-media-row">
|
||||
<div id="recently-watched-row-scroller" style="left: 0;">
|
||||
<ul class="dashboard-recent-media list-unstyled">
|
||||
% for item in data:
|
||||
<li>
|
||||
@@ -79,6 +80,7 @@ DOCUMENTATION :: END
|
||||
% endfor
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="text-muted">No stats to show.</div><br>
|
||||
% endif
|
@@ -33,12 +33,13 @@ load_rc_config ${name}
|
||||
: ${plexpy_dir:="/usr/local/plexpy"}
|
||||
: ${plexpy_chdir:="${plexpy_dir}"}
|
||||
: ${plexpy_pid:="${plexpy_dir}/plexpy.pid"}
|
||||
: ${plexpy_flags:=""}
|
||||
|
||||
status_cmd="${name}_status"
|
||||
stop_cmd="${name}_stop"
|
||||
|
||||
command="/usr/sbin/daemon"
|
||||
command_args="python2 ${plexpy_dir}/PlexPy.py --daemon --pidfile ${plexpy_pid} --quiet --nolaunch"
|
||||
command="${plexpy_dir}/PlexPy.py"
|
||||
command_args="--daemon --pidfile ${plexpy_pid} --quiet --nolaunch ${plexpy_flags}"
|
||||
|
||||
# Ensure user is root when running this script.
|
||||
if [ `id -u` != "0" ]; then
|
||||
|
5
init-scripts/init.freenas
Normal file → Executable file
5
init-scripts/init.freenas
Normal file → Executable file
@@ -33,12 +33,13 @@ load_rc_config ${name}
|
||||
: ${plexpy_dir:="/usr/local/share/plexpy"}
|
||||
: ${plexpy_chdir:="${plexpy_dir}"}
|
||||
: ${plexpy_pid:="${plexpy_dir}/plexpy.pid"}
|
||||
: ${plexpy_flags:=""}
|
||||
|
||||
status_cmd="${name}_status"
|
||||
stop_cmd="${name}_stop"
|
||||
|
||||
command="/usr/sbin/daemon"
|
||||
command_args="python2 ${plexpy_dir}/PlexPy.py --daemon --pidfile ${plexpy_pid} --quiet --nolaunch"
|
||||
command="${plexpy_dir}/PlexPy.py"
|
||||
command_args="--daemon --pidfile ${plexpy_pid} --quiet --nolaunch ${plexpy_flags}"
|
||||
|
||||
# Ensure user is root when running this script.
|
||||
if [ `id -u` != "0" ]; then
|
||||
|
@@ -103,7 +103,7 @@ def initialize(config_file):
|
||||
if not CONFIG.HTTPS_KEY:
|
||||
CONFIG.HTTPS_KEY = os.path.join(DATA_DIR, 'server.key')
|
||||
|
||||
if not CONFIG.LOG_DIR.startswith(os.path.abspath(DATA_DIR)):
|
||||
if not CONFIG.LOG_DIR:
|
||||
CONFIG.LOG_DIR = os.path.join(DATA_DIR, 'logs')
|
||||
|
||||
if not os.path.exists(CONFIG.LOG_DIR):
|
||||
@@ -120,8 +120,7 @@ def initialize(config_file):
|
||||
logger.initLogger(console=not QUIET, log_dir=CONFIG.LOG_DIR,
|
||||
verbose=VERBOSE)
|
||||
|
||||
if not CONFIG.BACKUP_DIR.startswith(os.path.abspath(DATA_DIR)):
|
||||
# Put the backup dir in the data dir for now
|
||||
if not CONFIG.BACKUP_DIR:
|
||||
CONFIG.BACKUP_DIR = os.path.join(DATA_DIR, 'backups')
|
||||
if not os.path.exists(CONFIG.BACKUP_DIR):
|
||||
try:
|
||||
@@ -129,14 +128,13 @@ def initialize(config_file):
|
||||
except OSError as e:
|
||||
logger.error("Could not create backup dir '%s': %s", BACKUP_DIR, e)
|
||||
|
||||
if not CONFIG.CACHE_DIR.startswith(os.path.abspath(DATA_DIR)):
|
||||
# Put the cache dir in the data dir for now
|
||||
if not CONFIG.CACHE_DIR:
|
||||
CONFIG.CACHE_DIR = os.path.join(DATA_DIR, 'cache')
|
||||
if not os.path.exists(CONFIG.CACHE_DIR):
|
||||
try:
|
||||
os.makedirs(CONFIG.CACHE_DIR)
|
||||
except OSError as e:
|
||||
logger.error("Could not create cache dir '%s': %s", DATA_DIR, e)
|
||||
logger.error("Could not create cache dir '%s': %s", CACHE_DIR, e)
|
||||
|
||||
# Initialize the database
|
||||
logger.info('Checking to see if the database has all tables....')
|
||||
@@ -306,6 +304,13 @@ def initialize_scheduler():
|
||||
schedule_job(activity_pinger.check_recently_added, 'Check for recently added items',
|
||||
hours=0, minutes=0, seconds=0)
|
||||
|
||||
if CONFIG.MONITOR_PMS_UPDATES:
|
||||
schedule_job(activity_pinger.check_server_updates, 'Check for Plex updates',
|
||||
hours=12, minutes=0, seconds=0)
|
||||
else:
|
||||
schedule_job(activity_pinger.check_server_updates, 'Check for Plex updates',
|
||||
hours=0, minutes=0, seconds=0)
|
||||
|
||||
if CONFIG.MONITOR_REMOTE_ACCESS:
|
||||
schedule_job(activity_pinger.check_server_response, 'Check for Plex remote access',
|
||||
hours=0, minutes=0, seconds=seconds)
|
||||
@@ -390,7 +395,7 @@ def dbcheck():
|
||||
# sessions table :: This is a temp table that logs currently active sessions
|
||||
c_db.execute(
|
||||
'CREATE TABLE IF NOT EXISTS sessions (id INTEGER PRIMARY KEY AUTOINCREMENT, '
|
||||
'session_key INTEGER, rating_key INTEGER, section_id INTEGER, media_type TEXT, started INTEGER, '
|
||||
'session_key INTEGER, rating_key INTEGER, section_id INTEGER, media_type TEXT, started INTEGER, stopped INTEGER, '
|
||||
'paused_counter INTEGER DEFAULT 0, state TEXT, user_id INTEGER, user TEXT, friendly_name TEXT, '
|
||||
'ip_address TEXT, machine_id TEXT, player TEXT, platform TEXT, title TEXT, parent_title TEXT, '
|
||||
'grandparent_title TEXT, parent_rating_key INTEGER, grandparent_rating_key INTEGER, '
|
||||
@@ -413,8 +418,8 @@ def dbcheck():
|
||||
|
||||
# session_history_media_info table :: This is a table which logs each session's media info
|
||||
c_db.execute(
|
||||
'CREATE TABLE IF NOT EXISTS session_history_media_info (id INTEGER PRIMARY KEY, '
|
||||
'rating_key INTEGER, video_decision TEXT, audio_decision TEXT, duration INTEGER DEFAULT 0, width INTEGER, '
|
||||
'CREATE TABLE IF NOT EXISTS session_history_media_info (id INTEGER PRIMARY KEY, rating_key INTEGER, '
|
||||
'video_decision TEXT, audio_decision TEXT, transcode_decision TEXT, duration INTEGER DEFAULT 0, width INTEGER, '
|
||||
'height INTEGER, container TEXT, video_codec TEXT, audio_codec TEXT, bitrate INTEGER, video_resolution TEXT, '
|
||||
'video_framerate TEXT, aspect_ratio TEXT, audio_channels INTEGER, transcode_protocol TEXT, '
|
||||
'transcode_container TEXT, transcode_video_codec TEXT, transcode_audio_codec TEXT, '
|
||||
@@ -443,10 +448,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
|
||||
@@ -614,6 +619,15 @@ def dbcheck():
|
||||
'ALTER TABLE sessions ADD COLUMN section_id INTEGER'
|
||||
)
|
||||
|
||||
# Upgrade sessions table from earlier versions
|
||||
try:
|
||||
c_db.execute('SELECT stopped FROM sessions')
|
||||
except sqlite3.OperationalError:
|
||||
logger.debug(u"Altering database. Updating database table sessions.")
|
||||
c_db.execute(
|
||||
'ALTER TABLE sessions ADD COLUMN stopped INTEGER'
|
||||
)
|
||||
|
||||
# Upgrade session_history table from earlier versions
|
||||
try:
|
||||
c_db.execute('SELECT reference_id FROM session_history')
|
||||
@@ -664,6 +678,21 @@ def dbcheck():
|
||||
'ALTER TABLE session_history_metadata ADD COLUMN section_id INTEGER'
|
||||
)
|
||||
|
||||
# Upgrade session_history_media_info table from earlier versions
|
||||
try:
|
||||
c_db.execute('SELECT transcode_decision FROM session_history_media_info')
|
||||
except sqlite3.OperationalError:
|
||||
logger.debug(u"Altering database. Updating database table session_history_media_info.")
|
||||
c_db.execute(
|
||||
'ALTER TABLE session_history_media_info ADD COLUMN transcode_decision TEXT'
|
||||
)
|
||||
c_db.execute(
|
||||
'UPDATE session_history_media_info SET transcode_decision = (CASE '
|
||||
'WHEN video_decision = "transcode" OR audio_decision = "transcode" THEN "transcode" '
|
||||
'WHEN video_decision = "copy" OR audio_decision = "copy" THEN "copy" '
|
||||
'WHEN video_decision = "direct play" OR audio_decision = "direct play" THEN "direct play" END)'
|
||||
)
|
||||
|
||||
# Upgrade users table from earlier versions
|
||||
try:
|
||||
c_db.execute('SELECT do_notify FROM users')
|
||||
@@ -724,6 +753,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()
|
||||
@@ -843,6 +919,12 @@ def shutdown(restart=False, update=False):
|
||||
if '--nolaunch' not in args:
|
||||
args += ['--nolaunch']
|
||||
logger.info('Restarting PlexPy with %s', args)
|
||||
|
||||
# os.execv fails with spaced names on Windows
|
||||
# https://bugs.python.org/issue19066
|
||||
if os.name == 'nt':
|
||||
subprocess.Popen(args, cwd=os.getcwd())
|
||||
else:
|
||||
os.execv(exe, args)
|
||||
|
||||
os._exit(0)
|
||||
|
@@ -16,7 +16,7 @@
|
||||
import time
|
||||
import plexpy
|
||||
|
||||
from plexpy import logger, pmsconnect, activity_processor, threading, notification_handler, helpers
|
||||
from plexpy import logger, pmsconnect, activity_processor, threading, notification_handler, helpers, notifiers
|
||||
|
||||
|
||||
class ActivityHandler(object):
|
||||
@@ -57,6 +57,8 @@ class ActivityHandler(object):
|
||||
if self.is_valid_session() and self.get_live_session():
|
||||
logger.debug(u"PlexPy ActivityHandler :: Session %s has started." % str(self.get_session_key()))
|
||||
|
||||
# Check if any notification agents have notifications enabled
|
||||
if any(d['on_play'] for d in notifiers.available_notification_agents()):
|
||||
# Fire off notifications
|
||||
threading.Thread(target=notification_handler.notify,
|
||||
kwargs=dict(stream_data=self.get_live_session(), notify_action='play')).start()
|
||||
@@ -77,11 +79,14 @@ class ActivityHandler(object):
|
||||
if not force_stop:
|
||||
ap.set_session_state(session_key=self.get_session_key(),
|
||||
state=self.timeline['state'],
|
||||
view_offset=self.timeline['viewOffset'])
|
||||
view_offset=self.timeline['viewOffset'],
|
||||
stopped=int(time.time()))
|
||||
|
||||
# Retrieve the session data from our temp table
|
||||
db_session = ap.get_session_by_key(session_key=self.get_session_key())
|
||||
|
||||
# Check if any notification agents have notifications enabled
|
||||
if any(d['on_stop'] for d in notifiers.available_notification_agents()):
|
||||
# Fire off notifications
|
||||
threading.Thread(target=notification_handler.notify,
|
||||
kwargs=dict(stream_data=db_session, notify_action='stop')).start()
|
||||
@@ -91,6 +96,7 @@ class ActivityHandler(object):
|
||||
monitor_proc.write_session_history(session=db_session)
|
||||
|
||||
# Remove the session from our temp session table
|
||||
logger.debug(u"PlexPy ActivityHandler :: Removing session %s from session queue" % str(self.get_session_key()))
|
||||
ap.delete_session(session_key=self.get_session_key())
|
||||
|
||||
def on_pause(self):
|
||||
@@ -109,6 +115,8 @@ class ActivityHandler(object):
|
||||
# Retrieve the session data from our temp table
|
||||
db_session = ap.get_session_by_key(session_key=self.get_session_key())
|
||||
|
||||
# Check if any notification agents have notifications enabled
|
||||
if any(d['on_pause'] for d in notifiers.available_notification_agents()):
|
||||
# Fire off notifications
|
||||
threading.Thread(target=notification_handler.notify,
|
||||
kwargs=dict(stream_data=db_session, notify_action='pause')).start()
|
||||
@@ -129,6 +137,8 @@ class ActivityHandler(object):
|
||||
# Retrieve the session data from our temp table
|
||||
db_session = ap.get_session_by_key(session_key=self.get_session_key())
|
||||
|
||||
# Check if any notification agents have notifications enabled
|
||||
if any(d['on_resume'] for d in notifiers.available_notification_agents()):
|
||||
# Fire off notifications
|
||||
threading.Thread(target=notification_handler.notify,
|
||||
kwargs=dict(stream_data=db_session, notify_action='resume')).start()
|
||||
@@ -159,6 +169,9 @@ class ActivityHandler(object):
|
||||
if plexpy.CONFIG.BUFFER_THRESHOLD > 0 and (current_buffer_count >= plexpy.CONFIG.BUFFER_THRESHOLD and \
|
||||
time_since_last_trigger == 0 or time_since_last_trigger >= plexpy.CONFIG.BUFFER_WAIT):
|
||||
ap.set_session_buffer_trigger_time(session_key=self.get_session_key())
|
||||
|
||||
# Check if any notification agents have notifications enabled
|
||||
if any(d['on_buffer'] for d in notifiers.available_notification_agents()):
|
||||
threading.Thread(target=notification_handler.notify,
|
||||
kwargs=dict(stream_data=db_stream, notify_action='buffer')).start()
|
||||
|
||||
@@ -202,10 +215,17 @@ class ActivityHandler(object):
|
||||
|
||||
# Monitor if the stream has reached the watch percentage for notifications
|
||||
# The only purpose of this is for notifications
|
||||
# Check if any notification agents have notifications enabled
|
||||
notify_agents = [d['id'] for d in notifiers.available_notification_agents() if d['on_watched']]
|
||||
# Get the current states for notifications from our db
|
||||
notified_agents = [d['agent_id'] for d in notification_handler.get_notify_state(session=db_session)
|
||||
if d['notify_action'] == 'watched'] if notify_agents else []
|
||||
|
||||
if any(a not in notified_agents for a in notify_agents):
|
||||
progress_percent = helpers.get_percent(self.timeline['viewOffset'], db_session['duration'])
|
||||
if progress_percent >= plexpy.CONFIG.NOTIFY_WATCHED_PERCENT and this_state != 'buffering':
|
||||
threading.Thread(target=notification_handler.notify,
|
||||
kwargs=dict(stream_data=db_session, notify_action='watched')).start()
|
||||
# Rather not put this on it's own thread so we know it completes before our next event.
|
||||
notification_handler.notify(stream_data=db_session, notify_action='watched')
|
||||
|
||||
else:
|
||||
# We don't have this session in our table yet, start a new one.
|
||||
|
@@ -13,7 +13,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from plexpy import logger, pmsconnect, plextv, notification_handler, database, helpers, activity_processor, libraries
|
||||
from plexpy import logger, pmsconnect, plextv, notification_handler, database, helpers, activity_processor, libraries, notifiers
|
||||
|
||||
import threading
|
||||
import plexpy
|
||||
@@ -38,6 +38,9 @@ def check_active_sessions(ws_request=False):
|
||||
if session_list:
|
||||
if int_ping_count >= 3:
|
||||
logger.info(u"PlexPy Monitor :: The Plex Media Server is back up.")
|
||||
|
||||
# Check if any notification agents have notifications enabled
|
||||
if any(d['on_intup'] for d in notifiers.available_notification_agents()):
|
||||
# Fire off notifications
|
||||
threading.Thread(target=notification_handler.notify_timeline,
|
||||
kwargs=dict(notify_action='intup')).start()
|
||||
@@ -58,12 +61,20 @@ def check_active_sessions(ws_request=False):
|
||||
# Here we can check the play states
|
||||
if session['state'] != stream['state']:
|
||||
if session['state'] == 'paused':
|
||||
logger.debug(u"PlexPy Monitor :: Session %s has been paused." % stream['session_key'])
|
||||
|
||||
# Check if any notification agents have notifications enabled
|
||||
if any(d['on_pause'] for d in notifiers.available_notification_agents()):
|
||||
# Push any notifications -
|
||||
# Push it on it's own thread so we don't hold up our db actions
|
||||
threading.Thread(target=notification_handler.notify,
|
||||
kwargs=dict(stream_data=stream, notify_action='pause')).start()
|
||||
|
||||
if session['state'] == 'playing' and stream['state'] == 'paused':
|
||||
logger.debug(u"PlexPy Monitor :: Session %s has been resumed." % stream['session_key'])
|
||||
|
||||
# Check if any notification agents have notifications enabled
|
||||
if any(d['on_resume'] for d in notifiers.available_notification_agents()):
|
||||
# Push any notifications -
|
||||
# Push it on it's own thread so we don't hold up our db actions
|
||||
threading.Thread(target=notification_handler.notify,
|
||||
@@ -105,6 +116,10 @@ def check_active_sessions(ws_request=False):
|
||||
'WHERE session_key = ? AND rating_key = ?',
|
||||
[stream['session_key'], stream['rating_key']])
|
||||
|
||||
# Check if any notification agents have notifications enabled
|
||||
if any(d['on_buffer'] for d in notifiers.available_notification_agents()):
|
||||
# Push any notifications -
|
||||
# Push it on it's own thread so we don't hold up our db actions
|
||||
threading.Thread(target=notification_handler.notify,
|
||||
kwargs=dict(stream_data=stream, notify_action='buffer')).start()
|
||||
else:
|
||||
@@ -119,11 +134,16 @@ def check_active_sessions(ws_request=False):
|
||||
'WHERE session_key = ? AND rating_key = ?',
|
||||
[stream['session_key'], stream['rating_key']])
|
||||
|
||||
# Check if any notification agents have notifications enabled
|
||||
if any(d['on_buffer'] for d in notifiers.available_notification_agents()):
|
||||
# Push any notifications -
|
||||
# Push it on it's own thread so we don't hold up our db actions
|
||||
threading.Thread(target=notification_handler.notify,
|
||||
kwargs=dict(stream_data=stream, notify_action='buffer')).start()
|
||||
|
||||
logger.debug(u"PlexPy Monitor :: Stream buffering. Count is now %s. Last triggered %s."
|
||||
% (buffer_values[0]['buffer_count'],
|
||||
logger.debug(u"PlexPy Monitor :: Session %s is buffering. Count is now %s. Last triggered %s."
|
||||
% (stream['session_key'],
|
||||
buffer_values[0]['buffer_count'],
|
||||
buffer_values[0]['buffer_last_triggered']))
|
||||
|
||||
# Check if the user has reached the offset in the media we defined as the "watched" percent
|
||||
@@ -132,6 +152,8 @@ def check_active_sessions(ws_request=False):
|
||||
if session['view_offset'] and session['duration'] and session['state'] != 'buffering':
|
||||
if helpers.get_percent(session['view_offset'],
|
||||
session['duration']) > plexpy.CONFIG.NOTIFY_WATCHED_PERCENT:
|
||||
# Check if any notification agents have notifications enabled
|
||||
if any(d['on_watched'] for d in notifiers.available_notification_agents()):
|
||||
# Push any notifications -
|
||||
# Push it on it's own thread so we don't hold up our db actions
|
||||
threading.Thread(target=notification_handler.notify,
|
||||
@@ -139,30 +161,52 @@ def check_active_sessions(ws_request=False):
|
||||
|
||||
else:
|
||||
# The user has stopped playing a stream
|
||||
logger.debug(u"PlexPy Monitor :: Removing sessionKey %s ratingKey %s from session queue"
|
||||
% (stream['session_key'], stream['rating_key']))
|
||||
monitor_db.action('DELETE FROM sessions WHERE session_key = ? AND rating_key = ?',
|
||||
[stream['session_key'], stream['rating_key']])
|
||||
if stream['state'] != 'stopped':
|
||||
logger.debug(u"PlexPy Monitor :: Session %s has stopped." % stream['session_key'])
|
||||
|
||||
# Set the stream stop time
|
||||
stream['stopped'] = int(time.time())
|
||||
monitor_db.action('UPDATE sessions SET stopped = ?, state = ? '
|
||||
'WHERE session_key = ? AND rating_key = ?',
|
||||
[stream['stopped'], 'stopped', stream['session_key'], stream['rating_key']])
|
||||
|
||||
# Check if the user has reached the offset in the media we defined as the "watched" percent
|
||||
if stream['view_offset'] and stream['duration']:
|
||||
if helpers.get_percent(stream['view_offset'],
|
||||
stream['duration']) > plexpy.CONFIG.NOTIFY_WATCHED_PERCENT:
|
||||
# Check if any notification agents have notifications enabled
|
||||
if any(d['on_watched'] for d in notifiers.available_notification_agents()):
|
||||
# Push any notifications -
|
||||
# Push it on it's own thread so we don't hold up our db actions
|
||||
threading.Thread(target=notification_handler.notify,
|
||||
kwargs=dict(stream_data=stream, notify_action='watched')).start()
|
||||
|
||||
# Check if any notification agents have notifications enabled
|
||||
if any(d['on_stop'] for d in notifiers.available_notification_agents()):
|
||||
# Push any notifications - Push it on it's own thread so we don't hold up our db actions
|
||||
threading.Thread(target=notification_handler.notify,
|
||||
kwargs=dict(stream_data=stream, notify_action='stop')).start()
|
||||
|
||||
# Write the item history on playback stop
|
||||
monitor_process.write_session_history(session=stream)
|
||||
success = monitor_process.write_session_history(session=stream)
|
||||
|
||||
if success:
|
||||
# If session is written to the databaase successfully, remove the session from the session table
|
||||
logger.debug(u"PlexPy Monitor :: Removing sessionKey %s ratingKey %s from session queue"
|
||||
% (stream['session_key'], stream['rating_key']))
|
||||
monitor_db.action('DELETE FROM sessions WHERE session_key = ? AND rating_key = ?',
|
||||
[stream['session_key'], stream['rating_key']])
|
||||
else:
|
||||
logger.warn(u"PlexPy Monitor :: Failed to write sessionKey %s ratingKey %s to the database. " \
|
||||
"Will try again on the next pass." % (stream['session_key'], stream['rating_key']))
|
||||
|
||||
# Process the newly received session data
|
||||
for session in media_container:
|
||||
monitor_process.write_session(session)
|
||||
new_session = monitor_process.write_session(session)
|
||||
|
||||
if new_session:
|
||||
logger.debug(u"PlexPy Monitor :: Session %s has started." % session['session_key'])
|
||||
|
||||
else:
|
||||
logger.debug(u"PlexPy Monitor :: Unable to read session list.")
|
||||
|
||||
@@ -171,6 +215,8 @@ def check_active_sessions(ws_request=False):
|
||||
% str(int_ping_count))
|
||||
|
||||
if int_ping_count == 3:
|
||||
# Check if any notification agents have notifications enabled
|
||||
if any(d['on_intdown'] for d in notifiers.available_notification_agents()):
|
||||
# Fire off notifications
|
||||
threading.Thread(target=notification_handler.notify_timeline,
|
||||
kwargs=dict(notify_action='intdown')).start()
|
||||
@@ -225,6 +271,9 @@ def check_recently_added():
|
||||
|
||||
if 0 < time_threshold - int(item['added_at']) <= time_interval:
|
||||
logger.debug(u"PlexPy Monitor :: Library item %s has been added to Plex." % str(item['rating_key']))
|
||||
|
||||
# Check if any notification agents have notifications enabled
|
||||
if any(d['on_created'] for d in notifiers.available_notification_agents()):
|
||||
# Fire off notifications
|
||||
threading.Thread(target=notification_handler.notify_timeline,
|
||||
kwargs=dict(timeline_data=item, notify_action='created')).start()
|
||||
@@ -243,6 +292,9 @@ def check_recently_added():
|
||||
% str(item['rating_key']))
|
||||
|
||||
logger.debug(u"PlexPy Monitor :: Library item %s has been added to Plex." % str(item['rating_key']))
|
||||
|
||||
# Check if any notification agents have notifications enabled
|
||||
if any(d['on_created'] for d in notifiers.available_notification_agents()):
|
||||
# Fire off notifications
|
||||
threading.Thread(target=notification_handler.notify_timeline,
|
||||
kwargs=dict(timeline_data=item, notify_action='created')).start()
|
||||
@@ -275,12 +327,44 @@ def check_server_response():
|
||||
else:
|
||||
if ext_ping_count >= 3:
|
||||
logger.info(u"PlexPy Monitor :: Plex remote access is back up.")
|
||||
|
||||
# Check if any notification agents have notifications enabled
|
||||
if any(d['on_extup'] for d in notifiers.available_notification_agents()):
|
||||
# Fire off notifications
|
||||
threading.Thread(target=notification_handler.notify_timeline,
|
||||
kwargs=dict(notify_action='extup')).start()
|
||||
ext_ping_count = 0
|
||||
|
||||
if ext_ping_count == 3:
|
||||
# Check if any notification agents have notifications enabled
|
||||
if any(d['on_extdown'] for d in notifiers.available_notification_agents()):
|
||||
# Fire off notifications
|
||||
threading.Thread(target=notification_handler.notify_timeline,
|
||||
kwargs=dict(notify_action='extdown')).start()
|
||||
|
||||
|
||||
def check_server_updates():
|
||||
|
||||
with monitor_lock:
|
||||
logger.info(u"PlexPy Monitor :: Checking for PMS updates...")
|
||||
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
|
||||
server_identity = pms_connect.get_server_identity()
|
||||
update_status = pms_connect.get_update_staus()
|
||||
|
||||
if server_identity and update_status:
|
||||
version = server_identity['version']
|
||||
logger.info(u"PlexPy Monitor :: Current PMS version: %s", version)
|
||||
|
||||
if update_status['state'] == 'available':
|
||||
update_version = update_status['version']
|
||||
logger.info(u"PlexPy Monitor :: PMS update available version: %s", update_version)
|
||||
|
||||
# Check if any notification agents have notifications enabled
|
||||
if any(d['on_pmsupdate'] for d in notifiers.available_notification_agents()):
|
||||
# Fire off notifications
|
||||
threading.Thread(target=notification_handler.notify_timeline,
|
||||
kwargs=dict(notify_action='pmsupdate')).start()
|
||||
else:
|
||||
logger.info(u"PlexPy Monitor :: No PMS update available.")
|
@@ -13,7 +13,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from plexpy import logger, pmsconnect, notification_handler, log_reader, database
|
||||
from plexpy import logger, pmsconnect, notification_handler, log_reader, database, notifiers
|
||||
|
||||
import threading
|
||||
import plexpy
|
||||
@@ -78,9 +78,10 @@ class ActivityProcessor(object):
|
||||
result = self.db.upsert('sessions', values, keys)
|
||||
|
||||
if result == 'insert':
|
||||
# Push any notifications - Push it on it's own thread so we don't hold up our db actions
|
||||
if notify:
|
||||
# Check if any notification agents have notifications enabled
|
||||
if notify and any(d['on_play'] for d in notifiers.available_notification_agents()):
|
||||
values.update({'ip_address': session['ip_address']})
|
||||
# Push any notifications - Push it on it's own thread so we don't hold up our db actions
|
||||
threading.Thread(target=notification_handler.notify,
|
||||
kwargs=dict(stream_data=values, notify_action='play')).start()
|
||||
|
||||
@@ -97,17 +98,24 @@ class ActivityProcessor(object):
|
||||
ip_address = {'ip_address': ip_address}
|
||||
self.db.upsert('sessions', ip_address, keys)
|
||||
|
||||
return True
|
||||
|
||||
def write_session_history(self, session=None, import_metadata=None, is_import=False, import_ignore_interval=0):
|
||||
from plexpy import users, libraries
|
||||
|
||||
section_id = session['section_id'] if not is_import else import_metadata['section_id']
|
||||
|
||||
if not is_import:
|
||||
user_data = users.Users()
|
||||
user_details = user_data.get_details(user_id=session['user_id'])
|
||||
|
||||
section_id = session['section_id'] if not is_import else import_metadata['section_id']
|
||||
|
||||
library_data = libraries.Libraries()
|
||||
library_details = library_data.get_details(section_id=section_id)
|
||||
|
||||
# Return false if failed to retrieve user or library details
|
||||
if not user_details or not library_details:
|
||||
return False
|
||||
|
||||
if session:
|
||||
logging_enabled = False
|
||||
|
||||
@@ -116,8 +124,13 @@ class ActivityProcessor(object):
|
||||
stopped = int(session['stopped'])
|
||||
else:
|
||||
stopped = int(time.time())
|
||||
elif session['stopped']:
|
||||
stopped = int(session['stopped'])
|
||||
else:
|
||||
stopped = int(time.time())
|
||||
self.set_session_state(session_key=session['session_key'],
|
||||
state='stopped',
|
||||
stopped=stopped)
|
||||
|
||||
if plexpy.CONFIG.MOVIE_LOGGING_ENABLE and str(session['rating_key']).isdigit() and \
|
||||
session['media_type'] == 'movie':
|
||||
@@ -137,14 +150,14 @@ class ActivityProcessor(object):
|
||||
else:
|
||||
real_play_time = stopped - session['started']
|
||||
|
||||
if plexpy.CONFIG.LOGGING_IGNORE_INTERVAL and not is_import:
|
||||
if not is_import and plexpy.CONFIG.LOGGING_IGNORE_INTERVAL:
|
||||
if (session['media_type'] == 'movie' or session['media_type'] == 'episode') and \
|
||||
(real_play_time < int(plexpy.CONFIG.LOGGING_IGNORE_INTERVAL)):
|
||||
logging_enabled = False
|
||||
logger.debug(u"PlexPy ActivityProcessor :: Play duration for ratingKey %s is %s secs which is less than %s "
|
||||
u"seconds, so we're not logging it." %
|
||||
(session['rating_key'], str(real_play_time), plexpy.CONFIG.LOGGING_IGNORE_INTERVAL))
|
||||
if session['media_type'] == 'track' and not is_import:
|
||||
if not is_import and session['media_type'] == 'track':
|
||||
if real_play_time < 15 and session['duration'] >= 30:
|
||||
logging_enabled = False
|
||||
logger.debug(u"PlexPy ActivityProcessor :: Play duration for ratingKey %s is %s secs, "
|
||||
@@ -156,17 +169,29 @@ class ActivityProcessor(object):
|
||||
logging_enabled = False
|
||||
logger.debug(u"PlexPy ActivityProcessor :: Play duration for ratingKey %s is %s secs which is less than %s "
|
||||
u"seconds, so we're not logging it." %
|
||||
(session['rating_key'], str(real_play_time),
|
||||
import_ignore_interval))
|
||||
(session['rating_key'], str(real_play_time), import_ignore_interval))
|
||||
|
||||
if not user_details['keep_history'] and not is_import:
|
||||
if not is_import and not user_details['keep_history']:
|
||||
logging_enabled = False
|
||||
logger.debug(u"PlexPy ActivityProcessor :: History logging for user '%s' is disabled." % user_details['username'])
|
||||
elif not library_details['keep_history'] and not is_import:
|
||||
elif not is_import and not library_details['keep_history']:
|
||||
logging_enabled = False
|
||||
logger.debug(u"PlexPy ActivityProcessor :: History logging for library '%s' is disabled." % library_details['section_name'])
|
||||
|
||||
if logging_enabled:
|
||||
|
||||
# Fetch metadata first so we can return false if it fails
|
||||
if not is_import:
|
||||
logger.debug(u"PlexPy ActivityProcessor :: Fetching metadata for item ratingKey %s" % session['rating_key'])
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
result = pms_connect.get_metadata_details(rating_key=str(session['rating_key']))
|
||||
if result:
|
||||
metadata = result['metadata']
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
metadata = import_metadata
|
||||
|
||||
# logger.debug(u"PlexPy ActivityProcessor :: Attempting to write to session_history table...")
|
||||
query = 'INSERT INTO session_history (started, stopped, rating_key, parent_rating_key, ' \
|
||||
'grandparent_rating_key, media_type, user_id, user, ip_address, paused_counter, player, ' \
|
||||
@@ -218,13 +243,22 @@ class ActivityProcessor(object):
|
||||
# % last_id)
|
||||
|
||||
# Write the session_history_media_info table
|
||||
|
||||
# Generate a combined transcode decision value
|
||||
if session['video_decision'] == 'transcode' or session['audio_decision'] == 'transcode':
|
||||
transcode_decision = 'transcode'
|
||||
elif session['video_decision'] == 'copy' or session['audio_decision'] == 'copy':
|
||||
transcode_decision = 'copy'
|
||||
else:
|
||||
transcode_decision = 'direct play'
|
||||
|
||||
# logger.debug(u"PlexPy ActivityProcessor :: Attempting to write to session_history_media_info table...")
|
||||
query = 'INSERT INTO session_history_media_info (id, rating_key, video_decision, audio_decision, ' \
|
||||
'duration, width, height, container, video_codec, audio_codec, bitrate, video_resolution, ' \
|
||||
'video_framerate, aspect_ratio, audio_channels, transcode_protocol, transcode_container, ' \
|
||||
'transcode_video_codec, transcode_audio_codec, transcode_audio_channels, transcode_width, ' \
|
||||
'transcode_height) VALUES ' \
|
||||
'(last_insert_rowid(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'
|
||||
'transcode_height, transcode_decision) VALUES ' \
|
||||
'(last_insert_rowid(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'
|
||||
|
||||
args = [session['rating_key'], session['video_decision'], session['audio_decision'],
|
||||
session['duration'], session['width'], session['height'], session['container'],
|
||||
@@ -232,19 +266,12 @@ class ActivityProcessor(object):
|
||||
session['video_resolution'], session['video_framerate'], session['aspect_ratio'],
|
||||
session['audio_channels'], session['transcode_protocol'], session['transcode_container'],
|
||||
session['transcode_video_codec'], session['transcode_audio_codec'],
|
||||
session['transcode_audio_channels'], session['transcode_width'], session['transcode_height']]
|
||||
session['transcode_audio_channels'], session['transcode_width'], session['transcode_height'],
|
||||
transcode_decision]
|
||||
|
||||
# logger.debug(u"PlexPy ActivityProcessor :: Writing session_history_media_info transaction...")
|
||||
self.db.action(query=query, args=args)
|
||||
|
||||
if not is_import:
|
||||
logger.debug(u"PlexPy ActivityProcessor :: Fetching metadata for item ratingKey %s" % session['rating_key'])
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
result = pms_connect.get_metadata_details(rating_key=str(session['rating_key']))
|
||||
metadata = result['metadata']
|
||||
else:
|
||||
metadata = import_metadata
|
||||
|
||||
# Write the session_history_metadata table
|
||||
directors = ";".join(metadata['directors'])
|
||||
writers = ";".join(metadata['writers'])
|
||||
@@ -280,6 +307,9 @@ class ActivityProcessor(object):
|
||||
# logger.debug(u"PlexPy ActivityProcessor :: Writing session_history_metadata transaction...")
|
||||
self.db.action(query=query, args=args)
|
||||
|
||||
# Return true when the session is successfully written to the database
|
||||
return True
|
||||
|
||||
def find_session_ip(self, rating_key=None, machine_id=None):
|
||||
|
||||
logger.debug(u"PlexPy ActivityProcessor :: Requesting log lines...")
|
||||
@@ -351,12 +381,16 @@ class ActivityProcessor(object):
|
||||
|
||||
return None
|
||||
|
||||
def set_session_state(self, session_key=None, state=None, view_offset=0):
|
||||
if str(session_key).isdigit() and str(view_offset).isdigit():
|
||||
values = {'view_offset': int(view_offset)}
|
||||
def set_session_state(self, session_key=None, state=None, **kwargs):
|
||||
if str(session_key).isdigit():
|
||||
values = {}
|
||||
|
||||
if state:
|
||||
values['state'] = state
|
||||
|
||||
for k,v in kwargs.iteritems():
|
||||
values[k] = v
|
||||
|
||||
keys = {'session_key': session_key}
|
||||
result = self.db.upsert('sessions', values, keys)
|
||||
|
||||
|
@@ -62,6 +62,7 @@ MEDIA_FLAGS_VIDEO = {'avc1': 'h264',
|
||||
SCHEDULER_LIST = ['Check GitHub for updates',
|
||||
'Check for active sessions',
|
||||
'Check for recently added items',
|
||||
'Check for Plex updates',
|
||||
'Check for Plex remote access',
|
||||
'Refresh users list',
|
||||
'Refresh libraries list',
|
||||
|
@@ -48,6 +48,7 @@ _CONFIG_DEFINITIONS = {
|
||||
'BOXCAR_ON_INTDOWN': (int, 'Boxcar', 0),
|
||||
'BOXCAR_ON_EXTUP': (int, 'Boxcar', 0),
|
||||
'BOXCAR_ON_INTUP': (int, 'Boxcar', 0),
|
||||
'BOXCAR_ON_PMSUPDATE': (int, 'Boxcar', 0),
|
||||
'BUFFER_THRESHOLD': (int, 'Monitoring', 3),
|
||||
'BUFFER_WAIT': (int, 'Monitoring', 900),
|
||||
'BACKUP_DIR': (str, 'General', ''),
|
||||
@@ -81,6 +82,7 @@ _CONFIG_DEFINITIONS = {
|
||||
'EMAIL_ON_INTDOWN': (int, 'Email', 0),
|
||||
'EMAIL_ON_EXTUP': (int, 'Email', 0),
|
||||
'EMAIL_ON_INTUP': (int, 'Email', 0),
|
||||
'EMAIL_ON_PMSUPDATE': (int, 'Email', 0),
|
||||
'ENABLE_HTTPS': (int, 'General', 0),
|
||||
'FACEBOOK_ENABLED': (int, 'Facebook', 0),
|
||||
'FACEBOOK_REDIRECT_URI': (str, 'Facebook', ''),
|
||||
@@ -88,6 +90,7 @@ _CONFIG_DEFINITIONS = {
|
||||
'FACEBOOK_APP_SECRET': (str, 'Facebook', ''),
|
||||
'FACEBOOK_TOKEN': (str, 'Facebook', ''),
|
||||
'FACEBOOK_GROUP': (str, 'Facebook', ''),
|
||||
'FACEBOOK_INCL_PMSLINK': (int, 'Facebook', 0),
|
||||
'FACEBOOK_INCL_POSTER': (int, 'Facebook', 1),
|
||||
'FACEBOOK_INCL_SUBJECT': (int, 'Facebook', 1),
|
||||
'FACEBOOK_ON_PLAY': (int, 'Facebook', 0),
|
||||
@@ -101,6 +104,7 @@ _CONFIG_DEFINITIONS = {
|
||||
'FACEBOOK_ON_INTDOWN': (int, 'Facebook', 0),
|
||||
'FACEBOOK_ON_EXTUP': (int, 'Facebook', 0),
|
||||
'FACEBOOK_ON_INTUP': (int, 'Facebook', 0),
|
||||
'FACEBOOK_ON_PMSUPDATE': (int, 'Facebook', 0),
|
||||
'FIRST_RUN_COMPLETE': (int, 'General', 0),
|
||||
'FREEZE_DB': (int, 'General', 0),
|
||||
'GET_FILE_SIZES': (int, 'General', 0),
|
||||
@@ -126,6 +130,7 @@ _CONFIG_DEFINITIONS = {
|
||||
'GROWL_ON_INTDOWN': (int, 'Growl', 0),
|
||||
'GROWL_ON_EXTUP': (int, 'Growl', 0),
|
||||
'GROWL_ON_INTUP': (int, 'Growl', 0),
|
||||
'GROWL_ON_PMSUPDATE': (int, 'Growl', 0),
|
||||
'HOME_LIBRARY_CARDS': (list, 'General', ['first_run']),
|
||||
'HOME_STATS_LENGTH': (int, 'General', 30),
|
||||
'HOME_STATS_TYPE': (int, 'General', 0),
|
||||
@@ -159,6 +164,7 @@ _CONFIG_DEFINITIONS = {
|
||||
'IFTTT_ON_INTDOWN': (int, 'IFTTT', 0),
|
||||
'IFTTT_ON_EXTUP': (int, 'IFTTT', 0),
|
||||
'IFTTT_ON_INTUP': (int, 'IFTTT', 0),
|
||||
'IFTTT_ON_PMSUPDATE': (int, 'IFTTT', 0),
|
||||
'JOURNAL_MODE': (str, 'Advanced', 'wal'),
|
||||
'LAUNCH_BROWSER': (int, 'General', 1),
|
||||
'LOG_DIR': (str, 'General', ''),
|
||||
@@ -173,6 +179,7 @@ _CONFIG_DEFINITIONS = {
|
||||
'MUSIC_NOTIFY_ON_START': (int, 'Monitoring', 1),
|
||||
'MUSIC_NOTIFY_ON_STOP': (int, 'Monitoring', 0),
|
||||
'MUSIC_NOTIFY_ON_PAUSE': (int, 'Monitoring', 0),
|
||||
'MONITOR_PMS_UPDATES': (int, 'Monitoring', 0),
|
||||
'MONITOR_REMOTE_ACCESS': (int, 'Monitoring', 0),
|
||||
'MONITORING_INTERVAL': (int, 'Monitoring', 60),
|
||||
'MONITORING_USE_WEBSOCKET': (int, 'Monitoring', 0),
|
||||
@@ -190,7 +197,9 @@ _CONFIG_DEFINITIONS = {
|
||||
'NMA_ON_INTDOWN': (int, 'NMA', 0),
|
||||
'NMA_ON_EXTUP': (int, 'NMA', 0),
|
||||
'NMA_ON_INTUP': (int, 'NMA', 0),
|
||||
'NMA_ON_PMSUPDATE': (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),
|
||||
@@ -217,6 +226,8 @@ _CONFIG_DEFINITIONS = {
|
||||
'NOTIFY_ON_EXTUP_BODY_TEXT': (unicode, 'Monitoring', 'The Plex Media Server remote access is back up.'),
|
||||
'NOTIFY_ON_INTUP_SUBJECT_TEXT': (unicode, 'Monitoring', 'PlexPy ({server_name})'),
|
||||
'NOTIFY_ON_INTUP_BODY_TEXT': (unicode, 'Monitoring', 'The Plex Media Server is back up.'),
|
||||
'NOTIFY_ON_PMSUPDATE_SUBJECT_TEXT': (unicode, 'Monitoring', 'PlexPy ({server_name})'),
|
||||
'NOTIFY_ON_PMSUPDATE_BODY_TEXT': (unicode, 'Monitoring', 'An update is available for the Plex Media Server (version {update_version}).'),
|
||||
'NOTIFY_SCRIPTS_ARGS_TEXT': (unicode, 'Monitoring', ''),
|
||||
'OSX_NOTIFY_APP': (str, 'OSX_Notify', '/Applications/PlexPy'),
|
||||
'OSX_NOTIFY_ENABLED': (int, 'OSX_Notify', 0),
|
||||
@@ -231,6 +242,7 @@ _CONFIG_DEFINITIONS = {
|
||||
'OSX_NOTIFY_ON_INTDOWN': (int, 'OSX_Notify', 0),
|
||||
'OSX_NOTIFY_ON_EXTUP': (int, 'OSX_Notify', 0),
|
||||
'OSX_NOTIFY_ON_INTUP': (int, 'OSX_Notify', 0),
|
||||
'OSX_NOTIFY_ON_PMSUPDATE': (int, 'OSX_Notify', 0),
|
||||
'PLEX_CLIENT_HOST': (str, 'Plex', ''),
|
||||
'PLEX_ENABLED': (int, 'Plex', 0),
|
||||
'PLEX_PASSWORD': (str, 'Plex', ''),
|
||||
@@ -246,6 +258,7 @@ _CONFIG_DEFINITIONS = {
|
||||
'PLEX_ON_INTDOWN': (int, 'Plex', 0),
|
||||
'PLEX_ON_EXTUP': (int, 'Plex', 0),
|
||||
'PLEX_ON_INTUP': (int, 'Plex', 0),
|
||||
'PLEX_ON_PMSUPDATE': (int, 'Plex', 0),
|
||||
'PROWL_ENABLED': (int, 'Prowl', 0),
|
||||
'PROWL_KEYS': (str, 'Prowl', ''),
|
||||
'PROWL_PRIORITY': (int, 'Prowl', 0),
|
||||
@@ -260,6 +273,7 @@ _CONFIG_DEFINITIONS = {
|
||||
'PROWL_ON_INTDOWN': (int, 'Prowl', 0),
|
||||
'PROWL_ON_EXTUP': (int, 'Prowl', 0),
|
||||
'PROWL_ON_INTUP': (int, 'Prowl', 0),
|
||||
'PROWL_ON_PMSUPDATE': (int, 'Prowl', 0),
|
||||
'PUSHALOT_APIKEY': (str, 'Pushalot', ''),
|
||||
'PUSHALOT_ENABLED': (int, 'Pushalot', 0),
|
||||
'PUSHALOT_ON_PLAY': (int, 'Pushalot', 0),
|
||||
@@ -273,6 +287,7 @@ _CONFIG_DEFINITIONS = {
|
||||
'PUSHALOT_ON_INTDOWN': (int, 'Pushalot', 0),
|
||||
'PUSHALOT_ON_EXTUP': (int, 'Pushalot', 0),
|
||||
'PUSHALOT_ON_INTUP': (int, 'Pushalot', 0),
|
||||
'PUSHALOT_ON_PMSUPDATE': (int, 'Pushalot', 0),
|
||||
'PUSHBULLET_APIKEY': (str, 'PushBullet', ''),
|
||||
'PUSHBULLET_DEVICEID': (str, 'PushBullet', ''),
|
||||
'PUSHBULLET_CHANNEL_TAG': (str, 'PushBullet', ''),
|
||||
@@ -288,8 +303,10 @@ _CONFIG_DEFINITIONS = {
|
||||
'PUSHBULLET_ON_INTDOWN': (int, 'PushBullet', 0),
|
||||
'PUSHBULLET_ON_EXTUP': (int, 'PushBullet', 0),
|
||||
'PUSHBULLET_ON_INTUP': (int, 'PushBullet', 0),
|
||||
'PUSHBULLET_ON_PMSUPDATE': (int, 'PushBullet', 0),
|
||||
'PUSHOVER_APITOKEN': (str, 'Pushover', ''),
|
||||
'PUSHOVER_ENABLED': (int, 'Pushover', 0),
|
||||
'PUSHOVER_HTML_SUPPORT': (int, 'Pushover', 1),
|
||||
'PUSHOVER_KEYS': (str, 'Pushover', ''),
|
||||
'PUSHOVER_PRIORITY': (int, 'Pushover', 0),
|
||||
'PUSHOVER_SOUND': (str, 'Pushover', ''),
|
||||
@@ -304,6 +321,7 @@ _CONFIG_DEFINITIONS = {
|
||||
'PUSHOVER_ON_INTDOWN': (int, 'Pushover', 0),
|
||||
'PUSHOVER_ON_EXTUP': (int, 'Pushover', 0),
|
||||
'PUSHOVER_ON_INTUP': (int, 'Pushover', 0),
|
||||
'PUSHOVER_ON_PMSUPDATE': (int, 'Pushover', 0),
|
||||
'REFRESH_LIBRARIES_INTERVAL': (int, 'Monitoring', 12),
|
||||
'REFRESH_LIBRARIES_ON_STARTUP': (int, 'Monitoring', 1),
|
||||
'REFRESH_USERS_INTERVAL': (int, 'Monitoring', 12),
|
||||
@@ -325,6 +343,7 @@ _CONFIG_DEFINITIONS = {
|
||||
'SLACK_ON_INTDOWN': (int, 'Slack', 0),
|
||||
'SLACK_ON_EXTUP': (int, 'Slack', 0),
|
||||
'SLACK_ON_INTUP': (int, 'Slack', 0),
|
||||
'SLACK_ON_PMSUPDATE': (int, 'Slack', 0),
|
||||
'SCRIPTS_ENABLED': (int, 'Scripts', 0),
|
||||
'SCRIPTS_FOLDER': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_ON_PLAY': (int, 'Scripts', 0),
|
||||
@@ -338,6 +357,7 @@ _CONFIG_DEFINITIONS = {
|
||||
'SCRIPTS_ON_EXTUP': (int, 'Scripts', 0),
|
||||
'SCRIPTS_ON_INTDOWN': (int, 'Scripts', 0),
|
||||
'SCRIPTS_ON_INTUP': (int, 'Scripts', 0),
|
||||
'SCRIPTS_ON_PMSUPDATE': (int, 'Scripts', 0),
|
||||
'SCRIPTS_ON_PLAY_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_ON_STOP_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_ON_PAUSE_SCRIPT': (unicode, 'Scripts', ''),
|
||||
@@ -349,6 +369,7 @@ _CONFIG_DEFINITIONS = {
|
||||
'SCRIPTS_ON_EXTUP_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_ON_INTDOWN_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_ON_INTUP_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'SCRIPTS_ON_PMSUPDATE_SCRIPT': (unicode, 'Scripts', ''),
|
||||
'TELEGRAM_BOT_TOKEN': (str, 'Telegram', ''),
|
||||
'TELEGRAM_ENABLED': (int, 'Telegram', 0),
|
||||
'TELEGRAM_CHAT_ID': (str, 'Telegram', ''),
|
||||
@@ -364,6 +385,7 @@ _CONFIG_DEFINITIONS = {
|
||||
'TELEGRAM_ON_INTDOWN': (int, 'Telegram', 0),
|
||||
'TELEGRAM_ON_EXTUP': (int, 'Telegram', 0),
|
||||
'TELEGRAM_ON_INTUP': (int, 'Telegram', 0),
|
||||
'TELEGRAM_ON_PMSUPDATE': (int, 'Telegram', 0),
|
||||
'TV_LOGGING_ENABLE': (int, 'Monitoring', 1),
|
||||
'TV_NOTIFY_ENABLE': (int, 'Monitoring', 0),
|
||||
'TV_NOTIFY_ON_START': (int, 'Monitoring', 1),
|
||||
@@ -386,6 +408,7 @@ _CONFIG_DEFINITIONS = {
|
||||
'TWITTER_ON_INTDOWN': (int, 'Twitter', 0),
|
||||
'TWITTER_ON_EXTUP': (int, 'Twitter', 0),
|
||||
'TWITTER_ON_INTUP': (int, 'Twitter', 0),
|
||||
'TWITTER_ON_PMSUPDATE': (int, 'Twitter', 0),
|
||||
'UPDATE_DB_INTERVAL': (int, 'General', 24),
|
||||
'UPDATE_SECTION_IDS': (int, 'General', 1),
|
||||
'VERIFY_SSL_CERT': (bool_int, 'Advanced', 1),
|
||||
@@ -404,7 +427,8 @@ _CONFIG_DEFINITIONS = {
|
||||
'XBMC_ON_EXTDOWN': (int, 'XBMC', 0),
|
||||
'XBMC_ON_INTDOWN': (int, 'XBMC', 0),
|
||||
'XBMC_ON_EXTUP': (int, 'XBMC', 0),
|
||||
'XBMC_ON_INTUP': (int, 'XBMC', 0)
|
||||
'XBMC_ON_INTUP': (int, 'XBMC', 0),
|
||||
'XBMC_ON_PMSUPDATE': (int, 'XBMC', 0)
|
||||
}
|
||||
|
||||
|
||||
|
@@ -18,6 +18,7 @@ import os
|
||||
import sqlite3
|
||||
import shutil
|
||||
import threading
|
||||
import time
|
||||
|
||||
import logger
|
||||
import plexpy
|
||||
@@ -37,9 +38,21 @@ def clear_history_tables():
|
||||
monitor_db.action('DELETE FROM session_history')
|
||||
monitor_db.action('DELETE FROM session_history_media_info')
|
||||
monitor_db.action('DELETE FROM session_history_metadata')
|
||||
monitor_db.action('VACUUM;')
|
||||
monitor_db.action('VACUUM')
|
||||
|
||||
|
||||
def delete_sessions():
|
||||
logger.debug(u"PlexPy Database :: Clearing temporary sessions from database.")
|
||||
monitor_db = MonitorDatabase()
|
||||
|
||||
try:
|
||||
monitor_db.action('DELETE FROM sessions')
|
||||
monitor_db.action('VACUUM')
|
||||
return 'Cleared temporary sessions.'
|
||||
except Exception as e:
|
||||
logger.warn(u"PlexPy Database :: Unable to clear temporary sessions from database: %s." % e)
|
||||
return 'Unable to clear temporary sessions.'
|
||||
|
||||
def db_filename(filename="plexpy.db"):
|
||||
""" Returns the filepath to the db """
|
||||
|
||||
|
@@ -58,11 +58,10 @@ class DataFactory(object):
|
||||
'session_history_metadata.thumb',
|
||||
'session_history_metadata.parent_thumb',
|
||||
'session_history_metadata.grandparent_thumb',
|
||||
'MAX((CASE WHEN view_offset IS NULL THEN 0.1 ELSE view_offset * 1.0 END) / \
|
||||
(CASE WHEN session_history_metadata.duration IS NULL THEN 1.0 \
|
||||
ELSE session_history_metadata.duration * 1.0 END) * 100) AS percent_complete',
|
||||
'session_history_media_info.video_decision',
|
||||
'session_history_media_info.audio_decision',
|
||||
'MAX((CASE WHEN (view_offset IS NULL OR view_offset = "") THEN 0.1 ELSE view_offset * 1.0 END) / \
|
||||
(CASE WHEN (session_history_metadata.duration IS NULL OR session_history_metadata.duration = "") \
|
||||
THEN 1.0 ELSE session_history_metadata.duration * 1.0 END) * 100) AS percent_complete',
|
||||
'session_history_media_info.transcode_decision',
|
||||
'COUNT(*) AS group_count',
|
||||
'GROUP_CONCAT(session_history.id) AS group_ids'
|
||||
]
|
||||
@@ -138,8 +137,7 @@ class DataFactory(object):
|
||||
'media_index': item['media_index'],
|
||||
'parent_media_index': item['parent_media_index'],
|
||||
'thumb': thumb,
|
||||
'video_decision': item['video_decision'],
|
||||
'audio_decision': item['audio_decision'],
|
||||
'transcode_decision': item['transcode_decision'],
|
||||
'percent_complete': int(round(item['percent_complete'])),
|
||||
'watched_status': watched_status,
|
||||
'group_count': item['group_count'],
|
||||
@@ -626,24 +624,21 @@ class DataFactory(object):
|
||||
|
||||
title = 'Concurrent Transcodes'
|
||||
query = base_query \
|
||||
+ 'AND (session_history_media_info.video_decision = "transcode" ' \
|
||||
'OR session_history_media_info.audio_decision = "transcode") '
|
||||
+ 'AND session_history_media_info.transcode_decision = "transcode" '
|
||||
result = monitor_db.select(query)
|
||||
if result:
|
||||
most_concurrent.append(calc_most_concurrent(title, result))
|
||||
|
||||
title = 'Concurrent Direct Streams'
|
||||
query = base_query \
|
||||
+ 'AND (session_history_media_info.video_decision != "transcode" ' \
|
||||
'AND session_history_media_info.audio_decision = "copy") '
|
||||
+ 'AND session_history_media_info.transcode_decision = "copy" '
|
||||
result = monitor_db.select(query)
|
||||
if result:
|
||||
most_concurrent.append(calc_most_concurrent(title, result))
|
||||
|
||||
title = 'Concurrent Direct Plays'
|
||||
query = base_query \
|
||||
+ 'AND (session_history_media_info.video_decision = "direct play" ' \
|
||||
'OR session_history_media_info.audio_decision = "direct play") '
|
||||
+ 'AND session_history_media_info.transcode_decision = "direct play" '
|
||||
result = monitor_db.select(query)
|
||||
if result:
|
||||
most_concurrent.append(calc_most_concurrent(title, result))
|
||||
@@ -828,6 +823,7 @@ class DataFactory(object):
|
||||
'SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) AS total_duration ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \
|
||||
'JOIN session_history_media_info ON session_history_media_info.id = session_history.id ' \
|
||||
'%s ' % where
|
||||
result = monitor_db.select(query)
|
||||
except Exception as e:
|
||||
@@ -843,19 +839,67 @@ class DataFactory(object):
|
||||
def get_session_ip(self, session_key=''):
|
||||
monitor_db = database.MonitorDatabase()
|
||||
|
||||
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 None
|
||||
|
||||
ip_address = 'N/A'
|
||||
return ip_address
|
||||
|
||||
for item in result:
|
||||
ip_address = item['ip_address']
|
||||
|
||||
return ip_address
|
||||
|
||||
def get_poster_url(self, rating_key='', metadata=None):
|
||||
monitor_db = database.MonitorDatabase()
|
||||
|
||||
poster_url = ''
|
||||
poster_key = ''
|
||||
|
||||
if rating_key:
|
||||
poster_key = rating_key
|
||||
elif metadata:
|
||||
if metadata['media_type'] == 'movie' or metadata['media_type'] == 'show' or \
|
||||
metadata['media_type'] == 'artist' or metadata['media_type'] == 'album':
|
||||
poster_key = metadata['rating_key']
|
||||
elif metadata['media_type'] == 'episode':
|
||||
poster_key = metadata['grandparent_rating_key']
|
||||
elif metadata['media_type'] == 'season' or metadata['media_type'] == 'track':
|
||||
poster_key = metadata['parent_rating_key']
|
||||
|
||||
if poster_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(poster_key), int(poster_key), int(poster_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 delete_poster_url(self, poster_url=''):
|
||||
monitor_db = database.MonitorDatabase()
|
||||
|
||||
if poster_url:
|
||||
logger.info(u"PlexPy DataFactory :: Deleting poster_url %s from the notify log database." % poster_url)
|
||||
monitor_db.upsert('notify_log', {'poster_url': None}, {'poster_url': poster_url})
|
||||
return 'Deleted poster_url %s.' % poster_url
|
||||
else:
|
||||
return 'Unable to delete poster_url.'
|
||||
|
||||
def get_search_query(self, rating_key=''):
|
||||
monitor_db = database.MonitorDatabase()
|
||||
|
||||
@@ -1095,3 +1139,83 @@ class DataFactory(object):
|
||||
old_rating_key]
|
||||
|
||||
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
|
||||
|
||||
def delete_notification_log(self):
|
||||
monitor_db = database.MonitorDatabase()
|
||||
|
||||
try:
|
||||
logger.info(u"PlexPy DataFactory :: Clearing notification logs from database.")
|
||||
monitor_db.action('DELETE FROM notify_log')
|
||||
monitor_db.action('VACUUM')
|
||||
return 'Cleared notification logs.'
|
||||
except Exception as e:
|
||||
logger.warn(u"PlexPy DataFactory :: Unable to execute database query for delete_notification_log: %s." % e)
|
||||
return 'Unable to clear notification logs.'
|
111
plexpy/graphs.py
111
plexpy/graphs.py
@@ -490,40 +490,39 @@ class Graphs(object):
|
||||
try:
|
||||
if y_axis == 'plays':
|
||||
query = 'SELECT date(session_history.started, "unixepoch", "localtime") AS date_played, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
|
||||
'OR session_history_media_info.audio_decision = "direct play") THEN 1 ELSE 0 END) AS dp_count, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
|
||||
'AND session_history_media_info.audio_decision = "copy") THEN 1 ELSE 0 END) AS ds_count, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
|
||||
'OR session_history_media_info.audio_decision = "transcode") THEN 1 ELSE 0 END) AS tc_count ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "direct play" ' \
|
||||
'THEN 1 ELSE 0 END) AS dp_count, ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "copy" ' \
|
||||
'THEN 1 ELSE 0 END) AS ds_count, ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "transcode" ' \
|
||||
'THEN 1 ELSE 0 END) AS tc_count ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
|
||||
'WHERE (datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-%s days", "localtime")) AND ' \
|
||||
'(session_history.media_type = "episode" OR session_history.media_type = "movie" OR session_history.media_type = "track") ' \
|
||||
'(session_history.media_type = "episode" OR session_history.media_type = "movie" OR ' \
|
||||
'session_history.media_type = "track") ' \
|
||||
'GROUP BY date_played ' \
|
||||
'ORDER BY started ASC' % time_range
|
||||
|
||||
result = monitor_db.select(query)
|
||||
else:
|
||||
query = 'SELECT date(session_history.started, "unixepoch", "localtime") AS date_played, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
|
||||
'OR session_history_media_info.audio_decision = "direct play") ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "direct play" ' \
|
||||
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
|
||||
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS dp_count, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
|
||||
'AND session_history_media_info.audio_decision = "copy") ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "copy" ' \
|
||||
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
|
||||
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS ds_count, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
|
||||
'OR session_history_media_info.audio_decision = "transcode") ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "transcode" ' \
|
||||
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
|
||||
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS tc_count ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
|
||||
'WHERE datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
|
||||
'datetime("now", "-%s days", "localtime") AND ' \
|
||||
'(session_history.media_type = "episode" OR session_history.media_type = "movie" OR session_history.media_type = "track") ' \
|
||||
'(session_history.media_type = "episode" OR session_history.media_type = "movie" OR ' \
|
||||
'session_history.media_type = "track") ' \
|
||||
'GROUP BY date_played ' \
|
||||
'ORDER BY started ASC' % time_range
|
||||
|
||||
@@ -583,12 +582,12 @@ class Graphs(object):
|
||||
try:
|
||||
if y_axis == 'plays':
|
||||
query = 'SELECT session_history_media_info.video_resolution AS resolution, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
|
||||
'OR session_history_media_info.audio_decision = "direct play") THEN 1 ELSE 0 END) AS dp_count, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
|
||||
'AND session_history_media_info.audio_decision = "copy") THEN 1 ELSE 0 END) AS ds_count, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
|
||||
'OR session_history_media_info.audio_decision = "transcode") THEN 1 ELSE 0 END) AS tc_count, ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "direct play" ' \
|
||||
'THEN 1 ELSE 0 END) AS dp_count, ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "copy" ' \
|
||||
'THEN 1 ELSE 0 END) AS ds_count, ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "transcode" ' \
|
||||
'THEN 1 ELSE 0 END) AS tc_count, ' \
|
||||
'COUNT(session_history.id) AS total_count ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
|
||||
@@ -602,16 +601,13 @@ class Graphs(object):
|
||||
result = monitor_db.select(query)
|
||||
else:
|
||||
query = 'SELECT session_history_media_info.video_resolution AS resolution,' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
|
||||
'OR session_history_media_info.audio_decision = "direct play") ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "direct play" ' \
|
||||
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
|
||||
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS dp_count, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
|
||||
'AND session_history_media_info.audio_decision = "copy") ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "copy" ' \
|
||||
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
|
||||
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS ds_count, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
|
||||
'OR session_history_media_info.audio_decision = "transcode") ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "transcode" ' \
|
||||
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
|
||||
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS tc_count, ' \
|
||||
'SUM(CASE WHEN stopped > 0 THEN (stopped - started) ' \
|
||||
@@ -671,12 +667,12 @@ class Graphs(object):
|
||||
'WHEN session_history_media_info.transcode_height <= 1440 THEN "QHD" ' \
|
||||
'WHEN session_history_media_info.transcode_height <= 2160 THEN "4K" ' \
|
||||
'ELSE "unknown" END) ELSE session_history_media_info.video_resolution END) AS resolution, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
|
||||
'OR session_history_media_info.audio_decision = "direct play") THEN 1 ELSE 0 END) AS dp_count, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
|
||||
'AND session_history_media_info.audio_decision = "copy") THEN 1 ELSE 0 END) AS ds_count, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" '\
|
||||
'OR session_history_media_info.audio_decision = "transcode") THEN 1 ELSE 0 END) AS tc_count, ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "direct play" ' \
|
||||
'THEN 1 ELSE 0 END) AS dp_count, ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "copy" ' \
|
||||
'THEN 1 ELSE 0 END) AS ds_count, ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "transcode" '\
|
||||
'THEN 1 ELSE 0 END) AS tc_count, ' \
|
||||
'COUNT(session_history.id) AS total_count ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
|
||||
@@ -700,16 +696,13 @@ class Graphs(object):
|
||||
'WHEN session_history_media_info.transcode_height <= 1440 THEN "QHD" ' \
|
||||
'WHEN session_history_media_info.transcode_height <= 2160 THEN "4K" ' \
|
||||
'ELSE "unknown" END) ELSE session_history_media_info.video_resolution END) AS resolution, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
|
||||
'OR session_history_media_info.audio_decision = "direct play") ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "direct play" ' \
|
||||
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
|
||||
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS dp_count, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
|
||||
'AND session_history_media_info.audio_decision = "copy") ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "copy" ' \
|
||||
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
|
||||
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS ds_count, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
|
||||
'OR session_history_media_info.audio_decision = "transcode") ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "transcode" ' \
|
||||
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
|
||||
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS tc_count, ' \
|
||||
'SUM(CASE WHEN stopped > 0 THEN (stopped - started) ' \
|
||||
@@ -759,12 +752,12 @@ class Graphs(object):
|
||||
try:
|
||||
if y_axis == 'plays':
|
||||
query = 'SELECT session_history.platform AS platform, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
|
||||
'OR session_history_media_info.audio_decision = "direct play") THEN 1 ELSE 0 END) AS dp_count, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
|
||||
'AND session_history_media_info.audio_decision = "copy") THEN 1 ELSE 0 END) AS ds_count, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
|
||||
'OR session_history_media_info.audio_decision = "transcode") THEN 1 ELSE 0 END) AS tc_count, ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "direct play" ' \
|
||||
'THEN 1 ELSE 0 END) AS dp_count, ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "copy" ' \
|
||||
'THEN 1 ELSE 0 END) AS ds_count, ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "transcode" ' \
|
||||
'THEN 1 ELSE 0 END) AS tc_count, ' \
|
||||
'COUNT(session_history.id) AS total_count ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
|
||||
@@ -777,16 +770,13 @@ class Graphs(object):
|
||||
result = monitor_db.select(query)
|
||||
else:
|
||||
query = 'SELECT session_history.platform AS platform, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
|
||||
'OR session_history_media_info.audio_decision = "direct play") ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "direct play" ' \
|
||||
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
|
||||
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS dp_count, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
|
||||
'AND session_history_media_info.audio_decision = "copy") ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "copy" ' \
|
||||
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
|
||||
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS ds_count, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
|
||||
'AND session_history_media_info.audio_decision = "transcode") ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "transcode" ' \
|
||||
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
|
||||
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS tc_count, ' \
|
||||
'SUM(CASE WHEN session_history.stopped > 0 ' \
|
||||
@@ -838,12 +828,12 @@ class Graphs(object):
|
||||
if y_axis == 'plays':
|
||||
query = 'SELECT ' \
|
||||
'(CASE WHEN users.friendly_name IS NULL THEN users.username ELSE users.friendly_name END) AS username, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
|
||||
'OR session_history_media_info.audio_decision = "direct play") THEN 1 ELSE 0 END) AS dp_count, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
|
||||
'AND session_history_media_info.audio_decision = "copy") THEN 1 ELSE 0 END) AS ds_count, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
|
||||
'OR session_history_media_info.audio_decision = "transcode") THEN 1 ELSE 0 END) AS tc_count, ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "direct play" ' \
|
||||
'THEN 1 ELSE 0 END) AS dp_count, ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "copy" ' \
|
||||
'THEN 1 ELSE 0 END) AS ds_count, ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "transcode" ' \
|
||||
'THEN 1 ELSE 0 END) AS tc_count, ' \
|
||||
'COUNT(session_history.id) AS total_count ' \
|
||||
'FROM session_history ' \
|
||||
'JOIN users ON session_history.user_id = users.user_id ' \
|
||||
@@ -858,16 +848,13 @@ class Graphs(object):
|
||||
else:
|
||||
query = 'SELECT ' \
|
||||
'(CASE WHEN users.friendly_name IS NULL THEN users.username ELSE users.friendly_name END) AS username, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
|
||||
'OR session_history_media_info.audio_decision = "direct play") ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "direct play" ' \
|
||||
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
|
||||
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS dp_count, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
|
||||
'AND session_history_media_info.audio_decision = "copy") ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "copy" ' \
|
||||
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
|
||||
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS ds_count, ' \
|
||||
'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
|
||||
'AND session_history_media_info.audio_decision = "transcode") ' \
|
||||
'SUM(CASE WHEN session_history_media_info.transcode_decision = "transcode" ' \
|
||||
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
|
||||
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS tc_count, ' \
|
||||
'SUM(CASE WHEN session_history.stopped > 0 ' \
|
||||
|
@@ -543,9 +543,10 @@ 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'
|
||||
|
||||
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())
|
||||
@@ -557,5 +558,7 @@ def uploadToImgur(imgPath, imgTitle=''):
|
||||
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
|
@@ -148,7 +148,6 @@ class Libraries(object):
|
||||
'session_history_metadata.year',
|
||||
'session_history_metadata.media_index',
|
||||
'session_history_metadata.parent_media_index',
|
||||
'session_history_media_info.video_decision',
|
||||
'library_sections.do_notify',
|
||||
'library_sections.do_notify_created',
|
||||
'library_sections.keep_history'
|
||||
@@ -540,7 +539,7 @@ class Libraries(object):
|
||||
def get_details(self, section_id=None):
|
||||
from plexpy import pmsconnect
|
||||
|
||||
default_return = {'section_id': None,
|
||||
default_return = {'section_id': 0,
|
||||
'section_name': 'Local',
|
||||
'section_type': '',
|
||||
'library_thumb': common.DEFAULT_COVER_THUMB,
|
||||
@@ -550,7 +549,7 @@ class Libraries(object):
|
||||
'child_count': 0,
|
||||
'do_notify': 0,
|
||||
'do_notify_created': 0,
|
||||
'keep_history': 0
|
||||
'keep_history': 1
|
||||
}
|
||||
|
||||
if not section_id:
|
||||
@@ -603,7 +602,8 @@ class Libraries(object):
|
||||
return library_details
|
||||
|
||||
else:
|
||||
logger.warn(u"PlexPy Libraries :: Unable to retrieve library from local database. Requesting library list refresh.")
|
||||
logger.warn(u"PlexPy Libraries :: Unable to retrieve library %s from database. Requesting library list refresh."
|
||||
% section_id)
|
||||
# Let's first refresh the libraries list to make sure the library isn't newly added and not in the db yet
|
||||
pmsconnect.refresh_libraries()
|
||||
|
||||
@@ -613,9 +613,9 @@ class Libraries(object):
|
||||
return library_details
|
||||
|
||||
else:
|
||||
logger.warn(u"PlexPy Users :: Unable to retrieve user from local database. Returning 'Local' library.")
|
||||
logger.warn(u"PlexPy Users :: Unable to retrieve library %s from database. Returning 'Local' library."
|
||||
% section_id)
|
||||
# If there is no library data we must return something
|
||||
# Use "Local" library to retain compatibility with PlexWatch database value
|
||||
return default_return
|
||||
|
||||
def get_watch_time_stats(self, section_id=None):
|
||||
|
@@ -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,234 @@ 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,
|
||||
agent_id=agent['id'])
|
||||
|
||||
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,
|
||||
agent_id=agent['id'])
|
||||
|
||||
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,
|
||||
agent_id=agent['id'])
|
||||
|
||||
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,
|
||||
agent_id=agent['id'])
|
||||
|
||||
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,
|
||||
agent_id=agent['id'])
|
||||
|
||||
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,
|
||||
agent_id=agent['id'])
|
||||
|
||||
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],
|
||||
set_notify_state(session=stream_data,
|
||||
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)
|
||||
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,
|
||||
agent_id=agent['id'])
|
||||
|
||||
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,
|
||||
agent_id=agent['id'])
|
||||
|
||||
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,
|
||||
agent_id=agent['id'])
|
||||
|
||||
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,
|
||||
agent_id=agent['id'])
|
||||
|
||||
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,
|
||||
agent_id=agent['id'])
|
||||
|
||||
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,137 +293,175 @@ 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,
|
||||
agent_id=agent['id'])
|
||||
|
||||
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=timeline_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,
|
||||
agent_id=agent['id'])
|
||||
|
||||
notifiers.send_notification(agent_id=agent['id'],
|
||||
subject=notify_strings[0],
|
||||
body=notify_strings[1],
|
||||
script_args=notify_strings[2],
|
||||
notify_action=notify_action)
|
||||
|
||||
# Set the notification state in the db
|
||||
set_notify_state(session={},
|
||||
notify_action=notify_action,
|
||||
script_args=notify_strings[2])
|
||||
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,
|
||||
agent_id=agent['id'])
|
||||
|
||||
notifiers.send_notification(agent_id=agent['id'],
|
||||
subject=notify_strings[0],
|
||||
body=notify_strings[1],
|
||||
script_args=notify_strings[2],
|
||||
notify_action=notify_action)
|
||||
|
||||
# Set the notification state in the db
|
||||
set_notify_state(session={},
|
||||
notify_action=notify_action,
|
||||
script_args=notify_strings[2])
|
||||
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,
|
||||
agent_id=agent['id'])
|
||||
|
||||
notifiers.send_notification(agent_id=agent['id'],
|
||||
subject=notify_strings[0],
|
||||
body=notify_strings[1],
|
||||
script_args=notify_strings[2],
|
||||
notify_action=notify_action)
|
||||
|
||||
# Set the notification state in the db
|
||||
set_notify_state(session={},
|
||||
notify_action=notify_action,
|
||||
script_args=notify_strings[2])
|
||||
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,
|
||||
agent_id=agent['id'])
|
||||
|
||||
notifiers.send_notification(agent_id=agent['id'],
|
||||
subject=notify_strings[0],
|
||||
body=notify_strings[1],
|
||||
script_args=notify_strings[2],
|
||||
notify_action=notify_action)
|
||||
|
||||
# Set the notification state in the db
|
||||
set_notify_state(session={},
|
||||
notify_action=notify_action,
|
||||
script_args=notify_strings[2])
|
||||
agent_info=agent,
|
||||
notify_strings=notify_strings,
|
||||
metadata={})
|
||||
|
||||
if agent['on_pmsupdate'] and notify_action == 'pmsupdate':
|
||||
# Build and send notification
|
||||
notify_strings = build_server_notify_text(notify_action=notify_action,
|
||||
agent_id=agent['id'])
|
||||
|
||||
notifiers.send_notification(agent_id=agent['id'],
|
||||
subject=notify_strings[0],
|
||||
body=notify_strings[1],
|
||||
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'],
|
||||
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'],
|
||||
'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']}
|
||||
'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, agent_id=None):
|
||||
# Get time formats
|
||||
date_format = plexpy.CONFIG.DATE_FORMAT.replace('Do','').replace('zz','')
|
||||
time_format = plexpy.CONFIG.TIME_FORMAT.replace('Do','').replace('zz','')
|
||||
duration_format = plexpy.CONFIG.TIME_FORMAT.replace('Do','').replace('zz','').replace('a','').replace('A','')
|
||||
date_format = plexpy.CONFIG.DATE_FORMAT.replace('Do','')
|
||||
time_format = plexpy.CONFIG.TIME_FORMAT.replace('Do','')
|
||||
duration_format = plexpy.CONFIG.TIME_FORMAT.replace('Do','').replace('a','').replace('A','')
|
||||
|
||||
# Get the server name
|
||||
server_name = plexpy.CONFIG.PMS_NAME
|
||||
@@ -366,6 +470,10 @@ def build_notify_text(session=None, timeline=None, state=None):
|
||||
plex_tv = plextv.PlexTV()
|
||||
server_times = plex_tv.get_server_times()
|
||||
|
||||
# Get the server version
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
server_identity = pms_connect.get_server_identity()
|
||||
|
||||
if server_times:
|
||||
updated_at = server_times[0]['updated_at']
|
||||
server_uptime = helpers.human_duration(int(time.time() - helpers.cast_to_int(updated_at)))
|
||||
@@ -393,13 +501,13 @@ def build_notify_text(session=None, timeline=None, state=None):
|
||||
# Check for exclusion tags
|
||||
if metadata['media_type'] == 'movie':
|
||||
# Regex pattern to remove the text in the tags we don't want
|
||||
pattern = re.compile('\n*<tv>[^>]+.</tv>\n*|\n*<music>[^>]+.</music>\n*', re.IGNORECASE | re.DOTALL)
|
||||
pattern = re.compile(r'<tv>[^>]+.<\/tv>|<music>[^>]+.<\/music>', re.IGNORECASE | re.DOTALL)
|
||||
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('\n*<movie>[^>]+.</movie>\n*|\n*?<music>[^>]+.</music>\n*', re.IGNORECASE | re.DOTALL)
|
||||
pattern = re.compile(r'<movie>[^>]+.<\/movie>|<music>[^>]+.<\/music>', re.IGNORECASE | re.DOTALL)
|
||||
elif metadata['media_type'] == 'artist' or metadata['media_type'] == 'track':
|
||||
# Regex pattern to remove the text in the tags we don't want
|
||||
pattern = re.compile('\n*<tv>[^>]+.</tv>\n*|\n*<movie>[^>]+.</movie>\n*', re.IGNORECASE | re.DOTALL)
|
||||
pattern = re.compile(r'<tv>[^>]+.<\/tv>|<movie>[^>]+.<\/movie>', re.IGNORECASE | re.DOTALL)
|
||||
else:
|
||||
pattern = None
|
||||
|
||||
@@ -408,37 +516,37 @@ def build_notify_text(session=None, timeline=None, state=None):
|
||||
or metadata['media_type'] == 'artist' or metadata['media_type'] == 'track' \
|
||||
and pattern:
|
||||
# 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_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_START_BODY_TEXT))
|
||||
on_stop_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_STOP_SUBJECT_TEXT))
|
||||
on_stop_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_STOP_BODY_TEXT))
|
||||
on_pause_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_PAUSE_SUBJECT_TEXT))
|
||||
on_pause_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_PAUSE_BODY_TEXT))
|
||||
on_resume_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_RESUME_SUBJECT_TEXT))
|
||||
on_resume_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_RESUME_BODY_TEXT))
|
||||
on_buffer_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_BUFFER_SUBJECT_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_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))
|
||||
script_args_text = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_SCRIPTS_ARGS_TEXT))
|
||||
on_start_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_START_SUBJECT_TEXT), agent_id)
|
||||
on_start_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_START_BODY_TEXT), agent_id)
|
||||
on_stop_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_STOP_SUBJECT_TEXT), agent_id)
|
||||
on_stop_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_STOP_BODY_TEXT), agent_id)
|
||||
on_pause_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_PAUSE_SUBJECT_TEXT), agent_id)
|
||||
on_pause_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_PAUSE_BODY_TEXT), agent_id)
|
||||
on_resume_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_RESUME_SUBJECT_TEXT), agent_id)
|
||||
on_resume_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_RESUME_BODY_TEXT), agent_id)
|
||||
on_buffer_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_BUFFER_SUBJECT_TEXT), agent_id)
|
||||
on_buffer_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_BUFFER_BODY_TEXT), agent_id)
|
||||
on_watched_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_WATCHED_SUBJECT_TEXT), agent_id)
|
||||
on_watched_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_WATCHED_BODY_TEXT), agent_id)
|
||||
on_created_subject = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_CREATED_SUBJECT_TEXT), agent_id)
|
||||
on_created_body = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_ON_CREATED_BODY_TEXT), agent_id)
|
||||
script_args_text = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_SCRIPTS_ARGS_TEXT), agent_id)
|
||||
else:
|
||||
on_start_subject = plexpy.CONFIG.NOTIFY_ON_START_SUBJECT_TEXT
|
||||
on_start_body = plexpy.CONFIG.NOTIFY_ON_START_BODY_TEXT
|
||||
on_stop_subject = plexpy.CONFIG.NOTIFY_ON_STOP_SUBJECT_TEXT
|
||||
on_stop_body = plexpy.CONFIG.NOTIFY_ON_STOP_BODY_TEXT
|
||||
on_pause_subject = plexpy.CONFIG.NOTIFY_ON_PAUSE_SUBJECT_TEXT
|
||||
on_pause_body = plexpy.CONFIG.NOTIFY_ON_PAUSE_BODY_TEXT
|
||||
on_resume_subject = plexpy.CONFIG.NOTIFY_ON_RESUME_SUBJECT_TEXT
|
||||
on_resume_body = plexpy.CONFIG.NOTIFY_ON_RESUME_BODY_TEXT
|
||||
on_buffer_subject = plexpy.CONFIG.NOTIFY_ON_BUFFER_SUBJECT_TEXT
|
||||
on_buffer_body = plexpy.CONFIG.NOTIFY_ON_BUFFER_BODY_TEXT
|
||||
on_watched_subject = plexpy.CONFIG.NOTIFY_ON_WATCHED_SUBJECT_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
|
||||
script_args_text = plexpy.CONFIG.NOTIFY_SCRIPTS_ARGS_TEXT
|
||||
on_start_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_START_SUBJECT_TEXT, agent_id)
|
||||
on_start_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_START_BODY_TEXT, agent_id)
|
||||
on_stop_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_STOP_SUBJECT_TEXT, agent_id)
|
||||
on_stop_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_STOP_BODY_TEXT, agent_id)
|
||||
on_pause_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_PAUSE_SUBJECT_TEXT, agent_id)
|
||||
on_pause_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_PAUSE_BODY_TEXT, agent_id)
|
||||
on_resume_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_RESUME_SUBJECT_TEXT, agent_id)
|
||||
on_resume_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_RESUME_BODY_TEXT, agent_id)
|
||||
on_buffer_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_BUFFER_SUBJECT_TEXT, agent_id)
|
||||
on_buffer_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_BUFFER_BODY_TEXT, agent_id)
|
||||
on_watched_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_WATCHED_SUBJECT_TEXT, agent_id)
|
||||
on_watched_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_WATCHED_BODY_TEXT, agent_id)
|
||||
on_created_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_CREATED_SUBJECT_TEXT, agent_id)
|
||||
on_created_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_CREATED_BODY_TEXT, agent_id)
|
||||
script_args_text = strip_tag(plexpy.CONFIG.NOTIFY_SCRIPTS_ARGS_TEXT, agent_id)
|
||||
|
||||
# Create a title
|
||||
if metadata['media_type'] == 'episode' or metadata['media_type'] == 'track':
|
||||
@@ -459,7 +567,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 +612,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:
|
||||
# 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
|
||||
metadata['poster_url'] = helpers.uploadToImgur(os.path.join(plexpy.CONFIG.CACHE_DIR, 'cache-poster.jpg'), full_title)
|
||||
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 +657,8 @@ 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(),
|
||||
'server_version': server_identity.get('version',''),
|
||||
'action': notify_action.title(),
|
||||
'datestamp': arrow.now().format(date_format),
|
||||
'timestamp': arrow.now().format(time_format),
|
||||
# Stream parameters
|
||||
@@ -628,7 +751,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 +772,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 +796,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 +820,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 +844,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 +868,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 +892,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,17 +914,17 @@ 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, agent_id=None):
|
||||
# Get time formats
|
||||
date_format = plexpy.CONFIG.DATE_FORMAT.replace('Do','').replace('zz','')
|
||||
time_format = plexpy.CONFIG.TIME_FORMAT.replace('Do','').replace('zz','')
|
||||
date_format = plexpy.CONFIG.DATE_FORMAT.replace('Do','')
|
||||
time_format = plexpy.CONFIG.TIME_FORMAT.replace('Do','')
|
||||
|
||||
# Get the server name
|
||||
server_name = plexpy.CONFIG.PMS_NAME
|
||||
@@ -810,6 +933,14 @@ def build_server_notify_text(state=None):
|
||||
plex_tv = plextv.PlexTV()
|
||||
server_times = plex_tv.get_server_times()
|
||||
|
||||
# Get the server version
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
server_identity = pms_connect.get_server_identity()
|
||||
|
||||
update_status = {}
|
||||
if notify_action == 'pmsupdate':
|
||||
update_status = pms_connect.get_update_staus()
|
||||
|
||||
if server_times:
|
||||
updated_at = server_times[0]['updated_at']
|
||||
server_uptime = helpers.human_duration(int(time.time() - helpers.cast_to_int(updated_at)))
|
||||
@@ -817,22 +948,31 @@ def build_server_notify_text(state=None):
|
||||
logger.error(u"PlexPy NotificationHandler :: 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
|
||||
on_extup_subject = plexpy.CONFIG.NOTIFY_ON_EXTUP_SUBJECT_TEXT
|
||||
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
|
||||
pattern = re.compile('\n*<tv>[^>]+.</tv>\n*|\n*<movie>[^>]+.</movie>\n*|\n*?<music>[^>]+.</music>\n*', re.IGNORECASE | re.DOTALL)
|
||||
|
||||
on_extdown_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_EXTDOWN_SUBJECT_TEXT, agent_id)
|
||||
on_extdown_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_EXTDOWN_BODY_TEXT, agent_id)
|
||||
on_intdown_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_INTDOWN_SUBJECT_TEXT, agent_id)
|
||||
on_intdown_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_INTDOWN_BODY_TEXT, agent_id)
|
||||
on_extup_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_EXTUP_SUBJECT_TEXT, agent_id)
|
||||
on_extup_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_EXTUP_BODY_TEXT, agent_id)
|
||||
on_intup_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_INTUP_SUBJECT_TEXT, agent_id)
|
||||
on_intup_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_INTUP_BODY_TEXT, agent_id)
|
||||
on_pmsupdate_subject = strip_tag(plexpy.CONFIG.NOTIFY_ON_PMSUPDATE_SUBJECT_TEXT, agent_id)
|
||||
on_pmsupdate_body = strip_tag(plexpy.CONFIG.NOTIFY_ON_PMSUPDATE_BODY_TEXT, agent_id)
|
||||
script_args_text = strip_tag(re.sub(pattern, '', plexpy.CONFIG.NOTIFY_SCRIPTS_ARGS_TEXT), agent_id)
|
||||
|
||||
available_params = {# Global paramaters
|
||||
'server_name': server_name,
|
||||
'server_uptime': server_uptime,
|
||||
'action': state.title(),
|
||||
'server_version': server_identity.get('version',''),
|
||||
'action': notify_action.title(),
|
||||
'datestamp': arrow.now().format(date_format),
|
||||
'timestamp': arrow.now().format(time_format)}
|
||||
'timestamp': arrow.now().format(time_format),
|
||||
# Update parameters
|
||||
'update_version': update_status.get('version',''),
|
||||
'update_url': update_status.get('download_url',''),
|
||||
'update_changelog': update_status.get('changelog','')}
|
||||
|
||||
# Default text
|
||||
subject_text = 'PlexPy (%s)' % server_name
|
||||
@@ -848,7 +988,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 +1011,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 +1033,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 +1055,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.'
|
||||
|
||||
@@ -938,10 +1078,39 @@ def build_server_notify_text(state=None):
|
||||
else:
|
||||
return [subject_text, body_text, script_args]
|
||||
|
||||
elif notify_action == 'pmsupdate':
|
||||
# Default body text
|
||||
body_text = 'An update is available for the Plex Media Server (version {update_version}).'
|
||||
|
||||
if on_pmsupdate_subject and on_pmsupdate_body:
|
||||
try:
|
||||
subject_text = unicode(on_pmsupdate_subject).format(**available_params)
|
||||
except LookupError, e:
|
||||
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification subject. Using fallback." % e)
|
||||
except:
|
||||
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification subject. Using fallback.")
|
||||
|
||||
try:
|
||||
body_text = unicode(on_pmsupdate_body).format(**available_params)
|
||||
except LookupError, e:
|
||||
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification body. Using fallback." % e)
|
||||
except:
|
||||
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification body. Using fallback.")
|
||||
|
||||
return [subject_text, body_text, script_args]
|
||||
else:
|
||||
return [subject_text, body_text, script_args]
|
||||
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def strip_tag(data):
|
||||
p = re.compile(r'<.*?>')
|
||||
def strip_tag(data, agent_id=None):
|
||||
# Allow tags b, i, u, a[href], font[color] for Pushover
|
||||
if agent_id == 7:
|
||||
p = re.compile(r'<(?!/?(b>|i>|u>)|(a\shref=\"[^\"\'\s]+\"|/a>|font\scolor=\"[^\"\'\s]+\"|/font>)).*?>',
|
||||
re.IGNORECASE | re.DOTALL)
|
||||
else:
|
||||
p = re.compile(r'<.*?>', re.IGNORECASE | re.DOTALL)
|
||||
|
||||
return p.sub('', data)
|
@@ -75,7 +75,8 @@ def available_notification_agents():
|
||||
'on_extdown': plexpy.CONFIG.GROWL_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.GROWL_ON_INTDOWN,
|
||||
'on_extup': plexpy.CONFIG.GROWL_ON_EXTUP,
|
||||
'on_intup': plexpy.CONFIG.GROWL_ON_INTUP
|
||||
'on_intup': plexpy.CONFIG.GROWL_ON_INTUP,
|
||||
'on_pmsupdate': plexpy.CONFIG.GROWL_ON_PMSUPDATE
|
||||
},
|
||||
{'name': 'Prowl',
|
||||
'id': AGENT_IDS['Prowl'],
|
||||
@@ -92,7 +93,8 @@ def available_notification_agents():
|
||||
'on_extdown': plexpy.CONFIG.PROWL_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.PROWL_ON_INTDOWN,
|
||||
'on_extup': plexpy.CONFIG.PROWL_ON_EXTUP,
|
||||
'on_intup': plexpy.CONFIG.PROWL_ON_INTUP
|
||||
'on_intup': plexpy.CONFIG.PROWL_ON_INTUP,
|
||||
'on_pmsupdate': plexpy.CONFIG.PROWL_ON_PMSUPDATE
|
||||
},
|
||||
{'name': 'XBMC',
|
||||
'id': AGENT_IDS['XBMC'],
|
||||
@@ -109,7 +111,8 @@ def available_notification_agents():
|
||||
'on_extdown': plexpy.CONFIG.XBMC_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.XBMC_ON_INTDOWN,
|
||||
'on_extup': plexpy.CONFIG.XBMC_ON_EXTUP,
|
||||
'on_intup': plexpy.CONFIG.XBMC_ON_INTUP
|
||||
'on_intup': plexpy.CONFIG.XBMC_ON_INTUP,
|
||||
'on_pmsupdate': plexpy.CONFIG.XBMC_ON_PMSUPDATE
|
||||
},
|
||||
{'name': 'Plex',
|
||||
'id': AGENT_IDS['Plex'],
|
||||
@@ -126,7 +129,8 @@ def available_notification_agents():
|
||||
'on_extdown': plexpy.CONFIG.PLEX_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.PLEX_ON_INTDOWN,
|
||||
'on_extup': plexpy.CONFIG.PLEX_ON_EXTUP,
|
||||
'on_intup': plexpy.CONFIG.PLEX_ON_INTUP
|
||||
'on_intup': plexpy.CONFIG.PLEX_ON_INTUP,
|
||||
'on_pmsupdate': plexpy.CONFIG.PLEX_ON_PMSUPDATE
|
||||
},
|
||||
{'name': 'NotifyMyAndroid',
|
||||
'id': AGENT_IDS['NMA'],
|
||||
@@ -143,7 +147,8 @@ def available_notification_agents():
|
||||
'on_extdown': plexpy.CONFIG.NMA_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.NMA_ON_INTDOWN,
|
||||
'on_extup': plexpy.CONFIG.NMA_ON_EXTUP,
|
||||
'on_intup': plexpy.CONFIG.NMA_ON_INTUP
|
||||
'on_intup': plexpy.CONFIG.NMA_ON_INTUP,
|
||||
'on_pmsupdate': plexpy.CONFIG.NMA_ON_PMSUPDATE
|
||||
},
|
||||
{'name': 'Pushalot',
|
||||
'id': AGENT_IDS['Pushalot'],
|
||||
@@ -160,7 +165,8 @@ def available_notification_agents():
|
||||
'on_extdown': plexpy.CONFIG.PUSHALOT_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.PUSHALOT_ON_INTDOWN,
|
||||
'on_extup': plexpy.CONFIG.PUSHALOT_ON_EXTUP,
|
||||
'on_intup': plexpy.CONFIG.PUSHALOT_ON_INTUP
|
||||
'on_intup': plexpy.CONFIG.PUSHALOT_ON_INTUP,
|
||||
'on_pmsupdate': plexpy.CONFIG.PUSHALOT_ON_PMSUPDATE
|
||||
},
|
||||
{'name': 'Pushbullet',
|
||||
'id': AGENT_IDS['Pushbullet'],
|
||||
@@ -177,7 +183,8 @@ def available_notification_agents():
|
||||
'on_extdown': plexpy.CONFIG.PUSHBULLET_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.PUSHBULLET_ON_INTDOWN,
|
||||
'on_extup': plexpy.CONFIG.PUSHBULLET_ON_EXTUP,
|
||||
'on_intup': plexpy.CONFIG.PUSHBULLET_ON_INTUP
|
||||
'on_intup': plexpy.CONFIG.PUSHBULLET_ON_INTUP,
|
||||
'on_pmsupdate': plexpy.CONFIG.PUSHBULLET_ON_PMSUPDATE
|
||||
},
|
||||
{'name': 'Pushover',
|
||||
'id': AGENT_IDS['Pushover'],
|
||||
@@ -194,7 +201,8 @@ def available_notification_agents():
|
||||
'on_extdown': plexpy.CONFIG.PUSHOVER_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.PUSHOVER_ON_INTDOWN,
|
||||
'on_extup': plexpy.CONFIG.PUSHOVER_ON_EXTUP,
|
||||
'on_intup': plexpy.CONFIG.PUSHOVER_ON_INTUP
|
||||
'on_intup': plexpy.CONFIG.PUSHOVER_ON_INTUP,
|
||||
'on_pmsupdate': plexpy.CONFIG.PUSHOVER_ON_PMSUPDATE
|
||||
},
|
||||
{'name': 'Boxcar2',
|
||||
'id': AGENT_IDS['Boxcar2'],
|
||||
@@ -211,7 +219,8 @@ def available_notification_agents():
|
||||
'on_extdown': plexpy.CONFIG.BOXCAR_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.BOXCAR_ON_INTDOWN,
|
||||
'on_extup': plexpy.CONFIG.BOXCAR_ON_EXTUP,
|
||||
'on_intup': plexpy.CONFIG.BOXCAR_ON_INTUP
|
||||
'on_intup': plexpy.CONFIG.BOXCAR_ON_INTUP,
|
||||
'on_pmsupdate': plexpy.CONFIG.BOXCAR_ON_PMSUPDATE
|
||||
},
|
||||
{'name': 'E-mail',
|
||||
'id': AGENT_IDS['Email'],
|
||||
@@ -228,7 +237,8 @@ def available_notification_agents():
|
||||
'on_extdown': plexpy.CONFIG.EMAIL_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.EMAIL_ON_INTDOWN,
|
||||
'on_extup': plexpy.CONFIG.EMAIL_ON_EXTUP,
|
||||
'on_intup': plexpy.CONFIG.EMAIL_ON_INTUP
|
||||
'on_intup': plexpy.CONFIG.EMAIL_ON_INTUP,
|
||||
'on_pmsupdate': plexpy.CONFIG.EMAIL_ON_PMSUPDATE
|
||||
},
|
||||
{'name': 'Twitter',
|
||||
'id': AGENT_IDS['Twitter'],
|
||||
@@ -245,7 +255,8 @@ def available_notification_agents():
|
||||
'on_extdown': plexpy.CONFIG.TWITTER_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.TWITTER_ON_INTDOWN,
|
||||
'on_extup': plexpy.CONFIG.TWITTER_ON_EXTUP,
|
||||
'on_intup': plexpy.CONFIG.TWITTER_ON_INTUP
|
||||
'on_intup': plexpy.CONFIG.TWITTER_ON_INTUP,
|
||||
'on_pmsupdate': plexpy.CONFIG.TWITTER_ON_PMSUPDATE
|
||||
},
|
||||
{'name': 'IFTTT',
|
||||
'id': AGENT_IDS['IFTTT'],
|
||||
@@ -262,7 +273,8 @@ def available_notification_agents():
|
||||
'on_extdown': plexpy.CONFIG.IFTTT_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.IFTTT_ON_INTDOWN,
|
||||
'on_extup': plexpy.CONFIG.IFTTT_ON_EXTUP,
|
||||
'on_intup': plexpy.CONFIG.IFTTT_ON_INTUP
|
||||
'on_intup': plexpy.CONFIG.IFTTT_ON_INTUP,
|
||||
'on_pmsupdate': plexpy.CONFIG.IFTTT_ON_PMSUPDATE
|
||||
},
|
||||
{'name': 'Telegram',
|
||||
'id': AGENT_IDS['Telegram'],
|
||||
@@ -279,7 +291,8 @@ def available_notification_agents():
|
||||
'on_extdown': plexpy.CONFIG.TELEGRAM_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.TELEGRAM_ON_INTDOWN,
|
||||
'on_extup': plexpy.CONFIG.TELEGRAM_ON_EXTUP,
|
||||
'on_intup': plexpy.CONFIG.TELEGRAM_ON_INTUP
|
||||
'on_intup': plexpy.CONFIG.TELEGRAM_ON_INTUP,
|
||||
'on_pmsupdate': plexpy.CONFIG.TELEGRAM_ON_PMSUPDATE
|
||||
},
|
||||
{'name': 'Slack',
|
||||
'id': AGENT_IDS['Slack'],
|
||||
@@ -296,7 +309,8 @@ def available_notification_agents():
|
||||
'on_extdown': plexpy.CONFIG.SLACK_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.SLACK_ON_INTDOWN,
|
||||
'on_extup': plexpy.CONFIG.SLACK_ON_EXTUP,
|
||||
'on_intup': plexpy.CONFIG.SLACK_ON_INTUP
|
||||
'on_intup': plexpy.CONFIG.SLACK_ON_INTUP,
|
||||
'on_pmsupdate': plexpy.CONFIG.SLACK_ON_PMSUPDATE
|
||||
},
|
||||
{'name': 'Scripts',
|
||||
'id': AGENT_IDS['Scripts'],
|
||||
@@ -313,7 +327,8 @@ def available_notification_agents():
|
||||
'on_extdown': plexpy.CONFIG.SCRIPTS_ON_EXTDOWN,
|
||||
'on_extup': plexpy.CONFIG.SCRIPTS_ON_EXTUP,
|
||||
'on_intdown': plexpy.CONFIG.SCRIPTS_ON_INTDOWN,
|
||||
'on_intup': plexpy.CONFIG.SCRIPTS_ON_INTUP
|
||||
'on_intup': plexpy.CONFIG.SCRIPTS_ON_INTUP,
|
||||
'on_pmsupdate': plexpy.CONFIG.SCRIPTS_ON_PMSUPDATE
|
||||
},
|
||||
{'name': 'Facebook',
|
||||
'id': AGENT_IDS['Facebook'],
|
||||
@@ -330,7 +345,8 @@ def available_notification_agents():
|
||||
'on_extdown': plexpy.CONFIG.FACEBOOK_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.FACEBOOK_ON_INTDOWN,
|
||||
'on_extup': plexpy.CONFIG.FACEBOOK_ON_EXTUP,
|
||||
'on_intup': plexpy.CONFIG.FACEBOOK_ON_INTUP
|
||||
'on_intup': plexpy.CONFIG.FACEBOOK_ON_INTUP,
|
||||
'on_pmsupdate': plexpy.CONFIG.FACEBOOK_ON_PMSUPDATE
|
||||
}
|
||||
]
|
||||
|
||||
@@ -352,7 +368,8 @@ def available_notification_agents():
|
||||
'on_extdown': plexpy.CONFIG.OSX_NOTIFY_ON_EXTDOWN,
|
||||
'on_intdown': plexpy.CONFIG.OSX_NOTIFY_ON_INTDOWN,
|
||||
'on_extup': plexpy.CONFIG.OSX_NOTIFY_ON_EXTUP,
|
||||
'on_intup': plexpy.CONFIG.OSX_NOTIFY_ON_INTUP
|
||||
'on_intup': plexpy.CONFIG.OSX_NOTIFY_ON_INTUP,
|
||||
'on_pmsupdate': plexpy.CONFIG.OSX_NOTIFY_ON_PMSUPDATE
|
||||
})
|
||||
|
||||
return agents
|
||||
@@ -1054,6 +1071,7 @@ class PUSHOVER(object):
|
||||
self.keys = plexpy.CONFIG.PUSHOVER_KEYS
|
||||
self.priority = plexpy.CONFIG.PUSHOVER_PRIORITY
|
||||
self.sound = plexpy.CONFIG.PUSHOVER_SOUND
|
||||
self.html_support = plexpy.CONFIG.PUSHOVER_HTML_SUPPORT
|
||||
|
||||
def conf(self, options):
|
||||
return cherrypy.config['config'].get('Pushover', options)
|
||||
@@ -1069,6 +1087,7 @@ class PUSHOVER(object):
|
||||
'title': event.encode("utf-8"),
|
||||
'message': message.encode("utf-8"),
|
||||
'sound': plexpy.CONFIG.PUSHOVER_SOUND,
|
||||
'html': plexpy.CONFIG.PUSHOVER_HTML_SUPPORT,
|
||||
'priority': plexpy.CONFIG.PUSHOVER_PRIORITY}
|
||||
|
||||
http_handler.request("POST",
|
||||
@@ -1095,11 +1114,12 @@ class PUSHOVER(object):
|
||||
# For uniformity reasons not removed
|
||||
return
|
||||
|
||||
def test(self, keys, priority, sound):
|
||||
def test(self, keys, priority, sound, html_support):
|
||||
self.enabled = True
|
||||
self.keys = keys
|
||||
self.priority = priority
|
||||
self.sound = sound
|
||||
self.html_support = html_support
|
||||
|
||||
self.notify('Main Screen Activate', 'Test Message')
|
||||
|
||||
@@ -1151,6 +1171,12 @@ class PUSHOVER(object):
|
||||
'description': 'Set the notification sound. Leave blank for the default sound.',
|
||||
'input_type': 'select',
|
||||
'select_options': self.get_sounds()
|
||||
},
|
||||
{'label': 'Enable HTML Support',
|
||||
'value': self.html_support,
|
||||
'name': 'pushover_html_support',
|
||||
'description': 'Style your messages using these HTML Tags: b, i, u, a[href], font[color]',
|
||||
'input_type': 'checkbox'
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1257,7 +1283,7 @@ class TwitterNotifier(object):
|
||||
|
||||
def return_config_options(self):
|
||||
config_option = [{'label': 'Instructions',
|
||||
'description': 'Step 1: Visit <a href="https://apps.twitter.com/" target="_blank"> \
|
||||
'description': 'Step 1: Visit <a href="' + helpers.anon_url('https://apps.twitter.com') + '" target="_blank"> \
|
||||
Twitter Apps</a> to <strong>Create New App</strong>. A vaild "Website" is not required.<br>\
|
||||
Step 2: Go to <strong>Keys and Access Tokens</strong> and click \
|
||||
<strong>Create my access token</strong>.<br>\
|
||||
@@ -1292,7 +1318,7 @@ class TwitterNotifier(object):
|
||||
{'label': 'Include Subject Line',
|
||||
'value': self.incl_subject,
|
||||
'name': 'twitter_incl_subject',
|
||||
'description': 'Include the subject line in the notifications.',
|
||||
'description': 'Include the subject line with the notifications.',
|
||||
'input_type': 'checkbox'
|
||||
}
|
||||
]
|
||||
@@ -1616,7 +1642,8 @@ class IFTTT(object):
|
||||
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>.',
|
||||
'description': 'Your Ifttt key. You can get a key from'
|
||||
' <a href="' + helpers.anon_url('https://ifttt.com/maker') + '" target="_blank">here</a>.',
|
||||
'input_type': 'text'
|
||||
},
|
||||
{'label': 'Ifttt Event',
|
||||
@@ -1690,19 +1717,23 @@ class TELEGRAM(object):
|
||||
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.',
|
||||
'description': 'Your Telegram bot token. '
|
||||
'Contact <a href="' + helpers.anon_url('https://telegram.me/BotFather') + '" target="_blank">@BotFather</a>'
|
||||
' on Telegram to get one.',
|
||||
'input_type': 'text'
|
||||
},
|
||||
{'label': 'Telegram Chat ID, Group ID, or Channel Username',
|
||||
'value': self.chat_id,
|
||||
'name': 'telegram_chat_id',
|
||||
'description': 'Your Telegram Chat ID, Group ID, or @channelusername. Contact <a href="http://telegram.me/myidbot" target="_blank">@myidbot</a> on Telegram to get an ID.',
|
||||
'description': 'Your Telegram Chat ID, Group ID, or @channelusername. '
|
||||
'Contact <a href="' + helpers.anon_url('https://telegram.me/myidbot') + '" target="_blank">@myidbot</a>'
|
||||
' on Telegram to get an ID.',
|
||||
'input_type': 'text'
|
||||
},
|
||||
{'label': 'Include Subject Line',
|
||||
'value': self.incl_subject,
|
||||
'name': 'telegram_incl_subject',
|
||||
'description': 'Include the subject line in the notifications.',
|
||||
'description': 'Include the subject line with the notifications.',
|
||||
'input_type': 'checkbox'
|
||||
}
|
||||
]
|
||||
@@ -1800,7 +1831,7 @@ class SLACK(object):
|
||||
{'label': 'Include Subject Line',
|
||||
'value': self.incl_subject,
|
||||
'name': 'slack_incl_subject',
|
||||
'description': 'Include the subject line in the notifications.',
|
||||
'description': 'Include the subject line with the notifications.',
|
||||
'input_type': 'checkbox'
|
||||
}
|
||||
]
|
||||
@@ -1871,14 +1902,14 @@ class Scripts(object):
|
||||
elif notify_action == 'resume':
|
||||
script = plexpy.CONFIG.SCRIPTS_ON_RESUME_SCRIPT
|
||||
|
||||
elif notify_action == 'watched':
|
||||
script = plexpy.CONFIG.SCRIPTS_ON_WATCHED_SCRIPT
|
||||
|
||||
elif notify_action == 'buffer':
|
||||
script = plexpy.CONFIG.SCRIPTS_ON_BUFFER_SCRIPT
|
||||
|
||||
elif notify_action == 'extdown':
|
||||
script = plexpy.CONFIG.SCRIPTS_ON_EXTDOWN_SCRIPT
|
||||
|
||||
elif notify_action == 'extup':
|
||||
script = plexpy.CONFIG.SCRIPTS_ON_EXTUP_SCRIPT
|
||||
elif notify_action == 'created':
|
||||
script = plexpy.CONFIG.SCRIPTS_ON_CREATED_SCRIPT
|
||||
|
||||
elif notify_action == 'intdown':
|
||||
script = plexpy.CONFIG.SCRIPTS_ON_INTDOWN_SCRIPT
|
||||
@@ -1886,11 +1917,14 @@ class Scripts(object):
|
||||
elif notify_action == 'intup':
|
||||
script = plexpy.CONFIG.SCRIPTS_ON_INTUP_SCRIPT
|
||||
|
||||
elif notify_action == 'created':
|
||||
script = plexpy.CONFIG.SCRIPTS_ON_CREATED_SCRIPT
|
||||
elif notify_action == 'extdown':
|
||||
script = plexpy.CONFIG.SCRIPTS_ON_EXTDOWN_SCRIPT
|
||||
|
||||
elif notify_action == 'watched':
|
||||
script = plexpy.CONFIG.SCRIPTS_ON_WATCHED_SCRIPT
|
||||
elif notify_action == 'extup':
|
||||
script = plexpy.CONFIG.SCRIPTS_ON_EXTUP_SCRIPT
|
||||
|
||||
elif notify_action == 'pmsupdate':
|
||||
script = plexpy.CONFIG.SCRIPTS_ON_PMSUPDATE_SCRIPT
|
||||
|
||||
else:
|
||||
# For manual scripts
|
||||
@@ -2032,13 +2066,6 @@ class Scripts(object):
|
||||
'input_type': 'select',
|
||||
'select_options': self.list_scripts()
|
||||
},
|
||||
{'label': 'Plex Remote Access Down',
|
||||
'value': plexpy.CONFIG.SCRIPTS_ON_EXTDOWN_SCRIPT,
|
||||
'name': 'scripts_on_extdown_script',
|
||||
'description': 'Choose the script for Plex remote access down.',
|
||||
'input_type': 'select',
|
||||
'select_options': self.list_scripts()
|
||||
},
|
||||
{'label': 'Plex Server Down',
|
||||
'value': plexpy.CONFIG.SCRIPTS_ON_INTDOWN_SCRIPT,
|
||||
'name': 'scripts_on_intdown_script',
|
||||
@@ -2046,6 +2073,20 @@ class Scripts(object):
|
||||
'input_type': 'select',
|
||||
'select_options': self.list_scripts()
|
||||
},
|
||||
{'label': 'Plex Server Back Up',
|
||||
'value': plexpy.CONFIG.SCRIPTS_ON_INTUP_SCRIPT,
|
||||
'name': 'scripts_on_intup_script',
|
||||
'description': 'Choose the script for Plex server back up.',
|
||||
'input_type': 'select',
|
||||
'select_options': self.list_scripts()
|
||||
},
|
||||
{'label': 'Plex Remote Access Down',
|
||||
'value': plexpy.CONFIG.SCRIPTS_ON_EXTDOWN_SCRIPT,
|
||||
'name': 'scripts_on_extdown_script',
|
||||
'description': 'Choose the script for Plex remote access down.',
|
||||
'input_type': 'select',
|
||||
'select_options': self.list_scripts()
|
||||
},
|
||||
{'label': 'Plex Remote Access Back Up',
|
||||
'value': plexpy.CONFIG.SCRIPTS_ON_EXTUP_SCRIPT,
|
||||
'name': 'scripts_on_extup_script',
|
||||
@@ -2053,10 +2094,10 @@ class Scripts(object):
|
||||
'input_type': 'select',
|
||||
'select_options': self.list_scripts()
|
||||
},
|
||||
{'label': 'Plex Server Back Up',
|
||||
'value': plexpy.CONFIG.SCRIPTS_ON_INTUP_SCRIPT,
|
||||
'name': 'scripts_on_intup_script',
|
||||
'description': 'Choose the script for Plex server back up.',
|
||||
{'label': 'Plex Update Available',
|
||||
'value': plexpy.CONFIG.SCRIPTS_ON_PMSUPDATE_SCRIPT,
|
||||
'name': 'scripts_on_pmsupdate_script',
|
||||
'description': 'Choose the script for Plex update available.',
|
||||
'input_type': 'select',
|
||||
'select_options': self.list_scripts()
|
||||
}
|
||||
@@ -2073,6 +2114,7 @@ class FacebookNotifier(object):
|
||||
self.app_id = plexpy.CONFIG.FACEBOOK_APP_ID
|
||||
self.app_secret = plexpy.CONFIG.FACEBOOK_APP_SECRET
|
||||
self.group_id = plexpy.CONFIG.FACEBOOK_GROUP
|
||||
self.incl_pmslink = plexpy.CONFIG.FACEBOOK_INCL_PMSLINK
|
||||
self.incl_poster = plexpy.CONFIG.FACEBOOK_INCL_POSTER
|
||||
self.incl_subject = plexpy.CONFIG.FACEBOOK_INCL_SUBJECT
|
||||
|
||||
@@ -2130,10 +2172,27 @@ class FacebookNotifier(object):
|
||||
poster_url = metadata.get('poster_url','')
|
||||
|
||||
if poster_url:
|
||||
if metadata['media_type'] == 'movie' or metadata['media_type'] == 'show':
|
||||
if metadata['media_type'] == 'movie':
|
||||
title = metadata['title']
|
||||
subtitle = metadata['year']
|
||||
rating_key = metadata['rating_key']
|
||||
if metadata.get('imdb_url',''):
|
||||
poster_link = metadata.get('imdb_url', '')
|
||||
caption = 'View on IMDB.'
|
||||
elif metadata.get('themoviedb_url',''):
|
||||
poster_link = metadata.get('themoviedb_url', '')
|
||||
caption = 'View on The Movie Database.'
|
||||
|
||||
elif metadata['media_type'] == 'show':
|
||||
title = metadata['title']
|
||||
subtitle = metadata['year']
|
||||
rating_key = metadata['rating_key']
|
||||
if metadata.get('thetvdb_url',''):
|
||||
poster_link = metadata.get('thetvdb_url', '')
|
||||
caption = 'View on TheTVDB.'
|
||||
elif metadata.get('themoviedb_url',''):
|
||||
poster_link = metadata.get('themoviedb_url', '')
|
||||
caption = 'View on The Movie Database.'
|
||||
|
||||
elif metadata['media_type'] == 'episode':
|
||||
title = '%s - %s' % (metadata['grandparent_title'], metadata['title'])
|
||||
@@ -2141,26 +2200,44 @@ class FacebookNotifier(object):
|
||||
'\xc2\xb7'.decode('utf8'),
|
||||
metadata['media_index'])
|
||||
rating_key = metadata['rating_key']
|
||||
if metadata.get('thetvdb_url',''):
|
||||
poster_link = metadata.get('thetvdb_url', '')
|
||||
caption = 'View on TheTVDB.'
|
||||
elif metadata.get('themoviedb_url',''):
|
||||
poster_link = metadata.get('themoviedb_url', '')
|
||||
caption = 'View on The Movie Database.'
|
||||
|
||||
elif metadata['media_type'] == 'artist':
|
||||
title = metadata['title']
|
||||
subtitle = ''
|
||||
rating_key = metadata['rating_key']
|
||||
if metadata.get('lastfm_url',''):
|
||||
poster_link = metadata.get('lastfm_url', '')
|
||||
caption = 'View on Last.fm.'
|
||||
|
||||
elif metadata['media_type'] == 'track':
|
||||
title = '%s - %s' % (metadata['grandparent_title'], metadata['title'])
|
||||
subtitle = metadata['parent_title']
|
||||
rating_key = metadata['parent_rating_key']
|
||||
|
||||
caption = 'View in Plex Web.'
|
||||
if metadata.get('lastfm_url',''):
|
||||
poster_link = metadata.get('lastfm_url', '')
|
||||
caption = 'View on Last.fm.'
|
||||
|
||||
# Build Facebook post attachment
|
||||
if self.incl_pmslink:
|
||||
caption = 'View on Plex Web.'
|
||||
attachment['link'] = 'http://app.plex.tv/web/app#!/server/' + plexpy.CONFIG.PMS_IDENTIFIER + \
|
||||
'/details/%2Flibrary%2Fmetadata%2F' + rating_key
|
||||
attachment['caption'] = caption
|
||||
elif poster_link:
|
||||
attachment['link'] = poster_link
|
||||
attachment['caption'] = caption
|
||||
else:
|
||||
attachment['link'] = poster_url
|
||||
|
||||
attachment['picture'] = poster_url
|
||||
attachment['name'] = title
|
||||
attachment['description'] = subtitle
|
||||
attachment['caption'] = caption
|
||||
|
||||
try:
|
||||
api.put_wall_post(profile_id=self.group_id, message=message, attachment=attachment)
|
||||
@@ -2177,7 +2254,7 @@ class FacebookNotifier(object):
|
||||
def return_config_options(self):
|
||||
config_option = [{'label': 'Instructions',
|
||||
'description': '<strong>Facebook notifications are currently experimental!</strong><br><br> \
|
||||
Step 1: Visit <a href="https://developers.facebook.com/apps/" target="_blank"> \
|
||||
Step 1: Visit <a href="' + helpers.anon_url('https://developers.facebook.com/apps') + '" target="_blank"> \
|
||||
Facebook Developers</a> to add a new app using <strong>basic setup</strong>.<br>\
|
||||
Step 2: Go to <strong>Settings > Basic</strong> and fill in a \
|
||||
<strong>Contact Email</strong>.<br>\
|
||||
@@ -2223,13 +2300,20 @@ class FacebookNotifier(object):
|
||||
{'label': 'Include Poster Image',
|
||||
'value': self.incl_poster,
|
||||
'name': 'facebook_incl_poster',
|
||||
'description': 'Include a poster and link in the notifications.',
|
||||
'description': 'Include a poster with the notifications.',
|
||||
'input_type': 'checkbox'
|
||||
},
|
||||
{'label': 'Include Link to Plex Web',
|
||||
'value': self.incl_pmslink,
|
||||
'name': 'facebook_incl_pmslink',
|
||||
'description': 'Include a link to the media in Plex Web with the notifications.<br>'
|
||||
'If disabled, the link will go to IMDB, TVDB, TMDb, or Last.fm instead, if available.',
|
||||
'input_type': 'checkbox'
|
||||
},
|
||||
{'label': 'Include Subject Line',
|
||||
'value': self.incl_subject,
|
||||
'name': 'facebook_incl_subject',
|
||||
'description': 'Include the subject line in the notifications.',
|
||||
'description': 'Include the subject line with the notifications.',
|
||||
'input_type': 'checkbox'
|
||||
}
|
||||
]
|
||||
|
@@ -251,7 +251,7 @@ def import_from_plexwatch(database=None, table_name=None, import_ignore_interval
|
||||
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)
|
||||
plexpy.schedule_job(activity_pinger.check_server_response, 'Check for server response',
|
||||
plexpy.schedule_job(activity_pinger.check_server_response, 'Check for Plex remote access',
|
||||
hours=0, minutes=0, seconds=0)
|
||||
|
||||
ap = activity_processor.ActivityProcessor()
|
||||
@@ -302,7 +302,7 @@ def import_from_plexwatch(database=None, table_name=None, import_ignore_interval
|
||||
|
||||
# Skip line if we don't have a ratingKey to work with
|
||||
if not row['rating_key']:
|
||||
logger.error(u"PlexPy Importer :: Skipping record due to null ratingRey.")
|
||||
logger.error(u"PlexPy Importer :: Skipping record due to null ratingKey.")
|
||||
continue
|
||||
|
||||
# If the user_id no longer exists in the friends list, pull it from the xml.
|
||||
|
@@ -389,6 +389,38 @@ class PmsConnect(object):
|
||||
|
||||
return request
|
||||
|
||||
def put_updater(self, output_format=''):
|
||||
"""
|
||||
Refresh updater status.
|
||||
|
||||
Optional parameters: output_format { dict, json }
|
||||
|
||||
Output: array
|
||||
"""
|
||||
uri = '/updater/check?download=0'
|
||||
request = self.request_handler.make_request(uri=uri,
|
||||
proto=self.protocol,
|
||||
request_type='PUT',
|
||||
output_format=output_format)
|
||||
|
||||
return request
|
||||
|
||||
def get_updater(self, output_format=''):
|
||||
"""
|
||||
Return updater status.
|
||||
|
||||
Optional parameters: output_format { dict, json }
|
||||
|
||||
Output: array
|
||||
"""
|
||||
uri = '/updater/status'
|
||||
request = self.request_handler.make_request(uri=uri,
|
||||
proto=self.protocol,
|
||||
request_type='GET',
|
||||
output_format=output_format)
|
||||
|
||||
return request
|
||||
|
||||
def get_recently_added_details(self, section_id='', count='0'):
|
||||
"""
|
||||
Return processed and validated list of recently added items.
|
||||
@@ -1008,7 +1040,7 @@ class PmsConnect(object):
|
||||
'user_id': user_details['user_id'],
|
||||
'friendly_name': user_details['friendly_name'],
|
||||
'user_thumb': user_details['user_thumb'],
|
||||
'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address').split(':')[-1],
|
||||
'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address').split('::ffff:')[-1],
|
||||
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
||||
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
||||
'machine_id': machine_id,
|
||||
@@ -1130,7 +1162,7 @@ class PmsConnect(object):
|
||||
'user_id': user_details['user_id'],
|
||||
'friendly_name': user_details['friendly_name'],
|
||||
'user_thumb': user_details['user_thumb'],
|
||||
'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address').split(':')[-1],
|
||||
'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address').split('::ffff:')[-1],
|
||||
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
||||
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
||||
'machine_id': machine_id,
|
||||
@@ -1188,7 +1220,7 @@ class PmsConnect(object):
|
||||
'user_id': user_details['user_id'],
|
||||
'friendly_name': user_details['friendly_name'],
|
||||
'user_thumb': user_details['user_thumb'],
|
||||
'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address').split(':')[-1],
|
||||
'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address').split('::ffff:')[-1],
|
||||
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
||||
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
||||
'machine_id': machine_id,
|
||||
@@ -1246,7 +1278,7 @@ class PmsConnect(object):
|
||||
'user_id': user_details['user_id'],
|
||||
'friendly_name': user_details['friendly_name'],
|
||||
'user_thumb': user_details['user_thumb'],
|
||||
'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address').split(':')[-1],
|
||||
'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address').split('::ffff:')[-1],
|
||||
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
||||
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
||||
'machine_id': machine_id,
|
||||
@@ -1337,7 +1369,7 @@ class PmsConnect(object):
|
||||
'user_id': user_details['user_id'],
|
||||
'friendly_name': user_details['friendly_name'],
|
||||
'user_thumb': user_details['user_thumb'],
|
||||
'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address').split(':')[-1],
|
||||
'ip_address': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'address').split('::ffff:')[-1],
|
||||
'player': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'title'),
|
||||
'platform': helpers.get_xml_attr(session.getElementsByTagName('Player')[0], 'platform'),
|
||||
'machine_id': machine_id,
|
||||
@@ -1479,7 +1511,7 @@ class PmsConnect(object):
|
||||
xml_head = identity.getElementsByTagName('MediaContainer')
|
||||
except Exception as e:
|
||||
logger.warn(u"PlexPy Pmsconnect :: Unable to parse XML for get_local_server_identity: %s." % e)
|
||||
return []
|
||||
return {}
|
||||
|
||||
server_identity = {}
|
||||
for a in xml_head:
|
||||
@@ -1995,3 +2027,40 @@ class PmsConnect(object):
|
||||
}
|
||||
|
||||
return server_response
|
||||
|
||||
def get_update_staus(self):
|
||||
# Refresh the Plex updater status first
|
||||
self.put_updater()
|
||||
updater_status = self.get_updater(output_format='xml')
|
||||
|
||||
try:
|
||||
xml_head = updater_status.getElementsByTagName('MediaContainer')
|
||||
except Exception as e:
|
||||
logger.warn(u"PlexPy Pmsconnect :: Unable to parse XML for get_update_staus: %s." % e)
|
||||
|
||||
# Catch the malformed XML on certain PMX version.
|
||||
# XML parser helper returns empty list if there is an error parsing XML
|
||||
if updater_status == []:
|
||||
logger.warn(u"Plex API updater XML is broken on the current PMS version. Please update your PMS manually.")
|
||||
logger.info(u"PlexPy is unable to check for Plex updates. Disabling check for Plex updates.")
|
||||
|
||||
# Disable check for Plex updates
|
||||
plexpy.CONFIG.MONITOR_PMS_UPDATES = 0
|
||||
plexpy.initialize_scheduler()
|
||||
plexpy.CONFIG.write()
|
||||
|
||||
return {}
|
||||
|
||||
updater_info = {}
|
||||
for a in xml_head:
|
||||
if a.getElementsByTagName('Release'):
|
||||
release = a.getElementsByTagName('Release')
|
||||
for item in release:
|
||||
updater_info = {'can_install': helpers.get_xml_attr(a, 'canInstall'),
|
||||
'download_url': helpers.get_xml_attr(a, 'downloadURL'),
|
||||
'version': helpers.get_xml_attr(item, 'version'),
|
||||
'state': helpers.get_xml_attr(item, 'state'),
|
||||
'changelog': helpers.get_xml_attr(item, 'fixed')
|
||||
}
|
||||
|
||||
return updater_info
|
||||
|
@@ -50,8 +50,7 @@ class Users(object):
|
||||
'session_history_metadata.year',
|
||||
'session_history_metadata.media_index',
|
||||
'session_history_metadata.parent_media_index',
|
||||
'session_history_media_info.video_decision',
|
||||
'session_history_media_info.audio_decision',
|
||||
'session_history_media_info.transcode_decision',
|
||||
'users.do_notify as do_notify',
|
||||
'users.keep_history as keep_history'
|
||||
]
|
||||
@@ -117,8 +116,7 @@ class Users(object):
|
||||
'year': item['year'],
|
||||
'media_index': item['media_index'],
|
||||
'parent_media_index': item['parent_media_index'],
|
||||
'video_decision': item['video_decision'],
|
||||
'audio_decision': item['audio_decision'],
|
||||
'transcode_decision': item['transcode_decision'],
|
||||
'do_notify': helpers.checked(item['do_notify']),
|
||||
'keep_history': helpers.checked(item['keep_history'])
|
||||
}
|
||||
@@ -154,8 +152,7 @@ class Users(object):
|
||||
'session_history_metadata.year',
|
||||
'session_history_metadata.media_index',
|
||||
'session_history_metadata.parent_media_index',
|
||||
'session_history_media_info.video_decision',
|
||||
'session_history_media_info.audio_decision',
|
||||
'session_history_media_info.transcode_decision',
|
||||
'session_history.user',
|
||||
'session_history.user_id as custom_user_id',
|
||||
'(CASE WHEN users.friendly_name IS NULL THEN users.username ELSE \
|
||||
@@ -213,8 +210,7 @@ class Users(object):
|
||||
'year': item['year'],
|
||||
'media_index': item['media_index'],
|
||||
'parent_media_index': item['parent_media_index'],
|
||||
'video_decision': item['video_decision'],
|
||||
'audio_decision': item['audio_decision'],
|
||||
'transcode_decision': item['transcode_decision'],
|
||||
'friendly_name': item['friendly_name']
|
||||
}
|
||||
|
||||
@@ -245,7 +241,7 @@ class Users(object):
|
||||
def get_details(self, user_id=None, user=None):
|
||||
from plexpy import plextv
|
||||
|
||||
default_return = {'user_id': None,
|
||||
default_return = {'user_id': 0,
|
||||
'username': 'Local',
|
||||
'friendly_name': 'Local',
|
||||
'user_thumb': common.DEFAULT_USER_THUMB,
|
||||
@@ -254,7 +250,7 @@ class Users(object):
|
||||
'is_allow_sync': 0,
|
||||
'is_restricted': 0,
|
||||
'do_notify': 0,
|
||||
'keep_history': 0
|
||||
'keep_history': 1
|
||||
}
|
||||
|
||||
if not user_id and not user:
|
||||
@@ -316,7 +312,8 @@ class Users(object):
|
||||
return user_details
|
||||
|
||||
else:
|
||||
logger.warn(u"PlexPy Users :: Unable to retrieve user from local database. Requesting user list refresh.")
|
||||
logger.warn(u"PlexPy Users :: Unable to retrieve user %s from database. Requesting user list refresh."
|
||||
% user_id if user_id else user)
|
||||
# Let's first refresh the user list to make sure the user isn't newly added and not in the db yet
|
||||
plextv.refresh_users()
|
||||
|
||||
@@ -326,7 +323,8 @@ class Users(object):
|
||||
return user_details
|
||||
|
||||
else:
|
||||
logger.warn(u"PlexPy Users :: Unable to retrieve user from local database. Returning 'Local' user.")
|
||||
logger.warn(u"PlexPy Users :: Unable to retrieve user %s from database. Returning 'Local' user."
|
||||
% user_id if user_id else user)
|
||||
# If there is no user data we must return something
|
||||
# Use "Local" user to retain compatibility with PlexWatch database value
|
||||
return default_return
|
||||
|
@@ -1,2 +1,2 @@
|
||||
PLEXPY_VERSION = "master"
|
||||
PLEXPY_RELEASE_VERSION = "1.3.7"
|
||||
PLEXPY_RELEASE_VERSION = "1.3.11"
|
||||
|
@@ -25,6 +25,7 @@ import websocket
|
||||
|
||||
name = 'websocket'
|
||||
opcode_data = (websocket.ABNF.OPCODE_TEXT, websocket.ABNF.OPCODE_BINARY)
|
||||
ws_reconnect = False
|
||||
|
||||
|
||||
def start_thread():
|
||||
@@ -34,34 +35,43 @@ def start_thread():
|
||||
threading.Thread(target=run).start()
|
||||
|
||||
|
||||
def reconnect():
|
||||
global ws_reconnect
|
||||
ws_reconnect = True
|
||||
|
||||
|
||||
def run():
|
||||
from websocket import create_connection
|
||||
|
||||
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:
|
||||
uri += '?X-Plex-Token=' + plexpy.CONFIG.PMS_TOKEN
|
||||
|
||||
global ws_reconnect
|
||||
ws_reconnect = False
|
||||
ws_connected = False
|
||||
reconnects = 0
|
||||
|
||||
# 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
|
||||
logger.info(u'PlexPy WebSocket :: Ready')
|
||||
logger.info(u"PlexPy WebSocket :: Ready")
|
||||
except IOError, e:
|
||||
logger.error(u'PlexPy WebSocket :: %s.' % e)
|
||||
logger.error(u"PlexPy WebSocket :: %s." % e)
|
||||
reconnects += 1
|
||||
time.sleep(5)
|
||||
|
||||
@@ -79,22 +89,30 @@ def run():
|
||||
if reconnects > 1:
|
||||
time.sleep(5)
|
||||
|
||||
logger.warn(u'PlexPy WebSocket :: Connection has closed, reconnecting...')
|
||||
logger.warn(u"PlexPy WebSocket :: Connection has closed, reconnecting...")
|
||||
try:
|
||||
ws = create_connection(uri)
|
||||
except IOError, e:
|
||||
logger.info(u'PlexPy WebSocket :: %s.' % e)
|
||||
logger.info(u"PlexPy WebSocket :: %s." % e)
|
||||
|
||||
else:
|
||||
ws.shutdown()
|
||||
ws_connected = False
|
||||
break
|
||||
|
||||
if not ws_connected:
|
||||
logger.error(u'PlexPy WebSocket :: Connection unavailable, falling back to polling.')
|
||||
# Check if we recieved a restart notification and close websocket connection cleanly
|
||||
if ws_reconnect:
|
||||
logger.info(u"PlexPy WebSocket :: Reconnecting websocket...")
|
||||
ws.shutdown()
|
||||
ws_connected = False
|
||||
start_thread()
|
||||
|
||||
if not ws_connected and not ws_reconnect:
|
||||
logger.error(u"PlexPy WebSocket :: Connection unavailable, falling back to polling.")
|
||||
plexpy.POLLING_FAILOVER = True
|
||||
plexpy.initialize_scheduler()
|
||||
|
||||
logger.debug(u'PlexPy WebSocket :: Leaving thread.')
|
||||
logger.debug(u"PlexPy WebSocket :: Leaving thread.")
|
||||
|
||||
|
||||
def receive(ws):
|
||||
@@ -122,7 +140,7 @@ def process(opcode, data):
|
||||
try:
|
||||
info = json.loads(data)
|
||||
except Exception as ex:
|
||||
logger.warn(u'PlexPy WebSocket :: Error decoding message from websocket: %s' % ex)
|
||||
logger.warn(u"PlexPy WebSocket :: Error decoding message from websocket: %s" % ex)
|
||||
logger.debug(data)
|
||||
return False
|
||||
|
||||
|
@@ -13,7 +13,8 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from plexpy import logger, notifiers, plextv, pmsconnect, common, log_reader, datafactory, graphs, users, libraries, database
|
||||
from plexpy import logger, notifiers, plextv, pmsconnect, common, log_reader, \
|
||||
datafactory, graphs, users, libraries, database, web_socket
|
||||
from plexpy.helpers import checked, addtoapi, get_ip, create_https_certificates
|
||||
|
||||
from mako.lookup import TemplateLookup
|
||||
@@ -253,6 +254,18 @@ class WebInterface(object):
|
||||
logger.warn(u"Unable to retrieve data for get_recently_added.")
|
||||
return serve_template(templatename="recently_added.html", data=None)
|
||||
|
||||
@cherrypy.expose
|
||||
def delete_temp_sessions(self):
|
||||
|
||||
result = database.delete_sessions()
|
||||
|
||||
if result:
|
||||
cherrypy.response.headers['Content-type'] = 'application/json'
|
||||
return json.dumps({'message': result})
|
||||
else:
|
||||
cherrypy.response.headers['Content-type'] = 'application/json'
|
||||
return json.dumps({'message': 'no data received'})
|
||||
|
||||
|
||||
##### Libraries #####
|
||||
|
||||
@@ -776,6 +789,10 @@ class WebInterface(object):
|
||||
media_type = kwargs.get('media_type', "")
|
||||
if media_type != 'all':
|
||||
custom_where.append(['session_history.media_type', media_type])
|
||||
if 'transcode_decision' in kwargs:
|
||||
transcode_decision = kwargs.get('transcode_decision', "")
|
||||
if transcode_decision:
|
||||
custom_where.append(['session_history_media_info.transcode_decision', transcode_decision])
|
||||
|
||||
data_factory = datafactory.DataFactory()
|
||||
history = data_factory.get_datatables_history(kwargs=kwargs, custom_where=custom_where, grouping=grouping, watched_percent=watched_percent)
|
||||
@@ -989,9 +1006,9 @@ class WebInterface(object):
|
||||
logger.warn(u"Unable to retrieve data for get_stream_type_by_top_10_platforms.")
|
||||
|
||||
@cherrypy.expose
|
||||
def history_table_modal(self, start_date=None, **kwargs):
|
||||
def history_table_modal(self, **kwargs):
|
||||
|
||||
return serve_template(templatename="history_table_modal.html", title="History Data", data=start_date)
|
||||
return serve_template(templatename="history_table_modal.html", title="History Data", data=kwargs)
|
||||
|
||||
|
||||
##### Sync #####
|
||||
@@ -1082,6 +1099,28 @@ 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
|
||||
@addtoapi()
|
||||
def clearNotifyLogs(self, **kwargs):
|
||||
data_factory = datafactory.DataFactory()
|
||||
result = data_factory.delete_notification_log()
|
||||
|
||||
if result:
|
||||
cherrypy.response.headers['Content-type'] = 'application/json'
|
||||
return json.dumps({'message': result})
|
||||
else:
|
||||
cherrypy.response.headers['Content-type'] = 'application/json'
|
||||
return json.dumps({'message': 'no data received'})
|
||||
|
||||
@cherrypy.expose
|
||||
def clearLogs(self):
|
||||
plexpy.LOG_LIST = []
|
||||
@@ -1138,8 +1177,9 @@ class WebInterface(object):
|
||||
"api_key": plexpy.CONFIG.API_KEY,
|
||||
"update_db_interval": plexpy.CONFIG.UPDATE_DB_INTERVAL,
|
||||
"freeze_db": checked(plexpy.CONFIG.FREEZE_DB),
|
||||
"log_dir": plexpy.CONFIG.LOG_DIR,
|
||||
"backup_dir": plexpy.CONFIG.BACKUP_DIR,
|
||||
"cache_dir": plexpy.CONFIG.CACHE_DIR,
|
||||
"log_dir": plexpy.CONFIG.LOG_DIR,
|
||||
"check_github": checked(plexpy.CONFIG.CHECK_GITHUB),
|
||||
"interface_list": interface_list,
|
||||
"cache_sizemb": plexpy.CONFIG.CACHE_SIZEMB,
|
||||
@@ -1169,6 +1209,7 @@ class WebInterface(object):
|
||||
"tv_notify_on_pause": checked(plexpy.CONFIG.TV_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),
|
||||
"monitor_pms_updates": checked(plexpy.CONFIG.MONITOR_PMS_UPDATES),
|
||||
"monitor_remote_access": checked(plexpy.CONFIG.MONITOR_REMOTE_ACCESS),
|
||||
"monitoring_interval": plexpy.CONFIG.MONITORING_INTERVAL,
|
||||
"monitoring_use_websocket": checked(plexpy.CONFIG.MONITORING_USE_WEBSOCKET),
|
||||
@@ -1183,6 +1224,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,
|
||||
@@ -1209,6 +1251,8 @@ class WebInterface(object):
|
||||
"notify_on_extup_body_text": plexpy.CONFIG.NOTIFY_ON_EXTUP_BODY_TEXT,
|
||||
"notify_on_intup_subject_text": plexpy.CONFIG.NOTIFY_ON_INTUP_SUBJECT_TEXT,
|
||||
"notify_on_intup_body_text": plexpy.CONFIG.NOTIFY_ON_INTUP_BODY_TEXT,
|
||||
"notify_on_pmsupdate_subject_text": plexpy.CONFIG.NOTIFY_ON_PMSUPDATE_SUBJECT_TEXT,
|
||||
"notify_on_pmsupdate_body_text": plexpy.CONFIG.NOTIFY_ON_PMSUPDATE_BODY_TEXT,
|
||||
"notify_scripts_args_text": plexpy.CONFIG.NOTIFY_SCRIPTS_ARGS_TEXT,
|
||||
"home_stats_length": plexpy.CONFIG.HOME_STATS_LENGTH,
|
||||
"home_stats_type": checked(plexpy.CONFIG.HOME_STATS_TYPE),
|
||||
@@ -1235,17 +1279,20 @@ 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",
|
||||
"notify_recently_added", "notify_recently_added_grandparent", "monitor_remote_access", "get_file_sizes"
|
||||
"pms_is_remote", "home_stats_type", "group_history_tables", "notify_consecutive", "notify_upload_posters",
|
||||
"notify_recently_added", "notify_recently_added_grandparent",
|
||||
"monitor_pms_updates", "monitor_remote_access", "get_file_sizes"
|
||||
]
|
||||
for checked_config in checked_configs:
|
||||
if checked_config not in kwargs:
|
||||
# checked items should be zero or one. if they were not sent then the item was not checked
|
||||
kwargs[checked_config] = '0'
|
||||
kwargs[checked_config] = 0
|
||||
else:
|
||||
kwargs[checked_config] = 1
|
||||
|
||||
# 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_')]:
|
||||
@@ -1264,13 +1311,14 @@ class WebInterface(object):
|
||||
if kwargs.get('monitoring_interval') != str(plexpy.CONFIG.MONITORING_INTERVAL) or \
|
||||
kwargs.get('refresh_libraries_interval') != str(plexpy.CONFIG.REFRESH_LIBRARIES_INTERVAL) or \
|
||||
kwargs.get('refresh_users_interval') != str(plexpy.CONFIG.REFRESH_USERS_INTERVAL) or \
|
||||
kwargs.get('notify_recently_added') != str(plexpy.CONFIG.NOTIFY_RECENTLY_ADDED) or \
|
||||
kwargs.get('monitor_remote_access') != str(plexpy.CONFIG.MONITOR_REMOTE_ACCESS):
|
||||
kwargs.get('notify_recently_added') != plexpy.CONFIG.NOTIFY_RECENTLY_ADDED or \
|
||||
kwargs.get('monitor_pms_updates') != plexpy.CONFIG.MONITOR_PMS_UPDATES or \
|
||||
kwargs.get('monitor_remote_access') != plexpy.CONFIG.MONITOR_REMOTE_ACCESS:
|
||||
reschedule = True
|
||||
|
||||
# If we change the SSL setting for PMS or PMS remote setting, make sure we grab the new url.
|
||||
if kwargs.get('pms_ssl') != str(plexpy.CONFIG.PMS_SSL) or \
|
||||
kwargs.get('pms_is_remote') != str(plexpy.CONFIG.PMS_IS_REMOTE):
|
||||
if kwargs.get('pms_ssl') != plexpy.CONFIG.PMS_SSL or \
|
||||
kwargs.get('pms_is_remote') != plexpy.CONFIG.PMS_IS_REMOTE:
|
||||
server_changed = True
|
||||
|
||||
# If we change the HTTPS setting, make sure we generate a new certificate.
|
||||
@@ -1317,6 +1365,7 @@ class WebInterface(object):
|
||||
if server_changed:
|
||||
plextv.get_real_pms_url()
|
||||
pmsconnect.get_server_friendly_name()
|
||||
web_socket.reconnect()
|
||||
|
||||
# Reconfigure scheduler if intervals changed
|
||||
if reschedule:
|
||||
@@ -1620,11 +1669,16 @@ class WebInterface(object):
|
||||
if source == 'history':
|
||||
data_factory = datafactory.DataFactory()
|
||||
metadata = data_factory.get_metadata_details(rating_key=rating_key)
|
||||
poster_url = data_factory.get_poster_url(metadata=metadata)
|
||||
metadata['poster_url'] = poster_url
|
||||
else:
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
result = pms_connect.get_metadata_details(rating_key=rating_key, get_media_info=True)
|
||||
if result:
|
||||
metadata = result['metadata']
|
||||
data_factory = datafactory.DataFactory()
|
||||
poster_url = data_factory.get_poster_url(metadata=metadata)
|
||||
metadata['poster_url'] = poster_url
|
||||
|
||||
if metadata:
|
||||
return serve_template(templatename="info.html", data=metadata, title="Info", config=config, source=source)
|
||||
@@ -1671,6 +1725,22 @@ class WebInterface(object):
|
||||
|
||||
return None
|
||||
|
||||
@cherrypy.expose
|
||||
def delete_poster_url(self, poster_url=''):
|
||||
|
||||
if poster_url:
|
||||
data_factory = datafactory.DataFactory()
|
||||
result = data_factory.delete_poster_url(poster_url=poster_url)
|
||||
else:
|
||||
result = None
|
||||
|
||||
if result:
|
||||
cherrypy.response.headers['Content-type'] = 'application/json'
|
||||
return json.dumps({'message': result})
|
||||
else:
|
||||
cherrypy.response.headers['Content-type'] = 'application/json'
|
||||
return json.dumps({'message': 'no data received'})
|
||||
|
||||
|
||||
##### Search #####
|
||||
|
||||
@@ -2173,3 +2243,9 @@ class WebInterface(object):
|
||||
a = Api()
|
||||
a.checkParams(*args, **kwargs)
|
||||
return a.fetchData()
|
||||
|
||||
@cherrypy.expose
|
||||
def check_pms_updater(self):
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
result = pms_connect.get_update_staus()
|
||||
return json.dumps(result)
|
Reference in New Issue
Block a user