Merge branch 'develop' of git://github.com/Boehemyth/CouchPotatoServer into Boehemyth-develop

This commit is contained in:
Ruud
2014-05-19 19:41:31 +02:00
14 changed files with 1109 additions and 51 deletions
@@ -26,7 +26,14 @@ class MovieResultModifier(Plugin):
'backdrop': [],
'poster_original': [],
'backdrop_original': [],
'actors': {}
'actors': {},
'landscape': [],
'logo': [],
'clearart': [],
'discart': [],
'banner': [],
'extrathumbs': [],
'extrafanart': []
},
'runtime': 0,
'plot': '',
@@ -0,0 +1,168 @@
import os
import traceback
from couchpotato.core.event import addEvent
from couchpotato.core.logger import CPLog
from couchpotato.core.media.movie.providers.base import MovieProvider
from couchpotato.core.plugins.quality import QualityPlugin
from libs.fanarttv.movie import Movie
import libs.fanarttv.errors as fanarttv_errors
log = CPLog(__name__)
autoload = 'FanartTV'
class FanartTV(MovieProvider):
MAX_EXTRAFANART = 20
def __init__(self):
addEvent('movie.extraart', self.getArt, priority=2)
# Configure fanarttv API settings
os.environ.setdefault('FANART_APIKEY', self.conf('api_key'))
def getArt(self, identifier):
# FIXME: I believe I should be registering a cache here... I need to look into that.
log.debug("Getting Extra Artwork from Fanart.tv...")
if not identifier:
return {}
images = {}
try:
try:
exists = True
movie = Movie.get(id=identifier)
except (fanarttv_errors.FanartError, IOError):
exists = False
if exists:
images = self._parseMovie(movie, True)
except:
log.error('Failed getting extra art for %s: %s',
(identifier, traceback.format_exc()))
return {}
return images
def _parseMovie(self, movie, isHD):
images = {
'landscape': [],
'logo': [],
'discart': [],
'clearart': [],
'banner': [],
'extrafanart': []
}
images['landscape'] = self._getMultImages(movie.thumbs, 1)
images['banner'] = self._getMultImages(movie.banners, 1)
images['discart'] = self._getMultImages(self._trimDiscs(movie.discs, isHD), 1)
images['clearart'] = self._getMultImages(movie.hdarts, 1)
if len(images['clearart']) is 0:
images['clearart'] = self._getMultImages(movie.arts, 1)
images['logo'] = self._getMultImages(movie.hdlogos, 1)
if len(images['logo']) is 0:
images['logo'] = self._getMultImages(movie.logos, 1)
fanarts = self._getMultImages(movie.backgrounds, self.MAX_EXTRAFANART + 1)
if fanarts:
images['backdrop_original'] = fanarts[0]
images['extrafanart'] = fanarts[1:]
# TODO: Add support for extra backgrounds
#extraFanart = self._getMultImages(movie.backgrounds, -1)
return images
def _trimDiscs(self, discImages, isHD):
'''
Return a subset of discImages based on isHD. If isHD is true, only
bluray disc images will be returned. If isHD is false, only dvd disc
images will be returned. If the resulting list would be an empty list,
then the original list is returned instead.
'''
trimmed = []
for disc in discImages:
if isHD and disc.disc_type == u'bluray':
trimmed.append(disc)
elif not isHD and disc.disc_type == u'dvd':
trimmed.append(disc)
if len(trimmed) is 0:
return discImages
else:
return trimmed
def _getImage(self, images):
image_url = None
highscore = -1
for image in images:
if image.likes > highscore:
highscore = image.likes
image_url = image.url
return image_url
def _getMultImages(self, images, n):
'''
Chooses the best n images and returns them as a list.
If n<0, all images will be returned.
'''
image_urls = []
pool = []
for image in images:
if image.lang == u'en':
pool.append(image)
origPoolSize = len(pool)
while len(pool) > 0 and (n < 0 or origPoolSize - len(pool) < n):
best = None
highscore = -1
for image in pool:
if image.likes > highscore:
highscore = image.likes
best = image
image_urls.append(best.url)
pool.remove(best)
return image_urls
def isDisabled(self):
if self.conf('api_key') == '':
log.error('No API key provided.')
return True
return False
def _determineHD(self, quality):
for qualityDef in QualityPlugin.qualities:
if quality == qualityDef.get('identifier'):
return bool(qualityDef.get('hd'))
return False
config = [{
'name': 'fanarttv',
'groups': [
{
'tab': 'providers',
'name': 'fanarttv',
'label': 'fanart.tv',
'hidden': True,
'description': 'Used for all calls to fanart.tv.',
'options': [
{
'name': 'api_key',
'default': 'd788b4822b9e1f44068026e05557e5d9',
'label': 'API Key',
},
],
},
],
}]
@@ -1,6 +1,6 @@
import traceback
from couchpotato.core.event import addEvent
from couchpotato.core.event import addEvent, fireEvent
from couchpotato.core.helpers.encoding import simplifyString, toUnicode, ss
from couchpotato.core.helpers.variable import tryInt
from couchpotato.core.logger import CPLog
@@ -13,6 +13,7 @@ autoload = 'TheMovieDb'
class TheMovieDb(MovieProvider):
MAX_EXTRATHUMBS = 4
def __init__(self):
addEvent('movie.info', self.getInfo, priority = 2)
@@ -87,6 +88,13 @@ class TheMovieDb(MovieProvider):
except:
log.error('Failed getting info for %s: %s', (identifier, traceback.format_exc()))
# Get extra artwork via Fanart.TV and merge into images dict
try:
extraArt = fireEvent('movie.extraart', identifier)[0]
result['images'] = dict(result['images'].items() + extraArt.items())
except IndexError:
pass
return result
def parseMovie(self, movie, extended = True):
@@ -100,13 +108,15 @@ class TheMovieDb(MovieProvider):
poster = self.getImage(movie, type = 'poster', size = 'poster')
poster_original = self.getImage(movie, type = 'poster', size = 'original')
backdrop_original = self.getImage(movie, type = 'backdrop', size = 'original')
extrathumbs = self.getMultImages(movie, type='backdrops', size='original', n=self.MAX_EXTRATHUMBS, skipfirst=True)
images = {
'poster': [poster] if poster else [],
#'backdrop': [backdrop] if backdrop else [],
'poster_original': [poster_original] if poster_original else [],
'backdrop_original': [backdrop_original] if backdrop_original else [],
'actors': {}
'actors': {},
'extrathumbs': extrathumbs
}
# Genres
@@ -171,6 +181,28 @@ class TheMovieDb(MovieProvider):
log.debug('Failed getting %s.%s for "%s"', (type, size, ss(str(movie))))
return image_url
def getMultImages(self, movie, type='backdrops', size='original', n=-1, skipfirst=False):
'''
If n < 0, return all images. Otherwise return n images.
If n > len(getattr(movie, type)), then return all images.
If skipfirst is True, then it will skip getattr(movie, type)[0]. This
is because backdrops[0] is typically backdrop.
'''
image_urls = []
try:
images = getattr(movie, type)
if n < 0 or n > len(images):
numImages = len(images)
else:
numImages = n
for i in range(int(skipfirst), numImages + int(skipfirst)):
image_urls.append(images[i].geturl(size=size))
except:
log.debug('Failed getting %i %s.%s for "%s"', (n, type, size, ss(str(movie))))
return image_urls
def isDisabled(self):
if self.conf('api_key') == '':
@@ -37,76 +37,150 @@ class MovieMetaData(MetaDataBase):
root = os.path.dirname(root_name)
movie_info = group['media'].get('info')
for file_type in ['nfo', 'thumbnail', 'fanart']:
for file_type in ['nfo']:
try:
# Get file path
name = getattr(self, 'get' + file_type.capitalize() + 'Name')(meta_name, root)
self._createType(meta_name, root, movie_info, group, file_type, 0)
except:
log.error('Unable to create %s file: %s', ('nfo', traceback.format_exc()))
if name and (self.conf('meta_' + file_type) or self.conf('meta_' + file_type) is None):
# Get file content
content = getattr(self, 'get' + file_type.capitalize())(movie_info = movie_info, data = group)
if content:
log.debug('Creating %s file: %s', (file_type, name))
if os.path.isfile(content):
content = sp(content)
name = sp(name)
shutil.copy2(content, name)
shutil.copyfile(content, name)
# Try and copy stats seperately
try: shutil.copystat(content, name)
except: pass
else:
self.createFile(name, content)
group['renamed_files'].append(name)
try:
os.chmod(sp(name), Env.getPermission('file'))
except:
log.debug('Failed setting permissions for %s: %s', (name, traceback.format_exc()))
for file_type in ['thumbnail', 'fanart', 'banner', 'discart', 'logo', 'clearart', 'landscape', 'extrathumbs', 'extrafanart']:
try:
if file_type == 'thumbnail':
numImages = len(movie_info['images']['poster_original'])
elif file_type == 'fanart':
numImages = len(movie_info['images']['backdrop_original'])
else:
numImages = len(movie_info['images'][file_type])
for i in range(numImages):
self._createType(meta_name, root, movie_info, group, file_type, i)
except:
log.error('Unable to create %s file: %s', (file_type, traceback.format_exc()))
def _createType(self, meta_name, root, movie_info, group, file_type, i):# Get file path
name = getattr(self, 'get' + file_type.capitalize() + 'Name')(meta_name, root, i)
if name and (self.conf('meta_' + file_type) or self.conf('meta_' + file_type) is None):
# Get file content
content = getattr(self, 'get' + file_type.capitalize())(movie_info=movie_info, data=group, i=i)
if content:
log.debug('Creating %s file: %s', (file_type, name))
if os.path.isfile(content):
content = sp(content)
name = sp(name)
if not os.path.exists(os.path.dirname(name)):
os.makedirs(os.path.dirname(name))
shutil.copy2(content, name)
shutil.copyfile(content, name)
# Try and copy stats seperately
try: shutil.copystat(content, name)
except: pass
else:
self.createFile(name, content)
group['renamed_files'].append(name)
try:
os.chmod(sp(name), Env.getPermission('file'))
except:
log.debug('Failed setting permissions for %s: %s', (name, traceback.format_exc()))
def getRootName(self, data = None):
if not data: data = {}
return os.path.join(data['destination_dir'], data['filename'])
def getFanartName(self, name, root):
def getFanartName(self, name, root, i):
return
def getThumbnailName(self, name, root):
def getThumbnailName(self, name, root, i):
return
def getNfoName(self, name, root):
def getBannerName(self, name, root, i):
return
def getNfo(self, movie_info = None, data = None):
def getClearartName(self, name, root, i):
return
def getLogoName(self, name, root, i):
return
def getDiscartName(self, name, root, i):
return
def getLandscapeName(self, name, root, i):
return
def getExtrathumbsName(self, name, root, i):
return
def getExtrafanartName(self, name, root, i):
return
def getNfoName(self, name, root, i):
return
def getNfo(self, movie_info=None, data=None, i=0):
if not data: data = {}
if not movie_info: movie_info = {}
def getThumbnail(self, movie_info = None, data = None, wanted_file_type = 'poster_original'):
def getThumbnail(self, movie_info = None, data = None, wanted_file_type = 'poster_original', i = 0):
if not data: data = {}
if not movie_info: movie_info = {}
# See if it is in current files
files = data['media'].get('files')
if files.get('image_' + wanted_file_type):
if os.path.isfile(files['image_' + wanted_file_type][0]):
return files['image_' + wanted_file_type][0]
if os.path.isfile(files['image_' + wanted_file_type][i]):
return files['image_' + wanted_file_type][i]
# Download using existing info
try:
images = movie_info['images'][wanted_file_type]
file_path = fireEvent('file.download', url = images[0], single = True)
file_path = fireEvent('file.download', url = images[i], single = True)
return file_path
except:
pass
def getFanart(self, movie_info = None, data = None):
def getFanart(self, movie_info = None, data = None, i = 0):
if not data: data = {}
if not movie_info: movie_info = {}
return self.getThumbnail(movie_info = movie_info, data = data, wanted_file_type = 'backdrop_original')
return self.getThumbnail(movie_info=movie_info, data=data, wanted_file_type='backdrop_original', i=i)
def getBanner(self, movie_info=None, data=None, i=0):
if not data: data = {}
if not movie_info: movie_info = {}
return self.getThumbnail(movie_info=movie_info, data=data, wanted_file_type='banner', i=i)
def getClearart(self, movie_info=None, data=None, i=0):
if not data: data = {}
if not movie_info: movie_info = {}
return self.getThumbnail(movie_info=movie_info, data=data, wanted_file_type='clearart', i=i)
def getLogo(self, movie_info=None, data=None, i=0):
if not data: data = {}
if not movie_info: movie_info = {}
return self.getThumbnail(movie_info=movie_info, data=data, wanted_file_type='logo', i=i)
def getDiscart(self, movie_info=None, data=None, i=0):
if not data: data = {}
if not movie_info: movie_info = {}
return self.getThumbnail(movie_info=movie_info, data=data, wanted_file_type='discart', i=i)
def getLandscape(self, movie_info=None, data=None, i=0):
if not data: data = {}
if not movie_info: movie_info = {}
return self.getThumbnail(movie_info=movie_info, data=data, wanted_file_type='landscape', i=i)
def getExtrathumbs(self, movie_info=None, data=None, i=0):
if not data: data = {}
if not movie_info: movie_info = {}
return self.getThumbnail(movie_info=movie_info, data=data, wanted_file_type='extrathumbs', i=i)
def getExtrafanart(self, movie_info=None, data=None, i=0):
if not data: data = {}
if not movie_info: movie_info = {}
return self.getThumbnail(movie_info=movie_info, data=data, wanted_file_type='extrafanart', i=i)
@@ -17,19 +17,43 @@ autoload = 'XBMC'
class XBMC(MovieMetaData):
def getFanartName(self, name, root):
def getFanartName(self, name, root, i):
return self.createMetaName(self.conf('meta_fanart_name'), name, root)
def getThumbnailName(self, name, root):
def getThumbnailName(self, name, root, i):
return self.createMetaName(self.conf('meta_thumbnail_name'), name, root)
def getNfoName(self, name, root):
def getNfoName(self, name, root, i):
return self.createMetaName(self.conf('meta_nfo_name'), name, root)
def getBannerName(self, name, root, i):
return self.createMetaName(self.conf('meta_banner_name'), name, root)
def getClearartName(self, name, root, i):
return self.createMetaName(self.conf('meta_clearart_name'), name, root)
def getLogoName(self, name, root, i):
return self.createMetaName(self.conf('meta_logo_name'), name, root)
def getDiscartName(self, name, root, i):
return self.createMetaName(self.conf('meta_discart_name'), name, root)
def getLandscapeName(self, name, root, i):
return self.createMetaName(self.conf('meta_landscape_name'), name, root)
def getExtrathumbsName(self, name, root, i):
return self.createMetaNameMult(self.conf('meta_extrathumbs_name'), name, root, i)
def getExtrafanartName(self, name, root, i):
return self.createMetaNameMult(self.conf('meta_extrafanart_name'), name, root, i)
def createMetaName(self, basename, name, root):
return os.path.join(root, basename.replace('%s', name))
def getNfo(self, movie_info = None, data = None):
def createMetaNameMult(self, basename, name, root, i):
return os.path.join(root, basename.replace('%s', name).replace('%i', str(i + 1)))
def getNfo(self, movie_info=None, data=None, i=0):
if not data: data = {}
if not movie_info: movie_info = {}
@@ -133,6 +157,35 @@ class XBMC(MovieMetaData):
for image_url in movie_info['images']['backdrop_original']:
image = SubElement(fanart, 'thumb')
image.text = toUnicode(image_url)
banner = SubElement(nfoxml, 'banner')
for image_url in movie_info['images']['banner']:
image = SubElement(banner, 'thumb')
image.text = toUnicode(image_url)
discart = SubElement(nfoxml, 'discart')
for image_url in movie_info['images']['discart']:
image = SubElement(discart, 'thumb')
image.text = toUnicode(image_url)
logo = SubElement(nfoxml, 'logo')
for image_url in movie_info['images']['logo']:
image = SubElement(logo, 'thumb')
image.text = toUnicode(image_url)
clearart = SubElement(nfoxml, 'clearart')
for image_url in movie_info['images']['clearart']:
image = SubElement(clearart, 'thumb')
image.text = toUnicode(image_url)
landscape = SubElement(nfoxml, 'landscape')
for image_url in movie_info['images']['landscape']:
image = SubElement(landscape, 'thumb')
image.text = toUnicode(image_url)
extrathumb = SubElement(nfoxml, 'extrathumb')
for image_url in movie_info['images']['extrathumbs']:
image = SubElement(extrathumb, 'thumb')
image.text = toUnicode(image_url)
extrafanart = SubElement(nfoxml, 'extrafanart')
for image_url in movie_info['images']['extrafanart']:
image = SubElement(extrafanart, 'thumb')
image.text = toUnicode(image_url)
# Add trailer if found
trailer_found = False
@@ -239,6 +292,90 @@ config = [{
'default': '%s.tbn',
'advanced': True,
},
{
'name': 'meta_banner',
'label': 'Banner',
'default': True,
'type': 'bool'
},
{
'name': 'meta_banner_name',
'label': 'Banner filename',
'default': 'banner.jpg',
'advanced': True,
},
{
'name': 'meta_clearart',
'label': 'ClearArt',
'default': True,
'type': 'bool'
},
{
'name': 'meta_clearart_name',
'label': 'ClearArt filename',
'default': 'clearart.png',
'advanced': True,
},
{
'name': 'meta_disc',
'label': 'DiscArt',
'default': True,
'type': 'bool'
},
{
'name': 'meta_discart_name',
'label': 'DiscArt filename',
'default': 'disc.png',
'advanced': True,
},
{
'name': 'meta_landscape',
'label': 'Landscape',
'default': True,
'type': 'bool'
},
{
'name': 'meta_landscape_name',
'label': 'Landscape filename',
'default': 'landscape.jpg',
'advanced': True,
},
{
'name': 'meta_logo',
'label': 'ClearLogo',
'default': True,
'type': 'bool'
},
{
'name': 'meta_logo_name',
'label': 'ClearLogo filename',
'default': 'logo.png',
'advanced': True,
},
{
'name': 'meta_extrathumbs',
'label': 'Extrathumbs',
'default': True,
'type': 'bool'
},
{
'name': 'meta_extrathumbs_name',
'label': 'Extrathumbs filename (%i is the image number, and must be included to have multiple images).',
'default': 'extrathumbs/thumb%i.jpg',
'advanced': True
},
{
'name': 'meta_extrafanart',
'lavel': 'Extrafanart',
'default': True,
'type': 'bool'
},
{
'name': 'meta_extrafanart_name',
'label': 'Extrafanart filename (%i is the image number, and must be included to have multiple images).',
'default': 'extrafanart/extrafanart%i.jpg',
'advanced': True
}
],
},
],
+72 -6
View File
@@ -3,6 +3,8 @@ import json
import socket
import traceback
import urllib
import time
import os
from couchpotato.core.helpers.variable import splitString, getTitle
from couchpotato.core.logger import CPLog
@@ -36,7 +38,7 @@ class XBMC(Notification):
if self.use_json_notifications.get(host):
calls = [
('GUI.ShowNotification', {'title': self.default_title, 'message': message, 'image': self.getNotificationImage('small')}),
('GUI.ShowNotification', None, {'title': self.default_title, 'message': message, 'image': self.getNotificationImage('small')}),
]
if data and data.get('destination_dir') and (not self.conf('only_first') or hosts.index(host) == 0):
@@ -44,7 +46,7 @@ class XBMC(Notification):
if not self.conf('force_full_scan') and (self.conf('remote_dir_scan') or socket.getfqdn('localhost') == socket.getfqdn(host.split(':')[0])):
param = {'directory': data['destination_dir']}
calls.append(('VideoLibrary.Scan', param))
calls.append(('VideoLibrary.Scan', None, param))
max_successful += len(calls)
response = self.request(host, calls)
@@ -66,6 +68,50 @@ class XBMC(Notification):
except:
log.error('Failed parsing results: %s', traceback.format_exc())
if self.conf('run_artwork_downloader') and data and self.use_json_notifications.get(host):
time.sleep(self.conf('run_artwork_downloader_delay'))
if self.conf('force_full_scan'):
calls = [('Addons.ExecuteAddon', None, {'addonid': 'script.artwork.downloader'})]
max_successful += len(calls)
response = self.request(host, calls)
try:
if response[0].get('result') and result['result'] == 'OK':
successful += 1
elif response[0].get('error'):
log.error('XBMC error; %s: %s (%s)', (result['id'], result['error']['message'], result['error']['code']))
except:
log.error('Failed parsing results: %s', traceback.format_exc())
else:
calls = [('VideoLibrary.GetMovies', 'libMovies', {"filter":{"field": "title", "operator": "is", "value": data['media']['title'], "year": data['media']['info']['year']}})]
max_successful += len(calls)
response = self.request(host, calls)
dbid = None
try:
if response[0].get('result'):
successful += 1
dbid = response[0]['result']['movies'][-1]['movieid']
elif response[0].get('error'):
log.error('XBMC error; %s: %s (%s)', (result['id'], result['error']['message'], result['error']['code']))
except:
log.error('Failed parsing results: %s', traceback.format_exc())
if dbid is not None:
calls = [('Addons.ExecuteAddon', None, {'addonid': 'script.artwork.downloader', 'params':{'media_type': 'movie', 'dbid': str(dbid)}})]
max_successful += len(calls)
response = self.request(host, calls)
try:
if response[0].get('result') and result['result'] == 'OK':
successful += 1
elif response[0].get('error'):
log.error('XBMC error; %s: %s (%s)', (result['id'], result['error']['message'], result['error']['code']))
except:
log.error('Failed parsing results: %s', traceback.format_exc())
return successful == max_successful
@@ -75,7 +121,7 @@ class XBMC(Notification):
# XBMC JSON-RPC version request
response = self.request(host, [
('JSONRPC.Version', {})
('JSONRPC.Version', None, {})
])
for result in response:
if result.get('result') and type(result['result']['version']).__name__ == 'int':
@@ -112,7 +158,7 @@ class XBMC(Notification):
self.use_json_notifications[host] = True
# send the text message
resp = self.request(host, [('GUI.ShowNotification', {'title':self.default_title, 'message':message, 'image': self.getNotificationImage('small')})])
resp = self.request(host, [('GUI.ShowNotification', None, {'title':self.default_title, 'message':message, 'image': self.getNotificationImage('small')})])
for r in resp:
if r.get('result') and r['result'] == 'OK':
log.debug('Message delivered successfully!')
@@ -184,12 +230,16 @@ class XBMC(Notification):
data = []
for req in do_requests:
method, kwargs = req
method, id, kwargs = req
if id is None:
id = method
data.append({
'method': method,
'params': kwargs,
'jsonrpc': '2.0',
'id': method,
'id': id,
})
data = json.dumps(data)
@@ -273,6 +323,22 @@ config = [{
'advanced': True,
'description': 'Also send message when movie is snatched.',
},
{
'name': 'run_artwork_downloader',
'label': 'Run the Artwork Downloader',
'default': 0,
'type': 'bool',
'advanced': True,
'description': 'Runs the Artwork Downloader script to initialize/download artwork. (Requires the Artwork Downloader addon and XBMC Frodo or later)'
},
{
'name': 'run_artwork_downloader_delay',
'label': 'Artwork Downloader delay',
'default': 5,
'type': 'int',
'advanced': True,
'description': 'Number of seconds to wait to start the Artwork Downloader script after notifying XBMC.',
},
],
}
],