from datetime import datetime, timedelta
from loguru import logger
from sqlalchemy import Boolean, Column, DateTime, Float, Integer, String, Table, Unicode
from sqlalchemy.orm import relationship
from sqlalchemy.schema import ForeignKey, Index
from flexget import db_schema
from flexget.components.imdb.utils import extract_id
from flexget.db_schema import UpgradeImpossible
logger = logger.bind(name='imdb.db')
SCHEMA_VER = 10
Base = db_schema.versioned_base('imdb_lookup', SCHEMA_VER)
# association tables
genres_table = Table(
'imdb_movie_genres',
Base.metadata,
Column('movie_id', Integer, ForeignKey('imdb_movies.id')),
Column('genre_id', Integer, ForeignKey('imdb_genres.id')),
Index('ix_imdb_movie_genres', 'movie_id', 'genre_id'),
)
Base.register_table(genres_table)
actors_table = Table(
'imdb_movie_actors',
Base.metadata,
Column('movie_id', Integer, ForeignKey('imdb_movies.id')),
Column('actor_id', Integer, ForeignKey('imdb_actors.id')),
Index('ix_imdb_movie_actors', 'movie_id', 'actor_id'),
)
Base.register_table(actors_table)
directors_table = Table(
'imdb_movie_directors',
Base.metadata,
Column('movie_id', Integer, ForeignKey('imdb_movies.id')),
Column('director_id', Integer, ForeignKey('imdb_directors.id')),
Index('ix_imdb_movie_directors', 'movie_id', 'director_id'),
)
Base.register_table(directors_table)
writers_table = Table(
'imdb_movie_writers',
Base.metadata,
Column('movie_id', Integer, ForeignKey('imdb_movies.id')),
Column('writer_id', Integer, ForeignKey('imdb_writers.id')),
Index('ix_imdb_movie_writers', 'movie_id', 'writer_id'),
)
Base.register_table(writers_table)
plot_keywords_table = Table(
'imdb_movie_plot_keywords',
Base.metadata,
Column('movie_id', Integer, ForeignKey('imdb_movies.id')),
Column('keyword_id', Integer, ForeignKey('imdb_plot_keywords.id')),
Index('ix_imdb_movie_plot_keywords', 'movie_id', 'keyword_id'),
)
Base.register_table(plot_keywords_table)
[docs]
class Movie(Base):
__tablename__ = 'imdb_movies'
id = Column(Integer, primary_key=True)
title = Column(Unicode)
original_title = Column(Unicode)
url = Column(String, index=True)
# many-to-many relations
genres = relationship('Genre', secondary=genres_table, backref='movies')
actors = relationship('Actor', secondary=actors_table, backref='movies')
directors = relationship('Director', secondary=directors_table, backref='movies')
writers = relationship('Writer', secondary=writers_table, backref='movies')
plot_keywords = relationship('PlotKeyword', secondary=plot_keywords_table, backref='movies')
languages = relationship('MovieLanguage', order_by='MovieLanguage.prominence')
score = Column(Float)
votes = Column(Integer)
meta_score = Column(Integer)
year = Column(Integer)
plot_outline = Column(Unicode)
mpaa_rating = Column(String, default='')
photo = Column(String)
# updated time, so we can grab new rating counts after 48 hours
# set a default, so existing data gets updated with a rating
updated = Column(DateTime)
@property
def imdb_id(self):
return extract_id(self.url)
@property
def expired(self):
""":return: True if movie details are considered to be expired, ie. need of update"""
if self.updated is None:
logger.debug('updated 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.updated < datetime.now() - timedelta(days=refresh_interval)
def __repr__(self):
return f'<Movie(name={self.title},votes={self.votes},year={self.year})>'
[docs]
class MovieLanguage(Base):
__tablename__ = 'imdb_movie_languages'
movie_id = Column(Integer, ForeignKey('imdb_movies.id'), primary_key=True)
language_id = Column(Integer, ForeignKey('imdb_languages.id'), primary_key=True)
prominence = Column(Integer)
language = relationship('Language')
def __init__(self, language, prominence=None):
self.language = language
self.prominence = prominence
[docs]
class Language(Base):
__tablename__ = 'imdb_languages'
id = Column(Integer, primary_key=True)
name = Column(Unicode)
def __init__(self, name):
self.name = name
[docs]
class Genre(Base):
__tablename__ = 'imdb_genres'
id = Column(Integer, primary_key=True)
name = Column(String)
def __init__(self, name):
self.name = name
[docs]
class Actor(Base):
__tablename__ = 'imdb_actors'
id = Column(Integer, primary_key=True)
imdb_id = Column(String)
name = Column(Unicode)
def __init__(self, imdb_id, name=None):
self.imdb_id = imdb_id
self.name = name
[docs]
class Director(Base):
__tablename__ = 'imdb_directors'
id = Column(Integer, primary_key=True)
imdb_id = Column(String)
name = Column(Unicode)
def __init__(self, imdb_id, name=None):
self.imdb_id = imdb_id
self.name = name
[docs]
class Writer(Base):
__tablename__ = 'imdb_writers'
id = Column(Integer, primary_key=True)
imdb_id = Column(String)
name = Column(Unicode)
def __init__(self, imdb_id, name=None):
self.imdb_id = imdb_id
self.name = name
[docs]
class PlotKeyword(Base):
__tablename__ = 'imdb_plot_keywords'
id = Column(Integer, primary_key=True)
name = Column(String)
def __init__(self, name):
self.name = name
[docs]
class SearchResult(Base):
__tablename__ = 'imdb_search'
id = Column(Integer, primary_key=True)
title = Column(Unicode, index=True)
url = Column(String)
fails = Column(Boolean, default=False)
queried = Column(DateTime)
@property
def imdb_id(self):
return extract_id(self.url)
def __init__(self, title, url=None):
self.title = title
self.url = url
self.queried = datetime.now()
def __repr__(self):
return f'<SearchResult(title={self.title},url={self.url},fails={self.fails})>'
@db_schema.upgrade('imdb_lookup')
def upgrade(ver, session):
# v5 We may have cached bad data due to imdb changes, just wipe everything. GitHub #697
# v6 The association tables were not cleared on the last upgrade, clear again. GitHub #714
# v7 Another layout change cached bad data. GitHub #729
# v8 Added writers to the DB Schema
# v9 Added Metacritic score exftraction/filtering
# v10 Added plot keywords to the DB schema
if ver is None or ver <= 9:
raise UpgradeImpossible(
'Resetting imdb_lookup caches because bad data may have been cached.'
)
return ver