local: Treat dots/periods as digits in destructuring directories.
[automanga.git] / manga / local.py
index d1a839c..7f5a34d 100644 (file)
@@ -1,15 +1,20 @@
-import os
-import lib
-pj = os.path.join
+import os, pathlib
+from . import lib
+
+def pdigit(s):
+    for c in s:
+        if c not in "0123456789.":
+            return False
+    return True
 
 def decode1(nm):
     ret = []
     p = 0
     while p < len(nm):
-        if nm[p].isdigit():
+        if pdigit(nm[p]):
             s = p
             p += 1
-            while p < len(nm) and nm[p].isdigit():
+            while p < len(nm) and pdigit(nm[p]):
                 p += 1
             ret += [nm[s:p]]
         elif nm[p].isalpha():
@@ -26,15 +31,36 @@ def decode1(nm):
 def genstr(s):
     ret = []
     for part in s:
-        if part.isdigit():
+        if pdigit(part):
             ret += [int]
         else:
             ret += [part]
     return ret
 
+def findname(names, files):
+    matches = list(names.keys())
+    for f in files:
+        matches = [pfx for pfx in matches if f.startswith(pfx)]
+        if len(matches) < 1: return None
+    matches.sort(key=len, reverse=True)
+    return names[matches[0]]
+
+def prefixes(path):
+    nmpath = path/"names"
+    if not nmpath.exists():
+        return {}
+    ret = {}
+    with nmpath.open("r") as fp:
+        for line in fp:
+            line = line.strip()
+            p = line.find(' ')
+            if p < 0: continue
+            ret[line[:p]] = line[p + 1:]
+    return ret
+
 class imgstream(lib.imgstream):
     def __init__(self, path):
-        self.bk = open(path, 'rb')
+        self.bk = path.open("rb")
         self.clen = os.fstat(self.bk.fileno()).st_size
 
     def close(self):
@@ -87,17 +113,17 @@ class manga(lib.manga):
     exts = ["jpg", "jpeg", "png", "gif"]
 
     def __init__(self, path):
-        path = os.path.abspath(path)
-        if not os.path.isdir(path):
+        path = path.resolve()
+        if not path.is_dir():
             raise IOError("No such directory: " + path)
         self.path = path
-        self.id = path
+        self.id = os.fspath(path)
         self.stack = []
-        if os.path.exists(pj(self.path, "name")):
-            with open(pj(self.path, "name")) as s:
-                self.name = s.readline().strip().decode("utf-8")
+        if (self.path/"name").exists():
+            with (self.path/"name").open("r") as s:
+                self.name = s.readline().strip()
         else:
-            self.name = os.path.basename(path).decode("utf-8")
+            self.name = path.name
         self.direct = self.destruct()
 
     def __len__(self):
@@ -107,11 +133,11 @@ class manga(lib.manga):
         return self.direct[idx]
 
     def imglist(self):
-        if os.path.exists(pj(self.path, "order")):
-            with open(pj(self.path, "order")) as s:
-                return True, [line.strip() for line in s if os.path.exists(pj(self.path, line.strip()))]
+        if (self.path/"order").exists():
+            with (self.path/"order").open("r") as s:
+                return True, [line.strip() for line in s if (self.path/line.strip()).exists()]
         else:
-            return False, [dent for dent in os.listdir(self.path) if '.' in dent and dent[dent.rindex('.') + 1:] in self.exts]
+            return False, [dent for dent in (dent.name for dent in self.path.iterdir()) if '.' in dent and dent[dent.rindex('.') + 1:] in self.exts]
 
     def bakenames(self, files):
         ret = []
@@ -128,18 +154,21 @@ class manga(lib.manga):
         ordered, files = self.imglist()
         pages, orig = self.bakenames(files)
         mx = maxstruct(pages)
+        if mx is None:
+            raise TypeError("could not figure out any structure")
         var = [i for i, part in enumerate(mx) if part == int]
         structs = [(nm, decode1(nm)) for nm in pages]
         if not ordered:
             structs.sort(key=lambda o: "".join(o[1][len(mx):]))
             for i in reversed(var):
-                structs.sort(key=lambda o: int(o[1][i]))
+                structs.sort(key=lambda o: o[1][i])
+        readnames = prefixes(self.path)
         def constree(p, structs, idx):
             if idx == len(var):
                 pages = []
                 for nm, st in structs:
                     id = "".join(st[len(mx):])
-                    pages.append(page(self, pj(self.path, orig[nm]), id, id, p.stack + [(p, len(pages))]))
+                    pages.append(page(self, self.path/orig[nm], id, id, p.stack + [(p, len(pages))]))
                 return pages
             else:
                 ids = set()
@@ -151,34 +180,47 @@ class manga(lib.manga):
                         oids.append(cur)
                 ret = []
                 for id in oids:
-                    cur = interm(id, id, p.stack + [(p, len(ret))], [])
-                    cur.direct = constree(cur, [(nm, st) for nm, st in structs if st[var[idx]] == id], idx + 1)
-                    ret.append(cur)
+                    sub = [(nm, st) for nm, st in structs if st[var[idx]] == id]
+                    if len(sub) == 1:
+                        nm, st = sub[0]
+                        id = "".join(st[var[idx]:])
+                        ret.append(page(self, self.path/orig[nm], id, id, p.stack + [(p, len(ret))]))
+                    else:
+                        name = findname(readnames, [nm for (nm, st) in sub]) or id
+                        cur = interm(name, id, p.stack + [(p, len(ret))], [])
+                        cur.direct = constree(cur, sub, idx + 1)
+                        ret.append(cur)
                 return ret
         return constree(self, structs, 0)
 
 class dumb(lib.library):
     def byid(self, id):
-        if not os.path.isdir(id):
+        path = pathlib.Path(id)
+        if not path.is_dir():
             raise KeyError(id)
-        return manga(id)
+        return manga(path)
 
 class directory(dumb):
     def __init__(self, path):
-        if not os.path.isdir(path):
+        if not path.is_dir():
             raise IOError("No such directory: " + path)
         self.path = path
 
     def byname(self, prefix):
         ret = []
         prefix = prefix.lower()
-        for dent in os.listdir(self.path):
-            if dent[:len(prefix)].lower() == prefix:
-                ret.append(manga(pj(self.path, dent)))
+        for dent in self.path.iterdir():
+            if dent.name[:len(prefix)].lower() == prefix:
+                ret.append(manga(dent))
         return ret
 
+    def search(self, expr):
+        expr = expr.lower()
+        return [manga(dent) for dent in self.path.iterdir() if expr in dent.name.lower()]
+
     def __iter__(self):
-        for dent in os.listdir(self.path):
-            yield manga(pj(self.path, dent))
+        for dent in self.path.iterdir():
+            yield manga(dent)
+
 
 library = dumb