8b4300a30a91e89f27ebe93200c5d699abc1c03c
[automanga.git] / manga / mangafox.py
1 import urllib.request, re
2 import bs4, json
3 from . import lib, htcache
4 soup = bs4.BeautifulSoup
5 soupify = lambda cont: soup(cont)
6
7 class page(lib.page):
8     def __init__(self, chapter, stack, n, url):
9         self.stack = stack
10         self.chapter = chapter
11         self.volume = self.chapter.volume
12         self.manga = self.volume.manga
13         self.n = n
14         self.id = str(n)
15         self.name = "Page %s" % n
16         self.url = url
17         self.ciurl = None
18
19     def iurl(self):
20         if self.ciurl is None:
21             page = soupify(htcache.fetch(self.url))
22             self.ciurl = page.find("div", id="viewer").find("img", id="image")["src"]
23         return self.ciurl
24
25     def open(self):
26         return lib.stdimgstream(self.iurl())
27
28     def __str__(self):
29         return self.name
30
31     def __repr__(self):
32         return "<mangafox.page %r.%r.%r.%r>" % (self.manga.name, self.volume.name, self.chapter.name, self.name)
33
34 class chapter(lib.pagelist):
35     def __init__(self, volume, stack, id, name, url):
36         self.stack = stack
37         self.volume = volume
38         self.manga = volume.manga
39         self.id = id
40         self.name = name
41         self.url = url
42         self.cpag = None
43
44     def __getitem__(self, i):
45         return self.pages()[i]
46
47     def __len__(self):
48         return len(self.pages())
49
50     def pages(self):
51         if self.cpag is None:
52             pg = soupify(htcache.fetch(self.url + "1.html"))
53             l = pg.find("form", id="top_bar").find("div", attrs={"class": "l"})
54             if len(l.contents) != 3:
55                 raise Exception("parse error: weird page list for %r" % self)
56             m = l.contents[2].strip()
57             if m[:3] != "of ":
58                 raise Exception("parse error: weird page list for %r" % self)
59             self.cpag = [page(self, self.stack + [(self, n)], n + 1, self.url + ("%i.html" % (n + 1))) for n in range(int(m[3:]))]
60         return self.cpag
61
62     def __str__(self):
63         return self.name
64
65     def __repr__(self):
66         return "<mangafox.chapter %r.%r.%r>" % (self.manga.name, self.volume.name, self.name)
67
68 class volume(lib.pagelist):
69     def __init__(self, manga, stack, id, name):
70         self.stack = stack
71         self.manga = manga
72         self.id = id
73         self.name = name
74         self.ch = []
75
76     def __getitem__(self, i):
77         return self.ch[i]
78
79     def __len__(self):
80         return len(self.ch)
81
82     def __str__(self):
83         return self.name
84
85     def __repr__(self):
86         return "<mangafox.volume %r.%r>" % (self.manga.name, self.name)
87
88 def nextel(el):
89     while True:
90         el = el.nextSibling
91         if isinstance(el, bs4.Tag):
92             return el
93
94 class manga(lib.manga):
95     cure = re.compile(r"/c[\d.]+/$")
96     
97     def __init__(self, lib, id, name, url):
98         self.lib = lib
99         self.id = id
100         self.name = name
101         self.url = url
102         self.cvol = None
103         self.stack = []
104
105     def __getitem__(self, i):
106         return self.vols()[i]
107
108     def __len__(self):
109         return len(self.vols())
110
111     def vols(self):
112         if self.cvol is None:
113             page = soupify(htcache.fetch(self.url))
114             vls = page.find("div", id="chapters").findAll("div", attrs={"class": "slide"})
115             cvol = []
116             for i, vn in enumerate(reversed(vls)):
117                 name = vn.find("h3", attrs={"class": "volume"}).contents[0].strip()
118                 vol = volume(self, [(self, i)], name, name)
119                 cls = nextel(vn)
120                 if cls.name != "ul" or "chlist" not in cls["class"]:
121                     raise Exception("parse error: weird volume list for %r" % self)
122                 for o, ch in enumerate(reversed(cls.findAll("li"))):
123                     n = ch.div.h3 or ch.div.h4
124                     name = n.a.string
125                     for span in ch("span"):
126                         try:
127                             if "title" in span["class"]:
128                                 name += " " + span.string
129                         except KeyError:
130                             pass
131                     url = n.a["href"]
132                     if url[-7:] == "/1.html":
133                         url = url[:-6]
134                     elif self.cure.search(url) is not None:
135                         pass
136                     else:
137                         raise Exception("parse error: unexpected chapter URL for %r: %s" % (self, url))
138                     vol.ch.append(chapter(vol, vol.stack + [(vol, o)], name, name, url))
139                 cvol.append(vol)
140             self.cvol = cvol
141         return self.cvol
142
143     def __str__(self):
144         return self.name
145
146     def __repr__(self):
147         return "<mangafox.manga %r>" % self.name
148
149 def libalphacmp(a, b):
150     return cmp(a.upper(), b.upper())
151
152 class library(lib.library):
153     def __init__(self):
154         self.base = "http://mangafox.me/"
155
156     def alphapage(self, pno):
157         page = soupify(htcache.fetch(self.base + ("directory/%i.htm?az" % pno)))
158         ls = page.find("div", id="mangalist").find("ul", attrs={"class": "list"}).findAll("li")
159         ret = []
160         ubase = self.base + "manga/"
161         for m in ls:
162             t = m.find("div", attrs={"class": "manga_text"}).find("a", attrs={"class": "title"})
163             name = t.string
164             url = t["href"]
165             if url[:len(ubase)] != ubase or url.find('/', len(ubase)) != (len(url) - 1):
166                 raise Exception("parse error: unexpected manga URL for %r: %s" % (name, url))
167             ret.append(manga(self, url[len(ubase):-1], name, url))
168         return ret
169
170     def alphapages(self):
171         page = soupify(htcache.fetch(self.base + "directory/?az"))
172         ls = page.find("div", id="mangalist").find("div", id="nav").find("ul").findAll("li")
173         return int(ls[-2].find("a").string)
174
175     def byname(self, prefix):
176         l = 1
177         r = self.alphapages()
178         while True:
179             if l > r:
180                 return
181             c = l + ((r + 1 - l) // 2)
182             ls = self.alphapage(c)
183             if libalphacmp(ls[0].name, prefix) > 0:
184                 r = c - 1
185             elif libalphacmp(ls[-1].name, prefix) < 0:
186                 l = c + 1
187             else:
188                 pno = c
189                 break
190         i = 0
191         while i < len(ls):
192             m = ls[i]
193             if libalphacmp(m.name, prefix) >= 0:
194                 break
195             i += 1
196         while True:
197             while i < len(ls):
198                 m = ls[i]
199                 if not m.name[:len(prefix)].upper() == prefix.upper():
200                     return
201                 yield m
202                 i += 1
203             pno += 1
204             ls = self.alphapage(pno)
205             i = 0
206
207     def search(self, expr):
208         with urllib.request.urlopen(self.base + ("ajax/search.php?term=%s" % urllib.quote(expr))) as resp:
209             rc = json.load(resp)
210         return [manga(self, id, name, self.base + ("manga/%s/" % id)) for num, name, id, genres, author in rc]
211
212     def byid(self, id):
213         url = self.base + ("manga/%s/" % id)
214         page = soupify(htcache.fetch(url))
215         if page.find("div", id="title") is None:
216             # Assume we got the search page
217             raise KeyError(id)
218         name = page.find("div", id="series_info").find("div", attrs={"class": "cover"}).img["alt"]
219         return manga(self, id, name, url)
220
221     def __iter__(self):
222         raise NotImplementedError("mangafox iterator")