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()
 
         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.
     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:
         """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:
 
     def byidlist(self, idlist):
         if len(idlist) == 0:
@@ -136,11 +149,45 @@ class imgstream(object):
         """Close this stream."""
         raise NotImplementedError()
 
         """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()
 
         """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):
 class cursor(object):
     def __init__(self, ob):
         if isinstance(ob, cursor):
@@ -148,9 +195,9 @@ class cursor(object):
         else:
             self.cur = self.descend(ob)
 
         else:
             self.cur = self.descend(ob)
 
-    def descend(self, ob):
+    def descend(self, ob, last=False):
         while isinstance(ob, pagelist):
         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
         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:
     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.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]