Small adjustments to the settings dialog
[smewt:smewt.git] / smewt / guessers / thetvdbapi.py
1 # -*- coding: utf-8 -*-
2 """
3 thetvdb.com Python API
4 (c) 2009 James Smith (http://loopj.com)
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 """
19
20 import urllib
21 import datetime
22 import re
23
24 import xml.etree.cElementTree as ET
25
26 class TheTVDB(object):
27     def __init__(self, api_key):
28         self.api_key = api_key
29         self.mirror_url = "http://www.thetvdb.com"
30         self.base_url =  self.mirror_url + "/api"
31         self.base_key_url = "%s/%s" % (self.base_url, self.api_key)
32
33     class Show(object):
34         """A python object representing a thetvdb.com show record."""
35         def __init__(self, node, mirror_url):
36             # Main show details
37             self.id = node.findtext("id")
38             self.name = node.findtext("SeriesName")
39             self.overview = node.findtext("Overview")
40             self.genre = [g for g in node.findtext("Genre").split("|") if g]
41             self.actors = [a for a in node.findtext("Actors").split("|") if a]
42             self.network = node.findtext("Network")
43             self.content_rating = node.findtext("ContentRating")
44             self.rating = node.findtext("Rating")
45             self.runtime = node.findtext("Runtime")
46             self.status = node.findtext("Status")
47             self.language = node.findtext("Language")
48
49             # Air details
50             self.first_aired = TheTVDB.convert_date(node.findtext("FirstAired"))
51             self.airs_day = node.findtext("Airs_DayOfWeek")
52             self.airs_time = TheTVDB.convert_time(node.findtext("Airs_Time"))
53
54             # Main show artwork
55             self.banner_url = "%s/banners/%s" % (mirror_url, node.findtext("banner"))
56             self.poster_url = "%s/banners/%s" % (mirror_url, node.findtext("poster"))
57             self.fanart_url = "%s/banners/%s" % (mirror_url, node.findtext("fanart"))
58
59             # External references
60             self.imdb_id = node.findtext("IMDB_ID")
61             self.tvcom_id = node.findtext("SeriesID")
62             self.zap2it_id = node.findtext("zap2it_id")
63
64             # When this show was last updated
65             self.last_updated = datetime.datetime.fromtimestamp(int(node.findtext("lastupdated")))
66
67         def __str__(self):
68             import pprint
69             return pprint.saferepr(self)
70
71     class Episode(object):
72         """A python object representing a thetvdb.com episode record."""
73         def __init__(self, node, mirror_url):
74             self.id = node.findtext("id")
75             self.show_id = node.findtext("seriesid")
76             self.name = node.findtext("EpisodeName")
77             self.overview = node.findtext("Overview")
78             self.season_number = node.findtext("SeasonNumber")
79             self.episode_number = node.findtext("EpisodeNumber")
80             self.director = node.findtext("Director")
81             self.guest_stars = node.findtext("GuestStars")
82             self.language = node.findtext("Language")
83             self.production_code = node.findtext("ProductionCode")
84             self.rating = node.findtext("Rating")
85             self.writer = node.findtext("Writer")
86
87             # Air date
88             self.first_aired = TheTVDB.convert_date(node.findtext("FirstAired"))
89
90             # DVD Information
91             self.dvd_chapter = node.findtext("DVD_chapter")
92             self.dvd_disc_id = node.findtext("DVD_discid")
93             self.dvd_episode_number = node.findtext("DVD_episodenumber")
94             self.dvd_season = node.findtext("DVD_season")
95
96             # Artwork/screenshot
97             self.image = node.findtext("filename")
98
99             # Episode ordering information (normally for specials)
100             self.airs_after_season = node.findtext("airsafter_season")
101             self.airs_before_season = node.findtext("airsbefore_season")
102             self.airs_before_episode = node.findtext("airsbefore_episode")
103
104             # Unknown
105             self.combined_episode_number = node.findtext("combined_episode_number")
106             self.combined_season = node.findtext("combined_season")
107             self.absolute_number = node.findtext("absolute_number")
108             self.season_id = node.findtext("seasonid")
109             self.ep_img_flag = node.findtext("EpImgFlag")
110
111             # External references
112             self.imdb_id = node.findtext("IMDB_ID")
113
114             # When this episode was last updated
115             self.last_updated = datetime.datetime.fromtimestamp(int(node.findtext("lastupdated")))
116
117         def __str__(self):
118             return repr(self)
119
120     @staticmethod
121     def convert_time(time_string):
122         """Convert a thetvdb time string into a datetime.time object."""
123         time_res = [re.compile(r"\D*(?P<hour>\d{1,2})(?::(?P<minute>\d{2}))?.*(?P<ampm>a|p)m.*", re.IGNORECASE), # 12 hour
124                     re.compile(r"\D*(?P<hour>\d{1,2}):?(?P<minute>\d{2}).*")]                                     # 24 hour
125
126         for r in time_res:
127             m = r.match(time_string)
128             if m:
129                 gd = m.groupdict()
130
131                 if "hour" in gd and "minute" in gd and gd["minute"] and "ampm" in gd:
132                     hour = int(gd["hour"])
133                     if gd["ampm"].lower() == "p":
134                         hour += 12
135
136                     return datetime.time(hour, int(gd["minute"]))
137                 elif "hour" in gd and "ampm" in gd:
138                     hour = int(gd["hour"])
139                     if gd["ampm"].lower() == "p":
140                         hour += 12
141
142                     return datetime.time(hour, 0)
143                 elif "hour" in gd and "minute" in gd:
144                     return datetime.time(int(gd["hour"]), int(gd["minute"]))
145
146         return None
147
148     @staticmethod
149     def convert_date(date_string):
150         """Convert a thetvdb date string into a datetime.date object."""
151         first_aired = None
152         try:
153             first_aired = datetime.date(*map(int, date_string.split("-")))
154         except ValueError:
155             pass
156
157         return first_aired
158
159     def get_matching_shows(self, show_name):
160         """Get a list of shows matching show_name."""
161         get_args = urllib.urlencode({"seriesname": show_name}, doseq=True)
162         url = "%s/GetSeries.php?%s&language=all" % (self.base_url, get_args)
163         data = urllib.urlopen(url)
164         show_list = []
165
166         if data:
167             try:
168                 tree = ET.parse(data)
169                 show_list = [(show.findtext("seriesid"), show.findtext("SeriesName"), show.findtext("language")) for show in tree.getiterator("Series")]
170             except SyntaxError:
171                 pass
172
173         return show_list
174
175     def get_show(self, show_id, language='en'):
176         """Get the show object matching this show_id."""
177         url = "%s/series/%s/%s.xml" % (self.base_key_url, show_id, language)
178         data = urllib.urlopen(url)
179
180         show = None
181         try:
182             tree = ET.parse(data)
183             show_node = tree.find("Series")
184
185             show = TheTVDB.Show(show_node, self.mirror_url)
186         except SyntaxError:
187             pass
188
189         return show
190
191     def get_episode(self, episode_id, language='en'):
192         """Get the episode object matching this episode_id."""
193         url = "%s/episodes/%s/%s.xml" % (self.base_key_url, episode_id, language)
194         data = urllib.urlopen(url)
195
196         episode = None
197         try:
198             tree = ET.parse(data)
199             episode_node = tree.find("Episode")
200
201             episode = TheTVDB.Episode(episode_node, self.mirror_url)
202         except SyntaxError:
203             pass
204
205         return episode
206
207     def get_show_and_episodes(self, show_id, language='en'):
208         """Get the show object and all matching episode objects for this show_id."""
209         url = "%s/series/%s/all/%s.xml" % (self.base_key_url, show_id, language)
210         data = urllib.urlopen(url)
211
212         show_and_episodes = None
213         try:
214             tree = ET.parse(data)
215             show_node = tree.find("Series")
216
217             show = TheTVDB.Show(show_node, self.mirror_url)
218             episodes = []
219
220             episode_nodes = tree.getiterator("Episode")
221             for episode_node in episode_nodes:
222                 episodes.append(TheTVDB.Episode(episode_node, self.mirror_url))
223
224             show_and_episodes = (show, episodes)
225         except SyntaxError:
226             pass
227
228         return show_and_episodes
229
230     def get_updated_shows(self, period = "day"):
231         """Get a list of show ids which have been updated within this period."""
232         url = "%s/updates/updates_%s.xml" % (self.base_key_url, period)
233         data = urllib.urlopen(url)
234         tree = ET.parse(data)
235
236         series_nodes = tree.getiterator("Series")
237
238         return [x.findtext("id") for x in series_nodes]
239
240     def get_updated_episodes(self, period = "day"):
241         """Get a list of episode ids which have been updated within this period."""
242         url = "%s/updates/updates_%s.xml" % (self.base_key_url, period)
243         data = urllib.urlopen(url)
244         tree = ET.parse(data)
245
246         episode_nodes = tree.getiterator("Episode")
247
248         return [(x.findtext("Series"), x.findtext("id")) for x in episode_nodes]
249
250     def get_show_image_choices(self, show_id):
251         """Get a list of image urls and types relating to this show."""
252         url = "%s/series/%s/banners.xml" % (self.base_key_url, show_id)
253         data = urllib.urlopen(url)
254         tree = ET.parse(data)
255
256         images = []
257
258         banner_data = tree.find("Banners")
259         banner_nodes = tree.getiterator("Banner")
260         for banner in banner_nodes:
261             banner_path = banner.findtext("BannerPath")
262             banner_type = banner.findtext("BannerType")
263             banner_url = "%s/banners/%s" % (self.mirror_url, banner_path)
264
265             images.append((banner_url, banner_type))
266
267         return images