Compare commits
33 Commits
v2.1.2-bet
...
v2.1.5-bet
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8e3fe7bfa2 | ||
![]() |
6f22c823be | ||
![]() |
34d7c67813 | ||
![]() |
862ed5ce9f | ||
![]() |
84406e6797 | ||
![]() |
19cf567366 | ||
![]() |
8af697a157 | ||
![]() |
76122bea5d | ||
![]() |
1a12422908 | ||
![]() |
2df9f0b48b | ||
![]() |
8540b80e57 | ||
![]() |
8ad565a444 | ||
![]() |
f91b6481b3 | ||
![]() |
826db082c9 | ||
![]() |
f3d64a7886 | ||
![]() |
031d078bc2 | ||
![]() |
04fcd78102 | ||
![]() |
53d1e0f541 | ||
![]() |
9719f0b25b | ||
![]() |
6d1d5bc822 | ||
![]() |
0d7bbe044d | ||
![]() |
b1dc5816a4 | ||
![]() |
476011a783 | ||
![]() |
e038c57c4c | ||
![]() |
a989a53750 | ||
![]() |
d8cfdea704 | ||
![]() |
ed4722c4ce | ||
![]() |
17ab5f05ed | ||
![]() |
71ab2248d7 | ||
![]() |
4fb4410552 | ||
![]() |
a915d2333f | ||
![]() |
aaf5a18251 | ||
![]() |
b90026801b |
35
CHANGELOG.md
35
CHANGELOG.md
@@ -1,5 +1,40 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v2.1.5-beta (2018-05-07)
|
||||||
|
|
||||||
|
* Newsletters:
|
||||||
|
* New: Added setting for a custom newsletter template folder.
|
||||||
|
* New: Added option to enable static newsletter URLs to retrieve the last sent scheduled newsletter.
|
||||||
|
* New: Added ability to change the newsletter output directory and filenames.
|
||||||
|
* New: Added option to save the newsletter file without sending it to a notification agent.
|
||||||
|
* Fix: Check for disabled image hosting setting.
|
||||||
|
* Fix: Cache newsletter images when refreshing the page.
|
||||||
|
* Fix: Refresh image from the Plex server when uploading to image hosting.
|
||||||
|
* Change: Allow all image hosting options with self-hosted newsletters.
|
||||||
|
* UI:
|
||||||
|
* Change: Don't retrieve recently added on the homepage if the Plex Cloud server is sleeping.
|
||||||
|
* Other:
|
||||||
|
* Fix: Imgur database upgrade migration.
|
||||||
|
|
||||||
|
|
||||||
|
## v2.1.4 (2018-05-05)
|
||||||
|
|
||||||
|
* Newsletters:
|
||||||
|
* Fix: Newsletter URL without an HTTP root.
|
||||||
|
|
||||||
|
|
||||||
|
## v2.1.3-beta (2018-05-04)
|
||||||
|
|
||||||
|
* Newsletters:
|
||||||
|
* Fix: HTTP root doubled in newsletter URL.
|
||||||
|
* Fix: Configuration would not open with failed hostname resolution.
|
||||||
|
* Fix: Schedule one day off when using weekday names in cron.
|
||||||
|
* Fix: Images not refreshing when changed in Plex.
|
||||||
|
* Fix: Cloudinary upload with non-ASCII image titles.
|
||||||
|
* Other:
|
||||||
|
* Fix: Potential XSS vulnerability in search.
|
||||||
|
|
||||||
|
|
||||||
## v2.1.2-beta (2018-05-01)
|
## v2.1.2-beta (2018-05-01)
|
||||||
|
|
||||||
* Newsletters:
|
* Newsletters:
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="body()">
|
<%def name="body()">
|
||||||
|
<% from plexpy import PLEX_SERVER_UP %>
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
% for section in config['home_sections']:
|
% for section in config['home_sections']:
|
||||||
% if section == 'current_activity':
|
% if section == 'current_activity':
|
||||||
@@ -22,7 +23,6 @@
|
|||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div id="currentActivity">
|
<div id="currentActivity">
|
||||||
<% from plexpy import PLEX_SERVER_UP %>
|
|
||||||
% if PLEX_SERVER_UP:
|
% if PLEX_SERVER_UP:
|
||||||
<div class="text-muted" id="dashboard-checking-activity"><i class="fa fa-refresh fa-spin"></i> Checking for activity...</div>
|
<div class="text-muted" id="dashboard-checking-activity"><i class="fa fa-refresh fa-spin"></i> Checking for activity...</div>
|
||||||
% elif config['pms_is_cloud']:
|
% elif config['pms_is_cloud']:
|
||||||
@@ -135,7 +135,17 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div id="recentlyAdded" style="margin-right: -15px;">
|
<div id="recentlyAdded" style="margin-right: -15px;">
|
||||||
|
% if PLEX_SERVER_UP:
|
||||||
<div class="text-muted"><i class="fa fa-refresh fa-spin"></i> Looking for new items...</div>
|
<div class="text-muted"><i class="fa fa-refresh fa-spin"></i> Looking for new items...</div>
|
||||||
|
% elif config['pms_is_cloud']:
|
||||||
|
<div class="text-muted">Plex Cloud server is sleeping.</div>
|
||||||
|
% else:
|
||||||
|
<div class="text-muted">There was an error communicating with your Plex Server.
|
||||||
|
% if _session['user_group'] == 'admin':
|
||||||
|
Check the <a href="logs">logs</a> and verify your server connection in the <a href="settings#tab_tabs-plex_media_server">settings</a>.
|
||||||
|
% endif
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
<br>
|
<br>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -222,6 +232,7 @@
|
|||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="javascriptIncludes()">
|
<%def name="javascriptIncludes()">
|
||||||
|
<% from plexpy import PLEX_SERVER_UP %>
|
||||||
<script src="${http_root}js/moment-with-locale.js"></script>
|
<script src="${http_root}js/moment-with-locale.js"></script>
|
||||||
<script src="${http_root}js/jquery.scrollbar.min.js"></script>
|
<script src="${http_root}js/jquery.scrollbar.min.js"></script>
|
||||||
<script src="${http_root}js/jquery.mousewheel.min.js"></script>
|
<script src="${http_root}js/jquery.mousewheel.min.js"></script>
|
||||||
@@ -254,7 +265,6 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<% from plexpy import PLEX_SERVER_UP %>
|
|
||||||
% if 'current_activity' in config['home_sections'] and PLEX_SERVER_UP:
|
% if 'current_activity' in config['home_sections'] and PLEX_SERVER_UP:
|
||||||
<script>
|
<script>
|
||||||
var defaultHandler = {
|
var defaultHandler = {
|
||||||
@@ -746,7 +756,7 @@
|
|||||||
getLibraryStats();
|
getLibraryStats();
|
||||||
</script>
|
</script>
|
||||||
% endif
|
% endif
|
||||||
% if 'recently_added' in config['home_sections']:
|
% if 'recently_added' in config['home_sections'] and PLEX_SERVER_UP:
|
||||||
<script>
|
<script>
|
||||||
function recentlyAdded(recently_added_count, recently_added_type) {
|
function recentlyAdded(recently_added_count, recently_added_type) {
|
||||||
showMsg("Loading recently added items...", true, false, 0);
|
showMsg("Loading recently added items...", true, false, 0);
|
||||||
|
@@ -166,6 +166,15 @@
|
|||||||
% endfor
|
% endfor
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-12" style="margin-top: 10px; padding-top: 10px; border-top: 1px solid #444;">
|
<div class="col-md-12" style="margin-top: 10px; padding-top: 10px; border-top: 1px solid #444;">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="newsletter_config_filename">Filename</label>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<input type="text" class="form-control" id="newsletter_config_filename" name="newsletter_config_filename" value="${newsletter['config']['filename']}" size="30">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="help-block">The filename to use when saving the newsletter (ending with <span class="inline-pre">.html</span>). You may use any of the <a href="#newsletter-text-sub-modal" data-toggle="modal">newsletter text parameters</a>. Leave blank for default.</p>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="friendly_name">Description</label>
|
<label for="friendly_name">Description</label>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -183,7 +192,14 @@
|
|||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" id="newsletter_config_formatted_checkbox" data-id="newsletter_config_formatted" class="checkboxes" value="1" ${checked(newsletter['config']['formatted'])}> Send newsletter as an HTML formatted Email
|
<input type="checkbox" id="newsletter_config_save_only_checkbox" data-id="newsletter_config_save_only" class="checkboxes" value="1" ${checked(newsletter['config']['save_only'])}> Save Newsletter File Only
|
||||||
|
</label>
|
||||||
|
<p class="help-block">Enable to save the newsletter file without sending it to any notification agent.</p>
|
||||||
|
<input type="hidden" id="newsletter_config_save_only" name="newsletter_config_save_only" value="${newsletter['config']['save_only']}">
|
||||||
|
</div>
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="newsletter_config_formatted_checkbox" data-id="newsletter_config_formatted" class="checkboxes" value="1" ${checked(newsletter['config']['formatted'])}> Send Newsletter as an HTML Formatted Email
|
||||||
</label>
|
</label>
|
||||||
<p class="help-block">Enable to send the newsletter as an HTML formatted Email. Disable to only send a subject and body message to a different notification agent.</p>
|
<p class="help-block">Enable to send the newsletter as an HTML formatted Email. Disable to only send a subject and body message to a different notification agent.</p>
|
||||||
<input type="hidden" id="newsletter_config_formatted" name="newsletter_config_formatted" value="${newsletter['config']['formatted']}">
|
<input type="hidden" id="newsletter_config_formatted" name="newsletter_config_formatted" value="${newsletter['config']['formatted']}">
|
||||||
@@ -458,6 +474,15 @@
|
|||||||
toggleCustomCron();
|
toggleCustomCron();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function validateFilename() {
|
||||||
|
var filename = $('#newsletter_config_filename').val();
|
||||||
|
if (filename !== '' && !(filename.endsWith('.html'))) {
|
||||||
|
showMsg('<i class="fa fa-times"></i> Failed to save newsletter. Invalid filename.', false, true, 5000, true);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
var $incl_libraries = $('#newsletter_config_incl_libraries').selectize({
|
var $incl_libraries = $('#newsletter_config_incl_libraries').selectize({
|
||||||
plugins: ['remove_button'],
|
plugins: ['remove_button'],
|
||||||
maxItems: null,
|
maxItems: null,
|
||||||
@@ -643,7 +668,9 @@
|
|||||||
if ($('#custom_cron').val() === '0'){
|
if ($('#custom_cron').val() === '0'){
|
||||||
$("#cron_value").val(cron_widget.cron('value'));
|
$("#cron_value").val(cron_widget.cron('value'));
|
||||||
}
|
}
|
||||||
doAjaxCall('set_newsletter_config', $(this), 'tabs', true, true, saveCallback);
|
if (validateFilename()){
|
||||||
|
doAjaxCall('set_newsletter_config', $(this), 'tabs', true, true, saveCallback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#delete-newsletter-item').click(function () {
|
$('#delete-newsletter-item').click(function () {
|
||||||
|
@@ -32,7 +32,7 @@
|
|||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
var frame = $('<iframe></iframe>', {
|
var frame = $('<iframe></iframe>', {
|
||||||
src: '${http_root}real_newsletter?${urllib.urlencode(kwargs) | n}',
|
src: 'real_newsletter?${urllib.urlencode(kwargs) | n}',
|
||||||
frameborder: '0',
|
frameborder: '0',
|
||||||
style: 'display: none; height: 100vh; width: 100vw;'
|
style: 'display: none; height: 100vh; width: 100vw;'
|
||||||
});
|
});
|
||||||
|
@@ -28,15 +28,17 @@
|
|||||||
|
|
||||||
<%def name="javascriptIncludes()">
|
<%def name="javascriptIncludes()">
|
||||||
<script>
|
<script>
|
||||||
|
var query_string = "${query.replace('"','\\"').replace('/','\\/') | n}";
|
||||||
|
|
||||||
$('#search_button').removeClass('btn-inactive');
|
$('#search_button').removeClass('btn-inactive');
|
||||||
$('#query').val("${query.replace('"','\\"') | n}").css({ right: '0', width: '250px' }).addClass('active');
|
$('#query').val(query_string).css({ right: '0', width: '250px' }).addClass('active');
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: 'get_search_results_children',
|
url: 'get_search_results_children',
|
||||||
type: "GET",
|
type: "POST",
|
||||||
async: true,
|
async: true,
|
||||||
data: {
|
data: {
|
||||||
query: "${query.replace('"','\\"') | n}",
|
query: query_string,
|
||||||
limit: 30
|
limit: 30
|
||||||
},
|
},
|
||||||
complete: function (xhr, status) {
|
complete: function (xhr, status) {
|
||||||
|
@@ -702,6 +702,17 @@
|
|||||||
The server URL that Tautulli will use to connect to your Plex server. Retrieved automatically.
|
The server URL that Tautulli will use to connect to your Plex server. Retrieved automatically.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group advanced-setting">
|
||||||
|
<label for="pms_url">Plex Server Identifier</label>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input type="text" class="form-control" id="pms_identifier" name="pms_identifier" value="${config['pms_identifier']}" size="30" readonly>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="help-block">
|
||||||
|
The unique identifier for your Plex server. Retrieved automatically.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<div class="checkbox advanced-setting">
|
<div class="checkbox advanced-setting">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" class="pms-settings" id="pms_url_manual" name="pms_url_manual" value="1" ${config['pms_url_manual']}> Manual Connection
|
<input type="checkbox" class="pms-settings" id="pms_url_manual" name="pms_url_manual" value="1" ${config['pms_url_manual']}> Manual Connection
|
||||||
@@ -728,7 +739,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<input type="hidden" id="pms_is_cloud" name="pms_is_cloud" value="${config['pms_is_cloud']}">
|
<input type="hidden" id="pms_is_cloud" name="pms_is_cloud" value="${config['pms_is_cloud']}">
|
||||||
<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;">
|
<input type="checkbox" name="server_changed" id="server_changed" value="1" style="display: none;">
|
||||||
|
|
||||||
<div class="form-group advanced-setting">
|
<div class="form-group advanced-setting">
|
||||||
@@ -956,10 +966,35 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="self_host_newsletter_options" style="overlfow: hidden; display: ${'block' if config['newsletter_self_hosted'] == 'checked' else 'none'}">
|
<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">
|
<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.<br>
|
Note: The <span class="inline-pre">${http_root}newsletter</span> endpoint on your domain must be publicly accessible from the internet.
|
||||||
Note: Newsletter images will be self-hosted regardless of the Image Hosting setting below.<br>
|
|
||||||
</p>
|
</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>
|
<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="checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="newsletter_static_url" name="newsletter_static_url" value="1" ${config['newsletter_static_url']}> Enable Static Newsletter URL
|
||||||
|
</label>
|
||||||
|
<p class="help-block">Enable static newsletter URLs to the last sent scheduled newsletter at <span class="inline-pre">${http_root}newsletter/id/<newsletter_id></span>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group advanced-setting">
|
||||||
|
<label for="newsletter_dir">Custom Newsletter Templates Folder</label>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input type="text" class="form-control" id="newsletter_custom_dir" name="newsletter_custom_dir" value="${config['newsletter_custom_dir']}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="help-block">Optional: Enter the full path to your custom newsletter templates folder. Leave blank for default.</p>
|
||||||
|
</div>
|
||||||
|
<div class="form-group advanced-setting">
|
||||||
|
<label for="newsletter_dir">Newsletter Output Directory</label>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input type="text" class="form-control" id="newsletter_dir" name="newsletter_dir" value="${config['newsletter_dir']}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="help-block">Enter the full path to where newsletter files will be saved.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="padded-header">
|
<div class="padded-header">
|
||||||
@@ -1084,7 +1119,7 @@
|
|||||||
Add a new newsletter agent, or configure an existing newsletter agent by clicking the settings icon on the right.
|
Add a new newsletter agent, or configure an existing newsletter agent by clicking the settings icon on the right.
|
||||||
</p>
|
</p>
|
||||||
<p class="help-block settings-warning" id="newsletter_upload_warning">
|
<p class="help-block settings-warning" id="newsletter_upload_warning">
|
||||||
Note: Either <a data-tab-destination="tabs-notifications" data-target="#notify_upload_posters">Image Hosting</a> or <a data-tab-destination="tabs-notifications" data-target="#newsletter_self_hosted">Self-Hosted Newsletters</a> must be enabled.</span>
|
Warning: <a data-tab-destination="tabs-notifications" data-target="#notify_upload_posters">Image Hosting</a> must be enabled for images to display on the newsletter.</span>
|
||||||
</p>
|
</p>
|
||||||
<br/>
|
<br/>
|
||||||
<div id="plexpy-newsletters-table">
|
<div id="plexpy-newsletters-table">
|
||||||
@@ -1173,14 +1208,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<label for="newsletter_dir">Newsletter Directory</label>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<input type="text" class="form-control directory-settings" id="newsletter_dir" name="newsletter_dir" value="${config['newsletter_dir']}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</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>
|
||||||
|
|
||||||
@@ -2605,10 +2632,10 @@ $(document).ready(function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function newsletterUploadEnabled() {
|
function newsletterUploadEnabled() {
|
||||||
if ($('#notify_upload_posters').val() !== '2' || $('#newsletter_self_hosted').is(':checked')) {
|
if ($('#notify_upload_posters').val() === '0') {
|
||||||
$('#newsletter_upload_warning').hide();
|
|
||||||
} else {
|
|
||||||
$('#newsletter_upload_warning').show();
|
$('#newsletter_upload_warning').show();
|
||||||
|
} else {
|
||||||
|
$('#newsletter_upload_warning').hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
newsletterUploadEnabled();
|
newsletterUploadEnabled();
|
||||||
|
@@ -188,7 +188,7 @@ DOCUMENTATION :: END
|
|||||||
},
|
},
|
||||||
complete: function (xhr, status) {
|
complete: function (xhr, status) {
|
||||||
$('#search-results-list').html(xhr.responseText);
|
$('#search-results-list').html(xhr.responseText);
|
||||||
$('#update_query_title').html(query_string)
|
$('#update_query_title').text(query_string)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -1,17 +1,24 @@
|
|||||||
% if data:
|
% if data:
|
||||||
<%
|
<%
|
||||||
import plexpy
|
import plexpy
|
||||||
from plexpy.helpers import grouper
|
from plexpy.helpers import grouper, get_img_service
|
||||||
|
|
||||||
recently_added = data['recently_added']
|
recently_added = data['recently_added']
|
||||||
if plexpy.CONFIG.NEWSLETTER_SELF_HOSTED and plexpy.CONFIG.HTTP_BASE_URL:
|
if plexpy.CONFIG.NEWSLETTER_SELF_HOSTED and plexpy.CONFIG.HTTP_BASE_URL:
|
||||||
base_url = plexpy.CONFIG.HTTP_BASE_URL + plexpy.HTTP_ROOT + 'newsletter/'
|
base_url = plexpy.CONFIG.HTTP_BASE_URL + plexpy.HTTP_ROOT + 'newsletter/'
|
||||||
base_url_image = base_url + 'image/'
|
|
||||||
elif preview:
|
elif preview:
|
||||||
base_url = 'newsletter/'
|
base_url = 'newsletter/'
|
||||||
base_url_image = base_url + 'image/'
|
|
||||||
else:
|
else:
|
||||||
base_url = base_url_image = ''
|
base_url = ''
|
||||||
|
|
||||||
|
service = get_img_service()
|
||||||
|
if get_img_service(include_self=True) == 'self-hosted' and plexpy.CONFIG.HTTP_BASE_URL:
|
||||||
|
base_url_image = plexpy.CONFIG.HTTP_BASE_URL + plexpy.HTTP_ROOT + 'newsletter/image/'
|
||||||
|
elif service and preview:
|
||||||
|
base_url_image = 'newsletter/image/'
|
||||||
|
else:
|
||||||
|
base_url_image = ''
|
||||||
|
|
||||||
%>
|
%>
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
@@ -83,6 +90,14 @@
|
|||||||
/* -------------------------------------
|
/* -------------------------------------
|
||||||
HEADER, FOOTER, MAIN
|
HEADER, FOOTER, MAIN
|
||||||
------------------------------------- */
|
------------------------------------- */
|
||||||
|
.local-preview-note {
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.local-preview-note p {
|
||||||
|
color: #282A2D;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
.main {
|
.main {
|
||||||
background: #282A2D;
|
background: #282A2D;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
@@ -608,6 +623,9 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;font-size: 14px;line-height: 1.4;margin: 0;padding: 0;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
|
<body class="" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;font-size: 14px;line-height: 1.4;margin: 0;padding: 0;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
|
||||||
|
% if preview and service:
|
||||||
|
<div class="local-preview-note" style="text-align: center;padding-top: 10px;"><p style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-weight: 400;margin: 0;color: #282A2D;font-size: 12px;">Note: Local preview only - images have not been uploaded to ${service.capitalize()}</p></div> <!-- IGNORE SAVE -->
|
||||||
|
% endif
|
||||||
<table border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate;mso-table-lspace: 0pt;mso-table-rspace: 0pt;width: 100%;">
|
<table border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate;mso-table-lspace: 0pt;mso-table-rspace: 0pt;width: 100%;">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="container" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 14px;vertical-align: top;display: block;max-width: 1042px;padding: 10px;width: 1042px;margin: 0 auto !important;">
|
<td class="container" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 14px;vertical-align: top;display: block;max-width: 1042px;padding: 10px;width: 1042px;margin: 0 auto !important;">
|
||||||
|
@@ -1,17 +1,24 @@
|
|||||||
% if data:
|
% if data:
|
||||||
<%
|
<%
|
||||||
import plexpy
|
import plexpy
|
||||||
from plexpy.helpers import grouper
|
from plexpy.helpers import grouper, get_img_service
|
||||||
|
|
||||||
recently_added = data['recently_added']
|
recently_added = data['recently_added']
|
||||||
if plexpy.CONFIG.NEWSLETTER_SELF_HOSTED and plexpy.CONFIG.HTTP_BASE_URL:
|
if plexpy.CONFIG.NEWSLETTER_SELF_HOSTED and plexpy.CONFIG.HTTP_BASE_URL:
|
||||||
base_url = plexpy.CONFIG.HTTP_BASE_URL + plexpy.HTTP_ROOT + 'newsletter/'
|
base_url = plexpy.CONFIG.HTTP_BASE_URL + plexpy.HTTP_ROOT + 'newsletter/'
|
||||||
base_url_image = base_url + 'image/'
|
|
||||||
elif preview:
|
elif preview:
|
||||||
base_url = 'newsletter/'
|
base_url = 'newsletter/'
|
||||||
base_url_image = base_url + 'image/'
|
|
||||||
else:
|
else:
|
||||||
base_url = base_url_image = ''
|
base_url = ''
|
||||||
|
|
||||||
|
service = get_img_service()
|
||||||
|
if get_img_service(include_self=True) == 'self-hosted' and plexpy.CONFIG.HTTP_BASE_URL:
|
||||||
|
base_url_image = plexpy.CONFIG.HTTP_BASE_URL + plexpy.HTTP_ROOT + 'newsletter/image/'
|
||||||
|
elif service and preview:
|
||||||
|
base_url_image = 'newsletter/image/'
|
||||||
|
else:
|
||||||
|
base_url_image = ''
|
||||||
|
|
||||||
%>
|
%>
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
@@ -83,6 +90,14 @@
|
|||||||
/* -------------------------------------
|
/* -------------------------------------
|
||||||
HEADER, FOOTER, MAIN
|
HEADER, FOOTER, MAIN
|
||||||
------------------------------------- */
|
------------------------------------- */
|
||||||
|
.local-preview-note {
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.local-preview-note p {
|
||||||
|
color: #282A2D;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
.main {
|
.main {
|
||||||
background: #282A2D;
|
background: #282A2D;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
@@ -608,6 +623,9 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="">
|
<body class="">
|
||||||
|
% if preview and service:
|
||||||
|
<div class="local-preview-note"><p>Note: Local preview only - images have not been uploaded to ${service.capitalize()}</p></div> <!-- IGNORE SAVE -->
|
||||||
|
% endif
|
||||||
<table border="0" cellpadding="0" cellspacing="0" class="body">
|
<table border="0" cellpadding="0" cellspacing="0" class="body">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="container">
|
<td class="container">
|
||||||
|
@@ -9,7 +9,7 @@ __all__ = ('AllExpression', 'RangeExpression', 'WeekdayRangeExpression',
|
|||||||
'WeekdayPositionExpression', 'LastDayOfMonthExpression')
|
'WeekdayPositionExpression', 'LastDayOfMonthExpression')
|
||||||
|
|
||||||
|
|
||||||
WEEKDAYS = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']
|
WEEKDAYS = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']
|
||||||
MONTHS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec']
|
MONTHS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec']
|
||||||
|
|
||||||
|
|
||||||
|
@@ -648,7 +648,7 @@ def dbcheck():
|
|||||||
'CREATE TABLE IF NOT EXISTS newsletter_log (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER, '
|
'CREATE TABLE IF NOT EXISTS newsletter_log (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER, '
|
||||||
'newsletter_id INTEGER, agent_id INTEGER, agent_name TEXT, notify_action TEXT, '
|
'newsletter_id INTEGER, agent_id INTEGER, agent_name TEXT, notify_action TEXT, '
|
||||||
'subject_text TEXT, body_text TEXT, message_text TEXT, start_date TEXT, end_date TEXT, '
|
'subject_text TEXT, body_text TEXT, message_text TEXT, start_date TEXT, end_date TEXT, '
|
||||||
'start_time INTEGER, end_time INTEGER, uuid TEXT UNIQUE, success INTEGER DEFAULT 0)'
|
'start_time INTEGER, end_time INTEGER, uuid TEXT UNIQUE, filename TEXT, success INTEGER DEFAULT 0)'
|
||||||
)
|
)
|
||||||
|
|
||||||
# recently_added table :: This table keeps record of recently added items
|
# recently_added table :: This table keeps record of recently added items
|
||||||
@@ -1495,6 +1495,15 @@ def dbcheck():
|
|||||||
'ALTER TABLE newsletter_log ADD COLUMN end_time INTEGER'
|
'ALTER TABLE newsletter_log ADD COLUMN end_time INTEGER'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Upgrade newsletter_log table from earlier versions
|
||||||
|
try:
|
||||||
|
c_db.execute('SELECT filename FROM newsletter_log')
|
||||||
|
except sqlite3.OperationalError:
|
||||||
|
logger.debug(u"Altering database. Updating database table newsletter_log.")
|
||||||
|
c_db.execute(
|
||||||
|
'ALTER TABLE newsletter_log ADD COLUMN filename TEXT'
|
||||||
|
)
|
||||||
|
|
||||||
# Upgrade library_sections table from earlier versions (remove UNIQUE constraint on section_id)
|
# Upgrade library_sections table from earlier versions (remove UNIQUE constraint on section_id)
|
||||||
try:
|
try:
|
||||||
result = c_db.execute('SELECT SQL FROM sqlite_master WHERE type="table" AND name="library_sections"').fetchone()
|
result = c_db.execute('SELECT SQL FROM sqlite_master WHERE type="table" AND name="library_sections"').fetchone()
|
||||||
@@ -1694,9 +1703,9 @@ def dbcheck():
|
|||||||
for row in result:
|
for row in result:
|
||||||
img_hash = notification_handler.set_hash_image_info(
|
img_hash = notification_handler.set_hash_image_info(
|
||||||
rating_key=row['rating_key'], width=1000, height=1500, fallback='poster')
|
rating_key=row['rating_key'], width=1000, height=1500, fallback='poster')
|
||||||
data_factory.set_imgur_info(img_hash=img_hash, imgur_title=row['poster_title'],
|
data_factory.set_img_info(img_hash=img_hash, imgur_title=row['poster_title'],
|
||||||
imgur_url=row['poster_url'], delete_hash=row['delete_hash'],
|
imgur_url=row['poster_url'], delete_hash=row['delete_hash'],
|
||||||
service='imgur')
|
service='imgur')
|
||||||
|
|
||||||
db.action('DROP TABLE poster_urls')
|
db.action('DROP TABLE poster_urls')
|
||||||
except sqlite3.OperationalError:
|
except sqlite3.OperationalError:
|
||||||
|
@@ -519,13 +519,15 @@ NEWSLETTER_PARAMETERS = [
|
|||||||
'category': 'Global',
|
'category': 'Global',
|
||||||
'parameters': [
|
'parameters': [
|
||||||
{'name': 'Server Name', 'type': 'str', 'value': 'server_name', 'description': 'The name of your Plex Server.'},
|
{'name': 'Server Name', 'type': 'str', 'value': 'server_name', 'description': 'The name of your Plex Server.'},
|
||||||
{'name': 'Start Date', 'type': 'str', 'value': 'start_date', 'description': 'The start date of the newesletter.'},
|
{'name': 'Start Date', 'type': 'str', 'value': 'start_date', 'description': 'The start date of the newsletter.'},
|
||||||
{'name': 'End Date', 'type': 'str', 'value': 'end_date', 'description': 'The end date of the newesletter.'},
|
{'name': 'End Date', 'type': 'str', 'value': 'end_date', 'description': 'The end date of the newsletter.'},
|
||||||
{'name': 'Week Number', 'type': 'int', 'value': 'week_number', 'description': 'The week number of the year.'},
|
{'name': 'Week Number', 'type': 'int', 'value': 'week_number', 'description': 'The week number of the year.'},
|
||||||
{'name': 'Newsletter Time Frame', 'type': 'int', 'value': 'newsletter_time_frame', 'description': 'The time frame included in the newsletter.'},
|
{'name': 'Newsletter Time Frame', 'type': 'int', 'value': 'newsletter_time_frame', 'description': 'The time frame included in the newsletter.'},
|
||||||
{'name': 'Newsletter Time Frame Units', 'type': 'str', 'value': 'newsletter_time_frame_units', 'description': 'The time frame units included in the newsletter.'},
|
{'name': 'Newsletter Time Frame Units', 'type': 'str', 'value': 'newsletter_time_frame_units', 'description': 'The time frame units included in the newsletter.'},
|
||||||
{'name': 'Newsletter URL', 'type': 'str', 'value': 'newsletter_url', 'description': 'The self-hosted URL to the newsletter.'},
|
{'name': 'Newsletter URL', 'type': 'str', 'value': 'newsletter_url', 'description': 'The self-hosted URL to the newsletter.'},
|
||||||
|
{'name': 'Newsletter Static URL', 'type': 'str', 'value': 'newsletter_static_url', 'description': 'The static self-hosted URL to the latest scheduled newsletter for the agent.'},
|
||||||
{'name': 'Newsletter UUID', 'type': 'str', 'value': 'newsletter_uuid', 'description': 'The unique identifier for the newsletter.'},
|
{'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 ID number for the newsletter agent.'},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@@ -312,9 +312,11 @@ _CONFIG_DEFINITIONS = {
|
|||||||
'MONITOR_REMOTE_ACCESS': (int, 'Monitoring', 0),
|
'MONITOR_REMOTE_ACCESS': (int, 'Monitoring', 0),
|
||||||
'MONITORING_INTERVAL': (int, 'Monitoring', 60),
|
'MONITORING_INTERVAL': (int, 'Monitoring', 60),
|
||||||
'MONITORING_USE_WEBSOCKET': (int, 'Monitoring', 0),
|
'MONITORING_USE_WEBSOCKET': (int, 'Monitoring', 0),
|
||||||
|
'NEWSLETTER_CUSTOM_DIR': (str, 'Newsletter', ''),
|
||||||
'NEWSLETTER_TEMPLATES': (str, 'Newsletter', 'newsletters'),
|
'NEWSLETTER_TEMPLATES': (str, 'Newsletter', 'newsletters'),
|
||||||
'NEWSLETTER_DIR': (str, 'Newsletter', ''),
|
'NEWSLETTER_DIR': (str, 'Newsletter', ''),
|
||||||
'NEWSLETTER_SELF_HOSTED': (int, 'Newsletter', 0),
|
'NEWSLETTER_SELF_HOSTED': (int, 'Newsletter', 0),
|
||||||
|
'NEWSLETTER_STATIC_URL': (int, 'Newsletter', 0),
|
||||||
'NMA_APIKEY': (str, 'NMA', ''),
|
'NMA_APIKEY': (str, 'NMA', ''),
|
||||||
'NMA_ENABLED': (int, 'NMA', 0),
|
'NMA_ENABLED': (int, 'NMA', 0),
|
||||||
'NMA_PRIORITY': (int, 'NMA', 0),
|
'NMA_PRIORITY': (int, 'NMA', 0),
|
||||||
|
@@ -792,8 +792,8 @@ def upload_to_cloudinary(img_data, img_title='', rating_key='', fallback=''):
|
|||||||
try:
|
try:
|
||||||
response = upload('data:image/png;base64,{}'.format(base64.b64encode(img_data)),
|
response = upload('data:image/png;base64,{}'.format(base64.b64encode(img_data)),
|
||||||
public_id='{}_{}'.format(fallback, rating_key),
|
public_id='{}_{}'.format(fallback, rating_key),
|
||||||
tags=[fallback, rating_key],
|
tags=[fallback, str(rating_key)],
|
||||||
context={'title': img_title, 'rating_key': rating_key, 'fallback': fallback})
|
context={'title': img_title.encode('utf-8'), 'rating_key': str(rating_key), 'fallback': fallback})
|
||||||
logger.debug(u"Tautulli Helpers :: Image '{}' ({}) uploaded to Cloudinary.".format(img_title, fallback))
|
logger.debug(u"Tautulli Helpers :: Image '{}' ({}) uploaded to Cloudinary.".format(img_title, fallback))
|
||||||
img_url = response.get('url', '')
|
img_url = response.get('url', '')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -834,13 +834,14 @@ def cloudinary_transform(rating_key=None, width=1000, height=1500, opacity=100,
|
|||||||
api_secret=plexpy.CONFIG.CLOUDINARY_API_SECRET
|
api_secret=plexpy.CONFIG.CLOUDINARY_API_SECRET
|
||||||
)
|
)
|
||||||
|
|
||||||
img_options = {}
|
img_options = {'format': img_format,
|
||||||
|
'version': int(time.time())}
|
||||||
|
|
||||||
if width != 1000:
|
if width != 1000:
|
||||||
img_options['width'] = width
|
img_options['width'] = str(width)
|
||||||
img_options['crop'] = 'fill'
|
img_options['crop'] = 'fill'
|
||||||
if height != 1500:
|
if height != 1500:
|
||||||
img_options['height'] = height
|
img_options['height'] = str(height)
|
||||||
img_options['crop'] = 'fill'
|
img_options['crop'] = 'fill'
|
||||||
if opacity != 100:
|
if opacity != 100:
|
||||||
img_options['opacity'] = opacity
|
img_options['opacity'] = opacity
|
||||||
@@ -849,14 +850,11 @@ def cloudinary_transform(rating_key=None, width=1000, height=1500, opacity=100,
|
|||||||
if blur != 0:
|
if blur != 0:
|
||||||
img_options['effect'] = 'blur:{}'.format(blur * 100)
|
img_options['effect'] = 'blur:{}'.format(blur * 100)
|
||||||
|
|
||||||
if img_options:
|
try:
|
||||||
img_options['format'] = img_format
|
url, options = cloudinary_url('{}_{}'.format(fallback, rating_key), **img_options)
|
||||||
|
logger.debug(u"Tautulli Helpers :: Image '{}' ({}) transformed on Cloudinary.".format(img_title, fallback))
|
||||||
try:
|
except Exception as e:
|
||||||
url, options = cloudinary_url('{}_{}'.format(fallback, rating_key), **img_options)
|
logger.error(u"Tautulli Helpers :: Unable to transform image '{}' ({}) on Cloudinary: {}".format(img_title, fallback, e))
|
||||||
logger.debug(u"Tautulli Helpers :: Image '{}' ({}) transformed on Cloudinary.".format(img_title, fallback))
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(u"Tautulli Helpers :: Unable to transform image '{}' ({}) on Cloudinary: {}".format(img_title, fallback, e))
|
|
||||||
|
|
||||||
return url
|
return url
|
||||||
|
|
||||||
@@ -1072,7 +1070,10 @@ def get_plexpy_url(hostname=None):
|
|||||||
s.connect(('<broadcast>', 0))
|
s.connect(('<broadcast>', 0))
|
||||||
hostname = s.getsockname()[0]
|
hostname = s.getsockname()[0]
|
||||||
except socket.error:
|
except socket.error:
|
||||||
hostname = socket.gethostbyname(socket.gethostname())
|
try:
|
||||||
|
hostname = socket.gethostbyname(socket.gethostname())
|
||||||
|
except socket.gaierror:
|
||||||
|
pass
|
||||||
|
|
||||||
if not hostname:
|
if not hostname:
|
||||||
hostname = 'localhost'
|
hostname = 'localhost'
|
||||||
|
@@ -86,7 +86,8 @@ def notify(newsletter_id=None, notify_action=None, **kwargs):
|
|||||||
body = newsletter_config['body']
|
body = newsletter_config['body']
|
||||||
message = newsletter_config['message']
|
message = newsletter_config['message']
|
||||||
|
|
||||||
newsletter_agent = newsletters.get_agent_class(agent_id=newsletter_config['agent_id'],
|
newsletter_agent = newsletters.get_agent_class(newsletter_id=newsletter_id,
|
||||||
|
agent_id=newsletter_config['agent_id'],
|
||||||
config=newsletter_config['config'],
|
config=newsletter_config['config'],
|
||||||
email_config=newsletter_config['email_config'],
|
email_config=newsletter_config['email_config'],
|
||||||
subject=subject,
|
subject=subject,
|
||||||
@@ -100,6 +101,7 @@ def notify(newsletter_id=None, notify_action=None, **kwargs):
|
|||||||
subject=newsletter_agent.subject_formatted,
|
subject=newsletter_agent.subject_formatted,
|
||||||
body=newsletter_agent.body_formatted,
|
body=newsletter_agent.body_formatted,
|
||||||
message=newsletter_agent.message_formatted,
|
message=newsletter_agent.message_formatted,
|
||||||
|
filename=newsletter_agent.filename_formatted,
|
||||||
start_date=newsletter_agent.start_date.format('YYYY-MM-DD'),
|
start_date=newsletter_agent.start_date.format('YYYY-MM-DD'),
|
||||||
end_date=newsletter_agent.end_date.format('YYYY-MM-DD'),
|
end_date=newsletter_agent.end_date.format('YYYY-MM-DD'),
|
||||||
start_time=newsletter_agent.start_time,
|
start_time=newsletter_agent.start_time,
|
||||||
@@ -114,7 +116,7 @@ def notify(newsletter_id=None, notify_action=None, **kwargs):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def set_notify_state(newsletter, notify_action, subject, body, message,
|
def set_notify_state(newsletter, notify_action, subject, body, message, filename,
|
||||||
start_date, end_date, start_time, end_time, newsletter_uuid):
|
start_date, end_date, start_time, end_time, newsletter_uuid):
|
||||||
|
|
||||||
if newsletter and notify_action:
|
if newsletter and notify_action:
|
||||||
@@ -133,7 +135,8 @@ def set_notify_state(newsletter, notify_action, subject, body, message,
|
|||||||
'start_date': start_date,
|
'start_date': start_date,
|
||||||
'end_date': end_date,
|
'end_date': end_date,
|
||||||
'start_time': start_time,
|
'start_time': start_time,
|
||||||
'end_time': end_time}
|
'end_time': end_time,
|
||||||
|
'filename': filename}
|
||||||
|
|
||||||
db.upsert(table_name='newsletter_log', key_dict=keys, value_dict=values)
|
db.upsert(table_name='newsletter_log', key_dict=keys, value_dict=values)
|
||||||
return db.last_insert_id()
|
return db.last_insert_id()
|
||||||
@@ -149,20 +152,29 @@ def set_notify_success(newsletter_log_id):
|
|||||||
db.upsert(table_name='newsletter_log', key_dict=keys, value_dict=values)
|
db.upsert(table_name='newsletter_log', key_dict=keys, value_dict=values)
|
||||||
|
|
||||||
|
|
||||||
def get_newsletter(newsletter_uuid):
|
def get_newsletter(newsletter_uuid=None, newsletter_id=None):
|
||||||
db = database.MonitorDatabase()
|
db = database.MonitorDatabase()
|
||||||
result = db.select_single('SELECT newsletter_id, start_date, end_date FROM newsletter_log '
|
|
||||||
'WHERE uuid = ?', [newsletter_uuid])
|
if newsletter_uuid:
|
||||||
|
result = db.select_single('SELECT newsletter_id, start_date, end_date, uuid, filename FROM newsletter_log '
|
||||||
|
'WHERE uuid = ?', [newsletter_uuid])
|
||||||
|
elif newsletter_id:
|
||||||
|
result = db.select_single('SELECT newsletter_id, start_date, end_date, uuid, filename FROM newsletter_log '
|
||||||
|
'WHERE newsletter_id = ? AND notify_action != "test" '
|
||||||
|
'ORDER BY timestamp DESC LIMIT 1', [newsletter_id])
|
||||||
|
else:
|
||||||
|
result = None
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
newsletter_id = result['newsletter_id']
|
newsletter_id = result['newsletter_id']
|
||||||
|
newsletter_uuid = result['uuid']
|
||||||
start_date = result['start_date']
|
start_date = result['start_date']
|
||||||
end_date = result['end_date']
|
end_date = result['end_date']
|
||||||
|
newsletter_file = result['filename'] or 'newsletter_%s-%s_%s.html' % (start_date.replace('-', ''),
|
||||||
|
end_date.replace('-', ''),
|
||||||
|
newsletter_uuid)
|
||||||
|
|
||||||
newsletter_file = 'newsletter_%s-%s_%s.html' % (start_date.replace('-', ''),
|
newsletter_folder = plexpy.CONFIG.NEWSLETTER_DIR or os.path.join(plexpy.DATA_DIR, 'newsletters')
|
||||||
end_date.replace('-', ''),
|
|
||||||
newsletter_uuid)
|
|
||||||
newsletter_folder = plexpy.CONFIG.NEWSLETTER_DIR
|
|
||||||
newsletter_file_fp = os.path.join(newsletter_folder, newsletter_file)
|
newsletter_file_fp = os.path.join(newsletter_folder, newsletter_file)
|
||||||
|
|
||||||
if newsletter_file in os.listdir(newsletter_folder):
|
if newsletter_file in os.listdir(newsletter_folder):
|
||||||
@@ -173,4 +185,4 @@ def get_newsletter(newsletter_uuid):
|
|||||||
except OSError as e:
|
except OSError as e:
|
||||||
logger.error(u"Tautulli NewsletterHandler :: Failed to retrieve newsletter '%s': %s" % (newsletter_uuid, e))
|
logger.error(u"Tautulli NewsletterHandler :: Failed to retrieve newsletter '%s': %s" % (newsletter_uuid, e))
|
||||||
else:
|
else:
|
||||||
logger.warn(u"Tautulli NewsletterHandler :: Newsletter '%s' file is missing." % newsletter_uuid)
|
logger.warn(u"Tautulli NewsletterHandler :: Newsletter file '%s' is missing." % newsletter_file)
|
||||||
|
@@ -63,12 +63,13 @@ def available_notification_actions():
|
|||||||
return actions
|
return actions
|
||||||
|
|
||||||
|
|
||||||
def get_agent_class(agent_id=None, config=None, email_config=None, start_date=None, end_date=None,
|
def get_agent_class(newsletter_id=None, agent_id=None, config=None, email_config=None, start_date=None, end_date=None,
|
||||||
subject=None, body=None, message=None):
|
subject=None, body=None, message=None):
|
||||||
if str(agent_id).isdigit():
|
if str(agent_id).isdigit():
|
||||||
agent_id = int(agent_id)
|
agent_id = int(agent_id)
|
||||||
|
|
||||||
kwargs = {'config': config,
|
kwargs = {'newsletter_id': newsletter_id,
|
||||||
|
'config': config,
|
||||||
'email_config': email_config,
|
'email_config': email_config,
|
||||||
'start_date': start_date,
|
'start_date': start_date,
|
||||||
'end_date': end_date,
|
'end_date': end_date,
|
||||||
@@ -138,7 +139,8 @@ def get_newsletter_config(newsletter_id=None):
|
|||||||
subject = result.pop('subject')
|
subject = result.pop('subject')
|
||||||
body = result.pop('body')
|
body = result.pop('body')
|
||||||
message = result.pop('message')
|
message = result.pop('message')
|
||||||
newsletter_agent = get_agent_class(agent_id=result['agent_id'], config=config, email_config=email_config,
|
newsletter_agent = get_agent_class(newsletter_id=newsletter_id, agent_id=result['agent_id'],
|
||||||
|
config=config, email_config=email_config,
|
||||||
subject=subject, body=body, message=message)
|
subject=subject, body=body, message=message)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(u"Tautulli Newsletters :: Failed to get newsletter config options: %s." % e)
|
logger.error(u"Tautulli Newsletters :: Failed to get newsletter config options: %s." % e)
|
||||||
@@ -223,7 +225,8 @@ def set_newsletter_config(newsletter_id=None, agent_id=None, **kwargs):
|
|||||||
body = kwargs.pop('body')
|
body = kwargs.pop('body')
|
||||||
message = kwargs.pop('message')
|
message = kwargs.pop('message')
|
||||||
|
|
||||||
agent_class = get_agent_class(agent_id=agent['id'], config=newsletter_config, email_config=email_config,
|
agent_class = get_agent_class(newsletter_id=newsletter_id, agent_id=agent['id'],
|
||||||
|
config=newsletter_config, email_config=email_config,
|
||||||
subject=subject, body=body, message=message)
|
subject=subject, body=body, message=message)
|
||||||
|
|
||||||
keys = {'id': newsletter_id}
|
keys = {'id': newsletter_id}
|
||||||
@@ -267,8 +270,11 @@ def send_newsletter(newsletter_id=None, subject=None, body=None, message=None, n
|
|||||||
|
|
||||||
|
|
||||||
def serve_template(templatename, **kwargs):
|
def serve_template(templatename, **kwargs):
|
||||||
interface_dir = os.path.join(str(plexpy.PROG_DIR), 'data/interfaces/')
|
if plexpy.CONFIG.NEWSLETTER_CUSTOM_DIR:
|
||||||
template_dir = os.path.join(str(interface_dir), plexpy.CONFIG.NEWSLETTER_TEMPLATES)
|
template_dir = plexpy.CONFIG.NEWSLETTER_CUSTOM_DIR
|
||||||
|
else:
|
||||||
|
interface_dir = os.path.join(str(plexpy.PROG_DIR), 'data/interfaces/')
|
||||||
|
template_dir = os.path.join(str(interface_dir), plexpy.CONFIG.NEWSLETTER_TEMPLATES)
|
||||||
|
|
||||||
_hplookup = TemplateLookup(directories=[template_dir], default_filters=['unicode', 'h'])
|
_hplookup = TemplateLookup(directories=[template_dir], default_filters=['unicode', 'h'])
|
||||||
|
|
||||||
@@ -299,22 +305,26 @@ class Newsletter(object):
|
|||||||
'time_frame': 7,
|
'time_frame': 7,
|
||||||
'time_frame_units': 'days',
|
'time_frame_units': 'days',
|
||||||
'formatted': 1,
|
'formatted': 1,
|
||||||
'notifier_id': 0}
|
'notifier_id': 0,
|
||||||
|
'filename': '',
|
||||||
|
'save_only': 0}
|
||||||
_DEFAULT_EMAIL_CONFIG = EMAIL().return_default_config()
|
_DEFAULT_EMAIL_CONFIG = EMAIL().return_default_config()
|
||||||
_DEFAULT_EMAIL_CONFIG['from_name'] = 'Tautulli Newsletter'
|
_DEFAULT_EMAIL_CONFIG['from_name'] = 'Tautulli Newsletter'
|
||||||
_DEFAULT_EMAIL_CONFIG['notifier_id'] = 0
|
_DEFAULT_EMAIL_CONFIG['notifier_id'] = 0
|
||||||
_DEFAULT_SUBJECT = 'Tautulli Newsletter'
|
_DEFAULT_SUBJECT = 'Tautulli Newsletter'
|
||||||
_DEFAULT_BODY = 'View the newsletter here: {newsletter_url}'
|
_DEFAULT_BODY = 'View the newsletter here: {newsletter_url}'
|
||||||
_DEFAULT_MESSAGE = ''
|
_DEFAULT_MESSAGE = ''
|
||||||
|
_DEFAULT_FILENAME = 'newsletter_{newsletter_uuid}.html'
|
||||||
_TEMPLATE_MASTER = ''
|
_TEMPLATE_MASTER = ''
|
||||||
_TEMPLATE = ''
|
_TEMPLATE = ''
|
||||||
|
|
||||||
def __init__(self, config=None, email_config=None, start_date=None, end_date=None,
|
def __init__(self, newsletter_id=None, config=None, email_config=None, start_date=None, end_date=None,
|
||||||
subject=None, body=None, message=None):
|
subject=None, body=None, message=None):
|
||||||
self.config = self.set_config(config=config, default=self._DEFAULT_CONFIG)
|
self.config = self.set_config(config=config, default=self._DEFAULT_CONFIG)
|
||||||
self.email_config = self.set_config(config=email_config, default=self._DEFAULT_EMAIL_CONFIG)
|
self.email_config = self.set_config(config=email_config, default=self._DEFAULT_EMAIL_CONFIG)
|
||||||
self.uuid = generate_newsletter_uuid()
|
self.uuid = generate_newsletter_uuid()
|
||||||
|
|
||||||
|
self.newsletter_id = newsletter_id
|
||||||
self.start_date = None
|
self.start_date = None
|
||||||
self.end_date = None
|
self.end_date = None
|
||||||
|
|
||||||
@@ -346,7 +356,13 @@ class Newsletter(object):
|
|||||||
self.subject = subject or self._DEFAULT_SUBJECT
|
self.subject = subject or self._DEFAULT_SUBJECT
|
||||||
self.body = body or self._DEFAULT_BODY
|
self.body = body or self._DEFAULT_BODY
|
||||||
self.message = message or self._DEFAULT_MESSAGE
|
self.message = message or self._DEFAULT_MESSAGE
|
||||||
|
self.filename = self.config['filename'] or self._DEFAULT_FILENAME
|
||||||
|
|
||||||
|
if not self.filename.endswith('.html'):
|
||||||
|
self.filename += '.html'
|
||||||
|
|
||||||
self.subject_formatted, self.body_formatted, self.message_formatted = self.build_text()
|
self.subject_formatted, self.body_formatted, self.message_formatted = self.build_text()
|
||||||
|
self.filename_formatted = self.build_filename()
|
||||||
|
|
||||||
self.data = {}
|
self.data = {}
|
||||||
self.newsletter = None
|
self.newsletter = None
|
||||||
@@ -421,13 +437,15 @@ class Newsletter(object):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
self._save()
|
self._save()
|
||||||
|
|
||||||
|
if self.config['save_only']:
|
||||||
|
return True
|
||||||
|
|
||||||
return self._send()
|
return self._send()
|
||||||
|
|
||||||
def _save(self):
|
def _save(self):
|
||||||
newsletter_file = 'newsletter_%s-%s_%s.html' % (self.start_date.format('YYYYMMDD'),
|
newsletter_file = self.filename_formatted
|
||||||
self.end_date.format('YYYYMMDD'),
|
newsletter_folder = plexpy.CONFIG.NEWSLETTER_DIR or os.path.join(plexpy.DATA_DIR, 'newsletters')
|
||||||
self.uuid)
|
|
||||||
newsletter_folder = plexpy.CONFIG.NEWSLETTER_DIR
|
|
||||||
newsletter_file_fp = os.path.join(newsletter_folder, newsletter_file)
|
newsletter_file_fp = os.path.join(newsletter_folder, newsletter_file)
|
||||||
|
|
||||||
# In case the user has deleted it manually
|
# In case the user has deleted it manually
|
||||||
@@ -440,9 +458,9 @@ class Newsletter(object):
|
|||||||
if '<!-- IGNORE SAVE -->' not in line:
|
if '<!-- IGNORE SAVE -->' not in line:
|
||||||
n_file.write(line + '\r\n')
|
n_file.write(line + '\r\n')
|
||||||
|
|
||||||
logger.info(u"Tautulli Newsletters :: %s newsletter saved to %s" % (self.NAME, newsletter_file))
|
logger.info(u"Tautulli Newsletters :: %s newsletter saved to '%s'" % (self.NAME, newsletter_file))
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
logger.error(u"Tautulli Newsletters :: Failed to save %s newsletter to %s: %s"
|
logger.error(u"Tautulli Newsletters :: Failed to save %s newsletter to '%s': %s"
|
||||||
% (self.NAME, newsletter_file, e))
|
% (self.NAME, newsletter_file, e))
|
||||||
|
|
||||||
def _send(self):
|
def _send(self):
|
||||||
@@ -475,7 +493,10 @@ class Newsletter(object):
|
|||||||
def _build_params(self):
|
def _build_params(self):
|
||||||
date_format = helpers.momentjs_to_arrow(plexpy.CONFIG.DATE_FORMAT)
|
date_format = helpers.momentjs_to_arrow(plexpy.CONFIG.DATE_FORMAT)
|
||||||
|
|
||||||
base_url = plexpy.CONFIG.HTTP_BASE_URL or helpers.get_plexpy_url()
|
if plexpy.CONFIG.NEWSLETTER_SELF_HOSTED and plexpy.CONFIG.HTTP_BASE_URL:
|
||||||
|
base_url = plexpy.CONFIG.HTTP_BASE_URL + plexpy.HTTP_ROOT + 'newsletter/'
|
||||||
|
else:
|
||||||
|
base_url = helpers.get_plexpy_url() + '/newsletter/'
|
||||||
|
|
||||||
parameters = {
|
parameters = {
|
||||||
'server_name': plexpy.CONFIG.PMS_NAME,
|
'server_name': plexpy.CONFIG.PMS_NAME,
|
||||||
@@ -484,8 +505,10 @@ class Newsletter(object):
|
|||||||
'week_number': self.start_date.isocalendar()[1],
|
'week_number': self.start_date.isocalendar()[1],
|
||||||
'newsletter_time_frame': self.config['time_frame'],
|
'newsletter_time_frame': self.config['time_frame'],
|
||||||
'newsletter_time_frame_units': self.config['time_frame_units'],
|
'newsletter_time_frame_units': self.config['time_frame_units'],
|
||||||
'newsletter_url': base_url.rstrip('/') + plexpy.HTTP_ROOT + 'newsletter/' + self.uuid,
|
'newsletter_url': base_url + self.uuid,
|
||||||
'newsletter_uuid': self.uuid
|
'newsletter_static_url': base_url + 'id/' + str(self.newsletter_id),
|
||||||
|
'newsletter_uuid': self.uuid,
|
||||||
|
'newsletter_id': self.newsletter_id
|
||||||
}
|
}
|
||||||
|
|
||||||
return parameters
|
return parameters
|
||||||
@@ -529,6 +552,23 @@ class Newsletter(object):
|
|||||||
|
|
||||||
return subject, body, message
|
return subject, body, message
|
||||||
|
|
||||||
|
def build_filename(self):
|
||||||
|
from notification_handler import CustomFormatter
|
||||||
|
custom_formatter = CustomFormatter()
|
||||||
|
|
||||||
|
try:
|
||||||
|
filename = custom_formatter.format(unicode(self.filename), **self.parameters)
|
||||||
|
except LookupError as e:
|
||||||
|
logger.error(
|
||||||
|
u"Tautulli Newsletter :: Unable to parse parameter %s in newsletter filename. Using fallback." % e)
|
||||||
|
filename = unicode(self._DEFAULT_FILENAME).format(**self.parameters)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(
|
||||||
|
u"Tautulli Newsletter :: Unable to parse custom newsletter subject: %s. Using fallback." % e)
|
||||||
|
filename = unicode(self._DEFAULT_FILENAME).format(**self.parameters)
|
||||||
|
|
||||||
|
return filename
|
||||||
|
|
||||||
def return_config_options(self):
|
def return_config_options(self):
|
||||||
return self._return_config_options()
|
return self._return_config_options()
|
||||||
|
|
||||||
@@ -692,7 +732,7 @@ class RecentlyAdded(Newsletter):
|
|||||||
artists = recently_added.get('artist', [])
|
artists = recently_added.get('artist', [])
|
||||||
albums = [a for artist in artists for a in artist['album']]
|
albums = [a for artist in artists for a in artist['album']]
|
||||||
|
|
||||||
if self.is_preview or plexpy.CONFIG.NEWSLETTER_SELF_HOSTED:
|
if self.is_preview or helpers.get_img_service(include_self=True) == 'self-hosted':
|
||||||
for item in movies + shows + albums:
|
for item in movies + shows + albums:
|
||||||
if item['media_type'] == 'album':
|
if item['media_type'] == 'album':
|
||||||
height = 150
|
height = 150
|
||||||
@@ -714,7 +754,7 @@ class RecentlyAdded(Newsletter):
|
|||||||
item['poster_url'] = ''
|
item['poster_url'] = ''
|
||||||
item['art_url'] = ''
|
item['art_url'] = ''
|
||||||
|
|
||||||
else:
|
elif helpers.get_img_service():
|
||||||
# Upload posters and art to image hosting service
|
# Upload posters and art to image hosting service
|
||||||
for item in movies + shows + albums:
|
for item in movies + shows + albums:
|
||||||
if item['media_type'] == 'album':
|
if item['media_type'] == 'album':
|
||||||
@@ -797,4 +837,4 @@ class RecentlyAdded(Newsletter):
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
return config_options + additional_config
|
return additional_config + config_options
|
||||||
|
@@ -256,7 +256,7 @@ def notify_custom_conditions(notifier_id=None, parameters=None):
|
|||||||
|
|
||||||
elif parameter_type == 'float':
|
elif parameter_type == 'float':
|
||||||
values = [helpers.cast_to_float(v) for v in values]
|
values = [helpers.cast_to_float(v) for v in values]
|
||||||
|
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
logger.error(u"Tautulli NotificationHandler :: Unable to cast condition '%s', values '%s', to type '%s'."
|
logger.error(u"Tautulli NotificationHandler :: Unable to cast condition '%s', values '%s', to type '%s'."
|
||||||
% (parameter, values, parameter_type))
|
% (parameter, values, parameter_type))
|
||||||
@@ -317,7 +317,9 @@ def notify_custom_conditions(notifier_id=None, parameters=None):
|
|||||||
else:
|
else:
|
||||||
evaluated_logic = all(evaluated_conditions[1:])
|
evaluated_logic = all(evaluated_conditions[1:])
|
||||||
|
|
||||||
logger.debug(u"Tautulli NotificationHandler :: Custom condition evaluated to '%s'." % str(evaluated_logic))
|
logger.debug(u"Tautulli NotificationHandler :: Custom condition evaluated to '{}'. Conditions: {}.".format(
|
||||||
|
evaluated_logic, evaluated_conditions[1:]))
|
||||||
|
|
||||||
return evaluated_logic
|
return evaluated_logic
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@@ -1096,7 +1098,10 @@ def get_img_info(img=None, rating_key=None, title='', width=1000, height=1500,
|
|||||||
|
|
||||||
service = helpers.get_img_service()
|
service = helpers.get_img_service()
|
||||||
|
|
||||||
if service == 'cloudinary':
|
if service is None:
|
||||||
|
return img_info
|
||||||
|
|
||||||
|
elif service == 'cloudinary':
|
||||||
if fallback == 'cover':
|
if fallback == 'cover':
|
||||||
w, h = 1000, 1000
|
w, h = 1000, 1000
|
||||||
elif fallback == 'art':
|
elif fallback == 'art':
|
||||||
@@ -1132,7 +1137,7 @@ def get_img_info(img=None, rating_key=None, title='', width=1000, height=1500,
|
|||||||
|
|
||||||
elif not database_img_info and img:
|
elif not database_img_info and img:
|
||||||
pms_connect = pmsconnect.PmsConnect()
|
pms_connect = pmsconnect.PmsConnect()
|
||||||
result = pms_connect.get_image(**image_info)
|
result = pms_connect.get_image(refresh=True, **image_info)
|
||||||
|
|
||||||
if result and result[0]:
|
if result and result[0]:
|
||||||
img_url = delete_hash = ''
|
img_url = delete_hash = ''
|
||||||
|
@@ -1310,26 +1310,30 @@ class EMAIL(Notifier):
|
|||||||
|
|
||||||
recipients = self.config['to'] + self.config['cc'] + self.config['bcc']
|
recipients = self.config['to'] + self.config['cc'] + self.config['bcc']
|
||||||
|
|
||||||
|
success = False
|
||||||
|
mailserver = smtplib.SMTP(self.config['smtp_server'], self.config['smtp_port'])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
mailserver = smtplib.SMTP(self.config['smtp_server'], self.config['smtp_port'])
|
mailserver.ehlo()
|
||||||
|
|
||||||
if self.config['tls']:
|
if self.config['tls']:
|
||||||
mailserver.starttls()
|
mailserver.starttls()
|
||||||
|
mailserver.ehlo()
|
||||||
mailserver.ehlo()
|
|
||||||
|
|
||||||
if self.config['smtp_user']:
|
if self.config['smtp_user']:
|
||||||
mailserver.login(str(self.config['smtp_user']), str(self.config['smtp_password']))
|
mailserver.login(str(self.config['smtp_user']), str(self.config['smtp_password']))
|
||||||
|
|
||||||
mailserver.sendmail(self.config['from'], recipients, msg.as_string())
|
mailserver.sendmail(self.config['from'], recipients, msg.as_string())
|
||||||
mailserver.quit()
|
success = True
|
||||||
|
|
||||||
logger.info(u"Tautulli Notifiers :: {name} notification sent.".format(name=self.NAME))
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(u"Tautulli Notifiers :: {name} notification failed: {e}".format(name=self.NAME, e=e))
|
logger.error(u"Tautulli Notifiers :: {name} notification failed: {e}".format(name=self.NAME, e=e))
|
||||||
return False
|
|
||||||
|
finally:
|
||||||
|
mailserver.quit()
|
||||||
|
logger.info(u"Tautulli Notifiers :: {name} notification sent.".format(name=self.NAME))
|
||||||
|
|
||||||
|
return success
|
||||||
|
|
||||||
def get_user_emails(self):
|
def get_user_emails(self):
|
||||||
emails = {u['email']: u['friendly_name'] for u in users.Users().get_users() if u['email']}
|
emails = {u['email']: u['friendly_name'] for u in users.Users().get_users() if u['email']}
|
||||||
|
@@ -2436,7 +2436,7 @@ class PmsConnect(object):
|
|||||||
return labels_list
|
return labels_list
|
||||||
|
|
||||||
def get_image(self, img=None, width=1000, height=1500, opacity=None, background=None, blur=None,
|
def get_image(self, img=None, width=1000, height=1500, opacity=None, background=None, blur=None,
|
||||||
img_format='png', clip=False, **kwargs):
|
img_format='png', clip=False, refresh=False, **kwargs):
|
||||||
"""
|
"""
|
||||||
Return image data as array.
|
Return image data as array.
|
||||||
Array contains the image content type and image binary
|
Array contains the image content type and image binary
|
||||||
@@ -2454,6 +2454,9 @@ class PmsConnect(object):
|
|||||||
height = height or 1500
|
height = height or 1500
|
||||||
|
|
||||||
if img:
|
if img:
|
||||||
|
if refresh:
|
||||||
|
img = '{}/{}'.format(img.rstrip('/'), int(time.time()))
|
||||||
|
|
||||||
if clip:
|
if clip:
|
||||||
params = {'url': '%s&%s' % (img, urllib.urlencode({'X-Plex-Token': self.token}))}
|
params = {'url': '%s&%s' % (img, urllib.urlencode({'X-Plex-Token': self.token}))}
|
||||||
else:
|
else:
|
||||||
@@ -2544,7 +2547,7 @@ class PmsConnect(object):
|
|||||||
metadata = self.get_metadata_details(rating_key=rating_key)
|
metadata = self.get_metadata_details(rating_key=rating_key)
|
||||||
search_results_list[metadata['media_type']].append(metadata)
|
search_results_list[metadata['media_type']].append(metadata)
|
||||||
|
|
||||||
output = {'results_count': sum(len(s) for s in search_results_list.items()),
|
output = {'results_count': sum(len(s) for s in search_results_list.values()),
|
||||||
'results_list': search_results_list
|
'results_list': search_results_list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,2 +1,2 @@
|
|||||||
PLEXPY_BRANCH = "beta"
|
PLEXPY_BRANCH = "beta"
|
||||||
PLEXPY_RELEASE_VERSION = "v2.1.2-beta"
|
PLEXPY_RELEASE_VERSION = "v2.1.5-beta"
|
||||||
|
@@ -2755,7 +2755,9 @@ class WebInterface(object):
|
|||||||
"tvmaze_lookup": checked(plexpy.CONFIG.TVMAZE_LOOKUP),
|
"tvmaze_lookup": checked(plexpy.CONFIG.TVMAZE_LOOKUP),
|
||||||
"show_advanced_settings": plexpy.CONFIG.SHOW_ADVANCED_SETTINGS,
|
"show_advanced_settings": plexpy.CONFIG.SHOW_ADVANCED_SETTINGS,
|
||||||
"newsletter_dir": plexpy.CONFIG.NEWSLETTER_DIR,
|
"newsletter_dir": plexpy.CONFIG.NEWSLETTER_DIR,
|
||||||
"newsletter_self_hosted": checked(plexpy.CONFIG.NEWSLETTER_SELF_HOSTED)
|
"newsletter_self_hosted": checked(plexpy.CONFIG.NEWSLETTER_SELF_HOSTED),
|
||||||
|
"newsletter_static_url": checked(plexpy.CONFIG.NEWSLETTER_STATIC_URL),
|
||||||
|
"newsletter_custom_dir": plexpy.CONFIG.NEWSLETTER_CUSTOM_DIR
|
||||||
}
|
}
|
||||||
|
|
||||||
return serve_template(templatename="settings.html", title="Settings", config=config, kwargs=kwargs)
|
return serve_template(templatename="settings.html", title="Settings", config=config, kwargs=kwargs)
|
||||||
@@ -2777,7 +2779,7 @@ class WebInterface(object):
|
|||||||
"allow_guest_access", "cache_images", "http_proxy", "http_basic_auth", "notify_concurrent_by_ip",
|
"allow_guest_access", "cache_images", "http_proxy", "http_basic_auth", "notify_concurrent_by_ip",
|
||||||
"history_table_activity", "plexpy_auto_update",
|
"history_table_activity", "plexpy_auto_update",
|
||||||
"themoviedb_lookup", "tvmaze_lookup", "http_plex_admin",
|
"themoviedb_lookup", "tvmaze_lookup", "http_plex_admin",
|
||||||
"newsletter_self_hosted"
|
"newsletter_self_hosted", "newsletter_static_url"
|
||||||
]
|
]
|
||||||
for checked_config in checked_configs:
|
for checked_config in checked_configs:
|
||||||
if checked_config not in kwargs:
|
if checked_config not in kwargs:
|
||||||
@@ -3962,13 +3964,20 @@ class WebInterface(object):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if rating_key and not img:
|
if rating_key and not img:
|
||||||
img = '/library/metadata/%s/thumb/1337' % rating_key
|
if fallback == 'art':
|
||||||
|
img = '/library/metadata/{}/art'.format(rating_key)
|
||||||
|
else:
|
||||||
|
img = '/library/metadata/{}/thumb'.format(rating_key)
|
||||||
|
|
||||||
img_string = img.rsplit('/', 1)[0] if '/library/metadata' in img else img
|
img_split = img.split('/')
|
||||||
img_string = '{}{}{}{}{}{}'.format(img_string, width, height, opacity, background, blur)
|
img = '/'.join(img_split[:5])
|
||||||
|
rating_key = rating_key or img_split[3]
|
||||||
|
|
||||||
fp = hashlib.md5(img_string).hexdigest()
|
img_string = '{}.{}.{}.{}.{}.{}.{}.{}'.format(
|
||||||
fp += '.%s' % img_format # we want to be able to preview the thumbs
|
plexpy.CONFIG.PMS_UUID, img, rating_key, width, height, opacity, background, blur, fallback)
|
||||||
|
img_hash = hashlib.sha256(img_string).hexdigest()
|
||||||
|
|
||||||
|
fp = '{}.{}'.format(img_hash, img_format) # we want to be able to preview the thumbs
|
||||||
c_dir = os.path.join(plexpy.CONFIG.CACHE_DIR, 'images')
|
c_dir = os.path.join(plexpy.CONFIG.CACHE_DIR, 'images')
|
||||||
ffp = os.path.join(c_dir, fp)
|
ffp = os.path.join(c_dir, fp)
|
||||||
|
|
||||||
@@ -3994,7 +4003,8 @@ class WebInterface(object):
|
|||||||
background=background,
|
background=background,
|
||||||
blur=blur,
|
blur=blur,
|
||||||
img_format=img_format,
|
img_format=img_format,
|
||||||
clip=clip)
|
clip=clip,
|
||||||
|
refresh=refresh)
|
||||||
|
|
||||||
if result and result[0]:
|
if result and result[0]:
|
||||||
cherrypy.response.headers['Content-type'] = result[1]
|
cherrypy.response.headers['Content-type'] = result[1]
|
||||||
@@ -5653,10 +5663,18 @@ class WebInterface(object):
|
|||||||
except NotFound:
|
except NotFound:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
cherrypy.response.headers['Cache-Control'] = 'max-age=2592000' # 30 days
|
||||||
return self.image(args[1], refresh=True)
|
return self.image(args[1], refresh=True)
|
||||||
|
|
||||||
newsletter_uuid = args[0]
|
if plexpy.CONFIG.NEWSLETTER_STATIC_URL and len(args) >= 2 and args[0] == 'id':
|
||||||
newsletter = newsletter_handler.get_newsletter(newsletter_uuid=newsletter_uuid)
|
newsletter_id = args[1]
|
||||||
|
newsletter_uuid = None
|
||||||
|
else:
|
||||||
|
newsletter_id = None
|
||||||
|
newsletter_uuid = args[0]
|
||||||
|
|
||||||
|
newsletter = newsletter_handler.get_newsletter(newsletter_uuid=newsletter_uuid,
|
||||||
|
newsletter_id=newsletter_id)
|
||||||
return newsletter
|
return newsletter
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
@@ -5675,7 +5693,8 @@ class WebInterface(object):
|
|||||||
newsletter = newsletters.get_newsletter_config(newsletter_id=newsletter_id)
|
newsletter = newsletters.get_newsletter_config(newsletter_id=newsletter_id)
|
||||||
|
|
||||||
if newsletter:
|
if newsletter:
|
||||||
newsletter_agent = newsletters.get_agent_class(agent_id=newsletter['agent_id'],
|
newsletter_agent = newsletters.get_agent_class(newsletter_id=newsletter_id,
|
||||||
|
agent_id=newsletter['agent_id'],
|
||||||
config=newsletter['config'],
|
config=newsletter['config'],
|
||||||
start_date=start_date,
|
start_date=start_date,
|
||||||
end_date=end_date,
|
end_date=end_date,
|
||||||
|
Reference in New Issue
Block a user