Added manganelo library.
[automanga.git] / getmanga
CommitLineData
64eb9fa5
FT
1#!/usr/bin/python3
2
3import sys, os, getopt, time, random
4import manga.lib, manga.profile
5from PIL import Image
6
7verbose = 0
8wait = 10
9
10def msg(vl, msg, *args):
11 if verbose >= vl:
12 sys.stderr.write("getmanga: " + (msg % args) + "\n")
13
14def getprop(nm, default=None):
6e3b8ae1 15 if mprof and "dl-" + nm in mprof.props:
64eb9fa5
FT
16 return mprof.props["dl-" + nm]
17 if nm in props:
18 return props[nm]
19 return default
20
21def digits(num):
22 n, i = 10, 1
23 while True:
24 if num < n:
25 return i
26 n, i = n * 10, i + 1
27
28def autoname(page):
29 ret = ""
30 for t, i in page.stack:
31 if ret:
32 ret += "-"
33 ret += "%0*i" % (digits(len(t) + 1), i + 1)
34 return ret
35
36def expand(pattern, page):
37 ret = ""
38 si = 0
39 fp = 0
67983628 40 stack = list(zip([t for t, i in page.stack], [t for t, i in page.stack[1:]] + [page], [i for t, i in page.stack]))
64eb9fa5
FT
41 while True:
42 p = pattern.find('%', fp)
43 if p < 0:
67983628 44 if si < len(stack):
64eb9fa5 45 sys.stderr.write("getmanga: pattern %s did not match page %s\n" %
67983628 46 (pattern, "/".join(c.name for t, c, i in stack)))
64eb9fa5
FT
47 sys.exit(1)
48 return ret + pattern[fp:]
49 ret += pattern[fp:p]
67983628
FT
50 m = pattern[p + 1:p + 2]
51 fp = p + 2
64eb9fa5
FT
52 if m == "%":
53 ret += "%"
54 else:
67983628 55 if si >= len(stack):
64eb9fa5 56 sys.stderr.write("getmanga: pattern %s did not match page %s\n" %
67983628 57 (pattern, "/".join(c.name for t, c, i in stack)))
64eb9fa5 58 sys.exit(1)
67983628 59 t, ct, ti = stack[si]
64eb9fa5
FT
60 si += 1
61 if m == "i":
62 ret += "%0*i" % (digits(len(t) + 1), ti + 1)
63 elif m == "n":
67983628 64 ret += ct.name
64eb9fa5 65 elif m == "d":
67983628 66 ret += ct.id
64eb9fa5 67 else:
67983628 68 sys.stderr.write("getmanga: %s: unknown specifier `%s'\n" % (pattern, m))
64eb9fa5
FT
69 sys.exit(1)
70
71def download(mng, tdir, pattern):
72 exts = ["", ".jpg", ".jpeg", ".png", ".gif"]
73 fmts = {"PNG": "png", "JPEG": "jpeg", "GIF": "gif"}
74 for page in manga.lib.cursor(mng):
75 if pattern is None:
76 nm = autoname(page)
77 else:
78 nm = expand(pattern, page)
79 path = os.path.join(tdir, nm)
80 if any(os.path.exists(path + ext) for ext in exts):
81 msg(2, "%s exists, skipping", nm)
82 continue
83 msg(1, "getting %s...", nm)
84 with page.open() as fp:
85 with open(path, "wb") as out:
daaea5d1
FT
86 done = False
87 try:
88 while True:
89 data = fp.read(65536)
90 if data == b"":
91 done = True
92 break
93 out.write(data)
94 finally:
95 if not done:
96 os.unlink(path)
64eb9fa5
FT
97 try:
98 img = Image.open(path)
99 except OSError:
100 fmt = None
101 else:
102 fmt = img.format
103 if fmt not in fmts:
104 sys.stderr.write("getmanga: warning: could not determine file format of %s, leaving as is\n" % nm)
105 else:
106 os.rename(path, path + "." + fmts[fmt])
107 msg(3, "%s -> %s", nm, nm + "." + fmts[fmt])
108 cwait = abs(random.gauss(0, 1) * wait)
109 msg(2, "waiting %.1f s...", cwait)
110 time.sleep(cwait)
111
112def usage(out):
da9e5bf6 113 out.write("usage: getmanga [-hv] [-w WAIT] [-p PROFILE] [-P PATTERN] DIRECTORY [LIBRARY ID]\n")
aaab61ee
FT
114 out.write("\tpattern templates:\n")
115 out.write("\t %i\tSequence number\n")
116 out.write("\t %n\tName\n")
117 out.write("\t %d\tID\n")
64eb9fa5
FT
118
119def main():
120 global verbose, wait, mprof, props
121
da9e5bf6 122 opts, args = getopt.getopt(sys.argv[1:], "hvp:w:P:")
6e3b8ae1 123 profnm = None
da9e5bf6 124 pattern = None
64eb9fa5
FT
125 for o, a in opts:
126 if o == "-h":
127 usage(sys.stdout)
128 sys.exit(0)
129 elif o == "-p":
130 profnm = a
131 elif o == "-v":
132 verbose += 1
133 elif o == "-w":
134 wait = int(a)
da9e5bf6
FT
135 elif o == "-P":
136 pattern = a
64eb9fa5
FT
137 if len(args) < 1:
138 usage(sys.stderr)
139 sys.exit(1)
140 tdir = args[0]
141
142 if not os.path.isdir(tdir):
143 sys.stderr.write("getmanga: %s: not a directory\n" % (tdir))
144 sys.exit(1)
145
146 pfile = os.path.join(tdir, ".props")
147 props = {}
148 if os.path.exists(pfile):
149 with open(pfile, "r") as fp:
66efacb2 150 for words in manga.profile.splitlines(fp):
64eb9fa5
FT
151 if words[0] == "set" and len(words) > 2:
152 props[words[1]] = words[2]
153 elif words[0] == "lset" and len(words) > 1:
154 props[words[1]] = words[2:]
155
6e3b8ae1 156 if profnm is None:
64eb9fa5 157 profile = manga.profile.profile.last()
6e3b8ae1
FT
158 elif profnm == "":
159 profile = None
64eb9fa5
FT
160 else:
161 profile = manga.profile.profile.byname(profnm)
162
163 if len(args) == 2:
164 usage(sys.stderr)
165 sys.exit(1)
166 elif len(args) > 2:
167 libnm, mid = args[1:3]
168 elif isinstance(props.get("manga"), list):
169 libnm, mid = props["manga"]
170 else:
171 sys.stderr.write("getmanga: %s: id is neither saved nor given\n" % (tdir))
172 sys.exit(1)
173 try:
174 lib = manga.lib.findlib(libnm)
175 except ImportError:
176 sys.stderr.write("getmanga: no such library: %s\n" % (libnm))
177 sys.exit(1)
178 try:
179 mng = lib.byid(mid)
180 except KeyError:
181 sys.stderr.write("getmanga: no such manga: %s\n" % (mid))
182 sys.exit(1)
6e3b8ae1
FT
183 if profile is not None:
184 mprof = profile.getmanga(libnm, mng.id)
185 else:
186 mprof = None
64eb9fa5 187
6e3b8ae1 188 download(mng, tdir, pattern or getprop("pattern"))
64eb9fa5
FT
189
190if __name__ == "__main__":
191 try:
192 main()
193 except KeyboardInterrupt:
194 pass