public
Description: Simple to use TVDB (thetvdb.com) API in Python, and automatic TV episode namer
Homepage: http://dbr.lighthouseapp.com/projects/13342-tvdb_api/tickets
Clone URL: git://github.com/dbr/tvdb_api.git
tvdb_api / tvdb_api.py
100644 205 lines (177 sloc) 7.881 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
__author__ = "dbr/Ben"
__version = "0.1"
 
class _Ddict(dict):
    """Lazy-dict, automatically creates multidimensional dicts
by having __getitem__ create sub-dict automatically"""
    def __init__(self, default=None):
        self.default = default
    #end __init__
 
    def __getitem__(self, key):
        if not self.has_key(key):
            self[key] = self.__class__(self.default) # Create sub-instance
        return dict.__getitem__(self, key)
    #end __getitem__
#end _Ddict
 
# Custom exceptions
class tvdb_error(Exception):pass
class tvdb_shownotfound(Exception):pass
class tvdb_userabort(Exception):pass
 
class tvdb:
    """
Create easy-to-use interface to name of season/episode name
>>> i = tvdb()
>>> i['showname']['1']['24']['name']
'Last Episode'
"""
    import urllib
    from BeautifulSoup import BeautifulStoneSoup
    
    def __init__(self,interactive=False,debug=False):
        self.config={}
        self.config['apikey'] = "0629B785CE550C8D"
        # The following url_ configs are based of the http://www.thetvdb.com API documentation
        self.config['url_mirror'] = "http://www.thetvdb.com/api/%s/mirrors.xml" % (self.config['apikey'])
        self.config['url_getSeries'] = "http://www.thetvdb.com/api/GetSeries.php?seriesname=%s"
        self.config['url_epInfo'] = "http://www.thetvdb.com/api/%s/series/%%s/all/" % (self.config['apikey'])
        
        self.config['interactive'] = interactive # prompt for correct series if needed
        
        self.config['debug_enabled'] = debug # show debugging messages
        self.config['debug_tofile'] = False
        self.config['debug_filename'] = "tvdb.log"
        self.config['debug_path'] = '.'
        
        self.log = self.initLogger() # Setups the logger (self.log.debug() etc)
        self.shows = {} # Holds all show data in shows[show_id] = dict of ep data
        self.corrections = {} # Holds show-name to show_id mapping
        
        # Config setup. Grab TVDB mirrors
        self.mirrors = self._getMirrors() # TODO: Apply random mirror urls (Minor: Currently 1 mirror)
    #end __init__
    
    def initLogger(self):
        import os,logging,sys
        logdir = os.path.expanduser( self.config['debug_path'] )
        logpath = os.path.join(logdir,self.config['debug_filename'])
        
        logger = logging.getLogger("tvdb")
        formatter = logging.Formatter('%(asctime)s) %(levelname)s %(message)s')
        
        if self.config['debug_tofile']:
            hdlr = logging.FileHandler(logpath)
        else:
            hdlr = logging.StreamHandler(sys.stdout)
        #end if debug_tofile
        
        hdlr.setFormatter(formatter)
        logger.addHandler(hdlr)
        
        if self.config['debug_enabled']:
            logger.setLevel(logging.DEBUG)
        else:
            logger.setLevel(logging.INFO)
        return logger
    #end initLogger
    
    def _getsoupsrc(self,url):
        self.log.debug('Retriving URL %s' % (url.replace(" ","+")))
        
        url=url.replace(" ","+")
        try:
            src=self.urllib.urlopen(url).read()
        except IOError,errormsg:
            raise tvdb_error("Could not connect to server: %s\n" % (errormsg))
        #end try
        soup=self.BeautifulStoneSoup(src)
        return soup
    #end _getsoupsrc
    
    def _getMirrors(self):
        mirrorSoup=self._getsoupsrc( self.config['url_mirror'] )
        mirrors=[]
        for mirror in mirrorSoup.findAll('mirror'):
            self.log.debug('Found mirror %s' % (mirror))
            
            mirrors.append(
                mirror.find('mirrorpath').contents[0]
            )
        #end for mirror
        self.log.debug('Found total of %s mirrors' % (len(mirrors)))
        return mirrors
    #end _getMirrors
 
    def _getSeries(self,series):
        seriesSoup = self._getsoupsrc( self.config['url_getSeries'] % (series) )
        allSeries=[]
        for series in seriesSoup.findAll('series'):
            cur_name = series.find('seriesname').contents[0]
            cur_sid = series.find('id').contents[0]
            self.log.debug('Found series %s (id: %s)' % (cur_name,cur_sid))
            allSeries.append( {'sid':cur_sid, 'name':cur_name} )
        #end for series
        
        if len(allSeries) == 0:
            self.log.debug('Series result returned zero')
            raise tvdb_shownotfound("Show-name search returned zero results")
        
        if self.config['interactive']:
            self.log.debug('Interactivily selecting show')
            for i in range(len(allSeries[:6])):
                i_show = i + 1 # Start at more human readable number 1 (not zero)
                self.log.debug( 'Showing allSeries[%s] = %s)' % (i_show,allSeries[i]) )
                print "%s -> %s (tvdb id: %s)" % (i_show,allSeries[i]['name'],allSeries[i]['sid'])
            print "Enter choice (first number):"
            ans=raw_input()
            self.log.debug( 'Got choice of: %s' % (ans))
            try:
                selected_id = int(ans) - 1 # The human entered 1 as first result, not zero
                self.log.debug( 'Trying to return ID: %d' % (selected_id))
                return allSeries[ selected_id ]
            except ValueError: # Input was not number
                if ans == "q":
                    self.log.debug('Got quit command (q)')
                    raise tvdb_userabort("User aborted")
                else:
                    self.log.debug('Unknown keypress %s' % (ans))
                    raise tvdb_userabort("Invalid keypress") # TODO: Better UI
            #end for k,v
        else:
            self.log.debug('Auto-selecting first search result')
            return allSeries[0]
    #end _getSeries
 
    def _getEps(self,sid):
        self.log.debug('Getting all episodes of %s' % (sid))
        epsSoup=self._getsoupsrc( self.config['url_epInfo']% (sid) )
        for ep in epsSoup.findAll('episode'):
            ep_no = int( ep.find('episodenumber').contents[0] )
            seas_no = int( ep.find('seasonnumber').contents[0] )
            ep_name = str( ep.find('episodename').contents[0] )
            
            self.shows[sid][seas_no][ep_no] = {'name':ep_name}
        #end for ep
    #end _geEps
    
    def _nameToSid(self,name):
        """
Takes show name, returns the correct series ID (if the show has
already been grabbed), or grabs all episodes and returns
the correct SID.
"""
        if self.corrections.has_key(name):
            self.log.debug('Correcting %s to %s' % (name,self.corrections[name]) )
            sid = self.corrections[name]
        else:
            self.log.debug('Getting show %s' % (name))
            selected_series = self._getSeries( name )
            sname, sid = selected_series['name'], selected_series['sid']
            self.log.debug( "Got %s, sid %s" % (sname,sid) )
            self.shows[sid] = _Ddict(dict)
            self.shows[sid]['showname'] = sname
            self.corrections[name] = sid
            self._getEps( sid )
        #end if self.corrections.has_key
        return sid
    #end _nameToSid
 
    def __getitem__(self,key):
        """
Handles tvdb_instance['showname'] calls.
The dict index should be the show name
"""
        key=key.lower() # make key lower case
        sid = self._nameToSid(key)
        self.log.debug('Got series id %s' % (sid))
        return dict.__getitem__(self.shows, sid)
    #end __getitem__
    
    def __setitem__(self,key,value):
        self.log.debug('Setting %s = %s' % (key,value))
        self.shows[key] = value
    #end __getitem__
    def __str__(self):
        return str(self.shows) #TODO: Improve this
    #end __str__
#end tvdb
 
if __name__ == '__main__':
    x=tvdb(interactive=True,debug=True)
    print x['lost'][1][4]
    print x['Lost'][1][4]