Added simplistic keyword searching to mrnet and local libraries.
[automanga.git] / manga / local.py
1 import os
2 import lib
3 pj = os.path.join
4
5 def decode1(nm):
6     ret = []
7     p = 0
8     while p < len(nm):
9         if nm[p].isdigit():
10             s = p
11             p += 1
12             while p < len(nm) and nm[p].isdigit():
13                 p += 1
14             ret += [nm[s:p]]
15         elif nm[p].isalpha():
16             s = p
17             p += 1
18             while p < len(nm) and nm[p].isalpha():
19                 p += 1
20             ret += [nm[s:p]]
21         else:
22             ret += [nm[p]]
23             p += 1
24     return ret
25
26 def genstr(s):
27     ret = []
28     for part in s:
29         if part.isdigit():
30             ret += [int]
31         else:
32             ret += [part]
33     return ret
34
35 class imgstream(lib.imgstream):
36     def __init__(self, path):
37         self.bk = open(path, 'rb')
38         self.clen = os.fstat(self.bk.fileno()).st_size
39
40     def close(self):
41         self.bk.close()
42
43     def read(self, sz=None):
44         return self.bk.read(sz)
45
46 class page(lib.page):
47     def __init__(self, manga, path, name, id, stack):
48         self.path = path
49         self.id = id
50         self.name = name
51         self.manga = manga
52         self.stack = stack
53
54     def open(self):
55         return imgstream(self.path)
56
57 class interm(lib.pagelist):
58     def __init__(self, name, id, stack, direct):
59         self.name = name
60         self.id = id
61         self.stack = stack
62         self.direct = direct
63
64     def __len__(self):
65         return len(self.direct)
66
67     def __getitem__(self, n):
68         return self.direct[n]
69
70 def maxstruct(flist):
71     mx = None
72     for dent in flist:
73         s = genstr(decode1(dent))
74         if mx is None:
75             mx = s
76         else:
77             nmx = []
78             for p, n in zip(mx, s):
79                 if p == n:
80                     nmx.append(p)
81                 else:
82                     break
83             mx = nmx
84     return mx
85
86 class manga(lib.manga):
87     exts = ["jpg", "jpeg", "png", "gif"]
88
89     def __init__(self, path):
90         path = os.path.abspath(path)
91         if not os.path.isdir(path):
92             raise IOError("No such directory: " + path)
93         self.path = path
94         self.id = path
95         self.stack = []
96         if os.path.exists(pj(self.path, "name")):
97             with open(pj(self.path, "name")) as s:
98                 self.name = s.readline().strip().decode("utf-8")
99         else:
100             self.name = os.path.basename(path).decode("utf-8")
101         self.direct = self.destruct()
102
103     def __len__(self):
104         return len(self.direct)
105
106     def __getitem__(self, idx):
107         return self.direct[idx]
108
109     def imglist(self):
110         if os.path.exists(pj(self.path, "order")):
111             with open(pj(self.path, "order")) as s:
112                 return True, [line.strip() for line in s if os.path.exists(pj(self.path, line.strip()))]
113         else:
114             return False, [dent for dent in os.listdir(self.path) if '.' in dent and dent[dent.rindex('.') + 1:] in self.exts]
115
116     def bakenames(self, files):
117         ret = []
118         map = {}
119         for orig in files:
120             nm = orig
121             if '.' in nm:
122                 nm = nm[:nm.rindex('.')]
123             ret.append(nm)
124             map[nm] = orig
125         return ret, map
126
127     def destruct(self):
128         ordered, files = self.imglist()
129         pages, orig = self.bakenames(files)
130         mx = maxstruct(pages)
131         if mx is None:
132             raise TypeError("could not figure out any structure")
133         var = [i for i, part in enumerate(mx) if part == int]
134         structs = [(nm, decode1(nm)) for nm in pages]
135         if not ordered:
136             structs.sort(key=lambda o: "".join(o[1][len(mx):]))
137             for i in reversed(var):
138                 structs.sort(key=lambda o: int(o[1][i]))
139         def constree(p, structs, idx):
140             if idx == len(var):
141                 pages = []
142                 for nm, st in structs:
143                     id = "".join(st[len(mx):])
144                     pages.append(page(self, pj(self.path, orig[nm]), id, id, p.stack + [(p, len(pages))]))
145                 return pages
146             else:
147                 ids = set()
148                 oids = []
149                 for nm, st in structs:
150                     cur = st[var[idx]]
151                     if cur not in ids:
152                         ids.add(cur)
153                         oids.append(cur)
154                 ret = []
155                 for id in oids:
156                     sub = [(nm, st) for nm, st in structs if st[var[idx]] == id]
157                     if len(sub) == 1:
158                         nm, st = sub[0]
159                         id = "".join(st[var[idx]:])
160                         ret.append(page(self, pj(self.path, orig[nm]), id, id, p.stack + [(p, len(ret))]))
161                     else:
162                         cur = interm(id, id, p.stack + [(p, len(ret))], [])
163                         cur.direct = constree(cur, sub, idx + 1)
164                         ret.append(cur)
165                 return ret
166         return constree(self, structs, 0)
167
168 class dumb(lib.library):
169     def byid(self, id):
170         if not os.path.isdir(id):
171             raise KeyError(id)
172         return manga(id)
173
174 class directory(dumb):
175     def __init__(self, path):
176         if not os.path.isdir(path):
177             raise IOError("No such directory: " + path)
178         self.path = path
179
180     def byname(self, prefix):
181         ret = []
182         prefix = prefix.lower()
183         for dent in os.listdir(self.path):
184             if dent[:len(prefix)].lower() == prefix:
185                 ret.append(manga(pj(self.path, dent)))
186         return ret
187
188     def search(self, expr):
189         expr = expr.lower()
190         return [manga(pj(self.path, dent)) for dent in os.listdir(self.path) if expr in dent.lower()]
191
192     def __iter__(self):
193         for dent in os.listdir(self.path):
194             yield manga(pj(self.path, dent))
195
196
197 library = dumb