mirror of
https://github.com/SickGear/SickGear.git
synced 2024-12-13 22:53:36 +00:00
0d9fbc1ad7
This version of SickBeard uses both TVDB and TVRage to search and gather it's series data from allowing you to now have access to and download shows that you couldn't before because of being locked into only what TheTVDB had to offer. Also this edition is based off the code we used in our XEM editon so it does come with scene numbering support as well as all the other features our XEM edition has to offer. Please before using this with your existing database (sickbeard.db) please make a backup copy of it and delete any other database files such as cache.db and failed.db if present, we HIGHLY recommend starting out with no database files at all to make this a fresh start but the choice is at your own risk! Enjoy!
377 lines
15 KiB
Python
377 lines
15 KiB
Python
# URL: http://code.google.com/p/sickbeard/
|
|
#
|
|
# This file is part of Sick Beard.
|
|
#
|
|
# Sick Beard 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.
|
|
#
|
|
# Sick Beard 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 Sick Beard. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import generic
|
|
|
|
import datetime
|
|
|
|
|
|
from sickbeard.indexers import indexer_api, indexer_exceptions
|
|
|
|
import sickbeard
|
|
from sickbeard import logger, exceptions, helpers
|
|
from sickbeard.exceptions import ex
|
|
|
|
import xml.etree.cElementTree as etree
|
|
|
|
|
|
class XBMC_12PlusMetadata(generic.GenericMetadata):
|
|
"""
|
|
Metadata generation class for XBMC 12+.
|
|
|
|
The following file structure is used:
|
|
|
|
show_root/tvshow.nfo (show metadata)
|
|
show_root/fanart.jpg (fanart)
|
|
show_root/poster.jpg (poster)
|
|
show_root/banner.jpg (banner)
|
|
show_root/Season ##/filename.ext (*)
|
|
show_root/Season ##/filename.nfo (episode metadata)
|
|
show_root/Season ##/filename-thumb.jpg (episode thumb)
|
|
show_root/season##-poster.jpg (season posters)
|
|
show_root/season##-banner.jpg (season banners)
|
|
show_root/season-all-poster.jpg (season all poster)
|
|
show_root/season-all-banner.jpg (season all banner)
|
|
"""
|
|
|
|
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):
|
|
|
|
generic.GenericMetadata.__init__(self,
|
|
show_metadata,
|
|
episode_metadata,
|
|
fanart,
|
|
poster,
|
|
banner,
|
|
episode_thumbnails,
|
|
season_posters,
|
|
season_banners,
|
|
season_all_poster,
|
|
season_all_banner)
|
|
|
|
self.name = 'XBMC 12+'
|
|
|
|
self.poster_name = "poster.jpg"
|
|
self.season_all_poster_name = "season-all-poster.jpg"
|
|
|
|
# web-ui metadata template
|
|
self.eg_show_metadata = "tvshow.nfo"
|
|
self.eg_episode_metadata = "Season##\\<i>filename</i>.nfo"
|
|
self.eg_fanart = "fanart.jpg"
|
|
self.eg_poster = "poster.jpg"
|
|
self.eg_banner = "banner.jpg"
|
|
self.eg_episode_thumbnails = "Season##\\<i>filename</i>-thumb.jpg"
|
|
self.eg_season_posters = "season##-poster.jpg"
|
|
self.eg_season_banners = "season##-banner.jpg"
|
|
self.eg_season_all_poster = "season-all-poster.jpg"
|
|
self.eg_season_all_banner = "season-all-banner.jpg"
|
|
|
|
def _show_data(self, show_obj):
|
|
"""
|
|
Creates an elementTree XML structure for an XBMC-style tvshow.nfo and
|
|
returns the resulting data object.
|
|
|
|
show_obj: a TVShow instance to create the NFO for
|
|
"""
|
|
|
|
show_ID = show_obj.indexerid
|
|
|
|
indexer_lang = show_obj.lang
|
|
# There's gotta be a better way of doing this but we don't wanna
|
|
# change the language value elsewhere
|
|
lindexer_api_parms = sickbeard.INDEXER_API_PARMS.copy()
|
|
|
|
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 = indexer_api.indexerApi(actors=True, **lindexer_api_parms)
|
|
|
|
tv_node = etree.Element("tvshow")
|
|
|
|
try:
|
|
myShow = t[int(show_ID)]
|
|
except indexer_exceptions.indexer_shownotfound:
|
|
logger.log(u"Unable to find show with id " + str(show_ID) + " on " + show_obj.indexer + ", skipping it", logger.ERROR)
|
|
raise
|
|
|
|
except indexer_exceptions.indexer_error:
|
|
logger.log(u"" + show_obj.indexer + " is down, can't use its data to add this show", logger.ERROR)
|
|
raise
|
|
|
|
# check for title and id
|
|
try:
|
|
if getattr(myShow, 'seriesname', None) is None or getattr(myShow, 'id') is None:
|
|
logger.log(u"Incomplete info for show with id " + str(show_ID) + " on tvdb, skipping it", logger.ERROR)
|
|
|
|
return False
|
|
except indexer_exceptions.indexer_attributenotfound:
|
|
logger.log(u"Incomplete info for show with id " + str(show_ID) + " on " + show_obj.indexer + ", skipping it", logger.ERROR)
|
|
|
|
return False
|
|
|
|
title = etree.SubElement(tv_node, "title")
|
|
if getattr(myShow, 'seriesname', None) is not None:
|
|
title.text = myShow["seriesname"]
|
|
|
|
rating = etree.SubElement(tv_node, "rating")
|
|
if getattr(myShow, 'rating', None) is not None:
|
|
rating.text = myShow["rating"]
|
|
|
|
year = etree.SubElement(tv_node, "year")
|
|
if getattr(myShow, 'firstaired', None) is not 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(tv_node, "plot")
|
|
if getattr(myShow, 'overview', None) is not None:
|
|
plot.text = myShow["overview"]
|
|
|
|
episodeguide = etree.SubElement(tv_node, "episodeguide")
|
|
episodeguideurl = etree.SubElement(episodeguide, "url")
|
|
episodeguideurl2 = etree.SubElement(tv_node, "episodeguideurl")
|
|
if myShow["id"] != None:
|
|
showurl = sickbeard.TVDB_BASE_URL + '/series/' + myShow["id"] + '/all/en.zip'
|
|
episodeguideurl.text = showurl
|
|
episodeguideurl2.text = showurl
|
|
|
|
mpaa = etree.SubElement(tv_node, "mpaa")
|
|
if getattr(myShow, 'contentrating', None) is not None:
|
|
mpaa.text = myShow["contentrating"]
|
|
|
|
indexerid = etree.SubElement(tv_node, "id")
|
|
if getattr(myShow, 'id', None) is not None:
|
|
indexerid.text = myShow["id"]
|
|
|
|
indexer = etree.SubElement(tv_node, "indexer")
|
|
if show_obj.indexer is not None:
|
|
indexer.text = show_obj.indexer
|
|
|
|
genre = etree.SubElement(tv_node, "genre")
|
|
if getattr(myShow, 'genre', None) is not None:
|
|
if isinstance(myShow["genre"], basestring):
|
|
genre.text = " / ".join(x.strip() for x in myShow["genre"].split('|') if x.strip())
|
|
|
|
premiered = etree.SubElement(tv_node, "premiered")
|
|
if getattr(myShow, 'firstaired', None) is not None:
|
|
premiered.text = myShow["firstaired"]
|
|
|
|
studio = etree.SubElement(tv_node, "studio")
|
|
if getattr(myShow, 'network', None) is not None:
|
|
studio.text = myShow["network"]
|
|
|
|
if getattr(myShow, 'actors', None) is not None:
|
|
for actor in myShow['_actors']:
|
|
cur_actor = etree.SubElement(tv_node, "actor")
|
|
|
|
cur_actor_name = etree.SubElement(cur_actor, "name")
|
|
cur_actor_name_text = actor['name']
|
|
if isinstance(cur_actor_name_text, basestring):
|
|
cur_actor_name.text = cur_actor_name_text.strip()
|
|
|
|
cur_actor_role = etree.SubElement(cur_actor, "role")
|
|
cur_actor_role_text = actor['role']
|
|
if cur_actor_role_text != None:
|
|
cur_actor_role.text = cur_actor_role_text
|
|
|
|
cur_actor_thumb = etree.SubElement(cur_actor, "thumb")
|
|
cur_actor_thumb_text = actor['image']
|
|
if cur_actor_thumb_text != None:
|
|
cur_actor_thumb.text = cur_actor_thumb_text
|
|
|
|
# Make it purdy
|
|
helpers.indentXML(tv_node)
|
|
|
|
data = etree.ElementTree(tv_node)
|
|
|
|
return data
|
|
|
|
def _ep_data(self, ep_obj):
|
|
"""
|
|
Creates an elementTree XML structure for an XBMC-style episode.nfo and
|
|
returns the resulting data object.
|
|
show_obj: a TVEpisode instance to create the NFO for
|
|
"""
|
|
|
|
eps_to_write = [ep_obj] + ep_obj.relatedEps
|
|
|
|
indexer_lang = ep_obj.show.lang
|
|
# There's gotta be a better way of doing this but we don't wanna
|
|
# change the language value elsewhere
|
|
lindexer_api_parms = sickbeard.INDEXER_API_PARMS.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
|
|
|
|
try:
|
|
t = indexer_api.indexerApi(actors=True, **lindexer_api_parms)
|
|
myShow = t[ep_obj.show.indexerid]
|
|
except indexer_exceptions.indexer_shownotfound, e:
|
|
raise exceptions.ShowNotFoundException(e.message)
|
|
except indexer_exceptions.indexer_error, e:
|
|
logger.log(u"Unable to connect to " + ep_obj.show.indexer + " while creating meta files - skipping - " + ex(e), logger.ERROR)
|
|
return
|
|
|
|
if len(eps_to_write) > 1:
|
|
rootNode = etree.Element("xbmcmultiepisode")
|
|
else:
|
|
rootNode = etree.Element("episodedetails")
|
|
|
|
# write an NFO containing info for all matching episodes
|
|
for curEpToWrite in eps_to_write:
|
|
|
|
try:
|
|
myEp = myShow[curEpToWrite.season][curEpToWrite.episode]
|
|
except (indexer_exceptions.indexer_episodenotfound, indexer_exceptions.indexer_seasonnotfound):
|
|
logger.log(u"Unable to find episode " + str(curEpToWrite.season) + "x" + str(curEpToWrite.episode) + " on " + ep_obj.show.indexer + ".. has it been removed? Should I delete from db?")
|
|
return None
|
|
|
|
if getattr(myShow, 'firstaired', None) is not None:
|
|
myEp["firstaired"] = str(datetime.date.fromordinal(1))
|
|
|
|
if getattr(myShow, 'episodename', None) is not None:
|
|
logger.log(u"Not generating nfo because the ep has no title", logger.DEBUG)
|
|
return None
|
|
|
|
logger.log(u"Creating metadata for episode " + str(ep_obj.season) + "x" + str(ep_obj.episode), logger.DEBUG)
|
|
|
|
if len(eps_to_write) > 1:
|
|
episode = etree.SubElement(rootNode, "episodedetails")
|
|
else:
|
|
episode = rootNode
|
|
|
|
title = etree.SubElement(episode, "title")
|
|
if curEpToWrite.name != None:
|
|
title.text = curEpToWrite.name
|
|
|
|
showtitle = etree.SubElement(episode, "showtitle")
|
|
if curEpToWrite.show.name != None:
|
|
showtitle.text = curEpToWrite.show.name
|
|
|
|
season = etree.SubElement(episode, "season")
|
|
season.text = str(curEpToWrite.season)
|
|
|
|
episodenum = etree.SubElement(episode, "episode")
|
|
episodenum.text = str(curEpToWrite.episode)
|
|
|
|
uniqueid = etree.SubElement(episode, "uniqueid")
|
|
uniqueid.text = str(curEpToWrite.indexerid)
|
|
|
|
aired = etree.SubElement(episode, "aired")
|
|
if curEpToWrite.airdate != datetime.date.fromordinal(1):
|
|
aired.text = str(curEpToWrite.airdate)
|
|
else:
|
|
aired.text = ''
|
|
|
|
plot = etree.SubElement(episode, "plot")
|
|
if curEpToWrite.description != None:
|
|
plot.text = curEpToWrite.description
|
|
|
|
runtime = etree.SubElement(episode, "runtime")
|
|
if curEpToWrite.season != 0:
|
|
if getattr(myEp, 'runtime', None) is not None:
|
|
runtime.text = myShow["runtime"]
|
|
|
|
displayseason = etree.SubElement(episode, "displayseason")
|
|
if getattr(myEp, 'airsbefore_season', None) is not None:
|
|
displayseason_text = myEp['airsbefore_season']
|
|
if displayseason_text != None:
|
|
displayseason.text = displayseason_text
|
|
|
|
displayepisode = etree.SubElement(episode, "displayepisode")
|
|
if getattr(myEp, 'airsbefore_episode', None) is not None:
|
|
displayepisode_text = myEp['airsbefore_episode']
|
|
if displayepisode_text != None:
|
|
displayepisode.text = displayepisode_text
|
|
|
|
thumb = etree.SubElement(episode, "thumb")
|
|
thumb_text = getattr(myEp, 'filename', None)
|
|
if thumb_text != None:
|
|
thumb.text = thumb_text
|
|
|
|
watched = etree.SubElement(episode, "watched")
|
|
watched.text = 'false'
|
|
|
|
credits = etree.SubElement(episode, "credits")
|
|
credits_text = getattr(myEp, 'writer', None)
|
|
if credits_text != None:
|
|
credits.text = credits_text
|
|
|
|
director = etree.SubElement(episode, "director")
|
|
director_text = getattr(myEp, 'director', None)
|
|
if director_text != None:
|
|
director.text = director_text
|
|
|
|
rating = etree.SubElement(episode, "rating")
|
|
rating_text = getattr(myEp, 'rating', None)
|
|
if rating_text != None:
|
|
rating.text = rating_text
|
|
|
|
gueststar_text = getattr(myEp, 'gueststars', None)
|
|
if isinstance(gueststar_text, basestring):
|
|
for actor in (x.strip() for x in gueststar_text.split('|') if x.strip()):
|
|
cur_actor = etree.SubElement(episode, "actor")
|
|
cur_actor_name = etree.SubElement(cur_actor, "name")
|
|
cur_actor_name.text = actor
|
|
|
|
if getattr(myEp, '_actors', None) is not None:
|
|
for actor in myShow['_actors']:
|
|
cur_actor = etree.SubElement(episode, "actor")
|
|
|
|
cur_actor_name = etree.SubElement(cur_actor, "name")
|
|
cur_actor_name_text = actor['name']
|
|
if isinstance(cur_actor_name_text, basestring):
|
|
cur_actor_name.text = cur_actor_name_text.strip()
|
|
|
|
cur_actor_role = etree.SubElement(cur_actor, "role")
|
|
cur_actor_role_text = actor['role']
|
|
if cur_actor_role_text != None:
|
|
cur_actor_role.text = cur_actor_role_text
|
|
|
|
cur_actor_thumb = etree.SubElement(cur_actor, "thumb")
|
|
cur_actor_thumb_text = actor['image']
|
|
if cur_actor_thumb_text != None:
|
|
cur_actor_thumb.text = cur_actor_thumb_text
|
|
|
|
# Make it purdy
|
|
helpers.indentXML(rootNode)
|
|
|
|
data = etree.ElementTree(rootNode)
|
|
|
|
return data
|
|
|
|
|
|
# present a standard "interface" from the module
|
|
metadata_class = XBMC_12PlusMetadata
|