Update nikiroo-utils, update Library
[fanfix.git] / src / be / nikiroo / fanfix / supported / MangaFox.java
CommitLineData
08fe2e33
NR
1package be.nikiroo.fanfix.supported;
2
68686a37 3import java.awt.image.BufferedImage;
08fe2e33
NR
4import java.io.IOException;
5import java.io.InputStream;
6import java.net.MalformedURLException;
7import java.net.URL;
8import java.util.ArrayList;
9import java.util.Collections;
10import java.util.List;
11import java.util.Map.Entry;
12import java.util.Scanner;
13
14import be.nikiroo.fanfix.Instance;
68686a37 15import be.nikiroo.fanfix.data.MetaData;
595dfa7a 16import be.nikiroo.utils.IOUtils;
ed08c171 17import be.nikiroo.utils.Progress;
08fe2e33
NR
18import be.nikiroo.utils.StringUtils;
19
20class MangaFox extends BasicSupport {
21 @Override
22 protected boolean isHtml() {
23 return true;
24 }
25
26 @Override
27 public String getSourceName() {
d3c15421 28 return "MangaFox.me";
08fe2e33
NR
29 }
30
31 @Override
68686a37
NR
32 protected MetaData getMeta(URL source, InputStream in) throws IOException {
33 MetaData meta = new MetaData();
34
35 meta.setTitle(getTitle(reset(in)));
36 meta.setAuthor(getAuthor(reset(in)));
37 meta.setDate(getDate(reset(in)));
38 meta.setTags(getTags(reset(in)));
39 meta.setSource(getSourceName());
2206ef66 40 meta.setUrl(source.toString());
68686a37
NR
41 meta.setPublisher(getSourceName());
42 meta.setUuid(source.toString());
43 meta.setLuid("");
44 meta.setLang("EN");
45 meta.setSubject("manga");
46 meta.setType(getType().toString());
47 meta.setImageDocument(true);
48 meta.setCover(getCover(reset(in)));
49
50 return meta;
08fe2e33
NR
51 }
52
68686a37 53 private List<String> getTags(InputStream in) {
08fe2e33
NR
54 List<String> tags = new ArrayList<String>();
55
56 String line = getLine(in, "/genres/", 0);
57 if (line != null) {
58 line = StringUtils.unhtml(line);
59 String[] tab = line.split(",");
60 if (tab != null) {
61 for (String tag : tab) {
62 tags.add(tag.trim());
63 }
64 }
65 }
66
67 return tags;
68 }
69
68686a37 70 private String getTitle(InputStream in) {
08fe2e33
NR
71 String line = getLine(in, " property=\"og:title\"", 0);
72 if (line != null) {
73 int pos = -1;
74 for (int i = 0; i < 3; i++) {
75 pos = line.indexOf('"', pos + 1);
76 }
77
78 if (pos >= 0) {
79 line = line.substring(pos + 1);
80 pos = line.indexOf('"');
81 if (pos >= 0) {
82 return line.substring(0, pos);
83 }
84 }
85 }
86
87 return null;
88 }
89
68686a37 90 private String getAuthor(InputStream in) {
08fe2e33
NR
91 List<String> authors = new ArrayList<String>();
92
93 String line = getLine(in, "/author/", 0, false);
94 if (line != null) {
95 for (String ln : StringUtils.unhtml(line).split(",")) {
96 if (ln != null && !ln.trim().isEmpty()
97 && !authors.contains(ln.trim())) {
98 authors.add(ln.trim());
99 }
100 }
101 }
102
103 try {
104 in.reset();
105 } catch (IOException e) {
106 Instance.syserr(e);
107 }
108
109 line = getLine(in, "/artist/", 0, false);
110 if (line != null) {
111 for (String ln : StringUtils.unhtml(line).split(",")) {
112 if (ln != null && !ln.trim().isEmpty()
113 && !authors.contains(ln.trim())) {
114 authors.add(ln.trim());
115 }
116 }
117 }
118
119 if (authors.isEmpty()) {
120 return null;
121 } else {
122 StringBuilder builder = new StringBuilder();
123 for (String author : authors) {
124 if (builder.length() > 0) {
125 builder.append(", ");
126 }
127
128 builder.append(author);
129 }
130
131 return builder.toString();
132 }
133 }
134
68686a37 135 private String getDate(InputStream in) {
08fe2e33
NR
136 String line = getLine(in, "/released/", 0);
137 if (line != null) {
138 line = StringUtils.unhtml(line);
139 return line.trim();
140 }
141
142 return null;
143 }
144
145 @Override
146 protected String getDesc(URL source, InputStream in) {
147 String line = getLine(in, " property=\"og:description\"", 0);
148 if (line != null) {
149 int pos = -1;
150 for (int i = 0; i < 3; i++) {
151 pos = line.indexOf('"', pos + 1);
152 }
153
154 if (pos >= 0) {
155 line = line.substring(pos + 1);
156 pos = line.indexOf('"');
157 if (pos >= 0) {
158 return line.substring(0, pos);
159 }
160 }
161 }
162
163 return null;
164 }
165
68686a37 166 private BufferedImage getCover(InputStream in) {
08fe2e33
NR
167 String line = getLine(in, " property=\"og:image\"", 0);
168 String cover = null;
169 if (line != null) {
170 int pos = -1;
171 for (int i = 0; i < 3; i++) {
172 pos = line.indexOf('"', pos + 1);
173 }
174
175 if (pos >= 0) {
176 line = line.substring(pos + 1);
177 pos = line.indexOf('"');
178 if (pos >= 0) {
179 cover = line.substring(0, pos);
180 }
181 }
182 }
183
184 if (cover != null) {
68686a37 185 InputStream coverIn;
08fe2e33 186 try {
68686a37
NR
187 coverIn = openEx(cover);
188 try {
595dfa7a 189 return IOUtils.toImage(coverIn);
68686a37
NR
190 } finally {
191 coverIn.close();
192 }
193 } catch (IOException e) {
08fe2e33
NR
194 }
195 }
196
197 return null;
198 }
199
200 @Override
ed08c171
NR
201 protected List<Entry<String, URL>> getChapters(URL source, InputStream in,
202 Progress pg) {
08fe2e33
NR
203 List<Entry<String, URL>> urls = new ArrayList<Entry<String, URL>>();
204
205 String volumeAt = "<h3 class=\"volume\">";
206 String linkAt = "href=\"http://mangafox.me/";
207 String endAt = "<script type=\"text/javascript\">";
208
209 boolean started = false;
210
211 @SuppressWarnings("resource")
212 Scanner scan = new Scanner(in, "UTF-8");
213 scan.useDelimiter("\\n");
214 while (scan.hasNext()) {
215 String line = scan.next();
216
217 if (started && line.contains(endAt)) {
218 break;
219 } else if (!started && line.contains(volumeAt)) {
220 started = true;
221 }
222
223 if (started && line.contains(linkAt)) {
224 // Chapter content url
225 String url = null;
226 int pos = line.indexOf("href=\"");
227 if (pos >= 0) {
228 line = line.substring(pos + "href=\"".length());
229 pos = line.indexOf('\"');
230 if (pos >= 0) {
231 url = line.substring(0, pos);
232 }
233 }
234
235 // Chapter name
236 String name = null;
237 if (scan.hasNext()) {
238 name = StringUtils.unhtml(scan.next()).trim();
239 // Remove the "new" tag if present
240 if (name.endsWith("new")) {
241 name = name.substring(0, name.length() - 3).trim();
242 }
243 }
244
08fe2e33
NR
245 try {
246 final String key = name;
247 final URL value = new URL(url);
248 urls.add(new Entry<String, URL>() {
249 public URL setValue(URL value) {
250 return null;
251 }
252
253 public String getKey() {
254 return key;
255 }
256
257 public URL getValue() {
258 return value;
259 }
260 });
261 } catch (MalformedURLException e) {
262 Instance.syserr(e);
263 }
264 }
265 }
266
ed08c171
NR
267 if (pg == null) {
268 pg = new Progress(0, urls.size());
269 } else {
270 pg.setMinMax(0, urls.size());
271 }
272
273 int i = 1;
274 for (Entry<String, URL> entry : urls) {
275 // to help with the retry and the originalUrl
276 refresh(entry.getValue().toString());
277 pg.setProgress(i++);
278 }
279
08fe2e33
NR
280 // the chapters are in reversed order
281 Collections.reverse(urls);
282
283 return urls;
284 }
285
286 @Override
ed08c171
NR
287 protected String getChapterContent(URL source, InputStream in, int number,
288 Progress pg) {
289 if (pg == null) {
290 pg = new Progress();
291 } else {
292 // Since we have no idea how many images we have, we cycle from 0
293 // to max, then again, then again...
294 pg.setMinMax(0, 20);
295 }
296
08fe2e33
NR
297 StringBuilder builder = new StringBuilder();
298 String base = getCurrentReferer().toString();
299 int pos = base.lastIndexOf('/');
300 base = base.substring(0, pos + 1); // including the '/' at the end
301
ed08c171 302 int i = 1;
08fe2e33
NR
303 boolean close = false;
304 while (in != null) {
305 String linkNextLine = getLine(in, "return enlarge()", 0);
306 try {
307 in.reset();
308 } catch (IOException e) {
309 Instance.syserr(e);
310 }
311
312 String linkImageLine = getLine(in, "return enlarge()", 1);
313 String linkNext = null;
314 String linkImage = null;
315 pos = linkNextLine.indexOf("href=\"");
316 if (pos >= 0) {
317 linkNextLine = linkNextLine.substring(pos + "href=\"".length());
318 pos = linkNextLine.indexOf('\"');
319 if (pos >= 0) {
320 linkNext = linkNextLine.substring(0, pos);
321 }
322 }
323 pos = linkImageLine.indexOf("src=\"");
324 if (pos >= 0) {
325 linkImageLine = linkImageLine
326 .substring(pos + "src=\"".length());
327 pos = linkImageLine.indexOf('\"');
328 if (pos >= 0) {
329 linkImage = linkImageLine.substring(0, pos);
330 }
331 }
332
333 if (linkImage != null) {
334 builder.append("[");
335 // to help with the retry and the originalUrl, part 1
336 builder.append(withoutQuery(linkImage));
406447a4 337 builder.append("]<br/>");
08fe2e33
NR
338 }
339
340 // to help with the retry and the originalUrl, part 2
341 refresh(linkImage);
ed08c171 342 pg.setProgress((i++) % pg.getMax());
08fe2e33
NR
343
344 if (close) {
345 try {
346 in.close();
347 } catch (IOException e) {
348 Instance.syserr(e);
349 }
350 }
351
352 in = null;
353 if (linkNext != null && !"javascript:void(0);".equals(linkNext)) {
354 URL url;
355 try {
356 url = new URL(base + linkNext);
357 in = openEx(base + linkNext);
358 setCurrentReferer(url);
ed08c171 359 pg.setProgress((i++) % pg.getMax());
08fe2e33
NR
360 } catch (IOException e) {
361 Instance.syserr(new IOException(
362 "Cannot get the next manga page which is: "
363 + linkNext, e));
364 }
365 }
366
367 close = true;
368 }
369
370 setCurrentReferer(source);
371 return builder.toString();
372 }
373
374 @Override
375 protected boolean supports(URL url) {
376 return "mangafox.me".equals(url.getHost())
377 || "www.mangafox.me".equals(url.getHost());
378 }
379
380 /**
381 * Refresh the {@link URL} by calling {@link MangaFox#openEx(String)}.
382 *
383 * @param url
384 * the URL to refresh
385 *
386 * @return TRUE if it was refreshed
387 */
388 private boolean refresh(String url) {
389 try {
390 openEx(url).close();
391 return true;
392 } catch (Exception e) {
393 return false;
394 }
395 }
396
397 /**
398 * Open the URL through the cache, but: retry a second time after 100ms if
399 * it fails, remove the query part of the {@link URL} before saving it to
400 * the cache (so it can be recalled later).
401 *
402 * @param url
403 * the {@link URL}
404 *
405 * @return the resource
406 *
407 * @throws IOException
408 * in case of I/O error
409 */
410 private InputStream openEx(String url) throws IOException {
411 try {
412 return Instance.getCache().open(new URL(url), this, true,
413 withoutQuery(url));
414 } catch (Exception e) {
415 // second chance
416 try {
417 Thread.sleep(100);
418 } catch (InterruptedException ee) {
419 }
420
421 return Instance.getCache().open(new URL(url), this, true,
422 withoutQuery(url));
423 }
424 }
425
426 /**
427 * Return the same input {@link URL} but without the query part.
428 *
429 * @param url
430 * the inpiut {@link URL} as a {@link String}
431 *
432 * @return the input {@link URL} without query
433 */
434 private URL withoutQuery(String url) {
435 URL o = null;
436 try {
437 // Remove the query from o (originalUrl), so it can be cached
438 // correctly
439 o = new URL(url);
440 o = new URL(o.getProtocol() + "://" + o.getHost() + o.getPath());
441
442 return o;
443 } catch (MalformedURLException e) {
444 return null;
445 }
446 }
447}