diff options
Diffstat (limited to 'anidb')
-rw-r--r-- | anidb/__init__.py | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/anidb/__init__.py b/anidb/__init__.py new file mode 100644 index 0000000..29e9b78 --- /dev/null +++ b/anidb/__init__.py @@ -0,0 +1,139 @@ +import requests +from bs4 import BeautifulSoup + +from datetime import datetime + +BASE_URL = "http://api.anidb.net:9001/httpapi" +SEARCH_URL = "http://anisearch.outrance.pl/" +PROTOCOL_VERSION = 1 + +try: + str = unicode +except: + pass + # str = str + +class AniDB: + def __init__(self, client_id, client_ver=0): + self.client_id = client_id + self.client_ver = client_ver + self._cache = {} + + def _request(self, datapage, params={}, cache=True): + params.update({ + 'client': self.client_id, + 'clientver': self.client_ver, + 'protover': PROTOCOL_VERSION, + 'request': datapage + }) + r = requests.get(BASE_URL, params=params) + r.raise_for_status() + return r + + # Anime http://wiki.anidb.net/w/HTTP_API_Definition#Access + + def search(self, q): + """ + Search for `aid`s by anime title using service provided by eloyard. + http://anisearch.outrance.pl/doc.html + """ + r = requests.get(SEARCH_URL, params={ + 'task': "search", + 'query': q, + }) + r.raise_for_status() + results = [] + animetitles = BeautifulSoup(r.text, 'xml').animetitles + for anime in animetitles.find_all('anime'): + results.append(Anime({ + 'id': int(anime['id']), + 'title': str(anime.find('title', attrs={'type': "main"}).string) + }, partial=True, updater=lambda: self.get(anime['id']))) + + return results + + def get(self, id): + """ + Allows retrieval of non-file or episode related information for a specific anime by AID (AniDB anime id). + http://wiki.anidb.net/w/HTTP_API_Definition#Anime + """ + id = int(id) # why? + + r = self._request("anime", {'aid': id}) + soup = BeautifulSoup(r.text, 'xml') + if soup.error is not None: + raise Exception(soup.error.string) + + anime = soup.anime + titles = anime.titles + + a = Anime({ + 'id': id, + 'type': str(anime.type.string), + 'episodecount': int(anime.episodecount.string), + 'startdate': datetime(*list(map(int, anime.startdate.string.split("-")))), + 'enddate': datetime(*list(map(int, anime.enddate.string.split("-")))), + 'titles': [( + str(title.string), + title['type'] if 'type' in title else "unknown" + ) for title in anime.find_all('title')], + 'title': str(titles.find('title', attrs={'type': "main"}).string), + 'relatedanime': [], + 'url': str(anime.url.string), + 'creators': [], + 'description': str(anime.description.string), + 'ratings': SmartDict({ + 'permanent': float(anime.ratings.permanent.string), + 'temporary': float(anime.ratings.temporary.string), + 'review': float(anime.ratings.review.string) + }), + 'picture': "http://img7.anidb.net/pics/anime/" + str(anime.picture.string), + 'categories': [], + 'tags': [], + 'characters': [], + 'episodes': [], + }) + + self._cache[id] = a + + return a + +class SmartDict(dict): + def __init__(self, *a, **kw): + super(SmartDict, self).__init__(**kw) + for x in a: + try: + self.update(x) + except TypeError: + self.update(x.__dict__) + self.__dict__ = self + +class Anime: + def __init__(self, data={}, partial=False, **kw): + self._data = data + self._partial = partial + if partial: + self._updater = kw['updater'] + + def _update(self): + if not self._partial: + raise Exception("Attempted to update a ready object") + else: + self._data = self._updater()._data + self._partial = False + + def __getattr__(self, name): + if name in self._data: + return self._data[name] + else: + if self._partial: + self._update() + if name in self._data: + return self._data[name] + else: + raise AttributeError("no attribute called '%s'" % name) + else: + raise AttributeError("no attribute called '%s'" % name) + + def __repr__(self): + return u'<Anime %i "%s">' % (self.id, self.title)
\ No newline at end of file |