Simplify fanart provider

This commit is contained in:
Ruud
2014-06-02 22:23:26 +02:00
parent c82b1f51e3
commit 681d8b1ddc
9 changed files with 40 additions and 629 deletions

View File

@@ -1,12 +1,9 @@
import os
import traceback
from couchpotato import tryInt
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
import libs.fanarttv.errors as fanarttv_errors
from libs.fanarttv.movie import Movie
log = CPLog(__name__)
@@ -16,16 +13,17 @@ autoload = 'FanartTV'
class FanartTV(MovieProvider):
urls = {
'api': 'http://api.fanart.tv/webservice/movie/%s/%s/JSON/all/1/2'
}
MAX_EXTRAFANART = 20
def __init__(self):
addEvent('movie.extra_art', self.getArt, priority=2)
addEvent('movie.info', self.getArt, priority = 1)
# Configure fanarttv API settings
os.environ.setdefault('FANART_APIKEY', self.conf('api_key'))
def getArt(self, identifier = None, **kwargs):
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 {}
@@ -33,23 +31,24 @@ class FanartTV(MovieProvider):
images = {}
try:
try:
exists = True
movie = Movie.get(id = identifier)
except (fanarttv_errors.FanartError, IOError):
exists = False
url = self.urls['api'] % (self.conf('api_key'), identifier)
fanart_data = self.getJsonData(url)
if exists:
images = self._parseMovie(movie, True)
if fanart_data:
name, resource = fanart_data.items()[0]
log.debug('Found images for %s', name)
images = self._parseMovie(resource)
except:
log.error('Failed getting extra art for %s: %s',
(identifier, traceback.format_exc()))
return {}
return images
return {
'images': images
}
def _parseMovie(self, movie, is_hd):
def _parseMovie(self, movie):
images = {
'landscape': [],
'logo': [],
@@ -59,56 +58,48 @@ class FanartTV(MovieProvider):
'extra_fanart': [],
}
images['landscape'] = self._getMultImages(movie.thumbs, 1)
images['banner'] = self._getMultImages(movie.banners, 1)
images['disc_art'] = self._getMultImages(self._trimDiscs(movie.discs, is_hd), 1)
images['landscape'] = self._getMultImages(movie.get('moviethumb', []), 1)
images['banner'] = self._getMultImages(movie.get('moviebanner', []), 1)
images['disc_art'] = self._getMultImages(self._trimDiscs(movie.get('moviedisc', [])), 1)
images['clear_art'] = self._getMultImages(movie.hdarts, 1)
if len(images['clear_art']) is 0:
images['clear_art'] = self._getMultImages(movie.arts, 1)
images['clear_art'] = self._getMultImages(movie.get('hdmovieart', []), 1)
if len(images['clear_art']) == 0:
images['clear_art'] = self._getMultImages(movie.get('movieart', []), 1)
images['logo'] = self._getMultImages(movie.hdlogos, 1)
if len(images['logo']) is 0:
images['logo'] = self._getMultImages(movie.logos, 1)
images['logo'] = self._getMultImages(movie.get('hdmovielogo', []), 1)
if len(images['logo']) == 0:
images['logo'] = self._getMultImages(movie.get('movielogo', []), 1)
fanarts = self._getMultImages(movie.backgrounds, self.MAX_EXTRAFANART + 1)
fanarts = self._getMultImages(movie.get('moviebackground', []), self.MAX_EXTRAFANART + 1)
if fanarts:
images['backdrop_original'] = fanarts[0]
images['extra_fanart'] = fanarts[1:]
# TODO: Add support for extra backgrounds
#extra_fanart = self._getMultImages(movie.backgrounds, -1)
return images
def _trimDiscs(self, disc_images, is_hd):
def _trimDiscs(self, disc_images):
"""
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.
Return a subset of discImages. Only bluray disc images will be returned.
"""
trimmed = []
for disc in disc_images:
if is_hd and disc.disc_type == u'bluray':
trimmed.append(disc)
elif not is_hd and disc.disc_type == u'dvd':
if disc.get('disc_type') == 'bluray':
trimmed.append(disc)
if len(trimmed) is 0:
if len(trimmed) == 0:
return disc_images
else:
return trimmed
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
if tryInt(image.get('likes')) > highscore:
highscore = tryInt(image.get('likes'))
image_url = image.get('url')
return image_url
@@ -120,7 +111,7 @@ class FanartTV(MovieProvider):
image_urls = []
pool = []
for image in images:
if image.lang == u'en':
if image.get('lang') == 'en':
pool.append(image)
orig_pool_size = len(pool)
@@ -128,10 +119,10 @@ class FanartTV(MovieProvider):
best = None
highscore = -1
for image in pool:
if image.likes > highscore:
highscore = image.likes
if tryInt(image.get('likes')) > highscore:
highscore = tryInt(image.get('likes'))
best = image
image_urls.append(best.url)
image_urls.append(best.get('url'))
pool.remove(best)
return image_urls
@@ -142,12 +133,6 @@ class FanartTV(MovieProvider):
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',

