3051013f971a43ce50166d107b9b340d5be0c8d2
[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         var = [i for i, part in enumerate(mx) if part == int]
132         structs = [(nm, decode1(nm)) for nm in pages]
133         if not ordered:
134             structs.sort(key=lambda o: "".join(o[1][len(mx):]))
135             for i in reversed(var):
136                 structs.sort(key=lambda o: int(o[1][i]))
137         def constree(p, structs, idx):
138             if idx == len(var):
139                 pages = []
140                 for nm, st in structs:
141                     id = "".join(st[len(mx):])
142                     pages.append(page(self, pj(self.path, orig[nm]), id, id, p.stack + [(p, len(pages))]))
143                 return pages
144             else:
145                 ids = set()
146                 oids = []
147                 for nm, st in structs:
148                     cur = st[var[idx]]
149                     if cur not in ids:
150                         ids.add(cur)
151                         oids.append(cur)
152                 ret = []
153                 for id in oids:
154                     sub = [(nm, st) for nm, st in structs if st[var[idx]] == id]
155                     if len(sub) == 1:
156                         nm, st = sub[0]
157                         id = "".join(st[var[idx]:])
158                         ret.append(page(self, pj(self.path, orig[nm]), id, id, p.stack + [(p, len(ret))]))
159                     else:
160                         cur = interm(id, id, p.stack + [(p, len(ret))], [])
161                         cur.direct = constree(cur, sub, idx + 1)
162                         ret.append(cur)
163                 return ret
164         return constree(self, structs, 0)
165
166 class dumb(lib.library):
167     def byid(self, id):
168         if not os.path.isdir(id):
169             raise KeyError(id)
170         return manga(id)
171
172 class directory(dumb):
173     def __init__(self, path):
174         if not os.path.isdir(path):
175             raise IOError("No such directory: " + path)
176         self.path = path
177
178     def byname(self, prefix):
179         ret = []
180         prefix = prefix.lower()
181         for dent in os.listdir(self.path):
182             if dent[:len(prefix)].lower() == prefix:
183                 ret.append(manga(pj(self.path, dent)))
184         return ret
185
186     def __iter__(self):
187         for dent in os.listdir(self.path):
188             yield manga(pj(self.path, dent))
189
190 library = dumb