1 package be
.nikiroo
.fanfix
.supported
;
3 import java
.io
.IOException
;
4 import java
.io
.InputStream
;
5 import java
.net
.MalformedURLException
;
7 import java
.util
.AbstractMap
;
8 import java
.util
.ArrayList
;
9 import java
.util
.Collections
;
10 import java
.util
.List
;
11 import java
.util
.Map
.Entry
;
12 import java
.util
.SortedMap
;
13 import java
.util
.TreeMap
;
15 import org
.jsoup
.helper
.DataUtil
;
16 import org
.jsoup
.nodes
.Element
;
17 import org
.jsoup
.select
.Elements
;
19 import be
.nikiroo
.fanfix
.Instance
;
20 import be
.nikiroo
.fanfix
.data
.MetaData
;
21 import be
.nikiroo
.utils
.Image
;
22 import be
.nikiroo
.utils
.Progress
;
23 import be
.nikiroo
.utils
.StringUtils
;
25 class MangaFox
extends BasicSupport
{
27 protected boolean isHtml() {
32 public String
getSourceName() {
37 protected MetaData
getMeta() throws IOException
{
38 MetaData meta
= new MetaData();
39 Element doc
= getSourceNode();
41 Element title
= doc
.getElementById("title");
42 Elements table
= null;
44 table
= title
.getElementsByTag("table");
48 Elements rows
= table
.first().getElementsByTag("tr");
49 if (rows
.size() > 1) {
50 table
= rows
.get(1).getElementsByTag("td");
51 // Columns: Realeased, Authors, Artists, Genres
52 if (table
.size() < 4) {
58 meta
.setTitle(getTitle());
60 meta
.setAuthor(getAuthors(table
.get(1).text() + ","
61 + table
.get(2).text()));
63 meta
.setDate(StringUtils
.unhtml(table
.get(0).text()).trim());
64 meta
.setTags(explode(table
.get(3).text()));
66 meta
.setSource(getSourceName());
67 meta
.setUrl(getSource().toString());
68 meta
.setPublisher(getSourceName());
69 meta
.setUuid(getSource().toString());
72 meta
.setSubject("manga");
73 meta
.setType(getType().toString());
74 meta
.setImageDocument(true);
75 meta
.setCover(getCover());
80 private String
getTitle() {
81 Element doc
= getSourceNode();
83 Element title
= doc
.getElementById("title");
84 Element h1
= title
.getElementsByTag("h1").first();
86 return StringUtils
.unhtml(h1
.text()).trim();
92 private String
getAuthors(String authorList
) {
94 for (String auth
: explode(authorList
)) {
95 if (!author
.isEmpty()) {
96 author
= author
+ ", ";
105 protected String
getDesc() {
106 Element doc
= getSourceNode();
107 Element title
= doc
.getElementsByClass("summary").first();
109 StringUtils
.unhtml(title
.text()).trim();
115 private Image
getCover() {
116 Element doc
= getSourceNode();
117 Element cover
= doc
.getElementsByClass("cover").first();
119 cover
= cover
.getElementsByTag("img").first();
123 String coverUrl
= cover
.absUrl("src");
127 coverIn
= openEx(coverUrl
);
129 return new Image(coverIn
);
133 } catch (IOException e
) {
134 Instance
.getTraceHandler().error(e
);
142 protected List
<Entry
<String
, URL
>> getChapters(Progress pg
) {
143 List
<Entry
<String
, URL
>> urls
= new ArrayList
<Entry
<String
, URL
>>();
145 String prefix
= null; // each chapter starts with this prefix, then a
146 // chapter number (including "x.5"), then name
148 Element doc
= getSourceNode();
149 for (Element li
: doc
.getElementsByTag("li")) {
150 Element el
= li
.getElementsByTag("h4").first();
152 el
= li
.getElementsByTag("h3").first();
155 Element a
= el
.getElementsByTag("a").first();
157 String title
= StringUtils
.unhtml(el
.text()).trim();
159 String url
= a
.absUrl("href");
160 if (url
.endsWith("1.html")) {
161 url
= url
.substring(0,
162 url
.length() - "1.html".length());
164 if (!url
.endsWith("/")) {
168 if (prefix
== null || !prefix
.isEmpty()) {
169 StringBuilder possiblePrefix
= new StringBuilder(
170 StringUtils
.unhtml(a
.text()).trim());
171 while (possiblePrefix
.length() > 0) {
172 char car
= possiblePrefix
.charAt(possiblePrefix
174 boolean punctuation
= (car
== '.' || car
== ' ');
175 boolean digit
= (car
>= '0' && car
<= '9');
176 if (!punctuation
&& !digit
) {
180 possiblePrefix
.setLength(possiblePrefix
184 if (prefix
== null) {
185 prefix
= possiblePrefix
.toString();
188 if (!prefix
.equalsIgnoreCase(possiblePrefix
190 prefix
= ""; // prefix not ok
194 urls
.add(new AbstractMap
.SimpleEntry
<String
, URL
>(
195 title
, new URL(url
)));
196 } catch (Exception e
) {
197 Instance
.getTraceHandler().error(e
);
203 if (prefix
!= null && !prefix
.isEmpty()) {
205 // We found a prefix, so everything should be sortable
206 SortedMap
<Double
, Entry
<String
, URL
>> map
= new TreeMap
<Double
, Entry
<String
, URL
>>();
207 for (Entry
<String
, URL
> entry
: urls
) {
208 String num
= entry
.getKey().substring(prefix
.length() + 1)
211 int pos
= num
.indexOf(' ');
213 name
= num
.substring(pos
).trim();
214 num
= num
.substring(0, pos
).trim();
217 if (!name
.isEmpty()) {
218 name
= "Tome " + num
+ ": " + name
;
220 name
= "Tome " + num
;
223 double key
= Double
.parseDouble(num
);
225 map
.put(key
, new AbstractMap
.SimpleEntry
<String
, URL
>(name
,
228 urls
= new ArrayList
<Entry
<String
, URL
>>(map
.values());
229 } catch (NumberFormatException e
) {
230 Instance
.getTraceHandler()
231 .error(new IOException(
232 "Cannot find a tome number, revert to default sorting",
234 // by default, the chapters are in reversed order
235 Collections
.reverse(urls
);
238 // by default, the chapters are in reversed order
239 Collections
.reverse(urls
);
246 protected String
getChapterContent(URL chapUrl
, int number
, Progress pg
)
252 StringBuilder builder
= new StringBuilder();
254 String url
= chapUrl
.toString();
255 InputStream imageIn
= null;
256 Element imageDoc
= null;
258 // 1. find out how many images there are
261 // note: when used, the base URL can be an ad-page
262 imageIn
= openEx(url
+ "1.html");
263 imageDoc
= DataUtil
.load(imageIn
, "UTF-8", url
+ "1.html");
267 Element select
= imageDoc
.getElementsByClass("m").first();
268 Elements options
= select
.getElementsByTag("option");
269 size
= options
.size() - 1; // last is "Comments"
271 pg
.setMinMax(0, size
);
274 for (int i
= 1; i
<= size
; i
++) {
275 if (i
> 1) { // because fist one was opened for size
277 imageIn
= openEx(url
+ i
+ ".html");
278 imageDoc
= DataUtil
.load(imageIn
, "UTF-8", url
+ i
285 String linkImage
= imageDoc
.getElementById("image").absUrl("src");
286 if (linkImage
!= null) {
288 // to help with the retry and the originalUrl, part 1
289 builder
.append(withoutQuery(linkImage
));
290 builder
.append("]<br/>");
293 // to help with the retry and the originalUrl, part 2
297 return builder
.toString();
301 * Refresh the {@link URL} by calling {@link MangaFox#openEx(String)}.
306 * @return TRUE if it was refreshed
308 private boolean refresh(String url
) {
312 } catch (Exception e
) {
318 * Open the URL through the cache, but: retry a second time after 100ms if
319 * it fails, remove the query part of the {@link URL} before saving it to
320 * the cache (so it can be recalled later).
325 * @return the resource
327 * @throws IOException
328 * in case of I/O error
330 private InputStream
openEx(String url
) throws IOException
{
332 return Instance
.getCache().open(new URL(url
), this, true,
334 } catch (Exception e
) {
338 } catch (InterruptedException ee
) {
341 return Instance
.getCache().open(new URL(url
), this, true,
347 * Return the same input {@link URL} but without the query part.
350 * the inpiut {@link URL} as a {@link String}
352 * @return the input {@link URL} without query
354 private URL
withoutQuery(String url
) {
357 // Remove the query from o (originalUrl), so it can be cached
360 o
= new URL(o
.getProtocol() + "://" + o
.getHost() + o
.getPath());
363 } catch (MalformedURLException e
) {
369 * Explode an HTML comma-separated list of values into a non-duplicate text
373 * the comma-separated values in HTML format
375 * @return the full list with no duplicate in text format
377 private List
<String
> explode(String values
) {
378 List
<String
> list
= new ArrayList
<String
>();
379 if (values
!= null && !values
.isEmpty()) {
380 for (String auth
: values
.split(",")) {
381 String a
= StringUtils
.unhtml(auth
).trim();
382 if (!a
.isEmpty() && !list
.contains(a
.trim())) {
392 protected boolean supports(URL url
) {
393 return "mangafox.me".equals(url
.getHost())
394 || "www.mangafox.me".equals(url
.getHost())
395 || "fanfox.net".equals(url
.getHost())
396 || "www.fanfox.net".equals(url
.getHost());