View File

@@ -1,110 +0,0 @@
__author__ = 'Andrea De Marco <24erre@gmail.com>'
__version__ = '1.4.0'
__classifiers__ = [
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Software Development :: Libraries',
]
__copyright__ = "2012, %s " % __author__
__license__ = """
Copyright %s.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied.
See the License for the specific language governing permissions and
limitations under the License.
""" % __copyright__
__docformat__ = 'restructuredtext en'
__doc__ = """
:abstract: Python interface to fanart.tv API
:version: %s
:author: %s
:contact: http://z4r.github.com/
:date: 2012-04-04
:copyright: %s
""" % (__version__, __author__, __license__)
def values(obj):
return [v for k, v in obj.__dict__.iteritems() if not k.startswith('_')]
BASEURL = 'http://api.fanart.tv/webservice'
class FORMAT(object):
JSON = 'JSON'
XML = 'XML'
PHP = 'PHP'
class WS(object):
MUSIC = 'artist'
MOVIE = 'movie'
TV = 'series'
class TYPE(object):
ALL = 'all'
class TV(object):
ART = 'clearart'
LOGO = 'clearlogo'
CHARACTER = 'characterart'
THUMB = 'tvthumb'
SEASONTHUMB = 'seasonthumb'
BACKGROUND = 'showbackground'
HDLOGO = 'hdtvlogo'
HDART = 'hdclearart'
POSTER = 'tvposter'
BANNER = 'tvbanner'
class MUSIC(object):
DISC = 'cdart'
LOGO = 'musiclogo'
BACKGROUND = 'artistbackground'
COVER = 'albumcover'
THUMB = 'artistthumb'
class MOVIE(object):
ART = 'movieart'
LOGO = 'movielogo'
DISC = 'moviedisc'
POSTER = 'movieposter'
BACKGROUND = 'moviebackground'
HDLOGO = 'hdmovielogo'
HDART = 'hdmovieclearart'
BANNER = 'moviebanner'
THUMB = 'moviethumb'
class SORT(object):
POPULAR = 1
NEWEST = 2
OLDEST = 3
class LIMIT(object):
ONE = 1
ALL = 2
FORMAT_LIST = values(FORMAT)
WS_LIST = values(WS)
TYPE_LIST = values(TYPE.MUSIC) + values(TYPE.TV) + values(TYPE.MOVIE) + [TYPE.ALL]
MUSIC_TYPE_LIST = values(TYPE.MUSIC) + [TYPE.ALL]
TV_TYPE_LIST = values(TYPE.TV) + [TYPE.ALL]
MOVIE_TYPE_LIST = values(TYPE.MOVIE) + [TYPE.ALL]
SORT_LIST = values(SORT)
LIMIT_LIST = values(LIMIT)

View File

