bd8401eda887715d90b91db93983f8c92877206f
[automanga.git] / getmanga
1 #!/usr/bin/python3
2
3 import sys, os, getopt, time, random
4 import manga.lib, manga.profile
5 from PIL import Image
6
7 verbose = 0
8 wait = 10
9
10 def msg(vl, msg, *args):
11     if verbose >= vl:
12         sys.stderr.write("getmanga: " + (msg % args) + "\n")
13
14 def getprop(nm, default=None):
15     if mprof and "dl-" + nm in mprof.props:
16         return mprof.props["dl-" + nm]
17     if nm in props:
18         return props[nm]
19     return default
20
21 def 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
28 def 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
36 def expand(pattern, page):
37     ret = ""
38     si = 0
39     fp = 0
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]))
41     while True:
42         p = pattern.find('%', fp)
43         if p < 0:
44             if si < len(stack):
45                 sys.stderr.write("getmanga: pattern %s did not match page %s\n" %
46                                  (pattern, "/".join(c.name for t, c, i in stack)))
47                 sys.exit(1)
48             return ret + pattern[fp:]
49         ret += pattern[fp:p]
50         m = pattern[p + 1:p + 2]
51         fp = p + 2
52         if m == "%":
53             ret += "%"
54         else:
55             if si >= len(stack):
56                 sys.stderr.write("getmanga: pattern %s did not match page %s\n" %
57                                  (pattern, "/".join(c.name for t, c, i in stack)))
58                 sys.exit(1)
59             t, ct, ti = stack[si]
60             si += 1
61             if m == "i":
62                 ret += "%0*i" % (digits(len(t) + 1), ti + 1)
63             elif m == "n":
64                 ret += ct.name
65             elif m == "d":
66                 ret += ct.id
67             else:
68                 sys.stderr.write("getmanga: %s: unknown specifier `%s'\n" % (pattern, m))
69                 sys.exit(1)
70
71 def 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:
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)
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
112 def usage(out):
113     out.write("usage: getmanga [-hv] [-w WAIT] [-p PROFILE] [-P PATTERN] DIRECTORY [LIBRARY ID]\n")
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")
118
119 def main():
120     global verbose, wait, mprof, props
121
122     opts, args = getopt.getopt(sys.argv[1:], "hvp:w:P:")
123     profnm = None
124     pattern = None
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)
135         elif o == "-P":
136             pattern = a
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:
150             for words in manga.profile.splitlines(fp):
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
156     if profnm is None:
157         profile = manga.profile.profile.last()
158     elif profnm == "":
159         profile = None
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)
183     if profile is not None:
184         mprof = profile.getmanga(libnm, mng.id)
185     else:
186         mprof = None
187
188     download(mng, tdir, pattern or getprop("pattern"))
189
190 if __name__ == "__main__":
191     try:
192         main()
193     except KeyboardInterrupt:
194         pass