diff --git a/data/interfaces/default/newsletter_config.html b/data/interfaces/default/newsletter_config.html index 6ff443bf..08ff8ccf 100644 --- a/data/interfaces/default/newsletter_config.html +++ b/data/interfaces/default/newsletter_config.html @@ -2,7 +2,6 @@ <%! import json from plexpy import helpers, notifiers - from plexpy.notification_handler import PILLOW email_notifiers = [n for n in notifiers.get_notifiers() if n['agent_name'] == 'email'] sorted(email_notifiers, key=lambda k: (k['agent_label'], k['friendly_name'], k['id'])) diff --git a/data/interfaces/newsletters/recently_added.html b/data/interfaces/newsletters/recently_added.html index bae06ee8..98ccba64 100644 --- a/data/interfaces/newsletters/recently_added.html +++ b/data/interfaces/newsletters/recently_added.html @@ -1,6 +1,5 @@ % if data: <% - from plexpy.notification_handler import PILLOW recently_added = data['recently_added'] %> @@ -351,26 +350,11 @@ margin: 3px auto !important; } .card-background { - background-color: #3F4245; background-position: center; background-size: cover; width: 100%; height: 100%; } - .card-background-preview { - background-color: #3F4245; - background-position: center; - background-size: cover; - opacity: 0.25; - -webkit-filter: blur(3px); - filter: blur(3px); - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 0; - } .card-poster-container { width: 150px; margin: 3px; @@ -387,7 +371,7 @@ height: 150px; } .card-poster { - background-color: #3F4245; + background-color: #282828; background-position: center; background-size: cover; height: 100%; @@ -617,19 +601,14 @@ % else:
% endif - % if PILLOW: % if preview: -
-
+
% else: -
- % endif - % else: -
+
% endif
-
+
@@ -705,19 +684,14 @@ % else:
% endif - % if PILLOW: % if preview: -
-
+
% else: -
- % endif - % else: -
+
% endif
-
+
@@ -815,19 +789,14 @@ % else:
% endif - % if PILLOW: % if preview: -
-
+
% else: -
- % endif - % else: -
+
% endif
-
+
diff --git a/data/interfaces/newsletters/recently_added_master.html b/data/interfaces/newsletters/recently_added_master.html index 2b76ac21..8d56ba3a 100644 --- a/data/interfaces/newsletters/recently_added_master.html +++ b/data/interfaces/newsletters/recently_added_master.html @@ -1,6 +1,5 @@ % if data: <% - from plexpy.notification_handler import PILLOW recently_added = data['recently_added'] %> @@ -351,26 +350,11 @@ margin: 3px auto !important; } .card-background { - background-color: #3F4245; background-position: center; background-size: cover; width: 100%; height: 100%; } - .card-background-preview { - background-color: #3F4245; - background-position: center; - background-size: cover; - opacity: 0.25; - -webkit-filter: blur(3px); - filter: blur(3px); - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 0; - } .card-poster-container { width: 150px; margin: 3px; @@ -617,15 +601,10 @@ % else:
% endif - % if PILLOW: % if preview: -
-
+
% else:
- % endif - % else: -
% endif
@@ -705,15 +684,10 @@ % else:
% endif - % if PILLOW: % if preview: -
-
+
% else:
- % endif - % else: -
% endif
@@ -815,15 +789,10 @@ % else:
% endif - % if PILLOW: % if preview: -
-
+
% else:
- % endif - % else: -
% endif
diff --git a/plexpy/__init__.py b/plexpy/__init__.py index f4f5232f..0d3ced3a 100644 --- a/plexpy/__init__.py +++ b/plexpy/__init__.py @@ -649,7 +649,7 @@ def dbcheck(): # art_urls table :: This table keeps record of the notification art urls c_db.execute( 'CREATE TABLE IF NOT EXISTS art_urls (id INTEGER PRIMARY KEY AUTOINCREMENT, ' - 'rating_key INTEGER, art_title TEXT, art_url TEXT, blur_art_url TEXT)' + 'rating_key INTEGER, art_title TEXT, art_url TEXT, delete_hash TEXT, blur INTEGER DEFAULT 0)' ) # recently_added table :: This table keeps record of recently added items diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py index 67f5594d..a972ca1b 100644 --- a/plexpy/datafactory.py +++ b/plexpy/datafactory.py @@ -1106,7 +1106,7 @@ class DataFactory(object): return ip_address - def get_poster_info(self, rating_key='', metadata=None, art=False): + def get_poster_info(self, rating_key='', metadata=None, art=False, blur=None): monitor_db = database.MonitorDatabase() poster_key = '' @@ -1125,31 +1125,31 @@ class DataFactory(object): if poster_key: try: if art: - query = 'SELECT art_title, art_url, blur_art_url FROM art_urls ' \ - 'WHERE rating_key = ?' + query = 'SELECT art_title, art_url, blur FROM art_urls ' \ + 'WHERE rating_key = ? AND blur = ?' + args = [poster_key, int(blur is not None)] else: query = 'SELECT poster_title, poster_url FROM poster_urls ' \ 'WHERE rating_key = ?' - poster_info = monitor_db.select_single(query, args=[poster_key]) + args = [poster_key] + poster_info = monitor_db.select_single(query, args=args) except Exception as e: logger.warn(u"Tautulli DataFactory :: Unable to execute database query for get_poster_url: %s." % e) return poster_info - def set_poster_url(self, rating_key='', poster_title='', poster_url='', delete_hash='', art=False, blur=False): + def set_poster_url(self, rating_key='', poster_title='', poster_url='', delete_hash='', art=False, blur=None): monitor_db = database.MonitorDatabase() if str(rating_key).isdigit(): keys = {'rating_key': int(rating_key)} if art: + keys['blur'] = int(blur is not None) table = 'art_urls' values = {'art_title': poster_title, + 'art_url': poster_url, 'delete_hash': delete_hash} - if blur: - values['blur_art_url'] = poster_url - else: - values['art_url'] = poster_url else: table = 'poster_urls' values = {'poster_title': poster_title, diff --git a/plexpy/newsletters.py b/plexpy/newsletters.py index 4407a260..7e6a6f3f 100644 --- a/plexpy/newsletters.py +++ b/plexpy/newsletters.py @@ -30,7 +30,7 @@ import logger import newsletter_handler import notification_handler import pmsconnect -from notification_handler import PILLOW, get_poster_info +from notification_handler import get_poster_info from notifiers import send_notification, EMAIL @@ -542,16 +542,16 @@ class RecentlyAdded(Newsletter): if poster_info: item['poster_url'] = poster_info['poster_url'] or common.ONLINE_POSTER_THUMB - art_info = {} - if PILLOW: - art_info = get_poster_info(poster_thumb=item['art'], - poster_key=item['rating_key'], - poster_title=item['title'], - art=True, - width='500', - height='280', - blur=True) - item['art_url'] = art_info.get('blur_art_url', '') + art_info = get_poster_info(poster_thumb=item['art'], + poster_key=item['rating_key'], + poster_title=item['title'], + art=True, + width='500', + height='280', + opacity='25', + background='282828', + blur='3') + item['art_url'] = art_info.get('art_url', '') self.data['recently_added'] = recently_added @@ -600,19 +600,4 @@ class RecentlyAdded(Newsletter): } ] - if not PILLOW: - pillow_message = { - 'label': 'Background Art', - 'description': 'The Pillow library is missing. ' - 'Background art on the newsletter media cards will not be included.
' - 'Install the Pillow library to add background art. ' - 'Instructions can be found in the ' - '
FAQ.', - 'name': 'recently_added_pillow', - 'input_type': 'help' - } - config_option.insert(0, pillow_message) - return config_option diff --git a/plexpy/notification_handler.py b/plexpy/notification_handler.py index 86665b0a..cfc576a0 100644 --- a/plexpy/notification_handler.py +++ b/plexpy/notification_handler.py @@ -26,12 +26,6 @@ from string import Formatter import threading import time -try: - from PIL import Image, ImageFilter - PILLOW = True -except ImportError: - PILLOW = False - import plexpy import activity_processor import common @@ -1071,53 +1065,42 @@ def format_group_index(group_keys): return ','.join(num) or '0', ','.join(num00) or '00' -def get_poster_info(poster_thumb='', poster_key='', poster_title='', art=False, width='', height='', blur=False): +def get_poster_info(poster_thumb='', poster_key='', poster_title='', art=False, + width='', height='', opacity=None, background=None, blur=None): default_poster_info = {'poster_title': '', 'poster_url': ''} default_art_info = {'art_title': '', 'art_url': ''} # Try to retrieve poster info from the database data_factory = datafactory.DataFactory() - poster_info = data_factory.get_poster_info(rating_key=poster_key, art=art) + poster_info = data_factory.get_poster_info(rating_key=poster_key, art=art, blur=blur) # If no previous poster info if not poster_info and poster_thumb: try: thread_name = str(threading.current_thread().ident) - poster_file = os.path.join(plexpy.CONFIG.CACHE_DIR, 'cache-image-%s.jpg' % thread_name) + poster_file = os.path.join(plexpy.CONFIG.CACHE_DIR, 'cache-image-%s.png' % thread_name) # Retrieve the poster from Plex and cache to file pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_image(img=poster_thumb, width=width, height=height) + result = pms_connect.get_image(img=poster_thumb, + width=width, + height=height, + opacity=opacity, + background=background, + blur=blur) if result and result[0]: with open(poster_file, 'wb') as f: f.write(result[0]) else: raise Exception(u'PMS image request failed') - if blur and PILLOW: - img = Image.open(poster_file) - img = img.convert("RGBA") - img = img.filter(ImageFilter.GaussianBlur(3)) # 3px blur - img.putalpha(64) # 40% opacity - - # Save as a png - poster_file_blur = os.path.join(plexpy.CONFIG.CACHE_DIR, 'cache-image-%s.png' % thread_name) - img.save(poster_file_blur) - # Upload poster_thumb to Imgur and get link - if blur: - poster_url, delete_hash = helpers.upload_to_imgur(poster_file_blur, poster_title) - else: - poster_url, delete_hash = helpers.upload_to_imgur(poster_file, poster_title) + poster_url, delete_hash = helpers.upload_to_imgur(poster_file, poster_title) if poster_url: # Create poster info if art: - poster_info = {'art_title': poster_title} - if blur: - poster_info['blur_art_url'] = poster_url - else: - poster_info['art_url'] = poster_url + poster_info = {'art_title': poster_title, 'art_url': poster_url} else: poster_info = {'poster_title': poster_title, 'poster_url': poster_url} @@ -1131,8 +1114,6 @@ def get_poster_info(poster_thumb='', poster_key='', poster_title='', art=False, # Delete the cached poster os.remove(poster_file) - if blur: - os.remove(poster_file_blur) except Exception as e: logger.error(u"Tautulli NotificationHandler :: Unable to retrieve poster for rating_key %s: %s." % (poster_key, e)) diff --git a/plexpy/notifiers.py b/plexpy/notifiers.py index 7c1f38a6..6757d15f 100644 --- a/plexpy/notifiers.py +++ b/plexpy/notifiers.py @@ -1656,7 +1656,7 @@ class GROUPME(Notifier): if poster_content: headers = {'X-Access-Token': self.config['access_token'], - 'Content-Type': 'image/jpeg'} + 'Content-Type': 'image/png'} r = requests.post('https://image.groupme.com/pictures', headers=headers, data=poster_content) @@ -2643,9 +2643,9 @@ class PUSHBULLET(Notifier): logger.error(u"Tautulli Notifiers :: Unable to retrieve image for {name}.".format(name=self.NAME)) if poster_content: - poster_filename = 'poster_{}.jpg'.format(pretty_metadata.parameters['rating_key']) - file_json = {'file_name': poster_filename, 'file_type': 'image/jpeg'} - files = {'file': (poster_filename, poster_content, 'image/jpeg')} + poster_filename = 'poster_{}.png'.format(pretty_metadata.parameters['rating_key']) + file_json = {'file_name': poster_filename, 'file_type': 'image/png'} + files = {'file': (poster_filename, poster_content, 'image/png')} r = requests.post('https://api.pushbullet.com/v2/upload-request', headers=headers, json=file_json) @@ -2799,8 +2799,8 @@ class PUSHOVER(Notifier): logger.error(u"Tautulli Notifiers :: Unable to retrieve image for {name}.".format(name=self.NAME)) if poster_content: - poster_filename = 'poster_{}.jpg'.format(pretty_metadata.parameters['rating_key']) - files = {'attachment': (poster_filename, poster_content, 'image/jpeg')} + poster_filename = 'poster_{}.png'.format(pretty_metadata.parameters['rating_key']) + files = {'attachment': (poster_filename, poster_content, 'image/png')} headers = {} return self.make_request('https://api.pushover.net/1/messages.json', headers=headers, data=data, files=files) @@ -3327,8 +3327,8 @@ class TELEGRAM(Notifier): logger.error(u"Tautulli Notifiers :: Unable to retrieve image for {name}.".format(name=self.NAME)) if poster_content: - poster_filename = 'poster_{}.jpg'.format(pretty_metadata.parameters['rating_key']) - files = {'photo': (poster_filename, poster_content, 'image/jpeg')} + poster_filename = 'poster_{}.png'.format(pretty_metadata.parameters['rating_key']) + files = {'photo': (poster_filename, poster_content, 'image/png')} data['caption'] = text return self.make_request('https://api.telegram.org/bot{}/sendPhoto'.format(self.config['bot_token']), diff --git a/plexpy/pmsconnect.py b/plexpy/pmsconnect.py index 22fa35f0..967fb8f4 100644 --- a/plexpy/pmsconnect.py +++ b/plexpy/pmsconnect.py @@ -2425,7 +2425,8 @@ class PmsConnect(object): return labels_list - def get_image(self, img=None, width='1000', height='1500', clip=False): + def get_image(self, img=None, width='1000', height='1500', opacity=None, background=None, blur=None, img_format='png', + clip=False): """ Return image data as array. Array contains the image content type and image binary @@ -2433,6 +2434,9 @@ class PmsConnect(object): Parameters required: img { Plex image location } Optional parameters: width { the image width } height { the image height } + opacity { the image opacity 0-100 } + background { the image background HEX } + blur { the image blur 0-100 } Output: array """ @@ -2447,6 +2451,14 @@ class PmsConnect(object): params['width'] = width params['height'] = height + params['format'] = img_format + + if opacity: + params['opacity'] = opacity + if background: + params['background'] = background + if blur: + params['blur'] = blur uri = '/photo/:/transcode?%s' % urllib.urlencode(params) result = self.request_handler.make_request(uri=uri, diff --git a/plexpy/webserve.py b/plexpy/webserve.py index f625e499..112b40db 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -3816,6 +3816,7 @@ class WebInterface(object): @addtoapi('pms_image_proxy') def real_pms_image_proxy(self, img='', rating_key=None, width='0', height='0', + opacity=None, background=None, blur=None, img_format='png', fallback=None, refresh=False, clip=False, **kwargs): """ Gets an image from the PMS and saves it to the image cache directory. @@ -3826,8 +3827,12 @@ class WebInterface(object): rating_key (str): 54321 Optional parameters: - width (str): 150 - height (str): 255 + width (str): 300 + height (str): 450 + opacity (str): 25 + background (str): 282828 + blur (str): 3 + img_format (str): png fallback (str): "poster", "cover", "art" refresh (bool): True or False whether to refresh the image cache @@ -3843,9 +3848,20 @@ class WebInterface(object): img = '/library/metadata/%s/thumb/1337' % rating_key img_string = img.rsplit('/', 1)[0] if '/library/metadata' in img else img - img_string += '%s%s' % (width, height) + + if width: + img_string += width + if height: + img_string += height + if opacity: + img_string += opacity + if background: + img_string += background + if blur: + img_string += blur + fp = hashlib.md5(img_string).hexdigest() - fp += '.jpg' # we want to be able to preview the thumbs + fp += '.%s' % img_format # we want to be able to preview the thumbs c_dir = os.path.join(plexpy.CONFIG.CACHE_DIR, 'images') ffp = os.path.join(c_dir, fp) @@ -3858,13 +3874,20 @@ class WebInterface(object): if not plexpy.CONFIG.CACHE_IMAGES or refresh or 'indexes' in img: raise NotFound - return serve_file(path=ffp, content_type='image/jpeg') + return serve_file(path=ffp, content_type='image/png') except NotFound: # the image does not exist, download it from pms try: pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_image(img, width, height, clip=clip) + result = pms_connect.get_image(img=img, + width=width, + height=height, + opacity=opacity, + background=background, + blur=blur, + img_format=img_format, + clip=clip) if result and result[0]: cherrypy.response.headers['Content-type'] = result[1]