aboutsummaryrefslogtreecommitdiffstats
path: root/anidb/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'anidb/__init__.py')
-rw-r--r--anidb/__init__.py139
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