Lookup TheMovieDB Links
@@ -1041,7 +1081,7 @@
Add a new newsletter agent, or configure an existing newsletter agent by clicking the settings icon on the right.
- Note: Either Image Hosting on Imgur or Self-Hosted Newsletters must be enabled.
+ Note: Either Image Hosting or Self-Hosted Newsletters must be enabled.
@@ -2538,6 +2578,11 @@ $(document).ready(function() {
} else {
$('#self_host_image_options').slideUp();
}
+ if (upload_val === '3') {
+ $('#cloudinary_upload_options').slideDown();
+ } else {
+ $('#cloudinary_upload_options').slideUp();
+ }
}
$('#notify_upload_posters').change(function () {
imageUpload();
@@ -2557,7 +2602,7 @@ $(document).ready(function() {
});
function newsletterUploadEnabled() {
- if ($('#notify_upload_posters').val() === '1' || $('#newsletter_self_hosted').is(':checked')) {
+ if ($('#notify_upload_posters').val() !== '2' || $('#newsletter_self_hosted').is(':checked')) {
$('#newsletter_upload_warning').hide();
} else {
$('#newsletter_upload_warning').show();
diff --git a/plexpy/__init__.py b/plexpy/__init__.py
index 74c4f676..a8ecae2b 100644
--- a/plexpy/__init__.py
+++ b/plexpy/__init__.py
@@ -693,6 +693,12 @@ def dbcheck():
'img_hash TEXT, imgur_title TEXT, imgur_url TEXT, delete_hash TEXT)'
)
+ # cloudinary_lookup table :: This table keeps record of the Cloudinary uploads
+ c_db.execute(
+ 'CREATE TABLE IF NOT EXISTS cloudinary_lookup (id INTEGER PRIMARY KEY AUTOINCREMENT, '
+ 'img_hash TEXT, cloudinary_title TEXT, cloudinary_url TEXT)'
+ )
+
# Upgrade sessions table from earlier versions
try:
c_db.execute('SELECT started FROM sessions')
@@ -1689,7 +1695,8 @@ def dbcheck():
img_hash = notification_handler.set_hash_image_info(
rating_key=row['rating_key'], width=600, height=1000, 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'])
+ imgur_url=row['poster_url'], delete_hash=row['delete_hash'],
+ service='imgur')
db.action('DROP TABLE poster_urls')
except sqlite3.OperationalError:
diff --git a/plexpy/config.py b/plexpy/config.py
index 57c92a22..b0d26188 100644
--- a/plexpy/config.py
+++ b/plexpy/config.py
@@ -115,6 +115,9 @@ _CONFIG_DEFINITIONS = {
'CHECK_GITHUB_INTERVAL': (int, 'General', 360),
'CHECK_GITHUB_ON_STARTUP': (int, 'General', 1),
'CLEANUP_FILES': (int, 'General', 0),
+ 'CLOUDINARY_CLOUD_NAME': (str, 'Cloudinary', ''),
+ 'CLOUDINARY_API_KEY': (str, 'Cloudinary', ''),
+ 'CLOUDINARY_API_SECRET': (str, 'Cloudinary', ''),
'CONFIG_VERSION': (int, 'Advanced', 0),
'DO_NOT_OVERRIDE_GIT_BRANCH': (int, 'General', 0),
'EMAIL_ENABLED': (int, 'Email', 0),
diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py
index 9727699b..59313226 100644
--- a/plexpy/datafactory.py
+++ b/plexpy/datafactory.py
@@ -1132,12 +1132,12 @@ class DataFactory(object):
return ip_address
- def get_imgur_info(self, img=None, rating_key=None, width=None, height=None,
- opacity=None, background=None, blur=None, fallback=None,
- order_by=''):
+ def get_img_info(self, img=None, rating_key=None, width=None, height=None,
+ opacity=None, background=None, blur=None, fallback=None,
+ order_by='', service=None):
monitor_db = database.MonitorDatabase()
- imgur_info = []
+ img_info = []
where_params = []
args = []
@@ -1174,26 +1174,46 @@ class DataFactory(object):
if order_by:
order_by = 'ORDER BY ' + order_by + ' DESC'
- query = 'SELECT imgur_title, imgur_url FROM imgur_lookup ' \
- 'JOIN image_hash_lookup ON imgur_lookup.img_hash = image_hash_lookup.img_hash ' \
- '%s %s' % (where, order_by)
+ if service == 'imgur':
+ query = 'SELECT imgur_title AS img_title, imgur_url AS img_url FROM imgur_lookup ' \
+ 'JOIN image_hash_lookup ON imgur_lookup.img_hash = image_hash_lookup.img_hash ' \
+ '%s %s' % (where, order_by)
+ elif service == 'cloudinary':
+ query = 'SELECT cloudinary_title AS img_title, cloudinary_url AS img_url FROM cloudinary_lookup ' \
+ 'JOIN image_hash_lookup ON cloudinary_lookup.img_hash = image_hash_lookup.img_hash ' \
+ '%s %s' % (where, order_by)
+ else:
+ logger.warn(u"Tautulli DataFactory :: Unable to execute database query for get_img_info: "
+ "service not provided.")
+ return img_info
try:
- imgur_info = monitor_db.select(query, args=args)
+ img_info = monitor_db.select(query, args=args)
except Exception as e:
- logger.warn(u"Tautulli DataFactory :: Unable to execute database query for get_imgur_info: %s." % e)
+ logger.warn(u"Tautulli DataFactory :: Unable to execute database query for get_img_info: %s." % e)
- return imgur_info
+ return img_info
- def set_imgur_info(self, img_hash=None, imgur_title=None, imgur_url=None, delete_hash=None):
+ def set_img_info(self, img_hash=None, img_title=None, img_url=None, delete_hash=None, service=None):
monitor_db = database.MonitorDatabase()
keys = {'img_hash': img_hash}
- values = {'imgur_title': imgur_title,
- 'imgur_url': imgur_url,
- 'delete_hash': delete_hash}
- monitor_db.upsert('imgur_lookup', key_dict=keys, value_dict=values)
+ if service == 'imgur':
+ table = 'imgur_lookup'
+ values = {'imgur_title': img_title,
+ 'imgur_url': img_url,
+ 'delete_hash': delete_hash}
+ elif service == 'cloudinary':
+ table = 'cloudinary_lookup'
+ values = {'cloudinary_title': img_title,
+ 'cloudinary_url': img_url}
+ else:
+ logger.warn(u"Tautulli DataFactory :: Unable to execute database query for set_img_info: "
+ "service not provided.")
+ return
+
+ monitor_db.upsert(table, key_dict=keys, value_dict=values)
def delete_imgur_info(self, rating_key=None):
monitor_db = database.MonitorDatabase()
@@ -1234,7 +1254,7 @@ class DataFactory(object):
poster_info = {}
if poster_key:
- imgur_info = self.get_imgur_info(rating_key=poster_key, order_by='height', fallback='poster')
+ imgur_info = self.get_img_info(rating_key=poster_key, order_by='height', fallback='poster', service='imgur')
if imgur_info:
poster_info = {'poster_title': imgur_info[0]['imgur_title'],
'poster_url': imgur_info[0]['imgur_url']}
diff --git a/plexpy/helpers.py b/plexpy/helpers.py
index d3d587f1..1dace202 100644
--- a/plexpy/helpers.py
+++ b/plexpy/helpers.py
@@ -14,6 +14,8 @@
# along with Tautulli. If not, see .
import base64
+import cloudinary
+from cloudinary.uploader import upload
import datetime
from functools import wraps
import geoip2.database, geoip2.errors
@@ -28,7 +30,6 @@ import math
import maxminddb
from operator import itemgetter
import os
-from ratelimit import rate_limited
import re
import socket
import sys
@@ -705,14 +706,13 @@ def anon_url(*url):
return '' if None in url else '%s%s' % (plexpy.CONFIG.ANON_REDIRECT, ''.join(str(s) for s in url))
-@rate_limited(450, 3600)
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:
- logger.error(u"Tautulli Helpers :: Cannot upload poster to Imgur. No Imgur client id specified in the settings.")
+ logger.error(u"Tautulli Helpers :: Cannot upload image to Imgur. No Imgur client id specified in the settings.")
return img_url, delete_hash
headers = {'Authorization': 'Client-ID %s' % client_id}
@@ -761,6 +761,32 @@ def delete_from_imgur(delete_hash, img_title='', fallback=''):
return False
+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
+
+ try:
+ response = upload('data:image/png;base64,{}'.format(base64.b64encode(img_data)),
+ public_id='{}_{}'.format(fallback, rating_key),
+ tags=[fallback, rating_key],
+ context={'title': img_title, 'rating_key': rating_key, 'fallback': fallback})
+ logger.debug(u"Tautulli Helpers :: Image '{}' ({}) uploaded to Cloudinary.".format(img_title, fallback))
+ img_url = response.get('url', '')
+ except Exception as e:
+ logger.error(u"Tautulli Helpers :: Unable to upload image '{}' ({}) to Cloudinary: {}".format(img_title, fallback, e))
+
+ return img_url
+
+
def cache_image(url, image=None):
"""
Saves an image to the cache directory.
diff --git a/plexpy/newsletters.py b/plexpy/newsletters.py
index 9d18ed07..9c3d2c2a 100644
--- a/plexpy/newsletters.py
+++ b/plexpy/newsletters.py
@@ -674,7 +674,7 @@ class RecentlyAdded(Newsletter):
return recently_added
def retrieve_data(self):
- from notification_handler import get_imgur_info, set_hash_image_info
+ from notification_handler import get_img_info, set_hash_image_info
if not self.config['incl_libraries']:
logger.warn(u"Tautulli Newsletters :: Failed to retrieve %s newsletter data: no libraries selected." % self.NAME)
@@ -715,7 +715,7 @@ class RecentlyAdded(Newsletter):
item['art_url'] = ''
else:
- # Upload posters and art to Imgur
+ # Upload posters and art to image hosting service
for item in movies + shows + albums:
if item['media_type'] == 'album':
height = 150
@@ -724,17 +724,17 @@ class RecentlyAdded(Newsletter):
height = 225
fallback = 'poster'
- imgur_info = get_imgur_info(
+ img_info = get_img_info(
img=item['thumb'], rating_key=item['rating_key'], title=item['title'],
width=150, height=height, fallback=fallback)
- item['poster_url'] = imgur_info.get('imgur_url') or common.ONLINE_POSTER_THUMB
+ item['poster_url'] = img_info.get('img_url') or common.ONLINE_POSTER_THUMB
- imgur_info = get_imgur_info(
+ img_info = get_img_info(
img=item['art'], rating_key=item['rating_key'], title=item['title'],
width=500, height=280, opacity=25, background='282828', blur=3, fallback='art')
- item['art_url'] = imgur_info.get('imgur_url')
+ item['art_url'] = img_info.get('img_url')
item['thumb_hash'] = ''
item['art_hash'] = ''
diff --git a/plexpy/notification_handler.py b/plexpy/notification_handler.py
index 0a5b25d8..825114e9 100644
--- a/plexpy/notification_handler.py
+++ b/plexpy/notification_handler.py
@@ -632,9 +632,9 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, m
else:
poster_thumb = ''
- if plexpy.CONFIG.NOTIFY_UPLOAD_POSTERS == 1:
- imgur_info = get_imgur_info(img=poster_thumb, rating_key=poster_key, title=poster_title, fallback='poster')
- poster_info = {'poster_title': imgur_info['imgur_title'], 'poster_url': imgur_info['imgur_url']}
+ if plexpy.CONFIG.NOTIFY_UPLOAD_POSTERS in (1, 3):
+ img_info = get_img_info(img=poster_thumb, rating_key=poster_key, title=poster_title, fallback='poster')
+ poster_info = {'poster_title': img_info['img_title'], 'poster_url': img_info['img_url']}
notify_params.update(poster_info)
elif plexpy.CONFIG.NOTIFY_UPLOAD_POSTERS == 2 and plexpy.CONFIG.HTTP_BASE_URL:
img_hash = set_hash_image_info(img=poster_thumb, fallback='poster')
@@ -1076,9 +1076,22 @@ def format_group_index(group_keys):
return ','.join(num) or '0', ','.join(num00) or '00'
-def get_imgur_info(img=None, rating_key=None, title='', width=600, height=1000,
- opacity=100, background='000000', blur=0, fallback=None):
- imgur_info = {'imgur_title': '', 'imgur_url': ''}
+def get_img_info(img=None, rating_key=None, title='', width=600, height=1000,
+ opacity=100, background='000000', blur=0, fallback=None):
+ img_info = {'img_title': '', 'img_url': ''}
+
+ if not rating_key and not img:
+ return img_info
+
+ if rating_key and not img:
+ if fallback == 'art':
+ img = '/library/metadata/{}/art'.format(rating_key)
+ else:
+ img = '/library/metadata/{}/thumb'.format(rating_key)
+
+ img_split = img.split('/')
+ img = '/'.join(img_split[:5])
+ rating_key = rating_key or img_split[3]
image_info = {'img': img,
'rating_key': rating_key,
@@ -1089,33 +1102,49 @@ def get_imgur_info(img=None, rating_key=None, title='', width=600, height=1000,
'blur': blur,
'fallback': fallback}
+ if plexpy.CONFIG.NOTIFY_UPLOAD_POSTERS == 1:
+ service = 'imgur'
+ elif plexpy.CONFIG.NOTIFY_UPLOAD_POSTERS == 3:
+ service = 'cloudinary'
+ else:
+ service = None
+
# Try to retrieve poster info from the database
data_factory = datafactory.DataFactory()
- database_imgur_info = data_factory.get_imgur_info(**image_info)
+ database_img_info = data_factory.get_img_info(service=service, **image_info)
- if database_imgur_info:
- imgur_info = database_imgur_info[0]
+ if database_img_info:
+ img_info = database_img_info[0]
- elif not database_imgur_info and img:
+ elif not database_img_info and img:
pms_connect = pmsconnect.PmsConnect()
result = pms_connect.get_image(**image_info)
if result and result[0]:
- imgur_url, delete_hash = helpers.upload_to_imgur(img_data=result[0],
- img_title=title,
- rating_key=rating_key,
- fallback=fallback)
+ img_url = delete_hash = ''
- if imgur_url:
+ if service == 'imgur':
+ img_url, delete_hash = helpers.upload_to_imgur(img_data=result[0],
+ img_title=title,
+ rating_key=rating_key,
+ fallback=fallback)
+ elif service == 'cloudinary':
+ img_url = helpers.upload_to_cloudinary(img_data=result[0],
+ img_title=title,
+ rating_key=rating_key,
+ fallback=fallback)
+
+ if img_url:
img_hash = set_hash_image_info(**image_info)
- data_factory.set_imgur_info(img_hash=img_hash,
- imgur_title=title,
- imgur_url=imgur_url,
- delete_hash=delete_hash)
+ data_factory.set_img_info(img_hash=img_hash,
+ img_title=title,
+ img_url=img_url,
+ delete_hash=delete_hash,
+ service=service)
- imgur_info = {'imgur_title': title, 'imgur_url': imgur_url}
+ img_info = {'img_title': title, 'img_url': img_url}
- return imgur_info
+ return img_info
def set_hash_image_info(img=None, rating_key=None, width=600, height=1000,
diff --git a/plexpy/webserve.py b/plexpy/webserve.py
index aeac2026..d7b47e52 100644
--- a/plexpy/webserve.py
+++ b/plexpy/webserve.py
@@ -2739,6 +2739,9 @@ class WebInterface(object):
"group_history_tables": checked(plexpy.CONFIG.GROUP_HISTORY_TABLES),
"git_token": plexpy.CONFIG.GIT_TOKEN,
"imgur_client_id": plexpy.CONFIG.IMGUR_CLIENT_ID,
+ "cloudinary_cloud_name": plexpy.CONFIG.CLOUDINARY_CLOUD_NAME,
+ "cloudinary_api_key": plexpy.CONFIG.CLOUDINARY_API_KEY,
+ "cloudinary_api_secret": plexpy.CONFIG.CLOUDINARY_API_SECRET,
"cache_images": checked(plexpy.CONFIG.CACHE_IMAGES),
"pms_version": plexpy.CONFIG.PMS_VERSION,
"plexpy_auto_update": checked(plexpy.CONFIG.PLEXPY_AUTO_UPDATE),