Compare commits
21 Commits
v2.1.7-bet
...
v2.1.9
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8e4aba7ed4 | ||
![]() |
8c0ef75d4c | ||
![]() |
76c4b3bb71 | ||
![]() |
112b1c7984 | ||
![]() |
c22a2513e3 | ||
![]() |
f336782fc1 | ||
![]() |
c19afa06de | ||
![]() |
e003850d31 | ||
![]() |
23cf790079 | ||
![]() |
e7f930bd0f | ||
![]() |
348707b6b9 | ||
![]() |
7ad78b4536 | ||
![]() |
a408a62234 | ||
![]() |
a1e9e7e87f | ||
![]() |
fa99f6e684 | ||
![]() |
11e9bd2d54 | ||
![]() |
50165af4b7 | ||
![]() |
5dd22c23f2 | ||
![]() |
79b45c1c46 | ||
![]() |
af917c4915 | ||
![]() |
c3238b5a83 |
17
CHANGELOG.md
17
CHANGELOG.md
@@ -1,5 +1,22 @@
|
||||
# Changelog
|
||||
|
||||
## v2.1.9 (2018-05-21)
|
||||
|
||||
* Notifications:
|
||||
* New: Added "live" to notification parameters.
|
||||
|
||||
|
||||
## v2.1.8-beta (2018-05-19)
|
||||
|
||||
* Newsletters:
|
||||
* New: Added authentication options for self-hosted newsletters.
|
||||
* Change: Check if the Tautulli footer has been removed in custom newsletter templates.
|
||||
* Notifications:
|
||||
* Fix: Cloudinary images not working for Twitter notifications.
|
||||
* API:
|
||||
* Fix: Return proper HTTP status codes for errors.
|
||||
|
||||
|
||||
## v2.1.7-beta (2018-05-13)
|
||||
|
||||
* Newsletters:
|
||||
|
@@ -4,12 +4,12 @@
|
||||
If you think you can contribute code to the Tautulli repository, do not hesitate to submit a pull request.
|
||||
|
||||
### 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 `nightly` branch, to minimize cross merges. When you want to develop a new feature, clone the repository with `git clone origin/nightly -b FEATURE_NAME`. Use meaningful commit messages.
|
||||
|
||||
### Python Code
|
||||
|
||||
#### Compatibility
|
||||
The code should work with Python 2.7. Note that Tautulli 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 many different platforms.
|
||||
|
||||
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.
|
||||
|
||||
@@ -29,13 +29,10 @@ Although Tautulli did not adapt a code convention in the past, we try to follow
|
||||
#### Documentation
|
||||
Document your code. Use docstrings See [PEP-257](https://www.python.org/dev/peps/pep-0257/) for more information.
|
||||
|
||||
#### Continuous Integration
|
||||
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
|
||||
|
||||
#### Compatibility
|
||||
HTML5 compatible browsers are targetted. There is no specific mobile version of Tautulli yet.
|
||||
HTML5 compatible browsers are targeted.
|
||||
|
||||
#### Conventions
|
||||
* 4 space indentation
|
||||
|
@@ -27,9 +27,9 @@ This project is based on code from [Headphones](https://github.com/rembo10/headp
|
||||
|
||||
## Preview
|
||||
|
||||
* [Full preview gallery available on our website](http://tautulli.com)
|
||||
* [Full preview gallery available on our website](https://tautulli.com)
|
||||
|
||||

|
||||

|
||||
|
||||
## Installation and Support
|
||||
|
||||
|
@@ -78,7 +78,7 @@ DOCUMENTATION :: END
|
||||
<tr>
|
||||
<td class="top-line">Resources:</td>
|
||||
<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('https://tautulli.com')}" target="_blank">Tautulli Website</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/%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/%s-Wiki' % (plexpy.CONFIG.GIT_USER, plexpy.CONFIG.GIT_REPO))}" target="_blank">GitHub Wiki</a> |
|
||||
|
@@ -2935,6 +2935,7 @@ a .home-platforms-list-cover-face:hover
|
||||
}
|
||||
.stacked-configs > li > span > a.toggle-left,
|
||||
.stacked-configs > li > span > span.toggle-left {
|
||||
float: left;
|
||||
color: #444;
|
||||
padding-right: 8px;
|
||||
}
|
||||
@@ -2945,16 +2946,6 @@ a .home-platforms-list-cover-face:hover
|
||||
.stacked-configs > li > span > span.active {
|
||||
color: #f9be03;
|
||||
}
|
||||
.stacked-configs > li.new-notification-agent,
|
||||
.stacked-configs > li.notification-agent,
|
||||
.stacked-configs > li.add-notification-agent,
|
||||
.stacked-configs > li.new-newsletter-agent,
|
||||
.stacked-configs > li.newsletter-agent,
|
||||
.stacked-configs > li.add-newsletter-agent,
|
||||
.stacked-configs > li.mobile-device,
|
||||
.stacked-configs > li.add-mobile-device {
|
||||
cursor: pointer;
|
||||
}
|
||||
.stacked-configs > li.mobile-device > span > a.toggle-left,
|
||||
.stacked-configs > li.mobile-device > span > span.toggle-left {
|
||||
color: #999;
|
||||
@@ -3525,8 +3516,7 @@ a.no-highlight:hover {
|
||||
}
|
||||
.login-logo {
|
||||
margin: 0 auto 50px auto;
|
||||
width: 340px;
|
||||
height: 100px;
|
||||
text-align: center;
|
||||
}
|
||||
.login-container .form-group {
|
||||
margin-bottom: 20px;
|
||||
@@ -4098,4 +4088,11 @@ a[data-tab-destination] {
|
||||
margin-top: 10px !important;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid #444;
|
||||
}
|
||||
}
|
||||
.newsletter-logo {
|
||||
margin: 0 auto 50px auto;
|
||||
text-align: center;
|
||||
}
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@@ -91,7 +91,7 @@ DOCUMENTATION :: END
|
||||
<div class="item-children-poster-face episode-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=500&height=250&fallback=art);">
|
||||
<div class="item-children-card-overlay">
|
||||
<div class="item-children-overlay-text">
|
||||
Episode ${child['media_index']}
|
||||
Episode ${child['media_index'] or child['originally_available_at']}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -11,12 +11,11 @@ DOCUMENTATION :: END
|
||||
|
||||
<ul class="stacked-configs list-unstyled">
|
||||
% for device in sorted(devices_list, key=lambda k: k['device_name']):
|
||||
<li class="mobile-device" data-id="${device['id']}" data-name="${device['device_name']}">
|
||||
<li class="mobile-device pointer" data-id="${device['id']}" data-name="${device['device_name']}">
|
||||
<span>
|
||||
<!--<span class="toggle-right mobile-device-tooltip edit-mobile-device" data-toggle="tooltip" data-placement="top" title="Edit Device"><i class="fa fa-lg fa-pencil"></i></span>-->
|
||||
<span class="toggle-left"><i class="fa fa-lg fa-mobile"></i></span>
|
||||
<span class="toggle-left"><i class="fa fa-lg fa-fw fa-mobile"></i></span>
|
||||
${device['friendly_name'] or device['device_name']} <span class="friendly_name">(${device['id']})</span>
|
||||
<span class="toggle-right"><i class="fa fa-lg fa-cog"></i></span>
|
||||
<span class="toggle-right"><i class="fa fa-lg fa-fw fa-cog"></i></span>
|
||||
<span class="toggle-right friendly_name" id="device-last_seen-${device['id']}">
|
||||
% if device['last_seen']:
|
||||
<script>
|
||||
@@ -26,14 +25,13 @@ DOCUMENTATION :: END
|
||||
never
|
||||
% endif
|
||||
</span>
|
||||
<!--<span class="toggle-right delete-mobile-device" data-toggle="tooltip" data-placement="top" title="Remove Device"><i class="fa fa-lg fa-times"></i></span>-->
|
||||
</span>
|
||||
</li>
|
||||
% endfor
|
||||
<li class="add-mobile-device" id="register-mobile-device" data-target="#api-qr-modal" data-toggle="modal">
|
||||
<li class="add-mobile-device pointer" id="register-mobile-device" data-target="#api-qr-modal" data-toggle="modal">
|
||||
<span>
|
||||
<span class="toggle-left"><i class="fa fa-lg fa-mobile"></i></span> Register a new device
|
||||
<span class="toggle-right"><i class="fa fa-lg fa-plus"></i></span>
|
||||
<span class="toggle-left"><i class="fa fa-lg fa-fw fa-mobile"></i></span> Register a new device
|
||||
<span class="toggle-right"><i class="fa fa-lg fa-fw fa-plus"></i></span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
43
data/interfaces/default/newsletter_auth.html
Normal file
43
data/interfaces/default/newsletter_auth.html
Normal file
@@ -0,0 +1,43 @@
|
||||
<%
|
||||
import urllib
|
||||
%>
|
||||
<!doctype html>
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Tautulli - ${title}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="${http_root}css/bootstrap3/bootstrap.css" rel="stylesheet">
|
||||
<link href="${http_root}css/tautulli.css${cache_param}" rel="stylesheet">
|
||||
<link href="${http_root}css/opensans.min.css" rel="stylesheet">
|
||||
<link href="${http_root}css/font-awesome.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="body-container">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="login-container">
|
||||
<div class="newsletter-logo">
|
||||
<img src="${http_root}images/newsletter/newsletter-header.png" height="100" alt="PlexPy">
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6 col-sm-offset-3">
|
||||
<form action="${uri}" method="post" id="newsletter-form">
|
||||
<div class="form-group">
|
||||
<label for="password" class="control-label">
|
||||
Password
|
||||
</label>
|
||||
<input type="password" id="key" name="key" class="form-control" autofocus>
|
||||
</div>
|
||||
<button id="enter" type="submit" class="btn btn-bright login-button"><i class="fa fa-sign-in"></i> Enter</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@@ -12,15 +12,15 @@ DOCUMENTATION :: END
|
||||
<% from plexpy.newsletter_handler import NEWSLETTER_SCHED %>
|
||||
<ul class="stacked-configs list-unstyled">
|
||||
% for newsletter in sorted(newsletters_list, key=lambda k: (k['agent_label'], k['friendly_name'], k['id'])):
|
||||
<li class="newsletter-agent" data-id="${newsletter['id']}">
|
||||
<li class="newsletter-agent pointer" data-id="${newsletter['id']}">
|
||||
<span>
|
||||
<span class="toggle-left trigger-tooltip ${'active' if newsletter['active'] else ''}" data-toggle="tooltip" data-placement="top" title="Newsletter ${'active' if newsletter['active'] else 'inactive'}"><i class="fa fa-lg fa-newspaper-o"></i></span>
|
||||
<span class="toggle-left trigger-tooltip ${'active' if newsletter['active'] else ''}" data-toggle="tooltip" data-placement="top" title="Newsletter ${'active' if newsletter['active'] else 'inactive'}"><i class="fa fa-lg fa-fw fa-newspaper-o"></i></span>
|
||||
% if newsletter['friendly_name']:
|
||||
${newsletter['agent_label']} <span class="friendly_name">(${newsletter['id']} - ${newsletter['friendly_name']})</span>
|
||||
% else:
|
||||
${newsletter['agent_label']} <span class="friendly_name">(${newsletter['id']})</span>
|
||||
% endif
|
||||
<span class="toggle-right"><i class="fa fa-lg fa-cog"></i></span>
|
||||
<span class="toggle-right"><i class="fa fa-lg fa-fw fa-cog"></i></span>
|
||||
<span class="toggle-right friendly_name" id="newsletter-next_run-${newsletter['id']}">
|
||||
% if NEWSLETTER_SCHED.get_job('newsletter-{}'.format(newsletter['id'])):
|
||||
<% job = NEWSLETTER_SCHED.get_job('newsletter-{}'.format(newsletter['id'])) %>
|
||||
@@ -32,10 +32,10 @@ DOCUMENTATION :: END
|
||||
</span>
|
||||
</li>
|
||||
% endfor
|
||||
<li class="add-newsletter-agent" id="add-newsletter-agent" data-target="#add-newsletter-modal" data-toggle="modal">
|
||||
<li class="add-newsletter-agent pointer" id="add-newsletter-agent" data-target="#add-newsletter-modal" data-toggle="modal">
|
||||
<span>
|
||||
<span class="toggle-left"><i class="fa fa-lg fa-newspaper-o"></i></span> Add a new newsletter agent
|
||||
<span class="toggle-right"><i class="fa fa-lg fa-plus"></i></span>
|
||||
<span class="toggle-left"><i class="fa fa-lg fa-fw fa-newspaper-o"></i></span> Add a new newsletter agent
|
||||
<span class="toggle-right"><i class="fa fa-lg fa-fw fa-plus"></i></span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
@@ -1,7 +1,8 @@
|
||||
% if notifier:
|
||||
<%!
|
||||
import json
|
||||
from plexpy import helpers, notifiers, users
|
||||
from plexpy import notifiers, users
|
||||
from plexpy.helpers import checked
|
||||
available_notification_actions = notifiers.available_notification_actions()
|
||||
|
||||
user_emails = [{'user': u['friendly_name'] or u['username'], 'email': u['email']} for u in users.Users().get_users() if u['email']]
|
||||
@@ -70,7 +71,7 @@
|
||||
% elif item['input_type'] == 'checkbox':
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" data-id="${item['name']}" class="checkboxes" value="1" ${helpers.checked(item['value'])}> ${item['label']}
|
||||
<input type="checkbox" data-id="${item['name']}" class="checkboxes" value="1" ${checked(item['value'])}> ${item['label']}
|
||||
</label>
|
||||
<p class="help-block">${item['description'] | n}</p>
|
||||
<input type="hidden" id="${item['name']}" name="${item['name']}" value="${item['value']}">
|
||||
@@ -146,7 +147,7 @@
|
||||
% for action in available_notification_actions:
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" data-id="${action['name']}" class="checkboxes" value="1" ${helpers.checked(notifier['actions'][action['name']])}> ${action['label']}
|
||||
<input type="checkbox" data-id="${action['name']}" class="checkboxes" value="1" ${checked(notifier['actions'][action['name']])}> ${action['label']}
|
||||
</label>
|
||||
<p class="help-block">${action['description'] | n}</p>
|
||||
<input type="hidden" id="${action['name']}" name="${action['name']}" value="${notifier['actions'][action['name']]}">
|
||||
|
@@ -11,22 +11,22 @@ DOCUMENTATION :: END
|
||||
|
||||
<ul class="stacked-configs list-unstyled">
|
||||
% for notifier in sorted(notifiers_list, key=lambda k: (k['agent_label'].lower(), k['friendly_name'], k['id'])):
|
||||
<li class="notification-agent" data-id="${notifier['id']}">
|
||||
<li class="notification-agent pointer" data-id="${notifier['id']}">
|
||||
<span>
|
||||
<span class="toggle-left trigger-tooltip ${'active' if notifier['active'] else ''}" data-toggle="tooltip" data-placement="top" title="Triggers ${'active' if notifier['active'] else 'inactive'}"><i class="fa fa-lg fa-bell"></i></span>
|
||||
<span class="toggle-left trigger-tooltip ${'active' if notifier['active'] else ''}" data-toggle="tooltip" data-placement="top" title="Triggers ${'active' if notifier['active'] else 'inactive'}"><i class="fa fa-lg fa-fw fa-bell"></i></span>
|
||||
% if notifier['friendly_name']:
|
||||
${notifier['agent_label']} <span class="friendly_name">(${notifier['id']} - ${notifier['friendly_name']})</span>
|
||||
% else:
|
||||
${notifier['agent_label']} <span class="friendly_name">(${notifier['id']})</span>
|
||||
% endif
|
||||
<span class="toggle-right"><i class="fa fa-lg fa-cog"></i></span>
|
||||
<span class="toggle-right"><i class="fa fa-lg fa-fw fa-cog"></i></span>
|
||||
</span>
|
||||
</li>
|
||||
% endfor
|
||||
<li class="add-notification-agent" id="add-notification-agent" data-target="#add-notifier-modal" data-toggle="modal">
|
||||
<li class="add-notification-agent pointer" id="add-notification-agent" data-target="#add-notifier-modal" data-toggle="modal">
|
||||
<span>
|
||||
<span class="toggle-left"><i class="fa fa-lg fa-bell"></i></span> Add a new notification agent
|
||||
<span class="toggle-right"><i class="fa fa-lg fa-plus"></i></span>
|
||||
<span class="toggle-left"><i class="fa fa-lg fa-fw fa-bell"></i></span> Add a new notification agent
|
||||
<span class="toggle-right"><i class="fa fa-lg fa-fw fa-plus"></i></span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
@@ -650,7 +650,7 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group has-feedback" id="pms_ip_group">
|
||||
<label for="pms_ip">Plex IP or Hostname</label>
|
||||
<label for="pms_ip">Plex IP Address or Hostname</label>
|
||||
<div class="row">
|
||||
<div class="col-md-9" id="selectize-pms-ip-container">
|
||||
<div class="input-group">
|
||||
@@ -965,10 +965,35 @@
|
||||
<p class="help-block">Enable to host newsletters on your own domain. This will generate a link to an HTML page where you can view the newsletter.</p>
|
||||
</div>
|
||||
<div id="self_host_newsletter_options" style="overlfow: hidden; display: ${'block' if config['newsletter_self_hosted'] == 'checked' else 'none'}">
|
||||
<p class="help-block" id="self_host_newsletter_message">
|
||||
Note: The <span class="inline-pre">${http_root}newsletter</span> endpoint on your domain must be publicly accessible from the internet.
|
||||
</p>
|
||||
<p class="help-block settings-warning base-url-warning">Warning: Public Tautulli domain not set under <a data-tab-destination="tabs-web_interface" data-target="#http_base_url">Web Interface</a>.</p>
|
||||
<div class="form-group">
|
||||
<p class="help-block" id="self_host_newsletter_message">
|
||||
Note: The <span class="inline-pre">${http_root}newsletter</span> endpoint on your domain must be publicly accessible from the internet.
|
||||
</p>
|
||||
<p class="help-block settings-warning base-url-warning">Warning: Public Tautulli domain not set under <a data-tab-destination="tabs-web_interface" data-target="#http_base_url">Web Interface</a>.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="newsletter_auth">Newsletter Authentication</label>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<select class="form-control" id="newsletter_auth" name="newsletter_auth">
|
||||
<option value="0" ${'selected' if config['newsletter_auth'] == 0 else ''}>Disabled</option>
|
||||
<option value="1" ${'selected' if config['newsletter_auth'] == 1 else ''}>Password</option>
|
||||
<option value="2" ${'selected' if config['newsletter_auth'] == 2 else ''}>Tautulli Guest Access</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<p class="help-block">Select the authentication method to use for self-hosted newsletters.</p>
|
||||
<p class="help-block settings-warning newsletter-guest-access-warning">Warning: Guest Access is not enabled under <a data-tab-destination="tabs-web_interface" data-target="#allow_guest_access">Web Interface</a>.</p>
|
||||
</div>
|
||||
<div class="form-group" id="newsletter_password_option">
|
||||
<label for="newsletter_password">Newsletter Password</label>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="newsletter_password" name="newsletter_password" value="${config['newsletter_password']}">
|
||||
</div>
|
||||
</div>
|
||||
<p class="help-block">Enter the password that will be required to view self-hosted newsletters.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checkbox advanced-setting">
|
||||
@@ -1025,10 +1050,12 @@
|
||||
<p class="help-block">Select where to host Plex images for notifications and newsletters.</p>
|
||||
</div>
|
||||
<div id="imgur_upload_options" style="overlfow: hidden; display: ${'none' if config['notify_upload_posters'] != 1 else 'block'}">
|
||||
<p class="help-block" id="imgur_upload_message">
|
||||
You can register a new Imgur application <a href="${anon_url('https://api.imgur.com/oauth2/addclient')}" target="_blank">here</a>.<br>
|
||||
Warning: Imgur uploads are rate-limited and newsletters may exceed the limit. Please use Cloudinary for newsletters instead.
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<p class="help-block" id="imgur_upload_message">
|
||||
You can register a new Imgur application <a href="${anon_url('https://api.imgur.com/oauth2/addclient')}" target="_blank">here</a>.<br>
|
||||
Warning: Imgur uploads are rate-limited and newsletters may exceed the limit. Please use Cloudinary for newsletters instead.
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="imgur_client_id">Imgur Client ID</label>
|
||||
<div class="row">
|
||||
@@ -1040,13 +1067,17 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="self_host_image_options" style="overlfow: hidden; display: ${'none' if config['notify_upload_posters'] != 2 else 'block'}">
|
||||
<p class="help-block" id="self_host_image_message">Note: The <span class="inline-pre">${http_root}image</span> endpoint on your domain must be publicly accessible from the internet.</p>
|
||||
<p class="help-block settings-warning base-url-warning">Warning: Public Tautulli domain not set under <a data-tab-destination="tabs-web_interface" data-target="#http_base_url">Web Interface</a>.</p>
|
||||
<div class="form-group">
|
||||
<p class="help-block" id="self_host_image_message">Note: The <span class="inline-pre">${http_root}image</span> endpoint on your domain must be publicly accessible from the internet.</p>
|
||||
<p class="help-block settings-warning base-url-warning">Warning: Public Tautulli domain not set under <a data-tab-destination="tabs-web_interface" data-target="#http_base_url">Web Interface</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="cloudinary_upload_options" style="overlfow: hidden; display: ${'none' if config['notify_upload_posters'] != 3 else 'block'}">
|
||||
<p class="help-block" id="imgur_upload_message">
|
||||
You can sign up for Cloudinary <a href="${anon_url('https://cloudinary.com')}" target="_blank">here</a>.<br>
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<p class="help-block" id="imgur_upload_message">
|
||||
You can sign up for Cloudinary <a href="${anon_url('https://cloudinary.com')}" target="_blank">here</a>.<br>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="cloudinary_cloud_name">Cloudinary Cloud Name</label>
|
||||
<div class="row">
|
||||
@@ -1237,7 +1268,7 @@
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<p class="form-group">
|
||||
<div class="form-group">
|
||||
<label>Registered Devices</label>
|
||||
<p class="help-block">Register a new device using a QR code, or configure an existing device by clicking the settings icon on the right.</p>
|
||||
<p id="app_api_msg" style="color: #eb8600;">The API must be enabled under <a data-tab-destination="tabs-web_interface" data-target="#api_enabled">Web Interface</a> to use the app.</p>
|
||||
@@ -1392,7 +1423,7 @@
|
||||
<div class="col-md-12">
|
||||
<ul class="stacked-configs list-unstyled">
|
||||
% for agent in sorted(available_notification_agents, key=lambda k: k['label'].lower()):
|
||||
<li class="new-notification-agent" data-id="${agent['id']}">
|
||||
<li class="new-notification-agent pointer" data-id="${agent['id']}">
|
||||
<span>${agent['label']}</span>
|
||||
</li>
|
||||
% endfor
|
||||
@@ -1420,7 +1451,7 @@
|
||||
<div class="col-md-12">
|
||||
<ul class="stacked-configs list-unstyled">
|
||||
% for agent in available_newsletter_agents:
|
||||
<li class="new-newsletter-agent" data-id="${agent['id']}">
|
||||
<li class="new-newsletter-agent pointer" data-id="${agent['id']}">
|
||||
<span>${agent['label']}</span>
|
||||
</li>
|
||||
% endfor
|
||||
@@ -1762,6 +1793,7 @@
|
||||
}
|
||||
|
||||
function loadNotifierConfig(notifier_id) {
|
||||
showMsg('<i class="fa fa-refresh fa-spin"></i> Loading Configuration', false);
|
||||
$.ajax({
|
||||
url: 'get_notifier_config_modal',
|
||||
data: { notifier_id: notifier_id },
|
||||
@@ -1769,6 +1801,7 @@
|
||||
async: true,
|
||||
complete: function (xhr, status) {
|
||||
$("#notifier-config-modal").html(xhr.responseText).modal('show');
|
||||
showMsg('<i class="fa fa-check"></i> Configuration Loaded', false, true, 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1785,6 +1818,7 @@
|
||||
}
|
||||
|
||||
function loadNewsletterConfig(newsletter_id) {
|
||||
showMsg('<i class="fa fa-refresh fa-spin"></i> Loading Configuration', false);
|
||||
$.ajax({
|
||||
url: 'get_newsletter_config_modal',
|
||||
data: { newsletter_id: newsletter_id },
|
||||
@@ -1792,6 +1826,7 @@
|
||||
async: true,
|
||||
complete: function (xhr, status) {
|
||||
$("#newsletter-config-modal").html(xhr.responseText).modal('show');
|
||||
showMsg('<i class="fa fa-check"></i> Configuration Loaded', false, true, 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1808,6 +1843,7 @@
|
||||
}
|
||||
|
||||
function loadMobileDeviceConfig(mobile_device_id) {
|
||||
showMsg('<i class="fa fa-refresh fa-spin"></i> Loading Configuration', false);
|
||||
$.ajax({
|
||||
url: 'get_mobile_device_config_modal',
|
||||
data: { mobile_device_id: mobile_device_id },
|
||||
@@ -1815,6 +1851,7 @@
|
||||
async: true,
|
||||
complete: function (xhr, status) {
|
||||
$("#mobile-device-config-modal").html(xhr.responseText).modal('show');
|
||||
showMsg('<i class="fa fa-check"></i> Configuration Loaded', false, true, 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2456,6 +2493,7 @@ $(document).ready(function() {
|
||||
$("#allow_guest_access").attr("disabled", false);
|
||||
$("#allowGuestCheck").html("");
|
||||
}
|
||||
newsletterPasswordEnabled();
|
||||
}
|
||||
allowGuestAccessCheck();
|
||||
|
||||
@@ -2559,7 +2597,7 @@ $(document).ready(function() {
|
||||
var result = $.parseJSON(xhr.responseText);
|
||||
var msg = result.message;
|
||||
$('#add-notifier-modal').modal('hide');
|
||||
if (result.result == 'success') {
|
||||
if (result.result === 'success') {
|
||||
showMsg('<i class="fa fa-check"></i> ' + msg, false, true, 5000);
|
||||
loadNotifierConfig(result.notifier_id);
|
||||
} else {
|
||||
@@ -2581,7 +2619,7 @@ $(document).ready(function() {
|
||||
var result = $.parseJSON(xhr.responseText);
|
||||
var msg = result.message;
|
||||
$('#add-newsletter-modal').modal('hide');
|
||||
if (result.result == 'success') {
|
||||
if (result.result === 'success') {
|
||||
showMsg('<i class="fa fa-check"></i> ' + msg, false, true, 5000);
|
||||
loadNewsletterConfig(result.newsletter_id);
|
||||
} else {
|
||||
@@ -2680,6 +2718,28 @@ $(document).ready(function() {
|
||||
newsletterUploadEnabled();
|
||||
});
|
||||
|
||||
function newsletterPasswordEnabled() {
|
||||
if ($('#newsletter_auth').val() === '1') {
|
||||
$('#newsletter_password_option').slideDown();
|
||||
} else {
|
||||
$('#newsletter_password_option').slideUp();
|
||||
}
|
||||
if ($('#newsletter_auth').val() === '2' && !($('#allow_guest_access').is(':checked'))) {
|
||||
$('.newsletter-guest-access-warning').show();
|
||||
} else {
|
||||
$('.newsletter-guest-access-warning').hide();
|
||||
}
|
||||
}
|
||||
newsletterPasswordEnabled();
|
||||
|
||||
$('#newsletter_auth').change(function () {
|
||||
newsletterPasswordEnabled();
|
||||
});
|
||||
|
||||
$('#allow_guest_access').click(function () {
|
||||
newsletterPasswordEnabled();
|
||||
})
|
||||
|
||||
$('body').on('click', 'a[data-tab-destination]', function () {
|
||||
var tab = $(this).data('tab-destination');
|
||||
$("a[href=#" + tab + "]").click();
|
||||
|
@@ -955,7 +955,7 @@
|
||||
<td class="footer" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 12px;vertical-align: top;clear: both;margin-top: 10px;text-align: center;width: 100%;">
|
||||
<div class="footer-bar" style="margin-left: auto;margin-right: auto;width: 200px;border-top: 1px solid #E5A00D;margin-top: 25px;"></div>
|
||||
<div class="content-block powered-by" style="padding-bottom: 10px;padding-top: 10px;">
|
||||
Newsletter generated by <a href="http://tautulli.com" target="_blank" style="text-decoration: underline;color: #fff;font-size: 12px;text-align: center;">Tautulli</a>.
|
||||
<!-- FOOTER MESSAGE - DO NOT REMOVE -->
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
@@ -956,7 +956,7 @@
|
||||
<td class="footer">
|
||||
<div class="footer-bar"></div>
|
||||
<div class="content-block powered-by">
|
||||
Newsletter generated by <a href="http://tautulli.com" target="_blank">Tautulli</a>.
|
||||
<!-- FOOTER MESSAGE - DO NOT REMOVE -->
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
@@ -1103,10 +1103,10 @@ class Api(object):
|
||||
except KeyError:
|
||||
raise TwitterError({'message': 'Media could not be uploaded'})
|
||||
|
||||
boundary = bytes("--{0}".format(uuid4()), 'utf-8')
|
||||
boundary = bytes("--{0}".format(uuid4())).encode('utf-8')
|
||||
media_id_bytes = bytes(str(media_id).encode('utf-8'))
|
||||
headers = {'Content-Type': 'multipart/form-data; boundary={0}'.format(
|
||||
str(boundary[2:], 'utf-8'))}
|
||||
str(boundary[2:]).encode('utf-8'))}
|
||||
|
||||
segment_id = 0
|
||||
while True:
|
||||
|
@@ -1723,8 +1723,8 @@ def dbcheck():
|
||||
for row in result:
|
||||
img_hash = notification_handler.set_hash_image_info(
|
||||
rating_key=row['rating_key'], width=1000, height=1500, fallback='poster')
|
||||
data_factory.set_img_info(img_hash=img_hash, imgur_title=row['poster_title'],
|
||||
imgur_url=row['poster_url'], delete_hash=row['delete_hash'],
|
||||
data_factory.set_img_info(img_hash=img_hash, img_title=row['poster_title'],
|
||||
img_url=row['poster_url'], delete_hash=row['delete_hash'],
|
||||
service='imgur')
|
||||
|
||||
db.action('DROP TABLE poster_urls')
|
||||
|
@@ -180,8 +180,9 @@ class ActivityProcessor(object):
|
||||
if str(session['rating_key']).isdigit() and session['media_type'] in ('movie', 'episode', 'track'):
|
||||
logging_enabled = True
|
||||
else:
|
||||
logger.debug(u"Tautulli ActivityProcessor :: ratingKey %s not logged. Does not meet logging criteria. "
|
||||
u"Media type is '%s'" % (session['rating_key'], session['media_type']))
|
||||
logger.debug(u"Tautulli ActivityProcessor :: Session %s ratingKey %s not logged. "
|
||||
u"Does not meet logging criteria. Media type is '%s'" %
|
||||
(session['session_key'], session['rating_key'], session['media_type']))
|
||||
return session['id']
|
||||
|
||||
if str(session['paused_counter']).isdigit():
|
||||
@@ -193,15 +194,16 @@ class ActivityProcessor(object):
|
||||
if (session['media_type'] == 'movie' or session['media_type'] == 'episode') and \
|
||||
(real_play_time < int(plexpy.CONFIG.LOGGING_IGNORE_INTERVAL)):
|
||||
logging_enabled = False
|
||||
logger.debug(u"Tautulli ActivityProcessor :: Play duration for ratingKey %s is %s secs which is less than %s "
|
||||
u"seconds, so we're not logging it." %
|
||||
(session['rating_key'], str(real_play_time), plexpy.CONFIG.LOGGING_IGNORE_INTERVAL))
|
||||
logger.debug(u"Tautulli ActivityProcessor :: Play duration for session %s ratingKey %s is %s secs "
|
||||
u"which is less than %s seconds, so we're not logging it." %
|
||||
(session['session_key'], session['rating_key'], str(real_play_time),
|
||||
plexpy.CONFIG.LOGGING_IGNORE_INTERVAL))
|
||||
if not is_import and session['media_type'] == 'track':
|
||||
if real_play_time < 15 and session['duration'] >= 30:
|
||||
logging_enabled = False
|
||||
logger.debug(u"Tautulli ActivityProcessor :: Play duration for ratingKey %s is %s secs, "
|
||||
logger.debug(u"Tautulli ActivityProcessor :: Play duration for session %s ratingKey %s is %s secs, "
|
||||
u"looks like it was skipped so we're not logging it" %
|
||||
(session['rating_key'], str(real_play_time)))
|
||||
(session['session_key'], session['rating_key'], str(real_play_time)))
|
||||
elif is_import and import_ignore_interval:
|
||||
if (session['media_type'] == 'movie' or session['media_type'] == 'episode') and \
|
||||
(real_play_time < int(import_ignore_interval)):
|
||||
|
@@ -611,6 +611,7 @@ General optional parameters:
|
||||
# if we fail to generate the output fake an error
|
||||
except Exception as e:
|
||||
logger.api_exception(u'Tautulli APIv2 :: ' + traceback.format_exc())
|
||||
cherrypy.response.status = 500
|
||||
out['message'] = traceback.format_exc()
|
||||
out['result'] = 'error'
|
||||
|
||||
@@ -620,6 +621,7 @@ General optional parameters:
|
||||
out = xmltodict.unparse(out, pretty=True)
|
||||
except Exception as e:
|
||||
logger.api_error(u'Tautulli APIv2 :: Failed to parse xml result')
|
||||
cherrypy.response.status = 500
|
||||
try:
|
||||
out['message'] = e
|
||||
out['result'] = 'error'
|
||||
@@ -660,6 +662,7 @@ General optional parameters:
|
||||
result = call(**self._api_kwargs)
|
||||
except Exception as e:
|
||||
logger.api_error(u'Tautulli APIv2 :: Failed to run %s with %s: %s' % (self._api_cmd, self._api_kwargs, e))
|
||||
cherrypy.response.status = 400
|
||||
if self._api_debug:
|
||||
cherrypy.request.show_tracebacks = True
|
||||
# Reraise the exception so the traceback hits the browser
|
||||
@@ -704,4 +707,7 @@ General optional parameters:
|
||||
if ret.get('result'):
|
||||
self._api_result_type = ret.pop('result', None)
|
||||
|
||||
if self._api_result_type == 'error':
|
||||
cherrypy.response.status = 500
|
||||
|
||||
return self._api_out_as(self._api_responds(result_type=self._api_result_type, msg=self._api_msg, data=ret))
|
||||
|
@@ -33,9 +33,9 @@ DEFAULT_POSTER_THUMB = "interfaces/default/images/poster.png"
|
||||
DEFAULT_COVER_THUMB = "interfaces/default/images/cover.png"
|
||||
DEFAULT_ART = "interfaces/default/images/art.png"
|
||||
|
||||
ONLINE_POSTER_THUMB = "http://tautulli.com/images/poster.png"
|
||||
ONLINE_COVER_THUMB = "http://tautulli.com/images/cover.png"
|
||||
ONLINE_ART = "http://tautulli.com/images/art.png"
|
||||
ONLINE_POSTER_THUMB = "https://tautulli.com/images/poster.png"
|
||||
ONLINE_COVER_THUMB = "https://tautulli.com/images/cover.png"
|
||||
ONLINE_ART = "https://tautulli.com/images/art.png"
|
||||
|
||||
MEDIA_TYPE_HEADERS = {
|
||||
'movie': 'Movies',
|
||||
@@ -339,6 +339,7 @@ NOTIFICATION_PARAMETERS = [
|
||||
{'name': 'Optimized Version', 'type': 'int', 'value': 'optimized_version', 'description': 'If the stream is an optimized version.', 'example': '0 or 1'},
|
||||
{'name': 'Optimized Version Profile', 'type': 'str', 'value': 'optimized_version_profile', 'description': 'The optimized version profile of the stream.'},
|
||||
{'name': 'Synced Version', 'type': 'int', 'value': 'synced_version', 'description': 'If the stream is an synced version.', 'example': '0 or 1'},
|
||||
{'name': 'Live', 'type': 'int', 'value': 'live', 'description': 'If the stream is live TV.', 'example': '0 or 1'},
|
||||
{'name': 'Stream Local', 'type': 'int', 'value': 'stream_local', 'description': 'If the stream is local.', 'example': '0 or 1'},
|
||||
{'name': 'Stream Location', 'type': 'str', 'value': 'stream_location', 'description': 'The network location of the stream.', 'example': 'lan or wan'},
|
||||
{'name': 'Stream Bandwidth', 'type': 'int', 'value': 'stream_bandwidth', 'description': 'The required bandwidth (in kbps) of the stream.', 'help_text': 'not the used bandwidth'},
|
||||
@@ -531,6 +532,7 @@ NEWSLETTER_PARAMETERS = [
|
||||
{'name': 'Newsletter UUID', 'type': 'str', 'value': 'newsletter_uuid', 'description': 'The unique identifier for the newsletter.'},
|
||||
{'name': 'Newsletter ID', 'type': 'int', 'value': 'newsletter_id', 'description': 'The unique ID number for the newsletter agent.'},
|
||||
{'name': 'Newsletter ID Name', 'type': 'int', 'value': 'newsletter_id_name', 'description': 'The unique ID name for the newsletter agent.'},
|
||||
{'name': 'Newsletter Password', 'type': 'str', 'value': 'newsletter_password', 'description': 'The password required to view the newsletter if enabled.'},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@@ -312,6 +312,8 @@ _CONFIG_DEFINITIONS = {
|
||||
'MONITOR_REMOTE_ACCESS': (int, 'Monitoring', 0),
|
||||
'MONITORING_INTERVAL': (int, 'Monitoring', 60),
|
||||
'MONITORING_USE_WEBSOCKET': (int, 'Monitoring', 0),
|
||||
'NEWSLETTER_AUTH': (int, 'Newsletter', 0),
|
||||
'NEWSLETTER_PASSWORD': (str, 'Newsletter', ''),
|
||||
'NEWSLETTER_CUSTOM_DIR': (str, 'Newsletter', ''),
|
||||
'NEWSLETTER_INLINE_STYLES': (int, 'Newsletter', 1),
|
||||
'NEWSLETTER_TEMPLATES': (str, 'Newsletter', 'newsletters'),
|
||||
|
@@ -835,6 +835,8 @@ def cloudinary_transform(rating_key=None, width=1000, height=1500, opacity=100,
|
||||
)
|
||||
|
||||
img_options = {'format': img_format,
|
||||
'fetch_format': 'auto',
|
||||
'quality': 'auto',
|
||||
'version': int(time.time())}
|
||||
|
||||
if width != 1000:
|
||||
|
@@ -19,6 +19,7 @@ from itertools import groupby
|
||||
from mako.lookup import TemplateLookup
|
||||
from mako import exceptions
|
||||
import os
|
||||
import re
|
||||
|
||||
import plexpy
|
||||
import common
|
||||
@@ -205,7 +206,7 @@ def set_newsletter_config(newsletter_id=None, agent_id=None, **kwargs):
|
||||
if str(agent_id).isdigit():
|
||||
agent_id = int(agent_id)
|
||||
else:
|
||||
logger.error(u"Tautulli Newsletters :: Unable to set exisiting newsletter: invalid agent_id %s."
|
||||
logger.error(u"Tautulli Newsletters :: Unable to set existing newsletter: invalid agent_id %s."
|
||||
% agent_id)
|
||||
return False
|
||||
|
||||
@@ -420,7 +421,7 @@ class Newsletter(object):
|
||||
|
||||
self.retrieve_data()
|
||||
|
||||
return serve_template(
|
||||
newsletter_rendered = serve_template(
|
||||
templatename=self._TEMPLATE,
|
||||
uuid=self.uuid,
|
||||
subject=self.subject_formatted,
|
||||
@@ -431,6 +432,25 @@ class Newsletter(object):
|
||||
preview=self.is_preview
|
||||
)
|
||||
|
||||
# Force Tautulli footer
|
||||
if '<!-- FOOTER MESSAGE - DO NOT REMOVE -->' in newsletter_rendered:
|
||||
newsletter_rendered = newsletter_rendered.replace(
|
||||
'<!-- FOOTER MESSAGE - DO NOT REMOVE -->',
|
||||
'Newsletter generated by <a href="https://tautulli.com" target="_blank" '
|
||||
'style="text-decoration: underline;color: #fff;font-size: 12px;">Tautulli</a>.'
|
||||
)
|
||||
return newsletter_rendered
|
||||
else:
|
||||
msg = ('<div style="text-align: center;padding-top: 100px;padding-bottom: 100px;">'
|
||||
'<p style="font-family: \'Open Sans\', Helvetica, Arial, sans-serif;color: #282A2D;'
|
||||
'font-size: 18px;line-height: 30px;">'
|
||||
'The Tautulli newsletter footer was removed from the newsletter template.<br>'
|
||||
'Please leave the footer in place as it is unobtrusive and supports '
|
||||
'<a href="https://tautulli.com" target="_blank">Tautulli</a>.<br>Thank you.'
|
||||
'</p></div>')
|
||||
newsletter_rendered = re.sub(r'(<body.*?>)', r'\1' + msg, newsletter_rendered)
|
||||
return newsletter_rendered
|
||||
|
||||
def send(self):
|
||||
self.newsletter = self.generate_newsletter()
|
||||
|
||||
@@ -520,7 +540,8 @@ class Newsletter(object):
|
||||
'newsletter_static_url': base_url + 'id/' + self.newsletter_id_name,
|
||||
'newsletter_uuid': self.uuid,
|
||||
'newsletter_id': self.newsletter_id,
|
||||
'newsletter_id_name': self.newsletter_id_name
|
||||
'newsletter_id_name': self.newsletter_id_name,
|
||||
'newsletter_password': plexpy.CONFIG.NEWSLETTER_PASSWORD
|
||||
}
|
||||
|
||||
return parameters
|
||||
|
@@ -509,7 +509,7 @@ def add_notifier_config(agent_id=None, **kwargs):
|
||||
'agent_name': agent['name'],
|
||||
'agent_label': agent['label'],
|
||||
'friendly_name': '',
|
||||
'notifier_config': json.dumps(get_agent_class(agent_id=agent['id']).config),
|
||||
'notifier_config': json.dumps(agent_class.config),
|
||||
'custom_conditions': json.dumps(DEFAULT_CUSTOM_CONDITIONS),
|
||||
'custom_conditions_logic': ''
|
||||
}
|
||||
@@ -540,7 +540,7 @@ def set_notifier_config(notifier_id=None, agent_id=None, **kwargs):
|
||||
if str(agent_id).isdigit():
|
||||
agent_id = int(agent_id)
|
||||
else:
|
||||
logger.error(u"Tautulli Notifiers :: Unable to set exisiting notifier: invalid agent_id %s."
|
||||
logger.error(u"Tautulli Notifiers :: Unable to set existing notifier: invalid agent_id %s."
|
||||
% agent_id)
|
||||
return False
|
||||
|
||||
@@ -570,7 +570,7 @@ def set_notifier_config(notifier_id=None, agent_id=None, **kwargs):
|
||||
'agent_name': agent['name'],
|
||||
'agent_label': agent['label'],
|
||||
'friendly_name': kwargs.get('friendly_name', ''),
|
||||
'notifier_config': json.dumps(notifier_config),
|
||||
'notifier_config': json.dumps(agent_class.config),
|
||||
'custom_conditions': kwargs.get('custom_conditions', json.dumps(DEFAULT_CUSTOM_CONDITIONS)),
|
||||
'custom_conditions_logic': kwargs.get('custom_conditions_logic', ''),
|
||||
}
|
||||
@@ -661,9 +661,9 @@ class PrettyMetadata(object):
|
||||
poster_url = self.parameters['poster_url']
|
||||
if not poster_url:
|
||||
if self.media_type in ('artist', 'album', 'track'):
|
||||
poster_url = 'http://tautulli.com/images/cover.png'
|
||||
poster_url = common.ONLINE_COVER_THUMB
|
||||
else:
|
||||
poster_url = 'http://tautulli.com/images/poster.png'
|
||||
poster_url = common.ONLINE_POSTER_THUMB
|
||||
return poster_url
|
||||
|
||||
def get_provider_name(self, provider):
|
||||
@@ -807,7 +807,7 @@ class Notifier(object):
|
||||
if response is not None and response.status_code >= 400 and response.status_code < 500:
|
||||
verify_msg = " Verify you notification agent settings are correct."
|
||||
|
||||
logger.error(u"Tautulli Notifiers :: {name} notification failed.{}".format(verify_msg, name=self.NAME))
|
||||
logger.error(u"Tautulli Notifiers :: {name} notification failed.{msg}".format(msg=verify_msg, name=self.NAME))
|
||||
|
||||
if err_msg:
|
||||
logger.error(u"Tautulli Notifiers :: {}".format(err_msg))
|
||||
@@ -1464,7 +1464,7 @@ class FACEBOOK(Notifier):
|
||||
|
||||
return facebook.auth_url(app_id=app_id,
|
||||
canvas_url=redirect_uri,
|
||||
perms=['user_managed_groups','publish_actions'])
|
||||
perms=['publish_to_groups'])
|
||||
|
||||
def _get_credentials(self, code=''):
|
||||
logger.info(u"Tautulli Notifiers :: Requesting access token from {name}.".format(name=self.NAME))
|
||||
@@ -3476,7 +3476,7 @@ class TWITTER(Notifier):
|
||||
poster_url = parameters.get('poster_url','')
|
||||
|
||||
# Hack to add media type to attachment
|
||||
if poster_url:
|
||||
if poster_url and not helpers.get_img_service():
|
||||
poster_url += '.png'
|
||||
|
||||
if self.config['incl_subject']:
|
||||
|
@@ -1,2 +1,2 @@
|
||||
PLEXPY_BRANCH = "beta"
|
||||
PLEXPY_RELEASE_VERSION = "v2.1.7-beta"
|
||||
PLEXPY_BRANCH = "master"
|
||||
PLEXPY_RELEASE_VERSION = "v2.1.9"
|
||||
|
@@ -56,7 +56,7 @@ import web_socket
|
||||
from plexpy.api2 import API2
|
||||
from plexpy.helpers import checked, addtoapi, get_ip, create_https_certificates, build_datatables_json
|
||||
from plexpy.session import get_session_info, get_session_user_id, allow_session_user, allow_session_library
|
||||
from plexpy.webauth import AuthController, requireAuth, member_of, name_is
|
||||
from plexpy.webauth import AuthController, requireAuth, member_of
|
||||
|
||||
|
||||
def serve_template(templatename, **kwargs):
|
||||
@@ -2826,6 +2826,8 @@ class WebInterface(object):
|
||||
"show_advanced_settings": plexpy.CONFIG.SHOW_ADVANCED_SETTINGS,
|
||||
"newsletter_dir": plexpy.CONFIG.NEWSLETTER_DIR,
|
||||
"newsletter_self_hosted": checked(plexpy.CONFIG.NEWSLETTER_SELF_HOSTED),
|
||||
"newsletter_auth": plexpy.CONFIG.NEWSLETTER_AUTH,
|
||||
"newsletter_password": plexpy.CONFIG.NEWSLETTER_PASSWORD,
|
||||
"newsletter_inline_styles": checked(plexpy.CONFIG.NEWSLETTER_INLINE_STYLES),
|
||||
"newsletter_custom_dir": plexpy.CONFIG.NEWSLETTER_CUSTOM_DIR
|
||||
}
|
||||
@@ -3220,7 +3222,7 @@ class WebInterface(object):
|
||||
@requireAuth(member_of("admin"))
|
||||
@addtoapi()
|
||||
def set_notifier_config(self, notifier_id=None, agent_id=None, **kwargs):
|
||||
""" Configure an exisitng notificaiton agent.
|
||||
""" Configure an existing notification agent.
|
||||
|
||||
```
|
||||
Required parameters:
|
||||
@@ -3339,10 +3341,10 @@ class WebInterface(object):
|
||||
return {'result': 'success', 'message': 'Notification queued.'}
|
||||
else:
|
||||
logger.debug(u"Unable to send %snotification, invalid notifier_id %s." % (test, notifier_id))
|
||||
return {'result': 'success', 'message': 'Invalid notifier id %s.' % notifier_id}
|
||||
return {'result': 'error', 'message': 'Invalid notifier id %s.' % notifier_id}
|
||||
else:
|
||||
logger.debug(u"Unable to send %snotification, no notifier_id received." % test)
|
||||
return {'result': 'success', 'message': 'No notifier id received.'}
|
||||
return {'result': 'error', 'message': 'No notifier id received.'}
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
@@ -3479,7 +3481,7 @@ class WebInterface(object):
|
||||
@requireAuth(member_of("admin"))
|
||||
@addtoapi()
|
||||
def set_mobile_device_config(self, mobile_device_id=None, **kwargs):
|
||||
""" Configure an exisitng notificaiton agent.
|
||||
""" Configure an existing notification agent.
|
||||
|
||||
```
|
||||
Required parameters:
|
||||
@@ -5676,7 +5678,7 @@ class WebInterface(object):
|
||||
@requireAuth(member_of("admin"))
|
||||
@addtoapi()
|
||||
def set_newsletter_config(self, newsletter_id=None, agent_id=None, **kwargs):
|
||||
""" Configure an exisitng newsletter agent.
|
||||
""" Configure an existing newsletter agent.
|
||||
|
||||
```
|
||||
Required parameters:
|
||||
@@ -5741,6 +5743,27 @@ class WebInterface(object):
|
||||
|
||||
@cherrypy.expose
|
||||
def newsletter(self, *args, **kwargs):
|
||||
request_uri = cherrypy.request.wsgi_environ['REQUEST_URI']
|
||||
if plexpy.CONFIG.NEWSLETTER_AUTH == 2:
|
||||
redirect_uri = request_uri.replace('/newsletter', '/newsletter_auth')
|
||||
raise cherrypy.HTTPRedirect(redirect_uri)
|
||||
|
||||
elif plexpy.CONFIG.NEWSLETTER_AUTH == 1 and plexpy.CONFIG.NEWSLETTER_PASSWORD:
|
||||
if len(args) >= 2 and args[0] == 'image':
|
||||
return self.newsletter_auth(*args, **kwargs)
|
||||
elif kwargs.pop('key', None) == plexpy.CONFIG.NEWSLETTER_PASSWORD:
|
||||
return self.newsletter_auth(*args, **kwargs)
|
||||
else:
|
||||
return serve_template(templatename="newsletter_auth.html",
|
||||
title="Newsletter Login",
|
||||
uri=request_uri)
|
||||
|
||||
else:
|
||||
return self.newsletter_auth(*args, **kwargs)
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth()
|
||||
def newsletter_auth(self, *args, **kwargs):
|
||||
if args:
|
||||
# Keep this for backwards compatibility for images through /newsletter/image
|
||||
if len(args) >= 2 and args[0] == 'image':
|
||||
|
Reference in New Issue
Block a user