All libraries should implement this."""
raise NotImplementedError()
+ def byid(self, id):
+ """Returns a previously known manga by its string ID, or
+ raises KeyError if no such manga could be found.
+
+ All libraries should implement this."""
+ raise KeyError(id)
+
def __iter__(self):
"""Return an iterator of all known mangas in this library.
class pagetree(object):
"""Base class for objects in the tree of pages and pagelists.
- All pagetree objects should contain an attribute `stack', contains
- a list of pairs. The last pair in the list should be the pagetree
- object which yielded this pagetree object, along with the index
- which yielded it. Every non-last pair should be the same
+ All pagetree objects should contain an attribute `stack',
+ containing a list of pairs. The last pair in the list should be
+ the pagetree object which yielded this pagetree object, along with
+ the index which yielded it. Every non-last pair should be the same
information for the pair following it. The only objects with empty
- `stack' lists should be `manga' objects."""
- pass
+ `stack' lists should be `manga' objects.
+
+ All non-root pagetree objects should also contain an attribute
+ `id', which should be a string that can be passed to the `byid'
+ function of its parent node to recover the node. Such string ID
+ should be more persistent than the node's numeric index in the
+ parent."""
+
+ def idlist(self):
+ """Returns a list of the IDs necessary to resolve this node
+ from the root node."""
+ if len(self.stack) == 0:
+ raise Exception("Cannot get ID list on root node.")
+ return [n.id for n, i in self.stack[1:]] + [self.id]
+
+ def byidlist(self, idlist):
+ if len(idlist) == 0:
+ return self
+ return self.byid(idlist[0]).byidlist(idlist[1:])
class pagelist(pagetree):
"""Class representing a list of either pages, or nested
All pagelists need to implement this."""
raise NotImplementedError()
+ def byid(self, id):
+ """Return the direct sub-node of this pagelist which has the
+ given string ID. If none is found, a KeyError is raised.
+
+ This default method iterates the children of this node, but
+ may be overridden by some more efficient implementation.
+ """
+ for ch in self:
+ if ch.id == id:
+ return ch
+ raise KeyError(id)
+
class manga(pagelist):
"""Class reprenting a single manga. Includes the pagelist class,
- and all constraints valid for it."""
+ and all constraints valid for it.
+
+ A manga is a root pagetree node, but should also contain an `id'
+ attribute, which can be used to recover the manga from its
+ library's `byid' function."""
pass
class page(pagetree):
self.volume = self.chapter.volume
self.manga = self.volume.manga
self.n = n
+ self.id = str(n)
self.url = url
self.ciurl = None
return imgstream(self.iurl())
class chapter(lib.pagelist):
- def __init__(self, volume, stack, name, url):
+ def __init__(self, volume, stack, id, name, url):
self.stack = stack
self.volume = volume
self.manga = volume.manga
+ self.id = id
self.name = name
self.url = url
self.cpag = None
return "<mangafox.chapter %r.%r.%r>" % (self.manga.name, self.volume.name, self.name)
class volume(lib.pagelist):
- def __init__(self, manga, stack, name):
+ def __init__(self, manga, stack, id, name):
self.stack = stack
self.manga = manga
+ self.id = id
self.name = name
self.ch = []
return el
class manga(lib.manga):
- def __init__(self, lib, name, url):
+ def __init__(self, lib, id, name, url):
self.lib = lib
+ self.id = id
self.name = name
self.url = url
self.cvol = None
vls = page.find("div", id="chapters").findAll("div", attrs={"class": "slide"})
self.cvol = []
for i, vn in enumerate(reversed(vls)):
- vol = volume(self, [(self, i)], vn.find("h3", attrs={"class": "volume"}).contents[0].strip())
+ name = vn.find("h3", attrs={"class": "volume"}).contents[0].strip()
+ vid = name.encode("utf8")
+ vol = volume(self, [(self, i)], vid, name)
cls = nextel(vn)
if cls.name != u"ul" or cls["class"] != u"chlist":
raise Exception("parse error: weird volume list for %r" % self)
for o, ch in enumerate(reversed(cls.findAll("li"))):
n = ch.div.h3 or ch.div.h4
name = n.a.string
+ chid = name.encode("utf8")
for span in ch("span"):
try:
if u" title " in (u" " + span["class"] + u" "):
url = n.a["href"].encode("us-ascii")
if url[-7:] != "/1.html":
raise Exception("parse error: unexpected chapter URL for %r: %s" % (self, url))
- vol.ch.append(chapter(vol, vol.stack + [(vol, o)], name, url[:-6]))
+ vol.ch.append(chapter(vol, vol.stack + [(vol, o)], chid, name, url[:-6]))
self.cvol.append(vol)
return self.cvol
page = soup(htcache.fetch(self.base + ("directory/%i.htm?az" % pno)))
ls = page.find("div", id="mangalist").find("ul", attrs={"class": "list"}).findAll("li")
ret = []
+ ubase = self.base + "manga/"
for m in ls:
t = m.find("div", attrs={"class": "manga_text"}).find("a", attrs={"class": "title"})
name = t.string
url = t["href"].encode("us-ascii")
- ret.append(manga(self, name, url))
+ if url[:len(ubase)] != ubase or url.find('/', len(ubase)) != (len(url) - 1):
+ raise Exception("parse error: unexpected manga URL for %r: %s" % (name, url))
+ ret.append(manga(self, url[len(ubase):-1], name, url))
return ret
def alphapages(self):
ls = self.alphapage(pno)
i = 0
+ def byid(self, id):
+ url = self.base + ("manga/%s/" % id)
+ page = soup(htcache.fetch(url))
+ if page.find("div", id="title") is None:
+ # Assume we got the search page
+ raise KeyError(id)
+ name = page.find("div", id="series_info").find("div", attrs={"class": "cover"}).img["alt"]
+ return manga(self, id, name, url)
+
def __iter__(self):
raise NotImplementedError("mangafox iterator")