Added manga aliases.
[automanga.git] / manga / profile.py
CommitLineData
375a030d
FT
1import os
2pj = os.path.join
3
4home = os.getenv("HOME")
5if home is None or not os.path.isdir(home):
6 raise Exception("Could not find home directory for profile keeping")
7basedir = pj(home, ".manga", "profiles")
8
9def openwdir(nm, mode="r"):
10 if os.path.exists(nm):
11 return open(nm, mode)
12 if mode != "r":
13 d = os.path.dirname(nm)
14 if not os.path.isdir(d):
15 os.makedirs(d)
16 return open(nm, mode)
17
18def splitline(line):
19 def bsq(c):
20 if c == "\\": return "\\"
21 elif c == '"': return '"'
22 elif c == " ": return " "
23 elif c == "n": return "\n"
24 else: return ""
25 ret = []
26 p = 0
27 buf = ""
28 a = False
29 while p < len(line):
30 c = line[p]
31 if c.isspace():
32 p += 1
33 else:
34 while p < len(line):
35 c = line[p]
36 p += 1
37 if c == '"':
38 a = True
39 while p < len(line):
40 c = line[p]
41 p += 1
42 if c == '"':
43 break
44 elif c == "\\" and p < len(line):
45 buf += bsq(line[p])
46 p += 1
47 else:
48 buf += c
49 elif c.isspace():
50 ret.append(buf)
51 buf = ""
012c4cae 52 a = False
375a030d
FT
53 break
54 elif c == "\\" and p < len(line):
55 buf += bsq(line[p])
56 p += 1
57 else:
58 buf += c
59 if a or buf != "":
60 ret.append(buf)
61 return ret
62
63def consline(*words):
64 buf = ""
65 for w in words:
66 if any((c == "\\" or c == '"' or c == "\n" for c in w)):
67 wb = ""
68 for c in w:
69 if c == "\\": wb += "\\\\"
70 elif c == '"': wb += '\\"'
71 elif c == "\n": wb += "\\n"
72 else: wb += c
73 w = wb
74 if w == "" or any((c.isspace() for c in w)):
75 w = '"' + w + '"'
76 if buf != "":
77 buf += " "
78 buf += w
79 return buf
80
81class manga(object):
82 def __init__(self, profile, libnm, id, path):
83 self.profile = profile
84 self.libnm = libnm
85 self.id = id
86 self.path = path
87 self.props = self.loadprops()
88
89 def loadprops(self):
90 ret = {}
91 with openwdir(self.path) as f:
92 for line in f:
93 words = splitline(line)
94 if len(words) < 1: continue
95 if words[0] == "set" and len(words) > 2:
96 ret[words[1]] = words[2]
97 elif words[0] == "lset" and len(words) > 1:
98 ret[words[1]] = words[2:]
99 return ret
100
101 def prop(self, key, default=KeyError):
102 if key not in self.props:
103 if default is KeyError:
104 raise KeyError(key)
105 return default
106 return self.props[key]
107
012c4cae
FT
108 def __getitem__(self, key):
109 return self.props[key]
110
111 def __contains__(self, key):
112 return key in self.props
113
375a030d
FT
114 def setprop(self, key, val):
115 self.props[key] = val
116
117 def saveprops(self):
118 with openwdir(self.path, "w") as f:
119 for key, val in self.props.iteritems():
120 if isinstance(val, str):
121 f.write(consline("set", key, val) + "\n")
122 else:
123 f.write(consline("lset", key, *val) + "\n")
124
125 def open(self):
126 import lib
127 return lib.findlib(self.libnm).byid(self.id)
128
129class profile(object):
130 def __init__(self, dir):
131 self.dir = dir
132 self.name = None
133
134 def getmapping(self):
135 seq = 0
136 ret = {}
137 if os.path.exists(pj(self.dir, "map")):
138 with openwdir(pj(self.dir, "map")) as f:
139 for ln in f:
140 words = splitline(ln)
141 if len(words) < 1:
142 continue
143 if words[0] == "seq" and len(words) > 1:
144 try:
145 seq = int(words[1])
146 except ValueError:
147 pass
148 elif words[0] == "manga" and len(words) > 3:
149 try:
150 ret[words[1], words[2]] = int(words[3])
151 except ValueError:
152 pass
153 return seq, ret
154
155 def savemapping(self, seq, m):
156 with openwdir(pj(self.dir, "map"), "w") as f:
157 f.write(consline("seq", str(seq)) + "\n")
158 for (libnm, id), num in m.iteritems():
159 f.write(consline("manga", libnm, id, str(num)) + "\n")
160
161 def getmanga(self, libnm, id, creat=False):
162 seq, m = self.getmapping()
163 if (libnm, id) in m:
164 return manga(self, libnm, id, pj(self.dir, "%i.manga" % m[(libnm, id)]))
165 if not creat:
166 raise KeyError("no such manga: (%s, %s)" % (libnm, id))
167 while True:
168 try:
169 fp = openwdir(pj(self.dir, "%i.manga" % seq), "wx")
170 except IOError:
171 seq += 1
172 else:
173 break
174 fp.close()
175 m[(libnm, id)] = seq
176 self.savemapping(seq, m)
177 return manga(self, libnm, id, pj(self.dir, "%i.manga" % seq))
178
179 def setlast(self):
180 if self.name is None:
181 raise ValueError("profile at " + self.dir + " has no name")
182 with openwdir(pj(basedir, "last"), "w") as f:
183 f.write(self.name + "\n")
184
271d68da
FT
185 def getaliases(self):
186 ret = {}
187 if os.path.exists(pj(self.dir, "alias")):
188 with openwdir(pj(self.dir, "alias")) as f:
189 for ln in f:
190 ln = splitline(ln)
191 if len(ln) < 1: continue
192 if ln[0] == "alias" and len(ln) > 3:
193 ret[ln[1]] = ln[2], ln[3]
194 return ret
195
196 def savealiases(self, map):
197 with openwdir(pj(self.dir, "alias"), "w") as f:
198 for nm, (libnm, id) in map.iteritems():
199 f.write(consline("alias", nm, libnm, id) + "\n")
200
201 def getalias(self, nm):
202 return self.getaliases()[nm]
203
204 def setalias(self, nm, libnm, id):
205 aliases = self.getaliases()
206 aliases[nm] = libnm, id
207 self.savealiases(aliases)
208
375a030d
FT
209 @classmethod
210 def byname(cls, name):
211 if not name or name == "last" or name[0] == '.':
212 raise KeyError("invalid profile name: " + name)
213 ret = cls(pj(basedir, name))
214 ret.name = name
215 return ret
216
217 @classmethod
218 def last(cls):
219 if not os.path.exists(pj(basedir, "last")):
220 raise KeyError("there is no last used profile")
221 with open(pj(basedir, "last")) as f:
222 return cls.byname(f.readline().strip())