示例#1
0
class FileListCookie(Base):
    __tablename__ = 'filelist_cookie'

    username = Column(Unicode, primary_key=True)
    _cookie = Column('cookie', Unicode)
    cookie = json_synonym('_cookie')
    expires = Column(DateTime)
示例#2
0
class Variables(Base):
    __tablename__ = 'variables'

    id = Column(Integer, primary_key=True)
    _variables = Column('variables', Unicode)
    variables = json_synonym('_variables')
    added = Column(DateTime, default=datetime.now)
示例#3
0
class PassThePopcornCookie(Base):
    __tablename__ = 'passthepopcorn_cookie'

    username = Column(Unicode, primary_key=True)
    _cookie = Column('cookie', Unicode)
    cookie = json_synonym('_cookie')
    expires = Column(DateTime)
示例#4
0
class MoreThanTVCookie(Base):
    __tablename__ = 'morethantv_cookie'

    username = Column(Unicode, primary_key=True)
    _cookie = Column('cookie', Unicode)
    cookie = json_synonym('_cookie')
    expires = Column(DateTime)
示例#5
0
文件: hebits.py 项目: luizoti/Flexget
class HEBitsCookies(Base):
    __tablename__ = 'hebits_cookies'

    user_name: str = Column(Unicode, primary_key=True)
    _cookies = Column('cookies', Unicode)
    cookies: dict = json_synonym('_cookies')
    expires: datetime = Column(DateTime)
示例#6
0
class AlphaRatioCookie(Base):
    __tablename__ = 'alpharatio_cookie'

    username = Column(Unicode, primary_key=True)
    _cookie = Column('cookie', Unicode)
    cookie = json_synonym('_cookie')
    expires = Column(DateTime)
示例#7
0
class Secrets(Base):
    __tablename__ = 'secrets'

    id = Column(Integer, primary_key=True)
    _secrets = Column('secrets', Unicode)
    secrets = json_synonym('_secrets')
    added = Column(DateTime, default=datetime.now)
示例#8
0
class GazelleSession(Base):
    __tablename__ = 'gazelle_session'

    username = Column(Unicode, primary_key=True)
    base_url = Column(String, primary_key=True)

    authkey = Column(String)
    passkey = Column(String)
    _cookies = Column('cookie', Unicode)
    cookies = json_synonym('_cookies')
    expires = Column(DateTime)
示例#9
0
class IMDBListUser(Base):
    __tablename__ = "imdb_list_user"

    user_id = Column(String, primary_key=True)
    user_name = Column(Unicode)
    _cookies = Column('cookies', Unicode)
    cookies = json_synonym('_cookies')

    lists = relation('IMDBListList', backref='imdb_user', cascade='all, delete, delete-orphan')

    def __init__(self, user_name, user_id, cookies):
        self.user_name = user_name
        self.user_id = user_id
        self.cookies = cookies
示例#10
0
class SimpleKeyValue(Base):
    __tablename__ = 'simple_persistence'

    id = Column(Integer, primary_key=True)
    task = Column('feed', String)
    plugin = Column(String)
    key = Column(String)
    _json = Column('json', Unicode)
    value = json_synonym('_json')
    added = Column(DateTime, default=datetime.now())

    def __init__(self, task, plugin, key, value):
        self.task = task
        self.plugin = plugin
        self.key = key
        self.value = value

    def __repr__(self):
        return "<SimpleKeyValue('%s','%s','%s')>" % (self.task, self.key, self.value)
示例#11
0
class TMDBConfig(Base):
    __tablename__ = 'tmdb_configuration'

    id = Column(Integer, primary_key=True)
    _configuration = Column('configuration', Unicode)
    configuration = json_synonym('_configuration')
    updated = Column(DateTime, default=datetime.now, nullable=False)

    def __init__(self):
        try:
            configuration = tmdb_request('configuration')
        except requests.RequestException as e:
            raise LookupError('Error updating data from tmdb: %s' % e)
        self.configuration = configuration

    @property
    def expired(self):
        if self.updated < datetime.now() - timedelta(days=5):
            return True
        return False
