local: Treat dots/periods as digits in destructuring directories.
[automanga.git] / manga / lib.py
index 2a11cb1..849e632 100644 (file)
@@ -9,6 +9,19 @@ class library(object):
         All libraries should implement this."""
         raise NotImplementedError()
 
+    def search(self, string):
+        """Returns an iterable object of mangas in this library that
+        matches the search string in a library-dependent manner. While
+        each library is at liberty to define its own matching
+        criteria, it is probably likely to involve something akin to
+        searching for keywords in the titles of the library.
+
+        Searching may return very many results and may be slow to
+        iterate.
+
+        Not all libraries need 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.
@@ -46,8 +59,8 @@ class pagetree(object):
         """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]
+            return []
+        return self.stack[-1][0].idlist() + [self.id]
 
     def byidlist(self, idlist):
         if len(idlist) == 0:
@@ -136,11 +149,45 @@ class imgstream(object):
         """Close this stream."""
         raise NotImplementedError()
 
-    def read(self, sz = None):
+    def read(self, sz=None):
         """Read SZ bytes from the stream, or the entire rest of the
         stream of SZ is not given."""
         raise NotImplementedError()
 
+class stdimgstream(imgstream):
+    """A standard implementation of imgstream, for libraries which
+    have no particular implementation requirements."""
+
+    def __init__(self, url, referer=None):
+        import urllib.request
+        headers = {"User-Agent": "automanga/1"}
+        if referer:
+            headers["Referer"] = referer
+        req = urllib.request.Request(url, headers=headers)
+        self.bk = urllib.request.urlopen(req)
+        ok = False
+        try:
+            if self.bk.getcode() != 200:
+                raise IOError("Server error: " + str(self.bk.getcode()))
+            self.ctype = self.bk.info()["Content-Type"]
+            self.clen = int(self.bk.info()["Content-Length"])
+            ok = True
+        finally:
+            if not ok:
+                self.bk.close()
+
+    def fileno(self):
+        return self.bk.fileno()
+
+    def close(self):
+        self.bk.close()
+
+    def read(self, sz=None):
+        if sz is None:
+            return self.bk.read()
+        else:
+            return self.bk.read(sz)
+
 class cursor(object):
     def __init__(self, ob):
         if isinstance(ob, cursor):
@@ -148,9 +195,9 @@ class cursor(object):
         else:
             self.cur = self.descend(ob)
 
-    def descend(self, ob):
+    def descend(self, ob, last=False):
         while isinstance(ob, pagelist):
-            ob = ob[0]
+            ob = ob[len(ob) - 1 if last else 0]
         if not isinstance(ob, page):
             raise TypeError("object in page tree was unexpectedly not a pagetree")
         return ob
@@ -165,24 +212,31 @@ class cursor(object):
     def prev(self):
         for n, i in reversed(self.cur.stack):
             if i > 0:
-                self.cur = self.descend(n[i - 1])
+                self.cur = self.descend(n[i - 1], True)
                 return self.cur
         raise StopIteration()
 
     def __iter__(self):
-        return self
-
-def _lazymod(name):
-    return __import__(name, fromlist=["dummy"])
-class _lazydict(object):
-    def __init__(self):
-        self.bk = {}
-    def __setitem__(self, key, val):
-        self.bk[key] = "u", val
-    def __getitem__(self, key):
-        st, v = self.bk[key]
-        if st == "u":
-            v = self.bk[key] = v()
-        return v
-libraries = _lazydict()
-libraries["mf"] = lambda: _lazymod("manga.mangafox").library()
+        def iterator():
+            yield self.cur
+            while True:
+                try:
+                    yield self.next()
+                except StopIteration:
+                    break
+        return iterator()
+
+loaded = {}
+def findlib(name):
+    def load(name):
+        import importlib
+        mod = importlib.import_module(name)
+        if not hasattr(mod, "library"):
+            raise ImportError("module " + name + " is not a manga library")
+        return mod.library()
+    if name not in loaded:
+        try:
+            loaded[name] = load("manga." + name)
+        except ImportError:
+            loaded[name] = load(name)
+    return loaded[name]