@@ -1,44 +0,0 @@
import libs.requests as requests
import libs.fanarttv as fanart
from libs.fanarttv.errors import RequestFanartError, ResponseFanartError
class Request(object):
def __init__(self, apikey, id, ws, type=None, sort=None, limit=None):
self._apikey = apikey
self._id = id
self._ws = ws
self._type = type or fanart.TYPE.ALL
self._sort = sort or fanart.SORT.POPULAR
self._limit = limit or fanart.LIMIT.ALL
self.validate()
self._response = None
def validate(self):
for attribute_name in ('ws', 'type', 'sort', 'limit'):
attribute = getattr(self, '_' + attribute_name)
choices = getattr(fanart, attribute_name.upper() + '_LIST')
if attribute not in choices:
raise RequestFanartError('Not allowed {0}: {1} [{2}]'.format(attribute_name, attribute, ', '.join(choices)))
def __str__(self):
return '/'.join(map(str, [
fanart.BASEURL,
self._ws,
self._apikey,
self._id,
fanart.FORMAT.JSON,
self._type,
self._sort,
self._limit,
]))
def response(self):
try:
response = requests.get(str(self))
rjson = response.json()
if not isinstance(rjson, dict):
raise Exception(response.text)
return rjson
except Exception as e:
raise ResponseFanartError(str(e))

View File

@@ -1,15 +0,0 @@
class FanartError(Exception):
def __str__(self):
return ', '.join(map(str, self.args))
def __repr__(self):
name = self.__class__.__name__
return '%s%r' % (name, self.args)
class ResponseFanartError(FanartError):
pass
class RequestFanartError(FanartError):
pass

View File

@@ -1,46 +0,0 @@
class Immutable(object):
_mutable = False
def __setattr__(self, name, value):
if self._mutable or name == '_mutable':
super(Immutable, self).__setattr__(name, value)
else:
raise TypeError("Can't modify immutable instance")
def __delattr__(self, name):
if self._mutable:
super(Immutable, self).__delattr__(name)
else:
raise TypeError("Can't modify immutable instance")
def __eq__(self, other):
return hash(self) == hash(other)
def __hash__(self):
return hash(repr(self))
def __repr__(self):
return '%s(%s)' % (
self.__class__.__name__,
', '.join(['{0}={1}'.format(k, repr(v)) for k, v in self])
)
def __iter__(self):
l = self.__dict__.keys()
l.sort()
for k in l:
if not k.startswith('_'):
yield k, getattr(self, k)
@staticmethod
def mutablemethod(f):
def func(self, *args, **kwargs):
if isinstance(self, Immutable):
old_mutable = self._mutable
self._mutable = True
res = f(self, *args, **kwargs)
self._mutable = old_mutable
else:
res = f(self, *args, **kwargs)
return res
return func

View File

@@ -1,68 +0,0 @@
import json
import os
import libs.requests as requests
from libs.fanarttv.core import Request
from libs.fanarttv.immutable import Immutable
class LeafItem(Immutable):
KEY = NotImplemented
@Immutable.mutablemethod
def __init__(self, id, url, likes):
self.id = int(id)
self.url = url
self.likes = int(likes)
self._content = None
@classmethod
def from_dict(cls, resource):
return cls(**dict([(str(k), v) for k, v in resource.iteritems()]))
@classmethod
def extract(cls, resource):
return [cls.from_dict(i) for i in resource.get(cls.KEY, {})]
@Immutable.mutablemethod
def content(self):
if not self._content:
self._content = requests.get(self.url).content
return self._content
def __str__(self):
return self.url
class ResourceItem(Immutable):
WS = NotImplemented
request_cls = Request
@classmethod
def from_dict(cls, map):
raise NotImplementedError
@classmethod
def get(cls, id):
map = cls.request_cls(
apikey=os.environ.get('FANART_APIKEY'),
id=id,
ws=cls.WS
).response()
return cls.from_dict(map)
def json(self, **kw):
return json.dumps(
self,
default=lambda o: dict([(k, v) for k, v in o.__dict__.items() if not k.startswith('_')]),
**kw
)
class CollectableItem(Immutable):
@classmethod
def from_dict(cls, key, map):
raise NotImplementedError
@classmethod
def collection_from_dict(cls, map):
return [cls.from_dict(k, v) for k, v in map.iteritems()]