示例#12
0
class TVMazeSeries(Base):
    __tablename__ = 'tvmaze_series'

    tvmaze_id = Column(Integer, primary_key=True)
    status = Column(Unicode)
    rating = Column(Float)
    genres = relation(TVMazeGenre, secondary=genres_table)
    weight = Column(Integer)
    updated = Column(DateTime)  # last time show was updated at tvmaze
    name = Column(Unicode)
    language = Column(Unicode)
    _schedule = Column('schedule', Unicode)
    schedule = json_synonym('_schedule')
    url = Column(String)
    original_image = Column(String)
    medium_image = Column(String)
    tvdb_id = Column(Integer)
    tvrage_id = Column(Integer)
    premiered = Column(DateTime)
    year = Column(Integer)
    summary = Column(Unicode)
    webchannel = Column(String)
    runtime = Column(Integer)
    show_type = Column(String)
    network = Column(Unicode)
    episodes = relation('TVMazeEpisodes',
                        order_by='TVMazeEpisodes.season_number',
                        cascade='all, delete, delete-orphan',
                        backref='series')
    last_update = Column(DateTime)  # last time we updated the db for the show

    def __init__(self, series, session):
        self.tvmaze_id = series['id']
        self.update(series, session)

    def to_dict(self):
        return {
            'tvmaze_id': self.tvmaze_id,
            'status': self.status,
            'rating': self.rating,
            'genres': [genre.name for genre in self.genres],
            'weight': self.weight,
            'updated': self.updated,
            'name': self.name,
            'language': self.language,
            'schedule': self.schedule,
            'url': self.url,
            'original_image': self.original_image,
            'medium_image': self.medium_image,
            'tvdb_id': self.tvdb_id,
            'tvrage_id': self.tvrage_id,
            'premiered': self.premiered,
            'year': self.year,
            'summary': self.summary,
            'webchannel': self.webchannel,
            'runtime': self.runtime,
            'show_type': self.show_type,
            'network': self.network,
            'last_update': self.last_update
        }

    def update(self, series, session):
        self.status = series['status']
        self.rating = series['rating']['average']
        self.weight = series['weight']
        self.updated = datetime.fromtimestamp(series['updated'])
        self.name = series['name']
        self.language = series['language']
        self.schedule = series['schedule']
        self.url = series['url']
        self.original_image = series.get('image').get(
            'original') if series.get('image') else None
        self.medium_image = series.get('image').get('medium') if series.get(
            'image') else None
        self.tvdb_id = series['externals'].get('thetvdb')
        self.tvrage_id = series['externals'].get('tvrage')
        self.premiered = parser.parse(
            series.get('premiered'),
            ignoretz=True) if series.get('premiered') else None
        self.year = int(
            series.get('premiered')[:4]) if series.get('premiered') else None
        self.summary = series['summary']
        self.webchannel = series.get('web_channel')['name'] if series.get(
            'web_channel') else None
        self.runtime = series['runtime']
        self.show_type = series['type']
        self.network = series.get('network')['name'] if series.get(
            'network') else None
        self.last_update = datetime.now()

        self.genres[:] = get_db_genres(series['genres'], session)

    def __repr__(self):
        return '<TVMazeSeries(title=%s,id=%s,last_update=%s)>' % (
            self.name, self.tvmaze_id, self.last_update)

    def __str__(self):
        return self.name

    @property
    def expired(self):
        if not self.last_update:
            log.debug('no last update attribute, series set for update')
            return True
        time_dif = datetime.now() - self.last_update
        expiration = time_dif.days > UPDATE_INTERVAL
        return expiration
