"""Movie module."""
# pylint: disable=too-many-public-methods
# pylint: disable=too-many-instance-attributes
import typing
from lxml import etree
from mymeco.files.video import Video
RatingType = typing.Mapping[str, typing.Union[int, bool]]
ActorType = typing.Mapping[str, typing.Union[str, int]]
[docs]
class Movie:
"""
Movie class.
Aim to group all information that could be stored in a movie entry of Kodi
NFO files, bind to an xml formatter.
"""
def __init__(self, movieid=None, tmdb=None):
"""Init a new movie data structure."""
self.__data = {}
if tmdb is not None and movieid is not None:
self.__fill_data_from_tmdb(tmdb, movieid)
def __fill_data_from_tmdb(self, tmdb, movieid):
staffs = tmdb.get_credits(movieid)
details = tmdb.get_movie(movieid)
self.title = details.get('title', None)
self.originaltitle = details.get('original_title', None)
self.rating('tmdb',
details.get('vote_average', None),
votes=details.get('vote_count', None), default=True)
self.plot = details.get('overview', None)
self.tagline = details.get('tagline', None)
self.runtime = str(details.get('runtime', None))
self.poster = details.get('poster_path', None)
self.add_uniqueid('tmdb', str(movieid))
self.add_uniqueid('imdb', details.get('imdb_id', None))
for genre in details.get('genres', []):
self.add_genre(genre['name'])
for country in details.get('production_countries', []):
self.add_country(country['name'])
# print(set(staff['department'] for staff in staffs['crew']))
# print(set(
# staff['job']
# for staff in staffs['crew']
# if staff['department'] == 'Directing'
# ))
for staff in staffs['crew']:
if staff['department'] == 'Writing':
self.add_writter(staff['name'])
if (
staff['department'] == 'Directing' and
staff['job'] == 'Director'
):
self.add_director(staff['name'])
self.premiered = details.get('release_date', None)
for studio in details.get('production_companies', []):
self.add_studio(studio['name'])
for staff in staffs['cast']:
self.add_actor(
staff['name'],
staff['character'],
staff['order'],
staff['profile_path']
)
@property
def title(self) -> typing.Optional[str]:
"""Get localized title of movie."""
return self.__data.get('title', None)
@title.setter
def title(self, title: str):
if title is not None:
self.__data['title'] = title
@property
def originaltitle(self) -> typing.Optional[str]:
"""Get original title of movie."""
return self.__data.get('originaltitle', self.title)
@originaltitle.setter
def originaltitle(self, originaltitle: str):
if originaltitle is not None:
self.__data['originaltitle'] = originaltitle
@property
def sorttitle(self) -> typing.Optional[str]:
"""Get sortable title of movie."""
return self.__data.get('sorttitle', self.title)
@sorttitle.setter
def sorttitle(self, sorttitle: str):
if sorttitle is not None:
self.__data['sorttitle'] = sorttitle
@property
def ratings(self) -> typing.Mapping[str, RatingType]:
"""Get list of all ratings associated to this movie."""
return self.__data['ratings']
[docs]
def rating(self, name: str, values: int, **kwargs: RatingType):
"""Store a new rating for given movie."""
if values is None:
return
if 'ratings' not in self.__data:
self.__data['ratings'] = {}
self.__data['ratings'][name] = {
'values': values,
'votes': kwargs.get('votes', None),
'max': kwargs.get('max', 10),
'default': kwargs.get('default', False)
}
@property
def plot(self) -> typing.Optional[str]:
"""Get movie plot."""
return self.__data.get('plot', None)
@plot.setter
def plot(self, plot):
if plot is not None:
self.__data['plot'] = plot
@property
def tagline(self):
"""Get movie tagline."""
return self.__data.get('tagline', None)
@tagline.setter
def tagline(self, tagline):
if tagline is not None:
self.__data['tagline'] = tagline
@property
def runtime(self) -> typing.Optional[int]:
"""Get runtime in minutes."""
return self.__data.get('runtime', None)
@runtime.setter
def runtime(self, minutes: typing.Optional[int]):
if minutes is not None:
self.__data['runtime'] = minutes
@property
def poster(self) -> typing.Optional[str]:
"""Get movie poster URL."""
return self.__data.get('thumb', {'poster': None}).get('poster', None)
@poster.setter
def poster(self, poster: typing.Optional[str]):
if poster is None:
return
if 'thumb' not in self.__data:
self.__data['thumb'] = {}
self.__data['thumb']['poster'] = poster
@property
def uniqueid(self) -> typing.Optional[typing.Mapping[str, str]]:
"""Retrieve list of unique ids."""
return self.__data.get('uniqueid', None)
[docs]
def add_uniqueid(self, idtype: str, value: str):
"""Add a new uniqueid in movie."""
if value is None:
return
if 'uniqueid' not in self.__data:
self.__data['uniqueid'] = {}
self.__data['uniqueid'][idtype] = value
@property
def genre(self) -> typing.Iterable[str]:
"""Get list of genres."""
return self.__data.get('genre', {})
[docs]
def add_genre(self, genre: str):
"""Add new genre to movie."""
if genre is not None:
if 'genre' not in self.__data:
self.__data['genre'] = {genre}
else:
self.__data['genre'].add(genre)
@property
def country(self) -> typing.Iterable[str]:
"""Get list of production countries."""
return self.__data.get('country', {})
[docs]
def add_country(self, country: str):
"""Add new production country."""
if country is not None:
if 'country' not in self.__data:
self.__data['country'] = {country}
else:
self.__data['country'].add(country)
@property
def writter(self) -> typing.Iterable[str]:
"""Get list of writter."""
return self.__data.get('credits', {})
[docs]
def add_writter(self, writter: str):
"""Add new writter."""
if writter is not None:
if 'credits' not in self.__data:
self.__data['credits'] = {writter}
else:
self.__data['credits'].add(writter)
@property
def director(self) -> typing.Iterable[str]:
"""Get list of director."""
return self.__data.get('director', {})
[docs]
def add_director(self, director: str):
"""Add new director."""
if director is not None:
if 'director' not in self.__data:
self.__data['director'] = {director}
else:
self.__data['director'].add(director)
@property
def premiered(self) -> typing.Optional[str]:
"""Get premierered date."""
return self.__data.get('premiered', None)
@premiered.setter
def premiered(self, release_date: typing.Optional[str]):
if release_date is not None:
self.__data['premiered'] = release_date
@property
def studio(self) -> typing.Sequence[str]:
"""Get production studios."""
return self.__data.get('studio', {})
[docs]
def add_studio(self, studio: typing.Optional[str]):
"""Add new studio."""
if studio is not None:
if 'studio' in self.__data:
self.__data['studio'].add(studio)
else:
self.__data['studio'] = {studio}
@property
def actors(self) -> typing.Sequence[ActorType]:
"""Get actors."""
return self.__data.get('actor', [])
[docs]
def add_actor(self, name: str, role: str, order: int, thumb: str):
"""Add new actor."""
if 'actor' not in self.__data:
self.__data['actor'] = []
self.__data['actor'].append({
'name': name,
'role': role,
'order': order,
'thumb': thumb
})
@property
def technical(self):
"""Retrieve technical information on movie file."""
return self.__data.get('fileinfo', {})
[docs]
def set_technical(self, video: Video):
"""Add technical information."""
self.__data['fileinfo'] = {
'streamdetails': {
'video': video.video,
'audio': video.audio,
'subtitle': video.subtitle
}
}
[docs]
def nfo(self, filehandle):
"""Write NFO data in stream-compatible object."""
root = etree.Element('movie')
dispatch = {
'ratings': self._handle_ratings,
'thumb': self._handle_thumb,
'uniqueid': self._handle_uniqueid,
'actor': self._handle_actors,
'fileinfo': self._handle_fileinfo,
}
for key, value in self.__data.items():
if key in dispatch:
dispatch[key](root, value)
continue
if isinstance(value, (str, int)):
element = etree.SubElement(root, key)
element.text = value
continue
if isinstance(value, (list, set)):
for item in value:
element = etree.SubElement(root, key)
element.text = item
continue
filehandle.write(etree.tounicode(root, pretty_print=True))
@staticmethod
def _handle_ratings(movie, values):
element = etree.SubElement(movie, 'ratings')
for name, content in values.items():
rating = etree.SubElement(element, 'rating')
rating.attrib['name'] = name
if 'max' in content:
rating.attrib['max'] = str(content['max'])
if 'default' in content:
rating.attrib['default'] = str(content['default'])
value = etree.SubElement(rating, 'value')
value.text = str(content['values'])
if 'votes' in content:
votes = etree.SubElement(rating, 'votes')
votes.text = str(content['votes'])
@staticmethod
def _handle_thumb(movie, values):
for aspect, url in values.items():
thumb = etree.SubElement(movie, 'thumb', aspect=aspect)
thumb.text = url
@staticmethod
def _handle_uniqueid(movie, values):
default = True
for idtype, value in values.items():
uniqueid = etree.SubElement(
movie, 'uniqueid', type=idtype, default=str(default)
)
uniqueid.text = value
default = False
@staticmethod
def _handle_actors(movie, values):
for actor in values:
element = etree.SubElement(movie, 'actor')
for item in ('name', 'role', 'order', 'thumb'):
subelement = etree.SubElement(element, item)
subelement.text = str(actor[item])
@staticmethod
def _handle_fileinfo(movie, values):
fileinfo = etree.SubElement(movie, 'fileinfo')
streamdetails = etree.SubElement(fileinfo, 'streamdetails')
for video in values['streamdetails']['video']:
stream = etree.SubElement(streamdetails, 'video')
for item in (
'codec', 'aspect', 'width', 'height', 'durationinseconds'
):
etree.SubElement(stream, item).text = str(video[item])
for audio in values['streamdetails']['audio']:
stream = etree.SubElement(streamdetails, 'audio')
for item in (
'codec', 'language', 'channels'
):
etree.SubElement(stream, item).text = str(audio[item])
for sub in values['streamdetails']['subtitle']:
stream = etree.SubElement(streamdetails, 'subtitle')
for item in ('language',):
etree.SubElement(stream, item).text = str(sub[item])