Merge branch 'release/4.0.8'
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 129 KiB |
Before Width: | Height: | Size: 506 B After Width: | Height: | Size: 241 B |
BIN
gui/slick/images/flags/ay.png
Normal file
After Width: | Height: | Size: 395 B |
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 444 B |
Before Width: | Height: | Size: 526 B After Width: | Height: | Size: 485 B |
Before Width: | Height: | Size: 628 B After Width: | Height: | Size: 203 B |
Before Width: | Height: | Size: 439 B After Width: | Height: | Size: 460 B |
Before Width: | Height: | Size: 495 B After Width: | Height: | Size: 441 B |
Before Width: | Height: | Size: 545 B After Width: | Height: | Size: 419 B |
Before Width: | Height: | Size: 487 B After Width: | Height: | Size: 483 B |
Before Width: | Height: | Size: 609 B After Width: | Height: | Size: 542 B |
BIN
gui/slick/images/flags/eo.png
Normal file
After Width: | Height: | Size: 170 B |
Before Width: | Height: | Size: 469 B After Width: | Height: | Size: 486 B |
Before Width: | Height: | Size: 592 B After Width: | Height: | Size: 390 B |
BIN
gui/slick/images/flags/fa.png
Normal file
After Width: | Height: | Size: 473 B |
Before Width: | Height: | Size: 489 B After Width: | Height: | Size: 451 B |
Before Width: | Height: | Size: 545 B After Width: | Height: | Size: 426 B |
Before Width: | Height: | Size: 470 B After Width: | Height: | Size: 319 B |
Before Width: | Height: | Size: 326 B After Width: | Height: | Size: 433 B |
BIN
gui/slick/images/flags/hi.png
Normal file
After Width: | Height: | Size: 380 B |
Before Width: | Height: | Size: 524 B After Width: | Height: | Size: 515 B |
Before Width: | Height: | Size: 432 B After Width: | Height: | Size: 433 B |
BIN
gui/slick/images/flags/hy.png
Normal file
After Width: | Height: | Size: 431 B |
Before Width: | Height: | Size: 430 B After Width: | Height: | Size: 393 B |
Before Width: | Height: | Size: 532 B After Width: | Height: | Size: 504 B |
Before Width: | Height: | Size: 420 B After Width: | Height: | Size: 420 B |
Before Width: | Height: | Size: 455 B After Width: | Height: | Size: 404 B |
BIN
gui/slick/images/flags/ka.png
Normal file
After Width: | Height: | Size: 295 B |
BIN
gui/slick/images/flags/kk.png
Normal file
After Width: | Height: | Size: 403 B |
Before Width: | Height: | Size: 327 B After Width: | Height: | Size: 457 B |
Before Width: | Height: | Size: 517 B After Width: | Height: | Size: 436 B |
Before Width: | Height: | Size: 508 B After Width: | Height: | Size: 174 B |
Before Width: | Height: | Size: 465 B After Width: | Height: | Size: 384 B |
Before Width: | Height: | Size: 664 B After Width: | Height: | Size: 304 B |
Before Width: | Height: | Size: 614 B After Width: | Height: | Size: 492 B |
Before Width: | Height: | Size: 453 B After Width: | Height: | Size: 454 B |
Before Width: | Height: | Size: 512 B After Width: | Height: | Size: 520 B |
BIN
gui/slick/images/flags/oc.png
Normal file
After Width: | Height: | Size: 271 B |
Before Width: | Height: | Size: 593 B After Width: | Height: | Size: 500 B |
Before Width: | Height: | Size: 374 B After Width: | Height: | Size: 393 B |
Before Width: | Height: | Size: 554 B After Width: | Height: | Size: 484 B |
Before Width: | Height: | Size: 495 B After Width: | Height: | Size: 440 B |
Before Width: | Height: | Size: 420 B After Width: | Height: | Size: 455 B |
Before Width: | Height: | Size: 510 B After Width: | Height: | Size: 409 B |
Before Width: | Height: | Size: 562 B After Width: | Height: | Size: 503 B |
Before Width: | Height: | Size: 436 B After Width: | Height: | Size: 462 B |
BIN
gui/slick/images/flags/sq.png
Normal file
After Width: | Height: | Size: 409 B |
Before Width: | Height: | Size: 513 B After Width: | Height: | Size: 218 B |
Before Width: | Height: | Size: 542 B After Width: | Height: | Size: 497 B |
Before Width: | Height: | Size: 452 B After Width: | Height: | Size: 484 B |
Before Width: | Height: | Size: 514 B After Width: | Height: | Size: 475 B |
Before Width: | Height: | Size: 492 B After Width: | Height: | Size: 421 B |
BIN
gui/slick/images/flags/uk.png
Normal file
After Width: | Height: | Size: 177 B |
BIN
gui/slick/images/flags/un.png
Normal file
After Width: | Height: | Size: 149 B |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 616 B After Width: | Height: | Size: 459 B |
Before Width: | Height: | Size: 472 B After Width: | Height: | Size: 379 B |
Before Width: | Height: | Size: 668 KiB After Width: | Height: | Size: 504 KiB |
@ -317,16 +317,6 @@
|
||||
|
||||
<fieldset class="component-group-list">
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="use_api">
|
||||
<span class="component-title">Enable API</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="use_api" class="enabler" id="use_api" #if $sickbeard.USE_API then 'checked="checked"' else ''#/>
|
||||
<p>allow the use of the SickRage API</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div id="content_use_api">
|
||||
<div class="field-pair">
|
||||
<label for="api_key">
|
||||
<span class="component-title">API key</span>
|
||||
@ -337,7 +327,6 @@
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="web_log">
|
||||
@ -581,10 +570,10 @@
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="git_reset">
|
||||
<span class="component-title">Git branch reset</span>
|
||||
<span class="component-title">Git reset</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="git_reset" id="git_reset" #if True == $sickbeard.GIT_RESET then 'checked="checked"' else ''#/>
|
||||
<p>reset git branch automatically to help resolve update issues</p>
|
||||
<p>removes untracked files and performs a hard reset on git branch automatically to help resolve update issues</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -474,7 +474,7 @@
|
||||
#if $sickbeard.USE_SUBTITLES and $show.subtitles:
|
||||
<td class="col-subtitles" align="center">
|
||||
#if $epResult["subtitles"]:
|
||||
#for $sub_lang in subliminal.language.language_list($epResult["subtitles"].split(',')):
|
||||
#for $sub_lang in subliminal.language.language_list([x.strip() for x in $epResult["subtitles"].split(',')]):
|
||||
#if sub_lang.alpha2 != ""
|
||||
<img src="$sbRoot/images/flags/${sub_lang.alpha2}.png" width="16" height="11" alt="${sub_lang}" />
|
||||
#end if
|
||||
|
@ -133,7 +133,7 @@
|
||||
|
||||
<b>Sports: </b>
|
||||
<input type="checkbox" name="sports" #if $show.sports == 1 then "checked=\"checked\"" else ""# /><br />
|
||||
(check this if the show is a sporting or MMA event)<br />
|
||||
(check this if the show is a sporting or MMA event and released as Show.03.02.2010 rather than Show.S02E03)<br />
|
||||
<br />
|
||||
|
||||
<b>Anime: </b>
|
||||
|
@ -1,4 +1,5 @@
|
||||
#import sickbeard
|
||||
#import calendar
|
||||
#import datetime
|
||||
#from sickbeard.common import *
|
||||
#from sickbeard import db, sbdatetime, network_timezones
|
||||
@ -330,7 +331,7 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name))
|
||||
|
||||
#set $data_date = '6000000000.0'
|
||||
#if $cur_airs_next:
|
||||
#set $data_date = $time.mktime($sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($cur_airs_next,$curShow.airs,$curShow.network)).timetuple())
|
||||
#set $data_date = $calendar.timegm($sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($cur_airs_next,$curShow.airs,$curShow.network)).timetuple())
|
||||
#else if None is not $display_status
|
||||
#if 'nded' not in $display_status and 1 == int($curShow.paused)
|
||||
#set $data_date = '5000000500.0'
|
||||
@ -528,7 +529,7 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name))
|
||||
|
||||
#if $cur_airs_next
|
||||
#set $ldatetime = $sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($cur_airs_next,$curShow.airs,$curShow.network))
|
||||
<td align="center" class="nowrap"><div class="${fuzzydate}">$sbdatetime.sbdatetime.sbfdate($ldatetime)</div><span class="sort_data">$time.mktime($ldatetime.timetuple())</span></td>
|
||||
<td align="center" class="nowrap"><div class="${fuzzydate}">$sbdatetime.sbdatetime.sbfdate($ldatetime)</div><span class="sort_data">$calendar.timegm($ldatetime.timetuple())</span></td>
|
||||
#else:
|
||||
<td align="center" class="nowrap"></td>
|
||||
#end if
|
||||
|
111
lib/fanart/__init__.py
Normal file
@ -0,0 +1,111 @@
|
||||
__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://webservice.fanart.tv/v3/%s/%s?api_key=%s'
|
||||
|
||||
class FORMAT(object):
|
||||
JSON = 'JSON'
|
||||
XML = 'XML'
|
||||
PHP = 'PHP'
|
||||
|
||||
|
||||
class WS(object):
|
||||
MUSIC = 'music'
|
||||
MOVIE = 'movies'
|
||||
TV = 'tv'
|
||||
|
||||
|
||||
class TYPE(object):
|
||||
ALL = 'all'
|
||||
|
||||
class TV(object):
|
||||
LOGO = 'clearlogo'
|
||||
CHARACTER = 'characterart'
|
||||
BACKGROUND = 'showbackground'
|
||||
HDLOGO = 'hdtvlogo'
|
||||
HDART = 'hdclearart'
|
||||
ART = 'clearart'
|
||||
THUMB = 'tvthumb'
|
||||
POSTER = 'tvposter'
|
||||
BANNER = 'tvbanner'
|
||||
SEASONTHUMB = 'seasonthumb'
|
||||
SEASONPOSTER = 'seasonposter'
|
||||
SEASONBANNER = 'seasonbanner'
|
||||
|
||||
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)
|
35
lib/fanart/core.py
Normal file
@ -0,0 +1,35 @@
|
||||
import requests
|
||||
import fanart
|
||||
from fanart.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 fanart.BASEURL % (self._ws, self._id, self._apikey)
|
||||
|
||||
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))
|
15
lib/fanart/errors.py
Normal file
@ -0,0 +1,15 @@
|
||||
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
|
46
lib/fanart/immutable.py
Normal file
@ -0,0 +1,46 @@
|
||||
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
|
68
lib/fanart/items.py
Normal file
@ -0,0 +1,68 @@
|
||||
import json
|
||||
import os
|
||||
import requests
|
||||
from fanart.core import Request
|
||||
from fanart.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()]
|
103
lib/fanart/movie.py
Normal file
@ -0,0 +1,103 @@
|
||||
import fanart
|
||||
from fanart.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),
|
||||
)
|
80
lib/fanart/music.py
Normal file
@ -0,0 +1,80 @@
|
||||
from fanart.items import Immutable, LeafItem, ResourceItem, CollectableItem
|
||||
import 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),
|
||||
)
|
3
lib/fanart/tests/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
import os
|
||||
|
||||
LOCALDIR = os.path.dirname(__file__)
|
196
lib/fanart/tests/json/wilfred.json
Normal file
@ -0,0 +1,196 @@
|
||||
{
|
||||
"logos": [
|
||||
{
|
||||
"lang": "en",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/clearlogo/wilfred-us-4e04b6495dfd3.png",
|
||||
"likes": 2,
|
||||
"id": 11977
|
||||
},
|
||||
{
|
||||
"lang": "en",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/clearlogo/wilfred-us-517ac36e39f67.png",
|
||||
"likes": 1,
|
||||
"id": 28249
|
||||
},
|
||||
{
|
||||
"lang": "en",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/clearlogo/wilfred-us-51f557082cfde.png",
|
||||
"likes": 0,
|
||||
"id": 31817
|
||||
}
|
||||
],
|
||||
"arts": [
|
||||
{
|
||||
"lang": "en",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/clearart/wilfred-us-4e05f10e87711.png",
|
||||
"likes": 2,
|
||||
"id": 11987
|
||||
},
|
||||
{
|
||||
"lang": "en",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/clearart/wilfred-us-4e2f151d5ed62.png",
|
||||
"likes": 1,
|
||||
"id": 12470
|
||||
}
|
||||
],
|
||||
"name": "Wilfred (US)",
|
||||
"hdarts": [
|
||||
{
|
||||
"lang": "en",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/hdclearart/wilfred-us-505f94ed0ba13.png",
|
||||
"likes": 1,
|
||||
"id": 21112
|
||||
},
|
||||
{
|
||||
"lang": "he",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/hdclearart/wilfred-us-52403264aa3ec.png",
|
||||
"likes": 1,
|
||||
"id": 33751
|
||||
}
|
||||
],
|
||||
"backgrounds": [
|
||||
{
|
||||
"lang": "en",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/showbackground/wilfred-us-5034dbd49115e.jpg",
|
||||
"id": 19965,
|
||||
"season": 0,
|
||||
"likes": 0
|
||||
},
|
||||
{
|
||||
"lang": "en",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/showbackground/wilfred-us-50b0c92db6973.jpg",
|
||||
"id": 23166,
|
||||
"season": 0,
|
||||
"likes": 0
|
||||
},
|
||||
{
|
||||
"lang": "en",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/showbackground/wilfred-us-50b0c92dbb46b.jpg",
|
||||
"id": 23167,
|
||||
"season": 0,
|
||||
"likes": 0
|
||||
},
|
||||
{
|
||||
"lang": "en",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/showbackground/wilfred-us-50b0c92dbb9d1.jpg",
|
||||
"id": 23168,
|
||||
"season": 0,
|
||||
"likes": 0
|
||||
}
|
||||
],
|
||||
"thumbs": [
|
||||
{
|
||||
"lang": "en",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/tvthumb/wilfred-us-501cf526174fe.jpg",
|
||||
"likes": 1,
|
||||
"id": 19596
|
||||
},
|
||||
{
|
||||
"lang": "en",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/tvthumb/wilfred-us-51bfb4a105904.jpg",
|
||||
"likes": 0,
|
||||
"id": 30060
|
||||
}
|
||||
],
|
||||
"characters": [],
|
||||
"posters": [
|
||||
{
|
||||
"lang": "he",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/tvposter/wilfred-us-525d893230d7c.jpg",
|
||||
"likes": 1,
|
||||
"id": 34584
|
||||
}
|
||||
],
|
||||
"seasons": [
|
||||
{
|
||||
"lang": "he",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-52403782bab55.jpg",
|
||||
"id": 33752,
|
||||
"season": 1,
|
||||
"likes": 1
|
||||
},
|
||||
{
|
||||
"lang": "he",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-5240379335232.jpg",
|
||||
"id": 33753,
|
||||
"season": 2,
|
||||
"likes": 1
|
||||
},
|
||||
{
|
||||
"lang": "he",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-524037bc83c7d.jpg",
|
||||
"id": 33754,
|
||||
"season": 3,
|
||||
"likes": 1
|
||||
},
|
||||
{
|
||||
"lang": "en",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-501bb0a8e60f9.jpg",
|
||||
"id": 19586,
|
||||
"season": 1,
|
||||
"likes": 0
|
||||
},
|
||||
{
|
||||
"lang": "en",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-501bb0b4bf229.jpg",
|
||||
"id": 19587,
|
||||
"season": 2,
|
||||
"likes": 0
|
||||
},
|
||||
{
|
||||
"lang": "en",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-501bb144e6a46.jpg",
|
||||
"id": 19588,
|
||||
"season": 0,
|
||||
"likes": 0
|
||||
},
|
||||
{
|
||||
"lang": "en",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-51c953105ef77.jpg",
|
||||
"id": 30309,
|
||||
"season": 3,
|
||||
"likes": 0
|
||||
}
|
||||
],
|
||||
"banners": [
|
||||
{
|
||||
"lang": "he",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/tvbanner/wilfred-us-52403a7185070.jpg",
|
||||
"likes": 1,
|
||||
"id": 33755
|
||||
},
|
||||
{
|
||||
"lang": "en",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/tvbanner/wilfred-us-5265193db51f7.jpg",
|
||||
"likes": 0,
|
||||
"id": 34716
|
||||
}
|
||||
],
|
||||
"hdlogos": [
|
||||
{
|
||||
"lang": "en",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/hdtvlogo/wilfred-us-505f373be58e6.png",
|
||||
"likes": 1,
|
||||
"id": 21101
|
||||
},
|
||||
{
|
||||
"lang": "en",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/hdtvlogo/wilfred-us-517ac360def17.png",
|
||||
"likes": 1,
|
||||
"id": 28248
|
||||
},
|
||||
{
|
||||
"lang": "he",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/hdtvlogo/wilfred-us-52402df7ed945.png",
|
||||
"likes": 1,
|
||||
"id": 33750
|
||||
},
|
||||
{
|
||||
"lang": "en",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/hdtvlogo/wilfred-us-51f556fb4abd3.png",
|
||||
"likes": 0,
|
||||
"id": 31816
|
||||
}
|
||||
],
|
||||
"tvdbid": "239761"
|
||||
}
|
BIN
lib/fanart/tests/response/50x50.png
Normal file
After Width: | Height: | Size: 180 B |
174
lib/fanart/tests/response/movie_thg.json
Normal file
@ -0,0 +1,174 @@
|
||||
{
|
||||
"The Hunger Games": {
|
||||
"tmdb_id": "70160",
|
||||
"imdb_id": "tt1392170",
|
||||
"movieart": [
|
||||
{
|
||||
"id": "1226",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/movieart/the-hunger-games-4f6dc995edb8f.png",
|
||||
"lang": "en",
|
||||
"likes": "3"
|
||||
},
|
||||
{
|
||||
"id": "1225",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/movieart/the-hunger-games-4f6dc980b4514.png",
|
||||
"lang": "en",
|
||||
"likes": "1"
|
||||
}
|
||||
],
|
||||
"movielogo": [
|
||||
{
|
||||
"id": "1230",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/movielogo/the-hunger-games-4f6e0e63a9d29.png",
|
||||
"lang": "en",
|
||||
"likes": "2"
|
||||
},
|
||||
{
|
||||
"id": "8020",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/movielogo/the-hunger-games-5018f873b5188.png",
|
||||
"lang": "en",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"id": "1224",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/movielogo/the-hunger-games-4f6dc95a08de1.png",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
}
|
||||
],
|
||||
"moviedisc": [
|
||||
{
|
||||
"id": "8431",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/moviedisc/the-hunger-games-501db4437623f.png",
|
||||
"lang": "en",
|
||||
"likes": "1",
|
||||
"disc": "1",
|
||||
"disc_type": "dvd"
|
||||
},
|
||||
{
|
||||
"id": "9787",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/moviedisc/the-hunger-games-502fd6d695a60.png",
|
||||
"lang": "en",
|
||||
"likes": "1",
|
||||
"disc": "1",
|
||||
"disc_type": "bluray"
|
||||
}
|
||||
],
|
||||
"moviethumb": [
|
||||
{
|
||||
"id": "10687",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/moviethumb/the-hunger-games-503c88b32cf66.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
}
|
||||
],
|
||||
"hdmovielogo": [
|
||||
{
|
||||
"id": "13004",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/hdmovielogo/the-hunger-games-50500118613e3.png",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
}
|
||||
],
|
||||
"moviebackground": [
|
||||
{
|
||||
"id": "14043",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5057c79ad3c56.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "14044",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5057c79ad5526.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "15911",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071de49311d1.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "15914",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071df619b835.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "15917",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071e01fee856.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "15918",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071e0adcc57a.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "15919",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071e12006159.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "15921",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071e206aa2ac.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "15922",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071e2869d774.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "15925",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071e30069b72.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "15927",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071e3c4979b7.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "15930",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071e5b3f039b.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "15931",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071e6369e812.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "15936",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071e8749e73a.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "15937",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/moviebackground/the-hunger-games-5071e9913bfeb.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
}
|
||||
],
|
||||
"hdmovieclearart": [
|
||||
{
|
||||
"id": "14104",
|
||||
"url": "http://assets.fanart.tv/fanart/movies/70160/hdmovieclearart/the-hunger-games-50582453b1375.png",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
171
lib/fanart/tests/response/music_a7f.json
Normal file
@ -0,0 +1,171 @@
|
||||
{
|
||||
"Avenged Sevenfold": {
|
||||
"mbid_id": "24e1b53c-3085-4581-8472-0b0088d2508c",
|
||||
"artistbackground": [
|
||||
{
|
||||
"id": "3027",
|
||||
"url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/artistbackground/avenged-sevenfold-4ddd7889a0fcf.jpg",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "64046",
|
||||
"url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/artistbackground/avenged-sevenfold-50c4db9a2c6e2.jpg",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "64048",
|
||||
"url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/artistbackground/avenged-sevenfold-50c4dc653f004.jpg",
|
||||
"likes": "0"
|
||||
}
|
||||
],
|
||||
"albums": {
|
||||
"180560ee-2d9d-33cf-8de7-cdaaba610739": {
|
||||
"albumcover": [
|
||||
{
|
||||
"id": "3028",
|
||||
"url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/albumcover/city-of-evil-4ddd79ca0beea.jpg",
|
||||
"likes": "0"
|
||||
}
|
||||
],
|
||||
"cdart": [
|
||||
{
|
||||
"id": "9921",
|
||||
"url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/cdart/city-of-evil-4e5f7b9f50d37.png",
|
||||
"likes": "0",
|
||||
"disc": "1",
|
||||
"size": "1000"
|
||||
}
|
||||
]
|
||||
},
|
||||
"1c7120ae-32b6-3693-8974-599977b01601": {
|
||||
"albumcover": [
|
||||
{
|
||||
"id": "3029",
|
||||
"url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/albumcover/waking-the-fallen-4ddd79ca1b11e.jpg",
|
||||
"likes": "0"
|
||||
}
|
||||
],
|
||||
"cdart": [
|
||||
{
|
||||
"id": "9922",
|
||||
"url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/cdart/waking-the-fallen-4e5f7b9f5ebdf.png",
|
||||
"likes": "0",
|
||||
"disc": "1",
|
||||
"size": "1000"
|
||||
}
|
||||
]
|
||||
},
|
||||
"94672194-7f42-3965-a489-f2f3cdc1c79e": {
|
||||
"albumcover": [
|
||||
{
|
||||
"id": "3030",
|
||||
"url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/albumcover/avenged-sevenfold-4ddd79ca1bcd6.jpg",
|
||||
"likes": "0"
|
||||
}
|
||||
],
|
||||
"cdart": [
|
||||
{
|
||||
"id": "9923",
|
||||
"url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/cdart/avenged-sevenfold-4e5f7b9f5fb7f.png",
|
||||
"likes": "0",
|
||||
"disc": "1",
|
||||
"size": "1000"
|
||||
}
|
||||
]
|
||||
},
|
||||
"9d642393-0005-3e89-b3d4-35d89c2f6ad6": {
|
||||
"albumcover": [
|
||||
{
|
||||
"id": "3031",
|
||||
"url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/albumcover/sounding-the-seventh-trumpet-4ddd79ca1d05e.jpg",
|
||||
"likes": "0"
|
||||
}
|
||||
],
|
||||
"cdart": [
|
||||
{
|
||||
"id": "9924",
|
||||
"url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/cdart/sounding-the-seventh-trumpet-4e5f7b9f62e47.png",
|
||||
"likes": "0",
|
||||
"disc": "1",
|
||||
"size": "1000"
|
||||
}
|
||||
]
|
||||
},
|
||||
"fe4373ed-5e89-46b3-b4c0-31433ce217df": {
|
||||
"albumcover": [
|
||||
{
|
||||
"id": "3032",
|
||||
"url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/albumcover/nightmare-4ddd79ca1dffe.jpg",
|
||||
"likes": "0"
|
||||
}
|
||||
],
|
||||
"cdart": [
|
||||
{
|
||||
"id": "11630",
|
||||
"url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/cdart/nightmare-4e8059a3c581c.png",
|
||||
"likes": "0",
|
||||
"disc": "1",
|
||||
"size": "1000"
|
||||
}
|
||||
]
|
||||
},
|
||||
"41d1b72b-1eee-3319-937f-c85d6d2fcfbb": {
|
||||
"albumcover": [
|
||||
{
|
||||
"id": "61014",
|
||||
"url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/albumcover/warmness-on-the-soul-509d2e9150bf4.jpg",
|
||||
"likes": "0"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"musiclogo": [
|
||||
{
|
||||
"id": "5712",
|
||||
"url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/musiclogo/avenged-sevenfold-4dfc8aee78b49.png",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "41835",
|
||||
"url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/musiclogo/avenged-sevenfold-4ffc75f3a7e54.png",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "41836",
|
||||
"url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/musiclogo/avenged-sevenfold-4ffc75f3a8473.png",
|
||||
"likes": "0"
|
||||
}
|
||||
],
|
||||
"artistthumb": [
|
||||
{
|
||||
"id": "31109",
|
||||
"url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/artistthumb/avenged-sevenfold-4fb2b533bc73a.jpg",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "64042",
|
||||
"url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/artistthumb/avenged-sevenfold-50c4d9279d6e9.jpg",
|
||||
"likes": "0"
|
||||
}
|
||||
],
|
||||
"hdmusiclogo": [
|
||||
{
|
||||
"id": "49644",
|
||||
"url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/hdmusiclogo/avenged-sevenfold-503fcebece042.png",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "49645",
|
||||
"url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/hdmusiclogo/avenged-sevenfold-503fcebecf17e.png",
|
||||
"likes": "0"
|
||||
}
|
||||
],
|
||||
"musicbanner": [
|
||||
{
|
||||
"id": "52630",
|
||||
"url": "http://assets.fanart.tv/fanart/music/24e1b53c-3085-4581-8472-0b0088d2508c/musicbanner/avenged-sevenfold-505b2346a559d.jpg",
|
||||
"likes": "0"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
196
lib/fanart/tests/response/tv_239761.json
Normal file
@ -0,0 +1,196 @@
|
||||
{
|
||||
"Wilfred (US)": {
|
||||
"hdclearart": [
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/hdclearart/wilfred-us-505f94ed0ba13.png",
|
||||
"lang": "en",
|
||||
"id": "21112",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/hdclearart/wilfred-us-52403264aa3ec.png",
|
||||
"lang": "he",
|
||||
"id": "33751",
|
||||
"likes": "1"
|
||||
}
|
||||
],
|
||||
"seasonthumb": [
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-52403782bab55.jpg",
|
||||
"lang": "he",
|
||||
"id": "33752",
|
||||
"season": "1",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-5240379335232.jpg",
|
||||
"lang": "he",
|
||||
"id": "33753",
|
||||
"season": "2",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-524037bc83c7d.jpg",
|
||||
"lang": "he",
|
||||
"id": "33754",
|
||||
"season": "3",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-501bb0a8e60f9.jpg",
|
||||
"lang": "en",
|
||||
"id": "19586",
|
||||
"season": "1",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-501bb0b4bf229.jpg",
|
||||
"lang": "en",
|
||||
"id": "19587",
|
||||
"season": "2",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-501bb144e6a46.jpg",
|
||||
"lang": "en",
|
||||
"id": "19588",
|
||||
"season": "0",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/seasonthumb/wilfred-us-51c953105ef77.jpg",
|
||||
"lang": "en",
|
||||
"id": "30309",
|
||||
"season": "3",
|
||||
"likes": "0"
|
||||
}
|
||||
],
|
||||
"tvbanner": [
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/tvbanner/wilfred-us-52403a7185070.jpg",
|
||||
"lang": "he",
|
||||
"id": "33755",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/tvbanner/wilfred-us-5265193db51f7.jpg",
|
||||
"lang": "en",
|
||||
"id": "34716",
|
||||
"likes": "0"
|
||||
}
|
||||
],
|
||||
"thetvdb_id": "239761",
|
||||
"clearlogo": [
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/clearlogo/wilfred-us-4e04b6495dfd3.png",
|
||||
"lang": "en",
|
||||
"id": "11977",
|
||||
"likes": "2"
|
||||
},
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/clearlogo/wilfred-us-517ac36e39f67.png",
|
||||
"lang": "en",
|
||||
"id": "28249",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/clearlogo/wilfred-us-51f557082cfde.png",
|
||||
"lang": "en",
|
||||
"id": "31817",
|
||||
"likes": "0"
|
||||
}
|
||||
],
|
||||
"tvposter": [
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/tvposter/wilfred-us-525d893230d7c.jpg",
|
||||
"lang": "he",
|
||||
"id": "34584",
|
||||
"likes": "1"
|
||||
}
|
||||
],
|
||||
"showbackground": [
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/showbackground/wilfred-us-5034dbd49115e.jpg",
|
||||
"lang": "en",
|
||||
"id": "19965",
|
||||
"season": "all",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/showbackground/wilfred-us-50b0c92db6973.jpg",
|
||||
"lang": "en",
|
||||
"id": "23166",
|
||||
"season": "all",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/showbackground/wilfred-us-50b0c92dbb46b.jpg",
|
||||
"lang": "en",
|
||||
"id": "23167",
|
||||
"season": "all",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/showbackground/wilfred-us-50b0c92dbb9d1.jpg",
|
||||
"lang": "en",
|
||||
"id": "23168",
|
||||
"season": "all",
|
||||
"likes": "0"
|
||||
}
|
||||
],
|
||||
"tvthumb": [
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/tvthumb/wilfred-us-501cf526174fe.jpg",
|
||||
"lang": "en",
|
||||
"id": "19596",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/tvthumb/wilfred-us-51bfb4a105904.jpg",
|
||||
"lang": "en",
|
||||
"id": "30060",
|
||||
"likes": "0"
|
||||
}
|
||||
],
|
||||
"clearart": [
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/clearart/wilfred-us-4e05f10e87711.png",
|
||||
"lang": "en",
|
||||
"id": "11987",
|
||||
"likes": "2"
|
||||
},
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/clearart/wilfred-us-4e2f151d5ed62.png",
|
||||
"lang": "en",
|
||||
"id": "12470",
|
||||
"likes": "1"
|
||||
}
|
||||
],
|
||||
"hdtvlogo": [
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/hdtvlogo/wilfred-us-505f373be58e6.png",
|
||||
"lang": "en",
|
||||
"id": "21101",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/hdtvlogo/wilfred-us-517ac360def17.png",
|
||||
"lang": "en",
|
||||
"id": "28248",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/hdtvlogo/wilfred-us-52402df7ed945.png",
|
||||
"lang": "he",
|
||||
"id": "33750",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"url": "http://assets.fanart.tv/fanart/tv/239761/hdtvlogo/wilfred-us-51f556fb4abd3.png",
|
||||
"lang": "en",
|
||||
"id": "31816",
|
||||
"likes": "0"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
756
lib/fanart/tests/response/tv_79349.json
Normal file
@ -0,0 +1,756 @@
|
||||
{
|
||||
"Dexter": {
|
||||
"thetvdb_id": "79349",
|
||||
"hdtvlogo": [
|
||||
{
|
||||
"id": "20959",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/hdtvlogo/dexter-50575994eb118.png",
|
||||
"lang": "en",
|
||||
"likes": "10"
|
||||
},
|
||||
{
|
||||
"id": "20378",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/hdtvlogo/dexter-503fc2f24d9b3.png",
|
||||
"lang": "en",
|
||||
"likes": "5"
|
||||
}
|
||||
],
|
||||
"hdclearart": [
|
||||
{
|
||||
"id": "23059",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/hdclearart/dexter-50af98e73b0a5.png",
|
||||
"lang": "en",
|
||||
"likes": "8"
|
||||
},
|
||||
{
|
||||
"id": "24313",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/hdclearart/dexter-50eb4363da522.png",
|
||||
"lang": "en",
|
||||
"likes": "5"
|
||||
},
|
||||
{
|
||||
"id": "20560",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/hdclearart/dexter-504775fd50557.png",
|
||||
"lang": "en",
|
||||
"likes": "4"
|
||||
},
|
||||
{
|
||||
"id": "29495",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/hdclearart/dexter-51aa63100548b.png",
|
||||
"lang": "en",
|
||||
"likes": "3"
|
||||
},
|
||||
{
|
||||
"id": "26712",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/hdclearart/dexter-51400b1672938.png",
|
||||
"lang": "en",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"id": "29496",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/hdclearart/dexter-51aa724f0a2ab.png",
|
||||
"lang": "en",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"id": "29505",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/hdclearart/dexter-51aab23851368.png",
|
||||
"lang": "en",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"id": "29594",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/hdclearart/dexter-51afbcdf38d5e.png",
|
||||
"lang": "en",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"id": "29595",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/hdclearart/dexter-51afbcdf3ea8e.png",
|
||||
"lang": "en",
|
||||
"likes": "1"
|
||||
}
|
||||
],
|
||||
"clearlogo": [
|
||||
{
|
||||
"id": "20958",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/clearlogo/dexter-5057573260826.png",
|
||||
"lang": "en",
|
||||
"likes": "6"
|
||||
},
|
||||
{
|
||||
"id": "2114",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/clearlogo/Dexter-79349-2.png",
|
||||
"lang": "en",
|
||||
"likes": "4"
|
||||
},
|
||||
{
|
||||
"id": "14577",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/clearlogo/dexter-4ecdf0c030189.png",
|
||||
"lang": "en",
|
||||
"likes": "3"
|
||||
},
|
||||
{
|
||||
"id": "16685",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/clearlogo/dexter-4f6879db58edf.png",
|
||||
"lang": "ru",
|
||||
"likes": "1"
|
||||
}
|
||||
],
|
||||
"characterart": [
|
||||
{
|
||||
"id": "16825",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/characterart/dexter-4f76318ae4410.png",
|
||||
"lang": "en",
|
||||
"likes": "5"
|
||||
},
|
||||
{
|
||||
"id": "29497",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/characterart/dexter-51aa726346bcf.png",
|
||||
"lang": "en",
|
||||
"likes": "3"
|
||||
},
|
||||
{
|
||||
"id": "14981",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/characterart/dexter-4eface5cee809.png",
|
||||
"lang": "en",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"id": "16996",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/characterart/dexter-4f8189d220d4b.png",
|
||||
"lang": "en",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"id": "26713",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/characterart/dexter-51400b26c65de.png",
|
||||
"lang": "en",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"id": "29597",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/characterart/dexter-51afbcf6002a7.png",
|
||||
"lang": "en",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"id": "29598",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/characterart/dexter-51afbcf6006e6.png",
|
||||
"lang": "en",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"id": "29646",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/characterart/dexter-51b0fc45e0dc0.png",
|
||||
"lang": "en",
|
||||
"likes": "1"
|
||||
}
|
||||
],
|
||||
"clearart": [
|
||||
{
|
||||
"id": "4980",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/clearart/D_79349 (3).png",
|
||||
"lang": "en",
|
||||
"likes": "4"
|
||||
},
|
||||
{
|
||||
"id": "14579",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/clearart/dexter-4ecdf0db2adf1.png",
|
||||
"lang": "en",
|
||||
"likes": "3"
|
||||
},
|
||||
{
|
||||
"id": "16682",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/clearart/dexter-4f68753540f2d.png",
|
||||
"lang": "ru",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"id": "4982",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/clearart/D_79349.png",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "4983",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/clearart/D_79349 (1).png",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "4984",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/clearart/D_79349 (0).png",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "14578",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/clearart/dexter-4ecdf0cf3fb38.png",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "17196",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/clearart/dexter-4f8af83f3bde7.png",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
}
|
||||
],
|
||||
"showbackground": [
|
||||
{
|
||||
"id": "18467",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-4fc683691dea7.jpg",
|
||||
"lang": "en",
|
||||
"likes": "4",
|
||||
"season": "1"
|
||||
},
|
||||
{
|
||||
"id": "18950",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-4fdf608e2df53.jpg",
|
||||
"lang": "en",
|
||||
"likes": "2",
|
||||
"season": "3"
|
||||
},
|
||||
{
|
||||
"id": "18466",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-4fc6830dc2ccc.jpg",
|
||||
"lang": "en",
|
||||
"likes": "1",
|
||||
"season": "4"
|
||||
},
|
||||
{
|
||||
"id": "18468",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-4fc683a5ab451.jpg",
|
||||
"lang": "en",
|
||||
"likes": "1",
|
||||
"season": "6"
|
||||
},
|
||||
{
|
||||
"id": "21524",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-506bdd9c35771.jpg",
|
||||
"lang": "en",
|
||||
"likes": "1",
|
||||
"season": "all"
|
||||
},
|
||||
{
|
||||
"id": "21526",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-506bddc9f04cb.jpg",
|
||||
"lang": "en",
|
||||
"likes": "1",
|
||||
"season": ""
|
||||
},
|
||||
{
|
||||
"id": "21530",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-506bde2654668.jpg",
|
||||
"lang": "en",
|
||||
"likes": "1",
|
||||
"season": "all"
|
||||
},
|
||||
{
|
||||
"id": "24058",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-50def777ea9c8.jpg",
|
||||
"lang": "en",
|
||||
"likes": "1",
|
||||
"season": "all"
|
||||
},
|
||||
{
|
||||
"id": "18515",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-4fc8eab16803c.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "all"
|
||||
},
|
||||
{
|
||||
"id": "18947",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-4fdf5e107be0d.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "5"
|
||||
},
|
||||
{
|
||||
"id": "18949",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-4fdf601385517.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "all"
|
||||
},
|
||||
{
|
||||
"id": "18952",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-4fdf6386ce1c1.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "all"
|
||||
},
|
||||
{
|
||||
"id": "21525",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-506bddb3bd3f4.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "all"
|
||||
},
|
||||
{
|
||||
"id": "21527",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-506bdddc3f476.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "all"
|
||||
},
|
||||
{
|
||||
"id": "21529",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-506bde113406e.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "all"
|
||||
},
|
||||
{
|
||||
"id": "24046",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-50de1f84e736f.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "all"
|
||||
},
|
||||
{
|
||||
"id": "24048",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-50de1f84e7d57.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "all"
|
||||
},
|
||||
{
|
||||
"id": "24049",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-50de21ac3ae25.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "all"
|
||||
},
|
||||
{
|
||||
"id": "24054",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-50def777e84d0.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "all"
|
||||
},
|
||||
{
|
||||
"id": "24055",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-50def777e8dbc.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "all"
|
||||
},
|
||||
{
|
||||
"id": "24056",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-50def777e9762.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "all"
|
||||
},
|
||||
{
|
||||
"id": "24986",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/showbackground/dexter-5101fa187c857.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "all"
|
||||
}
|
||||
],
|
||||
"seasonthumb": [
|
||||
{
|
||||
"id": "18986",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fe21b7708ebe.jpg",
|
||||
"lang": "en",
|
||||
"likes": "3",
|
||||
"season": "6"
|
||||
},
|
||||
{
|
||||
"id": "5002",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (6).jpg",
|
||||
"lang": "en",
|
||||
"likes": "1",
|
||||
"season": "3"
|
||||
},
|
||||
{
|
||||
"id": "5003",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (5).jpg",
|
||||
"lang": "en",
|
||||
"likes": "1",
|
||||
"season": "1"
|
||||
},
|
||||
{
|
||||
"id": "17802",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fa981a7251d7.jpg",
|
||||
"lang": "en",
|
||||
"likes": "1",
|
||||
"season": "5"
|
||||
},
|
||||
{
|
||||
"id": "17823",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4faab0bccbfb6.jpg",
|
||||
"lang": "en",
|
||||
"likes": "1",
|
||||
"season": "6"
|
||||
},
|
||||
{
|
||||
"id": "18980",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fe21a6955116.jpg",
|
||||
"lang": "en",
|
||||
"likes": "1",
|
||||
"season": "1"
|
||||
},
|
||||
{
|
||||
"id": "18982",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fe21b0767edb.jpg",
|
||||
"lang": "en",
|
||||
"likes": "1",
|
||||
"season": "2"
|
||||
},
|
||||
{
|
||||
"id": "18983",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fe21b292d661.jpg",
|
||||
"lang": "en",
|
||||
"likes": "1",
|
||||
"season": "3"
|
||||
},
|
||||
{
|
||||
"id": "18984",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fe21b42d983d.jpg",
|
||||
"lang": "en",
|
||||
"likes": "1",
|
||||
"season": "4"
|
||||
},
|
||||
{
|
||||
"id": "18985",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fe21b5847d7b.jpg",
|
||||
"lang": "en",
|
||||
"likes": "1",
|
||||
"season": "5"
|
||||
},
|
||||
{
|
||||
"id": "21883",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-5071800d37e80.jpg",
|
||||
"lang": "en",
|
||||
"likes": "1",
|
||||
"season": "7"
|
||||
},
|
||||
{
|
||||
"id": "4989",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (9).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "all"
|
||||
},
|
||||
{
|
||||
"id": "4990",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (19).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "4"
|
||||
},
|
||||
{
|
||||
"id": "4991",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (18).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "4"
|
||||
},
|
||||
{
|
||||
"id": "4992",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (17).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "3"
|
||||
},
|
||||
{
|
||||
"id": "4993",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (16).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "2"
|
||||
},
|
||||
{
|
||||
"id": "4994",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (15).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "1"
|
||||
},
|
||||
{
|
||||
"id": "4995",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (14).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "all"
|
||||
},
|
||||
{
|
||||
"id": "4996",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (13).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "4"
|
||||
},
|
||||
{
|
||||
"id": "4997",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (12).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "3"
|
||||
},
|
||||
{
|
||||
"id": "4998",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (11).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "2"
|
||||
},
|
||||
{
|
||||
"id": "4999",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (10).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "1"
|
||||
},
|
||||
{
|
||||
"id": "5000",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (8).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "2"
|
||||
},
|
||||
{
|
||||
"id": "5001",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (7).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "4"
|
||||
},
|
||||
{
|
||||
"id": "5004",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "all"
|
||||
},
|
||||
{
|
||||
"id": "5005",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (4).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "5"
|
||||
},
|
||||
{
|
||||
"id": "5006",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (3).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "4"
|
||||
},
|
||||
{
|
||||
"id": "5007",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (2).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "3"
|
||||
},
|
||||
{
|
||||
"id": "5008",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (1).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "2"
|
||||
},
|
||||
{
|
||||
"id": "5009",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/Dexter (0).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "1"
|
||||
},
|
||||
{
|
||||
"id": "17803",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fa981a7258fb.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "5"
|
||||
},
|
||||
{
|
||||
"id": "17804",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fa981a725c14.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "5"
|
||||
},
|
||||
{
|
||||
"id": "17805",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fa981c6607e4.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "0"
|
||||
},
|
||||
{
|
||||
"id": "17807",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fa98ac2b811d.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "6"
|
||||
},
|
||||
{
|
||||
"id": "17808",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fa98ac2b87ab.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "6"
|
||||
},
|
||||
{
|
||||
"id": "17810",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fa994697afa3.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "6"
|
||||
},
|
||||
{
|
||||
"id": "18514",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-4fc8e9fa79bf8.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "7"
|
||||
},
|
||||
{
|
||||
"id": "31022",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-51dc720661cb7.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "8"
|
||||
},
|
||||
{
|
||||
"id": "31023",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/seasonthumb/dexter-51dc72a19a0bb.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0",
|
||||
"season": "8"
|
||||
}
|
||||
],
|
||||
"tvthumb": [
|
||||
{
|
||||
"id": "5012",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (10).jpg",
|
||||
"lang": "en",
|
||||
"likes": "2"
|
||||
},
|
||||
{
|
||||
"id": "5023",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (0).jpg",
|
||||
"lang": "en",
|
||||
"likes": "2"
|
||||
},
|
||||
{
|
||||
"id": "14580",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/dexter-4ecdf5027a53c.jpg",
|
||||
"lang": "en",
|
||||
"likes": "2"
|
||||
},
|
||||
{
|
||||
"id": "5013",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (9).jpg",
|
||||
"lang": "en",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"id": "5016",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (6).jpg",
|
||||
"lang": "en",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"id": "5020",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (2).jpg",
|
||||
"lang": "en",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"id": "29341",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/dexter-51a338d376b4a.jpg",
|
||||
"lang": "de",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"id": "31722",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/dexter-51f27112a2a89.jpg",
|
||||
"lang": "en",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"id": "5010",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (12).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "5011",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (11).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "5014",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (8).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "5015",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (7).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "5017",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (5).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "5018",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (4).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "5019",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (3).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "5021",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "5022",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/D_79349 (1).jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
},
|
||||
{
|
||||
"id": "14277",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/tvthumb/dexter-4ead4375923fd.jpg",
|
||||
"lang": "en",
|
||||
"likes": "0"
|
||||
}
|
||||
],
|
||||
"tvbanner": [
|
||||
{
|
||||
"id": "30062",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/tvbanner/dexter-51bfc857c84fd.jpg",
|
||||
"lang": "en",
|
||||
"likes": "1"
|
||||
},
|
||||
{
|
||||
"id": "30063",
|
||||
"url": "http://assets.fanart.tv/fanart/tv/79349/tvbanner/dexter-51bfc89667267.jpg",
|
||||
"lang": "en",
|
||||
"likes": "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
23
lib/fanart/tests/test_core.py
Normal file
@ -0,0 +1,23 @@
|
||||
from unittest import TestCase
|
||||
from fanart.core import Request
|
||||
from fanart.errors import RequestFanartError, ResponseFanartError
|
||||
from httpretty import httprettified, HTTPretty
|
||||
|
||||
|
||||
class RequestTestCase(TestCase):
|
||||
def test_valitate_error(self):
|
||||
self.assertRaises(RequestFanartError, Request, 'key', 'id', 'sport')
|
||||
|
||||
@httprettified
|
||||
def test_response_error(self):
|
||||
request = Request('apikey', 'objid', 'series')
|
||||
HTTPretty.register_uri(
|
||||
HTTPretty.GET,
|
||||
'http://api.fanart.tv/webservice/series/apikey/objid/JSON/all/1/2',
|
||||
body='Please specify a valid API key',
|
||||
)
|
||||
try:
|
||||
request.response()
|
||||
except ResponseFanartError as e:
|
||||
self.assertEqual(repr(e), "ResponseFanartError('No JSON object could be decoded',)")
|
||||
self.assertEqual(str(e), 'No JSON object could be decoded')
|
49
lib/fanart/tests/test_immutable.py
Normal file
@ -0,0 +1,49 @@
|
||||
from unittest import TestCase
|
||||
from fanart.immutable import Immutable
|
||||
|
||||
|
||||
class TestMutable(object):
|
||||
def __init__(self, spam, ham, eggs):
|
||||
self.spam = spam
|
||||
self.ham = ham
|
||||
self.eggs = eggs
|
||||
|
||||
@Immutable.mutablemethod
|
||||
def anyway(self):
|
||||
self.spam = self.ham + self.eggs
|
||||
|
||||
|
||||
class TestImmutable(TestMutable, Immutable):
|
||||
@Immutable.mutablemethod
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TestImmutable, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class ImmutableTestCase(TestCase):
|
||||
def setUp(self):
|
||||
self.instance = TestImmutable('spam', 'ham', 'eggs')
|
||||
|
||||
def test_set_raises(self):
|
||||
self.assertRaises(TypeError, self.instance.__setattr__, 'spam', 'ham')
|
||||
|
||||
def test_set(self):
|
||||
self.instance._mutable = True
|
||||
self.instance.spam = 'ham'
|
||||
self.assertEqual(self.instance.spam, 'ham')
|
||||
|
||||
def test_del_raises(self):
|
||||
self.assertRaises(TypeError, self.instance.__delattr__, 'spam')
|
||||
|
||||
def test_del(self):
|
||||
self.instance._mutable = True
|
||||
del self.instance.spam
|
||||
self.assertRaises(AttributeError, self.instance.__getattribute__, 'spam')
|
||||
|
||||
def test_equal(self):
|
||||
new_instance = TestImmutable('spam', 'ham', 'eggs')
|
||||
self.assertEqual(self.instance, new_instance)
|
||||
|
||||
def test_mutable_dec(self):
|
||||
instance = TestMutable('spam', 'ham', 'eggs')
|
||||
instance.anyway()
|
||||
self.assertEqual(instance.spam, 'hameggs')
|
27
lib/fanart/tests/test_items.py
Normal file
@ -0,0 +1,27 @@
|
||||
from unittest import TestCase
|
||||
import os
|
||||
from fanart.items import LeafItem
|
||||
from httpretty import httprettified, HTTPretty
|
||||
from fanart.tests import LOCALDIR
|
||||
|
||||
|
||||
class LeafItemTestCase(TestCase):
|
||||
def setUp(self):
|
||||
self.leaf = LeafItem(id=11977, likes=2, url='http://test.tv/50x50.txt')
|
||||
|
||||
def test_str(self):
|
||||
self.assertEqual(str(self.leaf), 'http://test.tv/50x50.txt')
|
||||
|
||||
@httprettified
|
||||
def test_content(self):
|
||||
with open(os.path.join(LOCALDIR, 'response/50x50.png')) as fp:
|
||||
body = fp.read()
|
||||
HTTPretty.register_uri(
|
||||
HTTPretty.GET,
|
||||
'http://test.tv/50x50.txt',
|
||||
body=body
|
||||
)
|
||||
self.assertEqual(self.leaf.content(), body)
|
||||
self.assertEqual(len(HTTPretty.latest_requests), 1)
|
||||
self.assertEqual(self.leaf.content(), body) # Cached
|
||||
self.assertEqual(len(HTTPretty.latest_requests), 1)
|
21
lib/fanart/tests/test_movie.py
Normal file
@ -0,0 +1,21 @@
|
||||
import os
|
||||
import unittest
|
||||
from httpretty import HTTPretty, httprettified
|
||||
from fanart.movie import *
|
||||
from fanart.tests import LOCALDIR
|
||||
os.environ['FANART_APIKEY'] = 'e3c7f0d0beeaf45b3a0dd3b9dd8a3338'
|
||||
|
||||
|
||||
class TvItemTestCase(unittest.TestCase):
|
||||
@httprettified
|
||||
def test_get(self):
|
||||
with open(os.path.join(LOCALDIR, 'response/movie_thg.json')) as fp:
|
||||
body = fp.read()
|
||||
HTTPretty.register_uri(
|
||||
HTTPretty.GET,
|
||||
'http://api.fanart.tv/webservice/movie/e3c7f0d0beeaf45b3a0dd3b9dd8a3338/70160/JSON/all/1/2',
|
||||
body=body
|
||||
)
|
||||
hunger_games = Movie.get(id=70160)
|
||||
self.assertEqual(hunger_games.tmdbid, '70160')
|
||||
self.assertEqual(hunger_games, eval(repr(hunger_games)))
|
22
lib/fanart/tests/test_music.py
Normal file
@ -0,0 +1,22 @@
|
||||
import os
|
||||
import unittest
|
||||
from httpretty import HTTPretty, httprettified
|
||||
from fanart.music import *
|
||||
from fanart.tests import LOCALDIR
|
||||
os.environ['FANART_APIKEY'] = 'e3c7f0d0beeaf45b3a0dd3b9dd8a3338'
|
||||
|
||||
|
||||
class ArtistItemTestCase(unittest.TestCase):
|
||||
@httprettified
|
||||
def test_get(self):
|
||||
with open(os.path.join(LOCALDIR, 'response/music_a7f.json')) as fp:
|
||||
body = fp.read()
|
||||
HTTPretty.register_uri(
|
||||
HTTPretty.GET,
|
||||
'http://api.fanart.tv/webservice/artist/e3c7f0d0beeaf45b3a0dd3b9dd8a3338/24e1b53c-3085-4581-8472-0b0088d2508c/JSON/all/1/2',
|
||||
body=body
|
||||
)
|
||||
a7f = Artist.get(id='24e1b53c-3085-4581-8472-0b0088d2508c')
|
||||
self.assertEqual(a7f.mbid, '24e1b53c-3085-4581-8472-0b0088d2508c')
|
||||
self.assertEqual(a7f, eval(repr(a7f)))
|
||||
self.assertEqual(len(a7f.thumbs), 2)
|
46
lib/fanart/tests/test_tv.py
Normal file
@ -0,0 +1,46 @@
|
||||
import json
|
||||
from fanart.errors import ResponseFanartError
|
||||
import os
|
||||
import unittest
|
||||
from httpretty import HTTPretty, httprettified
|
||||
from fanart.tv import *
|
||||
from fanart.tests import LOCALDIR
|
||||
os.environ['FANART_APIKEY'] = 'e3c7f0d0beeaf45b3a0dd3b9dd8a3338'
|
||||
|
||||
|
||||
class TvItemTestCase(unittest.TestCase):
|
||||
@httprettified
|
||||
def test_get_wilfred(self):
|
||||
with open(os.path.join(LOCALDIR, 'response/tv_239761.json')) as fp:
|
||||
body = fp.read()
|
||||
HTTPretty.register_uri(
|
||||
HTTPretty.GET,
|
||||
'http://api.fanart.tv/webservice/series/e3c7f0d0beeaf45b3a0dd3b9dd8a3338/239761/JSON/all/1/2',
|
||||
body=body
|
||||
)
|
||||
wilfred = TvShow.get(id=239761)
|
||||
self.assertEqual(wilfred.tvdbid, '239761')
|
||||
with open(os.path.join(LOCALDIR, 'json/wilfred.json')) as fp:
|
||||
self.assertEqual(json.loads(wilfred.json()), json.load(fp))
|
||||
|
||||
@httprettified
|
||||
def test_get_dexter(self):
|
||||
with open(os.path.join(LOCALDIR, 'response/tv_79349.json')) as fp:
|
||||
body = fp.read()
|
||||
HTTPretty.register_uri(
|
||||
HTTPretty.GET,
|
||||
'http://api.fanart.tv/webservice/series/e3c7f0d0beeaf45b3a0dd3b9dd8a3338/79349/JSON/all/1/2',
|
||||
body=body
|
||||
)
|
||||
dexter = TvShow.get(id=79349)
|
||||
self.assertEqual(dexter.tvdbid, '79349')
|
||||
self.assertEqual(dexter, eval(repr(dexter)))
|
||||
|
||||
@httprettified
|
||||
def test_get_null(self):
|
||||
HTTPretty.register_uri(
|
||||
HTTPretty.GET,
|
||||
'http://api.fanart.tv/webservice/series/e3c7f0d0beeaf45b3a0dd3b9dd8a3338/79349/JSON/all/1/2',
|
||||
body='null'
|
||||
)
|
||||
self.assertRaises(ResponseFanartError, TvShow.get, id=79349)
|
108
lib/fanart/tv.py
Normal file
@ -0,0 +1,108 @@
|
||||
import fanart
|
||||
from fanart.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),
|
||||
)
|
@ -736,7 +736,7 @@ class Tvdb:
|
||||
return
|
||||
|
||||
banners = {}
|
||||
for cur_banner in bannersEt['banner']:
|
||||
for cur_banner in bannersEt['banner'] if isinstance(bannersEt['banner'], list) else [bannersEt['banner']]:
|
||||
bid = cur_banner['id']
|
||||
btype = cur_banner['bannertype']
|
||||
btype2 = cur_banner['bannertype2']
|
||||
@ -797,7 +797,7 @@ class Tvdb:
|
||||
return
|
||||
|
||||
cur_actors = Actors()
|
||||
for cur_actor in actorsEt['actor']:
|
||||
for cur_actor in actorsEt['actor'] if isinstance(actorsEt['actor'], list) else [actorsEt['actor']]:
|
||||
curActor = Actor()
|
||||
for k, v in cur_actor.items():
|
||||
if k is None or v is None:
|
||||
|
@ -58,7 +58,7 @@ CFG = None
|
||||
CONFIG_FILE = None
|
||||
|
||||
# This is the version of the config we EXPECT to find
|
||||
CONFIG_VERSION = 5
|
||||
CONFIG_VERSION = 6
|
||||
|
||||
# Default encryption version (0 for None)
|
||||
ENCRYPTION_VERSION = 0
|
||||
@ -148,7 +148,6 @@ CPU_PRESET = None
|
||||
|
||||
ANON_REDIRECT = None
|
||||
|
||||
USE_API = False
|
||||
API_KEY = None
|
||||
API_ROOT = None
|
||||
|
||||
@ -472,6 +471,7 @@ CALENDAR_UNPROTECTED = False
|
||||
|
||||
TMDB_API_KEY = 'edc5f123313769de83a71e157758030b'
|
||||
TRAKT_API_KEY = 'abd806c54516240c76e4ebc9c5ccf394'
|
||||
FANART_API_KEY = '9b3afaf26f6241bdb57d6cc6bd798da7'
|
||||
|
||||
__INITIALIZED__ = False
|
||||
|
||||
@ -484,7 +484,7 @@ def get_backlog_cycle_time():
|
||||
def initialize(consoleLogging=True):
|
||||
with INIT_LOCK:
|
||||
|
||||
global BRANCH, GIT_RESET, GIT_REMOTE, GIT_REMOTE_URL, CUR_COMMIT_HASH, CUR_COMMIT_BRANCH, ACTUAL_LOG_DIR, LOG_DIR, WEB_PORT, WEB_LOG, ENCRYPTION_VERSION, WEB_ROOT, WEB_USERNAME, WEB_PASSWORD, WEB_HOST, WEB_IPV6, USE_API, API_KEY, API_ROOT, ENABLE_HTTPS, HTTPS_CERT, HTTPS_KEY, \
|
||||
global BRANCH, GIT_RESET, GIT_REMOTE, GIT_REMOTE_URL, CUR_COMMIT_HASH, CUR_COMMIT_BRANCH, ACTUAL_LOG_DIR, LOG_DIR, WEB_PORT, WEB_LOG, ENCRYPTION_VERSION, WEB_ROOT, WEB_USERNAME, WEB_PASSWORD, WEB_HOST, WEB_IPV6, API_KEY, API_ROOT, ENABLE_HTTPS, HTTPS_CERT, HTTPS_KEY, \
|
||||
HANDLE_REVERSE_PROXY, USE_NZBS, USE_TORRENTS, NZB_METHOD, NZB_DIR, DOWNLOAD_PROPERS, RANDOMIZE_PROVIDERS, CHECK_PROPERS_INTERVAL, ALLOW_HIGH_PRIORITY, TORRENT_METHOD, \
|
||||
SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, SAB_CATEGORY_ANIME, SAB_HOST, \
|
||||
NZBGET_USERNAME, NZBGET_PASSWORD, NZBGET_CATEGORY, NZBGET_CATEGORY_ANIME, NZBGET_PRIORITY, NZBGET_HOST, NZBGET_USE_HTTPS, backlogSearchScheduler, \
|
||||
@ -658,7 +658,6 @@ def initialize(consoleLogging=True):
|
||||
|
||||
SORT_ARTICLE = bool(check_setting_int(CFG, 'General', 'sort_article', 0))
|
||||
|
||||
USE_API = bool(check_setting_int(CFG, 'General', 'use_api', 0))
|
||||
API_KEY = check_setting_str(CFG, 'General', 'api_key', '', censor_log=True)
|
||||
|
||||
ENABLE_HTTPS = bool(check_setting_int(CFG, 'General', 'enable_https', 0))
|
||||
@ -1435,7 +1434,6 @@ def save_config():
|
||||
new_config['General']['localhost_ip'] = LOCALHOST_IP
|
||||
new_config['General']['cpu_preset'] = CPU_PRESET
|
||||
new_config['General']['anon_redirect'] = ANON_REDIRECT
|
||||
new_config['General']['use_api'] = int(USE_API)
|
||||
new_config['General']['api_key'] = API_KEY
|
||||
new_config['General']['debug'] = int(DEBUG)
|
||||
new_config['General']['enable_https'] = int(ENABLE_HTTPS)
|
||||
|
@ -17,6 +17,7 @@
|
||||
# along with SickRage. If not, see <http://www.gnu.org/licenses/>.
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import sickbeard
|
||||
|
||||
@ -275,13 +276,6 @@ class UIError():
|
||||
"""
|
||||
|
||||
def __init__(self, message):
|
||||
try:
|
||||
self.title = sys.exc_info()[1].message
|
||||
except:
|
||||
self.title = None
|
||||
self.title = sys.exc_info()[-2]
|
||||
self.message = message
|
||||
self.time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
try:
|
||||
self.exc_info = sys.exc_info()
|
||||
except:
|
||||
self.exc_info = None
|
||||
self.time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
@ -457,7 +457,8 @@ class ConfigMigrator():
|
||||
2: 'Sync backup number with version number',
|
||||
3: 'Rename omgwtfnzb variables',
|
||||
4: 'Add newznab catIDs',
|
||||
5: 'Metadata update'
|
||||
5: 'Metadata update',
|
||||
6: 'Convert from XBMC to new KODI variables'
|
||||
}
|
||||
|
||||
def migrate_config(self):
|
||||
@ -673,11 +674,11 @@ class ConfigMigrator():
|
||||
new format: 0|0|0|0|0|0|0|0|0|0 -- 10 places
|
||||
|
||||
Drop the use of use_banner option.
|
||||
Migrate the poster override to just using the banner option (applies to kodi only).
|
||||
Migrate the poster override to just using the banner option (applies to xbmc only).
|
||||
"""
|
||||
|
||||
metadata_kodi = check_setting_str(self.config_obj, 'General', 'metadata_kodi', '0|0|0|0|0|0')
|
||||
metadata_kodi_12plus = check_setting_str(self.config_obj, 'General', 'metadata_kodi_12plus', '0|0|0|0|0|0')
|
||||
metadata_xbmc = check_setting_str(self.config_obj, 'General', 'metadata_xbmc', '0|0|0|0|0|0')
|
||||
metadata_xbmc_12plus = check_setting_str(self.config_obj, 'General', 'metadata_xbmc_12plus', '0|0|0|0|0|0')
|
||||
metadata_mediabrowser = check_setting_str(self.config_obj, 'General', 'metadata_mediabrowser', '0|0|0|0|0|0')
|
||||
metadata_ps3 = check_setting_str(self.config_obj, 'General', 'metadata_ps3', '0|0|0|0|0|0')
|
||||
metadata_wdtv = check_setting_str(self.config_obj, 'General', 'metadata_wdtv', '0|0|0|0|0|0')
|
||||
@ -698,7 +699,7 @@ class ConfigMigrator():
|
||||
# swap show fanart, show poster
|
||||
cur_metadata[3], cur_metadata[2] = cur_metadata[2], cur_metadata[3]
|
||||
# if user was using use_banner to override the poster, instead enable the banner option and deactivate poster
|
||||
if metadata_name == 'KODI' and use_banner:
|
||||
if metadata_name == 'XBMC' and use_banner:
|
||||
cur_metadata[4], cur_metadata[3] = cur_metadata[3], '0'
|
||||
# write new format
|
||||
metadata = '|'.join(cur_metadata)
|
||||
@ -717,10 +718,27 @@ class ConfigMigrator():
|
||||
|
||||
return metadata
|
||||
|
||||
sickbeard.METADATA_KODI = _migrate_metadata(metadata_kodi, 'KODI', use_banner)
|
||||
sickbeard.METADATA_KODI_12PLUS = _migrate_metadata(metadata_kodi_12plus, 'KODI 12+', use_banner)
|
||||
sickbeard.METADATA_XBMC = _migrate_metadata(metadata_xbmc, 'XBMC', use_banner)
|
||||
sickbeard.METADATA_XBMC_12PLUS = _migrate_metadata(metadata_xbmc_12plus, 'XBMC 12+', use_banner)
|
||||
sickbeard.METADATA_MEDIABROWSER = _migrate_metadata(metadata_mediabrowser, 'MediaBrowser', use_banner)
|
||||
sickbeard.METADATA_PS3 = _migrate_metadata(metadata_ps3, 'PS3', use_banner)
|
||||
sickbeard.METADATA_WDTV = _migrate_metadata(metadata_wdtv, 'WDTV', use_banner)
|
||||
sickbeard.METADATA_TIVO = _migrate_metadata(metadata_tivo, 'TIVO', use_banner)
|
||||
sickbeard.METADATA_MEDE8ER = _migrate_metadata(metadata_mede8er, 'Mede8er', use_banner)
|
||||
|
||||
# Migration v6: Convert from XBMC to KODI variables
|
||||
def _migrate_v6(self):
|
||||
sickbeard.USE_KODI = bool(check_setting_int(self.config_obj, 'XBMC', 'use_xbmc', 0))
|
||||
sickbeard.KODI_ALWAYS_ON = bool(check_setting_int(self.config_obj, 'XBMC', 'xbmc_always_on', 1))
|
||||
sickbeard.KODI_NOTIFY_ONSNATCH = bool(check_setting_int(self.config_obj, 'XBMC', 'xbmc_notify_onsnatch', 0))
|
||||
sickbeard.KODI_NOTIFY_ONDOWNLOAD = bool(check_setting_int(self.config_obj, 'XBMC', 'xbmc_notify_ondownload', 0))
|
||||
sickbeard.KODI_NOTIFY_ONSUBTITLEDOWNLOAD = bool(check_setting_int(self.config_obj, 'XBMC', 'xbmc_notify_onsubtitledownload', 0))
|
||||
sickbeard.KODI_UPDATE_LIBRARY = bool(check_setting_int(self.config_obj, 'XBMC', 'xbmc_update_library', 0))
|
||||
sickbeard.KODI_UPDATE_FULL = bool(check_setting_int(self.config_obj, 'XBMC', 'xbmc_update_full', 0))
|
||||
sickbeard.KODI_UPDATE_ONLYFIRST = bool(check_setting_int(self.config_obj, 'XBMC', 'xbmc_update_onlyfirst', 0))
|
||||
sickbeard.KODI_HOST = check_setting_str(self.config_obj, 'XBMC', 'xbmc_host', '')
|
||||
sickbeard.KODI_USERNAME = check_setting_str(self.config_obj, 'XBMC', 'xbmc_username', '', censor_log=True)
|
||||
sickbeard.KODI_PASSWORD = check_setting_str(self.config_obj, 'XBMC', 'xbmc_password', '', censor_log=True)
|
||||
sickbeard.METADATA_KODI = check_setting_str(self.config_obj, 'General', 'metadata_xbmc', '0|0|0|0|0|0|0|0|0|0')
|
||||
sickbeard.METADATA_KODI_12PLUS = check_setting_str(self.config_obj, 'General', 'metadata_xbmc_12plus', '0|0|0|0|0|0|0|0|0|0')
|
||||
|
||||
|
@ -73,18 +73,9 @@ class DBConnection(object):
|
||||
raise
|
||||
|
||||
def _execute(self, query, args):
|
||||
def convert(x):
|
||||
if isinstance(x, basestring):
|
||||
try:
|
||||
x = unicode(x).decode(sickbeard.SYS_ENCODING)
|
||||
except:
|
||||
pass
|
||||
return x
|
||||
|
||||
try:
|
||||
if not args:
|
||||
return self.connection.cursor().execute(query)
|
||||
# args = map(convert, args)
|
||||
return self.connection.cursor().execute(query, args)
|
||||
except Exception as e:
|
||||
raise e
|
||||
@ -238,7 +229,10 @@ class DBConnection(object):
|
||||
return columns
|
||||
|
||||
def _unicode_text_factory(self, x):
|
||||
return unicode(x, 'utf-8')
|
||||
try:
|
||||
return unicode(x, 'utf-8')
|
||||
except:
|
||||
return unicode(x, sickbeard.SYS_ENCODING)
|
||||
|
||||
def _dict_factory(self, cursor, row):
|
||||
d = {}
|
||||
|
@ -45,11 +45,16 @@ reverseNames = {u'ERROR': ERROR,
|
||||
|
||||
censoredItems = {}
|
||||
|
||||
|
||||
class NullHandler(logging.Handler):
|
||||
def emit(self, record):
|
||||
pass
|
||||
|
||||
class CensoredFormatter(logging.Formatter):
|
||||
|
||||
class CensoredFormatter(logging.Formatter, object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CensoredFormatter, self).__init__(*args, **kwargs)
|
||||
|
||||
def format(self, record):
|
||||
msg = super(CensoredFormatter, self).format(record)
|
||||
for k, v in censoredItems.items():
|
||||
@ -57,6 +62,7 @@ class CensoredFormatter(logging.Formatter):
|
||||
msg = msg.replace(v, len(v) * '*')
|
||||
return msg
|
||||
|
||||
|
||||
class Logger(object):
|
||||
def __init__(self):
|
||||
self.logger = logging.getLogger('sickrage')
|
||||
@ -65,7 +71,7 @@ class Logger(object):
|
||||
logging.getLogger('sickrage'),
|
||||
logging.getLogger('tornado.general'),
|
||||
logging.getLogger('tornado.application'),
|
||||
#logging.getLogger('tornado.access'),
|
||||
# logging.getLogger('tornado.access'),
|
||||
]
|
||||
|
||||
self.consoleLogging = False
|
||||
@ -119,13 +125,14 @@ class Logger(object):
|
||||
|
||||
# pass exception information if debugging enabled
|
||||
|
||||
kwargs["exc_info"] = 1 if level == ERROR else 0
|
||||
self.logger.log(level, message, *args, **kwargs)
|
||||
|
||||
if level == ERROR:
|
||||
self.logger.exception(message, *args, **kwargs)
|
||||
classes.ErrorViewer.add(classes.UIError(message))
|
||||
#if sickbeard.GIT_AUTOISSUES:
|
||||
|
||||
# if sickbeard.GIT_AUTOISSUES:
|
||||
# self.submit_errors()
|
||||
else:
|
||||
self.logger.log(level, message, *args, **kwargs)
|
||||
|
||||
def log_error_and_exit(self, error_msg, *args, **kwargs):
|
||||
self.log(error_msg, ERROR, *args, **kwargs)
|
||||
@ -139,37 +146,33 @@ class Logger(object):
|
||||
if not (sickbeard.GIT_USERNAME and sickbeard.GIT_PASSWORD and len(classes.ErrorViewer.errors) > 0):
|
||||
return
|
||||
|
||||
title = "[APP SUBMITTED]: "
|
||||
|
||||
gh_org = sickbeard.GIT_ORG or 'SiCKRAGETV'
|
||||
gh_repo = 'sickrage-issues'
|
||||
|
||||
self.gh_issues = Github(login_or_token=sickbeard.GIT_USERNAME, password=sickbeard.GIT_PASSWORD,
|
||||
user_agent="SiCKRAGE").get_organization(gh_org).get_repo(gh_repo)
|
||||
gh_issues = Github(login_or_token=sickbeard.GIT_USERNAME, password=sickbeard.GIT_PASSWORD,
|
||||
user_agent="SiCKRAGE").get_organization(gh_org).get_repo(gh_repo)
|
||||
|
||||
try:
|
||||
# read log file
|
||||
if self.logFile and os.path.isfile(self.logFile):
|
||||
with ek.ek(open, self.logFile) as f:
|
||||
log_data = f.readlines()
|
||||
except Exception as e:
|
||||
pass
|
||||
log_data = [line for line in reversed(log_data)]
|
||||
|
||||
try:
|
||||
# parse and submit errors to issue tracker
|
||||
for curError in sorted(classes.ErrorViewer.errors, key=lambda error: error.time, reverse=True)[:500]:
|
||||
if not curError.title:
|
||||
continue
|
||||
|
||||
regex = "^(%s)\s*([A-Z]+)\s*(.+?)\s*\:\:\s*(.*)$" % curError.time
|
||||
|
||||
maxlines = 50
|
||||
pastebin_url = None
|
||||
for i, x in enumerate(reversed(log_data)):
|
||||
regex = "^(%s)\s*([A-Z]+)\s*(.+?)\s*\:\:\s*(.*)$" % curError.time
|
||||
for i, x in enumerate(log_data):
|
||||
x = ek.ss(x)
|
||||
match = re.match(regex, x)
|
||||
if match:
|
||||
level = match.group(2)
|
||||
if reverseNames[level] == ERROR:
|
||||
paste_data = "".join(log_data[len(log_data) - i - 50:])
|
||||
paste_data = "".join(log_data[i:50])
|
||||
pastebin_url = PastebinAPI().paste('f59b8e9fa1fc2d033e399e6c7fb09d19', paste_data)
|
||||
break
|
||||
|
||||
@ -187,14 +190,16 @@ class Logger(object):
|
||||
message += u"---\n"
|
||||
message += u"_STAFF NOTIFIED_: @SiCKRAGETV/owners @SiCKRAGETV/moderators"
|
||||
|
||||
issue = self.gh_issues.create_issue(title + curError.title, message)
|
||||
issue = gh_issues.create_issue("[APP SUBMITTED]: " + curError.title, message)
|
||||
if issue:
|
||||
self.log('Your issue ticket #%s was submitted successfully!' % issue.number)
|
||||
|
||||
if not sickbeard.GIT_AUTOISSUES:
|
||||
ui.notifications.message('Your issue ticket #%s was submitted successfully!' % issue.number)
|
||||
finally:
|
||||
classes.ErrorViewer.clear()
|
||||
# clear error from error list
|
||||
classes.ErrorViewer.errors.remove(curError)
|
||||
|
||||
return issue
|
||||
except Exception as e:
|
||||
self.log(sickbeard.exceptions.ex(e), ERROR)
|
||||
|
||||
|
||||
class Wrapper(object):
|
||||
@ -209,5 +214,6 @@ class Wrapper(object):
|
||||
except AttributeError:
|
||||
return getattr(self.instance, name)
|
||||
|
||||
|
||||
_globals = sys.modules[__name__] = Wrapper(sys.modules[__name__])
|
||||
|
||||
|
@ -26,7 +26,7 @@ import re
|
||||
|
||||
import sickbeard
|
||||
|
||||
from sickbeard import exceptions, helpers
|
||||
from sickbeard import helpers
|
||||
from sickbeard.metadata import helpers as metadata_helpers
|
||||
from sickbeard import logger
|
||||
from sickbeard import encodingKludge as ek
|
||||
@ -35,6 +35,8 @@ from sickbeard.show_name_helpers import allPossibleShowNames
|
||||
|
||||
from lib.tmdb_api.tmdb_api import TMDB
|
||||
|
||||
import fanart
|
||||
from fanart.core import Request as fanartRequest
|
||||
|
||||
class GenericMetadata():
|
||||
"""
|
||||
@ -616,6 +618,7 @@ class GenericMetadata():
|
||||
continue
|
||||
|
||||
result = result + [self._write_image(seasonData, season_poster_file_path)]
|
||||
|
||||
if result:
|
||||
return all(result)
|
||||
else:
|
||||
@ -664,6 +667,7 @@ class GenericMetadata():
|
||||
continue
|
||||
|
||||
result = result + [self._write_image(seasonData, season_banner_file_path)]
|
||||
|
||||
if result:
|
||||
return all(result)
|
||||
else:
|
||||
@ -774,19 +778,27 @@ class GenericMetadata():
|
||||
if image_type == 'poster_thumb':
|
||||
if getattr(indexer_show_obj, 'poster', None) is not None:
|
||||
image_url = re.sub('posters', '_cache/posters', indexer_show_obj['poster'])
|
||||
if not image_url:
|
||||
# Try and get images from Fanart.TV
|
||||
image_url = self._retrieve_show_images_from_fanart(show_obj, image_type)
|
||||
if not image_url:
|
||||
# Try and get images from TMDB
|
||||
image_url = self._retrieve_show_images_from_tmdb(show_obj, image_type)
|
||||
elif image_type == 'banner_thumb':
|
||||
if getattr(indexer_show_obj, 'banner', None) is not None:
|
||||
image_url = re.sub('graphical', '_cache/graphical', indexer_show_obj['banner'])
|
||||
if not image_url:
|
||||
# Try and get images from Fanart.TV
|
||||
image_url = self._retrieve_show_images_from_fanart(show_obj, image_type)
|
||||
else:
|
||||
if getattr(indexer_show_obj, image_type, None) is not None:
|
||||
image_url = indexer_show_obj[image_type]
|
||||
|
||||
# Try and get posters and fanart from TMDB
|
||||
if image_url is None:
|
||||
if image_type in ('poster', 'poster_thumb'):
|
||||
image_url = self._retrieve_show_images_from_tmdb(show_obj, poster=True)
|
||||
elif image_type == 'fanart':
|
||||
image_url = self._retrieve_show_images_from_tmdb(show_obj, backdrop=True)
|
||||
if not image_url:
|
||||
# Try and get images from Fanart.TV
|
||||
image_url = self._retrieve_show_images_from_fanart(show_obj, image_type)
|
||||
if not image_url:
|
||||
# Try and get images from TMDB
|
||||
image_url = self._retrieve_show_images_from_tmdb(show_obj, image_type)
|
||||
|
||||
if image_url:
|
||||
image_data = metadata_helpers.getShowImage(image_url, which)
|
||||
@ -961,7 +973,13 @@ class GenericMetadata():
|
||||
|
||||
return (indexer_id, name, indexer)
|
||||
|
||||
def _retrieve_show_images_from_tmdb(self, show, backdrop=False, poster=False):
|
||||
def _retrieve_show_images_from_tmdb(self, show, type):
|
||||
types = {'poster': 'poster_path',
|
||||
'banner': None,
|
||||
'fanart': 'backdrop_path',
|
||||
'poster_thumb': 'poster_path',
|
||||
'banner_thumb': None}
|
||||
|
||||
# get TMDB configuration info
|
||||
tmdb = TMDB(sickbeard.TMDB_API_KEY)
|
||||
config = tmdb.Configuration()
|
||||
@ -977,14 +995,41 @@ class GenericMetadata():
|
||||
try:
|
||||
search = tmdb.Search()
|
||||
for show_name in set(allPossibleShowNames(show)):
|
||||
for result in search.collection({'query': show_name})['results'] + search.tv({'query': show_name})[
|
||||
'results']:
|
||||
if backdrop and result['backdrop_path']:
|
||||
return "{0}{1}{2}".format(base_url, max_size, result['backdrop_path'])
|
||||
elif poster and result['poster_path']:
|
||||
return "{0}{1}{2}".format(base_url, max_size, result['poster_path'])
|
||||
for result in search.collection({'query': show_name})['results'] + search.tv({'query': show_name})['results']:
|
||||
if types[type] and getattr(result, types[type]):
|
||||
return "{0}{1}{2}".format(base_url, max_size, result[types[type]])
|
||||
|
||||
except Exception, e:
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
logger.log(u"Could not find any posters or background for " + show.name, logger.DEBUG)
|
||||
logger.log(u"Could not find any " + type + " images on TMDB for " + show.name, logger.DEBUG)
|
||||
|
||||
def _retrieve_show_images_from_fanart(self, show, type, thumb=False):
|
||||
types = {'poster': fanart.TYPE.TV.POSTER,
|
||||
'banner': fanart.TYPE.TV.BANNER,
|
||||
'poster_thumb': fanart.TYPE.TV.POSTER,
|
||||
'banner_thumb': fanart.TYPE.TV.BANNER,
|
||||
'fanart': fanart.TYPE.TV.BACKGROUND,
|
||||
}
|
||||
|
||||
try:
|
||||
indexerid = helpers.mapIndexersToShow(show)[1]
|
||||
if indexerid:
|
||||
request = fanartRequest(
|
||||
apikey=sickbeard.FANART_API_KEY,
|
||||
id=indexerid,
|
||||
ws=fanart.WS.TV,
|
||||
type=types[type],
|
||||
sort=fanart.SORT.POPULAR,
|
||||
limit=fanart.LIMIT.ONE,
|
||||
)
|
||||
|
||||
resp = request.response()
|
||||
url = resp[types[type]][0]['url']
|
||||
if thumb:
|
||||
url = re.sub('/fanart/', '/preview/', url)
|
||||
return url
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
logger.log(u"Could not find any " + type + " images on Fanart.tv for " + show.name, logger.DEBUG)
|
@ -24,6 +24,7 @@ import os
|
||||
import re
|
||||
import itertools
|
||||
import urllib
|
||||
|
||||
import sickbeard
|
||||
import requests
|
||||
|
||||
@ -124,7 +125,9 @@ class GenericProvider:
|
||||
if not self._doLogin():
|
||||
return
|
||||
|
||||
self.headers.update({'Referer': self.proxy.getProxyURL()})
|
||||
if self.proxy.isEnabled():
|
||||
self.headers.update({'Referer': self.proxy.getProxyURL()})
|
||||
|
||||
return helpers.getURL(self.proxy._buildURL(url), post_data=post_data, params=params, headers=self.headers, timeout=timeout,
|
||||
session=self.session, json=json)
|
||||
|
||||
|
@ -38,10 +38,6 @@ from sickbeard import logger
|
||||
from sickbeard import tvcache
|
||||
from sickbeard.exceptions import ex, AuthException
|
||||
|
||||
from lib import requests
|
||||
from lib.requests import exceptions
|
||||
from lib.bencode import bdecode
|
||||
|
||||
class NewznabProvider(generic.NZBProvider):
|
||||
def __init__(self, name, url, key='', catIDs='5030,5040', search_mode='eponly', search_fallback=False,
|
||||
enable_daily=False, enable_backlog=False):
|
||||
@ -118,8 +114,6 @@ class NewznabProvider(generic.NZBProvider):
|
||||
return (False, return_categories, "Error getting html for [%s]" %
|
||||
("%s/api?%s" % (self.url, '&'.join("%s=%s" % (x,y) for x,y in params.items()) )))
|
||||
|
||||
#xml_categories = helpers.parse_xml(categories)
|
||||
|
||||
if not xml_categories:
|
||||
logger.log(u"Error parsing xml for [%s]" % (self.name),
|
||||
logger.DEBUG)
|
||||
|
@ -320,7 +320,7 @@ class QueueItemAdd(ShowQueueItem):
|
||||
return
|
||||
|
||||
except exceptions.MultipleShowObjectsException:
|
||||
logger.log(u"The show in " + self.showDir + " is already in your show list, skipping", logger.ERROR)
|
||||
logger.log(u"The show in " + self.showDir + " is already in your show list, skipping", logger.WARNING)
|
||||
ui.notifications.error('Show skipped', "The show in " + self.showDir + " is already in your show list")
|
||||
self._finishEarly()
|
||||
return
|
||||
@ -335,7 +335,6 @@ class QueueItemAdd(ShowQueueItem):
|
||||
try:
|
||||
self.show.loadIMDbInfo()
|
||||
except imdb_exceptions.IMDbError, e:
|
||||
#todo Insert UI notification
|
||||
logger.log(u" Something wrong on IMDb api: " + ex(e), logger.WARNING)
|
||||
except Exception, e:
|
||||
logger.log(u"Error loading IMDb info: " + ex(e), logger.ERROR)
|
||||
|
@ -1686,8 +1686,8 @@ class TVEpisode(object):
|
||||
except (ValueError, IndexError):
|
||||
logger.log(u"Malformed air date of " + str(firstaired) + " retrieved from " + sickbeard.indexerApi(
|
||||
self.indexer).name + " for (" + self.show.name + " - " + str(season) + "x" + str(episode) + ")",
|
||||
logger.ERROR)
|
||||
# if I'm incomplete on TVDB but I once was complete then just delete myself from the DB for now
|
||||
logger.WARNING)
|
||||
# if I'm incomplete on the indexer but I once was complete then just delete myself from the DB for now
|
||||
if self.indexerid != -1:
|
||||
self.deleteEpisode()
|
||||
return False
|
||||
|
@ -139,7 +139,8 @@ class TVCache():
|
||||
logger.log(traceback.format_exc(), logger.DEBUG)
|
||||
|
||||
def getRSSFeed(self, url, post_data=None, items=[]):
|
||||
self.provider.headers.update({'Referer': self.provider.proxy.getProxyURL()})
|
||||
if self.provider.proxy.isEnabled():
|
||||
self.provider.headers.update({'Referer': self.provider.proxy.getProxyURL()})
|
||||
return RSSFeeds(self.providerID).getFeed(self.provider.proxy._buildURL(url), post_data, self.provider.headers, items)
|
||||
|
||||
def _translateTitle(self, title):
|
||||
|
@ -395,6 +395,11 @@ class GitUpdateManager(UpdateManager):
|
||||
# update remote origin url
|
||||
self.update_remote_origin()
|
||||
|
||||
# remove untracked files and performs a hard reset on git branch to avoid update issues
|
||||
if sickbeard.GIT_RESET:
|
||||
self.clean()
|
||||
self.reset()
|
||||
|
||||
if self.branch == self._find_installed_branch():
|
||||
output, err, exit_status = self._run_git(self._git_path, 'pull -f %s %s' % (sickbeard.GIT_REMOTE, self.branch)) # @UnusedVariable
|
||||
else:
|
||||
@ -406,26 +411,26 @@ class GitUpdateManager(UpdateManager):
|
||||
# Notify update successful
|
||||
if sickbeard.NOTIFY_ON_UPDATE:
|
||||
notifiers.notify_git_update(sickbeard.CUR_COMMIT_HASH if sickbeard.CUR_COMMIT_HASH else "")
|
||||
return True
|
||||
else:
|
||||
# perform a hard reset to try and resolve the issue
|
||||
if self.reset() and self.update():
|
||||
return True
|
||||
|
||||
return False
|
||||
return True
|
||||
|
||||
def clean(self):
|
||||
"""
|
||||
Calls git clean to remove all untracked files. Returns a bool depending
|
||||
on the call's success.
|
||||
"""
|
||||
output, err, exit_status = self._run_git(self._git_path, 'clean -d -fx ""') # @UnusedVariable
|
||||
if exit_status == 0:
|
||||
return True
|
||||
|
||||
def reset(self):
|
||||
"""
|
||||
Calls git reset --hard to perform a hard reset. Returns a bool depending
|
||||
on the call's success.
|
||||
"""
|
||||
if sickbeard.GIT_RESET:
|
||||
output, err, exit_status = self._run_git(self._git_path, 'reset --hard') # @UnusedVariable
|
||||
|
||||
if exit_status == 0:
|
||||
return True
|
||||
|
||||
return False
|
||||
output, err, exit_status = self._run_git(self._git_path, 'reset --hard') # @UnusedVariable
|
||||
if exit_status == 0:
|
||||
return True
|
||||
|
||||
def list_remote_branches(self):
|
||||
# update remote origin url
|
||||
|
@ -94,13 +94,8 @@ class ApiHandler(RequestHandler):
|
||||
'image': lambda x: x['image'],
|
||||
}
|
||||
|
||||
if sickbeard.USE_API is not True:
|
||||
accessMsg = u"API :: " + self.request.remote_ip + " - SB API Disabled. ACCESS DENIED"
|
||||
logger.log(accessMsg, logger.WARNING)
|
||||
return self.finish(outputCallbackDict['default'](_responds(RESULT_DENIED, msg=accessMsg)))
|
||||
else:
|
||||
accessMsg = u"API :: " + self.request.remote_ip + " - gave correct API KEY. ACCESS GRANTED"
|
||||
logger.log(accessMsg, logger.DEBUG)
|
||||
accessMsg = u"API :: " + self.request.remote_ip + " - gave correct API KEY. ACCESS GRANTED"
|
||||
logger.log(accessMsg, logger.DEBUG)
|
||||
|
||||
# set the original call_dispatcher as the local _call_dispatcher
|
||||
_call_dispatcher = self.call_dispatcher
|
||||
|
@ -3425,7 +3425,7 @@ class ConfigGeneral(Config):
|
||||
def saveGeneral(self, log_dir=None, web_port=None, web_log=None, encryption_version=None, web_ipv6=None,
|
||||
update_shows_on_start=None, trash_remove_show=None, trash_rotate_logs=None, update_frequency=None,
|
||||
launch_browser=None, web_username=None,
|
||||
use_api=None, api_key=None, indexer_default=None, timezone_display=None, cpu_preset=None,
|
||||
api_key=None, indexer_default=None, timezone_display=None, cpu_preset=None,
|
||||
web_password=None, version_notify=None, enable_https=None, https_cert=None, https_key=None,
|
||||
handle_reverse_proxy=None, sort_article=None, auto_update=None, notify_on_update=None,
|
||||
proxy_setting=None, proxy_indexers=None, anon_redirect=None, git_path=None, git_remote=None,
|
||||
@ -3492,7 +3492,6 @@ class ConfigGeneral(Config):
|
||||
if not config.change_LOG_DIR(log_dir, web_log):
|
||||
results += ["Unable to create directory " + os.path.normpath(log_dir) + ", log directory not changed."]
|
||||
|
||||
sickbeard.USE_API = config.checkbox_to_value(use_api)
|
||||
sickbeard.API_KEY = api_key
|
||||
|
||||
sickbeard.ENABLE_HTTPS = config.checkbox_to_value(enable_https)
|
||||
@ -4713,6 +4712,8 @@ class ErrorLogs(WebRoot):
|
||||
if not (sickbeard.GIT_USERNAME and sickbeard.GIT_PASSWORD):
|
||||
logger.log(u'Please set your GitHub username and password in the config, unable to submit issue ticket to GitHub!')
|
||||
else:
|
||||
logger.submit_errors()
|
||||
issue = logger.submit_errors()
|
||||
if issue:
|
||||
ui.notifications.message('Your issue ticket #%s was submitted successfully!' % issue.number)
|
||||
|
||||
return self.redirect("/errorlogs/")
|
49
tests/issue_submitter_tests.py
Normal file
@ -0,0 +1,49 @@
|
||||
# coding=UTF-8
|
||||
# Author: Dennis Lutter <lad1337@gmail.com>
|
||||
# URL: http://code.google.com/p/sickbeard/
|
||||
#
|
||||
# This file is part of SickRage.
|
||||
#
|
||||
# SickRage is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# SickRage is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with SickRage. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
import unittest
|
||||
import sys, os.path
|
||||
from configobj import ConfigObj
|
||||
|
||||
sys.path.append(os.path.abspath('..'))
|
||||
sys.path.append(os.path.abspath('../lib'))
|
||||
|
||||
import sickbeard
|
||||
import test_lib as test
|
||||
|
||||
def error():
|
||||
try:
|
||||
raise Exception('FAKE EXCEPTION')
|
||||
except Exception as e:
|
||||
sickbeard.logger.log("FAKE ERROR: " + sickbeard.exceptions.ex(e), sickbeard.logger.ERROR)
|
||||
sickbeard.logger.submit_errors()
|
||||
raise
|
||||
|
||||
class IssueSubmitterBasicTests(unittest.TestCase):
|
||||
def test_submitter(self):
|
||||
self.assertRaises(Exception, error)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print "=================="
|
||||
print "STARTING - ISSUE SUBMITTER TESTS"
|
||||
print "=================="
|
||||
print "######################################################################"
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(IssueSubmitterBasicTests)
|
@ -25,6 +25,7 @@ import sqlite3
|
||||
|
||||
import sys
|
||||
import os.path
|
||||
from configobj import ConfigObj
|
||||
|
||||
sys.path.append(os.path.abspath('..'))
|
||||
sys.path.append(os.path.abspath('../lib'))
|
||||
@ -54,9 +55,6 @@ FILEDIR = os.path.join(TESTDIR, SHOWNAME)
|
||||
FILEPATH = os.path.join(FILEDIR, FILENAME)
|
||||
SHOWDIR = os.path.join(TESTDIR, SHOWNAME + " final")
|
||||
|
||||
sickbeard.logger.logFile = os.path.join(os.path.join(TESTDIR, 'Logs'), 'test_sickbeard.log')
|
||||
sickbeard.logger.initLogging()
|
||||
|
||||
#=================
|
||||
# prepare env functions
|
||||
#=================
|
||||
@ -74,6 +72,7 @@ def createTestCacheFolder():
|
||||
# sickbeard globals
|
||||
#=================
|
||||
sickbeard.SYS_ENCODING = 'UTF-8'
|
||||
|
||||
sickbeard.showList = []
|
||||
sickbeard.QUALITY_DEFAULT = 4 # hdtv
|
||||
sickbeard.FLATTEN_FOLDERS_DEFAULT = 0
|
||||
@ -90,13 +89,23 @@ sickbeard.providerList = providers.makeProviderList()
|
||||
|
||||
sickbeard.PROG_DIR = os.path.abspath('..')
|
||||
sickbeard.DATA_DIR = sickbeard.PROG_DIR
|
||||
sickbeard.CONFIG_FILE = os.path.join(sickbeard.DATA_DIR, "config.ini")
|
||||
sickbeard.CFG = ConfigObj(sickbeard.CONFIG_FILE)
|
||||
|
||||
sickbeard.BRANCG = sickbeard.config.check_setting_str(sickbeard.CFG, 'General', 'branch', '')
|
||||
sickbeard.CUR_COMMIT_HASH = sickbeard.config.check_setting_str(sickbeard.CFG, 'General', 'cur_commit_hash', '')
|
||||
sickbeard.GIT_USERNAME = sickbeard.config.check_setting_str(sickbeard.CFG, 'General', 'git_username', '')
|
||||
sickbeard.GIT_PASSWORD = sickbeard.config.check_setting_str(sickbeard.CFG, 'General', 'git_password', '', censor_log=True)
|
||||
|
||||
sickbeard.LOG_DIR = os.path.join(TESTDIR, 'Logs')
|
||||
sickbeard.logger.logFile = os.path.join(sickbeard.LOG_DIR, 'test_sickbeard.log')
|
||||
createTestLogFolder()
|
||||
sickbeard.logger.initLogging(False)
|
||||
|
||||
sickbeard.CACHE_DIR = os.path.join(TESTDIR, 'cache')
|
||||
createTestCacheFolder()
|
||||
|
||||
sickbeard.logger.initLogging(False, True)
|
||||
|
||||
#=================
|
||||
# dummy functions
|
||||
#=================
|
||||
|