示例#13
0
文件: api_tvdb.py 项目: umeku/Flexget
class TVDBSeries(Base):
    __tablename__ = "tvdb_series"

    id = Column(Integer, primary_key=True, autoincrement=False)
    last_updated = Column(Integer)
    expired = Column(Boolean)
    name = Column(Unicode)
    language = Column(Unicode)
    rating = Column(Float)
    status = Column(Unicode)
    runtime = Column(Integer)
    airs_time = Column(Unicode)
    airs_dayofweek = Column(Unicode)
    content_rating = Column(Unicode)
    network = Column(Unicode)
    overview = Column(Unicode)
    imdb_id = Column(Unicode)
    zap2it_id = Column(Unicode)
    _banner = Column('banner', Unicode)

    _first_aired = Column('first_aired', DateTime)
    first_aired = text_date_synonym('_first_aired')
    _aliases = Column('aliases', Unicode)
    aliases = json_synonym('_aliases')
    _actors = Column('actors', Unicode)
    actors_list = json_synonym('_actors')
    _posters = Column('posters', Unicode)
    posters_list = json_synonym('_posters')

    _genres = relation('TVDBGenre', secondary=genres_table)
    genres = association_proxy('_genres', 'name')

    episodes = relation('TVDBEpisode',
                        backref='series',
                        cascade='all, delete, delete-orphan')

    def __init__(self, tvdb_id):
        """
        Looks up movie on tvdb and creates a new database model for it.
        These instances should only be added to a session via `session.merge`.
        """
        self.id = tvdb_id

        try:
            series = TVDBRequest().get('series/%s' % self.id)
        except requests.RequestException as e:
            raise LookupError('Error updating data from tvdb: %s' % e)

        self.id = series['id']
        self.language = 'en'
        self.last_updated = series['lastUpdated']
        self.name = series['seriesName']
        self.rating = float(
            series['siteRating']) if series['siteRating'] else 0.0
        self.status = series['status']
        self.runtime = int(series['runtime']) if series['runtime'] else 0
        self.airs_time = series['airsTime']
        self.airs_dayofweek = series['airsDayOfWeek']
        self.content_rating = series['rating']
        self.network = series['network']
        self.overview = series['overview']
        self.imdb_id = series['imdbId']
        self.zap2it_id = series['zap2itId']
        self.first_aired = series['firstAired']
        self.expired = False
        self.aliases = series['aliases']
        self._banner = series['banner']
        self._genres = [TVDBGenre(
            id=name) for name in series['genre']] if series['genre'] else []

        # Actors and Posters are lazy populated
        self._actors = None
        self._posters = None

    def __repr__(self):
        return '<TVDBSeries name=%s,tvdb_id=%s>' % (self.name, self.id)

    @property
    def banner(self):
        if self._banner:
            return TVDBRequest.BANNER_URL + self._banner

    @property
    def actors(self):
        return self.get_actors()

    @property
    def posters(self):
        return self.get_posters()

    def get_actors(self):
        if not self._actors:
            log.debug('Looking up actors for series %s' % self.name)
            try:
                actors_query = TVDBRequest().get('series/%s/actors' % self.id)
                self.actors_list = [a['name'] for a in actors_query
                                    ] if actors_query else []
            except requests.RequestException as e:
                if None is not e.response and e.response.status_code == 404:
                    self.actors_list = []
                else:
                    raise LookupError('Error updating actors from tvdb: %s' %
                                      e)

        return self.actors_list

    def get_posters(self):
        if not self._posters:
            log.debug('Getting top 5 posters for series %s' % self.name)
            try:
                poster_query = TVDBRequest().get('series/%s/images/query' %
                                                 self.id,
                                                 keyType='poster')
                self.posters_list = [p['fileName'] for p in poster_query[:5]
                                     ] if poster_query else []
            except requests.RequestException as e:
                if None is not e.response and e.response.status_code == 404:
                    self.posters_list = []
                else:
                    raise LookupError('Error updating posters from tvdb: %s' %
                                      e)

        return [TVDBRequest.BANNER_URL + p for p in self.posters_list]

    def to_dict(self):
        return {
            'tvdb_id':
            self.id,
            'last_updated':
            datetime.fromtimestamp(
                self.last_updated).strftime('%Y-%m-%d %H:%M:%S'),
            'expired':
            self.expired,
            'series_name':
            self.name,
            'language':
            self.language,
            'rating':
            self.rating,
            'status':
            self.status,
            'runtime':
            self.runtime,
            'airs_time':
            self.airs_time,
            'airs_dayofweek':
            self.airs_dayofweek,
            'content_rating':
            self.content_rating,
            'network':
            self.network,
            'overview':
            self.overview,
            'imdb_id':
            self.imdb_id,
            'zap2it_id':
            self.zap2it_id,
            'banner':
            self.banner,
            'posters':
            self.posters,
            'genres': [g for g in self.genres],
            'actors':
            self.actors,
            'first_aired':
            self.first_aired,
        }