View File

@@ -1,103 +0,0 @@
import libs.fanarttv as fanart
from libs.fanarttv.items import LeafItem, Immutable, ResourceItem
__all__ = (
'ArtItem',
'DiscItem',
'LogoItem',
'PosterItem',
'BackgroundItem',
'HdLogoItem',
'HdArtItem',
'BannerItem',
'ThumbItem',
'Movie',
)
class MovieItem(LeafItem):
@Immutable.mutablemethod
def __init__(self, id, url, likes, lang):
super(MovieItem, self).__init__(id, url, likes)
self.lang = lang
class DiscItem(MovieItem):
KEY = fanart.TYPE.MOVIE.DISC
@Immutable.mutablemethod
def __init__(self, id, url, likes, lang, disc, disc_type):
super(DiscItem, self).__init__(id, url, likes, lang)
self.disc = int(disc)
self.disc_type = disc_type
class ArtItem(MovieItem):
KEY = fanart.TYPE.MOVIE.ART
class LogoItem(MovieItem):
KEY = fanart.TYPE.MOVIE.LOGO
class PosterItem(MovieItem):
KEY = fanart.TYPE.MOVIE.POSTER
class BackgroundItem(MovieItem):
KEY = fanart.TYPE.MOVIE.BACKGROUND
class HdLogoItem(MovieItem):
KEY = fanart.TYPE.MOVIE.HDLOGO
class HdArtItem(MovieItem):
KEY = fanart.TYPE.MOVIE.HDART
class BannerItem(MovieItem):
KEY = fanart.TYPE.MOVIE.BANNER
class ThumbItem(MovieItem):
KEY = fanart.TYPE.MOVIE.THUMB
class Movie(ResourceItem):
WS = fanart.WS.MOVIE
@Immutable.mutablemethod
def __init__(self, name, imdbid, tmdbid, arts, logos, discs, posters, backgrounds, hdlogos, hdarts,
banners, thumbs):
self.name = name
self.imdbid = imdbid
self.tmdbid = tmdbid
self.arts = arts
self.posters = posters
self.logos = logos
self.discs = discs
self.backgrounds = backgrounds
self.hdlogos = hdlogos
self.hdarts = hdarts
self.banners = banners
self.thumbs = thumbs
@classmethod
def from_dict(cls, resource):
assert len(resource) == 1, 'Bad Format Map'
name, resource = resource.items()[0]
return cls(
name=name,
imdbid=resource['imdb_id'],
tmdbid=resource['tmdb_id'],
arts=ArtItem.extract(resource),
logos=LogoItem.extract(resource),
discs=DiscItem.extract(resource),
posters=PosterItem.extract(resource),
backgrounds=BackgroundItem.extract(resource),
hdlogos=HdLogoItem.extract(resource),
hdarts=HdArtItem.extract(resource),
banners=BannerItem.extract(resource),
thumbs=ThumbItem.extract(resource),
)

View File

@@ -1,80 +0,0 @@
from libs.fanarttv.items import Immutable, LeafItem, ResourceItem, CollectableItem
import libs.fanarttv as fanart
__all__ = (
'BackgroundItem',
'CoverItem',
'LogoItem',
'ThumbItem',
'DiscItem',
'Artist',
'Album',
)
class BackgroundItem(LeafItem):
KEY = fanart.TYPE.MUSIC.BACKGROUND
class CoverItem(LeafItem):
KEY = fanart.TYPE.MUSIC.COVER
class LogoItem(LeafItem):
KEY = fanart.TYPE.MUSIC.LOGO
class ThumbItem(LeafItem):
KEY = fanart.TYPE.MUSIC.THUMB
class DiscItem(LeafItem):
KEY = fanart.TYPE.MUSIC.DISC
@Immutable.mutablemethod
def __init__(self, id, url, likes, disc, size):
super(DiscItem, self).__init__(id, url, likes)
self.disc = int(disc)
self.size = int(size)
class Artist(ResourceItem):
WS = fanart.WS.MUSIC
@Immutable.mutablemethod
def __init__(self, name, mbid, albums, backgrounds, logos, thumbs):
self.name = name
self.mbid = mbid
self.albums = albums
self.backgrounds = backgrounds
self.logos = logos
self.thumbs = thumbs
@classmethod
def from_dict(cls, resource):
assert len(resource) == 1, 'Bad Format Map'
name, resource = resource.items()[0]
return cls(
name=name,
mbid=resource['mbid_id'],
albums=Album.collection_from_dict(resource.get('albums', {})),
backgrounds=BackgroundItem.extract(resource),
thumbs=ThumbItem.extract(resource),
logos=LogoItem.extract(resource),
)
class Album(CollectableItem):
@Immutable.mutablemethod
def __init__(self, mbid, covers, arts):
self.mbid = mbid
self.covers = covers
self.arts = arts
@classmethod
def from_dict(cls, key, resource):
return cls(
mbid=key,
covers=CoverItem.extract(resource),
arts=DiscItem.extract(resource),
)

