mirror of
https://github.com/SickGear/SickGear.git
synced 2024-12-01 00:43:37 +00:00
bd8f0a7b61
Change consolidate show data validator into base class. Change move validator test to inside the respective tvinfo source getter for future proofing.
367 lines
14 KiB
Python
367 lines
14 KiB
Python
# Author: Nic Wolfe <nic@wolfeden.ca>
|
|
# URL: http://code.google.com/p/sickbeard/
|
|
#
|
|
# This file is part of SickGear.
|
|
#
|
|
# SickGear is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# SickGear is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import datetime
|
|
|
|
import sickbeard
|
|
|
|
import mediabrowser
|
|
|
|
from sickbeard import logger, exceptions, helpers
|
|
from sickbeard.exceptions import ex
|
|
|
|
try:
|
|
import xml.etree.cElementTree as etree
|
|
except ImportError:
|
|
import elementtree.ElementTree as etree
|
|
|
|
|
|
class Mede8erMetadata(mediabrowser.MediaBrowserMetadata):
|
|
"""
|
|
Metadata generation class for Mede8er based on the MediaBrowser.
|
|
|
|
The following file structure is used:
|
|
|
|
show_root/series.xml (show metadata)
|
|
show_root/folder.jpg (poster)
|
|
show_root/fanart.jpg (fanart)
|
|
show_root/Season ##/folder.jpg (season thumb)
|
|
show_root/Season ##/filename.ext (*)
|
|
show_root/Season ##/filename.xml (episode metadata)
|
|
show_root/Season ##/filename.jpg (episode thumb)
|
|
"""
|
|
|
|
def __init__(self,
|
|
show_metadata=False,
|
|
episode_metadata=False,
|
|
fanart=False,
|
|
poster=False,
|
|
banner=False,
|
|
episode_thumbnails=False,
|
|
season_posters=False,
|
|
season_banners=False,
|
|
season_all_poster=False,
|
|
season_all_banner=False):
|
|
|
|
mediabrowser.MediaBrowserMetadata.__init__(self,
|
|
show_metadata,
|
|
episode_metadata,
|
|
fanart,
|
|
poster,
|
|
banner,
|
|
episode_thumbnails,
|
|
season_posters,
|
|
season_banners,
|
|
season_all_poster,
|
|
season_all_banner)
|
|
|
|
self.name = "Mede8er"
|
|
|
|
self.fanart_name = "fanart.jpg"
|
|
|
|
# web-ui metadata template
|
|
# self.eg_show_metadata = "series.xml"
|
|
self.eg_episode_metadata = "Season##\\<i>filename</i>.xml"
|
|
self.eg_fanart = "fanart.jpg"
|
|
# self.eg_poster = "folder.jpg"
|
|
# self.eg_banner = "banner.jpg"
|
|
self.eg_episode_thumbnails = "Season##\\<i>filename</i>.jpg"
|
|
# self.eg_season_posters = "Season##\\folder.jpg"
|
|
# self.eg_season_banners = "Season##\\banner.jpg"
|
|
# self.eg_season_all_poster = "<i>not supported</i>"
|
|
# self.eg_season_all_banner = "<i>not supported</i>"
|
|
|
|
def get_episode_file_path(self, ep_obj):
|
|
return helpers.replaceExtension(ep_obj.location, self._ep_nfo_extension)
|
|
|
|
def get_episode_thumb_path(self, ep_obj):
|
|
return helpers.replaceExtension(ep_obj.location, 'jpg')
|
|
|
|
def _show_data(self, show_obj):
|
|
"""
|
|
Creates an elementTree XML structure for a MediaBrowser-style series.xml
|
|
returns the resulting data object.
|
|
|
|
show_obj: a TVShow instance to create the NFO for
|
|
"""
|
|
|
|
indexer_lang = show_obj.lang
|
|
lINDEXER_API_PARMS = sickbeard.indexerApi(show_obj.indexer).api_params.copy()
|
|
|
|
lINDEXER_API_PARMS['actors'] = True
|
|
|
|
if indexer_lang and not indexer_lang == 'en':
|
|
lINDEXER_API_PARMS['language'] = indexer_lang
|
|
|
|
if show_obj.dvdorder != 0:
|
|
lINDEXER_API_PARMS['dvdorder'] = True
|
|
|
|
t = sickbeard.indexerApi(show_obj.indexer).indexer(**lINDEXER_API_PARMS)
|
|
|
|
rootNode = etree.Element("details")
|
|
tv_node = etree.SubElement(rootNode, "movie")
|
|
tv_node.attrib["isExtra"] = "false"
|
|
tv_node.attrib["isSet"] = "false"
|
|
tv_node.attrib["isTV"] = "true"
|
|
|
|
try:
|
|
myShow = t[int(show_obj.indexerid)]
|
|
except sickbeard.indexer_shownotfound:
|
|
logger.log(u"Unable to find show with id " + str(show_obj.indexerid) + " on tvdb, skipping it", logger.ERROR)
|
|
raise
|
|
|
|
except sickbeard.indexer_error:
|
|
logger.log(u"TVDB is down, can't use its data to make the NFO", logger.ERROR)
|
|
raise
|
|
|
|
if not self._valid_show(myShow, show_obj):
|
|
return
|
|
|
|
# check for title and id
|
|
try:
|
|
if myShow['seriesname'] == None or myShow['seriesname'] == "" or myShow['id'] == None or myShow['id'] == "":
|
|
logger.log(u"Incomplete info for show with id " + str(show_obj.indexerid) + " on tvdb, skipping it", logger.ERROR)
|
|
return False
|
|
except sickbeard.indexer_attributenotfound:
|
|
logger.log(u"Incomplete info for show with id " + str(show_obj.indexerid) + " on tvdb, skipping it", logger.ERROR)
|
|
return False
|
|
|
|
SeriesName = etree.SubElement(tv_node, "title")
|
|
if myShow['seriesname'] != None:
|
|
SeriesName.text = myShow['seriesname']
|
|
else:
|
|
SeriesName.text = ""
|
|
|
|
Genres = etree.SubElement(tv_node, "genres")
|
|
if myShow["genre"] != None:
|
|
for genre in myShow['genre'].split('|'):
|
|
if genre and genre.strip():
|
|
cur_genre = etree.SubElement(Genres, "Genre")
|
|
cur_genre.text = genre.strip()
|
|
|
|
FirstAired = etree.SubElement(tv_node, "premiered")
|
|
if myShow['firstaired'] != None:
|
|
FirstAired.text = myShow['firstaired']
|
|
|
|
year = etree.SubElement(tv_node, "year")
|
|
if myShow["firstaired"] != None:
|
|
try:
|
|
year_text = str(datetime.datetime.strptime(myShow["firstaired"], '%Y-%m-%d').year)
|
|
if year_text:
|
|
year.text = year_text
|
|
except:
|
|
pass
|
|
|
|
if myShow['rating'] != None:
|
|
try:
|
|
rating = int((float(myShow['rating']) * 10))
|
|
except ValueError:
|
|
rating = 0
|
|
Rating = etree.SubElement(tv_node, "rating")
|
|
rating_text = str(rating)
|
|
if rating_text != None:
|
|
Rating.text = rating_text
|
|
|
|
Status = etree.SubElement(tv_node, "status")
|
|
if myShow['status'] != None:
|
|
Status.text = myShow['status']
|
|
|
|
mpaa = etree.SubElement(tv_node, "mpaa")
|
|
if myShow["contentrating"] != None:
|
|
mpaa.text = myShow["contentrating"]
|
|
|
|
IMDB_ID = etree.SubElement(tv_node, "id")
|
|
if myShow['imdb_id'] != None:
|
|
IMDB_ID.attrib["moviedb"] = "imdb"
|
|
IMDB_ID.text = myShow['imdb_id']
|
|
|
|
indexerid = etree.SubElement(tv_node, "indexerid")
|
|
if myShow['id'] != None:
|
|
indexerid.text = myShow['id']
|
|
|
|
Runtime = etree.SubElement(tv_node, "runtime")
|
|
if myShow['runtime'] != None:
|
|
Runtime.text = myShow['runtime']
|
|
|
|
cast = etree.SubElement(tv_node, "cast")
|
|
|
|
if getattr(myShow, '_actors', None) is not None:
|
|
for actor in myShow['_actors']:
|
|
cur_actor_name_text = actor['name']
|
|
|
|
if cur_actor_name_text != None and cur_actor_name_text.strip():
|
|
cur_actor = etree.SubElement(cast, "actor")
|
|
cur_actor.text = cur_actor_name_text.strip()
|
|
|
|
helpers.indentXML(rootNode)
|
|
|
|
data = etree.ElementTree(rootNode)
|
|
|
|
return data
|
|
|
|
def _ep_data(self, ep_obj):
|
|
"""
|
|
Creates an elementTree XML structure for a MediaBrowser style episode.xml
|
|
and returns the resulting data object.
|
|
|
|
show_obj: a TVShow instance to create the NFO for
|
|
"""
|
|
|
|
eps_to_write = [ep_obj] + ep_obj.relatedEps
|
|
|
|
indexer_lang = ep_obj.show.lang
|
|
|
|
try:
|
|
# There's gotta be a better way of doing this but we don't wanna
|
|
# change the language value elsewhere
|
|
lINDEXER_API_PARMS = sickbeard.indexerApi(ep_obj.show.indexer).api_params.copy()
|
|
|
|
if indexer_lang and not indexer_lang == 'en':
|
|
lINDEXER_API_PARMS['language'] = indexer_lang
|
|
|
|
if ep_obj.show.dvdorder != 0:
|
|
lINDEXER_API_PARMS['dvdorder'] = True
|
|
|
|
t = sickbeard.indexerApi(ep_obj.show.indexer).indexer(**lINDEXER_API_PARMS)
|
|
myShow = t[ep_obj.show.indexerid]
|
|
except sickbeard.indexer_shownotfound as e:
|
|
raise exceptions.ShowNotFoundException(e.message)
|
|
except sickbeard.indexer_error as e:
|
|
logger.log(u"Unable to connect to TVDB while creating meta files - skipping - " + ex(e), logger.ERROR)
|
|
return False
|
|
|
|
if not self._valid_show(myShow, ep_obj.show):
|
|
return
|
|
|
|
rootNode = etree.Element("details")
|
|
movie = etree.SubElement(rootNode, "movie")
|
|
|
|
movie.attrib["isExtra"] = "false"
|
|
movie.attrib["isSet"] = "false"
|
|
movie.attrib["isTV"] = "true"
|
|
|
|
# write an MediaBrowser XML containing info for all matching episodes
|
|
for curEpToWrite in eps_to_write:
|
|
|
|
try:
|
|
myEp = myShow[curEpToWrite.season][curEpToWrite.episode]
|
|
except (StandardError, Exception):
|
|
logger.log(u"Unable to find episode " + str(curEpToWrite.season) + "x" + str(curEpToWrite.episode) + " on tvdb... has it been removed? Should I delete from db?")
|
|
return None
|
|
|
|
if curEpToWrite == ep_obj:
|
|
# root (or single) episode
|
|
|
|
# default to today's date for specials if firstaired is not set
|
|
if myEp['firstaired'] == None and ep_obj.season == 0:
|
|
myEp['firstaired'] = str(datetime.date.fromordinal(1))
|
|
|
|
if myEp['episodename'] == None or myEp['firstaired'] == None:
|
|
return None
|
|
|
|
episode = movie
|
|
|
|
EpisodeName = etree.SubElement(episode, "title")
|
|
if curEpToWrite.name != None:
|
|
EpisodeName.text = curEpToWrite.name
|
|
else:
|
|
EpisodeName.text = ""
|
|
|
|
SeasonNumber = etree.SubElement(episode, "season")
|
|
SeasonNumber.text = str(curEpToWrite.season)
|
|
|
|
EpisodeNumber = etree.SubElement(episode, "episode")
|
|
EpisodeNumber.text = str(ep_obj.episode)
|
|
|
|
year = etree.SubElement(episode, "year")
|
|
if myShow["firstaired"] != None:
|
|
try:
|
|
year_text = str(datetime.datetime.strptime(myShow["firstaired"], '%Y-%m-%d').year)
|
|
if year_text:
|
|
year.text = year_text
|
|
except:
|
|
pass
|
|
|
|
plot = etree.SubElement(episode, "plot")
|
|
if myShow["overview"] != None:
|
|
plot.text = myShow["overview"]
|
|
|
|
Overview = etree.SubElement(episode, "episodeplot")
|
|
if curEpToWrite.description != None:
|
|
Overview.text = curEpToWrite.description
|
|
else:
|
|
Overview.text = ""
|
|
|
|
mpaa = etree.SubElement(episode, "mpaa")
|
|
if myShow["contentrating"] != None:
|
|
mpaa.text = myShow["contentrating"]
|
|
|
|
if not ep_obj.relatedEps:
|
|
if myEp["rating"] != None:
|
|
try:
|
|
rating = int((float(myEp['rating']) * 10))
|
|
except ValueError:
|
|
rating = 0
|
|
Rating = etree.SubElement(episode, "rating")
|
|
rating_text = str(rating)
|
|
if rating_text != None:
|
|
Rating.text = rating_text
|
|
|
|
director = etree.SubElement(episode, "director")
|
|
director_text = myEp['director']
|
|
if director_text != None:
|
|
director.text = director_text
|
|
|
|
credits = etree.SubElement(episode, "credits")
|
|
credits_text = myEp['writer']
|
|
if credits_text != None:
|
|
credits.text = credits_text
|
|
|
|
cast = etree.SubElement(episode, "cast")
|
|
|
|
if getattr(myShow, '_actors', None) is not None:
|
|
for actor in myShow['_actors']:
|
|
cur_actor_name_text = actor['name']
|
|
|
|
if cur_actor_name_text != None and cur_actor_name_text.strip():
|
|
cur_actor = etree.SubElement(cast, "actor")
|
|
cur_actor.text = cur_actor_name_text.strip()
|
|
|
|
else:
|
|
# append data from (if any) related episodes
|
|
|
|
if curEpToWrite.name:
|
|
if not EpisodeName.text:
|
|
EpisodeName.text = curEpToWrite.name
|
|
else:
|
|
EpisodeName.text = EpisodeName.text + ", " + curEpToWrite.name
|
|
|
|
if curEpToWrite.description:
|
|
if not Overview.text:
|
|
Overview.text = curEpToWrite.description
|
|
else:
|
|
Overview.text = Overview.text + "\r" + curEpToWrite.description
|
|
|
|
helpers.indentXML(rootNode)
|
|
data = etree.ElementTree(rootNode)
|
|
|
|
return data
|
|
|
|
|
|
# present a standard "interface" from the module
|
|
metadata_class = Mede8erMetadata
|