Added basic profile handling.
[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 = ""
52 break
53 elif c == "\\" and p < len(line):
54 buf += bsq(line[p])
55 p += 1
56 else:
57 buf += c
58 if a or buf != "":
59 ret.append(buf)
60 return ret
61
62def consline(*words):
63 buf = ""
64 for w in words:
65 if any((c == "\\" or c == '"' or c == "\n" for c in w)):
66 wb = ""
67 for c in w:
68 if c == "\\": wb += "\\\\"
69 elif c == '"': wb += '\\"'
70 elif c == "\n": wb += "\\n"
71 else: wb += c
72 w = wb
73 if w == "" or any((c.isspace() for c in w)):
74 w = '"' + w + '"'
75 if buf != "":
76 buf += " "
77 buf += w
78 return buf
79
80class manga(object):
81 def __init__(self, profile, libnm, id, path):
82 self.profile = profile
83 self.libnm = libnm
84 self.id = id
85 self.path = path
86 self.props = self.loadprops()
87
88 def loadprops(self):
89 ret = {}
90 with openwdir(self.path) as f:
91 for line in f:
92 words = splitline(line)
93 if len(words) < 1: continue
94 if words[0] == "set" and len(words) > 2:
95 ret[words[1]] = words[2]
96 elif words[0] == "lset" and len(words) > 1:
97 ret[words[1]] = words[2:]
98 return ret
99
100 def prop(self, key, default=KeyError):
101 if key not in self.props:
102 if default is KeyError:
103 raise KeyError(key)
104 return default
105 return self.props[key]
106
107 def setprop(self, key, val):
108 self.props[key] = val
109
110 def saveprops(self):
111 with openwdir(self.path, "w") as f:
112 for key, val in self.props.iteritems():
113 if isinstance(val, str):
114 f.write(consline("set", key, val) + "\n")
115 else:
116 f.write(consline("lset", key, *val) + "\n")
117
118 def open(self):
119 import lib
120 return lib.findlib(self.libnm).byid(self.id)
121
122class profile(object):
123 def __init__(self, dir):
124 self.dir = dir
125 self.name = None
126
127 def getmapping(self):
128 seq = 0
129 ret = {}
130 if os.path.exists(pj(self.dir, "map")):
131 with openwdir(pj(self.dir, "map")) as f:
132 for ln in f:
133 words = splitline(ln)
134 if len(words) < 1:
135 continue
136 if words[0] == "seq" and len(words) > 1:
137 try:
138 seq = int(words[1])
139 except ValueError:
140 pass
141 elif words[0] == "manga" and len(words) > 3:
142 try:
143 ret[words[1], words[2]] = int(words[3])
144 except ValueError:
145 pass
146 return seq, ret
147
148 def savemapping(self, seq, m):
149 with openwdir(pj(self.dir, "map"), "w") as f:
150 f.write(consline("seq", str(seq)) + "\n")
151 for (libnm, id), num in m.iteritems():
152 f.write(consline("manga", libnm, id, str(num)) + "\n")
153
154 def getmanga(self, libnm, id, creat=False):
155 seq, m = self.getmapping()
156 if (libnm, id) in m:
157 return manga(self, libnm, id, pj(self.dir, "%i.manga" % m[(libnm, id)]))
158 if not creat:
159 raise KeyError("no such manga: (%s, %s)" % (libnm, id))
160 while True:
161 try:
162 fp = openwdir(pj(self.dir, "%i.manga" % seq), "wx")
163 except IOError:
164 seq += 1
165 else:
166 break
167 fp.close()
168 m[(libnm, id)] = seq
169 self.savemapping(seq, m)
170 return manga(self, libnm, id, pj(self.dir, "%i.manga" % seq))
171
172 def setlast(self):
173 if self.name is None:
174 raise ValueError("profile at " + self.dir + " has no name")
175 with openwdir(pj(basedir, "last"), "w") as f:
176 f.write(self.name + "\n")
177
178 @classmethod
179 def byname(cls, name):
180 if not name or name == "last" or name[0] == '.':
181 raise KeyError("invalid profile name: " + name)
182 ret = cls(pj(basedir, name))
183 ret.name = name
184 return ret
185
186 @classmethod
187 def last(cls):
188 if not os.path.exists(pj(basedir, "last")):
189 raise KeyError("there is no last used profile")
190 with open(pj(basedir, "last")) as f:
191 return cls.byname(f.readline().strip())