示例#14
0
class TVDBSeriesSearchResult(Base):
    """
    This table will hold a single result that results from the /search/series endpoint,
    which return a series with a minimal set of parameters.
    """

    __tablename__ = 'tvdb_series_search_results'

    id = Column(Integer, primary_key=True, autoincrement=False)
    lookup_term = Column(Unicode)

    name = Column(Unicode)
    status = Column(Unicode)
    network = Column(Unicode)
    overview = Column(Unicode)

    _banner = Column('banner', Unicode)

    _first_aired = Column('first_aired', DateTime)
    first_aired = text_date_synonym('_first_aired')

    _aliases = Column('aliases', Unicode)
    aliases = json_synonym('_aliases')

    created_at = Column(DateTime)
    search_name = Column(Unicode)

    def __init__(self, series, lookup_term=None):
        self.lookup_term = lookup_term
        self.id = series['id']
        self.name = series['seriesName']
        self.first_aired = series['firstAired']
        self.network = series['network']
        self.overview = series['overview']
        self.status = series['status']
        self._banner = series['banner']
        self.aliases = series['aliases']
        self.created_at = datetime.now()

    @property
    def banner(self):
        if self._banner:
            return TVDBRequest.BANNER_URL + self._banner

    def to_dict(self):
        return {
            'aliases': [a for a in self.aliases],
            'banner': self.banner,
            'first_aired': self.first_aired,
            'tvdb_id': self.id,
            'network': self.network,
            'overview': self.overview,
            'series_name': self.name,
            'status': self.status,
        }

    @property
    def expired(self):
        logger.debug('checking series {} for expiration', self.original_name)
        if datetime.now() - self.created_at >= timedelta(
                days=SEARCH_RESULT_EXPIRATION_DAYS):
            logger.debug('series {} is expires, should re-fetch',
                         self.original_name)
            return True
        logger.debug('series {} is not expired', self.original_name)
        return False
示例#15
0
文件: db.py 项目: vgerak/Flexget
class TraktMovie(Base):
    __tablename__ = 'trakt_movies'

    id = Column(Integer, primary_key=True, autoincrement=False)
    title = Column(Unicode)
    year = Column(Integer)
    slug = Column(Unicode)
    imdb_id = Column(Unicode)
    tmdb_id = Column(Integer)
    tagline = Column(Unicode)
    overview = Column(Unicode)
    released = Column(Date)
    runtime = Column(Integer)
    rating = Column(Integer)
    votes = Column(Integer)
    trailer = Column(Unicode)
    homepage = Column(Unicode)
    language = Column(Unicode)
    updated_at = Column(DateTime)
    cached_at = Column(DateTime)
    _translations = relation(TraktMovieTranslation, backref='movie')
    _translation_languages = Column('translation_languages', Unicode)
    translation_languages = json_synonym('_translation_languages')
    genres = relation(TraktGenre, secondary=movie_genres_table)
    _actors = relation(TraktActor, secondary=movie_actors_table)

    def __init__(self, trakt_movie, session):
        super().__init__()
        self.update(trakt_movie, session)

    def to_dict(self):
        return {
            "id": self.id,
            "title": self.title,
            "year": self.year,
            "slug": self.slug,
            "imdb_id": self.imdb_id,
            "tmdb_id": self.tmdb_id,
            "tagline": self.tagline,
            "overview": self.overview,
            "released": self.released,
            "runtime": self.runtime,
            "rating": self.rating,
            "votes": self.votes,
            "language": self.language,
            "homepage": self.homepage,
            "trailer": self.trailer,
            "genres": [g.name for g in self.genres],
            "updated_at": self.updated_at,
            "cached_at": self.cached_at,
        }

    def update(self, trakt_movie, session):
        """Updates this record from the trakt media object `trakt_movie` returned by the trakt api."""
        if self.id and self.id != trakt_movie['ids']['trakt']:
            raise Exception(
                'Tried to update db movie with different movie data')
        elif not self.id:
            self.id = trakt_movie['ids']['trakt']
        self.slug = trakt_movie['ids']['slug']
        self.imdb_id = trakt_movie['ids']['imdb']
        self.tmdb_id = trakt_movie['ids']['tmdb']
        for col in [
                'title',
                'overview',
                'runtime',
                'rating',
                'votes',
                'language',
                'tagline',
                'year',
                'trailer',
                'homepage',
        ]:
            setattr(self, col, trakt_movie.get(col))
        if trakt_movie.get('released'):
            self.released = dateutil_parse(trakt_movie.get('released'),
                                           ignoretz=True).date()
        self.updated_at = dateutil_parse(trakt_movie.get('updated_at'),
                                         ignoretz=True)
        self.genres = [
            TraktGenre(name=g.replace(' ', '-'))
            for g in trakt_movie.get('genres', [])
        ]
        self.cached_at = datetime.now()
        self.translation_languages = trakt_movie.get('available_translations',
                                                     [])

    @property
    def expired(self):
        """
        :return: True if movie details are considered to be expired, ie. need of update
        """
        # TODO stolen from imdb plugin, maybe there's a better way?
        if self.updated_at is None:
            logger.debug('updated_at is None: {}', self)
            return True
        refresh_interval = 2
        if self.year:
            # Make sure age is not negative
            age = max((datetime.now().year - self.year), 0)
            refresh_interval += age * 5
            logger.debug('movie `{}` age {} expires in {} days', self.title,
                         age, refresh_interval)
        return self.cached_at < datetime.now() - timedelta(
            days=refresh_interval)

    @property
    def translations(self):
        if not self._translations:
            self._translations = get_translations(self.id, 'movie')
        return self._translations

    @property
    def actors(self):
        if not self._actors:
            self._actors[:] = get_db_actors(self.id, 'movie')
        return self._actors
