mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-12 08:16:06 -07:00
Upload Plex posters to Imgur for notifications
This commit is contained in:
parent
eab6365af9
commit
284ab45a17
4 changed files with 93 additions and 171 deletions
|
@ -1,107 +0,0 @@
|
||||||
'''OpenAnything: a kind and thoughtful library for HTTP web services
|
|
||||||
|
|
||||||
This program is part of 'Dive Into Python', a free Python book for
|
|
||||||
experienced programmers. Visit http://diveintopython.org/ for the
|
|
||||||
latest version.
|
|
||||||
'''
|
|
||||||
|
|
||||||
__author__ = 'Mark Pilgrim (mark@diveintopython.org)'
|
|
||||||
__version__ = '$Revision: 1.6 $'[11:-2]
|
|
||||||
__date__ = '$Date: 2004/04/16 21:16:24 $'
|
|
||||||
__copyright__ = 'Copyright (c) 2004 Mark Pilgrim'
|
|
||||||
__license__ = 'Python'
|
|
||||||
|
|
||||||
import urllib2, urlparse, gzip
|
|
||||||
from StringIO import StringIO
|
|
||||||
|
|
||||||
USER_AGENT = 'OpenAnything/%s +http://diveintopython.org/http_web_services/' % __version__
|
|
||||||
|
|
||||||
class SmartRedirectHandler(urllib2.HTTPRedirectHandler):
|
|
||||||
def http_error_301(self, req, fp, code, msg, headers):
|
|
||||||
result = urllib2.HTTPRedirectHandler.http_error_301(
|
|
||||||
self, req, fp, code, msg, headers)
|
|
||||||
result.status = code
|
|
||||||
return result
|
|
||||||
|
|
||||||
def http_error_302(self, req, fp, code, msg, headers):
|
|
||||||
result = urllib2.HTTPRedirectHandler.http_error_302(
|
|
||||||
self, req, fp, code, msg, headers)
|
|
||||||
result.status = code
|
|
||||||
return result
|
|
||||||
|
|
||||||
class DefaultErrorHandler(urllib2.HTTPDefaultErrorHandler):
|
|
||||||
def http_error_default(self, req, fp, code, msg, headers):
|
|
||||||
result = urllib2.HTTPError(
|
|
||||||
req.get_full_url(), code, msg, headers, fp)
|
|
||||||
result.status = code
|
|
||||||
return result
|
|
||||||
|
|
||||||
def openAnything(source, etag=None, lastmodified=None, agent=USER_AGENT):
|
|
||||||
"""URL, filename, or string --> stream
|
|
||||||
|
|
||||||
This function lets you define parsers that take any input source
|
|
||||||
(URL, pathname to local or network file, or actual data as a string)
|
|
||||||
and deal with it in a uniform manner. Returned object is guaranteed
|
|
||||||
to have all the basic stdio read methods (read, readline, readlines).
|
|
||||||
Just .close() the object when you're done with it.
|
|
||||||
|
|
||||||
If the etag argument is supplied, it will be used as the value of an
|
|
||||||
If-None-Match request header.
|
|
||||||
|
|
||||||
If the lastmodified argument is supplied, it must be a formatted
|
|
||||||
date/time string in GMT (as returned in the Last-Modified header of
|
|
||||||
a previous request). The formatted date/time will be used
|
|
||||||
as the value of an If-Modified-Since request header.
|
|
||||||
|
|
||||||
If the agent argument is supplied, it will be used as the value of a
|
|
||||||
User-Agent request header.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if hasattr(source, 'read'):
|
|
||||||
return source
|
|
||||||
|
|
||||||
if source == '-':
|
|
||||||
return sys.stdin
|
|
||||||
|
|
||||||
if urlparse.urlparse(source)[0] == 'http':
|
|
||||||
# open URL with urllib2
|
|
||||||
request = urllib2.Request(source)
|
|
||||||
request.add_header('User-Agent', agent)
|
|
||||||
if lastmodified:
|
|
||||||
request.add_header('If-Modified-Since', lastmodified)
|
|
||||||
if etag:
|
|
||||||
request.add_header('If-None-Match', etag)
|
|
||||||
request.add_header('Accept-encoding', 'gzip')
|
|
||||||
opener = urllib2.build_opener(SmartRedirectHandler(), DefaultErrorHandler())
|
|
||||||
return opener.open(request)
|
|
||||||
|
|
||||||
# try to open with native open function (if source is a filename)
|
|
||||||
try:
|
|
||||||
return open(source)
|
|
||||||
except (IOError, OSError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# treat source as string
|
|
||||||
return StringIO(str(source))
|
|
||||||
|
|
||||||
def fetch(source, etag=None, lastmodified=None, agent=USER_AGENT):
|
|
||||||
'''Fetch data and metadata from a URL, file, stream, or string'''
|
|
||||||
result = {}
|
|
||||||
f = openAnything(source, etag, lastmodified, agent)
|
|
||||||
result['data'] = f.read()
|
|
||||||
if hasattr(f, 'headers'):
|
|
||||||
# save ETag, if the server sent one
|
|
||||||
result['etag'] = f.headers.get('ETag')
|
|
||||||
# save Last-Modified header, if the server sent one
|
|
||||||
result['lastmodified'] = f.headers.get('Last-Modified')
|
|
||||||
if f.headers.get('content-encoding') == 'gzip':
|
|
||||||
# data came back gzip-compressed, decompress it
|
|
||||||
result['data'] = gzip.GzipFile(fileobj=StringIO(result['data'])).read()
|
|
||||||
if hasattr(f, 'url'):
|
|
||||||
result['url'] = f.url
|
|
||||||
result['status'] = 200
|
|
||||||
if hasattr(f, 'status'):
|
|
||||||
result['status'] = f.status
|
|
||||||
f.close()
|
|
||||||
return result
|
|
||||||
|
|
|
@ -13,23 +13,25 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from IPy import IP
|
import base64
|
||||||
import datetime
|
import datetime
|
||||||
import fnmatch
|
import fnmatch
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
from IPy import IP
|
||||||
import json
|
import json
|
||||||
import os
|
|
||||||
import math
|
import math
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import socket
|
import socket
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from xml.dom import minidom
|
|
||||||
import unicodedata
|
import unicodedata
|
||||||
|
import urllib, urllib2
|
||||||
|
from xml.dom import minidom
|
||||||
import xmltodict
|
import xmltodict
|
||||||
|
|
||||||
import plexpy
|
import plexpy
|
||||||
from api2 import API2
|
from api2 import API2
|
||||||
|
|
||||||
|
@ -523,3 +525,37 @@ def anon_url(*url):
|
||||||
Return a URL string consisting of the Anonymous redirect URL and an arbitrary number of values appended.
|
Return a URL string consisting of the Anonymous redirect URL and an arbitrary number of values appended.
|
||||||
"""
|
"""
|
||||||
return '' if None in url else '%s%s' % (plexpy.CONFIG.ANON_REDIRECT, ''.join(str(s) for s in url))
|
return '' if None in url else '%s%s' % (plexpy.CONFIG.ANON_REDIRECT, ''.join(str(s) for s in url))
|
||||||
|
|
||||||
|
def uploadToImgur(imgPath, imgTitle=''):
|
||||||
|
from plexpy import logger
|
||||||
|
|
||||||
|
client_id = '743b1a443ccd2b0'
|
||||||
|
img_url = ''
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(imgPath, 'rb') as imgFile:
|
||||||
|
img = imgFile.read()
|
||||||
|
except IOError as e:
|
||||||
|
logger.error(u"PlexPy Helpers :: Unable to read image file for Imgur: %s" % e)
|
||||||
|
return img_url
|
||||||
|
|
||||||
|
headers = {'Authorization': 'Client-ID %s' % client_id}
|
||||||
|
data = {'type': 'base64',
|
||||||
|
'image': base64.b64encode(img)}
|
||||||
|
if imgTitle:
|
||||||
|
data['title'] = imgTitle
|
||||||
|
data['name'] = imgTitle + '.jpg'
|
||||||
|
|
||||||
|
request = urllib2.Request('https://api.imgur.com/3/image', headers=headers, data=urllib.urlencode(data))
|
||||||
|
response = urllib2.urlopen(request)
|
||||||
|
response = json.loads(response.read())
|
||||||
|
|
||||||
|
if response.get('status') == 200:
|
||||||
|
logger.debug(u"PlexPy Helpers :: Image uploaded to Imgur.")
|
||||||
|
img_url = response.get('data').get('link', '')
|
||||||
|
elif response.get('status') >= 400 and response.get('status') < 500:
|
||||||
|
logger.warn(u"PlexPy Helpers :: Unable to upload image to Imgur: %s" % response.reason)
|
||||||
|
else:
|
||||||
|
logger.warn(u"PlexPy Helpers :: Unable to upload image to Imgur.")
|
||||||
|
|
||||||
|
return img_url
|
|
@ -15,12 +15,10 @@
|
||||||
|
|
||||||
|
|
||||||
import arrow
|
import arrow
|
||||||
import json
|
import os
|
||||||
from httplib import HTTPConnection
|
|
||||||
import openanything
|
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
import urllib2
|
import urllib
|
||||||
|
|
||||||
from plexpy import logger, config, notifiers, database, helpers, plextv, pmsconnect
|
from plexpy import logger, config, notifiers, database, helpers, plextv, pmsconnect
|
||||||
import plexpy
|
import plexpy
|
||||||
|
@ -157,7 +155,8 @@ def notify(stream_data=None, notify_action=None):
|
||||||
subject=notify_strings[0],
|
subject=notify_strings[0],
|
||||||
body=notify_strings[1],
|
body=notify_strings[1],
|
||||||
notify_action=notify_action,
|
notify_action=notify_action,
|
||||||
script_args=notify_strings[2])
|
script_args=notify_strings[2],
|
||||||
|
metadata=notify_strings[3])
|
||||||
|
|
||||||
# Set the notification state in the db
|
# Set the notification state in the db
|
||||||
set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
|
set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
|
||||||
|
@ -169,7 +168,8 @@ def notify(stream_data=None, notify_action=None):
|
||||||
subject=notify_strings[0],
|
subject=notify_strings[0],
|
||||||
body=notify_strings[1],
|
body=notify_strings[1],
|
||||||
notify_action=notify_action,
|
notify_action=notify_action,
|
||||||
script_args=notify_strings[2])
|
script_args=notify_strings[2],
|
||||||
|
metadata=notify_strings[3])
|
||||||
|
|
||||||
# Set the notification state in the db
|
# Set the notification state in the db
|
||||||
set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
|
set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
|
||||||
|
@ -181,7 +181,8 @@ def notify(stream_data=None, notify_action=None):
|
||||||
subject=notify_strings[0],
|
subject=notify_strings[0],
|
||||||
body=notify_strings[1],
|
body=notify_strings[1],
|
||||||
notify_action=notify_action,
|
notify_action=notify_action,
|
||||||
script_args=notify_strings[2])
|
script_args=notify_strings[2],
|
||||||
|
metadata=notify_strings[3])
|
||||||
|
|
||||||
# Set the notification state in the db
|
# Set the notification state in the db
|
||||||
set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
|
set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
|
||||||
|
@ -193,7 +194,8 @@ def notify(stream_data=None, notify_action=None):
|
||||||
subject=notify_strings[0],
|
subject=notify_strings[0],
|
||||||
body=notify_strings[1],
|
body=notify_strings[1],
|
||||||
notify_action=notify_action,
|
notify_action=notify_action,
|
||||||
script_args=notify_strings[2])
|
script_args=notify_strings[2],
|
||||||
|
metadata=notify_strings[3])
|
||||||
|
|
||||||
# Set the notification state in the db
|
# Set the notification state in the db
|
||||||
set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
|
set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
|
||||||
|
@ -205,7 +207,8 @@ def notify(stream_data=None, notify_action=None):
|
||||||
subject=notify_strings[0],
|
subject=notify_strings[0],
|
||||||
body=notify_strings[1],
|
body=notify_strings[1],
|
||||||
notify_action=notify_action,
|
notify_action=notify_action,
|
||||||
script_args=notify_strings[2])
|
script_args=notify_strings[2],
|
||||||
|
metadata=notify_strings[3])
|
||||||
|
|
||||||
# Set the notification state in the db
|
# Set the notification state in the db
|
||||||
set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
|
set_notify_state(session=stream_data, state=notify_action, agent_info=agent)
|
||||||
|
@ -499,46 +502,21 @@ def build_notify_text(session=None, timeline=None, state=None):
|
||||||
metadata['lastfm_id'] = metadata['guid'].split('lastfm://')[1].rsplit('/', 1)[0]
|
metadata['lastfm_id'] = metadata['guid'].split('lastfm://')[1].rsplit('/', 1)[0]
|
||||||
metadata['lastfm_url'] = 'https://www.last.fm/music/' + metadata['lastfm_id']
|
metadata['lastfm_url'] = 'https://www.last.fm/music/' + metadata['lastfm_id']
|
||||||
|
|
||||||
# Get posters (only IMDB and TheTVDB supported)
|
if metadata['media_type'] == 'movie' or metadata['media_type'] == 'show' or metadata['media_type'] == 'artist':
|
||||||
if metadata['media_type'] == 'movie' and metadata.get('imdb_id', ''):
|
thumb = metadata['thumb']
|
||||||
uri = '/?i=' + metadata['imdb_id']
|
elif metadata['media_type'] == 'episode':
|
||||||
|
thumb = metadata['grandparent_thumb']
|
||||||
# Get poster using OMDb API
|
elif metadata['media_type'] == 'track':
|
||||||
http_handler = HTTPConnection("www.omdbapi.com")
|
thumb = metadata['parent_thumb']
|
||||||
http_handler.request('GET', uri)
|
|
||||||
response = http_handler.getresponse()
|
|
||||||
request_status = response.status
|
|
||||||
|
|
||||||
if request_status == 200:
|
|
||||||
data = json.loads(response.read())
|
|
||||||
poster_url = data.get('Poster', '')
|
|
||||||
metadata['poster_url'] = poster_url if poster_url != 'N/A' else ''
|
|
||||||
elif request_status >= 400 and request_status < 500:
|
|
||||||
logger.warn(u"PlexPy Notifiers :: Unable to retrieve IMDB poster: %s" % response.reason)
|
|
||||||
else:
|
else:
|
||||||
logger.warn(u"PlexPy Notifiers :: Unable to retrieve IMDB poster.")
|
thumb = None
|
||||||
|
|
||||||
elif (metadata['media_type'] == 'show' or metadata['media_type'] == 'episode') \
|
if thumb:
|
||||||
and (metadata.get('imdb_id', '') or metadata.get('thetvdb_id', '')):
|
# Retrieve the poster from Plex and cache to file
|
||||||
if metadata.get('imdb_id', ''):
|
urllib.urlretrieve(plexpy.CONFIG.PMS_URL + thumb + '?X-Plex-Token=' + plexpy.CONFIG.PMS_TOKEN,
|
||||||
uri = '/lookup/shows?imdb=' + metadata['imdb_id']
|
os.path.join(plexpy.CONFIG.CACHE_DIR, 'cache-poster.jpg'))
|
||||||
elif metadata.get('thetvdb_id', ''):
|
# Upload thumb to Imgur and get link
|
||||||
uri = '/lookup/shows?thetvdb=' + metadata['thetvdb_id']
|
metadata['poster_url'] = helpers.uploadToImgur(os.path.join(plexpy.CONFIG.CACHE_DIR, 'cache-poster.jpg'), full_title)
|
||||||
|
|
||||||
# Get poster using TVmaze API
|
|
||||||
request = urllib2.Request('http://api.tvmaze.com' + uri)
|
|
||||||
opener = urllib2.build_opener(openanything.SmartRedirectHandler())
|
|
||||||
response = opener.open(request)
|
|
||||||
request_status = response.status
|
|
||||||
|
|
||||||
if request_status == 301:
|
|
||||||
data = json.loads(response.read())
|
|
||||||
image = data.get('image', '')
|
|
||||||
metadata['poster_url'] = image.get('original', image.get('medium',''))
|
|
||||||
elif request_status >= 400 and request_status < 500:
|
|
||||||
logger.warn(u"PlexPy Notifiers :: Unable to retrieve TVmaze poster: %s" % response.reason)
|
|
||||||
else:
|
|
||||||
logger.warn(u"PlexPy Notifiers :: Unable to retrieve TVmaze poster.")
|
|
||||||
|
|
||||||
# Fix metadata params for notify recently added grandparent
|
# Fix metadata params for notify recently added grandparent
|
||||||
if state == 'created' and plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT:
|
if state == 'created' and plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT:
|
||||||
|
|
|
@ -19,8 +19,7 @@ import json
|
||||||
import cherrypy
|
import cherrypy
|
||||||
from email.mime.text import MIMEText
|
from email.mime.text import MIMEText
|
||||||
import email.utils
|
import email.utils
|
||||||
from httplib import HTTPSConnection, HTTPConnection
|
from httplib import HTTPSConnection
|
||||||
import openanything
|
|
||||||
import os
|
import os
|
||||||
import shlex
|
import shlex
|
||||||
import smtplib
|
import smtplib
|
||||||
|
@ -2129,19 +2128,35 @@ class FacebookNotifier(object):
|
||||||
if self.incl_poster and 'metadata' in kwargs:
|
if self.incl_poster and 'metadata' in kwargs:
|
||||||
metadata = kwargs['metadata']
|
metadata = kwargs['metadata']
|
||||||
poster_url = metadata.get('poster_url','')
|
poster_url = metadata.get('poster_url','')
|
||||||
caption = 'View in Plex Web.'
|
|
||||||
|
|
||||||
|
if poster_url:
|
||||||
if metadata['media_type'] == 'movie' or metadata['media_type'] == 'show':
|
if metadata['media_type'] == 'movie' or metadata['media_type'] == 'show':
|
||||||
title = metadata['title']
|
title = metadata['title']
|
||||||
subtitle = metadata['year']
|
subtitle = metadata['year']
|
||||||
elif metadata['media_type'] == 'episode':
|
rating_key = metadata['rating_key']
|
||||||
title = metadata['grandparent_title'] + ' - ' + metadata['title']
|
|
||||||
subtitle = 'S' + metadata['parent_media_index'] + ' ' + '\xc2\xb7'.decode('utf8') + \
|
|
||||||
' E' + metadata['media_index']
|
|
||||||
|
|
||||||
if poster_url:
|
elif metadata['media_type'] == 'episode':
|
||||||
|
title = '%s - %s' % (metadata['grandparent_title'], metadata['title'])
|
||||||
|
subtitle = 'S%s %s E%s' % (metadata['parent_media_index'],
|
||||||
|
'\xc2\xb7'.decode('utf8'),
|
||||||
|
metadata['media_index'])
|
||||||
|
rating_key = metadata['rating_key']
|
||||||
|
|
||||||
|
elif metadata['media_type'] == 'artist':
|
||||||
|
title = metadata['title']
|
||||||
|
subtitle = ''
|
||||||
|
rating_key = metadata['rating_key']
|
||||||
|
|
||||||
|
elif metadata['media_type'] == 'track':
|
||||||
|
title = '%s - %s' % (metadata['grandparent_title'], metadata['title'])
|
||||||
|
subtitle = metadata['parent_title']
|
||||||
|
rating_key = metadata['parent_rating_key']
|
||||||
|
|
||||||
|
caption = 'View in Plex Web.'
|
||||||
|
|
||||||
|
# Build Facebook post attachment
|
||||||
attachment['link'] = 'http://app.plex.tv/web/app#!/server/' + plexpy.CONFIG.PMS_IDENTIFIER + \
|
attachment['link'] = 'http://app.plex.tv/web/app#!/server/' + plexpy.CONFIG.PMS_IDENTIFIER + \
|
||||||
'/details/%2Flibrary%2Fmetadata%2F' + metadata['rating_key']
|
'/details/%2Flibrary%2Fmetadata%2F' + rating_key
|
||||||
attachment['picture'] = poster_url
|
attachment['picture'] = poster_url
|
||||||
attachment['name'] = title
|
attachment['name'] = title
|
||||||
attachment['description'] = subtitle
|
attachment['description'] = subtitle
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue