diff --git a/plexpy/__init__.py b/plexpy/__init__.py index a8ecae2b..59c7cfa6 100644 --- a/plexpy/__init__.py +++ b/plexpy/__init__.py @@ -1693,7 +1693,7 @@ def dbcheck(): for row in result: img_hash = notification_handler.set_hash_image_info( - rating_key=row['rating_key'], width=600, height=1000, 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'], imgur_url=row['poster_url'], delete_hash=row['delete_hash'], service='imgur') diff --git a/plexpy/helpers.py b/plexpy/helpers.py index 2e7be67f..100e2002 100644 --- a/plexpy/helpers.py +++ b/plexpy/helpers.py @@ -17,6 +17,7 @@ import base64 import cloudinary from cloudinary.api import delete_resources_by_tag from cloudinary.uploader import upload +from cloudinary.utils import cloudinary_url import datetime from functools import wraps import geoip2.database, geoip2.errors @@ -720,14 +721,13 @@ def get_img_service(include_self=False): def upload_to_imgur(img_data, img_title='', rating_key='', fallback=''): """ Uploads an image to Imgur """ - client_id = plexpy.CONFIG.IMGUR_CLIENT_ID img_url = delete_hash = '' - if not client_id: + if not plexpy.CONFIG.IMGUR_CLIENT_ID: logger.error(u"Tautulli Helpers :: Cannot upload image to Imgur. No Imgur client id specified in the settings.") return img_url, delete_hash - headers = {'Authorization': 'Client-ID %s' % client_id} + headers = {'Authorization': 'Client-ID %s' % plexpy.CONFIG.IMGUR_CLIENT_ID} data = {'image': base64.b64encode(img_data), 'title': img_title.encode('utf-8'), 'name': str(rating_key) + '.png', @@ -755,9 +755,11 @@ def upload_to_imgur(img_data, img_title='', rating_key='', fallback=''): def delete_from_imgur(delete_hash, img_title='', fallback=''): """ Deletes an image from Imgur """ - client_id = plexpy.CONFIG.IMGUR_CLIENT_ID + if not plexpy.CONFIG.IMGUR_CLIENT_ID: + logger.error(u"Tautulli Helpers :: Cannot delete image from Imgur. No Imgur client id specified in the settings.") + return False - headers = {'Authorization': 'Client-ID %s' % client_id} + headers = {'Authorization': 'Client-ID %s' % plexpy.CONFIG.IMGUR_CLIENT_ID} response, err_msg, req_msg = request.request_response2('https://api.imgur.com/3/image/%s' % delete_hash, 'DELETE', headers=headers) @@ -775,17 +777,18 @@ def delete_from_imgur(delete_hash, img_title='', fallback=''): def upload_to_cloudinary(img_data, img_title='', rating_key='', fallback=''): """ Uploads an image to Cloudinary """ - cloudinary.config( - cloud_name=plexpy.CONFIG.CLOUDINARY_CLOUD_NAME, - api_key=plexpy.CONFIG.CLOUDINARY_API_KEY, - api_secret=plexpy.CONFIG.CLOUDINARY_API_SECRET - ) img_url = '' if not plexpy.CONFIG.CLOUDINARY_CLOUD_NAME or not plexpy.CONFIG.CLOUDINARY_API_KEY or not plexpy.CONFIG.CLOUDINARY_API_SECRET: logger.error(u"Tautulli Helpers :: Cannot upload image to Cloudinary. Cloudinary settings not specified in the settings.") return img_url + cloudinary.config( + cloud_name=plexpy.CONFIG.CLOUDINARY_CLOUD_NAME, + api_key=plexpy.CONFIG.CLOUDINARY_API_KEY, + api_secret=plexpy.CONFIG.CLOUDINARY_API_SECRET + ) + try: response = upload('data:image/png;base64,{}'.format(base64.b64encode(img_data)), public_id='{}_{}'.format(fallback, rating_key), @@ -801,6 +804,10 @@ def upload_to_cloudinary(img_data, img_title='', rating_key='', fallback=''): def delete_from_cloudinary(rating_key): """ Deletes an image from Cloudinary """ + if not plexpy.CONFIG.CLOUDINARY_CLOUD_NAME or not plexpy.CONFIG.CLOUDINARY_API_KEY or not plexpy.CONFIG.CLOUDINARY_API_SECRET: + logger.error(u"Tautulli Helpers :: Cannot delete image from Cloudinary. Cloudinary settings not specified in the settings.") + return False + cloudinary.config( cloud_name=plexpy.CONFIG.CLOUDINARY_CLOUD_NAME, api_key=plexpy.CONFIG.CLOUDINARY_API_KEY, @@ -813,6 +820,47 @@ def delete_from_cloudinary(rating_key): return True +def cloudinary_transform(rating_key=None, width=1000, height=1500, opacity=100, background='000000', blur=0, + img_format='png', img_title='', fallback=None): + url = '' + + if not plexpy.CONFIG.CLOUDINARY_CLOUD_NAME or not plexpy.CONFIG.CLOUDINARY_API_KEY or not plexpy.CONFIG.CLOUDINARY_API_SECRET: + logger.error(u"Tautulli Helpers :: Cannot transform image on Cloudinary. Cloudinary settings not specified in the settings.") + return url + + cloudinary.config( + cloud_name=plexpy.CONFIG.CLOUDINARY_CLOUD_NAME, + api_key=plexpy.CONFIG.CLOUDINARY_API_KEY, + api_secret=plexpy.CONFIG.CLOUDINARY_API_SECRET + ) + + img_options = {} + + if width != 1000: + img_options['width'] = width + img_options['crop'] = 'fill' + if height != 1500: + img_options['height'] = height + img_options['crop'] = 'fill' + if opacity != 100: + img_options['opacity'] = opacity + if background != '000000': + img_options['background'] = 'rgb:{}'.format(background) + if blur != 0: + img_options['effect'] = 'blur:{}'.format(blur * 100) + + if img_options: + img_options['format'] = img_format + + try: + url, options = cloudinary_url('{}_{}'.format(fallback, rating_key), **img_options) + 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 + + def cache_image(url, image=None): """ Saves an image to the cache directory. diff --git a/plexpy/notification_handler.py b/plexpy/notification_handler.py index 8f9996c9..8ffc42aa 100644 --- a/plexpy/notification_handler.py +++ b/plexpy/notification_handler.py @@ -1077,7 +1077,7 @@ def format_group_index(group_keys): return ','.join(num) or '0', ','.join(num00) or '00' -def get_img_info(img=None, rating_key=None, title='', width=600, height=1000, +def get_img_info(img=None, rating_key=None, title='', width=1000, height=1500, opacity=100, background='000000', blur=0, fallback=None): img_info = {'img_title': '', 'img_url': ''} @@ -1094,17 +1094,35 @@ def get_img_info(img=None, rating_key=None, title='', width=600, height=1000, img = '/'.join(img_split[:5]) rating_key = rating_key or img_split[3] - image_info = {'img': img, - 'rating_key': rating_key, - 'width': width, - 'height': height, - 'opacity': opacity, - 'background': background, - 'blur': blur, - 'fallback': fallback} - service = helpers.get_img_service() + if service == 'cloudinary': + if fallback == 'cover': + w, h = 1000, 1000 + elif fallback == 'art': + w, h = 1920, 1080 + else: + w, h = 1000, 1500 + + image_info = {'img': img, + 'rating_key': rating_key, + 'width': w, + 'height': h, + 'opacity': 100, + 'background': '000000', + 'blur': 0, + 'fallback': fallback} + + else: + image_info = {'img': img, + 'rating_key': rating_key, + 'width': width, + 'height': height, + 'opacity': opacity, + 'background': background, + 'blur': blur, + 'fallback': fallback} + # Try to retrieve poster info from the database data_factory = datafactory.DataFactory() database_img_info = data_factory.get_img_info(service=service, **image_info) @@ -1140,10 +1158,25 @@ def get_img_info(img=None, rating_key=None, title='', width=600, height=1000, img_info = {'img_title': title, 'img_url': img_url} + if img_info['img_url'] and service == 'cloudinary': + # Transform image using Cloudinary + image_info = {'rating_key': rating_key, + 'width': width, + 'height': height, + 'opacity': opacity, + 'background': background, + 'blur': blur, + 'fallback': fallback, + 'img_title': title} + + transformed_url = helpers.cloudinary_transform(**image_info) + if transformed_url: + img_info['img_url'] = transformed_url + return img_info -def set_hash_image_info(img=None, rating_key=None, width=600, height=1000, +def set_hash_image_info(img=None, rating_key=None, width=750, height=1000, opacity=100, background='000000', blur=0, fallback=None): if not rating_key and not img: return fallback diff --git a/plexpy/pmsconnect.py b/plexpy/pmsconnect.py index 9a426c0f..4ca2adf3 100644 --- a/plexpy/pmsconnect.py +++ b/plexpy/pmsconnect.py @@ -2435,7 +2435,7 @@ class PmsConnect(object): return labels_list - def get_image(self, img=None, width=600, height=1000, 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): """ Return image data as array. @@ -2450,8 +2450,8 @@ class PmsConnect(object): Output: array """ - width = width or 600 - height = height or 1000 + width = width or 1000 + height = height or 1500 if img: if clip: