Source code for mymeco.kodi.movie

"""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])