Compare commits
20 Commits
v2.0.17-be
...
v2.0.18-be
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3a5d5918de | ||
![]() |
3380e39de2 | ||
![]() |
7d31079897 | ||
![]() |
c287b6df77 | ||
![]() |
dab1f8ba20 | ||
![]() |
a26de7f6c2 | ||
![]() |
ab32b2cbc2 | ||
![]() |
503c249fc3 | ||
![]() |
2a03ce757e | ||
![]() |
373a15524f | ||
![]() |
13036183d3 | ||
![]() |
170591c79e | ||
![]() |
a15d225a5f | ||
![]() |
a0106874e2 | ||
![]() |
ab157d1c0e | ||
![]() |
0b95c9fe2e | ||
![]() |
d693514ca9 | ||
![]() |
56987b3aaa | ||
![]() |
3ca1bd5d78 | ||
![]() |
770f12b632 |
18
CHANGELOG.md
18
CHANGELOG.md
@@ -1,5 +1,23 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v2.0.18-beta (2018-02-12)
|
||||||
|
|
||||||
|
* Notifications:
|
||||||
|
* Fix: Default text for Tautulli update notifications using the wrong parameter.
|
||||||
|
* Fix: Playback pause and resume notifications only triggering once.
|
||||||
|
* Change: Negative operators for custom conditions now use "and" instead of "or".
|
||||||
|
* UI:
|
||||||
|
* New: Added button to delete the 3rd party lookup info from the info pages.
|
||||||
|
* Fix: Missing host info in the login logs when logging in using Firefox.
|
||||||
|
* Change: Cleaned up settings. Advanced settings are now hidden behind a toggle.
|
||||||
|
* API:
|
||||||
|
* New: Updated API documentation for v2.
|
||||||
|
* Other:
|
||||||
|
* Fix: DeprecationWarning when using HTTPS with self-signed certificates.
|
||||||
|
* Change: Deleting the Imgur poster URL also deletes the poster from Imgur (only available for new uploads).
|
||||||
|
* Change: GitHub repository moved to Tautulli/Tautulli. Old GitHub URLs will still work.
|
||||||
|
|
||||||
|
|
||||||
## v2.0.17-beta (2018-02-03)
|
## v2.0.17-beta (2018-02-03)
|
||||||
|
|
||||||
* Notifications:
|
* Notifications:
|
||||||
|
@@ -1,48 +1,7 @@
|
|||||||
# Contributing to PlexPy
|
# Contributing to Tautulli
|
||||||
|
|
||||||
## 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. 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.
|
|
||||||
|
|
||||||
##### 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/JonnyWong16/plexpy/wiki) for
|
|
||||||
[ [Installation] ](https://github.com/JonnyWong16/plexpy/wiki/Installation) and
|
|
||||||
[ [FAQs] ](https://github.com/JonnyWong16/plexpy/wiki/Frequently-Asked-Questions-(FAQ)).
|
|
||||||
- For basic questions try asking on [Gitter](https://gitter.im/plexpy/general) 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/JonnyWong16/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
|
|
||||||
|
|
||||||
Feature requests are handled on [FeatHub](http://feathub.com/JonnyWong16/plexpy).
|
|
||||||
|
|
||||||
1. Search the existing requests to see if your suggestion has already been submitted.
|
|
||||||
2. If a similar request exists, give it a thumbs up (+1), or add additional comments to the request.
|
|
||||||
3. If no similar requests exist, you can create a new one. Make sure to provide a clear title to easily identify the feature request.
|
|
||||||
|
|
||||||
## Pull Requests
|
## Pull Requests
|
||||||
If you think you can contribute code to the PlexPy repository, do not hesitate to submit a pull request.
|
If you think you can contribute code to the Tautulli repository, do not hesitate to submit a pull request.
|
||||||
|
|
||||||
### Branches
|
### Branches
|
||||||
All pull requests should be based on the `dev` branch, to minimize cross merges. When you want to develop a new feature, clone the repository with `git clone origin/dev -b FEATURE_NAME`. Use meaningful commit messages.
|
All pull requests should be based on the `dev` branch, to minimize cross merges. When you want to develop a new feature, clone the repository with `git clone origin/dev -b FEATURE_NAME`. Use meaningful commit messages.
|
||||||
@@ -50,12 +9,12 @@ All pull requests should be based on the `dev` branch, to minimize cross merges.
|
|||||||
### Python Code
|
### Python Code
|
||||||
|
|
||||||
#### Compatibility
|
#### Compatibility
|
||||||
The code should work with Python 2.6 and 2.7. Note that PlexPy runs on different platforms, including Network Attached Storage devices such as Synology.
|
The code should work with Python 2.7. Note that Tautulli runs on different platforms, including Network Attached Storage devices such as Synology.
|
||||||
|
|
||||||
Re-use existing code. Do not hesitate to add logging in your code. You can the logger module `plexpy.logger.*` for this. Web requests are invoked via `plexpy.request.*` and derived ones. Use these methods to automatically add proper and meaningful error handling.
|
Re-use existing code. Do not hesitate to add logging in your code. You can the logger module `plexpy.logger.*` for this. Web requests are invoked via `plexpy.request.*` and derived ones. Use these methods to automatically add proper and meaningful error handling.
|
||||||
|
|
||||||
#### Code conventions
|
#### Code conventions
|
||||||
Although PlexPy did not adapt a code convention in the past, we try to follow the [PEP8](http://legacy.python.org/dev/peps/pep-0008/) conventions for future code. A short summary to remind you (copied from http://wiki.ros.org/PyStyleGuide):
|
Although Tautulli did not adapt a code convention in the past, we try to follow the [PEP8](http://legacy.python.org/dev/peps/pep-0008/) conventions for future code. A short summary to remind you (copied from http://wiki.ros.org/PyStyleGuide):
|
||||||
|
|
||||||
* 4 space indentation
|
* 4 space indentation
|
||||||
* 80 characters per line
|
* 80 characters per line
|
||||||
@@ -71,12 +30,12 @@ Although PlexPy did not adapt a code convention in the past, we try to follow th
|
|||||||
Document your code. Use docstrings See [PEP-257](https://www.python.org/dev/peps/pep-0257/) for more information.
|
Document your code. Use docstrings See [PEP-257](https://www.python.org/dev/peps/pep-0257/) for more information.
|
||||||
|
|
||||||
#### Continuous Integration
|
#### Continuous Integration
|
||||||
PlexPy has a configuration file for [travis-ci](https://travis-ci.org/). You can add your forked repo to Travis to have it check your code against PEP8, PyLint, and PyFlakes for you. Your pull request will show a green check mark or a red cross on each tested commit, depending on if linting passes.
|
Tautulli has a configuration file for [travis-ci](https://travis-ci.org/). You can add your forked repo to Travis to have it check your code against PEP8, PyLint, and PyFlakes for you. Your pull request will show a green check mark or a red cross on each tested commit, depending on if linting passes.
|
||||||
|
|
||||||
### HTML/Template code
|
### HTML/Template code
|
||||||
|
|
||||||
#### Compatibility
|
#### Compatibility
|
||||||
HTML5 compatible browsers are targetted. There is no specific mobile version of PlexPy yet.
|
HTML5 compatible browsers are targetted. There is no specific mobile version of Tautulli yet.
|
||||||
|
|
||||||
#### Conventions
|
#### Conventions
|
||||||
* 4 space indentation
|
* 4 space indentation
|
||||||
|
@@ -8,7 +8,7 @@ Reporting Issues:
|
|||||||
Please use [Gist](http://gist.github.com) or [Pastebin](http://pastebin.com/).
|
Please use [Gist](http://gist.github.com) or [Pastebin](http://pastebin.com/).
|
||||||
|
|
||||||
Feature Requests:
|
Feature Requests:
|
||||||
* Feature requests are handled on FeatHub: http://feathub.com/JonnyWong16/plexpy
|
* Feature requests are handled on FeatHub: http://feathub.com/Tautulli/Tautulli
|
||||||
* Do not post them on the GitHub issues tracker.
|
* Do not post them on the GitHub issues tracker.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
53
README.md
53
README.md
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[](https://discord.gg/tQcWEUp)
|
[](https://discord.gg/tQcWEUp)
|
||||||
[](https://www.reddit.com/r/Tautulli/)
|
[](https://www.reddit.com/r/Tautulli/)
|
||||||
[](https://forums.plex.tv/discussion/169591/plexpy-another-plex-monitoring-program)
|
[](https://forums.plex.tv/discussion/307821/tautulli-monitor-your-plex-media-server)
|
||||||
|
|
||||||
A python based web application for monitoring, analytics and notifications for [Plex Media Server](https://plex.tv).
|
A python based web application for monitoring, analytics and notifications for [Plex Media Server](https://plex.tv).
|
||||||
|
|
||||||
@@ -27,56 +27,19 @@ This project is based on code from [Headphones](https://github.com/rembo10/headp
|
|||||||
|
|
||||||
## Preview
|
## Preview
|
||||||
|
|
||||||
* [Full preview gallery on our website](http://tautulli.com)
|
* [Full preview gallery available on our website](http://tautulli.com)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Installation and Support
|
## Installation and Support
|
||||||
|
|
||||||
* [Installation Guides](https://github.com/JonnyWong16/plexpy/wiki/Installation) shows you how to install Tautulli.
|
* Read the [Installation Guides](https://github.com/Tautulli/Tautulli-Wiki/wiki/Installation) for instructions to install Tautulli.
|
||||||
* [FAQs](https://github.com/JonnyWong16/plexpy/wiki/Frequently-Asked-Questions-(FAQ)) in the wiki can help you with common problems.
|
* The [Frequently Asked Questions](https://github.com/Tautulli/Tautulli-Wiki/wiki/Frequently-Asked-Questions) in the wiki can help you with common problems.
|
||||||
|
* Support is available on [Discord](https://discord.gg/tQcWEUp), [Reddit](https://www.reddit.com/r/Tautulli), or the [Plex Forums](https://forums.plex.tv/discussion/307821/tautulli-monitor-your-plex-media-server).
|
||||||
|
|
||||||
**Support** the project by implementing new features, solving support tickets and provide bug fixes.
|
## Issues & Feature Requests
|
||||||
|
|
||||||
## Issues
|
* Please see the [Issues Repository](https://github.com/Tautulli/Tautulli-Issues).
|
||||||
|
|
||||||
##### 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/JonnyWong16/plexpy/wiki) for
|
|
||||||
[ [Installation] ](https://github.com/JonnyWong16/plexpy/wiki/Installation) and
|
|
||||||
[ [FAQs] ](https://github.com/JonnyWong16/plexpy/wiki/Frequently-Asked-Questions-(FAQ)).
|
|
||||||
- For basic questions try asking on [Discord](https://discord.gg/tQcWEUp), [Reddit](https://www.reddit.com/r/Tautulli), 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/JonnyWong16/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
|
|
||||||
|
|
||||||
Feature requests are handled on [FeatHub](http://feathub.com/JonnyWong16/plexpy).
|
|
||||||
|
|
||||||
1. Search the existing requests to see if your suggestion has already been submitted.
|
|
||||||
2. If a similar request exists, give it a thumbs up (+1), or add additional comments to the request.
|
|
||||||
3. If no similar requests exist, you can create a new one. Make sure to provide a clear title to easily identify the feature request.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
@@ -49,7 +49,7 @@
|
|||||||
</div>
|
</div>
|
||||||
% elif plexpy.CONFIG.CHECK_GITHUB and plexpy.CURRENT_VERSION != plexpy.LATEST_VERSION and plexpy.COMMITS_BEHIND > 0 and plexpy.INSTALL_TYPE != 'win':
|
% 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;">
|
<div id="updatebar" style="display: none;">
|
||||||
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">
|
A <a href="${anon_url('https://github.com/%s/%s/compare/%s...%s' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO, plexpy.CURRENT_VERSION, plexpy.LATEST_VERSION))}" target="_blank">
|
||||||
newer version</a> is available.<br />
|
newer version</a> is available.<br />
|
||||||
You're ${plexpy.COMMITS_BEHIND} commits behind.<br />
|
You're ${plexpy.COMMITS_BEHIND} commits behind.<br />
|
||||||
<a href="update">Update</a> or <a href="#" id="updateDismiss">Close</a>
|
<a href="update">Update</a> or <a href="#" id="updateDismiss">Close</a>
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
<li><a href="settings"><i class="fa fa-fw fa-cogs"></i> Settings</a></li>
|
<li><a href="settings"><i class="fa fa-fw fa-cogs"></i> Settings</a></li>
|
||||||
<li role="separator" class="divider"></li>
|
<li role="separator" class="divider"></li>
|
||||||
<li><a href="logs"><i class="fa fa-fw fa-list-alt"></i> View Logs</a></li>
|
<li><a href="logs"><i class="fa fa-fw fa-list-alt"></i> View Logs</a></li>
|
||||||
<li><a href="${anon_url('https://github.com/%s/plexpy/wiki/Frequently-Asked-Questions-(FAQ)' % plexpy.CONFIG.GIT_USER)}" target="_blank"><i class="fa fa-fw fa-question-circle"></i> FAQ</a></li>
|
<li><a href="${anon_url('https://github.com/%s/%s-Wiki/wiki/Frequently-Asked-Questions' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}" target="_blank"><i class="fa fa-fw fa-question-circle"></i> FAQ</a></li>
|
||||||
<li><a href="settings?support=true"><i class="fa fa-fw fa-comment"></i> Support</a></li>
|
<li><a href="settings?support=true"><i class="fa fa-fw fa-comment"></i> Support</a></li>
|
||||||
<li role="separator" class="divider"></li>
|
<li role="separator" class="divider"></li>
|
||||||
<li><a href="#" data-target="#donate-modal" data-toggle="modal"><i class="fa fa-fw fa-heart"></i> Donate</a></li>
|
<li><a href="#" data-target="#donate-modal" data-toggle="modal"><i class="fa fa-fw fa-heart"></i> Donate</a></li>
|
||||||
@@ -239,7 +239,7 @@ ${next.modalIncludes()}
|
|||||||
<p>
|
<p>
|
||||||
Click the button below to continue to Flattr.
|
Click the button below to continue to Flattr.
|
||||||
</p>
|
</p>
|
||||||
<a href="${anon_url('https://flattr.com/submit/auto?user_id=JonnyWong16&url=https://github.com/JonnyWong16/plexpy&title=Tautulli&language=en_GB&tags=github&category=software')}" target="_blank">
|
<a href="${anon_url('https://flattr.com/submit/auto?user_id=JonnyWong16&url=https://github.com/%s/%s&title=Tautulli&language=en_GB&tags=github&category=software' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}" target="_blank">
|
||||||
<img src="images/flattr-badge-large.png" alt="Flattr">
|
<img src="images/flattr-badge-large.png" alt="Flattr">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -22,11 +22,11 @@ DOCUMENTATION :: END
|
|||||||
% if plexpy.CURRENT_VERSION:
|
% if plexpy.CURRENT_VERSION:
|
||||||
<tr>
|
<tr>
|
||||||
<td>Git Branch:</td>
|
<td>Git Branch:</td>
|
||||||
<td><a class="no-highlight" href="${anon_url('https://github.com/%s/plexpy/tree/%s' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_BRANCH))}">${plexpy.CONFIG.GIT_BRANCH}</a></td>
|
<td><a class="no-highlight" href="${anon_url('https://github.com/%s/%s/tree/%s' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO, plexpy.CONFIG.GIT_BRANCH))}">${plexpy.CONFIG.GIT_BRANCH}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Git Commit Hash:</td>
|
<td>Git Commit Hash:</td>
|
||||||
<td><a class="no-highlight" href="${anon_url('https://github.com/%s/plexpy/commit/%s' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_BRANCH))}">${plexpy.CURRENT_VERSION}</a></td>
|
<td><a class="no-highlight" href="${anon_url('https://github.com/%s/%s/commit/%s' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO, plexpy.CONFIG.GIT_BRANCH))}">${plexpy.CURRENT_VERSION}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
% endif
|
% endif
|
||||||
<tr>
|
<tr>
|
||||||
@@ -75,10 +75,10 @@ DOCUMENTATION :: END
|
|||||||
<td class="top-line">Resources:</td>
|
<td class="top-line">Resources:</td>
|
||||||
<td class="top-line">
|
<td class="top-line">
|
||||||
<a class="no-highlight" href="${anon_url('http://tautulli.com')}" target="_blank">Tautulli Website</a> |
|
<a class="no-highlight" href="${anon_url('http://tautulli.com')}" target="_blank">Tautulli Website</a> |
|
||||||
<a class="no-highlight" href="${anon_url('https://github.com/%s/plexpy' % plexpy.CONFIG.GIT_USER)}" target="_blank">GitHub Source</a> |
|
<a class="no-highlight" href="${anon_url('https://github.com/%s/%s' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}" target="_blank">GitHub Source</a> |
|
||||||
<a class="no-highlight guidelines-modal-link" href="${anon_url('https://github.com/%s/plexpy/issues' % plexpy.CONFIG.GIT_USER)}" data-id="issue">GitHub Issues</a> |
|
<a class="no-highlight guidelines-modal-link" href="${anon_url('https://github.com/%s/%s-Issues' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}" data-id="issue">GitHub Issues</a> |
|
||||||
<a class="no-highlight" href="${anon_url('https://github.com/%s/plexpy/wiki' % plexpy.CONFIG.GIT_USER)}" target="_blank">GitHub Wiki & FAQ</a> |
|
<a class="no-highlight" href="${anon_url('https://github.com/%s/%s-Wiki' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}" target="_blank">GitHub Wiki</a> |
|
||||||
<a class="no-highlight guidelines-modal-link" href="${anon_url('http://feathub.com/%s/plexpy' % plexpy.CONFIG.GIT_USER)}" data-id="feature request">FeatHub Feature Requests</a> |
|
<a class="no-highlight guidelines-modal-link" href="${anon_url('http://feathub.com/%s/%s' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}" data-id="feature request">FeatHub Feature Requests</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -86,7 +86,7 @@ DOCUMENTATION :: END
|
|||||||
<td>
|
<td>
|
||||||
<a class="no-highlight support-modal-link" href="${anon_url('https://discord.gg/tQcWEUp')}" target="_blank">Tautulli Discord Server</a> |
|
<a class="no-highlight support-modal-link" href="${anon_url('https://discord.gg/tQcWEUp')}" target="_blank">Tautulli Discord Server</a> |
|
||||||
<a class="no-highlight support-modal-link" href="${anon_url('https://www.reddit.com/r/Tautulli')}" target="_blank">Tautulli Subreddit</a> |
|
<a class="no-highlight support-modal-link" href="${anon_url('https://www.reddit.com/r/Tautulli')}" target="_blank">Tautulli Subreddit</a> |
|
||||||
<a class="no-highlight support-modal-link" href="${anon_url('https://forums.plex.tv/discussion/169591/plexpy-another-plex-monitoring-program')}" target="_blank">Plex Forums</a>
|
<a class="no-highlight support-modal-link" href="${anon_url('https://forums.plex.tv/discussion/307821/tautulli-monitor-your-plex-media-server')}" target="_blank">Plex Forums</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@@ -108,6 +108,9 @@ select.form-control {
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
}
|
}
|
||||||
|
.react-selectize.root-node .react-selectize-control .react-selectize-search-field-and-selected-values.negative-operator .value-wrapper:not(:first-child):before {
|
||||||
|
content: "and" !important;
|
||||||
|
}
|
||||||
.react-selectize.root-node .react-selectize-control .react-selectize-search-field-and-selected-values .resizable-input {
|
.react-selectize.root-node .react-selectize-control .react-selectize-search-field-and-selected-values .resizable-input {
|
||||||
padding-top: 3px !important;
|
padding-top: 3px !important;
|
||||||
padding-bottom: 3px !important;
|
padding-bottom: 3px !important;
|
||||||
@@ -2134,6 +2137,20 @@ a:hover .item-children-poster {
|
|||||||
top: 5px;
|
top: 5px;
|
||||||
left: 12px;
|
left: 12px;
|
||||||
}
|
}
|
||||||
|
#menu_link_show_advanced_settings.active {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #cc7b19;
|
||||||
|
}
|
||||||
|
.advanced-setting {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
div.advanced-setting {
|
||||||
|
border-left: 1px solid #cc7b19;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
li.advanced-setting {
|
||||||
|
border-left: 1px solid #cc7b19;
|
||||||
|
}
|
||||||
.user-info-wrapper {
|
.user-info-wrapper {
|
||||||
}
|
}
|
||||||
.user-info-poster-face {
|
.user-info-poster-face {
|
||||||
|
@@ -388,6 +388,15 @@ DOCUMENTATION :: END
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
|
% if data.get('tvmaze_id') or data.get('themoviedb_id'):
|
||||||
|
<div class="btn-group">
|
||||||
|
<button class="btn btn-danger btn-edit" data-toggle="modal" aria-pressed="false" autocomplete="off" id="delete-lookup-info"
|
||||||
|
data-id="${data['grandparent_rating_key'] if data['media_type'] in ('episode', 'track') else data['parent_rating_key'] if data['media_type'] in ('season', 'album') else data['rating_key']}"
|
||||||
|
data-title="${data['grandparent_title'] if data['media_type'] in ('episode', 'track') else data['parent_title'] if data['media_type'] in ('season', 'album') else data['title']}">
|
||||||
|
<i class="fa fa-search"></i> Delete Lookup Info
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
% if data.get('poster_url'):
|
% if data.get('poster_url'):
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
% if data['media_type'] == 'artist' or data['media_type'] == 'album' or data['media_type'] == 'track':
|
% if data['media_type'] == 'artist' or data['media_type'] == 'album' or data['media_type'] == 'track':
|
||||||
@@ -396,8 +405,9 @@ DOCUMENTATION :: END
|
|||||||
<span class="imgur-poster-tooltip" data-toggle="popover" data-img="${data['poster_url']}" data-height="120" data-width="80" style="display: inline-flex;">
|
<span class="imgur-poster-tooltip" data-toggle="popover" data-img="${data['poster_url']}" data-height="120" data-width="80" style="display: inline-flex;">
|
||||||
% endif
|
% endif
|
||||||
<button class="btn btn-danger btn-edit" data-toggle="modal" aria-pressed="false" autocomplete="off" id="delete-imgur-poster"
|
<button class="btn btn-danger btn-edit" data-toggle="modal" aria-pressed="false" autocomplete="off" id="delete-imgur-poster"
|
||||||
data-id="${data['parent_rating_key'] if data['media_type'] in ('episode', 'track') else data['rating_key']}">
|
data-id="${data['parent_rating_key'] if data['media_type'] in ('episode', 'track') else data['rating_key']}"
|
||||||
<i class="fa fa-picture-o"></i> Reset Imgur Poster
|
data-title="${data["poster_title"]}">
|
||||||
|
<i class="fa fa-picture-o"></i> Delete Imgur Poster
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -706,13 +716,28 @@ DOCUMENTATION :: END
|
|||||||
});
|
});
|
||||||
|
|
||||||
$('#delete-imgur-poster').on('click', function () {
|
$('#delete-imgur-poster').on('click', function () {
|
||||||
var msg = 'Are you sure you want to reset the Imgur poster for <strong>${data["poster_title"]}</strong>?';
|
var msg = 'Are you sure you want to delete the Imgur poster for <strong>' + $(this).data('title') + '</strong>?<br><br>' +
|
||||||
var url = 'delete_poster_url';
|
'All previous links to this image will no longer work.';
|
||||||
var data = { rating_key: $(this).data('id') }
|
var url = 'delete_imgur_poster';
|
||||||
|
var data = { rating_key: $(this).data('id') };
|
||||||
var callback = function () {
|
var callback = function () {
|
||||||
$('.imgur-poster-tooltip').popover('destroy');
|
$('.imgur-poster-tooltip').popover('destroy');
|
||||||
$('#delete-imgur-poster').closest('span').remove();
|
$('#delete-imgur-poster').closest('.btn-group').remove();
|
||||||
}
|
};
|
||||||
|
confirmAjaxCall(url, msg, data, false, callback);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
% endif
|
||||||
|
% if data.get('tvmaze_id') or data.get('themoviedb_id'):
|
||||||
|
<script>
|
||||||
|
$('#delete-lookup-info').on('click', function () {
|
||||||
|
var msg = 'Are you sure you want to delete the 3rd party API lookup for <strong>' + $(this).data('title') + '</strong>?<br><br>' +
|
||||||
|
'The info will be looked up again the next time a notification is sent.';
|
||||||
|
var url = 'delete_lookup_info';
|
||||||
|
var data = { rating_key: $(this).data('id'), title: $(this).data('title') };
|
||||||
|
var callback = function () {
|
||||||
|
$('#delete-lookup-info').closest('.btn-group').remove();
|
||||||
|
};
|
||||||
confirmAjaxCall(url, msg, data, false, callback);
|
confirmAjaxCall(url, msg, data, false, callback);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@@ -163,7 +163,7 @@
|
|||||||
<div role="tabpanel" class="tab-pane" id="tabs-notify_conditions">
|
<div role="tabpanel" class="tab-pane" id="tabs-notify_conditions">
|
||||||
<label>Notification Conditions</label>
|
<label>Notification Conditions</label>
|
||||||
<p class="help-block">
|
<p class="help-block">
|
||||||
Add custom conditions to only <strong>allow certain notifications</strong>. By default, all notifications will be sent if there are no conditions.
|
Add custom conditions to only <em>allow certain notifications</em>. By default, all notifications will be sent if there are no conditions.
|
||||||
<a href="#notify-text-sub-modal" data-toggle="modal">Click here</a> for a description of all the parameters.
|
<a href="#notify-text-sub-modal" data-toggle="modal">Click here</a> for a description of all the parameters.
|
||||||
</p>
|
</p>
|
||||||
<div id="condition-widget"></div>
|
<div id="condition-widget"></div>
|
||||||
@@ -342,7 +342,22 @@
|
|||||||
$('#custom_conditions').val(JSON.stringify(newConditions));
|
$('#custom_conditions').val(JSON.stringify(newConditions));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function setNegativeOperator(select) {
|
||||||
|
if (select.val() === 'does not contain' || select.val() === 'is not') {
|
||||||
|
select.closest('.form-group').find('.react-selectize-search-field-and-selected-values').addClass('negative-operator');
|
||||||
|
} else {
|
||||||
|
select.closest('.form-group').find('.react-selectize-search-field-and-selected-values').removeClass('negative-operator');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#condition-widget select[name=operator]').each(function () {
|
||||||
|
setNegativeOperator($(this));
|
||||||
|
});
|
||||||
|
$('#condition-widget').on('change', 'select[name=operator]', function () {
|
||||||
|
setNegativeOperator($(this));
|
||||||
|
});
|
||||||
|
|
||||||
function reloadModal() {
|
function reloadModal() {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: 'get_notifier_config_modal',
|
url: 'get_notifier_config_modal',
|
||||||
|
@@ -26,11 +26,16 @@
|
|||||||
<span><i class="fa fa-cogs"></i> Settings</span>
|
<span><i class="fa fa-cogs"></i> Settings</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-bar">
|
<div class="button-bar">
|
||||||
|
% if config['show_advanced_settings'] == 1:
|
||||||
|
<button id="menu_link_show_advanced_settings" class="btn btn-dark active"><i class="fa fa-wrench"></i> Hide Advanced</button>
|
||||||
|
% else:
|
||||||
|
<button id="menu_link_show_advanced_settings" class="btn btn-dark"><i class="fa fa-wrench"></i> Show Advanced</button>
|
||||||
|
% endif
|
||||||
% if config['check_github']:
|
% if config['check_github']:
|
||||||
<button id="menu_link_update_check" class="btn btn-dark"><i class="fa fa-arrow-circle-up"></i> Check for Updates</button>
|
<button id="menu_link_update_check" class="btn btn-dark"><i class="fa fa-arrow-circle-up"></i> Check for Updates</button>
|
||||||
% endif
|
% endif
|
||||||
<button id="menu_link_restart" class="btn btn-dark"><i class="fa fa-refresh"></i> Restart</button>
|
<button id="menu_link_restart" class="btn btn-dark"><i class="fa fa-refresh"></i> Restart</button>
|
||||||
<button id="menu_link_shutdown" class="btn btn-dark"><i class="fa fa-power-off"></i> Shut Down</button>
|
<button id="menu_link_shutdown" class="btn btn-dark"><i class="fa fa-power-off"></i> Shutdown</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -43,19 +48,16 @@
|
|||||||
<li role="presentation"><a href="#tabs-general" aria-controls="tabs-general" role="tab" data-toggle="tab">General</a></li>
|
<li role="presentation"><a href="#tabs-general" aria-controls="tabs-general" role="tab" data-toggle="tab">General</a></li>
|
||||||
<li role="presentation"><a href="#tabs-homepage" aria-controls="tabs-homepage" role="tab" data-toggle="tab">Homepage</a></li>
|
<li role="presentation"><a href="#tabs-homepage" aria-controls="tabs-homepage" role="tab" data-toggle="tab">Homepage</a></li>
|
||||||
<li role="presentation"><a href="#tabs-web_interface" aria-controls="tabs-web_interface" role="tab" data-toggle="tab">Web Interface</a></li>
|
<li role="presentation"><a href="#tabs-web_interface" aria-controls="tabs-web_interface" role="tab" data-toggle="tab">Web Interface</a></li>
|
||||||
<li role="presentation"><a href="#tabs-access_control" aria-controls="tabs-access_control" role="tab" data-toggle="tab">Access Control</a></li>
|
|
||||||
<li role="presentation"><a href="#tabs-plex_media_server" aria-controls="tabs-plex_media_server" role="tab" data-toggle="tab">Plex Media Server</a></li>
|
<li role="presentation"><a href="#tabs-plex_media_server" aria-controls="tabs-plex_media_server" role="tab" data-toggle="tab">Plex Media Server</a></li>
|
||||||
<li role="presentation"><a href="#tabs-plextv_account" aria-controls="tabs-plextv_account" role="tab" data-toggle="tab">Plex.tv Account</a></li>
|
|
||||||
<li role="presentation"><a href="#tabs-activity_monitoring" aria-controls="tabs-activity_monitoring" role="tab" data-toggle="tab">Activity Monitoring</a></li>
|
|
||||||
<li role="presentation"><a href="#tabs-notifications" aria-controls="tabs-notifications" role="tab" data-toggle="tab">Notifications</a></li>
|
<li role="presentation"><a href="#tabs-notifications" aria-controls="tabs-notifications" role="tab" data-toggle="tab">Notifications</a></li>
|
||||||
<li role="presentation"><a href="#tabs-notification_agents" aria-controls="tabs-notification_agents" role="tab" data-toggle="tab">Notification Agents</a></li>
|
<li role="presentation"><a href="#tabs-notification_agents" aria-controls="tabs-notification_agents" role="tab" data-toggle="tab">Notification Agents</a></li>
|
||||||
<li role="presentation"><a href="#tabs-extra_settings" aria-controls="tabs-extra_settings" role="tab" data-toggle="tab">Extra Settings</a></li>
|
|
||||||
<li role="presentation"><a href="#tabs-import_backups" aria-controls="tabs-import_backups" role="tab" data-toggle="tab">Import & Backups</a></li>
|
<li role="presentation"><a href="#tabs-import_backups" aria-controls="tabs-import_backups" role="tab" data-toggle="tab">Import & Backups</a></li>
|
||||||
<li role="presentation"><a href="#tabs-android_app" aria-controls="tabs-android_app" role="tab" data-toggle="tab">Tautulli Remote Android App <sup><small>beta</small></sup></a></li>
|
<li role="presentation"><a href="#tabs-android_app" aria-controls="tabs-android_app" role="tab" data-toggle="tab">Tautulli Remote Android App <sup><small>beta</small></sup></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
<form action="configUpdate" method="post" class="form" id="configUpdate" data-parsley-validate>
|
<form action="configUpdate" method="post" class="form" id="configUpdate" data-parsley-validate>
|
||||||
|
<input type="hidden" id="show_advanced_settings" name="show_advanced_settings" value="${config['show_advanced_settings']}" required>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div role="tabpanel" class="tab-pane active" id="tabs-help_info">
|
<div role="tabpanel" class="tab-pane active" id="tabs-help_info">
|
||||||
% if common.VERSION_NUMBER:
|
% if common.VERSION_NUMBER:
|
||||||
@@ -78,6 +80,7 @@
|
|||||||
<br>
|
<br>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tabs-general">
|
<div role="tabpanel" class="tab-pane" id="tabs-general">
|
||||||
|
|
||||||
<div class="padded-header">
|
<div class="padded-header">
|
||||||
@@ -102,24 +105,54 @@
|
|||||||
</div>
|
</div>
|
||||||
<p class="help-block">Set your preferred time format. <a href="javascript:void(0)" data-target="#dateTimeOptionsModal" data-toggle="modal">Click here</a> to see the parameter list.</p>
|
<p class="help-block">Set your preferred time format. <a href="javascript:void(0)" data-target="#dateTimeOptionsModal" data-toggle="modal">Click here</a> to see the parameter list.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="checkbox">
|
<div class="checkbox advanced-setting">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" id="week_start_monday" name="week_start_monday" value="1" ${config['week_start_monday']}> Week Starting on Monday
|
<input type="checkbox" id="week_start_monday" name="week_start_monday" value="1" ${config['week_start_monday']}> Week Starting on Monday
|
||||||
</label>
|
</label>
|
||||||
<p class="help-block">Change the "<em>Play by day of week</em>" graph to start on Monday. Default is start on Sunday.</p>
|
<p class="help-block">Change the "<em>Play by day of week</em>" graph to start on Monday. Default is start on Sunday.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="checkbox">
|
<div class="checkbox advanced-setting">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" id="group_history_tables" name="group_history_tables" value="1" ${config['group_history_tables']}> Group Table and Watch Statistics History
|
<input type="checkbox" id="group_history_tables" name="group_history_tables" value="1" ${config['group_history_tables']}> Group Table and Watch Statistics History
|
||||||
</label>
|
</label>
|
||||||
<p class="help-block">Group successive play history by the same user as a single entry in the tables and watch statistics.</p>
|
<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>
|
||||||
<div class="checkbox">
|
<div class="checkbox advanced-setting">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" id="history_table_activity" name="history_table_activity" value="1" ${config['history_table_activity']}> Current Activity in History Tables
|
<input type="checkbox" id="history_table_activity" name="history_table_activity" value="1" ${config['history_table_activity']}> Current Activity in History Tables
|
||||||
</label>
|
</label>
|
||||||
<p class="help-block">Include current activity in the history tables. Statistics will not be counted until the stream has ended.</p>
|
<p class="help-block">Include current activity in the history tables. Statistics will not be counted until the stream has ended.</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="checkbox advanced-setting">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="get_file_sizes" name="get_file_sizes" value="1" ${config['get_file_sizes']}> Calculate Total File Sizes
|
||||||
|
</label>
|
||||||
|
<p class="help-block">Enable if you want Tautulli to calculate the total file size for TV Shows/Seasons and Artists/Albums on the media info tables.</p>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox advanced-setting">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="log_blacklist" name="log_blacklist" value="1" ${config['log_blacklist']}> Mask Sensitive Information in Logs
|
||||||
|
</label>
|
||||||
|
<p class="help-block">
|
||||||
|
Enable to mask passwords, access tokens, and public IP addresses with asterisks (*) in the logs.<br />
|
||||||
|
Note: Only logs from the time this setting is enabled will be masked. Do not post your logs publically without masking sensitive information!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="padded-header">
|
||||||
|
<h3>History Logging</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group advanced-setting">
|
||||||
|
<label for="logging_ignore_interval">Ignore Interval</label>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-2">
|
||||||
|
<input type="text" class="form-control" data-parsley-type="integer" id="logging_ignore_interval" name="logging_ignore_interval" value="${config['logging_ignore_interval']}" size="5" data-parsley-min="0" data-parsley-trigger="change" data-parsley-errors-container="#logging_ignore_interval_error" required>
|
||||||
|
</div>
|
||||||
|
<div id="logging_ignore_interval_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||||
|
</div>
|
||||||
|
<p class="help-block">The interval (in seconds) an item must be in a playing state before logging it. 0 to disable.</p>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="movie_watched_percent">Movie Watched Percent</label>
|
<label for="movie_watched_percent">Movie Watched Percent</label>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -150,6 +183,20 @@
|
|||||||
</div>
|
</div>
|
||||||
<p class="help-block">Set the percentage for a music track to be considered as listened. Minimum 50, Maximum 95.</p>
|
<p class="help-block">Set the percentage for a music track to be considered as listened. Minimum 50, Maximum 95.</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group advanced-setting">
|
||||||
|
<label>Flush Temporary Sessions</label>
|
||||||
|
<p class="help-block">
|
||||||
|
Attempt to fix history logging by flushing out all of the temporary sessions in the database.<br />
|
||||||
|
Warning: This will reset all currently active sessions. For emergency use only when history logging is stuck!
|
||||||
|
</p>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button class="btn btn-form" type="button" id="delete_temp_sessions">Flush</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="padded-header">
|
<div class="padded-header">
|
||||||
<h3>Updates</h3>
|
<h3>Updates</h3>
|
||||||
@@ -168,7 +215,7 @@
|
|||||||
</label>
|
</label>
|
||||||
<p class="help-block">Update Tautulli automatically if an update is available.</p>
|
<p class="help-block">Update Tautulli automatically if an update is available.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group advanced-setting">
|
||||||
<label for="git_token">GitHub API Token</label>
|
<label for="git_token">GitHub API Token</label>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
@@ -179,6 +226,41 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
% if plexpy.INSTALL_TYPE == 'git':
|
||||||
|
<div class="form-group advanced-setting">
|
||||||
|
<label for="git_branch">Git Remote / Branch</label>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="input-group git-group">
|
||||||
|
<input type="text" class="form-control" id="git_remote" name="git_remote" value="${config['git_remote']}" data-parsley-trigger="change">
|
||||||
|
<select class="form-control" id="git_branch" name="git_branch">
|
||||||
|
<% branches = ('master', 'beta', 'nightly') %>
|
||||||
|
% for branch in branches:
|
||||||
|
<option value="${branch}" ${'selected' if config['git_branch'] == branch else ''}>${branch}</option>
|
||||||
|
% endfor
|
||||||
|
% if config['git_branch'] not in branches:
|
||||||
|
<option value="${config['git_branch']}" selected>${config['git_branch']}</option>
|
||||||
|
% endif
|
||||||
|
</select>
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button class="btn btn-form" type="button" id="switch_git_branch">Checkout Branch</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="help-block">The git tracking remote and branch (default "origin/master"). Select to switch the git branch (requires restart).</p>
|
||||||
|
</div>
|
||||||
|
<div class="form-group advanced-setting">
|
||||||
|
<label for="git_path">Git Path</label>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<input type="text" class="form-control" id="git_path" name="git_path" value="${config['git_path']}" size="30">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="help-block">Optional: The path to your git environment variable. Leave blank for default.</p>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
|
||||||
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
|
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -331,7 +413,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="help-block">Note: Web interface changes require a restart.</p>
|
<p class="help-block">Note: Web interface changes require a restart.</p>
|
||||||
<div class="form-group">
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="launch_browser" id="launch_browser" value="1" ${config['launch_browser']}> Launch Browser on Startup
|
||||||
|
</label>
|
||||||
|
<p class="help-block">Launch browser pointed to Tautulli on startup.</p>
|
||||||
|
</div>
|
||||||
|
<div class="form-group advanced-setting">
|
||||||
<label for="http_host">HTTP Host</label>
|
<label for="http_host">HTTP Host</label>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
@@ -350,7 +438,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<p class="help-block">Port to bind web server to. Note that ports below 1024 may require root.</p>
|
<p class="help-block">Port to bind web server to. Note that ports below 1024 may require root.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group advanced-setting">
|
||||||
<label for="http_root">HTTP Root</label>
|
<label for="http_root">HTTP Root</label>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
@@ -359,34 +447,27 @@
|
|||||||
</div>
|
</div>
|
||||||
<p class="help-block">The base URL of the web server. Used for reverse proxies.</p>
|
<p class="help-block">The base URL of the web server. Used for reverse proxies.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="checkbox">
|
<div class="checkbox advanced-setting">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" class="http-settings" name="http_proxy" id="http_proxy" value="1" ${config['http_proxy']}> Enable HTTP Proxy
|
<input type="checkbox" class="http-settings" name="http_proxy" id="http_proxy" value="1" ${config['http_proxy']}> Enable HTTP Proxy
|
||||||
</label>
|
</label>
|
||||||
<p class="help-block">Respect the X-Forwarded-Proto header. Used for reverse proxies with SSL.</p>
|
<p class="help-block">Respect the X-Forwarded-Proto header. Used for reverse proxies with SSL.</p>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<div class="checkbox advanced-setting">
|
||||||
<div class="checkbox">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" name="launch_browser" id="launch_browser" value="1" ${config['launch_browser']}> Launch Browser on Startup
|
|
||||||
</label>
|
|
||||||
<p class="help-block">Launch browser pointed to Tautulli on startup.</p>
|
|
||||||
</div>
|
|
||||||
<div class="checkbox">
|
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" class="http-settings" name="enable_https" id="enable_https" value="1" ${config['enable_https']} /> Enable HTTPS
|
<input type="checkbox" class="http-settings" name="enable_https" id="enable_https" value="1" ${config['enable_https']} /> Enable HTTPS
|
||||||
</label>
|
</label>
|
||||||
<p class="help-block">Enable HTTPS for web server for encrypted communication.</p>
|
<p class="help-block">Enable HTTPS for web server for encrypted communication.</p>
|
||||||
</div>
|
</div>
|
||||||
<div id="https_options">
|
<div id="https_options">
|
||||||
<div class="checkbox">
|
<div class="checkbox advanced-setting">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" class="http-settings" name="https_create_cert" id="https_create_cert" value="1" ${config['https_create_cert']} /> Create Self-signed Certificate
|
<input type="checkbox" class="http-settings" name="https_create_cert" id="https_create_cert" value="1" ${config['https_create_cert']} /> Create Self-signed Certificate
|
||||||
</label>
|
</label>
|
||||||
<p class="help-block">Check to have Tautulli create a self-signed SSL certificate. Uncheck if you want to use your own certificate.</p>
|
<p class="help-block">Check to have Tautulli create a self-signed SSL certificate. Uncheck if you want to use your own certificate.</p>
|
||||||
</div>
|
</div>
|
||||||
<div id="https_options_self-signed">
|
<div id="https_options_self-signed">
|
||||||
<div class="form-group">
|
<div class="form-group advanced-setting">
|
||||||
<label for="https_domain">HTTPS Domains</label>
|
<label for="https_domain">HTTPS Domains</label>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
@@ -395,7 +476,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<p class="help-block">The domain names used to access Tautulli, separated by commas (,).</p>
|
<p class="help-block">The domain names used to access Tautulli, separated by commas (,).</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group advanced-setting">
|
||||||
<label for="https_ip">HTTPS IPs</label>
|
<label for="https_ip">HTTPS IPs</label>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
@@ -405,7 +486,7 @@
|
|||||||
<p class="help-block">The IP addresses used to access Tautulli, separated by commas (,).</p>
|
<p class="help-block">The IP addresses used to access Tautulli, separated by commas (,).</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group advanced-setting">
|
||||||
<label for="https_cert">HTTPS Certificate</label>
|
<label for="https_cert">HTTPS Certificate</label>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
@@ -414,7 +495,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<p class="help-block">The location of the SSL certificate.</p>
|
<p class="help-block">The location of the SSL certificate.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group advanced-setting">
|
||||||
<label for="https_cert_chain">HTTPS Certificate Chain</label>
|
<label for="https_cert_chain">HTTPS Certificate Chain</label>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
@@ -423,7 +504,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<p class="help-block">The location of the SSL certificate chain.</p>
|
<p class="help-block">The location of the SSL certificate chain.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group advanced-setting">
|
||||||
<label for="https_key">HTTPS Key</label>
|
<label for="https_key">HTTPS Key</label>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
@@ -433,17 +514,22 @@
|
|||||||
<p class="help-block">The location of the SSL key.</p>
|
<p class="help-block">The location of the SSL key.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
|
|
||||||
|
|
||||||
</div>
|
<div class="form-group advanced-setting">
|
||||||
|
<label for="anon_redirect">Anonymous Redirect</label>
|
||||||
<div role="tabpanel" class="tab-pane" id="tabs-access_control">
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<input type="text" class="form-control" id="anon_redirect" name="anon_redirect" value="${config['anon_redirect']}" size="30">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="help-block">Backlink protection via anonymizer service, must end in "?".</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="padded-header">
|
<div class="padded-header">
|
||||||
<h3>Authentication</h3>
|
<h3>Authentication</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="help-block">Authentication changes require a restart.</p>
|
<p class="help-block">Note: Authentication changes require a restart.</p>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="http_username">HTTP Username</label>
|
<label for="http_username">HTTP Username</label>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -463,23 +549,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<p class="help-block">Password for web server authentication. Leave empty to disable.</p>
|
<p class="help-block">Password for web server authentication. Leave empty to disable.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="checkbox">
|
|
||||||
|
<div class="checkbox advanced-setting">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="http_hash_password" id="http_hash_password" value="1" ${config['http_hash_password']} data-parsley-trigger="change"> Hash Password in the Config File
|
<input type="checkbox" name="http_hash_password" id="http_hash_password" value="1" ${config['http_hash_password']} data-parsley-trigger="change"> Hash Password in the Config File
|
||||||
</label>
|
</label>
|
||||||
<span id="hashPasswordCheck" style="color: #eb8600; padding-left: 10px;"></span>
|
<span id="hashPasswordCheck" style="color: #eb8600; padding-left: 10px;"></span>
|
||||||
<p class="help-block">Store a hashed password in the config file.<br />Warning: Your password cannot be recovered if forgotten!</p>
|
<p class="help-block">Store a hashed password in the config file.<br />Warning: Your password cannot be recovered if forgotten!</p>
|
||||||
</div>
|
</div>
|
||||||
<input type="text" id="http_hashed_password" name="http_hashed_password" value="${config['http_hashed_password']}" style="display: none;" data-parsley-trigger="change" data-parsley-type="integer" data-parsley-range="[0, 1]"
|
<input type="text" id="http_hashed_password" name="http_hashed_password" value="${config['http_hashed_password']}" style="display: none;" data-parsley-trigger="change" data-parsley-type="integer" data-parsley-range="[0, 1]"
|
||||||
data-parsley-errors-container="#http_hash_password_error" data-parsley-error-message="Cannot un-hash password, please set a new password." data-parsley-no-focus required>
|
data-parsley-errors-container="#http_hash_password_error" data-parsley-error-message="Cannot un-hash password, please set a new password." data-parsley-no-focus required>
|
||||||
<div class="checkbox">
|
<div class="checkbox advanced-setting">
|
||||||
<label>
|
|
||||||
<input type="checkbox" class="auth-settings" name="http_plex_admin" id="http_plex_admin" value="1" ${config['http_plex_admin']} data-parsley-trigger="change"> Allow Plex Admin
|
|
||||||
</label>
|
|
||||||
<span id="allowPlexCheck" style="color: #eb8600; padding-left: 10px;"></span>
|
|
||||||
<p class="help-block">Allow the Plex server admin to login as a Tautulli admin using their Plex.tv account.</p>
|
|
||||||
</div>
|
|
||||||
<div class="checkbox">
|
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" class="auth-settings" name="http_basic_auth" id="http_basic_auth" value="1" ${config['http_basic_auth']} data-parsley-trigger="change"> Use Basic Authentication
|
<input type="checkbox" class="auth-settings" name="http_basic_auth" id="http_basic_auth" value="1" ${config['http_basic_auth']} data-parsley-trigger="change"> Use Basic Authentication
|
||||||
</label>
|
</label>
|
||||||
@@ -488,10 +568,13 @@
|
|||||||
|
|
||||||
<input type="checkbox" name="auth_changed" id="auth_changed" value="1" style="display: none;">
|
<input type="checkbox" name="auth_changed" id="auth_changed" value="1" style="display: none;">
|
||||||
|
|
||||||
<div class="padded-header">
|
<div class="checkbox">
|
||||||
<h3>Guest Access</h3>
|
<label>
|
||||||
|
<input type="checkbox" class="auth-settings" name="http_plex_admin" id="http_plex_admin" value="1" ${config['http_plex_admin']} data-parsley-trigger="change"> Allow Plex Admin
|
||||||
|
</label>
|
||||||
|
<span id="allowPlexCheck" style="color: #eb8600; padding-left: 10px;"></span>
|
||||||
|
<p class="help-block">Allow the Plex server admin to login as a Tautulli admin using their Plex.tv account.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" id="allow_guest_access" name="allow_guest_access" value="1" ${config['allow_guest_access']}> Allow Guest Access to Tautulli
|
<input type="checkbox" id="allow_guest_access" name="allow_guest_access" value="1" ${config['allow_guest_access']}> Allow Guest Access to Tautulli
|
||||||
@@ -537,6 +620,106 @@
|
|||||||
<h3>Plex Media Server <small style="color: #fff;">Version <span id="pms_version">${config['pms_version']}</span></small></h3>
|
<h3>Plex Media Server <small style="color: #fff;">Version <span id="pms_version">${config['pms_version']}</span></small></h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group has-feedback" id="pms_ip_group">
|
||||||
|
<label for="pms_ip">Plex IP or Hostname</label>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="pms-settings form-control" id="pms_ip" name="pms_ip" value="${config['pms_ip']}" size="30" data-parsley-trigger="change" aria-describedby="server-verified" data-parsley-errors-container="#pms_ip_error" required>
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button class="btn btn-form" type="button" id="verify_server_button">Verify Server</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span class="form-control-feedback" id="pms_verify" aria-hidden="true" style="display: none; right: 110px;"></span>
|
||||||
|
</div>
|
||||||
|
<div id="pms_ip_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||||
|
</div>
|
||||||
|
<p class="help-block">IP Address or hostname for Plex Media Server.</p>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="pms_port">Plex Port</label>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-2">
|
||||||
|
<input data-parsley-type="integer" class="pms-settings form-control" type="text" id="pms_port" name="pms_port" value="${config['pms_port']}" size="30" data-parsley-trigger="change" data-parsley-errors-container="#pms_port_error" required>
|
||||||
|
</div>
|
||||||
|
<div id="pms_port_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||||
|
</div>
|
||||||
|
<p class="help-block">Port that Plex Media Server is listening on.</p>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="pms_is_remote" name="pms_is_remote" value="1" ${config['pms_is_remote']}> Remote Server
|
||||||
|
</label>
|
||||||
|
<p class="help-block">Check this if your Plex Server is not on the same local network as Tautulli.</p>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="pms_ssl" name="pms_ssl" value="1" ${config['pms_ssl']}> Use SSL
|
||||||
|
</label>
|
||||||
|
<p class="help-block">If you have secure connections enabled on your Plex Server, communicate with it securely.</p>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox advanced-setting">
|
||||||
|
% if config['pms_is_cloud']:
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="pms_url_manual" name="pms_url_manual" value="1" disabled> Manual Connection
|
||||||
|
</label>
|
||||||
|
<span style="color: #eb8600; padding-left: 10px;"> Not available for Plex Cloud servers.</span>
|
||||||
|
% else:
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="pms_url_manual" name="pms_url_manual" value="1" ${config['pms_url_manual']}> Manual Connection
|
||||||
|
</label>
|
||||||
|
% endif
|
||||||
|
<p class="help-block">Use the user defined connection details. Do not retrieve the server connection URL automatically.</p>
|
||||||
|
</div>
|
||||||
|
<div class="form-group advanced-setting">
|
||||||
|
<label for="pms_logs_folder">Plex Web URL</label>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control" id="pms_web_url" name="pms_web_url" value="${config['pms_web_url']}" size="30" data-parsley-trigger="change" data-parsley-pattern="^https?:\/\/\S+$|^https:\/\/app.plex.tv\/desktop$" data-parsley-errors-container="#pms_web_url_error" data-parsley-error-message="Invalid Plex Web URL.">
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button class="btn btn-form" type="button" id="test_pms_web_button">Test URL</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="pms_web_url_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||||
|
</div>
|
||||||
|
<p class="help-block">
|
||||||
|
Optional: Manually override the Plex Web URL used for click-through links on the media info pages and notifications. Default <strong>https://app.plex.tv/desktop</strong>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="hidden" id="pms_identifier" name="pms_identifier" value="${config['pms_identifier']}">
|
||||||
|
<input type="checkbox" name="server_changed" id="server_changed" value="1" style="display: none;">
|
||||||
|
|
||||||
|
<div class="form-group advanced-setting">
|
||||||
|
<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" 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">
|
||||||
|
Optional: Set your Plex logs folder to use Tautulli as a log viewer. Plex logs are not needed for Tautulli to function.
|
||||||
|
A complete folder path is required, shortcuts are not recognized, and the logs must be accessible from the machine where Tautulli is installed.
|
||||||
|
<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.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox advanced-setting">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="cache_images" name="cache_images" value="1" ${config['cache_images']}> Cache Plex Images
|
||||||
|
</label>
|
||||||
|
<p class="help-block">
|
||||||
|
Enable to cache images from Plex to reduce API calls and improve loading times.<br />
|
||||||
|
Note: Video preview thumbnails (BIF) are not cached.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="padded-header">
|
||||||
|
<h3>Server Monitoring</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
% if config['pms_is_cloud']:
|
% if config['pms_is_cloud']:
|
||||||
<label>
|
<label>
|
||||||
@@ -584,102 +767,40 @@
|
|||||||
<p class="help-block">Enable to have Tautulli check if remote access to the Plex Media Server goes down.</p>
|
<p class="help-block">Enable to have Tautulli check if remote access to the Plex Media Server goes down.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group has-feedback" id="pms_ip_group">
|
<div class="form-group advanced-setting">
|
||||||
<label for="pms_ip">Plex IP or Hostname</label>
|
<label for="refresh_users_interval">Users List Refresh Interval</label>
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="input-group">
|
|
||||||
<input type="text" class="pms-settings form-control" id="pms_ip" name="pms_ip" value="${config['pms_ip']}" size="30" data-parsley-trigger="change" aria-describedby="server-verified" data-parsley-errors-container="#pms_ip_error" required>
|
|
||||||
<span class="input-group-btn">
|
|
||||||
<button class="btn btn-form" type="button" id="verify_server_button">Verify Server</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<span class="form-control-feedback" id="pms_verify" aria-hidden="true" style="display: none; right: 110px;"></span>
|
|
||||||
</div>
|
|
||||||
<div id="pms_ip_error" class="alert alert-danger settings-alert" role="alert"></div>
|
|
||||||
</div>
|
|
||||||
<p class="help-block">IP Address or hostname for Plex Media Server.</p>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="pms_port">Plex Port</label>
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-2">
|
<div class="col-md-2">
|
||||||
<input data-parsley-type="integer" class="pms-settings form-control" type="text" id="pms_port" name="pms_port" value="${config['pms_port']}" size="30" data-parsley-trigger="change" data-parsley-errors-container="#pms_port_error" required>
|
<input type="text" class="form-control" data-parsley-type="integer" id="refresh_users_interval" name="refresh_users_interval" value="${config['refresh_users_interval']}" size="5" data-parsley-range="[1,24]" data-parsley-trigger="change" data-parsley-errors-container="#refresh_users_interval_error" required>
|
||||||
</div>
|
</div>
|
||||||
<div id="pms_port_error" class="alert alert-danger settings-alert" role="alert"></div>
|
<div id="refresh_users_interval_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
<p class="help-block">Port that Plex Media Server is listening on.</p>
|
<p class="help-block">The interval (in hours) Tautulli will request an updated friends list from Plex.tv. Minimum 1, maximum 24, default 12.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="checkbox">
|
<div class="checkbox advanced-setting">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" id="pms_is_remote" name="pms_is_remote" value="1" ${config['pms_is_remote']}> Remote Server
|
<input type="checkbox" id="refresh_users_on_startup" name="refresh_users_on_startup" value="1" ${config['refresh_users_on_startup']}> Refresh Users List on Startup
|
||||||
</label>
|
</label>
|
||||||
<p class="help-block">Check this if your Plex Server is not on the same local network as Tautulli.</p>
|
<p class="help-block">Refresh the users list when Tautulli starts.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="checkbox">
|
|
||||||
<label>
|
<div class="form-group advanced-setting">
|
||||||
<input type="checkbox" id="pms_ssl" name="pms_ssl" value="1" ${config['pms_ssl']}> Use SSL
|
<label for="refresh_libraries_interval">Libraries List Refresh Interval</label>
|
||||||
</label>
|
|
||||||
<p class="help-block">If you have secure connections enabled on your Plex Server, communicate with it securely.</p>
|
|
||||||
</div>
|
|
||||||
<div class="checkbox">
|
|
||||||
% if config['pms_is_cloud']:
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" id="pms_url_manual" name="pms_url_manual" value="1" disabled> Manual Connection
|
|
||||||
</label>
|
|
||||||
<span style="color: #eb8600; padding-left: 10px;"> Not available for Plex Cloud servers.</span>
|
|
||||||
% else:
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" id="pms_url_manual" name="pms_url_manual" value="1" ${config['pms_url_manual']}> Manual Connection
|
|
||||||
</label>
|
|
||||||
% endif
|
|
||||||
<p class="help-block">Use the user defined connection details. Do not retrieve the server connection URL automatically.</p>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="pms_logs_folder">Plex Web URL</label>
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-2">
|
||||||
<div class="input-group">
|
<input type="text" class="form-control" data-parsley-type="integer" id="refresh_libraries_interval" name="refresh_libraries_interval" value="${config['refresh_libraries_interval']}" size="5" data-parsley-range="[1,24]" data-parsley-trigger="change" data-parsley-errors-container="#refresh_libraries_interval_error" required>
|
||||||
<input type="text" class="form-control" id="pms_web_url" name="pms_web_url" value="${config['pms_web_url']}" size="30" data-parsley-trigger="change" data-parsley-pattern="^https?:\/\/\S+$|^https:\/\/app.plex.tv\/desktop$" data-parsley-errors-container="#pms_web_url_error" data-parsley-error-message="Invalid Plex Web URL.">
|
|
||||||
<span class="input-group-btn">
|
|
||||||
<button class="btn btn-form" type="button" id="test_pms_web_button">Test URL</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="pms_web_url_error" class="alert alert-danger settings-alert" role="alert"></div>
|
<div id="refresh_libraries_interval_error" class="alert alert-danger settings-alert" role="alert"></div>
|
||||||
</div>
|
</div>
|
||||||
<p class="help-block">
|
<p class="help-block">The interval (in hours) Tautulli will request an updated libraries list from your Plex Media Server. Minimum 1, maximum 24, default 12.</p>
|
||||||
Optional: Manually override the Plex Web URL used for click-through links on the media info pages and notifications. Default <strong>https://app.plex.tv/desktop</strong>.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="checkbox advanced-setting">
|
||||||
<input type="hidden" id="pms_identifier" name="pms_identifier" value="${config['pms_identifier']}">
|
<label>
|
||||||
<input type="checkbox" name="server_changed" id="server_changed" value="1" style="display: none;">
|
<input type="checkbox" id="refresh_libraries_on_startup" name="refresh_libraries_on_startup" value="1" ${config['refresh_libraries_on_startup']}> Refresh Libraries List on Startup
|
||||||
|
</label>
|
||||||
<div class="padded-header">
|
<p class="help-block">Refresh the libraries list when Tautulli starts.</p>
|
||||||
<h3>Plex Logs</h3>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<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" 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">
|
|
||||||
Optional: Set your Plex logs folder to use Tautulli as a log viewer. Plex logs are not needed for Tautulli to function.
|
|
||||||
A complete folder path is required, shortcuts are not recognized, and the logs must be accessible from the machine where Tautulli is installed.
|
|
||||||
<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.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tabs-plextv_account">
|
|
||||||
|
|
||||||
<div class="padded-header">
|
<div class="padded-header">
|
||||||
<h3>Plex.tv Authentication</h3>
|
<h3>Plex.tv Authentication</h3>
|
||||||
</div>
|
</div>
|
||||||
@@ -700,71 +821,14 @@
|
|||||||
<p class="help-block">Token for Plex.tv authentication.</p>
|
<p class="help-block">Token for Plex.tv authentication.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="padded-header">
|
|
||||||
<h3>Friends List</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="refresh_users_interval">Users List Refresh Interval</label>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-2">
|
|
||||||
<input type="text" class="form-control" data-parsley-type="integer" id="refresh_users_interval" name="refresh_users_interval" value="${config['refresh_users_interval']}" size="5" data-parsley-range="[1,24]" data-parsley-trigger="change" data-parsley-errors-container="#refresh_users_interval_error" required>
|
|
||||||
</div>
|
|
||||||
<div id="refresh_users_interval_error" class="alert alert-danger settings-alert" role="alert"></div>
|
|
||||||
</div>
|
|
||||||
<p class="help-block">The interval (in hours) Tautulli will request an updated friends list from Plex.tv. Minimum 1, maximum 24, default 12.</p>
|
|
||||||
</div>
|
|
||||||
<div class="checkbox">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" id="refresh_users_on_startup" name="refresh_users_on_startup" value="1" ${config['refresh_users_on_startup']}> Refresh Users List on Startup
|
|
||||||
</label>
|
|
||||||
<p class="help-block">Refresh the users list when Tautulli starts.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="padded-header">
|
|
||||||
<h3>Libraries List</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="refresh_libraries_interval">Libraries List Refresh Interval</label>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-2">
|
|
||||||
<input type="text" class="form-control" data-parsley-type="integer" id="refresh_libraries_interval" name="refresh_libraries_interval" value="${config['refresh_libraries_interval']}" size="5" data-parsley-range="[1,24]" data-parsley-trigger="change" data-parsley-errors-container="#refresh_libraries_interval_error" required>
|
|
||||||
</div>
|
|
||||||
<div id="refresh_libraries_interval_error" class="alert alert-danger settings-alert" role="alert"></div>
|
|
||||||
</div>
|
|
||||||
<p class="help-block">The interval (in hours) Tautulli will request an updated libraries list from your Plex Media Server. Minimum 1, maximum 24, default 12.</p>
|
|
||||||
</div>
|
|
||||||
<div class="checkbox">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" id="refresh_libraries_on_startup" name="refresh_libraries_on_startup" value="1" ${config['refresh_libraries_on_startup']}> Refresh Libraries List on Startup
|
|
||||||
</label>
|
|
||||||
<p class="help-block">Refresh the libraries list when Tautulli starts.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
|
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tabs-activity_monitoring">
|
<div role="tabpanel" class="tab-pane" id="tabs-notifications">
|
||||||
|
|
||||||
<div class="padded-header">
|
<div class="padded-header">
|
||||||
<h3>History Logging</h3>
|
<h3>Current Activity Notifications</h3>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="logging_ignore_interval">Ignore Interval</label>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-2">
|
|
||||||
<input type="text" class="form-control" data-parsley-type="integer" id="logging_ignore_interval" name="logging_ignore_interval" value="${config['logging_ignore_interval']}" size="5" data-parsley-min="0" data-parsley-trigger="change" data-parsley-errors-container="#logging_ignore_interval_error" required>
|
|
||||||
</div>
|
|
||||||
<div id="logging_ignore_interval_error" class="alert alert-danger settings-alert" role="alert"></div>
|
|
||||||
</div>
|
|
||||||
<p class="help-block">The interval (in seconds) an item must be in a playing state before logging it. 0 to disable.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="padded-header">
|
|
||||||
<h3>Buffer Warnings</h3>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="help-block">Note: Buffer warnings only work on certain Plex clients. Android and Plex Web do not report buffer events accurately or at all.</p>
|
<p class="help-block">Note: Buffer warnings only work on certain Plex clients. Android and Plex Web do not report buffer events accurately or at all.</p>
|
||||||
@@ -778,7 +842,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<p class="help-block">How many buffer events should we wait before triggering the first warning. Buffer events increment on each monitor ping if play state is buffering. 0 to disable buffer warnings.</p>
|
<p class="help-block">How many buffer events should we wait before triggering the first warning. Buffer events increment on each monitor ping if play state is buffering. 0 to disable buffer warnings.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group advanced-setting">
|
||||||
<label for="buffer_wait">Buffer Wait</label>
|
<label for="buffer_wait">Buffer Wait</label>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-2">
|
<div class="col-md-2">
|
||||||
@@ -788,24 +852,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<p class="help-block">The value (in seconds) Tautulli should wait before triggering the next buffer warning. 0 to always trigger.</p>
|
<p class="help-block">The value (in seconds) Tautulli should wait before triggering the next buffer warning. 0 to always trigger.</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="checkbox advanced-setting">
|
||||||
<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-notifications">
|
|
||||||
|
|
||||||
<div class="padded-header">
|
|
||||||
<h3>Current Activity Notifications</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="checkbox">
|
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="notify_consecutive" id="notify_consecutive" value="1" ${config['notify_consecutive']}> Allow Consecutive Notifications
|
<input type="checkbox" name="notify_consecutive" id="notify_consecutive" value="1" ${config['notify_consecutive']}> Allow Consecutive Notifications
|
||||||
</label>
|
</label>
|
||||||
<p class="help-block">Enable to allow sending of consecutive notifications (i.e. both watched & stopped notifications).</p>
|
<p class="help-block">Enable to allow sending of consecutive notifications (i.e. both watched & stopped notifications).</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="checkbox">
|
<div class="checkbox advanced-setting">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="notify_concurrent_by_ip" id="notify_concurrent_by_ip" value="1" ${config['notify_concurrent_by_ip']}> User Concurrent Streams Notifications by IP Address
|
<input type="checkbox" name="notify_concurrent_by_ip" id="notify_concurrent_by_ip" value="1" ${config['notify_concurrent_by_ip']}> User Concurrent Streams Notifications by IP Address
|
||||||
</label>
|
</label>
|
||||||
@@ -921,103 +974,6 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="tabs-extra_settings">
|
|
||||||
|
|
||||||
<div class="padded-header">
|
|
||||||
<h3>Extra Settings</h3>
|
|
||||||
</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
|
|
||||||
</label>
|
|
||||||
<p class="help-block">Enable if you want Tautulli to calculate the total file size for TV Shows/Seasons and Artists/Albums on the media info tables.</p>
|
|
||||||
</div>
|
|
||||||
<div class="checkbox">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" id="log_blacklist" name="log_blacklist" value="1" ${config['log_blacklist']}> Mask Sensitive Information in Logs
|
|
||||||
</label>
|
|
||||||
<p class="help-block">
|
|
||||||
Enable to mask passwords, access tokens, and public IP addresses with asterisks (*) in the logs.<br />
|
|
||||||
Note: Only logs from the time this setting is enabled will be masked. Do not post your logs publically without masking sensitive information!
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="checkbox">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" id="cache_images" name="cache_images" value="1" ${config['cache_images']}> Cache Plex Images
|
|
||||||
</label>
|
|
||||||
<p class="help-block">
|
|
||||||
Enable to cache images from Plex to reduce API calls and improve loading times.<br />
|
|
||||||
Note: Video preview thumbnails (BIF) are not cached.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="anon_redirect">Anonymous Redirect</label>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-4">
|
|
||||||
<input type="text" class="form-control" id="anon_redirect" name="anon_redirect" value="${config['anon_redirect']}" size="30">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p class="help-block">Backlink protection via anonymizer service, must end in "?".</p>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Flush Temporary Sessions</label>
|
|
||||||
<p class="help-block">
|
|
||||||
Attempt to fix history logging by flushing out all of the temporary sessions in the database.<br />
|
|
||||||
Warning: This will reset all currently active sessions. For emergency use only when history logging is stuck!
|
|
||||||
</p>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-4">
|
|
||||||
<div class="btn-group">
|
|
||||||
<button class="btn btn-form" type="button" id="delete_temp_sessions">Flush</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
% if plexpy.INSTALL_TYPE == 'git':
|
|
||||||
<div class="padded-header">
|
|
||||||
<h3>Git Settings</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="git_branch">Git Remote / Branch</label>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="input-group git-group">
|
|
||||||
<input type="text" class="form-control" id="git_remote" name="git_remote" value="${config['git_remote']}" data-parsley-trigger="change">
|
|
||||||
<select class="form-control" id="git_branch" name="git_branch">
|
|
||||||
<% branches = ('master', 'beta', 'nightly') %>
|
|
||||||
% for branch in branches:
|
|
||||||
<option value="${branch}" ${'selected' if config['git_branch'] == branch else ''}>${branch}</option>
|
|
||||||
% endfor
|
|
||||||
% if config['git_branch'] not in branches:
|
|
||||||
<option value="${config['git_branch']}" selected>${config['git_branch']}</option>
|
|
||||||
% endif
|
|
||||||
</select>
|
|
||||||
<span class="input-group-btn">
|
|
||||||
<button class="btn btn-form" type="button" id="switch_git_branch">Checkout Branch</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p class="help-block">The git tracking remote and branch (default "origin/master"). Select to switch the git branch (requires restart).</p>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="git_path">Git Path</label>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-4">
|
|
||||||
<input type="text" class="form-control" id="git_path" name="git_path" value="${config['git_path']}" size="30">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p class="help-block">Optional: The path to your git environment variable. Leave blank for default.</p>
|
|
||||||
</div>
|
|
||||||
% endif
|
|
||||||
|
|
||||||
<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-import_backups">
|
<div role="tabpanel" class="tab-pane" id="tabs-import_backups">
|
||||||
|
|
||||||
<div class="padded-header">
|
<div class="padded-header">
|
||||||
@@ -1147,8 +1103,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div style="text-align: center; margin-top: 20px; margin-bottom: 20px;">
|
<div style="text-align: center; margin-top: 20px; margin-bottom: 20px;">
|
||||||
<strong>Please read the <a href="${anon_url('https://github.com/%s/plexpy/blob/master/CONTRIBUTING.md' % plexpy.CONFIG.GIT_USER)}" target="_blank">guidelines</a>
|
<strong>Please read the <a href="${anon_url('https://github.com/%s/%s-Issues/blob/master/README.md' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}" target="_blank">guidelines</a>
|
||||||
in the CONTRIBUTING document <br />before submitting a new <span id="guidelines-type"></span>!</strong>
|
in the README document <br />before submitting a new <span id="guidelines-type"></span>!</strong>
|
||||||
<br /><br />
|
<br /><br />
|
||||||
Your post may be removed for failure to follow the guidelines.
|
Your post may be removed for failure to follow the guidelines.
|
||||||
</div>
|
</div>
|
||||||
@@ -1169,7 +1125,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div style="text-align: center; margin-top: 20px; margin-bottom: 20px;">
|
<div style="text-align: center; margin-top: 20px; margin-bottom: 20px;">
|
||||||
<strong>Please read the <a href="${anon_url('https://github.com/%s/plexpy/wiki/Frequently-Asked-Questions-(FAQ)' % plexpy.CONFIG.GIT_USER)}" target="_blank">FAQ</a>
|
<strong>Please read the <a href="${anon_url('https://github.com/%s/%s-Wiki/wiki/Frequently-Asked-Questions' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}" target="_blank">FAQ</a>
|
||||||
before asking for help!</strong>
|
before asking for help!</strong>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1344,13 +1300,22 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div>
|
<div>
|
||||||
|
<div style="padding-bottom: 10px;">
|
||||||
|
<p class="help-block">
|
||||||
|
Note: Tags separate the <em>media type</em> that triggered the notifications (i.e. a complete show added to Plex vs. a single episode added to Plex).
|
||||||
|
They <em>do not</em> separate the notification parameters (i.e. <span class="inline-pre">{show_name}</span> vs. <span class="inline-pre">{episode_name}</span>.
|
||||||
|
</p>
|
||||||
|
<p class="help-block">
|
||||||
|
Note: Nesting tags inside each other is not supported.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h4>Movie Tag</h4>
|
<h4>Movie Tag</h4>
|
||||||
</div>
|
</div>
|
||||||
<div style="padding-bottom: 10px;">
|
<div style="padding-bottom: 10px;">
|
||||||
<p class="help-block">All text inside <span class="inline-pre"><movie></movie></span> tags will only be sent when the media item is a movie.</p>
|
<p class="help-block">All text inside <span class="inline-pre"><movie></movie></span> tags will only be sent when the media type is movie.</p>
|
||||||
<p><strong style="color: #fff;">Example:</strong></p>
|
<p><strong style="color: #fff;">Example:</strong></p>
|
||||||
<pre>{title} <movie>({year})</movie> was recently added to Plex</pre>
|
<pre>{title}<movie>({year})</movie> was recently added to Plex</pre>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h4>Show / Season / Episode Tags</h4>
|
<h4>Show / Season / Episode Tags</h4>
|
||||||
@@ -1358,7 +1323,7 @@
|
|||||||
<div style="padding-bottom: 10px;">
|
<div style="padding-bottom: 10px;">
|
||||||
<p class="help-block">
|
<p class="help-block">
|
||||||
All text inside <span class="inline-pre"><show></show></span>/<span class="inline-pre"><season></season></span>/<span class="inline-pre"><episode></episode></span>
|
All text inside <span class="inline-pre"><show></show></span>/<span class="inline-pre"><season></season></span>/<span class="inline-pre"><episode></episode></span>
|
||||||
tags will only be sent when the media item is a show/season/episode.
|
tags will only be sent when the media type is show/season/episode.
|
||||||
</p>
|
</p>
|
||||||
<p><strong style="color: #fff;">Example:</strong></p>
|
<p><strong style="color: #fff;">Example:</strong></p>
|
||||||
<pre>{show_name}<season> - Season {season_num}</season><episode> - S{season_num}E{episode_num} - {episode_name}</episode> was recently added to Plex.</pre>
|
<pre>{show_name}<season> - Season {season_num}</season><episode> - S{season_num}E{episode_num} - {episode_name}</episode> was recently added to Plex.</pre>
|
||||||
@@ -1369,7 +1334,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<p class="help-block">
|
<p class="help-block">
|
||||||
All text inside <span class="inline-pre"><artist></artist></span>/<span class="inline-pre"><album></album></span>/<span class="inline-pre"><track></track></span>
|
All text inside <span class="inline-pre"><artist></artist></span>/<span class="inline-pre"><album></album></span>/<span class="inline-pre"><track></track></span>
|
||||||
tags will only be sent when the media item is an artist/album/track.
|
tags will only be sent when the media type is artist/album/track.
|
||||||
</p>
|
</p>
|
||||||
<p><strong style="color: #fff;">Example:</strong></p>
|
<p><strong style="color: #fff;">Example:</strong></p>
|
||||||
<pre>{artist_name}<album> - {album_name}</album><track> - {album_name} - {track_name}</track> was recently added to Plex.</pre>
|
<pre>{artist_name}<album> - {album_name}</album><track> - {album_name} - {track_name}</track> was recently added to Plex.</pre>
|
||||||
@@ -1659,6 +1624,15 @@ $(document).ready(function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function advancedSettings() {
|
||||||
|
var advanced_button = $('#menu_link_show_advanced_settings');
|
||||||
|
if (advanced_button.hasClass('active')) {
|
||||||
|
$('.advanced-setting').show();
|
||||||
|
} else {
|
||||||
|
$('.advanced-setting').hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$('.save-button').click(function() {
|
$('.save-button').click(function() {
|
||||||
preSaveChecks(function () { saveSettings() });
|
preSaveChecks(function () { saveSettings() });
|
||||||
});
|
});
|
||||||
@@ -1698,6 +1672,19 @@ $(document).ready(function() {
|
|||||||
window.location.href = 'restart';
|
window.location.href = 'restart';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#menu_link_show_advanced_settings').click(function() {
|
||||||
|
$(this).toggleClass('active');
|
||||||
|
if ($(this).hasClass('active')) {
|
||||||
|
$(this).html('<i class="fa fa-wrench"></i> Hide Advanced');
|
||||||
|
$('#show_advanced_settings').val(1);
|
||||||
|
} else {
|
||||||
|
$(this).html('<i class="fa fa-wrench"></i> Show Advanced');
|
||||||
|
$('#show_advanced_settings').val(0);
|
||||||
|
}
|
||||||
|
advancedSettings()
|
||||||
|
});
|
||||||
|
|
||||||
|
advancedSettings();
|
||||||
getConfigurationTable();
|
getConfigurationTable();
|
||||||
getSchedulerTable();
|
getSchedulerTable();
|
||||||
getNotifiersTable();
|
getNotifiersTable();
|
||||||
|
@@ -607,7 +607,7 @@ def dbcheck():
|
|||||||
# poster_urls table :: This table keeps record of the notification poster urls
|
# poster_urls table :: This table keeps record of the notification poster urls
|
||||||
c_db.execute(
|
c_db.execute(
|
||||||
'CREATE TABLE IF NOT EXISTS poster_urls (id INTEGER PRIMARY KEY AUTOINCREMENT, '
|
'CREATE TABLE IF NOT EXISTS poster_urls (id INTEGER PRIMARY KEY AUTOINCREMENT, '
|
||||||
'rating_key INTEGER, poster_title TEXT, poster_url TEXT)'
|
'rating_key INTEGER, poster_title TEXT, poster_url TEXT, delete_hash TEXT)'
|
||||||
)
|
)
|
||||||
|
|
||||||
# recently_added table :: This table keeps record of recently added items
|
# recently_added table :: This table keeps record of recently added items
|
||||||
@@ -1572,6 +1572,15 @@ def dbcheck():
|
|||||||
'ALTER TABLE user_login ADD COLUMN success INTEGER DEFAULT 1'
|
'ALTER TABLE user_login ADD COLUMN success INTEGER DEFAULT 1'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Upgrade poster_urls table from earlier versions
|
||||||
|
try:
|
||||||
|
c_db.execute('SELECT delete_hash FROM poster_urls')
|
||||||
|
except sqlite3.OperationalError:
|
||||||
|
logger.debug(u"Altering database. Updating database table poster_urls.")
|
||||||
|
c_db.execute(
|
||||||
|
'ALTER TABLE poster_urls ADD COLUMN delete_hash TEXT'
|
||||||
|
)
|
||||||
|
|
||||||
# Add "Local" user to database as default unauthenticated user.
|
# Add "Local" user to database as default unauthenticated user.
|
||||||
result = c_db.execute('SELECT id FROM users WHERE username = "Local"')
|
result = c_db.execute('SELECT id FROM users WHERE username = "Local"')
|
||||||
if not result.fetchone():
|
if not result.fetchone():
|
||||||
|
@@ -279,6 +279,7 @@ class ActivityHandler(object):
|
|||||||
db_session['media_type'] == 'episode' and progress_percent >= plexpy.CONFIG.TV_WATCHED_PERCENT or
|
db_session['media_type'] == 'episode' and progress_percent >= plexpy.CONFIG.TV_WATCHED_PERCENT or
|
||||||
db_session['media_type'] == 'track' and progress_percent >= plexpy.CONFIG.MUSIC_WATCHED_PERCENT) \
|
db_session['media_type'] == 'track' and progress_percent >= plexpy.CONFIG.MUSIC_WATCHED_PERCENT) \
|
||||||
and not any(d['notify_action'] == 'on_watched' for d in notify_states):
|
and not any(d['notify_action'] == 'on_watched' for d in notify_states):
|
||||||
|
logger.debug(u"Tautulli ActivityHandler :: Session %s watched." % str(self.get_session_key()))
|
||||||
plexpy.NOTIFY_QUEUE.put({'stream_data': db_session.copy(), 'notify_action': 'on_watched'})
|
plexpy.NOTIFY_QUEUE.put({'stream_data': db_session.copy(), 'notify_action': 'on_watched'})
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@@ -176,13 +176,13 @@ _CONFIG_DEFINITIONS = {
|
|||||||
'GIT_PATH': (str, 'General', ''),
|
'GIT_PATH': (str, 'General', ''),
|
||||||
'GIT_REMOTE': (str, 'General', 'origin'),
|
'GIT_REMOTE': (str, 'General', 'origin'),
|
||||||
'GIT_TOKEN': (str, 'General', ''),
|
'GIT_TOKEN': (str, 'General', ''),
|
||||||
'GIT_USER': (str, 'General', 'JonnyWong16'),
|
'GIT_USER': (str, 'General', 'Tautulli'),
|
||||||
'GIT_REPO': (str, 'General', 'plexpy'),
|
'GIT_REPO': (str, 'General', 'Tautulli'),
|
||||||
'GRAPH_TYPE': (str, 'General', 'plays'),
|
'GRAPH_TYPE': (str, 'General', 'plays'),
|
||||||
'GRAPH_DAYS': (int, 'General', 30),
|
'GRAPH_DAYS': (int, 'General', 30),
|
||||||
'GRAPH_MONTHS': (int, 'General', 12),
|
'GRAPH_MONTHS': (int, 'General', 12),
|
||||||
'GRAPH_TAB': (str, 'General', 'tabs-1'),
|
'GRAPH_TAB': (str, 'General', 'tabs-1'),
|
||||||
'GROUP_HISTORY_TABLES': (int, 'General', 0),
|
'GROUP_HISTORY_TABLES': (int, 'General', 1),
|
||||||
'GROWL_ENABLED': (int, 'Growl', 0),
|
'GROWL_ENABLED': (int, 'Growl', 0),
|
||||||
'GROWL_HOST': (str, 'Growl', ''),
|
'GROWL_HOST': (str, 'Growl', ''),
|
||||||
'GROWL_PASSWORD': (str, 'Growl', ''),
|
'GROWL_PASSWORD': (str, 'Growl', ''),
|
||||||
@@ -480,6 +480,7 @@ _CONFIG_DEFINITIONS = {
|
|||||||
'REFRESH_USERS_ON_STARTUP': (int, 'Monitoring', 1),
|
'REFRESH_USERS_ON_STARTUP': (int, 'Monitoring', 1),
|
||||||
'REMOTE_ACCESS_PING_THRESHOLD': (int, 'Advanced', 3),
|
'REMOTE_ACCESS_PING_THRESHOLD': (int, 'Advanced', 3),
|
||||||
'SESSION_DB_WRITE_ATTEMPTS': (int, 'Advanced', 5),
|
'SESSION_DB_WRITE_ATTEMPTS': (int, 'Advanced', 5),
|
||||||
|
'SHOW_ADVANCED_SETTINGS': (int, 'General', 0),
|
||||||
'SLACK_ENABLED': (int, 'Slack', 0),
|
'SLACK_ENABLED': (int, 'Slack', 0),
|
||||||
'SLACK_HOOK': (str, 'Slack', ''),
|
'SLACK_HOOK': (str, 'Slack', ''),
|
||||||
'SLACK_CHANNEL': (str, 'Slack', ''),
|
'SLACK_CHANNEL': (str, 'Slack', ''),
|
||||||
@@ -882,3 +883,9 @@ class Config(object):
|
|||||||
self.PMS_UPDATE_CHANNEL = 'beta'
|
self.PMS_UPDATE_CHANNEL = 'beta'
|
||||||
|
|
||||||
self.CONFIG_VERSION = 10
|
self.CONFIG_VERSION = 10
|
||||||
|
|
||||||
|
if self.CONFIG_VERSION == 10:
|
||||||
|
self.GIT_USER = 'Tautulli'
|
||||||
|
self.GIT_REPO = 'Tautulli'
|
||||||
|
|
||||||
|
self.CONFIG_VERSION = 11
|
||||||
|
@@ -1107,6 +1107,7 @@ class DataFactory(object):
|
|||||||
def get_poster_info(self, rating_key='', metadata=None):
|
def get_poster_info(self, rating_key='', metadata=None):
|
||||||
monitor_db = database.MonitorDatabase()
|
monitor_db = database.MonitorDatabase()
|
||||||
|
|
||||||
|
poster_key = ''
|
||||||
if str(rating_key).isdigit():
|
if str(rating_key).isdigit():
|
||||||
poster_key = rating_key
|
poster_key = rating_key
|
||||||
elif metadata:
|
elif metadata:
|
||||||
@@ -1118,6 +1119,7 @@ class DataFactory(object):
|
|||||||
poster_key = metadata['parent_rating_key']
|
poster_key = metadata['parent_rating_key']
|
||||||
|
|
||||||
poster_info = {}
|
poster_info = {}
|
||||||
|
|
||||||
if poster_key:
|
if poster_key:
|
||||||
try:
|
try:
|
||||||
query = 'SELECT poster_title, poster_url FROM poster_urls ' \
|
query = 'SELECT poster_title, poster_url FROM poster_urls ' \
|
||||||
@@ -1128,14 +1130,15 @@ class DataFactory(object):
|
|||||||
|
|
||||||
return poster_info
|
return poster_info
|
||||||
|
|
||||||
def set_poster_url(self, rating_key='', poster_title='', poster_url=''):
|
def set_poster_url(self, rating_key='', poster_title='', poster_url='', delete_hash=''):
|
||||||
monitor_db = database.MonitorDatabase()
|
monitor_db = database.MonitorDatabase()
|
||||||
|
|
||||||
if str(rating_key).isdigit():
|
if str(rating_key).isdigit():
|
||||||
keys = {'rating_key': int(rating_key)}
|
keys = {'rating_key': int(rating_key)}
|
||||||
|
|
||||||
values = {'poster_title': poster_title,
|
values = {'poster_title': poster_title,
|
||||||
'poster_url': poster_url}
|
'poster_url': poster_url,
|
||||||
|
'delete_hash': delete_hash}
|
||||||
|
|
||||||
monitor_db.upsert(table_name='poster_urls', key_dict=keys, value_dict=values)
|
monitor_db.upsert(table_name='poster_urls', key_dict=keys, value_dict=values)
|
||||||
|
|
||||||
@@ -1143,10 +1146,62 @@ class DataFactory(object):
|
|||||||
monitor_db = database.MonitorDatabase()
|
monitor_db = database.MonitorDatabase()
|
||||||
|
|
||||||
if rating_key:
|
if rating_key:
|
||||||
logger.info(u"Tautulli DataFactory :: Deleting poster_url for rating_key %s from the database." % rating_key)
|
poster_info = monitor_db.select_single('SELECT poster_title, delete_hash '
|
||||||
|
'FROM poster_urls WHERE rating_key = ?',
|
||||||
|
[rating_key])
|
||||||
|
if poster_info['delete_hash']:
|
||||||
|
helpers.delete_from_imgur(poster_info['delete_hash'], poster_info['poster_title'])
|
||||||
|
|
||||||
|
logger.info(u"Tautulli DataFactory :: Deleting poster_url for '%s' (rating_key %s) from the database."
|
||||||
|
% (poster_info['poster_title'], rating_key))
|
||||||
result = monitor_db.action('DELETE FROM poster_urls WHERE rating_key = ?', [rating_key])
|
result = monitor_db.action('DELETE FROM poster_urls WHERE rating_key = ?', [rating_key])
|
||||||
return True if result else False
|
return True if result else False
|
||||||
|
|
||||||
|
def get_lookup_info(self, rating_key='', metadata=None):
|
||||||
|
monitor_db = database.MonitorDatabase()
|
||||||
|
|
||||||
|
lookup_key = ''
|
||||||
|
if str(rating_key).isdigit():
|
||||||
|
lookup_key = rating_key
|
||||||
|
elif metadata:
|
||||||
|
if metadata['media_type'] in ('movie', 'show', 'artist'):
|
||||||
|
lookup_key = metadata['rating_key']
|
||||||
|
elif metadata['media_type'] in ('season', 'album'):
|
||||||
|
lookup_key = metadata['parent_rating_key']
|
||||||
|
elif metadata['media_type'] in ('episode', 'track'):
|
||||||
|
lookup_key = metadata['grandparent_rating_key']
|
||||||
|
|
||||||
|
lookup_info = {'tvmaze_id': '',
|
||||||
|
'themoviedb_id': ''}
|
||||||
|
|
||||||
|
if lookup_key:
|
||||||
|
try:
|
||||||
|
query = 'SELECT tvmaze_id FROM tvmaze_lookup ' \
|
||||||
|
'WHERE rating_key = ?'
|
||||||
|
tvmaze_info = monitor_db.select_single(query, args=[lookup_key])
|
||||||
|
if tvmaze_info:
|
||||||
|
lookup_info['tvmaze_id'] = tvmaze_info['tvmaze_id']
|
||||||
|
|
||||||
|
query = 'SELECT themoviedb_id FROM themoviedb_lookup ' \
|
||||||
|
'WHERE rating_key = ?'
|
||||||
|
themoviedb_info = monitor_db.select_single(query, args=[lookup_key])
|
||||||
|
if themoviedb_info:
|
||||||
|
lookup_info['themoviedb_id'] = themoviedb_info['themoviedb_id']
|
||||||
|
except Exception as e:
|
||||||
|
logger.warn(u"Tautulli DataFactory :: Unable to execute database query for get_lookup_info: %s." % e)
|
||||||
|
|
||||||
|
return lookup_info
|
||||||
|
|
||||||
|
def delete_lookup_info(self, rating_key='', title=''):
|
||||||
|
monitor_db = database.MonitorDatabase()
|
||||||
|
|
||||||
|
if rating_key:
|
||||||
|
logger.info(u"Tautulli DataFactory :: Deleting lookup info for '%s' (rating_key %s) from the database."
|
||||||
|
% (title, rating_key))
|
||||||
|
result_tvmaze = monitor_db.action('DELETE FROM tvmaze_lookup WHERE rating_key = ?', [rating_key])
|
||||||
|
result_themoviedb = monitor_db.action('DELETE FROM themoviedb_lookup WHERE rating_key = ?', [rating_key])
|
||||||
|
return True if (result_tvmaze or result_themoviedb) else False
|
||||||
|
|
||||||
def get_search_query(self, rating_key=''):
|
def get_search_query(self, rating_key=''):
|
||||||
monitor_db = database.MonitorDatabase()
|
monitor_db = database.MonitorDatabase()
|
||||||
|
|
||||||
|
@@ -680,21 +680,21 @@ def anon_url(*url):
|
|||||||
"""
|
"""
|
||||||
return '' if None in url else '%s%s' % (plexpy.CONFIG.ANON_REDIRECT, ''.join(str(s) for s in url))
|
return '' if None in url else '%s%s' % (plexpy.CONFIG.ANON_REDIRECT, ''.join(str(s) for s in url))
|
||||||
|
|
||||||
def uploadToImgur(imgPath, imgTitle=''):
|
def upload_to_imgur(imgPath, imgTitle=''):
|
||||||
""" Uploads an image to Imgur """
|
""" Uploads an image to Imgur """
|
||||||
client_id = plexpy.CONFIG.IMGUR_CLIENT_ID
|
client_id = plexpy.CONFIG.IMGUR_CLIENT_ID
|
||||||
img_url = ''
|
img_url = delete_hash = ''
|
||||||
|
|
||||||
if not client_id:
|
if not client_id:
|
||||||
logger.error(u"Tautulli Helpers :: Cannot upload poster to Imgur. No Imgur client id specified in the settings.")
|
logger.error(u"Tautulli Helpers :: Cannot upload poster to Imgur. No Imgur client id specified in the settings.")
|
||||||
return img_url
|
return img_url, delete_hash
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(imgPath, 'rb') as imgFile:
|
with open(imgPath, 'rb') as imgFile:
|
||||||
img = imgFile.read()
|
img = imgFile.read()
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
logger.error(u"Tautulli Helpers :: Unable to read image file for Imgur: %s" % e)
|
logger.error(u"Tautulli Helpers :: Unable to read image file for Imgur: %s" % e)
|
||||||
return img_url
|
return img_url, delete_hash
|
||||||
|
|
||||||
headers = {'Authorization': 'Client-ID %s' % client_id}
|
headers = {'Authorization': 'Client-ID %s' % client_id}
|
||||||
data = {'type': 'base64',
|
data = {'type': 'base64',
|
||||||
@@ -703,13 +703,15 @@ def uploadToImgur(imgPath, imgTitle=''):
|
|||||||
data['title'] = imgTitle.encode('utf-8')
|
data['title'] = imgTitle.encode('utf-8')
|
||||||
data['name'] = imgTitle.encode('utf-8') + '.jpg'
|
data['name'] = imgTitle.encode('utf-8') + '.jpg'
|
||||||
|
|
||||||
response, err_msg, req_msg = request.request_response2('https://api.imgur.com/3/image', 'POST', headers=headers, data=data)
|
response, err_msg, req_msg = request.request_response2('https://api.imgur.com/3/image', 'POST',
|
||||||
|
headers=headers, data=data)
|
||||||
|
|
||||||
if response and not err_msg:
|
if response and not err_msg:
|
||||||
t = '\'' + imgTitle + '\' ' if imgTitle else ''
|
t = '\'' + imgTitle + '\' ' if imgTitle else ''
|
||||||
logger.debug(u"Tautulli Helpers :: Image {}uploaded to Imgur.".format(t))
|
logger.debug(u"Tautulli Helpers :: Image {}uploaded to Imgur.".format(t))
|
||||||
img_url = response.json().get('data').get('link', '').replace('http://', 'https://')
|
imgur_response_data = response.json().get('data')
|
||||||
|
img_url = imgur_response_data.get('link', '').replace('http://', 'https://')
|
||||||
|
delete_hash = imgur_response_data.get('deletehash', '')
|
||||||
else:
|
else:
|
||||||
if err_msg:
|
if err_msg:
|
||||||
logger.error(u"Tautulli Helpers :: Unable to upload image to Imgur: {}".format(err_msg))
|
logger.error(u"Tautulli Helpers :: Unable to upload image to Imgur: {}".format(err_msg))
|
||||||
@@ -719,7 +721,27 @@ def uploadToImgur(imgPath, imgTitle=''):
|
|||||||
if req_msg:
|
if req_msg:
|
||||||
logger.debug(u"Tautulli Helpers :: Request response: {}".format(req_msg))
|
logger.debug(u"Tautulli Helpers :: Request response: {}".format(req_msg))
|
||||||
|
|
||||||
return img_url
|
return img_url, delete_hash
|
||||||
|
|
||||||
|
def delete_from_imgur(delete_hash, imgTitle=''):
|
||||||
|
""" Deletes an image from Imgur """
|
||||||
|
client_id = plexpy.CONFIG.IMGUR_CLIENT_ID
|
||||||
|
|
||||||
|
headers = {'Authorization': 'Client-ID %s' % client_id}
|
||||||
|
|
||||||
|
response, err_msg, req_msg = request.request_response2('https://api.imgur.com/3/image/%s' % delete_hash, 'DELETE',
|
||||||
|
headers=headers)
|
||||||
|
|
||||||
|
if response and not err_msg:
|
||||||
|
t = '\'' + imgTitle + '\' ' if imgTitle else ''
|
||||||
|
logger.debug(u"Tautulli Helpers :: Image {}deleted from Imgur.".format(t))
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
if err_msg:
|
||||||
|
logger.error(u"Tautulli Helpers :: Unable to delete image from Imgur: {}".format(err_msg))
|
||||||
|
else:
|
||||||
|
logger.error(u"Tautulli Helpers :: Unable to delete image from Imgur.")
|
||||||
|
return False
|
||||||
|
|
||||||
def cache_image(url, image=None):
|
def cache_image(url, image=None):
|
||||||
"""
|
"""
|
||||||
|
@@ -65,7 +65,7 @@ class HTTPHandler(object):
|
|||||||
Output: list
|
Output: list
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.uri = uri
|
self.uri = uri.encode('utf-8')
|
||||||
self.request_type = request_type.upper()
|
self.request_type = request_type.upper()
|
||||||
self.output_format = output_format.lower()
|
self.output_format = output_format.lower()
|
||||||
self.return_type = return_type
|
self.return_type = return_type
|
||||||
|
@@ -82,11 +82,18 @@ def add_notifier_each(notifier_id=None, notify_action=None, stream_data=None, ti
|
|||||||
# Check if any notification agents have notifications enabled for the action
|
# Check if any notification agents have notifications enabled for the action
|
||||||
notifiers_enabled = notifiers.get_notifiers(notify_action=notify_action)
|
notifiers_enabled = notifiers.get_notifiers(notify_action=notify_action)
|
||||||
|
|
||||||
|
# Check if the watched notifications has already been sent
|
||||||
|
if stream_data and notify_action == 'on_watched':
|
||||||
|
watched_notifiers = [d['notifier_id'] for d in get_notify_state(session=stream_data)]
|
||||||
|
notifiers_enabled = [n for n in notifiers_enabled if n['id'] not in watched_notifiers]
|
||||||
|
|
||||||
if notifiers_enabled and not manual_trigger:
|
if notifiers_enabled and not manual_trigger:
|
||||||
# Check if notification conditions are satisfied
|
# Check if notification conditions are satisfied
|
||||||
conditions = notify_conditions(notify_action=notify_action,
|
conditions = notify_conditions(notify_action=notify_action,
|
||||||
stream_data=stream_data,
|
stream_data=stream_data,
|
||||||
timeline_data=timeline_data)
|
timeline_data=timeline_data)
|
||||||
|
else:
|
||||||
|
conditions = True
|
||||||
|
|
||||||
if notifiers_enabled and (manual_trigger or conditions):
|
if notifiers_enabled and (manual_trigger or conditions):
|
||||||
if stream_data or timeline_data:
|
if stream_data or timeline_data:
|
||||||
@@ -277,13 +284,13 @@ def notify_custom_conditions(notifier_id=None, parameters=None):
|
|||||||
evaluated_conditions.append(any(c in parameter_value for c in values))
|
evaluated_conditions.append(any(c in parameter_value for c in values))
|
||||||
|
|
||||||
elif operator == 'does not contain':
|
elif operator == 'does not contain':
|
||||||
evaluated_conditions.append(any(c not in parameter_value for c in values))
|
evaluated_conditions.append(all(c not in parameter_value for c in values))
|
||||||
|
|
||||||
elif operator == 'is':
|
elif operator == 'is':
|
||||||
evaluated_conditions.append(any(parameter_value == c for c in values))
|
evaluated_conditions.append(any(parameter_value == c for c in values))
|
||||||
|
|
||||||
elif operator == 'is not':
|
elif operator == 'is not':
|
||||||
evaluated_conditions.append(any(parameter_value != c for c in values))
|
evaluated_conditions.append(all(parameter_value != c for c in values))
|
||||||
|
|
||||||
elif operator == 'begins with':
|
elif operator == 'begins with':
|
||||||
evaluated_conditions.append(parameter_value.startswith(tuple(values)))
|
evaluated_conditions.append(parameter_value.startswith(tuple(values)))
|
||||||
@@ -318,13 +325,6 @@ def notify_custom_conditions(notifier_id=None, parameters=None):
|
|||||||
|
|
||||||
|
|
||||||
def notify(notifier_id=None, notify_action=None, stream_data=None, timeline_data=None, parameters=None, **kwargs):
|
def notify(notifier_id=None, notify_action=None, stream_data=None, timeline_data=None, parameters=None, **kwargs):
|
||||||
# Double check again if the notification has already been sent
|
|
||||||
if stream_data and \
|
|
||||||
any(d['notifier_id'] == notifier_id and d['notify_action'] == notify_action
|
|
||||||
for d in get_notify_state(session=stream_data)):
|
|
||||||
# Return if the notification has already been sent
|
|
||||||
return
|
|
||||||
|
|
||||||
logger.info(u"Tautulli NotificationHandler :: Preparing notifications for notifier_id %s." % notifier_id)
|
logger.info(u"Tautulli NotificationHandler :: Preparing notifications for notifier_id %s." % notifier_id)
|
||||||
|
|
||||||
notifier_config = notifiers.get_notifier_config(notifier_id=notifier_id)
|
notifier_config = notifiers.get_notifier_config(notifier_id=notifier_id)
|
||||||
@@ -531,7 +531,7 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, m
|
|||||||
notify_params['trakt_url'] = 'https://trakt.tv/search/tvdb/' + notify_params['thetvdb_id'] + '?id_type=show'
|
notify_params['trakt_url'] = 'https://trakt.tv/search/tvdb/' + notify_params['thetvdb_id'] + '?id_type=show'
|
||||||
|
|
||||||
elif 'thetvdbdvdorder://' in notify_params['guid']:
|
elif 'thetvdbdvdorder://' in notify_params['guid']:
|
||||||
notify_params['thetvdb_id'] = notify_params['guid'].split('thetvdbdvdorder://')[1].split('/')[0]
|
notify_params['thetvdb_id'] = notify_params['guid'].split('thetvdbdvdorder://')[1].split('/')[0].split('?')[0]
|
||||||
notify_params['thetvdb_url'] = 'https://thetvdb.com/?tab=series&id=' + notify_params['thetvdb_id']
|
notify_params['thetvdb_url'] = 'https://thetvdb.com/?tab=series&id=' + notify_params['thetvdb_id']
|
||||||
notify_params['trakt_url'] = 'https://trakt.tv/search/tvdb/' + notify_params['thetvdb_id'] + '?id_type=show'
|
notify_params['trakt_url'] = 'https://trakt.tv/search/tvdb/' + notify_params['thetvdb_id'] + '?id_type=show'
|
||||||
|
|
||||||
@@ -542,7 +542,7 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, m
|
|||||||
notify_params['trakt_url'] = 'https://trakt.tv/search/tmdb/' + notify_params['themoviedb_id'] + '?id_type=movie'
|
notify_params['trakt_url'] = 'https://trakt.tv/search/tmdb/' + notify_params['themoviedb_id'] + '?id_type=movie'
|
||||||
|
|
||||||
elif notify_params['media_type'] in ('show', 'season', 'episode'):
|
elif notify_params['media_type'] in ('show', 'season', 'episode'):
|
||||||
notify_params['themoviedb_id'] = notify_params['guid'].split('themoviedb://')[1].split('/')[0]
|
notify_params['themoviedb_id'] = notify_params['guid'].split('themoviedb://')[1].split('/')[0].split('?')[0]
|
||||||
notify_params['themoviedb_url'] = 'https://www.themoviedb.org/tv/' + notify_params['themoviedb_id']
|
notify_params['themoviedb_url'] = 'https://www.themoviedb.org/tv/' + notify_params['themoviedb_id']
|
||||||
notify_params['trakt_url'] = 'https://trakt.tv/search/tmdb/' + notify_params['themoviedb_id'] + '?id_type=show'
|
notify_params['trakt_url'] = 'https://trakt.tv/search/tmdb/' + notify_params['themoviedb_id'] + '?id_type=show'
|
||||||
|
|
||||||
@@ -562,7 +562,14 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, m
|
|||||||
notify_params['imdb_url'] = 'https://www.imdb.com/title/' + themoveidb_json['imdb_id']
|
notify_params['imdb_url'] = 'https://www.imdb.com/title/' + themoveidb_json['imdb_id']
|
||||||
|
|
||||||
elif notify_params.get('thetvdb_id') or notify_params.get('imdb_id'):
|
elif notify_params.get('thetvdb_id') or notify_params.get('imdb_id'):
|
||||||
themoviedb_info = lookup_themoviedb_by_id(rating_key=rating_key,
|
if notify_params['media_type'] in ('episode', 'track'):
|
||||||
|
lookup_key = notify_params['grandparent_rating_key']
|
||||||
|
elif notify_params['media_type'] in ('season', 'album'):
|
||||||
|
lookup_key = notify_params['parent_rating_key']
|
||||||
|
else:
|
||||||
|
lookup_key = rating_key
|
||||||
|
|
||||||
|
themoviedb_info = lookup_themoviedb_by_id(rating_key=lookup_key,
|
||||||
thetvdb_id=notify_params.get('thetvdb_id'),
|
thetvdb_id=notify_params.get('thetvdb_id'),
|
||||||
imdb_id=notify_params.get('imdb_id'))
|
imdb_id=notify_params.get('imdb_id'))
|
||||||
notify_params.update(themoviedb_info)
|
notify_params.update(themoviedb_info)
|
||||||
@@ -570,7 +577,14 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, m
|
|||||||
# Get TVmaze info (for tv shows only)
|
# Get TVmaze info (for tv shows only)
|
||||||
if plexpy.CONFIG.TVMAZE_LOOKUP:
|
if plexpy.CONFIG.TVMAZE_LOOKUP:
|
||||||
if notify_params['media_type'] in ('show', 'season', 'episode') and (notify_params.get('thetvdb_id') or notify_params.get('imdb_id')):
|
if notify_params['media_type'] in ('show', 'season', 'episode') and (notify_params.get('thetvdb_id') or notify_params.get('imdb_id')):
|
||||||
tvmaze_info = lookup_tvmaze_by_id(rating_key=rating_key,
|
if notify_params['media_type'] in ('episode', 'track'):
|
||||||
|
lookup_key = notify_params['grandparent_rating_key']
|
||||||
|
elif notify_params['media_type'] in ('season', 'album'):
|
||||||
|
lookup_key = notify_params['parent_rating_key']
|
||||||
|
else:
|
||||||
|
lookup_key = rating_key
|
||||||
|
|
||||||
|
tvmaze_info = lookup_tvmaze_by_id(rating_key=lookup_key,
|
||||||
thetvdb_id=notify_params.get('thetvdb_id'),
|
thetvdb_id=notify_params.get('thetvdb_id'),
|
||||||
imdb_id=notify_params.get('imdb_id'))
|
imdb_id=notify_params.get('imdb_id'))
|
||||||
notify_params.update(tvmaze_info)
|
notify_params.update(tvmaze_info)
|
||||||
@@ -1039,14 +1053,17 @@ def get_poster_info(poster_thumb, poster_key, poster_title):
|
|||||||
raise Exception(u'PMS image request failed')
|
raise Exception(u'PMS image request failed')
|
||||||
|
|
||||||
# Upload poster_thumb to Imgur and get link
|
# Upload poster_thumb to Imgur and get link
|
||||||
poster_url = helpers.uploadToImgur(poster_file, poster_title)
|
poster_url, delete_hash = helpers.upload_to_imgur(poster_file, poster_title)
|
||||||
|
|
||||||
if poster_url:
|
if poster_url:
|
||||||
# Create poster info
|
# Create poster info
|
||||||
poster_info = {'poster_title': poster_title, 'poster_url': poster_url}
|
poster_info = {'poster_title': poster_title, 'poster_url': poster_url}
|
||||||
|
|
||||||
# Save the poster url in the database
|
# Save the poster url in the database
|
||||||
data_factory.set_poster_url(rating_key=poster_key, poster_title=poster_title, poster_url=poster_url)
|
data_factory.set_poster_url(rating_key=poster_key,
|
||||||
|
poster_title=poster_title,
|
||||||
|
poster_url=poster_url,
|
||||||
|
delete_hash=delete_hash)
|
||||||
|
|
||||||
# Delete the cached poster
|
# Delete the cached poster
|
||||||
os.remove(poster_file)
|
os.remove(poster_file)
|
||||||
@@ -1196,6 +1213,17 @@ def get_themoviedb_info(rating_key=None, media_type=None, themoviedb_id=None):
|
|||||||
|
|
||||||
if response and not err_msg:
|
if response and not err_msg:
|
||||||
themoviedb_json = response.json()
|
themoviedb_json = response.json()
|
||||||
|
themoviedb_id = themoviedb_json['id']
|
||||||
|
themoviedb_url = 'https://www.themoviedb.org/{}/{}'.format(media_type, themoviedb_id)
|
||||||
|
|
||||||
|
keys = {'themoviedb_id': themoviedb_id}
|
||||||
|
themoviedb_info = {'rating_key': rating_key,
|
||||||
|
'imdb_id': themoviedb_json.get('imdb_id'),
|
||||||
|
'themoviedb_url': themoviedb_url,
|
||||||
|
'themoviedb_json': json.dumps(themoviedb_json)
|
||||||
|
}
|
||||||
|
|
||||||
|
db.upsert(table_name='themoviedb_lookup', key_dict=keys, value_dict=themoviedb_info)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if err_msg:
|
if err_msg:
|
||||||
|
@@ -321,7 +321,7 @@ def available_notification_actions():
|
|||||||
'name': 'on_plexpyupdate',
|
'name': 'on_plexpyupdate',
|
||||||
'description': 'Trigger a notification when an update for the Tautulli is available.',
|
'description': 'Trigger a notification when an update for the Tautulli is available.',
|
||||||
'subject': 'Tautulli ({server_name})',
|
'subject': 'Tautulli ({server_name})',
|
||||||
'body': 'An update is available for Tautulli (version {plexpy_update_version}).',
|
'body': 'An update is available for Tautulli (version {tautulli_update_version}).',
|
||||||
'icon': 'fa-refresh',
|
'icon': 'fa-refresh',
|
||||||
'media_types': ('server',)
|
'media_types': ('server',)
|
||||||
}
|
}
|
||||||
@@ -896,7 +896,8 @@ class ANDROIDAPP(Notifier):
|
|||||||
'The content of your notifications will be sent unencrypted!</strong><br>' \
|
'The content of your notifications will be sent unencrypted!</strong><br>' \
|
||||||
'Please install the library to encrypt the notification contents. ' \
|
'Please install the library to encrypt the notification contents. ' \
|
||||||
'Instructions can be found in the ' \
|
'Instructions can be found in the ' \
|
||||||
'<a href="' + helpers.anon_url('https://github.com/%s/plexpy/wiki/Frequently-Asked-Questions-(FAQ)#notifications-pycryptodome' % plexpy.CONFIG.GIT_USER) + '" target="_blank">FAQ</a>.',
|
'<a href="' + helpers.anon_url('https://github.com/%s/%s-Wiki/wiki/Frequently-Asked-Questions#notifications-pycryptodome'
|
||||||
|
% (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO)) + '" target="_blank">FAQ</a>.',
|
||||||
'input_type': 'help'
|
'input_type': 'help'
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
|
@@ -1,2 +1,2 @@
|
|||||||
PLEXPY_BRANCH = "beta"
|
PLEXPY_BRANCH = "beta"
|
||||||
PLEXPY_RELEASE_VERSION = "v2.0.17-beta"
|
PLEXPY_RELEASE_VERSION = "v2.0.18-beta"
|
||||||
|
@@ -136,7 +136,9 @@ def checkGithub(auto_update=False):
|
|||||||
|
|
||||||
# Get the latest version available from github
|
# Get the latest version available from github
|
||||||
logger.info('Retrieving latest version information from GitHub')
|
logger.info('Retrieving latest version information from GitHub')
|
||||||
url = 'https://api.github.com/repos/%s/plexpy/commits/%s' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_BRANCH)
|
url = 'https://api.github.com/repos/%s/%s/commits/%s' % (plexpy.CONFIG.GIT_USER,
|
||||||
|
plexpy.CONFIG.GIT_REPO,
|
||||||
|
plexpy.CONFIG.GIT_BRANCH)
|
||||||
if plexpy.CONFIG.GIT_TOKEN: url = url + '?access_token=%s' % plexpy.CONFIG.GIT_TOKEN
|
if plexpy.CONFIG.GIT_TOKEN: url = url + '?access_token=%s' % plexpy.CONFIG.GIT_TOKEN
|
||||||
version = request.request_json(url, timeout=20, validator=lambda x: type(x) == dict)
|
version = request.request_json(url, timeout=20, validator=lambda x: type(x) == dict)
|
||||||
|
|
||||||
@@ -157,7 +159,10 @@ def checkGithub(auto_update=False):
|
|||||||
return plexpy.LATEST_VERSION
|
return plexpy.LATEST_VERSION
|
||||||
|
|
||||||
logger.info('Comparing currently installed version with latest GitHub version')
|
logger.info('Comparing currently installed version with latest GitHub version')
|
||||||
url = 'https://api.github.com/repos/%s/plexpy/compare/%s...%s' % (plexpy.CONFIG.GIT_USER, plexpy.LATEST_VERSION, plexpy.CURRENT_VERSION)
|
url = 'https://api.github.com/repos/%s/%s/compare/%s...%s' % (plexpy.CONFIG.GIT_USER,
|
||||||
|
plexpy.CONFIG.GIT_REPO,
|
||||||
|
plexpy.LATEST_VERSION,
|
||||||
|
plexpy.CURRENT_VERSION)
|
||||||
if plexpy.CONFIG.GIT_TOKEN: url = url + '?access_token=%s' % plexpy.CONFIG.GIT_TOKEN
|
if plexpy.CONFIG.GIT_TOKEN: url = url + '?access_token=%s' % plexpy.CONFIG.GIT_TOKEN
|
||||||
commits = request.request_json(url, timeout=20, whitelist_status_code=404, validator=lambda x: type(x) == dict)
|
commits = request.request_json(url, timeout=20, whitelist_status_code=404, validator=lambda x: type(x) == dict)
|
||||||
|
|
||||||
@@ -175,7 +180,7 @@ def checkGithub(auto_update=False):
|
|||||||
if plexpy.COMMITS_BEHIND > 0:
|
if plexpy.COMMITS_BEHIND > 0:
|
||||||
logger.info('New version is available. You are %s commits behind' % plexpy.COMMITS_BEHIND)
|
logger.info('New version is available. You are %s commits behind' % plexpy.COMMITS_BEHIND)
|
||||||
|
|
||||||
url = 'https://api.github.com/repos/%s/plexpy/releases' % plexpy.CONFIG.GIT_USER
|
url = 'https://api.github.com/repos/%s/%s/releases' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO)
|
||||||
releases = request.request_json(url, timeout=20, whitelist_status_code=404, validator=lambda x: type(x) == list)
|
releases = request.request_json(url, timeout=20, whitelist_status_code=404, validator=lambda x: type(x) == list)
|
||||||
|
|
||||||
if releases is None:
|
if releases is None:
|
||||||
|
@@ -220,7 +220,7 @@ class AuthController(object):
|
|||||||
|
|
||||||
# Save login to the database
|
# Save login to the database
|
||||||
ip_address = cherrypy.request.headers.get('X-Forwarded-For', cherrypy.request.headers.get('Remote-Addr'))
|
ip_address = cherrypy.request.headers.get('X-Forwarded-For', cherrypy.request.headers.get('Remote-Addr'))
|
||||||
host = cherrypy.request.headers.get('Origin')
|
host = cherrypy.request.headers.get('Host', cherrypy.request.headers.get('Origin'))
|
||||||
user_agent = cherrypy.request.headers.get('User-Agent')
|
user_agent = cherrypy.request.headers.get('User-Agent')
|
||||||
|
|
||||||
Users().set_user_login(user_id=user_id,
|
Users().set_user_login(user_id=user_id,
|
||||||
|
@@ -62,7 +62,7 @@ def serve_template(templatename, **kwargs):
|
|||||||
|
|
||||||
http_root = plexpy.HTTP_ROOT
|
http_root = plexpy.HTTP_ROOT
|
||||||
server_name = plexpy.CONFIG.PMS_NAME
|
server_name = plexpy.CONFIG.PMS_NAME
|
||||||
cache_param = '?' + plexpy.CURRENT_VERSION or common.VERSION_NUMBER
|
cache_param = '?' + (plexpy.CURRENT_VERSION or common.VERSION_NUMBER)
|
||||||
|
|
||||||
_session = get_session_info()
|
_session = get_session_info()
|
||||||
|
|
||||||
@@ -512,8 +512,6 @@ class WebInterface(object):
|
|||||||
|
|
||||||
Optional parameters:
|
Optional parameters:
|
||||||
custom_thumb (str): The URL for the custom library thumbnail
|
custom_thumb (str): The URL for the custom library thumbnail
|
||||||
do_notify (int): 0 or 1
|
|
||||||
do_notify_created (int): 0 or 1
|
|
||||||
keep_history (int): 0 or 1
|
keep_history (int): 0 or 1
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -624,7 +622,7 @@ class WebInterface(object):
|
|||||||
|
|
||||||
Optional parameters:
|
Optional parameters:
|
||||||
section_type (str): "movie", "show", "artist", "photo"
|
section_type (str): "movie", "show", "artist", "photo"
|
||||||
order_column (str): "added_at", "title", "container", "bitrate", "video_codec",
|
order_column (str): "added_at", "sort_title", "container", "bitrate", "video_codec",
|
||||||
"video_resolution", "video_framerate", "audio_codec", "audio_channels",
|
"video_resolution", "video_framerate", "audio_codec", "audio_channels",
|
||||||
"file_size", "last_played", "play_count"
|
"file_size", "last_played", "play_count"
|
||||||
order_dir (str): "desc" or "asc"
|
order_dir (str): "desc" or "asc"
|
||||||
@@ -981,7 +979,7 @@ class WebInterface(object):
|
|||||||
else:
|
else:
|
||||||
return {'message': 'no data received'}
|
return {'message': 'no data received'}
|
||||||
else:
|
else:
|
||||||
return {'message': 'Cannot refresh library while getting file sizes.'}
|
return {'message': 'Cannot delete media info cache while getting file sizes.'}
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
@cherrypy.tools.json_out()
|
@cherrypy.tools.json_out()
|
||||||
@@ -1130,9 +1128,8 @@ class WebInterface(object):
|
|||||||
Optional paramters:
|
Optional paramters:
|
||||||
friendly_name(str): The friendly name of the user
|
friendly_name(str): The friendly name of the user
|
||||||
custom_thumb (str): The URL for the custom user thumbnail
|
custom_thumb (str): The URL for the custom user thumbnail
|
||||||
do_notify (int): 0 or 1
|
|
||||||
do_notify_created (int): 0 or 1
|
|
||||||
keep_history (int): 0 or 1
|
keep_history (int): 0 or 1
|
||||||
|
allow_guest (int): 0 or 1
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
None
|
None
|
||||||
@@ -2611,7 +2608,8 @@ class WebInterface(object):
|
|||||||
"tv_watched_percent": plexpy.CONFIG.TV_WATCHED_PERCENT,
|
"tv_watched_percent": plexpy.CONFIG.TV_WATCHED_PERCENT,
|
||||||
"music_watched_percent": plexpy.CONFIG.MUSIC_WATCHED_PERCENT,
|
"music_watched_percent": plexpy.CONFIG.MUSIC_WATCHED_PERCENT,
|
||||||
"themoviedb_lookup": checked(plexpy.CONFIG.THEMOVIEDB_LOOKUP),
|
"themoviedb_lookup": checked(plexpy.CONFIG.THEMOVIEDB_LOOKUP),
|
||||||
"tvmaze_lookup": checked(plexpy.CONFIG.TVMAZE_LOOKUP)
|
"tvmaze_lookup": checked(plexpy.CONFIG.TVMAZE_LOOKUP),
|
||||||
|
"show_advanced_settings": plexpy.CONFIG.SHOW_ADVANCED_SETTINGS
|
||||||
}
|
}
|
||||||
|
|
||||||
return serve_template(templatename="settings.html", title="Settings", config=config, kwargs=kwargs)
|
return serve_template(templatename="settings.html", title="Settings", config=config, kwargs=kwargs)
|
||||||
@@ -3014,12 +3012,14 @@ class WebInterface(object):
|
|||||||
Pass all the config options for the agent with the agent prefix:
|
Pass all the config options for the agent with the agent prefix:
|
||||||
e.g. For Telegram: telegram_bot_token
|
e.g. For Telegram: telegram_bot_token
|
||||||
telegram_chat_id
|
telegram_chat_id
|
||||||
disable_web_preview
|
telegram_disable_web_preview
|
||||||
html_support
|
telegram_html_support
|
||||||
incl_poster
|
telegram_incl_poster
|
||||||
incl_subject
|
telegram_incl_subject
|
||||||
Notify actions with 'trigger_' prefix (trigger_on_play, trigger_on_stop, etc.),
|
Notify actions (int): 0 or 1,
|
||||||
and notify text with 'text_' prefix (text_on_play_subject, text_on_play_body, etc.) are optional.
|
e.g. on_play, on_stop, etc.
|
||||||
|
Notify text (str):
|
||||||
|
e.g. on_play_subject, on_play_body, etc.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
None
|
None
|
||||||
@@ -3591,6 +3591,8 @@ class WebInterface(object):
|
|||||||
if metadata:
|
if metadata:
|
||||||
poster_info = data_factory.get_poster_info(metadata=metadata)
|
poster_info = data_factory.get_poster_info(metadata=metadata)
|
||||||
metadata.update(poster_info)
|
metadata.update(poster_info)
|
||||||
|
lookup_info = data_factory.get_lookup_info(metadata=metadata)
|
||||||
|
metadata.update(lookup_info)
|
||||||
else:
|
else:
|
||||||
pms_connect = pmsconnect.PmsConnect()
|
pms_connect = pmsconnect.PmsConnect()
|
||||||
metadata = pms_connect.get_metadata_details(rating_key=rating_key)
|
metadata = pms_connect.get_metadata_details(rating_key=rating_key)
|
||||||
@@ -3598,6 +3600,8 @@ class WebInterface(object):
|
|||||||
data_factory = datafactory.DataFactory()
|
data_factory = datafactory.DataFactory()
|
||||||
poster_info = data_factory.get_poster_info(metadata=metadata)
|
poster_info = data_factory.get_poster_info(metadata=metadata)
|
||||||
metadata.update(poster_info)
|
metadata.update(poster_info)
|
||||||
|
lookup_info = data_factory.get_lookup_info(metadata=metadata)
|
||||||
|
metadata.update(lookup_info)
|
||||||
|
|
||||||
if metadata:
|
if metadata:
|
||||||
if metadata['section_id'] and not allow_session_library(metadata['section_id']):
|
if metadata['section_id'] and not allow_session_library(metadata['section_id']):
|
||||||
@@ -3882,15 +3886,60 @@ class WebInterface(object):
|
|||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
@cherrypy.tools.json_out()
|
@cherrypy.tools.json_out()
|
||||||
@requireAuth(member_of("admin"))
|
@requireAuth(member_of("admin"))
|
||||||
def delete_poster_url(self, rating_key='', **kwargs):
|
@addtoapi()
|
||||||
|
def delete_imgur_poster(self, rating_key='', **kwargs):
|
||||||
|
""" Delete the Imgur poster.
|
||||||
|
|
||||||
|
```
|
||||||
|
Required parameters:
|
||||||
|
rating_key (int): 1234
|
||||||
|
(Note: Must be the movie, show, season, artist, or album rating key)
|
||||||
|
Optional parameters:
|
||||||
|
None
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
json:
|
||||||
|
{"result": "success",
|
||||||
|
"message": "Deleted Imgur poster."}
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
|
||||||
data_factory = datafactory.DataFactory()
|
data_factory = datafactory.DataFactory()
|
||||||
result = data_factory.delete_poster_url(rating_key=rating_key)
|
result = data_factory.delete_poster_url(rating_key=rating_key)
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
return {'result': 'success', 'message': 'Deleted Imgur poster url.'}
|
return {'result': 'success', 'message': 'Deleted Imgur poster.'}
|
||||||
else:
|
else:
|
||||||
return {'result': 'error', 'message': 'Failed to delete Imgur poster url.'}
|
return {'result': 'error', 'message': 'Failed to delete Imgur poster.'}
|
||||||
|
|
||||||
|
@cherrypy.expose
|
||||||
|
@cherrypy.tools.json_out()
|
||||||
|
@requireAuth(member_of("admin"))
|
||||||
|
@addtoapi()
|
||||||
|
def delete_lookup_info(self, rating_key='', title='', **kwargs):
|
||||||
|
""" Delete the 3rd party API lookup info.
|
||||||
|
|
||||||
|
```
|
||||||
|
Required parameters:
|
||||||
|
rating_key (int): 1234
|
||||||
|
(Note: Must be the movie, show, or artist rating key)
|
||||||
|
Optional parameters:
|
||||||
|
None
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
json:
|
||||||
|
{"result": "success",
|
||||||
|
"message": "Deleted lookup info."}
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
|
||||||
|
data_factory = datafactory.DataFactory()
|
||||||
|
result = data_factory.delete_lookup_info(rating_key=rating_key, title=title)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
return {'result': 'success', 'message': 'Deleted lookup info.'}
|
||||||
|
else:
|
||||||
|
return {'result': 'error', 'message': 'Failed to delete lookup info.'}
|
||||||
|
|
||||||
|
|
||||||
##### Search #####
|
##### Search #####
|
||||||
@@ -4116,11 +4165,15 @@ class WebInterface(object):
|
|||||||
],
|
],
|
||||||
"added_at": "1461572396",
|
"added_at": "1461572396",
|
||||||
"art": "/library/metadata/1219/art/1462175063",
|
"art": "/library/metadata/1219/art/1462175063",
|
||||||
|
"audience_rating": "8",
|
||||||
|
"banner": "/library/metadata/1219/banner/1462175063",
|
||||||
|
"collections": [],
|
||||||
"content_rating": "TV-MA",
|
"content_rating": "TV-MA",
|
||||||
"directors": [
|
"directors": [
|
||||||
"Jeremy Podeswa"
|
"Jeremy Podeswa"
|
||||||
],
|
],
|
||||||
"duration": "2998290",
|
"duration": "2998290",
|
||||||
|
"full_title": "Game of Thrones - The Red Woman",
|
||||||
"genres": [
|
"genres": [
|
||||||
"Adventure",
|
"Adventure",
|
||||||
"Drama",
|
"Drama",
|
||||||
@@ -4134,6 +4187,74 @@ class WebInterface(object):
|
|||||||
"last_viewed_at": "1462165717",
|
"last_viewed_at": "1462165717",
|
||||||
"library_name": "TV Shows",
|
"library_name": "TV Shows",
|
||||||
"media_index": "1",
|
"media_index": "1",
|
||||||
|
"media_info": [
|
||||||
|
{
|
||||||
|
"aspect_ratio": "1.78",
|
||||||
|
"audio_channel_layout": "5.1",
|
||||||
|
"audio_channels": "6",
|
||||||
|
"audio_codec": "ac3",
|
||||||
|
"audio_profile": "",
|
||||||
|
"bitrate": "10617",
|
||||||
|
"container": "mkv",
|
||||||
|
"height": "1078",
|
||||||
|
"id": "257925",
|
||||||
|
"optimized_version": 0,
|
||||||
|
"parts": [
|
||||||
|
{
|
||||||
|
"file": "/media/TV Shows/Game of Thrones/Season 06/Game of Thrones - S06E01 - The Red Woman.mkv",
|
||||||
|
"file_size": "3979115377",
|
||||||
|
"id": "274169",
|
||||||
|
"indexes": 1,
|
||||||
|
"streams": [
|
||||||
|
{
|
||||||
|
"id": "511663",
|
||||||
|
"type": "1",
|
||||||
|
"video_bit_depth": "8",
|
||||||
|
"video_bitrate": "10233",
|
||||||
|
"video_codec": "h264",
|
||||||
|
"video_codec_level": "41",
|
||||||
|
"video_frame_rate": "23.976",
|
||||||
|
"video_height": "1078",
|
||||||
|
"video_language": "",
|
||||||
|
"video_language_code": "",
|
||||||
|
"video_profile": "high",
|
||||||
|
"video_ref_frames": "4",
|
||||||
|
"video_width": "1920"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"audio_bitrate": "384",
|
||||||
|
"audio_bitrate_mode": "",
|
||||||
|
"audio_channel_layout": "5.1(side)",
|
||||||
|
"audio_channels": "6",
|
||||||
|
"audio_codec": "ac3",
|
||||||
|
"audio_language": "",
|
||||||
|
"audio_language_code": "",
|
||||||
|
"audio_profile": "",
|
||||||
|
"audio_sample_rate": "48000",
|
||||||
|
"id": "511664",
|
||||||
|
"type": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "511953",
|
||||||
|
"subtitle_codec": "srt",
|
||||||
|
"subtitle_container": "",
|
||||||
|
"subtitle_forced": 0,
|
||||||
|
"subtitle_format": "srt",
|
||||||
|
"subtitle_language": "English",
|
||||||
|
"subtitle_language_code": "eng",
|
||||||
|
"subtitle_location": "external",
|
||||||
|
"type": "3"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"video_codec": "h264",
|
||||||
|
"video_framerate": "24p",
|
||||||
|
"video_profile": "high",
|
||||||
|
"video_resolution": "1080",
|
||||||
|
"width": "1920"
|
||||||
|
}
|
||||||
|
],
|
||||||
"media_type": "episode",
|
"media_type": "episode",
|
||||||
"originally_available_at": "2016-04-24",
|
"originally_available_at": "2016-04-24",
|
||||||
"parent_media_index": "6",
|
"parent_media_index": "6",
|
||||||
@@ -4143,11 +4264,13 @@ class WebInterface(object):
|
|||||||
"rating": "7.8",
|
"rating": "7.8",
|
||||||
"rating_key": "153037",
|
"rating_key": "153037",
|
||||||
"section_id": "2",
|
"section_id": "2",
|
||||||
|
"sort_title": "Game of Thrones",
|
||||||
"studio": "HBO",
|
"studio": "HBO",
|
||||||
"summary": "Jon Snow is dead. Daenerys meets a strong man. Cersei sees her daughter again.",
|
"summary": "Jon Snow is dead. Daenerys meets a strong man. Cersei sees her daughter again.",
|
||||||
"tagline": "",
|
"tagline": "",
|
||||||
"thumb": "/library/metadata/153037/thumb/1462175060",
|
"thumb": "/library/metadata/153037/thumb/1462175060",
|
||||||
"title": "The Red Woman",
|
"title": "The Red Woman",
|
||||||
|
"user_rating": "9.0",
|
||||||
"updated_at": "1462175060",
|
"updated_at": "1462175060",
|
||||||
"writers": [
|
"writers": [
|
||||||
"David Benioff",
|
"David Benioff",
|
||||||
@@ -4386,67 +4509,219 @@ class WebInterface(object):
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
json:
|
json:
|
||||||
{"stream_count": 3,
|
{"lan_bandwidth": 25318,
|
||||||
"sessions":
|
"sessions": [
|
||||||
[{"art": "/library/metadata/1219/art/1462175063",
|
{
|
||||||
"aspect_ratio": "1.78",
|
"actors": [
|
||||||
"audio_channels": "6",
|
"Kit Harington",
|
||||||
"audio_codec": "ac3",
|
"Emilia Clarke",
|
||||||
"audio_decision": "transcode",
|
"Isaac Hempstead-Wright",
|
||||||
"bif_thumb": "/library/parts/274169/indexes/sd/",
|
"Maisie Williams",
|
||||||
"bitrate": "10617",
|
"Liam Cunningham",
|
||||||
"container": "mkv",
|
],
|
||||||
"content_rating": "TV-MA",
|
"added_at": "1461572396",
|
||||||
"duration": "2998290",
|
"allow_guest": 1,
|
||||||
"friendly_name": "Mother of Dragons",
|
"art": "/library/metadata/1219/art/1503306930",
|
||||||
"grandparent_rating_key": "1219",
|
"aspect_ratio": "1.78",
|
||||||
"grandparent_thumb": "/library/metadata/1219/thumb/1462175063",
|
"audience_rating": "",
|
||||||
"grandparent_title": "Game of Thrones",
|
"audio_bitrate": "384",
|
||||||
"height": "1078",
|
"audio_bitrate_mode": "",
|
||||||
"indexes": 1,
|
"audio_channel_layout": "5.1(side)",
|
||||||
"ip_address": "xxx.xxx.xxx.xxx",
|
"audio_channels": "6",
|
||||||
"labels": [],
|
"audio_codec": "ac3",
|
||||||
"machine_id": "83f189w617623ccs6a1lqpby",
|
"audio_decision": "direct play",
|
||||||
"media_index": "1",
|
"audio_language": "",
|
||||||
"media_type": "episode",
|
"audio_language_code": "",
|
||||||
"parent_media_index": "6",
|
"audio_profile": "",
|
||||||
"parent_rating_key": "153036",
|
"audio_sample_rate": "48000",
|
||||||
"parent_thumb": "/library/metadata/153036/thumb/1462175062",
|
"bandwidth": "25318",
|
||||||
"parent_title": "",
|
"banner": "/library/metadata/1219/banner/1503306930",
|
||||||
"platform": "Chrome",
|
"bif_thumb": "/library/parts/274169/indexes/sd/1000",
|
||||||
"player": "Plex Web (Chrome)",
|
"bitrate": "10617",
|
||||||
"progress_percent": "0",
|
"channel_stream": 0,
|
||||||
"rating_key": "153037",
|
"collections": [],
|
||||||
"section_id": "2",
|
"container": "mkv",
|
||||||
"session_key": "291",
|
"content_rating": "TV-MA",
|
||||||
"state": "playing",
|
"deleted_user": 0,
|
||||||
"throttled": "1",
|
"device": "Windows",
|
||||||
"thumb": "/library/metadata/153037/thumb/1462175060",
|
"directors": [
|
||||||
"title": "The Red Woman",
|
"Jeremy Podeswa"
|
||||||
"transcode_audio_channels": "2",
|
],
|
||||||
"transcode_audio_codec": "aac",
|
"do_notify": 0,
|
||||||
"transcode_container": "mkv",
|
"duration": "2998272",
|
||||||
"transcode_height": "1078",
|
"email": "Jon.Snow.1337@CastleBlack.com",
|
||||||
"transcode_key": "tiv5p524wcupe8nxegc26s9k9",
|
"file": "/media/TV Shows/Game of Thrones/Season 06/Game of Thrones - S06E01 - The Red Woman.mkv",
|
||||||
"transcode_progress": 2,
|
"file_size": "3979115377",
|
||||||
"transcode_protocol": "http",
|
"friendly_name": "Jon Snow",
|
||||||
"transcode_speed": "0.0",
|
"full_title": "Game of Thrones - The Red Woman",
|
||||||
"transcode_video_codec": "h264",
|
"genres": [
|
||||||
"transcode_width": "1920",
|
"Adventure",
|
||||||
"user": "DanyKhaleesi69",
|
"Drama",
|
||||||
"user_id": 8008135,
|
"Fantasy"
|
||||||
"user_thumb": "https://plex.tv/users/568gwwoib5t98a3a/avatar",
|
],
|
||||||
"video_codec": "h264",
|
"grandparent_rating_key": "1219",
|
||||||
"video_decision": "copy",
|
"grandparent_thumb": "/library/metadata/1219/thumb/1503306930",
|
||||||
"video_framerate": "24p",
|
"grandparent_title": "Game of Thrones",
|
||||||
"video_resolution": "1080",
|
"guid": "com.plexapp.agents.thetvdb://121361/6/1?lang=en",
|
||||||
"view_offset": "",
|
"height": "1078",
|
||||||
"width": "1920",
|
"id": "",
|
||||||
"year": "2016"
|
"indexes": 1,
|
||||||
},
|
"ip_address": "10.10.10.1",
|
||||||
{...},
|
"ip_address_public": "64.123.23.111",
|
||||||
{...}
|
"is_admin": 1,
|
||||||
]
|
"is_allow_sync": null,
|
||||||
|
"is_home_user": 1,
|
||||||
|
"is_restricted": 0,
|
||||||
|
"keep_history": 1,
|
||||||
|
"labels": [],
|
||||||
|
"last_viewed_at": "1462165717",
|
||||||
|
"library_name": "TV Shows",
|
||||||
|
"local": "1",
|
||||||
|
"location": "lan",
|
||||||
|
"machine_id": "lmd93nkn12k29j2lnm",
|
||||||
|
"media_index": "1",
|
||||||
|
"media_type": "episode",
|
||||||
|
"optimized_version": 0,
|
||||||
|
"optimized_version_profile": "",
|
||||||
|
"optimized_version_title": "",
|
||||||
|
"originally_available_at": "2016-04-24",
|
||||||
|
"parent_media_index": "6",
|
||||||
|
"parent_rating_key": "153036",
|
||||||
|
"parent_thumb": "/library/metadata/153036/thumb/1503889210",
|
||||||
|
"parent_title": "Season 6",
|
||||||
|
"platform": "Plex Media Player",
|
||||||
|
"platform_name": "plex",
|
||||||
|
"platform_version": "2.4.1.787-54a020cd",
|
||||||
|
"player": "Castle-PC",
|
||||||
|
"product": "Plex Media Player",
|
||||||
|
"product_version": "3.35.2",
|
||||||
|
"profile": "Konvergo",
|
||||||
|
"progress_percent": "0",
|
||||||
|
"quality_profile": "Original",
|
||||||
|
"rating": "7.8",
|
||||||
|
"rating_key": "153037",
|
||||||
|
"section_id": "2",
|
||||||
|
"session_id": "helf15l3rxgw01xxe0jf3l3d",
|
||||||
|
"session_key": "27",
|
||||||
|
"shared_libraries": [
|
||||||
|
"10",
|
||||||
|
"1",
|
||||||
|
"4",
|
||||||
|
"5",
|
||||||
|
"15",
|
||||||
|
"20",
|
||||||
|
"2"
|
||||||
|
],
|
||||||
|
"sort_title": "Red Woman",
|
||||||
|
"state": "playing",
|
||||||
|
"stream_aspect_ratio": "1.78",
|
||||||
|
"stream_audio_bitrate": "384",
|
||||||
|
"stream_audio_bitrate_mode": "",
|
||||||
|
"stream_audio_channel_layout": "5.1(side)",
|
||||||
|
"stream_audio_channel_layout_": "5.1(side)",
|
||||||
|
"stream_audio_channels": "6",
|
||||||
|
"stream_audio_codec": "ac3",
|
||||||
|
"stream_audio_decision": "direct play",
|
||||||
|
"stream_audio_language": "",
|
||||||
|
"stream_audio_language_code": "",
|
||||||
|
"stream_audio_sample_rate": "48000",
|
||||||
|
"stream_bitrate": "10617",
|
||||||
|
"stream_container": "mkv",
|
||||||
|
"stream_container_decision": "direct play",
|
||||||
|
"stream_duration": "2998272",
|
||||||
|
"stream_subtitle_codec": "",
|
||||||
|
"stream_subtitle_container": "",
|
||||||
|
"stream_subtitle_decision": "",
|
||||||
|
"stream_subtitle_forced": 0,
|
||||||
|
"stream_subtitle_format": "",
|
||||||
|
"stream_subtitle_language": "",
|
||||||
|
"stream_subtitle_language_code": "",
|
||||||
|
"stream_subtitle_location": "",
|
||||||
|
"stream_video_bit_depth": "8",
|
||||||
|
"stream_video_bitrate": "10233",
|
||||||
|
"stream_video_codec": "h264",
|
||||||
|
"stream_video_codec_level": "41",
|
||||||
|
"stream_video_decision": "direct play",
|
||||||
|
"stream_video_framerate": "24p",
|
||||||
|
"stream_video_height": "1078",
|
||||||
|
"stream_video_language": "",
|
||||||
|
"stream_video_language_code": "",
|
||||||
|
"stream_video_ref_frames": "4",
|
||||||
|
"stream_video_resolution": "1080",
|
||||||
|
"stream_video_width": "1920",
|
||||||
|
"studio": "HBO",
|
||||||
|
"subtitle_codec": "",
|
||||||
|
"subtitle_container": "",
|
||||||
|
"subtitle_decision": "",
|
||||||
|
"subtitle_forced": 0,
|
||||||
|
"subtitle_format": "",
|
||||||
|
"subtitle_language": "",
|
||||||
|
"subtitle_language_code": "",
|
||||||
|
"subtitle_location": "",
|
||||||
|
"subtitles": 0,
|
||||||
|
"summary": "Jon Snow is dead. Daenerys meets a strong man. Cersei sees her daughter again.",
|
||||||
|
"synced_version": 0,
|
||||||
|
"synced_version_profile": "",
|
||||||
|
"tagline": "",
|
||||||
|
"throttled": "0",
|
||||||
|
"thumb": "/library/metadata/153037/thumb/1503889207",
|
||||||
|
"title": "The Red Woman",
|
||||||
|
"transcode_audio_channels": "",
|
||||||
|
"transcode_audio_codec": "",
|
||||||
|
"transcode_container": "",
|
||||||
|
"transcode_decision": "direct play",
|
||||||
|
"transcode_height": "",
|
||||||
|
"transcode_hw_decode": "",
|
||||||
|
"transcode_hw_decode_title": "",
|
||||||
|
"transcode_hw_decoding": 0,
|
||||||
|
"transcode_hw_encode": "",
|
||||||
|
"transcode_hw_encode_title": "",
|
||||||
|
"transcode_hw_encoding": 0,
|
||||||
|
"transcode_hw_full_pipeline": 0,
|
||||||
|
"transcode_hw_requested": 0,
|
||||||
|
"transcode_key": "",
|
||||||
|
"transcode_progress": 0,
|
||||||
|
"transcode_protocol": "",
|
||||||
|
"transcode_speed": "",
|
||||||
|
"transcode_throttled": 0,
|
||||||
|
"transcode_video_codec": "",
|
||||||
|
"transcode_width": "",
|
||||||
|
"type": "",
|
||||||
|
"updated_at": "1503889207",
|
||||||
|
"user": "LordCommanderSnow",
|
||||||
|
"user_id": 133788,
|
||||||
|
"user_rating": "",
|
||||||
|
"user_thumb": "https://plex.tv/users/k10w42309cynaopq/avatar",
|
||||||
|
"username": "LordCommanderSnow",
|
||||||
|
"video_bit_depth": "8",
|
||||||
|
"video_bitrate": "10233",
|
||||||
|
"video_codec": "h264",
|
||||||
|
"video_codec_level": "41",
|
||||||
|
"video_decision": "direct play",
|
||||||
|
"video_frame_rate": "23.976",
|
||||||
|
"video_framerate": "24p",
|
||||||
|
"video_height": "1078",
|
||||||
|
"video_language": "",
|
||||||
|
"video_language_code": "",
|
||||||
|
"video_profile": "high",
|
||||||
|
"video_ref_frames": "4",
|
||||||
|
"video_resolution": "1080",
|
||||||
|
"video_width": "1920",
|
||||||
|
"view_offset": "1000",
|
||||||
|
"width": "1920",
|
||||||
|
"writers": [
|
||||||
|
"David Benioff",
|
||||||
|
"D. B. Weiss"
|
||||||
|
],
|
||||||
|
"year": "2016"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stream_count": "1",
|
||||||
|
"stream_count_direct_play": 1,
|
||||||
|
"stream_count_direct_stream": 0,
|
||||||
|
"stream_count_transcode": 0,
|
||||||
|
"total_bandwidth": 25318,
|
||||||
|
"wan_bandwidth": 0
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
@@ -4583,27 +4858,29 @@ class WebInterface(object):
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
json:
|
json:
|
||||||
[{"content_type": "video",
|
[{"audio_bitrate": "192",
|
||||||
|
"client_id": "95434se643fsf24f-com-plexapp-android",
|
||||||
|
"content_type": "video",
|
||||||
"device_name": "Tyrion's iPad",
|
"device_name": "Tyrion's iPad",
|
||||||
"failure": "",
|
"failure": "",
|
||||||
"friendly_name": "Tyrion Lannister",
|
"item_complete_count": "1",
|
||||||
"item_complete_count": "0",
|
|
||||||
"item_count": "1",
|
"item_count": "1",
|
||||||
"item_downloaded_count": "0",
|
"item_downloaded_count": "1",
|
||||||
"item_downloaded_percent_complete": 0,
|
"item_downloaded_percent_complete": 100,
|
||||||
"metadata_type": "movie",
|
"metadata_type": "movie",
|
||||||
"music_bitrate": "192",
|
|
||||||
"photo_quality": "74",
|
"photo_quality": "74",
|
||||||
"platform": "iOS",
|
"platform": "iOS",
|
||||||
"rating_key": "154092",
|
"rating_key": "154092",
|
||||||
"root_title": "Deadpool",
|
"root_title": "Movies",
|
||||||
"state": "pending",
|
"state": "complete",
|
||||||
"sync_id": "11617019",
|
"sync_id": "11617019",
|
||||||
"title": "Deadpool",
|
"sync_title": "Deadpool",
|
||||||
"total_size": "0",
|
"total_size": "560718134",
|
||||||
|
"user": "DrukenDwarfMan",
|
||||||
"user_id": "696969",
|
"user_id": "696969",
|
||||||
"username": "DrukenDwarfMan",
|
"username": "DrukenDwarfMan",
|
||||||
"video_quality": "60"
|
"video_bitrate": "4000"
|
||||||
|
"video_quality": "100"
|
||||||
},
|
},
|
||||||
{...},
|
{...},
|
||||||
{...}
|
{...}
|
||||||
|
Reference in New Issue
Block a user