View File

@@ -1,108 +0,0 @@
import libs.fanarttv as fanart
from libs.fanarttv.items import LeafItem, Immutable, ResourceItem
__all__ = (
'CharacterItem',
'ArtItem',
'LogoItem',
'BackgroundItem',
'SeasonItem',
'ThumbItem',
'HdLogoItem',
'HdArtItem',
'PosterItem',
'BannerItem',
'TvShow',
)
class TvItem(LeafItem):
@Immutable.mutablemethod
def __init__(self, id, url, likes, lang):
super(TvItem, self).__init__(id, url, likes)
self.lang = lang
class SeasonedTvItem(TvItem):
@Immutable.mutablemethod
def __init__(self, id, url, likes, lang, season):
super(SeasonedTvItem, self).__init__(id, url, likes, lang)
self.season = 0 if season == 'all' else int(season or 0)
class CharacterItem(TvItem):
KEY = fanart.TYPE.TV.CHARACTER
class ArtItem(TvItem):
KEY = fanart.TYPE.TV.ART
class LogoItem(TvItem):
KEY = fanart.TYPE.TV.LOGO
class BackgroundItem(SeasonedTvItem):
KEY = fanart.TYPE.TV.BACKGROUND
class SeasonItem(SeasonedTvItem):
KEY = fanart.TYPE.TV.SEASONTHUMB
class ThumbItem(TvItem):
KEY = fanart.TYPE.TV.THUMB
class HdLogoItem(TvItem):
KEY = fanart.TYPE.TV.HDLOGO
class HdArtItem(TvItem):
KEY = fanart.TYPE.TV.HDART
class PosterItem(TvItem):
KEY = fanart.TYPE.TV.POSTER
class BannerItem(TvItem):
KEY = fanart.TYPE.TV.BANNER
class TvShow(ResourceItem):
WS = fanart.WS.TV
@Immutable.mutablemethod
def __init__(self, name, tvdbid, backgrounds, characters, arts, logos, seasons, thumbs, hdlogos, hdarts, posters,
banners):
self.name = name
self.tvdbid = tvdbid
self.backgrounds = backgrounds
self.characters = characters
self.arts = arts
self.logos = logos
self.seasons = seasons
self.thumbs = thumbs
self.hdlogos = hdlogos
self.hdarts = hdarts
self.posters = posters
self.banners = banners
@classmethod
def from_dict(cls, resource):
assert len(resource) == 1, 'Bad Format Map'
name, resource = resource.items()[0]
return cls(
name=name,
tvdbid=resource['thetvdb_id'],
backgrounds=BackgroundItem.extract(resource),
characters=CharacterItem.extract(resource),
arts=ArtItem.extract(resource),
logos=LogoItem.extract(resource),
seasons=SeasonItem.extract(resource),
thumbs=ThumbItem.extract(resource),
hdlogos=HdLogoItem.extract(resource),
hdarts=HdArtItem.extract(resource),
posters=PosterItem.extract(resource),
banners=BannerItem.extract(resource),
)