示例#16
0
文件: db.py 项目: vgerak/Flexget
class TraktShow(Base):
    __tablename__ = 'trakt_shows'

    id = Column(Integer, primary_key=True, autoincrement=False)
    title = Column(Unicode)
    year = Column(Integer)
    slug = Column(Unicode)
    tvdb_id = Column(Integer)
    imdb_id = Column(Unicode)
    tmdb_id = Column(Integer)
    tvrage_id = Column(Unicode)
    overview = Column(Unicode)
    first_aired = Column(DateTime)
    air_day = Column(Unicode)
    air_time = Column(Time)
    timezone = Column(Unicode)
    runtime = Column(Integer)
    certification = Column(Unicode)
    network = Column(Unicode)
    country = Column(Unicode)
    status = Column(String)
    rating = Column(Integer)
    votes = Column(Integer)
    language = Column(Unicode)
    homepage = Column(Unicode)
    trailer = Column(Unicode)
    aired_episodes = Column(Integer)
    _translations = relation(TraktShowTranslation)
    _translation_languages = Column('translation_languages', Unicode)
    translation_languages = json_synonym('_translation_languages')
    episodes = relation(TraktEpisode,
                        backref='show',
                        cascade='all, delete, delete-orphan',
                        lazy='dynamic')
    seasons = relation(TraktSeason,
                       backref='show',
                       cascade='all, delete, delete-orphan',
                       lazy='dynamic')
    genres = relation(TraktGenre, secondary=show_genres_table)
    _actors = relation(TraktActor, secondary=show_actors_table)
    updated_at = Column(DateTime)
    cached_at = Column(DateTime)

    def to_dict(self):
        return {
            "id": self.id,
            "title": self.title,
            "year": self.year,
            "slug": self.slug,
            "tvdb_id": self.tvdb_id,
            "imdb_id": self.imdb_id,
            "tmdb_id": self.tmdb_id,
            "tvrage_id": self.tvrage_id,
            "overview": self.overview,
            "first_aired": self.first_aired,
            "air_day": self.air_day,
            "air_time":
            self.air_time.strftime("%H:%M") if self.air_time else None,
            "timezone": self.timezone,
            "runtime": self.runtime,
            "certification": self.certification,
            "network": self.network,
            "country": self.country,
            "status": self.status,
            "rating": self.rating,
            "votes": self.votes,
            "language": self.language,
            "homepage": self.homepage,
            "number_of_aired_episodes": self.aired_episodes,
            "genres": [g.name for g in self.genres],
            "updated_at": self.updated_at,
            "cached_at": self.cached_at,
        }

    def __init__(self, trakt_show, session):
        super().__init__()
        self.update(trakt_show, session)

    def update(self, trakt_show, session):
        """Updates this record from the trakt media object `trakt_show` returned by the trakt api."""
        if self.id and self.id != trakt_show['ids']['trakt']:
            raise Exception('Tried to update db show with different show data')
        elif not self.id:
            self.id = trakt_show['ids']['trakt']
        self.slug = trakt_show['ids']['slug']
        self.imdb_id = trakt_show['ids']['imdb']
        self.tmdb_id = trakt_show['ids']['tmdb']
        self.tvrage_id = trakt_show['ids']['tvrage']
        self.tvdb_id = trakt_show['ids']['tvdb']
        if trakt_show.get('airs'):
            airs = trakt_show.get('airs')
            self.air_day = airs.get('day')
            self.timezone = airs.get('timezone')
            if airs.get('time'):
                self.air_time = datetime.strptime(airs.get('time'),
                                                  '%H:%M').time()
            else:
                self.air_time = None
        if trakt_show.get('first_aired'):
            self.first_aired = dateutil_parse(trakt_show.get('first_aired'),
                                              ignoretz=True)
        else:
            self.first_aired = None
        self.updated_at = dateutil_parse(trakt_show.get('updated_at'),
                                         ignoretz=True)

        for col in [
                'overview',
                'runtime',
                'rating',
                'votes',
                'language',
                'title',
                'year',
                'runtime',
                'certification',
                'network',
                'country',
                'status',
                'aired_episodes',
                'trailer',
                'homepage',
        ]:
            setattr(self, col, trakt_show.get(col))

        # Sometimes genres and translations are None but we really do want a list, hence the "or []"
        self.genres = [
            TraktGenre(name=g.replace(' ', '-'))
            for g in trakt_show.get('genres') or []
        ]
        self.cached_at = datetime.now()
        self.translation_languages = trakt_show.get(
            'available_translations') or []

    def get_episode(self, season, number, session, only_cached=False):
        # TODO: Does series data being expired mean all episode data should be refreshed?
        episode = (self.episodes.filter(TraktEpisode.season == season).filter(
            TraktEpisode.number == number).first())
        if not episode or self.expired:
            url = get_api_url('shows', self.id, 'seasons', season, 'episodes',
                              number, '?extended=full')
            if only_cached:
                raise LookupError('Episode %s %s not found in cache' %
                                  (season, number))
            logger.debug(
                'Episode {} {} not found in cache, looking up from trakt.',
                season, number)
            try:
                data = get_session().get(url).json()
            except requests.RequestException:
                raise LookupError('Error Retrieving Trakt url: %s' % url)
            if not data:
                raise LookupError('No data in response from trakt %s' % url)
            episode = self.episodes.filter(
                TraktEpisode.id == data['ids']['trakt']).first()
            if episode:
                episode.update(data, session)
            else:
                episode = TraktEpisode(data, session)
                self.episodes.append(episode)
            session.commit()
        return episode

    def get_season(self, number, session, only_cached=False):
        # TODO: Does series data being expired mean all season data should be refreshed?
        season = self.seasons.filter(TraktSeason.number == number).first()
        if not season or self.expired:
            url = get_api_url('shows', self.id, 'seasons', '?extended=full')
            if only_cached:
                raise LookupError('Season %s not found in cache' % number)
            logger.debug(
                'Season {} not found in cache, looking up from trakt.', number)
            try:
                ses = get_session()
                data = ses.get(url).json()
            except requests.RequestException:
                raise LookupError('Error Retrieving Trakt url: %s' % url)
            if not data:
                raise LookupError('No data in response from trakt %s' % url)
            # We fetch all seasons for the given show because we barely get any data otherwise
            for season_result in data:
                db_season = self.seasons.filter(
                    TraktSeason.id == season_result['ids']['trakt']).first()
                if db_season:
                    db_season.update(season_result, session)
                else:
                    db_season = TraktSeason(season_result, session)
                    self.seasons.append(db_season)
                if number == season_result['number']:
                    season = db_season
            if not season:
                raise LookupError('Season %s not found for show %s' %
                                  (number, self.title))
            session.commit()
        return season

    @property
    def expired(self):
        """
        :return: True if show details are considered to be expired, ie. need of update
        """
        # TODO stolen from imdb plugin, maybe there's a better way?
        if self.cached_at is None:
            logger.debug('cached_at is None: {}', self)
            return True
        refresh_interval = 2
        # if show has been cancelled or ended, then it is unlikely to be updated often
        if self.year and (self.status == 'ended' or self.status == 'canceled'):
            # Make sure age is not negative
            age = max((datetime.now().year - self.year), 0)
            refresh_interval += age * 5
            logger.debug('show `{}` age {} expires in {} days', self.title,
                         age, refresh_interval)
        return self.cached_at < datetime.now() - timedelta(
            days=refresh_interval)

    @property
    def translations(self):
        if not self._translations:
            self._translations = get_translations(self.id, 'show')
        return self._translations

    @property
    def actors(self):
        if not self._actors:
            self._actors[:] = get_db_actors(self.id, 'show')
        return self._actors

    def __repr__(self):
        return '<name=%s, id=%s>' % (self.title, self.id)