""" thetvdb.com Python API (c) 2009 James Smith (http://loopj.com) This program 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 2 of the License, or (at your option) any later version. This program 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 this program. If not, see . """ import urllib import datetime import re import xml.etree.cElementTree as ET class TheTVDB(object): def __init__(self, api_key): self.api_key = api_key self.mirror_url = "http://www.thetvdb.com" self.base_url = self.mirror_url + "/api" self.base_key_url = "%s/%s" % (self.base_url, self.api_key) class Show(object): """A python object representing a thetvdb.com show record.""" def __init__(self, node, mirror_url): # Main show details self.id = node.findtext("id") self.name = node.findtext("SeriesName") self.overview = node.findtext("Overview") self.genre = [g for g in node.findtext("Genre").split("|") if g] self.actors = [a for a in node.findtext("Actors").split("|") if a] self.network = node.findtext("Network") self.content_rating = node.findtext("ContentRating") self.rating = node.findtext("Rating") self.runtime = node.findtext("Runtime") self.status = node.findtext("Status") self.language = node.findtext("Language") # Air details self.first_aired = TheTVDB.convert_date(node.findtext("FirstAired")) self.airs_day = node.findtext("Airs_DayOfWeek") self.airs_time = TheTVDB.convert_time(node.findtext("Airs_Time")) # Main show artwork self.banner_url = "%s/banners/%s" % (mirror_url, node.findtext("banner")) self.poster_url = "%s/banners/%s" % (mirror_url, node.findtext("poster")) self.fanart_url = "%s/banners/%s" % (mirror_url, node.findtext("fanart")) # External references self.imdb_id = node.findtext("IMDB_ID") self.tvcom_id = node.findtext("SeriesID") self.zap2it_id = node.findtext("zap2it_id") # When this show was last updated self.last_updated = datetime.datetime.fromtimestamp(int(node.findtext("lastupdated"))) def __str__(self): import pprint return pprint.saferepr(self) class Episode(object): """A python object representing a thetvdb.com episode record.""" def __init__(self, node, mirror_url): self.id = node.findtext("id") self.show_id = node.findtext("seriesid") self.name = node.findtext("EpisodeName") self.overview = node.findtext("Overview") self.season_number = node.findtext("SeasonNumber") self.episode_number = node.findtext("EpisodeNumber") self.director = node.findtext("Director") self.guest_stars = node.findtext("GuestStars") self.language = node.findtext("Language") self.production_code = node.findtext("ProductionCode") self.rating = node.findtext("Rating") self.writer = node.findtext("Writer") # Air date self.first_aired = TheTVDB.convert_date(node.findtext("FirstAired")) # DVD Information self.dvd_chapter = node.findtext("DVD_chapter") self.dvd_disc_id = node.findtext("DVD_discid") self.dvd_episode_number = node.findtext("DVD_episodenumber") self.dvd_season = node.findtext("DVD_season") # Artwork/screenshot self.image = node.findtext("filename") # Episode ordering information (normally for specials) self.airs_after_season = node.findtext("airsafter_season") self.airs_before_season = node.findtext("airsbefore_season") self.airs_before_episode = node.findtext("airsbefore_episode") # Unknown self.combined_episode_number = node.findtext("combined_episode_number") self.combined_season = node.findtext("combined_season") self.absolute_number = node.findtext("absolute_number") self.season_id = node.findtext("seasonid") self.ep_img_flag = node.findtext("EpImgFlag") # External references self.imdb_id = node.findtext("IMDB_ID") # When this episode was last updated self.last_updated = datetime.datetime.fromtimestamp(int(node.findtext("lastupdated"))) def __str__(self): return repr(self) @staticmethod def convert_time(time_string): """Convert a thetvdb time string into a datetime.time object.""" time_res = [re.compile(r"\D*(?P\d{1,2})(?::(?P\d{2}))?.*(?Pa|p)m.*", re.IGNORECASE), # 12 hour re.compile(r"\D*(?P\d{1,2}):?(?P\d{2}).*")] # 24 hour for r in time_res: m = r.match(time_string) if m: gd = m.groupdict() if "hour" in gd and "minute" in gd and gd["minute"] and "ampm" in gd: hour = int(gd["hour"]) if gd["ampm"].lower() == "p": hour += 12 return datetime.time(hour, int(gd["minute"])) elif "hour" in gd and "ampm" in gd: hour = int(gd["hour"]) if gd["ampm"].lower() == "p": hour += 12 return datetime.time(hour, 0) elif "hour" in gd and "minute" in gd: return datetime.time(int(gd["hour"]), int(gd["minute"])) return None @staticmethod def convert_date(date_string): """Convert a thetvdb date string into a datetime.date object.""" first_aired = None try: first_aired = datetime.date(*map(int, date_string.split("-"))) except ValueError: pass return first_aired def get_matching_shows(self, show_name): """Get a list of shows matching show_name.""" get_args = urllib.urlencode({"seriesname": show_name}, doseq=True) url = "%s/GetSeries.php?%s" % (self.base_url, get_args) data = urllib.urlopen(url) show_list = [] if data: try: tree = ET.parse(data) show_list = [(show.findtext("seriesid"), show.findtext("SeriesName")) for show in tree.getiterator("Series")] except SyntaxError: pass return show_list def get_show(self, show_id): """Get the show object matching this show_id.""" url = "%s/series/%s/" % (self.base_key_url, show_id) data = urllib.urlopen(url) show = None try: tree = ET.parse(data) show_node = tree.find("Series") show = TheTVDB.Show(show_node, self.mirror_url) except SyntaxError: pass return show def get_episode(self, episode_id): """Get the episode object matching this episode_id.""" url = "%s/episodes/%s/" % (self.base_key_url, episode_id) data = urllib.urlopen(url) episode = None try: tree = ET.parse(data) episode_node = tree.find("Episode") episode = TheTVDB.Episode(episode_node, self.mirror_url) except SyntaxError: pass return episode def get_show_and_episodes(self, show_id): """Get the show object and all matching episode objects for this show_id.""" url = "%s/series/%s/all/" % (self.base_key_url, show_id) data = urllib.urlopen(url) show_and_episodes = None try: tree = ET.parse(data) show_node = tree.find("Series") show = TheTVDB.Show(show_node, self.mirror_url) episodes = [] episode_nodes = tree.getiterator("Episode") for episode_node in episode_nodes: episodes.append(TheTVDB.Episode(episode_node, self.mirror_url)) show_and_episodes = (show, episodes) except SyntaxError: pass return show_and_episodes def get_updated_shows(self, period = "day"): """Get a list of show ids which have been updated within this period.""" url = "%s/updates/updates_%s.xml" % (self.base_key_url, period) data = urllib.urlopen(url) tree = ET.parse(data) series_nodes = tree.getiterator("Series") return [x.findtext("id") for x in series_nodes] def get_updated_episodes(self, period = "day"): """Get a list of episode ids which have been updated within this period.""" url = "%s/updates/updates_%s.xml" % (self.base_key_url, period) data = urllib.urlopen(url) tree = ET.parse(data) episode_nodes = tree.getiterator("Episode") return [(x.findtext("Series"), x.findtext("id")) for x in episode_nodes] def get_show_image_choices(self, show_id): """Get a list of image urls and types relating to this show.""" url = "%s/series/%s/banners.xml" % (self.base_key_url, show_id) data = urllib.urlopen(url) tree = ET.parse(data) images = [] banner_data = tree.find("Banners") banner_nodes = tree.getiterator("Banner") for banner in banner_nodes: banner_path = banner.findtext("BannerPath") banner_type = banner.findtext("BannerType") banner_url = "%s/banners/%s" % (self.mirror_url, banner_path) images.append((banner_url, banner_type)) return images