Commit 11269dae authored by echel0n's avatar echel0n

Merge branch 'release/4.0.8'

parents 49ea8f5a 35fcca38
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
......@@ -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>
......
#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
......
__author__ = 'Andrea De Marco <[email protected]>'
__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)
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))
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
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
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()]
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),
)
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', {})),